From a829c9713662b8b6e18684eceb4f75682f62a771 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Wed, 3 Jun 2026 16:07:21 -0400 Subject: [PATCH] fix(tts): rewrap sentence glow when innerHTML overwrites destroyed spans wrapSentencesForGlow gated only on dataset.glowWrapped='1', but that flag lives on the container element and survives innerHTML replacement. The streaming chunk handler does targetMessageElement.innerHTML = sanitized on every delta, and message_updated / edit-save both replace innerHTML too. Spans got blown away while the flag persisted, so subsequent glow attaches found the flag, skipped re-wrapping, and the highlight never appeared. Now also require an actual .tts-sentence span to exist before short- circuiting. If the wrap was destroyed, re-wrap. --- templates/chat.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/templates/chat.html b/templates/chat.html index 4c25d1c..457a41c 100644 --- a/templates/chat.html +++ b/templates/chat.html @@ -975,7 +975,13 @@ async function fetchTTSStreaming(cleanText, model, voice) { // comparing audio.currentTime to those boundaries. No Web Audio / RMS heuristics. function wrapSentencesForGlow(container) { - if (!container || container.dataset.glowWrapped === '1') return; + if (!container) return; + // Skip only if the previous wrap survives. innerHTML overwrites (streaming + // chunks, message_updated, edit/save) wipe the spans but the dataset flag + // lives on the container itself — without this spans-present check, manual + // replay after a re-render finds the flag set, skips wrapping, and the + // glow no-ops because querySelectorAll('.tts-sentence') comes back empty. + if (container.dataset.glowWrapped === '1' && container.querySelector('.tts-sentence')) return; const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, null); const nodes = []; let t;