diff --git a/app/src/workspace/view.rs b/app/src/workspace/view.rs index 6af9ef53..9ca2d2fa 100644 --- a/app/src/workspace/view.rs +++ b/app/src/workspace/view.rs @@ -5987,7 +5987,7 @@ impl Workspace { /// Builds the unified new-session menu items /// tab bar chevron and the vertical tab bar `+` button. /// - /// Order: Agent → Terminal (sidecar) → Cloud Oz → [tab configs] → separator → New worktree config (sidecar) → New tab config. + /// Order: Agent → Terminal (sidecar) → Cloud Oz → [tab configs] → separator → New worktree config (sidecar) → New tab config → separator → Reopen closed session. fn unified_new_session_menu_items( &self, ctx: &mut ViewContext, @@ -5999,6 +5999,8 @@ impl Workspace { let effective_default = ai_settings.default_session_mode(ctx); let default_tab_config_path = ai_settings.default_tab_config_path().to_string(); let shortcut_label = keybinding_name_to_display_string(NEW_TAB_BINDING_NAME, ctx); + let reopen_closed_session_shortcut_label = + keybinding_name_to_display_string("app:reopen_closed_session", ctx); // 1. Agent (if AI enabled) if is_any_ai_enabled { @@ -6159,6 +6161,15 @@ impl Workspace { ); } + menu_items.push(MenuItem::Separator); + menu_items.push( + MenuItemFields::new("Reopen closed session") + .with_on_select_action(WorkspaceAction::ReopenClosedSession) + .with_key_shortcut_label(reopen_closed_session_shortcut_label) + .with_disabled(UndoCloseStack::handle(ctx).as_ref(ctx).is_empty()) + .into_item(), + ); + menu_items } diff --git a/app/src/workspace/view_test.rs b/app/src/workspace/view_test.rs index fbff35ff..24a3bef2 100644 --- a/app/src/workspace/view_test.rs +++ b/app/src/workspace/view_test.rs @@ -686,6 +686,15 @@ fn new_session_menu_label(item: &MenuItem) -> String { } } +fn reopen_closed_session_menu_item( + menu_items: &[MenuItem], +) -> &MenuItemFields { + match menu_items.last() { + Some(MenuItem::Item(fields)) if fields.label() == "Reopen closed session" => fields, + _ => panic!("expected Reopen closed session to be the last new-session menu item"), + } +} + #[test] fn test_reward_modal_no_overlap() { App::test((), |mut app| async move { @@ -2575,6 +2584,37 @@ fn test_unified_new_session_menu_uses_new_worktree_config_label_and_order() { }); } +#[test] +fn test_unified_new_session_menu_includes_reopen_closed_session() { + App::test((), |mut app| async move { + initialize_app(&mut app); + + let workspace = mock_workspace(&mut app); + + workspace.update(&mut app, |workspace, ctx| { + let menu_items = workspace.unified_new_session_menu_items(ctx); + assert!(matches!( + menu_items.get(menu_items.len() - 2), + Some(MenuItem::Separator) + )); + + let reopen_item = reopen_closed_session_menu_item(&menu_items); + assert!(reopen_item.is_disabled()); + assert!(matches!( + reopen_item.on_select_action(), + Some(action) if matches!(action, WorkspaceAction::ReopenClosedSession) + )); + + workspace.add_terminal_tab(false, ctx); + workspace.remove_tab(workspace.active_tab_index(), true, true, ctx); + + let menu_items = workspace.unified_new_session_menu_items(ctx); + let reopen_item = reopen_closed_session_menu_item(&menu_items); + assert!(!reopen_item.is_disabled()); + }); + }); +} + #[cfg(feature = "local_fs")] #[test] fn test_worktree_sidecar_search_editor_proxies_navigation_and_escape() {