aboutsummaryrefslogtreecommitdiff
path: root/src/client/shell/overlay_input.rs
diff options
context:
space:
mode:
authorEric Yue <jiacheng.yue@foxmail.com>2026-09-21 01:33:11 +0800
committerGitHub <noreply@github.com>2026-09-20 19:33:11 +0200
commitf090ce95d05d2e7811869be93f9a23bf4ac2c3af (patch)
treed5a1a447fd6043056e5ea3cb1a9bd0419957e57b /src/client/shell/overlay_input.rs
parentf10df75c8a5f1b5e5f90689c3a8ceb6758366e05 (diff)
fix: confirm closing the last tab in the tui (#4409)
* fix: confirm closing the last tab in the tui refs #4379 * docs: clarify last-tab worktree confirmation in translations refs #4379 --------- Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
Diffstat (limited to 'src/client/shell/overlay_input.rs')
-rw-r--r--src/client/shell/overlay_input.rs91
1 files changed, 76 insertions, 15 deletions
diff --git a/src/client/shell/overlay_input.rs b/src/client/shell/overlay_input.rs
index e4248304..10bd4a45 100644
--- a/src/client/shell/overlay_input.rs
+++ b/src/client/shell/overlay_input.rs
@@ -877,19 +877,7 @@ impl ClientShellState {
if matches!(self.overlay, Some(ClientShellOverlay::ConfirmClose(_))) {
if key.code == KeyCode::Enter {
- let Some(ClientShellOverlay::ConfirmClose(confirm)) = self.overlay.take() else {
- return;
- };
- self.push_endpoint_method(
- crate::api::schema::Method::WorkspaceClose(
- crate::api::schema::WorkspaceCloseParams {
- workspace_id: confirm.workspace_id,
- close_group: true,
- },
- ),
- outcome,
- );
- outcome.repaint = true;
+ self.accept_close_confirmation(outcome);
} else if key.code == KeyCode::Esc {
self.overlay = None;
self.mode = ClientShellMode::Navigate;
@@ -999,16 +987,74 @@ impl ClientShellState {
outcome.repaint = true;
}
+ pub(super) fn request_tab_close(&mut self, tab_id: String, outcome: &mut ClientShellInput) {
+ let workspace_id = self.snapshot.as_deref().and_then(|snapshot| {
+ let target = snapshot.tabs.iter().find(|tab| tab.tab_id == tab_id)?;
+ (self.config.confirm_close
+ && !snapshot
+ .tabs
+ .iter()
+ .any(|tab| tab.workspace_id == target.workspace_id && tab.tab_id != tab_id))
+ .then(|| target.workspace_id.clone())
+ });
+ if let Some(workspace_id) = workspace_id {
+ if self.open_close_confirmation(workspace_id, Some(tab_id.clone())) {
+ outcome.repaint = true;
+ return;
+ }
+ }
+ self.push_endpoint_method(
+ crate::api::schema::Method::TabClose(crate::api::schema::TabTarget { tab_id }),
+ outcome,
+ );
+ }
+
+ pub(super) fn accept_close_confirmation(&mut self, outcome: &mut ClientShellInput) {
+ let Some(ClientShellOverlay::ConfirmClose(confirm)) = self.overlay.take() else {
+ return;
+ };
+ outcome.repaint = true;
+ let method = if let Some(target) = confirm.tab_target {
+ if target.workspace.endpoint_id != self.active_endpoint_id
+ || !self.navigation_target_valid(&target.workspace)
+ || !self.snapshot.as_deref().is_some_and(|snapshot| {
+ snapshot.tabs.iter().any(|tab| {
+ tab.tab_id == target.tab_id
+ && tab.workspace_id == target.workspace.workspace_id
+ })
+ })
+ {
+ self.receive_endpoint_unavailable(
+ "Close target changed; try closing the tab again".into(),
+ );
+ return;
+ }
+ crate::api::schema::Method::TabClose(crate::api::schema::TabTarget {
+ tab_id: target.tab_id,
+ })
+ } else {
+ crate::api::schema::Method::WorkspaceClose(crate::api::schema::WorkspaceCloseParams {
+ workspace_id: confirm.workspace_id,
+ close_group: true,
+ })
+ };
+ self.push_endpoint_method(method, outcome);
+ }
+
pub(super) fn open_confirm_close_overlay(&mut self, workspace_id: String) {
+ self.open_close_confirmation(workspace_id, None);
+ }
+
+ fn open_close_confirmation(&mut self, workspace_id: String, tab_id: Option<String>) -> bool {
let Some(snapshot) = self.snapshot.as_deref() else {
- return;
+ return false;
};
let Some(workspace) = snapshot
.workspaces
.iter()
.find(|workspace| workspace.workspace_id == workspace_id)
else {
- return;
+ return false;
};
let group_key = workspace
.worktree
@@ -1030,6 +1076,19 @@ impl ClientShellState {
})
.unwrap_or_else(|| vec![workspace]);
let closes_group = group.len() > 1;
+ // Keep parent-group tab closes on the existing server confirmation path.
+ if tab_id.is_some() && closes_group {
+ return false;
+ }
+ let tab_target = if let Some(tab_id) = tab_id {
+ let Some(workspace) = self.navigation_target(&self.active_endpoint_id, &workspace_id)
+ else {
+ return false;
+ };
+ Some(ClientTabCloseConfirmation { tab_id, workspace })
+ } else {
+ None
+ };
let pane_count = group
.iter()
.map(|member| {
@@ -1053,6 +1112,7 @@ impl ClientShellState {
self.overlay = Some(ClientShellOverlay::ConfirmClose(
ClientConfirmCloseOverlay {
workspace_id,
+ tab_target,
title: if closes_group {
"Close worktree group?".to_owned()
} else {
@@ -1061,5 +1121,6 @@ impl ClientShellState {
detail: format!("{} — {scope}", workspace.label),
},
));
+ true
}
}