@milkdown/plugin-collab
Version:
138 lines (137 loc) • 4.4 kB
JavaScript
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