Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 24 additions & 45 deletions ui/desktop/src/components/settings/dictation/DictationSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export const DictationSettings = () => {
);
const [apiKey, setApiKey] = useState('');
const [isEditingKey, setIsEditingKey] = useState(false);
const [keyValidationError, setKeyValidationError] = useState('');
const { read, upsert, remove } = useConfig();

useEffect(() => {
Expand Down Expand Up @@ -59,49 +58,33 @@ export const DictationSettings = () => {
if (!providerConfig || providerConfig.uses_provider_config) return;

const trimmedKey = apiKey.trim();
if (!trimmedKey) {
setKeyValidationError('API key is required');
return;
}
if (!trimmedKey) return;
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clicking “Save” with an empty/whitespace API key currently just returns with no UI feedback, which makes the button appear broken; disable the Save button until a non-empty key is entered or reintroduce a small inline validation message.

Suggested change
if (!trimmedKey) return;
if (!trimmedKey) {
window.alert('Please enter a non-empty API key before saving.');
return;
}

Copilot uses AI. Check for mistakes.

try {
const keyName = providerConfig.config_key!;
await upsert(keyName, trimmedKey, true);
setApiKey('');
setKeyValidationError('');
setIsEditingKey(false);
const keyName = providerConfig.config_key!;
await upsert(keyName, trimmedKey, true);
setApiKey('');
setIsEditingKey(false);

const audioConfig = await getDictationConfig();
setProviderStatuses(audioConfig.data || {});
} catch (error) {
console.error('Error saving API key:', error);
setKeyValidationError('Failed to save API key');
}
const audioConfig = await getDictationConfig();
setProviderStatuses(audioConfig.data || {});
Comment on lines +63 to +69
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleSaveKey no longer catches failures from upsert/getDictationConfig, so a network/backend error will surface as an unhandled promise rejection and the user gets no feedback. Please restore error handling (e.g., try/catch with a toast or inline error) and keep the UI in a sensible state on failure.

Copilot uses AI. Check for mistakes.
};

const handleRemoveKey = async () => {
if (!provider) return;
const providerConfig = providerStatuses[provider];
if (!providerConfig || providerConfig.uses_provider_config) return;

try {
const keyName = providerConfig.config_key!;
await remove(keyName, true);
setApiKey('');
setKeyValidationError('');
setIsEditingKey(false);
const keyName = providerConfig.config_key!;
await remove(keyName, true);
setApiKey('');
setIsEditingKey(false);

const audioConfig = await getDictationConfig();
setProviderStatuses(audioConfig.data || {});
} catch (error) {
console.error('Error removing API key:', error);
setKeyValidationError('Failed to remove API key');
}
const audioConfig = await getDictationConfig();
setProviderStatuses(audioConfig.data || {});
Comment on lines +77 to +83
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleRemoveKey now has no error handling around remove/getDictationConfig, so failures will result in an unhandled promise rejection and no user-visible error. Please wrap this in try/catch and surface a failure state (toast/inline) without leaving the UI ambiguous.

Copilot uses AI. Check for mistakes.
};

const handleCancelEdit = () => {
setApiKey('');
setKeyValidationError('');
setIsEditingKey(false);
};

Expand Down Expand Up @@ -193,37 +176,33 @@ export const DictationSettings = () => {
</div>

{!isEditingKey ? (
<Button variant="outline" size="sm" onClick={() => setIsEditingKey(true)}>
{providerStatuses[provider]?.configured ? 'Update API Key' : 'Add API Key'}
</Button>
<div className="flex gap-2 flex-wrap">
<Button variant="outline" size="sm" onClick={() => setIsEditingKey(true)}>
{providerStatuses[provider]?.configured ? 'Update API Key' : 'Add API Key'}
</Button>
{providerStatuses[provider]?.configured && (
<Button variant="destructive" size="sm" onClick={handleRemoveKey}>
Remove API Key
</Button>
)}
</div>
) : (
<div className="space-y-2">
<Input
type="password"
value={apiKey}
onChange={(e) => {
setApiKey(e.target.value);
if (keyValidationError) setKeyValidationError('');
}}
onChange={(e) => setApiKey(e.target.value)}
placeholder="Enter your API key"
className="max-w-md"
autoFocus
/>
{keyValidationError && (
<p className="text-xs text-red-600 mt-1">{keyValidationError}</p>
)}
<div className="flex gap-2">
<Button size="sm" onClick={handleSaveKey}>
Save
</Button>
<Button variant="outline" size="sm" onClick={handleCancelEdit}>
Cancel
</Button>
{providerStatuses[provider]?.configured && (
<Button variant="destructive" size="sm" onClick={handleRemoveKey}>
Remove
</Button>
)}
</div>
</div>
)}
Expand Down
Loading