@@ -45,6 +45,7 @@ const TermCacheFileName = "cache:term:full";
4545const MinDataProcessedForCache = 100 * 1024 ;
4646export const SupportsImageInput = true ;
4747const IMEDedupWindowMs = 20 ;
48+ const MaxRepaintTransactionMs = 2000 ;
4849
4950// detect webgl support
5051function detectWebGLSupport ( ) : boolean {
@@ -113,6 +114,10 @@ export class TermWrap {
113114 recentWrites : { idx : number ; data : string ; ts : number } [ ] = [ ] ;
114115 recentWritesCounter : number = 0 ;
115116 lastClearScrollbackTs : number = 0 ;
117+ lastMode2026SetTs : number = 0 ;
118+ lastMode2026ResetTs : number = 0 ;
119+ inSyncTransaction : boolean = false ;
120+ inRepaintTransaction : boolean = false ;
116121
117122 constructor (
118123 tabId : string ,
@@ -197,6 +202,36 @@ export class TermWrap {
197202 this . terminal . parser . registerCsiHandler ( { final : "J" } , ( params ) => {
198203 if ( params [ 0 ] === 3 ) {
199204 this . lastClearScrollbackTs = Date . now ( ) ;
205+ if ( this . inSyncTransaction ) {
206+ console . log ( "[termwrap] repaint transaction starting" ) ;
207+ this . inRepaintTransaction = true ;
208+ }
209+ }
210+ return false ;
211+ } )
212+ ) ;
213+ this . toDispose . push (
214+ this . terminal . parser . registerCsiHandler ( { prefix : "?" , final : "h" } , ( params ) => {
215+ if ( params [ 0 ] === 2026 ) {
216+ this . lastMode2026SetTs = Date . now ( ) ;
217+ this . inSyncTransaction = true ;
218+ }
219+ return false ;
220+ } )
221+ ) ;
222+ this . toDispose . push (
223+ this . terminal . parser . registerCsiHandler ( { prefix : "?" , final : "l" } , ( params ) => {
224+ if ( params [ 0 ] === 2026 ) {
225+ this . lastMode2026ResetTs = Date . now ( ) ;
226+ this . inSyncTransaction = false ;
227+ const wasRepaint = this . inRepaintTransaction ;
228+ this . inRepaintTransaction = false ;
229+ if ( wasRepaint && Date . now ( ) - this . lastClearScrollbackTs <= MaxRepaintTransactionMs ) {
230+ setTimeout ( ( ) => {
231+ console . log ( "[termwrap] repaint transaction complete, scrolling to bottom" ) ;
232+ this . terminal . scrollToBottom ( ) ;
233+ } , 20 ) ;
234+ }
200235 }
201236 return false ;
202237 } )
@@ -568,7 +603,14 @@ export class TermWrap {
568603 this . fitAddon . fit ( ) ;
569604 if ( oldRows !== this . terminal . rows || oldCols !== this . terminal . cols ) {
570605 const termSize : TermSize = { rows : this . terminal . rows , cols : this . terminal . cols } ;
571- console . log ( "[termwrap] resize" , `${ oldRows } x${ oldCols } ` , "->" , `${ this . terminal . rows } x${ this . terminal . cols } ` , "atBottom:" , atBottom ) ;
606+ console . log (
607+ "[termwrap] resize" ,
608+ `${ oldRows } x${ oldCols } ` ,
609+ "->" ,
610+ `${ this . terminal . rows } x${ this . terminal . cols } ` ,
611+ "atBottom:" ,
612+ atBottom
613+ ) ;
572614 RpcApi . ControllerInputCommand ( TabRpcClient , { blockid : this . blockId , termsize : termSize } ) ;
573615 }
574616 dlog ( "resize" , `${ this . terminal . rows } x${ this . terminal . cols } ` , `${ oldRows } x${ oldCols } ` , this . hasResized ) ;
0 commit comments