diff --git a/src/uix/eidos/components/words/words-active-rail.svelte b/src/uix/eidos/components/words/words-active-rail.svelte index f31167fbf..650cdae9c 100644 --- a/src/uix/eidos/components/words/words-active-rail.svelte +++ b/src/uix/eidos/components/words/words-active-rail.svelte @@ -28,7 +28,13 @@ rail = null; return; } - const br = el.getBoundingClientRect(); + // Climb to the TOP-LEVEL block (a direct child of content) so the rail + // spans the whole block — e.g. a full table, not the clicked cell. + let top: HTMLElement = el; + while (top.parentElement && top.parentElement !== content) { + top = top.parentElement; + } + const br = top.getBoundingClientRect(); const fr = frame.getBoundingClientRect(); const cr = content.getBoundingClientRect(); // Hide when the active block is scrolled out of the content viewport. diff --git a/src/uix/eidos/components/words/words-block-gutter.svelte b/src/uix/eidos/components/words/words-block-gutter.svelte index 8f7089924..2a322abe0 100644 --- a/src/uix/eidos/components/words/words-block-gutter.svelte +++ b/src/uix/eidos/components/words/words-block-gutter.svelte @@ -227,7 +227,12 @@ show = false; } function remove() { + // Capture the target BEFORE the delete (reading the doc after returns + // the wrong block); move the active to the previous block. + const kids = api.document.children; + const prevId = (index > 0 ? kids[index - 1]?.id : kids[1]?.id) ?? null; api.applyCommand({ type: 'deleteBlock', blockIndex: index }); + api.setActiveBlock(prevId); show = false; } function insert(block: Record) { diff --git a/src/uix/eidos/components/words/words-inspector.svelte b/src/uix/eidos/components/words/words-inspector.svelte index 388f893ad..89d9e3bd1 100644 --- a/src/uix/eidos/components/words/words-inspector.svelte +++ b/src/uix/eidos/components/words/words-inspector.svelte @@ -80,6 +80,19 @@ const activeBlockPath = $derived(activeLoc?.path); + // Delete the active TOP-LEVEL block (mirrors the gutter menu's Delete) and + // keep a block active by moving to the previous one (or the new first). + function deleteActiveBlock() { + if (activeIndex < 0) return; + // Capture the target BEFORE the delete — reading the doc AFTER + // `deleteBlock` returns the wrong block. Move to the previous block + // (or, when deleting the first, the one that becomes first). + const kids = api.document.children; + const prevId = (activeIndex > 0 ? kids[activeIndex - 1]?.id : kids[1]?.id) ?? null; + api.applyCommand({ type: 'deleteBlock', blockIndex: activeIndex }); + api.setActiveBlock(prevId); + } + // Nesting depth of the active node (1 = top-level block, 2 = column, // 3 = block-inside-a-column, …), capped at 4. Stamped on the inspector // root so the header mark/wash picks up the SAME depth accent the canvas @@ -414,6 +427,19 @@ activeLoc.ancestry[activeLoc.ancestry.length - 1] )}{/if} + {#if activeIndex >= 0} + + {/if} diff --git a/src/uix/eidos/components/words/words.css b/src/uix/eidos/components/words/words.css index fecbb0161..4da5884a3 100644 --- a/src/uix/eidos/components/words/words.css +++ b/src/uix/eidos/components/words/words.css @@ -454,6 +454,12 @@ text-overflow: ellipsis; white-space: nowrap; } +/* Delete-block button — pinned to the right edge of the inspector title row. */ +[data-words-inspector-delete] { + flex: none; + margin-inline-start: auto; + color: var(--color-threat-solid); +} /* Invalid free-text input (column width / gap / image URL) — a red ring that does not shift layout. Set imperatively by the panel's commit helpers when a value fails validation; cleared on the next valid commit