From b6acd3cc45ecab2f9a3775161de7ab483ec10af4 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Fri, 24 Jul 2026 07:40:02 +0200 Subject: [PATCH] fix: web speech STT repeating previous transcriptions and inserting text on cancel (#26793) The VoiceRecording component stays mounted (hidden) between recordings, and the web speech engine accumulated every session's transcript into the never-reset transcription variable. Each new recording therefore confirmed all previous utterances again, so the inserted text repeated once per session and previously deleted text reappeared in the input. Additionally, cancelling a recording (X button, Escape or a recognition error) called stopRecording(), which stops the SpeechRecognition instance and fires its onend handler, which unconditionally confirms the transcription. Cancelled recordings therefore still inserted the accumulated transcript. Reset the transcription at the start of each web speech session and detach the onend handler on cancel so cancelled recordings no longer confirm. Fixes #26784 --- .../chat/MessageInput/VoiceRecording.svelte | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/lib/components/chat/MessageInput/VoiceRecording.svelte b/src/lib/components/chat/MessageInput/VoiceRecording.svelte index 16368ee365..b5a882f67c 100644 --- a/src/lib/components/chat/MessageInput/VoiceRecording.svelte +++ b/src/lib/components/chat/MessageInput/VoiceRecording.svelte @@ -300,6 +300,9 @@ if (transcribe) { if ($config.audio.stt.engine === 'web' || ($settings?.audio?.stt?.engine ?? '') === 'web') { if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) { + // reset accumulated transcription from previous sessions + transcription = ''; + // Create a SpeechRecognition object speechRecognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)(); @@ -353,13 +356,21 @@ toast.error($i18n.t(`Speech recognition error: {{error}}`, { error: event.error })); onCancel(); - stopRecording(); + cancelRecording(); }; } } } }; + const cancelRecording = async () => { + if (speechRecognition) { + // detach onend so cancelling does not confirm the transcription + speechRecognition.onend = null; + } + await stopRecording(); + }; + const stopRecording = async () => { if (recording && mediaRecorder) { await mediaRecorder.stop(); @@ -411,7 +422,7 @@ const handleKeyDown = (e) => { if (e.key === 'Escape') { e.preventDefault(); - stopRecording(); + cancelRecording(); onCancel(); } }; @@ -468,7 +479,7 @@ rounded-full" on:click={async () => { - stopRecording(); + cancelRecording(); onCancel(); }} >