mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-12 12:25:20 +00:00
enh: proper undo/redo note editor support
This commit is contained in:
parent
3b9d86de0b
commit
3bc5485867
4 changed files with 85 additions and 9 deletions
7
package-lock.json
generated
7
package-lock.json
generated
|
|
@ -24,6 +24,7 @@
|
||||||
"@tiptap/extension-code-block-lowlight": "^2.11.9",
|
"@tiptap/extension-code-block-lowlight": "^2.11.9",
|
||||||
"@tiptap/extension-floating-menu": "^2.25.0",
|
"@tiptap/extension-floating-menu": "^2.25.0",
|
||||||
"@tiptap/extension-highlight": "^2.10.0",
|
"@tiptap/extension-highlight": "^2.10.0",
|
||||||
|
"@tiptap/extension-history": "^2.25.1",
|
||||||
"@tiptap/extension-link": "^2.25.0",
|
"@tiptap/extension-link": "^2.25.0",
|
||||||
"@tiptap/extension-placeholder": "^2.10.0",
|
"@tiptap/extension-placeholder": "^2.10.0",
|
||||||
"@tiptap/extension-table": "^2.12.0",
|
"@tiptap/extension-table": "^2.12.0",
|
||||||
|
|
@ -3330,9 +3331,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-history": {
|
"node_modules/@tiptap/extension-history": {
|
||||||
"version": "2.10.0",
|
"version": "2.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.25.1.tgz",
|
||||||
"integrity": "sha512-5aYOmxqaCnw7e7wmWqFZmkpYCxxDjEzFbgVI6WknqNwqeOizR4+YJf3aAt/lTbksLJe47XF+NBX51gOm/ZBCiw==",
|
"integrity": "sha512-ZoxxOAObk1U8H3d+XEG0MjccJN0ViGIKEZqnLUSswmVweYPdkJG2WF2pEif9hpwJONslvLTKa+f8jwK5LEnJLQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@
|
||||||
"@tiptap/extension-code-block-lowlight": "^2.11.9",
|
"@tiptap/extension-code-block-lowlight": "^2.11.9",
|
||||||
"@tiptap/extension-floating-menu": "^2.25.0",
|
"@tiptap/extension-floating-menu": "^2.25.0",
|
||||||
"@tiptap/extension-highlight": "^2.10.0",
|
"@tiptap/extension-highlight": "^2.10.0",
|
||||||
|
"@tiptap/extension-history": "^2.25.1",
|
||||||
"@tiptap/extension-link": "^2.25.0",
|
"@tiptap/extension-link": "^2.25.0",
|
||||||
"@tiptap/extension-placeholder": "^2.10.0",
|
"@tiptap/extension-placeholder": "^2.10.0",
|
||||||
"@tiptap/extension-table": "^2.12.0",
|
"@tiptap/extension-table": "^2.12.0",
|
||||||
|
|
|
||||||
|
|
@ -50,16 +50,20 @@
|
||||||
|
|
||||||
import { onMount, onDestroy, tick, getContext } from 'svelte';
|
import { onMount, onDestroy, tick, getContext } from 'svelte';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import { socket } from '$lib/stores';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
const eventDispatch = createEventDispatcher();
|
const eventDispatch = createEventDispatcher();
|
||||||
|
|
||||||
import { Fragment, DOMParser } from 'prosemirror-model';
|
import { Fragment, DOMParser } from 'prosemirror-model';
|
||||||
import { EditorState, Plugin, PluginKey, TextSelection, Selection } from 'prosemirror-state';
|
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 { Decoration, DecorationSet } from 'prosemirror-view';
|
||||||
import { Editor } from '@tiptap/core';
|
import { Editor } from '@tiptap/core';
|
||||||
|
|
||||||
import { AIAutocompletion } from './RichTextInput/AutoCompletion.js';
|
import { AIAutocompletion } from './RichTextInput/AutoCompletion.js';
|
||||||
|
import History from '@tiptap/extension-history';
|
||||||
import Table from '@tiptap/extension-table';
|
import Table from '@tiptap/extension-table';
|
||||||
import TableRow from '@tiptap/extension-table-row';
|
import TableRow from '@tiptap/extension-table-row';
|
||||||
import TableHeader from '@tiptap/extension-table-header';
|
import TableHeader from '@tiptap/extension-table-header';
|
||||||
|
|
@ -118,6 +122,75 @@
|
||||||
export let largeTextAsFile = false;
|
export let largeTextAsFile = false;
|
||||||
export let insertPromptAsRichText = 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 floatingMenuElement = null;
|
||||||
let bubbleMenuElement = null;
|
let bubbleMenuElement = null;
|
||||||
let element;
|
let element;
|
||||||
|
|
|
||||||
|
|
@ -772,16 +772,16 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="flex items-center gap-0.5 translate-x-1">
|
<div class="flex items-center gap-0.5 translate-x-1">
|
||||||
{#if note.data?.versions?.length > 0}
|
{#if editor}
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center gap-0.5 self-center min-w-fit" dir="ltr">
|
<div class="flex items-center gap-0.5 self-center min-w-fit" dir="ltr">
|
||||||
<button
|
<button
|
||||||
class="self-center p-1 hover:enabled:bg-black/5 dark:hover:enabled:bg-white/5 dark:hover:enabled:text-white hover:enabled:text-black rounded-md transition disabled:cursor-not-allowed disabled:text-gray-500 disabled:hover:text-gray-500"
|
class="self-center p-1 hover:enabled:bg-black/5 dark:hover:enabled:bg-white/5 dark:hover:enabled:text-white hover:enabled:text-black rounded-md transition disabled:cursor-not-allowed disabled:text-gray-500 disabled:hover:text-gray-500"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
versionNavigateHandler('prev');
|
editor.chain().focus().undo().run();
|
||||||
|
// versionNavigateHandler('prev');
|
||||||
}}
|
}}
|
||||||
disabled={(versionIdx === null && note.data.versions.length === 0) ||
|
disabled={!editor.can().undo()}
|
||||||
versionIdx === 0}
|
|
||||||
>
|
>
|
||||||
<ArrowUturnLeft className="size-4" />
|
<ArrowUturnLeft className="size-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -789,9 +789,10 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
|
||||||
<button
|
<button
|
||||||
class="self-center p-1 hover:enabled:bg-black/5 dark:hover:enabled:bg-white/5 dark:hover:enabled:text-white hover:enabled:text-black rounded-md transition disabled:cursor-not-allowed disabled:text-gray-500 disabled:hover:text-gray-500"
|
class="self-center p-1 hover:enabled:bg-black/5 dark:hover:enabled:bg-white/5 dark:hover:enabled:text-white hover:enabled:text-black rounded-md transition disabled:cursor-not-allowed disabled:text-gray-500 disabled:hover:text-gray-500"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
versionNavigateHandler('next');
|
editor.chain().focus().redo().run();
|
||||||
|
// versionNavigateHandler('next');
|
||||||
}}
|
}}
|
||||||
disabled={versionIdx >= note.data.versions.length || versionIdx === null}
|
disabled={!editor.can().redo()}
|
||||||
>
|
>
|
||||||
<ArrowUturnRight className="size-4" />
|
<ArrowUturnRight className="size-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue