Route all TTS through handleTTS for streaming support

- speakChatText now uses handleTTS instead of speakTextDirect
  so embed modal TTS (intro, chat messages) respects streaming mode
- Streaming result updates blobUrl on completion so existing
  download click handlers work without changes
This commit is contained in:
russell@unturf.com 2026-01-27 08:12:47 -05:00
parent ab13f2c19f
commit 3019fd0e15
2 changed files with 13 additions and 3 deletions

View file

@ -260,7 +260,17 @@ export async function speakTextStreaming(text, voice = "alloy", rate = 0.9, mode
audio.play().catch(e => console.warn("Initial play blocked:", e.message));
// Return immediately with audio element - caller uses done promise to know when stream finishes
return { audio, blob: null, blobUrl: audioUrl, streamed: true, done };
// The result object is mutable: blob/blobUrl get updated when done resolves,
// so click handlers that read result.blobUrl later will get the downloadable URL
const result = { audio, blob: null, blobUrl: audioUrl, streamed: true, done };
done.then(({ blob, blobUrl }) => {
result.blob = blob;
// Create a downloadable blob URL (MediaSource URL can't be downloaded)
result.blobUrl = URL.createObjectURL(blob);
}).catch(() => {});
return result;
}
// Process page content using Hermes (deprecated - use extractSpokenTokens instead)

View file

@ -48,9 +48,9 @@ function parseVoiceSelection(selectedValue) {
// Import required functions dynamically to avoid circular dependencies
async function speakChatText(text, voiceSelection, speed) {
const { speakTextDirect } = await import("./tts.js");
const { handleTTS } = await import("./tts.js");
const { model, voice } = parseVoiceSelection(voiceSelection);
return await speakTextDirect(text, voice, speed, model);
return await handleTTS(text, voice, speed, model);
}
async function* sendMessage(message) {