on main help sign in
fix(ui): consistent box sizing (#2127)
fd43746Carlos Alexandro Becker
internal/ui/chat/agent.go+6−8
@@ -99,7 +99,6 @@ type AgentToolRenderContext struct {
99
99
100// RenderTool implements the [ToolRenderer] interface.
100// RenderTool implements the [ToolRenderer] interface.
101func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
101func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
102− cappedWidth := cappedMessageWidth(width)
103 if !opts.ToolCall.Finished && !opts.IsCanceled() && len(r.agent.nestedTools) == 0 {
102 if !opts.ToolCall.Finished && !opts.IsCanceled() && len(r.agent.nestedTools) == 0 {
104 return pendingTool(sty, "Agent", opts.Anim)
103 return pendingTool(sty, "Agent", opts.Anim)
105 }
104 }
@@ -110,7 +109,7 @@ func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
110 prompt := params.Prompt
109 prompt := params.Prompt
111 prompt = strings.ReplaceAll(prompt, "\n", " ")
110 prompt = strings.ReplaceAll(prompt, "\n", " ")
112
111
113− header := toolHeader(sty, opts.Status, "Agent", cappedWidth, opts.Compact)
112+ header := toolHeader(sty, opts.Status, "Agent", width, opts.Compact)
114 if opts.Compact {
113 if opts.Compact {
115 return header
114 return header
116 }
115 }
@@ -120,7 +119,7 @@ func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
120 taskTagWidth := lipgloss.Width(taskTag)
119 taskTagWidth := lipgloss.Width(taskTag)
121
120
122 // Calculate remaining width for prompt.
121 // Calculate remaining width for prompt.
123− remainingWidth := min(cappedWidth-taskTagWidth-3, maxTextWidth-taskTagWidth-3) // -3 for spacing
122+ remainingWidth := width - taskTagWidth - 3 // -3 for spacing
124
123
125 promptText := sty.Tool.AgentPrompt.Width(remainingWidth).Render(prompt)
124 promptText := sty.Tool.AgentPrompt.Width(remainingWidth).Render(prompt)
126
125
@@ -157,7 +156,7 @@ func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
157
156
158 // Add body content when completed.
157 // Add body content when completed.
159 if opts.HasResult() && opts.Result.Content != "" {
158 if opts.HasResult() && opts.Result.Content != "" {
160− body := toolOutputMarkdownContent(sty, opts.Result.Content, cappedWidth-toolBodyLeftPaddingTotal, opts.ExpandedContent)
159+ body := toolOutputMarkdownContent(sty, opts.Result.Content, width-toolBodyLeftPaddingTotal, opts.ExpandedContent)
161 return joinToolParts(result, body)
160 return joinToolParts(result, body)
162 }
161 }
163
162
@@ -230,7 +229,6 @@ type agenticFetchParams struct {
230
229
231// RenderTool implements the [ToolRenderer] interface.
230// RenderTool implements the [ToolRenderer] interface.
232func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
231func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
233− cappedWidth := cappedMessageWidth(width)
234 if !opts.ToolCall.Finished && !opts.IsCanceled() && len(r.fetch.nestedTools) == 0 {
232 if !opts.ToolCall.Finished && !opts.IsCanceled() && len(r.fetch.nestedTools) == 0 {
235 return pendingTool(sty, "Agentic Fetch", opts.Anim)
233 return pendingTool(sty, "Agentic Fetch", opts.Anim)
236 }
234 }
@@ -247,7 +245,7 @@ func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int
247 toolParams = append(toolParams, params.URL)
245 toolParams = append(toolParams, params.URL)
248 }
246 }
249
247
250− header := toolHeader(sty, opts.Status, "Agentic Fetch", cappedWidth, opts.Compact, toolParams...)
248+ header := toolHeader(sty, opts.Status, "Agentic Fetch", width, opts.Compact, toolParams...)
251 if opts.Compact {
249 if opts.Compact {
252 return header
250 return header
253 }
251 }
@@ -257,7 +255,7 @@ func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int
257 promptTagWidth := lipgloss.Width(promptTag)
255 promptTagWidth := lipgloss.Width(promptTag)
258
256
259 // Calculate remaining width for prompt text.
257 // Calculate remaining width for prompt text.
260− remainingWidth := min(cappedWidth-promptTagWidth-3, maxTextWidth-promptTagWidth-3) // -3 for spacing
258+ remainingWidth := width - promptTagWidth - 3 // -3 for spacing
261
259
262 promptText := sty.Tool.AgentPrompt.Width(remainingWidth).Render(prompt)
260 promptText := sty.Tool.AgentPrompt.Width(remainingWidth).Render(prompt)
263
261
@@ -294,7 +292,7 @@ func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int
294
292
295 // Add body content when completed.
293 // Add body content when completed.
296 if opts.HasResult() && opts.Result.Content != "" {
294 if opts.HasResult() && opts.Result.Content != "" {
297− body := toolOutputMarkdownContent(sty, opts.Result.Content, cappedWidth-toolBodyLeftPaddingTotal, opts.ExpandedContent)
295+ body := toolOutputMarkdownContent(sty, opts.Result.Content, width-toolBodyLeftPaddingTotal, opts.ExpandedContent)
298 return joinToolParts(result, body)
296 return joinToolParts(result, body)
299 }
297 }
300
298
internal/ui/chat/assistant.go+4−6
@@ -79,22 +79,20 @@ func (a *AssistantMessageItem) ID() string {
79
79
80// RawRender implements [MessageItem].
80// RawRender implements [MessageItem].
81func (a *AssistantMessageItem) RawRender(width int) string {
81func (a *AssistantMessageItem) RawRender(width int) string {
82− cappedWidth := cappedMessageWidth(width)
83−
84 var spinner string
82 var spinner string
85 if a.isSpinning() {
83 if a.isSpinning() {
86 spinner = a.renderSpinning()
84 spinner = a.renderSpinning()
87 }
85 }
88
86
89− content, height, ok := a.getCachedRender(cappedWidth)
87+ content, height, ok := a.getCachedRender(width)
90 if !ok {
88 if !ok {
91− content = a.renderMessageContent(cappedWidth)
89+ content = a.renderMessageContent(width)
92 height = lipgloss.Height(content)
90 height = lipgloss.Height(content)
93 // cache the rendered content
91 // cache the rendered content
94− a.setCachedRender(content, cappedWidth, height)
92+ a.setCachedRender(content, width, height)
95 }
93 }
96
94
97− highlightedContent := a.renderHighlighted(content, cappedWidth, height)
95+ highlightedContent := a.renderHighlighted(content, width, height)
98 if spinner != "" {
96 if spinner != "" {
99 if highlightedContent != "" {
97 if highlightedContent != "" {
100 highlightedContent += "\n\n"
98 highlightedContent += "\n\n"
internal/ui/chat/bash.go+8−11
@@ -39,7 +39,6 @@ type BashToolRenderContext struct{}
39
39
40// RenderTool implements the [ToolRenderer] interface.
40// RenderTool implements the [ToolRenderer] interface.
41func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
41func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
42− cappedWidth := cappedMessageWidth(width)
43 if opts.IsPending() {
42 if opts.IsPending() {
44 return pendingTool(sty, "Bash", opts.Anim)
43 return pendingTool(sty, "Bash", opts.Anim)
45 }
44 }
@@ -58,7 +57,7 @@ func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
58 if meta.Background {
57 if meta.Background {
59 description := cmp.Or(meta.Description, params.Command)
58 description := cmp.Or(meta.Description, params.Command)
60 content := "Command: " + params.Command + "\n" + opts.Result.Content
59 content := "Command: " + params.Command + "\n" + opts.Result.Content
61− return renderJobTool(sty, opts, cappedWidth, "Start", meta.ShellID, description, content)
60+ return renderJobTool(sty, opts, width, "Start", meta.ShellID, description, content)
62 }
61 }
63
62
64 // Regular bash command.
63 // Regular bash command.
@@ -69,12 +68,12 @@ func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
69 toolParams = append(toolParams, "background", "true")
68 toolParams = append(toolParams, "background", "true")
70 }
69 }
71
70
72− header := toolHeader(sty, opts.Status, "Bash", cappedWidth, opts.Compact, toolParams...)
71+ header := toolHeader(sty, opts.Status, "Bash", width, opts.Compact, toolParams...)
73 if opts.Compact {
72 if opts.Compact {
74 return header
73 return header
75 }
74 }
76
75
77− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
76+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
78 return joinToolParts(header, earlyState)
77 return joinToolParts(header, earlyState)
79 }
78 }
80
79
@@ -90,7 +89,7 @@ func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
90 return header
89 return header
91 }
90 }
92
91
93− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
92+ bodyWidth := width - toolBodyLeftPaddingTotal
94 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, output, bodyWidth, opts.ExpandedContent))
93 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, output, bodyWidth, opts.ExpandedContent))
95 return joinToolParts(header, body)
94 return joinToolParts(header, body)
96}
95}
@@ -121,14 +120,13 @@ type JobOutputToolRenderContext struct{}
121
120
122// RenderTool implements the [ToolRenderer] interface.
121// RenderTool implements the [ToolRenderer] interface.
123func (j *JobOutputToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
122func (j *JobOutputToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
124− cappedWidth := cappedMessageWidth(width)
125 if opts.IsPending() {
123 if opts.IsPending() {
126 return pendingTool(sty, "Job", opts.Anim)
124 return pendingTool(sty, "Job", opts.Anim)
127 }
125 }
128
126
129 var params tools.JobOutputParams
127 var params tools.JobOutputParams
130 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
128 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
131− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
129+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
132 }
130 }
133
131
134 var description string
132 var description string
@@ -143,7 +141,7 @@ func (j *JobOutputToolRenderContext) RenderTool(sty *styles.Styles, width int, o
143 if opts.HasResult() {
141 if opts.HasResult() {
144 content = opts.Result.Content
142 content = opts.Result.Content
145 }
143 }
146− return renderJobTool(sty, opts, cappedWidth, "Output", params.ShellID, description, content)
144+ return renderJobTool(sty, opts, width, "Output", params.ShellID, description, content)
147}
145}
148
146
149// -----------------------------------------------------------------------------
147// -----------------------------------------------------------------------------
@@ -172,14 +170,13 @@ type JobKillToolRenderContext struct{}
172
170
173// RenderTool implements the [ToolRenderer] interface.
171// RenderTool implements the [ToolRenderer] interface.
174func (j *JobKillToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
172func (j *JobKillToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
175− cappedWidth := cappedMessageWidth(width)
176 if opts.IsPending() {
173 if opts.IsPending() {
177 return pendingTool(sty, "Job", opts.Anim)
174 return pendingTool(sty, "Job", opts.Anim)
178 }
175 }
179
176
180 var params tools.JobKillParams
177 var params tools.JobKillParams
181 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
178 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
182− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
179+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
183 }
180 }
184
181
185 var description string
182 var description string
@@ -194,7 +191,7 @@ func (j *JobKillToolRenderContext) RenderTool(sty *styles.Styles, width int, opt
194 if opts.HasResult() {
191 if opts.HasResult() {
195 content = opts.Result.Content
192 content = opts.Result.Content
196 }
193 }
197− return renderJobTool(sty, opts, cappedWidth, "Kill", params.ShellID, description, content)
194+ return renderJobTool(sty, opts, width, "Kill", params.ShellID, description, content)
198}
195}
199
196
200// renderJobTool renders a job-related tool with the common pattern:
197// renderJobTool renders a job-related tool with the common pattern:
internal/ui/chat/diagnostics.go+3−4
@@ -35,7 +35,6 @@ type DiagnosticsToolRenderContext struct{}
35
35
36// RenderTool implements the [ToolRenderer] interface.
36// RenderTool implements the [ToolRenderer] interface.
37func (d *DiagnosticsToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
37func (d *DiagnosticsToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
38− cappedWidth := cappedMessageWidth(width)
39 if opts.IsPending() {
38 if opts.IsPending() {
40 return pendingTool(sty, "Diagnostics", opts.Anim)
39 return pendingTool(sty, "Diagnostics", opts.Anim)
41 }
40 }
@@ -49,12 +48,12 @@ func (d *DiagnosticsToolRenderContext) RenderTool(sty *styles.Styles, width int,
49 mainParam = fsext.PrettyPath(params.FilePath)
48 mainParam = fsext.PrettyPath(params.FilePath)
50 }
49 }
51
50
52− header := toolHeader(sty, opts.Status, "Diagnostics", cappedWidth, opts.Compact, mainParam)
51+ header := toolHeader(sty, opts.Status, "Diagnostics", width, opts.Compact, mainParam)
53 if opts.Compact {
52 if opts.Compact {
54 return header
53 return header
55 }
54 }
56
55
57− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
56+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
58 return joinToolParts(header, earlyState)
57 return joinToolParts(header, earlyState)
59 }
58 }
60
59
@@ -62,7 +61,7 @@ func (d *DiagnosticsToolRenderContext) RenderTool(sty *styles.Styles, width int,
62 return header
61 return header
63 }
62 }
64
63
65− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
64+ bodyWidth := width - toolBodyLeftPaddingTotal
66 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
65 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
67 return joinToolParts(header, body)
66 return joinToolParts(header, body)
68}
67}
internal/ui/chat/fetch.go+12−15
@@ -34,14 +34,13 @@ type FetchToolRenderContext struct{}
34
34
35// RenderTool implements the [ToolRenderer] interface.
35// RenderTool implements the [ToolRenderer] interface.
36func (f *FetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
36func (f *FetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
37− cappedWidth := cappedMessageWidth(width)
38 if opts.IsPending() {
37 if opts.IsPending() {
39 return pendingTool(sty, "Fetch", opts.Anim)
38 return pendingTool(sty, "Fetch", opts.Anim)
40 }
39 }
41
40
42 var params tools.FetchParams
41 var params tools.FetchParams
43 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
42 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
44− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
43+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
45 }
44 }
46
45
47 toolParams := []string{params.URL}
46 toolParams := []string{params.URL}
@@ -52,12 +51,12 @@ func (f *FetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
52 toolParams = append(toolParams, "timeout", formatTimeout(params.Timeout))
51 toolParams = append(toolParams, "timeout", formatTimeout(params.Timeout))
53 }
52 }
54
53
55− header := toolHeader(sty, opts.Status, "Fetch", cappedWidth, opts.Compact, toolParams...)
54+ header := toolHeader(sty, opts.Status, "Fetch", width, opts.Compact, toolParams...)
56 if opts.Compact {
55 if opts.Compact {
57 return header
56 return header
58 }
57 }
59
58
60− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
59+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
61 return joinToolParts(header, earlyState)
60 return joinToolParts(header, earlyState)
62 }
61 }
63
62
@@ -67,7 +66,7 @@ func (f *FetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
67
66
68 // Determine file extension for syntax highlighting based on format.
67 // Determine file extension for syntax highlighting based on format.
69 file := getFileExtensionForFormat(params.Format)
68 file := getFileExtensionForFormat(params.Format)
70− body := toolOutputCodeContent(sty, file, opts.Result.Content, 0, cappedWidth, opts.ExpandedContent)
69+ body := toolOutputCodeContent(sty, file, opts.Result.Content, 0, width, opts.ExpandedContent)
71 return joinToolParts(header, body)
70 return joinToolParts(header, body)
72}
71}
73
72
@@ -109,23 +108,22 @@ type WebFetchToolRenderContext struct{}
109
108
110// RenderTool implements the [ToolRenderer] interface.
109// RenderTool implements the [ToolRenderer] interface.
111func (w *WebFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
110func (w *WebFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
112− cappedWidth := cappedMessageWidth(width)
113 if opts.IsPending() {
111 if opts.IsPending() {
114 return pendingTool(sty, "Fetch", opts.Anim)
112 return pendingTool(sty, "Fetch", opts.Anim)
115 }
113 }
116
114
117 var params tools.WebFetchParams
115 var params tools.WebFetchParams
118 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
116 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
119− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
117+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
120 }
118 }
121
119
122 toolParams := []string{params.URL}
120 toolParams := []string{params.URL}
123− header := toolHeader(sty, opts.Status, "Fetch", cappedWidth, opts.Compact, toolParams...)
121+ header := toolHeader(sty, opts.Status, "Fetch", width, opts.Compact, toolParams...)
124 if opts.Compact {
122 if opts.Compact {
125 return header
123 return header
126 }
124 }
127
125
128− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
126+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
129 return joinToolParts(header, earlyState)
127 return joinToolParts(header, earlyState)
130 }
128 }
131
129
@@ -133,7 +131,7 @@ func (w *WebFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, op
133 return header
131 return header
134 }
132 }
135
133
136− body := toolOutputMarkdownContent(sty, opts.Result.Content, cappedWidth, opts.ExpandedContent)
134+ body := toolOutputMarkdownContent(sty, opts.Result.Content, width, opts.ExpandedContent)
137 return joinToolParts(header, body)
135 return joinToolParts(header, body)
138}
136}
139
137
@@ -163,23 +161,22 @@ type WebSearchToolRenderContext struct{}
163
161
164// RenderTool implements the [ToolRenderer] interface.
162// RenderTool implements the [ToolRenderer] interface.
165func (w *WebSearchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
163func (w *WebSearchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
166− cappedWidth := cappedMessageWidth(width)
167 if opts.IsPending() {
164 if opts.IsPending() {
168 return pendingTool(sty, "Search", opts.Anim)
165 return pendingTool(sty, "Search", opts.Anim)
169 }
166 }
170
167
171 var params tools.WebSearchParams
168 var params tools.WebSearchParams
172 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
169 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
173− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
170+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
174 }
171 }
175
172
176 toolParams := []string{params.Query}
173 toolParams := []string{params.Query}
177− header := toolHeader(sty, opts.Status, "Search", cappedWidth, opts.Compact, toolParams...)
174+ header := toolHeader(sty, opts.Status, "Search", width, opts.Compact, toolParams...)
178 if opts.Compact {
175 if opts.Compact {
179 return header
176 return header
180 }
177 }
181
178
182− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
179+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
183 return joinToolParts(header, earlyState)
180 return joinToolParts(header, earlyState)
184 }
181 }
185
182
@@ -187,6 +184,6 @@ func (w *WebSearchToolRenderContext) RenderTool(sty *styles.Styles, width int, o
187 return header
184 return header
188 }
185 }
189
186
190− body := toolOutputMarkdownContent(sty, opts.Result.Content, cappedWidth, opts.ExpandedContent)
187+ body := toolOutputMarkdownContent(sty, opts.Result.Content, width, opts.ExpandedContent)
191 return joinToolParts(header, body)
188 return joinToolParts(header, body)
192}
189}
internal/ui/chat/file.go+12−15
@@ -37,14 +37,13 @@ type ViewToolRenderContext struct{}
37
37
38// RenderTool implements the [ToolRenderer] interface.
38// RenderTool implements the [ToolRenderer] interface.
39func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
39func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
40− cappedWidth := cappedMessageWidth(width)
41 if opts.IsPending() {
40 if opts.IsPending() {
42 return pendingTool(sty, "View", opts.Anim)
41 return pendingTool(sty, "View", opts.Anim)
43 }
42 }
44
43
45 var params tools.ViewParams
44 var params tools.ViewParams
46 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
45 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
47− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
46+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
48 }
47 }
49
48
50 file := fsext.PrettyPath(params.FilePath)
49 file := fsext.PrettyPath(params.FilePath)
@@ -56,12 +55,12 @@ func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
56 toolParams = append(toolParams, "offset", fmt.Sprintf("%d", params.Offset))
55 toolParams = append(toolParams, "offset", fmt.Sprintf("%d", params.Offset))
57 }
56 }
58
57
59− header := toolHeader(sty, opts.Status, "View", cappedWidth, opts.Compact, toolParams...)
58+ header := toolHeader(sty, opts.Status, "View", width, opts.Compact, toolParams...)
60 if opts.Compact {
59 if opts.Compact {
61 return header
60 return header
62 }
61 }
63
62
64− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
63+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
65 return joinToolParts(header, earlyState)
64 return joinToolParts(header, earlyState)
66 }
65 }
67
66
@@ -87,7 +86,7 @@ func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
87 }
86 }
88
87
89 // Render code content with syntax highlighting.
88 // Render code content with syntax highlighting.
90− body := toolOutputCodeContent(sty, params.FilePath, content, params.Offset, cappedWidth, opts.ExpandedContent)
89+ body := toolOutputCodeContent(sty, params.FilePath, content, params.Offset, width, opts.ExpandedContent)
91 return joinToolParts(header, body)
90 return joinToolParts(header, body)
92}
91}
93
92
@@ -117,23 +116,22 @@ type WriteToolRenderContext struct{}
117
116
118// RenderTool implements the [ToolRenderer] interface.
117// RenderTool implements the [ToolRenderer] interface.
119func (w *WriteToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
118func (w *WriteToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
120− cappedWidth := cappedMessageWidth(width)
121 if opts.IsPending() {
119 if opts.IsPending() {
122 return pendingTool(sty, "Write", opts.Anim)
120 return pendingTool(sty, "Write", opts.Anim)
123 }
121 }
124
122
125 var params tools.WriteParams
123 var params tools.WriteParams
126 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
124 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
127− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
125+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
128 }
126 }
129
127
130 file := fsext.PrettyPath(params.FilePath)
128 file := fsext.PrettyPath(params.FilePath)
131− header := toolHeader(sty, opts.Status, "Write", cappedWidth, opts.Compact, file)
129+ header := toolHeader(sty, opts.Status, "Write", width, opts.Compact, file)
132 if opts.Compact {
130 if opts.Compact {
133 return header
131 return header
134 }
132 }
135
133
136− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
134+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
137 return joinToolParts(header, earlyState)
135 return joinToolParts(header, earlyState)
138 }
136 }
139
137
@@ -142,7 +140,7 @@ func (w *WriteToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
142 }
140 }
143
141
144 // Render code content with syntax highlighting.
142 // Render code content with syntax highlighting.
145− body := toolOutputCodeContent(sty, params.FilePath, params.Content, 0, cappedWidth, opts.ExpandedContent)
143+ body := toolOutputCodeContent(sty, params.FilePath, params.Content, 0, width, opts.ExpandedContent)
146 return joinToolParts(header, body)
144 return joinToolParts(header, body)
147}
145}
148
146
@@ -303,14 +301,13 @@ type DownloadToolRenderContext struct{}
303
301
304// RenderTool implements the [ToolRenderer] interface.
302// RenderTool implements the [ToolRenderer] interface.
305func (d *DownloadToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
303func (d *DownloadToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
306− cappedWidth := cappedMessageWidth(width)
307 if opts.IsPending() {
304 if opts.IsPending() {
308 return pendingTool(sty, "Download", opts.Anim)
305 return pendingTool(sty, "Download", opts.Anim)
309 }
306 }
310
307
311 var params tools.DownloadParams
308 var params tools.DownloadParams
312 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
309 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
313− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
310+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
314 }
311 }
315
312
316 toolParams := []string{params.URL}
313 toolParams := []string{params.URL}
@@ -321,12 +318,12 @@ func (d *DownloadToolRenderContext) RenderTool(sty *styles.Styles, width int, op
321 toolParams = append(toolParams, "timeout", formatTimeout(params.Timeout))
318 toolParams = append(toolParams, "timeout", formatTimeout(params.Timeout))
322 }
319 }
323
320
324− header := toolHeader(sty, opts.Status, "Download", cappedWidth, opts.Compact, toolParams...)
321+ header := toolHeader(sty, opts.Status, "Download", width, opts.Compact, toolParams...)
325 if opts.Compact {
322 if opts.Compact {
326 return header
323 return header
327 }
324 }
328
325
329− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
326+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
330 return joinToolParts(header, earlyState)
327 return joinToolParts(header, earlyState)
331 }
328 }
332
329
@@ -334,7 +331,7 @@ func (d *DownloadToolRenderContext) RenderTool(sty *styles.Styles, width int, op
334 return header
331 return header
335 }
332 }
336
333
337− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
334+ bodyWidth := width - toolBodyLeftPaddingTotal
338 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
335 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
339 return joinToolParts(header, body)
336 return joinToolParts(header, body)
340}
337}
internal/ui/chat/generic.go+4−5
@@ -31,7 +31,6 @@ type GenericToolRenderContext struct{}
31
31
32// RenderTool implements the [ToolRenderer] interface.
32// RenderTool implements the [ToolRenderer] interface.
33func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
33func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
34− cappedWidth := cappedMessageWidth(width)
35 name := genericPrettyName(opts.ToolCall.Name)
34 name := genericPrettyName(opts.ToolCall.Name)
36
35
37 if opts.IsPending() {
36 if opts.IsPending() {
@@ -40,7 +39,7 @@ func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opt
40
39
41 var params map[string]any
40 var params map[string]any
42 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
41 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
43− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
42+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
44 }
43 }
45
44
46 var toolParams []string
45 var toolParams []string
@@ -49,12 +48,12 @@ func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opt
49 toolParams = append(toolParams, string(parsed))
48 toolParams = append(toolParams, string(parsed))
50 }
49 }
51
50
52− header := toolHeader(sty, opts.Status, name, cappedWidth, opts.Compact, toolParams...)
51+ header := toolHeader(sty, opts.Status, name, width, opts.Compact, toolParams...)
53 if opts.Compact {
52 if opts.Compact {
54 return header
53 return header
55 }
54 }
56
55
57− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
56+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
58 return joinToolParts(header, earlyState)
57 return joinToolParts(header, earlyState)
59 }
58 }
60
59
@@ -62,7 +61,7 @@ func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opt
62 return header
61 return header
63 }
62 }
64
63
65− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
64+ bodyWidth := width - toolBodyLeftPaddingTotal
66
65
67 // Handle image data.
66 // Handle image data.
68 if opts.Result.Data != "" && strings.HasPrefix(opts.Result.MIMEType, "image/") {
67 if opts.Result.Data != "" && strings.HasPrefix(opts.Result.MIMEType, "image/") {
internal/ui/chat/lsp_restart.go+3−4
@@ -30,7 +30,6 @@ type LSPRestartToolRenderContext struct{}
30
30
31// RenderTool implements the [ToolRenderer] interface.
31// RenderTool implements the [ToolRenderer] interface.
32func (r *LSPRestartToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
32func (r *LSPRestartToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
33− cappedWidth := cappedMessageWidth(width)
34 if opts.IsPending() {
33 if opts.IsPending() {
35 return pendingTool(sty, "Restart LSP", opts.Anim)
34 return pendingTool(sty, "Restart LSP", opts.Anim)
36 }
35 }
@@ -43,12 +42,12 @@ func (r *LSPRestartToolRenderContext) RenderTool(sty *styles.Styles, width int,
43 toolParams = append(toolParams, params.Name)
42 toolParams = append(toolParams, params.Name)
44 }
43 }
45
44
46− header := toolHeader(sty, opts.Status, "Restart LSP", cappedWidth, opts.Compact, toolParams...)
45+ header := toolHeader(sty, opts.Status, "Restart LSP", width, opts.Compact, toolParams...)
47 if opts.Compact {
46 if opts.Compact {
48 return header
47 return header
49 }
48 }
50
49
51− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
50+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
52 return joinToolParts(header, earlyState)
51 return joinToolParts(header, earlyState)
53 }
52 }
54
53
@@ -56,7 +55,7 @@ func (r *LSPRestartToolRenderContext) RenderTool(sty *styles.Styles, width int,
56 return header
55 return header
57 }
56 }
58
57
59− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
58+ bodyWidth := width - toolBodyLeftPaddingTotal
60 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
59 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
61 return joinToolParts(header, body)
60 return joinToolParts(header, body)
62}
61}
internal/ui/chat/mcp.go+5−6
@@ -32,10 +32,9 @@ type MCPToolRenderContext struct{}
32
32
33// RenderTool implements the [ToolRenderer] interface.
33// RenderTool implements the [ToolRenderer] interface.
34func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
34func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
35− cappedWidth := cappedMessageWidth(width)
36 toolNameParts := strings.SplitN(opts.ToolCall.Name, "_", 3)
35 toolNameParts := strings.SplitN(opts.ToolCall.Name, "_", 3)
37 if len(toolNameParts) != 3 {
36 if len(toolNameParts) != 3 {
38− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid tool name"}, cappedWidth)
37+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid tool name"}, width)
39 }
38 }
40 mcpName := prettyName(toolNameParts[1])
39 mcpName := prettyName(toolNameParts[1])
41 toolName := prettyName(toolNameParts[2])
40 toolName := prettyName(toolNameParts[2])
@@ -51,7 +50,7 @@ func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *T
51
50
52 var params map[string]any
51 var params map[string]any
53 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
52 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
54− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
53+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
55 }
54 }
56
55
57 var toolParams []string
56 var toolParams []string
@@ -60,12 +59,12 @@ func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *T
60 toolParams = append(toolParams, string(parsed))
59 toolParams = append(toolParams, string(parsed))
61 }
60 }
62
61
63− header := toolHeader(sty, opts.Status, name, cappedWidth, opts.Compact, toolParams...)
62+ header := toolHeader(sty, opts.Status, name, width, opts.Compact, toolParams...)
64 if opts.Compact {
63 if opts.Compact {
65 return header
64 return header
66 }
65 }
67
66
68− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
67+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
69 return joinToolParts(header, earlyState)
68 return joinToolParts(header, earlyState)
70 }
69 }
71
70
@@ -73,7 +72,7 @@ func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *T
73 return header
72 return header
74 }
73 }
75
74
76− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
75+ bodyWidth := width - toolBodyLeftPaddingTotal
77 // see if the result is json
76 // see if the result is json
78 var result json.RawMessage
77 var result json.RawMessage
79 var body string
78 var body string
internal/ui/chat/messages.go−5
@@ -245,11 +245,6 @@ func (a *AssistantInfoItem) renderContent(width int) string {
245 return common.Section(a.sty, assistant, width)
245 return common.Section(a.sty, assistant, width)
246}
246}
247
247
248−// cappedMessageWidth returns the maximum width for message content for readability.
249−func cappedMessageWidth(availableWidth int) int {
250− return min(availableWidth-MessageLeftPaddingTotal, maxTextWidth)
251−}
252−
253// ExtractMessageItems extracts [MessageItem]s from a [message.Message]. It
248// ExtractMessageItems extracts [MessageItem]s from a [message.Message]. It
254// returns all parts of the message as [MessageItem]s.
249// returns all parts of the message as [MessageItem]s.
255//
250//
internal/ui/chat/references.go+3−4
@@ -31,7 +31,6 @@ type ReferencesToolRenderContext struct{}
31
31
32// RenderTool implements the [ToolRenderer] interface.
32// RenderTool implements the [ToolRenderer] interface.
33func (r *ReferencesToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
33func (r *ReferencesToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
34− cappedWidth := cappedMessageWidth(width)
35 if opts.IsPending() {
34 if opts.IsPending() {
36 return pendingTool(sty, "Find References", opts.Anim)
35 return pendingTool(sty, "Find References", opts.Anim)
37 }
36 }
@@ -44,12 +43,12 @@ func (r *ReferencesToolRenderContext) RenderTool(sty *styles.Styles, width int,
44 toolParams = append(toolParams, "path", fsext.PrettyPath(params.Path))
43 toolParams = append(toolParams, "path", fsext.PrettyPath(params.Path))
45 }
44 }
46
45
47− header := toolHeader(sty, opts.Status, "Find References", cappedWidth, opts.Compact, toolParams...)
46+ header := toolHeader(sty, opts.Status, "Find References", width, opts.Compact, toolParams...)
48 if opts.Compact {
47 if opts.Compact {
49 return header
48 return header
50 }
49 }
51
50
52− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
51+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
53 return joinToolParts(header, earlyState)
52 return joinToolParts(header, earlyState)
54 }
53 }
55
54
@@ -57,7 +56,7 @@ func (r *ReferencesToolRenderContext) RenderTool(sty *styles.Styles, width int,
57 return header
56 return header
58 }
57 }
59
58
60− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
59+ bodyWidth := width - toolBodyLeftPaddingTotal
61 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
60 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
62 return joinToolParts(header, body)
61 return joinToolParts(header, body)
63}
62}
internal/ui/chat/search.go+16−20
@@ -35,14 +35,13 @@ type GlobToolRenderContext struct{}
35
35
36// RenderTool implements the [ToolRenderer] interface.
36// RenderTool implements the [ToolRenderer] interface.
37func (g *GlobToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
37func (g *GlobToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
38− cappedWidth := cappedMessageWidth(width)
39 if opts.IsPending() {
38 if opts.IsPending() {
40 return pendingTool(sty, "Glob", opts.Anim)
39 return pendingTool(sty, "Glob", opts.Anim)
41 }
40 }
42
41
43 var params tools.GlobParams
42 var params tools.GlobParams
44 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
43 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
45− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
44+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
46 }
45 }
47
46
48 toolParams := []string{params.Pattern}
47 toolParams := []string{params.Pattern}
@@ -50,12 +49,12 @@ func (g *GlobToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
50 toolParams = append(toolParams, "path", params.Path)
49 toolParams = append(toolParams, "path", params.Path)
51 }
50 }
52
51
53− header := toolHeader(sty, opts.Status, "Glob", cappedWidth, opts.Compact, toolParams...)
52+ header := toolHeader(sty, opts.Status, "Glob", width, opts.Compact, toolParams...)
54 if opts.Compact {
53 if opts.Compact {
55 return header
54 return header
56 }
55 }
57
56
58− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
57+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
59 return joinToolParts(header, earlyState)
58 return joinToolParts(header, earlyState)
60 }
59 }
61
60
@@ -63,7 +62,7 @@ func (g *GlobToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
63 return header
62 return header
64 }
63 }
65
64
66− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
65+ bodyWidth := width - toolBodyLeftPaddingTotal
67 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
66 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
68 return joinToolParts(header, body)
67 return joinToolParts(header, body)
69}
68}
@@ -94,14 +93,13 @@ type GrepToolRenderContext struct{}
94
93
95// RenderTool implements the [ToolRenderer] interface.
94// RenderTool implements the [ToolRenderer] interface.
96func (g *GrepToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
95func (g *GrepToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
97− cappedWidth := cappedMessageWidth(width)
98 if opts.IsPending() {
96 if opts.IsPending() {
99 return pendingTool(sty, "Grep", opts.Anim)
97 return pendingTool(sty, "Grep", opts.Anim)
100 }
98 }
101
99
102 var params tools.GrepParams
100 var params tools.GrepParams
103 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
101 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
104− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
102+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
105 }
103 }
106
104
107 toolParams := []string{params.Pattern}
105 toolParams := []string{params.Pattern}
@@ -115,12 +113,12 @@ func (g *GrepToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
115 toolParams = append(toolParams, "literal", "true")
113 toolParams = append(toolParams, "literal", "true")
116 }
114 }
117
115
118− header := toolHeader(sty, opts.Status, "Grep", cappedWidth, opts.Compact, toolParams...)
116+ header := toolHeader(sty, opts.Status, "Grep", width, opts.Compact, toolParams...)
119 if opts.Compact {
117 if opts.Compact {
120 return header
118 return header
121 }
119 }
122
120
123− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
121+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
124 return joinToolParts(header, earlyState)
122 return joinToolParts(header, earlyState)
125 }
123 }
126
124
@@ -128,7 +126,7 @@ func (g *GrepToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
128 return header
126 return header
129 }
127 }
130
128
131− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
129+ bodyWidth := width - toolBodyLeftPaddingTotal
132 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
130 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
133 return joinToolParts(header, body)
131 return joinToolParts(header, body)
134}
132}
@@ -159,14 +157,13 @@ type LSToolRenderContext struct{}
159
157
160// RenderTool implements the [ToolRenderer] interface.
158// RenderTool implements the [ToolRenderer] interface.
161func (l *LSToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
159func (l *LSToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
162− cappedWidth := cappedMessageWidth(width)
163 if opts.IsPending() {
160 if opts.IsPending() {
164 return pendingTool(sty, "List", opts.Anim)
161 return pendingTool(sty, "List", opts.Anim)
165 }
162 }
166
163
167 var params tools.LSParams
164 var params tools.LSParams
168 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
165 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
169− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
166+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
170 }
167 }
171
168
172 path := params.Path
169 path := params.Path
@@ -175,12 +172,12 @@ func (l *LSToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *To
175 }
172 }
176 path = fsext.PrettyPath(path)
173 path = fsext.PrettyPath(path)
177
174
178− header := toolHeader(sty, opts.Status, "List", cappedWidth, opts.Compact, path)
175+ header := toolHeader(sty, opts.Status, "List", width, opts.Compact, path)
179 if opts.Compact {
176 if opts.Compact {
180 return header
177 return header
181 }
178 }
182
179
183− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
180+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
184 return joinToolParts(header, earlyState)
181 return joinToolParts(header, earlyState)
185 }
182 }
186
183
@@ -188,7 +185,7 @@ func (l *LSToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *To
188 return header
185 return header
189 }
186 }
190
187
191− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
188+ bodyWidth := width - toolBodyLeftPaddingTotal
192 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
189 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
193 return joinToolParts(header, body)
190 return joinToolParts(header, body)
194}
191}
@@ -219,14 +216,13 @@ type SourcegraphToolRenderContext struct{}
219
216
220// RenderTool implements the [ToolRenderer] interface.
217// RenderTool implements the [ToolRenderer] interface.
221func (s *SourcegraphToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
218func (s *SourcegraphToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
222− cappedWidth := cappedMessageWidth(width)
223 if opts.IsPending() {
219 if opts.IsPending() {
224 return pendingTool(sty, "Sourcegraph", opts.Anim)
220 return pendingTool(sty, "Sourcegraph", opts.Anim)
225 }
221 }
226
222
227 var params tools.SourcegraphParams
223 var params tools.SourcegraphParams
228 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
224 if err := json.Unmarshal([]byte(opts.ToolCall.Input), &params); err != nil {
229− return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth)
225+ return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, width)
230 }
226 }
231
227
232 toolParams := []string{params.Query}
228 toolParams := []string{params.Query}
@@ -237,12 +233,12 @@ func (s *SourcegraphToolRenderContext) RenderTool(sty *styles.Styles, width int,
237 toolParams = append(toolParams, "context", formatNonZero(params.ContextWindow))
233 toolParams = append(toolParams, "context", formatNonZero(params.ContextWindow))
238 }
234 }
239
235
240− header := toolHeader(sty, opts.Status, "Sourcegraph", cappedWidth, opts.Compact, toolParams...)
236+ header := toolHeader(sty, opts.Status, "Sourcegraph", width, opts.Compact, toolParams...)
241 if opts.Compact {
237 if opts.Compact {
242 return header
238 return header
243 }
239 }
244
240
245− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
241+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
246 return joinToolParts(header, earlyState)
242 return joinToolParts(header, earlyState)
247 }
243 }
248
244
@@ -250,7 +246,7 @@ func (s *SourcegraphToolRenderContext) RenderTool(sty *styles.Styles, width int,
250 return header
246 return header
251 }
247 }
252
248
253− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
249+ bodyWidth := width - toolBodyLeftPaddingTotal
254 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
250 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
255 return joinToolParts(header, body)
251 return joinToolParts(header, body)
256}
252}
internal/ui/chat/todos.go+4−5
@@ -39,7 +39,6 @@ type TodosToolRenderContext struct{}
39
39
40// RenderTool implements the [ToolRenderer] interface.
40// RenderTool implements the [ToolRenderer] interface.
41func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
41func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
42− cappedWidth := cappedMessageWidth(width)
43 if opts.IsPending() {
42 if opts.IsPending() {
44 return pendingTool(sty, "To-Do", opts.Anim)
43 return pendingTool(sty, "To-Do", opts.Anim)
45 }
44 }
@@ -82,7 +81,7 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
82 } else {
81 } else {
83 headerText = fmt.Sprintf("created %d todos", meta.Total)
82 headerText = fmt.Sprintf("created %d todos", meta.Total)
84 }
83 }
85− body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, cappedWidth)
84+ body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, width)
86 } else {
85 } else {
87 // Build header based on what changed.
86 // Build header based on what changed.
88 hasCompleted := len(meta.JustCompleted) > 0
87 hasCompleted := len(meta.JustCompleted) > 0
@@ -108,7 +107,7 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
108 // Build body with details.
107 // Build body with details.
109 if allCompleted {
108 if allCompleted {
110 // Show all todos when all are completed, like when created.
109 // Show all todos when all are completed, like when created.
111− body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, cappedWidth)
110+ body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, width)
112 } else if meta.JustStarted != "" {
111 } else if meta.JustStarted != "" {
113 body = sty.Tool.TodoInProgressIcon.Render(styles.ArrowRightIcon+" ") +
112 body = sty.Tool.TodoInProgressIcon.Render(styles.ArrowRightIcon+" ") +
114 sty.Base.Render(meta.JustStarted)
113 sty.Base.Render(meta.JustStarted)
@@ -119,12 +118,12 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
119 }
118 }
120
119
121 toolParams := []string{headerText}
120 toolParams := []string{headerText}
122− header := toolHeader(sty, opts.Status, "To-Do", cappedWidth, opts.Compact, toolParams...)
121+ header := toolHeader(sty, opts.Status, "To-Do", width, opts.Compact, toolParams...)
123 if opts.Compact {
122 if opts.Compact {
124 return header
123 return header
125 }
124 }
126
125
127− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
126+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
128 return joinToolParts(header, earlyState)
127 return joinToolParts(header, earlyState)
129 }
128 }
130
129
internal/ui/chat/tools.go−8
@@ -295,9 +295,6 @@ func (t *baseToolMessageItem) Animate(msg anim.StepMsg) tea.Cmd {
295// RawRender implements [MessageItem].
295// RawRender implements [MessageItem].
296func (t *baseToolMessageItem) RawRender(width int) string {
296func (t *baseToolMessageItem) RawRender(width int) string {
297 toolItemWidth := width - MessageLeftPaddingTotal
297 toolItemWidth := width - MessageLeftPaddingTotal
298− if t.hasCappedWidth {
299− toolItemWidth = cappedMessageWidth(width)
300− }
301
298
302 content, height, ok := t.getCachedRender(toolItemWidth)
299 content, height, ok := t.getCachedRender(toolItemWidth)
303 // if we are spinning or there is no cache rerender
300 // if we are spinning or there is no cache rerender
@@ -773,11 +770,6 @@ func roundedEnumerator(lPadding, width int) tree.Enumerator {
773func toolOutputMarkdownContent(sty *styles.Styles, content string, width int, expanded bool) string {
770func toolOutputMarkdownContent(sty *styles.Styles, content string, width int, expanded bool) string {
774 content = stringext.NormalizeSpace(content)
771 content = stringext.NormalizeSpace(content)
775
772
776− // Cap width for readability.
777− if width > maxTextWidth {
778− width = maxTextWidth
779− }
780−
781 renderer := common.PlainMarkdownRenderer(sty, width)
773 renderer := common.PlainMarkdownRenderer(sty, width)
782 rendered, err := renderer.Render(content)
774 rendered, err := renderer.Render(content)
783 if err != nil {
775 if err != nil {
internal/ui/chat/user.go+6−8
@@ -36,15 +36,13 @@ func NewUserMessageItem(sty *styles.Styles, message *message.Message, attachment
36
36
37// RawRender implements [MessageItem].
37// RawRender implements [MessageItem].
38func (m *UserMessageItem) RawRender(width int) string {
38func (m *UserMessageItem) RawRender(width int) string {
39− cappedWidth := cappedMessageWidth(width)
40−
41− content, height, ok := m.getCachedRender(cappedWidth)
39+ content, height, ok := m.getCachedRender(width)
42 // cache hit
40 // cache hit
43 if ok {
41 if ok {
44− return m.renderHighlighted(content, cappedWidth, height)
42+ return m.renderHighlighted(content, width, height)
45 }
43 }
46
44
47− renderer := common.MarkdownRenderer(m.sty, cappedWidth)
45+ renderer := common.MarkdownRenderer(m.sty, width)
48
46
49 msgContent := strings.TrimSpace(m.message.Content().Text)
47 msgContent := strings.TrimSpace(m.message.Content().Text)
50 result, err := renderer.Render(msgContent)
48 result, err := renderer.Render(msgContent)
@@ -55,7 +53,7 @@ func (m *UserMessageItem) RawRender(width int) string {
55 }
53 }
56
54
57 if len(m.message.BinaryContent()) > 0 {
55 if len(m.message.BinaryContent()) > 0 {
58− attachmentsStr := m.renderAttachments(cappedWidth)
56+ attachmentsStr := m.renderAttachments(width)
59 if content == "" {
57 if content == "" {
60 content = attachmentsStr
58 content = attachmentsStr
61 } else {
59 } else {
@@ -64,8 +62,8 @@ func (m *UserMessageItem) RawRender(width int) string {
64 }
62 }
65
63
66 height = lipgloss.Height(content)
64 height = lipgloss.Height(content)
67− m.setCachedRender(content, cappedWidth, height)
65+ m.setCachedRender(content, width, height)
68− return m.renderHighlighted(content, cappedWidth, height)
66+ return m.renderHighlighted(content, width, height)
69}
67}
70
68
71// Render implements MessageItem.
69// Render implements MessageItem.