From a70c718a0daec70ffba45d20f6ecbc9aef49e514 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Fri, 6 Mar 2026 21:46:31 +0100 Subject: [PATCH] fix: TTS reading thinking content when reasoning has code blocks (#22237) removeAllDetails() uses replaceOutsideCode() which splits content on triple-backtick code blocks before applying the details-removal regex. When thinking/reasoning content inside a
block contained code blocks (backticks survive html.escape), the
opening and
closing tags ended up in different split segments, making the regex unable to match either. This caused thinking content to leak through to TTS playback. Fix: add a direct
strip (without code-block splitting) as the first step of getMessageContentParts(), which is the TTS-specific entry point. This catches the edge case while keeping removeAllDetails safe for copy-to-clipboard (where legitimate
inside code blocks should be preserved). Fixes #22197 --- src/lib/utils/index.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 3c42370063..4a1cf2e919 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -362,9 +362,9 @@ export const generateInitialsImage = (name) => { const initials = sanitizedName.length > 0 ? sanitizedName[0] + - (sanitizedName.split(' ').length > 1 - ? sanitizedName[sanitizedName.lastIndexOf(' ') + 1] - : '') + (sanitizedName.split(' ').length > 1 + ? sanitizedName[sanitizedName.lastIndexOf(' ') + 1] + : '') : ''; ctx.fillText(initials.toUpperCase(), canvas.width / 2, canvas.height / 2); @@ -520,10 +520,10 @@ export const compareVersion = (latest, current) => { return current === '0.0.0' ? false : current.localeCompare(latest, undefined, { - numeric: true, - sensitivity: 'case', - caseFirst: 'upper' - }) < 0; + numeric: true, + sensitivity: 'case', + caseFirst: 'upper' + }) < 0; }; export const extractCurlyBraceWords = (text) => { @@ -956,6 +956,16 @@ export const extractSentencesForAudio = (text: string) => { }; export const getMessageContentParts = (content: string, splitOn: string = 'punctuation') => { + // Strip
blocks directly on the full string before any + // code-block-aware processing. removeAllDetails (which callers use) + // applies the regex via replaceOutsideCode, which splits on triple- + // backtick code fences first. If a
block contains code + // fences (e.g. reasoning with code examples), the opening and + // closing tags land in separate segments and the regex fails, + // leaking thinking content into TTS. Applying the strip here on + // the full string catches those cases. (Fixes #22197) + content = content.replace(/]*>[\s\S]*?<\/details>/gi, ''); + const messageContentParts: string[] = []; switch (splitOn) {