Skip to content

fix(tui): remove spurious blank line from every assistant message#2369

Merged
dgageot merged 1 commit intodocker:mainfrom
steilerDev:fix/tui-assistant-message-blank-line
Apr 10, 2026
Merged

fix(tui): remove spurious blank line from every assistant message#2369
dgageot merged 1 commit intodocker:mainfrom
steilerDev:fix/tui-assistant-message-blank-line

Conversation

@steilerDev
Copy link
Copy Markdown
Contributor

Problem

Since v1.41.0 (commit 574a466), every assistant message in the TUI has an extra blank line at the top in its normal (non-hovered) state. In longer conversations this accumulates, causing the input area to be displaced and ghost text to appear at unexpected positions on screen. The artifacts are most visible when the session panel is open.

Closes #2368

Root cause

The copy-button commit applied the user-message "topRow+\n" rendering pattern to assistant messages without accounting for a style difference:

Style PaddingTop PaddingTop(0) effect topRow+"\n" net change
UserMessageStyle 1 (from BaseMessageStyle.Padding(1,1)) removes 1 line 0 — replaces removed padding ✓
AssistantMessageStyle 0 (BaseMessageStyle.Padding(0,1) override) no-op +1 spurious blank line ✗

The unconditional topRow+"\n"+rendered always prepends "\n" when not hovered/selected (because topRow == ""), adding one blank line to every assistant message at all times.

Fix

Only use the topRow+"\n" path when the copy icon is actually visible (hovered or selected). For the common case fall back to the original messageStyle.Render(rendered) from before 574a466.

-       // Always reserve a top row for the copy icon to avoid layout shifts.
-       // The icon is only visible when hovered or selected.
-       innerWidth := width - messageStyle.GetHorizontalFrameSize()
-       var topRow string
+       // Show copy icon in the top-right corner when hovered or selected.
+       // AssistantMessageStyle has PaddingTop=0 (unlike UserMessageStyle which has
+       // PaddingTop=1), so we cannot unconditionally prepend topRow+"\n" — doing so
+       // would add a spurious blank line to every message in the default state.
+       // Accept the 1-line layout shift on hover; it is less disruptive than the
+       // blank-line artifact that affects all messages at all times.
        if mv.hovered || mv.selected {
+               innerWidth := width - messageStyle.GetHorizontalFrameSize()
                copyIcon := styles.MutedStyle.Render(types.AssistantMessageCopyLabel)
                iconWidth := ansi.StringWidth(types.AssistantMessageCopyLabel)
                padding := max(innerWidth-iconWidth, 0)
-               topRow = strings.Repeat(" ", padding) + copyIcon
+               topRow := strings.Repeat(" ", padding) + copyIcon
+               noTopPaddingStyle := messageStyle.PaddingTop(0)
+               return prefix + noTopPaddingStyle.Width(width).Render(topRow+"\n"+rendered)
        }
-       noTopPaddingStyle := messageStyle.PaddingTop(0)
-       return prefix + noTopPaddingStyle.Width(width).Render(topRow+"\n"+rendered)
+       return prefix + messageStyle.Render(rendered)

The trade-off is a 1-line layout shift when transitioning into the hovered/selected state. A layout-shift-free fix would require giving AssistantMessageStyle a PaddingTop(1) (matching UserMessageStyle) and filling topRow with spaces when not hovered — but that changes the visual appearance of all assistant messages. The simpler fix here is least-invasive and fully restores v1.40.0 rendering behaviour for the common path.

Testing

Verified: go build ./pkg/tui/... clean on current main.

Commit 574a466 introduced the copy-on-hover button for assistant
messages using the same "topRow+\n" pattern as user messages. However,
UserMessageStyle has PaddingTop=1 (inherited from BaseMessageStyle) so
PaddingTop(0)+topRow+"\n" is net-zero. AssistantMessageStyle overrides
to Padding(0,1), meaning PaddingTop=0 already; PaddingTop(0) is a
no-op and the unconditional "\n" prefix adds a spurious blank line to
every assistant message in its normal (non-hovered) state.

Fix: only use the topRow+"\n" rendering path when the copy icon is
actually visible (hovered or selected). Fall back to the original
messageStyle.Render(rendered) for the common case. This accepts a
1-line layout shift on hover rather than a permanent blank-line
artifact on every message.

Fixes: docker#2368

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@steilerDev steilerDev requested a review from a team as a code owner April 10, 2026 09:02
@dgageot dgageot merged commit 5bd7746 into docker:main Apr 10, 2026
8 checks passed
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.

TUI rendering artifacts since v1.41.0: spurious blank line in every assistant message + input area displaced

2 participants