Repository navigation
feat(tui) 04 - Live TUI resize via SIGWINCH #144
Description
EPIC: #140
Branch: feat/bun14-terminal
Depends on: #142 and #143 (correct column measurements must be in place before activating dynamic redraws)
Blocks: #145
Context / Problem
runInteractive() in src/tui.ts captures the terminal height once at startup:
const termHeight = process.stdout.rows ?? 40;This value is never updated. If the user resizes the window during a session:
- Content overflows vertically when the window shrinks.
- Empty space accumulates at the bottom when the window expands.
- No redraw is triggered; the user must press a key before anything changes.
termWidth is already re-read on every redraw() call via process.stdout.columns ?? 80, but without an active resize handler the width is only picked up on the next keypress.
SIGWINCH was unavailable on Windows before Bun 1.4. It is now supported on all platforms.
Solution
1. Make dimensions mutable
Change const to let for both dimension variables:
let termHeight = process.stdout.rows ?? 40;
let termWidth = process.stdout.columns ?? 80;getViewportHeight() reads termHeight via its closure. No change to the viewport or scroll logic is needed beyond this declaration change.
2. Install the SIGWINCH handler before the event loop
const onResize = () => {
const newH = process.stdout.rows ?? 40;
const newW = process.stdout.columns ?? 80;
if (newH === termHeight && newW === termWidth) return;
termHeight = newH;
termWidth = newW;
redraw();
};
process.on("SIGWINCH", onResize);3. Remove the handler on every exit path
process.off("SIGWINCH", onResize) must be called in every exit path inside runInteractive:
- Ctrl+C in each mode handler (normal, filter, team pick, repick)
qin normal mode- Enter / confirm output
- Natural end of the
for awaitloop
Recommended: extract a local exit() function that centralizes process.off, setRawMode(false), and process.stdout.write(ANSI_CLEAR), then substitute it for all process.exit(0) calls inside the loop.
Resize event flow
sequenceDiagram
participant OS as OS / terminal emulator
participant TUI as tui.ts (runInteractive)
participant Render as renderGroups()
participant Stdout as process.stdout
Note over TUI: process.on("SIGWINCH", onResize)
OS->>TUI: SIGWINCH
TUI->>Stdout: read .rows / .columns
Stdout-->>TUI: newH, newW
alt dimensions changed
TUI->>TUI: termHeight = newH, termWidth = newW
TUI->>Render: redraw()
Render-->>Stdout: ANSI_CLEAR + re-rendered frame
else no change
TUI->>TUI: return early (no redraw)
end
Acceptance Criteria
-
termHeightis declared asletand updated by theSIGWINCHhandler. -
process.on("SIGWINCH", onResize)is installed before thefor awaitloop. -
process.off("SIGWINCH", onResize)is called in every exit path ofrunInteractive. - After a resize,
getViewportHeight()andisCursorVisible()remain consistent: no cursor outside the viewport, no negativescrollOffset. - Manual test results documented in the PR description:
- Run
bun github-code-search.ts query --org <org> <query>with at least one result. - Shrink the window to approximately 40 columns: lines truncate without overflowing.
- Expand to approximately 200 columns: lines extend to fill the new width.
- Reduce height to 10 lines: the footer stays at the bottom and the viewport shrinks.
- Restore the original window size: the rendering returns to normal without any keypress.
- Run
-
bun testpasses (unit tests do not simulateSIGWINCHbut must remain green). -
bun run lintandbun run format:checkare clean.
Definition of Done
- PR reviewed and merged into
feat/bun14-terminal. - Zero lint errors, zero format diff, test suite green.
- Results of the five-step manual test documented in the PR description.
Activity
[-]feat(tui) 04 - Redimensionnement live du TUI via SIGWINCH[/-][+]feat(tui) 04 - Live TUI resize via SIGWINCH[/+]