From a91c45d68758fc454ae75a9c68a8420d7e8368e6 Mon Sep 17 00:00:00 2001 From: Ravi Kumar L Date: Tue, 9 Jun 2026 16:24:28 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=99=EF=B8=8F=20fix:=20Resolve=20Speech?= =?UTF-8?q?=20Recognition=20CJS/ESM=20Import=20Shape=20(#13631)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/hooks/Input/useSpeechToTextBrowser.ts | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/client/src/hooks/Input/useSpeechToTextBrowser.ts b/client/src/hooks/Input/useSpeechToTextBrowser.ts index 373c84d005..5053e31d54 100644 --- a/client/src/hooks/Input/useSpeechToTextBrowser.ts +++ b/client/src/hooks/Input/useSpeechToTextBrowser.ts @@ -1,12 +1,31 @@ -import { useEffect, useRef, useMemo } from 'react'; +import { useCallback, useEffect, useRef } from 'react'; import { useRecoilState } from 'recoil'; import { useToastContext } from '@librechat/client'; -import SpeechRecognition, { useSpeechRecognition } from 'react-speech-recognition'; import { useGetCustomConfigSpeechQuery } from 'librechat-data-provider/react-query'; +import SpeechRecognitionImport, { useSpeechRecognition } from 'react-speech-recognition'; import useGetAudioSettings from './useGetAudioSettings'; import { useLocalize } from '~/hooks'; import store from '~/store'; +type SpeechRecognitionController = Pick< + typeof SpeechRecognitionImport, + 'startListening' | 'stopListening' +>; +type SpeechRecognitionModule = Partial & { + default?: Partial; +}; + +const hasSpeechRecognitionController = ( + controller?: Partial, +): controller is SpeechRecognitionController => + typeof controller?.startListening === 'function' && + typeof controller.stopListening === 'function'; + +const speechRecognitionModule = SpeechRecognitionImport as SpeechRecognitionModule; +const SpeechRecognition = hasSpeechRecognitionController(speechRecognitionModule) + ? speechRecognitionModule + : speechRecognitionModule.default; + const useSpeechToTextBrowser = ( setText: (text: string) => void, onTranscriptionComplete: (text: string) => void, @@ -33,7 +52,7 @@ const useSpeechToTextBrowser = ( isMicrophoneAvailable, browserSupportsSpeechRecognition, } = useSpeechRecognition(); - const isListening = useMemo(() => listening, [listening]); + const isListening = listening; useEffect(() => { if (interimTranscript == null || interimTranscript === '') { @@ -73,7 +92,7 @@ const useSpeechToTextBrowser = ( }; }, [setText, onTranscriptionComplete, resetTranscript, finalTranscript, autoSendText]); - const toggleListening = () => { + const toggleListening = useCallback(() => { if (!browserSupportsSpeechRecognition) { showToast({ message: sttExternal @@ -92,6 +111,16 @@ const useSpeechToTextBrowser = ( return; } + if (!hasSpeechRecognitionController(SpeechRecognition)) { + showToast({ + message: sttExternal + ? localize('com_ui_speech_not_supported_use_external') + : localize('com_ui_speech_not_supported'), + status: 'error', + }); + return; + } + if (isListening === true) { SpeechRecognition.stopListening(); } else { @@ -100,7 +129,16 @@ const useSpeechToTextBrowser = ( continuous: autoTranscribeAudio, }); } - }; + }, [ + autoTranscribeAudio, + browserSupportsSpeechRecognition, + isListening, + isMicrophoneAvailable, + languageSTT, + localize, + showToast, + sttExternal, + ]); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { @@ -111,7 +149,7 @@ const useSpeechToTextBrowser = ( window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); - }, []); + }, [isBrowserSTTEnabled, toggleListening]); return { isListening,