on main help sign in
fix(ui): render nested tools consistently across states

Prior to this, nested tool calls would start in one color and finish in another.

commitb05a4add77941265581e3508ce50429143e6a8fa authorChristian Rocha <christian@rocha.is> dateSat 14 Mar 2026, 23:12 (7 months ago) parenta1fc44b
13 files changed+38−34
internal/ui/chat/agent.go+2−2 internal/ui/chat/bash.go+3−3 internal/ui/chat/diagnostics.go+1−1 internal/ui/chat/fetch.go+3−3 internal/ui/chat/file.go+5−5 internal/ui/chat/generic.go+1−1 internal/ui/chat/lsp_restart.go+1−1 internal/ui/chat/mcp.go+1−1 internal/ui/chat/references.go+1−1 internal/ui/chat/search.go+4−4 internal/ui/chat/todos.go+1−1 internal/ui/chat/tools.go+6−2 internal/ui/styles/styles.go+9−9
internal/ui/chat/agent.go+2−2
@@ -101,7 +101,7 @@ type AgentToolRenderContext struct {
101101func (r *AgentToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
102102 cappedWidth := cappedMessageWidth(width)
103103 if !opts.ToolCall.Finished && !opts.IsCanceled() && len(r.agent.nestedTools) == 0 {
104− return pendingTool(sty, "Agent", opts.Anim)
104+ return pendingTool(sty, "Agent", opts.Anim, opts.Compact)
105105 }
106106
107107 var params agent.AgentParams
@@ -232,7 +232,7 @@ type agenticFetchParams struct {
232232func (r *AgenticFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
233233 cappedWidth := cappedMessageWidth(width)
234234 if !opts.ToolCall.Finished && !opts.IsCanceled() && len(r.fetch.nestedTools) == 0 {
235− return pendingTool(sty, "Agentic Fetch", opts.Anim)
235+ return pendingTool(sty, "Agentic Fetch", opts.Anim, opts.Compact)
236236 }
237237
238238 var params agenticFetchParams
internal/ui/chat/bash.go+3−3
@@ -41,7 +41,7 @@ type BashToolRenderContext struct{}
4141func (b *BashToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
4242 cappedWidth := cappedMessageWidth(width)
4343 if opts.IsPending() {
44− return pendingTool(sty, "Bash", opts.Anim)
44+ return pendingTool(sty, "Bash", opts.Anim, opts.Compact)
4545 }
4646
4747 var params tools.BashParams
@@ -123,7 +123,7 @@ type JobOutputToolRenderContext struct{}
123123func (j *JobOutputToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
124124 cappedWidth := cappedMessageWidth(width)
125125 if opts.IsPending() {
126− return pendingTool(sty, "Job", opts.Anim)
126+ return pendingTool(sty, "Job", opts.Anim, opts.Compact)
127127 }
128128
129129 var params tools.JobOutputParams
@@ -174,7 +174,7 @@ type JobKillToolRenderContext struct{}
174174func (j *JobKillToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
175175 cappedWidth := cappedMessageWidth(width)
176176 if opts.IsPending() {
177− return pendingTool(sty, "Job", opts.Anim)
177+ return pendingTool(sty, "Job", opts.Anim, opts.Compact)
178178 }
179179
180180 var params tools.JobKillParams
internal/ui/chat/diagnostics.go+1−1
@@ -37,7 +37,7 @@ type DiagnosticsToolRenderContext struct{}
3737func (d *DiagnosticsToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
3838 cappedWidth := cappedMessageWidth(width)
3939 if opts.IsPending() {
40− return pendingTool(sty, "Diagnostics", opts.Anim)
40+ return pendingTool(sty, "Diagnostics", opts.Anim, opts.Compact)
4141 }
4242
4343 var params tools.DiagnosticsParams
internal/ui/chat/fetch.go+3−3
@@ -36,7 +36,7 @@ type FetchToolRenderContext struct{}
3636func (f *FetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
3737 cappedWidth := cappedMessageWidth(width)
3838 if opts.IsPending() {
39− return pendingTool(sty, "Fetch", opts.Anim)
39+ return pendingTool(sty, "Fetch", opts.Anim, opts.Compact)
4040 }
4141
4242 var params tools.FetchParams
@@ -111,7 +111,7 @@ type WebFetchToolRenderContext struct{}
111111func (w *WebFetchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
112112 cappedWidth := cappedMessageWidth(width)
113113 if opts.IsPending() {
114− return pendingTool(sty, "Fetch", opts.Anim)
114+ return pendingTool(sty, "Fetch", opts.Anim, opts.Compact)
115115 }
116116
117117 var params tools.WebFetchParams
@@ -165,7 +165,7 @@ type WebSearchToolRenderContext struct{}
165165func (w *WebSearchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
166166 cappedWidth := cappedMessageWidth(width)
167167 if opts.IsPending() {
168− return pendingTool(sty, "Search", opts.Anim)
168+ return pendingTool(sty, "Search", opts.Anim, opts.Compact)
169169 }
170170
171171 var params tools.WebSearchParams
internal/ui/chat/file.go+5−5
@@ -39,7 +39,7 @@ type ViewToolRenderContext struct{}
3939func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
4040 cappedWidth := cappedMessageWidth(width)
4141 if opts.IsPending() {
42− return pendingTool(sty, "View", opts.Anim)
42+ return pendingTool(sty, "View", opts.Anim, opts.Compact)
4343 }
4444
4545 var params tools.ViewParams
@@ -125,7 +125,7 @@ type WriteToolRenderContext struct{}
125125func (w *WriteToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
126126 cappedWidth := cappedMessageWidth(width)
127127 if opts.IsPending() {
128− return pendingTool(sty, "Write", opts.Anim)
128+ return pendingTool(sty, "Write", opts.Anim, opts.Compact)
129129 }
130130
131131 var params tools.WriteParams
@@ -180,7 +180,7 @@ type EditToolRenderContext struct{}
180180func (e *EditToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
181181 // Edit tool uses full width for diffs.
182182 if opts.IsPending() {
183− return pendingTool(sty, "Edit", opts.Anim)
183+ return pendingTool(sty, "Edit", opts.Anim, opts.Compact)
184184 }
185185
186186 var params tools.EditParams
@@ -243,7 +243,7 @@ type MultiEditToolRenderContext struct{}
243243func (m *MultiEditToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
244244 // MultiEdit tool uses full width for diffs.
245245 if opts.IsPending() {
246− return pendingTool(sty, "Multi-Edit", opts.Anim)
246+ return pendingTool(sty, "Multi-Edit", opts.Anim, opts.Compact)
247247 }
248248
249249 var params tools.MultiEditParams
@@ -311,7 +311,7 @@ type DownloadToolRenderContext struct{}
311311func (d *DownloadToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
312312 cappedWidth := cappedMessageWidth(width)
313313 if opts.IsPending() {
314− return pendingTool(sty, "Download", opts.Anim)
314+ return pendingTool(sty, "Download", opts.Anim, opts.Compact)
315315 }
316316
317317 var params tools.DownloadParams
internal/ui/chat/generic.go+1−1
@@ -35,7 +35,7 @@ func (g *GenericToolRenderContext) RenderTool(sty *styles.Styles, width int, opt
3535 name := genericPrettyName(opts.ToolCall.Name)
3636
3737 if opts.IsPending() {
38− return pendingTool(sty, name, opts.Anim)
38+ return pendingTool(sty, name, opts.Anim, opts.Compact)
3939 }
4040
4141 var params map[string]any
internal/ui/chat/lsp_restart.go+1−1
@@ -32,7 +32,7 @@ type LSPRestartToolRenderContext struct{}
3232func (r *LSPRestartToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
3333 cappedWidth := cappedMessageWidth(width)
3434 if opts.IsPending() {
35− return pendingTool(sty, "Restart LSP", opts.Anim)
35+ return pendingTool(sty, "Restart LSP", opts.Anim, opts.Compact)
3636 }
3737
3838 var params tools.LSPRestartParams
internal/ui/chat/mcp.go+1−1
@@ -46,7 +46,7 @@ func (b *MCPToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *T
4646 name := fmt.Sprintf("%s %s %s", mcpName, sty.Tool.MCPArrow.String(), toolName)
4747
4848 if opts.IsPending() {
49− return pendingTool(sty, name, opts.Anim)
49+ return pendingTool(sty, name, opts.Anim, opts.Compact)
5050 }
5151
5252 var params map[string]any
internal/ui/chat/references.go+1−1
@@ -33,7 +33,7 @@ type ReferencesToolRenderContext struct{}
3333func (r *ReferencesToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
3434 cappedWidth := cappedMessageWidth(width)
3535 if opts.IsPending() {
36− return pendingTool(sty, "Find References", opts.Anim)
36+ return pendingTool(sty, "Find References", opts.Anim, opts.Compact)
3737 }
3838
3939 var params tools.ReferencesParams
internal/ui/chat/search.go+4−4
@@ -37,7 +37,7 @@ type GlobToolRenderContext struct{}
3737func (g *GlobToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
3838 cappedWidth := cappedMessageWidth(width)
3939 if opts.IsPending() {
40− return pendingTool(sty, "Glob", opts.Anim)
40+ return pendingTool(sty, "Glob", opts.Anim, opts.Compact)
4141 }
4242
4343 var params tools.GlobParams
@@ -96,7 +96,7 @@ type GrepToolRenderContext struct{}
9696func (g *GrepToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
9797 cappedWidth := cappedMessageWidth(width)
9898 if opts.IsPending() {
99− return pendingTool(sty, "Grep", opts.Anim)
99+ return pendingTool(sty, "Grep", opts.Anim, opts.Compact)
100100 }
101101
102102 var params tools.GrepParams
@@ -161,7 +161,7 @@ type LSToolRenderContext struct{}
161161func (l *LSToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
162162 cappedWidth := cappedMessageWidth(width)
163163 if opts.IsPending() {
164− return pendingTool(sty, "List", opts.Anim)
164+ return pendingTool(sty, "List", opts.Anim, opts.Compact)
165165 }
166166
167167 var params tools.LSParams
@@ -221,7 +221,7 @@ type SourcegraphToolRenderContext struct{}
221221func (s *SourcegraphToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
222222 cappedWidth := cappedMessageWidth(width)
223223 if opts.IsPending() {
224− return pendingTool(sty, "Sourcegraph", opts.Anim)
224+ return pendingTool(sty, "Sourcegraph", opts.Anim, opts.Compact)
225225 }
226226
227227 var params tools.SourcegraphParams
internal/ui/chat/todos.go+1−1
@@ -41,7 +41,7 @@ type TodosToolRenderContext struct{}
4141func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string {
4242 cappedWidth := cappedMessageWidth(width)
4343 if opts.IsPending() {
44− return pendingTool(sty, "To-Do", opts.Anim)
44+ return pendingTool(sty, "To-Do", opts.Anim, opts.Compact)
4545 }
4646
4747 var params tools.TodosParams
internal/ui/chat/tools.go+6−2
@@ -424,9 +424,13 @@ func (t *baseToolMessageItem) HandleKeyEvent(key tea.KeyMsg) (bool, tea.Cmd) {
424424}
425425
426426// pendingTool renders a tool that is still in progress with an animation.
427−func pendingTool(sty *styles.Styles, name string, anim *anim.Anim) string {
427+func pendingTool(sty *styles.Styles, name string, anim *anim.Anim, nested bool) string {
428428 icon := sty.Tool.IconPending.Render()
429− toolName := sty.Tool.NameNormal.Render(name)
429+ nameStyle := sty.Tool.NameNormal
430+ if nested {
431+ nameStyle = sty.Tool.NameNested
432+ }
433+ toolName := nameStyle.Render(name)
430434
431435 var animView string
432436 if anim != nil {
internal/ui/styles/styles.go+9−9
@@ -254,18 +254,18 @@ type Styles struct {
254254 // Tool - styles for tool call rendering
255255 Tool struct {
256256 // Icon styles with tool status
257− IconPending lipgloss.Style // Pending operation icon
258− IconSuccess lipgloss.Style // Successful operation icon
259− IconError lipgloss.Style // Error operation icon
260− IconCancelled lipgloss.Style // Cancelled operation icon
257+ IconPending lipgloss.Style
258+ IconSuccess lipgloss.Style
259+ IconError lipgloss.Style
260+ IconCancelled lipgloss.Style
261261
262262 // Tool name styles
263− NameNormal lipgloss.Style // Normal tool name
264− NameNested lipgloss.Style // Nested tool name
263+ NameNormal lipgloss.Style // Top-level tool name
264+ NameNested lipgloss.Style // Nested child tool name (inside Agent/Agentic Fetch)
265265
266266 // Parameter list styles
267− ParamMain lipgloss.Style // Main parameter
268− ParamKey lipgloss.Style // Parameter keys
267+ ParamMain lipgloss.Style
268+ ParamKey lipgloss.Style
269269
270270 // Content rendering styles
271271 ContentLine lipgloss.Style // Individual content line with background and width
@@ -1119,7 +1119,7 @@ func DefaultStyles() Styles {
11191119 s.Tool.IconCancelled = s.Muted.SetString(ToolPending)
11201120
11211121 s.Tool.NameNormal = base.Foreground(blue)
1122− s.Tool.NameNested = base.Foreground(fgHalfMuted)
1122+ s.Tool.NameNested = base.Foreground(blue)
11231123
11241124 s.Tool.ParamMain = s.Subtle
11251125 s.Tool.ParamKey = s.Subtle