Skip to content
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
b7d78ae
feat(core): add composable read-only restrictions
nperez0111 Sep 15, 2026
72a1702
feat(core)!: refine attribution colors and deleted-block styling
nperez0111 Sep 15, 2026
621902e
refactor(core)!: unify versioning state and preview lifecycle
nperez0111 Sep 15, 2026
11b4d59
feat(react)!: rebuild the version history sidebar
nperez0111 Sep 15, 2026
9be8d14
chore(examples): refresh versioning demos and setup
nperez0111 Sep 15, 2026
f2857ad
Improve versioning sidebar menu customization
nperez0111 Sep 15, 2026
f8ecfc9
Polish suggestion gallery styling and expand versioning coverage
nperez0111 Sep 15, 2026
194f69f
Fix build and snapshot runner dependency conflicts
nperez0111 Sep 15, 2026
48bec82
Fix version history seeding retries and preview state
nperez0111 Sep 15, 2026
a82f062
refactor: unify editability in ReadOnly extension
nperez0111 Sep 15, 2026
9cb2b5b
fix: animate versioning highlights without DOM mutations
nperez0111 Sep 16, 2026
b60ac63
Upgrade Yjs and y-prosemirror
nperez0111 Sep 16, 2026
2f9b255
Add attribution for block property changes
nperez0111 Sep 16, 2026
e4a5779
Improve version history keyboard accessibility and remove Save action
nperez0111 Sep 16, 2026
459b4db
Refactor YHub versioning and add safe history refresh
nperez0111 Sep 16, 2026
10062b6
Fix YHub attribution bounds for current version previews
nperez0111 Sep 16, 2026
449b9c5
Require synchronous version previews and simplify adapter contracts
nperez0111 Sep 16, 2026
e754cb6
Keep version selection visible when deletion clears a name
nperez0111 Sep 16, 2026
72fd1e5
Share versioning UI primitives and simplify sidebar state
nperez0111 Sep 16, 2026
c7fe526
Deduplicate versioning example history fixtures
nperez0111 Sep 16, 2026
afec519
refactor(react): move versioning to a dedicated sub-export
nperez0111 Sep 16, 2026
c9bc61b
fix: use read-only
nperez0111 Sep 16, 2026
6105796
chore: yjs versions
nperez0111 Sep 16, 2026
e537940
refactor: simplify
nperez0111 Sep 16, 2026
4dd8c83
refactor: inline version history sidebar, default editable true, use …
nperez0111 Sep 16, 2026
6939146
refactor: simplify scrollToFirstChange
nperez0111 Sep 16, 2026
26312d6
refactor: i18n versionLabel via blocknote dictionary
nperez0111 Sep 16, 2026
60fbd84
fix(core): report ReadOnly state from isEditable while unmounted
nperez0111 Sep 16, 2026
c492792
refactor(react): fixed header over scrolling version list, drop focus…
nperez0111 Sep 16, 2026
2f583c7
fix(react): return to Current when deleted version leaves the list
nperez0111 Sep 16, 2026
4a26842
chore: rm
nperez0111 Sep 16, 2026
8aa535a
fix(react): dedupe generic formatting-change tooltip label
nperez0111 Sep 16, 2026
ad01251
docs(gallery): mark textcolor-vs-bgcolor prop attribution as resolved
nperez0111 Sep 16, 2026
b93aa89
fix(core): stable empty-doc binding in new y/ collab stack
nperez0111 Sep 16, 2026
ba4e246
refactor(core): split y snapshot preview into codec + render modules
nperez0111 Sep 17, 2026
f96a8a6
refactor(core): drop yhub restore refresh delay, guard empty doc
nperez0111 Sep 17, 2026
1ce3379
refactor(core): rework Versioning list/preview sessions, add sub-modu…
nperez0111 Sep 17, 2026
44ffe1c
fix(core): flatten legacy anonymous text nodes when diffing snapshots
nperez0111 Sep 17, 2026
15ec810
test(e2e): refresh snapshots for pristine empty docs and trailing block
nperez0111 Sep 17, 2026
3c5ee8a
refactor(core): render version diffs without clearing the document
nperez0111 Sep 17, 2026
b29be57
refactor(versioning): derive loading state instead of storing it
nperez0111 Sep 17, 2026
ae21c0b
refactor(core): render version diffs via patched @y/prosemirror fragm…
nperez0111 Sep 17, 2026
bffcb7a
refactor(core): gate empty-doc sync via initialContentCompare instead…
nperez0111 Sep 18, 2026
32463db
fix(core): re-anchor initial-content gate when the empty skeleton is …
nperez0111 Sep 18, 2026
a38ba03
fix(tests): isPristineEmptyEditor ignores skeleton block ids
nperez0111 Sep 18, 2026
e2d5fbc
refactor(versioning): remove unused review leftovers and clarify refs
nperez0111 Sep 21, 2026
784de3a
fix(core): update YHub activity grouping API
nperez0111 Sep 21, 2026
46605aa
feat(versioning): refine sidebar skins and naming
nperez0111 Sep 21, 2026
32197f9
fix(core): reconcile version preview attribution
nperez0111 Sep 22, 2026
43e2f6f
Merge remote-tracking branch 'origin/feat/versioning-sidebar-ux-b' in…
nperez0111 Sep 22, 2026
2170aa7
Merge remote-tracking branch 'origin/main' into feat/versioning-sideb…
nperez0111 Sep 22, 2026
85734d9
fix: stabilize experimental yjs13 versioning
nperez0111 Sep 22, 2026
10082b8
docs(examples): restore unresolved suggestion feedback
nperez0111 Sep 22, 2026
206d4be
fix(examples): wire suggestion multi-editor mode
nperez0111 Sep 22, 2026
fa085bd
test(react): colocate browser UI tests
nperez0111 Sep 22, 2026
155d256
fix(versioning): localize version labels
nperez0111 Sep 22, 2026
616f23a
fix(versioning): keep sidebar menus visible
nperez0111 Sep 23, 2026
60a6a3a
Merge remote-tracking branch 'origin/main' into feat/versioning-sideb…
nperez0111 Sep 23, 2026
7db8d83
refactor(react): simplify attribution tooltip reference
nperez0111 Sep 23, 2026
57020b3
chore: remove y-prosemirror patch
nperez0111 Sep 23, 2026
ca3de6a
test: update concurrent formatting snapshot
nperez0111 Sep 23, 2026
12d669c
fix(react): forward client rects to floating references
nperez0111 Sep 23, 2026
bb83965
fix(collaboration): preserve sync across editor remounts
nperez0111 Sep 24, 2026
3ff2fa3
fix(versioning): keep in-memory checkpoint current until edit
nperez0111 Sep 24, 2026
0d62219
fix(versioning): theme externally portaled sidebar
nperez0111 Sep 24, 2026
33a4f5e
feat(versioning): support Yjs 13 snapshot restore
nperez0111 Sep 24, 2026
fc7fda1
fix(versioning): tolerate attribution fetch failures
nperez0111 Sep 25, 2026
6f08421
fix(core): remove unused loader selector
nperez0111 Sep 25, 2026
0a489b5
chore: upgrade @y/prosemirror and remove patch
nperez0111 Sep 25, 2026
5021fd0
Merge branch 'main' of github.com:TypeCellOS/BlockNote into feat/vers…
nperez0111 Sep 25, 2026
d3b9cf5
test: cover legacy Yjs document binding compatibility
nperez0111 Sep 25, 2026
0682856
chore: upgrade lib0 to 1.0.0-rc.33
nperez0111 Sep 25, 2026
2e8bf07
Merge branch 'add-legacy-yjs-doc-binding-test' into feat/versioning-s…
nperez0111 Sep 25, 2026
ce541df
chore(playground): remove y-prosemirror optimizer exclusion
nperez0111 Sep 25, 2026
07a833a
Remove compare since beginning version action
nperez0111 Sep 28, 2026
225d57c
Normalize version comparison order in sidebar
nperez0111 Sep 28, 2026
be2fcfb
Fix attribution tooltips for source-backed diff previews
nperez0111 Sep 28, 2026
019d1f1
chore: retrigger PR preview package build
nperez0111 Sep 28, 2026
6147acf
Merge main into versioning sidebar branch
nperez0111 Sep 28, 2026
47fe3fb
fix(math): update selected inline content styling (#2983)
matthewlipski Sep 28, 2026
f4d4991
feat: Down arrow in block without content at end of doc creates parag…
matthewlipski Sep 28, 2026
4c2828f
chore: update lib0 version
nperez0111 Sep 28, 2026
e1dc6bb
fix: `insertInlineContent` adding content after cursor (BLO-1356) (#3…
matthewlipski Sep 28, 2026
ce406d8
ci(deps): bump voidzero-dev/setup-vp from 1.20.0 to 1.21.1 (#3141)
dependabot[bot] Sep 29, 2026
d96013e
feat: Update backspace handling in nested blocks (BLO-1326) (#3124)
matthewlipski Sep 30, 2026
adbede8
chore: upgrade Yjs 13 and 14 dependencies
nperez0111 Sep 30, 2026
664aab7
chore(deps): update @y/prosemirror and lib0 prereleases
nperez0111 Oct 2, 2026
d87fd62
feat: use native YHub versions with typed metadata
nperez0111 Oct 2, 2026
cc63dad
fix: isolate version previews from undo history
nperez0111 Oct 2, 2026
e463df3
Merge remote-tracking branch 'origin/main' into feat/versioning-sideb…
nperez0111 Oct 2, 2026
154e925
Match multi-doc history sidebar to the example theme
nperez0111 Oct 2, 2026
326f1f6
fix: clean up REST thread store test editor
nperez0111 Oct 2, 2026
f833032
fix: handle version history fetch failures with typed results
nperez0111 Oct 5, 2026
fb29f1f
fix: use partial document fixture in suggestion gallery
nperez0111 Oct 5, 2026
f175543
chore: update suggestion gallery table issue statuses
nperez0111 Oct 5, 2026
89c1253
refactor(react): render sidebars into app layouts with RenderInPortal…
YousefED Oct 5, 2026
0708896
chore(examples): regenerate example data
YousefED Oct 5, 2026
12a81e6
Fix runtime extension lifecycle and Yjs fork state isolation
nperez0111 Oct 5, 2026
18ffca9
Fix version attribution tooltips and unnamed snapshot labels
nperez0111 Oct 5, 2026
ef4015d
Merge remote-tracking branch 'origin/feat/versioning-sidebar-ux-b' in…
nperez0111 Oct 5, 2026
6863e9a
refactor: simplify previewing of version history
nperez0111 Oct 5, 2026
9eb1b06
refactor(core): expose built-in extensions only through extensions en…
nperez0111 Oct 6, 2026
5c033cb
fix(build): refresh cached declarations and support custom-schema ver…
nperez0111 Oct 6, 2026
98eac12
refactor(versioning): distinguish backends and accept JSON history seeds
nperez0111 Oct 6, 2026
cd9808c
chore: lint
nperez0111 Oct 6, 2026
55c872a
feat(core): expose editor destroy event
nperez0111 Oct 6, 2026
01e7fdb
fix(core): enable undo and redo for Yjs v14 collaboration
nperez0111 Oct 6, 2026
c1e2f51
fix(versioning): format version dates with one locale-aware formatter
YousefED Oct 6, 2026
903b460
fix: keep version snapshot renames visible while saving
nperez0111 Oct 6, 2026
a2d830e
fix(core): retain menu helpers in root exports
nperez0111 Oct 6, 2026
8cfb79a
fix: allow version naming without switching comparison
nperez0111 Oct 6, 2026
58079f7
fix(core): preserve code and audio rendering in version comparisons
nperez0111 Oct 6, 2026
1c1be9f
fix(tests): refresh deleted code block baselines and Docker build
nperez0111 Oct 6, 2026
04fd335
fix(build): isolate package declaration writes
nperez0111 Oct 6, 2026
087ef08
fix(versioning): name only the kind of change in version diff tooltips
YousefED Oct 6, 2026
6514154
fix(versioning): handle provider failures and simplify sidebar actions
nperez0111 Oct 6, 2026
efae099
fix(versioning): use latest YHub checkpoint as current
nperez0111 Oct 6, 2026
045f94b
fix(core): render previews from the newer source in diffs and suggest…
YousefED Oct 6, 2026
ba40c2b
docs(core): flag plain content filtering for suggestion mode
nperez0111 Oct 6, 2026
7567884
Merge pull request #3157 from TypeCellOS/fix/preview-hidden-source-diff
nperez0111 Oct 6, 2026
cd6e9aa
fix(core): use ProseMirror history for Yjs v14 collaboration
nperez0111 Oct 6, 2026
bcd6a21
Merge remote-tracking branch 'origin/main' into feat/versioning-sideb…
nperez0111 Oct 6, 2026
b5c2ed3
fix(build): emit package declarations reliably
nperez0111 Oct 6, 2026
12432ac
fix(react): restore versioning build entry
nperez0111 Oct 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@
"yjs": "^13.6.27",
"zod": "^4.3.5",
"@blocknote/xl-typst-exporter": "workspace:*",
"@blocknote/xl-typst-compiler": "workspace:*"
"@blocknote/xl-typst-compiler": "workspace:*",
"y-prosemirror": "^1.3.7"
},
"devDependencies": {
"@blocknote/code-block": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"react": "^19.2.3",
"react-dom": "^19.2.3",
"@y/protocols": "^1.0.6-rc.1",
"@y/y": "^14.0.0-rc.23",
"@y/prosemirror": "^2.0.0-6",
"@y/y": "^14.0.0-rc.26",
"@y/prosemirror": "^2.0.0-11",
"@y/websocket": "^4.0.0-rc.2"
},
"devDependencies": {
Expand Down
12 changes: 7 additions & 5 deletions examples/07-collaboration/10-suggestion-multi-editor/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ provider2.awareness.setLocalStateField("user", {
color: "#6eeb83",
});

const attrs = new Y.Attributions();
const attrs = Y.createContentMap();
Comment thread
nperez0111 marked this conversation as resolved.

// Batch timestamps: reuse the same timestamp for edits from the same user
// within a 10-second window of inactivity.
Expand Down Expand Up @@ -62,7 +62,7 @@ function getBatchedTimestamp(userName: string): number {
function trackAttributions(
trackedDoc: Y.Doc,
userName: string,
attributions: Y.Attributions,
attributions: Y.ContentMap,
) {
trackedDoc.on(
"update",
Expand Down Expand Up @@ -105,7 +105,9 @@ suggestingProvider.awareness.setLocalStateField("user", {
name: "Charlie",
color: "#ffbc42",
});
const suggestingRenderer = Y.createDiffRenderer(doc, suggestingDoc, { attrs });
const suggestingRenderer = Y.createDiffRenderer(doc, suggestingDoc, {
attributions: attrs,
});
suggestingRenderer.suggestionMode = false;

const suggestionModeDoc = new Y.Doc({ isSuggestionDoc: true });
Expand All @@ -117,7 +119,7 @@ suggestionModeProvider.awareness.setLocalStateField("user", {
color: "#ee6352",
});
const suggestionModeRenderer = Y.createDiffRenderer(doc, suggestionModeDoc, {
attrs,
attributions: attrs,
});
suggestionModeRenderer.suggestionMode = true;

Expand Down Expand Up @@ -155,7 +157,7 @@ function Editor({
userName,
userColor,
}: {
fragment: Y.Type;
fragment: Y.Node;
provider: { awareness?: Awareness };
renderer?: Y.DiffRenderer;
userName: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"dependencies": {
"y-websocket": "^2.1.0",
"yjs": "^13.6.27",
"lib0": "^0.2.99"
"lib0": "^0.2.99",
"y-prosemirror": "^1.3.7"
}
}
2 changes: 1 addition & 1 deletion examples/07-collaboration/11-versioning-yjs13/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This example shows how to use the `VersioningExtension` with collaborative editing using `yjs` (v13). Snapshots are stored in localStorage using Yjs state updates.

**Try it out:** Edit the document, then click the "Version History" button to open the sidebar. From there you can save snapshots, preview older versions, rename them, and restore them.
The sidebar opens on a document with a few versions already in its history, so you can preview them, rename them, and restore them right away. The editor is read-only while the sidebar is open: close it to edit the document, then reopen it with the "History" button and press "Save version" to add a version of your own.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Save version doesn't exist anymore

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

might be good to mark this example as deprecated / unsupported, because it's yjs 13. In general, I think it's worthwhile to think which example should have docs: true, whether naming is clear, or whether we should introduce a "versioning" folder or not

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also, styling for this example is off?


**Relevant Docs:**

Expand Down
3 changes: 2 additions & 1 deletion examples/07-collaboration/11-versioning-yjs13/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"react-dom": "^19.2.3",
"y-websocket": "^2.1.0",
"yjs": "^13.6.27",
"lib0": "^0.2.99"
"lib0": "^0.2.99",
"y-prosemirror": "^1.3.7"
},
"devDependencies": {
"@types/react": "^19.2.3",
Expand Down
65 changes: 45 additions & 20 deletions examples/07-collaboration/11-versioning-yjs13/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,61 @@ import "@blocknote/core/fonts/inter.css";
import { withCollaboration } from "@blocknote/core/yjs";
import { VersioningExtension } from "@blocknote/core/extensions";
import { createYjsVersioningAdapter } from "@blocknote/core/yjs";
import { localStorageEndpoints } from "./localStorageEndpoints";
import {
BlockNoteViewEditor,
useCreateBlockNote,
useExtensionState,
} from "@blocknote/react";
hasStoredVersions,
localStorageEndpoints,
storeVersions,
} from "./localStorageEndpoints";
import { BlockNoteViewEditor, useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { useState } from "react";

import * as Y from "yjs";
import { WebsocketProvider } from "y-websocket";
import { toBase64, fromBase64 } from "lib0/buffer";

import { VersionHistorySidebar } from "./VersionHistorySidebar";
import {
blocksToUpdate,
DAY_MS,
LIVE_DOCUMENT,
SAMPLE_HISTORY,
} from "./sampleVersions";
import "./style.css";

const roomName = "blocknote-versioning-yjs-example";
const FRAGMENT_NAME = "document-store";
// localStorage key for the live ("current version") document. Snapshots are
// persisted separately by `localStorageEndpoints`; this keeps the live doc
// itself across refreshes since the demo has no server-side persistence.
const DOC_STORAGE_KEY = "blocknote-versioning-yjs-current-doc";
const doc = new Y.Doc();
const fragment = doc.getXmlFragment("document-store");
const fragment = doc.getXmlFragment(FRAGMENT_NAME);

// Persist the full document state on every change.
doc.on("update", () => {
localStorage.setItem(DOC_STORAGE_KEY, toBase64(Y.encodeStateAsUpdate(doc)));
});

// Restore the persisted live document before the editor is created, so it
// adopts the stored content instead of starting empty.
const persistedDoc = localStorage.getItem(DOC_STORAGE_KEY);
if (persistedDoc) {
Y.applyUpdate(doc, fromBase64(persistedDoc));
} else if (!hasStoredVersions()) {
// First visit: seed a few named versions so the history has something to
// show, and open on the newest state of the same document.
storeVersions(
SAMPLE_HISTORY.map((version) => ({
name: version.name,
createdAt: Date.now() - version.daysAgo * DAY_MS,
content: blocksToUpdate(version.blocks, FRAGMENT_NAME),
})),
);
Y.applyUpdate(doc, blocksToUpdate(LIVE_DOCUMENT, FRAGMENT_NAME));
}

// Persist the full document state on every change.
doc.on("update", () => {
localStorage.setItem(DOC_STORAGE_KEY, toBase64(Y.encodeStateAsUpdate(doc)));
});

const provider = new WebsocketProvider(
"wss://demos.yjs.dev/ws",
roomName,
Expand Down Expand Up @@ -66,22 +85,28 @@ export default function App() {
}),
);

const { previewedSnapshotId } = useExtensionState(VersioningExtension, {
editor,
});
const [showSidebar, setShowSidebar] = useState(true);

return (
<div className="wrapper">
<BlockNoteView
editor={editor}
editable={previewedSnapshotId === undefined}
renderEditor={false}
>
{/* No `editable` prop: the sidebar makes the editor read-only for as long
as it's open, and restores it on close. */}
<BlockNoteView editor={editor} renderEditor={false}>
<div className="layout">
<div className="editor-panel">
<BlockNoteViewEditor />
{!showSidebar && (
<button
className="show-history-button"
onClick={() => setShowSidebar(true)}
>
History
</button>
)}
</div>
<VersionHistorySidebar />
{showSidebar && (
<VersionHistorySidebar onClose={() => setShowSidebar(false)} />
)}
</div>
</BlockNoteView>
</div>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,33 +1,11 @@
import { VersioningSidebar } from "@blocknote/react";
import { useState } from "react";

import { SettingsSelect } from "./SettingsSelect";

export const VersionHistorySidebar = () => {
const [filter, setFilter] = useState<"named" | "all">("all");
import { VersioningSidebar } from "@blocknote/react/versioning";

export const VersionHistorySidebar = ({ onClose }: { onClose: () => void }) => {
return (
<div className={"sidebar-section"}>
<div className={"settings"}>
<SettingsSelect
label={"Filter"}
items={[
{
text: "All",
icon: null,
onClick: () => setFilter("all"),
isSelected: filter === "all",
},
{
text: "Named",
icon: null,
onClick: () => setFilter("named"),
isSelected: filter === "named",
},
]}
/>
</div>
<VersioningSidebar filter={filter} />
{/* Filtering to named versions is built in — the sidebar's own header
toggle drives it. The header's close button calls `onClose`. */}
<VersioningSidebar onClose={onClose} />
</div>
);
};
Loading
Loading