mirror of
https://github.com/zed-industries/zed.git
synced 2026-05-31 19:05:00 +07:00
agent: Improve sidebar design and behavior (#51763)
- Selection/focus improvements (reduces flickers, move selection more correctly throughout the list) - Adds open folder button in the sidebar header - Fixes sidebar header design, including the thread view one, too - Fixes behavior of cmd-n when focused in the sidebar - Changes the design for the "new thread" button in the sidebar and adds a preview of the prompt on it - Rename items in the "start thread in" dropdown Release Notes: - N/A --------- Co-authored-by: cameron <cameron.studdstreet@gmail.com>
This commit is contained in:
parent
65b80ff689
commit
9f3e3be65f
13 changed files with 915 additions and 549 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -15870,6 +15870,7 @@ dependencies = [
|
|||
"pretty_assertions",
|
||||
"project",
|
||||
"prompt_store",
|
||||
"recent_projects",
|
||||
"serde_json",
|
||||
"settings",
|
||||
"theme",
|
||||
|
|
|
|||
|
|
@ -674,7 +674,7 @@
|
|||
"context": "ThreadsSidebar",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-n": "multi_workspace::NewWorkspaceInWindow",
|
||||
"ctrl-n": "agents_sidebar::NewThreadInGroup",
|
||||
"left": "agents_sidebar::CollapseSelectedEntry",
|
||||
"right": "agents_sidebar::ExpandSelectedEntry",
|
||||
"enter": "menu::Confirm",
|
||||
|
|
|
|||
|
|
@ -742,7 +742,7 @@
|
|||
"context": "ThreadsSidebar",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-n": "multi_workspace::NewWorkspaceInWindow",
|
||||
"cmd-n": "agents_sidebar::NewThreadInGroup",
|
||||
"left": "agents_sidebar::CollapseSelectedEntry",
|
||||
"right": "agents_sidebar::ExpandSelectedEntry",
|
||||
"enter": "menu::Confirm",
|
||||
|
|
|
|||
|
|
@ -678,7 +678,7 @@
|
|||
"context": "ThreadsSidebar",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-n": "multi_workspace::NewWorkspaceInWindow",
|
||||
"ctrl-n": "agents_sidebar::NewThreadInGroup",
|
||||
"left": "agents_sidebar::CollapseSelectedEntry",
|
||||
"right": "agents_sidebar::ExpandSelectedEntry",
|
||||
"enter": "menu::Confirm",
|
||||
|
|
|
|||
|
|
@ -601,8 +601,8 @@ impl From<Agent> for AgentType {
|
|||
impl StartThreadIn {
|
||||
fn label(&self) -> SharedString {
|
||||
match self {
|
||||
Self::LocalProject => "Current Project".into(),
|
||||
Self::NewWorktree => "New Worktree".into(),
|
||||
Self::LocalProject => "Current Worktree".into(),
|
||||
Self::NewWorktree => "New Git Worktree".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1951,6 +1951,21 @@ impl AgentPanel {
|
|||
self.background_threads.contains_key(session_id)
|
||||
}
|
||||
|
||||
pub fn cancel_thread(&self, session_id: &acp::SessionId, cx: &mut Context<Self>) -> bool {
|
||||
let conversation_views = self
|
||||
.active_conversation_view()
|
||||
.into_iter()
|
||||
.chain(self.background_threads.values());
|
||||
|
||||
for conversation_view in conversation_views {
|
||||
if let Some(thread_view) = conversation_view.read(cx).thread_view(session_id) {
|
||||
thread_view.update(cx, |view, cx| view.cancel_generation(cx));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// active thread plus any background threads that are still running or
|
||||
/// completed but unseen.
|
||||
pub fn parent_threads(&self, cx: &App) -> Vec<Entity<ThreadView>> {
|
||||
|
|
@ -3551,7 +3566,7 @@ impl AgentPanel {
|
|||
|
||||
menu.header("Start Thread In…")
|
||||
.item(
|
||||
ContextMenuEntry::new("Current Project")
|
||||
ContextMenuEntry::new("Current Worktree")
|
||||
.toggleable(IconPosition::End, is_local_selected)
|
||||
.documentation_aside(documentation_side, move |_| {
|
||||
HoldForDefault::new(is_local_default)
|
||||
|
|
@ -3579,7 +3594,7 @@ impl AgentPanel {
|
|||
}),
|
||||
)
|
||||
.item({
|
||||
let entry = ContextMenuEntry::new("New Worktree")
|
||||
let entry = ContextMenuEntry::new("New Git Worktree")
|
||||
.toggleable(IconPosition::End, is_new_worktree_selected)
|
||||
.disabled(new_worktree_disabled)
|
||||
.handler({
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use theme::ActiveTheme;
|
|||
use ui::{
|
||||
ButtonLike, CommonAnimationExt, ContextMenu, ContextMenuEntry, HighlightedLabel, ListItem,
|
||||
PopoverMenu, PopoverMenuHandle, Tab, TintColor, Tooltip, WithScrollbar, prelude::*,
|
||||
utils::platform_title_bar_height,
|
||||
};
|
||||
use util::ResultExt as _;
|
||||
use zed_actions::editor::{MoveDown, MoveUp};
|
||||
|
|
@ -676,32 +677,56 @@ impl ThreadsArchiveView {
|
|||
})
|
||||
}
|
||||
|
||||
fn render_header(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
fn render_header(&self, window: &Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let has_query = !self.filter_editor.read(cx).text(cx).is_empty();
|
||||
let traffic_lights = cfg!(target_os = "macos") && !window.is_fullscreen();
|
||||
let header_height = platform_title_bar_height(window);
|
||||
|
||||
h_flex()
|
||||
.h(Tab::container_height(cx))
|
||||
.px_1()
|
||||
.justify_between()
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
v_flex()
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_1()
|
||||
.w_full()
|
||||
.gap_1p5()
|
||||
.h(header_height)
|
||||
.mt_px()
|
||||
.pb_px()
|
||||
.when(traffic_lights, |this| {
|
||||
this.pl(px(ui::utils::TRAFFIC_LIGHT_PADDING))
|
||||
})
|
||||
.pr_1p5()
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.justify_between()
|
||||
.child(
|
||||
IconButton::new("back", IconName::ArrowLeft)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip(Tooltip::text("Back to Sidebar"))
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.go_back(window, cx);
|
||||
})),
|
||||
h_flex()
|
||||
.gap_1p5()
|
||||
.child(
|
||||
IconButton::new("back", IconName::ArrowLeft)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip(Tooltip::text("Back to Sidebar"))
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.go_back(window, cx);
|
||||
})),
|
||||
)
|
||||
.child(Label::new("Threads Archive").size(LabelSize::Small).mb_px()),
|
||||
)
|
||||
.child(self.render_agent_picker(cx)),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.h(Tab::container_height(cx))
|
||||
.p_2()
|
||||
.pr_1p5()
|
||||
.gap_1p5()
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(
|
||||
Icon::new(IconName::MagnifyingGlass)
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(self.filter_editor.clone())
|
||||
.when(has_query, |this| {
|
||||
this.border_r_1().child(
|
||||
IconButton::new("clear_archive_filter", IconName::Close)
|
||||
this.child(
|
||||
IconButton::new("clear_filter", IconName::Close)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip(Tooltip::text("Clear Search"))
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
|
|
@ -711,7 +736,6 @@ impl ThreadsArchiveView {
|
|||
)
|
||||
}),
|
||||
)
|
||||
.child(self.render_agent_picker(cx))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -783,8 +807,7 @@ impl Render for ThreadsArchiveView {
|
|||
.on_action(cx.listener(Self::confirm))
|
||||
.on_action(cx.listener(Self::remove_selected_thread))
|
||||
.size_full()
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
.child(self.render_header(cx))
|
||||
.child(self.render_header(window, cx))
|
||||
.child(content)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ fs.workspace = true
|
|||
gpui.workspace = true
|
||||
menu.workspace = true
|
||||
project.workspace = true
|
||||
recent_projects.workspace = true
|
||||
settings.workspace = true
|
||||
theme.workspace = true
|
||||
ui.workspace = true
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -41,8 +41,8 @@ use std::sync::Arc;
|
|||
use theme::ActiveTheme;
|
||||
use title_bar_settings::TitleBarSettings;
|
||||
use ui::{
|
||||
Avatar, ButtonLike, ContextMenu, IconWithIndicator, Indicator, PopoverMenu, PopoverMenuHandle,
|
||||
TintColor, Tooltip, prelude::*, utils::platform_title_bar_height,
|
||||
Avatar, ButtonLike, ContextMenu, Divider, IconWithIndicator, Indicator, PopoverMenu,
|
||||
PopoverMenuHandle, TintColor, Tooltip, prelude::*, utils::platform_title_bar_height,
|
||||
};
|
||||
use update_version::UpdateVersion;
|
||||
use util::ResultExt;
|
||||
|
|
@ -169,6 +169,7 @@ impl Render for TitleBar {
|
|||
|
||||
children.push(
|
||||
h_flex()
|
||||
.h_full()
|
||||
.gap_0p5()
|
||||
.map(|title_bar| {
|
||||
let mut render_project_items = title_bar_settings.show_branch_name
|
||||
|
|
@ -705,23 +706,29 @@ impl TitleBar {
|
|||
let has_notifications = self.platform_titlebar.read(cx).sidebar_has_notifications();
|
||||
|
||||
Some(
|
||||
IconButton::new(
|
||||
"toggle-workspace-sidebar",
|
||||
IconName::ThreadsSidebarLeftClosed,
|
||||
)
|
||||
.icon_size(IconSize::Small)
|
||||
.when(has_notifications, |button| {
|
||||
button
|
||||
.indicator(Indicator::dot().color(Color::Accent))
|
||||
.indicator_border_color(Some(cx.theme().colors().title_bar_background))
|
||||
})
|
||||
.tooltip(move |_, cx| {
|
||||
Tooltip::for_action("Open Threads Sidebar", &ToggleWorkspaceSidebar, cx)
|
||||
})
|
||||
.on_click(|_, window, cx| {
|
||||
window.dispatch_action(ToggleWorkspaceSidebar.boxed_clone(), cx);
|
||||
})
|
||||
.into_any_element(),
|
||||
h_flex()
|
||||
.h_full()
|
||||
.gap_0p5()
|
||||
.child(
|
||||
IconButton::new(
|
||||
"toggle-workspace-sidebar",
|
||||
IconName::ThreadsSidebarLeftClosed,
|
||||
)
|
||||
.icon_size(IconSize::Small)
|
||||
.when(has_notifications, |button| {
|
||||
button
|
||||
.indicator(Indicator::dot().color(Color::Accent))
|
||||
.indicator_border_color(Some(cx.theme().colors().title_bar_background))
|
||||
})
|
||||
.tooltip(move |_, cx| {
|
||||
Tooltip::for_action("Open Threads Sidebar", &ToggleWorkspaceSidebar, cx)
|
||||
})
|
||||
.on_click(|_, window, cx| {
|
||||
window.dispatch_action(ToggleWorkspaceSidebar.boxed_clone(), cx);
|
||||
}),
|
||||
)
|
||||
.child(Divider::vertical().color(ui::DividerColor::Border))
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -741,6 +748,14 @@ impl TitleBar {
|
|||
"Open Recent Project".to_string()
|
||||
};
|
||||
|
||||
let is_sidebar_open = self.platform_titlebar.read(cx).is_workspace_sidebar_open();
|
||||
|
||||
if is_sidebar_open {
|
||||
return self
|
||||
.render_project_name_with_sidebar_popover(display_name, is_project_selected, cx)
|
||||
.into_any_element();
|
||||
}
|
||||
|
||||
let focus_handle = workspace
|
||||
.upgrade()
|
||||
.map(|w| w.read(cx).focus_handle(cx))
|
||||
|
|
@ -782,6 +797,53 @@ impl TitleBar {
|
|||
.into_any_element()
|
||||
}
|
||||
|
||||
/// When the sidebar is open, the title bar's project name button becomes a
|
||||
/// plain button that toggles the sidebar's popover (so the popover is always
|
||||
/// anchored to the sidebar). Both buttons show their selected state together.
|
||||
fn render_project_name_with_sidebar_popover(
|
||||
&self,
|
||||
display_name: String,
|
||||
is_project_selected: bool,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let multi_workspace = self.multi_workspace.clone();
|
||||
|
||||
let is_popover_deployed = multi_workspace
|
||||
.as_ref()
|
||||
.and_then(|mw| mw.upgrade())
|
||||
.map(|mw| mw.read(cx).is_recent_projects_popover_deployed(cx))
|
||||
.unwrap_or(false);
|
||||
|
||||
Button::new("project_name_trigger", display_name)
|
||||
.label_size(LabelSize::Small)
|
||||
.when(self.worktree_count(cx) > 1, |this| {
|
||||
this.end_icon(
|
||||
Icon::new(IconName::ChevronDown)
|
||||
.size(IconSize::XSmall)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
})
|
||||
.toggle_state(is_popover_deployed)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.when(!is_project_selected, |s| s.color(Color::Muted))
|
||||
.tooltip(move |_window, cx| {
|
||||
Tooltip::for_action(
|
||||
"Recent Projects",
|
||||
&zed_actions::OpenRecent {
|
||||
create_new_window: false,
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.on_click(move |_, window, cx| {
|
||||
if let Some(mw) = multi_workspace.as_ref().and_then(|mw| mw.upgrade()) {
|
||||
mw.update(cx, |mw, cx| {
|
||||
mw.toggle_recent_projects_popover(window, cx);
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn render_project_branch(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
|
||||
let effective_worktree = self.effective_active_worktree(cx)?;
|
||||
let repository = self.get_repository_for_worktree(&effective_worktree, cx)?;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
mod configured_api_card;
|
||||
mod thread_item;
|
||||
mod thread_sidebar_toggle;
|
||||
|
||||
pub use configured_api_card::*;
|
||||
pub use thread_item::*;
|
||||
pub use thread_sidebar_toggle::*;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ use crate::{
|
|||
IconDecorationKind, prelude::*,
|
||||
};
|
||||
|
||||
use gpui::{Animation, AnimationExt, AnyView, ClickEvent, Hsla, SharedString, pulsating_between};
|
||||
use gpui::{
|
||||
Animation, AnimationExt, AnyView, ClickEvent, Hsla, MouseButton, SharedString,
|
||||
pulsating_between,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
|
|
@ -36,6 +39,7 @@ pub struct ThreadItem {
|
|||
worktree_highlight_positions: Vec<usize>,
|
||||
on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
|
||||
on_hover: Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>,
|
||||
title_label_color: Option<Color>,
|
||||
action_slot: Option<AnyElement>,
|
||||
tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>>,
|
||||
}
|
||||
|
|
@ -62,6 +66,7 @@ impl ThreadItem {
|
|||
worktree_highlight_positions: Vec::new(),
|
||||
on_click: None,
|
||||
on_hover: Box::new(|_, _, _| {}),
|
||||
title_label_color: None,
|
||||
action_slot: None,
|
||||
tooltip: None,
|
||||
}
|
||||
|
|
@ -155,6 +160,11 @@ impl ThreadItem {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn title_label_color(mut self, color: Color) -> Self {
|
||||
self.title_label_color = Some(color);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn action_slot(mut self, element: impl IntoElement) -> Self {
|
||||
self.action_slot = Some(element.into_any_element());
|
||||
self
|
||||
|
|
@ -230,7 +240,7 @@ impl RenderOnce for ThreadItem {
|
|||
let title = self.title;
|
||||
let highlight_positions = self.highlight_positions;
|
||||
let title_label = if self.generating_title {
|
||||
Label::new("New Thread…")
|
||||
Label::new(title)
|
||||
.color(Color::Muted)
|
||||
.with_animation(
|
||||
"generating-title",
|
||||
|
|
@ -241,15 +251,31 @@ impl RenderOnce for ThreadItem {
|
|||
)
|
||||
.into_any_element()
|
||||
} else if highlight_positions.is_empty() {
|
||||
Label::new(title).into_any_element()
|
||||
let label = Label::new(title);
|
||||
let label = if let Some(color) = self.title_label_color {
|
||||
label.color(color)
|
||||
} else {
|
||||
label
|
||||
};
|
||||
label.into_any_element()
|
||||
} else {
|
||||
HighlightedLabel::new(title, highlight_positions).into_any_element()
|
||||
let label = HighlightedLabel::new(title, highlight_positions);
|
||||
let label = if let Some(color) = self.title_label_color {
|
||||
label.color(color)
|
||||
} else {
|
||||
label
|
||||
};
|
||||
label.into_any_element()
|
||||
};
|
||||
|
||||
let b_bg = color
|
||||
.title_bar_background
|
||||
.blend(color.panel_background.opacity(0.8));
|
||||
|
||||
let base_bg = if self.selected {
|
||||
color.element_active
|
||||
} else {
|
||||
color.panel_background
|
||||
b_bg
|
||||
};
|
||||
|
||||
let gradient_overlay =
|
||||
|
|
@ -314,7 +340,15 @@ impl RenderOnce for ThreadItem {
|
|||
.gradient_stop(0.75)
|
||||
.group_name("thread-item");
|
||||
|
||||
this.child(h_flex().relative().child(overlay).child(slot))
|
||||
this.child(
|
||||
h_flex()
|
||||
.relative()
|
||||
.on_mouse_down(MouseButton::Left, |_, _, cx| {
|
||||
cx.stop_propagation()
|
||||
})
|
||||
.child(overlay)
|
||||
.child(slot),
|
||||
)
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,177 +0,0 @@
|
|||
use gpui::{AnyView, ClickEvent};
|
||||
use ui_macros::RegisterComponent;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{IconButton, IconName, Tooltip};
|
||||
|
||||
#[derive(IntoElement, RegisterComponent)]
|
||||
pub struct ThreadSidebarToggle {
|
||||
sidebar_selected: bool,
|
||||
thread_selected: bool,
|
||||
flipped: bool,
|
||||
sidebar_tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>>,
|
||||
thread_tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>>,
|
||||
on_sidebar_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
|
||||
on_thread_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
|
||||
}
|
||||
|
||||
impl ThreadSidebarToggle {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
sidebar_selected: false,
|
||||
thread_selected: false,
|
||||
flipped: false,
|
||||
sidebar_tooltip: None,
|
||||
thread_tooltip: None,
|
||||
on_sidebar_click: None,
|
||||
on_thread_click: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sidebar_selected(mut self, selected: bool) -> Self {
|
||||
self.sidebar_selected = selected;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn thread_selected(mut self, selected: bool) -> Self {
|
||||
self.thread_selected = selected;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn flipped(mut self, flipped: bool) -> Self {
|
||||
self.flipped = flipped;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sidebar_tooltip(
|
||||
mut self,
|
||||
tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
|
||||
) -> Self {
|
||||
self.sidebar_tooltip = Some(Box::new(tooltip));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn thread_tooltip(
|
||||
mut self,
|
||||
tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
|
||||
) -> Self {
|
||||
self.thread_tooltip = Some(Box::new(tooltip));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_sidebar_click(
|
||||
mut self,
|
||||
handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
|
||||
) -> Self {
|
||||
self.on_sidebar_click = Some(Box::new(handler));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_thread_click(
|
||||
mut self,
|
||||
handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
|
||||
) -> Self {
|
||||
self.on_thread_click = Some(Box::new(handler));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for ThreadSidebarToggle {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let sidebar_icon = match (self.sidebar_selected, self.flipped) {
|
||||
(true, false) => IconName::ThreadsSidebarLeftOpen,
|
||||
(false, false) => IconName::ThreadsSidebarLeftClosed,
|
||||
(true, true) => IconName::ThreadsSidebarRightOpen,
|
||||
(false, true) => IconName::ThreadsSidebarRightClosed,
|
||||
};
|
||||
|
||||
h_flex()
|
||||
.min_w_0()
|
||||
.rounded_sm()
|
||||
.gap_px()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.when(self.flipped, |this| this.flex_row_reverse())
|
||||
.child(
|
||||
IconButton::new("sidebar-toggle", sidebar_icon)
|
||||
.icon_size(IconSize::Small)
|
||||
.toggle_state(self.sidebar_selected)
|
||||
.when_some(self.sidebar_tooltip, |this, tooltip| this.tooltip(tooltip))
|
||||
.when_some(self.on_sidebar_click, |this, handler| {
|
||||
this.on_click(handler)
|
||||
}),
|
||||
)
|
||||
.child(div().h_4().w_px().bg(cx.theme().colors().border))
|
||||
.child(
|
||||
IconButton::new("thread-toggle", IconName::Thread)
|
||||
.icon_size(IconSize::Small)
|
||||
.toggle_state(self.thread_selected)
|
||||
.when_some(self.thread_tooltip, |this, tooltip| this.tooltip(tooltip))
|
||||
.when_some(self.on_thread_click, |this, handler| this.on_click(handler)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for ThreadSidebarToggle {
|
||||
fn scope() -> ComponentScope {
|
||||
ComponentScope::Agent
|
||||
}
|
||||
|
||||
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
|
||||
let container = || div().p_2().bg(cx.theme().colors().status_bar_background);
|
||||
|
||||
let examples = vec![
|
||||
single_example(
|
||||
"Both Unselected",
|
||||
container()
|
||||
.child(ThreadSidebarToggle::new())
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Sidebar Selected",
|
||||
container()
|
||||
.child(ThreadSidebarToggle::new().sidebar_selected(true))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Thread Selected",
|
||||
container()
|
||||
.child(ThreadSidebarToggle::new().thread_selected(true))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Both Selected",
|
||||
container()
|
||||
.child(
|
||||
ThreadSidebarToggle::new()
|
||||
.sidebar_selected(true)
|
||||
.thread_selected(true),
|
||||
)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Flipped",
|
||||
container()
|
||||
.child(
|
||||
ThreadSidebarToggle::new()
|
||||
.sidebar_selected(true)
|
||||
.thread_selected(true)
|
||||
.flipped(true),
|
||||
)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"With Tooltips",
|
||||
container()
|
||||
.child(
|
||||
ThreadSidebarToggle::new()
|
||||
.sidebar_tooltip(Tooltip::text("Toggle Sidebar"))
|
||||
.thread_tooltip(Tooltip::text("Toggle Thread")),
|
||||
)
|
||||
.into_any_element(),
|
||||
),
|
||||
];
|
||||
|
||||
Some(example_group(examples).into_any_element())
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +45,8 @@ pub trait Sidebar: Focusable + Render + Sized {
|
|||
fn width(&self, cx: &App) -> Pixels;
|
||||
fn set_width(&mut self, width: Option<Pixels>, cx: &mut Context<Self>);
|
||||
fn has_notifications(&self, cx: &App) -> bool;
|
||||
fn toggle_recent_projects_popover(&self, window: &mut Window, cx: &mut App);
|
||||
fn is_recent_projects_popover_deployed(&self) -> bool;
|
||||
}
|
||||
|
||||
pub trait SidebarHandle: 'static + Send + Sync {
|
||||
|
|
@ -55,6 +57,8 @@ pub trait SidebarHandle: 'static + Send + Sync {
|
|||
fn has_notifications(&self, cx: &App) -> bool;
|
||||
fn to_any(&self) -> AnyView;
|
||||
fn entity_id(&self) -> EntityId;
|
||||
fn toggle_recent_projects_popover(&self, window: &mut Window, cx: &mut App);
|
||||
fn is_recent_projects_popover_deployed(&self, cx: &App) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -95,6 +99,16 @@ impl<T: Sidebar> SidebarHandle for Entity<T> {
|
|||
fn entity_id(&self) -> EntityId {
|
||||
Entity::entity_id(self)
|
||||
}
|
||||
|
||||
fn toggle_recent_projects_popover(&self, window: &mut Window, cx: &mut App) {
|
||||
self.update(cx, |this, cx| {
|
||||
this.toggle_recent_projects_popover(window, cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn is_recent_projects_popover_deployed(&self, cx: &App) -> bool {
|
||||
self.read(cx).is_recent_projects_popover_deployed()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MultiWorkspace {
|
||||
|
|
@ -167,6 +181,18 @@ impl MultiWorkspace {
|
|||
.map_or(false, |s| s.has_notifications(cx))
|
||||
}
|
||||
|
||||
pub fn toggle_recent_projects_popover(&self, window: &mut Window, cx: &mut App) {
|
||||
if let Some(sidebar) = &self.sidebar {
|
||||
sidebar.toggle_recent_projects_popover(window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_recent_projects_popover_deployed(&self, cx: &App) -> bool {
|
||||
self.sidebar
|
||||
.as_ref()
|
||||
.map_or(false, |s| s.is_recent_projects_popover_deployed(cx))
|
||||
}
|
||||
|
||||
pub fn multi_workspace_enabled(&self, cx: &App) -> bool {
|
||||
cx.has_flag::<AgentV2FeatureFlag>() && !DisableAiSettings::get_global(cx).disable_ai
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue