@liveblocks/node-lexical
Version:
A server-side utility that lets you modify lexical documents hosted in Liveblocks.
290 lines (267 loc) • 7.96 kB
JavaScript
;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/MentionNodeLite.ts
var MENTION_CHARACTER = "@";
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 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.4.0";
var PKG_FORMAT = "cjs";
// src/index.ts
_core.detectDupes.call(void 0, PKG_NAME, PKG_VERSION, PKG_FORMAT);
var LIVEBLOCKS_NODES = [ThreadMarkNode, MentionNode];
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