@@ -42,8 +42,12 @@ import {
4242 normalizeSearchQuery ,
4343 scoreQueryMatch ,
4444} from "@t3tools/shared/searchRanking" ;
45+ import {
46+ getModelSelectionBooleanOptionValue ,
47+ getModelSelectionStringOptionValue ,
48+ } from "@t3tools/shared/model" ;
4549import { useComposerPathSearch } from "../../state/use-composer-path-search" ;
46- import { CLAUDE_AGENT_EFFORT_OPTIONS , type ClaudeAgentEffort } from "./claudeEffortOptions" ;
50+ import { CLAUDE_AGENT_EFFORT_OPTIONS } from "./claudeEffortOptions" ;
4751import { ComposerCommandPopover , type ComposerCommandItem } from "./ComposerCommandPopover" ;
4852
4953/**
@@ -65,22 +69,6 @@ export const COMPOSER_EXPANDED_CHROME = 174;
6569 */
6670export const COMPOSER_EXPANDED_TOOLBAR_CHROME = 54 ;
6771
68- function withModelSelectionOption (
69- selection : ModelSelection ,
70- id : string ,
71- value : string | boolean | undefined ,
72- ) : ModelSelection {
73- const options = ( selection . options ?? [ ] ) . filter ( ( option ) => option . id !== id ) ;
74- if ( value !== undefined ) {
75- options . push ( { id, value } ) ;
76- }
77- if ( options . length === 0 ) {
78- const { options : _options , ...rest } = selection ;
79- return rest as ModelSelection ;
80- }
81- return { ...selection , options } as ModelSelection ;
82- }
83-
8472export interface ThreadComposerProps {
8573 readonly draftMessage : string ;
8674 readonly draftAttachments : ReadonlyArray < DraftComposerImageAttachment > ;
@@ -145,6 +133,18 @@ function ComposerSurface(props: {
145133 ) ;
146134}
147135
136+ function withModelSelectionOption (
137+ selection : ModelSelection ,
138+ id : string ,
139+ value : string | boolean | undefined ,
140+ ) : ModelSelection {
141+ const options = ( selection . options ?? [ ] ) . filter ( ( option ) => option . id !== id ) ;
142+ return {
143+ ...selection ,
144+ options : value === undefined ? options : [ ...options , { id, value } ] ,
145+ } ;
146+ }
147+
148148export const ThreadComposer = memo ( function ThreadComposer ( props : ThreadComposerProps ) {
149149 const isDarkMode = useColorScheme ( ) === "dark" ;
150150 const themePlaceholderColor = useThemeColor ( "--color-placeholder" ) ;
@@ -184,23 +184,30 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer
184184 props . queueCount > 0 ;
185185
186186 const sendLabel = props . activeThreadBusy || props . queueCount > 0 ? "Queue" : "Send" ;
187- const modelProvider = props . selectedThread . modelSelection ?. provider ?? null ;
187+ const modelProvider = props . selectedThread . modelSelection ?. instanceId ?? null ;
188188 const currentModelSelection = props . selectedThread . modelSelection ;
189189 const currentRuntimeMode = props . selectedThread . runtimeMode ;
190190 const currentInteractionMode = props . selectedThread . interactionMode ?? "default" ;
191+ const selectedProviderStatus = useMemo ( ( ) => {
192+ if ( ! props . serverConfig ) return null ;
193+ return (
194+ props . serverConfig . providers . find (
195+ ( p ) => p . instanceId === props . selectedThread . modelSelection . instanceId ,
196+ ) ?? null
197+ ) ;
198+ } , [ props . serverConfig , props . selectedThread . modelSelection . instanceId ] ) ;
191199
192200 // Extract current model options (effort, fastMode, contextWindow)
201+ const selectedProviderDriver = selectedProviderStatus ?. driver ?? null ;
193202 const currentEffort =
194- currentModelSelection . provider === "claudeAgent"
195- ? ( currentModelSelection . options ?. effort ?? "high" )
203+ selectedProviderDriver === "claudeAgent"
204+ ? ( getModelSelectionStringOptionValue ( currentModelSelection , " effort" ) ?? "high" )
196205 : "high" ;
197206 const currentFastMode =
198- currentModelSelection . options && "fastMode" in currentModelSelection . options
199- ? ( currentModelSelection . options . fastMode ?? false )
200- : false ;
207+ getModelSelectionBooleanOptionValue ( currentModelSelection , "fastMode" ) ?? false ;
201208 const currentContextWindow =
202- currentModelSelection . provider === "claudeAgent"
203- ? ( currentModelSelection . options ?. contextWindow ?? "1M" )
209+ selectedProviderDriver === "claudeAgent"
210+ ? ( getModelSelectionStringOptionValue ( currentModelSelection , " contextWindow" ) ?? "1M" )
204211 : "1M" ;
205212
206213 const handleNativePaste = useNativePaste ( ( uris ) => {
@@ -228,16 +235,6 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer
228235 query : composerTrigger ?. kind === "path" ? composerTrigger . query : null ,
229236 } ) ;
230237
231- // ── Build menu items ─────────────────────────────────────
232- const selectedProviderStatus = useMemo ( ( ) => {
233- if ( ! props . serverConfig ) return null ;
234- return (
235- props . serverConfig . providers . find (
236- ( p ) => p . provider === props . selectedThread . modelSelection . provider ,
237- ) ?? null
238- ) ;
239- } , [ props . serverConfig , props . selectedThread . modelSelection . provider ] ) ;
240-
241238 const composerMenuItems : ComposerCommandItem [ ] = useMemo ( ( ) => {
242239 if ( ! composerTrigger ) return [ ] ;
243240
@@ -446,14 +443,14 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer
446443 title : group . providerLabel ,
447444 subtitle : group . models . find (
448445 ( model ) =>
449- model . selection . provider === currentModelSelection . provider &&
446+ model . selection . instanceId === currentModelSelection . instanceId &&
450447 model . selection . model === currentModelSelection . model ,
451448 ) ?. label ,
452449 subactions : group . models . map ( ( option ) => ( {
453450 id : `model:${ option . key } ` ,
454451 title : option . label ,
455452 state :
456- option . selection . provider === currentModelSelection . provider &&
453+ option . selection . instanceId === currentModelSelection . instanceId &&
457454 option . selection . model === currentModelSelection . model
458455 ? ( "on" as const )
459456 : undefined ,
@@ -560,36 +557,31 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer
560557 if ( event . startsWith ( "options:effort:" ) ) {
561558 const effort = event . slice ( "options:effort:" . length ) ;
562559 const updated : ModelSelection =
563- currentModelSelection . provider === "claudeAgent"
564- ? {
565- ...currentModelSelection ,
566- options : { ...currentModelSelection . options , effort : effort as typeof currentEffort } ,
567- }
560+ selectedProviderDriver === "claudeAgent"
561+ ? withModelSelectionOption (
562+ currentModelSelection ,
563+ "effort" ,
564+ effort as typeof currentEffort ,
565+ )
568566 : currentModelSelection ;
569567 void props . onUpdateModelSelection ( updated ) ;
570568 return ;
571569 }
572570 if ( event . startsWith ( "options:fast-mode:" ) ) {
573571 const fastMode = event . endsWith ( ":on" ) ;
574572 const nextFast = fastMode || undefined ;
575- if ( currentModelSelection . provider === "opencode" ) {
573+ if ( selectedProviderDriver === "opencode" ) {
576574 return ;
577575 }
578- const updated : ModelSelection = {
579- ...currentModelSelection ,
580- options : { ...currentModelSelection . options , fastMode : nextFast } ,
581- } ;
576+ const updated = withModelSelectionOption ( currentModelSelection , "fastMode" , nextFast ) ;
582577 void props . onUpdateModelSelection ( updated ) ;
583578 return ;
584579 }
585580 if ( event . startsWith ( "options:context-window:" ) ) {
586581 const contextWindow = event . slice ( "options:context-window:" . length ) ;
587582 const updated : ModelSelection =
588- currentModelSelection . provider === "claudeAgent"
589- ? {
590- ...currentModelSelection ,
591- options : { ...currentModelSelection . options , contextWindow } ,
592- }
583+ selectedProviderDriver === "claudeAgent"
584+ ? withModelSelectionOption ( currentModelSelection , "contextWindow" , contextWindow )
593585 : currentModelSelection ;
594586 void props . onUpdateModelSelection ( updated ) ;
595587 return ;
0 commit comments