diff --git a/src/lib/components/chat/Messages/Markdown.svelte b/src/lib/components/chat/Messages/Markdown.svelte index 5b699578ed..36a2ed5400 100644 --- a/src/lib/components/chat/Messages/Markdown.svelte +++ b/src/lib/components/chat/Messages/Markdown.svelte @@ -63,6 +63,10 @@ let lastContent = ''; let lastParsedContent = ''; + // Incremental lexing state: cache stable prefix tokens during streaming + let cachedStablePrefix = ''; + let cachedStableTokens = []; + const parseTokens = () => { if (content === lastContent) return; lastContent = content; @@ -71,7 +75,35 @@ if (processed === lastParsedContent) return; lastParsedContent = processed; - tokens = marked.lexer(processed); + // When done or for short content, do a full lex for correctness + if (done || processed.length < 2000) { + cachedStablePrefix = ''; + cachedStableTokens = []; + tokens = marked.lexer(processed); + return; + } + + // Incremental lexing: find the last stable boundary (double newline) + // in the PREVIOUS content. Everything before it won't change as + // streaming only appends to the end. + const boundary = processed.lastIndexOf('\n\n'); + if (boundary <= 0) { + tokens = marked.lexer(processed); + return; + } + + const stablePrefix = processed.slice(0, boundary + 2); + + // If the stable prefix changed, re-lex everything + if (stablePrefix !== cachedStablePrefix) { + cachedStablePrefix = stablePrefix; + cachedStableTokens = marked.lexer(stablePrefix); + } + + // Only lex the volatile suffix (current paragraph being streamed) + const volatileSuffix = processed.slice(boundary + 2); + const suffixTokens = volatileSuffix ? marked.lexer(volatileSuffix) : []; + tokens = [...cachedStableTokens, ...suffixTokens]; }; const updateHandler = (content) => {