diff --git a/assets/icons/focus.svg b/assets/icons/focus.svg
deleted file mode 100644
index 9003e437cee1afa43e87fa273c9510284bb5ae0b..0000000000000000000000000000000000000000
--- a/assets/icons/focus.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/crates/icons/src/icons.rs b/crates/icons/src/icons.rs
index 89932125c1bfbc05202038c1abac2a6380e19e93..acec738030ab2d36c2aebaaf45cf127e41bf385f 100644
--- a/crates/icons/src/icons.rs
+++ b/crates/icons/src/icons.rs
@@ -131,7 +131,6 @@ pub enum IconName {
FileTree,
Filter,
Flame,
- Focus,
Folder,
FolderOpen,
FolderPlus,
diff --git a/crates/sidebar/src/sidebar.rs b/crates/sidebar/src/sidebar.rs
index a0f6e88c2b3a6238c89d1d57aa684923d81bc473..31c796b200cc84d062cad6f404dd7ec4736453a4 100644
--- a/crates/sidebar/src/sidebar.rs
+++ b/crates/sidebar/src/sidebar.rs
@@ -1281,13 +1281,14 @@ impl Sidebar {
) -> AnyElement {
let id_prefix = if is_sticky { "sticky-" } else { "" };
let id = SharedString::from(format!("{id_prefix}project-header-{ix}"));
+ let disclosure_id = SharedString::from(format!("disclosure-{ix}"));
let group_name = SharedString::from(format!("{id_prefix}header-group-{ix}"));
let is_collapsed = self.collapsed_groups.contains(path_list);
- let disclosure_icon = if is_collapsed {
- IconName::ChevronRight
+ let (disclosure_icon, disclosure_tooltip) = if is_collapsed {
+ (IconName::ChevronRight, "Expand Project")
} else {
- IconName::ChevronDown
+ (IconName::ChevronDown, "Collapse Project")
};
let has_new_thread_entry = self
@@ -1325,8 +1326,8 @@ impl Sidebar {
.group(&group_name)
.h(Tab::content_height(cx))
.w_full()
- .pl_1p5()
- .pr_1()
+ .pl(px(5.))
+ .pr_1p5()
.border_1()
.map(|this| {
if is_selected {
@@ -1339,16 +1340,21 @@ impl Sidebar {
.hover(|s| s.bg(hover_color))
.child(
h_flex()
+ .when(!is_active, |this| this.cursor_pointer())
.relative()
.min_w_0()
.w_full()
- .gap_1p5()
+ .gap(px(5.))
.child(
- h_flex().size_4().flex_none().justify_center().child(
- Icon::new(disclosure_icon)
- .size(IconSize::Small)
- .color(Color::Custom(cx.theme().colors().icon_muted.opacity(0.5))),
- ),
+ IconButton::new(disclosure_id, disclosure_icon)
+ .shape(ui::IconButtonShape::Square)
+ .icon_size(IconSize::Small)
+ .icon_color(Color::Custom(cx.theme().colors().icon_muted.opacity(0.5)))
+ .tooltip(Tooltip::text(disclosure_tooltip))
+ .on_click(cx.listener(move |this, _, window, cx| {
+ this.selection = None;
+ this.toggle_collapse(&path_list_for_toggle, window, cx);
+ })),
)
.child(label)
.when_some(
@@ -1425,39 +1431,6 @@ impl Sidebar {
})),
)
})
- .when(!is_active, |this| {
- this.child(
- IconButton::new(
- SharedString::from(format!(
- "{id_prefix}project-header-open-workspace-{ix}",
- )),
- IconName::Focus,
- )
- .icon_size(IconSize::Small)
- .icon_color(Color::Muted)
- .tooltip(Tooltip::text("Activate Workspace"))
- .on_click(cx.listener({
- move |this, _, window, cx| {
- this.active_entry =
- Some(ActiveEntry::Draft(workspace_for_open.clone()));
- if let Some(multi_workspace) = this.multi_workspace.upgrade() {
- multi_workspace.update(cx, |multi_workspace, cx| {
- multi_workspace.activate(
- workspace_for_open.clone(),
- window,
- cx,
- );
- });
- }
- if AgentPanel::is_visible(&workspace_for_open, cx) {
- workspace_for_open.update(cx, |workspace, cx| {
- workspace.focus_panel::(window, cx);
- });
- }
- }
- })),
- )
- })
.when(show_new_thread_button, |this| {
this.child(
IconButton::new(
@@ -1483,10 +1456,29 @@ impl Sidebar {
)
})
})
- .on_click(cx.listener(move |this, _, window, cx| {
- this.selection = None;
- this.toggle_collapse(&path_list_for_toggle, window, cx);
- }))
+ .when(!is_active, |this| {
+ this.tooltip(Tooltip::text("Activate Workspace"))
+ .on_click(cx.listener({
+ move |this, _, window, cx| {
+ this.active_entry =
+ Some(ActiveEntry::Draft(workspace_for_open.clone()));
+ if let Some(multi_workspace) = this.multi_workspace.upgrade() {
+ multi_workspace.update(cx, |multi_workspace, cx| {
+ multi_workspace.activate(
+ workspace_for_open.clone(),
+ window,
+ cx,
+ );
+ });
+ }
+ if AgentPanel::is_visible(&workspace_for_open, cx) {
+ workspace_for_open.update(cx, |workspace, cx| {
+ workspace.focus_panel::(window, cx);
+ });
+ }
+ }
+ }))
+ })
.into_any_element()
}
@@ -2779,6 +2771,11 @@ impl Sidebar {
let id = SharedString::from(format!("thread-entry-{}", ix));
+ let color = cx.theme().colors();
+ let sidebar_bg = color
+ .title_bar_background
+ .blend(color.panel_background.opacity(0.32));
+
let timestamp = format_history_entry_timestamp(
self.thread_last_message_sent_or_queued
.get(&thread.metadata.session_id)
@@ -2788,6 +2785,7 @@ impl Sidebar {
);
ThreadItem::new(id, title)
+ .base_bg(sidebar_bg)
.icon(thread.icon)
.status(thread.status)
.when_some(thread.icon_from_external_svg.clone(), |this, svg| {
diff --git a/crates/ui/src/components/ai/thread_item.rs b/crates/ui/src/components/ai/thread_item.rs
index e03dce5fe2e5ce9e52ca01da72e69605abfff765..d6b5f56e0abb33521ae69acc0b61b36b015cf987 100644
--- a/crates/ui/src/components/ai/thread_item.rs
+++ b/crates/ui/src/components/ai/thread_item.rs
@@ -218,21 +218,23 @@ impl RenderOnce for ThreadItem {
let color = cx.theme().colors();
let sidebar_base_bg = color
.title_bar_background
- .blend(color.panel_background.opacity(0.2));
+ .blend(color.panel_background.opacity(0.32));
- let base_bg = self.base_bg.unwrap_or(sidebar_base_bg);
+ let raw_bg = self.base_bg.unwrap_or(sidebar_base_bg);
+ let apparent_bg = color.background.blend(raw_bg);
let base_bg = if self.selected {
- color.element_active
+ apparent_bg.blend(color.element_active)
} else {
- base_bg
+ apparent_bg
};
let hover_color = color
.element_active
.blend(color.element_background.opacity(0.2));
+ let hover_bg = apparent_bg.blend(hover_color);
- let gradient_overlay = GradientFade::new(base_bg, hover_color, hover_color)
+ let gradient_overlay = GradientFade::new(base_bg, hover_bg, hover_bg)
.width(px(64.0))
.right(px(-10.0))
.gradient_stop(0.75)
@@ -399,7 +401,7 @@ impl RenderOnce for ThreadItem {
.child(gradient_overlay)
.when(self.hovered, |this| {
this.when_some(self.action_slot, |this, slot| {
- let overlay = GradientFade::new(base_bg, hover_color, hover_color)
+ let overlay = GradientFade::new(base_bg, hover_bg, hover_bg)
.width(px(64.0))
.right(px(6.))
.gradient_stop(0.75)
@@ -432,6 +434,7 @@ impl RenderOnce for ThreadItem {
.collect::>()
.join("\n")
.into();
+
let worktree_tooltip_title = if self.worktrees.len() > 1 {
"Thread Running in Local Git Worktrees"
} else {
diff --git a/crates/ui/src/components/gradient_fade.rs b/crates/ui/src/components/gradient_fade.rs
index 2173fdf06ea8c07c947f092066c2a12d716d4b44..8a982695ecea7cef9abcf9de2db7ba550971eb8a 100644
--- a/crates/ui/src/components/gradient_fade.rs
+++ b/crates/ui/src/components/gradient_fade.rs
@@ -49,10 +49,14 @@ impl GradientFade {
}
impl RenderOnce for GradientFade {
- fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
+ fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let stop = self.gradient_stop;
- let hover_bg = self.hover_bg;
- let active_bg = self.active_bg;
+
+ // Best-effort to flatten potentially-transparent colors to opaque ones.
+ let app_bg = cx.theme().colors().background;
+ let base_bg = app_bg.blend(self.base_bg);
+ let hover_bg = app_bg.blend(self.hover_bg);
+ let active_bg = app_bg.blend(self.active_bg);
div()
.id("gradient_fade")
@@ -63,8 +67,8 @@ impl RenderOnce for GradientFade {
.h_full()
.bg(linear_gradient(
90.,
- linear_color_stop(self.base_bg, stop),
- linear_color_stop(self.base_bg.opacity(0.0), 0.),
+ linear_color_stop(base_bg, stop),
+ linear_color_stop(base_bg.opacity(0.0), 0.),
))
.when_some(self.group_name.clone(), |element, group_name| {
element.group_hover(group_name, move |s| {