on main help sign in
fix(ui): consistent box sizing (#2127)

* fix(ui): width Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: simplify Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>

commitfd437468b74e250f4d197b29c7857ce1ebbb406e authorCarlos Alexandro Becker <caarlos0@users.noreply.github.com> dateThu 5 Feb 2026, 07:44 (8 months ago) parentdd78d5d
15 files changed+86−124
internal/ui/chat/agent.go+6−8 internal/ui/chat/assistant.go+4−6 internal/ui/chat/bash.go+8−11 internal/ui/chat/diagnostics.go+3−4 internal/ui/chat/fetch.go+12−15 internal/ui/chat/file.go+12−15 internal/ui/chat/generic.go+4−5 internal/ui/chat/lsp_restart.go+3−4 internal/ui/chat/mcp.go+5−6 internal/ui/chat/messages.go−5 internal/ui/chat/references.go+3−4 internal/ui/chat/search.go+16−20 internal/ui/chat/todos.go+4−5 internal/ui/chat/tools.go−8 internal/ui/chat/user.go+6−8
internal/ui/chat/agent.go+6−8
@@ -99,7 +99,6 @@ type AgentToolRenderContext struct {
9999
100100// RenderTool implements the [ToolRenderer] interface.
101101func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
102− cappedWidth := cappedMessageWidth(width)
103102 if !opts.ToolCall.Finished && !opts.IsCanceled() && len(r.agent.nestedTools) == 0 {
104103 return pendingTool(sty, "Agent", opts.Anim)
105104 }
@@ -110,7 +109,7 @@ func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
110109 prompt := params.Prompt
111110 prompt = strings.ReplaceAll(prompt, "\n", " ")
112111
113− header := toolHeader(sty, opts.Status, "Agent", cappedWidth, opts.Compact)
112+ header := toolHeader(sty, opts.Status, "Agent", width, opts.Compact)
114113 if opts.Compact {
115114 return header
116115 }
@@ -120,7 +119,7 @@ func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
120119 taskTagWidth := lipgloss.Width(taskTag)
121120
122121 // 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
124123
125124 promptText := sty.Tool.AgentPrompt.Width(remainingWidth).Render(prompt)
126125
@@ -157,7 +156,7 @@ func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
157156
158157 // Add body content when completed.
159158 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)
161160 return joinToolParts(result, body)
162161 }
163162
@@ -230,7 +229,6 @@ type agenticFetchParams struct {
230229
231230// RenderTool implements the [ToolRenderer] interface.
232231func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
233− cappedWidth := cappedMessageWidth(width)
234232 if !opts.ToolCall.Finished && !opts.IsCanceled() && len(r.fetch.nestedTools) == 0 {
235233 return pendingTool(sty, "Agentic Fetch", opts.Anim)
236234 }
@@ -247,7 +245,7 @@ func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int
247245 toolParams = append(toolParams, params.URL)
248246 }
249247
250− header := toolHeader(sty, opts.Status, "Agentic Fetch", cappedWidth, opts.Compact, toolParams...)
248+ header := toolHeader(sty, opts.Status, "Agentic Fetch", width, opts.Compact, toolParams...)
251249 if opts.Compact {
252250 return header
253251 }
@@ -257,7 +255,7 @@ func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int
257255 promptTagWidth := lipgloss.Width(promptTag)
258256
259257 // 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
261259
262260 promptText := sty.Tool.AgentPrompt.Width(remainingWidth).Render(prompt)
263261
@@ -294,7 +292,7 @@ func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int
294292
295293 // Add body content when completed.
296294 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)
298296 return joinToolParts(result, body)
299297 }
300298
internal/ui/chat/assistant.go+4−6
@@ -79,22 +79,20 @@ func (a *AssistantMessageItem) ID() string {
7979
8080// RawRender implements [MessageItem].
8181func (a *AssistantMessageItem) RawRender(width int) string {
82− cappedWidth := cappedMessageWidth(width)
83−
8482 var spinner string
8583 if a.isSpinning() {
8684 spinner = a.renderSpinning()
8785 }
8886
89− content, height, ok := a.getCachedRender(cappedWidth)
87+ content, height, ok := a.getCachedRender(width)
9088 if !ok {
91− content = a.renderMessageContent(cappedWidth)
89+ content = a.renderMessageContent(width)
9290 height = lipgloss.Height(content)
9391 // cache the rendered content
94− a.setCachedRender(content, cappedWidth, height)
92+ a.setCachedRender(content, width, height)
9593 }
9694
97− highlightedContent := a.renderHighlighted(content, cappedWidth, height)
95+ highlightedContent := a.renderHighlighted(content, width, height)
9896 if spinner != "" {
9997 if highlightedContent != "" {
10098 highlightedContent += "\n\n"
internal/ui/chat/bash.go+8−11
@@ -39,7 +39,6 @@ type BashToolRenderContext struct{}
3939
4040// RenderTool implements the [ToolRenderer] interface.
4141func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
42− cappedWidth := cappedMessageWidth(width)
4342 if opts.IsPending() {
4443 return pendingTool(sty, "Bash", opts.Anim)
4544 }
@@ -58,7 +57,7 @@ func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
5857 if meta.Background {
5958 description := cmp.Or(meta.Description, params.Command)
6059 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)
6261 }
6362
6463 // Regular bash command.
@@ -69,12 +68,12 @@ func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
6968 toolParams = append(toolParams, "background", "true")
7069 }
7170
72− header := toolHeader(sty, opts.Status, "Bash", cappedWidth, opts.Compact, toolParams...)
71+ header := toolHeader(sty, opts.Status, "Bash", width, opts.Compact, toolParams...)
7372 if opts.Compact {
7473 return header
7574 }
7675
77− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
76+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
7877 return joinToolParts(header, earlyState)
7978 }
8079
@@ -90,7 +89,7 @@ func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
9089 return header
9190 }
9291
93− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
92+ bodyWidth := width - toolBodyLeftPaddingTotal
9493 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, output, bodyWidth, opts.ExpandedContent))
9594 return joinToolParts(header, body)
9695}
@@ -121,14 +120,13 @@ type JobOutputToolRenderContext struct{}
121120
122121// RenderTool implements the [ToolRenderer] interface.
123122func (j *JobOutputToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
124− cappedWidth := cappedMessageWidth(width)
125123 if opts.IsPending() {
126124 return pendingTool(sty, "Job", opts.Anim)
127125 }
128126
129127 var params tools.JobOutputParams
130128 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)
132130 }
133131
134132 var description string
@@ -143,7 +141,7 @@ func (j *JobOutputToolRenderContext) RenderTool(sty *styles.Styles, width int, o
143141 if opts.HasResult() {
144142 content = opts.Result.Content
145143 }
146− return renderJobTool(sty, opts, cappedWidth, "Output", params.ShellID, description, content)
144+ return renderJobTool(sty, opts, width, "Output", params.ShellID, description, content)
147145}
148146
149147// -----------------------------------------------------------------------------
@@ -172,14 +170,13 @@ type JobKillToolRenderContext struct{}
172170
173171// RenderTool implements the [ToolRenderer] interface.
174172func (j *JobKillToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
175− cappedWidth := cappedMessageWidth(width)
176173 if opts.IsPending() {
177174 return pendingTool(sty, "Job", opts.Anim)
178175 }
179176
180177 var params tools.JobKillParams
181178 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)
183180 }
184181
185182 var description string
@@ -194,7 +191,7 @@ func (j *JobKillToolRenderContext) RenderTool(sty *styles.Styles, width int, opt
194191 if opts.HasResult() {
195192 content = opts.Result.Content
196193 }
197− return renderJobTool(sty, opts, cappedWidth, "Kill", params.ShellID, description, content)
194+ return renderJobTool(sty, opts, width, "Kill", params.ShellID, description, content)
198195}
199196
200197// renderJobTool renders a job-related tool with the common pattern:
internal/ui/chat/diagnostics.go+3−4
@@ -35,7 +35,6 @@ type DiagnosticsToolRenderContext struct{}
3535
3636// RenderTool implements the [ToolRenderer] interface.
3737func (d *DiagnosticsToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
38− cappedWidth := cappedMessageWidth(width)
3938 if opts.IsPending() {
4039 return pendingTool(sty, "Diagnostics", opts.Anim)
4140 }
@@ -49,12 +48,12 @@ func (d *DiagnosticsToolRenderContext) RenderTool(sty *styles.Styles, width int,
4948 mainParam = fsext.PrettyPath(params.FilePath)
5049 }
5150
52− header := toolHeader(sty, opts.Status, "Diagnostics", cappedWidth, opts.Compact, mainParam)
51+ header := toolHeader(sty, opts.Status, "Diagnostics", width, opts.Compact, mainParam)
5352 if opts.Compact {
5453 return header
5554 }
5655
57− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
56+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
5857 return joinToolParts(header, earlyState)
5958 }
6059
@@ -62,7 +61,7 @@ func (d *DiagnosticsToolRenderContext) RenderTool(sty *styles.Styles, width int,
6261 return header
6362 }
6463
65− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
64+ bodyWidth := width - toolBodyLeftPaddingTotal
6665 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
6766 return joinToolParts(header, body)
6867}
internal/ui/chat/fetch.go+12−15
@@ -34,14 +34,13 @@ type FetchToolRenderContext struct{}
3434
3535// RenderTool implements the [ToolRenderer] interface.
3636func (f *FetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
37− cappedWidth := cappedMessageWidth(width)
3837 if opts.IsPending() {
3938 return pendingTool(sty, "Fetch", opts.Anim)
4039 }
4140
4241 var params tools.FetchParams
4342 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)
4544 }
4645
4746 toolParams := []string{params.URL}
@@ -52,12 +51,12 @@ func (f *FetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
5251 toolParams = append(toolParams, "timeout", formatTimeout(params.Timeout))
5352 }
5453
55− header := toolHeader(sty, opts.Status, "Fetch", cappedWidth, opts.Compact, toolParams...)
54+ header := toolHeader(sty, opts.Status, "Fetch", width, opts.Compact, toolParams...)
5655 if opts.Compact {
5756 return header
5857 }
5958
60− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
59+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
6160 return joinToolParts(header, earlyState)
6261 }
6362
@@ -67,7 +66,7 @@ func (f *FetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
6766
6867 // Determine file extension for syntax highlighting based on format.
6968 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)
7170 return joinToolParts(header, body)
7271}
7372
@@ -109,23 +108,22 @@ type WebFetchToolRenderContext struct{}
109108
110109// RenderTool implements the [ToolRenderer] interface.
111110func (w *WebFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
112− cappedWidth := cappedMessageWidth(width)
113111 if opts.IsPending() {
114112 return pendingTool(sty, "Fetch", opts.Anim)
115113 }
116114
117115 var params tools.WebFetchParams
118116 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)
120118 }
121119
122120 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...)
124122 if opts.Compact {
125123 return header
126124 }
127125
128− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
126+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
129127 return joinToolParts(header, earlyState)
130128 }
131129
@@ -133,7 +131,7 @@ func (w *WebFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, op
133131 return header
134132 }
135133
136− body := toolOutputMarkdownContent(sty, opts.Result.Content, cappedWidth, opts.ExpandedContent)
134+ body := toolOutputMarkdownContent(sty, opts.Result.Content, width, opts.ExpandedContent)
137135 return joinToolParts(header, body)
138136}
139137
@@ -163,23 +161,22 @@ type WebSearchToolRenderContext struct{}
163161
164162// RenderTool implements the [ToolRenderer] interface.
165163func (w *WebSearchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
166− cappedWidth := cappedMessageWidth(width)
167164 if opts.IsPending() {
168165 return pendingTool(sty, "Search", opts.Anim)
169166 }
170167
171168 var params tools.WebSearchParams
172169 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)
174171 }
175172
176173 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...)
178175 if opts.Compact {
179176 return header
180177 }
181178
182− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
179+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
183180 return joinToolParts(header, earlyState)
184181 }
185182
@@ -187,6 +184,6 @@ func (w *WebSearchToolRenderContext) RenderTool(sty *styles.Styles, width int, o
187184 return header
188185 }
189186
190− body := toolOutputMarkdownContent(sty, opts.Result.Content, cappedWidth, opts.ExpandedContent)
187+ body := toolOutputMarkdownContent(sty, opts.Result.Content, width, opts.ExpandedContent)
191188 return joinToolParts(header, body)
192189}
internal/ui/chat/file.go+12−15
@@ -37,14 +37,13 @@ type ViewToolRenderContext struct{}
3737
3838// RenderTool implements the [ToolRenderer] interface.
3939func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
40− cappedWidth := cappedMessageWidth(width)
4140 if opts.IsPending() {
4241 return pendingTool(sty, "View", opts.Anim)
4342 }
4443
4544 var params tools.ViewParams
4645 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)
4847 }
4948
5049 file := fsext.PrettyPath(params.FilePath)
@@ -56,12 +55,12 @@ func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
5655 toolParams = append(toolParams, "offset", fmt.Sprintf("%d", params.Offset))
5756 }
5857
59− header := toolHeader(sty, opts.Status, "View", cappedWidth, opts.Compact, toolParams...)
58+ header := toolHeader(sty, opts.Status, "View", width, opts.Compact, toolParams...)
6059 if opts.Compact {
6160 return header
6261 }
6362
64− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
63+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
6564 return joinToolParts(header, earlyState)
6665 }
6766
@@ -87,7 +86,7 @@ func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
8786 }
8887
8988 // 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)
9190 return joinToolParts(header, body)
9291}
9392
@@ -117,23 +116,22 @@ type WriteToolRenderContext struct{}
117116
118117// RenderTool implements the [ToolRenderer] interface.
119118func (w *WriteToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
120− cappedWidth := cappedMessageWidth(width)
121119 if opts.IsPending() {
122120 return pendingTool(sty, "Write", opts.Anim)
123121 }
124122
125123 var params tools.WriteParams
126124 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)
128126 }
129127
130128 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)
132130 if opts.Compact {
133131 return header
134132 }
135133
136− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
134+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
137135 return joinToolParts(header, earlyState)
138136 }
139137
@@ -142,7 +140,7 @@ func (w *WriteToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
142140 }
143141
144142 // 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)
146144 return joinToolParts(header, body)
147145}
148146
@@ -303,14 +301,13 @@ type DownloadToolRenderContext struct{}
303301
304302// RenderTool implements the [ToolRenderer] interface.
305303func (d *DownloadToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
306− cappedWidth := cappedMessageWidth(width)
307304 if opts.IsPending() {
308305 return pendingTool(sty, "Download", opts.Anim)
309306 }
310307
311308 var params tools.DownloadParams
312309 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)
314311 }
315312
316313 toolParams := []string{params.URL}
@@ -321,12 +318,12 @@ func (d *DownloadToolRenderContext) RenderTool(sty *styles.Styles, width int, op
321318 toolParams = append(toolParams, "timeout", formatTimeout(params.Timeout))
322319 }
323320
324− header := toolHeader(sty, opts.Status, "Download", cappedWidth, opts.Compact, toolParams...)
321+ header := toolHeader(sty, opts.Status, "Download", width, opts.Compact, toolParams...)
325322 if opts.Compact {
326323 return header
327324 }
328325
329− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
326+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
330327 return joinToolParts(header, earlyState)
331328 }
332329
@@ -334,7 +331,7 @@ func (d *DownloadToolRenderContext) RenderTool(sty *styles.Styles, width int, op
334331 return header
335332 }
336333
337− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
334+ bodyWidth := width - toolBodyLeftPaddingTotal
338335 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
339336 return joinToolParts(header, body)
340337}
internal/ui/chat/generic.go+4−5
@@ -31,7 +31,6 @@ type GenericToolRenderContext struct{}
3131
3232// RenderTool implements the [ToolRenderer] interface.
3333func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
34− cappedWidth := cappedMessageWidth(width)
3534 name := genericPrettyName(opts.ToolCall.Name)
3635
3736 if opts.IsPending() {
@@ -40,7 +39,7 @@ func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opt
4039
4140 var params map[string]any
4241 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)
4443 }
4544
4645 var toolParams []string
@@ -49,12 +48,12 @@ func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opt
4948 toolParams = append(toolParams, string(parsed))
5049 }
5150
52− header := toolHeader(sty, opts.Status, name, cappedWidth, opts.Compact, toolParams...)
51+ header := toolHeader(sty, opts.Status, name, width, opts.Compact, toolParams...)
5352 if opts.Compact {
5453 return header
5554 }
5655
57− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
56+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
5857 return joinToolParts(header, earlyState)
5958 }
6059
@@ -62,7 +61,7 @@ func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opt
6261 return header
6362 }
6463
65− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
64+ bodyWidth := width - toolBodyLeftPaddingTotal
6665
6766 // Handle image data.
6867 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{}
3030
3131// RenderTool implements the [ToolRenderer] interface.
3232func (r *LSPRestartToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
33− cappedWidth := cappedMessageWidth(width)
3433 if opts.IsPending() {
3534 return pendingTool(sty, "Restart LSP", opts.Anim)
3635 }
@@ -43,12 +42,12 @@ func (r *LSPRestartToolRenderContext) RenderTool(sty *styles.Styles, width int,
4342 toolParams = append(toolParams, params.Name)
4443 }
4544
46− header := toolHeader(sty, opts.Status, "Restart LSP", cappedWidth, opts.Compact, toolParams...)
45+ header := toolHeader(sty, opts.Status, "Restart LSP", width, opts.Compact, toolParams...)
4746 if opts.Compact {
4847 return header
4948 }
5049
51− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
50+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
5251 return joinToolParts(header, earlyState)
5352 }
5453
@@ -56,7 +55,7 @@ func (r *LSPRestartToolRenderContext) RenderTool(sty *styles.Styles, width int,
5655 return header
5756 }
5857
59− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
58+ bodyWidth := width - toolBodyLeftPaddingTotal
6059 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
6160 return joinToolParts(header, body)
6261}
internal/ui/chat/mcp.go+5−6
@@ -32,10 +32,9 @@ type MCPToolRenderContext struct{}
3232
3333// RenderTool implements the [ToolRenderer] interface.
3434func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
35− cappedWidth := cappedMessageWidth(width)
3635 toolNameParts := strings.SplitN(opts.ToolCall.Name, "_", 3)
3736 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)
3938 }
4039 mcpName := prettyName(toolNameParts[1])
4140 toolName := prettyName(toolNameParts[2])
@@ -51,7 +50,7 @@ func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *T
5150
5251 var params map[string]any
5352 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)
5554 }
5655
5756 var toolParams []string
@@ -60,12 +59,12 @@ func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *T
6059 toolParams = append(toolParams, string(parsed))
6160 }
6261
63− header := toolHeader(sty, opts.Status, name, cappedWidth, opts.Compact, toolParams...)
62+ header := toolHeader(sty, opts.Status, name, width, opts.Compact, toolParams...)
6463 if opts.Compact {
6564 return header
6665 }
6766
68− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
67+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
6968 return joinToolParts(header, earlyState)
7069 }
7170
@@ -73,7 +72,7 @@ func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *T
7372 return header
7473 }
7574
76− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
75+ bodyWidth := width - toolBodyLeftPaddingTotal
7776 // see if the result is json
7877 var result json.RawMessage
7978 var body string
internal/ui/chat/messages.go−5
@@ -245,11 +245,6 @@ func (a *AssistantInfoItem) renderContent(width int) string {
245245 return common.Section(a.sty, assistant, width)
246246}
247247
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−
253248// ExtractMessageItems extracts [MessageItem]s from a [message.Message]. It
254249// returns all parts of the message as [MessageItem]s.
255250//
internal/ui/chat/references.go+3−4
@@ -31,7 +31,6 @@ type ReferencesToolRenderContext struct{}
3131
3232// RenderTool implements the [ToolRenderer] interface.
3333func (r *ReferencesToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
34− cappedWidth := cappedMessageWidth(width)
3534 if opts.IsPending() {
3635 return pendingTool(sty, "Find References", opts.Anim)
3736 }
@@ -44,12 +43,12 @@ func (r *ReferencesToolRenderContext) RenderTool(sty *styles.Styles, width int,
4443 toolParams = append(toolParams, "path", fsext.PrettyPath(params.Path))
4544 }
4645
47− header := toolHeader(sty, opts.Status, "Find References", cappedWidth, opts.Compact, toolParams...)
46+ header := toolHeader(sty, opts.Status, "Find References", width, opts.Compact, toolParams...)
4847 if opts.Compact {
4948 return header
5049 }
5150
52− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
51+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
5352 return joinToolParts(header, earlyState)
5453 }
5554
@@ -57,7 +56,7 @@ func (r *ReferencesToolRenderContext) RenderTool(sty *styles.Styles, width int,
5756 return header
5857 }
5958
60− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
59+ bodyWidth := width - toolBodyLeftPaddingTotal
6160 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
6261 return joinToolParts(header, body)
6362}
internal/ui/chat/search.go+16−20
@@ -35,14 +35,13 @@ type GlobToolRenderContext struct{}
3535
3636// RenderTool implements the [ToolRenderer] interface.
3737func (g *GlobToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
38− cappedWidth := cappedMessageWidth(width)
3938 if opts.IsPending() {
4039 return pendingTool(sty, "Glob", opts.Anim)
4140 }
4241
4342 var params tools.GlobParams
4443 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)
4645 }
4746
4847 toolParams := []string{params.Pattern}
@@ -50,12 +49,12 @@ func (g *GlobToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
5049 toolParams = append(toolParams, "path", params.Path)
5150 }
5251
53− header := toolHeader(sty, opts.Status, "Glob", cappedWidth, opts.Compact, toolParams...)
52+ header := toolHeader(sty, opts.Status, "Glob", width, opts.Compact, toolParams...)
5453 if opts.Compact {
5554 return header
5655 }
5756
58− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
57+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
5958 return joinToolParts(header, earlyState)
6059 }
6160
@@ -63,7 +62,7 @@ func (g *GlobToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
6362 return header
6463 }
6564
66− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
65+ bodyWidth := width - toolBodyLeftPaddingTotal
6766 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
6867 return joinToolParts(header, body)
6968}
@@ -94,14 +93,13 @@ type GrepToolRenderContext struct{}
9493
9594// RenderTool implements the [ToolRenderer] interface.
9695func (g *GrepToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
97− cappedWidth := cappedMessageWidth(width)
9896 if opts.IsPending() {
9997 return pendingTool(sty, "Grep", opts.Anim)
10098 }
10199
102100 var params tools.GrepParams
103101 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)
105103 }
106104
107105 toolParams := []string{params.Pattern}
@@ -115,12 +113,12 @@ func (g *GrepToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
115113 toolParams = append(toolParams, "literal", "true")
116114 }
117115
118− header := toolHeader(sty, opts.Status, "Grep", cappedWidth, opts.Compact, toolParams...)
116+ header := toolHeader(sty, opts.Status, "Grep", width, opts.Compact, toolParams...)
119117 if opts.Compact {
120118 return header
121119 }
122120
123− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
121+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
124122 return joinToolParts(header, earlyState)
125123 }
126124
@@ -128,7 +126,7 @@ func (g *GrepToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *
128126 return header
129127 }
130128
131− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
129+ bodyWidth := width - toolBodyLeftPaddingTotal
132130 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
133131 return joinToolParts(header, body)
134132}
@@ -159,14 +157,13 @@ type LSToolRenderContext struct{}
159157
160158// RenderTool implements the [ToolRenderer] interface.
161159func (l *LSToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
162− cappedWidth := cappedMessageWidth(width)
163160 if opts.IsPending() {
164161 return pendingTool(sty, "List", opts.Anim)
165162 }
166163
167164 var params tools.LSParams
168165 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)
170167 }
171168
172169 path := params.Path
@@ -175,12 +172,12 @@ func (l *LSToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *To
175172 }
176173 path = fsext.PrettyPath(path)
177174
178− header := toolHeader(sty, opts.Status, "List", cappedWidth, opts.Compact, path)
175+ header := toolHeader(sty, opts.Status, "List", width, opts.Compact, path)
179176 if opts.Compact {
180177 return header
181178 }
182179
183− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
180+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
184181 return joinToolParts(header, earlyState)
185182 }
186183
@@ -188,7 +185,7 @@ func (l *LSToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *To
188185 return header
189186 }
190187
191− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
188+ bodyWidth := width - toolBodyLeftPaddingTotal
192189 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
193190 return joinToolParts(header, body)
194191}
@@ -219,14 +216,13 @@ type SourcegraphToolRenderContext struct{}
219216
220217// RenderTool implements the [ToolRenderer] interface.
221218func (s *SourcegraphToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
222− cappedWidth := cappedMessageWidth(width)
223219 if opts.IsPending() {
224220 return pendingTool(sty, "Sourcegraph", opts.Anim)
225221 }
226222
227223 var params tools.SourcegraphParams
228224 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)
230226 }
231227
232228 toolParams := []string{params.Query}
@@ -237,12 +233,12 @@ func (s *SourcegraphToolRenderContext) RenderTool(sty *styles.Styles, width int,
237233 toolParams = append(toolParams, "context", formatNonZero(params.ContextWindow))
238234 }
239235
240− header := toolHeader(sty, opts.Status, "Sourcegraph", cappedWidth, opts.Compact, toolParams...)
236+ header := toolHeader(sty, opts.Status, "Sourcegraph", width, opts.Compact, toolParams...)
241237 if opts.Compact {
242238 return header
243239 }
244240
245− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
241+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
246242 return joinToolParts(header, earlyState)
247243 }
248244
@@ -250,7 +246,7 @@ func (s *SourcegraphToolRenderContext) RenderTool(sty *styles.Styles, width int,
250246 return header
251247 }
252248
253− bodyWidth := cappedWidth - toolBodyLeftPaddingTotal
249+ bodyWidth := width - toolBodyLeftPaddingTotal
254250 body := sty.Tool.Body.Render(toolOutputPlainContent(sty, opts.Result.Content, bodyWidth, opts.ExpandedContent))
255251 return joinToolParts(header, body)
256252}
internal/ui/chat/todos.go+4−5
@@ -39,7 +39,6 @@ type TodosToolRenderContext struct{}
3939
4040// RenderTool implements the [ToolRenderer] interface.
4141func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
42− cappedWidth := cappedMessageWidth(width)
4342 if opts.IsPending() {
4443 return pendingTool(sty, "To-Do", opts.Anim)
4544 }
@@ -82,7 +81,7 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
8281 } else {
8382 headerText = fmt.Sprintf("created %d todos", meta.Total)
8483 }
85− body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, cappedWidth)
84+ body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, width)
8685 } else {
8786 // Build header based on what changed.
8887 hasCompleted := len(meta.JustCompleted) > 0
@@ -108,7 +107,7 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
108107 // Build body with details.
109108 if allCompleted {
110109 // 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)
112111 } else if meta.JustStarted != "" {
113112 body = sty.Tool.TodoInProgressIcon.Render(styles.ArrowRightIcon+" ") +
114113 sty.Base.Render(meta.JustStarted)
@@ -119,12 +118,12 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
119118 }
120119
121120 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...)
123122 if opts.Compact {
124123 return header
125124 }
126125
127− if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok {
126+ if earlyState, ok := toolEarlyStateContent(sty, opts, width); ok {
128127 return joinToolParts(header, earlyState)
129128 }
130129
internal/ui/chat/tools.go−8
@@ -295,9 +295,6 @@ func (t *baseToolMessageItem) Animate(msg anim.StepMsg) tea.Cmd {
295295// RawRender implements [MessageItem].
296296func (t *baseToolMessageItem) RawRender(width int) string {
297297 toolItemWidth := width - MessageLeftPaddingTotal
298− if t.hasCappedWidth {
299− toolItemWidth = cappedMessageWidth(width)
300− }
301298
302299 content, height, ok := t.getCachedRender(toolItemWidth)
303300 // if we are spinning or there is no cache rerender
@@ -773,11 +770,6 @@ func roundedEnumerator(lPadding, width int) tree.Enumerator {
773770func toolOutputMarkdownContent(sty *styles.Styles, content string, width int, expanded bool) string {
774771 content = stringext.NormalizeSpace(content)
775772
776− // Cap width for readability.
777− if width > maxTextWidth {
778− width = maxTextWidth
779− }
780−
781773 renderer := common.PlainMarkdownRenderer(sty, width)
782774 rendered, err := renderer.Render(content)
783775 if err != nil {
internal/ui/chat/user.go+6−8
@@ -36,15 +36,13 @@ func NewUserMessageItem(sty *styles.Styles, message *message.Message, attachment
3636
3737// RawRender implements [MessageItem].
3838func (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)
4240 // cache hit
4341 if ok {
44− return m.renderHighlighted(content, cappedWidth, height)
42+ return m.renderHighlighted(content, width, height)
4543 }
4644
47− renderer := common.MarkdownRenderer(m.sty, cappedWidth)
45+ renderer := common.MarkdownRenderer(m.sty, width)
4846
4947 msgContent := strings.TrimSpace(m.message.Content().Text)
5048 result, err := renderer.Render(msgContent)
@@ -55,7 +53,7 @@ func (m *UserMessageItem) RawRender(width int) string {
5553 }
5654
5755 if len(m.message.BinaryContent()) > 0 {
58− attachmentsStr := m.renderAttachments(cappedWidth)
56+ attachmentsStr := m.renderAttachments(width)
5957 if content == "" {
6058 content = attachmentsStr
6159 } else {
@@ -64,8 +62,8 @@ func (m *UserMessageItem) RawRender(width int) string {
6462 }
6563
6664 height = lipgloss.Height(content)
67− m.setCachedRender(content, cappedWidth, height)
68− return m.renderHighlighted(content, cappedWidth, height)
65+ m.setCachedRender(content, width, height)
66+ return m.renderHighlighted(content, width, height)
6967}
7068
7169// Render implements MessageItem.