Skip to content

fix(versioning): diff structural changes made by the old Yjs binding - #3173

Draft
YousefED wants to merge 4 commits into
feat/versioning-sidebar-ux-bfrom
fix/legacy-version-diff-structure
Draft

YousefED wants to merge 4 commits into
feat/versioning-sidebar-ux-bfrom
fix/legacy-version-diff-structure

Conversation

@YousefED

@YousefED YousefED commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Replaces #3167. GitHub marked #3167 as merged during a restack of the PRs, so it cannot be reopened.

Stacked on #3090. This fix is on by default. It has no option, because without it the diff does not show the changed block.

Problem

The old y-prosemirror (Yjs v13) binding stored some structural changes inside the same block container. These changes are:

  • a type change of a block;
  • a block that gets or loses nested children.

The current binding prevents this with blockMatchNodes. It stores these changes as a replaced block.

Version history can make a diff of such an old change. The merged render then has two block contents (or two groups) in one container. The schema does not allow this. Thus, @y/prosemirror drops it and shows no error. The changed block is not in the diff. For example, a paragraph → heading change shows only the deleted paragraph.

Changes

snapshotPreview.ts: before the diff renders, splitChangedBlocks examines the decoded snapshot. Some blocks existed in the baseline, and blockMatchNodes considers them changed. splitChangedBlocks replaces each of these blocks with a new copy. The diff then shows a deleted block next to an inserted block. This is the same diff as for a change by the current binding.

  • The live document does not change. The split changes only the decoded snapshot. The snapshot is a temporary document. It is never the live document or the stored document. A test makes sure that the document stays the same when the diff shows.
  • Attributions. The copy and the deleted block get the attributions of the items that changed the structure of the block: the new or deleted content node or child group. Each attribution keeps its user and its time. When the change only inserted (or only deleted), the attributions also go to the other side, as the other kind (insert/insertAt becomes delete/deleteAt). Other content in the block, such as text that a different user typed, does not get the credit for the change.

blockMatchNodes.ts: the table rule is off. Before, the collaboration binding replaced a table when one edit changed the number of rows and the number of columns. This rule has two problems:

  • It replaced the full table. Thus, a concurrent edit of a different user in the table was lost without a message.
  • A version diff compares all the changes between two versions. Two users can each add a row or a column at the same time. Together, these changes cross the rule. Thus, the diff showed the full table as deleted and inserted again.

Now a table edit always merges in place. One-direction resizes always did this. Two concurrent resizes can still merge into a table with an unusual shape. This was already possible before this change.

Covered (the diff is the same as for a change by the current binding, with the same authors)

  • type change (paragraph → heading);
  • type change of a block with nested children;
  • type change and a text edit in the same block;
  • nesting change (a block gets a child group);
  • table resize in the two directions: compared cell by cell, without a split;
  • plain text edits by the old binding: not split (control test).

Not covered

  • Suggestion mode over type changes from the old binding: live suggestions do not use snapshotPreview.
  • Restore of a version across such a change: not tested.
  • Concurrent type changes between two clients with the old binding: these clients delete the block before history sees it. The block is attributed to one of the clients. See the provenance discussion in feat!: rebuild version history and customize snapshot actions #3090.
  • Old-binding documents with two child groups in one block: these can occur after concurrent nesting with the old binding. This problem is older than this stack.
  • Performance: for each comparison, the split makes a delta of the two versions for each block that existed in the baseline. We did not measure this on large documents.

Tests

The tests are in #3090, in legacyYjsDocBinding.test.ts. On #3090, the failing tests have the comment "To be fixed by #3173". This PR changes them to normal tests:

  • "old binding diffs like the current binding", for the 4 structural cases;
  • a table resize, compared cell by cell;
  • a type change after a text edit by a different user: only the user that changed the type gets the credit, and no time shows as a user.
  • tableReshape.test.ts: "keeps a concurrent cell edit when a different user reshapes the table". Before, the table rule replaced the table, and the cell edit was lost.

Other tests in this file pass on #3090 and on this PR:

  • a control: the type-change diff of the current binding, with authors;
  • a text-edit control, which must not split;
  • the live document stays the same when the diff shows.

Versioning snapshot (tests/src/end-to-end/y-prosemirror/__snapshots__/versioning.test.tsx.snap): the table rule change changes 5 table scenarios. In each of them, two users add or delete rows and columns at the same time. The table now merges in place, and the diff shows the added or deleted rows and columns. Before, the diff showed the full table as deleted and inserted again:

  • "A adds column then row, B adds column";
  • "A adds row then column, B adds row";
  • "Add column vs add row";
  • "Add row vs add column";
  • "Delete row vs add column".

All other scenarios show the same diff as on #3090.

@vercel

vercel Bot commented Oct 8, 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 Oct 9, 2026 2:19pm UTC
blocknote-website Ready Ready Preview Oct 9, 2026 2:19pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

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

@pkg-pr-new

pkg-pr-new Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@blocknote/ariakit

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

@blocknote/code-block

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

@blocknote/core

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

@blocknote/diagram-block

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

@blocknote/mantine

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

@blocknote/math-block

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

@blocknote/react

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

@blocknote/server-util

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

@blocknote/shadcn

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

@blocknote/xl-ai

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

@blocknote/xl-docx-exporter

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

@blocknote/xl-email-exporter

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

@blocknote/xl-multi-column

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

@blocknote/xl-odt-exporter

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

@blocknote/xl-pdf-exporter

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

@blocknote/xl-typst-exporter

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

commit: a7e23b9

@github-actions

github-actions Bot commented Oct 8, 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-3173/

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

The old binding changed a block's type in place, keeping the old and the new
content in one block, which a diff renders as schema-invalid content that is
then dropped. Before rendering a diff, replace each block that changed
structurally since the earlier version with a copy, as the current binding
stores such a change, so it shows as a deleted and an inserted block. The
copy and the deletion keep the change's authors.

The versioning snapshot shows that this also splits tables two users resize
concurrently (to revisit).
… ways

Replacing such a table silently dropped concurrent edits to it. Version
diffs compare all the changes between two versions, so two concurrent
one-way reshapes (a row and a column) replaced the whole table there too.
Reshapes now merge in place, as one-way reshapes always did.
…ver made it

The split credited every attribution found anywhere in the block to both
sides, timestamps included: `insertAt` became an "insert" by a user named
like the time. It now credits the content nodes and child groups that
changed the block, with their own attributions. The split only changes the
decoded snapshot; a test checks that the document stays unchanged.
…ange to its writer

The split replaced the block with a clone and credited all of the clone to
whoever changed the block's structure, including text that someone else
typed into the block later. The clone is now paired with the original unit
by unit (it has the same content in the same order), and content added
after the baseline keeps its own author.

This branch was successfully deployed

2 active deployments
Preview – blocknote-website — a7e23b9e Deployed Oct 9, 2026 by vercel[bot]
Preview – blocknote — a7e23b9e Deployed Oct 9, 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.

1 participant