diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-04 03:14:44 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-04 03:15:11 +0300 |
| commit | edd08e895fc65e07e500a1fe80e88202deae6b8d (patch) | |
| tree | 4d4c9df1e94b60a97c8a6be2ffb4f930896898a8 /src/app/input/modal.rs | |
| parent | 797981c7e907bc6e3050373ba150fb7a8bc651fb (diff) | |
refactor: route pane layout tui actions through runtime wrappers
Diffstat (limited to 'src/app/input/modal.rs')
| -rw-r--r-- | src/app/input/modal.rs | 148 |
1 files changed, 120 insertions, 28 deletions
diff --git a/src/app/input/modal.rs b/src/app/input/modal.rs index 31e0aa1e..ba1fd2d4 100644 --- a/src/app/input/modal.rs +++ b/src/app/input/modal.rs @@ -993,14 +993,12 @@ impl App { (self.state.active, self.state.rename_pane_target) { if let Some(pane_id) = self.public_pane_id(ws_idx, pane_id) { - self.dispatch_runtime_mutation( + self.runtime_pane_rename( "tui.pane.rename", - crate::api::schema::Method::PaneRename( - crate::api::schema::PaneRenameParams { - pane_id, - label: Some(new_name), - }, - ), + crate::api::schema::PaneRenameParams { + pane_id, + label: Some(new_name), + }, ); } } @@ -1058,13 +1056,13 @@ impl App { _ => None, }; if let Some(direction) = direction { - self.dispatch_runtime_mutation( + self.runtime_pane_resize( "tui.pane.resize", - crate::api::schema::Method::PaneResize(crate::api::schema::PaneResizeParams { + crate::api::schema::PaneResizeParams { pane_id: None, direction: super::navigate::api_pane_direction(direction), amount: None, - }), + }, ); } } @@ -1173,8 +1171,9 @@ impl App { (ContextMenuKind::Tab { ws_idx, tab_idx }, Some("Close")) => { self.focus_workspace_idx_via_api(ws_idx); self.focus_tab_idx_via_api(tab_idx); - self.close_active_tab_via_api(); - leave_modal(&mut self.state); + if !self.close_active_tab_via_api_requires_confirmation() { + leave_modal(&mut self.state); + } } (ContextMenuKind::Pane { pane_id, .. }, Some("Rename pane")) => { open_rename_pane(&mut self.state, pane_id); @@ -1186,14 +1185,12 @@ impl App { Some("Clear pane name"), ) => { if let Some(pane_id) = self.public_pane_id(ws_idx, pane_id) { - self.dispatch_runtime_mutation( + self.runtime_pane_rename( "tui.pane.clear_name", - crate::api::schema::Method::PaneRename( - crate::api::schema::PaneRenameParams { - pane_id, - label: None, - }, - ), + crate::api::schema::PaneRenameParams { + pane_id, + label: None, + }, ); } self.state.mode = Mode::Terminal; @@ -1212,14 +1209,14 @@ impl App { if let (Some(source_public_id), Some(target_public_id)) = (source_public_id, target_public_id) { - self.dispatch_runtime_mutation( + self.runtime_pane_swap( "tui.pane.swap_exact", - crate::api::schema::Method::PaneSwap(crate::api::schema::PaneSwapParams { + crate::api::schema::PaneSwapParams { pane_id: None, direction: None, source_pane_id: Some(source_public_id), target_pane_id: Some(target_public_id), - }), + }, ); self.focus_pane_internal_via_api(ws_idx, source_pane_id); } @@ -1262,12 +1259,13 @@ impl App { Some("Close pane"), ) => { self.focus_pane_internal_via_api(ws_idx, pane_id); - self.close_focused_pane_via_api(); - self.state.mode = if self.state.active.is_some() { - Mode::Terminal - } else { - Mode::Navigate - }; + if !self.close_focused_pane_via_api_requires_confirmation() { + self.state.mode = if self.state.active.is_some() { + Mode::Terminal + } else { + Mode::Navigate + }; + } } _ => leave_modal(&mut self.state), } @@ -1305,6 +1303,7 @@ mod tests { use super::super::{capture_snapshot, state_with_workspaces}; use super::*; + use crate::workspace::Workspace; fn config_env_lock() -> &'static std::sync::Mutex<()> { crate::config::test_config_env_lock() @@ -1322,6 +1321,32 @@ mod tests { std::env::temp_dir().join(unique).join("config.toml") } + fn app_with_test_workspaces(names: &[&str]) -> App { + let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel(); + let mut app = App::new( + &crate::config::Config::default(), + true, + None, + api_rx, + crate::api::EventHub::default(), + ); + app.state.workspaces = names.iter().map(|name| Workspace::test_new(name)).collect(); + app.state.ensure_test_terminals(); + app.state.active = (!app.state.workspaces.is_empty()).then_some(0); + app.state.selected = 0; + app + } + + fn mark_worktree_space_member(state: &mut AppState, ws_idx: usize, key: &str) { + state.workspaces[ws_idx].worktree_space = Some(crate::workspace::WorktreeSpaceMembership { + key: key.into(), + label: "herdr".into(), + repo_root: "/repo/herdr".into(), + checkout_path: format!("/repo/worktree-{ws_idx}").into(), + is_linked_worktree: ws_idx != 0, + }); + } + #[test] fn custom_resize_key_exits_resize_mode() { let mut state = state_with_workspaces(&["test"]); @@ -1865,4 +1890,71 @@ mod tests { assert_eq!(state.mode, Mode::ConfirmClose); assert_eq!(state.workspaces.len(), 2); } + + #[test] + fn api_context_menu_close_tab_last_parent_group_workspace_keeps_confirmation_mode() { + let mut app = app_with_test_workspaces(&["main", "issue"]); + mark_worktree_space_member(&mut app.state, 0, "repo-key"); + mark_worktree_space_member(&mut app.state, 1, "repo-key"); + app.state.active = Some(0); + app.state.selected = 1; + app.state.mode = Mode::ContextMenu; + let menu = ContextMenuState { + kind: ContextMenuKind::Tab { + ws_idx: 0, + tab_idx: 0, + }, + x: 0, + y: 0, + list: MenuListState::new(0), + }; + let idx = menu + .items() + .iter() + .position(|item| *item == "Close") + .expect("close tab item"); + + app.apply_context_menu_action_via_api(menu, idx); + + assert_eq!(app.state.selected, 0); + assert_eq!(app.state.mode, Mode::ConfirmClose); + assert_eq!(app.state.workspaces.len(), 2); + } + + #[test] + fn api_context_menu_enter_close_pane_last_parent_group_pane_keeps_confirmation_mode() { + let mut app = app_with_test_workspaces(&["main", "issue"]); + mark_worktree_space_member(&mut app.state, 0, "repo-key"); + mark_worktree_space_member(&mut app.state, 1, "repo-key"); + app.state.active = Some(0); + app.state.selected = 1; + app.state.mode = Mode::ContextMenu; + let pane_id = app.state.workspaces[0].tabs[0].root_pane; + let mut menu = ContextMenuState { + kind: ContextMenuKind::Pane { + ws_idx: 0, + tab_idx: 0, + pane_id, + source_pane_id: None, + has_manual_label: false, + }, + x: 0, + y: 0, + list: MenuListState::new(0), + }; + let close_idx = menu + .items() + .iter() + .position(|item| *item == "Close pane") + .expect("close pane item"); + menu.list.highlighted = close_idx; + app.state.context_menu = Some(menu); + + app.handle_context_menu_key_via_api(KeyEvent::new(KeyCode::Enter, KeyModifiers::empty())); + + assert_eq!(app.state.selected, 0); + assert_eq!(app.state.mode, Mode::ConfirmClose); + assert_eq!(app.state.workspaces.len(), 2); + assert!(app.state.context_menu.is_none()); + } } |
