You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
consttermHeight=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.
process.off("SIGWINCH", onResize) must be called in every exit path inside runInteractive:
Ctrl+C in each mode handler (normal, filter, team pick, repick)
q in normal mode
Enter / confirm output
Natural end of the for await loop
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
Loading
Acceptance Criteria
termHeight is declared as let and updated by the SIGWINCH handler.
process.on("SIGWINCH", onResize) is installed before the for await loop.
process.off("SIGWINCH", onResize) is called in every exit path of runInteractive.
After a resize, getViewportHeight() and isCursorVisible() remain consistent: no cursor outside the viewport, no negative scrollOffset.
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.
bun test passes (unit tests do not simulate SIGWINCH but must remain green).
bun run lint and bun run format:check are 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.
EPIC: #140
Branch:
feat/bun14-terminalDepends on: #142 and #143 (correct column measurements must be in place before activating dynamic redraws)
Blocks: #145
Context / Problem
runInteractive()insrc/tui.tscaptures the terminal height once at startup:This value is never updated. If the user resizes the window during a session:
termWidthis already re-read on everyredraw()call viaprocess.stdout.columns ?? 80, but without an active resize handler the width is only picked up on the next keypress.SIGWINCHwas unavailable on Windows before Bun 1.4. It is now supported on all platforms.Solution
1. Make dimensions mutable
Change
consttoletfor both dimension variables:getViewportHeight()readstermHeightvia 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
3. Remove the handler on every exit path
process.off("SIGWINCH", onResize)must be called in every exit path insiderunInteractive:qin normal modefor awaitloopRecommended: extract a local
exit()function that centralizesprocess.off,setRawMode(false), andprocess.stdout.write(ANSI_CLEAR), then substitute it for allprocess.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) endAcceptance 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.getViewportHeight()andisCursorVisible()remain consistent: no cursor outside the viewport, no negativescrollOffset.bun github-code-search.ts query --org <org> <query>with at least one result.bun testpasses (unit tests do not simulateSIGWINCHbut must remain green).bun run lintandbun run format:checkare clean.Definition of Done
feat/bun14-terminal.