EPIC: #140
Branch: feat/bun14-terminal
Depends on: #141
Parallelizable with: #142
Blocks: #144
Context / Problem
renderTeamPickHeader() in src/render/team-pick.ts computes the sliding window width using text.length (UTF-16 code units) and truncates with .slice().
A team name containing a wide character produces an overflow or a misaligned truncation. Concrete example: "[ squad-\u{1F916} ]" (a team name with a robot emoji) has .length === 13 but occupies 14 terminal columns. The sliding window overflows by one column. A team name with two CJK characters (e.g. "\u524D\u7AEF") similarly miscounts.
Solution
Two targeted replacements in renderTeamPickHeader():
widths[i] = texts[i].length -> widths[i] = visibleWidth(texts[i])
texts[safeIndex].slice(0, maxWidth - 1) + "..." -> clipToWidth(texts[safeIndex], maxWidth - 1) + "..."
The remaining windowing calculations (addCost, totalWidth, EL_LEFT, EL_RIGHT) already read from widths[i] and require no further changes once the width values are correct.
src/render/team-pick.ts imports from src/render/terminal.ts only. It does not call Bun.stringWidth, Bun.sliceAnsi, or Bun.stripANSI directly.
Acceptance Criteria
Definition of Done
- PR reviewed and merged into
feat/bun14-terminal.
- Zero lint errors, zero format diff, test suite green.
EPIC: #140
Branch:
feat/bun14-terminalDepends on: #141
Parallelizable with: #142
Blocks: #144
Context / Problem
renderTeamPickHeader()insrc/render/team-pick.tscomputes the sliding window width usingtext.length(UTF-16 code units) and truncates with.slice().A team name containing a wide character produces an overflow or a misaligned truncation. Concrete example:
"[ squad-\u{1F916} ]"(a team name with a robot emoji) has.length === 13but occupies 14 terminal columns. The sliding window overflows by one column. A team name with two CJK characters (e.g."\u524D\u7AEF") similarly miscounts.Solution
Two targeted replacements in
renderTeamPickHeader():widths[i] = texts[i].length->widths[i] = visibleWidth(texts[i])texts[safeIndex].slice(0, maxWidth - 1) + "..."->clipToWidth(texts[safeIndex], maxWidth - 1) + "..."The remaining windowing calculations (
addCost,totalWidth,EL_LEFT,EL_RIGHT) already read fromwidths[i]and require no further changes once the width values are correct.src/render/team-pick.tsimports fromsrc/render/terminal.tsonly. It does not callBun.stringWidth,Bun.sliceAnsi, orBun.stripANSIdirectly.Acceptance Criteria
src/render/team-pick.tsno longer uses.lengthto compute candidate text widths.src/render/team-pick.tsdoes not callBun.stringWidth,Bun.sliceAnsi, orBun.stripANSIdirectly.src/render/team-pick.test.ts- new test cases:"squad-\u{1F916}") and a constrainedmaxWidth:visibleWidth(stripAnsi(output)) <= maxWidth."\u524D\u7AEF-team") and a constrainedmaxWidth: same assertion.src/render/team-pick.test.tsstill pass.bun test src/render/team-pick.test.tspasses.bun run lintandbun run format:checkare clean.Definition of Done
feat/bun14-terminal.