From f15aee70a8c937bdc56da5d20225168cc04f541f Mon Sep 17 00:00:00 2001 From: David Gageot Date: Wed, 15 Oct 2025 10:22:37 +0200 Subject: [PATCH] Fix #501 Signed-off-by: David Gageot --- pkg/tui/components/markdown/renderer.go | 22 +++++++++++++ pkg/tui/components/message/message.go | 43 ++++++++++++------------- pkg/tui/components/messages/messages.go | 22 ++----------- 3 files changed, 46 insertions(+), 41 deletions(-) create mode 100644 pkg/tui/components/markdown/renderer.go diff --git a/pkg/tui/components/markdown/renderer.go b/pkg/tui/components/markdown/renderer.go new file mode 100644 index 000000000..5c60211ed --- /dev/null +++ b/pkg/tui/components/markdown/renderer.go @@ -0,0 +1,22 @@ +package markdown + +import ( + "github.com/charmbracelet/glamour/v2" + allstyles "github.com/charmbracelet/glamour/v2/styles" +) + +func uintPtr(u uint) *uint { return &u } + +func NewRenderer(width int) *glamour.TermRenderer { + customDarkStyle := *allstyles.DefaultStyles["dark"] + + customDarkStyle.Document.Margin = uintPtr(0) + customDarkStyle.Document.BlockPrefix = "" + customDarkStyle.Document.BlockSuffix = "" + + r, _ := glamour.NewTermRenderer( + glamour.WithWordWrap(min(width, 120)), + glamour.WithStyles(customDarkStyle), + ) + return r +} diff --git a/pkg/tui/components/message/message.go b/pkg/tui/components/message/message.go index 59aa7a7fd..6e6626a2d 100644 --- a/pkg/tui/components/message/message.go +++ b/pkg/tui/components/message/message.go @@ -7,8 +7,9 @@ import ( "github.com/charmbracelet/bubbles/v2/spinner" tea "github.com/charmbracelet/bubbletea/v2" - "github.com/charmbracelet/glamour/v2" + "github.com/charmbracelet/lipgloss/v2" + "github.com/docker/cagent/pkg/tui/components/markdown" "github.com/docker/cagent/pkg/tui/core/layout" "github.com/docker/cagent/pkg/tui/styles" "github.com/docker/cagent/pkg/tui/types" @@ -23,23 +24,21 @@ type Model interface { // messageModel implements Model type messageModel struct { - message *types.Message - renderer *glamour.TermRenderer - width int - height int - focused bool - spinner spinner.Model + message *types.Message + width int + height int + focused bool + spinner spinner.Model } // New creates a new message view -func New(msg *types.Message, renderer *glamour.TermRenderer) Model { +func New(msg *types.Message) *messageModel { return &messageModel{ - message: msg, - width: 80, // Default width - height: 1, // Will be calculated - focused: false, - spinner: spinner.New(spinner.WithSpinner(spinner.Points)), - renderer: renderer, + message: msg, + width: 80, // Default width + height: 1, // Will be calculated + focused: false, + spinner: spinner.New(spinner.WithSpinner(spinner.Points)), } } @@ -73,18 +72,18 @@ func (mv *messageModel) View() string { return mv.Render(mv.width) } -// MessageView specific methods - // Render renders the message view content -func (mv *messageModel) Render(int) string { +func (mv *messageModel) Render(width int) string { msg := mv.message switch msg.Type { case types.MessageTypeSpinner: return mv.spinner.View() case types.MessageTypeUser: - if rendered, err := mv.renderer.Render("> " + msg.Content); err == nil { - return strings.TrimRight(rendered, "\n\r\t ") + s := lipgloss.NewStyle().PaddingLeft(1).BorderLeft(true).BorderStyle(lipgloss.ThickBorder()) + if rendered, err := markdown.NewRenderer(width - len(s.Render(""))).Render(msg.Content); err == nil { + return s.Render(strings.TrimRight(rendered, "\n\r\t ")) } + return msg.Content case types.MessageTypeAssistant: if msg.Content == "" { @@ -92,7 +91,7 @@ func (mv *messageModel) Render(int) string { } text := senderPrefix(msg.Sender) + msg.Content - rendered, err := mv.renderer.Render(text) + rendered, err := markdown.NewRenderer(width).Render(text) if err != nil { return text } @@ -104,7 +103,7 @@ func (mv *messageModel) Render(int) string { } text := "Thinking: " + senderPrefix(msg.Sender) + msg.Content // Render through the markdown renderer to ensure proper wrapping to width - rendered, err := mv.renderer.Render(text) + rendered, err := markdown.NewRenderer(width).Render(text) if err != nil { return styles.MutedStyle.Italic(true).Render(text) } @@ -112,7 +111,7 @@ func (mv *messageModel) Render(int) string { clean := stripANSI(strings.TrimRight(rendered, "\n\r\t ")) return styles.MutedStyle.Italic(true).Render(clean) case types.MessageTypeShellOutput: - if rendered, err := mv.renderer.Render(fmt.Sprintf("```console\n%s\n```", msg.Content)); err == nil { + if rendered, err := markdown.NewRenderer(width).Render(fmt.Sprintf("```console\n%s\n```", msg.Content)); err == nil { return strings.TrimRight(rendered, "\n\r\t ") } return msg.Content diff --git a/pkg/tui/components/messages/messages.go b/pkg/tui/components/messages/messages.go index 7d58b142f..6758c24ad 100644 --- a/pkg/tui/components/messages/messages.go +++ b/pkg/tui/components/messages/messages.go @@ -7,13 +7,12 @@ import ( "github.com/charmbracelet/bubbles/v2/help" "github.com/charmbracelet/bubbles/v2/key" tea "github.com/charmbracelet/bubbletea/v2" - "github.com/charmbracelet/glamour/v2" - "github.com/charmbracelet/glamour/v2/styles" "github.com/charmbracelet/lipgloss/v2" "github.com/docker/cagent/pkg/app" "github.com/docker/cagent/pkg/runtime" "github.com/docker/cagent/pkg/tools" + "github.com/docker/cagent/pkg/tui/components/markdown" "github.com/docker/cagent/pkg/tui/components/message" "github.com/docker/cagent/pkg/tui/components/tool" "github.com/docker/cagent/pkg/tui/core" @@ -53,7 +52,6 @@ type renderedItem struct { // model implements Model type model struct { - renderer *glamour.TermRenderer messages []types.Message views []layout.Model width int @@ -207,18 +205,6 @@ func (m *model) SetSize(width, height int) tea.Cmd { width = 10 } - // Build a custom style - customDarkStyle := *styles.DefaultStyles["dark"] - customDarkStyle.Document.Margin = uintPtr(0) - - // Initialize or update renderer - if r, err := glamour.NewTermRenderer( - glamour.WithWordWrap(min(width, 120)), - glamour.WithStyles(customDarkStyle), - ); err == nil { - m.renderer = r - } - // Update all views with new size for _, view := range m.views { view.SetSize(width, 0) @@ -626,13 +612,13 @@ func (m *model) PlainTextTranscript() string { } func (m *model) createToolCallView(msg *types.Message) layout.Model { - view := tool.New(msg, m.app, m.renderer) + view := tool.New(msg, m.app, markdown.NewRenderer(m.width)) view.SetSize(m.width, 0) return view } func (m *model) createMessageView(msg *types.Message) layout.Model { - view := message.New(msg, m.renderer) + view := message.New(msg) view.SetSize(m.width, 0) return view } @@ -703,5 +689,3 @@ func toolResultLabel(msg types.Message) string { } return fmt.Sprintf("Tool Result (%s)", name) } - -func uintPtr(u uint) *uint { return &u }