Forked from fantactuka/createHeadlessCollaborativeEditor.js
Created
December 15, 2022 22:59
-
-
Save jamespantalones/7daecacf2e1365441b01eaead8526f24 to your computer and use it in GitHub Desktop.
Revisions
-
fantactuka created this gist
Nov 8, 2022 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,106 @@ "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, }; }