Skip to content

refactor(core): use the new position helpers in the keyboard shortcuts - #3101

Open
YousefED wants to merge 3 commits into
mainfrom
block-info-api/position-helpers
Open

YousefED wants to merge 3 commits into
mainfrom
block-info-api/position-helpers

Conversation

@YousefED

@YousefED YousefED commented Sep 21, 2026 •

Copy link
Copy Markdown
Collaborator

Three follow-ups on top of #3051, all behaviour-preserving. Based on that branch, so the diff is just these changes.

1. tableContentCaretPos no longer escapes the module

Both callers in KeyboardShortcutsExtension checked contentKind === "table" themselves and then called the table helper — re-making the decision blockEdgePos already makes:

if (info.contentKind === "table") {
  ...setTextSelection(tableContentCaretPos(info.content, "end"))
} else if (info.contentKind === "none") {
  ...setNodeSelection(info.content.beforePos)
} else {
  ...setTextSelection(info.content.afterPos - 1)
}

All three arms are blockEdgePos, whose non-table path returns exactly contentStart / contentEnd. Each site is now one call plus the null case — content with no caret, i.e. an image — which is the only thing the helper can't decide for you. The +4 table offset is back to living in one place instead of three, and the function is module-private again.

2. The keyboard shortcuts use contentStart / contentEnd

BlockInfo gained those fields in #3051 precisely so callers stop writing the arithmetic, but this file still did it by hand in twelve places (content.beforePos + 1, content.afterPos - 1) — every one a "is the caret at the start/end of this block's content?" check.

Replacing them turned out to make content unused in five of the destructures, so those shrank too:

const { block, content, contentStart } = blockInfo;   →   const { block, contentStart } = blockInfo;

3. A comment on getInsertionPos's lazy-blockGroup branch

Reading if (!info.children) it isn't obvious why hardcoding wrapIn: blockGroup is safe, or why hasContent is tested when it can't be false. Both follow from the BlockInfo union — the container arm makes children required, so only a regular block reaches that branch, and the hasContent test is there to narrow the union so content can be read. The comment says so.

Verification

Behaviour-preserving, checked against both main and #3051 with two differential harnesses — identical scenarios run on each branch and the outputs diffed:

result
core: 1,025 scenarios (keyboard at many offsets over 8 document shapes, block API, navigation, conversions, selection, undo/redo) vs #3051 before these changes 0 differences
core: same, vs main 0 differences
columns: 737 scenarios over 4 layouts, vs #3051 before these changes 0 differences

Plus core 796, multi-column 82, tests 908, type-aware lint clean.

Net −56 / +39 across the two files.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Block insertion now supports placing blocks as the first or last child of another block.
    • Added clearer guidance for inserting nested blocks.
  • Bug Fixes

    • Improved Backspace, Delete, and Enter behavior when editing blocks, including at block boundaries and in nested content.
    • Caret positioning works more consistently across text selections, node selections, tables, and empty content.
    • Invalid initial documents and incompatible block placements are now reported.

nperez0111 and others added 3 commits September 7, 2026 12:14
… for block plumbing

Replaces the BlockInfo union's isBlockContainer/childContainer/blockContent
shape with block/content/children, and annotates it with the facts callers
kept re-deriving by hand: contentStart/contentEnd, childrenStart/
childrenEnd, contentKind (read off the spec config stored on the node), and
isContentEmpty. The +1/-1 position arithmetic around content edges, tables
and child ranges moves into blockEdgePos/blockEdgeSelection/
tableContentCaretPos and the ChildrenInfo fields.

The six producers collapse to four named by the input you already have:
getBlockInfoFromNode, getBlockInfoAt, getBlockInfoNearPos,
getBlockInfoFromSelection. Block navigation (parent/prev/next/last-
descendant) joins them here instead of living beside the merge command.

All block manipulation (insert/move/nest/replace/split/update, selections,
clipboard, serialization, conversions, keyboard shortcuts) is rewired onto
the new vocabulary. insertBlocks gains "first-child"/"last-child"
placements resolved through getInsertionPos, shared with the move commands
so "can this block go here?" has one schema-driven answer; hand-written
nodes are checked against their declared content kind when the schema is
built (checkNodeMatchesConfig).
Resolve block shape directly, validate wrapper structure at the BlockInfo boundary, and convert content from the established content kind. Move insertion resolution into BlockInfo and use node bounds for last-descendant navigation, updating callers and regression coverage.
Three follow-ups on the BlockInfo refactor, all behaviour-preserving.

`tableContentCaretPos` is no longer exported: both callers checked
`contentKind === "table"` themselves and then called it, duplicating the
branch `blockEdgePos` already makes. They now ask `blockEdgePos` for the
edge, so the table offset lives in one place instead of three.

The keyboard shortcuts computed a block's content edges by hand in twelve
places (`content.beforePos + 1` / `content.afterPos - 1`) rather than
reading `contentStart` / `contentEnd`, which the refactor added for
exactly that. Replacing them left `content` unused in five destructures.

`getInsertionPos`'s lazy-blockGroup branch says why only a regular block
reaches it, so `wrapIn: blockGroup` reads as implied rather than assumed,
and why the `hasContent` check is there to narrow the union.
@vercel

vercel Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
blocknote Ready Ready Preview Sep 21, 2026 10:00am UTC
blocknote-website Ready Ready Preview Sep 21, 2026 10:00am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 14d98142-dace-4a7b-a4cf-2c09c7a49577

📥 Commits

Reviewing files that changed from the base of the PR and between 862e4ea and 862e4ea.


📒 Files selected for processing (46)
  • docs/content/docs/reference/editor/manipulating-content.mdx
  • packages/core/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts
  • packages/core/src/api/blockManipulation/commands/insertBlocks/insertPlacement.test.ts
  • packages/core/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts
  • packages/core/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.ts
  • packages/core/src/api/blockManipulation/commands/moveBlocks/moveBlocks.test.ts
  • packages/core/src/api/blockManipulation/commands/moveBlocks/moveBlocks.ts
  • packages/core/src/api/blockManipulation/commands/nestBlock/nestBlock.ts
  • packages/core/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.test.ts
  • packages/core/src/api/blockManipulation/commands/splitBlock/splitBlock.test.ts
  • packages/core/src/api/blockManipulation/commands/splitBlock/splitBlock.ts
  • packages/core/src/api/blockManipulation/commands/updateBlock/updateBlock.test.ts
  • packages/core/src/api/blockManipulation/commands/updateBlock/updateBlock.ts
  • packages/core/src/api/blockManipulation/getBlock/getBlock.ts
  • packages/core/src/api/blockManipulation/selections/selection.ts
  • packages/core/src/api/blockManipulation/selections/textCursorPosition.ts
  • packages/core/src/api/blockManipulation/setupTestEnv.ts
  • packages/core/src/api/clipboard/fromClipboard/handleFileInsertion.ts
  • packages/core/src/api/getBlockInfoFromPos.test.ts
  • packages/core/src/api/getBlockInfoFromPos.ts
  • packages/core/src/api/getBlocksChangedByTransaction.test.ts
  • packages/core/src/api/nodeConversions/nodeToBlock.ts
  • packages/core/src/api/nodeUtil.ts
  • packages/core/src/blocks/ListItem/ListItemKeyboardShortcuts.ts
  • packages/core/src/blocks/ListItem/NumberedListItem/IndexingPlugin.ts
  • packages/core/src/blocks/utils/listItemEnterHandler.ts
  • packages/core/src/editor/BlockNoteEditor.test.ts
  • packages/core/src/editor/BlockNoteEditor.ts
  • packages/core/src/editor/managers/BlockManager.ts
  • packages/core/src/editor/managers/ExtensionManager/index.ts
  • packages/core/src/editor/transformPasted.ts
  • packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.test.ts
  • packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts
  • packages/core/src/extensions/tiptap-extensions/UniqueID/UniqueID.ts
  • packages/core/src/schema/blocks/createSpec.ts
  • packages/core/src/schema/blocks/types.ts
  • packages/core/vitestSetup.ts
  • packages/react/vitestSetup.ts
  • packages/xl-ai/src/api/formats/html-blocks/collabUpdate.test.ts
  • packages/xl-ai/src/prosemirror/agent.test.ts
  • packages/xl-ai/src/prosemirror/rebaseTool.test.ts
  • packages/xl-ai/src/testUtil/cases/combinedOperationsTestCases.ts
  • packages/xl-ai/src/testUtil/cases/updateOperationTestCases.ts
  • packages/xl-multi-column/src/extensions/DropCursor/multiColumnHandleDropPlugin.ts
  • tests/src/end-to-end/keyboardhandlers/keyboardhandlers.test.tsx
  • tests/vitestSetup.browser.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.



📝 Walkthrough

Walkthrough

Block metadata now represents both content-bearing blocks and child containers. Block insertion supports child placements with schema validation. Editing commands, keyboard handlers, selection helpers, and integrations use the updated metadata and position APIs.

Changes

Block operations

Layer / File(s) Summary
Block metadata and schema contract
packages/core/src/api/getBlockInfoFromPos.ts, packages/core/src/api/getBlockInfoFromPos.test.ts, packages/core/src/schema/blocks/*
BlockInfo now reports block, child, and content positions and content classifications. The lookup API adds exact, nearby, parent, sibling, and descendant helpers. Schema construction attaches block configuration to node specs and validates configured content expressions.
Placement, movement, and nesting
packages/core/src/api/blockManipulation/commands/insertBlocks/*, packages/core/src/api/blockManipulation/commands/{mergeBlocks,moveBlocks,nestBlock}/*, packages/core/src/editor/BlockNoteEditor.ts, packages/core/src/editor/managers/BlockManager.ts, docs/content/docs/reference/editor/manipulating-content.mdx
insertBlocks accepts before, after, first-child, and last-child placements and checks schema acceptance. Move, merge, and nesting commands use block metadata and schema-compatible placement checks. The reference guide documents child placement.
Block editing and selection
packages/core/src/api/blockManipulation/commands/{replaceBlocks,splitBlock,updateBlock}/*, packages/core/src/api/blockManipulation/selections/*, packages/core/src/api/blockManipulation/getBlock/*
Update, split, replace, cursor, selection, and parent lookup paths use the updated block and content positions. Tests cover replacement within transactions and metadata-based selection setup.
Keyboard and list-item behavior
packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/*, packages/core/src/blocks/ListItem/*
Backspace, Delete, Enter, and Shift-Tab handlers use generic block information, content boundaries, and edge-selection helpers. Added tests cover merges, lifting, deletion, insertion, and un-nesting. List-item handlers use content presence and emptiness metadata.
Editor integrations and test support
packages/core/src/api/nodeConversions/*, packages/core/src/editor/*, packages/core/src/api/clipboard/*, packages/core/vitestSetup.ts, packages/react/vitestSetup.ts, packages/xl-ai/src/*, packages/xl-multi-column/src/*, tests/*
Node conversion, paste handling, input rules, drop handling, and associated tests use the new metadata API. Initial editor documents are checked, and test setup supports globalThis where window is unavailable.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~60 minutes

Change: Refactor

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant BlockManager
  participant InsertBlocks
  participant Schema
  Caller->>BlockManager: insertBlocks with placement
  BlockManager->>InsertBlocks: forward blocks and placement
  InsertBlocks->>Schema: resolve valid insertion position
  Schema-->>InsertBlocks: position and optional wrapper
  InsertBlocks-->>Caller: inserted blocks
Loading

Suggested reviewers: nperez0111


Merge Risk: ⚪ Minimal · up to 862e4

This update refactors how keyboard shortcuts calculate caret positions inside blocks and adds tests that pin down the current behavior. No concrete user-facing defect was found, so the change looks ready to merge. One caveat: the block-info helpers were renamed and reshaped. If outside code relies on the old helper names, consider noting this in the release notes.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 70 functions across 45 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check Passed The title clearly identifies the main change: refactoring keyboard shortcut position handling to use the new helpers.
Description check Passed The description explains the changes, rationale, behavior impact, and extensive verification results. It does not use every template heading, but it provides the required substantive information.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.

Full details: Docstring Coverage

Explanation

Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 70 functions across 45 files. (1 skipped: 1 unsupported.)



  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch block-info-api/position-helpers

🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR



Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit checks each block in line
Then nests a page with care
It taps the keys, the edges align
And hops through content everywhere
The burrow grows one child at a time

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@blocknote/ariakit

npm i https://pkg.pr.new/@blocknote/ariakit@3101

@blocknote/code-block

npm i https://pkg.pr.new/@blocknote/code-block@3101

@blocknote/core

npm i https://pkg.pr.new/@blocknote/core@3101

@blocknote/diagram-block

npm i https://pkg.pr.new/@blocknote/diagram-block@3101

@blocknote/mantine

npm i https://pkg.pr.new/@blocknote/mantine@3101

@blocknote/math-block

npm i https://pkg.pr.new/@blocknote/math-block@3101

@blocknote/react

npm i https://pkg.pr.new/@blocknote/react@3101

@blocknote/server-util

npm i https://pkg.pr.new/@blocknote/server-util@3101

@blocknote/shadcn

npm i https://pkg.pr.new/@blocknote/shadcn@3101

@blocknote/xl-ai

npm i https://pkg.pr.new/@blocknote/xl-ai@3101

@blocknote/xl-docx-exporter

npm i https://pkg.pr.new/@blocknote/xl-docx-exporter@3101

@blocknote/xl-email-exporter

npm i https://pkg.pr.new/@blocknote/xl-email-exporter@3101

@blocknote/xl-multi-column

npm i https://pkg.pr.new/@blocknote/xl-multi-column@3101

@blocknote/xl-odt-exporter

npm i https://pkg.pr.new/@blocknote/xl-odt-exporter@3101

@blocknote/xl-pdf-exporter

npm i https://pkg.pr.new/@blocknote/xl-pdf-exporter@3101

@blocknote/xl-typst-exporter

npm i https://pkg.pr.new/@blocknote/xl-typst-exporter@3101

commit: 862e4ea

@github-actions

github-actions Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://TypeCellOS.github.io/BlockNote/pr-preview/pr-3101/

Built to branch gh-pages at 2026-09-22 10:14 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Base automatically changed from refactor/block-info-api to main October 9, 2026 15:18

This branch was successfully deployed

2 active deployments
Preview – blocknote-website — 862e4ea6 Deployed Sep 21, 2026 by vercel[bot]
Preview – blocknote — 862e4ea6 Deployed Sep 21, 2026 by vercel[bot]
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