@@ -10,7 +10,6 @@ import { AGENTS_PANEL_LINGER_MS } from "./chrome-state.js";
1010import { createHarness } from "./harness.js" ;
1111import {
1212 acceptOverlaySelection ,
13- closeInsetOverlay ,
1413 handleListFilterKey ,
1514 moveOverlaySelection ,
1615 runOverlayAction ,
@@ -690,7 +689,7 @@ describe("flat type-to-filter model picker", () => {
690689
691690 const altA = { name : "a" , ctrl : false , meta : false , option : true } as KeyEvent ;
692691
693- test ( "the model picker footer advertises Alt+A" , async ( ) => {
692+ test ( "the model picker footer advertises Alt+A and /connect " , async ( ) => {
694693 const { harness, host } = await mountPicker ( {
695694 // The hint requires the full wiring — choices AND the connect handler —
696695 // because that is exactly when the key actually works.
@@ -700,7 +699,9 @@ describe("flat type-to-filter model picker", () => {
700699 try {
701700 host . openModels ?.( ) ;
702701 await harness . renderOnce ( ) ;
703- expect ( harness . captureCharFrame ( ) ) . toContain ( "Alt+A" ) ;
702+ const frame = harness . captureCharFrame ( ) ;
703+ expect ( frame ) . toContain ( "Alt+A" ) ;
704+ expect ( frame ) . toContain ( "/connect" ) ;
704705 } finally {
705706 host . dispose ( ) ;
706707 harness . destroy ( ) ;
@@ -799,6 +800,74 @@ describe("flat type-to-filter model picker", () => {
799800 }
800801 } ) ;
801802
803+ test ( "closed-prompt å stays in the prompt and does not open add-provider" , async ( ) => {
804+ const { harness, host } = await mountPicker ( {
805+ onConnectProvider : ( ) => { } ,
806+ addProviderChoices : ( ) => [ { id : "codex" , label : "Codex" , hint : "" , accountCount : 0 } ] ,
807+ } ) ;
808+ try {
809+ expect ( host . shell . overlayKind ) . toBeNull ( ) ;
810+ harness . pressKey ( "å" ) ;
811+ await harness . renderOnce ( ) ;
812+ expect ( host . shell . overlayKind ) . toBeNull ( ) ;
813+ expect ( host . shell . prompt . value ) . toContain ( "å" ) ;
814+ } finally {
815+ host . dispose ( ) ;
816+ harness . destroy ( ) ;
817+ }
818+ } ) ;
819+
820+ test ( "other composed glyphs still type-to-filter in the model picker" , async ( ) => {
821+ const { harness, host } = await mountPicker ( {
822+ onConnectProvider : ( ) => { } ,
823+ addProviderChoices : ( ) => [ { id : "codex" , label : "Codex" , hint : "" , accountCount : 0 } ] ,
824+ } ) ;
825+ try {
826+ host . openModels ?.( ) ;
827+ await harness . renderOnce ( ) ;
828+ for ( const glyph of [ "ø" , "ä" , "æ" ] as const ) {
829+ const composed = {
830+ name : glyph ,
831+ sequence : glyph ,
832+ ctrl : false ,
833+ meta : false ,
834+ option : false ,
835+ } as KeyEvent ;
836+ expect ( handleListFilterKey ( host . shell , composed ) ) . toBe ( true ) ;
837+ expect ( host . shell . overlayKind ) . toBe ( "model_picker" ) ;
838+ }
839+ } finally {
840+ host . dispose ( ) ;
841+ harness . destroy ( ) ;
842+ }
843+ } ) ;
844+
845+ test ( "sequence-only å with name a opens add-provider from the model picker" , async ( ) => {
846+ // Terminals can report Option+A as sequence å while name stays ASCII a
847+ // and option/meta stay false (#482).
848+ const { harness, host } = await mountPicker ( {
849+ onConnectProvider : ( ) => { } ,
850+ addProviderChoices : ( ) => [ { id : "codex" , label : "Codex" , hint : "" , accountCount : 0 } ] ,
851+ } ) ;
852+ try {
853+ host . openModels ?.( ) ;
854+ await harness . renderOnce ( ) ;
855+ const sequenceOnly = {
856+ name : "a" ,
857+ sequence : "å" ,
858+ ctrl : false ,
859+ meta : false ,
860+ option : false ,
861+ } as KeyEvent ;
862+ expect ( handleListFilterKey ( host . shell , sequenceOnly ) ) . toBe ( false ) ;
863+ expect ( runOverlayAction ( host . shell , sequenceOnly ) ) . toBe ( true ) ;
864+ expect ( host . shell . overlayKind ) . toBe ( "add_provider" ) ;
865+ } finally {
866+ host . dispose ( ) ;
867+ harness . destroy ( ) ;
868+ }
869+ } ) ;
870+
802871 test ( "composed å still type-to-filters when add-provider is not wired" , async ( ) => {
803872 const { harness, host } = await mountPicker ( ) ;
804873 try {
@@ -901,7 +970,8 @@ describe("flat type-to-filter model picker", () => {
901970 runOverlayAction ( host . shell , altA ) ;
902971 await harness . renderOnce ( ) ;
903972 expect ( host . shell . overlayKind ) . toBe ( "add_provider" ) ;
904- closeInsetOverlay ( host . shell ) ;
973+ harness . pressKey ( "Escape" ) ;
974+ await new Promise ( ( r ) => setTimeout ( r , 60 ) ) ;
905975 await harness . renderOnce ( ) ;
906976 expect ( host . shell . overlayKind ) . toBe ( "model_picker" ) ;
907977 expect ( host . shell . overlayItems ) . toEqual ( modelItems ) ;
@@ -921,7 +991,8 @@ describe("flat type-to-filter model picker", () => {
921991 host . openAddProvider ?.( ) ;
922992 await harness . renderOnce ( ) ;
923993 expect ( host . shell . overlayKind ) . toBe ( "add_provider" ) ;
924- closeInsetOverlay ( host . shell ) ;
994+ harness . pressKey ( "Escape" ) ;
995+ await new Promise ( ( r ) => setTimeout ( r , 60 ) ) ;
925996 await harness . renderOnce ( ) ;
926997 expect ( host . shell . overlayKind ) . not . toBe ( "model_picker" ) ;
927998 expect ( host . shell . overlayKind ) . toBeNull ( ) ;
@@ -931,6 +1002,41 @@ describe("flat type-to-filter model picker", () => {
9311002 }
9321003 } ) ;
9331004
1005+ test ( "typed /connect then Enter opens add-provider and Esc leaves overlay null" , async ( ) => {
1006+ const queued : { open ?: ( ) => void } = { } ;
1007+ const { harness, host } = await mountPicker ( {
1008+ onConnectProvider : ( ) => { } ,
1009+ addProviderChoices : ( ) => [ { id : "codex" , label : "Codex" , hint : "" , accountCount : 1 } ] ,
1010+ commands : [
1011+ {
1012+ id : "connect" ,
1013+ label : "/connect" ,
1014+ description : "Add a provider account" ,
1015+ keywords : [ "connect" , "Add a provider account" , "slash" , "command" ] ,
1016+ } ,
1017+ ] ,
1018+ onCommand : ( name ) => {
1019+ if ( name === "connect" ) queued . open ?.( ) ;
1020+ } ,
1021+ } ) ;
1022+ queued . open = ( ) => host . openAddProvider ?.( ) ;
1023+ try {
1024+ expect ( host . shell . overlayKind ) . toBeNull ( ) ;
1025+ for ( const ch of "/connect" ) harness . pressKey ( ch ) ;
1026+ await harness . renderOnce ( ) ;
1027+ harness . pressKey ( "Enter" ) ;
1028+ await harness . renderOnce ( ) ;
1029+ expect ( host . shell . overlayKind ) . toBe ( "add_provider" ) ;
1030+ harness . pressKey ( "Escape" ) ;
1031+ await new Promise ( ( r ) => setTimeout ( r , 60 ) ) ;
1032+ await harness . renderOnce ( ) ;
1033+ expect ( host . shell . overlayKind ) . toBeNull ( ) ;
1034+ } finally {
1035+ host . dispose ( ) ;
1036+ harness . destroy ( ) ;
1037+ }
1038+ } ) ;
1039+
9341040 test ( "Enter on an add-provider row runs the connect flow for that provider" , async ( ) => {
9351041 const connected : string [ ] = [ ] ;
9361042 const { harness, host } = await mountPicker ( {
0 commit comments