11import type { ReactNode } from "react" ;
2- import React , { createContext , useContext } from "react" ;
2+ import React , { createContext , useContext , useEffect , useState , useCallback } from "react" ;
33import type { ThinkingLevel } from "@/common/types/thinking" ;
4- import { usePersistedState } from "@/browser/hooks/usePersistedState" ;
4+ import {
5+ usePersistedState ,
6+ readPersistedState ,
7+ updatePersistedState ,
8+ } from "@/browser/hooks/usePersistedState" ;
59import {
610 getThinkingLevelKey ,
711 getProjectScopeId ,
12+ getModelKey ,
813 GLOBAL_SCOPE_ID ,
914} from "@/common/constants/storage" ;
15+ import { getDefaultModel } from "@/browser/hooks/useModelLRU" ;
1016
1117interface ThinkingContextType {
1218 thinkingLevel : ThinkingLevel ;
@@ -16,23 +22,71 @@ interface ThinkingContextType {
1622const ThinkingContext = createContext < ThinkingContextType | undefined > ( undefined ) ;
1723
1824interface ThinkingProviderProps {
19- workspaceId ?: string ; // Workspace-scoped storage (highest priority)
20- projectPath ?: string ; // Project-scoped storage (fallback if no workspaceId )
25+ workspaceId ?: string ; // Workspace-scoped storage for model selection
26+ projectPath ?: string ; // Project-scoped storage for model selection (fallback )
2127 children : ReactNode ;
2228}
2329
30+ /**
31+ * ThinkingProvider manages thinking level state per model.
32+ *
33+ * The thinking level is stored per model (e.g., "thinkingLevel:claude-sonnet-4-20250514")
34+ * so users can set different levels for different models and have them remembered.
35+ *
36+ * When the selected model changes, the thinking level is loaded from that model's storage.
37+ */
2438export const ThinkingProvider : React . FC < ThinkingProviderProps > = ( {
2539 workspaceId,
2640 projectPath,
2741 children,
2842} ) => {
29- // Priority: workspace-scoped > project-scoped > global
30- const scopeId = workspaceId ?? ( projectPath ? getProjectScopeId ( projectPath ) : GLOBAL_SCOPE_ID ) ;
31- const key = getThinkingLevelKey ( scopeId ) ;
32- const [ thinkingLevel , setThinkingLevel ] = usePersistedState < ThinkingLevel > (
33- key ,
34- "off" ,
35- { listener : true } // Listen for changes from command palette and other sources
43+ // Derive model storage scope (workspace or project)
44+ const modelScopeId =
45+ workspaceId ?? ( projectPath ? getProjectScopeId ( projectPath ) : GLOBAL_SCOPE_ID ) ;
46+ const modelKey = getModelKey ( modelScopeId ) ;
47+
48+ // Listen for model changes in this scope
49+ const [ selectedModel ] = usePersistedState < string | null > ( modelKey , null , { listener : true } ) ;
50+ const currentModel = selectedModel ?? getDefaultModel ( ) ;
51+
52+ // Local state for thinking level (managed per model)
53+ const [ thinkingLevel , setThinkingLevelState ] = useState < ThinkingLevel > ( ( ) => {
54+ return readPersistedState < ThinkingLevel > ( getThinkingLevelKey ( currentModel ) , "off" ) ;
55+ } ) ;
56+
57+ // When model changes, load that model's thinking level
58+ useEffect ( ( ) => {
59+ const modelThinkingKey = getThinkingLevelKey ( currentModel ) ;
60+ const modelThinkingLevel = readPersistedState < ThinkingLevel > ( modelThinkingKey , "off" ) ;
61+ setThinkingLevelState ( modelThinkingLevel ) ;
62+ } , [ currentModel ] ) ;
63+
64+ // Listen for storage events (from command palette or other sources)
65+ useEffect ( ( ) => {
66+ const modelThinkingKey = getThinkingLevelKey ( currentModel ) ;
67+
68+ const handleStorage = ( e : StorageEvent ) => {
69+ if ( e . key === modelThinkingKey && e . newValue ) {
70+ try {
71+ const parsed = JSON . parse ( e . newValue ) as ThinkingLevel ;
72+ setThinkingLevelState ( parsed ) ;
73+ } catch {
74+ // Invalid JSON, ignore
75+ }
76+ }
77+ } ;
78+
79+ window . addEventListener ( "storage" , handleStorage ) ;
80+ return ( ) => window . removeEventListener ( "storage" , handleStorage ) ;
81+ } , [ currentModel ] ) ;
82+
83+ // Save thinking level to current model's storage
84+ const setThinkingLevel = useCallback (
85+ ( level : ThinkingLevel ) => {
86+ setThinkingLevelState ( level ) ;
87+ updatePersistedState ( getThinkingLevelKey ( currentModel ) , level ) ;
88+ } ,
89+ [ currentModel ]
3690 ) ;
3791
3892 return (
0 commit comments