@@ -68,7 +68,7 @@ function createMockTerminal() {
6868 focus,
6969 element : null ,
7070 buffer : {
71- active : { baseY : 0 , length : 24 } ,
71+ active : { baseY : 0 , viewportY : 0 , length : 24 } ,
7272 } ,
7373 hasSelection : ( ) => false ,
7474 options : { } ,
@@ -88,6 +88,8 @@ vi.mock('../../lib/terminal-utils', () => ({
8888 } ,
8989 createFitAddon : ( ) => ( { fit : vi . fn ( ) } ) ,
9090 attachKeyHandlers : vi . fn ( ( ) => ( ) => { } ) ,
91+ isTerminalViewportAtBottom : ( terminal : any , viewportY = terminal . buffer . active . viewportY ) =>
92+ viewportY >= terminal . buffer . active . baseY ,
9193 loadAddons : vi . fn ( ) ,
9294 updateTerminalTheme : vi . fn ( ) ,
9395} ) ) ;
@@ -188,10 +190,11 @@ describe('TerminalPool', () => {
188190
189191 expect ( handle . autoScroll ) . toBe ( true ) ;
190192
191- // Simulate user being scrolled up (baseY is 0, length is 100)
192- terminal . buffer . active . baseY = 0 ;
193+ // Simulate user being scrolled up. `baseY` marks the viewport position when
194+ // fully scrolled down, while `viewportY` is where the user currently is.
195+ terminal . buffer . active . baseY = 76 ;
196+ terminal . buffer . active . viewportY = 0 ;
193197 terminal . buffer . active . length = 100 ;
194- // 0 + 24 < 100, so we're scrolled up
195198
196199 // Simulate PTY output
197200 const data = new TextEncoder ( ) . encode ( 'new output' ) ;
@@ -203,19 +206,18 @@ describe('TerminalPool', () => {
203206 } ) ;
204207
205208 describe ( 'scroll position detection' , ( ) => {
206- it ( 'detects at-bottom when baseY + rows >= length' , ( ) => {
207- // This tests the logic: buffer.baseY + terminal.rows >= buffer.length
209+ it ( 'detects at-bottom when viewportY reaches baseY' , ( ) => {
208210 const cases = [
209- { baseY : 0 , rows : 24 , length : 24 , expected : true } , // exactly at bottom
210- { baseY : 10 , rows : 24 , length : 34 , expected : true } , // scrollback, at bottom
211- { baseY : 10 , rows : 24 , length : 35 , expected : false } , // 1 line scrolled up
212- { baseY : 0 , rows : 24 , length : 50 , expected : false } , // scrolled to top
213- { baseY : 100 , rows : 24 , length : 124 , expected : true } , // large buffer, at bottom
214- { baseY : 100 , rows : 24 , length : 125 , expected : false } , // large buffer, 1 line up
211+ { baseY : 0 , viewportY : 0 , expected : true } , // no scrollback
212+ { baseY : 10 , viewportY : 10 , expected : true } , // scrolled to bottom
213+ { baseY : 10 , viewportY : 9 , expected : false } , // 1 line above bottom
214+ { baseY : 76 , viewportY : 0 , expected : false } , // top of long scrollback
215+ { baseY : 100 , viewportY : 100 , expected : true } , // large buffer, at bottom
216+ { baseY : 100 , viewportY : 50 , expected : false } , // large buffer, midway up
215217 ] ;
216218
217- for ( const { baseY, rows , length , expected } of cases ) {
218- const isAtBottom = baseY + rows >= length ;
219+ for ( const { baseY, viewportY , expected } of cases ) {
220+ const isAtBottom = viewportY >= baseY ;
219221 expect ( isAtBottom ) . toBe ( expected ) ;
220222 }
221223 } ) ;
0 commit comments