Skip to content

Commit a8a9458

Browse files
committed
fix(tui): reserve stable top row for copy icon to prevent layout shift
Always render the top row in assistant messages so that hovering to reveal the copy icon does not cause a 1-line layout shift. When not hovered, the row is filled with spaces (invisible); on hover, the copy icon appears in the same reserved space. This fixes the layout shift introduced by 80bccc8 without reintroducing the spurious blank-line artifact it originally fixed. Assisted-By: docker-agent
1 parent b355a29 commit a8a9458

File tree

1 file changed

+8
-11
lines changed

1 file changed

+8
-11
lines changed

pkg/tui/components/message/message.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -145,22 +145,19 @@ func (mv *messageModel) Render(width int) string {
145145
prefix = mv.senderPrefix(msg.Sender)
146146
}
147147

148-
// Show copy icon in the top-right corner when hovered or selected.
149-
// AssistantMessageStyle has PaddingTop=0 (unlike UserMessageStyle which has
150-
// PaddingTop=1), so we cannot unconditionally prepend topRow+"\n" — doing so
151-
// would add a spurious blank line to every message in the default state.
152-
// Accept the 1-line layout shift on hover; it is less disruptive than the
153-
// blank-line artifact that affects all messages at all times.
148+
// Always reserve a top row to avoid layout shifts when the copy icon
149+
// appears on hover. When not hovered, the row is filled with spaces
150+
// (invisible). AssistantMessageStyle has PaddingTop=0, so this extra
151+
// row acts as a stable spacer.
152+
innerWidth := width - messageStyle.GetHorizontalFrameSize()
153+
topRow := strings.Repeat(" ", innerWidth)
154154
if mv.hovered || mv.selected {
155-
innerWidth := width - messageStyle.GetHorizontalFrameSize()
156155
copyIcon := styles.MutedStyle.Render(types.AssistantMessageCopyLabel)
157156
iconWidth := ansi.StringWidth(types.AssistantMessageCopyLabel)
158157
padding := max(innerWidth-iconWidth, 0)
159-
topRow := strings.Repeat(" ", padding) + copyIcon
160-
noTopPaddingStyle := messageStyle.PaddingTop(0)
161-
return prefix + noTopPaddingStyle.Width(width).Render(topRow+"\n"+rendered)
158+
topRow = strings.Repeat(" ", padding) + copyIcon
162159
}
163-
return prefix + messageStyle.Render(rendered)
160+
return prefix + messageStyle.Width(width).Render(topRow+"\n"+rendered)
164161
case types.MessageTypeShellOutput:
165162
if rendered, err := markdown.NewRenderer(width).Render(fmt.Sprintf("```console\n%s\n```", msg.Content)); err == nil {
166163
return rendered

0 commit comments

Comments
 (0)