@liveblocks/node-lexical 2.0.0-alpha4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -16,6 +16,36 @@ declare type LiveblocksDocumentApi = {
16
16
  toJSON: () => SerializedEditorState<SerializedLexicalNode>;
17
17
  toMarkdown: () => string;
18
18
  };
19
+ /**
20
+ *
21
+ * `withLexicalDocument` is the main entry point to access and modify Lexical documents on your backend.
22
+ * This function internally instantiates a Lexical headless editor and allows you to modify and export its values asynchronously
23
+ * with a simplified interface.
24
+ *
25
+ * @param options Specify the roomId, client, and nodes.
26
+ * @param callback The call back function is optionally async and receives the document API as its only argument.
27
+ *
28
+ * @example
29
+ *
30
+ * import { Liveblocks } from "@liveblocks/node";
31
+ * import { withLexicalDocument } from "@liveblocks/node-lexical";
32
+ *
33
+ * const client = new Liveblocks({secret: "sk_your_secret_key"});
34
+ * const text = await withLexicalDocument(
35
+ * { client, roomId: "your-room" },
36
+ * async (doc) => {
37
+ * await doc.update(() => {
38
+ * const root = $getRoot();
39
+ * const paragraphNode = $createParagraphNode();
40
+ * const textNode = $createTextNode("Hello from node");
41
+ * paragraphNode.append(textNode);
42
+ * root.append(paragraphNode);
43
+ * });
44
+ * return doc.getTextContent();
45
+ * }
46
+ * );
47
+ *
48
+ */
19
49
  declare function withLexicalDocument<T>({ roomId, nodes, client }: LiveblocksLexicalOptions, callback: (api: LiveblocksDocumentApi) => Promise<T> | T): Promise<T>;
20
50
 
21
51
  export { type LiveblocksDocumentApi, type LiveblocksLexicalOptions, withLexicalDocument };
package/dist/index.d.ts CHANGED
@@ -16,6 +16,36 @@ declare type LiveblocksDocumentApi = {
16
16
  toJSON: () => SerializedEditorState<SerializedLexicalNode>;
17
17
  toMarkdown: () => string;
18
18
  };
19
+ /**
20
+ *
21
+ * `withLexicalDocument` is the main entry point to access and modify Lexical documents on your backend.
22
+ * This function internally instantiates a Lexical headless editor and allows you to modify and export its values asynchronously
23
+ * with a simplified interface.
24
+ *
25
+ * @param options Specify the roomId, client, and nodes.
26
+ * @param callback The call back function is optionally async and receives the document API as its only argument.
27
+ *
28
+ * @example
29
+ *
30
+ * import { Liveblocks } from "@liveblocks/node";
31
+ * import { withLexicalDocument } from "@liveblocks/node-lexical";
32
+ *
33
+ * const client = new Liveblocks({secret: "sk_your_secret_key"});
34
+ * const text = await withLexicalDocument(
35
+ * { client, roomId: "your-room" },
36
+ * async (doc) => {
37
+ * await doc.update(() => {
38
+ * const root = $getRoot();
39
+ * const paragraphNode = $createParagraphNode();
40
+ * const textNode = $createTextNode("Hello from node");
41
+ * paragraphNode.append(textNode);
42
+ * root.append(paragraphNode);
43
+ * });
44
+ * return doc.getTextContent();
45
+ * }
46
+ * );
47
+ *
48
+ */
19
49
  declare function withLexicalDocument<T>({ roomId, nodes, client }: LiveblocksLexicalOptions, callback: (api: LiveblocksDocumentApi) => Promise<T> | T): Promise<T>;
20
50
 
21
51
  export { type LiveblocksDocumentApi, type LiveblocksLexicalOptions, withLexicalDocument };
package/dist/index.js CHANGED
@@ -166,7 +166,7 @@ var ThreadMarkNode = class _ThreadMarkNode extends _lexical.ElementNode {
166
166
 
167
167
  // src/version.ts
168
168
  var PKG_NAME = "@liveblocks/node-lexical";
169
- var PKG_VERSION = "2.0.0-alpha4";
169
+ var PKG_VERSION = "2.0.0";
170
170
  var PKG_FORMAT = "cjs";
171
171
 
172
172
  // src/index.ts
@@ -190,6 +190,9 @@ async function withLexicalDocument({ roomId, nodes, client }, callback) {
190
190
  editor.update(() => {
191
191
  }, { discrete: true });
192
192
  const val = await callback({
193
+ /**
194
+ * Fetches and resyncs the latest document with Liveblocks
195
+ */
193
196
  refresh: async () => {
194
197
  const latest = new Uint8Array(
195
198
  await client.getYjsDocumentAsBinaryUpdate(roomId)
@@ -198,6 +201,9 @@ async function withLexicalDocument({ roomId, nodes, client }, callback) {
198
201
  editor.update(() => {
199
202
  }, { discrete: true });
200
203
  },
204
+ /**
205
+ * Provide a callback to modify documetns with Lexical's standard api. All calls are discrete.
206
+ */
201
207
  update: async (modifyFn) => {
202
208
  editor.update(() => {
203
209
  }, { discrete: true });
@@ -211,6 +217,9 @@ async function withLexicalDocument({ roomId, nodes, client }, callback) {
211
217
  const diffUpdate = _yjs3.encodeStateAsUpdate.call(void 0, binding.doc, beforeVector);
212
218
  return client.sendYjsBinaryUpdate(roomId, diffUpdate);
213
219
  },
220
+ /**
221
+ * Helper function to easily provide the text content from the root, i.e. `$getRoot().getTextContent()`
222
+ */
214
223
  getTextContent: () => {
215
224
  let content = "";
216
225
  editor.getEditorState().read(() => {
@@ -218,9 +227,15 @@ async function withLexicalDocument({ roomId, nodes, client }, callback) {
218
227
  });
219
228
  return content;
220
229
  },
230
+ /**
231
+ * Helper function to return editorState in JSON form
232
+ */
221
233
  toJSON: () => {
222
234
  return editor.getEditorState().toJSON();
223
235
  },
236
+ /**
237
+ * Helper function to return editor state as Markdown
238
+ */
224
239
  toMarkdown: () => {
225
240
  let markdown = "";
226
241
  editor.getEditorState().read(() => {
@@ -228,9 +243,15 @@ async function withLexicalDocument({ roomId, nodes, client }, callback) {
228
243
  });
229
244
  return markdown;
230
245
  },
246
+ /**
247
+ * Helper function to return the editor's current state
248
+ */
231
249
  getEditorState: () => {
232
250
  return editor.getEditorState();
233
251
  },
252
+ /**
253
+ * Helper function to return the current headless editor instance
254
+ */
234
255
  getLexicalEditor: () => {
235
256
  return editor;
236
257
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/collab.ts","../src/MentionNodeLite.ts","../src/ThreadNodeLite.ts","../src/version.ts"],"names":["createHeadlessEditor","createBinding","Doc","$applyNodeReplacement","$getRoot"],"mappings":"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","sourcesContent":["import { createHeadlessEditor } from \"@lexical/headless\";\nimport { $convertToMarkdownString, TRANSFORMERS } from \"@lexical/markdown\";\nimport { createBinding } from \"@lexical/yjs\";\nimport { detectDupes } from \"@liveblocks/core\";\nimport type { Liveblocks } from \"@liveblocks/node\";\nimport type {\n EditorState,\n Klass,\n LexicalEditor,\n LexicalNode,\n LexicalNodeReplacement,\n SerializedEditorState,\n SerializedLexicalNode,\n} from \"lexical\";\nimport { $getRoot } from \"lexical\";\nimport { applyUpdate, Doc, encodeStateAsUpdate, encodeStateVector } from \"yjs\";\n\nimport { createNoOpProvider, registerCollaborationListeners } from \"./collab\";\nimport { MentionNode } from \"./MentionNodeLite\";\nimport { ThreadMarkNode } from \"./ThreadNodeLite\";\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nconst LIVEBLOCKS_NODES = [ThreadMarkNode, MentionNode];\n\nexport { $createParagraphNode, $createTextNode, $getRoot } from \"lexical\";\n\nexport type LiveblocksLexicalOptions = {\n roomId: string;\n nodes?: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>;\n client: Liveblocks;\n};\n\nexport type LiveblocksDocumentApi = {\n refresh: () => Promise<void>;\n update: (modifyFn: () => void) => Promise<void>;\n getTextContent: () => string;\n getEditorState: () => EditorState;\n getLexicalEditor: () => LexicalEditor;\n toJSON: () => SerializedEditorState<SerializedLexicalNode>;\n toMarkdown: () => string;\n};\n\nexport async function withLexicalDocument<T>(\n { roomId, nodes, client }: LiveblocksLexicalOptions,\n callback: (api: LiveblocksDocumentApi) => Promise<T> | T\n): Promise<T> {\n const update = new Uint8Array(\n await client.getYjsDocumentAsBinaryUpdate(roomId)\n );\n const editor = createHeadlessEditor({\n nodes: [...LIVEBLOCKS_NODES, ...(nodes ?? [])],\n });\n const id = \"root\";\n const doc = new Doc();\n const docMap = new Map([[id, doc]]);\n const provider = createNoOpProvider();\n const binding = createBinding(editor, provider, id, doc, docMap);\n const unsubscribe = registerCollaborationListeners(editor, provider, binding);\n applyUpdate(binding.doc, update);\n editor.update(() => {}, { discrete: true });\n\n const val = await callback({\n refresh: async () => {\n const latest = new Uint8Array(\n await client.getYjsDocumentAsBinaryUpdate(roomId)\n );\n applyUpdate(binding.doc, latest);\n editor.update(() => {}, { discrete: true });\n },\n update: async (modifyFn) => {\n // Flush any pending updates (there really shouldn't be any?), this may be a NOOP\n editor.update(() => {}, { discrete: true });\n const beforeVector = encodeStateVector(binding.doc);\n editor.update(\n () => {\n modifyFn();\n },\n { discrete: true }\n );\n // grab update after diffing\n const diffUpdate = encodeStateAsUpdate(binding.doc, beforeVector);\n return client.sendYjsBinaryUpdate(roomId, diffUpdate);\n },\n getTextContent: () => {\n let content = \"\";\n editor.getEditorState().read(() => {\n content = $getRoot().getTextContent();\n });\n return content;\n },\n toJSON: () => {\n return editor.getEditorState().toJSON();\n },\n toMarkdown: () => {\n let markdown: string = \"\";\n editor.getEditorState().read(() => {\n markdown = $convertToMarkdownString(TRANSFORMERS);\n });\n return markdown;\n },\n getEditorState: () => {\n return editor.getEditorState();\n },\n getLexicalEditor: () => {\n return editor;\n },\n });\n unsubscribe();\n\n return val;\n}\n","import { createHeadlessEditor } from \"@lexical/headless\";\nimport type { Binding, Provider } from \"@lexical/yjs\";\nimport {\n createBinding,\n syncLexicalUpdateToYjs,\n syncYjsChangesToLexical,\n} from \"@lexical/yjs\";\nimport type {\n Klass,\n LexicalEditor,\n LexicalNode,\n LexicalNodeReplacement,\n} from \"lexical\";\nimport type { Transaction, YEvent } from \"yjs\";\nimport { Doc } from \"yjs\";\n\nexport function withHeadlessCollaborationEditor<T>(\n nodes: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>,\n callback: (editor: LexicalEditor, binding: Binding, provider: Provider) => T\n): T {\n const editor = createHeadlessEditor({\n nodes,\n });\n\n const id = \"root\";\n const doc = new Doc();\n const docMap = new Map([[id, doc]]);\n const provider = createNoOpProvider();\n const binding = createBinding(editor, provider, id, doc, docMap);\n\n const unsubscribe = registerCollaborationListeners(editor, provider, binding);\n\n const res = callback(editor, binding, provider);\n\n unsubscribe();\n\n return res;\n}\n\nexport function registerCollaborationListeners(\n editor: LexicalEditor,\n provider: Provider,\n binding: Binding\n): () => void {\n const unsubscribeUpdateListener = editor.registerUpdateListener(\n ({\n dirtyElements,\n dirtyLeaves,\n editorState,\n normalizedNodes,\n prevEditorState,\n tags,\n }) => {\n if (tags.has(\"skip-collab\") === false) {\n syncLexicalUpdateToYjs(\n binding,\n provider,\n prevEditorState,\n editorState,\n dirtyElements,\n dirtyLeaves,\n normalizedNodes,\n tags\n );\n }\n }\n );\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const observer = (events: Array<YEvent<any>>, transaction: Transaction) => {\n if (transaction.origin !== binding) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n syncYjsChangesToLexical(binding, provider, events, false);\n }\n };\n\n binding.root.getSharedType().observeDeep(observer);\n\n return () => {\n unsubscribeUpdateListener();\n binding.root.getSharedType().unobserveDeep(observer);\n };\n}\n\nexport function createNoOpProvider(): Provider {\n const emptyFunction = () => {};\n\n return {\n awareness: {\n getLocalState: () => null,\n getStates: () => new Map(),\n off: emptyFunction,\n on: emptyFunction,\n setLocalState: emptyFunction,\n },\n connect: emptyFunction,\n disconnect: emptyFunction,\n off: emptyFunction,\n on: emptyFunction,\n };\n}\n","import type { NodeKey, SerializedLexicalNode, Spread } from \"lexical\";\nimport { $applyNodeReplacement, DecoratorNode } from \"lexical\";\n\nexport type SerializedMentionNode = Spread<\n {\n value: string;\n },\n SerializedLexicalNode\n>;\n\nexport class MentionNode extends DecoratorNode<null> {\n __id: string;\n\n constructor(value: string, key?: NodeKey) {\n super(key);\n this.__id = value;\n }\n\n static getType(): string {\n return \"lb-mention\";\n }\n\n static clone(node: MentionNode): MentionNode {\n return new MentionNode(node.__id);\n }\n\n static importJSON(serializedNode: SerializedMentionNode): MentionNode {\n const node = new MentionNode(serializedNode.value);\n return $applyNodeReplacement(node);\n }\n\n exportJSON(): SerializedMentionNode {\n return {\n value: this.getTextContent(),\n type: \"lb-mention\",\n version: 1,\n };\n }\n\n getTextContent(): string {\n const self = this.getLatest();\n return self.__id;\n }\n\n decorate(): null {\n return null;\n }\n}\n","import type {\n BaseSelection,\n LexicalNode,\n NodeKey,\n SerializedElementNode,\n Spread,\n} from \"lexical\";\nimport { $applyNodeReplacement, $isRangeSelection, ElementNode } from \"lexical\";\n\nexport type SerializedThreadMarkNode = Spread<\n {\n ids: Array<string>;\n },\n SerializedElementNode\n>;\n\nexport class ThreadMarkNode extends ElementNode {\n /** @internal */\n __ids: Array<string>; // The ids of the threads that this mark is associated with\n\n static getType(): string {\n return \"lb-thread-mark\";\n }\n\n static clone(node: ThreadMarkNode): ThreadMarkNode {\n return new ThreadMarkNode(Array.from(node.__ids), node.__key);\n }\n\n static importJSON(serializedNode: SerializedThreadMarkNode): ThreadMarkNode {\n const node = $applyNodeReplacement<ThreadMarkNode>(\n new ThreadMarkNode(serializedNode.ids)\n );\n node.setFormat(serializedNode.format);\n node.setIndent(serializedNode.indent);\n node.setDirection(serializedNode.direction);\n return node;\n }\n\n exportJSON(): SerializedThreadMarkNode {\n return {\n ...super.exportJSON(),\n ids: this.getIDs(),\n type: \"lb-thread-mark\",\n version: 1,\n };\n }\n\n getIDs(): Array<string> {\n const self = this.getLatest();\n return self instanceof ThreadMarkNode ? self.__ids : [];\n }\n\n constructor(ids: Array<string>, key?: NodeKey) {\n super(key);\n this.__ids = ids || [];\n }\n\n canInsertTextBefore(): false {\n return false;\n }\n\n canInsertTextAfter(): false {\n return false;\n }\n\n canBeEmpty(): false {\n return false;\n }\n\n isInline(): true {\n return true;\n }\n\n extractWithChild(\n _: LexicalNode,\n selection: BaseSelection,\n destination: \"clone\" | \"html\"\n ): boolean {\n if (!$isRangeSelection(selection) || destination === \"html\") {\n return false;\n }\n const anchor = selection.anchor;\n const focus = selection.focus;\n const anchorNode = anchor.getNode();\n const focusNode = focus.getNode();\n const isBackward = selection.isBackward();\n const selectionLength = isBackward\n ? anchor.offset - focus.offset\n : focus.offset - anchor.offset;\n return (\n this.isParentOf(anchorNode) &&\n this.isParentOf(focusNode) &&\n this.getTextContent().length === selectionLength\n );\n }\n\n excludeFromCopy(destination: \"clone\" | \"html\"): boolean {\n return destination !== \"clone\";\n }\n}\n","declare const __VERSION__: string;\ndeclare const TSUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/node-lexical\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof TSUP_FORMAT === \"string\" && TSUP_FORMAT;\n"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/collab.ts","../src/MentionNodeLite.ts","../src/ThreadNodeLite.ts","../src/version.ts"],"names":["createHeadlessEditor","createBinding","Doc","$applyNodeReplacement","$getRoot"],"mappings":"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","sourcesContent":["import { createHeadlessEditor } from \"@lexical/headless\";\nimport { $convertToMarkdownString, TRANSFORMERS } from \"@lexical/markdown\";\nimport { createBinding } from \"@lexical/yjs\";\nimport { detectDupes } from \"@liveblocks/core\";\nimport type { Liveblocks } from \"@liveblocks/node\";\nimport type {\n EditorState,\n Klass,\n LexicalEditor,\n LexicalNode,\n LexicalNodeReplacement,\n SerializedEditorState,\n SerializedLexicalNode,\n} from \"lexical\";\nimport { $getRoot } from \"lexical\";\nimport { applyUpdate, Doc, encodeStateAsUpdate, encodeStateVector } from \"yjs\";\n\nimport { createNoOpProvider, registerCollaborationListeners } from \"./collab\";\nimport { MentionNode } from \"./MentionNodeLite\";\nimport { ThreadMarkNode } from \"./ThreadNodeLite\";\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nconst LIVEBLOCKS_NODES = [ThreadMarkNode, MentionNode];\n\nexport { $createParagraphNode, $createTextNode, $getRoot } from \"lexical\";\n\nexport type LiveblocksLexicalOptions = {\n roomId: string;\n nodes?: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>;\n client: Liveblocks;\n};\n\nexport type LiveblocksDocumentApi = {\n refresh: () => Promise<void>;\n update: (modifyFn: () => void) => Promise<void>;\n getTextContent: () => string;\n getEditorState: () => EditorState;\n getLexicalEditor: () => LexicalEditor;\n toJSON: () => SerializedEditorState<SerializedLexicalNode>;\n toMarkdown: () => string;\n};\n\n/**\n *\n * `withLexicalDocument` is the main entry point to access and modify Lexical documents on your backend.\n * This function internally instantiates a Lexical headless editor and allows you to modify and export its values asynchronously\n * with a simplified interface.\n *\n * @param options Specify the roomId, client, and nodes.\n * @param callback The call back function is optionally async and receives the document API as its only argument.\n *\n * @example\n *\n * import { Liveblocks } from \"@liveblocks/node\";\n * import { withLexicalDocument } from \"@liveblocks/node-lexical\";\n *\n * const client = new Liveblocks({secret: \"sk_your_secret_key\"});\n * const text = await withLexicalDocument(\n * { client, roomId: \"your-room\" },\n * async (doc) => {\n * await doc.update(() => {\n * const root = $getRoot();\n * const paragraphNode = $createParagraphNode();\n * const textNode = $createTextNode(\"Hello from node\");\n * paragraphNode.append(textNode);\n * root.append(paragraphNode);\n * });\n * return doc.getTextContent();\n * }\n * );\n *\n */\nexport async function withLexicalDocument<T>(\n { roomId, nodes, client }: LiveblocksLexicalOptions,\n callback: (api: LiveblocksDocumentApi) => Promise<T> | T\n): Promise<T> {\n const update = new Uint8Array(\n await client.getYjsDocumentAsBinaryUpdate(roomId)\n );\n const editor = createHeadlessEditor({\n nodes: [...LIVEBLOCKS_NODES, ...(nodes ?? [])],\n });\n const id = \"root\";\n const doc = new Doc();\n const docMap = new Map([[id, doc]]);\n const provider = createNoOpProvider();\n const binding = createBinding(editor, provider, id, doc, docMap);\n const unsubscribe = registerCollaborationListeners(editor, provider, binding);\n applyUpdate(binding.doc, update);\n editor.update(() => {}, { discrete: true });\n\n const val = await callback({\n /**\n * Fetches and resyncs the latest document with Liveblocks\n */\n refresh: async () => {\n const latest = new Uint8Array(\n await client.getYjsDocumentAsBinaryUpdate(roomId)\n );\n applyUpdate(binding.doc, latest);\n editor.update(() => {}, { discrete: true });\n },\n /**\n * Provide a callback to modify documetns with Lexical's standard api. All calls are discrete.\n */\n update: async (modifyFn) => {\n // Flush any pending updates (there really shouldn't be any?), this may be a NOOP\n editor.update(() => {}, { discrete: true });\n const beforeVector = encodeStateVector(binding.doc);\n editor.update(\n () => {\n modifyFn();\n },\n { discrete: true }\n );\n // grab update after diffing\n const diffUpdate = encodeStateAsUpdate(binding.doc, beforeVector);\n return client.sendYjsBinaryUpdate(roomId, diffUpdate);\n },\n /**\n * Helper function to easily provide the text content from the root, i.e. `$getRoot().getTextContent()`\n */\n getTextContent: () => {\n let content = \"\";\n editor.getEditorState().read(() => {\n content = $getRoot().getTextContent();\n });\n return content;\n },\n /**\n * Helper function to return editorState in JSON form\n */\n toJSON: () => {\n return editor.getEditorState().toJSON();\n },\n /**\n * Helper function to return editor state as Markdown\n */\n toMarkdown: () => {\n let markdown: string = \"\";\n editor.getEditorState().read(() => {\n markdown = $convertToMarkdownString(TRANSFORMERS);\n });\n return markdown;\n },\n /**\n * Helper function to return the editor's current state\n */\n getEditorState: () => {\n return editor.getEditorState();\n },\n /**\n * Helper function to return the current headless editor instance\n */\n getLexicalEditor: () => {\n return editor;\n },\n });\n unsubscribe();\n\n return val;\n}\n","import { createHeadlessEditor } from \"@lexical/headless\";\nimport type { Binding, Provider } from \"@lexical/yjs\";\nimport {\n createBinding,\n syncLexicalUpdateToYjs,\n syncYjsChangesToLexical,\n} from \"@lexical/yjs\";\nimport type {\n Klass,\n LexicalEditor,\n LexicalNode,\n LexicalNodeReplacement,\n} from \"lexical\";\nimport type { Transaction, YEvent } from \"yjs\";\nimport { Doc } from \"yjs\";\n\nexport function withHeadlessCollaborationEditor<T>(\n nodes: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>,\n callback: (editor: LexicalEditor, binding: Binding, provider: Provider) => T\n): T {\n const editor = createHeadlessEditor({\n nodes,\n });\n\n const id = \"root\";\n const doc = new Doc();\n const docMap = new Map([[id, doc]]);\n const provider = createNoOpProvider();\n const binding = createBinding(editor, provider, id, doc, docMap);\n\n const unsubscribe = registerCollaborationListeners(editor, provider, binding);\n\n const res = callback(editor, binding, provider);\n\n unsubscribe();\n\n return res;\n}\n\nexport function registerCollaborationListeners(\n editor: LexicalEditor,\n provider: Provider,\n binding: Binding\n): () => void {\n const unsubscribeUpdateListener = editor.registerUpdateListener(\n ({\n dirtyElements,\n dirtyLeaves,\n editorState,\n normalizedNodes,\n prevEditorState,\n tags,\n }) => {\n if (tags.has(\"skip-collab\") === false) {\n syncLexicalUpdateToYjs(\n binding,\n provider,\n prevEditorState,\n editorState,\n dirtyElements,\n dirtyLeaves,\n normalizedNodes,\n tags\n );\n }\n }\n );\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const observer = (events: Array<YEvent<any>>, transaction: Transaction) => {\n if (transaction.origin !== binding) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n syncYjsChangesToLexical(binding, provider, events, false);\n }\n };\n\n binding.root.getSharedType().observeDeep(observer);\n\n return () => {\n unsubscribeUpdateListener();\n binding.root.getSharedType().unobserveDeep(observer);\n };\n}\n\nexport function createNoOpProvider(): Provider {\n const emptyFunction = () => {};\n\n return {\n awareness: {\n getLocalState: () => null,\n getStates: () => new Map(),\n off: emptyFunction,\n on: emptyFunction,\n setLocalState: emptyFunction,\n },\n connect: emptyFunction,\n disconnect: emptyFunction,\n off: emptyFunction,\n on: emptyFunction,\n };\n}\n","import type { NodeKey, SerializedLexicalNode, Spread } from \"lexical\";\nimport { $applyNodeReplacement, DecoratorNode } from \"lexical\";\n\nexport type SerializedMentionNode = Spread<\n {\n value: string;\n },\n SerializedLexicalNode\n>;\n\nexport class MentionNode extends DecoratorNode<null> {\n __id: string;\n\n constructor(value: string, key?: NodeKey) {\n super(key);\n this.__id = value;\n }\n\n static getType(): string {\n return \"lb-mention\";\n }\n\n static clone(node: MentionNode): MentionNode {\n return new MentionNode(node.__id);\n }\n\n static importJSON(serializedNode: SerializedMentionNode): MentionNode {\n const node = new MentionNode(serializedNode.value);\n return $applyNodeReplacement(node);\n }\n\n exportJSON(): SerializedMentionNode {\n return {\n value: this.getTextContent(),\n type: \"lb-mention\",\n version: 1,\n };\n }\n\n getTextContent(): string {\n const self = this.getLatest();\n return self.__id;\n }\n\n decorate(): null {\n return null;\n }\n}\n","import type {\n BaseSelection,\n LexicalNode,\n NodeKey,\n SerializedElementNode,\n Spread,\n} from \"lexical\";\nimport { $applyNodeReplacement, $isRangeSelection, ElementNode } from \"lexical\";\n\nexport type SerializedThreadMarkNode = Spread<\n {\n ids: Array<string>;\n },\n SerializedElementNode\n>;\n\nexport class ThreadMarkNode extends ElementNode {\n /** @internal */\n __ids: Array<string>; // The ids of the threads that this mark is associated with\n\n static getType(): string {\n return \"lb-thread-mark\";\n }\n\n static clone(node: ThreadMarkNode): ThreadMarkNode {\n return new ThreadMarkNode(Array.from(node.__ids), node.__key);\n }\n\n static importJSON(serializedNode: SerializedThreadMarkNode): ThreadMarkNode {\n const node = $applyNodeReplacement<ThreadMarkNode>(\n new ThreadMarkNode(serializedNode.ids)\n );\n node.setFormat(serializedNode.format);\n node.setIndent(serializedNode.indent);\n node.setDirection(serializedNode.direction);\n return node;\n }\n\n exportJSON(): SerializedThreadMarkNode {\n return {\n ...super.exportJSON(),\n ids: this.getIDs(),\n type: \"lb-thread-mark\",\n version: 1,\n };\n }\n\n getIDs(): Array<string> {\n const self = this.getLatest();\n return self instanceof ThreadMarkNode ? self.__ids : [];\n }\n\n constructor(ids: Array<string>, key?: NodeKey) {\n super(key);\n this.__ids = ids || [];\n }\n\n canInsertTextBefore(): false {\n return false;\n }\n\n canInsertTextAfter(): false {\n return false;\n }\n\n canBeEmpty(): false {\n return false;\n }\n\n isInline(): true {\n return true;\n }\n\n extractWithChild(\n _: LexicalNode,\n selection: BaseSelection,\n destination: \"clone\" | \"html\"\n ): boolean {\n if (!$isRangeSelection(selection) || destination === \"html\") {\n return false;\n }\n const anchor = selection.anchor;\n const focus = selection.focus;\n const anchorNode = anchor.getNode();\n const focusNode = focus.getNode();\n const isBackward = selection.isBackward();\n const selectionLength = isBackward\n ? anchor.offset - focus.offset\n : focus.offset - anchor.offset;\n return (\n this.isParentOf(anchorNode) &&\n this.isParentOf(focusNode) &&\n this.getTextContent().length === selectionLength\n );\n }\n\n excludeFromCopy(destination: \"clone\" | \"html\"): boolean {\n return destination !== \"clone\";\n }\n}\n","declare const __VERSION__: string;\ndeclare const TSUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/node-lexical\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof TSUP_FORMAT === \"string\" && TSUP_FORMAT;\n"]}
package/dist/index.mjs CHANGED
@@ -166,7 +166,7 @@ var ThreadMarkNode = class _ThreadMarkNode extends ElementNode {
166
166
 
167
167
  // src/version.ts
168
168
  var PKG_NAME = "@liveblocks/node-lexical";
169
- var PKG_VERSION = "2.0.0-alpha4";
169
+ var PKG_VERSION = "2.0.0";
170
170
  var PKG_FORMAT = "esm";
171
171
 
172
172
  // src/index.ts
@@ -190,6 +190,9 @@ async function withLexicalDocument({ roomId, nodes, client }, callback) {
190
190
  editor.update(() => {
191
191
  }, { discrete: true });
192
192
  const val = await callback({
193
+ /**
194
+ * Fetches and resyncs the latest document with Liveblocks
195
+ */
193
196
  refresh: async () => {
194
197
  const latest = new Uint8Array(
195
198
  await client.getYjsDocumentAsBinaryUpdate(roomId)
@@ -198,6 +201,9 @@ async function withLexicalDocument({ roomId, nodes, client }, callback) {
198
201
  editor.update(() => {
199
202
  }, { discrete: true });
200
203
  },
204
+ /**
205
+ * Provide a callback to modify documetns with Lexical's standard api. All calls are discrete.
206
+ */
201
207
  update: async (modifyFn) => {
202
208
  editor.update(() => {
203
209
  }, { discrete: true });
@@ -211,6 +217,9 @@ async function withLexicalDocument({ roomId, nodes, client }, callback) {
211
217
  const diffUpdate = encodeStateAsUpdate(binding.doc, beforeVector);
212
218
  return client.sendYjsBinaryUpdate(roomId, diffUpdate);
213
219
  },
220
+ /**
221
+ * Helper function to easily provide the text content from the root, i.e. `$getRoot().getTextContent()`
222
+ */
214
223
  getTextContent: () => {
215
224
  let content = "";
216
225
  editor.getEditorState().read(() => {
@@ -218,9 +227,15 @@ async function withLexicalDocument({ roomId, nodes, client }, callback) {
218
227
  });
219
228
  return content;
220
229
  },
230
+ /**
231
+ * Helper function to return editorState in JSON form
232
+ */
221
233
  toJSON: () => {
222
234
  return editor.getEditorState().toJSON();
223
235
  },
236
+ /**
237
+ * Helper function to return editor state as Markdown
238
+ */
224
239
  toMarkdown: () => {
225
240
  let markdown = "";
226
241
  editor.getEditorState().read(() => {
@@ -228,9 +243,15 @@ async function withLexicalDocument({ roomId, nodes, client }, callback) {
228
243
  });
229
244
  return markdown;
230
245
  },
246
+ /**
247
+ * Helper function to return the editor's current state
248
+ */
231
249
  getEditorState: () => {
232
250
  return editor.getEditorState();
233
251
  },
252
+ /**
253
+ * Helper function to return the current headless editor instance
254
+ */
234
255
  getLexicalEditor: () => {
235
256
  return editor;
236
257
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/collab.ts","../src/MentionNodeLite.ts","../src/ThreadNodeLite.ts","../src/version.ts"],"sourcesContent":["import { createHeadlessEditor } from \"@lexical/headless\";\nimport { $convertToMarkdownString, TRANSFORMERS } from \"@lexical/markdown\";\nimport { createBinding } from \"@lexical/yjs\";\nimport { detectDupes } from \"@liveblocks/core\";\nimport type { Liveblocks } from \"@liveblocks/node\";\nimport type {\n EditorState,\n Klass,\n LexicalEditor,\n LexicalNode,\n LexicalNodeReplacement,\n SerializedEditorState,\n SerializedLexicalNode,\n} from \"lexical\";\nimport { $getRoot } from \"lexical\";\nimport { applyUpdate, Doc, encodeStateAsUpdate, encodeStateVector } from \"yjs\";\n\nimport { createNoOpProvider, registerCollaborationListeners } from \"./collab\";\nimport { MentionNode } from \"./MentionNodeLite\";\nimport { ThreadMarkNode } from \"./ThreadNodeLite\";\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nconst LIVEBLOCKS_NODES = [ThreadMarkNode, MentionNode];\n\nexport { $createParagraphNode, $createTextNode, $getRoot } from \"lexical\";\n\nexport type LiveblocksLexicalOptions = {\n roomId: string;\n nodes?: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>;\n client: Liveblocks;\n};\n\nexport type LiveblocksDocumentApi = {\n refresh: () => Promise<void>;\n update: (modifyFn: () => void) => Promise<void>;\n getTextContent: () => string;\n getEditorState: () => EditorState;\n getLexicalEditor: () => LexicalEditor;\n toJSON: () => SerializedEditorState<SerializedLexicalNode>;\n toMarkdown: () => string;\n};\n\nexport async function withLexicalDocument<T>(\n { roomId, nodes, client }: LiveblocksLexicalOptions,\n callback: (api: LiveblocksDocumentApi) => Promise<T> | T\n): Promise<T> {\n const update = new Uint8Array(\n await client.getYjsDocumentAsBinaryUpdate(roomId)\n );\n const editor = createHeadlessEditor({\n nodes: [...LIVEBLOCKS_NODES, ...(nodes ?? [])],\n });\n const id = \"root\";\n const doc = new Doc();\n const docMap = new Map([[id, doc]]);\n const provider = createNoOpProvider();\n const binding = createBinding(editor, provider, id, doc, docMap);\n const unsubscribe = registerCollaborationListeners(editor, provider, binding);\n applyUpdate(binding.doc, update);\n editor.update(() => {}, { discrete: true });\n\n const val = await callback({\n refresh: async () => {\n const latest = new Uint8Array(\n await client.getYjsDocumentAsBinaryUpdate(roomId)\n );\n applyUpdate(binding.doc, latest);\n editor.update(() => {}, { discrete: true });\n },\n update: async (modifyFn) => {\n // Flush any pending updates (there really shouldn't be any?), this may be a NOOP\n editor.update(() => {}, { discrete: true });\n const beforeVector = encodeStateVector(binding.doc);\n editor.update(\n () => {\n modifyFn();\n },\n { discrete: true }\n );\n // grab update after diffing\n const diffUpdate = encodeStateAsUpdate(binding.doc, beforeVector);\n return client.sendYjsBinaryUpdate(roomId, diffUpdate);\n },\n getTextContent: () => {\n let content = \"\";\n editor.getEditorState().read(() => {\n content = $getRoot().getTextContent();\n });\n return content;\n },\n toJSON: () => {\n return editor.getEditorState().toJSON();\n },\n toMarkdown: () => {\n let markdown: string = \"\";\n editor.getEditorState().read(() => {\n markdown = $convertToMarkdownString(TRANSFORMERS);\n });\n return markdown;\n },\n getEditorState: () => {\n return editor.getEditorState();\n },\n getLexicalEditor: () => {\n return editor;\n },\n });\n unsubscribe();\n\n return val;\n}\n","import { createHeadlessEditor } from \"@lexical/headless\";\nimport type { Binding, Provider } from \"@lexical/yjs\";\nimport {\n createBinding,\n syncLexicalUpdateToYjs,\n syncYjsChangesToLexical,\n} from \"@lexical/yjs\";\nimport type {\n Klass,\n LexicalEditor,\n LexicalNode,\n LexicalNodeReplacement,\n} from \"lexical\";\nimport type { Transaction, YEvent } from \"yjs\";\nimport { Doc } from \"yjs\";\n\nexport function withHeadlessCollaborationEditor<T>(\n nodes: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>,\n callback: (editor: LexicalEditor, binding: Binding, provider: Provider) => T\n): T {\n const editor = createHeadlessEditor({\n nodes,\n });\n\n const id = \"root\";\n const doc = new Doc();\n const docMap = new Map([[id, doc]]);\n const provider = createNoOpProvider();\n const binding = createBinding(editor, provider, id, doc, docMap);\n\n const unsubscribe = registerCollaborationListeners(editor, provider, binding);\n\n const res = callback(editor, binding, provider);\n\n unsubscribe();\n\n return res;\n}\n\nexport function registerCollaborationListeners(\n editor: LexicalEditor,\n provider: Provider,\n binding: Binding\n): () => void {\n const unsubscribeUpdateListener = editor.registerUpdateListener(\n ({\n dirtyElements,\n dirtyLeaves,\n editorState,\n normalizedNodes,\n prevEditorState,\n tags,\n }) => {\n if (tags.has(\"skip-collab\") === false) {\n syncLexicalUpdateToYjs(\n binding,\n provider,\n prevEditorState,\n editorState,\n dirtyElements,\n dirtyLeaves,\n normalizedNodes,\n tags\n );\n }\n }\n );\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const observer = (events: Array<YEvent<any>>, transaction: Transaction) => {\n if (transaction.origin !== binding) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n syncYjsChangesToLexical(binding, provider, events, false);\n }\n };\n\n binding.root.getSharedType().observeDeep(observer);\n\n return () => {\n unsubscribeUpdateListener();\n binding.root.getSharedType().unobserveDeep(observer);\n };\n}\n\nexport function createNoOpProvider(): Provider {\n const emptyFunction = () => {};\n\n return {\n awareness: {\n getLocalState: () => null,\n getStates: () => new Map(),\n off: emptyFunction,\n on: emptyFunction,\n setLocalState: emptyFunction,\n },\n connect: emptyFunction,\n disconnect: emptyFunction,\n off: emptyFunction,\n on: emptyFunction,\n };\n}\n","import type { NodeKey, SerializedLexicalNode, Spread } from \"lexical\";\nimport { $applyNodeReplacement, DecoratorNode } from \"lexical\";\n\nexport type SerializedMentionNode = Spread<\n {\n value: string;\n },\n SerializedLexicalNode\n>;\n\nexport class MentionNode extends DecoratorNode<null> {\n __id: string;\n\n constructor(value: string, key?: NodeKey) {\n super(key);\n this.__id = value;\n }\n\n static getType(): string {\n return \"lb-mention\";\n }\n\n static clone(node: MentionNode): MentionNode {\n return new MentionNode(node.__id);\n }\n\n static importJSON(serializedNode: SerializedMentionNode): MentionNode {\n const node = new MentionNode(serializedNode.value);\n return $applyNodeReplacement(node);\n }\n\n exportJSON(): SerializedMentionNode {\n return {\n value: this.getTextContent(),\n type: \"lb-mention\",\n version: 1,\n };\n }\n\n getTextContent(): string {\n const self = this.getLatest();\n return self.__id;\n }\n\n decorate(): null {\n return null;\n }\n}\n","import type {\n BaseSelection,\n LexicalNode,\n NodeKey,\n SerializedElementNode,\n Spread,\n} from \"lexical\";\nimport { $applyNodeReplacement, $isRangeSelection, ElementNode } from \"lexical\";\n\nexport type SerializedThreadMarkNode = Spread<\n {\n ids: Array<string>;\n },\n SerializedElementNode\n>;\n\nexport class ThreadMarkNode extends ElementNode {\n /** @internal */\n __ids: Array<string>; // The ids of the threads that this mark is associated with\n\n static getType(): string {\n return \"lb-thread-mark\";\n }\n\n static clone(node: ThreadMarkNode): ThreadMarkNode {\n return new ThreadMarkNode(Array.from(node.__ids), node.__key);\n }\n\n static importJSON(serializedNode: SerializedThreadMarkNode): ThreadMarkNode {\n const node = $applyNodeReplacement<ThreadMarkNode>(\n new ThreadMarkNode(serializedNode.ids)\n );\n node.setFormat(serializedNode.format);\n node.setIndent(serializedNode.indent);\n node.setDirection(serializedNode.direction);\n return node;\n }\n\n exportJSON(): SerializedThreadMarkNode {\n return {\n ...super.exportJSON(),\n ids: this.getIDs(),\n type: \"lb-thread-mark\",\n version: 1,\n };\n }\n\n getIDs(): Array<string> {\n const self = this.getLatest();\n return self instanceof ThreadMarkNode ? self.__ids : [];\n }\n\n constructor(ids: Array<string>, key?: NodeKey) {\n super(key);\n this.__ids = ids || [];\n }\n\n canInsertTextBefore(): false {\n return false;\n }\n\n canInsertTextAfter(): false {\n return false;\n }\n\n canBeEmpty(): false {\n return false;\n }\n\n isInline(): true {\n return true;\n }\n\n extractWithChild(\n _: LexicalNode,\n selection: BaseSelection,\n destination: \"clone\" | \"html\"\n ): boolean {\n if (!$isRangeSelection(selection) || destination === \"html\") {\n return false;\n }\n const anchor = selection.anchor;\n const focus = selection.focus;\n const anchorNode = anchor.getNode();\n const focusNode = focus.getNode();\n const isBackward = selection.isBackward();\n const selectionLength = isBackward\n ? anchor.offset - focus.offset\n : focus.offset - anchor.offset;\n return (\n this.isParentOf(anchorNode) &&\n this.isParentOf(focusNode) &&\n this.getTextContent().length === selectionLength\n );\n }\n\n excludeFromCopy(destination: \"clone\" | \"html\"): boolean {\n return destination !== \"clone\";\n }\n}\n","declare const __VERSION__: string;\ndeclare const TSUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/node-lexical\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof TSUP_FORMAT === \"string\" && TSUP_FORMAT;\n"],"mappings":"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","names":["createHeadlessEditor","createBinding","Doc","$applyNodeReplacement","$getRoot","createHeadlessEditor","Doc","createBinding"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/collab.ts","../src/MentionNodeLite.ts","../src/ThreadNodeLite.ts","../src/version.ts"],"sourcesContent":["import { createHeadlessEditor } from \"@lexical/headless\";\nimport { $convertToMarkdownString, TRANSFORMERS } from \"@lexical/markdown\";\nimport { createBinding } from \"@lexical/yjs\";\nimport { detectDupes } from \"@liveblocks/core\";\nimport type { Liveblocks } from \"@liveblocks/node\";\nimport type {\n EditorState,\n Klass,\n LexicalEditor,\n LexicalNode,\n LexicalNodeReplacement,\n SerializedEditorState,\n SerializedLexicalNode,\n} from \"lexical\";\nimport { $getRoot } from \"lexical\";\nimport { applyUpdate, Doc, encodeStateAsUpdate, encodeStateVector } from \"yjs\";\n\nimport { createNoOpProvider, registerCollaborationListeners } from \"./collab\";\nimport { MentionNode } from \"./MentionNodeLite\";\nimport { ThreadMarkNode } from \"./ThreadNodeLite\";\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nconst LIVEBLOCKS_NODES = [ThreadMarkNode, MentionNode];\n\nexport { $createParagraphNode, $createTextNode, $getRoot } from \"lexical\";\n\nexport type LiveblocksLexicalOptions = {\n roomId: string;\n nodes?: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>;\n client: Liveblocks;\n};\n\nexport type LiveblocksDocumentApi = {\n refresh: () => Promise<void>;\n update: (modifyFn: () => void) => Promise<void>;\n getTextContent: () => string;\n getEditorState: () => EditorState;\n getLexicalEditor: () => LexicalEditor;\n toJSON: () => SerializedEditorState<SerializedLexicalNode>;\n toMarkdown: () => string;\n};\n\n/**\n *\n * `withLexicalDocument` is the main entry point to access and modify Lexical documents on your backend.\n * This function internally instantiates a Lexical headless editor and allows you to modify and export its values asynchronously\n * with a simplified interface.\n *\n * @param options Specify the roomId, client, and nodes.\n * @param callback The call back function is optionally async and receives the document API as its only argument.\n *\n * @example\n *\n * import { Liveblocks } from \"@liveblocks/node\";\n * import { withLexicalDocument } from \"@liveblocks/node-lexical\";\n *\n * const client = new Liveblocks({secret: \"sk_your_secret_key\"});\n * const text = await withLexicalDocument(\n * { client, roomId: \"your-room\" },\n * async (doc) => {\n * await doc.update(() => {\n * const root = $getRoot();\n * const paragraphNode = $createParagraphNode();\n * const textNode = $createTextNode(\"Hello from node\");\n * paragraphNode.append(textNode);\n * root.append(paragraphNode);\n * });\n * return doc.getTextContent();\n * }\n * );\n *\n */\nexport async function withLexicalDocument<T>(\n { roomId, nodes, client }: LiveblocksLexicalOptions,\n callback: (api: LiveblocksDocumentApi) => Promise<T> | T\n): Promise<T> {\n const update = new Uint8Array(\n await client.getYjsDocumentAsBinaryUpdate(roomId)\n );\n const editor = createHeadlessEditor({\n nodes: [...LIVEBLOCKS_NODES, ...(nodes ?? [])],\n });\n const id = \"root\";\n const doc = new Doc();\n const docMap = new Map([[id, doc]]);\n const provider = createNoOpProvider();\n const binding = createBinding(editor, provider, id, doc, docMap);\n const unsubscribe = registerCollaborationListeners(editor, provider, binding);\n applyUpdate(binding.doc, update);\n editor.update(() => {}, { discrete: true });\n\n const val = await callback({\n /**\n * Fetches and resyncs the latest document with Liveblocks\n */\n refresh: async () => {\n const latest = new Uint8Array(\n await client.getYjsDocumentAsBinaryUpdate(roomId)\n );\n applyUpdate(binding.doc, latest);\n editor.update(() => {}, { discrete: true });\n },\n /**\n * Provide a callback to modify documetns with Lexical's standard api. All calls are discrete.\n */\n update: async (modifyFn) => {\n // Flush any pending updates (there really shouldn't be any?), this may be a NOOP\n editor.update(() => {}, { discrete: true });\n const beforeVector = encodeStateVector(binding.doc);\n editor.update(\n () => {\n modifyFn();\n },\n { discrete: true }\n );\n // grab update after diffing\n const diffUpdate = encodeStateAsUpdate(binding.doc, beforeVector);\n return client.sendYjsBinaryUpdate(roomId, diffUpdate);\n },\n /**\n * Helper function to easily provide the text content from the root, i.e. `$getRoot().getTextContent()`\n */\n getTextContent: () => {\n let content = \"\";\n editor.getEditorState().read(() => {\n content = $getRoot().getTextContent();\n });\n return content;\n },\n /**\n * Helper function to return editorState in JSON form\n */\n toJSON: () => {\n return editor.getEditorState().toJSON();\n },\n /**\n * Helper function to return editor state as Markdown\n */\n toMarkdown: () => {\n let markdown: string = \"\";\n editor.getEditorState().read(() => {\n markdown = $convertToMarkdownString(TRANSFORMERS);\n });\n return markdown;\n },\n /**\n * Helper function to return the editor's current state\n */\n getEditorState: () => {\n return editor.getEditorState();\n },\n /**\n * Helper function to return the current headless editor instance\n */\n getLexicalEditor: () => {\n return editor;\n },\n });\n unsubscribe();\n\n return val;\n}\n","import { createHeadlessEditor } from \"@lexical/headless\";\nimport type { Binding, Provider } from \"@lexical/yjs\";\nimport {\n createBinding,\n syncLexicalUpdateToYjs,\n syncYjsChangesToLexical,\n} from \"@lexical/yjs\";\nimport type {\n Klass,\n LexicalEditor,\n LexicalNode,\n LexicalNodeReplacement,\n} from \"lexical\";\nimport type { Transaction, YEvent } from \"yjs\";\nimport { Doc } from \"yjs\";\n\nexport function withHeadlessCollaborationEditor<T>(\n nodes: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>,\n callback: (editor: LexicalEditor, binding: Binding, provider: Provider) => T\n): T {\n const editor = createHeadlessEditor({\n nodes,\n });\n\n const id = \"root\";\n const doc = new Doc();\n const docMap = new Map([[id, doc]]);\n const provider = createNoOpProvider();\n const binding = createBinding(editor, provider, id, doc, docMap);\n\n const unsubscribe = registerCollaborationListeners(editor, provider, binding);\n\n const res = callback(editor, binding, provider);\n\n unsubscribe();\n\n return res;\n}\n\nexport function registerCollaborationListeners(\n editor: LexicalEditor,\n provider: Provider,\n binding: Binding\n): () => void {\n const unsubscribeUpdateListener = editor.registerUpdateListener(\n ({\n dirtyElements,\n dirtyLeaves,\n editorState,\n normalizedNodes,\n prevEditorState,\n tags,\n }) => {\n if (tags.has(\"skip-collab\") === false) {\n syncLexicalUpdateToYjs(\n binding,\n provider,\n prevEditorState,\n editorState,\n dirtyElements,\n dirtyLeaves,\n normalizedNodes,\n tags\n );\n }\n }\n );\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const observer = (events: Array<YEvent<any>>, transaction: Transaction) => {\n if (transaction.origin !== binding) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n syncYjsChangesToLexical(binding, provider, events, false);\n }\n };\n\n binding.root.getSharedType().observeDeep(observer);\n\n return () => {\n unsubscribeUpdateListener();\n binding.root.getSharedType().unobserveDeep(observer);\n };\n}\n\nexport function createNoOpProvider(): Provider {\n const emptyFunction = () => {};\n\n return {\n awareness: {\n getLocalState: () => null,\n getStates: () => new Map(),\n off: emptyFunction,\n on: emptyFunction,\n setLocalState: emptyFunction,\n },\n connect: emptyFunction,\n disconnect: emptyFunction,\n off: emptyFunction,\n on: emptyFunction,\n };\n}\n","import type { NodeKey, SerializedLexicalNode, Spread } from \"lexical\";\nimport { $applyNodeReplacement, DecoratorNode } from \"lexical\";\n\nexport type SerializedMentionNode = Spread<\n {\n value: string;\n },\n SerializedLexicalNode\n>;\n\nexport class MentionNode extends DecoratorNode<null> {\n __id: string;\n\n constructor(value: string, key?: NodeKey) {\n super(key);\n this.__id = value;\n }\n\n static getType(): string {\n return \"lb-mention\";\n }\n\n static clone(node: MentionNode): MentionNode {\n return new MentionNode(node.__id);\n }\n\n static importJSON(serializedNode: SerializedMentionNode): MentionNode {\n const node = new MentionNode(serializedNode.value);\n return $applyNodeReplacement(node);\n }\n\n exportJSON(): SerializedMentionNode {\n return {\n value: this.getTextContent(),\n type: \"lb-mention\",\n version: 1,\n };\n }\n\n getTextContent(): string {\n const self = this.getLatest();\n return self.__id;\n }\n\n decorate(): null {\n return null;\n }\n}\n","import type {\n BaseSelection,\n LexicalNode,\n NodeKey,\n SerializedElementNode,\n Spread,\n} from \"lexical\";\nimport { $applyNodeReplacement, $isRangeSelection, ElementNode } from \"lexical\";\n\nexport type SerializedThreadMarkNode = Spread<\n {\n ids: Array<string>;\n },\n SerializedElementNode\n>;\n\nexport class ThreadMarkNode extends ElementNode {\n /** @internal */\n __ids: Array<string>; // The ids of the threads that this mark is associated with\n\n static getType(): string {\n return \"lb-thread-mark\";\n }\n\n static clone(node: ThreadMarkNode): ThreadMarkNode {\n return new ThreadMarkNode(Array.from(node.__ids), node.__key);\n }\n\n static importJSON(serializedNode: SerializedThreadMarkNode): ThreadMarkNode {\n const node = $applyNodeReplacement<ThreadMarkNode>(\n new ThreadMarkNode(serializedNode.ids)\n );\n node.setFormat(serializedNode.format);\n node.setIndent(serializedNode.indent);\n node.setDirection(serializedNode.direction);\n return node;\n }\n\n exportJSON(): SerializedThreadMarkNode {\n return {\n ...super.exportJSON(),\n ids: this.getIDs(),\n type: \"lb-thread-mark\",\n version: 1,\n };\n }\n\n getIDs(): Array<string> {\n const self = this.getLatest();\n return self instanceof ThreadMarkNode ? self.__ids : [];\n }\n\n constructor(ids: Array<string>, key?: NodeKey) {\n super(key);\n this.__ids = ids || [];\n }\n\n canInsertTextBefore(): false {\n return false;\n }\n\n canInsertTextAfter(): false {\n return false;\n }\n\n canBeEmpty(): false {\n return false;\n }\n\n isInline(): true {\n return true;\n }\n\n extractWithChild(\n _: LexicalNode,\n selection: BaseSelection,\n destination: \"clone\" | \"html\"\n ): boolean {\n if (!$isRangeSelection(selection) || destination === \"html\") {\n return false;\n }\n const anchor = selection.anchor;\n const focus = selection.focus;\n const anchorNode = anchor.getNode();\n const focusNode = focus.getNode();\n const isBackward = selection.isBackward();\n const selectionLength = isBackward\n ? anchor.offset - focus.offset\n : focus.offset - anchor.offset;\n return (\n this.isParentOf(anchorNode) &&\n this.isParentOf(focusNode) &&\n this.getTextContent().length === selectionLength\n );\n }\n\n excludeFromCopy(destination: \"clone\" | \"html\"): boolean {\n return destination !== \"clone\";\n }\n}\n","declare const __VERSION__: string;\ndeclare const TSUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/node-lexical\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof TSUP_FORMAT === \"string\" && TSUP_FORMAT;\n"],"mappings":"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","names":["createHeadlessEditor","createBinding","Doc","$applyNodeReplacement","$getRoot","createHeadlessEditor","Doc","createBinding"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liveblocks/node-lexical",
3
- "version": "2.0.0-alpha4",
3
+ "version": "2.0.0",
4
4
  "description": "A server-side utility that lets you modify lexical documents hosted in Liveblocks.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "commonjs",
@@ -34,8 +34,8 @@
34
34
  "test:watch": "jest --silent --verbose --color=always --watch"
35
35
  },
36
36
  "dependencies": {
37
- "@liveblocks/core": "2.0.0-alpha4",
38
- "@liveblocks/node": "2.0.0-alpha4",
37
+ "@liveblocks/core": "2.0.0",
38
+ "@liveblocks/node": "2.0.0",
39
39
  "yjs": "^13.6.15"
40
40
  },
41
41
  "peerDependencies": {