UNPKG

@liveblocks/node-lexical

Version:

A server-side utility that lets you modify lexical documents hosted in Liveblocks.

347 lines (317 loc) 9.19 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/index.ts var _headless = require('@lexical/headless'); var _markdown = require('@lexical/markdown'); var _yjs = require('@lexical/yjs'); var _core = require('@liveblocks/core'); var _lexical = require('lexical'); var _yjs3 = require('yjs'); // src/collab.ts function registerCollaborationListeners(editor, provider, binding) { const unsubscribeUpdateListener = editor.registerUpdateListener( ({ dirtyElements, dirtyLeaves, editorState, normalizedNodes, prevEditorState, tags }) => { if (tags.has("skip-collab") === false) { _yjs.syncLexicalUpdateToYjs.call(void 0, binding, provider, prevEditorState, editorState, dirtyElements, dirtyLeaves, normalizedNodes, tags ); } } ); const observer = (events, transaction) => { if (transaction.origin !== binding) { _yjs.syncYjsChangesToLexical.call(void 0, binding, provider, events, false); } }; binding.root.getSharedType().observeDeep(observer); return () => { unsubscribeUpdateListener(); binding.root.getSharedType().unobserveDeep(observer); }; } function createNoOpProvider() { const emptyFunction = () => { }; return { awareness: { getLocalState: () => null, setLocalStateField: emptyFunction, getStates: () => /* @__PURE__ */ new Map(), off: emptyFunction, on: emptyFunction, setLocalState: emptyFunction }, connect: emptyFunction, disconnect: emptyFunction, off: emptyFunction, on: emptyFunction }; } // src/GroupMentionNodeLite.ts var GroupMentionNode = class _GroupMentionNode extends _lexical.DecoratorNode { constructor(id, groupId, userIds, key) { super(key); this.__id = id; this.__groupId = groupId; this.__userIds = userIds; } static getType() { return "lb-group-mention"; } static clone(node) { return new _GroupMentionNode(node.__id, node.__groupId, node.__userIds); } static importJSON(serializedNode) { const node = new _GroupMentionNode( serializedNode.id, serializedNode.groupId, serializedNode.userIds ); return _lexical.$applyNodeReplacement.call(void 0, node); } exportJSON() { return { id: this.getId(), groupId: this.getGroupId(), userIds: this.getUserIds(), type: "lb-group-mention", version: 1 }; } getId() { const self = this.getLatest(); return self.__id; } getGroupId() { const self = this.getLatest(); return self.__groupId; } getUserIds() { const self = this.getLatest(); return self.__userIds; } getTextContent() { const groupId = this.getGroupId(); return _core.MENTION_CHARACTER + groupId; } decorate() { return null; } }; // src/MentionNodeLite.ts var MentionNode = class _MentionNode extends _lexical.DecoratorNode { constructor(id, userId, key) { super(key); this.__id = id; this.__userId = userId; } static getType() { return "lb-mention"; } static clone(node) { return new _MentionNode(node.__id, node.__userId); } static importJSON(serializedNode) { const node = new _MentionNode( _nullishCoalesce(serializedNode.value, () => ( serializedNode.id)), serializedNode.userId ); return _lexical.$applyNodeReplacement.call(void 0, node); } exportJSON() { return { id: this.getId(), userId: this.getUserId(), type: "lb-mention", version: 1 }; } getId() { const self = this.getLatest(); return self.__id; } getUserId() { const self = this.getLatest(); return self.__userId; } getTextContent() { const userId = this.getUserId(); if (userId) { return _core.MENTION_CHARACTER + userId; } return this.getId(); } decorate() { return null; } }; // src/ThreadNodeLite.ts var ThreadMarkNode = class _ThreadMarkNode extends _lexical.ElementNode { /** @internal */ // The ids of the threads that this mark is associated with static getType() { return "lb-thread-mark"; } static clone(node) { return new _ThreadMarkNode(Array.from(node.__ids), node.__key); } static importJSON(serializedNode) { const node = _lexical.$applyNodeReplacement.call(void 0, new _ThreadMarkNode(serializedNode.ids) ); node.setFormat(serializedNode.format); node.setIndent(serializedNode.indent); node.setDirection(serializedNode.direction); return node; } exportJSON() { return { ...super.exportJSON(), ids: this.getIDs(), type: "lb-thread-mark", version: 1 }; } getIDs() { const self = this.getLatest(); return self instanceof _ThreadMarkNode ? self.__ids : []; } constructor(ids, key) { super(key); this.__ids = ids || []; } canInsertTextBefore() { return false; } canInsertTextAfter() { return false; } canBeEmpty() { return false; } isInline() { return true; } extractWithChild(_, selection, destination) { if (!_lexical.$isRangeSelection.call(void 0, selection) || destination === "html") { return false; } const anchor = selection.anchor; const focus = selection.focus; const anchorNode = anchor.getNode(); const focusNode = focus.getNode(); const isBackward = selection.isBackward(); const selectionLength = isBackward ? anchor.offset - focus.offset : focus.offset - anchor.offset; return this.isParentOf(anchorNode) && this.isParentOf(focusNode) && this.getTextContent().length === selectionLength; } excludeFromCopy(destination) { return destination !== "clone"; } }; // src/version.ts var PKG_NAME = "@liveblocks/node-lexical"; var PKG_VERSION = "3.12.1"; var PKG_FORMAT = "cjs"; // src/index.ts _core.detectDupes.call(void 0, PKG_NAME, PKG_VERSION, PKG_FORMAT); var LIVEBLOCKS_NODES = [ThreadMarkNode, MentionNode, GroupMentionNode]; async function withLexicalDocument({ roomId, nodes, client }, callback) { const update = new Uint8Array( await client.getYjsDocumentAsBinaryUpdate(roomId) ); const editor = _headless.createHeadlessEditor.call(void 0, { nodes: [...LIVEBLOCKS_NODES, ..._nullishCoalesce(nodes, () => ( []))] }); const id = "root"; const doc = new (0, _yjs3.Doc)(); const docMap = /* @__PURE__ */ new Map([[id, doc]]); const provider = createNoOpProvider(); const binding = _yjs.createBinding.call(void 0, editor, provider, id, doc, docMap); const unsubscribe = registerCollaborationListeners(editor, provider, binding); _yjs3.applyUpdate.call(void 0, binding.doc, update); editor.update(() => { }, { discrete: true }); const val = await callback({ /** * Fetches and resyncs the latest document with Liveblocks */ refresh: async () => { const latest = new Uint8Array( await client.getYjsDocumentAsBinaryUpdate(roomId) ); _yjs3.applyUpdate.call(void 0, binding.doc, latest); editor.update(() => { }, { discrete: true }); }, /** * Provide a callback to modify documetns with Lexical's standard api. All calls are discrete. */ update: async (modifyFn) => { editor.update(() => { }, { discrete: true }); const beforeVector = _yjs3.encodeStateVector.call(void 0, binding.doc); editor.update( () => { modifyFn(); }, { discrete: true } ); const diffUpdate = _yjs3.encodeStateAsUpdate.call(void 0, binding.doc, beforeVector); return client.sendYjsBinaryUpdate(roomId, diffUpdate); }, /** * Helper function to easily provide the text content from the root, i.e. `$getRoot().getTextContent()` */ getTextContent: () => { let content = ""; editor.getEditorState().read(() => { content = _lexical.$getRoot.call(void 0, ).getTextContent(); }); return content; }, /** * Helper function to return editorState in JSON form */ toJSON: () => { return editor.getEditorState().toJSON(); }, /** * Helper function to return editor state as Markdown */ toMarkdown: () => { let markdown = ""; editor.getEditorState().read(() => { markdown = _markdown.$convertToMarkdownString.call(void 0, _markdown.TRANSFORMERS); }); return markdown; }, /** * Helper function to return the editor's current state */ getEditorState: () => { return editor.getEditorState(); }, /** * Helper function to return the current headless editor instance */ getLexicalEditor: () => { return editor; } }); unsubscribe(); return val; } exports.$createParagraphNode = _lexical.$createParagraphNode; exports.$createTextNode = _lexical.$createTextNode; exports.$getRoot = _lexical.$getRoot; exports.withLexicalDocument = withLexicalDocument; //# sourceMappingURL=index.cjs.map