From 8550b27c4a96ed79febe84b5a2009ba6bfdb26fb Mon Sep 17 00:00:00 2001
From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Date: Fri, 22 Aug 2025 09:52:44 -0300
Subject: [PATCH] thread view: Add more UI improvements (#36750)
Release Notes:
- N/A
---
assets/icons/attach.svg | 3 ++
assets/icons/tool_think.svg | 2 +-
crates/agent_servers/src/claude.rs | 2 +-
crates/agent_servers/src/gemini.rs | 2 +-
crates/agent_ui/src/acp/thread_view.rs | 68 +++++++++-----------------
crates/agent_ui/src/agent_panel.rs | 5 ++
crates/icons/src/icons.rs | 1 +
7 files changed, 36 insertions(+), 47 deletions(-)
create mode 100644 assets/icons/attach.svg
diff --git a/assets/icons/attach.svg b/assets/icons/attach.svg
new file mode 100644
index 0000000000..f923a3c7c8
--- /dev/null
+++ b/assets/icons/attach.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/tool_think.svg b/assets/icons/tool_think.svg
index efd5908a90..773f5e7fa7 100644
--- a/assets/icons/tool_think.svg
+++ b/assets/icons/tool_think.svg
@@ -1,3 +1,3 @@
diff --git a/crates/agent_servers/src/claude.rs b/crates/agent_servers/src/claude.rs
index d6ccabb130..ef666974f1 100644
--- a/crates/agent_servers/src/claude.rs
+++ b/crates/agent_servers/src/claude.rs
@@ -44,7 +44,7 @@ pub struct ClaudeCode;
impl AgentServer for ClaudeCode {
fn name(&self) -> &'static str {
- "Welcome to Claude Code"
+ "Claude Code"
}
fn empty_state_headline(&self) -> &'static str {
diff --git a/crates/agent_servers/src/gemini.rs b/crates/agent_servers/src/gemini.rs
index 3b892e7931..29120fff6e 100644
--- a/crates/agent_servers/src/gemini.rs
+++ b/crates/agent_servers/src/gemini.rs
@@ -23,7 +23,7 @@ impl AgentServer for Gemini {
}
fn empty_state_headline(&self) -> &'static str {
- "Welcome to Gemini CLI"
+ self.name()
}
fn empty_state_message(&self) -> &'static str {
diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs
index 619885144a..d27dee1fe6 100644
--- a/crates/agent_ui/src/acp/thread_view.rs
+++ b/crates/agent_ui/src/acp/thread_view.rs
@@ -1697,7 +1697,7 @@ impl AcpThreadView {
.absolute()
.top_0()
.right_0()
- .w_12()
+ .w_16()
.h_full()
.bg(linear_gradient(
90.,
@@ -1837,6 +1837,7 @@ impl AcpThreadView {
.w_full()
.max_w_full()
.ml_1p5()
+ .overflow_hidden()
.child(h_flex().pr_8().child(self.render_markdown(
tool_call.label.clone(),
default_markdown_style(false, true, window, cx),
@@ -1906,13 +1907,10 @@ impl AcpThreadView {
.text_color(cx.theme().colors().text_muted)
.child(self.render_markdown(markdown, default_markdown_style(false, false, window, cx)))
.child(
- Button::new(button_id, "Collapse")
+ IconButton::new(button_id, IconName::ChevronUp)
.full_width()
.style(ButtonStyle::Outlined)
- .label_size(LabelSize::Small)
- .icon(IconName::ChevronUp)
.icon_color(Color::Muted)
- .icon_position(IconPosition::Start)
.on_click(cx.listener({
move |this: &mut Self, _, _, cx: &mut Context| {
this.expanded_tool_calls.remove(&tool_call_id);
@@ -2414,39 +2412,32 @@ impl AcpThreadView {
return None;
}
+ let has_both = user_rules_text.is_some() && rules_file_text.is_some();
+
Some(
- v_flex()
+ h_flex()
.px_2p5()
- .gap_1()
+ .pb_1()
+ .child(
+ Icon::new(IconName::Attach)
+ .size(IconSize::XSmall)
+ .color(Color::Disabled),
+ )
.when_some(user_rules_text, |parent, user_rules_text| {
parent.child(
h_flex()
- .group("user-rules")
.id("user-rules")
- .w_full()
- .child(
- Icon::new(IconName::Reader)
- .size(IconSize::XSmall)
- .color(Color::Disabled),
- )
+ .ml_1()
+ .mr_1p5()
.child(
Label::new(user_rules_text)
.size(LabelSize::XSmall)
.color(Color::Muted)
.truncate()
- .buffer_font(cx)
- .ml_1p5()
- .mr_0p5(),
- )
- .child(
- IconButton::new("open-prompt-library", IconName::ArrowUpRight)
- .shape(ui::IconButtonShape::Square)
- .icon_size(IconSize::XSmall)
- .icon_color(Color::Ignored)
- .visible_on_hover("user-rules")
- // TODO: Figure out a way to pass focus handle here so we can display the `OpenRulesLibrary` keybinding
- .tooltip(Tooltip::text("View User Rules")),
+ .buffer_font(cx),
)
+ .hover(|s| s.bg(cx.theme().colors().element_hover))
+ .tooltip(Tooltip::text("View User Rules"))
.on_click(move |_event, window, cx| {
window.dispatch_action(
Box::new(OpenRulesLibrary {
@@ -2457,33 +2448,20 @@ impl AcpThreadView {
}),
)
})
+ .when(has_both, |this| this.child(Divider::vertical()))
.when_some(rules_file_text, |parent, rules_file_text| {
parent.child(
h_flex()
- .group("project-rules")
.id("project-rules")
- .w_full()
- .child(
- Icon::new(IconName::Reader)
- .size(IconSize::XSmall)
- .color(Color::Disabled),
- )
+ .ml_1p5()
.child(
Label::new(rules_file_text)
.size(LabelSize::XSmall)
.color(Color::Muted)
- .buffer_font(cx)
- .ml_1p5()
- .mr_0p5(),
- )
- .child(
- IconButton::new("open-rule", IconName::ArrowUpRight)
- .shape(ui::IconButtonShape::Square)
- .icon_size(IconSize::XSmall)
- .icon_color(Color::Ignored)
- .visible_on_hover("project-rules")
- .tooltip(Tooltip::text("View Project Rules")),
+ .buffer_font(cx),
)
+ .hover(|s| s.bg(cx.theme().colors().element_hover))
+ .tooltip(Tooltip::text("View Project Rules"))
.on_click(cx.listener(Self::handle_open_rules)),
)
})
@@ -4080,8 +4058,10 @@ impl AcpThreadView {
.group("thread-controls-container")
.w_full()
.mr_1()
+ .pt_1()
.pb_2()
.px(RESPONSE_PADDING_X)
+ .gap_px()
.opacity(0.4)
.hover(|style| style.opacity(1.))
.flex_wrap()
diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs
index d2ff6aa4f3..469898d10f 100644
--- a/crates/agent_ui/src/agent_panel.rs
+++ b/crates/agent_ui/src/agent_panel.rs
@@ -2041,9 +2041,11 @@ impl AgentPanel {
match state {
ThreadSummary::Pending => Label::new(ThreadSummary::DEFAULT)
.truncate()
+ .color(Color::Muted)
.into_any_element(),
ThreadSummary::Generating => Label::new(LOADING_SUMMARY_PLACEHOLDER)
.truncate()
+ .color(Color::Muted)
.into_any_element(),
ThreadSummary::Ready(_) => div()
.w_full()
@@ -2098,6 +2100,7 @@ impl AgentPanel {
.into_any_element()
} else {
Label::new(thread_view.read(cx).title(cx))
+ .color(Color::Muted)
.truncate()
.into_any_element()
}
@@ -2111,6 +2114,7 @@ impl AgentPanel {
match summary {
ContextSummary::Pending => Label::new(ContextSummary::DEFAULT)
+ .color(Color::Muted)
.truncate()
.into_any_element(),
ContextSummary::Content(summary) => {
@@ -2122,6 +2126,7 @@ impl AgentPanel {
} else {
Label::new(LOADING_SUMMARY_PLACEHOLDER)
.truncate()
+ .color(Color::Muted)
.into_any_element()
}
}
diff --git a/crates/icons/src/icons.rs b/crates/icons/src/icons.rs
index b5f891713a..4fc6039fd7 100644
--- a/crates/icons/src/icons.rs
+++ b/crates/icons/src/icons.rs
@@ -34,6 +34,7 @@ pub enum IconName {
ArrowRightLeft,
ArrowUp,
ArrowUpRight,
+ Attach,
AudioOff,
AudioOn,
Backspace,