Add agent drawer buttons

Eric Holk created

Change summary

crates/agent_ui/src/sidebar.rs | 10 +++++
crates/workspace/src/dock.rs   | 56 ++++++++++++++++++++++++++++++++++-
2 files changed, 62 insertions(+), 4 deletions(-)

Detailed changes

crates/agent_ui/src/sidebar.rs 🔗

@@ -33,7 +33,7 @@ use workspace::{
     dock::{DockPosition, Panel, PanelEvent, PanelIconButton},
     multi_workspace_enabled,
 };
-use zed_actions::assistant::ToggleThreadsSidebar;
+use zed_actions::assistant::{ToggleAgentDrawer, ToggleThreadsSidebar};
 use zed_actions::editor::{MoveDown, MoveUp};
 
 actions!(
@@ -2034,6 +2034,14 @@ impl Panel for Sidebar {
         }
     }
 
+    fn secondary_button(&self, _window: &Window, _cx: &App) -> Option<PanelIconButton> {
+        Some(PanelIconButton {
+            icon: IconName::Ai,
+            tooltip: "Agent Drawer",
+            action: Box::new(ToggleAgentDrawer),
+        })
+    }
+
     fn activation_priority(&self) -> u32 {
         4
     }

crates/workspace/src/dock.rs 🔗

@@ -41,6 +41,9 @@ pub trait Panel: Focusable + EventEmitter<PanelEvent> + Render + Sized {
     fn size(&self, window: &Window, cx: &App) -> Pixels;
     fn set_size(&mut self, size: Option<Pixels>, window: &mut Window, cx: &mut Context<Self>);
     fn icon_button(&self, window: &Window, cx: &App) -> PanelIconButton;
+    fn secondary_button(&self, _window: &Window, _cx: &App) -> Option<PanelIconButton> {
+        None
+    }
     fn is_zoomed(&self, _window: &Window, _cx: &App) -> bool {
         false
     }
@@ -76,6 +79,7 @@ pub trait PanelHandle: Send + Sync {
     fn size(&self, window: &Window, cx: &App) -> Pixels;
     fn set_size(&self, size: Option<Pixels>, window: &mut Window, cx: &mut App);
     fn icon_button(&self, window: &Window, cx: &App) -> PanelIconButton;
+    fn secondary_button(&self, window: &Window, cx: &App) -> Option<PanelIconButton>;
     fn panel_focus_handle(&self, cx: &App) -> FocusHandle;
     fn to_any(&self) -> AnyView;
     fn activation_priority(&self, cx: &App) -> u32;
@@ -157,6 +161,10 @@ where
         self.read(cx).icon_button(window, cx)
     }
 
+    fn secondary_button(&self, window: &Window, cx: &App) -> Option<PanelIconButton> {
+        self.read(cx).secondary_button(window, cx)
+    }
+
     fn to_any(&self) -> AnyView {
         self.clone().into()
     }
@@ -907,6 +915,7 @@ impl Render for PanelButtons {
                     tooltip: icon_tooltip,
                     action: toggle_action,
                 } = entry.panel.icon_button(window, cx);
+                let secondary_button = entry.panel.secondary_button(window, cx);
                 let name = entry.panel.persistent_name();
                 let panel = entry.panel.clone();
 
@@ -953,10 +962,10 @@ impl Render for PanelButtons {
                         })
                         .anchor(menu_anchor)
                         .attach(menu_attach)
-                        .trigger(move |is_active, _window, _cx| {
+                        .trigger(move |is_active, _window, cx| {
                             // Include active state in element ID to invalidate the cached
                             // tooltip when panel state changes (e.g., via keyboard shortcut)
-                            IconButton::new((name, is_active_button as u64), icon)
+                            let button = IconButton::new((name, is_active_button as u64), icon)
                                 .icon_size(IconSize::Small)
                                 .toggle_state(is_active_button)
                                 .on_click({
@@ -970,7 +979,48 @@ impl Render for PanelButtons {
                                     this.tooltip(move |_window, cx| {
                                         Tooltip::for_action(tooltip.clone(), &*action, cx)
                                     })
-                                })
+                                });
+
+                            match secondary_button {
+                                Some(secondary_button) => {
+                                    let action = secondary_button.action.boxed_clone();
+                                    let secondary_button =
+                                        IconButton::new("secondary-button", secondary_button.icon)
+                                            .icon_size(IconSize::Small)
+                                            .toggle_state(false) // todo! show active when drawer is open
+                                            .on_click({
+                                                let action = action.boxed_clone();
+                                                move |_, window, cx| {
+                                                    window.dispatch_action(action.boxed_clone(), cx)
+                                                }
+                                            })
+                                            .when(!is_active, |this| {
+                                                this.tooltip(move |_window, cx| {
+                                                    Tooltip::for_action(
+                                                        secondary_button.tooltip,
+                                                        &*action,
+                                                        cx,
+                                                    )
+                                                })
+                                            });
+
+                                    h_flex()
+                                        .min_w_0()
+                                        .rounded_sm()
+                                        .gap_px()
+                                        .border_1()
+                                        .border_color(cx.theme().colors().border)
+                                        .when(
+                                            matches!(dock_position, DockPosition::Right),
+                                            |this| this.flex_row_reverse(),
+                                        )
+                                        .child(button)
+                                        .child(div().h_4().w_px().bg(cx.theme().colors().border))
+                                        .child(secondary_button)
+                                        .into_any_element()
+                                }
+                                None => button.into_any_element(),
+                            }
                         }),
                 )
             })