Skip to content

Commit 958890f

Browse files
committed
fix: batch markdown parsing with rAF to prevent UI freezes during streaming
Replaces per-chunk marked.lexer() calls with requestAnimationFrame batching, reducing ~500 full parses per response to ~60 (one per frame). Eliminates redundant KaTeX regex work that blocked the main thread. Fixes open-webui#20878
1 parent c98e1ce commit 958890f

1 file changed

Lines changed: 18 additions & 5 deletions

File tree

src/lib/components/chat/Messages/Markdown.svelte

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script>
22
import { marked } from 'marked';
3+
import { onDestroy } from 'svelte';
34
import { replaceTokens, processResponseContent } from '$lib/utils';
45
import { user } from '$lib/stores';
56
@@ -34,6 +35,7 @@
3435
export let onTaskClick = () => {};
3536
3637
let tokens = [];
38+
let parseFrame = null;
3739
3840
const options = {
3941
throwOnError: false,
@@ -49,13 +51,24 @@
4951
extensions: [mentionExtension({ triggerChar: '@' }), mentionExtension({ triggerChar: '#' })]
5052
});
5153
52-
$: (async () => {
53-
if (content) {
54+
// Batch content updates into one parse per animation frame
55+
function scheduleTokenize(text, modelName, userName) {
56+
if (parseFrame) cancelAnimationFrame(parseFrame);
57+
parseFrame = requestAnimationFrame(() => {
58+
parseFrame = null;
5459
tokens = marked.lexer(
55-
replaceTokens(processResponseContent(content), model?.name, $user?.name)
60+
replaceTokens(processResponseContent(text), modelName, userName)
5661
);
57-
}
58-
})();
62+
});
63+
}
64+
65+
$: if (content) {
66+
scheduleTokenize(content, model?.name, $user?.name);
67+
}
68+
69+
onDestroy(() => {
70+
if (parseFrame) cancelAnimationFrame(parseFrame);
71+
});
5972
</script>
6073
6174
{#key id}

0 commit comments

Comments
 (0)