Improve tool call design (change icon for read tool and disclosure btn

position)

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
This commit is contained in:
Danilo Leal 2025-08-25 14:41:07 -03:00
parent 65b1547056
commit 9ce92bcf7d
2 changed files with 40 additions and 102 deletions

View file

@ -1675,15 +1675,16 @@ impl AcpThreadView {
.into_any_element() .into_any_element()
} }
fn render_tool_call_icon( fn render_tool_call(
&self, &self,
group_name: SharedString,
entry_ix: usize, entry_ix: usize,
is_collapsible: bool,
is_open: bool,
tool_call: &ToolCall, tool_call: &ToolCall,
window: &Window,
cx: &Context<Self>, cx: &Context<Self>,
) -> Div { ) -> Div {
let header_id = SharedString::from(format!("outer-tool-call-header-{}", entry_ix));
let card_header_id = SharedString::from("inner-tool-call-header");
let tool_icon = let tool_icon =
if tool_call.kind == acp::ToolKind::Edit && tool_call.locations.len() == 1 { if tool_call.kind == acp::ToolKind::Edit && tool_call.locations.len() == 1 {
FileIcons::get_icon(&tool_call.locations[0].path, cx) FileIcons::get_icon(&tool_call.locations[0].path, cx)
@ -1691,7 +1692,7 @@ impl AcpThreadView {
.unwrap_or(Icon::new(IconName::ToolPencil)) .unwrap_or(Icon::new(IconName::ToolPencil))
} else { } else {
Icon::new(match tool_call.kind { Icon::new(match tool_call.kind {
acp::ToolKind::Read => IconName::ToolRead, acp::ToolKind::Read => IconName::ToolSearch,
acp::ToolKind::Edit => IconName::ToolPencil, acp::ToolKind::Edit => IconName::ToolPencil,
acp::ToolKind::Delete => IconName::ToolDeleteFile, acp::ToolKind::Delete => IconName::ToolDeleteFile,
acp::ToolKind::Move => IconName::ArrowRightLeft, acp::ToolKind::Move => IconName::ArrowRightLeft,
@ -1705,54 +1706,6 @@ impl AcpThreadView {
.size(IconSize::Small) .size(IconSize::Small)
.color(Color::Muted); .color(Color::Muted);
let base_container = h_flex().flex_shrink_0().size_4().justify_center();
if is_collapsible {
base_container
.child(
div()
.group_hover(&group_name, |s| s.invisible().w_0())
.child(tool_icon),
)
.child(
h_flex()
.absolute()
.inset_0()
.invisible()
.justify_center()
.group_hover(&group_name, |s| s.visible())
.child(
Disclosure::new(("expand", entry_ix), is_open)
.opened_icon(IconName::ChevronUp)
.closed_icon(IconName::ChevronRight)
.on_click(cx.listener({
let id = tool_call.id.clone();
move |this: &mut Self, _, _, cx: &mut Context<Self>| {
if is_open {
this.expanded_tool_calls.remove(&id);
} else {
this.expanded_tool_calls.insert(id.clone());
}
cx.notify();
}
})),
),
)
} else {
base_container.child(tool_icon)
}
}
fn render_tool_call(
&self,
entry_ix: usize,
tool_call: &ToolCall,
window: &Window,
cx: &Context<Self>,
) -> Div {
let header_id = SharedString::from(format!("outer-tool-call-header-{}", entry_ix));
let card_header_id = SharedString::from("inner-tool-call-header");
let in_progress = match &tool_call.status { let in_progress = match &tool_call.status {
ToolCallStatus::InProgress => true, ToolCallStatus::InProgress => true,
_ => false, _ => false,
@ -1857,6 +1810,7 @@ impl AcpThreadView {
.child( .child(
h_flex() h_flex()
.id(header_id) .id(header_id)
.group(&card_header_id)
.relative() .relative()
.w_full() .w_full()
.max_w_full() .max_w_full()
@ -1874,19 +1828,11 @@ impl AcpThreadView {
}) })
.child( .child(
h_flex() h_flex()
.group(&card_header_id)
.relative() .relative()
.w_full() .w_full()
.h(window.line_height() - px(2.)) .h(window.line_height() - px(2.))
.text_size(self.tool_name_font_size()) .text_size(self.tool_name_font_size())
.child(self.render_tool_call_icon( .child(tool_icon)
card_header_id,
entry_ix,
is_collapsible,
is_open,
tool_call,
cx,
))
.child(if tool_call.locations.len() == 1 { .child(if tool_call.locations.len() == 1 {
let name = tool_call.locations[0] let name = tool_call.locations[0]
.path .path
@ -1914,13 +1860,13 @@ impl AcpThreadView {
}) })
.child(name) .child(name)
.tooltip(Tooltip::text("Jump to File")) .tooltip(Tooltip::text("Jump to File"))
.cursor(gpui::CursorStyle::PointingHand)
.on_click(cx.listener(move |this, _, window, cx| { .on_click(cx.listener(move |this, _, window, cx| {
this.open_tool_call_location(entry_ix, 0, window, cx); this.open_tool_call_location(entry_ix, 0, window, cx);
})) }))
.into_any_element() .into_any_element()
} else { } else {
h_flex() h_flex()
.id("non-card-label-container")
.relative() .relative()
.w_full() .w_full()
.max_w_full() .max_w_full()
@ -1931,47 +1877,39 @@ impl AcpThreadView {
default_markdown_style(false, true, window, cx), default_markdown_style(false, true, window, cx),
))) )))
.child(gradient_overlay(gradient_color)) .child(gradient_overlay(gradient_color))
.on_click(cx.listener({
let id = tool_call.id.clone();
move |this: &mut Self, _, _, cx: &mut Context<Self>| {
if is_open {
this.expanded_tool_calls.remove(&id);
} else {
this.expanded_tool_calls.insert(id.clone());
}
cx.notify();
}
}))
.into_any() .into_any()
}), }),
) )
.when(in_progress && use_card_layout && !is_open, |this| { .child(
this.child( h_flex()
div().absolute().right_2().child( .gap_px()
Icon::new(IconName::ArrowCircle) .when(is_collapsible, |this| {
.color(Color::Muted) this.child(
.size(IconSize::Small) Disclosure::new(("expand", entry_ix), is_open)
.with_animation( .opened_icon(IconName::ChevronUp)
"running", .closed_icon(IconName::ChevronDown)
Animation::new(Duration::from_secs(3)).repeat(), .visible_on_hover(&card_header_id)
|icon, delta| { .on_click(cx.listener({
icon.transform(Transformation::rotate(percentage( let id = tool_call.id.clone();
delta, move |this: &mut Self, _, _, cx: &mut Context<Self>| {
))) if is_open {
}, this.expanded_tool_calls.remove(&id);
), } else {
), this.expanded_tool_calls.insert(id.clone());
) }
}) cx.notify();
.when(failed_or_canceled, |this| { }
this.child( })),
div().absolute().right_2().child( )
Icon::new(IconName::Close) })
.color(Color::Error) .when(failed_or_canceled, |this| {
.size(IconSize::Small), this.child(
), Icon::new(IconName::Close)
) .color(Color::Error)
}), .size(IconSize::Small),
)
}),
),
) )
.children(tool_output_display) .children(tool_output_display)
} }

View file

@ -68,7 +68,7 @@ impl Tool for ReadFileTool {
} }
fn icon(&self) -> IconName { fn icon(&self) -> IconName {
IconName::ToolRead IconName::ToolSearch
} }
fn input_schema(&self, format: LanguageModelToolSchemaFormat) -> Result<serde_json::Value> { fn input_schema(&self, format: LanguageModelToolSchemaFormat) -> Result<serde_json::Value> {