diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 7bd610cb..058f8c66 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed editor/footer visibility drift during terminal resize by forcing full redraws when terminal width or height changes ([#1844](https://github.com/badlogic/pi-mono/pull/1844) by [@ghoulr](https://github.com/ghoulr)). + ## [0.56.1] - 2026-03-05 ### Fixed diff --git a/packages/tui/CHANGELOG.md b/packages/tui/CHANGELOG.md index 15aaf3ff..5e6d5cc9 100644 --- a/packages/tui/CHANGELOG.md +++ b/packages/tui/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed editor/footer visibility drift during terminal resize by forcing full redraws when terminal width or height changes ([#1844](https://github.com/badlogic/pi-mono/pull/1844) by [@ghoulr](https://github.com/ghoulr)). + ## [0.56.1] - 2026-03-05 ### Fixed diff --git a/packages/tui/src/tui.ts b/packages/tui/src/tui.ts index 02f5cdbd..36ec45f1 100644 --- a/packages/tui/src/tui.ts +++ b/packages/tui/src/tui.ts @@ -202,6 +202,7 @@ export class TUI extends Container { public terminal: Terminal; private previousLines: string[] = []; private previousWidth = 0; + private previousHeight = 0; private focusedComponent: Component | null = null; private inputListeners = new Set(); @@ -425,6 +426,7 @@ export class TUI extends Container { if (force) { this.previousLines = []; this.previousWidth = -1; // -1 triggers widthChanged, forcing a full clear + this.previousHeight = -1; // -1 triggers heightChanged, forcing a full clear this.cursorRow = 0; this.hardwareCursorRow = 0; this.maxLinesRendered = 0; @@ -871,8 +873,9 @@ export class TUI extends Container { newLines = this.applyLineResets(newLines); - // Width changed - need full re-render (line wrapping changes) + // Width or height changed - need full re-render const widthChanged = this.previousWidth !== 0 && this.previousWidth !== width; + const heightChanged = this.previousHeight !== 0 && this.previousHeight !== height; // Helper to clear scrollback and viewport and render all new lines const fullRender = (clear: boolean): void => { @@ -897,6 +900,7 @@ export class TUI extends Container { this.positionHardwareCursor(cursorPos, newLines.length); this.previousLines = newLines; this.previousWidth = width; + this.previousHeight = height; }; const debugRedraw = process.env.PI_DEBUG_REDRAW === "1"; @@ -908,15 +912,15 @@ export class TUI extends Container { }; // First render - just output everything without clearing (assumes clean screen) - if (this.previousLines.length === 0 && !widthChanged) { + if (this.previousLines.length === 0 && !widthChanged && !heightChanged) { logRedraw("first render"); fullRender(false); return; } - // Width changed - full re-render (line wrapping changes) - if (widthChanged) { - logRedraw(`width changed (${this.previousWidth} -> ${width})`); + // Width or height changed - full re-render + if (widthChanged || heightChanged) { + logRedraw(`terminal size changed (${this.previousWidth}x${this.previousHeight} -> ${width}x${height})`); fullRender(true); return; } @@ -958,6 +962,7 @@ export class TUI extends Container { if (firstChanged === -1) { this.positionHardwareCursor(cursorPos, newLines.length); this.previousViewportTop = Math.max(0, this.maxLinesRendered - height); + this.previousHeight = height; return; } @@ -996,6 +1001,7 @@ export class TUI extends Container { this.positionHardwareCursor(cursorPos, newLines.length); this.previousLines = newLines; this.previousWidth = width; + this.previousHeight = height; this.previousViewportTop = Math.max(0, this.maxLinesRendered - height); return; } @@ -1144,6 +1150,7 @@ export class TUI extends Container { this.previousLines = newLines; this.previousWidth = width; + this.previousHeight = height; } /** diff --git a/packages/tui/test/tui-render.test.ts b/packages/tui/test/tui-render.test.ts index f72f65ca..0830bfe4 100644 --- a/packages/tui/test/tui-render.test.ts +++ b/packages/tui/test/tui-render.test.ts @@ -23,6 +23,31 @@ function getCellItalic(terminal: VirtualTerminal, row: number, col: number): num } describe("TUI resize handling", () => { + it("triggers full re-render when terminal height changes", async () => { + const terminal = new VirtualTerminal(40, 10); + const tui = new TUI(terminal); + const component = new TestComponent(); + tui.addChild(component); + + component.lines = ["Line 0", "Line 1", "Line 2"]; + tui.start(); + await terminal.flush(); + + const initialRedraws = tui.fullRedraws; + + // Resize height + terminal.resize(40, 15); + await terminal.flush(); + + // Should have triggered a full redraw + assert.ok(tui.fullRedraws > initialRedraws, "Height change should trigger full redraw"); + + const viewport = terminal.getViewport(); + assert.ok(viewport[0]?.includes("Line 0"), "Content preserved after height change"); + + tui.stop(); + }); + it("triggers full re-render when terminal width changes", async () => { const terminal = new VirtualTerminal(40, 10); const tui = new TUI(terminal);