diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-29 18:57:13 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-29 18:57:13 +0300 |
| commit | 97f78226bf1f34dcdef164291a9c50fddf448c6e (patch) | |
| tree | 20f8793644f314521c6e064bef8ba4b25576524b /src | |
| parent | 1a4e94e57e181379e2ed42d63a49fbc56ed1dba4 (diff) | |
fix: route tui modal mutations through api authority
Diffstat (limited to 'src')
| -rw-r--r-- | src/app/actions.rs | 1 | ||||
| -rw-r--r-- | src/app/api/worktrees.rs | 1 | ||||
| -rw-r--r-- | src/app/input/mod.rs | 10 | ||||
| -rw-r--r-- | src/app/input/modal.rs | 73 | ||||
| -rw-r--r-- | src/app/input/mouse.rs | 98 | ||||
| -rw-r--r-- | src/app/mod.rs | 53 | ||||
| -rw-r--r-- | src/app/worktrees.rs | 94 | ||||
| -rw-r--r-- | src/server/headless.rs | 6 |
8 files changed, 278 insertions, 58 deletions
diff --git a/src/app/actions.rs b/src/app/actions.rs index 56f6e789..d791da03 100644 --- a/src/app/actions.rs +++ b/src/app/actions.rs @@ -1587,6 +1587,7 @@ impl AppState { } } + #[cfg(test)] pub fn resize_pane(&mut self, direction: NavDirection) { if let Some(first) = self.view.pane_infos.first() { let area = self diff --git a/src/app/api/worktrees.rs b/src/app/api/worktrees.rs index 111d50cf..69f43c1b 100644 --- a/src/app/api/worktrees.rs +++ b/src/app/api/worktrees.rs @@ -611,6 +611,7 @@ impl App { }); } + #[cfg(test)] pub(crate) fn emit_worktree_opened_for_workspace(&mut self, ws_idx: usize, already_open: bool) { let Some(worktree) = self.worktree_info_for_workspace(ws_idx) else { return; diff --git a/src/app/input/mod.rs b/src/app/input/mod.rs index 31a99c23..164c4dd4 100644 --- a/src/app/input/mod.rs +++ b/src/app/input/mod.rs @@ -45,8 +45,7 @@ mod terminal; pub(crate) use self::{ modal::{ - handle_confirm_close_key, handle_context_menu_key, handle_global_menu_key, - handle_keybind_help_key, handle_navigator_key, handle_rename_key, handle_resize_key, + handle_global_menu_key, handle_keybind_help_key, handle_navigator_key, insert_navigator_search_text, insert_rename_input_text, }, navigate::terminal_direct_navigation_action, @@ -318,6 +317,13 @@ impl App { MouseAction::SetSplitRatio { path, ratio } => { self.set_split_ratio_via_api(path, ratio) } + MouseAction::RenameModal(action) => { + self.apply_rename_mouse_action_via_api(action) + } + MouseAction::ConfirmCloseAccept => self.confirm_close_accept_via_api(), + MouseAction::ContextMenu { menu, idx } => { + self.apply_context_menu_action_via_api(menu, idx) + } } } } diff --git a/src/app/input/modal.rs b/src/app/input/modal.rs index 2c3f4e4a..e63cdb70 100644 --- a/src/app/input/modal.rs +++ b/src/app/input/modal.rs @@ -1,5 +1,7 @@ use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; -use ratatui::layout::{Direction, Rect}; +#[cfg(test)] +use ratatui::layout::Direction; +use ratatui::layout::Rect; use crate::{ app::{ @@ -426,6 +428,7 @@ pub(super) const SETTINGS_ACTIONS: &[ModalActionSpec<ModalAction>] = &[ }, ]; +#[cfg(test)] pub(super) fn apply_rename_action(state: &mut AppState, action: ModalAction) { match action { ModalAction::Save => { @@ -588,12 +591,7 @@ fn delete_rename_input_word(state: &mut AppState) { } } -pub(crate) fn handle_rename_key(state: &mut AppState, key: KeyEvent) { - if let Some(action) = modal_action_from_key(&key, RENAME_ACTIONS) { - apply_rename_action(state, action); - return; - } - +fn handle_rename_edit_key(state: &mut AppState, key: KeyEvent) { match key.code { KeyCode::Char('u') if key.modifiers.contains(KeyModifiers::CONTROL) => { clear_rename_input(state); @@ -618,6 +616,17 @@ pub(crate) fn handle_rename_key(state: &mut AppState, key: KeyEvent) { } } +#[cfg(test)] +pub(crate) fn handle_rename_key(state: &mut AppState, key: KeyEvent) { + if let Some(action) = modal_action_from_key(&key, RENAME_ACTIONS) { + apply_rename_action(state, action); + return; + } + + handle_rename_edit_key(state, key); +} + +#[cfg(test)] pub(crate) fn handle_resize_key(state: &mut AppState, raw_key: TerminalKey) { let key = raw_key.as_key_event(); if key.code == KeyCode::Esc @@ -646,6 +655,7 @@ pub(super) fn open_confirm_close(state: &mut AppState) { state.mode = Mode::ConfirmClose; } +#[cfg(test)] pub(super) fn confirm_close_accept(state: &mut AppState) { state.close_selected_workspace(); if state.workspaces.is_empty() { @@ -659,6 +669,7 @@ pub(super) fn confirm_close_cancel(state: &mut AppState) { state.mode = Mode::Navigate; } +#[cfg(test)] pub(crate) fn handle_confirm_close_key(state: &mut AppState, key: KeyEvent) { match modal_action_from_key(&key, CONFIRM_CLOSE_ACTIONS) { Some(ModalAction::Confirm) => confirm_close_accept(state), @@ -667,6 +678,7 @@ pub(crate) fn handle_confirm_close_key(state: &mut AppState, key: KeyEvent) { } } +#[cfg(test)] pub(super) fn apply_context_menu_action( state: &mut AppState, terminal_runtimes: &mut crate::terminal::TerminalRuntimeRegistry, @@ -870,6 +882,7 @@ pub(super) fn apply_context_menu_action( } } +#[cfg(test)] pub(crate) fn handle_context_menu_key( state: &mut AppState, terminal_runtimes: &mut crate::terminal::TerminalRuntimeRegistry, @@ -903,19 +916,11 @@ pub(crate) fn handle_context_menu_key( impl App { pub(crate) fn handle_rename_key_via_api(&mut self, key: KeyEvent) { if let Some(action) = modal_action_from_key(&key, RENAME_ACTIONS) { - match action { - ModalAction::Save => self.save_rename_modal_via_api(), - ModalAction::Clear => { - self.state.name_input.clear(); - self.state.name_input_replace_on_type = false; - } - ModalAction::Cancel => cancel_rename_modal(&mut self.state), - _ => {} - } + self.apply_rename_mouse_action_via_api(action); return; } - handle_rename_key(&mut self.state, key); + handle_rename_edit_key(&mut self.state, key); } fn save_rename_modal_via_api(&mut self) { @@ -1006,6 +1011,30 @@ impl App { cancel_rename_modal(&mut self.state); } + pub(super) fn apply_rename_mouse_action_via_api(&mut self, action: ModalAction) { + match action { + ModalAction::Save => self.save_rename_modal_via_api(), + ModalAction::Clear => { + self.state.name_input.clear(); + self.state.name_input_replace_on_type = false; + } + ModalAction::Cancel => cancel_rename_modal(&mut self.state), + _ => {} + } + } + + pub(super) fn confirm_close_accept_via_api(&mut self) { + let ws_idx = self.state.selected; + if ws_idx < self.state.workspaces.len() { + self.close_workspace_idx_via_api(ws_idx); + } + self.state.mode = if self.state.active.is_some() { + Mode::Terminal + } else { + Mode::Navigate + }; + } + pub(crate) fn handle_resize_key_via_api(&mut self, raw_key: TerminalKey) { let key = raw_key.as_key_event(); if key.code == KeyCode::Esc @@ -1043,15 +1072,7 @@ impl App { pub(crate) fn handle_confirm_close_key_via_api(&mut self, key: KeyEvent) { match modal_action_from_key(&key, CONFIRM_CLOSE_ACTIONS) { Some(ModalAction::Confirm) => { - let ws_idx = self.state.selected; - if ws_idx < self.state.workspaces.len() { - self.close_workspace_idx_via_api(ws_idx); - } - self.state.mode = if self.state.active.is_some() { - Mode::Terminal - } else { - Mode::Navigate - }; + self.confirm_close_accept_via_api(); } Some(ModalAction::Cancel) => confirm_close_cancel(&mut self.state), _ => {} diff --git a/src/app/input/mouse.rs b/src/app/input/mouse.rs index 71c0cfbd..7f9f9cde 100644 --- a/src/app/input/mouse.rs +++ b/src/app/input/mouse.rs @@ -18,8 +18,7 @@ use crate::{ use super::WheelRouting; use super::{ modal::{ - apply_context_menu_action, apply_global_menu_action, apply_rename_action, - confirm_close_accept, confirm_close_cancel, global_menu_actions, leave_modal, + apply_global_menu_action, confirm_close_cancel, global_menu_actions, leave_modal, modal_action_from_buttons, open_global_menu, open_new_tab_dialog, ModalAction, }, settings::SettingsAction, @@ -52,6 +51,12 @@ pub(super) enum MouseAction { path: Vec<bool>, ratio: f32, }, + RenameModal(ModalAction), + ConfirmCloseAccept, + ContextMenu { + menu: ContextMenuState, + idx: usize, + }, } enum MobileMouseResult { @@ -234,7 +239,9 @@ impl AppState { (cancel, ModalAction::Cancel), ], ) { - Some(ModalAction::Confirm) => confirm_close_accept(self), + Some(ModalAction::Confirm) => { + return Some(MouseAction::ConfirmCloseAccept); + } Some(ModalAction::Cancel) | None => confirm_close_cancel(self), _ => {} } @@ -396,15 +403,14 @@ impl AppState { ) }) .unwrap_or(ModalAction::Cancel); - apply_rename_action(self, action); - return None; + return Some(MouseAction::RenameModal(action)); } if self.mode == Mode::ContextMenu { let item_idx = self.context_menu_item_at(mouse.column, mouse.row); if let Some(menu) = self.context_menu.take() { if let Some(idx) = item_idx { - apply_context_menu_action(self, terminal_runtimes, menu, idx); + return Some(MouseAction::ContextMenu { menu, idx }); } else { leave_modal(self); } @@ -900,8 +906,26 @@ impl AppState { if self.on_tab_bar(mouse.column, mouse.row) => { match mouse.kind { - MouseEventKind::ScrollUp => self.previous_tab(), - MouseEventKind::ScrollDown => self.next_tab(), + MouseEventKind::ScrollUp => { + if let Some(ws) = self.active.and_then(|i| self.workspaces.get(i)) { + if !ws.tabs.is_empty() { + let prev = if ws.active_tab == 0 { + ws.tabs.len() - 1 + } else { + ws.active_tab - 1 + }; + return Some(MouseAction::FocusTab { tab_idx: prev }); + } + } + } + MouseEventKind::ScrollDown => { + if let Some(ws) = self.active.and_then(|i| self.workspaces.get(i)) { + if !ws.tabs.is_empty() { + let next = (ws.active_tab + 1) % ws.tabs.len(); + return Some(MouseAction::FocusTab { tab_idx: next }); + } + } + } _ => {} } } @@ -1814,10 +1838,10 @@ mod tests { use ratatui::layout::{Direction, Rect}; use super::super::{ - app_for_mouse_test, capture_snapshot, handle_context_menu_key, mouse, numbered_lines_bytes, - root_layout_ratio, + app_for_mouse_test, capture_snapshot, mouse, numbered_lines_bytes, root_layout_ratio, }; use super::*; + use crate::app::input::modal::handle_context_menu_key; use crate::{ app::state::{ContextMenuKind, ContextMenuState, MenuListState, Mode, ViewLayout}, detect::{Agent, AgentState}, @@ -2450,6 +2474,31 @@ mod tests { } #[test] + fn clicking_rename_save_submits_workspace_rename_through_api_path() { + let mut app = app_for_mouse_test(); + app.state.workspaces = vec![Workspace::test_new("old")]; + app.state.active = Some(0); + app.state.selected = 0; + app.state.mode = Mode::RenameWorkspace; + app.state.name_input = "new".into(); + + crate::ui::compute_view(&mut app.state, Rect::new(0, 0, 106, 24)); + let inner = app.state.rename_modal_inner().unwrap(); + let (save, _, _) = crate::ui::rename_button_rects(inner); + + app.handle_mouse(mouse( + MouseEventKind::Down(MouseButton::Left), + save.x, + save.y, + )); + + assert_eq!(app.state.workspaces[0].custom_name.as_deref(), Some("new")); + assert!(app.event_hub.events_after(0).iter().any(|(_, event)| { + matches!(event.event, crate::api::schema::EventKind::WorkspaceRenamed) + })); + } + + #[test] fn clicking_open_worktree_row_selects_and_requests_open() { let mut app = app_for_mouse_test(); app.state.mode = Mode::OpenExistingWorktree; @@ -2615,6 +2664,35 @@ mod tests { assert_eq!(app.state.workspaces[0].display_name(), "a"); } + #[test] + fn clicking_context_menu_close_routes_through_api_path() { + let mut app = app_for_mouse_test(); + app.state.workspaces = vec![Workspace::test_new("a"), Workspace::test_new("b")]; + app.state.active = Some(0); + app.state.selected = 0; + app.state.confirm_close = false; + app.state.context_menu = Some(ContextMenuState { + kind: ContextMenuKind::Workspace { ws_idx: 1 }, + x: 2, + y: 2, + list: MenuListState::new(1), + }); + app.state.mode = Mode::ContextMenu; + + let menu = app.state.context_menu_rect().unwrap(); + app.handle_mouse(mouse( + MouseEventKind::Down(MouseButton::Left), + menu.x + 2, + menu.y + 2, + )); + + assert_eq!(app.state.workspaces.len(), 1); + assert_eq!(app.state.workspaces[0].display_name(), "a"); + assert!(app.event_hub.events_after(0).iter().any(|(_, event)| { + matches!(event.event, crate::api::schema::EventKind::WorkspaceClosed) + })); + } + #[cfg(unix)] #[tokio::test] async fn keyboard_context_menu_split_keeps_new_runtime() { diff --git a/src/app/mod.rs b/src/app/mod.rs index 65863fd8..2860664f 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1619,7 +1619,7 @@ impl App { self.handle_copy_mode_key(key); } Mode::RenameWorkspace | Mode::RenameTab | Mode::RenamePane => { - input::handle_rename_key(&mut self.state, key_event); + self.handle_rename_key_via_api(key_event); } Mode::NewLinkedWorktree => { self.handle_worktree_create_key(key_event); @@ -1631,17 +1631,13 @@ impl App { self.handle_worktree_remove_key(key_event); } Mode::Resize => { - input::handle_resize_key(&mut self.state, key); + self.handle_resize_key_via_api(key); } Mode::ConfirmClose => { - input::handle_confirm_close_key(&mut self.state, key_event); + self.handle_confirm_close_key_via_api(key_event); } Mode::ContextMenu => { - input::handle_context_menu_key( - &mut self.state, - &mut self.terminal_runtimes, - key_event, - ); + self.handle_context_menu_key_via_api(key_event); } Mode::KeybindHelp => { input::handle_keybind_help_key(&mut self.state, key_event); @@ -4364,6 +4360,47 @@ last_pane = "prefix+tab" } #[test] + fn route_client_input_rename_enter_submits_through_api_path() { + let mut app = test_app(); + app.state.workspaces = vec![Workspace::test_new("old")]; + app.state.active = Some(0); + app.state.selected = 0; + app.state.mode = Mode::RenameWorkspace; + app.state.name_input = "new".into(); + + app.route_client_input(b"\r".to_vec()); + + assert_eq!(app.state.workspaces[0].custom_name.as_deref(), Some("new")); + assert!(app.event_hub.events_after(0).iter().any(|(_, event)| { + matches!(event.event, crate::api::schema::EventKind::WorkspaceRenamed) + })); + } + + #[test] + fn route_client_input_context_menu_enter_submits_through_api_path() { + let mut app = test_app(); + app.state.workspaces = vec![Workspace::test_new("a"), Workspace::test_new("b")]; + app.state.active = Some(0); + app.state.selected = 0; + app.state.confirm_close = false; + app.state.context_menu = Some(state::ContextMenuState { + kind: state::ContextMenuKind::Workspace { ws_idx: 1 }, + x: 2, + y: 2, + list: state::MenuListState::new(1), + }); + app.state.mode = Mode::ContextMenu; + + app.route_client_input(b"\r".to_vec()); + + assert_eq!(app.state.workspaces.len(), 1); + assert_eq!(app.state.workspaces[0].display_name(), "a"); + assert!(app.event_hub.events_after(0).iter().any(|(_, event)| { + matches!(event.event, crate::api::schema::EventKind::WorkspaceClosed) + })); + } + + #[test] fn raw_ctrl_v_decodes_as_modal_paste_shortcut() { let events = crate::raw_input::parse_raw_input_bytes_sync(&[0x16]); let Some(crate::raw_input::RawInputEvent::Key(key)) = events.first() else { diff --git a/src/app/worktrees.rs b/src/app/worktrees.rs index d33e7f3c..ce8be5ec 100644 --- a/src/app/worktrees.rs +++ b/src/app/worktrees.rs @@ -7,7 +7,9 @@ use super::{ state::{WorktreeCreateState, WorktreeOpenEntry, WorktreeOpenState, WorktreeRemoveState}, App, Mode, }; -use crate::events::{AppEvent, WorktreeAddResult, WorktreeRemoveResult}; +#[cfg(test)] +use crate::events::AppEvent; +use crate::events::{WorktreeAddResult, WorktreeRemoveResult}; impl App { fn worktree_source_metadata( @@ -245,7 +247,7 @@ impl App { } self.close_worktree_create_dialog(); } - KeyCode::Enter => self.start_worktree_add(), + KeyCode::Enter => self.submit_worktree_create_via_api(), KeyCode::Backspace => { if self.state.name_input_replace_on_type { self.state.name_input.clear(); @@ -324,7 +326,7 @@ impl App { open.normalize_selection(); } } - KeyCode::Enter => self.open_selected_existing_worktree(), + KeyCode::Enter => self.submit_worktree_open_via_api(), _ => {} } } @@ -340,6 +342,7 @@ impl App { open.normalize_selection(); } + #[cfg(test)] pub(crate) fn open_selected_existing_worktree(&mut self) { let Some(open) = self.state.worktree_open.as_ref() else { return; @@ -432,6 +435,7 @@ impl App { // The caller has already extracted the open-worktree dialog state; keeping the // membership fields explicit here avoids borrowing AppState across workspace creation. #[allow(clippy::too_many_arguments)] + #[cfg(test)] fn mark_opened_existing_worktree_membership( &mut self, source_workspace_id: &str, @@ -497,6 +501,7 @@ impl App { create.error = None; } + #[cfg(test)] pub(crate) fn start_worktree_add(&mut self) { self.sync_worktree_branch_from_input(); let Some(create) = &mut self.state.worktree_create else { @@ -623,11 +628,12 @@ impl App { Mode::Navigate }; } - KeyCode::Enter => self.start_worktree_remove(), + KeyCode::Enter => self.submit_worktree_remove_via_api(), _ => {} } } + #[cfg(test)] pub(crate) fn start_worktree_remove(&mut self) { let Some((workspace_id, repo_root, path, force)) = self.state.worktree_remove.as_mut().and_then(|remove| { @@ -1519,7 +1525,7 @@ mod tests { } #[test] - fn submit_worktree_create_via_api_restores_modal_on_immediate_rejection() { + fn worktree_create_enter_submits_through_api_path() { let mut app = app_for_worktree_tests(); app.state.worktree_directory = std::path::PathBuf::from("/w"); app.state.workspaces = vec![crate::workspace::Workspace::test_new("source")]; @@ -1531,12 +1537,13 @@ mod tests { checkout_path: "/repo/herdr".into(), is_linked_worktree: false, }; - let branch = "issue/191"; + let branch = "issue/195"; let checkout_path = crate::worktree::default_checkout_path(&app.state.worktree_directory, "herdr", branch); let checkout_key = crate::worktree::canonical_or_original(&checkout_path); app.pending_api_worktree_creates.insert(checkout_key, 1); app.state.workspaces[0].worktree_space = Some(source_membership.clone()); + app.state.mode = Mode::NewLinkedWorktree; app.state.name_input = branch.into(); app.state.worktree_create = Some(WorktreeCreateState { source_workspace_id, @@ -1551,7 +1558,7 @@ mod tests { creating: false, }); - app.submit_worktree_create_via_api(); + app.handle_worktree_create_key(KeyEvent::new(KeyCode::Enter, KeyModifiers::empty())); let create = app.state.worktree_create.as_ref().unwrap(); assert!(!create.creating); @@ -1561,8 +1568,77 @@ mod tests { ); } + #[tokio::test] + async fn worktree_open_enter_submits_through_api_path() { + let repo = create_committed_repo("app-worktree-open-enter-repo"); + let checkout = unique_temp_path("app-worktree-open-enter-checkout"); + run_git( + &repo, + &[ + "worktree", + "add", + "--quiet", + "-b", + "worktree/open-enter", + checkout.to_str().unwrap(), + "HEAD", + ], + ); + + let event_hub = crate::api::EventHub::default(); + let mut app = app_for_worktree_tests_with_event_hub(event_hub.clone()); + app.state.workspaces = vec![crate::workspace::Workspace::test_new("source")]; + let source_workspace_id = app.state.workspaces[0].id.clone(); + let source_membership = crate::workspace::WorktreeSpaceMembership { + key: "repo-key".into(), + label: "herdr".into(), + repo_root: repo.clone(), + checkout_path: repo.clone(), + is_linked_worktree: false, + }; + app.state.workspaces[0].worktree_space = Some(source_membership.clone()); + app.state.mode = Mode::OpenExistingWorktree; + app.state.worktree_open = Some(WorktreeOpenState { + source_workspace_id, + source_existing_membership: Some(source_membership), + source_checkout_path: repo.clone(), + source_repo_root: repo.clone(), + repo_key: "repo-key".into(), + repo_name: "herdr".into(), + entries: vec![WorktreeOpenEntry { + path: checkout.clone(), + branch: Some("worktree/open-enter".into()), + is_linked_worktree: true, + already_open_ws_idx: None, + }], + selected: 0, + query: String::new(), + search_focused: false, + error: None, + }); + + app.handle_worktree_open_key(KeyEvent::new(KeyCode::Enter, KeyModifiers::empty())); + + assert!(app.state.worktree_open.is_none()); + assert_eq!( + event_kinds(&event_hub), + vec![ + crate::api::schema::EventKind::WorkspaceCreated, + crate::api::schema::EventKind::TabCreated, + crate::api::schema::EventKind::PaneCreated, + crate::api::schema::EventKind::WorktreeOpened, + ] + ); + shutdown_test_runtimes(&mut app); + run_git( + &repo, + &["worktree", "remove", "--force", checkout.to_str().unwrap()], + ); + let _ = std::fs::remove_dir_all(repo); + } + #[test] - fn submit_worktree_remove_via_api_restores_modal_on_immediate_rejection() { + fn worktree_remove_enter_submits_through_api_path() { let mut app = app_for_worktree_tests(); app.state.workspaces = vec![crate::workspace::Workspace::test_new("issue")]; let workspace_id = app.state.workspaces[0].id.clone(); @@ -1577,7 +1653,7 @@ mod tests { app.pending_api_worktree_removes .insert(workspace_id.clone(), 1); - app.submit_worktree_remove_via_api(); + app.handle_worktree_remove_key(KeyEvent::new(KeyCode::Enter, KeyModifiers::empty())); let remove = app.state.worktree_remove.as_ref().unwrap(); assert_eq!(remove.workspace_id, workspace_id); diff --git a/src/server/headless.rs b/src/server/headless.rs index 36519d47..469320f9 100644 --- a/src/server/headless.rs +++ b/src/server/headless.rs @@ -559,7 +559,7 @@ impl HeadlessServer { if self.app.state.request_submit_worktree_create { self.app.state.request_submit_worktree_create = false; - self.app.start_worktree_add(); + self.app.submit_worktree_create_via_api(); needs_render = true; needs_full_render = true; crate::render_prof::event("full_render_cause.deferred_worktree_submit"); @@ -567,7 +567,7 @@ impl HeadlessServer { if self.app.state.request_submit_worktree_open { self.app.state.request_submit_worktree_open = false; - self.app.open_selected_existing_worktree(); + self.app.submit_worktree_open_via_api(); needs_render = true; needs_full_render = true; crate::render_prof::event("full_render_cause.deferred_worktree_submit"); @@ -575,7 +575,7 @@ impl HeadlessServer { if self.app.state.request_submit_worktree_remove { self.app.state.request_submit_worktree_remove = false; - self.app.start_worktree_remove(); + self.app.submit_worktree_remove_via_api(); needs_render = true; needs_full_render = true; crate::render_prof::event("full_render_cause.deferred_worktree_submit"); |
