Skip to content

A navigator drag keeps its drop marker until it lands, leaves the panel or is cancelled - #33

Open
LilianBarbe wants to merge 3 commits into
flowtricks:mainfrom
LilianBarbe:navigator-drop-line
Open

LilianBarbe wants to merge 3 commits into
flowtricks:mainfrom
LilianBarbe:navigator-drop-line

Conversation

@LilianBarbe

Copy link
Copy Markdown
Contributor

Navigator drag, before and after

Illustration of the navigator panel, not a screenshot.

The bug

While dragging a row in the navigator, the insertion line flickers and often disappears. NavigatorBody clears the drop target on every dragleave, and dragleave also fires when the pointer moves between two children of the panel: from one row to the next, or onto the marker itself. The marker also takes up layout space and catches the pointer, which makes it worse.

The fix

  • The drop target is cleared only when the pointer really leaves the panel (relatedTarget outside the body), hovers blank space, or the drag ends (drop or Esc).
  • .drop-indicator is absolutely positioned inside its gap with pointer-events: none, so it no longer shifts the gap or becomes a drag target.

Two UI choices, easy to drop if you'd rather not

Each one is a single line in StructureTree.tsx:

  1. No native drag ghost. setDragImage gets a transparent 1×1 image, so no copy of the row sits under the pointer and hides the rows around the drop point. Only the drop line shows where the node will land. This depends on the fix above: with a flickering line, you would lose track of the drop point.
  2. Starting a drag selects the row. Without the ghost, the highlighted row shows what is being moved.

Tests

  • test/navigator.js gets 8 new checks: the marker survives moving onto itself and onto a row, and it clears on leaving the panel, on blank space and on dragend. Without the fix, 5 of them fail. With it, all 49 pass.
  • test:navigatoropencode passes and tsc --noEmit reports nothing.

🤖 Generated with Claude Code

cusable and others added 3 commits September 28, 2026 14:57
…ys put

Typing a word then a space sent the caret to the start of the line. The
Content field emits its text and remembers the html it emitted; every live
save echoes back through the parser, and the echo must come back identical
or the field's sync rewrites the contentEditable mid-keystroke, resetting
the caret to position 0.

The echo differed exactly when a run's boundary gained a space, because the
two sides disagreed on where boundary whitespace lives. The parse keeps
exactly one space where the source had any (collapseText) — a space typed
after a word IS the value. The serializer trimmed it anyway on edited inline
runs written on one line: inlineString(...).trim(). A text node on a line of
its own may trim — the file's indent carries the boundary whitespace back in
on reparse (serializeNodeText) — but a one-line run has nothing else to hold
it, so parse∘serialize was lossy and the field saw its own edit come back
different. The trim predates the boundary-keeping rule; 0.1.33 made it
visible by installing the re-parsed file on every save ack, and 0.1.34's id
adoption fixed the focus loss on top of it, leaving the caret jump as the
remaining symptom (verified: emitted 'hello ' -> written '<h1>hello</h1>' ->
echo 'hello').

The serializer now writes the run's boundary spaces, and the Content field
canonicalizes DOM-read text with the parser's own rule (runs of whitespace
squeeze to one space, one space kept at either boundary) so multi-space
keystrokes converge immediately instead of mismatching once. The browser
renders whitespace runs collapsed, so the field shows the same thing either
way.

Tested by a roundtrip section asserting an edited run keeps its boundary
spaces and saves twice identically, and renderer-rich cases for the
canonicalization including text around an expression chip. Gate: 153/153.
…is cancelled

The panel body cleared the drop target on every dragleave, including
moves between two of its own descendants, so the insertion line flickered
and vanished while hovering the marker or a row. It now clears only when
the pointer leaves the panel, hovers blank space, or the drag ends. The
marker is absolutely positioned and ignores the pointer, so it no longer
shifts the gap or becomes a drag target itself.

Also, as separate UI choices:
- the native drag ghost is replaced by a transparent image, so only the
  drop line shows where the node will land;
- starting a drag selects the dragged row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants