mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-05 02:41:34 +00:00
perf: stop the message input re-serializing its whole content three times per keystroke
After pasting a large text into the chat input (the same editor backs channel messages and the expanded input), every keystroke and every cursor move froze the tab. Each editor transaction converted the entire content to HTML, JSON and Markdown, and the value sync then did it twice more, right after the transaction and again on the next animation frame, only to compare the result with the value the editor had just emitted. The editor now serializes only when the document or the rich text mode changed, and the value sync skips the value the editor itself just emitted. Everything the editor emits is unchanged: paste, typing, cursor moves, drafts, prompt commands, the expand modal, channels, rich text off and the sent messages were compared byte for byte against dev. After pasting N lines into the chat input (production build, headless Chromium): | Lines | Keystroke | Arrow key | Paste | | --- | --- | --- | --- | | 4,000 | 383 ms → 150 ms | 243 ms → 33 ms | 0.50 s → 0.26 s | | 8,000 | 1.2 s → 0.58 s | 0.77 s → 33 ms | 2.0 s → 0.75 s | | 16,000 | 6.5 s → 2.0 s | 4.1 s → 33 ms | 7.1 s → 2.6 s | The cost that remains grows with the square of the text length inside turndown's own output joining. That is fixed upstream in mixmark-io/turndown#536; with it, a keystroke at 16,000 lines takes 0.14 s. Refs #12087
This commit is contained in:
parent
fdae17f8a6
commit
9196e2d89e
1 changed files with 34 additions and 25 deletions
|
|
@ -353,6 +353,8 @@
|
|||
let htmlValue = '';
|
||||
let jsonValue = '';
|
||||
let mdValue = '';
|
||||
let lastSerializedDoc = null;
|
||||
let lastSerializedRichText = null;
|
||||
|
||||
let provider: SocketIOCollaborationProvider | null = null;
|
||||
|
||||
|
|
@ -965,30 +967,34 @@
|
|||
});
|
||||
}
|
||||
|
||||
htmlValue = editor.getHTML();
|
||||
jsonValue = editor.getJSON();
|
||||
if (editor.state.doc !== lastSerializedDoc || richText !== lastSerializedRichText) {
|
||||
lastSerializedDoc = editor.state.doc;
|
||||
lastSerializedRichText = richText;
|
||||
htmlValue = editor.getHTML();
|
||||
jsonValue = editor.getJSON();
|
||||
|
||||
if (richText) {
|
||||
mdValue = turndownService
|
||||
.turndown(
|
||||
htmlValue
|
||||
.replace(/<p><\/p>/g, '<br/>')
|
||||
.replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
|
||||
)
|
||||
.replace(/\u00a0/g, ' ');
|
||||
} else {
|
||||
mdValue = turndownService
|
||||
.turndown(
|
||||
htmlValue
|
||||
// Replace empty paragraphs with line breaks
|
||||
.replace(/<p><\/p>/g, '<br/>')
|
||||
// Replace multiple spaces with non-breaking spaces
|
||||
.replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
|
||||
// Replace tabs with non-breaking spaces (preserve indentation)
|
||||
.replace(/\t/g, '\u00a0\u00a0\u00a0\u00a0') // 1 tab = 4 spaces
|
||||
)
|
||||
// Convert non-breaking spaces back to regular spaces for markdown
|
||||
.replace(/\u00a0/g, ' ');
|
||||
if (richText) {
|
||||
mdValue = turndownService
|
||||
.turndown(
|
||||
htmlValue
|
||||
.replace(/<p><\/p>/g, '<br/>')
|
||||
.replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
|
||||
)
|
||||
.replace(/\u00a0/g, ' ');
|
||||
} else {
|
||||
mdValue = turndownService
|
||||
.turndown(
|
||||
htmlValue
|
||||
// Replace empty paragraphs with line breaks
|
||||
.replace(/<p><\/p>/g, '<br/>')
|
||||
// Replace multiple spaces with non-breaking spaces
|
||||
.replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
|
||||
// Replace tabs with non-breaking spaces (preserve indentation)
|
||||
.replace(/\t/g, '\u00a0\u00a0\u00a0\u00a0') // 1 tab = 4 spaces
|
||||
)
|
||||
// Convert non-breaking spaces back to regular spaces for markdown
|
||||
.replace(/\u00a0/g, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
onChange({
|
||||
|
|
@ -1361,7 +1367,10 @@
|
|||
const onValueChange = () => {
|
||||
if (!editor) return;
|
||||
|
||||
const jsonValue = editor.getJSON();
|
||||
// Nothing to sync when value is what the editor just emitted (both start as '')
|
||||
if (value !== '' && value === jsonValue) return;
|
||||
|
||||
const editorJsonValue = editor.getJSON();
|
||||
const htmlValue = editor.getHTML();
|
||||
let mdValue = turndownService
|
||||
.turndown(
|
||||
|
|
@ -1380,7 +1389,7 @@
|
|||
}
|
||||
|
||||
if (json) {
|
||||
if (!equalEditorJSON(value, jsonValue)) {
|
||||
if (!equalEditorJSON(value, editorJsonValue)) {
|
||||
editor.commands.setContent(value);
|
||||
selectTemplate();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue