aboutsummaryrefslogtreecommitdiff
path: root/src/app/api/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/api/plugins')
-rw-r--r--src/app/api/plugins/context.rs11
-rw-r--r--src/app/api/plugins/mod.rs159
-rw-r--r--src/app/api/plugins/panes.rs29
3 files changed, 197 insertions, 2 deletions
diff --git a/src/app/api/plugins/context.rs b/src/app/api/plugins/context.rs
index e81a74d3..ead5c8e2 100644
--- a/src/app/api/plugins/context.rs
+++ b/src/app/api/plugins/context.rs
@@ -121,6 +121,17 @@ impl App {
context.tab_id = Some(tab_id.clone());
context
}),
+ EventData::LayoutUpdated { layout } => self
+ .plugin_context_for_tab_id(&layout.tab_id, correlation_id)
+ .or_else(|| {
+ self.plugin_context_for_workspace_id(&layout.workspace_id, correlation_id)
+ })
+ .unwrap_or_else(|| {
+ let mut context = empty_plugin_context(correlation_id);
+ context.workspace_id = Some(layout.workspace_id.clone());
+ context.tab_id = Some(layout.tab_id.clone());
+ context
+ }),
EventData::PaneCreated { pane } => {
self.plugin_context_for_pane_info(pane, correlation_id)
}
diff --git a/src/app/api/plugins/mod.rs b/src/app/api/plugins/mod.rs
index 833134b5..ec0a5d64 100644
--- a/src/app/api/plugins/mod.rs
+++ b/src/app/api/plugins/mod.rs
@@ -1424,7 +1424,166 @@ command = ["sh", "-c", "sleep 1"]
.iter()
.position(|event| *event == crate::api::schema::EventKind::PaneCreated)
.expect("pane.created should be emitted");
+ let layout_updated = events
+ .iter()
+ .position(|event| *event == crate::api::schema::EventKind::LayoutUpdated)
+ .expect("layout.updated should be emitted");
assert!(tab_created < pane_created);
+ assert!(pane_created < layout_updated);
+
+ for (_, runtime) in app.terminal_runtimes.drain() {
+ runtime.shutdown();
+ }
+ let _ = std::fs::remove_dir_all(root);
+ }
+
+ #[cfg(unix)]
+ #[tokio::test]
+ async fn plugin_pane_open_zoomed_split_emits_layout_updated() {
+ 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(),
+ );
+ app.state.workspaces = vec![crate::workspace::Workspace::test_new("plugin-split")];
+ app.state.ensure_test_terminals();
+ app.state.active = Some(0);
+ app.state.selected = 0;
+ app.state.mode = crate::app::Mode::Terminal;
+
+ let root = unique_temp_path("plugin-pane-split-layout-event");
+ write_manifest_content(
+ &root,
+ r#"
+id = "example.split"
+name = "Split Plugin"
+version = "0.1.0"
+min_herdr_version = "0.6.10"
+platforms = ["linux", "macos"]
+
+[[panes]]
+id = "board"
+title = "Plugin Board"
+placement = "split"
+command = ["sh", "-c", "sleep 1"]
+"#,
+ );
+ link_manifest(&mut app, &root);
+
+ let open = app.handle_api_request(Request {
+ id: "pane-open-split".into(),
+ method: Method::PluginPaneOpen(PluginPaneOpenParams {
+ plugin_id: "example.split".into(),
+ entrypoint: "board".into(),
+ placement: Some(PluginPanePlacement::Zoomed),
+ workspace_id: None,
+ target_pane_id: None,
+ direction: Some(crate::api::schema::SplitDirection::Right),
+ cwd: None,
+ focus: true,
+ env: std::collections::HashMap::new(),
+ }),
+ });
+ let ResponseResult::PluginPaneOpened { .. } = response_result(&open) else {
+ panic!("expected plugin pane opened response: {open}");
+ };
+
+ let events = event_hub.events_after(0);
+ let pane_created = events
+ .iter()
+ .position(|(_, event)| event.event == crate::api::schema::EventKind::PaneCreated)
+ .expect("pane.created 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_created < layout_updated);
+ assert!(matches!(
+ &events[layout_updated].1.data,
+ crate::api::schema::EventData::LayoutUpdated { layout }
+ if layout.zoomed && layout.panes.len() == 2
+ ));
+
+ for (_, runtime) in app.terminal_runtimes.drain() {
+ runtime.shutdown();
+ }
+ let _ = std::fs::remove_dir_all(root);
+ }
+
+ #[cfg(unix)]
+ #[tokio::test]
+ async fn plugin_pane_open_overlay_emits_layout_updated() {
+ 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(),
+ );
+ app.state.workspaces = vec![crate::workspace::Workspace::test_new("plugin-overlay")];
+ app.state.ensure_test_terminals();
+ app.state.active = Some(0);
+ app.state.selected = 0;
+ app.state.mode = crate::app::Mode::Terminal;
+
+ let root = unique_temp_path("plugin-pane-overlay-layout-event");
+ write_manifest_content(
+ &root,
+ r#"
+id = "example.overlay"
+name = "Overlay Plugin"
+version = "0.1.0"
+min_herdr_version = "0.6.10"
+platforms = ["linux", "macos"]
+
+[[panes]]
+id = "board"
+title = "Plugin Board"
+placement = "overlay"
+command = ["sh", "-c", "sleep 1"]
+"#,
+ );
+ link_manifest(&mut app, &root);
+
+ let open = app.handle_api_request(Request {
+ id: "pane-open-overlay".into(),
+ method: Method::PluginPaneOpen(PluginPaneOpenParams {
+ plugin_id: "example.overlay".into(),
+ entrypoint: "board".into(),
+ placement: None,
+ workspace_id: None,
+ target_pane_id: None,
+ direction: None,
+ cwd: None,
+ focus: true,
+ env: std::collections::HashMap::new(),
+ }),
+ });
+ let ResponseResult::PluginPaneOpened { .. } = response_result(&open) else {
+ panic!("expected plugin pane opened response: {open}");
+ };
+
+ let events = event_hub.events_after(0);
+ let pane_created = events
+ .iter()
+ .position(|(_, event)| event.event == crate::api::schema::EventKind::PaneCreated)
+ .expect("pane.created 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_created < layout_updated);
+ assert!(matches!(
+ &events[layout_updated].1.data,
+ crate::api::schema::EventData::LayoutUpdated { layout }
+ if layout.zoomed && layout.panes.len() == 2
+ ));
for (_, runtime) in app.terminal_runtimes.drain() {
runtime.shutdown();
diff --git a/src/app/api/plugins/panes.rs b/src/app/api/plugins/panes.rs
index 5a935a8c..b2988998 100644
--- a/src/app/api/plugins/panes.rs
+++ b/src/app/api/plugins/panes.rs
@@ -27,7 +27,19 @@ impl App {
Ok(result) => result,
Err(err) => return encode_error(id, "plugin_pane_open_failed", err.to_string()),
};
- self.finish_plugin_pane_open(id, ws_idx, None, new_pane, plugin.plugin_id.clone(), pane)
+ let layout_tab_idx = self
+ .overlay_panes
+ .get(&new_pane.pane_id)
+ .map(|overlay| overlay.tab_idx);
+ self.finish_plugin_pane_open(
+ id,
+ ws_idx,
+ None,
+ layout_tab_idx,
+ new_pane,
+ plugin.plugin_id.clone(),
+ pane,
+ )
}
pub(super) fn open_plugin_split_pane(
@@ -110,7 +122,15 @@ impl App {
tab.zoomed = true;
}
}
- self.finish_plugin_pane_open(id, ws_idx, None, new_pane, plugin.plugin_id.clone(), pane)
+ self.finish_plugin_pane_open(
+ id,
+ ws_idx,
+ None,
+ Some(tab_idx),
+ new_pane,
+ plugin.plugin_id.clone(),
+ pane,
+ )
}
pub(super) fn open_plugin_tab(
@@ -167,6 +187,7 @@ impl App {
id,
ws_idx,
Some(tab_idx),
+ Some(tab_idx),
new_pane,
plugin.plugin_id.clone(),
pane,
@@ -212,6 +233,7 @@ impl App {
id: String,
ws_idx: usize,
created_tab_idx: Option<usize>,
+ layout_tab_idx: Option<usize>,
new_pane: crate::workspace::NewPane,
plugin_id: String,
pane_manifest: PluginManifestPane,
@@ -248,6 +270,9 @@ impl App {
event: crate::api::schema::EventKind::PaneCreated,
data: crate::api::schema::EventData::PaneCreated { pane: pane.clone() },
});
+ if let Some(tab_idx) = layout_tab_idx {
+ self.emit_layout_updated_event(ws_idx, tab_idx);
+ }
encode_success(
id,
ResponseResult::PluginPaneOpened {