diff --git a/package-lock.json b/package-lock.json index 092ca85733..d0c3efdcad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "@tiptap/extension-code-block-lowlight": "^2.11.9", "@tiptap/extension-floating-menu": "^2.25.0", "@tiptap/extension-highlight": "^2.10.0", + "@tiptap/extension-history": "^2.25.1", "@tiptap/extension-link": "^2.25.0", "@tiptap/extension-placeholder": "^2.10.0", "@tiptap/extension-table": "^2.12.0", @@ -3330,9 +3331,9 @@ } }, "node_modules/@tiptap/extension-history": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.10.0.tgz", - "integrity": "sha512-5aYOmxqaCnw7e7wmWqFZmkpYCxxDjEzFbgVI6WknqNwqeOizR4+YJf3aAt/lTbksLJe47XF+NBX51gOm/ZBCiw==", + "version": "2.25.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.25.1.tgz", + "integrity": "sha512-ZoxxOAObk1U8H3d+XEG0MjccJN0ViGIKEZqnLUSswmVweYPdkJG2WF2pEif9hpwJONslvLTKa+f8jwK5LEnJLQ==", "license": "MIT", "funding": { "type": "github", diff --git a/package.json b/package.json index ae875dd25d..7a7c745bbf 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@tiptap/extension-code-block-lowlight": "^2.11.9", "@tiptap/extension-floating-menu": "^2.25.0", "@tiptap/extension-highlight": "^2.10.0", + "@tiptap/extension-history": "^2.25.1", "@tiptap/extension-link": "^2.25.0", "@tiptap/extension-placeholder": "^2.10.0", "@tiptap/extension-table": "^2.12.0", diff --git a/src/lib/components/common/RichTextInput.svelte b/src/lib/components/common/RichTextInput.svelte index 52f42bc01b..587b1a647f 100644 --- a/src/lib/components/common/RichTextInput.svelte +++ b/src/lib/components/common/RichTextInput.svelte @@ -50,16 +50,20 @@ import { onMount, onDestroy, tick, getContext } from 'svelte'; import { createEventDispatcher } from 'svelte'; + import { socket } from '$lib/stores'; const i18n = getContext('i18n'); const eventDispatch = createEventDispatcher(); import { Fragment, DOMParser } from 'prosemirror-model'; import { EditorState, Plugin, PluginKey, TextSelection, Selection } from 'prosemirror-state'; + import { receiveTransaction, sendableSteps, getVersion } from 'prosemirror-collab'; + import { Step } from 'prosemirror-transform'; import { Decoration, DecorationSet } from 'prosemirror-view'; import { Editor } from '@tiptap/core'; import { AIAutocompletion } from './RichTextInput/AutoCompletion.js'; + import History from '@tiptap/extension-history'; import Table from '@tiptap/extension-table'; import TableRow from '@tiptap/extension-table-row'; import TableHeader from '@tiptap/extension-table-header'; @@ -118,6 +122,75 @@ export let largeTextAsFile = false; export let insertPromptAsRichText = false; + let isConnected = false; + let collaborators = new Map(); + let version = 0; + + // Custom collaboration plugin + const collaborationPlugin = () => { + return new Plugin({ + key: new PluginKey('collaboration'), + state: { + init: () => ({ version: 0 }), + apply: (tr, pluginState) => { + const newState = { ...pluginState }; + + if (tr.getMeta('collaboration')) { + newState.version = tr.getMeta('collaboration').version; + } + + return newState; + } + }, + view: () => ({ + update: (view, prevState) => { + const sendable = sendableSteps(view.state); + if (sendable) { + $socket.emit('document_steps', { + document_id: documentId, + user_id: userId, + version: sendable.version, + steps: sendable.steps.map((step) => step.toJSON()), + clientID: sendable.clientID + }); + } + } + }) + }); + }; + + function handleDocumentSteps(data) { + if (data.user_id !== userId && editor) { + const steps = data.steps.map((stepJSON) => Step.fromJSON(editor.schema, stepJSON)); + const tr = receiveTransaction(editor.state, steps, data.clientID); + + if (tr) { + editor.view.dispatch(tr); + } + } + } + + function handleDocumentState(data) { + version = data.version; + if (data.content && editor) { + editor.commands.setContent(data.content); + } + isConnected = true; + } + + function handleUserJoined(data) { + collaborators.set(data.user_id, { + name: data.user_name, + color: data.user_color + }); + collaborators = collaborators; + } + + function handleUserLeft(data) { + collaborators.delete(data.user_id); + collaborators = collaborators; + } + let floatingMenuElement = null; let bubbleMenuElement = null; let element; diff --git a/src/lib/components/notes/NoteEditor.svelte b/src/lib/components/notes/NoteEditor.svelte index 869c8ad054..e81f1ff1a3 100644 --- a/src/lib/components/notes/NoteEditor.svelte +++ b/src/lib/components/notes/NoteEditor.svelte @@ -772,16 +772,16 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings, />
- {#if note.data?.versions?.length > 0} + {#if editor}
@@ -789,9 +789,10 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,