Description
truncateMiddle in packages/tui/src/util/locale.ts returns a string longer than maxLength when maxLength is 1 or 2:
truncateMiddle("abcdefghij", 1) -> "…abcdefghij" (11 chars, asked for 1)
truncateMiddle("abcdefghij", 2) -> "a…abcdefghij" (12 chars, asked for 2)
The last line is:
return str.slice(0, keepStart) + ellipsis + str.slice(-keepEnd)
keepEnd is Math.floor((maxLength - 1) / 2), which is 0 when maxLength is 1 or 2. str.slice(-0) is str.slice(0) — the whole string — so instead of truncating, the function returns the entire input with an ellipsis attached. maxLength >= 3 is unaffected, which is why this hasn't shown up before.
This is reachable from the TUI. packages/tui/src/ui/dialog-select.tsx:701:
Locale.truncateMiddle(detail, Math.max(1, Math.min(76, dimensions().width - 12)))
The Math.max(1, ...) guard is there to handle narrow terminals, but it clamps to exactly the broken value. Running that expression against a 39-char detail string:
terminal width=13 -> maxLength=1 -> result is 40 chars (overflows)
terminal width=14 -> maxLength=2 -> result is 41 chars (overflows)
terminal width=15 -> maxLength=3 -> result is 3 chars (correct)
So below ~15 columns the detail line renders the full untruncated string and overflows the layout it was meant to fit. packages/opencode/src/cli/cmd/run/splash.ts:203 uses the same Math.max(1, ...) shape.
Same root cause as #37368, but a different function — #37369 fixes truncateLeft and doesn't touch this one.
Note: packages/core/src/util/path.ts:31 has an independent copy of truncateMiddle with the same -0 defect. It currently has no callers, so it isn't user-visible. Happy to fix it in the same PR or leave it out.
Steps to reproduce
- Resize the terminal to 14 columns or fewer
- Open a select dialog that renders a detail line (e.g. the session or model picker) with a detail string longer than the width
- The detail renders in full instead of truncated, overflowing the dialog
Operating System
Linux
Terminal
ghostty
kitty
Description
truncateMiddleinpackages/tui/src/util/locale.tsreturns a string longer thanmaxLengthwhenmaxLengthis 1 or 2:The last line is:
keepEndisMath.floor((maxLength - 1) / 2), which is0whenmaxLengthis 1 or 2.str.slice(-0)isstr.slice(0)— the whole string — so instead of truncating, the function returns the entire input with an ellipsis attached.maxLength >= 3is unaffected, which is why this hasn't shown up before.This is reachable from the TUI.
packages/tui/src/ui/dialog-select.tsx:701:The
Math.max(1, ...)guard is there to handle narrow terminals, but it clamps to exactly the broken value. Running that expression against a 39-char detail string:So below ~15 columns the detail line renders the full untruncated string and overflows the layout it was meant to fit.
packages/opencode/src/cli/cmd/run/splash.ts:203uses the sameMath.max(1, ...)shape.Same root cause as #37368, but a different function — #37369 fixes
truncateLeftand doesn't touch this one.Note:
packages/core/src/util/path.ts:31has an independent copy oftruncateMiddlewith the same-0defect. It currently has no callers, so it isn't user-visible. Happy to fix it in the same PR or leave it out.Steps to reproduce
Operating System
Linux
Terminal
ghostty
kitty