feat: auto send text slider (#3312)

* feat: convert main component to float

* feat: convert the remaining components

* feat: use `recoilState` instead of `recoilValue`

* feat: replaced `AutoSendTextSwitch` to `AutoSendTextSelector`

* feat: use `autoSendText` in the `useSpeechToTextExternal` hook

* fix: `autoSendText` timeout
This commit is contained in:
Marco Beretta 2024-07-17 16:07:11 +02:00 committed by GitHub
parent d5d188eebf
commit d5782ac66c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 737 additions and 749 deletions

View file

@ -38,8 +38,8 @@ const ChatForm = ({ index = 0 }) => {
const submitButtonRef = useRef<HTMLButtonElement>(null);
const textAreaRef = useRef<HTMLTextAreaElement | null>(null);
const SpeechToText = useRecoilValue(store.speechToText);
const TextToSpeech = useRecoilValue(store.textToSpeech);
const SpeechToText = useRecoilState<boolean>(store.speechToText);
const TextToSpeech = useRecoilState<boolean>(store.textToSpeech);
const automaticPlayback = useRecoilValue(store.automaticPlayback);
const [showStopButton, setShowStopButton] = useRecoilState(store.showStopButtonByIndex(index));

View file

@ -10,15 +10,15 @@ export default function ConversationModeSwitch({
}) {
const localize = useLocalize();
const [conversationMode, setConversationMode] = useRecoilState<boolean>(store.conversationMode);
const [speechToText] = useRecoilState<boolean>(store.speechToText);
const [textToSpeech] = useRecoilState<boolean>(store.textToSpeech);
const [, setAutoSendText] = useRecoilState<boolean>(store.autoSendText);
const speechToText = useRecoilState<boolean>(store.speechToText);
const textToSpeech = useRecoilState<boolean>(store.textToSpeech);
const [, setAutoSendText] = useRecoilState(store.autoSendText);
const [, setDecibelValue] = useRecoilState(store.decibelValue);
const [, setAutoTranscribeAudio] = useRecoilState<boolean>(store.autoTranscribeAudio);
const handleCheckedChange = (value: boolean) => {
setAutoTranscribeAudio(value);
setAutoSendText(value);
setAutoSendText(3);
setDecibelValue(-45);
setConversationMode(value);
if (onCheckedChange) {

View file

@ -0,0 +1,50 @@
import React from 'react';
import { useRecoilState } from 'recoil';
import { cn, defaultTextProps, optionText } from '~/utils/';
import { Slider, InputNumber } from '~/components/ui';
import { useLocalize } from '~/hooks';
import store from '~/store';
export default function AutoSendTextSelector() {
const localize = useLocalize();
const speechToText = useRecoilState<boolean>(store.speechToText);
const [autoSendText, setAutoSendText] = useRecoilState(store.autoSendText);
return (
<div className="flex items-center justify-between">
<div className="flex items-center justify-between">
<div>{localize('com_nav_auto_send_text')}</div>
<div className="w-2" />
<small className="opacity-40">({localize('com_nav_auto_send_text_disabled')})</small>
</div>
<div className="flex items-center justify-between">
<Slider
value={[autoSendText ?? -1]}
onValueChange={(value) => setAutoSendText(value[0])}
doubleClickHandler={() => setAutoSendText(-1)}
min={-1}
max={60}
step={1}
className="ml-4 flex h-4 w-24"
disabled={!speechToText}
/>
<div className="w-2" />
<InputNumber
value={`${autoSendText} s`}
disabled={!speechToText}
onChange={(value) => setAutoSendText(value ? value[0] : 0)}
min={-1}
max={60}
className={cn(
defaultTextProps,
cn(
optionText,
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200',
),
)}
/>
</div>
</div>
);
}

View file

@ -1,35 +0,0 @@
import { useRecoilState } from 'recoil';
import { Switch } from '~/components/ui';
import { useLocalize } from '~/hooks';
import store from '~/store';
export default function AutoSendTextSwitch({
onCheckedChange,
}: {
onCheckedChange?: (value: boolean) => void;
}) {
const localize = useLocalize();
const [autoSendText, setAutoSendText] = useRecoilState<boolean>(store.autoSendText);
const [SpeechToText] = useRecoilState<boolean>(store.speechToText);
const handleCheckedChange = (value: boolean) => {
setAutoSendText(value);
if (onCheckedChange) {
onCheckedChange(value);
}
};
return (
<div className="flex items-center justify-between">
<div>{localize('com_nav_auto_send_text')}</div>
<Switch
id="AutoSendText"
checked={autoSendText}
onCheckedChange={handleCheckedChange}
className="ml-4"
data-testid="AutoSendText"
disabled={!SpeechToText}
/>
</div>
);
}

View file

@ -12,7 +12,7 @@ export default function AutoTranscribeAudioSwitch({
const [autoTranscribeAudio, setAutoTranscribeAudio] = useRecoilState<boolean>(
store.autoTranscribeAudio,
);
const [speechToText] = useRecoilState<boolean>(store.speechToText);
const speechToText = useRecoilState<boolean>(store.speechToText);
const handleCheckedChange = (value: boolean) => {
setAutoTranscribeAudio(value);

View file

@ -1,5 +1,5 @@
import React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useRecoilState } from 'recoil';
import { Slider, InputNumber } from '~/components/ui';
import { useLocalize } from '~/hooks';
import store from '~/store';
@ -7,7 +7,7 @@ import { cn, defaultTextProps, optionText } from '~/utils/';
export default function DecibelSelector() {
const localize = useLocalize();
const speechToText = useRecoilValue(store.speechToText);
const speechToText = useRecoilState<boolean>(store.speechToText);
const [decibelValue, setDecibelValue] = useRecoilState(store.decibelValue);
return (

View file

@ -1,38 +0,0 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { render, fireEvent } from 'test/layout-test-utils';
import AutoSendTextSwitch from '../AutoSendTextSwitch';
import { RecoilRoot } from 'recoil';
describe('AutoSendTextSwitch', () => {
/**
* Mock function to set the auto-send-text state.
*/
let mockSetAutoSendText: jest.Mock<void, [boolean]> | ((value: boolean) => void) | undefined;
beforeEach(() => {
mockSetAutoSendText = jest.fn();
});
it('renders correctly', () => {
const { getByTestId } = render(
<RecoilRoot>
<AutoSendTextSwitch />
</RecoilRoot>,
);
expect(getByTestId('AutoSendText')).toBeInTheDocument();
});
it('calls onCheckedChange when the switch is toggled', () => {
const { getByTestId } = render(
<RecoilRoot>
<AutoSendTextSwitch onCheckedChange={mockSetAutoSendText} />
</RecoilRoot>,
);
const switchElement = getByTestId('AutoSendText');
fireEvent.click(switchElement);
expect(mockSetAutoSendText).toHaveBeenCalledWith(true);
});
});

View file

@ -1,4 +1,4 @@
export { default as AutoSendTextSwitch } from './AutoSendTextSwitch';
export { default as AutoSendTextSelector } from './AutoSendTextSelector';
export { default as SpeechToTextSwitch } from './SpeechToTextSwitch';
export { default as EngineSTTDropdown } from './EngineSTTDropdown';
export { default as DecibelSelector } from './DecibelSelector';

View file

@ -20,7 +20,7 @@ import {
AutoTranscribeAudioSwitch,
LanguageSTTDropdown,
SpeechToTextSwitch,
AutoSendTextSwitch,
AutoSendTextSelector,
EngineSTTDropdown,
DecibelSelector,
} from './STT';
@ -220,8 +220,8 @@ function Speech() {
<DecibelSelector />
</div>
)}
<div className="border-b last-of-type:border-b-0 dark:border-gray-700">
<AutoSendTextSwitch />
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<AutoSendTextSelector />
</div>
<div className="h-px bg-black/20 bg-white/20" role="none" />
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">

View file

@ -10,7 +10,7 @@ const useSpeechToTextExternal = (onTranscriptionComplete: (text: string) => void
const { externalSpeechToText } = useGetAudioSettings();
const [speechToText] = useRecoilState<boolean>(store.speechToText);
const [autoTranscribeAudio] = useRecoilState<boolean>(store.autoTranscribeAudio);
const [autoSendText] = useRecoilState<boolean>(store.autoSendText);
const [autoSendText] = useRecoilState(store.autoSendText);
const [text, setText] = useState<string>('');
const [isListening, setIsListening] = useState(false);
const [permission, setPermission] = useState(false);
@ -27,10 +27,11 @@ const useSpeechToTextExternal = (onTranscriptionComplete: (text: string) => void
const extractedText = data.text;
setText(extractedText);
setIsRequestBeingMade(false);
if (autoSendText && speechToText && extractedText.length > 0) {
if (autoSendText > -1 && speechToText && extractedText.length > 0) {
setTimeout(() => {
onTranscriptionComplete(extractedText);
}, 3000);
}, autoSendText * 1000);
}
},
onError: () => {

View file

@ -628,7 +628,8 @@ export default {
com_nav_delete_warning: 'WARNING: This will permanently delete your account.',
com_nav_delete_data_info: 'All your data will be deleted.',
com_nav_conversation_mode: 'Conversation Mode',
com_nav_auto_send_text: 'Auto send text (after 3 sec)',
com_nav_auto_send_text: 'Auto send text',
com_nav_auto_send_text_disabled: 'set -1 to disable',
com_nav_auto_transcribe_audio: 'Auto transcribe audio',
com_nav_db_sensitivity: 'Decibel sensitivity',
com_nav_playback_rate: 'Audio Playback Rate',

View file

@ -30,8 +30,7 @@ export default {
'Avustaja täytyy ensin luoda, ja Kooditulkki tai Tiedonhaku täytyy olla päällä ja asetukset tallennettuna, ennen kuin tiedostoja voidaan ladata Tietoihin.',
com_assistants_image_vision: 'Kuvanäkö',
com_assistants_code_interpreter: 'Kooditulkki',
com_assistants_code_interpreter_files:
'Seuraavat tiedostot ovat vain Kooditulkin käytettävissä:',
com_assistants_code_interpreter_files: 'Seuraavat tiedostot ovat vain Kooditulkin käytettävissä:',
com_assistants_retrieval: 'Tiedonhaku',
com_assistants_search_name: 'Hae Avustajia nimen perusteella',
com_assistants_tools: 'Työkalut',
@ -50,7 +49,8 @@ export default {
com_assistants_update_actions_success: 'Toiminto luotiiin tai päivitettiin onnistuneesti',
com_assistants_update_actions_error: 'Toiminnon luomisessa tai päivittämisessä tapahtui virhe.',
com_assistants_delete_actions_error: 'Toiminnon poistamisessa tapahtui virhe.',
com_assistants_actions_info: 'Salli Avustajalle Tiedonhaku tai Toimintojen suorittaminen API-kutsujen kautta',
com_assistants_actions_info:
'Salli Avustajalle Tiedonhaku tai Toimintojen suorittaminen API-kutsujen kautta',
com_assistants_name_placeholder: 'Valinnainen: Avustajan nimi',
com_assistants_instructions_placeholder: 'Avustajan käyttämät järjestelmäohjeet',
com_assistants_description_placeholder: 'Valinnainen: Kuvaus Avustajasta',
@ -79,7 +79,8 @@ export default {
com_ui_download_error: 'Virhe tiedoston lataamisesta. Tiedosto on saatettu poistaa.',
com_ui_attach_error_type: 'Päätepiste ei tue tiedostotyyppiä::',
com_ui_attach_error_openai: 'Avustajan tiedostoja ei voi liittää muihin päätepisteisiin',
com_ui_attach_warn_endpoint: 'Ilman yhteensopivaa työkalua muut kuin Avustajan tiedostot voidaan jättää huomiotta.',
com_ui_attach_warn_endpoint:
'Ilman yhteensopivaa työkalua muut kuin Avustajan tiedostot voidaan jättää huomiotta.',
com_ui_attach_error_size: 'Tiedoston koko ylittää päätepisteen rajan:',
com_ui_attach_error:
'Tiedosto ei voi liittää. Luo tai valitse keskustelu, tai kokeile ladata sivu uudestaan.',
@ -147,7 +148,8 @@ export default {
com_ui_showing: 'Näytetään',
com_ui_of: '/',
com_ui_entries: 'Merkinnät',
com_ui_pay_per_call: 'Kaikki tekoälykeskustelut yhdessä paikassa. Maksa kerrasta, älä kuukaudesta.',
com_ui_pay_per_call:
'Kaikki tekoälykeskustelut yhdessä paikassa. Maksa kerrasta, älä kuukaudesta.',
com_ui_new_footer: 'Kaikki tekoälykeskustelut yhdessä paikassa.',
com_ui_latest_footer: 'Kaikki tekoälyt kaikille.',
com_ui_enter: 'Syötä',
@ -155,8 +157,10 @@ export default {
com_ui_none_selected: 'Ei valintaa',
com_ui_upload_success: 'Tiedoston lataus onnistui',
com_ui_upload_error: 'Tiedoston lataamisessa tapahtui virhe',
com_ui_upload_invalid: 'Virheellinen ladattava tiedosto. Tiedoston täytyy olla kokorajaan mahtuva kuvatiedosto',
com_ui_upload_invalid_var: 'Virheellinen ladattava tiedosto. Tiedoston täytyy olla enintään {0} MB kokoinen kuvatiedosto',
com_ui_upload_invalid:
'Virheellinen ladattava tiedosto. Tiedoston täytyy olla kokorajaan mahtuva kuvatiedosto',
com_ui_upload_invalid_var:
'Virheellinen ladattava tiedosto. Tiedoston täytyy olla enintään {0} MB kokoinen kuvatiedosto',
com_ui_cancel: 'Peruuta',
com_ui_save: 'Tallenna',
com_ui_renaming_var: 'Uudelleennimetään "{0}"',
@ -256,7 +260,8 @@ export default {
com_ui_share_error: 'Keskustelulinkin jakamisessa tapahtui virhe',
com_ui_share_retrieve_error: 'Jaettujen linkkien jakamisessa tapahtui virhe',
com_ui_share_delete_error: 'Jaetun linkin poistossa tapahtui virhe',
com_ui_share_create_message: 'Nimesi ja jakamisen jälkeen lisätäämäsi viestit pysyvät yksityisinä.',
com_ui_share_create_message:
'Nimesi ja jakamisen jälkeen lisätäämäsi viestit pysyvät yksityisinä.',
com_ui_share_created_message:
'Jakolinkki keskusteluun on luotu. Hallinnoi aiemmin jaettuja keskusteluja milloin vain Asetusten kautta.',
com_ui_share_update_message:
@ -291,10 +296,8 @@ export default {
'Kirjautuminen annetuilla tiedoilla ei onnistunut. Tarkista kirjautumistiedot, ja yritä uudestaan.',
com_auth_error_login_rl:
'Liian monta kirjautumisyritystä lyhyen ajan sisällä. Yritä myöhemmin uudestaan.',
com_auth_error_login_ban:
'Tilisi on väliaikaisesti suljettu palvelun sääntöjen rikkomisesta.',
com_auth_error_login_server:
'Tapahtui sisäinen palvelinvirhe. Odota hetki, ja yritä uudestaan.',
com_auth_error_login_ban: 'Tilisi on väliaikaisesti suljettu palvelun sääntöjen rikkomisesta.',
com_auth_error_login_server: 'Tapahtui sisäinen palvelinvirhe. Odota hetki, ja yritä uudestaan.',
com_auth_error_login_unverified:
'Tiliäsi ei ole vahvistettu. Vahvistuslinkin pitäisi löytyä sähköposteistasi.',
com_auth_no_account: 'Ei tunnusta?',
@ -319,8 +322,7 @@ export default {
com_auth_password_not_match: 'Salasanat eivät täsmää',
com_auth_continue: 'Jatka',
com_auth_create_account: 'Luo tili',
com_auth_error_create:
'Tilin rekisteröinnissä tapahtui virhe. Yritä uudestaan.',
com_auth_error_create: 'Tilin rekisteröinnissä tapahtui virhe. Yritä uudestaan.',
com_auth_full_name: 'Koko nimi',
com_auth_name_required: 'Nimi on pakollinen',
com_auth_name_min_length: 'Nimessä on oltava vähintään 3 merkkiä',
@ -332,7 +334,8 @@ export default {
com_auth_already_have_account: 'Käyttäjätilisi on jo luotu?',
com_auth_login: 'Kirjaudu',
com_auth_registration_success_insecure: 'Rekisteröityminen onnistui.',
com_auth_registration_success_generic: 'Tarkista sähköpostisi sähköpostiosoitteen vahvistamiseksi.',
com_auth_registration_success_generic:
'Tarkista sähköpostisi sähköpostiosoitteen vahvistamiseksi.',
com_auth_reset_password: 'Aseta uusi salasana',
com_auth_click: 'Napauta',
com_auth_here: 'TÄTÄ',
@ -355,7 +358,8 @@ export default {
com_auth_email_verification_success: 'Sähköposti varmennettu',
com_auth_email_resent_success: 'Varmennussähköpostin uudelleenlähetys onnistui',
com_auth_email_resent_failed: 'Varmennussähköpostin uudelleenlähetys epäonnistui',
com_auth_email_verification_failed_token_missing: 'Varmennus epäonnistui tunnisteen puuttumisen vuoksi',
com_auth_email_verification_failed_token_missing:
'Varmennus epäonnistui tunnisteen puuttumisen vuoksi',
com_auth_email_verification_invalid: 'Sähköpostin varmentaminen ei voimassa',
com_auth_email_verification_in_progress: 'Varmennetaan sähköpostia. Ole hyvä ja odota.',
com_auth_email_verification_resend_prompt: 'Sähköposti ei saapunut perille?',
@ -383,7 +387,7 @@ export default {
com_endpoint_token_count: 'Token-määrä',
com_endpoint_output: 'Tulos',
com_endpoint_context_tokens: 'Konteksti-tokenien maksimimäärä',
com_endpoint_context_info: `Kontekstia varten käytettävien tokeneiden maksimimäärä. Käytä tätä pyyntökohtaisten token-määrien hallinnointiin. Jos tätä ei määritetä, käytössä ovat järjestelmän oletusarvot perustuen tiedossa olevien mallien konteksti-ikkunoiden kokoon. Korkeamman arvon asettaminen voi aiheuttaa virheitä tai korkeamman token-hinnan.`,
com_endpoint_context_info: 'Kontekstia varten käytettävien tokeneiden maksimimäärä. Käytä tätä pyyntökohtaisten token-määrien hallinnointiin. Jos tätä ei määritetä, käytössä ovat järjestelmän oletusarvot perustuen tiedossa olevien mallien konteksti-ikkunoiden kokoon. Korkeamman arvon asettaminen voi aiheuttaa virheitä tai korkeamman token-hinnan.',
com_endpoint_google_temp:
'Korkeampi arvo = satunnaisempi; matalampi arvo = keskittyneempi ja deterministisempi. Suosittelemme, että muokkaat tätä tai Top P:tä, mutta ei molempia.',
com_endpoint_google_topp:
@ -393,7 +397,8 @@ export default {
com_endpoint_google_maxoutputtokens:
'Maksimimäärä tokeneillre, joita generoidaan tulokseen. Valitse pienempi arvo saadaksesi lyhyempiä vastauksia, ja suurempi arvo pitkiä vastauksia varten.',
com_endpoint_google_custom_name_placeholder: 'Aseta Googlelle mukautettu nimi',
com_endpoint_prompt_prefix_placeholder: 'Aseta mukautetut ohjeet tai konteksti. Jätetään huomiotta, jos tyhjä.',
com_endpoint_prompt_prefix_placeholder:
'Aseta mukautetut ohjeet tai konteksti. Jätetään huomiotta, jos tyhjä.',
com_endpoint_instructions_assistants_placeholder:
'Yliajaa Avustajan ohjeet. Tätä voi hyödyntää käytöksen muuttamiseen keskustelukohtaisesti.',
com_endpoint_prompt_prefix_assistants_placeholder:
@ -428,7 +433,8 @@ export default {
'Lähetä uudestaan kaikki aiemmin liitetyt tiedostot. Huom: tämä lisää token-kustannuksia, ja useiden tiedostojen käsittelystä kerralla voi seurata virheitä.',
com_endpoint_openai_detail:
'Kuvatarkkuus Vision-pyynnöille. "Matala" on halvempi ja nopeampi, "Korkea" on yksityiskohtaisempi ja kalliimpi, ja "Auto" valitsee näiden välillä automaattisesti kuvan koon perusteella.',
com_endpoint_openai_stop: 'Enintään 4 sekvenssiä, joiden kohdalla API lopettaa tokenien luomisen.',
com_endpoint_openai_stop:
'Enintään 4 sekvenssiä, joiden kohdalla API lopettaa tokenien luomisen.',
com_endpoint_openai_custom_name_placeholder: 'Anna tekoälylle mukautettu nimi',
com_endpoint_openai_prompt_prefix_placeholder:
'Aseta mukautetut ohjeet Järjestelmäohjeisiin sisällytettäväksi. Oletus: tyhjä',
@ -488,7 +494,8 @@ export default {
com_endpoint_presets_clear_warning:
'Haluatko varmasti tyhjentää kaikki esiasetukset? Tätä toimintoa ei voi perua.',
com_endpoint_not_implemented: 'Ei toteutettu',
com_endpoint_no_presets: 'Ei vielä esiasetuksia. Käytä Asetukset-painiketta luodaksesi esiasetuksen.',
com_endpoint_no_presets:
'Ei vielä esiasetuksia. Käytä Asetukset-painiketta luodaksesi esiasetuksen.',
com_endpoint_not_available: 'Päätepistettä ei ole tarjolla',
com_endpoint_view_options: 'Katseluvaihtoehdot',
com_endpoint_save_convo_as_preset: 'Tallenna keskustelu esiasetukseksi',
@ -513,9 +520,11 @@ export default {
com_endpoint_config_google_cloud_platform: '(Google Cloud Platform:ista)',
com_endpoint_config_google_api_key: 'Google API Key',
com_endpoint_config_google_gemini_api: '(Gemini API)',
com_endpoint_config_google_api_info: 'Saadaksesi Generative Language API -avaimesi (Gemini:a varten),',
com_endpoint_config_google_api_info:
'Saadaksesi Generative Language API -avaimesi (Gemini:a varten),',
com_endpoint_config_key_import_json_key: 'Tuo palveluosoitteen JSON-avain.',
com_endpoint_config_key_import_json_key_success: 'Palveluosoitteetn JSON-avain tuotu onnistuneesti',
com_endpoint_config_key_import_json_key_success:
'Palveluosoitteetn JSON-avain tuotu onnistuneesti',
com_endpoint_config_key_import_json_key_invalid:
'Virheellinen palveluosoitteen JSON-avain. Toitko oikean tiedoston?',
com_endpoint_config_key_get_edge_key: 'Saadaksisi pääsytunnuksesi Bingiä varten, kirjaudu',
@ -523,7 +532,8 @@ export default {
'Käytä kehitystyökaluja ja lisäosaa sivustolle kirjautuneena _U -evästeen kopioimiseen. Jos tämä ei toimi, seuraa näitä',
com_endpoint_config_key_edge_instructions: 'ohjeita',
com_endpoint_config_key_edge_full_key_string: 'saadaksesi täydet evästemerkkijonot.',
com_endpoint_config_key_chatgpt: 'Saadaksesi pääsytunnuksesi ChatGPT:n \'ilmaisversiota\' varten, kirjaudu',
com_endpoint_config_key_chatgpt:
'Saadaksesi pääsytunnuksesi ChatGPT:n \'ilmaisversiota\' varten, kirjaudu',
com_endpoint_config_key_chatgpt_then_visit: 'sitten vieraile',
com_endpoint_config_key_chatgpt_copy_token: 'Kopioi pääsytunnus.',
com_endpoint_config_key_google_need_to: 'Sinun täytyy',
@ -566,8 +576,7 @@ export default {
com_show_examples: 'Näytä esimerkit',
com_nav_plugin_search: 'Hae lisäosaa',
com_nav_tool_search: 'Hakutyökalut',
com_nav_plugin_auth_error:
'Tämän lisäosan varmentamisessa tapahtui virhe. Yritä uudestaan.',
com_nav_plugin_auth_error: 'Tämän lisäosan varmentamisessa tapahtui virhe. Yritä uudestaan.',
com_nav_export_filename: 'Tiedoston nimi',
com_nav_export_filename_placeholder: 'Aseta tiedoston nimi',
com_nav_export_type: 'Tyyppi',
@ -620,7 +629,8 @@ export default {
com_nav_delete_account_confirm: 'Poista käyttäjätili - oletko varma?',
com_nav_delete_account_button: 'Poista käyttäjätilini pysyvästi',
com_nav_delete_account_email_placeholder: 'Syötä käyttäjätilisi sähköpostiosoite',
com_nav_delete_account_confirm_placeholder: 'Jatkaaksesi syötä "DELETE" alla olevaan syötekenttään',
com_nav_delete_account_confirm_placeholder:
'Jatkaaksesi syötä "DELETE" alla olevaan syötekenttään',
com_nav_delete_warning: 'VAROITUS: Tämä poistaa käyttäjätilisi pysyvästi.',
com_nav_delete_data_info: 'Kaikki tietosi poistetaan.',
com_nav_conversation_mode: 'Keskustelumoodi',
@ -660,5 +670,4 @@ export default {
com_nav_setting_speech: 'Puhe',
com_nav_language: 'Kieli',
com_nav_lang_auto: 'Tunnista automaattisesti',
};
};

View file

@ -45,7 +45,7 @@ const localStorageAtoms = {
languageSTT: atomWithLocalStorage('languageSTT', ''),
autoTranscribeAudio: atomWithLocalStorage('autoTranscribeAudio', false),
decibelValue: atomWithLocalStorage('decibelValue', -45),
autoSendText: atomWithLocalStorage('autoSendText', false),
autoSendText: atomWithLocalStorage('autoSendText', -1),
textToSpeech: atomWithLocalStorage('textToSpeech', true),
engineTTS: atomWithLocalStorage('engineTTS', 'browser'),

View file

@ -304,7 +304,7 @@ const speechTab = z
languageSTT: z.string().optional(),
autoTranscribeAudio: z.boolean().optional(),
decibelValue: z.number().optional(),
autoSendText: z.boolean().optional(),
autoSendText: z.number().optional(),
}),
)
.optional(),