aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-utils/src/plugin_api
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2025-12-23 13:41:56 +0100
committerGitHub <noreply@github.com>2025-12-23 13:41:56 +0100
commit3fe48a972c55537502128779116d38d8f8aedb7e (patch)
tree350386442db2a46e1d8ba3d9d4dd0c7d53b34c13 /zellij-utils/src/plugin_api
parentRead only web tokens (#4551) (diff)
downloadsl0ppij-3fe48a972c55537502128779116d38d8f8aedb7e.tar.gz
sl0ppij-3fe48a972c55537502128779116d38d8f8aedb7e.tar.xz
sl0ppij-3fe48a972c55537502128779116d38d8f8aedb7e.zip
Feature: allow overriding a layout for a currently running tab (change layout at runtime) (#4566)
* add tests for LayoutApplier * initial untested implementation of override functions * add override-layout cli action with stub in screen * filter run instructions to ignore * implement override-layout roundtrip through plugin and pty threads * initial full-cycle working override implementation * fix some bugs (eg. actually start new terminals) * fix some bugs with focus after override * fix floating pane matching bug in layout applier * formatting * use first tab, error on more than 1 tab * fix some floating_panes ordering and shell issues * get new tab name to work * respect swap layouts * allow specifying tab properties on main layout node if we just have one tab * add tests for new override methods * start work on retaining panes, hard-coded things, kind of working but broken in many places * fixed most tiled pane override bugs * reset viewport before reapplying * revert constraint system changes * retain-existing-terminal-panes flag * add retain terminal panes tests * add retain plugin panes flag * add keybind * add retain plugin panes tests * add override-layout e2e test * refactor: layout_applier * style: rustfmt * some cleanups * fix cli flags * no crashy * formatting and artifacts * moar cleanups * fix: respect expanded in stack when overriding * docs(changelog): add PR
Diffstat (limited to 'zellij-utils/src/plugin_api')
-rw-r--r--zellij-utils/src/plugin_api/action.proto12
-rw-r--r--zellij-utils/src/plugin_api/action.rs101
2 files changed, 113 insertions, 0 deletions
diff --git a/zellij-utils/src/plugin_api/action.proto b/zellij-utils/src/plugin_api/action.proto
index c29884af..de8ef44f 100644
--- a/zellij-utils/src/plugin_api/action.proto
+++ b/zellij-utils/src/plugin_api/action.proto
@@ -162,6 +162,16 @@ message NewTabPayload {
optional UnblockCondition first_pane_unblock_condition = 9;
}
+message OverrideLayoutPayload {
+ optional TiledPaneLayout tiled_layout = 1;
+ repeated FloatingPaneLayout floating_layouts = 2;
+ repeated SwapTiledLayout swap_tiled_layouts = 3;
+ repeated SwapFloatingLayout swap_floating_layouts = 4;
+ optional string tab_name = 5;
+ bool retain_existing_terminal_panes = 10;
+ bool retain_existing_plugin_panes = 11;
+}
+
message Action {
ActionName name = 1;
oneof optional_payload {
@@ -213,6 +223,7 @@ message Action {
NewBlockingPanePayload new_blocking_pane_payload = 50;
NewTabPayload new_tab_payload = 51;
NewInPlacePanePayload new_in_place_pane_payload = 52;
+ OverrideLayoutPayload override_layout_payload = 53;
}
}
@@ -429,6 +440,7 @@ enum ActionName {
SwitchSession = 90;
NewBlockingPane = 91;
NewInPlacePane = 92;
+ OverrideLayout = 93;
}
message Position {
diff --git a/zellij-utils/src/plugin_api/action.rs b/zellij-utils/src/plugin_api/action.rs
index f8de7b76..860b090b 100644
--- a/zellij-utils/src/plugin_api/action.rs
+++ b/zellij-utils/src/plugin_api/action.rs
@@ -37,6 +37,7 @@ pub use super::generated_api::api::{
NewPluginPanePayload,
NewTabPayload,
NewTiledPanePayload,
+ OverrideLayoutPayload,
PaneId as ProtobufPaneId,
PaneIdAndShouldFloat,
PaneRun as ProtobufPaneRun,
@@ -720,6 +721,55 @@ impl TryFrom<ProtobufAction> for Action {
Some(_) => Err("NextSwapLayout should not have a payload"),
None => Ok(Action::NextSwapLayout),
},
+ Some(ProtobufActionName::OverrideLayout) => match protobuf_action.optional_payload {
+ Some(OptionalPayload::OverrideLayoutPayload(payload)) => {
+ let tiled_layout = payload.tiled_layout.map(|l| l.try_into()).transpose()?;
+
+ let floating_layouts = payload
+ .floating_layouts
+ .into_iter()
+ .map(|l| l.try_into())
+ .collect::<Result<Vec<_>, _>>()?;
+
+ let swap_tiled_layouts = if payload.swap_tiled_layouts.is_empty() {
+ None
+ } else {
+ Some(
+ payload
+ .swap_tiled_layouts
+ .into_iter()
+ .map(|l| l.try_into())
+ .collect::<Result<Vec<_>, _>>()?,
+ )
+ };
+
+ let swap_floating_layouts = if payload.swap_floating_layouts.is_empty() {
+ None
+ } else {
+ Some(
+ payload
+ .swap_floating_layouts
+ .into_iter()
+ .map(|l| l.try_into())
+ .collect::<Result<Vec<_>, _>>()?,
+ )
+ };
+
+ let tab_name = payload.tab_name.filter(|s| !s.is_empty());
+
+ Ok(Action::OverrideLayout {
+ tiled_layout,
+ floating_layouts,
+ swap_tiled_layouts,
+ swap_floating_layouts,
+ tab_name,
+ retain_existing_terminal_panes: payload.retain_existing_terminal_panes,
+ retain_existing_plugin_panes: payload.retain_existing_plugin_panes,
+ })
+ },
+ Some(_) => Err("Mismatched payload for OverrideLayout"),
+ None => Err("Missing payload for OverrideLayout"),
+ },
Some(ProtobufActionName::QueryTabNames) => match protobuf_action.optional_payload {
Some(_) => Err("QueryTabNames should not have a payload"),
None => Ok(Action::QueryTabNames),
@@ -1505,6 +1555,57 @@ impl TryFrom<Action> for ProtobufAction {
name: ProtobufActionName::NextSwapLayout as i32,
optional_payload: None,
}),
+ Action::OverrideLayout {
+ tiled_layout,
+ floating_layouts,
+ swap_tiled_layouts,
+ swap_floating_layouts,
+ tab_name,
+ retain_existing_terminal_panes,
+ retain_existing_plugin_panes,
+ } => {
+ let protobuf_tiled_layout = tiled_layout.map(|l| l.try_into()).transpose()?;
+
+ let protobuf_floating_layouts = floating_layouts
+ .into_iter()
+ .map(|l| l.try_into())
+ .collect::<Result<Vec<_>, _>>()?;
+
+ let protobuf_swap_tiled_layouts = swap_tiled_layouts
+ .map(|layouts| {
+ layouts
+ .into_iter()
+ .map(|l| l.try_into())
+ .collect::<Result<Vec<_>, _>>()
+ })
+ .transpose()?
+ .unwrap_or_default();
+
+ let protobuf_swap_floating_layouts = swap_floating_layouts
+ .map(|layouts| {
+ layouts
+ .into_iter()
+ .map(|l| l.try_into())
+ .collect::<Result<Vec<_>, _>>()
+ })
+ .transpose()?
+ .unwrap_or_default();
+
+ Ok(ProtobufAction {
+ name: ProtobufActionName::OverrideLayout as i32,
+ optional_payload: Some(OptionalPayload::OverrideLayoutPayload(
+ OverrideLayoutPayload {
+ tiled_layout: protobuf_tiled_layout,
+ floating_layouts: protobuf_floating_layouts,
+ swap_tiled_layouts: protobuf_swap_tiled_layouts,
+ swap_floating_layouts: protobuf_swap_floating_layouts,
+ tab_name: tab_name.clone(),
+ retain_existing_terminal_panes,
+ retain_existing_plugin_panes,
+ },
+ )),
+ })
+ },
Action::QueryTabNames => Ok(ProtobufAction {
name: ProtobufActionName::QueryTabNames as i32,
optional_payload: None,