From f54d0fd6cd13352f86186746682463daf8e30c12 Mon Sep 17 00:00:00 2001 From: Ammar Date: Sat, 13 Dec 2025 20:07:22 -0600 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=A4=96=20refactor:=20persist=20thinki?= =?UTF-8?q?ng=20level=20per-model=20globally?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change thinking level storage from per-workspace to per-model Key format: thinkingLevel:model:{modelName} - Remove lastThinkingByModel toggle memory (replaced by simple cycle) - Update ThinkingProvider to read model from workspace storage - Simplify keybind toggle to cycle through allowed levels - Remove thinking level from workspace storage copy/sync This makes thinking preferences follow the model rather than the workspace, which better matches how thinking capability varies per model. --- _Generated with `mux` • Model: `` • Thinking: ``_ --- src/browser/App.tsx | 92 +++++++++++-------- .../ChatInput/useCreationWorkspace.test.tsx | 7 +- .../ChatInput/useCreationWorkspace.ts | 11 +-- src/browser/components/ThinkingSlider.tsx | 10 +- src/browser/contexts/ThinkingContext.tsx | 42 ++++++--- src/browser/hooks/useAIViewKeybinds.ts | 40 +++----- src/browser/utils/messages/sendOptions.ts | 6 +- src/common/constants/storage.ts | 20 ++-- 8 files changed, 112 insertions(+), 116 deletions(-) diff --git a/src/browser/App.tsx b/src/browser/App.tsx index 0a8df93f1c..a56e4a9cc3 100644 --- a/src/browser/App.tsx +++ b/src/browser/App.tsx @@ -30,7 +30,9 @@ import { buildCoreSources, type BuildSourcesParams } from "./utils/commands/sour import type { ThinkingLevel } from "@/common/types/thinking"; import { CUSTOM_EVENTS } from "@/common/constants/events"; import { isWorkspaceForkSwitchEvent } from "./utils/workspaceEvents"; -import { getThinkingLevelKey } from "@/common/constants/storage"; +import { getThinkingLevelByModelKey, getModelKey } from "@/common/constants/storage"; +import { migrateGatewayModel } from "@/browser/hooks/useGatewayModels"; +import { getDefaultModel } from "@/browser/hooks/useModelsFromSettings"; import type { BranchListResult } from "@/common/orpc/types"; import { useTelemetry } from "./hooks/useTelemetry"; import { getRuntimeTypeForTelemetry } from "@/common/telemetry"; @@ -262,50 +264,68 @@ function AppInner() { close: closeCommandPalette, } = useCommandRegistry(); - const getThinkingLevelForWorkspace = useCallback((workspaceId: string): ThinkingLevel => { - if (!workspaceId) { - return "off"; + /** + * Get model for a workspace, returning canonical format. + */ + const getModelForWorkspace = useCallback((workspaceId: string): string => { + const defaultModel = getDefaultModel(); + try { + const raw = window.localStorage?.getItem(getModelKey(workspaceId)); + const model = raw ? (JSON.parse(raw) as string) : defaultModel; + return migrateGatewayModel(model || defaultModel); + } catch { + return defaultModel; } + }, []); - if (typeof window === "undefined" || !window.localStorage) { - return "off"; - } + const getThinkingLevelForWorkspace = useCallback( + (workspaceId: string): ThinkingLevel => { + if (!workspaceId || typeof window === "undefined" || !window.localStorage) { + return "off"; + } - try { - const key = getThinkingLevelKey(workspaceId); - const stored = window.localStorage.getItem(key); - if (!stored || stored === "undefined") { + try { + const model = getModelForWorkspace(workspaceId); + const key = getThinkingLevelByModelKey(model); + const stored = window.localStorage.getItem(key); + if (!stored || stored === "undefined") { + return "off"; + } + const parsed = JSON.parse(stored) as ThinkingLevel; + return THINKING_LEVELS.includes(parsed) ? parsed : "off"; + } catch (error) { + console.warn("Failed to read thinking level", error); return "off"; } - const parsed = JSON.parse(stored) as ThinkingLevel; - return THINKING_LEVELS.includes(parsed) ? parsed : "off"; - } catch (error) { - console.warn("Failed to read thinking level", error); - return "off"; - } - }, []); + }, + [getModelForWorkspace] + ); - const setThinkingLevelFromPalette = useCallback((workspaceId: string, level: ThinkingLevel) => { - if (!workspaceId) { - return; - } + const setThinkingLevelFromPalette = useCallback( + (workspaceId: string, level: ThinkingLevel) => { + if (!workspaceId) { + return; + } - const normalized = THINKING_LEVELS.includes(level) ? level : "off"; - const key = getThinkingLevelKey(workspaceId); + const normalized = THINKING_LEVELS.includes(level) ? level : "off"; + const model = getModelForWorkspace(workspaceId); + const key = getThinkingLevelByModelKey(model); - // Use the utility function which handles localStorage and event dispatch - // ThinkingProvider will pick this up via its listener - updatePersistedState(key, normalized); + // Use the utility function which handles localStorage and event dispatch + // ThinkingProvider will pick this up via its listener + updatePersistedState(key, normalized); - // Dispatch toast notification event for UI feedback - if (typeof window !== "undefined") { - window.dispatchEvent( - new CustomEvent(CUSTOM_EVENTS.THINKING_LEVEL_TOAST, { - detail: { workspaceId, level: normalized }, - }) - ); - } - }, []); + // Dispatch toast notification event for UI feedback + if (typeof window !== "undefined") { + window.dispatchEvent( + new CustomEvent(CUSTOM_EVENTS.THINKING_LEVEL_TOAST, { + detail: { workspaceId, level: normalized }, + }) + ); + } + }, + [getModelForWorkspace] + ); const registerParamsRef = useRef(null); diff --git a/src/browser/components/ChatInput/useCreationWorkspace.test.tsx b/src/browser/components/ChatInput/useCreationWorkspace.test.tsx index 8d9d2848ef..43f22cf6e3 100644 --- a/src/browser/components/ChatInput/useCreationWorkspace.test.tsx +++ b/src/browser/components/ChatInput/useCreationWorkspace.test.tsx @@ -6,7 +6,6 @@ import { getModeKey, getPendingScopeId, getProjectScopeId, - getThinkingLevelKey, } from "@/common/constants/storage"; import type { SendMessageError as _SendMessageError } from "@/common/types/errors"; import type { WorkspaceChatMessage } from "@/common/orpc/types"; @@ -404,7 +403,6 @@ describe("useCreationWorkspace", () => { }); persistedPreferences[getModeKey(getProjectScopeId(TEST_PROJECT_PATH))] = "plan"; - persistedPreferences[getThinkingLevelKey(getProjectScopeId(TEST_PROJECT_PATH))] = "high"; // Set model preference for the project scope (read by getSendOptionsFromStorage) persistedPreferences[getModelKey(getProjectScopeId(TEST_PROJECT_PATH))] = "gpt-4"; @@ -460,15 +458,12 @@ describe("useCreationWorkspace", () => { expect(onWorkspaceCreated.mock.calls[0][0]).toEqual(TEST_METADATA); const projectModeKey = getModeKey(getProjectScopeId(TEST_PROJECT_PATH)); - const projectThinkingKey = getThinkingLevelKey(getProjectScopeId(TEST_PROJECT_PATH)); expect(readPersistedStateCalls).toContainEqual([projectModeKey, null]); - expect(readPersistedStateCalls).toContainEqual([projectThinkingKey, null]); const modeKey = getModeKey(TEST_WORKSPACE_ID); - const thinkingKey = getThinkingLevelKey(TEST_WORKSPACE_ID); const pendingInputKey = getInputKey(getPendingScopeId(TEST_PROJECT_PATH)); expect(updatePersistedStateCalls).toContainEqual([modeKey, "plan"]); - expect(updatePersistedStateCalls).toContainEqual([thinkingKey, "high"]); + // Note: thinking level is no longer synced per-workspace, it's stored per-model globally expect(updatePersistedStateCalls).toContainEqual([pendingInputKey, ""]); }); diff --git a/src/browser/components/ChatInput/useCreationWorkspace.ts b/src/browser/components/ChatInput/useCreationWorkspace.ts index f3d9112bbe..ed389db5e2 100644 --- a/src/browser/components/ChatInput/useCreationWorkspace.ts +++ b/src/browser/components/ChatInput/useCreationWorkspace.ts @@ -2,7 +2,6 @@ import { useState, useEffect, useCallback } from "react"; import type { FrontendWorkspaceMetadata } from "@/common/types/workspace"; import type { RuntimeConfig, RuntimeMode } from "@/common/types/runtime"; import type { UIMode } from "@/common/types/mode"; -import type { ThinkingLevel } from "@/common/types/thinking"; import { parseRuntimeString } from "@/browser/utils/chatCommands"; import { useDraftWorkspaceSettings } from "@/browser/hooks/useDraftWorkspaceSettings"; import { readPersistedState, updatePersistedState } from "@/browser/hooks/usePersistedState"; @@ -13,7 +12,6 @@ import { getModeKey, getPendingScopeId, getProjectScopeId, - getThinkingLevelKey, } from "@/common/constants/storage"; import type { Toast } from "@/browser/components/ChatInputToast"; import { createErrorToast } from "@/browser/components/ChatInputToasts"; @@ -47,13 +45,8 @@ function syncCreationPreferences(projectPath: string, workspaceId: string): void updatePersistedState(getModeKey(workspaceId), projectMode); } - const projectThinking = readPersistedState( - getThinkingLevelKey(projectScopeId), - null - ); - if (projectThinking) { - updatePersistedState(getThinkingLevelKey(workspaceId), projectThinking); - } + // Note: thinking level is stored per-model globally, not per-workspace, + // so no sync is needed here } interface UseCreationWorkspaceReturn { diff --git a/src/browser/components/ThinkingSlider.tsx b/src/browser/components/ThinkingSlider.tsx index 2529549020..02e105fb16 100644 --- a/src/browser/components/ThinkingSlider.tsx +++ b/src/browser/components/ThinkingSlider.tsx @@ -1,11 +1,9 @@ import React, { useEffect, useId } from "react"; -import type { ThinkingLevel, ThinkingLevelOn } from "@/common/types/thinking"; +import type { ThinkingLevel } from "@/common/types/thinking"; import { useThinkingLevel } from "@/browser/hooks/useThinkingLevel"; import { Tooltip, TooltipTrigger, TooltipContent } from "./ui/tooltip"; import { formatKeybind, KEYBINDS } from "@/browser/utils/ui/keybinds"; import { getThinkingPolicyForModel } from "@/browser/utils/thinking/policy"; -import { updatePersistedState } from "@/browser/hooks/usePersistedState"; -import { getLastThinkingByModelKey } from "@/common/constants/storage"; // Uses CSS variable --color-thinking-mode for theme compatibility // Glow is applied via CSS using color-mix with the theme color @@ -147,12 +145,6 @@ export const ThinkingSliderComponent: React.FC = ({ modelS const handleThinkingLevelChange = (newLevel: ThinkingLevel) => { setThinkingLevel(newLevel); - // Also save to lastThinkingByModel for Ctrl+Shift+T toggle memory - // Only save active levels (not "off") - matches useAIViewKeybinds logic - if (newLevel !== "off") { - const lastThinkingKey = getLastThinkingByModelKey(modelString); - updatePersistedState(lastThinkingKey, newLevel as ThinkingLevelOn); - } }; // Cycle through allowed thinking levels diff --git a/src/browser/contexts/ThinkingContext.tsx b/src/browser/contexts/ThinkingContext.tsx index 613e927606..1adc9ff052 100644 --- a/src/browser/contexts/ThinkingContext.tsx +++ b/src/browser/contexts/ThinkingContext.tsx @@ -1,12 +1,10 @@ import type { ReactNode } from "react"; import React, { createContext, useContext } from "react"; import type { ThinkingLevel } from "@/common/types/thinking"; -import { usePersistedState } from "@/browser/hooks/usePersistedState"; -import { - getThinkingLevelKey, - getProjectScopeId, - GLOBAL_SCOPE_ID, -} from "@/common/constants/storage"; +import { usePersistedState, readPersistedState } from "@/browser/hooks/usePersistedState"; +import { getThinkingLevelByModelKey, getModelKey } from "@/common/constants/storage"; +import { getDefaultModel } from "@/browser/hooks/useModelsFromSettings"; +import { migrateGatewayModel } from "@/browser/hooks/useGatewayModels"; interface ThinkingContextType { thinkingLevel: ThinkingLevel; @@ -16,19 +14,41 @@ interface ThinkingContextType { const ThinkingContext = createContext(undefined); interface ThinkingProviderProps { - workspaceId?: string; // Workspace-scoped storage (highest priority) - projectPath?: string; // Project-scoped storage (fallback if no workspaceId) + workspaceId?: string; // For existing workspaces + projectPath?: string; // For workspace creation (uses project-scoped model key) children: ReactNode; } +/** + * Reads the current model from localStorage for the given scope. + * Returns canonical model format (after gateway migration). + */ +function getScopedModel(workspaceId?: string, projectPath?: string): string { + const defaultModel = getDefaultModel(); + // Use workspace-scoped model key if available, otherwise project-scoped + const modelKey = workspaceId + ? getModelKey(workspaceId) + : projectPath + ? getModelKey(`__project__/${projectPath}`) + : null; + + if (!modelKey) { + return defaultModel; + } + + const rawModel = readPersistedState(modelKey, defaultModel); + // Normalize to canonical format (e.g., strip legacy gateway prefix) + return migrateGatewayModel(rawModel || defaultModel); +} + export const ThinkingProvider: React.FC = ({ workspaceId, projectPath, children, }) => { - // Priority: workspace-scoped > project-scoped > global - const scopeId = workspaceId ?? (projectPath ? getProjectScopeId(projectPath) : GLOBAL_SCOPE_ID); - const key = getThinkingLevelKey(scopeId); + // Read current model from localStorage (non-reactive, re-reads on each render) + const modelString = getScopedModel(workspaceId, projectPath); + const key = getThinkingLevelByModelKey(modelString); const [thinkingLevel, setThinkingLevel] = usePersistedState( key, "off", diff --git a/src/browser/hooks/useAIViewKeybinds.ts b/src/browser/hooks/useAIViewKeybinds.ts index 6219a1c64f..6157f104cd 100644 --- a/src/browser/hooks/useAIViewKeybinds.ts +++ b/src/browser/hooks/useAIViewKeybinds.ts @@ -1,10 +1,9 @@ import { useEffect } from "react"; import type { ChatInputAPI } from "@/browser/components/ChatInput"; import { matchesKeybind, KEYBINDS, isEditableElement } from "@/browser/utils/ui/keybinds"; -import { getLastThinkingByModelKey, getModelKey } from "@/common/constants/storage"; -import { updatePersistedState, readPersistedState } from "@/browser/hooks/usePersistedState"; -import type { ThinkingLevel, ThinkingLevelOn } from "@/common/types/thinking"; -import { DEFAULT_THINKING_LEVEL } from "@/common/types/thinking"; +import { getModelKey } from "@/common/constants/storage"; +import { readPersistedState } from "@/browser/hooks/usePersistedState"; +import type { ThinkingLevel } from "@/common/types/thinking"; import { getThinkingPolicyForModel } from "@/browser/utils/thinking/policy"; import { getDefaultModel } from "@/browser/hooks/useModelsFromSettings"; import type { StreamingMessageAggregator } from "@/browser/utils/messages/StreamingMessageAggregator"; @@ -32,7 +31,7 @@ interface UseAIViewKeybindsParams { * Manages keyboard shortcuts for AIView: * - Esc (non-vim) or Ctrl+C (vim): Interrupt stream (always, regardless of selection) * - Ctrl+I: Focus chat input - * - Ctrl+Shift+T: Toggle thinking level + * - Ctrl+Shift+T: Cycle thinking level through allowed values for current model * - Ctrl+G: Jump to bottom * - Ctrl+T: Open terminal * - Ctrl+Shift+E: Open in editor @@ -99,40 +98,25 @@ export function useAIViewKeybinds({ return; } - // Toggle thinking works even when focused in input fields + // Cycle thinking level - works even when focused in input fields if (matchesKeybind(e, KEYBINDS.TOGGLE_THINKING)) { e.preventDefault(); // Get selected model from localStorage (what user sees in UI) // Fall back to message history model, then to the Settings default model - // This matches the same logic as useSendMessageOptions const selectedModel = readPersistedState(getModelKey(workspaceId), null); const modelToUse = selectedModel ?? currentModel ?? getDefaultModel(); - // Storage key for remembering this model's last-used active thinking level - const lastThinkingKey = getLastThinkingByModelKey(modelToUse); - - // Special-case: if model has single-option policy (e.g., gpt-5-pro only supports HIGH), - // the toggle is a no-op to avoid confusing state transitions. + // Get allowed levels for this model const allowed = getThinkingPolicyForModel(modelToUse); - if (allowed.length === 1) { - return; // No toggle for single-option policies + if (allowed.length <= 1) { + return; // No cycling for single-option policies } - if (currentWorkspaceThinking !== "off") { - // Thinking is currently ON - save the level for this model and turn it off - // Type system ensures we can only store active levels (not "off") - const activeLevel: ThinkingLevelOn = currentWorkspaceThinking; - updatePersistedState(lastThinkingKey, activeLevel); - setThinkingLevel("off"); - } else { - // Thinking is currently OFF - restore the last level used for this model - const lastUsedThinkingForModel = readPersistedState( - lastThinkingKey, - DEFAULT_THINKING_LEVEL - ); - setThinkingLevel(lastUsedThinkingForModel); - } + // Cycle to the next allowed level + const currentIndex = allowed.indexOf(currentWorkspaceThinking); + const nextIndex = (currentIndex + 1) % allowed.length; + setThinkingLevel(allowed[nextIndex]); return; } diff --git a/src/browser/utils/messages/sendOptions.ts b/src/browser/utils/messages/sendOptions.ts index ee0ac5f40f..e26114558d 100644 --- a/src/browser/utils/messages/sendOptions.ts +++ b/src/browser/utils/messages/sendOptions.ts @@ -1,4 +1,4 @@ -import { getModelKey, getThinkingLevelKey, getModeKey } from "@/common/constants/storage"; +import { getModelKey, getThinkingLevelByModelKey, getModeKey } from "@/common/constants/storage"; import { modeToToolPolicy } from "@/common/utils/ui/modeUtils"; import { readPersistedState } from "@/browser/hooks/usePersistedState"; import { getDefaultModel } from "@/browser/hooks/useModelsFromSettings"; @@ -47,9 +47,9 @@ export function getSendOptionsFromStorage(workspaceId: string): SendMessageOptio // Transform to gateway format if gateway is enabled for this model const model = toGatewayModel(baseModel); - // Read thinking level (workspace-specific) + // Read thinking level (per-model global storage) const thinkingLevel = readPersistedState( - getThinkingLevelKey(workspaceId), + getThinkingLevelByModelKey(baseModel), WORKSPACE_DEFAULTS.thinkingLevel ); diff --git a/src/common/constants/storage.ts b/src/common/constants/storage.ts index beb43ba657..08eb2e9689 100644 --- a/src/common/constants/storage.ts +++ b/src/common/constants/storage.ts @@ -58,10 +58,12 @@ export function getMCPTestResultsKey(projectPath: string): string { } /** - * Helper to create a thinking level storage key for a workspace - * Format: "thinkingLevel:{workspaceId}" + * Get the localStorage key for thinking level preference per model (global) + * Format: "thinkingLevel:model:{modelName}" */ -export const getThinkingLevelKey = (workspaceId: string): string => `thinkingLevel:${workspaceId}`; +export function getThinkingLevelByModelKey(modelName: string): string { + return `thinkingLevel:model:${modelName}`; +} /** * Get the localStorage key for the user's preferred model for a workspace @@ -92,15 +94,6 @@ export function getRetryStateKey(workspaceId: string): string { return `${workspaceId}-retryState`; } -/** - * Get the localStorage key for the last active thinking level used for a model - * Stores only active levels ("low" | "medium" | "high"), never "off" - * Format: "lastThinkingByModel:{modelName}" - */ -export function getLastThinkingByModelKey(modelName: string): string { - return `lastThinkingByModel:${modelName}`; -} - /** * Get storage key for cancelled compaction tracking. * Stores compaction-request user message ID to verify freshness across reloads. @@ -288,7 +281,6 @@ const PERSISTENT_WORKSPACE_KEY_FUNCTIONS: Array<(workspaceId: string) => string> getModelKey, getInputKey, getModeKey, - getThinkingLevelKey, getAutoRetryKey, getRetryStateKey, getReviewStateKey, @@ -298,7 +290,7 @@ const PERSISTENT_WORKSPACE_KEY_FUNCTIONS: Array<(workspaceId: string) => string> getReviewsKey, getAutoCompactionEnabledKey, getStatusStateKey, - // Note: getAutoCompactionThresholdKey is per-model, not per-workspace + // Note: thinking level and auto-compaction threshold are per-model, not per-workspace ]; /** From 9b0d6225ba9623a75727c33ce4c9ed7948c263fd Mon Sep 17 00:00:00 2001 From: Ammar Date: Sat, 13 Dec 2025 20:11:02 -0600 Subject: [PATCH 2/3] fix: use readPersistedState instead of direct localStorage access --- src/browser/App.tsx | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/src/browser/App.tsx b/src/browser/App.tsx index a56e4a9cc3..86964df89d 100644 --- a/src/browser/App.tsx +++ b/src/browser/App.tsx @@ -269,34 +269,21 @@ function AppInner() { */ const getModelForWorkspace = useCallback((workspaceId: string): string => { const defaultModel = getDefaultModel(); - try { - const raw = window.localStorage?.getItem(getModelKey(workspaceId)); - const model = raw ? (JSON.parse(raw) as string) : defaultModel; - return migrateGatewayModel(model || defaultModel); - } catch { - return defaultModel; - } + const rawModel = readPersistedState(getModelKey(workspaceId), defaultModel); + return migrateGatewayModel(rawModel || defaultModel); }, []); const getThinkingLevelForWorkspace = useCallback( (workspaceId: string): ThinkingLevel => { - if (!workspaceId || typeof window === "undefined" || !window.localStorage) { - return "off"; - } - - try { - const model = getModelForWorkspace(workspaceId); - const key = getThinkingLevelByModelKey(model); - const stored = window.localStorage.getItem(key); - if (!stored || stored === "undefined") { - return "off"; - } - const parsed = JSON.parse(stored) as ThinkingLevel; - return THINKING_LEVELS.includes(parsed) ? parsed : "off"; - } catch (error) { - console.warn("Failed to read thinking level", error); + if (!workspaceId) { return "off"; } + const model = getModelForWorkspace(workspaceId); + const level = readPersistedState( + getThinkingLevelByModelKey(model), + "off" + ); + return THINKING_LEVELS.includes(level) ? level : "off"; }, [getModelForWorkspace] ); From db7f1eac2a6f80cfe30f8218dfff0b90974a16dd Mon Sep 17 00:00:00 2001 From: Ammar Date: Sat, 13 Dec 2025 20:12:16 -0600 Subject: [PATCH 3/3] docs: strengthen localStorage guidance in AGENTS.md --- docs/AGENTS.md | 2 +- src/browser/App.tsx | 11 ++++++----- src/browser/components/ThinkingSlider.tsx | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/AGENTS.md b/docs/AGENTS.md index b1a2667ed6..5192353a54 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -128,7 +128,7 @@ Avoid mock-heavy tests that verify implementation details rather than behavior. ## Component State & Storage - Parent components own localStorage interactions; children announce intent only. -- Use `usePersistedState`/`readPersistedState`/`updatePersistedState` helpers—never call `localStorage` directly. +- **Never call `localStorage` directly** — always use `usePersistedState`/`readPersistedState`/`updatePersistedState` helpers. This includes inside `useCallback`, event handlers, and non-React functions. The helpers handle JSON parsing, error recovery, and cross-component sync. - When a component needs to read persisted state it doesn't own (to avoid layout flash), use `readPersistedState` in `useState` initializer: `useState(() => readPersistedState(key, default))`. - When multiple components need the same persisted value, use `usePersistedState` with identical keys and `{ listener: true }` for automatic cross-component sync. - Avoid destructuring props in function signatures; access via `props.field` to keep rename-friendly code. diff --git a/src/browser/App.tsx b/src/browser/App.tsx index 86964df89d..80b08243de 100644 --- a/src/browser/App.tsx +++ b/src/browser/App.tsx @@ -7,7 +7,11 @@ import { LeftSidebar } from "./components/LeftSidebar"; import { ProjectCreateModal } from "./components/ProjectCreateModal"; import { AIView } from "./components/AIView"; import { ErrorBoundary } from "./components/ErrorBoundary"; -import { usePersistedState, updatePersistedState } from "./hooks/usePersistedState"; +import { + usePersistedState, + updatePersistedState, + readPersistedState, +} from "./hooks/usePersistedState"; import { matchesKeybind, KEYBINDS } from "./utils/ui/keybinds"; import { buildSortedWorkspacesByProject } from "./utils/ui/workspaceFiltering"; import { useResumeManager } from "./hooks/useResumeManager"; @@ -279,10 +283,7 @@ function AppInner() { return "off"; } const model = getModelForWorkspace(workspaceId); - const level = readPersistedState( - getThinkingLevelByModelKey(model), - "off" - ); + const level = readPersistedState(getThinkingLevelByModelKey(model), "off"); return THINKING_LEVELS.includes(level) ? level : "off"; }, [getModelForWorkspace] diff --git a/src/browser/components/ThinkingSlider.tsx b/src/browser/components/ThinkingSlider.tsx index 02e105fb16..047f8ac180 100644 --- a/src/browser/components/ThinkingSlider.tsx +++ b/src/browser/components/ThinkingSlider.tsx @@ -199,7 +199,7 @@ export const ThinkingSliderComponent: React.FC = ({ modelS - Thinking: {formatKeybind(KEYBINDS.TOGGLE_THINKING)} to toggle. Click level to cycle. + Thinking: {formatKeybind(KEYBINDS.TOGGLE_THINKING)} to cycle. Saved per model. );