9090 border-radius : 0 0 10px 10px ;
9191 box-shadow : 0 10px 40px rgba (0 , 0 , 0 , 0.3 );
9292 overflow : hidden;
93- flex : 1 ;
9493 display : flex;
9594 flex-direction : column;
96- min- height: 600 px ;
95+ height : 400 px ;
9796 }
9897
9998 # terminal-container {
10099 flex : 1 ;
101100 padding : 10px ;
102101 background : # 1e1e1e ;
103102 position : relative;
103+ overflow : hidden; /* Prevent browser scrolling */
104104 }
105105
106106 .info-box {
@@ -281,21 +281,19 @@ <h1>🖥️ Ghostty Terminal</h1>
281281 </ div >
282282
283283 < div class ="feature-panel ">
284- < h3 > 🎯 xterm.js API Features</ h3 >
284+ < h3 > 📜 Scrolling Features</ h3 >
285285 < div class ="button-grid ">
286- < button class ="test-button " id ="btn-paste " > 📋 Test paste() </ button >
287- < button class ="test-button " id ="btn-blur " > 👁️ Test blur() </ button >
288- < button class ="test-button " id ="btn-input " > ⌨️ Test input() </ button >
289- < button class ="test-button " id ="btn-select " > 🔤 Test select() </ button >
290- < button class ="test-button " id ="btn-selectLines " > 📄 Test selectLines() </ button >
291- < button class ="test-button " id ="btn-getSelectionPos " > 📍 Get Selection Position </ button >
292- < button class ="test-button " id ="btn-setTitle " > 🏷️ Set Title (OSC) </ button >
293- < button class ="test-button " id ="btn-customHandler " > 🔧 Toggle Custom Handler </ button >
286+ < button class ="test-button " id ="btn-scrollUp " > ⬆️ Scroll Up 5 Lines </ button >
287+ < button class ="test-button " id ="btn-scrollDown " > ⬇️ Scroll Down 5 Lines </ button >
288+ < button class ="test-button " id ="btn-scrollPageUp " > 📄⬆️ Scroll Page Up </ button >
289+ < button class ="test-button " id ="btn-scrollPageDown " > 📄⬇️ Scroll Page Down </ button >
290+ < button class ="test-button " id ="btn-scrollToTop " > 🔝 Scroll to Top </ button >
291+ < button class ="test-button " id ="btn-scrollToBottom " > ⬇️ Scroll to Bottom </ button >
292+ < button class ="test-button " id ="btn-scrollToLine " > 🎯 Scroll to Line 10 </ button >
293+ < button class ="test-button " id ="btn-generateContent " > 📝 Generate Scrollback </ button >
294294 </ div >
295295 < div >
296- < strong style ="display: block; margin-bottom: 8px "
297- > Event Log (onKey, onTitleChange):</ strong
298- >
296+ < strong style ="display: block; margin-bottom: 8px "> Event Log (onScroll):</ strong >
299297 < div class ="event-log " id ="event-log ">
300298 < div style ="opacity: 0.5; text-align: center "> Events will appear here...</ div >
301299 </ div >
@@ -340,9 +338,7 @@ <h3>🎯 xterm.js API Features</h3>
340338 updateStatus ( 'connecting' , 'Connecting to server...' ) ;
341339
342340 // Include terminal size in WebSocket URL
343- console . log ( `Terminal dimensions at connect time: ${ term . cols } x${ term . rows } ` ) ;
344341 const wsUrlWithSize = `${ WS_URL } ?cols=${ term . cols } &rows=${ term . rows } ` ;
345- console . log ( 'Connecting to WebSocket:' , wsUrlWithSize ) ;
346342
347343 // Show WebSocket URL in status bar
348344 document . getElementById ( 'ws-url' ) . textContent = WS_URL ;
@@ -351,8 +347,6 @@ <h3>🎯 xterm.js API Features</h3>
351347
352348 ws . onopen = ( ) => {
353349 updateStatus ( 'connected' , 'Connected (PTY mode)' ) ;
354- console . log ( 'WebSocket connected - PTY mode active' ) ;
355- console . log ( 'Terminal onData handler:' , term . onData ? 'registered' : 'NOT REGISTERED' ) ;
356350
357351 // Focus the terminal
358352 term . focus ( ) ;
@@ -361,18 +355,9 @@ <h3>🎯 xterm.js API Features</h3>
361355 ws . onmessage = ( event ) => {
362356 // PTY server sends raw text, not JSON
363357 const data = event . data ;
364- console . log (
365- 'WebSocket received:' ,
366- typeof data ,
367- 'length:' ,
368- data ?. length ,
369- 'data:' ,
370- JSON . stringify ( data ?. substring ( 0 , 50 ) )
371- ) ;
372358
373359 // If it's a string (raw PTY output), write directly to terminal
374360 if ( typeof data === 'string' ) {
375- console . log ( 'Writing to terminal:' , JSON . stringify ( data . substring ( 0 , 50 ) ) ) ;
376361 term . write ( data ) ;
377362 }
378363 // Otherwise try JSON parsing for file-browser-server compatibility
@@ -394,7 +379,6 @@ <h3>🎯 xterm.js API Features</h3>
394379
395380 ws . onclose = ( ) => {
396381 updateStatus ( 'disconnected' , 'Disconnected' ) ;
397- console . log ( 'WebSocket closed' ) ;
398382 term ?. write ( '\r\n\x1b[1;33mConnection closed.\x1b[0m\r\n' ) ;
399383 } ;
400384 }
@@ -517,22 +501,10 @@ <h3>🎯 xterm.js API Features</h3>
517501 }
518502
519503 function handleInput ( data ) {
520- console . log (
521- 'handleInput called, data:' ,
522- data ,
523- 'isPtyMode:' ,
524- isPtyMode ,
525- 'ws state:' ,
526- ws ?. readyState
527- ) ;
528-
529504 // PTY mode: send every keystroke directly to shell
530505 if ( isPtyMode ) {
531506 if ( ws && ws . readyState === WebSocket . OPEN ) {
532- console . log ( 'Sending to shell:' , JSON . stringify ( data ) ) ;
533507 ws . send ( data ) ;
534- } else {
535- console . error ( 'WebSocket not open, state:' , ws ?. readyState ) ;
536508 }
537509 return ;
538510 }
@@ -629,8 +601,6 @@ <h3>🎯 xterm.js API Features</h3>
629601 // Event Logging & Handlers
630602 // =========================================================================
631603
632- let customHandlerEnabled = false ;
633-
634604 function logEvent ( type , data ) {
635605 const eventLog = document . getElementById ( 'event-log' ) ;
636606 const eventDiv = document . createElement ( 'div' ) ;
@@ -655,104 +625,75 @@ <h3>🎯 xterm.js API Features</h3>
655625 }
656626
657627 function setupEventHandlers ( ) {
658- // Event: onKey - fires on every keypress
659- term . onKey ( ( e ) => {
660- logEvent ( 'onKey ' , `key=" ${ e . key } " ctrl= ${ e . domEvent . ctrlKey } alt= ${ e . domEvent . altKey } ` ) ;
628+ // Scrolling event handlers
629+ term . onScroll ( ( position ) => {
630+ logEvent ( 'onScroll ' , `viewportY= ${ position } ` ) ;
661631 } ) ;
662632
663- // Event: onTitleChange - fires when terminal title changes via OSC sequences
664- term . onTitleChange ( ( title ) => {
665- logEvent ( 'onTitleChange' , title ) ;
666- document . title = `${ title } - Ghostty Terminal` ;
633+ document . getElementById ( 'btn-clear-log' ) . addEventListener ( 'click' , ( ) => {
634+ document . getElementById ( 'event-log' ) . innerHTML =
635+ '<div style="opacity: 0.5; text-align: center;">Log cleared</div>' ;
667636 } ) ;
668637
669- // Button handlers
670- document . getElementById ( 'btn-paste' ) . addEventListener ( 'click' , ( ) => {
671- term . paste ( 'Pasted text from paste() method!\n' ) ;
672- logEvent ( 'Action' , 'Called paste()' ) ;
638+ // Scrolling button handlers
639+ document . getElementById ( 'btn-scrollUp' ) . addEventListener ( 'click' , ( ) => {
640+ const scrollbackLen = term . getScrollbackLength ( ) ;
641+ term . scrollLines ( - 5 ) ;
642+ logEvent ( 'Action' , `Called scrollLines(-5)` ) ;
673643 } ) ;
674644
675- document . getElementById ( 'btn-blur ' ) . addEventListener ( 'click' , ( ) => {
676- term . blur ( ) ;
677- logEvent ( 'Action' , 'Called blur( ) - terminal lost focus ' ) ;
645+ document . getElementById ( 'btn-scrollDown ' ) . addEventListener ( 'click' , ( ) => {
646+ term . scrollLines ( 5 ) ;
647+ logEvent ( 'Action' , 'Called scrollLines(5 ) - scroll down ' ) ;
678648 } ) ;
679649
680- document . getElementById ( 'btn-input ' ) . addEventListener ( 'click' , ( ) => {
681- term . input ( 'echo "Input from input() method"\n' , true ) ;
682- logEvent ( 'Action' , 'Called input() with wasUserInput=true ' ) ;
650+ document . getElementById ( 'btn-scrollPageUp ' ) . addEventListener ( 'click' , ( ) => {
651+ term . scrollPages ( - 1 ) ;
652+ logEvent ( 'Action' , 'Called scrollPages(-1) - page up ' ) ;
683653 } ) ;
684654
685- document . getElementById ( 'btn-select' ) . addEventListener ( 'click' , ( ) => {
686- // Select 20 characters starting at column 0, row 0
687- // This selects from the current cursor position backwards 20 chars
688- const cursor = term . wasmTerm . getCursor ( ) ;
689- const startRow = Math . max ( 0 , cursor . y - 1 ) ;
690- term . select ( 0 , startRow , 30 ) ;
691- logEvent (
692- 'Action' ,
693- `Called select(0, ${ startRow } , 30) - selects 30 chars from row ${ startRow } `
694- ) ;
655+ document . getElementById ( 'btn-scrollPageDown' ) . addEventListener ( 'click' , ( ) => {
656+ term . scrollPages ( 1 ) ;
657+ logEvent ( 'Action' , 'Called scrollPages(1) - page down' ) ;
695658 } ) ;
696659
697- document . getElementById ( 'btn-selectLines' ) . addEventListener ( 'click' , ( ) => {
698- // Select last 3 visible lines
699- const cursor = term . wasmTerm . getCursor ( ) ;
700- const endRow = cursor . y ;
701- const startRow = Math . max ( 0 , endRow - 2 ) ;
702- term . selectLines ( startRow , endRow ) ;
703- logEvent ( 'Action' , `Called selectLines(${ startRow } , ${ endRow } ) - selects 3 lines` ) ;
660+ document . getElementById ( 'btn-scrollToTop' ) . addEventListener ( 'click' , ( ) => {
661+ const scrollbackLen = term . getScrollbackLength ( ) ;
662+ term . scrollToTop ( ) ;
663+ logEvent ( 'Action' , `Called scrollToTop() (${ scrollbackLen } lines)` ) ;
704664 } ) ;
705665
706- document . getElementById ( 'btn-getSelectionPos' ) . addEventListener ( 'click' , ( ) => {
707- const pos = term . getSelectionPosition ( ) ;
708- if ( pos ) {
709- logEvent (
710- 'getSelectionPosition' ,
711- `start=(${ pos . start . x } ,${ pos . start . y } ) end=(${ pos . end . x } ,${ pos . end . y } )`
712- ) ;
713- } else {
714- logEvent ( 'getSelectionPosition' , 'No selection' ) ;
715- }
666+ document . getElementById ( 'btn-scrollToBottom' ) . addEventListener ( 'click' , ( ) => {
667+ term . scrollToBottom ( ) ;
668+ logEvent ( 'Action' , 'Called scrollToBottom()' ) ;
716669 } ) ;
717670
718- document . getElementById ( 'btn-setTitle' ) . addEventListener ( 'click' , ( ) => {
719- // Send OSC 2 sequence to change title
720- const newTitle = `Ghostty Terminal ${ new Date ( ) . toLocaleTimeString ( ) } ` ;
721- term . write ( `\x1b]2;${ newTitle } \x07` ) ;
722- logEvent ( 'Action' , `Sent OSC 2 sequence with title: ${ newTitle } ` ) ;
671+ document . getElementById ( 'btn-scrollToLine' ) . addEventListener ( 'click' , ( ) => {
672+ term . scrollToLine ( 10 ) ;
673+ logEvent ( 'Action' , 'Called scrollToLine(10)' ) ;
723674 } ) ;
724675
725- document . getElementById ( 'btn-customHandler' ) . addEventListener ( 'click' , ( ) => {
726- customHandlerEnabled = ! customHandlerEnabled ;
727-
728- if ( customHandlerEnabled ) {
729- // Custom handler that blocks Ctrl+K
730- term . attachCustomKeyEventHandler ( ( event ) => {
731- if ( event . ctrlKey && event . key === 'k' ) {
732- logEvent ( 'CustomHandler' , 'Blocked Ctrl+K' ) ;
733- return true ; // Block default handling
734- }
735- return false ; // Allow default handling
736- } ) ;
737- logEvent ( 'Action' , 'Custom handler enabled (blocks Ctrl+K)' ) ;
738- document . getElementById ( 'btn-customHandler' ) . style . background =
739- 'rgba(33, 150, 243, 0.8)' ;
740- } else {
741- term . attachCustomKeyEventHandler ( undefined ) ;
742- logEvent ( 'Action' , 'Custom handler disabled' ) ;
743- document . getElementById ( 'btn-customHandler' ) . style . background =
744- 'rgba(76, 175, 80, 0.8)' ;
676+ document . getElementById ( 'btn-generateContent' ) . addEventListener ( 'click' , ( ) => {
677+ // First clear the terminal to start fresh
678+ term . clear ( ) ;
679+
680+ // Generate lots of content to create scrollback
681+ // Terminal is ~24 rows, so 200 lines will create ~176 lines of scrollback
682+ for ( let i = 1 ; i <= 200 ; i ++ ) {
683+ term . write (
684+ `Line ${ i . toString ( ) . padStart ( 3 , '0' ) } : Test content for scrollback buffer\r\n`
685+ ) ;
745686 }
746- } ) ;
747687
748- document . getElementById ( 'btn-clear-log' ) . addEventListener ( 'click' , ( ) => {
749- document . getElementById ( 'event-log' ) . innerHTML =
750- '<div style="opacity: 0.5; text-align: center;">Log cleared</div>' ;
688+ // Wait a bit for rendering, then check scrollback
689+ setTimeout ( ( ) => {
690+ const scrollbackLen = term . getScrollbackLength ( ) ;
691+ logEvent ( 'Action' , `Generated 200 lines, scrollback: ${ scrollbackLen } lines` ) ;
692+ } , 100 ) ;
751693 } ) ;
752694
753695 // Expose terminal to console for debugging
754696 window . term = term ;
755- console . log ( 'Terminal exposed to window.term for debugging' ) ;
756697 }
757698
758699 // =========================================================================
@@ -805,7 +746,6 @@ <h3>🎯 xterm.js API Features</h3>
805746
806747 // Fit terminal to container
807748 fitAddon . fit ( ) ;
808- console . log ( `After fit(): Terminal size is ${ term . cols } x${ term . rows } ` ) ;
809749
810750 // Handle window resize
811751 window . addEventListener ( 'resize' , ( ) => {
@@ -816,7 +756,6 @@ <h3>🎯 xterm.js API Features</h3>
816756 term . onResize ( ( { cols, rows } ) => {
817757 if ( ws && ws . readyState === WebSocket . OPEN ) {
818758 ws . send ( JSON . stringify ( { type : 'resize' , cols, rows } ) ) ;
819- console . log ( `Sent resize to server: ${ cols } x${ rows } ` ) ;
820759 }
821760 } ) ;
822761
0 commit comments