diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-04 15:31:14 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-04 16:51:10 +0300 |
| commit | 15cab964f3494b77159caaea289e17e1596e0720 (patch) | |
| tree | d9614056c26b0d148771a464fc2eb0f0cabc60a2 /src/app/api.rs | |
| parent | b78a8a5c565e66e7e5ba9a06d9cb7f3a771fc4b0 (diff) | |
feat: emit layout update events for runtime layout changes
Diffstat (limited to 'src/app/api.rs')
| -rw-r--r-- | src/app/api.rs | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/src/app/api.rs b/src/app/api.rs index 8743c08f..96c34393 100644 --- a/src/app/api.rs +++ b/src/app/api.rs @@ -188,6 +188,13 @@ impl App { } } } + let pane_exit_layout_target = if let AppEvent::PaneDied { pane_id } = &ev { + self.find_pane(*pane_id).and_then(|(ws_idx, _)| { + self.layout_update_target_after_pane_removal(ws_idx, *pane_id) + }) + } else { + None + }; let released_agent = if let AppEvent::HookAgentReleased { pane_id, @@ -259,6 +266,9 @@ impl App { tab_zoomed_before_exit, ); } + if let Some((ws_idx, tab_idx)) = pane_exit_layout_target { + self.emit_layout_updated_event(ws_idx, tab_idx); + } if self.local_terminal_notifications && matches!( @@ -1727,6 +1737,90 @@ mod tests { } #[test] + fn pane_exit_emits_layout_updated_when_tab_survives() { + let event_hub = crate::api::EventHub::default(); + let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel(); + let mut app = App::new( + &crate::config::Config::default(), + true, + None, + api_rx, + event_hub.clone(), + ); + let mut workspace = crate::workspace::Workspace::test_new("pane-exit-layout"); + let dead_pane = workspace.test_split(ratatui::layout::Direction::Horizontal); + app.state.workspaces = vec![workspace]; + app.state.ensure_test_terminals(); + let tab_id = app.public_tab_id(0, 0).unwrap(); + + app.handle_internal_event(AppEvent::PaneDied { pane_id: dead_pane }); + + let events = event_hub.events_after(0); + let pane_exited = events + .iter() + .position(|(_, event)| event.event == crate::api::schema::EventKind::PaneExited) + .expect("pane.exited should be emitted"); + let layout_updated = events + .iter() + .position(|(_, event)| event.event == crate::api::schema::EventKind::LayoutUpdated) + .expect("layout.updated should be emitted"); + assert!(pane_exited < layout_updated); + assert!(matches!( + &events[layout_updated].1.data, + crate::api::schema::EventData::LayoutUpdated { layout } + if layout.tab_id == tab_id && layout.panes.len() == 1 + )); + } + + #[test] + fn overlay_exit_layout_updated_uses_restored_zoom_state() { + let event_hub = crate::api::EventHub::default(); + let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel(); + let mut app = App::new( + &crate::config::Config::default(), + true, + None, + api_rx, + event_hub.clone(), + ); + let mut workspace = crate::workspace::Workspace::test_new("overlay-layout"); + let previous_focus = workspace.tabs[0].root_pane; + let overlay_pane = workspace.test_split(ratatui::layout::Direction::Horizontal); + workspace.tabs[0].layout.focus_pane(previous_focus); + workspace.tabs[0].zoomed = true; + app.state.workspaces = vec![workspace]; + app.state.ensure_test_terminals(); + app.state.active = Some(0); + app.state.mode = Mode::Terminal; + let tab_id = app.public_tab_id(0, 0).unwrap(); + app.overlay_panes.insert( + overlay_pane, + OverlayPaneState { + ws_idx: 0, + tab_idx: 0, + previous_focus, + previous_zoomed: false, + temp_files: Vec::new(), + }, + ); + + app.handle_internal_event(AppEvent::PaneDied { + pane_id: overlay_pane, + }); + + let events = event_hub.events_after(0); + let layout_updated = events + .iter() + .rposition(|(_, event)| event.event == crate::api::schema::EventKind::LayoutUpdated) + .expect("layout.updated should be emitted"); + assert!(matches!( + &events[layout_updated].1.data, + crate::api::schema::EventData::LayoutUpdated { layout } + if layout.tab_id == tab_id && layout.zoomed + )); + } + + #[test] fn overlay_exit_preserves_same_tab_focus_changed_before_exit() { let mut workspace = crate::workspace::Workspace::test_new("overlay"); let previous_focus = workspace.tabs[0].root_pane; |
