From a08122663e4e805fe3d326ca5d8831735215df61 Mon Sep 17 00:00:00 2001 From: Shibo Lyu Date: Sat, 12 Apr 2025 02:48:45 +0800 Subject: [PATCH] refactor: Message input now use ProseMirror directly Remove Delta/Typewriter-editor dependency and implement ProseMirror-based rich text conversion. Add methods to access the editor view and improve the architecture of text input components. --- src/lib/components/RichTextInput.svelte | 10 +- .../RichTextInput/ClientInput.svelte | 20 ++-- src/lib/richText.ts | 107 +++++++----------- .../chats/[server]/[chatId]/ChatInput.svelte | 32 +++--- src/routes/(internal)/_rich-text/+page.svelte | 6 +- 5 files changed, 87 insertions(+), 88 deletions(-) diff --git a/src/lib/components/RichTextInput.svelte b/src/lib/components/RichTextInput.svelte index 6417f63..06d5a37 100644 --- a/src/lib/components/RichTextInput.svelte +++ b/src/lib/components/RichTextInput.svelte @@ -3,6 +3,9 @@ import InputFrame from '$lib/components/InputFrame.svelte'; import type { Props as ClientInputProps } from './RichTextInput/ClientInput.svelte'; import { tw } from '$lib/tw'; + import type { EditorView } from 'prosemirror-view'; + import type { Component } from 'svelte'; + import ClientInput from './RichTextInput/ClientInput.svelte'; interface Props extends ClientInputProps { class?: string; @@ -15,6 +18,11 @@ const { default: ClientInput } = await import('./RichTextInput/ClientInput.svelte'); return ClientInput; }; + + let clientInput: ReturnType | null = $state(null); + export function getEditorView(): EditorView | null { + return clientInput?.getEditorView() ?? null; + } @@ -27,7 +35,7 @@ {/if} {:then ClientInput} - + {@render children?.()} {/await} diff --git a/src/lib/components/RichTextInput/ClientInput.svelte b/src/lib/components/RichTextInput/ClientInput.svelte index ccdb386..c36135b 100644 --- a/src/lib/components/RichTextInput/ClientInput.svelte +++ b/src/lib/components/RichTextInput/ClientInput.svelte @@ -13,6 +13,7 @@ const { onDocChange, placeholder = '', children, ...stateConfiguration }: Props = $props(); let domEl: HTMLDivElement; + let editorView: EditorView; let isEmpty = $state(!children); @@ -21,26 +22,29 @@ domEl.replaceChildren(); onDocChange?.(initialDoc); - let state = createProseMirrorEditorState({ initialDoc, ...stateConfiguration }); - let view = new EditorView( + const state = createProseMirrorEditorState({ initialDoc, ...stateConfiguration }); + editorView = new EditorView( { mount: domEl }, { state, dispatchTransaction: (tr) => { - state = state.apply(tr); - view.updateState(state); - onDocChange?.(state.doc); + const newState = state.apply(tr); + editorView.updateState(newState); + onDocChange?.(newState.doc); - const doc = state.doc; - isEmpty = doc.textContent.length === 0; + isEmpty = newState.doc.textContent.length === 0; } } ); return () => { - view.destroy(); + editorView.destroy(); }; }); + + export function getEditorView(): EditorView | null { + return editorView; + }
void; } = $props(); - let editor: Editor | undefined = $state(); - let delta: Delta | undefined = $state(); - let plainText: string = $state(''); let form: HTMLFormElement | null = $state(null); - function onKeyboardSubmit() { - editor?.select(null); + let doc: Node | null = $state(null); + let input: ReturnType; + + function onKeyboardSubmit(newDoc: Node) { + doc = newDoc; form?.requestSubmit(); } async function submit(event: SubmitEvent) { event.preventDefault(); - if (plainText.trim() === '' || !delta) return; + if (!doc || doc.textContent.trim() === '') return; - const brt = deltaToBlahRichText(delta); + const brt = proseMirrorDocToBlahRichText(doc); onSendMessage(brt); - plainText = ''; + const view = input.getEditorView(); + if (view) { + const tr = view.state.tr; + tr.delete(0, view.state.doc.content.size); + view.dispatch(tr); + } } @@ -56,13 +62,13 @@ Attach (doc = newDoc)} />