From 35f28a6e7733675813b34a3e6e2bda5907cdc393 Mon Sep 17 00:00:00 2001
From: kaitranntt
Date: Thu, 8 Jan 2026 16:56:05 -0500
Subject: [PATCH] fix(ui): add provider indicator, retry button, and optimistic
locking
- U4: add blue info box showing supported providers
- W1: add inline retry button in error alert
- W4: track lastModified, handle 409 conflict with auto-refresh
---
.../settings/hooks/use-thinking-config.ts | 27 +++++++++++++--
.../settings/sections/thinking/index.tsx | 33 +++++++++++++++++--
2 files changed, 54 insertions(+), 6 deletions(-)
diff --git a/ui/src/pages/settings/hooks/use-thinking-config.ts b/ui/src/pages/settings/hooks/use-thinking-config.ts
index b4571ff3..9c59bbb7 100644
--- a/ui/src/pages/settings/hooks/use-thinking-config.ts
+++ b/ui/src/pages/settings/hooks/use-thinking-config.ts
@@ -1,9 +1,10 @@
/**
* Thinking Config Hook
* Manages thinking budget configuration with direct API calls
+ * Includes W4 optimistic locking via lastModified timestamps
*/
-import { useCallback, useState } from 'react';
+import { useCallback, useState, useRef } from 'react';
import type { ThinkingConfig, ThinkingMode } from '../types';
const DEFAULT_THINKING_CONFIG: ThinkingConfig = {
@@ -24,6 +25,8 @@ export function useThinkingConfig() {
const [saving, setSaving] = useState(false);
const [error, setError] = useState(null);
const [success, setSuccess] = useState(false);
+ // W4: Track lastModified for optimistic locking
+ const lastModifiedRef = useRef(undefined);
const fetchConfig = useCallback(async () => {
const controller = new AbortController();
@@ -37,6 +40,8 @@ export function useThinkingConfig() {
if (!res.ok) throw new Error('Failed to load Thinking config');
const data = await res.json();
setConfig(data.config || DEFAULT_THINKING_CONFIG);
+ // W4: Store lastModified for optimistic locking
+ lastModifiedRef.current = data.lastModified;
} catch (err) {
clearTimeout(timeoutId);
if ((err as Error).name === 'AbortError') {
@@ -63,10 +68,16 @@ export function useThinkingConfig() {
setSaving(true);
setError(null);
+ // W4: Include lastModified for optimistic locking
+ const payload = {
+ ...optimisticConfig,
+ lastModified: lastModifiedRef.current,
+ };
+
const res = await fetch('/api/thinking', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(optimisticConfig),
+ body: JSON.stringify(payload),
signal: controller.signal,
});
@@ -74,11 +85,17 @@ export function useThinkingConfig() {
if (!res.ok) {
const data = await res.json();
+ // W4: Handle conflict (409) with user-friendly message
+ if (res.status === 409) {
+ throw new Error('Config changed by another session. Refreshing...');
+ }
throw new Error(data.error || 'Failed to save');
}
const data = await res.json();
setConfig(data.config);
+ // W4: Update lastModified after successful save
+ lastModifiedRef.current = data.lastModified;
setSuccess(true);
setTimeout(() => setSuccess(false), 1500);
} catch (err) {
@@ -88,12 +105,16 @@ export function useThinkingConfig() {
setError('Request timeout - please try again');
} else {
setError((err as Error).message);
+ // W4: On conflict, auto-refresh to get latest
+ if ((err as Error).message.includes('another session')) {
+ setTimeout(() => fetchConfig(), 1000);
+ }
}
} finally {
setSaving(false);
}
},
- [config]
+ [config, fetchConfig]
);
const setMode = useCallback(
diff --git a/ui/src/pages/settings/sections/thinking/index.tsx b/ui/src/pages/settings/sections/thinking/index.tsx
index fb5d5c17..e4754d2c 100644
--- a/ui/src/pages/settings/sections/thinking/index.tsx
+++ b/ui/src/pages/settings/sections/thinking/index.tsx
@@ -16,7 +16,7 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
-import { RefreshCw, CheckCircle2, AlertCircle, Brain } from 'lucide-react';
+import { RefreshCw, CheckCircle2, AlertCircle, Brain, Info } from 'lucide-react';
import { useThinkingConfig } from '../../hooks';
import type { ThinkingMode } from '../../types';
@@ -69,8 +69,22 @@ export default function ThinkingSection() {
>
{error && (
-
- {error}
+
+
+ {/* W1: Retry button on error */}
+
+
)}
{success && (
@@ -91,6 +105,19 @@ export default function ThinkingSection() {
+ {/* U4: Provider support indicator */}
+
+
+
+
Supported Providers
+
+ Thinking budget works with: agy (Antigravity),{' '}
+ gemini (with thinking models). Other providers may ignore this
+ setting.
+
+
+
+
{/* Mode Selection */}
Thinking Mode