Fixes the wedge case the user just hit: two atomic blocks back-to-back
(image, table, code) leave no caret position between them, so there's
no way to insert a paragraph by clicking. Now: hover near the seam
between any two top-level blocks and a "+" appears on the left margin
with a faint accent line spanning the seam. Click "+" → a new
paragraph is dropped at that index and the caret lands at the start
so the user starts typing immediately.
Same overlay pattern as the block-handle: single component watches
mousemove inside `[data-words-content]`, computes the boundaries of
all top-level blocks (plus "before first" and "after last" seams),
snaps to the closest seam within 18px of the cursor's Y, and renders
a fixed-positioned strip with the "+" button and accent line.
New engine surface:
- `insertBlockAt(state, blockIndex, block)` in `operations.ts` —
splices the block in, normalizes, drops caret at `[blockIndex, 0]`.
`blockIndex === children.length` appends to the tail.
- `insertBlock` case added to `WordsCommand` + dispatcher in
`commands.ts`. The command surface keeps `block` as a loose JSON
record so consumers don't need to import `WordsBlock` — the
reducer casts on the boundary and `normalizeDocument` validates.
Verified in browser: hover at the seam between code (index 3) and
image (index 4) → "+" appears at the left margin → click → doc
becomes 0:heading | 1:paragraph | 2:quote | 3:code | 4:paragraph |
5:image; caret in the new paragraph ready for input.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>