From d44d0f61e4026da35c0d1a4acb87ba71ed6cd599 Mon Sep 17 00:00:00 2001 From: Clawd Date: Sun, 1 Mar 2026 08:31:52 -0800 Subject: feat(settings): add configurable model selection - Add Model settings section with free-text input for model override - Pass configured model through to SDK query() calls - Display active model badge in ActionBar next to token usage - Seed model state from DB on mount, update from system:init events - Empty/unset value uses SDK default (no breaking change) --- renderer/src/App.tsx | 15 +++- renderer/src/components/ActionBar.tsx | 9 +++ renderer/src/components/SettingsPage.tsx | 14 +++- renderer/src/components/settings/ModelSettings.tsx | 94 ++++++++++++++++++++++ renderer/src/styles/globals.css | 29 +++++++ 5 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 renderer/src/components/settings/ModelSettings.tsx (limited to 'renderer/src') diff --git a/renderer/src/App.tsx b/renderer/src/App.tsx index 36d3a82..b2cd168 100644 --- a/renderer/src/App.tsx +++ b/renderer/src/App.tsx @@ -59,6 +59,7 @@ export function App() { }); const [error, setError] = useState(null); const [showSettings, setShowSettings] = useState(false); + const [activeModel, setActiveModel] = useState(null); const [theme, setTheme] = useState( () => (localStorage.getItem("cf-theme") as Theme) ?? "dark" @@ -147,9 +148,14 @@ export function App() { return () => window.removeEventListener("keydown", handleKeyDown); }, [selectedSession, isLoading]); - // Load projects on mount + // Load projects and initial model setting on mount useEffect(() => { api.listProjects().then(setProjects); + // Seed the model badge from the DB so it shows before any query fires. + // system:init will overwrite this with the SDK-resolved name once a query runs. + api.getSettings(["model"]).then((s) => { + if (s["model"]) setActiveModel(s["model"]); + }); }, []); // Load sessions when project changes @@ -209,6 +215,12 @@ export function App() { ); } + // ── Model resolved by SDK ──────────────────────────────────────── + // SDKSystemMessage (subtype "init") contains the actual model in use. + if (msg.type === "system" && msg.subtype === "init") { + setActiveModel((msg as { model?: string }).model ?? null); + } + // ── Result (success or error) ──────────────────────────────────── // Always clear loading state on any result subtype so error results // don't leave the UI stuck in the loading/thinking state. @@ -498,6 +510,7 @@ export function App() { } }} disabled={!selectedSession} + modelName={activeModel} /> {showSettings && ( diff --git a/renderer/src/components/ActionBar.tsx b/renderer/src/components/ActionBar.tsx index 22f34b4..d270583 100644 --- a/renderer/src/components/ActionBar.tsx +++ b/renderer/src/components/ActionBar.tsx @@ -11,6 +11,7 @@ interface ActionBarProps { onSubmit: () => void; onPermissionModeChange: (mode: PermissionMode) => void; disabled: boolean; + modelName?: string | null; } export function ActionBar({ @@ -23,6 +24,7 @@ export function ActionBar({ onSubmit, onPermissionModeChange, disabled, + modelName, }: ActionBarProps) { const totalTokens = tokenUsage.inputTokens + tokenUsage.outputTokens; const maxTokens = 200000; @@ -52,6 +54,13 @@ export function ActionBar({ + {/* ── Model badge ── */} + {modelName && ( + + {modelName} + + )} + {phase === "implement" && (