Skip to content

fix(tui): wrap status line instead of truncating when too long#3054

Open
CnsMaple wants to merge 1 commit into
esengine:main-v2from
CnsMaple:fix/status-line-wrap
Open

fix(tui): wrap status line instead of truncating when too long#3054
CnsMaple wants to merge 1 commit into
esengine:main-v2from
CnsMaple:fix/status-line-wrap

Conversation

@CnsMaple
Copy link
Copy Markdown
Contributor

@CnsMaple CnsMaple commented Jun 4, 2026

Problem

The bottom status/data line (model · git · effort · context · cache · jobs · balance, or custom statusline command output) was truncated with when its content exceeded the terminal width on narrow terminals or with many active tags. Information was silently lost with no way to reveal it.

Solution

Replace truncation with ANSI-aware hard-wrapping so the data line flows onto additional rows instead of being cut off.

Changes

  1. wrapStatusLine (replaces clampStatusLine): uses ansi.Hardwrap to wrap text at word boundaries to the given width instead of truncating with ansi.Truncate(…).

  2. computeStatusLineCount: new method that replicates the data-tag construction from View() and returns the number of terminal rows the status block will occupy after wrapping. Called from Update() so the viewport height is correctly reserved.

  3. statusLineCount field: stored on the model and used by bottomRows() instead of the hardcoded + 2. Falls back to 2 when uninitialized (e.g. in tests).

  4. View(): updated to use wrapStatusLine for both status rows and the working spinner line.

Tests

All existing tests pass. No behavioral change on wide terminals — wrapping only kicks in when the content exceeds width columns.

The bottom status/data line (model · git · effort · context · cache · jobs ·
balance, or custom statusline) was truncated with an ellipsis when its content
exceeded the terminal width. This lost information with no way to reveal it.

Changes:
- Replace clampStatusLine (ansi.Truncate) with wrapStatusLine (ansi.Hardwrap)
  so the data line wraps to additional rows instead of being silently cut.
- Add statusLineCount field to chatTUI, computed during Update() via the new
  computeStatusLineCount() method, so bottomRows() reserves the correct height.
- bottomRows() uses statusLineCount instead of hardcoded +2 when available,
  falling back to 2 for tests that don't set the field.
- Update comments to reflect that wrapping is safe in alt-screen mode.

The behavior change is transparent to the user: on a wide terminal nothing
changes; on a narrow terminal or with many active tags, the data line flows
onto extra rows under the composer rather than being cut off with '…'.
@github-actions github-actions Bot added the v2 Go rewrite (1.x) — main-v2 branch, active development label Jun 4, 2026
@esengine
Copy link
Copy Markdown
Owner

esengine commented Jun 4, 2026

Good direction — now that the chat view is alt-screen (v.AltScreen = true), wrapping the status line is safe (no scrollback to strand), so dropping the truncate-and-fix-two-rows shape makes sense, and using ansi.Hardwrap is the right call. I built it; it compiles.

Before it can land, computeStatusLineCount needs to match what View() actually renders, or the reserved height drifts from the drawn height on narrow terminals and the bottom rows overlap/clip. Two concrete gaps:

  1. Width mismatch. Update calls computeStatusLineCount(contentW) where contentW = m.width - 1 (scrollbar column), but View() wraps the status block at boxW = m.width. Computing the wrap count at a different width than the render means they disagree by a row right at a wrap boundary. Pass the same width both places.

  2. The first status line isn't counted as wrappable. computeStatusLineCount hardcodes lines := 1 ("always fits one row"), but View() does wrapStatusLine(status, boxW) on the first line too. The mode-tag + state line (e.g. NORMAL · idle (shift+tab to cycle)) does wrap on a narrow terminal, so on a thin window the count is short by a row and the layout breaks.

Fix: count both lines through the same wrapStatusLine(..., width) at the same width View() uses (e.g. strings.Count(wrapStatusLine(status, w), "\n") + strings.Count(wrapStatusLine(dataLine, w), "\n") + 2). And please add a test that drives a narrow width with a long mode line + long data line and asserts computeStatusLineCount equals the actual rendered row count — this is the load-bearing invariant and there's no coverage for it yet. Ping me after and I'll merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants