aboutsummaryrefslogtreecommitdiff
path: root/src/app/api.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-07-04 15:31:14 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-07-04 16:51:10 +0300
commit15cab964f3494b77159caaea289e17e1596e0720 (patch)
treed9614056c26b0d148771a464fc2eb0f0cabc60a2 /src/app/api.rs
parentb78a8a5c565e66e7e5ba9a06d9cb7f3a771fc4b0 (diff)
feat: emit layout update events for runtime layout changes
Diffstat (limited to 'src/app/api.rs')
-rw-r--r--src/app/api.rs94
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;