Skip to content

Fix/progress tty line overflow 13595#13840

Open
glours wants to merge 2 commits into
docker:mainfrom
glours:fix/progress-tty-line-overflow-13595
Open

Fix/progress tty line overflow 13595#13840
glours wants to merge 2 commits into
docker:mainfrom
glours:fix/progress-tty-line-overflow-13595

Conversation

@glours

@glours glours commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

What I did
Fixes the TTY progress UI mangled output in narrow terminals (most visibly tmux panes) reported in #13595.

adjustLineWidth could shrink details and taskID but never the progress field. When progress carried the X.XMB / Y.YMB size suffix, the truncation loop exited with overflow > 0 and applyPadding's max(timerPad, 1) floor pushed the line one char over terminalWidth. tmux then
wrapped it visually while print() kept counting logical lines, desyncing aec.Up() on the next render and producing the [+] pull X/Y header overwriting prior task lines.

Commits

  1. drop size info from progress bar when line overflows — track the size suffix byte length on lineData and let adjustLineWidth drop it as an intermediate truncation step before abbreviating the taskID. Adds two regression tests (unit + multi-render).
  2. measure taskID width in runes, not bytesmaxBeforeStatusWidth used len(l.taskID) while applyPadding used utf8.RuneCountInString. For multi-byte UTF-8 task IDs (CJK, emoji, accented Latin) this overestimated width, triggering spurious truncation that sliced mid-multibyte and
    corrupted the rendered string. Aligns the two measurements.

Related issue

Fixes #13595

(not mandatory) A picture of a cute animal, if possible in relation to what you did
image

glours added 2 commits June 12, 2026 14:51
In narrow terminals (e.g. tmux panes), the TTY progress UI emitted
lines wider than terminalWidth because adjustLineWidth could shrink
details and taskID but never the progress field. When progress
carried the "X.XMB / Y.YMB" size suffix, the truncation loop exited
with overflow > 0 and applyPadding's max(timerPad, 1) floor pushed
the line one char over. tmux then wrapped the line visually while
print() kept counting logical lines, desyncing aec.Up() on the next
render and producing the mangled "[+] pull X/Y" header overwriting
prior task lines.

Track the size suffix byte length on lineData and let
adjustLineWidth drop it as an intermediate truncation step before
abbreviating the taskID.

Fixes docker#13595

Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
maxBeforeStatusWidth used len(l.taskID) (bytes) while applyPadding
used utf8.RuneCountInString (runes). For ASCII task IDs the two
agree and no symptom surfaces, but a taskID containing multi-byte
UTF-8 chars (CJK, emoji, accented Latin) reported a width larger
than its visual columns. computeOverflow then triggered truncation
where none was needed, and truncateLongestTaskID's byte-indexed
slice could land mid-multibyte sequence, corrupting the displayed
string.

Align the two measurements on rune count.

Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 12, 2026 13:12
@glours glours requested a review from a team as a code owner June 12, 2026 13:12
@glours glours requested a review from ndeloof June 12, 2026 13:12

@docker-agent docker-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Assessment: 🟢 APPROVE

The fix correctly addresses two bugs in the TTY progress display:

  1. Progress size suffix strippingtruncateProgressSize now drops trailing X.XMB / Y.YMB size info before truncating task IDs, closing the overflow path that caused tmux line-wrapping and desync'd aec.Up() cursor movement.
  2. Rune-count alignmentmaxBeforeStatusWidth now uses utf8.RuneCountInString(l.taskID) to match applyPadding, preventing spurious truncation and mid-multibyte slicing for CJK/emoji/accented task IDs.

The logic is sound, new regression tests cover both scenarios, and no CONFIRMED/LIKELY bugs were found in the changed code.

Note (pre-existing, out of scope): truncateLongestTaskID (not changed by this PR) still uses byte-length len() for taskID selection and slicing while overflow is now measured in runes — this inconsistency could cause incorrect truncation for multi-byte task IDs in a future interaction, but it is not introduced by this PR.

Copilot AI 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.

Pull request overview

Fixes Compose’s TTY progress renderer so lines don’t overflow/wrap in narrow terminals (notably tmux panes), preventing cursor desync and mangled multi-line progress output (Fixes #13595).

Changes:

  • Track the byte length of the trailing progress size suffix and allow adjustLineWidth to drop that suffix as an intermediate truncation step.
  • Align maxBeforeStatusWidth with applyPadding by measuring taskID width in runes (UTF-8 aware).
  • Add regression tests covering multi-byte task IDs and multi-render narrow-terminal scenarios.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
cmd/display/tty.go Adds progress-size suffix tracking and truncation step; fixes taskID width computation to be rune-based.
cmd/display/tty_test.go Adds unit + multi-render regression tests for overflow behavior and UTF-8 taskID width handling.

Comment thread cmd/display/tty.go
Comment on lines +483 to +493
func truncateProgressSize(lines []lineData) bool {
for i := range lines {
l := &lines[i]
if l.progressSizeBytes > 0 {
l.progress = l.progress[:len(l.progress)-l.progressSizeBytes]
l.progressSizeBytes = 0
return true
}
}
return false
}
Comment thread cmd/display/tty.go
Comment on lines 448 to 453
func maxBeforeStatusWidth(lines []lineData) int {
var maxWidth int
for i := range lines {
l := &lines[i]
width := 3 + lenAnsi(l.prefix) + len(l.taskID) + lenAnsi(l.progress)
width := 3 + lenAnsi(l.prefix) + utf8.RuneCountInString(l.taskID) + lenAnsi(l.progress)
if width > maxWidth {
@codecov

codecov Bot commented Jun 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.65217% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cmd/display/tty.go 95.65% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

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.

[BUG] Terminal output is broken in tmux

2 participants