Notion-style :: grip rendered to the left of the hovered block via a
single overlay component (`Words.BlockHandle`). Click reveals a small
popover with Move up / Move down / Duplicate / Delete. Each fires via
`snippet.applyCommand` so the discrete imperatives bypass the sema
runtime (no extra event noise on top of what the operation already
implies for downstream consumers).
Backed by three new generic operations on the engine:
- `moveBlockAt(state, blockIndex, direction)` — swap with neighbour.
- `duplicateBlockAt(state, blockIndex)` — JSON-clone insert-after.
- `deleteBlockAt(state, blockIndex)` — already existed; now wired
to the menu's Delete entry.
The three operations are exposed as `moveBlock` / `duplicateBlock` /
`deleteBlock` commands in `WordsCommand` so any future block-toolbar
can reuse them without per-feature engine plumbing.
Also: add `applyCommand` / `selectAtomicBlock` / `clearSelectedBlock`
to `WordsProviderSnippetProps` — they were emitted by the provider
but missing from the type. Float bar + block handle both consume
`snippet.applyCommand`.
Verified in browser: hover over a block renders the grip (24×24,
opacity 0.45 → 1 on hover, z-index popover); click opens menu with
all 4 actions correctly labelled; Duplicate on the heading at
index 0 produced a new heading at index 1 and pushed the rest down.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>