@@ -756,9 +756,10 @@ export class AICodeUI {
756756 lines . push ( ...conversationLines ) ;
757757 }
758758
759- // Fill remaining space (account for multiline input)
760- const inputLineCount = this . lineEditor . lines . length ;
761- const reservedLines = 3 + inputLineCount ; // separator + input lines + status
759+ // Fill remaining space (use fixed max input height for stable layout)
760+ // Max input area: 5 lines (or fewer if buffer is smaller)
761+ const maxVisibleInputLines = Math . min ( 5 , this . lineEditor . lines . length ) ;
762+ const reservedLines = 3 + maxVisibleInputLines ; // separator + input lines + status
762763 while ( lines . length < this . viewportHeight - reservedLines ) {
763764 lines . push ( '' ) ;
764765 }
@@ -919,42 +920,40 @@ export class AICodeUI {
919920
920921 /**
921922 * Render the input lines (supports multiline with scrolling)
923+ * Uses a fixed max height (5 lines) to keep layout stable
922924 */
923925 private renderInputLines ( width : number ) : string [ ] {
924926 const lines : string [ ] = [ ] ;
925927 const inputText = this . getInput ( ) ;
926928 const hasContent = inputText . length > 0 ;
929+ const totalLines = this . lineEditor . lines . length ;
927930
928- // Limit visible input lines to prevent overflow
929- // Reserve space for: welcome/conversation, separator, status bar
930- const maxInputLines = Math . min ( this . lineEditor . lines . length , Math . max ( 3 , this . viewportHeight - 10 ) ) ;
931+ // Fixed max visible input lines (must match layout calculation in render())
932+ const maxInputLines = 5 ;
933+ const visibleCount = Math . min ( maxInputLines , totalLines ) ;
931934
932935 // Calculate which lines to show (keep cursor line visible)
933936 const cursorLine = this . lineEditor . lineIndex ;
934937 let startLine = 0 ;
935- let endLine = this . lineEditor . lines . length ;
936938
937- if ( this . lineEditor . lines . length > maxInputLines ) {
938- // Center the view around the cursor line
939- const halfVisible = Math . floor ( maxInputLines / 2 ) ;
940- startLine = Math . max ( 0 , cursorLine - halfVisible ) ;
941- endLine = startLine + maxInputLines ;
942-
943- // Adjust if we're near the end
944- if ( endLine > this . lineEditor . lines . length ) {
945- endLine = this . lineEditor . lines . length ;
946- startLine = Math . max ( 0 , endLine - maxInputLines ) ;
939+ if ( totalLines > visibleCount ) {
940+ // Keep cursor visible with some context
941+ if ( cursorLine < 2 ) {
942+ startLine = 0 ;
943+ } else if ( cursorLine > totalLines - 3 ) {
944+ startLine = totalLines - visibleCount ;
945+ } else {
946+ startLine = cursorLine - 2 ;
947947 }
948948 }
949949
950- // Show scroll indicator at top if there are hidden lines above
951- if ( startLine > 0 ) {
952- lines . push ( dim ( ` [${ startLine } more lines above]` ) ) ;
953- }
950+ const endLine = Math . min ( startLine + visibleCount , totalLines ) ;
954951
955952 for ( let idx = startLine ; idx < endLine ; idx ++ ) {
956953 const line = this . lineEditor . lines [ idx ] ;
957- const prefix = idx === 0 ? cyan ( '> ' ) : ' ' ;
954+ // Show > prefix on first visible line if it's line 0, otherwise show line number hint
955+ const isFirstLogicalLine = idx === 0 ;
956+ const prefix = isFirstLogicalLine ? cyan ( '> ' ) : ' ' ;
958957 let lineContent : string ;
959958
960959 if ( idx === this . lineEditor . lineIndex && ! this . isStreaming ) {
@@ -969,25 +968,23 @@ export class AICodeUI {
969968 lineContent = line ;
970969 }
971970
972- // Add send hint on last visible line if there's content and it's the actual last line
973- if ( idx === this . lineEditor . lines . length - 1 && hasContent ) {
974- const hint = dim ( ' ↵ send' ) ;
971+ // Add info on the last visible line
972+ if ( idx === endLine - 1 && hasContent ) {
973+ // Show line count if multiline, otherwise show send hint
974+ const info = totalLines > 1
975+ ? dim ( ` ln ${ cursorLine + 1 } /${ totalLines } ` )
976+ : dim ( ' ↵ send' ) ;
975977 const lineWidth = displayWidth ( prefix + lineContent ) ;
976- const hintWidth = displayWidth ( hint ) ;
977- if ( lineWidth + hintWidth < width - 2 ) {
978- const padding = width - lineWidth - hintWidth - 2 ;
979- lineContent = lineContent + ' ' . repeat ( Math . max ( 0 , padding ) ) + hint ;
978+ const infoWidth = displayWidth ( info ) ;
979+ if ( lineWidth + infoWidth < width - 2 ) {
980+ const padding = width - lineWidth - infoWidth - 2 ;
981+ lineContent = lineContent + ' ' . repeat ( Math . max ( 0 , padding ) ) + info ;
980982 }
981983 }
982984
983985 lines . push ( prefix + lineContent ) ;
984986 }
985987
986- // Show scroll indicator at bottom if there are hidden lines below
987- if ( endLine < this . lineEditor . lines . length ) {
988- lines . push ( dim ( ` [${ this . lineEditor . lines . length - endLine } more lines below]` ) ) ;
989- }
990-
991988 return lines ;
992989 }
993990}
0 commit comments