Skip to content

docs(openspec): propose block merging via BlocksAPI.merge - #208

Open
gohabereg wants to merge 1 commit into
mainfrom
feat/blocks-merge
Open

gohabereg wants to merge 1 commit into
mainfrom
feat/blocks-merge

Conversation

@gohabereg

Copy link
Copy Markdown
Member

What

Planning only. This PR adds the merge-blocks-on-delete OpenSpec change for #135. It contains no code changes.

One new API method, BlocksAPI.merge, with two overloads:

  • merge({ block }) merges a block into the block before it. Only neighbouring blocks can be merged, so one block is enough.
  • merge({ index }) takes a range that crosses blocks. It removes the content inside the range and the blocks in between, then joins the end block into the start block.
  • Both return where the blocks joined ({ block, key, offset }). merge never moves the caret, like split and convert.

How merging works:

  • The second block is always converted into the first. The first block keeps its id and tool. What the second block contributes is chosen by its conversionConfig.export: a key path keeps formatting, a function gives plain text, and with no config the first text input is used. There's no direction and no separate convert step.
  • Empty blocks give way. If the first block is empty, it's removed instead and the second block survives.
  • Blocks without text inputs are unmergeable. merge throws for them. On the keyboard, Backspace or Delete next to such a block removes it.

The DOM adapter only maps events to calls:

  • Deleting, typing, pasting or pressing Enter over a cross-block selection makes one merge({ index }) call, then the insert or split where the blocks joined.
  • Backspace and Delete at block edges call merge({ block }).
  • The current per-adapter cross-block slicing is removed.

There's no canMerge, no new tool option, and no change to SelectionAPI, TextAPI or the model.

Why

v3 is one contenteditable with cross-block selection, so merging is also needed whenever a deletion crosses a block boundary, not only on Backspace/Delete as in v2. Today none of these cases join anything:

  • Backspace and Delete at a boundary do nothing.
  • A cross-block selection delete leaves the two edge blocks apart. There's a @todo support input merging in BlockToolAdapter at that spot.

Notes for reviewers

🤖 Generated with Claude Code

Add the merge-blocks-on-delete change for #135. A single BlocksAPI.merge
overloaded for a block or a cross-block range joins neighbouring blocks;
Backspace/Delete at boundaries and deletions over cross-block selections
map to it from the DOM adapter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gohabereg
gohabereg requested a balanced review from Copilot October 17, 2026 03:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants