@@ -3,6 +3,7 @@ package messages
33import (
44 "encoding/json"
55 "fmt"
6+ "os"
67 "strings"
78 "time"
89
@@ -95,7 +96,7 @@ func (br baseRenderer) renderWithParams(v *toolCallCmp, toolName string, args []
9596 if v .isNested {
9697 width -= 4 // Adjust for nested tool call indentation
9798 }
98- header := makeHeader (toolName , width , args ... )
99+ header := br . makeHeader (v , toolName , width , args ... )
99100 if v .isNested {
100101 return v .style ().Render (header )
101102 }
@@ -111,6 +112,32 @@ func (br baseRenderer) unmarshalParams(input string, target any) error {
111112 return json .Unmarshal ([]byte (input ), target )
112113}
113114
115+ // makeHeader builds "<Tool>: param (key=value)" and truncates as needed.
116+ func (br baseRenderer ) makeHeader (v * toolCallCmp , tool string , width int , params ... string ) string {
117+ t := styles .CurrentTheme ()
118+ icon := t .S ().Base .Foreground (t .GreenDark ).Render (styles .ToolPending )
119+ if v .result .ToolCallID != "" {
120+ if v .result .IsError {
121+ icon = t .S ().Base .Foreground (t .RedDark ).Render (styles .ToolError )
122+ } else {
123+ icon = t .S ().Base .Foreground (t .Green ).Render (styles .ToolSuccess )
124+ }
125+ } else if v .cancelled {
126+ icon = t .S ().Muted .Render (styles .ToolPending )
127+ }
128+ tool = t .S ().Base .Foreground (t .Blue ).Render (tool )
129+ prefix := fmt .Sprintf ("%s %s: " , icon , tool )
130+ return prefix + renderParamList (width - lipgloss .Width (prefix ), params ... )
131+ }
132+
133+ // renderError provides consistent error rendering
134+ func (br baseRenderer ) renderError (v * toolCallCmp , message string ) string {
135+ t := styles .CurrentTheme ()
136+ header := br .makeHeader (v , prettifyToolName (v .call .Name ), v .textWidth (), "" )
137+ message = t .S ().Error .Render (v .fit (message , v .textWidth ()- 2 )) // -2 for padding
138+ return joinHeaderBody (header , message )
139+ }
140+
114141// Register tool renderers
115142func init () {
116143 registry .register (tools .BashToolName , func () renderer { return bashRenderer {} })
@@ -167,12 +194,6 @@ func (br bashRenderer) Render(v *toolCallCmp) string {
167194 })
168195}
169196
170- // renderError provides consistent error rendering
171- func (br baseRenderer ) renderError (v * toolCallCmp , message string ) string {
172- header := makeHeader ("Error" , v .textWidth (), message )
173- return joinHeaderBody (header , "" )
174- }
175-
176197// -----------------------------------------------------------------------------
177198// View renderer
178199// -----------------------------------------------------------------------------
@@ -189,7 +210,7 @@ func (vr viewRenderer) Render(v *toolCallCmp) string {
189210 return vr .renderError (v , "Invalid view parameters" )
190211 }
191212
192- file := removeWorkingDirPrefix (params .FilePath )
213+ file := prettyPath (params .FilePath )
193214 args := newParamBuilder ().
194215 addMain (file ).
195216 addKeyValue ("limit" , formatNonZero (params .Limit )).
@@ -229,7 +250,7 @@ func (er editRenderer) Render(v *toolCallCmp) string {
229250 return er .renderError (v , "Invalid edit parameters" )
230251 }
231252
232- file := removeWorkingDirPrefix (params .FilePath )
253+ file := prettyPath (params .FilePath )
233254 args := newParamBuilder ().addMain (file ).build ()
234255
235256 return er .renderWithParams (v , "Edit" , args , func () string {
@@ -239,7 +260,7 @@ func (er editRenderer) Render(v *toolCallCmp) string {
239260 }
240261
241262 trunc := truncateHeight (meta .Diff , responseContextHeight )
242- diffView , _ := diff .FormatDiff (trunc , diff .WithTotalWidth (v .textWidth ()))
263+ diffView , _ := diff .FormatDiff (trunc , diff .WithTotalWidth (v .textWidth ()- 2 ))
243264 return diffView
244265 })
245266}
@@ -260,7 +281,7 @@ func (wr writeRenderer) Render(v *toolCallCmp) string {
260281 return wr .renderError (v , "Invalid write parameters" )
261282 }
262283
263- file := removeWorkingDirPrefix (params .FilePath )
284+ file := prettyPath (params .FilePath )
264285 args := newParamBuilder ().addMain (file ).build ()
265286
266287 return wr .renderWithParams (v , "Write" , args , func () string {
@@ -494,7 +515,7 @@ func (tr agentRenderer) Render(v *toolCallCmp) string {
494515 prompt = strings .ReplaceAll (prompt , "\n " , " " )
495516 args := newParamBuilder ().addMain (prompt ).build ()
496517
497- header := makeHeader ("Task" , v .textWidth (), args ... )
518+ header := tr . makeHeader (v , "Task" , v .textWidth (), args ... )
498519 t := tree .Root (header )
499520
500521 for _ , call := range v .nestedToolCalls {
@@ -524,12 +545,6 @@ func (tr agentRenderer) Render(v *toolCallCmp) string {
524545 return joinHeaderBody (header , body )
525546}
526547
527- // makeHeader builds "<Tool>: param (key=value)" and truncates as needed.
528- func makeHeader (tool string , width int , params ... string ) string {
529- prefix := tool + ": "
530- return prefix + renderParamList (width - lipgloss .Width (prefix ), params ... )
531- }
532-
533548// renderParamList renders params, params[0] (params[1]=params[2] ....)
534549func renderParamList (paramsWidth int , params ... string ) string {
535550 if len (params ) == 0 {
@@ -575,38 +590,46 @@ func renderParamList(paramsWidth int, params ...string) string {
575590
576591// earlyState returns immediately‑rendered error/cancelled/ongoing states.
577592func earlyState (header string , v * toolCallCmp ) (string , bool ) {
593+ t := styles .CurrentTheme ()
594+ message := ""
578595 switch {
579596 case v .result .IsError :
580- return lipgloss . JoinVertical ( lipgloss . Left , header , v .renderToolError ()), true
597+ message = v .renderToolError ()
581598 case v .cancelled :
582- return lipgloss . JoinVertical ( lipgloss . Left , header , "Cancelled" ), true
599+ message = "Cancelled"
583600 case v .result .ToolCallID == "" :
584- return lipgloss . JoinVertical ( lipgloss . Left , header , "Waiting for tool to finish ..." ), true
601+ message = "Waiting for tool to start ..."
585602 default :
586603 return "" , false
587604 }
605+
606+ message = t .S ().Base .PaddingLeft (2 ).Render (message )
607+ return lipgloss .JoinVertical (lipgloss .Left , header , message ), true
588608}
589609
590610func joinHeaderBody (header , body string ) string {
591- return lipgloss .JoinVertical (lipgloss .Left , header , "" , body , "" )
611+ t := styles .CurrentTheme ()
612+ body = t .S ().Base .PaddingLeft (2 ).Render (body )
613+ return lipgloss .JoinVertical (lipgloss .Left , header , body , "" )
592614}
593615
594616func renderPlainContent (v * toolCallCmp , content string ) string {
595617 t := styles .CurrentTheme ()
596618 content = strings .TrimSpace (content )
597619 lines := strings .Split (content , "\n " )
598620
621+ width := v .textWidth () - 2 // -2 for left padding
599622 var out []string
600623 for i , ln := range lines {
601624 if i >= responseContextHeight {
602625 break
603626 }
604627 ln = " " + ln // left padding
605- if len (ln ) > v . textWidth () {
606- ln = v .fit (ln , v . textWidth () )
628+ if len (ln ) > width {
629+ ln = v .fit (ln , width )
607630 }
608631 out = append (out , t .S ().Muted .
609- Width (v . textWidth () ).
632+ Width (width ).
610633 Background (t .BgSubtle ).
611634 Render (ln ))
612635 }
@@ -638,7 +661,7 @@ func renderCodeContent(v *toolCallCmp, path, content string, offset int) string
638661 PaddingLeft (4 ).
639662 PaddingRight (2 ).
640663 Render (fmt .Sprintf ("%d" , i + 1 + offset ))
641- w := v .textWidth () - lipgloss .Width (num )
664+ w := v .textWidth () - 2 - lipgloss .Width (num ) // -2 for left padding
642665 lines [i ] = lipgloss .JoinHorizontal (lipgloss .Left ,
643666 num ,
644667 t .S ().Base .
@@ -669,6 +692,15 @@ func truncateHeight(s string, h int) string {
669692 return s
670693}
671694
695+ func prettyPath (path string ) string {
696+ // replace home directory with ~
697+ homeDir , err := os .UserHomeDir ()
698+ if err == nil {
699+ path = strings .ReplaceAll (path , homeDir , "~" )
700+ }
701+ return path
702+ }
703+
672704func prettifyToolName (name string ) string {
673705 switch name {
674706 case agent .AgentToolName :
0 commit comments