feat: add OpenRouter dictation and speech recognition settings
This commit is contained in:
@@ -703,7 +703,8 @@ export function AgentSection({
|
||||
<button type="button" data-testid={`slot-fast-${slot}`} aria-label={t("{v0} fast mode", { v0: t(SLOT_META[slot].label) })}
|
||||
aria-pressed={provider === "chatgpt" && override?.fastMode === true} disabled={provider !== "chatgpt" || !chosen || savingModels}
|
||||
onClick={() => { setOpenMenu(null); void updateModels(composeSlotFast(models, slot, !override?.fastMode)); }}
|
||||
className={cn("flex h-7 items-center gap-1 rounded-sm border border-border px-1.5 font-ui text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40",
|
||||
className={cn("flex h-7 items-center gap-1 rounded-sm border border-border px-1.5 font-ui text-xs transition-colors disabled:opacity-40",
|
||||
provider === "chatgpt" && chosen ? "disabled:cursor-default" : "disabled:cursor-not-allowed",
|
||||
provider === "chatgpt" && override?.fastMode ? "bg-muted text-foreground" : "bg-background text-muted-foreground hover:border-muted-foreground/50")}>
|
||||
<ZapIcon className="size-3.5" aria-hidden /><span>{t("Fast")}</span>
|
||||
</button>
|
||||
|
||||
@@ -335,6 +335,7 @@ export function KeysSection({
|
||||
/** The paste editor for a freshly chosen Add-provider target. */
|
||||
const [adding, setAdding] = useState<ProviderKeyInfo | null>(null);
|
||||
const [addError, setAddError] = useState<string | null>(null);
|
||||
const followedProvider = useRef<string | null>(null);
|
||||
|
||||
// Escape folds any open editor (unfolded row or the Add-provider paste)
|
||||
// WITHOUT closing the dialog. Claimed at document CAPTURE phase — the
|
||||
@@ -372,7 +373,17 @@ export function KeysSection({
|
||||
// named row; a fresh mount seeds it via the useState initializer.
|
||||
useEffect(() => {
|
||||
if (initialRow !== undefined) setOpenRow(initialRow);
|
||||
followedProvider.current = null;
|
||||
}, [initialRow]);
|
||||
// A missing model-provider key has no table row yet. Deep-link into the
|
||||
// existing Add-provider editor instead (for example, OpenRouter STT setup).
|
||||
useEffect(() => {
|
||||
if (!initialRow || !statuses || !providers || followedProvider.current === initialRow) return;
|
||||
followedProvider.current = initialRow;
|
||||
if (statuses.some((status) => status.id === initialRow)) return;
|
||||
const provider = providers.find((provider) => provider.envVar === initialRow);
|
||||
if (provider) { setAdding(provider); setAddError(null); setOpenRow(null); }
|
||||
}, [initialRow, statuses, providers]);
|
||||
|
||||
async function saveKey(id: string, value: string): Promise<string | null> {
|
||||
const res = await fetch(`/api/keys/${id}`, {
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
type GraphSettings,
|
||||
type UserSettings,
|
||||
} from "@/src/core";
|
||||
import { LanguageSelect, SttPreferredLanguages, useI18n } from "@/components/i18n";
|
||||
import { LanguageSelect, SttModelSelect, SttPreferredLanguages, useI18n } from "@/components/i18n";
|
||||
import { useAppearance } from "./appearance.tsx";
|
||||
import { SettingsSection, SettingsRow, Toggle } from "./controls.tsx";
|
||||
import { AgentSection } from "./agent-section.tsx";
|
||||
@@ -62,6 +62,7 @@ const SECTIONS = [
|
||||
{ id: "appearance", label: "Appearance" },
|
||||
{ id: "graph", label: "Graph" },
|
||||
{ id: "keyboard", label: "Keyboard", hint: "Stored on this device" },
|
||||
{ id: "stt", label: "Speech recognition" },
|
||||
{ id: "general", label: "General" },
|
||||
] as const;
|
||||
type SectionId = (typeof SECTIONS)[number]["id"];
|
||||
@@ -367,10 +368,6 @@ export function SettingsDialogBody({
|
||||
testid="section-general"
|
||||
>
|
||||
<SettingsRow label={t("Interface language")} testid="row-language"><LanguageSelect field="language" /></SettingsRow>
|
||||
<SettingsRow label={t("Speech recognition language")} testid="row-stt-language"><LanguageSelect field="sttLanguage" /></SettingsRow>
|
||||
<SettingsRow label={t("Preferred languages")} hint={t("Used with Multilingual. Favors selected languages; other languages may still be recognized.")} testid="row-stt-preferred-languages" className="flex-col items-start gap-2">
|
||||
<SttPreferredLanguages />
|
||||
</SettingsRow>
|
||||
<SettingsRow
|
||||
label={t("Auto-update")}
|
||||
hint={AUTO_UPDATE_HINT}
|
||||
@@ -386,6 +383,15 @@ export function SettingsDialogBody({
|
||||
</SettingsRow>
|
||||
</SettingsSection>
|
||||
)}
|
||||
{active === "stt" && (
|
||||
<SettingsSection title={t("Speech recognition")} testid="section-stt">
|
||||
<SettingsRow label={t("Speech recognition model")} testid="row-stt-model" className="flex-wrap items-start gap-y-2 [&>span:first-child]:flex-[1_1_12rem]"><SttModelSelect /></SettingsRow>
|
||||
<SettingsRow label={t("Speech recognition language")} testid="row-stt-language"><LanguageSelect field="sttLanguage" /></SettingsRow>
|
||||
<SettingsRow label={t("Preferred languages")} testid="row-stt-preferred-languages">
|
||||
<SttPreferredLanguages />
|
||||
</SettingsRow>
|
||||
</SettingsSection>
|
||||
)}
|
||||
{active === "keys" && (
|
||||
<SettingsSection title={t("Providers")} testid="section-keys">
|
||||
<KeysSection initialRow={initialRow ?? undefined} />
|
||||
|
||||
Reference in New Issue
Block a user