From fd437468b74e250f4d197b29c7857ce1ebbb406e Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Thu, 5 Feb 2026 07:44:24 -0300 Subject: [PATCH] fix(ui): consistent box sizing (#2127) * fix(ui): width Signed-off-by: Carlos Alexandro Becker * fix: simplify Signed-off-by: Carlos Alexandro Becker --------- Signed-off-by: Carlos Alexandro Becker --- internal/ui/chat/agent.go | 14 ++++++------- internal/ui/chat/assistant.go | 10 ++++----- internal/ui/chat/bash.go | 19 ++++++++--------- internal/ui/chat/diagnostics.go | 7 +++---- internal/ui/chat/fetch.go | 27 +++++++++++-------------- internal/ui/chat/file.go | 27 +++++++++++-------------- internal/ui/chat/generic.go | 9 ++++----- internal/ui/chat/lsp_restart.go | 7 +++---- internal/ui/chat/mcp.go | 11 +++++----- internal/ui/chat/messages.go | 5 ----- internal/ui/chat/references.go | 7 +++---- internal/ui/chat/search.go | 36 +++++++++++++++------------------ internal/ui/chat/todos.go | 9 ++++----- internal/ui/chat/tools.go | 8 -------- internal/ui/chat/user.go | 14 ++++++------- 15 files changed, 86 insertions(+), 124 deletions(-) diff --git a/internal/ui/chat/agent.go b/internal/ui/chat/agent.go index c2a439ff23d0bd046b75076ea30de68b60cdcc54..4784b314169f92efe4e80bf875eea5fd3780fe86 100644 --- a/internal/ui/chat/agent.go +++ b/internal/ui/chat/agent.go @@ -99,7 +99,6 @@ type AgentToolRenderContext struct { // RenderTool implements the [ToolRenderer] interface. func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if !opts.ToolCall.Finished && !opts.IsCanceled() && len(r.agent.nestedTools) == 0 { return pendingTool(sty, "Agent", opts.Anim) } @@ -110,7 +109,7 @@ func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts prompt := params.Prompt prompt = strings.ReplaceAll(prompt, "\n", " ") - header := toolHeader(sty, opts.Status, "Agent", cappedWidth, opts.Compact) + header := toolHeader(sty, opts.Status, "Agent", width, opts.Compact) if opts.Compact { return header } @@ -120,7 +119,7 @@ func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts taskTagWidth := lipgloss.Width(taskTag) // Calculate remaining width for prompt. - remainingWidth := min(cappedWidth-taskTagWidth-3, maxTextWidth-taskTagWidth-3) // -3 for spacing + remainingWidth := width - taskTagWidth - 3 // -3 for spacing promptText := sty.Tool.AgentPrompt.Width(remainingWidth).Render(prompt) @@ -157,7 +156,7 @@ func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts // Add body content when completed. if opts.HasResult() && opts.Result.Content != "" { - body := toolOutputMarkdownContent(sty, opts.Result.Content, cappedWidth-toolBodyLeftPaddingTotal, opts.ExpandedContent) + body := toolOutputMarkdownContent(sty, opts.Result.Content, width-toolBodyLeftPaddingTotal, opts.ExpandedContent) return joinToolParts(result, body) } @@ -230,7 +229,6 @@ type agenticFetchParams struct { // RenderTool implements the [ToolRenderer] interface. func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if !opts.ToolCall.Finished && !opts.IsCanceled() && len(r.fetch.nestedTools) == 0 { return pendingTool(sty, "Agentic Fetch", opts.Anim) } @@ -247,7 +245,7 @@ func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int toolParams = append(toolParams, params.URL) } - header := toolHeader(sty, opts.Status, "Agentic Fetch", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "Agentic Fetch", width, opts.Compact, toolParams...) if opts.Compact { return header } @@ -257,7 +255,7 @@ func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int promptTagWidth := lipgloss.Width(promptTag) // Calculate remaining width for prompt text. - remainingWidth := min(cappedWidth-promptTagWidth-3, maxTextWidth-promptTagWidth-3) // -3 for spacing + remainingWidth := width - promptTagWidth - 3 // -3 for spacing promptText := sty.Tool.AgentPrompt.Width(remainingWidth).Render(prompt) @@ -294,7 +292,7 @@ func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int // Add body content when completed. if opts.HasResult() && opts.Result.Content != "" { - body := toolOutputMarkdownContent(sty, opts.Result.Content, cappedWidth-toolBodyLeftPaddingTotal, opts.ExpandedContent) + body := toolOutputMarkdownContent(sty, opts.Result.Content, width-toolBodyLeftPaddingTotal, opts.ExpandedContent) return joinToolParts(result, body) } diff --git a/internal/ui/chat/assistant.go b/internal/ui/chat/assistant.go index 4ce71dda2515e5489900c33eb716e1d6d884409a..b9aa19456eb05739484c0b4d1a28813a7b46bb11 100644 --- a/internal/ui/chat/assistant.go +++ b/internal/ui/chat/assistant.go @@ -79,22 +79,20 @@ func (a *AssistantMessageItem) ID() string { // RawRender implements [MessageItem]. func (a *AssistantMessageItem) RawRender(width int) string { - cappedWidth := cappedMessageWidth(width) - var spinner string if a.isSpinning() { spinner = a.renderSpinning() } - content, height, ok := a.getCachedRender(cappedWidth) + content, height, ok := a.getCachedRender(width) if !ok { - content = a.renderMessageContent(cappedWidth) + content = a.renderMessageContent(width) height = lipgloss.Height(content) // cache the rendered content - a.setCachedRender(content, cappedWidth, height) + a.setCachedRender(content, width, height) } - highlightedContent := a.renderHighlighted(content, cappedWidth, height) + highlightedContent := a.renderHighlighted(content, width, height) if spinner != "" { if highlightedContent != "" { highlightedContent += "\n\n" diff --git a/internal/ui/chat/bash.go b/internal/ui/chat/bash.go index 18be27ee01b4fcc21749789fc65ec0b71c2b0d4b..445043aef9809b69126d0c409596a299f6a3aa58 100644 --- a/internal/ui/chat/bash.go +++ b/internal/ui/chat/bash.go @@ -39,7 +39,6 @@ type BashToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Bash", opts.Anim) } @@ -58,7 +57,7 @@ func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts * if meta.Background { description := cmp.Or(meta.Description, params.Command) content := "Command: " + params.Command + "\n" + opts.Result.Content - return renderJobTool(sty, opts, cappedWidth, "Start", meta.ShellID, description, content) + return renderJobTool(sty, opts, width, "Start", meta.ShellID, description, content) } // Regular bash command. @@ -69,12 +68,12 @@ func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts * toolParams = append(toolParams, "background", "true") } - header := toolHeader(sty, opts.Status, "Bash", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "Bash", width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -90,7 +89,7 @@ func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts * return header } - bodyWidth := cappedWidth - toolBodyLeftPaddingTotal + bodyWidth := width - toolBodyLeftPaddingTotal body := sty.Tool.Body.Render(toolOutputPlainContent(sty, output, bodyWidth, opts.ExpandedContent)) return joinToolParts(header, body) } @@ -121,14 +120,13 @@ type JobOutputToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (j *JobOutputToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Job", opts.Anim) } var params tools.JobOutputParams if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } var description string @@ -143,7 +141,7 @@ func (j *JobOutputToolRenderContext) RenderTool(sty *styles.Styles, width int, o if opts.HasResult() { content = opts.Result.Content } - return renderJobTool(sty, opts, cappedWidth, "Output", params.ShellID, description, content) + return renderJobTool(sty, opts, width, "Output", params.ShellID, description, content) } // ----------------------------------------------------------------------------- @@ -172,14 +170,13 @@ type JobKillToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (j *JobKillToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Job", opts.Anim) } var params tools.JobKillParams if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } var description string @@ -194,7 +191,7 @@ func (j *JobKillToolRenderContext) RenderTool(sty *styles.Styles, width int, opt if opts.HasResult() { content = opts.Result.Content } - return renderJobTool(sty, opts, cappedWidth, "Kill", params.ShellID, description, content) + return renderJobTool(sty, opts, width, "Kill", params.ShellID, description, content) } // renderJobTool renders a job-related tool with the common pattern: diff --git a/internal/ui/chat/diagnostics.go b/internal/ui/chat/diagnostics.go index 68d2ac4a00dc880c27904468008fb8f6b2fcf9c5..16dbda3563b55d881944eea4328d1f2ff99d2d87 100644 --- a/internal/ui/chat/diagnostics.go +++ b/internal/ui/chat/diagnostics.go @@ -35,7 +35,6 @@ type DiagnosticsToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (d *DiagnosticsToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Diagnostics", opts.Anim) } @@ -49,12 +48,12 @@ func (d *DiagnosticsToolRenderContext) RenderTool(sty *styles.Styles, width int, mainParam = fsext.PrettyPath(params.FilePath) } - header := toolHeader(sty, opts.Status, "Diagnostics", cappedWidth, opts.Compact, mainParam) + header := toolHeader(sty, opts.Status, "Diagnostics", width, opts.Compact, mainParam) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -62,7 +61,7 @@ func (d *DiagnosticsToolRenderContext) RenderTool(sty *styles.Styles, width int, return header } - bodyWidth := cappedWidth - toolBodyLeftPaddingTotal + bodyWidth := width - toolBodyLeftPaddingTotal body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent)) return joinToolParts(header, body) } diff --git a/internal/ui/chat/fetch.go b/internal/ui/chat/fetch.go index e3f3a809550385dfd0ec557e98151ffc731acc93..588b2926258b01b8579330211de83eb266a5adcd 100644 --- a/internal/ui/chat/fetch.go +++ b/internal/ui/chat/fetch.go @@ -34,14 +34,13 @@ type FetchToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (f *FetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Fetch", opts.Anim) } var params tools.FetchParams if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } toolParams := []string{params.URL} @@ -52,12 +51,12 @@ func (f *FetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts toolParams = append(toolParams, "timeout", formatTimeout(params.Timeout)) } - header := toolHeader(sty, opts.Status, "Fetch", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "Fetch", width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -67,7 +66,7 @@ func (f *FetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts // Determine file extension for syntax highlighting based on format. file := getFileExtensionForFormat(params.Format) - body := toolOutputCodeContent(sty, file, opts.Result.Content, 0, cappedWidth, opts.ExpandedContent) + body := toolOutputCodeContent(sty, file, opts.Result.Content, 0, width, opts.ExpandedContent) return joinToolParts(header, body) } @@ -109,23 +108,22 @@ type WebFetchToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (w *WebFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Fetch", opts.Anim) } var params tools.WebFetchParams if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } toolParams := []string{params.URL} - header := toolHeader(sty, opts.Status, "Fetch", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "Fetch", width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -133,7 +131,7 @@ func (w *WebFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, op return header } - body := toolOutputMarkdownContent(sty, opts.Result.Content, cappedWidth, opts.ExpandedContent) + body := toolOutputMarkdownContent(sty, opts.Result.Content, width, opts.ExpandedContent) return joinToolParts(header, body) } @@ -163,23 +161,22 @@ type WebSearchToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (w *WebSearchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Search", opts.Anim) } var params tools.WebSearchParams if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } toolParams := []string{params.Query} - header := toolHeader(sty, opts.Status, "Search", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "Search", width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -187,6 +184,6 @@ func (w *WebSearchToolRenderContext) RenderTool(sty *styles.Styles, width int, o return header } - body := toolOutputMarkdownContent(sty, opts.Result.Content, cappedWidth, opts.ExpandedContent) + body := toolOutputMarkdownContent(sty, opts.Result.Content, width, opts.ExpandedContent) return joinToolParts(header, body) } diff --git a/internal/ui/chat/file.go b/internal/ui/chat/file.go index d558f79d597871bf6074d33c76b44549ee6725d5..13cb5104233af51756806cebb9b545b3bb5076f0 100644 --- a/internal/ui/chat/file.go +++ b/internal/ui/chat/file.go @@ -37,14 +37,13 @@ type ViewToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "View", opts.Anim) } var params tools.ViewParams if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } file := fsext.PrettyPath(params.FilePath) @@ -56,12 +55,12 @@ func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts * toolParams = append(toolParams, "offset", fmt.Sprintf("%d", params.Offset)) } - header := toolHeader(sty, opts.Status, "View", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "View", width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -87,7 +86,7 @@ func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts * } // Render code content with syntax highlighting. - body := toolOutputCodeContent(sty, params.FilePath, content, params.Offset, cappedWidth, opts.ExpandedContent) + body := toolOutputCodeContent(sty, params.FilePath, content, params.Offset, width, opts.ExpandedContent) return joinToolParts(header, body) } @@ -117,23 +116,22 @@ type WriteToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (w *WriteToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Write", opts.Anim) } var params tools.WriteParams if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } file := fsext.PrettyPath(params.FilePath) - header := toolHeader(sty, opts.Status, "Write", cappedWidth, opts.Compact, file) + header := toolHeader(sty, opts.Status, "Write", width, opts.Compact, file) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -142,7 +140,7 @@ func (w *WriteToolRenderContext) RenderTool(sty *styles.Styles, width int, opts } // Render code content with syntax highlighting. - body := toolOutputCodeContent(sty, params.FilePath, params.Content, 0, cappedWidth, opts.ExpandedContent) + body := toolOutputCodeContent(sty, params.FilePath, params.Content, 0, width, opts.ExpandedContent) return joinToolParts(header, body) } @@ -303,14 +301,13 @@ type DownloadToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (d *DownloadToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Download", opts.Anim) } var params tools.DownloadParams if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } toolParams := []string{params.URL} @@ -321,12 +318,12 @@ func (d *DownloadToolRenderContext) RenderTool(sty *styles.Styles, width int, op toolParams = append(toolParams, "timeout", formatTimeout(params.Timeout)) } - header := toolHeader(sty, opts.Status, "Download", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "Download", width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -334,7 +331,7 @@ func (d *DownloadToolRenderContext) RenderTool(sty *styles.Styles, width int, op return header } - bodyWidth := cappedWidth - toolBodyLeftPaddingTotal + bodyWidth := width - toolBodyLeftPaddingTotal body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent)) return joinToolParts(header, body) } diff --git a/internal/ui/chat/generic.go b/internal/ui/chat/generic.go index 6b0ac433028daf7a06c57f85c7799250e9652f6f..269bf651f7ec402d5e41aecabfb7aee0d9272cb5 100644 --- a/internal/ui/chat/generic.go +++ b/internal/ui/chat/generic.go @@ -31,7 +31,6 @@ type GenericToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) name := genericPrettyName(opts.ToolCall.Name) if opts.IsPending() { @@ -40,7 +39,7 @@ func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opt var params map[string]any if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } var toolParams []string @@ -49,12 +48,12 @@ func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opt toolParams = append(toolParams, string(parsed)) } - header := toolHeader(sty, opts.Status, name, cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, name, width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -62,7 +61,7 @@ func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opt return header } - bodyWidth := cappedWidth - toolBodyLeftPaddingTotal + bodyWidth := width - toolBodyLeftPaddingTotal // Handle image data. if opts.Result.Data != "" && strings.HasPrefix(opts.Result.MIMEType, "image/") { diff --git a/internal/ui/chat/lsp_restart.go b/internal/ui/chat/lsp_restart.go index 66c316fcaf7c949711babeb9ebe864e558ae5bc0..4ee188a42428167314cd34aa60828cb87d121b79 100644 --- a/internal/ui/chat/lsp_restart.go +++ b/internal/ui/chat/lsp_restart.go @@ -30,7 +30,6 @@ type LSPRestartToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (r *LSPRestartToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Restart LSP", opts.Anim) } @@ -43,12 +42,12 @@ func (r *LSPRestartToolRenderContext) RenderTool(sty *styles.Styles, width int, toolParams = append(toolParams, params.Name) } - header := toolHeader(sty, opts.Status, "Restart LSP", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "Restart LSP", width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -56,7 +55,7 @@ func (r *LSPRestartToolRenderContext) RenderTool(sty *styles.Styles, width int, return header } - bodyWidth := cappedWidth - toolBodyLeftPaddingTotal + bodyWidth := width - toolBodyLeftPaddingTotal body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent)) return joinToolParts(header, body) } diff --git a/internal/ui/chat/mcp.go b/internal/ui/chat/mcp.go index c4d124e7381a9ddaa39f56750367d3f2cf4d207f..5cf750bacf7227744f06cc2d5253d98ad1713cbd 100644 --- a/internal/ui/chat/mcp.go +++ b/internal/ui/chat/mcp.go @@ -32,10 +32,9 @@ type MCPToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) toolNameParts := strings.SplitN(opts.ToolCall.Name, "_", 3) if len(toolNameParts) != 3 { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid tool name"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid tool name"}, width) } mcpName := prettyName(toolNameParts[1]) toolName := prettyName(toolNameParts[2]) @@ -51,7 +50,7 @@ func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *T var params map[string]any if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } var toolParams []string @@ -60,12 +59,12 @@ func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *T toolParams = append(toolParams, string(parsed)) } - header := toolHeader(sty, opts.Status, name, cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, name, width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -73,7 +72,7 @@ func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *T return header } - bodyWidth := cappedWidth - toolBodyLeftPaddingTotal + bodyWidth := width - toolBodyLeftPaddingTotal // see if the result is json var result json.RawMessage var body string diff --git a/internal/ui/chat/messages.go b/internal/ui/chat/messages.go index 0c5668a20d52c5975dc63cb37da8090e9aa0ca7f..0c5a3ed6a8a9d65d26cf67adff5f308e3d82a929 100644 --- a/internal/ui/chat/messages.go +++ b/internal/ui/chat/messages.go @@ -245,11 +245,6 @@ func (a *AssistantInfoItem) renderContent(width int) string { return common.Section(a.sty, assistant, width) } -// cappedMessageWidth returns the maximum width for message content for readability. -func cappedMessageWidth(availableWidth int) int { - return min(availableWidth-MessageLeftPaddingTotal, maxTextWidth) -} - // ExtractMessageItems extracts [MessageItem]s from a [message.Message]. It // returns all parts of the message as [MessageItem]s. // diff --git a/internal/ui/chat/references.go b/internal/ui/chat/references.go index 2d7efe8df3ed38bf3768d7ae13c433fc05c17418..25fee7a15710c5ce1f470e470ff3b491da5000c3 100644 --- a/internal/ui/chat/references.go +++ b/internal/ui/chat/references.go @@ -31,7 +31,6 @@ type ReferencesToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (r *ReferencesToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Find References", opts.Anim) } @@ -44,12 +43,12 @@ func (r *ReferencesToolRenderContext) RenderTool(sty *styles.Styles, width int, toolParams = append(toolParams, "path", fsext.PrettyPath(params.Path)) } - header := toolHeader(sty, opts.Status, "Find References", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "Find References", width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -57,7 +56,7 @@ func (r *ReferencesToolRenderContext) RenderTool(sty *styles.Styles, width int, return header } - bodyWidth := cappedWidth - toolBodyLeftPaddingTotal + bodyWidth := width - toolBodyLeftPaddingTotal body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent)) return joinToolParts(header, body) } diff --git a/internal/ui/chat/search.go b/internal/ui/chat/search.go index 2342f671fdaed3bfdcf56619864bd3b60987d8a6..2a252936f63c41dd18afde4ef725ed43a3c23a95 100644 --- a/internal/ui/chat/search.go +++ b/internal/ui/chat/search.go @@ -35,14 +35,13 @@ type GlobToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (g *GlobToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Glob", opts.Anim) } var params tools.GlobParams if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } toolParams := []string{params.Pattern} @@ -50,12 +49,12 @@ func (g *GlobToolRenderContext) RenderTool(sty *styles.Styles, width int, opts * toolParams = append(toolParams, "path", params.Path) } - header := toolHeader(sty, opts.Status, "Glob", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "Glob", width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -63,7 +62,7 @@ func (g *GlobToolRenderContext) RenderTool(sty *styles.Styles, width int, opts * return header } - bodyWidth := cappedWidth - toolBodyLeftPaddingTotal + bodyWidth := width - toolBodyLeftPaddingTotal body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent)) return joinToolParts(header, body) } @@ -94,14 +93,13 @@ type GrepToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (g *GrepToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Grep", opts.Anim) } var params tools.GrepParams if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } toolParams := []string{params.Pattern} @@ -115,12 +113,12 @@ func (g *GrepToolRenderContext) RenderTool(sty *styles.Styles, width int, opts * toolParams = append(toolParams, "literal", "true") } - header := toolHeader(sty, opts.Status, "Grep", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "Grep", width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -128,7 +126,7 @@ func (g *GrepToolRenderContext) RenderTool(sty *styles.Styles, width int, opts * return header } - bodyWidth := cappedWidth - toolBodyLeftPaddingTotal + bodyWidth := width - toolBodyLeftPaddingTotal body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent)) return joinToolParts(header, body) } @@ -159,14 +157,13 @@ type LSToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (l *LSToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "List", opts.Anim) } var params tools.LSParams if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } path := params.Path @@ -175,12 +172,12 @@ func (l *LSToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *To } path = fsext.PrettyPath(path) - header := toolHeader(sty, opts.Status, "List", cappedWidth, opts.Compact, path) + header := toolHeader(sty, opts.Status, "List", width, opts.Compact, path) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -188,7 +185,7 @@ func (l *LSToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *To return header } - bodyWidth := cappedWidth - toolBodyLeftPaddingTotal + bodyWidth := width - toolBodyLeftPaddingTotal body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent)) return joinToolParts(header, body) } @@ -219,14 +216,13 @@ type SourcegraphToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (s *SourcegraphToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "Sourcegraph", opts.Anim) } var params tools.SourcegraphParams if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) + return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width) } toolParams := []string{params.Query} @@ -237,12 +233,12 @@ func (s *SourcegraphToolRenderContext) RenderTool(sty *styles.Styles, width int, toolParams = append(toolParams, "context", formatNonZero(params.ContextWindow)) } - header := toolHeader(sty, opts.Status, "Sourcegraph", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "Sourcegraph", width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } @@ -250,7 +246,7 @@ func (s *SourcegraphToolRenderContext) RenderTool(sty *styles.Styles, width int, return header } - bodyWidth := cappedWidth - toolBodyLeftPaddingTotal + bodyWidth := width - toolBodyLeftPaddingTotal body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent)) return joinToolParts(header, body) } diff --git a/internal/ui/chat/todos.go b/internal/ui/chat/todos.go index 5678d0e47f4c3a808c13c1dc6209f9194e9f9482..42e9762b8bf1685495b65626bc36b1b3f45031a8 100644 --- a/internal/ui/chat/todos.go +++ b/internal/ui/chat/todos.go @@ -39,7 +39,6 @@ type TodosToolRenderContext struct{} // RenderTool implements the [ToolRenderer] interface. func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) if opts.IsPending() { return pendingTool(sty, "To-Do", opts.Anim) } @@ -82,7 +81,7 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts } else { headerText = fmt.Sprintf("created %d todos", meta.Total) } - body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, cappedWidth) + body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, width) } else { // Build header based on what changed. hasCompleted := len(meta.JustCompleted) > 0 @@ -108,7 +107,7 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts // Build body with details. if allCompleted { // Show all todos when all are completed, like when created. - body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, cappedWidth) + body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, width) } else if meta.JustStarted != "" { body = sty.Tool.TodoInProgressIcon.Render(styles.ArrowRightIcon+" ") + sty.Base.Render(meta.JustStarted) @@ -119,12 +118,12 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts } toolParams := []string{headerText} - header := toolHeader(sty, opts.Status, "To-Do", cappedWidth, opts.Compact, toolParams...) + header := toolHeader(sty, opts.Status, "To-Do", width, opts.Compact, toolParams...) if opts.Compact { return header } - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { + if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok { return joinToolParts(header, earlyState) } diff --git a/internal/ui/chat/tools.go b/internal/ui/chat/tools.go index f7702cc1fe516bb3dee7d57ce15fed050299019f..07bf40f96b08c24907f0bd65d80cebfb74eae58b 100644 --- a/internal/ui/chat/tools.go +++ b/internal/ui/chat/tools.go @@ -295,9 +295,6 @@ func (t *baseToolMessageItem) Animate(msg anim.StepMsg) tea.Cmd { // RawRender implements [MessageItem]. func (t *baseToolMessageItem) RawRender(width int) string { toolItemWidth := width - MessageLeftPaddingTotal - if t.hasCappedWidth { - toolItemWidth = cappedMessageWidth(width) - } content, height, ok := t.getCachedRender(toolItemWidth) // if we are spinning or there is no cache rerender @@ -773,11 +770,6 @@ func roundedEnumerator(lPadding, width int) tree.Enumerator { func toolOutputMarkdownContent(sty *styles.Styles, content string, width int, expanded bool) string { content = stringext.NormalizeSpace(content) - // Cap width for readability. - if width > maxTextWidth { - width = maxTextWidth - } - renderer := common.PlainMarkdownRenderer(sty, width) rendered, err := renderer.Render(content) if err != nil { diff --git a/internal/ui/chat/user.go b/internal/ui/chat/user.go index 91211590ce66dd0dd7edbde03becdf469e26b521..814a0270aad00bbf85c78629ffdfaf01a17c2e7f 100644 --- a/internal/ui/chat/user.go +++ b/internal/ui/chat/user.go @@ -36,15 +36,13 @@ func NewUserMessageItem(sty *styles.Styles, message *message.Message, attachment // RawRender implements [MessageItem]. func (m *UserMessageItem) RawRender(width int) string { - cappedWidth := cappedMessageWidth(width) - - content, height, ok := m.getCachedRender(cappedWidth) + content, height, ok := m.getCachedRender(width) // cache hit if ok { - return m.renderHighlighted(content, cappedWidth, height) + return m.renderHighlighted(content, width, height) } - renderer := common.MarkdownRenderer(m.sty, cappedWidth) + renderer := common.MarkdownRenderer(m.sty, width) msgContent := strings.TrimSpace(m.message.Content().Text) result, err := renderer.Render(msgContent) @@ -55,7 +53,7 @@ func (m *UserMessageItem) RawRender(width int) string { } if len(m.message.BinaryContent()) > 0 { - attachmentsStr := m.renderAttachments(cappedWidth) + attachmentsStr := m.renderAttachments(width) if content == "" { content = attachmentsStr } else { @@ -64,8 +62,8 @@ func (m *UserMessageItem) RawRender(width int) string { } height = lipgloss.Height(content) - m.setCachedRender(content, cappedWidth, height) - return m.renderHighlighted(content, cappedWidth, height) + m.setCachedRender(content, width, height) + return m.renderHighlighted(content, width, height) } // Render implements MessageItem.