Skip to content

Commit 28ef8ac

Browse files
committed
fix(inquirerer): enforce viewport height invariant
- Add bottom-anchored clamping to guarantee exactly viewportHeight lines - Fix renderConversation to reserve space for scroll indicator - Clamp availableContentLines to prevent negative values - This ensures the UI never goes below the terminal
1 parent c2345cc commit 28ef8ac

1 file changed

Lines changed: 19 additions & 4 deletions

File tree

packages/inquirerer/src/ui/aicode.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,8 @@ export class AICodeUI {
753753
const actualInputLines = Math.min(this.lineEditor.lines.length, maxInputLines);
754754

755755
// Calculate available space for content (welcome box or conversation)
756-
const availableContentLines = this.viewportHeight - chromeLines - actualInputLines;
756+
// Clamp to 0 minimum to handle very small terminals
757+
const availableContentLines = Math.max(0, this.viewportHeight - chromeLines - actualInputLines);
757758

758759
// Show welcome box if no messages, otherwise show conversation
759760
if (this.messages.length === 0 && !this.isStreaming) {
@@ -784,6 +785,16 @@ export class AICodeUI {
784785
const statusBar = this.renderStatusBar(width);
785786
lines.push(statusBar);
786787

788+
// INVARIANT: lines must be exactly viewportHeight
789+
// If too many lines, keep the bottom (input/status) - slice from end
790+
if (lines.length > this.viewportHeight) {
791+
lines.splice(0, lines.length - this.viewportHeight);
792+
}
793+
// If too few lines, pad at top
794+
while (lines.length < this.viewportHeight) {
795+
lines.unshift('');
796+
}
797+
787798
this.viewport.render({ lines });
788799
}
789800

@@ -895,15 +906,19 @@ export class AICodeUI {
895906
}
896907
}
897908

909+
// Reserve 1 line for scroll indicator if needed
910+
const hasScrollIndicator = this.scrollOffset > 0;
911+
const effectiveMaxLines = hasScrollIndicator ? maxLines - 1 : maxLines;
912+
898913
// Apply scroll offset and take last N lines
899-
const startIdx = Math.max(0, allLines.length - maxLines - this.scrollOffset);
914+
const startIdx = Math.max(0, allLines.length - effectiveMaxLines - this.scrollOffset);
900915
const endIdx = allLines.length - this.scrollOffset;
901916
const visibleLines = allLines.slice(startIdx, endIdx);
902917

903918
lines.push(...visibleLines);
904919

905-
// Show scroll indicator if needed
906-
if (this.scrollOffset > 0) {
920+
// Show scroll indicator if needed (already reserved space above)
921+
if (hasScrollIndicator) {
907922
lines.push(dim(` [${this.scrollOffset} more below - PageDown to scroll]`));
908923
}
909924

0 commit comments

Comments
 (0)