Skip to content

Commit e23160e

Browse files
authored
Merge pull request #1422 from Pnkcaht/fix/1366-editor-scroll-after-failed-edit
tui(edit_file): skip diff rendering on tool failure
2 parents 500fe59 + adcdd1b commit e23160e

1 file changed

Lines changed: 63 additions & 12 deletions

File tree

pkg/tui/components/tool/editfile/editfile.go

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,88 @@ import (
1515

1616
type ToggleDiffViewMsg struct{}
1717

18+
// New creates the edit_file tool UI model.
1819
func New(msg *types.Message, sessionState *service.SessionState) layout.Model {
1920
return toolcommon.NewBase(msg, sessionState, render)
2021
}
2122

22-
func render(msg *types.Message, s spinner.Spinner, sessionState *service.SessionState, width, _ int) string {
23+
// render displays the edit_file tool output in the TUI.
24+
// It prioritizes the agent-provided friendly header when available,
25+
// hides results when collapsed by the user, and renders tool errors
26+
// in a single-line error style consistent with other tools.
27+
func render(
28+
msg *types.Message,
29+
s spinner.Spinner,
30+
sessionState *service.SessionState,
31+
width,
32+
_ int,
33+
) string {
34+
// Parse tool arguments to extract the file path for display.
2335
var args builtin.EditFileArgs
2436
if err := json.Unmarshal([]byte(msg.ToolCall.Function.Arguments), &args); err != nil {
37+
// If arguments cannot be parsed, fail silently to avoid breaking the TUI.
2538
return ""
2639
}
2740

41+
// When the tool failed, render a single-line error header
42+
// consistent with other tool error renderings.
43+
if msg.ToolStatus == types.ToolStatusError {
44+
if msg.Content == "" {
45+
return ""
46+
}
47+
48+
// Render everything on a single line:
49+
// - error icon
50+
// - tool name in error style
51+
// - rejection/error message
52+
line := fmt.Sprintf(
53+
"%s%s %s",
54+
toolcommon.Icon(msg, s),
55+
styles.ToolNameError.Render(msg.ToolDefinition.DisplayName()),
56+
styles.ToolErrorMessageStyle.Render(msg.Content),
57+
)
58+
59+
// Truncate to terminal width to avoid wrapping
60+
return styles.BaseStyle.
61+
MaxWidth(width).
62+
Render(line)
63+
}
64+
65+
// ---- Normal (non-error) rendering ----
66+
2867
// Check for friendly description first
2968
var content string
3069
if header, ok := toolcommon.RenderFriendlyHeader(msg, s); ok {
3170
content = header
3271
} else {
33-
content = fmt.Sprintf("%s%s %s",
72+
content = fmt.Sprintf(
73+
"%s%s %s",
3474
toolcommon.Icon(msg, s),
3575
styles.ToolName.Render(msg.ToolDefinition.DisplayName()),
36-
styles.ToolMessageStyle.Render(toolcommon.ShortenPath(args.Path)))
76+
styles.ToolMessageStyle.Render(toolcommon.ShortenPath(args.Path)),
77+
)
3778
}
3879

39-
if !sessionState.HideToolResults() {
40-
if msg.ToolCall.Function.Arguments != "" {
41-
contentWidth := width - styles.ToolCallResult.GetHorizontalFrameSize()
42-
content += "\n" + styles.ToolCallResult.Render(
43-
renderEditFile(msg.ToolCall, contentWidth, sessionState.SplitDiffView(), msg.ToolStatus))
44-
}
80+
// Tool results are hidden when the user collapses them.
81+
if sessionState.HideToolResults() {
82+
return content
83+
}
4584

46-
if (msg.ToolStatus == types.ToolStatusError) && msg.Content != "" {
47-
content += toolcommon.FormatToolResult(msg.Content, width)
48-
}
85+
// Successful (or pending/confirmation) execution:
86+
// render the diff output inside the ToolCallResult container.
87+
if msg.ToolCall.Function.Arguments != "" {
88+
// Calculate available width for diff rendering, accounting for
89+
// ToolCallResult frame padding.
90+
contentWidth := width - styles.ToolCallResult.GetHorizontalFrameSize()
91+
92+
content += "\n" + styles.ToolCallResult.Render(
93+
renderEditFile(
94+
msg.ToolCall,
95+
contentWidth,
96+
sessionState.SplitDiffView(),
97+
msg.ToolStatus,
98+
),
99+
)
49100
}
50101

51102
return content

0 commit comments

Comments
 (0)