From c1ca8038451f5350d1fb3cd3c38e363fac3ae66c Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Sun, 14 Jun 2026 20:22:47 +0300 Subject: fix: drop plugin pane records when layout apply replaces a tab --- src/app/api/layouts.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'src/app/api') diff --git a/src/app/api/layouts.rs b/src/app/api/layouts.rs index 9c492937..32dec32a 100644 --- a/src/app/api/layouts.rs +++ b/src/app/api/layouts.rs @@ -159,10 +159,12 @@ impl App { let terminal_ids = self .state .terminal_ids_for_tab(target_ws_idx, target_tab_idx); + let plugin_pane_ids = self.state.pane_ids_for_tab(target_ws_idx, target_tab_idx); let Some(ws) = self.state.workspaces.get_mut(target_ws_idx) else { return encode_error(id, "tab_not_found", "tab not found"); }; if ws.close_tab(target_tab_idx) { + self.state.remove_plugin_pane_records(plugin_pane_ids); self.state.remove_unattached_terminal_ids(terminal_ids); self.shutdown_detached_terminal_runtimes(); self.emit_event(EventEnvelope { @@ -464,12 +466,14 @@ impl App { return; }; let terminal_ids = self.state.terminal_ids_for_tab(ws_idx, tab_idx); + let plugin_pane_ids = self.state.pane_ids_for_tab(ws_idx, tab_idx); if self .state .workspaces .get_mut(ws_idx) .is_some_and(|ws| ws.close_tab(tab_idx)) { + self.state.remove_plugin_pane_records(plugin_pane_ids); self.state.remove_unattached_terminal_ids(terminal_ids); self.shutdown_detached_terminal_runtimes(); } @@ -699,6 +703,41 @@ mod tests { ); } + #[tokio::test] + async fn layout_apply_replace_drops_plugin_pane_records_of_replaced_tab() { + let mut app = app_with_workspace(); + let original_tab_id = app.public_tab_id(0, 0).unwrap(); + let replaced_pane = app.state.workspaces[0].tabs[0].root_pane; + app.state.plugin_panes.insert( + replaced_pane, + crate::app::state::PluginPaneRecord { + plugin_id: "example.layout".into(), + entrypoint: "board".into(), + }, + ); + + let response = app.handle_layout_apply( + "req".into(), + LayoutApplyParams { + workspace_id: None, + tab_id: Some(original_tab_id), + tab_label: Some("dev".into()), + focus: true, + root: LayoutNode::Pane { + pane: LayoutPane { + label: Some("editor".into()), + ..Default::default() + }, + }, + }, + ); + + let success: SuccessResponse = serde_json::from_str(&response).unwrap(); + assert!(matches!(success.result, ResponseResult::LayoutApply { .. })); + assert!(!app.state.plugin_panes.contains_key(&replaced_pane)); + app.state.assert_invariants_for_test(); + } + #[tokio::test] async fn layout_apply_rejects_invalid_deep_leaf_without_creating_tab() { let mut app = app_with_workspace(); -- cgit v1.2.3