import * as Tabs from '@radix-ui/react-tabs'; import { SettingsTabValues } from 'librechat-data-provider'; import React, { useState, useRef, useEffect, useCallback } from 'react'; import { useRecoilState } from 'recoil'; import { Lightbulb, Cog } from 'lucide-react'; import { useOnClickOutside, useMediaQuery } from '~/hooks'; import store from '~/store'; import { cn } from '~/utils'; import ConversationModeSwitch from './ConversationModeSwitch'; import { CloudBrowserVoicesSwitch, AutomaticPlaybackSwitch, TextToSpeechSwitch, EngineTTSDropdown, CacheTTSSwitch, VoiceDropdown, PlaybackRate, } from './TTS'; import { AutoTranscribeAudioSwitch, LanguageSTTDropdown, SpeechToTextSwitch, AutoSendTextSwitch, EngineSTTDropdown, DecibelSelector, } from './STT'; import { useGetCustomConfigSpeechQuery } from 'librechat-data-provider/react-query'; function Speech() { const [confirmClear, setConfirmClear] = useState(false); const { data } = useGetCustomConfigSpeechQuery(); const isSmallScreen = useMediaQuery('(max-width: 767px)'); const [advancedMode, setAdvancedMode] = useRecoilState(store.advancedMode); const [autoTranscribeAudio, setAutoTranscribeAudio] = useRecoilState(store.autoTranscribeAudio); const [conversationMode, setConversationMode] = useRecoilState(store.conversationMode); const [speechToText, setSpeechToText] = useRecoilState(store.speechToText); const [textToSpeech, setTextToSpeech] = useRecoilState(store.textToSpeech); const [cacheTTS, setCacheTTS] = useRecoilState(store.cacheTTS); const [engineSTT, setEngineSTT] = useRecoilState(store.engineSTT); const [languageSTT, setLanguageSTT] = useRecoilState(store.languageSTT); const [decibelValue, setDecibelValue] = useRecoilState(store.decibelValue); const [autoSendText, setAutoSendText] = useRecoilState(store.autoSendText); const [engineTTS, setEngineTTS] = useRecoilState(store.engineTTS); const [voice, setVoice] = useRecoilState(store.voice); const [cloudBrowserVoices, setCloudBrowserVoices] = useRecoilState( store.cloudBrowserVoices, ); const [languageTTS, setLanguageTTS] = useRecoilState(store.languageTTS); const [automaticPlayback, setAutomaticPlayback] = useRecoilState(store.automaticPlayback); const [playbackRate, setPlaybackRate] = useRecoilState(store.playbackRate); const updateSetting = useCallback( (key, newValue) => { const settings = { conversationMode: { value: conversationMode, setFunc: setConversationMode }, advancedMode: { value: advancedMode, setFunc: setAdvancedMode }, speechToText: { value: speechToText, setFunc: setSpeechToText }, textToSpeech: { value: textToSpeech, setFunc: setTextToSpeech }, cacheTTS: { value: cacheTTS, setFunc: setCacheTTS }, engineSTT: { value: engineSTT, setFunc: setEngineSTT }, languageSTT: { value: languageSTT, setFunc: setLanguageSTT }, autoTranscribeAudio: { value: autoTranscribeAudio, setFunc: setAutoTranscribeAudio }, decibelValue: { value: decibelValue, setFunc: setDecibelValue }, autoSendText: { value: autoSendText, setFunc: setAutoSendText }, engineTTS: { value: engineTTS, setFunc: setEngineTTS }, voice: { value: voice, setFunc: setVoice }, cloudBrowserVoices: { value: cloudBrowserVoices, setFunc: setCloudBrowserVoices }, languageTTS: { value: languageTTS, setFunc: setLanguageTTS }, automaticPlayback: { value: automaticPlayback, setFunc: setAutomaticPlayback }, playbackRate: { value: playbackRate, setFunc: setPlaybackRate }, }; if (settings[key].value !== newValue || settings[key].value === newValue || !settings[key]) { return; } const setting = settings[key]; setting.setFunc(newValue); }, [ conversationMode, advancedMode, speechToText, textToSpeech, cacheTTS, engineSTT, languageSTT, autoTranscribeAudio, decibelValue, autoSendText, engineTTS, voice, cloudBrowserVoices, languageTTS, automaticPlayback, playbackRate, setConversationMode, setAdvancedMode, setSpeechToText, setTextToSpeech, setCacheTTS, setEngineSTT, setLanguageSTT, setAutoTranscribeAudio, setDecibelValue, setAutoSendText, setEngineTTS, setVoice, setCloudBrowserVoices, setLanguageTTS, setAutomaticPlayback, setPlaybackRate, ], ); useEffect(() => { if (data) { Object.entries(data).forEach(([key, value]) => { updateSetting(key, value); }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [data]); const contentRef = useRef(null); useOnClickOutside(contentRef, () => confirmClear && setConfirmClear(false), []); return (
setAdvancedMode(false)} className={cn( 'group m-1 flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm text-black transition-all duration-200 ease-in-out radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600', isSmallScreen ? 'flex-row items-center justify-center text-sm text-gray-700 radix-state-active:bg-gray-100 radix-state-active:text-black dark:text-gray-300 dark:radix-state-active:text-white' : 'bg-white radix-state-active:bg-gray-100 dark:bg-gray-700', 'w-full', )} value="simple" style={{ userSelect: 'none' }} > Simple setAdvancedMode(true)} className={cn( 'group m-1 flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm text-black transition-all duration-200 ease-in-out radix-state-active:bg-white radix-state-active:text-black dark:text-white dark:radix-state-active:bg-gray-600', isSmallScreen ? 'flex-row items-center justify-center text-sm text-gray-700 radix-state-active:bg-gray-100 radix-state-active:text-black dark:text-gray-300 dark:radix-state-active:text-white' : 'bg-white radix-state-active:bg-gray-100 dark:bg-gray-700', 'w-full', )} value="advanced" style={{ userSelect: 'none' }} > Advanced
{autoTranscribeAudio && (
)}
{engineTTS === 'browser' && (
)}
); } export default React.memo(Speech);