Skip to content

Commit c0c9aba

Browse files
committed
fix(tui): correct CHROME_ROWS (8→10) and clip frame to terminal height
CHROME_ROWS undercounted by 2 — column headers and separator inside renderAgentList() were not budgeted. This caused render() to produce rows+2 lines, and flush() wrote them all, scrolling the terminal and creating visual line duplication on cursor movement. Three layers of defense: - CHROME_ROWS = 10 (root cause) - flush() clips nextLines to process.stdout.rows (both diff and full-redraw paths) - render() clips output to rows after bottom border
1 parent d554312 commit c0c9aba

4 files changed

Lines changed: 7 additions & 4 deletions

File tree

src/tui/renderer.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,8 @@ export function render(state) {
727727

728728
// Bottom border
729729
out.push(botBorder(cols));
730+
// Clip to terminal height — safety net against chrome miscount
731+
if (out.length > rows) out.length = rows;
730732

731733
return out.join('\n');
732734
}

src/tui/screen.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,13 @@ export function exit() {
8989
* @param {string} buffer - Complete frame content.
9090
*/
9191
export function flush(buffer) {
92-
const nextLines = buffer.split('\n');
92+
const nextLines = buffer.split('\n').slice(0, process.stdout.rows);
93+
const clipped = nextLines.join('\n');
9394
let out = SYNC_START;
9495

9596
if (nextLines.length !== prevLines.length) {
9697
// Line count changed — full redraw
97-
out += CURSOR_HOME + buffer + CLEAR_TO_END;
98+
out += CURSOR_HOME + clipped + CLEAR_TO_END;
9899
} else {
99100
// Diff: only repaint changed lines
100101
for (let i = 0; i < nextLines.length; i++) {

src/tui/state.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { getWarningsForPreset, getWarningsForPermission } from '../permissions/w
99

1010
// ─── Constants ───────────────────────────────────────────────────────────────
1111

12-
const CHROME_ROWS = 8;
12+
const CHROME_ROWS = 10;
1313
const MIN_VIEWPORT = 5;
1414

1515
// ─── Types (JSDoc) ───────────────────────────────────────────────────────────

tests/tui.test.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1120,7 +1120,7 @@ describe('getViewportHeight', () => {
11201120
it('returns rows minus chrome, at least MIN_VIEWPORT', () => {
11211121
const state = makeState({}, { cols: 80, rows: 24 });
11221122
const vh = getViewportHeight(state);
1123-
assert.equal(vh, 24 - 8); // rows - CHROME_ROWS
1123+
assert.equal(vh, 24 - 10); // rows - CHROME_ROWS
11241124
});
11251125

11261126
it('clamps to MIN_VIEWPORT for small terminals', () => {

0 commit comments

Comments
 (0)