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 submitButtonRef = useRef<HTMLButtonElement>(null);
const textAreaRef = useRef<HTMLTextAreaElement | null>(null); const textAreaRef = useRef<HTMLTextAreaElement | null>(null);
const SpeechToText = useRecoilValue(store.speechToText); const SpeechToText = useRecoilState<boolean>(store.speechToText);
const TextToSpeech = useRecoilValue(store.textToSpeech); const TextToSpeech = useRecoilState<boolean>(store.textToSpeech);
const automaticPlayback = useRecoilValue(store.automaticPlayback); const automaticPlayback = useRecoilValue(store.automaticPlayback);
const [showStopButton, setShowStopButton] = useRecoilState(store.showStopButtonByIndex(index)); const [showStopButton, setShowStopButton] = useRecoilState(store.showStopButtonByIndex(index));

View file

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

View file

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

View file

@ -20,7 +20,7 @@ import {
AutoTranscribeAudioSwitch, AutoTranscribeAudioSwitch,
LanguageSTTDropdown, LanguageSTTDropdown,
SpeechToTextSwitch, SpeechToTextSwitch,
AutoSendTextSwitch, AutoSendTextSelector,
EngineSTTDropdown, EngineSTTDropdown,
DecibelSelector, DecibelSelector,
} from './STT'; } from './STT';
@ -220,8 +220,8 @@ function Speech() {
<DecibelSelector /> <DecibelSelector />
</div> </div>
)} )}
<div className="border-b last-of-type:border-b-0 dark:border-gray-700"> <div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<AutoSendTextSwitch /> <AutoSendTextSelector />
</div> </div>
<div className="h-px bg-black/20 bg-white/20" role="none" /> <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"> <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 { externalSpeechToText } = useGetAudioSettings();
const [speechToText] = useRecoilState<boolean>(store.speechToText); const [speechToText] = useRecoilState<boolean>(store.speechToText);
const [autoTranscribeAudio] = useRecoilState<boolean>(store.autoTranscribeAudio); const [autoTranscribeAudio] = useRecoilState<boolean>(store.autoTranscribeAudio);
const [autoSendText] = useRecoilState<boolean>(store.autoSendText); const [autoSendText] = useRecoilState(store.autoSendText);
const [text, setText] = useState<string>(''); const [text, setText] = useState<string>('');
const [isListening, setIsListening] = useState(false); const [isListening, setIsListening] = useState(false);
const [permission, setPermission] = useState(false); const [permission, setPermission] = useState(false);
@ -27,10 +27,11 @@ const useSpeechToTextExternal = (onTranscriptionComplete: (text: string) => void
const extractedText = data.text; const extractedText = data.text;
setText(extractedText); setText(extractedText);
setIsRequestBeingMade(false); setIsRequestBeingMade(false);
if (autoSendText && speechToText && extractedText.length > 0) {
if (autoSendText > -1 && speechToText && extractedText.length > 0) {
setTimeout(() => { setTimeout(() => {
onTranscriptionComplete(extractedText); onTranscriptionComplete(extractedText);
}, 3000); }, autoSendText * 1000);
} }
}, },
onError: () => { onError: () => {

View file

@ -628,7 +628,8 @@ export default {
com_nav_delete_warning: 'WARNING: This will permanently delete your account.', com_nav_delete_warning: 'WARNING: This will permanently delete your account.',
com_nav_delete_data_info: 'All your data will be deleted.', com_nav_delete_data_info: 'All your data will be deleted.',
com_nav_conversation_mode: 'Conversation Mode', 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_auto_transcribe_audio: 'Auto transcribe audio',
com_nav_db_sensitivity: 'Decibel sensitivity', com_nav_db_sensitivity: 'Decibel sensitivity',
com_nav_playback_rate: 'Audio Playback Rate', 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.', '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_image_vision: 'Kuvanäkö',
com_assistants_code_interpreter: 'Kooditulkki', com_assistants_code_interpreter: 'Kooditulkki',
com_assistants_code_interpreter_files: com_assistants_code_interpreter_files: 'Seuraavat tiedostot ovat vain Kooditulkin käytettävissä:',
'Seuraavat tiedostot ovat vain Kooditulkin käytettävissä:',
com_assistants_retrieval: 'Tiedonhaku', com_assistants_retrieval: 'Tiedonhaku',
com_assistants_search_name: 'Hae Avustajia nimen perusteella', com_assistants_search_name: 'Hae Avustajia nimen perusteella',
com_assistants_tools: 'Työkalut', 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_success: 'Toiminto luotiiin tai päivitettiin onnistuneesti',
com_assistants_update_actions_error: 'Toiminnon luomisessa tai päivittämisessä tapahtui virhe.', com_assistants_update_actions_error: 'Toiminnon luomisessa tai päivittämisessä tapahtui virhe.',
com_assistants_delete_actions_error: 'Toiminnon poistamisessa 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_name_placeholder: 'Valinnainen: Avustajan nimi',
com_assistants_instructions_placeholder: 'Avustajan käyttämät järjestelmäohjeet', com_assistants_instructions_placeholder: 'Avustajan käyttämät järjestelmäohjeet',
com_assistants_description_placeholder: 'Valinnainen: Kuvaus Avustajasta', 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_download_error: 'Virhe tiedoston lataamisesta. Tiedosto on saatettu poistaa.',
com_ui_attach_error_type: 'Päätepiste ei tue tiedostotyyppiä::', 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_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_size: 'Tiedoston koko ylittää päätepisteen rajan:',
com_ui_attach_error: com_ui_attach_error:
'Tiedosto ei voi liittää. Luo tai valitse keskustelu, tai kokeile ladata sivu uudestaan.', '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_showing: 'Näytetään',
com_ui_of: '/', com_ui_of: '/',
com_ui_entries: 'Merkinnät', 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_new_footer: 'Kaikki tekoälykeskustelut yhdessä paikassa.',
com_ui_latest_footer: 'Kaikki tekoälyt kaikille.', com_ui_latest_footer: 'Kaikki tekoälyt kaikille.',
com_ui_enter: 'Syötä', com_ui_enter: 'Syötä',
@ -155,8 +157,10 @@ export default {
com_ui_none_selected: 'Ei valintaa', com_ui_none_selected: 'Ei valintaa',
com_ui_upload_success: 'Tiedoston lataus onnistui', com_ui_upload_success: 'Tiedoston lataus onnistui',
com_ui_upload_error: 'Tiedoston lataamisessa tapahtui virhe', 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:
com_ui_upload_invalid_var: 'Virheellinen ladattava tiedosto. Tiedoston täytyy olla enintään {0} MB kokoinen kuvatiedosto', '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_cancel: 'Peruuta',
com_ui_save: 'Tallenna', com_ui_save: 'Tallenna',
com_ui_renaming_var: 'Uudelleennimetään "{0}"', com_ui_renaming_var: 'Uudelleennimetään "{0}"',
@ -256,7 +260,8 @@ export default {
com_ui_share_error: 'Keskustelulinkin jakamisessa tapahtui virhe', com_ui_share_error: 'Keskustelulinkin jakamisessa tapahtui virhe',
com_ui_share_retrieve_error: 'Jaettujen linkkien 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_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: com_ui_share_created_message:
'Jakolinkki keskusteluun on luotu. Hallinnoi aiemmin jaettuja keskusteluja milloin vain Asetusten kautta.', 'Jakolinkki keskusteluun on luotu. Hallinnoi aiemmin jaettuja keskusteluja milloin vain Asetusten kautta.',
com_ui_share_update_message: com_ui_share_update_message:
@ -291,10 +296,8 @@ export default {
'Kirjautuminen annetuilla tiedoilla ei onnistunut. Tarkista kirjautumistiedot, ja yritä uudestaan.', 'Kirjautuminen annetuilla tiedoilla ei onnistunut. Tarkista kirjautumistiedot, ja yritä uudestaan.',
com_auth_error_login_rl: com_auth_error_login_rl:
'Liian monta kirjautumisyritystä lyhyen ajan sisällä. Yritä myöhemmin uudestaan.', 'Liian monta kirjautumisyritystä lyhyen ajan sisällä. Yritä myöhemmin uudestaan.',
com_auth_error_login_ban: com_auth_error_login_ban: 'Tilisi on väliaikaisesti suljettu palvelun sääntöjen rikkomisesta.',
'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_server:
'Tapahtui sisäinen palvelinvirhe. Odota hetki, ja yritä uudestaan.',
com_auth_error_login_unverified: com_auth_error_login_unverified:
'Tiliäsi ei ole vahvistettu. Vahvistuslinkin pitäisi löytyä sähköposteistasi.', 'Tiliäsi ei ole vahvistettu. Vahvistuslinkin pitäisi löytyä sähköposteistasi.',
com_auth_no_account: 'Ei tunnusta?', com_auth_no_account: 'Ei tunnusta?',
@ -319,8 +322,7 @@ export default {
com_auth_password_not_match: 'Salasanat eivät täsmää', com_auth_password_not_match: 'Salasanat eivät täsmää',
com_auth_continue: 'Jatka', com_auth_continue: 'Jatka',
com_auth_create_account: 'Luo tili', com_auth_create_account: 'Luo tili',
com_auth_error_create: com_auth_error_create: 'Tilin rekisteröinnissä tapahtui virhe. Yritä uudestaan.',
'Tilin rekisteröinnissä tapahtui virhe. Yritä uudestaan.',
com_auth_full_name: 'Koko nimi', com_auth_full_name: 'Koko nimi',
com_auth_name_required: 'Nimi on pakollinen', com_auth_name_required: 'Nimi on pakollinen',
com_auth_name_min_length: 'Nimessä on oltava vähintään 3 merkkiä', 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_already_have_account: 'Käyttäjätilisi on jo luotu?',
com_auth_login: 'Kirjaudu', com_auth_login: 'Kirjaudu',
com_auth_registration_success_insecure: 'Rekisteröityminen onnistui.', 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_reset_password: 'Aseta uusi salasana',
com_auth_click: 'Napauta', com_auth_click: 'Napauta',
com_auth_here: 'TÄTÄ', com_auth_here: 'TÄTÄ',
@ -355,7 +358,8 @@ export default {
com_auth_email_verification_success: 'Sähköposti varmennettu', com_auth_email_verification_success: 'Sähköposti varmennettu',
com_auth_email_resent_success: 'Varmennussähköpostin uudelleenlähetys onnistui', 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_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_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_in_progress: 'Varmennetaan sähköpostia. Ole hyvä ja odota.',
com_auth_email_verification_resend_prompt: 'Sähköposti ei saapunut perille?', 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_token_count: 'Token-määrä',
com_endpoint_output: 'Tulos', com_endpoint_output: 'Tulos',
com_endpoint_context_tokens: 'Konteksti-tokenien maksimimäärä', 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: com_endpoint_google_temp:
'Korkeampi arvo = satunnaisempi; matalampi arvo = keskittyneempi ja deterministisempi. Suosittelemme, että muokkaat tätä tai Top P:tä, mutta ei molempia.', 'Korkeampi arvo = satunnaisempi; matalampi arvo = keskittyneempi ja deterministisempi. Suosittelemme, että muokkaat tätä tai Top P:tä, mutta ei molempia.',
com_endpoint_google_topp: com_endpoint_google_topp:
@ -393,7 +397,8 @@ export default {
com_endpoint_google_maxoutputtokens: com_endpoint_google_maxoutputtokens:
'Maksimimäärä tokeneillre, joita generoidaan tulokseen. Valitse pienempi arvo saadaksesi lyhyempiä vastauksia, ja suurempi arvo pitkiä vastauksia varten.', '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_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: com_endpoint_instructions_assistants_placeholder:
'Yliajaa Avustajan ohjeet. Tätä voi hyödyntää käytöksen muuttamiseen keskustelukohtaisesti.', 'Yliajaa Avustajan ohjeet. Tätä voi hyödyntää käytöksen muuttamiseen keskustelukohtaisesti.',
com_endpoint_prompt_prefix_assistants_placeholder: 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ä.', '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: 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.', '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_custom_name_placeholder: 'Anna tekoälylle mukautettu nimi',
com_endpoint_openai_prompt_prefix_placeholder: com_endpoint_openai_prompt_prefix_placeholder:
'Aseta mukautetut ohjeet Järjestelmäohjeisiin sisällytettäväksi. Oletus: tyhjä', 'Aseta mukautetut ohjeet Järjestelmäohjeisiin sisällytettäväksi. Oletus: tyhjä',
@ -488,7 +494,8 @@ export default {
com_endpoint_presets_clear_warning: com_endpoint_presets_clear_warning:
'Haluatko varmasti tyhjentää kaikki esiasetukset? Tätä toimintoa ei voi perua.', 'Haluatko varmasti tyhjentää kaikki esiasetukset? Tätä toimintoa ei voi perua.',
com_endpoint_not_implemented: 'Ei toteutettu', 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_not_available: 'Päätepistettä ei ole tarjolla',
com_endpoint_view_options: 'Katseluvaihtoehdot', com_endpoint_view_options: 'Katseluvaihtoehdot',
com_endpoint_save_convo_as_preset: 'Tallenna keskustelu esiasetukseksi', 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_cloud_platform: '(Google Cloud Platform:ista)',
com_endpoint_config_google_api_key: 'Google API Key', com_endpoint_config_google_api_key: 'Google API Key',
com_endpoint_config_google_gemini_api: '(Gemini API)', 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: '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: com_endpoint_config_key_import_json_key_invalid:
'Virheellinen palveluosoitteen JSON-avain. Toitko oikean tiedoston?', 'Virheellinen palveluosoitteen JSON-avain. Toitko oikean tiedoston?',
com_endpoint_config_key_get_edge_key: 'Saadaksisi pääsytunnuksesi Bingiä varten, kirjaudu', 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ä', '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_instructions: 'ohjeita',
com_endpoint_config_key_edge_full_key_string: 'saadaksesi täydet evästemerkkijonot.', 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_then_visit: 'sitten vieraile',
com_endpoint_config_key_chatgpt_copy_token: 'Kopioi pääsytunnus.', com_endpoint_config_key_chatgpt_copy_token: 'Kopioi pääsytunnus.',
com_endpoint_config_key_google_need_to: 'Sinun täytyy', com_endpoint_config_key_google_need_to: 'Sinun täytyy',
@ -566,8 +576,7 @@ export default {
com_show_examples: 'Näytä esimerkit', com_show_examples: 'Näytä esimerkit',
com_nav_plugin_search: 'Hae lisäosaa', com_nav_plugin_search: 'Hae lisäosaa',
com_nav_tool_search: 'Hakutyökalut', com_nav_tool_search: 'Hakutyökalut',
com_nav_plugin_auth_error: com_nav_plugin_auth_error: 'Tämän lisäosan varmentamisessa tapahtui virhe. Yritä uudestaan.',
'Tämän lisäosan varmentamisessa tapahtui virhe. Yritä uudestaan.',
com_nav_export_filename: 'Tiedoston nimi', com_nav_export_filename: 'Tiedoston nimi',
com_nav_export_filename_placeholder: 'Aseta tiedoston nimi', com_nav_export_filename_placeholder: 'Aseta tiedoston nimi',
com_nav_export_type: 'Tyyppi', 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_confirm: 'Poista käyttäjätili - oletko varma?',
com_nav_delete_account_button: 'Poista käyttäjätilini pysyvästi', 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_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_warning: 'VAROITUS: Tämä poistaa käyttäjätilisi pysyvästi.',
com_nav_delete_data_info: 'Kaikki tietosi poistetaan.', com_nav_delete_data_info: 'Kaikki tietosi poistetaan.',
com_nav_conversation_mode: 'Keskustelumoodi', com_nav_conversation_mode: 'Keskustelumoodi',
@ -661,4 +671,3 @@ export default {
com_nav_language: 'Kieli', com_nav_language: 'Kieli',
com_nav_lang_auto: 'Tunnista automaattisesti', com_nav_lang_auto: 'Tunnista automaattisesti',
}; };

View file

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

View file

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