Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jvandenaardweg/ba7f38fe6d0cc39544b814e0b1111d01 to your computer and use it in GitHub Desktop.
Save jvandenaardweg/ba7f38fe6d0cc39544b814e0b1111d01 to your computer and use it in GitHub Desktop.
"use strict";
import type { LexicalEditor, LexicalNode } from "Lexical";
import type { Binding, Provider } from "LexicalYjs";
import {
createBinding,
syncLexicalUpdateToYjs,
syncYjsChangesToLexical,
} from "LexicalYjs";
import { Doc, encodeStateAsUpdate } from "yjs";
export default function headlessConvertYDocStateToLexicalJSON(
nodes: Array<Class<LexicalNode>>,
yDocState: Uint8Array
): SerializedEditorState {
const { binding, editor } = createHeadlessCollaborationEditor(nodes);
applyUpdate(binding.doc, yDocState, { isUpdateRemote: true });
editor.update(() => {}, { discrete: true });
return editor.getEditorState().toJSON();
}
/**
* Creates headless collaboration editor with no-op provider (since it won't
* connect to message distribution infra) and binding. It also sets up
* bi-directional synchronization between yDoc and editor
*/
function createHeadlessCollaborationEditor(nodes: Array<Class<LexicalNode>>): {
editor: LexicalEditor,
provider: Provider,
binding: Binding,
} {
const editor = createEditor({
headless: true,
namespace: "headless",
nodes,
onError: (error) => {
throw error;
},
});
const id = "main";
const doc = new Doc();
const docMap = new Map([[id, doc]]);
const provider = createNoOpProvider();
const binding = createBinding(editor, provider, id, doc, docMap);
registerCollaborationListeners(editor, provider, binding);
return {
binding,
editor,
provider,
};
}
function registerCollaborationListeners(
editor: LexicalEditor,
provider: Provider,
binding: Binding
): void {
editor.registerUpdateListener(
({
dirtyElements,
dirtyLeaves,
editorState,
normalizedNodes,
prevEditorState,
tags,
}) => {
if (tags.has("skip-collab") === false) {
syncLexicalUpdateToYjs(
binding,
provider,
prevEditorState,
editorState,
dirtyElements,
dirtyLeaves,
normalizedNodes,
tags
);
}
}
);
binding.root.getSharedType().observeDeep((events, transaction) => {
if (transaction?.origin !== binding) {
syncYjsChangesToLexical(binding, provider, events);
}
});
}
function createNoOpProvider(): Provider {
const emptyFunction = () => {};
return {
awareness: {
getLocalState: () => null,
getStates: () => new Map(),
off: emptyFunction,
on: emptyFunction,
setLocalState: emptyFunction,
},
connect: emptyFunction,
disconnect: emptyFunction,
off: emptyFunction,
on: emptyFunction,
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment