UNPKG

@milkdown/plugin-collab

Version:
138 lines (137 loc) 4.4 kB
import { EditorViewReady, editorViewCtx, getDoc, parserCtx, prosePluginsCtx, schemaCtx } from "@milkdown/core"; import { createSlice, createTimer } from "@milkdown/ctx"; import { ctxNotBind, missingYjsDoc } from "@milkdown/exception"; import { keydownHandler } from "@milkdown/prose/keymap"; import { Plugin, PluginKey } from "@milkdown/prose/state"; import { prosemirrorToYDoc, redo, undo, yCursorPlugin, yCursorPluginKey, ySyncPlugin, ySyncPluginKey, yUndoPlugin, yUndoPluginKey, yXmlFragmentToProseMirrorRootNode } from "y-prosemirror"; import { applyUpdate, encodeStateAsUpdate } from "yjs"; //#region src/collab-service.ts var CollabKeymapPluginKey = new PluginKey("MILKDOWN_COLLAB_KEYMAP"); var collabPluginKeys = [ CollabKeymapPluginKey, ySyncPluginKey, yCursorPluginKey, yUndoPluginKey ]; var CollabService = class { #options = {}; #xmlFragment = null; #awareness = null; #ctx = null; #connected = false; #valueToNode(value) { if (!this.#ctx) throw ctxNotBind(); const schema = this.#ctx.get(schemaCtx); return getDoc(value, this.#ctx.get(parserCtx), schema); } #createPlugins() { if (!this.#xmlFragment) throw missingYjsDoc(); const { ySyncOpts, yUndoOpts } = this.#options; const plugins = [ ySyncPlugin(this.#xmlFragment, ySyncOpts), yUndoPlugin(yUndoOpts), new Plugin({ key: CollabKeymapPluginKey, props: { handleKeyDown: keydownHandler({ "Mod-z": undo, "Mod-y": redo, "Mod-Shift-z": redo }) } }) ]; if (this.#awareness) { const { yCursorOpts, yCursorStateField } = this.#options; plugins.push(yCursorPlugin(this.#awareness, yCursorOpts, yCursorStateField)); } return plugins; } #flushEditor(plugins) { if (!this.#ctx) throw ctxNotBind(); this.#ctx.set(prosePluginsCtx, plugins); const view = this.#ctx.get(editorViewCtx); const newState = view.state.reconfigure({ plugins }); view.updateState(newState); } bindCtx(ctx) { this.#ctx = ctx; return this; } bindDoc(doc) { this.#xmlFragment = doc.getXmlFragment("prosemirror"); return this; } bindXmlFragment(xmlFragment) { this.#xmlFragment = xmlFragment; return this; } setOptions(options) { this.#options = options; return this; } mergeOptions(options) { Object.assign(this.#options, options); return this; } setAwareness(awareness) { this.#awareness = awareness; return this; } applyTemplate(template, condition) { if (!this.#ctx) throw ctxNotBind(); if (!this.#xmlFragment) throw missingYjsDoc(); const conditionFn = condition || ((yDocNode) => yDocNode.textContent.length === 0); const node = this.#valueToNode(template); const schema = this.#ctx.get(schemaCtx); const yDocNode = yXmlFragmentToProseMirrorRootNode(this.#xmlFragment, schema); if (node && conditionFn(yDocNode, node)) { const fragment = this.#xmlFragment; fragment.delete(0, fragment.length); const templateDoc = prosemirrorToYDoc(node); const template = encodeStateAsUpdate(templateDoc); if (fragment.doc) applyUpdate(fragment.doc, template); templateDoc.destroy(); } return this; } connect() { if (!this.#ctx) throw ctxNotBind(); if (this.#connected) return; const prosePlugins = this.#ctx.get(prosePluginsCtx); const collabPlugins = this.#createPlugins(); const plugins = prosePlugins.concat(collabPlugins); this.#flushEditor(plugins); this.#connected = true; return this; } disconnect() { if (!this.#ctx) throw ctxNotBind(); if (!this.#connected) return this; const plugins = this.#ctx.get(prosePluginsCtx).filter((plugin) => !plugin.spec.key || !collabPluginKeys.includes(plugin.spec.key)); this.#flushEditor(plugins); this.#connected = false; return this; } }; //#endregion //#region src/index.ts var collabServiceCtx = createSlice(new CollabService(), "collabServiceCtx"); var CollabReady = createTimer("CollabReady"); var collab = (ctx) => { const collabService = new CollabService(); ctx.inject(collabServiceCtx, collabService).record(CollabReady); return async () => { await ctx.wait(EditorViewReady); collabService.bindCtx(ctx); ctx.done(CollabReady); return () => { ctx.remove(collabServiceCtx).clearTimer(CollabReady); }; }; }; collab.meta = { package: "@milkdown/plugin-collab", displayName: "Collab" }; //#endregion export { CollabKeymapPluginKey, CollabReady, CollabService, collab, collabServiceCtx }; //# sourceMappingURL=index.js.map