UNPKG

@liveblocks/node-prosemirror

Version:

A server-side utility that lets you modify prosemirror and tiptap documents hosted in Liveblocks.

328 lines (316 loc) 9.44 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/index.ts var _core = require('@liveblocks/core'); // src/version.ts var PKG_NAME = "@liveblocks/node-prosemirror"; var PKG_VERSION = "3.12.1"; var PKG_FORMAT = "cjs"; // src/document.ts var _core3 = require('@tiptap/core'); var _state = require('@tiptap/pm/state'); var _starterkit = require('@tiptap/starter-kit'); var _starterkit2 = _interopRequireDefault(_starterkit); var _prosemirrormarkdown = require('prosemirror-markdown'); var _yprosemirror = require('y-prosemirror'); var _yjs = require('yjs'); // src/comment.ts var LIVEBLOCKS_COMMENT_MARK_TYPE = "liveblocksCommentMark"; var CommentExtension = _core3.Mark.create({ name: LIVEBLOCKS_COMMENT_MARK_TYPE, excludes: "", inclusive: false, keepOnSplit: true, addAttributes() { return { orphan: { parseHTML: (element) => !!element.getAttribute("data-orphan"), renderHTML: (attributes) => { return attributes.orphan ? { "data-orphan": "true" } : {}; }, default: false }, threadId: { parseHTML: (element) => element.getAttribute("data-lb-thread-id"), renderHTML: (attributes) => { return { "data-lb-thread-id": attributes.threadId }; }, default: "" } }; }, renderHTML({ HTMLAttributes }) { return [ "span", _core3.mergeAttributes.call(void 0, HTMLAttributes, { class: "lb-root lb-tiptap-thread-mark" }) ]; } }); // src/group-mention.ts var LIVEBLOCKS_GROUP_MENTION_TYPE = "liveblocksGroupMention"; var GroupMentionExtension = _core3.Node.create({ name: LIVEBLOCKS_GROUP_MENTION_TYPE, group: "inline", inline: true, selectable: true, atom: true, priority: 101, parseHTML() { return [ { tag: "liveblocks-group-mention" } ]; }, renderHTML({ HTMLAttributes }) { return ["liveblocks-group-mention", _core3.mergeAttributes.call(void 0, HTMLAttributes)]; }, addAttributes() { return { id: { default: null, parseHTML: (element) => element.getAttribute("data-id"), renderHTML: (attributes) => { if (!attributes.id) { return {}; } return { "data-id": attributes.id // "as" typing because TipTap doesn't have a way to type attributes }; } }, userIds: { default: null, parseHTML: (element) => { const userIdsAttribute = element.getAttribute("data-user-ids"); if (!userIdsAttribute) { return null; } try { const userIds = JSON.parse(userIdsAttribute); return Array.isArray(userIds) ? userIds : null; } catch (e) { return null; } }, renderHTML: (attributes) => { if (!attributes.userIds || !Array.isArray(attributes.userIds)) { return {}; } return { "data-user-ids": JSON.stringify(attributes.userIds) }; } }, notificationId: { default: null, parseHTML: (element) => element.getAttribute("data-notification-id"), renderHTML: (attributes) => { if (!attributes.notificationId) { return {}; } return { "data-notification-id": attributes.notificationId // "as" typing because TipTap doesn't have a way to type attributes }; } } }; } }); // src/mention.ts var LIVEBLOCKS_MENTION_TYPE = "liveblocksMention"; var MentionExtension = _core3.Node.create({ name: LIVEBLOCKS_MENTION_TYPE, group: "inline", inline: true, selectable: true, atom: true, priority: 101, parseHTML() { return [ { tag: "liveblocks-mention" } ]; }, renderHTML({ HTMLAttributes }) { return ["liveblocks-mention", _core3.mergeAttributes.call(void 0, HTMLAttributes)]; }, addAttributes() { return { id: { default: null, parseHTML: (element) => element.getAttribute("data-id"), renderHTML: (attributes) => { if (!attributes.id) { return {}; } return { "data-id": attributes.id // "as" typing because TipTap doesn't have a way to type attributes }; } }, notificationId: { default: null, parseHTML: (element) => element.getAttribute("data-notification-id"), renderHTML: (attributes) => { if (!attributes.notificationId) { return {}; } return { "data-notification-id": attributes.notificationId // "as" typing because TipTap doesn't have a way to type attributes }; } } }; } }); // src/document.ts var DEFAULT_SCHEMA = _core3.getSchema.call(void 0, [ CommentExtension, MentionExtension, _starterkit2.default, GroupMentionExtension ]); var getLiveblocksDocumentState = async (roomId, client, schema, field) => { const update = new Uint8Array( await client.getYjsDocumentAsBinaryUpdate(roomId) ); const ydoc = new (0, _yjs.Doc)(); _yjs.applyUpdate.call(void 0, ydoc, update); const fragment = ydoc.getXmlFragment(_nullishCoalesce(field, () => ( "default"))); const { mapping, doc } = _yprosemirror.initProseMirrorDoc.call(void 0, fragment, schema); const state = _state.EditorState.create({ schema, doc }); return { fragment, state, ydoc, mapping }; }; var createDocumentFromContent = (content, schema) => { try { return schema.nodeFromJSON(content); } catch (error) { console.warn( "[warn]: Invalid content.", "Passed value:", content, "Error:", error ); return false; } }; async function withProsemirrorDocument({ roomId, schema: maybeSchema, client, field }, callback) { const schema = _nullishCoalesce(maybeSchema, () => ( DEFAULT_SCHEMA)); let liveblocksState = await getLiveblocksDocumentState( roomId, client, schema, _nullishCoalesce(field, () => ( "default")) ); const val = await callback({ /** * Fetches and resyncs the latest document with Liveblocks */ async refresh() { liveblocksState = await getLiveblocksDocumentState( roomId, client, schema, _nullishCoalesce(field, () => ( "default")) ); }, /** * Provide a callback to modify documetns with prosemirrors's standard api. */ async update(modifyFn) { const { ydoc, fragment, state, mapping } = liveblocksState; const beforeVector = _yjs.encodeStateVector.call(void 0, ydoc); const afterState = state.apply(modifyFn(state.doc, state.tr)); ydoc.transact(() => { _yprosemirror.updateYFragment.call(void 0, ydoc, fragment, afterState.doc, { mapping, isOMark: /* @__PURE__ */ new Map() }); }); const diffUpdate = _yjs.encodeStateAsUpdate.call(void 0, ydoc, beforeVector); await client.sendYjsBinaryUpdate(roomId, diffUpdate); await this.refresh(); }, /** * allows you to set content similar to TipTap's setcontent. Only accepts nulls, objects or strings. * Unlike TipTap, strings won't be parsed with DOMParser * */ async setContent(content) { if (typeof content === "string") { return this.update((doc, tr) => { tr.delete(0, doc.content.size); tr.insertText(content); return tr; }); } if (content === null) { return this.clearContent(); } const node = createDocumentFromContent(content, schema); if (!node) { throw "Invalid content"; } return this.update((doc, tr) => { tr.delete(0, doc.content.size); tr.insert(0, node); return tr; }); }, async clearContent() { await this.update((doc, tr) => { tr.delete(0, doc.content.size); return tr; }); }, /** * Uses TipTap's getText function, which allows passing a custom text serializer */ getText(options) { const { state } = liveblocksState; return _core3.getText.call(void 0, state.doc, options); }, /** * Helper function to return prosemirror document in JSON form */ toJSON() { return liveblocksState.state.doc.toJSON(); }, /** * Helper function to return editor state as Markdown. By default it uses the defaultMarkdownSerializer from prosemirror-markdown, but you may pass your own */ toMarkdown(serializer) { return (_nullishCoalesce(serializer, () => ( _prosemirrormarkdown.defaultMarkdownSerializer))).serialize( liveblocksState.state.doc ); }, /** * Helper function to return the editor's current prosemirror state */ getEditorState() { return liveblocksState.state; } }); return val; } // src/index.ts _core.detectDupes.call(void 0, PKG_NAME, PKG_VERSION, PKG_FORMAT); exports.withProsemirrorDocument = withProsemirrorDocument; //# sourceMappingURL=index.cjs.map