Skip to content

feat(versioning): show copied blocks once in version diffs (experimental) - #3172

Closed
YousefED wants to merge 3 commits into
fix/version-diff-cascade-attributionfrom
feat/version-diff-block-copies
Closed

YousefED wants to merge 3 commits into
fix/version-diff-cascade-attributionfrom
feat/version-diff-block-copies

Conversation

@YousefED

@YousefED YousefED commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #3166.

Summary

This PR shows a type change or a move of a block once in a version diff. It is behind an experimental option: collaboration.experimental.versionDiffFixes: "implicitDeleteAttributionAndRecreatedBlocks". This value also includes the fix of #3166 ("implicitDeleteAttribution"). The option changes only what the version diff shows. It does not change the stored document.

Problem

In Yjs, the type of a node cannot change. Thus, a type change or a move re-creates the block: it stores a copy of the block (with the same block id) and deletes the original. The version diff showed the original as deleted and the copy as inserted. The diff also credited all the copied content to the user who made the change.

Examples:

  • A user changes a paragraph to a heading. The diff showed a deleted paragraph and an inserted heading, both credited to that user.
  • A user indents a block. The diff showed the block twice: deleted at the old position and inserted at the new position.

Solution

When the diff is rendered, we pair each copy with its original (by block id). Then we show the pair once:

  • Type change between two blocks with inline content: shown as a formatting change ("Formatting change" in the tooltip).
  • Indent or outdent: shown as moved, at the new position only. An indent or outdent does not change the order of the blocks from top to bottom.
  • Other moves: shown as moved at the new position, and struck through at the old position. Both show "Moved by: X".
  • Copy in the same position (for example, because its children changed): shown as unchanged.

Copied content keeps its original authors, character by character. This also applies when you compare with the first version: the copied text is credited to the users who inserted the original text.

A block can be re-created more than once between two versions: for example, retyped and then indented, or moved twice. The copies then form a chain. Each copy is deleted by the update that inserts the next copy. We follow the chain back by the users and times of these updates. Thus:

  • Text that a user typed into an intermediate copy keeps that user as its author.
  • Content that was added to the original after the earlier version is credited to the user who added it. It does not show as unchanged.
  • The final copy is paired with the block of the earlier version, not with an intermediate copy. Before, the diff could credit the final copy to the user who made the intermediate copy. When the earlier version has no such block and there are several candidates, we do not pair them.

We do not pair a copy with its original when the copy lost content. For example, a concurrent edit deleted text from the original. In this case, the diff shows a delete and an insert, as before.

A type change between a text block and a block without inline content (for example, paragraph to image) is not paired.

Display:

  • The text of a moved block has no insert mark, because it keeps its authors. Thus, Block.css gives the text element the highlight of inserted text. In a code block, the highlight uses the dark variant of inserted code. A moved block without inline content (image, file, table, divider) gets the card of a formatting change.
  • The delete mark has a moved attribute, as the insert mark does. The tooltip shows "Moved" for both.

Not fixed

  • Two concurrent copies of the same block (for example, "Cascading indents" or "Both nest a new block under N0" in the gallery) still show as two inserted copies. feat(versioning): diff nesting changes in place #3168 fixed this when editing, but it needed an experimental Yjs API and changed the stored document. Thus, we closed it.
  • "Delete a nested block": the parent loses its child group, and the copy loses content. Thus, it still shows as deleted and inserted.
  • Tables are not in scope.

Testing

The tests are in #3090. On #3090, 12 markers say "To be fixed by #3172" and the tests are marked it.fails. This PR changes them to normal tests:

  • versionDiffAttribution.test.ts:
    • type changes, a block moved among its siblings (at both places), comparisons with the first version, later edits, a rewritten text;
    • "strikes a moved block's children through with it at its old place";
    • "shows a block indented, then outdented by the same user / a different user, as unchanged";
    • "credits two type changes to the last one";
    • "credits every word to its writer, between any two versions": a document that 4 users change in 7 versions (text edits, type changes, an indent, an outdent, a move, a nested block, a delete). For each of the 21 pairs of versions, the diff must credit each word to the user who wrote it.
  • nestingChanges.test.ts: an indent and an unindent show as one moved block.

This PR removes the #3166 test "attributes a move to the mover". With this fix, an indent shows as moved and not as deleted.

  • versionDiffFlags.test.ts: each value of the option, for 4 scenarios. Each value stores the same updates, and showing a diff never writes to the document (no update, and the same bytes before and after).
  • Versioning snapshot (versioning.test.tsx): without the option, all scenarios are the same as on feat!: rebuild version history and customize snapshot actions #3090. With the option, 15 scenarios change. Each of these scenarios has a note for the option in the gallery.
  • Gallery: one more choice for the option ("+ re-created blocks"). The notes of the scenarios with type changes and moves describe the diff without and with the fix.

@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 9:57am UTC
blocknote-website Ready Ready Preview Oct 9, 2026 9:57am 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@3172

@blocknote/code-block

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

@blocknote/core

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

@blocknote/diagram-block

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

@blocknote/mantine

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

@blocknote/math-block

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

@blocknote/react

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

@blocknote/server-util

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

@blocknote/shadcn

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

@blocknote/xl-ai

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

@blocknote/xl-docx-exporter

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

@blocknote/xl-email-exporter

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

@blocknote/xl-multi-column

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

@blocknote/xl-odt-exporter

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

@blocknote/xl-pdf-exporter

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

@blocknote/xl-typst-exporter

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

commit: 7fa4b13

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-09 11:54 UTC

@YousefED
YousefED force-pushed the feat/version-diff-block-copies branch from fe4831e to bb192fe Compare October 8, 2026 20:38
YousefED added a commit to YousefED/docs-1 that referenced this pull request Oct 8, 2026
Use the pkg.pr.new build of TypeCellOS/BlockNote#3172 at bb192fe8fc. This build replaces the two experimental version diff flags with one versionDiffFixes option.

Signed-off-by: yousefed <yousefdardiry@gmail.com>
…tal)

Behind `experimental.blockCopyDiffs` in the collaboration options; it only
changes what a version diff shows. A type change or a move stores a copy
of the block, so the diff showed it as a deleted and an inserted block,
with the copied content credited to whoever made the change. Pair each
copy with its original (by block id) and show it once:

- a type change between text blocks as a formatting change;
- a move, including an indent, as "Moved";
- a copy in the same place (its children changed) as unchanged.

Copied content keeps its authors. Content the earlier version had that no
copy kept still shows as deleted.
…ier version

A block copied twice between two versions (indented, then outdented;
retyped twice) leaves an intermediate copy that is deleted too. The pairing
could pick that copy as the original, and credit the final copy to whoever
made the intermediate one. It now pairs with the block of the earlier
version; without one, several candidates aren't paired.

Also from the stack review: the version diff never writes to the document
(checked for each value of the option), the moved-text highlight shares the
inserted-text rules, and Remove a column has its own note.
A block re-created more than once between two versions (retyped, then
indented; moved twice) is a chain of copies, each deleted by the update
that inserts the next. Follow the chain back by those updates' users and
times, so text typed into an intermediate copy keeps its writer. Content
added to the original after the earlier version is credited to whoever
added it, not shown as unchanged. Also: the pairing no longer recomputes
reading order or scans all removed items for each copy.
@YousefED
YousefED force-pushed the feat/version-diff-block-copies branch from bb192fe to 7fa4b13 Compare October 9, 2026 09:54
@YousefED

YousefED commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Closed: merged into #3166, which now has both experimental version diff fixes (implicit delete attribution and re-created blocks) behind one option.

@YousefED YousefED closed this Oct 9, 2026
YousefED added a commit that referenced this pull request Oct 9, 2026
#3172 is merged into #3166, which now has both experimental fixes.

This branch was successfully deployed

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