From 2f6bdb48312db85b0534a9df512e329d80be2d8a Mon Sep 17 00:00:00 2001 From: dev Date: Wed, 3 Jun 2026 03:27:15 +0200 Subject: [PATCH] =?UTF-8?q?words:=20rail=20spans=20the=20top-level=20block?= =?UTF-8?q?=20+=20delete=20button=20+=20delete=E2=86=92previous=20active?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three rich-text-chrome fixes: - The active rail tracked the clicked element, so a table cell gave a 39px rail on a 118px table. `words-active-rail` now climbs to the TOP-LEVEL block (direct child of content) and spans its full height. - Delete-block was only in the (hidden) gutter grip menu. Added a Trash button to the inspector title row — always visible for the active block. - Deleting a block left the active dangling (it fell back to the first block). Both delete paths (inspector + gutter menu) now capture the previous block's id BEFORE the delete (reading the doc AFTER `deleteBlock` returns the wrong index) and move the active to it — so there's always a sensible active block. Verified in-browser: clicking a table cell, the rail spans the whole table (118px); the inspector delete button is present; deleting a block moves the active to the previous one (e.g. delete "Lists" → active "const greet"). Check clean for the touched files. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../components/words/words-active-rail.svelte | 8 +++++- .../words/words-block-gutter.svelte | 5 ++++ .../components/words/words-inspector.svelte | 26 +++++++++++++++++++ src/uix/eidos/components/words/words.css | 6 +++++ 4 files changed, 44 insertions(+), 1 deletion(-) 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