import { Loader2Icon } from "lucide-react"; import { useState, type ReactNode } from "react"; import { toast } from "../lib/toast"; import { type AppContextValue, useAppContext } from "../app-context"; import { type VoiceInputProviderId, type WorkspaceVoiceInputSettings, resolveWorkspaceVoiceInputEnabled, } from "@opengeni/sdk"; import { cn } from "../lib/utils"; import { RowSelect } from "./settings/row-select"; import type { SelectOption } from "../../ui/select-menu"; import { SettingRow } from "../../ui/setting-row"; import { Switch } from "../../ui/switch"; export const voiceInputProviderLabels: Record = { "supergrok-subscription": "SuperGrok subscription", "codex-subscription": "Codex subscription", openai: "OpenAI · Opengeni credits", "azure-openai": "Azure OpenAI · Opengeni credits", "azure-mai": "MAI Transcribe · Opengeni credits", }; /** The Voice input row of Settings > General > New session defaults. */ export function VoiceInputPreferenceRow({ workspaceId, canManage, }: { workspaceId: string; canManage: boolean; }) { return ( ); } export function VoiceInputPreferences({ workspaceId, canManage, context, }: { workspaceId: string; canManage: boolean; context: Pick< AppContextValue, | "workspaces" | "clientConfig" | "captureWorkspaceInvocation" | "ownsWorkspaceInvocation" | "updateWorkspaceSettings" >; }) { const workspace = context.workspaces.find((candidate) => candidate.id === workspaceId); const capability = context.clientConfig.voiceInput; const [saving, setSaving] = useState(false); const enabled = resolveWorkspaceVoiceInputEnabled(workspace?.settings) ?? true; const preferences = workspace?.settings.voiceInput as WorkspaceVoiceInputSettings | undefined; const providers = capability?.providers ?? []; // Settings stay editable while any provider is ready: `capability.available` // honours the saved preference, so gating on it would lock an admin out of // changing a preference whose provider stopped being available. const available = capability?.available === true || providers.length > 0; const selected = preferences?.preferredProvider ?? null; const fallbackEnabled = preferences?.fallbackEnabled ?? true; async function save(patch: Partial, message: string) { if (!canManage || saving || !available) return; const acceptedTransition = context.captureWorkspaceInvocation(workspaceId); if (!acceptedTransition) return; setSaving(true); try { const updated = await context.updateWorkspaceSettings(workspaceId, { voiceInput: { ...preferences, enabled, ...patch }, }); if (updated && context.ownsWorkspaceInvocation(workspaceId, acceptedTransition)) { toast.success(message); } } catch { if (context.ownsWorkspaceInvocation(workspaceId, acceptedTransition)) toast.error("Couldn't update voice input"); } finally { setSaving(false); } } const reason = !available ? "Voice input isn’t available in this workspace. Connect a supported subscription or ask your operator to enable it." : !canManage ? "Only workspace admins can change this." : undefined; const options: SelectOption[] = [ { value: AUTOMATIC, label: "Automatic", description: "Uses the first provider that's available.", ...(providers[0] ? { meta: voiceInputProviderLabels[providers[0]] } : {}), }, ...(selected && !providers.includes(selected) ? [ { value: selected, label: voiceInputProviderLabels[selected], disabled: true, disabledReason: "No longer available on this deployment.", }, ] : []), ...providers.map((provider) => ({ value: provider, label: voiceInputProviderLabels[provider], })), ]; return ( void save( { enabled: next }, next ? "Voice input is on for new sessions" : "Voice input is off for new sessions", ) } /> } > {enabled && available && // Also when the saved provider is gone, so it can be changed back. (providers.length > 1 || (selected !== null && !providers.includes(selected))) ? ( <> void save( { preferredProvider: next === AUTOMATIC ? null : (next as VoiceInputProviderId), }, "Transcription provider saved", ) } /> } /> void save({ fallbackEnabled: next }, "Transcription fallback saved") } /> } /> ) : null} ); } const AUTOMATIC = "automatic"; /** Shared dense toggle row for workspace preference lists. */ export function PreferenceToggleRow(props: { icon?: ReactNode | undefined; label: string; description: string; checked: boolean; disabled?: boolean | undefined; saving?: boolean | undefined; control?: ReactNode | undefined; wrapDescription?: boolean | undefined; onToggle: () => void; }) { return (
{props.icon ? {props.icon} : null}
{props.label}

{props.description}

{props.control} {props.saving ? ( ) : null}
); }