@@ -25,23 +25,38 @@ afterEach(() => {
2525} ) ;
2626
2727/** Live heading colour in the transcript registry. */
28- function registryHeadingFg ( ) : RGBA | undefined {
29- return transcriptSyntaxStyle ( ) . getAllStyles ( ) . get ( "markup.heading.1" ) ?. fg ;
28+ function registryHeadingFg ( h : Harness ) : RGBA | undefined {
29+ return transcriptSyntaxStyle ( h . renderer )
30+ . getAllStyles ( )
31+ . get ( "markup.heading.1" ) ?. fg ;
3032}
3133
3234function painted ( spans : CapturedSpan [ ] , text : string , fg : RGBA ) : boolean {
3335 return spans . some ( ( s ) => s . text . includes ( text ) && s . fg . equals ( fg ) ) ;
3436}
3537
38+ /** 0.6.x leases a session buffer around each presented frame; a capture
39+ * landing inside that window throws OutputBusy. The poll retries on the
40+ * next round anyway, so a losing capture keeps polling instead of
41+ * throwing — the ready predicate, never the trace, decides the outcome. */
42+ function readSpans ( h : Harness ) : CapturedSpan [ ] {
43+ try {
44+ return h . captureSpans ( ) . lines . flatMap ( ( line ) => line . spans ) ;
45+ } catch ( err ) {
46+ if ( err instanceof Error && err . message . includes ( "OutputBusy" ) ) return [ ] ;
47+ throw err ;
48+ }
49+ }
50+
3651async function settleSpans (
3752 h : Harness ,
3853 ready : ( spans : CapturedSpan [ ] ) => boolean ,
3954) : Promise < CapturedSpan [ ] > {
40- let spans = h . captureSpans ( ) . lines . flatMap ( ( line ) => line . spans ) ;
55+ let spans = readSpans ( h ) ;
4156 for ( let i = 0 ; i < 400 && ! ready ( spans ) ; i ++ ) {
4257 await h . renderOnce ( ) ;
4358 await new Promise ( ( resolve ) => setTimeout ( resolve , 25 ) ) ;
44- spans = h . captureSpans ( ) . lines . flatMap ( ( line ) => line . spans ) ;
59+ spans = readSpans ( h ) ;
4560 }
4661 return spans ;
4762}
@@ -64,7 +79,7 @@ describe("applyThemePinLive", () => {
6479 painted ( spans , "pin cycle" , darkHeading ) ,
6580 ) ;
6681 expect ( painted ( before , "pin cycle" , darkHeading ) ) . toBe ( true ) ;
67- expect ( registryHeadingFg ( ) ?. equals ( darkHeading ) ) . toBe ( true ) ;
82+ expect ( registryHeadingFg ( h ) ?. equals ( darkHeading ) ) . toBe ( true ) ;
6883
6984 // No cost movement anywhere in this cycle: the meter never moves,
7085 // so only an unconditional repaint repaints anything.
@@ -74,7 +89,7 @@ describe("applyThemePinLive", () => {
7489 expect ( applied ) . toBe ( "corbits-light" ) ;
7590 expect ( UI . name ) . toBe ( "corbits-light" ) ;
7691 expect ( UI . ground ) . not . toBe ( darkGround ) ;
77- expect ( registryHeadingFg ( ) ?. equals ( lightHeading ) ) . toBe ( true ) ;
92+ expect ( registryHeadingFg ( h ) ?. equals ( lightHeading ) ) . toBe ( true ) ;
7893
7994 const after = await settleSpans ( h , ( spans ) =>
8095 painted ( spans , "pin cycle" , lightHeading ) ,
@@ -106,14 +121,14 @@ describe("applyThemePinLive", () => {
106121 painted ( spans , "pin cycle" , lightHeading ) ,
107122 ) ;
108123 expect ( painted ( before , "pin cycle" , lightHeading ) ) . toBe ( true ) ;
109- expect ( registryHeadingFg ( ) ?. equals ( lightHeading ) ) . toBe ( true ) ;
124+ expect ( registryHeadingFg ( h ) ?. equals ( lightHeading ) ) . toBe ( true ) ;
110125
111126 const applied = applyThemePinLive ( "dark" , ( ) =>
112127 repaintShellForTheme ( shell ) ,
113128 ) ;
114129 expect ( applied ) . toBe ( "corbits-dark" ) ;
115130 expect ( UI . name ) . toBe ( "corbits-dark" ) ;
116- expect ( registryHeadingFg ( ) ?. equals ( darkHeading ) ) . toBe ( true ) ;
131+ expect ( registryHeadingFg ( h ) ?. equals ( darkHeading ) ) . toBe ( true ) ;
117132
118133 const after = await settleSpans ( h , ( spans ) =>
119134 painted ( spans , "pin cycle" , darkHeading ) ,
@@ -129,11 +144,13 @@ describe("applyThemePinLive", () => {
129144 ) ;
130145 } , 30000 ) ;
131146
132- test ( "auto re-resolves through the startup path and rebuilds the registry" , ( ) => {
147+ test ( "auto re-resolves through the startup path and rebuilds the registry" , async ( ) => {
133148 setTheme ( "corbits-dark" ) ;
134- const applied = applyThemePinLive ( "auto" , ( ) => undefined ) ;
135- expect ( [ "corbits-dark" , "corbits-light" ] ) . toContain ( applied ) ;
136- expect ( UI . name ) . toBe ( applied ) ;
137- expect ( registryHeadingFg ( ) ?. equals ( RGBA . fromHex ( UI . heading ) ) ) . toBe ( true ) ;
149+ await withTestRenderer ( async ( h ) => {
150+ const applied = applyThemePinLive ( "auto" , ( ) => undefined ) ;
151+ expect ( [ "corbits-dark" , "corbits-light" ] ) . toContain ( applied ) ;
152+ expect ( UI . name ) . toBe ( applied ) ;
153+ expect ( registryHeadingFg ( h ) ?. equals ( RGBA . fromHex ( UI . heading ) ) ) . toBe ( true ) ;
154+ } ) ;
138155 } ) ;
139156} ) ;
0 commit comments