Skip to content

fix(tui): pin the header box width so the border survives the first paint - #770

Merged
ericleepi314 merged 1 commit into
mainfrom
fix/header-box-first-paint-width
Jul 29, 2026
Merged

fix(tui): pin the header box width so the border survives the first paint#770
ericleepi314 merged 1 commit into
mainfrom
fix/header-box-first-paint-width

Conversation

@ericleepi314

Copy link
Copy Markdown
Collaborator

Follow-up to #769. The right border was still missing in a real terminal — and only appeared after resizing the window. That resize clue is what cracked it.

What was actually wrong

#769 fixed the width the panel is told, but also removed the box's explicit width, letting it size to its container. The arithmetic was never the problem — the container was.

Instrumented the component and ran the real TUI on a pty at 200 columns:

frame 1: {"term":200,"maxWidth":196,"cols":196,"leftW":44,"w":145,"wide":true}
          → box should be 196.  Rendered: 198.

On the first paint the transcript's ScrollBox has not settled: it reports its full width, without the scrollbar gutter its steady-state layout reserves. A container-sized box therefore renders two columns too wide and gets clipped. And because the intro row is committed to scrollback and never repainted, it stays clipped — until a resize forces a relayout. Hence "I have to resize the window to see the border."

Same pty run, before and after a resize:

BEFORE (200 cols)   w=198  ╭─── clawcodex v1.3.0 ──────────…────────   ← no ╮
AFTER  (190 cols)   w=187  ╭─── clawcodex v1.3.0 ──────────…───────╮   ← closes

Fix

Restore width={cols}. cols derives from transcriptPanelWidth and process.stdout.columns — both correct on frame 1 — so pinning to it is right at first paint and after resize. The same pty run now closes the box at both sizes.

#769's other half stands and is load-bearing. Without its maxWidth fix, cols would still be the composer's budget and pinning to it would reproduce the original bug. The two changes are complementary; only the second half of #769 was the wrong lever.

Why the test didn't catch it

It asserted the box fills its container — and under a correctly-sized container, "fills the container" and "holds its own width" are indistinguishable. The test only ever built a correct container, so both behaviors passed.

It now renders against a deliberately oversized container (gutter omitted, reproducing frame 1) and asserts the box holds its steady-state width regardless.

Change Result
Remove width={cols} (the #769 state) 9 tests fail — one per width

Testing

  • Verified end-to-end on a pty against the real installed TUI, before and after resize, at 200 → 190 columns.
  • npm run typecheck clean; eslint clean on both changed files.
  • Suite 8 failed / 1581 passed — same 8 pre-existing failures (createGatewayEventHandler, cursorDriftRegression, statusRule, useConfigSync, virtualHeights).

🤖 Generated with Claude Code

…aint

#769 fixed the width the panel is TOLD (the transcript reserves a scrollbar
gutter the composer budget does not) but also removed the box's explicit
`width`, letting it size to its container instead. On a real terminal the
right border was still missing, and only appeared after resizing the window.

Measured on a pty at 200 columns, with the component instrumented: the panel
computes `maxWidth=196, cols=196, leftW=44, w=145` on the very first frame --
correct. The rendered box is 198. So the arithmetic was never wrong; the
container was. On the first paint the transcript's ScrollBox has not settled
and reports its full width, without the scrollbar gutter its steady-state
layout reserves, so a container-sized box renders two columns too wide and is
clipped. Because the intro row is committed to scrollback and never repainted,
it stays clipped until a resize forces a relayout -- exactly the reported "I
have to resize the window to see the border".

Restore `width={cols}`. `cols` derives from `transcriptPanelWidth` and
`process.stdout.columns`, both correct on frame 1, so pinning to it is right at
first paint AND after resize; the same pty run now closes the box at both
sizes. #769's other half stands: without its `maxWidth` fix, `cols` would still
be the composer's budget and pinning to it would reproduce the original bug.

The render test asserted the box fills its container, which is what let this
through -- under a correctly-sized container both behaviors agree. It now
renders against a DELIBERATELY oversized container (gutter omitted, reproducing
frame 1) and asserts the box holds its steady-state width regardless. Removing
`width={cols}` fails it at all 9 widths.

Suite: 8 failed / 1581 passed -- the same 8 pre-existing failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ericleepi314
ericleepi314 merged commit 62a1422 into main Jul 29, 2026
3 checks passed
@github-actions

Copy link
Copy Markdown

Test Results

    1 files      1 suites   7m 42s ⏱️
9 065 tests 9 059 ✅ 6 💤 0 ❌
9 311 runs  9 305 ✅ 6 💤 0 ❌

Results for commit 843b228.

@ericleepi314
ericleepi314 deleted the fix/header-box-first-paint-width branch July 29, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant