import { useEffect, useState } from "react"; import { api } from "./api"; import { t } from "./i18n"; import { X } from "./animated-icons"; import type { VoiceProviderId, VoiceSettings } from "./types"; export type { VoiceProviderId, VoiceSettings }; export function VoiceSettingsDialog({ close }: { close: () => void }) { const [settings, setSettings] = useState(null); const [enabled, setEnabled] = useState(false); const [provider, setProvider] = useState("xai"); const [modelId, setModelId] = useState(""); const [voiceId, setVoiceId] = useState(""); const [apiKey, setApiKey] = useState(""); const [clearKey, setClearKey] = useState(false); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); useEffect(() => { api("/api/voice/settings") .then((value) => { setSettings(value); setEnabled(value.enabled); setProvider(value.provider); setModelId(value.modelId); setVoiceId(value.voiceId); }) .catch((err) => setError(err instanceof Error ? err.message : t("loadFailed"))); }, []); const current = settings?.providers.find((item) => item.id === provider); const models = (settings?.provider === provider ? settings.models : null) ?? (provider === "openai" ? [{ id: "gpt-realtime", name: "GPT Realtime" }] : [{ id: "grok-voice-latest", name: "Grok Voice Latest" }, { id: "grok-voice-think-fast-2.0", name: "Grok Voice Think Fast 2.0" }]); const voices = (settings?.provider === provider ? settings.voices : null) ?? (provider === "openai" ? [{ id: "marin", name: "Marin" }, { id: "alloy", name: "Alloy" }, { id: "verse", name: "Verse" }] : [{ id: "eve", name: "Eve" }, { id: "ara", name: "Ara" }, { id: "leo", name: "Leo" }, { id: "rex", name: "Rex" }, { id: "sal", name: "Sal" }]); function pickProvider(id: VoiceProviderId) { setProvider(id); setError(""); if (id === "openai") { setModelId("gpt-realtime"); setVoiceId("marin"); return; } setModelId("grok-voice-latest"); setVoiceId("eve"); } return (
{ event.preventDefault(); setBusy(true); setError(""); try { await api("/api/voice/settings", { method: "PATCH", body: JSON.stringify({ enabled, provider, modelId: modelId.trim(), voiceId: voiceId.trim(), apiKey: clearKey ? "" : apiKey.trim() || null, clearApiKey: clearKey, }), }); close(); } catch (err) { setError(err instanceof Error ? err.message : t("settingsFailed")); } finally { setBusy(false); } }} >

{t("voiceSettings")}

{t("voiceSettingsHint")}

{t("voiceEnabledHint")}

{enabled ? <>
{t("voiceProvider")}
{(settings?.providers || [ { id: "xai" as const, name: t("providerXai"), envKeyName: "XAI_API_KEY" }, { id: "openai" as const, name: t("providerOpenai"), envKeyName: "OPENAI_API_KEY" }, ]).map((item) => ( ))}

{provider === "openai" ? t("providerOpenaiVoiceHint") : t("providerXaiVoiceHint")}

{settings?.reusesTextKey && !apiKey && !clearKey ?

{t("voiceReusesTextKey")}

: null} {settings?.envKeySet && !apiKey && !clearKey ?

{t("usingEnvKey", { name: current?.envKeyName || settings.envKeyName })}

: null} {settings?.apiKeySet ? : null} : null} {error ?
{error}
: null}
); }