Tool call UI refinement

This commit is contained in:
Danilo Leal 2025-08-26 11:36:39 -03:00
parent 86cbf63b6f
commit ba1fd139f8

View file

@ -1705,7 +1705,6 @@ impl AcpThreadView {
window: &Window, 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 card_header_id = SharedString::from("inner-tool-call-header");
let tool_icon = let tool_icon =
@ -1734,11 +1733,7 @@ impl AcpThreadView {
_ => false, _ => false,
}; };
let failed_tool_call = matches!( let has_location = tool_call.locations.len() == 1;
tool_call.status,
ToolCallStatus::Rejected | ToolCallStatus::Canceled | ToolCallStatus::Failed
);
let needs_confirmation = matches!( let needs_confirmation = matches!(
tool_call.status, tool_call.status,
ToolCallStatus::WaitingForConfirmation { .. } ToolCallStatus::WaitingForConfirmation { .. }
@ -1751,23 +1746,31 @@ impl AcpThreadView {
let is_open = needs_confirmation || self.expanded_tool_calls.contains(&tool_call.id); let is_open = needs_confirmation || self.expanded_tool_calls.contains(&tool_call.id);
let gradient_overlay = |color: Hsla| { let gradient_overlay = {
div() div()
.absolute() .absolute()
.top_0() .top_0()
.right_0() .right_0()
.w_12() .w_12()
.h_full() .h_full()
.bg(linear_gradient( .map(|this| {
90., if use_card_layout {
linear_color_stop(color, 1.), this.bg(linear_gradient(
linear_color_stop(color.opacity(0.2), 0.), 90.,
)) linear_color_stop(self.tool_card_header_bg(cx), 1.),
}; linear_color_stop(self.tool_card_header_bg(cx).opacity(0.2), 0.),
let gradient_color = if use_card_layout { ))
self.tool_card_header_bg(cx) } else {
} else { this.bg(linear_gradient(
cx.theme().colors().panel_background 90.,
linear_color_stop(cx.theme().colors().panel_background, 1.),
linear_color_stop(
cx.theme().colors().panel_background.opacity(0.2),
0.,
),
))
}
})
}; };
let tool_output_display = if is_open { let tool_output_display = if is_open {
@ -1818,41 +1821,58 @@ impl AcpThreadView {
}; };
v_flex() v_flex()
.when(use_card_layout, |this| { .map(|this| {
this.rounded_md() if use_card_layout {
.border_1() this.my_2()
.border_color(self.tool_card_border_color(cx)) .rounded_md()
.bg(cx.theme().colors().editor_background) .border_1()
.overflow_hidden() .border_color(self.tool_card_border_color(cx))
.bg(cx.theme().colors().editor_background)
.overflow_hidden()
} else {
this.my_1()
}
}) })
.map(|this| {
if has_location && !use_card_layout {
this.ml_4()
} else {
this.ml_5()
}
})
.mr_5()
.child( .child(
h_flex() h_flex()
.id(header_id)
.group(&card_header_id) .group(&card_header_id)
.relative() .relative()
.w_full() .w_full()
.max_w_full()
.gap_1() .gap_1()
.justify_between()
.when(use_card_layout, |this| { .when(use_card_layout, |this| {
this.pl_1p5() this.p_0p5()
.pr_1()
.py_0p5()
.rounded_t_md() .rounded_t_md()
.when(is_open && !failed_tool_call, |this| { .bg(self.tool_card_header_bg(cx))
.when(is_open && !failed_or_canceled, |this| {
this.border_b_1() this.border_b_1()
.border_color(self.tool_card_border_color(cx)) .border_color(self.tool_card_border_color(cx))
}) })
.bg(self.tool_card_header_bg(cx))
}) })
.child( .child(
h_flex() h_flex()
.relative() .relative()
.w_full() .w_full()
.h(window.line_height() - px(2.)) .h(window.line_height())
.text_size(self.tool_name_font_size()) .text_size(self.tool_name_font_size())
.gap_0p5() .gap_1p5()
.when(has_location || use_card_layout, |this| this.px_1())
.when(has_location, |this| {
this.cursor(CursorStyle::PointingHand)
.rounded_sm()
.hover(|s| s.bg(cx.theme().colors().element_hover.opacity(0.5)))
})
.overflow_hidden()
.child(tool_icon) .child(tool_icon)
.child(if tool_call.locations.len() == 1 { .child(if has_location {
let name = tool_call.locations[0] let name = tool_call.locations[0]
.path .path
.file_name() .file_name()
@ -1863,13 +1883,6 @@ impl AcpThreadView {
h_flex() h_flex()
.id(("open-tool-call-location", entry_ix)) .id(("open-tool-call-location", entry_ix))
.w_full() .w_full()
.max_w_full()
.px_1p5()
.rounded_sm()
.overflow_x_scroll()
.hover(|label| {
label.bg(cx.theme().colors().element_hover.opacity(0.5))
})
.map(|this| { .map(|this| {
if use_card_layout { if use_card_layout {
this.text_color(cx.theme().colors().text) this.text_color(cx.theme().colors().text)
@ -1879,31 +1892,28 @@ 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()
.relative()
.w_full() .w_full()
.max_w_full() .child(self.render_markdown(
.ml_1p5()
.overflow_hidden()
.child(h_flex().pr_8().child(self.render_markdown(
tool_call.label.clone(), tool_call.label.clone(),
default_markdown_style(false, true, window, cx), default_markdown_style(false, true, window, cx),
))) ))
.child(gradient_overlay(gradient_color))
.into_any() .into_any()
}), })
.when(!has_location, |this| this.child(gradient_overlay)),
) )
.child( .when(is_collapsible || failed_or_canceled, |this| {
h_flex() this.child(
.gap_px() h_flex()
.when(is_collapsible, |this| { .px_1()
this.child( .gap_px()
.when(is_collapsible, |this| {
this.child(
Disclosure::new(("expand", entry_ix), is_open) Disclosure::new(("expand", entry_ix), is_open)
.opened_icon(IconName::ChevronUp) .opened_icon(IconName::ChevronUp)
.closed_icon(IconName::ChevronDown) .closed_icon(IconName::ChevronDown)
@ -1920,15 +1930,16 @@ impl AcpThreadView {
} }
})), })),
) )
}) })
.when(failed_or_canceled, |this| { .when(failed_or_canceled, |this| {
this.child( this.child(
Icon::new(IconName::Close) Icon::new(IconName::Close)
.color(Color::Error) .color(Color::Error)
.size(IconSize::Small), .size(IconSize::Small),
) )
}), }),
), )
}),
) )
.children(tool_output_display) .children(tool_output_display)
} }