From ee5f41764b57ac8bbae3d499c25824ba18c8a055 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Thu, 26 Feb 2026 12:51:44 +0300 Subject: [PATCH] fix(ui): remove borders from agent messages, move user border to left with Primary color Agent messages now render borderless. User messages use a left border with theme.Primary (Mauve) instead of a right border with Secondary. --- internal/ui/block_renderer.go | 80 +++++++++++++++++++++-------------- internal/ui/messages.go | 9 ++-- 2 files changed, 53 insertions(+), 36 deletions(-) diff --git a/internal/ui/block_renderer.go b/internal/ui/block_renderer.go index 57686a85..d6eb4736 100644 --- a/internal/ui/block_renderer.go +++ b/internal/ui/block_renderer.go @@ -11,6 +11,7 @@ type blockRenderer struct { align *lipgloss.Position borderColor *color.Color fullWidth bool + noBorder bool paddingTop int paddingBottom int paddingLeft int @@ -32,6 +33,14 @@ func WithFullWidth() renderingOption { } } +// WithNoBorder returns a renderingOption that disables all borders on the +// block, rendering content with only padding. +func WithNoBorder() renderingOption { + return func(c *blockRenderer) { + c.noBorder = true + } +} + // WithAlign returns a renderingOption that sets the horizontal alignment // of the block content within its container. The align parameter accepts // lipgloss.Left, lipgloss.Center, or lipgloss.Right positions. @@ -134,44 +143,53 @@ func renderContentBlock(content string, containerWidth int, options ...rendering PaddingBottom(renderer.paddingBottom). PaddingLeft(renderer.paddingLeft). PaddingRight(renderer.paddingRight). - Foreground(theme.Text). - BorderStyle(lipgloss.ThickBorder()) + Foreground(theme.Text) - align := lipgloss.Left - if renderer.align != nil { - align = *renderer.align - } + // Border width used for full-width calculation. + borderChars := 0 - // Default to transparent/no border color - var borderColor color.Color = lipgloss.NoColor{} - if renderer.borderColor != nil { - borderColor = *renderer.borderColor - } + if renderer.noBorder { + // No borders — just padding. + } else { + style = style.BorderStyle(lipgloss.ThickBorder()) - // Very muted color for the opposite border - mutedOppositeBorder := AdaptiveColor("#F3F4F6", "#1F2937") + align := lipgloss.Left + if renderer.align != nil { + align = *renderer.align + } - // Align determines which border gets the accent color. - // All blocks span full width — no horizontal floating. - switch align { - case lipgloss.Right: - style = style. - BorderRight(true). - BorderLeft(true). - BorderRightForeground(borderColor). - BorderLeftForeground(mutedOppositeBorder) - default: // Left (and fallback) - style = style. - BorderLeft(true). - BorderRight(true). - BorderLeftForeground(borderColor). - BorderRightForeground(mutedOppositeBorder) + // Default to transparent/no border color + var borderColor color.Color = lipgloss.NoColor{} + if renderer.borderColor != nil { + borderColor = *renderer.borderColor + } + + // Very muted color for the opposite border + mutedOppositeBorder := AdaptiveColor("#F3F4F6", "#1F2937") + + // Align determines which border gets the accent color. + switch align { + case lipgloss.Right: + style = style. + BorderRight(true). + BorderLeft(true). + BorderRightForeground(borderColor). + BorderLeftForeground(mutedOppositeBorder) + borderChars = 2 + default: // Left (and fallback) + style = style. + BorderLeft(true). + BorderRight(true). + BorderLeftForeground(borderColor). + BorderRightForeground(mutedOppositeBorder) + borderChars = 2 + } } if renderer.fullWidth { - // Subtract 2 for left + right border characters so the total - // rendered width equals containerWidth exactly. - style = style.Width(renderer.width - 2) + // Subtract border characters so the total rendered width + // equals containerWidth exactly. + style = style.Width(renderer.width - borderChars) } content = style.Render(content) diff --git a/internal/ui/messages.go b/internal/ui/messages.go index f5ad0760..58d1bb2d 100644 --- a/internal/ui/messages.go +++ b/internal/ui/messages.go @@ -107,8 +107,8 @@ func (r *MessageRenderer) RenderUserMessage(content string, timestamp time.Time) rendered := renderContentBlock( fullContent, r.width, - WithAlign(lipgloss.Right), - WithBorderColor(theme.Secondary), + WithAlign(lipgloss.Left), + WithBorderColor(theme.Primary), WithMarginBottom(1), ) @@ -151,12 +151,11 @@ func (r *MessageRenderer) RenderAssistantMessage(content string, timestamp time. fullContent := strings.TrimSuffix(messageContent, "\n") + "\n" + lipgloss.NewStyle().Foreground(theme.VeryMuted).Render(info) - // Use the new block renderer + // Use the new block renderer — no borders for agent messages. rendered := renderContentBlock( fullContent, r.width, - WithAlign(lipgloss.Left), - WithBorderColor(theme.Primary), + WithNoBorder(), WithMarginBottom(1), )