From 833a015dc65f5b1658af1ae8cbb58ebe313cdf66 Mon Sep 17 00:00:00 2001
From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Date: Tue, 7 Apr 2026 12:44:19 -0300
Subject: [PATCH] recent_projects: Make the currently active project visible in
the picker (#53302)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This PR improves the recent projects picker in the context of
multi-workspace:
- The currently active project now appears in the "This Window" section
with a checkmark indicator. Clicking it simply dismisses the picker,
since there's nothing to switch to. This feels like a better UX because
it gives you visual confirmation of where you are.
- The remove button is hidden for the current project entry, both in the
row and the footer, to prevent accidentally removing the workspace
you're actively using.
- The "Add to Workspace" button now uses a more descriptive icon
(`FolderOpenAdd`) and shows a meta tooltip clarifying that it adds the
project as a multi-root folder project.
The primary click/enter behavior remains unchanged—it opens the selected
project in the current window's multi-workspace. The "Open in New
Window" action continues to be available via the icon button or
shift+enter.
Release Notes:
- Improved the recent projects picker to show the currently active
project in the "This Window" section with a checkmark indicator.
---
assets/icons/folder_open_add.svg | 5 +
assets/icons/folder_plus.svg | 5 -
assets/icons/open_new_window.svg | 7 +
crates/agent_ui/src/threads_archive_view.rs | 1 +
crates/icons/src/icons.rs | 3 +-
.../src/highlighted_match_with_paths.rs | 23 +++-
crates/recent_projects/src/recent_projects.rs | 127 ++++++++++++------
.../src/sidebar_recent_projects.rs | 10 +-
8 files changed, 127 insertions(+), 54 deletions(-)
create mode 100644 assets/icons/folder_open_add.svg
delete mode 100644 assets/icons/folder_plus.svg
create mode 100644 assets/icons/open_new_window.svg
diff --git a/assets/icons/folder_open_add.svg b/assets/icons/folder_open_add.svg
new file mode 100644
index 0000000000000000000000000000000000000000..d5ebbdaa8b080037a2faee0ee0fc3606eec9c6ca
--- /dev/null
+++ b/assets/icons/folder_open_add.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/folder_plus.svg b/assets/icons/folder_plus.svg
deleted file mode 100644
index a543448ed6197043291369bee640e23b6ad729b9..0000000000000000000000000000000000000000
--- a/assets/icons/folder_plus.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/assets/icons/open_new_window.svg b/assets/icons/open_new_window.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c81d49f9ff9edfbc965055568efc72e0214efb41
--- /dev/null
+++ b/assets/icons/open_new_window.svg
@@ -0,0 +1,7 @@
+
diff --git a/crates/agent_ui/src/threads_archive_view.rs b/crates/agent_ui/src/threads_archive_view.rs
index 13b2aa1a37cd506c338d13db78bce751882e426a..7cb8410e5017438b0e8adde673887c13397d9abf 100644
--- a/crates/agent_ui/src/threads_archive_view.rs
+++ b/crates/agent_ui/src/threads_archive_view.rs
@@ -1236,6 +1236,7 @@ impl PickerDelegate for ProjectPickerDelegate {
},
match_label: HighlightedMatch::join(match_labels.into_iter().flatten(), ", "),
paths: Vec::new(),
+ active: false,
};
Some(
diff --git a/crates/icons/src/icons.rs b/crates/icons/src/icons.rs
index e29b7d3593025556771d62dc0124786672c540de..bdc3890432414e0a78f69a226bb9174510453331 100644
--- a/crates/icons/src/icons.rs
+++ b/crates/icons/src/icons.rs
@@ -134,7 +134,7 @@ pub enum IconName {
Flame,
Folder,
FolderOpen,
- FolderPlus,
+ FolderOpenAdd,
FolderSearch,
Font,
FontSize,
@@ -184,6 +184,7 @@ pub enum IconName {
NewThread,
Notepad,
OpenFolder,
+ OpenNewWindow,
Option,
PageDown,
PageUp,
diff --git a/crates/picker/src/highlighted_match_with_paths.rs b/crates/picker/src/highlighted_match_with_paths.rs
index 74271047621b26be573dc2eebfffe9e9e0f1a138..7c88213437feea17e6b431dff9c97b0b8557872a 100644
--- a/crates/picker/src/highlighted_match_with_paths.rs
+++ b/crates/picker/src/highlighted_match_with_paths.rs
@@ -5,6 +5,7 @@ pub struct HighlightedMatchWithPaths {
pub prefix: Option,
pub match_label: HighlightedMatch,
pub paths: Vec,
+ pub active: bool,
}
#[derive(Debug, Clone, IntoElement)]
@@ -63,18 +64,30 @@ impl HighlightedMatchWithPaths {
.color(Color::Muted)
}))
}
+
+ pub fn is_active(mut self, active: bool) -> Self {
+ self.active = active;
+ self
+ }
}
impl RenderOnce for HighlightedMatchWithPaths {
fn render(mut self, _window: &mut Window, _: &mut App) -> impl IntoElement {
v_flex()
.child(
- h_flex().gap_1().child(self.match_label.clone()).when_some(
- self.prefix.as_ref(),
- |this, prefix| {
+ h_flex()
+ .gap_1()
+ .child(self.match_label.clone())
+ .when_some(self.prefix.as_ref(), |this, prefix| {
this.child(Label::new(format!("({})", prefix)).color(Color::Muted))
- },
- ),
+ })
+ .when(self.active, |this| {
+ this.child(
+ Icon::new(IconName::Check)
+ .size(IconSize::Small)
+ .color(Color::Accent),
+ )
+ }),
)
.when(!self.paths.is_empty(), |this| {
self.render_paths_children(this)
diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs
index e3bfc0dc08c95c0ce57b818e50965433a6c6bc98..57754dadec20146cb1f21039266de88a0bd5da9f 100644
--- a/crates/recent_projects/src/recent_projects.rs
+++ b/crates/recent_projects/src/recent_projects.rs
@@ -720,6 +720,9 @@ impl RecentProjects {
picker.delegate.workspaces.get(hit.candidate_id)
{
let workspace_id = *workspace_id;
+ if picker.delegate.is_current_workspace(workspace_id, cx) {
+ return;
+ }
picker
.delegate
.remove_sibling_workspace(workspace_id, window, cx);
@@ -939,7 +942,7 @@ impl PickerDelegate for RecentProjectsDelegate {
.workspaces
.iter()
.enumerate()
- .filter(|(_, (id, _, _, _))| self.is_sibling_workspace(*id, cx))
+ .filter(|(_, (id, _, _, _))| self.sibling_workspace_ids.contains(id))
.map(|(id, (_, _, paths, _))| {
let combined_string = paths
.ordered_paths()
@@ -1028,7 +1031,7 @@ impl PickerDelegate for RecentProjectsDelegate {
if is_empty_query {
for (id, (workspace_id, _, _, _)) in self.workspaces.iter().enumerate() {
- if self.is_sibling_workspace(*workspace_id, cx) {
+ if self.sibling_workspace_ids.contains(workspace_id) {
entries.push(ProjectPickerEntry::OpenProject(StringMatch {
candidate_id: id,
score: 0.0,
@@ -1106,6 +1109,11 @@ impl PickerDelegate for RecentProjectsDelegate {
};
let workspace_id = *workspace_id;
+ if self.is_current_workspace(workspace_id, cx) {
+ cx.emit(DismissEvent);
+ return;
+ }
+
if let Some(handle) = window.window_handle().downcast::() {
cx.defer(move |cx| {
handle
@@ -1349,6 +1357,7 @@ impl PickerDelegate for RecentProjectsDelegate {
ProjectPickerEntry::OpenProject(hit) => {
let (workspace_id, location, paths, _) = self.workspaces.get(hit.candidate_id)?;
let workspace_id = *workspace_id;
+ let is_current = self.is_current_workspace(workspace_id, cx);
let ordered_paths: Vec<_> = paths
.ordered_paths()
.map(|p| p.compact().to_string_lossy().to_string())
@@ -1388,6 +1397,7 @@ impl PickerDelegate for RecentProjectsDelegate {
prefix,
match_label: HighlightedMatch::join(match_labels.into_iter().flatten(), ", "),
paths,
+ active: is_current,
};
let icon = icon_for_remote_connection(match location {
@@ -1397,20 +1407,24 @@ impl PickerDelegate for RecentProjectsDelegate {
let secondary_actions = h_flex()
.gap_1()
- .child(
- IconButton::new("remove_open_project", IconName::Close)
- .icon_size(IconSize::Small)
- .tooltip(Tooltip::text("Remove Project from Window"))
- .on_click(cx.listener(move |picker, _, window, cx| {
- cx.stop_propagation();
- window.prevent_default();
- picker
- .delegate
- .remove_sibling_workspace(workspace_id, window, cx);
- let query = picker.query(cx);
- picker.update_matches(query, window, cx);
- })),
- )
+ .when(!is_current, |this| {
+ this.child(
+ IconButton::new("remove_open_project", IconName::Close)
+ .icon_size(IconSize::Small)
+ .tooltip(Tooltip::text("Remove Project from Window"))
+ .on_click(cx.listener(move |picker, _, window, cx| {
+ cx.stop_propagation();
+ window.prevent_default();
+ picker.delegate.remove_sibling_workspace(
+ workspace_id,
+ window,
+ cx,
+ );
+ let query = picker.query(cx);
+ picker.update_matches(query, window, cx);
+ })),
+ )
+ })
.into_any_element();
Some(
@@ -1483,6 +1497,7 @@ impl PickerDelegate for RecentProjectsDelegate {
prefix,
match_label: HighlightedMatch::join(match_labels.into_iter().flatten(), ", "),
paths,
+ active: false,
};
let focus_handle = self.focus_handle.clone();
@@ -1491,9 +1506,16 @@ impl PickerDelegate for RecentProjectsDelegate {
.gap_px()
.when(is_local, |this| {
this.child(
- IconButton::new("add_to_workspace", IconName::FolderPlus)
+ IconButton::new("add_to_workspace", IconName::FolderOpenAdd)
.icon_size(IconSize::Small)
- .tooltip(Tooltip::text("Add Project to this Workspace"))
+ .tooltip(move |_, cx| {
+ Tooltip::with_meta(
+ "Add Project to this Workspace",
+ None,
+ "As a multi-root folder project",
+ cx,
+ )
+ })
.on_click({
let paths_to_add = paths_to_add.clone();
cx.listener(move |picker, _event, window, cx| {
@@ -1509,8 +1531,8 @@ impl PickerDelegate for RecentProjectsDelegate {
)
})
.child(
- IconButton::new("open_new_window", IconName::ArrowUpRight)
- .icon_size(IconSize::XSmall)
+ IconButton::new("open_new_window", IconName::OpenNewWindow)
+ .icon_size(IconSize::Small)
.tooltip({
move |_, cx| {
Tooltip::for_action_in(
@@ -1565,7 +1587,14 @@ impl PickerDelegate for RecentProjectsDelegate {
}
highlighted.render(window, cx)
})
- .tooltip(Tooltip::text(tooltip_path)),
+ .tooltip(move |_, cx| {
+ Tooltip::with_meta(
+ "Open Project in This Window",
+ None,
+ tooltip_path.clone(),
+ cx,
+ )
+ }),
)
.end_slot(secondary_actions)
.show_end_slot_on_hover()
@@ -1625,27 +1654,41 @@ impl PickerDelegate for RecentProjectsDelegate {
let selected_entry = self.filtered_entries.get(self.selected_index);
+ let is_current_workspace_entry =
+ if let Some(ProjectPickerEntry::OpenProject(hit)) = selected_entry {
+ self.workspaces
+ .get(hit.candidate_id)
+ .map(|(id, ..)| self.is_current_workspace(*id, cx))
+ .unwrap_or(false)
+ } else {
+ false
+ };
+
let secondary_footer_actions: Option = match selected_entry {
- Some(ProjectPickerEntry::OpenFolder { .. } | ProjectPickerEntry::OpenProject(_)) => {
- let label = if matches!(selected_entry, Some(ProjectPickerEntry::OpenFolder { .. }))
- {
- "Remove Folder"
- } else {
- "Remove from Window"
- };
- Some(
- Button::new("remove_selected", label)
- .key_binding(KeyBinding::for_action_in(
- &RemoveSelected,
- &focus_handle,
- cx,
- ))
- .on_click(|_, window, cx| {
- window.dispatch_action(RemoveSelected.boxed_clone(), cx)
- })
- .into_any_element(),
- )
- }
+ Some(ProjectPickerEntry::OpenFolder { .. }) => Some(
+ Button::new("remove_selected", "Remove Folder")
+ .key_binding(KeyBinding::for_action_in(
+ &RemoveSelected,
+ &focus_handle,
+ cx,
+ ))
+ .on_click(|_, window, cx| {
+ window.dispatch_action(RemoveSelected.boxed_clone(), cx)
+ })
+ .into_any_element(),
+ ),
+ Some(ProjectPickerEntry::OpenProject(_)) if !is_current_workspace_entry => Some(
+ Button::new("remove_selected", "Remove from Window")
+ .key_binding(KeyBinding::for_action_in(
+ &RemoveSelected,
+ &focus_handle,
+ cx,
+ ))
+ .on_click(|_, window, cx| {
+ window.dispatch_action(RemoveSelected.boxed_clone(), cx)
+ })
+ .into_any_element(),
+ ),
Some(ProjectPickerEntry::RecentProject(_)) => Some(
Button::new("delete_recent", "Delete")
.key_binding(KeyBinding::for_action_in(
@@ -1748,7 +1791,7 @@ impl PickerDelegate for RecentProjectsDelegate {
menu.context(focus_handle)
.when(show_add_to_workspace, |menu| {
menu.action(
- "Add to Workspace",
+ "Add to this Workspace",
AddToWorkspace.boxed_clone(),
)
.separator()
diff --git a/crates/recent_projects/src/sidebar_recent_projects.rs b/crates/recent_projects/src/sidebar_recent_projects.rs
index 1fe0d2ae86aefdad45136c496f8049689d77e048..dec269c07eada3a1d6172482cb886f9ed44d784c 100644
--- a/crates/recent_projects/src/sidebar_recent_projects.rs
+++ b/crates/recent_projects/src/sidebar_recent_projects.rs
@@ -374,6 +374,7 @@ impl PickerDelegate for SidebarRecentProjectsDelegate {
prefix,
match_label: HighlightedMatch::join(match_labels.into_iter().flatten(), ", "),
paths: Vec::new(),
+ active: false,
};
let icon = icon_for_remote_connection(match location {
@@ -395,7 +396,14 @@ impl PickerDelegate for SidebarRecentProjectsDelegate {
})
.child(highlighted_match.render(window, cx)),
)
- .tooltip(Tooltip::text(tooltip_path))
+ .tooltip(move |_, cx| {
+ Tooltip::with_meta(
+ "Open Project in This Window",
+ None,
+ tooltip_path.clone(),
+ cx,
+ )
+ })
.into_any_element(),
)
}