@@ -60,63 +60,6 @@ import type { StreamRow } from "./stream.js"
6060
6161import type { PendingImageAttachment } from "./image-attachments.js"
6262
63- const PROVIDER_GROUP_PREFIX = "providerGroup:"
64-
65- function providerGroupRowId ( provider : string ) : string {
66- return `${ PROVIDER_GROUP_PREFIX } ${ provider } `
67- }
68-
69- function providerFromGroupRowId ( id : string ) : string | null {
70- return id . startsWith ( PROVIDER_GROUP_PREFIX ) ? id . slice ( PROVIDER_GROUP_PREFIX . length ) : null
71- }
72-
73- /** Provider (account) segment of a `provider:model` row id. */
74- function providerOfRowId ( id : string ) : string {
75- const i = id . indexOf ( ":" )
76- return i === - 1 ? id : id . slice ( 0 , i )
77- }
78-
79- /** Provider label segment of a `Provider Label / model` row label. */
80- function providerLabelOfRow ( label : string ) : string {
81- const i = label . indexOf ( " / " )
82- return i === - 1 ? label : label . slice ( 0 , i )
83- }
84-
85- type ModelGroup = {
86- readonly label : string
87- readonly rows : ProductHostModelOption [ ]
88- }
89-
90- /**
91- * Split a flat, section-tagged models list into the provider-first picker's
92- * top level (recent/favorites/unconnected pass through flat; each distinct
93- * provider collapses into one group row, in first-seen order) plus the
94- * per-provider model rows reached by descending into a group. Rows with no
95- * `section` (a caller not using buildModelsFirstCatalog) pass through
96- * ungrouped, preserving today's single-level picker for that caller.
97- */
98- function groupModelsForPicker (
99- models : readonly ProductHostModelOption [ ] ,
100- ) : { readonly top : ProductHostModelOption [ ] ; readonly groups : ReadonlyMap < string , ModelGroup > } {
101- const top : ProductHostModelOption [ ] = [ ]
102- const groups = new Map < string , ModelGroup > ( )
103- for ( const row of models ) {
104- if ( row . section !== "provider" ) {
105- top . push ( row )
106- continue
107- }
108- const provider = providerOfRowId ( row . id )
109- let group = groups . get ( provider )
110- if ( group === undefined ) {
111- group = { label : providerLabelOfRow ( row . label ) , rows : [ ] }
112- groups . set ( provider , group )
113- top . push ( { id : providerGroupRowId ( provider ) , label : group . label , section : "provider" } )
114- }
115- group . rows . push ( row )
116- }
117- return { top, groups }
118- }
119-
12063/** Suffix the row matching `activeId` (if any) so it reads as the current pick. */
12164function annotateCurrent (
12265 rows : readonly ProductHostModelOption [ ] ,
@@ -567,26 +510,14 @@ export async function mountProductHost(
567510 const onConnect = config . onConnectProvider
568511 const onFavoriteToggle = config . onFavoriteToggle
569512
570- // Provider rows have no catalog entry of their own to describe; fall back
571- // to a plain model count so the description zone is never blank.
572- const describe = ( itemId : string ) : ItemDescription | null => {
573- const groupProvider = providerFromGroupRowId ( itemId )
574- if ( groupProvider !== null ) {
575- const { groups } = groupModelsForPicker ( currentModels )
576- const count = groups . get ( groupProvider ) ?. rows . length ?? 0
577- return {
578- what : `${ count } model${ count === 1 ? "" : "s" } available.` ,
579- impact : "Press Enter to see them." ,
580- tone : "plain" ,
581- }
582- }
583- return currentDescribeModel ?.( itemId ) ?? null
584- }
585-
586- const openLevel = ( items : readonly ProductHostModelOption [ ] , onCancel ?: ( ) => void ) : void => {
513+ openModels = ( ) : void => {
514+ const activeId = config . activeModelId ?.( )
515+ const items = annotateCurrent ( currentModels , activeId )
587516 openModelPickerOverlay ( shell , {
588517 items : items . map ( ( m ) => m . label ) ,
589518 itemIds : items . map ( ( m ) => m . id ) ,
519+ // Flat list: type to narrow rather than drill into a provider pane.
520+ typeToFilter : true ,
590521 onAccept : ( sel ) => {
591522 const id = sel . id ?? items [ sel . index ] ?. id
592523 if ( ! id ) return
@@ -595,51 +526,23 @@ export async function mountProductHost(
595526 onConnect ?.( providerName )
596527 return
597528 }
598- const groupProvider = providerFromGroupRowId ( id )
599- if ( groupProvider !== null ) {
600- const { groups } = groupModelsForPicker ( currentModels )
601- const group = groups . get ( groupProvider )
602- if ( group !== undefined ) {
603- openLevel ( annotateCurrent ( group . rows , config . activeModelId ?.( ) ) , openModels )
604- }
605- return
606- }
607529 onSelect ( id )
608530 } ,
609- describe,
531+ describe : ( itemId ) => currentDescribeModel ?. ( itemId ) ?? null ,
610532 ...( onFavoriteToggle !== undefined
611533 ? {
612534 onAction : ( itemId , key ) => {
613- // Alt+F, never bare f — the palette filters as you type, so a
614- // bare letter narrows the list instead of toggling a favorite.
535+ // Alt+F, never bare f — type-to-filter claims printable keys.
615536 const name = typeof key . name === "string" ? key . name . toLowerCase ( ) : ""
616537 if ( name !== "f" || key . ctrl || ! ( key . meta || key . option ) ) return false
617- if ( itemId . startsWith ( "connect:" ) || providerFromGroupRowId ( itemId ) !== null ) return false
538+ if ( itemId . startsWith ( "connect:" ) ) return false
618539 onFavoriteToggle ( itemId )
619540 return true
620541 } ,
621542 }
622543 : { } ) ,
623- ...( onCancel !== undefined ? { onCancel } : { } ) ,
624544 } )
625545 }
626-
627- openModels = ( ) : void => {
628- const { top, groups } = groupModelsForPicker ( currentModels )
629- const activeId = config . activeModelId ?.( )
630- // The active model's own row already reads "(current)" via annotateCurrent
631- // below; when it lives inside a provider group, mark the group row too
632- // so the pick is visible without descending into it.
633- const activeGroupId = [ ...groups . entries ( ) ] . find ( ( [ , g ] ) =>
634- g . rows . some ( ( r ) => r . id === activeId ) ,
635- ) ?. [ 0 ]
636- const withGroupMark = activeGroupId === undefined
637- ? top
638- : top . map ( ( r ) =>
639- r . id === providerGroupRowId ( activeGroupId ) ? { ...r , label : `${ r . label } (current)` } : r ,
640- )
641- openLevel ( annotateCurrent ( withGroupMark , activeId ) )
642- }
643546 ; ( shell as AppShell & { __openModels ?: ( ) => void } ) . __openModels =
644547 openModels
645548 }
0 commit comments