diff --git a/templates/chat.html b/templates/chat.html index f7b58e1..210f20b 100644 --- a/templates/chat.html +++ b/templates/chat.html @@ -1254,12 +1254,23 @@ async function speakTextQueued(text, playButton, messageId) { function bindLifecycle(audio) { currentQueuedAudio = audio; + // Save the message-level click handler so we can restore it + // once this queued audio ends — it's how replay-from-cache works. + const originalClick = playButton.onclick; + const toggleThisAudio = () => { + if (audio.paused) audio.play(); + else audio.pause(); + }; audio.onplay = () => { playButton.classList.remove("tts-queued", "tts-loading"); playButton.disabled = false; playButton.textContent = "Pause"; currentAudio = audio; currentAudio.playButton = playButton; + // Click should pause/resume THIS audio while it owns playback. + // Without this, the original onclick re-runs speakText and + // restarts from the cached blob — sounds like a double-play. + playButton.onclick = toggleThisAudio; }; audio.onpause = () => { if (!audio.ended) playButton.textContent = "Play"; @@ -1269,6 +1280,7 @@ async function speakTextQueued(text, playButton, messageId) { playButton.classList.remove("tts-queued", "tts-loading"); playButton.disabled = false; playButton.textContent = "Play"; + playButton.onclick = originalClick; currentQueuedAudio = null; resolve(); }; @@ -1277,6 +1289,7 @@ async function speakTextQueued(text, playButton, messageId) { playButton.classList.remove("tts-queued", "tts-loading"); playButton.disabled = false; playButton.textContent = "Play"; + playButton.onclick = originalClick; currentQueuedAudio = null; reject(new Error("Audio playback failed")); };