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| {