words: rail spans the top-level block + delete button + delete→previous active

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) <noreply@anthropic.com>
active-uix
dev 4 months ago
parent dd7d67d2a1
commit 2f6bdb4831

@ -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.

@ -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<string, unknown>) {

@ -80,6 +80,19 @@
const activeBlockPath = $derived<readonly number[] | undefined>(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}
</span>
{#if activeIndex >= 0}
<Button
data-words-inspector-delete
variant="ghost"
size="xs"
iconOnly
rounded="md"
aria-label="Delete block"
onclick={deleteActiveBlock}
>
{#snippet icon()}<Trash2 />{/snippet}
</Button>
{/if}
</div>
</header>

@ -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

Loading…
Cancel
Save

Powered by TurnKey Linux.