@liveblocks/node-prosemirror
Version:
A server-side utility that lets you modify prosemirror and tiptap documents hosted in Liveblocks.
249 lines (239 loc) • 7.33 kB
JavaScript
;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.4.0";
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/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, [
_starterkit2.default,
CommentExtension,
MentionExtension
]);
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 Lexical's standard api. All calls are discrete.
*/
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);
});
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