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
The TUI relies on three fragile in-house helpers in src/render.ts:
stripAnsi(str): a regex limited to SGR codes that misses OSC 8 terminal hyperlinks and cursor movement sequences (ESC[A, ESC[H...).
clipAnsi(str, maxVisible): a manual character loop that treats every code point as exactly 1 column wide. Emoji, CJK characters, and multi-code-point grapheme clusters (flags, skin tone modifiers) are miscounted, causing line overflow or premature truncation.
src/render/team-pick.ts uses .length directly to measure team name widths in the sliding window, with the same measurement flaw.
Additionally, termHeight is captured once at startup (const termHeight = process.stdout.rows ?? 40). Resizing the terminal window has no effect until the process is restarted.
Bun 1.4 ships Bun.stringWidth(), Bun.stripANSI(), Bun.sliceAnsi(), and Bun.wrapAnsi() as native built-in APIs, covering exactly these needs with SIMD-backed implementations (more than 6,000x faster than the equivalent npm packages according to the Bun 1.4 release notes). Bun 1.4 also adds SIGWINCH support on Windows, where it was previously unavailable.
Solution and Architecture Principles
Pure module src/render/terminal.ts: a single facade between application code and Bun 1.4 native APIs. No other source file calls Bun.stringWidth, Bun.sliceAnsi, or Bun.stripANSI directly.
Remove in-house helpers: stripAnsi and clipAnsi in src/render.ts are deleted after migration. No parallel implementations are kept.
Mutable terminal dimensions in tui.ts: termHeight and termWidth become let variables refreshed from process.stdout.rows and process.stdout.columns; a SIGWINCH handler triggers an immediate redraw.
Bun.Terminal is out of scope: this API attaches a PTY to a child process. It does not represent the current interactive terminal. It may be used in a future integration test.
Bun.wrapAnsi for multi-line content only: TUI rows must remain single-line; Bun.sliceAnsi is the right tool for truncation. Bun.wrapAnsi is only relevant if the help overlay is made responsive (not in this EPIC).
Bun 1.4+ required: these APIs are called without a fallback. The minimum runtime version is documented in README.md and AGENTS.md.
Existing layering preserved: src/render/terminal.ts is a pure module (no I/O), consumed through the existing src/render.ts facade.
Context / Problem
The TUI relies on three fragile in-house helpers in
src/render.ts:stripAnsi(str): a regex limited to SGR codes that misses OSC 8 terminal hyperlinks and cursor movement sequences (ESC[A,ESC[H...).clipAnsi(str, maxVisible): a manual character loop that treats every code point as exactly 1 column wide. Emoji, CJK characters, and multi-code-point grapheme clusters (flags, skin tone modifiers) are miscounted, causing line overflow or premature truncation.src/render/team-pick.tsuses.lengthdirectly to measure team name widths in the sliding window, with the same measurement flaw.Additionally,
termHeightis captured once at startup (const termHeight = process.stdout.rows ?? 40). Resizing the terminal window has no effect until the process is restarted.Bun 1.4 ships
Bun.stringWidth(),Bun.stripANSI(),Bun.sliceAnsi(), andBun.wrapAnsi()as native built-in APIs, covering exactly these needs with SIMD-backed implementations (more than 6,000x faster than the equivalent npm packages according to the Bun 1.4 release notes). Bun 1.4 also addsSIGWINCHsupport on Windows, where it was previously unavailable.Solution and Architecture Principles
src/render/terminal.ts: a single facade between application code and Bun 1.4 native APIs. No other source file callsBun.stringWidth,Bun.sliceAnsi, orBun.stripANSIdirectly.stripAnsiandclipAnsiinsrc/render.tsare deleted after migration. No parallel implementations are kept.tui.ts:termHeightandtermWidthbecomeletvariables refreshed fromprocess.stdout.rowsandprocess.stdout.columns; aSIGWINCHhandler triggers an immediate redraw.Bun.Terminalis out of scope: this API attaches a PTY to a child process. It does not represent the current interactive terminal. It may be used in a future integration test.Bun.wrapAnsifor multi-line content only: TUI rows must remain single-line;Bun.sliceAnsiis the right tool for truncation.Bun.wrapAnsiis only relevant if the help overlay is made responsive (not in this EPIC).README.mdandAGENTS.md.src/render/terminal.tsis a pure module (no I/O), consumed through the existingsrc/render.tsfacade.Sub-issue Dependency Graph
flowchart LR I01["01 - terminal.ts\nno dependencies"] I02["02 - render.ts\nmigration"] I03["03 - team-pick.ts\nmigration"] I04["04 - SIGWINCH\nlive resize"] I05["05 - Documentation"] I01 --> I02 I01 --> I03 I02 --> I04 I03 --> I04 I04 --> I0502 and 03 can be worked in parallel once 01 is merged.
Acceptance Criteria
Bun.stringWidth,Bun.stripANSI, andBun.sliceAnsiare called exclusively fromsrc/render/terminal.ts.stripAnsiandclipAnsihelpers are deleted fromsrc/render.ts.src/render/team-pick.tsno longer uses.lengthto measure team name widths.bun test,bun run lint,bun run format:check,bun run knip, andbun run build.tsall pass.docs/architecture/andAGENTS.mdreflect the newterminal.tslayer.README.mdexplicitly states Bun 1.4+ as the minimum runtime requirement.Definition of Done
feat/bun14-terminal.mainvia a reviewed PR.AGENTS.md - Release process.Sub-issues (execution order)
src/render/terminal.tsBun 1.4 native API facade (no dependencies, start here)src/render.ts(depends on 01)src/render/team-pick.ts(depends on 01, parallelizable with 02)SIGWINCH(depends on 02 and 03)Branch
feat/bun14-terminal