fix(words): "+" inserter only shows in the rail gap between blocks

Previously the "+" snapped to any seam within 18 px of the cursor's
Y — which meant it appeared even when the cursor was hovering on a
block's body (near its top/bottom edge) or in the rail aligned with
the block. That clashed with the grip's territory.

Per user spec: when the cursor is on a block (text or rail-aligned),
the grip is the actor — the "+" should be hidden. The "+" only
surfaces when the cursor is in the rail column AND vertically
positioned in a gap BETWEEN two blocks.

Two new gates added to `findSeamForCursor`:
1. **Out-of-content X**: if `e.clientX` falls inside the content
   box's horizontal extent (the editor's text area), return null
   immediately. The cursor is reading text or near the toolbar/
   status row, not the rail.
2. **Out-of-block Y**: if `e.clientY` falls inside ANY top-level
   block's vertical band, return null. The block-handle owns that
   slot.

Only when both gates pass do we snap to the nearest seam. The
existing `dragover` path is untouched — drag indicator still works
inside the content area because the drag operation has different
ergonomics (you're actively moving a block, the line is the drop
target, not a discoverability cue).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
active-uix
dev 4 months ago
parent 6bcbd8a983
commit ccd9d3e0ab

@ -73,13 +73,39 @@
return seams;
}
/**
* The "+" only shows up when the cursor is OUTSIDE the content box
* (i.e. in the rail column) AND vertically positioned in a gap
* between two blocks. Hovering over a block — either on its text
* or on the rail aligned with the block — surfaces the grip
* instead (block-handle is the actor there). Per user spec:
* "el + solo aparece en la banda entre bloques".
*/
function findSeamForCursor(e: MouseEvent, content: HTMLElement): Seam | null {
const contentRect = content.getBoundingClientRect();
// Cursor inside the content's horizontal extent → user is reading
// text or is in the editor body, not the rail. No "+".
if (e.clientX >= contentRect.left && e.clientX <= contentRect.right) {
return null;
}
// Cursor inside ANY block's vertical band → grip's territory.
const blocks = Array.from(
content.querySelectorAll<HTMLElement>(
'[data-words-node="block"][data-words-path]'
)
).filter((b) => !(b.getAttribute('data-words-path') ?? '').includes('.'));
for (const block of blocks) {
const r = block.getBoundingClientRect();
if (e.clientY >= r.top && e.clientY <= r.bottom) return null;
}
// Cursor is in the rail AND between blocks — snap to the nearest
// seam. No X-bound check needed; we already know X is in the rail.
const seams = listBlockBoundaries(content);
let best: Seam | null = null;
let bestDelta = SEAM_RANGE_PX;
for (const s of seams) {
const delta = Math.abs(s.y - e.clientY);
if (delta < bestDelta && e.clientX >= s.left - 32 && e.clientX <= s.right + 32) {
if (delta < bestDelta) {
best = s;
bestDelta = delta;
}

Loading…
Cancel
Save

Powered by TurnKey Linux.