Skip to content

fix(tui): don't panic when the terminal reports a degenerate size#3736

Merged
aheritier merged 2 commits into
docker:mainfrom
dgageot:fix/tui-degenerate-terminal-size-panic
Jul 18, 2026
Merged

fix(tui): don't panic when the terminal reports a degenerate size#3736
aheritier merged 2 commits into
docker:mainfrom
dgageot:fix/tui-degenerate-terminal-size-panic

Conversation

@dgageot

@dgageot dgageot commented Jul 18, 2026

Copy link
Copy Markdown
Member

When a terminal reports a degenerate size (0×0 or 1×1), the resize handle in pkg/tui/tui.go computed a negative repeat count for strings.Repeat, crashing the TUI with panic: strings: negative Repeat count. The existing width <= 0 guard did not account for the horizontal padding subtracted afterwards, so any width narrower than appPaddingHorizontal slipped through and produced a negative inner width.

The fix adds a guard on the padded inner width itself. A follow-up commit also handles the status suffix: lipgloss ignores non-positive MaxWidth, so a paused/working suffix wider than the terminal could overflow the row on very narrow terminals. The suffix logic is extracted into a lineWithSuffix helper that truncates the suffix when the terminal is too narrow to accommodate it.

Both fixes are covered by a unit test over tiny widths (pkg/tui/tui_resize_handle_test.go), a full-TUI e2e resize cycle through degenerate sizes (e2e/tui/degenerate_resize_test.go), and a board-TUI render sweep across degenerate sizes × all dialogs (pkg/board/tui/degenerate_size_test.go). All three tests were verified to panic on main without the fix.

dgageot added 2 commits July 17, 2026 18:29
renderResizeHandle guarded width <= 0 but the padded inner width
(width - appPaddingHorizontal) still went negative for tiny widths
(e.g. a 1x1 terminal), feeding strings.Repeat a negative count.
Guard on the inner width instead.

Adds regression tests: a unit test on renderResizeHandle, a full-TUI
e2e resize cycle through degenerate sizes, and a board-TUI render
sweep across sizes and dialogs.

Assisted-By: Claude
MaxWidth(innerWidth-suffixWidth) is ignored by lipgloss when non-positive,
letting the handle line overflow the row when the paused/working suffix is
wider than the terminal. Extract the duplicated suffix logic into
lineWithSuffix, which truncates the suffix itself when it alone does not fit.
Also drop a now-redundant max(0, …), switch the regression test to testify,
and clarify the e2e comment.

Assisted-By: Claude
@dgageot
dgageot requested a review from a team as a code owner July 18, 2026 07:53

@docker-agent docker-agent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment: 🟢 APPROVE

The degenerate-size panic fix is correct and well-tested. The innerWidth <= 0 guard properly catches all cases where width - appPaddingHorizontal would be non-positive, preventing the negative strings.Repeat count that caused the panic. The extracted lineWithSuffix helper handles both the overflow and normal paths correctly. The three test layers (unit, e2e, board-TUI sweep) provide solid coverage.

@aheritier aheritier added area/tui For features/issues/fixes related to the TUI kind/fix PR fixes a bug (maps to fix:). Use on PRs only. labels Jul 18, 2026
@aheritier
aheritier merged commit 4d1fb51 into docker:main Jul 18, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/tui For features/issues/fixes related to the TUI kind/fix PR fixes a bug (maps to fix:). Use on PRs only.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants