@blocknote/core
Version:
A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.
84 lines (75 loc) • 3.02 kB
text/typescript
import * as Y from "yjs";
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
import type { PreviewController } from "../../extensions/Versioning/index.js";
import type { CollaborationOptions } from "./index.js";
import { ForkYDocExtension } from "./ForkYDoc.js";
/**
* Creates a Yjs v13 adapter that provides the {@link PreviewController}
* and `getCurrentDocument` callback required by the base
* {@link VersioningExtension}.
*
* Delegates to the {@link ForkYDocExtension} for entering/exiting preview:
* - **enterPreview**: calls `fork({ initialUpdate: snapshotContent })` to
* switch the editor to a temporary doc built from the snapshot.
* - **exitPreview**: calls `merge({ keepChanges: false })` to discard the
* preview and restore the live document.
* - **applyRestore**: calls `merge({ keepChanges: true })` to apply the
* snapshot content back to the live document.
*/
export function createYjsVersioningAdapter(
/** The BlockNote editor instance (must have ForkYDocExtension). */
editor: BlockNoteEditor<any, any, any>,
/** The full collaboration options (used for `fragment` access). */
options: CollaborationOptions,
): {
preview: PreviewController<Uint8Array>;
getCurrentDocument: () => Y.XmlFragment;
serializeCurrentContent: () => Uint8Array;
} {
const { fragment } = options;
function getForkYDoc() {
const ext = editor.getExtension(ForkYDocExtension);
if (!ext) {
throw new Error(
"ForkYDocExtension is required for the Yjs versioning adapter. " +
"Make sure it is registered before the VersioningExtension.",
);
}
return ext;
}
return {
getCurrentDocument: () => fragment,
serializeCurrentContent: () => Y.encodeStateAsUpdateV2(fragment.doc!),
preview: {
// Yjs v13 can only fork the document to a single snapshot; it has no way
// to diff two versions, so comparison is unsupported.
supportsComparison: false,
enterPreview(
snapshotContent: Uint8Array,
_compareToContent?: Uint8Array,
) {
const forkYDoc = getForkYDoc();
// If already in a preview (forked state), exit first.
if (forkYDoc.store.state.isForked) {
forkYDoc.merge({ keepChanges: false });
}
forkYDoc.fork({ initialUpdate: snapshotContent });
},
exitPreview() {
const forkYDoc = getForkYDoc();
if (forkYDoc.store.state.isForked) {
forkYDoc.merge({ keepChanges: false });
}
},
applyRestore(_snapshotContent: Uint8Array) {
// Restoring to an older Yjs state cannot be done by merging a fork
// because the original doc already contains all CRDT state vectors
// from the snapshot. Restore must be handled at the endpoint/server
// level (e.g., the server creates a new Y.Doc and syncs it).
throw new Error(
"Restore is not yet implemented for Yjs v13 versioning adapter.",
);
},
},
};
}