From ccd9d3e0abe24ef25d06abecfee61312da1b3438 Mon Sep 17 00:00:00 2001 From: dev Date: Thu, 28 May 2026 00:33:41 +0200 Subject: [PATCH] fix(words): "+" inserter only shows in the rail gap between blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../words/words-block-inserter.svelte | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/uix/eidos/components/words/words-block-inserter.svelte b/src/uix/eidos/components/words/words-block-inserter.svelte index b6cde0f9a..806fa9528 100644 --- a/src/uix/eidos/components/words/words-block-inserter.svelte +++ b/src/uix/eidos/components/words/words-block-inserter.svelte @@ -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( + '[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; }