From 4862bf193f7c4673d5e4222f3e532729a0e2b5d4 Mon Sep 17 00:00:00 2001 From: Xinyu Ma Date: Tue, 10 Dec 2024 22:34:18 -0800 Subject: [PATCH] feat: enable multiple files in the same yjs doc (#1551) * feat: enable multiple files in the same yjs doc * feat: add storybook page for plugin-collab * [autofix.ci] apply automated fixes * chore: resolve lock file conflict * chore: remove unneeded conversion * chore: f --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Mirone --- packages/integrations/vue/tsconfig.json | 5 ++-- packages/plugins/plugin-collab/package.json | 2 +- .../plugin-collab/src/collab-service.ts | 30 ++++++++++++------- packages/plugins/plugin-collab/src/shim.d.ts | 5 +++- pnpm-lock.yaml | 10 +++---- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/packages/integrations/vue/tsconfig.json b/packages/integrations/vue/tsconfig.json index d1e6930996d5..5f5ed458264e 100644 --- a/packages/integrations/vue/tsconfig.json +++ b/packages/integrations/vue/tsconfig.json @@ -1,12 +1,13 @@ { "extends": "../../../tsconfig.base.json", "compilerOptions": { - "jsx": "react", + "jsx": "preserve", "jsxFactory": "h", "jsxFragmentFactory": "Fragment", "rootDir": "src", "types": ["vue"], - "outDir": "lib" + "outDir": "lib", + "jsxImportSource": "vue" }, "include": ["src"] } diff --git a/packages/plugins/plugin-collab/package.json b/packages/plugins/plugin-collab/package.json index ea7de5fe9b4f..458e06ceafc0 100644 --- a/packages/plugins/plugin-collab/package.json +++ b/packages/plugins/plugin-collab/package.json @@ -59,7 +59,7 @@ "@milkdown/core": "workspace:*", "@milkdown/ctx": "workspace:*", "@milkdown/prose": "workspace:*", - "y-prosemirror": "^1.2.1", + "y-prosemirror": "^1.2.12", "y-protocols": "^1.0.5", "yjs": "^13.5.38" }, diff --git a/packages/plugins/plugin-collab/src/collab-service.ts b/packages/plugins/plugin-collab/src/collab-service.ts index 9b6979303959..f1263bfec4ef 100644 --- a/packages/plugins/plugin-collab/src/collab-service.ts +++ b/packages/plugins/plugin-collab/src/collab-service.ts @@ -18,14 +18,14 @@ import { undo, yCursorPlugin, yCursorPluginKey, - yDocToProsemirror, + yXmlFragmentToProseMirrorRootNode, ySyncPlugin, ySyncPluginKey, yUndoPlugin, yUndoPluginKey, } from 'y-prosemirror' import type { Awareness } from 'y-protocols/awareness' -import type { Doc, PermanentUserData } from 'yjs' +import type { Doc, PermanentUserData, XmlFragment } from 'yjs' import { applyUpdate, encodeStateAsUpdate } from 'yjs' /// @internal @@ -86,7 +86,7 @@ export class CollabService { /// @internal #options: CollabServiceOptions = {} /// @internal - #doc: Doc | null = null + #xmlFragment: XmlFragment | null = null /// @internal #awareness: Awareness | null = null /// @internal @@ -107,11 +107,10 @@ export class CollabService { /// @internal #createPlugins(): Plugin[] { - if (!this.#doc) throw missingYjsDoc() + if (!this.#xmlFragment) throw missingYjsDoc() const { ySyncOpts, yUndoOpts } = this.#options - const type = this.#doc.getXmlFragment('prosemirror') const plugins = [ - ySyncPlugin(type, ySyncOpts), + ySyncPlugin(this.#xmlFragment, ySyncOpts), yUndoPlugin(yUndoOpts), new Plugin({ key: CollabKeymapPluginKey, @@ -156,7 +155,13 @@ export class CollabService { /// Bind the document to the service. bindDoc(doc: Doc) { - this.#doc = doc + this.#xmlFragment = doc.getXmlFragment('prosemirror') + return this + } + + /// Bind the Yjs XmlFragment to the service. + bindXmlFragment(xmlFragment: XmlFragment) { + this.#xmlFragment = xmlFragment return this } @@ -186,20 +191,23 @@ export class CollabService { condition?: (yDocNode: Node, templateNode: Node) => boolean ) { if (!this.#ctx) throw ctxNotBind() - if (!this.#doc) throw missingYjsDoc() + 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 = yDocToProsemirror(schema, this.#doc) + const yDocNode = yXmlFragmentToProseMirrorRootNode( + this.#xmlFragment, + schema + ) if (node && conditionFn(yDocNode, node)) { - const fragment = this.#doc.getXmlFragment('prosemirror') + const fragment = this.#xmlFragment fragment.delete(0, fragment.length) const templateDoc = prosemirrorToYDoc(node) const template = encodeStateAsUpdate(templateDoc) - applyUpdate(this.#doc, template) + if (fragment.doc) applyUpdate(fragment.doc, template) templateDoc.destroy() } diff --git a/packages/plugins/plugin-collab/src/shim.d.ts b/packages/plugins/plugin-collab/src/shim.d.ts index 41d7a78d0de0..790192dde7fb 100644 --- a/packages/plugins/plugin-collab/src/shim.d.ts +++ b/packages/plugins/plugin-collab/src/shim.d.ts @@ -51,7 +51,10 @@ declare module 'y-prosemirror' { export const yCursorPluginKey: PluginKey export const ySyncPluginKey: PluginKey export const yUndoPluginKey: PluginKey - export function yDocToProsemirror(schema: Schema, ydoc: Y.Doc): Node + export function yXmlFragmentToProseMirrorRootNode( + yXmlFragment: Y.XmlFragment, + schema: Schema + ): Node export function ySyncPlugin( yXmlFragment: Y.XmlFragment, { colors, colorMapping, permanentUserData, onFirstRender }?: YSyncOpts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e2faf4baf002..7f37cdc80a80 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -613,8 +613,8 @@ importers: specifier: workspace:* version: link:../../prose y-prosemirror: - specifier: ^1.2.1 - version: 1.2.2(prosemirror-model@1.21.3)(prosemirror-state@1.4.3)(prosemirror-view@1.33.8)(y-protocols@1.0.6(yjs@13.6.10))(yjs@13.6.10) + specifier: ^1.2.12 + version: 1.2.15(prosemirror-model@1.21.3)(prosemirror-state@1.4.3)(prosemirror-view@1.33.8)(y-protocols@1.0.6(yjs@13.6.10))(yjs@13.6.10) y-protocols: specifier: ^1.0.5 version: 1.0.6(yjs@13.6.10) @@ -8175,8 +8175,8 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - y-prosemirror@1.2.2: - resolution: {integrity: sha512-hHdnIAhfa8mIoLWtTkMDb6RBzN3lye1QVkaZwVm58sledAA1zTl+yyEtgkrY/sdH6SaQL0rsLj61zHjgr5D0HQ==} + y-prosemirror@1.2.15: + resolution: {integrity: sha512-XDdrytq2M5bIy3qusQvfRclLu2eWZYPA+BbGWAb9FFWEhOB5FCrnzez2vsA+gvAd0FJTAcr89mjJ5g45r0j7TQ==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} peerDependencies: prosemirror-model: ^1.7.1 @@ -16374,7 +16374,7 @@ snapshots: xmlchars@2.2.0: {} - y-prosemirror@1.2.2(prosemirror-model@1.21.3)(prosemirror-state@1.4.3)(prosemirror-view@1.33.8)(y-protocols@1.0.6(yjs@13.6.10))(yjs@13.6.10): + y-prosemirror@1.2.15(prosemirror-model@1.21.3)(prosemirror-state@1.4.3)(prosemirror-view@1.33.8)(y-protocols@1.0.6(yjs@13.6.10))(yjs@13.6.10): dependencies: lib0: 0.2.88 prosemirror-model: 1.21.3