Skip to content

Conversation

@matthewlipski
Copy link
Collaborator

Summary

This PR rewrites setSelection to use MultipleNodeSelection when spanning multiple blocks. When the start and end blocks are the same, the entire block content is selected, i.e.:

  • When a block has inline content, a TextSelection spanning it is used.
  • When a block has table content, a CellSelection spanning all cells is used.
  • When a block has no content, a NodeSelection spanning the blockContent node is used.

Rationale

Currently, setSelection uses a TextSelection when spanning multiple nodes. This can cause issues when the start/end blocks do not have content, as it's not possible to find a TextSelection that spans them.

Changes

  • Rewrote setSelection
  • Updated getSelection
  • Added fromJSON to MultipleNodeSelection
  • Updated moveBlocks to work with a MultipleNodeSelection.
  • Added "Mod-a" keyboard handler which calls setSelection on the whole document. The default "Mod-a" shortcut attempts to create a TextSelection, which has the aforementioned issues.

Impact

Should not be a breaking change as the BlockNote API is unchanged.

Testing

We already have tests for getSelection/setSelection. When implementing the changes, I ran them against the tests to ensure they were correct.

Screenshots/Video

N/A

Checklist

  • Code follows the project's coding standards.
  • Unit tests covering the new feature have been added.
  • All existing tests pass.
  • The documentation has been updated to reflect the new feature

Additional Notes

@vercel
Copy link

vercel bot commented Oct 23, 2025

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

Project Deployment Preview Updated (UTC)
blocknote Ready Ready Preview Oct 23, 2025 10:32am
blocknote-website Ready Ready Preview Oct 23, 2025 10:32am

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 23, 2025

Open in StackBlitz

@blocknote/ariakit

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/ariakit@2125

@blocknote/code-block

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/code-block@2125

@blocknote/core

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/core@2125

@blocknote/mantine

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/mantine@2125

@blocknote/react

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/react@2125

@blocknote/server-util

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/server-util@2125

@blocknote/shadcn

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/shadcn@2125

@blocknote/xl-ai

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-ai@2125

@blocknote/xl-docx-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-docx-exporter@2125

@blocknote/xl-email-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-email-exporter@2125

@blocknote/xl-multi-column

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-multi-column@2125

@blocknote/xl-odt-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-odt-exporter@2125

@blocknote/xl-pdf-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-pdf-exporter@2125

commit: b4380ae

);
const $startBlockBeforePos =
tr.selection instanceof MultipleNodeSelection
? tr.selection.$anchor
Copy link
Contributor

@nperez0111 nperez0111 Oct 27, 2025

Choose a reason for hiding this comment

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

anchor can be different than from, depending on direction, so we need to decide whether we are using $from or $anchor

Copy link
Contributor

@nperez0111 nperez0111 left a comment

Choose a reason for hiding this comment

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

Good improvement to make

Comment on lines +202 to +209
tr.setSelection(
CellSelection.create(
tr.doc,
firstCellBeforePos,
lastCellAfterPos -
tr.doc.resolve(lastCellAfterPos).nodeBefore!.nodeSize,
),
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should it be a cell selection or a node selection over the table 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer having less special cases for things like tables & columns, so if we can simplify it. Let's do that instead

Comment on lines +220 to +247
if (blockInfo.blockNoteType === "columnList") {
const firstColumnBeforePos = blockInfo.bnBlock.beforePos + 1;
const firstBlockBeforePos = firstColumnBeforePos + 1;
const lastColumnAfterPos = blockInfo.bnBlock.afterPos - 1;
const lastBlockAfterPos = lastColumnAfterPos - 1;

tr.setSelection(
MultipleNodeSelection.create(
tr.doc,
firstBlockBeforePos,
lastBlockAfterPos -
tr.doc.resolve(lastBlockAfterPos).nodeBefore!.nodeSize,
),
);
}

// Case for when block is a `column`.
if (blockInfo.blockNoteType === "column") {
const firstBlockBeforePos = blockInfo.bnBlock.beforePos + 1;
const lastBlockAfterPos = blockInfo.bnBlock.afterPos - 1;

// Run recursively as the column may only have one block.
setSelection(
tr,
tr.doc.resolve(firstBlockBeforePos).nodeAfter!.attrs.id,
tr.doc.resolve(lastBlockAfterPos).nodeBefore!.attrs.id,
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of always searching for the correct position, can we use helpers to get this so we don't have to write out this logic every time.

In this case specifically though, I think we should be using the getNearestBlock APIs so we don't have to care about the specific strcutures. If we ever change this for columns it will be a hell of a refactor & I'd rather things not be special cased everywhere

Copy link
Contributor

@nperez0111 nperez0111 left a comment

Choose a reason for hiding this comment

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

Hold for refactor

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.

3 participants