@@ -104,6 +104,9 @@ export class TermWrap {
104104 // xterm.js paste() method triggers onData event, which can cause duplicate sends
105105 lastPasteData : string = "" ;
106106 lastPasteTime : number = 0 ;
107+ lastAtBottomTime : number = Date . now ( ) ;
108+ lastScrollAtBottom : boolean = true ;
109+ cachedAtBottomForResize : boolean | null = null ;
107110
108111 constructor (
109112 tabId : string ,
@@ -226,6 +229,19 @@ export class TermWrap {
226229 this . connectElem . removeEventListener ( "paste" , pasteHandler , true ) ;
227230 } ,
228231 } ) ;
232+ const viewportElem = this . connectElem . querySelector ( ".xterm-viewport" ) as HTMLElement ;
233+ if ( viewportElem ) {
234+ const scrollHandler = ( ) => {
235+ const atBottom = viewportElem . scrollTop + viewportElem . clientHeight >= viewportElem . scrollHeight - 20 ;
236+ this . setAtBottom ( atBottom ) ;
237+ } ;
238+ viewportElem . addEventListener ( "scroll" , scrollHandler ) ;
239+ this . toDispose . push ( {
240+ dispose : ( ) => {
241+ viewportElem . removeEventListener ( "scroll" , scrollHandler ) ;
242+ } ,
243+ } ) ;
244+ }
229245 }
230246
231247 getZoneId ( ) : string {
@@ -465,9 +481,30 @@ export class TermWrap {
465481 }
466482 }
467483
484+ setAtBottom ( atBottom : boolean ) {
485+ if ( this . lastScrollAtBottom && ! atBottom ) {
486+ this . lastAtBottomTime = Date . now ( ) ;
487+ }
488+ this . lastScrollAtBottom = atBottom ;
489+ if ( atBottom ) {
490+ this . lastAtBottomTime = Date . now ( ) ;
491+ }
492+ }
493+
494+ wasRecentlyAtBottom ( ) : boolean {
495+ if ( this . lastScrollAtBottom ) {
496+ return true ;
497+ }
498+ return Date . now ( ) - this . lastAtBottomTime <= 1000 ;
499+ }
500+
468501 handleResize ( ) {
469502 const oldRows = this . terminal . rows ;
470503 const oldCols = this . terminal . cols ;
504+ const atBottom = this . cachedAtBottomForResize ?? this . wasRecentlyAtBottom ( ) ;
505+ if ( ! atBottom ) {
506+ this . cachedAtBottomForResize = null ;
507+ }
471508 this . fitAddon . fit ( ) ;
472509 if ( oldRows !== this . terminal . rows || oldCols !== this . terminal . cols ) {
473510 const termSize : TermSize = { rows : this . terminal . rows , cols : this . terminal . cols } ;
@@ -478,6 +515,13 @@ export class TermWrap {
478515 this . hasResized = true ;
479516 this . resyncController ( "initial resize" ) ;
480517 }
518+ if ( atBottom ) {
519+ setTimeout ( ( ) => {
520+ this . cachedAtBottomForResize = null ;
521+ this . terminal . scrollToBottom ( ) ;
522+ this . setAtBottom ( true ) ;
523+ } , 20 ) ;
524+ }
481525 }
482526
483527 processAndCacheData ( ) {
0 commit comments