@@ -106,6 +106,9 @@ export class TermWrap {
106106 // xterm.js paste() method triggers onData event, which can cause duplicate sends
107107 lastPasteData : string = "" ;
108108 lastPasteTime : number = 0 ;
109+ lastAtBottomTime : number = Date . now ( ) ;
110+ lastScrollAtBottom : boolean = true ;
111+ cachedAtBottomForResize : boolean | null = null ;
109112
110113 constructor (
111114 tabId : string ,
@@ -241,6 +244,19 @@ export class TermWrap {
241244 this . connectElem . removeEventListener ( "paste" , pasteHandler , true ) ;
242245 } ,
243246 } ) ;
247+ const viewportElem = this . connectElem . querySelector ( ".xterm-viewport" ) as HTMLElement ;
248+ if ( viewportElem ) {
249+ const scrollHandler = ( ) => {
250+ const atBottom = viewportElem . scrollTop + viewportElem . clientHeight >= viewportElem . scrollHeight - 20 ;
251+ this . setAtBottom ( atBottom ) ;
252+ } ;
253+ viewportElem . addEventListener ( "scroll" , scrollHandler ) ;
254+ this . toDispose . push ( {
255+ dispose : ( ) => {
256+ viewportElem . removeEventListener ( "scroll" , scrollHandler ) ;
257+ } ,
258+ } ) ;
259+ }
244260 }
245261
246262 getZoneId ( ) : string {
@@ -509,8 +525,29 @@ export class TermWrap {
509525 }
510526 }
511527
528+ setAtBottom ( atBottom : boolean ) {
529+ if ( this . lastScrollAtBottom && ! atBottom ) {
530+ this . lastAtBottomTime = Date . now ( ) ;
531+ }
532+ this . lastScrollAtBottom = atBottom ;
533+ if ( atBottom ) {
534+ this . lastAtBottomTime = Date . now ( ) ;
535+ }
536+ }
537+
538+ wasRecentlyAtBottom ( ) : boolean {
539+ if ( this . lastScrollAtBottom ) {
540+ return true ;
541+ }
542+ return Date . now ( ) - this . lastAtBottomTime <= 1000 ;
543+ }
544+
512545 handleResize ( ) {
513546 const oldTermSize = this . getTermSize ( ) ;
547+ const atBottom = this . cachedAtBottomForResize ?? this . wasRecentlyAtBottom ( ) ;
548+ if ( ! atBottom ) {
549+ this . cachedAtBottomForResize = null ;
550+ }
514551 this . fitAddon . fit ( ) ;
515552 const newTermSize = this . getTermSize ( ) ;
516553 if ( ! this . areTermSizesEqual ( oldTermSize , newTermSize ) ) {
@@ -526,6 +563,13 @@ export class TermWrap {
526563 this . hasResized = true ;
527564 this . resyncController ( "initial resize" ) ;
528565 }
566+ if ( atBottom ) {
567+ setTimeout ( ( ) => {
568+ this . cachedAtBottomForResize = null ;
569+ this . terminal . scrollToBottom ( ) ;
570+ this . setAtBottom ( true ) ;
571+ } , 20 ) ;
572+ }
529573 }
530574
531575 processAndCacheData ( ) {
0 commit comments