add flag to hide select voice providers (#7406)

This commit is contained in:
Zane
2026-02-23 09:23:45 -08:00
committed by GitHub
parent 5349dd6098
commit 717d17a35f
4 changed files with 61 additions and 76 deletions
@@ -172,14 +172,12 @@ export const NavigationProvider: React.FC<NavigationProviderProps> = ({ 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);
@@ -74,7 +74,7 @@ export default function SettingsView({
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
if (event.key === 'Escape' && !event.defaultPrevented) {
onClose();
}
};
@@ -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<DictationProvider | null>(null);
const [showProviderDropdown, setShowProviderDropdown] = useState(false);
const [providerStatuses, setProviderStatuses] = useState<Record<string, DictationProviderStatus>>(
{}
);
@@ -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;
const audioConfig = await getDictationConfig();
setProviderStatuses(audioConfig.data || {});
if (
DICTATION_ALLOWED_PROVIDERS &&
loadedProvider &&
!DICTATION_ALLOWED_PROVIDERS.includes(loadedProvider)
) {
loadedProvider = null;
await upsert('voice_dictation_provider', '', false);
}
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 (
<div className="space-y-4">
<div className="flex items-center justify-between py-2 px-2 hover:bg-background-secondary rounded-lg transition-all">
@@ -102,47 +107,28 @@ export const DictationSettings = () => {
Choose how voice is converted to text
</p>
</div>
<div className="relative">
<button
onClick={handleDropdownToggle}
className="flex items-center gap-2 px-3 py-1.5 text-sm border border-border-primary rounded-md hover:border-border-primary transition-colors text-text-primary bg-background-primary"
>
<DropdownMenu onOpenChange={(open) => open && refreshStatuses()}>
<DropdownMenuTrigger className="flex items-center gap-2 px-3 py-1.5 text-sm border border-border-primary rounded-md hover:border-border-primary transition-colors text-text-primary bg-background-primary">
{getProviderLabel(provider)}
<ChevronDown className="w-4 h-4" />
</button>
{showProviderDropdown && (
<div className="absolute right-0 mt-1 w-max min-w-[250px] max-w-[350px] bg-background-primary border border-border-primary rounded-md shadow-lg z-50">
<button
onClick={() => handleProviderChange(null)}
className="w-full px-3 py-2 text-left text-sm transition-colors hover:bg-background-secondary text-text-primary whitespace-nowrap first:rounded-t-md"
>
<span className="flex items-center justify-between gap-2">
<span>Disabled</span>
{provider === null && <span></span>}
</span>
</button>
{(Object.keys(providerStatuses) as DictationProvider[]).map((p) => (
<button
key={p}
onClick={() => handleProviderChange(p)}
className="w-full px-3 py-2 text-left text-sm transition-colors hover:bg-background-secondary text-text-primary whitespace-nowrap last:rounded-b-md"
>
<span className="flex items-center justify-between gap-2">
<span>
{getProviderLabel(p)}
{!providerStatuses[p]?.configured && (
<span className="text-xs ml-1 text-text-secondary">(not configured)</span>
)}
</span>
{provider === p && <span></span>}
</span>
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-max min-w-[250px] max-w-[350px]">
<DropdownMenuRadioGroup
value={provider ?? 'disabled'}
onValueChange={handleProviderChange}
>
<DropdownMenuRadioItem value="disabled">Disabled</DropdownMenuRadioItem>
{visibleProviders.map((p) => (
<DropdownMenuRadioItem key={p} value={p}>
{getProviderLabel(p)}
{!providerStatuses[p]?.configured && (
<span className="text-xs ml-1 text-text-secondary">(not configured)</span>
)}
</DropdownMenuRadioItem>
))}
</div>
)}
</div>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
{provider && providerStatuses[provider] && (
+1
View File
@@ -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;