fix(tui): avoid mutating stdout dimensions - #35909
Conversation
There was a problem hiding this comment.
Pull request overview
Replaces the global process.stdout.columns/rows monkey-patch (which broke Ink layout in Warp) with read-site sanitization via new safeColumns/safeRows/safeTerminalSize helpers. The helpers defensively read dimensions (with try/catch for throwing getters) and sanitize values without mutating the host stream.
Changes:
- Removed
clampStdoutDimensionsand its startup invocation inentry.tsx; addedsafeColumns/safeRows/safeTerminalSizeaccessors inui-tui/src/lib/terminalDimensions.ts. - Replaced all
stdout?.columns ?? 80/stdout?.rows ?? 24reads in TUI app and overlay components with the safe accessors; added a parallel helper inside the vendored Ink package and wired its constructor/resize/render paths. - Added focused tests for both helpers covering invalid, oversized, missing, fractional, throwing, and fallback cases.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| ui-tui/src/lib/terminalDimensions.ts | Removes clampStdoutDimensions; adds safeColumns/safeRows/safeTerminalSize with defensive reads and fallback sanitization. |
| ui-tui/src/entry.tsx | Drops the startup stdout monkey-patch. |
| ui-tui/src/app/useMainApp.ts | Uses safeColumns(stdout) for cols state and resize RPC. |
| ui-tui/src/app/useInputHandlers.ts | Uses safeRows(terminal.stdout) for pager / page-up viewport. |
| ui-tui/src/components/{branding,modelPicker,sessionPicker,skillsHub,activeSessionSwitcher,agentsOverlay}.tsx | Switches direct stdout dimension reads to the safe accessors. |
| ui-tui/src/tests/terminalDimensions.test.ts | Replaces clamping tests with safe-reader coverage. |
| ui-tui/packages/hermes-ink/src/ink/terminal-dimensions.ts | New package-local mirror of the sanitizer + safe readers. |
| ui-tui/packages/hermes-ink/src/ink/terminal-dimensions.test.ts | Vitest coverage for the package-local helpers. |
| ui-tui/packages/hermes-ink/src/ink/ink.tsx | Uses safeTerminalSize/safeColumns/safeRows in constructor, resize handler, and render path. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Replaces the global stdout columns/rows monkey-patch with safe terminal dimension accessors. This avoids mutating wrapped stdout streams, which can break Ink layout in terminals such as Warp. Validation: focused terminal dimension tests and TUI builds.
789028b to
46dafa9
Compare
|
Thanks for this, @sxuff — solid work and a real cross-platform concern. Closing as redundant against current Separately, the original WSL2 symptom that motivated all this — Appreciate the careful test coverage on this PR — the read-site sanitization approach is sound and we may revisit it for the Ink TUI surface if the WSL garbage value resurfaces there. |
What does this PR do?
Replaces the global
process.stdout.columns/process.stdout.rowsmonkey-patch with safe terminal dimension accessors.The previous approach used
Object.defineProperty()onprocess.stdout, which can break wrapped stdout implementations and cause Ink layout issues in terminals such as Warp. This PR keeps the existing terminal dimension sanitization behavior, but applies it at dimension read sites instead of mutating the host stream.It also makes dimension reads defensive against unusual stdout wrappers and adds focused test coverage for invalid, oversized, missing, fractional, and throwing terminal dimension sources.
Related Issue
Fixes #35738
Type of Change
Changes Made
ui-tui/src/entry.tsx.ui-tui/src/lib/terminalDimensions.ts.safeColumns()/safeRows().ui-tui/packages/hermes-ink/src/ink/terminal-dimensions.ts.ui-tui/src/__tests__/terminalDimensions.test.tsui-tui/packages/hermes-ink/src/ink/terminal-dimensions.test.tsHow to Test
cd ui-tuinpm.cmd exec -- vitest run src/__tests__/terminalDimensions.test.ts packages/hermes-ink/src/ink/terminal-dimensions.test.tsnpm.cmd run build --prefix packages/hermes-inknpm.cmd run buildChecklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AScreenshots / Logs
Focused terminal dimension tests pass: