@@ -476,24 +476,88 @@ function inferMaestroMissingTabSlotMatch(
476476 query : string ,
477477) : MaestroResolvedSnapshotMatch | null {
478478 if ( ! isMaestroTabStripContainerMatch ( match , query ) ) return null ;
479- const children : Array < SnapshotNode & { rect : Rect } > = [ ] ;
480- for ( const node of nodes ) {
481- if ( node . parentIndex !== match . node . index || ! node . rect ) continue ;
482- const candidate = node as SnapshotNode & { rect : Rect } ;
483- if ( isMaestroTabStripChildCandidate ( candidate , match . rect , query ) ) {
484- children . push ( candidate ) ;
485- }
486- }
487- children . sort ( ( left , right ) => left . rect . x - right . rect . x ) ;
479+ const children = collectMaestroTabStripChildCandidates ( nodes , match , query ) ;
488480 if ( children . length === 0 ) return null ;
489481 const medianChildWidth = median ( children . map ( ( child ) => child . rect . width ) ) ;
490- const gaps = resolveHorizontalGaps (
482+ const allGaps = resolveHorizontalGaps (
491483 match . rect ,
492484 children . map ( ( child ) => child . rect ) ,
493- ) . filter ( ( gap ) => isPlausibleMissingTabSlot ( gap . width , medianChildWidth ) ) ;
494- if ( gaps . length !== 1 ) return null ;
495- const gap = gaps [ 0 ] ;
485+ ) ;
486+ const gap = selectMaestroMissingSlotGap ( match , query , allGaps , medianChildWidth ) ;
496487 if ( ! gap ) return null ;
488+ return matchWithRect ( match , gap ) ;
489+ }
490+
491+ function collectMaestroTabStripChildCandidates (
492+ nodes : SnapshotState [ 'nodes' ] ,
493+ match : MaestroResolvedSnapshotMatch ,
494+ query : string ,
495+ ) : Array < SnapshotNode & { rect : Rect } > {
496+ return nodes
497+ . filter ( ( node ) : node is SnapshotNode & { rect : Rect } => {
498+ return (
499+ node . parentIndex === match . node . index &&
500+ Boolean ( node . rect ) &&
501+ isMaestroTabStripChildCandidate ( node as SnapshotNode & { rect : Rect } , match . rect , query )
502+ ) ;
503+ } )
504+ . sort ( ( left , right ) => left . rect . x - right . rect . x ) ;
505+ }
506+
507+ function selectMaestroMissingSlotGap (
508+ match : MaestroResolvedSnapshotMatch ,
509+ query : string ,
510+ gaps : Array < { x : number ; width : number } > ,
511+ medianChildWidth : number ,
512+ ) : { x : number ; width : number } | null {
513+ const plausibleGaps = gaps . filter ( ( gap ) => isPlausibleMissingTabSlot ( gap . width , medianChildWidth ) ) ;
514+ const leadingTextSlot = inferMaestroLeadingTextSlotGap ( match , query , gaps ) ;
515+ const hasPlausibleLeadingGap = plausibleGaps . some ( ( gap ) => isLeadingGap ( match . rect , gap ) ) ;
516+ if ( leadingTextSlot && ! hasPlausibleLeadingGap ) return leadingTextSlot ;
517+ if ( plausibleGaps . length === 1 ) return plausibleGaps [ 0 ] ?? null ;
518+ return leadingTextSlot ;
519+ }
520+
521+ function inferMaestroLeadingTextSlotGap (
522+ match : MaestroResolvedSnapshotMatch ,
523+ query : string ,
524+ gaps : Array < { x : number ; width : number } > ,
525+ ) : { x : number ; width : number } | null {
526+ const leadingGap = gaps . find ( ( gap ) => Math . abs ( gap . x - match . rect . x ) < 1 ) ;
527+ const estimatedLabelWidth = Math . max ( 48 , Math . min ( 220 , query . length * 8 + 24 ) ) ;
528+ if ( ! isLeadingTextSlotCandidate ( match , query , leadingGap , estimatedLabelWidth ) ) return null ;
529+ return {
530+ x : match . rect . x ,
531+ width : Math . min ( estimatedLabelWidth , leadingGap . width ) ,
532+ } ;
533+ }
534+
535+ function isLeadingTextSlotCandidate (
536+ match : MaestroResolvedSnapshotMatch ,
537+ query : string ,
538+ gap : { x : number ; width : number } | undefined ,
539+ estimatedLabelWidth : number ,
540+ ) : gap is { x : number ; width : number } {
541+ if ( ! gap ) return false ;
542+ return (
543+ normalizeType ( match . node . type ?? '' ) === 'scrollview' &&
544+ maestroVisibleTextMatchRank ( match . node , query ) <= 1 &&
545+ match . rect . width >= 240 &&
546+ match . rect . height >= 32 &&
547+ match . rect . height <= 80 &&
548+ gap . width <= match . rect . width * 0.55 &&
549+ gap . width >= estimatedLabelWidth * 0.6
550+ ) ;
551+ }
552+
553+ function isLeadingGap ( rect : Rect , gap : { x : number ; width : number } ) : boolean {
554+ return Math . abs ( gap . x - rect . x ) < 1 ;
555+ }
556+
557+ function matchWithRect (
558+ match : MaestroResolvedSnapshotMatch ,
559+ gap : { x : number ; width : number } ,
560+ ) : MaestroResolvedSnapshotMatch {
497561 return {
498562 ...match ,
499563 rect : {
0 commit comments