Skip to content

Commit f021e9f

Browse files
committed
fix(settings): reasoning depth UI now reflects the picked value
Two bugs in one small control: 1. The <select> was bound directly to the `value` prop from the parent, and the parent never knew a save happened (onUpdated was a no-op stub), so after clicking a new level the dropdown snapped right back to the old one. 2. Even if the parent HAD re-fetched, we'd still show a flash of stale state during the IPC round-trip — not great. Fix: local controlled state with optimistic update. The select tracks its own `current` initialized from the prop and kept in sync via useEffect. On change we set current immediately, kick off the IPC, and roll back only if the save fails AND no newer pick is in flight. The refreshed row is now actually bubbled to ProvidersList via a new onRowChanged callback so the canonical rows list stays fresh. - Settings.tsx: ReasoningDepthSelector uses setState + saveSeq guard - ProviderCard gains onRowChanged prop, wired through from ProvidersList
1 parent a5b737f commit f021e9f

1 file changed

Lines changed: 22 additions & 8 deletions

File tree

apps/desktop/src/renderer/src/components/Settings.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -253,11 +253,13 @@ function ProviderCard({
253253
config,
254254
onDelete,
255255
onActivate,
256+
onRowChanged,
256257
}: {
257258
row: ProviderRow;
258259
config: OnboardingState | null;
259260
onDelete: (p: string) => void;
260261
onActivate: (p: string) => void;
262+
onRowChanged: (row: ProviderRow) => void;
261263
}) {
262264
const t = useT();
263265
const pushToast = useCodesignStore((s) => s.pushToast);
@@ -359,12 +361,7 @@ function ProviderCard({
359361
<ReasoningDepthSelector
360362
provider={row.provider}
361363
value={row.reasoningLevel}
362-
onUpdated={(nextRow) => {
363-
// Fire-and-forget refresh of parent rows list is handled by the
364-
// callback set in ProvidersList; this component just needs to
365-
// let the parent know a write succeeded.
366-
void nextRow;
367-
}}
364+
onUpdated={onRowChanged}
368365
/>
369366
)}
370367
</div>
@@ -470,9 +467,21 @@ function ReasoningDepthSelector({
470467
const t = useT();
471468
const pushToast = useCodesignStore((s) => s.pushToast);
472469
const [saving, setSaving] = useState(false);
470+
// Controlled local state — optimistic so the dropdown reflects the user's
471+
// choice immediately, before the IPC round-trip resolves. Without this,
472+
// the <select> re-renders from the stale `value` prop and snaps back to
473+
// the previous level the instant the user picks a new one.
474+
const [current, setCurrent] = useState<ReasoningOption>(value ?? '');
475+
useEffect(() => {
476+
setCurrent(value ?? '');
477+
}, [value]);
478+
const saveSeq = useRef(0);
473479

474480
async function handleChange(next: ReasoningOption) {
475481
if (!window.codesign?.config?.updateProvider) return;
482+
const prev = current;
483+
const seq = ++saveSeq.current;
484+
setCurrent(next); // optimistic
476485
setSaving(true);
477486
try {
478487
// '' means "clear the per-provider override and fall back to the
@@ -486,17 +495,19 @@ function ReasoningDepthSelector({
486495
if (row) onUpdated(row);
487496
}
488497
} catch (err) {
498+
// Roll back the optimistic update only if this is still the latest
499+
// in-flight save — otherwise a newer pick is about to land.
500+
if (seq === saveSeq.current) setCurrent(prev);
489501
pushToast({
490502
variant: 'error',
491503
title: t('settings.providers.toast.reasoningSaveFailed'),
492504
description: err instanceof Error ? err.message : t('settings.common.unknownError'),
493505
});
494506
} finally {
495-
setSaving(false);
507+
if (seq === saveSeq.current) setSaving(false);
496508
}
497509
}
498510

499-
const current: ReasoningOption = value ?? '';
500511
const options: Array<{ value: ReasoningOption; label: string }> = [
501512
{ value: '', label: t('settings.providers.reasoning.default') },
502513
{ value: 'minimal', label: t('settings.providers.reasoning.minimal') },
@@ -799,6 +810,9 @@ function ModelsTab() {
799810
config={config}
800811
onDelete={handleDelete}
801812
onActivate={handleActivate}
813+
onRowChanged={(next) =>
814+
setRows((prev) => prev.map((r) => (r.provider === next.provider ? next : r)))
815+
}
802816
/>
803817
))}
804818
</div>

0 commit comments

Comments
 (0)