diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 5da15a1c1f304743c55e87ecc208fd6adbdc7cc2..bb1fbf7059879899e616f5a84c165f88191f3deb 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -1123,6 +1123,8 @@ "g g": "menu::SelectFirst", "shift-g": "menu::SelectLast", "/": "agents_sidebar::FocusSidebarFilter", + "d d": "agents_sidebar::RemoveSelected", + "o": "agents_sidebar::NewThreadInGroup", "z a": "editor::ToggleFold", "z c": "menu::SelectParent", "z o": "menu::SelectChild", diff --git a/crates/sidebar/src/sidebar.rs b/crates/sidebar/src/sidebar.rs index 501b55a73260f0d453775fc245868669c35ab406..c4e4041f718abff536102d302d1e2ee271a263fb 100644 --- a/crates/sidebar/src/sidebar.rs +++ b/crates/sidebar/src/sidebar.rs @@ -54,6 +54,10 @@ gpui::actions!( NewThreadInGroup, /// Toggles between the thread list and the archive view. ToggleArchive, + /// Closes the currently selected workspace. + RemoveSelectedWorkspace, + /// Removes the selected entry: archives a thread or closes a workspace. + RemoveSelected, ] ); @@ -2453,6 +2457,43 @@ impl Sidebar { self.archive_thread(&session_id, window, cx); } + fn remove_selected_workspace( + &mut self, + _: &RemoveSelectedWorkspace, + window: &mut Window, + cx: &mut Context, + ) { + let Some(ix) = self.selection else { + return; + }; + let Some(ListEntry::ProjectHeader { workspace, .. }) = self.contents.entries.get(ix) + else { + return; + }; + let workspace = workspace.clone(); + self.remove_workspace(&workspace, window, cx); + } + + fn remove_selected( + &mut self, + _: &RemoveSelected, + window: &mut Window, + cx: &mut Context, + ) { + let Some(ix) = self.selection else { + return; + }; + match self.contents.entries.get(ix) { + Some(ListEntry::Thread(_)) => { + self.remove_selected_thread(&RemoveSelectedThread, window, cx); + } + Some(ListEntry::ProjectHeader { .. }) => { + self.remove_selected_workspace(&RemoveSelectedWorkspace, window, cx); + } + _ => {} + } + } + fn render_thread( &self, ix: usize, @@ -3160,6 +3201,8 @@ impl Render for Sidebar { .on_action(cx.listener(Self::unfold_all)) .on_action(cx.listener(Self::cancel)) .on_action(cx.listener(Self::remove_selected_thread)) + .on_action(cx.listener(Self::remove_selected_workspace)) + .on_action(cx.listener(Self::remove_selected)) .on_action(cx.listener(Self::new_thread_in_group)) .on_action(cx.listener(Self::toggle_archive)) .on_action(cx.listener(Self::focus_sidebar_filter)) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 37cac09863b2251a7c8dc259d3fb1fc68c00c07e..31fba952549e8618d02e3092aba7065e95c6d540 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -4730,12 +4730,36 @@ impl Workspace { .as_ref() .map(|h| Target::Sidebar(h.clone())); + let sidebar_on_left = self + .multi_workspace + .as_ref() + .and_then(|mw| mw.upgrade()) + .map_or(true, |mw| mw.read(cx).sidebar_side(cx) == SidebarSide::Left); + + let sidebar_on_side = |side: SplitDirection| -> Option { + if (side == SplitDirection::Left) == sidebar_on_left { + sidebar_target.clone() + } else { + None + } + }; + let target = match (origin, direction) { - // From the sidebar, only Right navigates into the workspace. - (Origin::Sidebar, SplitDirection::Right) => try_dock(&self.left_dock) - .or_else(|| get_last_active_pane().map(Target::Pane)) - .or_else(|| try_dock(&self.bottom_dock)) - .or_else(|| try_dock(&self.right_dock)), + // From the sidebar, only the inward direction navigates into + // the workspace. Which direction is "inward" depends on which + // side the sidebar is on. + (Origin::Sidebar, SplitDirection::Right) if sidebar_on_left => { + try_dock(&self.left_dock) + .or_else(|| get_last_active_pane().map(Target::Pane)) + .or_else(|| try_dock(&self.bottom_dock)) + .or_else(|| try_dock(&self.right_dock)) + } + (Origin::Sidebar, SplitDirection::Left) if !sidebar_on_left => { + try_dock(&self.right_dock) + .or_else(|| get_last_active_pane().map(Target::Pane)) + .or_else(|| try_dock(&self.bottom_dock)) + .or_else(|| try_dock(&self.left_dock)) + } (Origin::Sidebar, _) => None, @@ -4748,8 +4772,12 @@ impl Workspace { match direction { SplitDirection::Up => None, SplitDirection::Down => try_dock(&self.bottom_dock), - SplitDirection::Left => try_dock(&self.left_dock).or(sidebar_target), - SplitDirection::Right => try_dock(&self.right_dock), + SplitDirection::Left => { + try_dock(&self.left_dock).or_else(|| sidebar_on_side(direction)) + } + SplitDirection::Right => { + try_dock(&self.right_dock).or_else(|| sidebar_on_side(direction)) + } } } } @@ -4762,27 +4790,29 @@ impl Workspace { } } - (Origin::LeftDock, SplitDirection::Left) => sidebar_target, + (Origin::LeftDock, SplitDirection::Left) => sidebar_on_side(SplitDirection::Left), (Origin::LeftDock, SplitDirection::Down) | (Origin::RightDock, SplitDirection::Down) => try_dock(&self.bottom_dock), (Origin::BottomDock, SplitDirection::Up) => get_last_active_pane().map(Target::Pane), (Origin::BottomDock, SplitDirection::Left) => { - try_dock(&self.left_dock).or(sidebar_target) + try_dock(&self.left_dock).or_else(|| sidebar_on_side(SplitDirection::Left)) + } + (Origin::BottomDock, SplitDirection::Right) => { + try_dock(&self.right_dock).or_else(|| sidebar_on_side(SplitDirection::Right)) } - (Origin::BottomDock, SplitDirection::Right) => try_dock(&self.right_dock), (Origin::RightDock, SplitDirection::Left) => { if let Some(last_active_pane) = get_last_active_pane() { Some(Target::Pane(last_active_pane)) } else { - try_dock(&self.bottom_dock) - .or_else(|| try_dock(&self.left_dock)) - .or(sidebar_target) + try_dock(&self.bottom_dock).or_else(|| try_dock(&self.left_dock)) } } + (Origin::RightDock, SplitDirection::Right) => sidebar_on_side(SplitDirection::Right), + _ => None, };