diff --git a/ui/desktop/src/components/Layout/NavigationContext.tsx b/ui/desktop/src/components/Layout/NavigationContext.tsx index e0f472fd95c8..5f714fb09520 100644 --- a/ui/desktop/src/components/Layout/NavigationContext.tsx +++ b/ui/desktop/src/components/Layout/NavigationContext.tsx @@ -172,14 +172,12 @@ export const NavigationProvider: React.FC = ({ children }, [setIsNavExpanded]); useEffect(() => { - const handleModeChange = (e: Event) => - setNavigationModeState((e as CustomEvent).detail.mode); + const handleModeChange = (e: Event) => setNavigationModeState((e as CustomEvent).detail.mode); const handleStyleChange = (e: Event) => setNavigationStyleState((e as CustomEvent).detail.style); const handlePositionChange = (e: Event) => setNavigationPositionState((e as CustomEvent).detail.position); - const handlePrefsChange = (e: Event) => - setPreferences((e as CustomEvent).detail); + const handlePrefsChange = (e: Event) => setPreferences((e as CustomEvent).detail); window.addEventListener('navigation-mode-changed', handleModeChange); window.addEventListener('navigation-style-changed', handleStyleChange); diff --git a/ui/desktop/src/components/settings/SettingsView.tsx b/ui/desktop/src/components/settings/SettingsView.tsx index 92d2024ceb49..f67e362fd84c 100644 --- a/ui/desktop/src/components/settings/SettingsView.tsx +++ b/ui/desktop/src/components/settings/SettingsView.tsx @@ -74,7 +74,7 @@ export default function SettingsView({ useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === 'Escape') { + if (event.key === 'Escape' && !event.defaultPrevented) { onClose(); } }; diff --git a/ui/desktop/src/components/settings/dictation/DictationSettings.tsx b/ui/desktop/src/components/settings/dictation/DictationSettings.tsx index 848346c38d3c..07a3a5e84e19 100644 --- a/ui/desktop/src/components/settings/dictation/DictationSettings.tsx +++ b/ui/desktop/src/components/settings/dictation/DictationSettings.tsx @@ -6,10 +6,17 @@ import { Input } from '../../ui/input'; import { Button } from '../../ui/button'; import { trackSettingToggled } from '../../../utils/analytics'; import { LocalModelManager } from './LocalModelManager'; +import { DICTATION_ALLOWED_PROVIDERS } from '../../../updates'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuTrigger, +} from '../../ui/dropdown-menu'; export const DictationSettings = () => { const [provider, setProvider] = useState(null); - const [showProviderDropdown, setShowProviderDropdown] = useState(false); const [providerStatuses, setProviderStatuses] = useState>( {} ); @@ -17,41 +24,39 @@ export const DictationSettings = () => { const [isEditingKey, setIsEditingKey] = useState(false); const { read, upsert, remove } = useConfig(); + const refreshStatuses = async () => { + const audioConfig = await getDictationConfig(); + setProviderStatuses(audioConfig.data || {}); + }; + useEffect(() => { const loadSettings = async () => { const providerValue = await read('voice_dictation_provider', false); - const loadedProvider: DictationProvider | null = (providerValue as DictationProvider) || null; - setProvider(loadedProvider); + let loadedProvider: DictationProvider | null = (providerValue as DictationProvider) || null; + + if ( + DICTATION_ALLOWED_PROVIDERS && + loadedProvider && + !DICTATION_ALLOWED_PROVIDERS.includes(loadedProvider) + ) { + loadedProvider = null; + await upsert('voice_dictation_provider', '', false); + } - const audioConfig = await getDictationConfig(); - setProviderStatuses(audioConfig.data || {}); + setProvider(loadedProvider); + await refreshStatuses(); }; loadSettings(); - }, [read]); + }, [read, upsert]); - const saveProvider = async (newProvider: DictationProvider | null) => { - console.log('Saving dictation provider to backend config:', newProvider); + const handleProviderChange = (value: string) => { + const newProvider = value === 'disabled' ? null : (value as DictationProvider); setProvider(newProvider); - await upsert('voice_dictation_provider', newProvider || '', false); + upsert('voice_dictation_provider', newProvider || '', false); trackSettingToggled('voice_dictation', newProvider !== null); }; - const handleProviderChange = (newProvider: DictationProvider | null) => { - saveProvider(newProvider); - setShowProviderDropdown(false); - }; - - const handleDropdownToggle = async () => { - const newShowState = !showProviderDropdown; - setShowProviderDropdown(newShowState); - - if (newShowState) { - const audioConfig = await getDictationConfig(); - setProviderStatuses(audioConfig.data || {}); - } - }; - const handleSaveKey = async () => { if (!provider) return; const providerConfig = providerStatuses[provider]; @@ -64,9 +69,7 @@ export const DictationSettings = () => { await upsert(keyName, trimmedKey, true); setApiKey(''); setIsEditingKey(false); - - const audioConfig = await getDictationConfig(); - setProviderStatuses(audioConfig.data || {}); + await refreshStatuses(); }; const handleRemoveKey = async () => { @@ -78,9 +81,7 @@ export const DictationSettings = () => { await remove(keyName, true); setApiKey(''); setIsEditingKey(false); - - const audioConfig = await getDictationConfig(); - setProviderStatuses(audioConfig.data || {}); + await refreshStatuses(); }; const handleCancelEdit = () => { @@ -88,11 +89,15 @@ export const DictationSettings = () => { setIsEditingKey(false); }; - const getProviderLabel = (provider: DictationProvider | null): string => { - if (!provider) return 'Disabled'; - return provider.charAt(0).toUpperCase() + provider.slice(1); + const getProviderLabel = (p: DictationProvider | null): string => { + if (!p) return 'Disabled'; + return p.charAt(0).toUpperCase() + p.slice(1); }; + const visibleProviders = (Object.keys(providerStatuses) as DictationProvider[]).filter( + (p) => !DICTATION_ALLOWED_PROVIDERS || DICTATION_ALLOWED_PROVIDERS.includes(p) + ); + return (
@@ -102,47 +107,28 @@ export const DictationSettings = () => { Choose how voice is converted to text

-
- - - {showProviderDropdown && ( -
- - - {(Object.keys(providerStatuses) as DictationProvider[]).map((p) => ( - + + + + Disabled + {visibleProviders.map((p) => ( + + {getProviderLabel(p)} + {!providerStatuses[p]?.configured && ( + (not configured) + )} + ))} -
- )} -
+ + +
{provider && providerStatuses[provider] && ( diff --git a/ui/desktop/src/updates.ts b/ui/desktop/src/updates.ts index 14e49fc10a07..88f257c8e301 100644 --- a/ui/desktop/src/updates.ts +++ b/ui/desktop/src/updates.ts @@ -3,3 +3,4 @@ export const COST_TRACKING_ENABLED = true; export const ANNOUNCEMENTS_ENABLED = false; export const CONFIGURATION_ENABLED = true; export const TELEMETRY_UI_ENABLED = true; +export const DICTATION_ALLOWED_PROVIDERS: string[] | null = null;