diff --git a/packages/core/src/editor/BlockNoteEditor.test.ts b/packages/core/src/editor/BlockNoteEditor.test.ts
index 0f73ac2ea3..db62dafb76 100644
--- a/packages/core/src/editor/BlockNoteEditor.test.ts
+++ b/packages/core/src/editor/BlockNoteEditor.test.ts
@@ -5,6 +5,7 @@ import {
} from "../api/getBlockInfoFromPos.js";
import { BlockNoteEditor } from "./BlockNoteEditor.js";
import { BlockNoteExtension } from "./BlockNoteExtension.js";
+import * as Y from "yjs";
/**
* @vitest-environment jsdom
@@ -146,3 +147,67 @@ it("onCreate event", () => {
});
expect(created).toBe(true);
});
+
+it("sets an initial block id when using Y.js", async () => {
+ const doc = new Y.Doc();
+ const fragment = doc.getXmlFragment("doc");
+ let transactionCount = 0;
+ const editor = BlockNoteEditor.create({
+ collaboration: {
+ fragment,
+ user: { name: "Hello", color: "#FFFFFF" },
+ provider: null,
+ },
+ _tiptapOptions: {
+ onTransaction: () => {
+ transactionCount++;
+ },
+ },
+ });
+
+ editor.mount(document.createElement("div"));
+
+ expect(editor.prosemirrorState.doc.toJSON()).toMatchInlineSnapshot(`
+ {
+ "content": [
+ {
+ "content": [
+ {
+ "attrs": {
+ "id": "initialBlockId",
+ },
+ "content": [
+ {
+ "attrs": {
+ "backgroundColor": "default",
+ "textAlignment": "left",
+ "textColor": "default",
+ },
+ "type": "paragraph",
+ },
+ ],
+ "type": "blockContainer",
+ },
+ ],
+ "type": "blockGroup",
+ },
+ ],
+ "type": "doc",
+ }
+ `);
+ expect(transactionCount).toBe(1);
+ // The fragment should not be modified yet, since the editor's content is only the initial content
+ expect(fragment.toJSON()).toMatchInlineSnapshot(`""`);
+
+ editor.replaceBlocks(editor.document, [
+ {
+ type: "paragraph",
+ content: [{ text: "Hello", styles: {}, type: "text" }],
+ },
+ ]);
+ expect(transactionCount).toBe(2);
+ // Only after a real modification is made, will the fragment be updated
+ expect(fragment.toJSON()).toMatchInlineSnapshot(
+ `"Hello"`,
+ );
+});
diff --git a/packages/core/src/editor/BlockNoteEditor.ts b/packages/core/src/editor/BlockNoteEditor.ts
index 9a5451763e..2292cf2fb9 100644
--- a/packages/core/src/editor/BlockNoteEditor.ts
+++ b/packages/core/src/editor/BlockNoteEditor.ts
@@ -917,6 +917,25 @@ export class BlockNoteEditor<
);
}
+ // When y-prosemirror creates an empty document, the `blockContainer` node is created with an `id` of `null`.
+ // This causes the unique id extension to generate a new id for the initial block, which is not what we want
+ // Since it will be randomly generated & cause there to be more updates to the ydoc
+ // This is a hack to make it so that anytime `schema.doc.createAndFill` is called, the initial block id is already set to "initialBlockId"
+ let cache: Node | undefined = undefined;
+ const oldCreateAndFill = this.pmSchema.nodes.doc.createAndFill;
+ this.pmSchema.nodes.doc.createAndFill = (...args: any) => {
+ if (cache) {
+ return cache;
+ }
+ const ret = oldCreateAndFill.apply(this.pmSchema.nodes.doc, args)!;
+
+ // create a copy that we can mutate (otherwise, assigning attrs is not safe and corrupts the pm state)
+ const jsonNode = JSON.parse(JSON.stringify(ret.toJSON()));
+ jsonNode.content[0].content[0].attrs.id = "initialBlockId";
+
+ cache = Node.fromJSON(this.pmSchema, jsonNode);
+ return cache;
+ };
this.pmSchema.cached.blockNoteEditor = this;
// Initialize managers
diff --git a/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json b/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json
index 704076c85a..786e727b7e 100644
--- a/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json
+++ b/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json
@@ -8,7 +8,7 @@
"type": "text",
},
],
- "id": "3",
+ "id": "2",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
@@ -19,7 +19,7 @@
{
"children": [],
"content": [],
- "id": "4",
+ "id": "3",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
diff --git a/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json b/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json
index dd12eb46bd..e7580c5b7b 100644
--- a/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json
+++ b/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json
@@ -8,7 +8,7 @@
"type": "text",
},
],
- "id": "1",
+ "id": "0",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
@@ -19,7 +19,7 @@
{
"children": [],
"content": [],
- "id": "2",
+ "id": "1",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
diff --git a/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html b/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html
index 6442ffd900..8957bbb259 100644
--- a/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html
+++ b/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html
@@ -1 +1 @@
-Hello World
\ No newline at end of file
+Hello World
\ No newline at end of file
diff --git a/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html b/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html
index b9adef68d0..063ddebeac 100644
--- a/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html
+++ b/packages/core/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html
@@ -1 +1 @@
-Hello
\ No newline at end of file
+Hello
\ No newline at end of file