@@ -189,6 +189,13 @@ class FakeTerminal implements Terminal {
189189 }
190190}
191191
192+ // Apple Terminal can save ED 2 clears into scrollback before the renderer clears history.
193+ class ClearToScrollbackTerminal extends VirtualTerminal {
194+ override write ( data : string ) : void {
195+ super . write ( data . replaceAll ( "\x1b[2J" , `\x1b[${ this . rows } ;1H${ "\r\n" . repeat ( this . rows ) } \x1b[2J` ) ) ;
196+ }
197+ }
198+
192199function renderTerminalViewport (
193200 app : ReturnType < typeof createTuiApp > ,
194201 terminal : FakeTerminal ,
@@ -6041,6 +6048,93 @@ describe("createTuiApp", () => {
60416048 await app . stop ( ) ;
60426049 } ) ;
60436050
6051+ it ( "does not push the conversation downward after closing /usage" , async ( ) => {
6052+ const terminal = new VirtualTerminal ( 80 , 50 ) ;
6053+ const runtime = createRuntime ( ) ;
6054+ vi . mocked ( runtime . getAccountStatus ) . mockResolvedValue ( {
6055+ status : "ready" ,
6056+ authMode : "managed-login" ,
6057+ managedTokenPresent : true ,
6058+ modelSource : "token-plan" ,
6059+ tokenPlanQuotaState : "available" ,
6060+ tokenPlanSummary : { tier : "Ultra Plan" , creditBalance : "100" } ,
6061+ tokenPlanQuota : {
6062+ fiveHour : { remainingPercent : 75 , unlimited : false } ,
6063+ weekly : { remainingPercent : 50 , unlimited : false } ,
6064+ video : { remainingCount : 5 , totalCount : 5 , unlimited : false } ,
6065+ } ,
6066+ warnings : [ ] ,
6067+ } ) ;
6068+ const app = createTuiApp ( {
6069+ runtime,
6070+ terminal,
6071+ version : "0.1.0" ,
6072+ workspaceDir : "/workspace" ,
6073+ } ) ;
6074+
6075+ app . start ( ) ;
6076+ try {
6077+ await app . ready ;
6078+ for ( let index = 0 ; index < 2 ; index ++ ) await app . submit ( `Message ${ index } ` ) ;
6079+ app . tui . renderNow ( ) ;
6080+ await terminal . flush ( ) ;
6081+ expect ( terminal . getViewport ( ) . join ( "\n" ) ) . toContain ( "Message 1" ) ;
6082+ const firstContentRowBefore = terminal . getViewport ( ) . findIndex ( ( line ) => line . trim ( ) . length > 0 ) ;
6083+
6084+ await app . submit ( "/usage" ) ;
6085+ app . tui . renderNow ( ) ;
6086+ await terminal . flush ( ) ;
6087+ expect ( terminal . getViewport ( ) . join ( "\n" ) ) . toContain ( "Usage" ) ;
6088+
6089+ terminal . sendInput ( "\x1b" ) ;
6090+ app . tui . renderNow ( ) ;
6091+ await terminal . flush ( ) ;
6092+ const viewport = terminal . getViewport ( ) . join ( "\n" ) ;
6093+ expect ( viewport ) . toContain ( "Message 1" ) ;
6094+ expect ( viewport ) . toContain ( "Message" ) ;
6095+ expect ( app . tui . render ( 80 ) . findIndex ( ( line ) => line . trim ( ) . length > 0 ) ) . toBe ( firstContentRowBefore ) ;
6096+ expect ( terminal . getViewport ( ) . findIndex ( ( line ) => line . trim ( ) . length > 0 ) ) . toBe ( firstContentRowBefore ) ;
6097+ } finally {
6098+ await app . stop ( ) ;
6099+ }
6100+ } ) ;
6101+
6102+ it . each ( [
6103+ [ 'xterm' , VirtualTerminal ] ,
6104+ [ 'clear-to-scrollback terminal' , ClearToScrollbackTerminal ] ,
6105+ ] as const ) ( 'restores unique long history after /usage on %s' , async ( _name , Terminal ) => {
6106+ const terminal = new Terminal ( 80 , 24 ) ;
6107+ const app = createTuiApp ( {
6108+ runtime : createRuntime ( ) ,
6109+ terminal,
6110+ version : "0.1.0" ,
6111+ workspaceDir : "/workspace" ,
6112+ } ) ;
6113+
6114+ app . start ( ) ;
6115+ try {
6116+ await app . ready ;
6117+ for ( let index = 0 ; index < 12 ; index ++ ) await app . submit ( `Message ${ index } ` ) ;
6118+ await app . submit ( "/usage" ) ;
6119+ app . tui . renderNow ( ) ;
6120+ await terminal . flush ( ) ;
6121+ expect ( terminal . getViewport ( ) . join ( "\n" ) ) . toContain ( "Usage" ) ;
6122+
6123+ terminal . sendInput ( "\x1b" ) ;
6124+ app . tui . renderNow ( ) ;
6125+ await terminal . flush ( ) ;
6126+ const history = terminal . getScrollBuffer ( ) ;
6127+ for ( let index = 0 ; index < 12 ; index ++ ) {
6128+ expect ( history . filter ( ( line ) => line . trimEnd ( ) . endsWith ( `› Message ${ index } ` ) ) ) . toHaveLength ( 1 ) ;
6129+ }
6130+ expect ( history . join ( "\n" ) ) . not . toContain ( "Session usage" ) ;
6131+ expect ( terminal . getViewport ( ) . join ( "\n" ) ) . toContain ( "Ask Mcode to do anything" ) ;
6132+ expect ( terminal . getViewport ( ) . join ( "\n" ) ) . toContain ( "/workspace" ) ;
6133+ } finally {
6134+ await app . stop ( ) ;
6135+ }
6136+ } ) ;
6137+
60446138 it ( "uses public Runtime owners for Session inspection commands" , async ( ) => {
60456139 const terminal = new FakeTerminal ( ) ;
60466140 terminal . rows = 40 ;
0 commit comments