aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-utils/src
diff options
context:
space:
mode:
authorDawidPietrykowski <53954695+DawidPietrykowski@users.noreply.github.com>2026-06-25 18:12:51 +0200
committerGitHub <noreply@github.com>2026-06-25 18:12:51 +0200
commitebd731b0ff12737a6e2160a255280534ef3991bd (patch)
tree4313963a269205391aa519fd090ea1de2b7cc4af /zellij-utils/src
parenttests: add more integration tests (#5308) (diff)
downloadsl0ppij-ebd731b0ff12737a6e2160a255280534ef3991bd.tar.gz
sl0ppij-ebd731b0ff12737a6e2160a255280534ef3991bd.tar.xz
sl0ppij-ebd731b0ff12737a6e2160a255280534ef3991bd.zip
feat(panes): `FocusLastPane` action (#4241)
* feat(panes): focus last pane (#4142) * add tests * add client id parameter to `add_floating_pane` * add action for floating panes * add tests for floating panes * add keybinds to configs * rebase fixes * simplify focus logic when adding stacked pane * formatting fix * formatting fix 2 * fix: make sure expanding pane always focuses it for all clients in stack * add pr * fix rebase --------- Co-authored-by: Aram Drevekenin <aram@poor.dev>
Diffstat (limited to 'zellij-utils/src')
-rw-r--r--zellij-utils/src/cli.rs2
-rw-r--r--zellij-utils/src/client_server_contract/common_types.proto2
-rw-r--r--zellij-utils/src/data.rs1
-rw-r--r--zellij-utils/src/errors.rs1
-rw-r--r--zellij-utils/src/input/actions.rs3
-rw-r--r--zellij-utils/src/ipc/protobuf_conversion.rs5
-rw-r--r--zellij-utils/src/kdl/mod.rs6
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap3
-rw-r--r--zellij-utils/src/plugin_api/action.proto1
-rw-r--r--zellij-utils/src/plugin_api/action.rs8
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.proto1
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.rs10
12 files changed, 41 insertions, 2 deletions
diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs
index 5b169170..a00c4d05 100644
--- a/zellij-utils/src/cli.rs
+++ b/zellij-utils/src/cli.rs
@@ -765,6 +765,8 @@ pub enum CliAction {
/// The pane_id of the pane, eg. terminal_1, plugin_2 or 3
pane_id: String,
},
+ /// Change focus to the last focused frame
+ FocusLastPane,
/// Move the focused pane in the specified direction. [right|left|up|down]
MoveFocus {
direction: Direction,
diff --git a/zellij-utils/src/client_server_contract/common_types.proto b/zellij-utils/src/client_server_contract/common_types.proto
index 1dfc3994..718cc279 100644
--- a/zellij-utils/src/client_server_contract/common_types.proto
+++ b/zellij-utils/src/client_server_contract/common_types.proto
@@ -278,6 +278,7 @@ message Action {
SetLightThemeAction set_light_theme = 138;
ToggleThemeAction toggle_theme = 139;
ToggleMobileModeAction toggle_mobile_mode = 140;
+ FocusLastPaneAction focus_last_pane = 141;
}
}
@@ -287,6 +288,7 @@ message Action {
message QuitAction {}
message FocusNextPaneAction {}
message FocusPreviousPaneAction {}
+message FocusLastPaneAction {}
message SwitchFocusAction {}
message MovePaneBackwardsAction {}
message ClearScreenAction {}
diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs
index 16ff560e..c11f777f 100644
--- a/zellij-utils/src/data.rs
+++ b/zellij-utils/src/data.rs
@@ -3384,6 +3384,7 @@ pub enum PluginCommand {
ResizeWithDirection(ResizeStrategy),
FocusNextPane,
FocusPreviousPane,
+ FocusLastPane,
MoveFocus(Direction),
MoveFocusOrTab(Direction),
Detach,
diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs
index 8e4bc66c..c9e8e25f 100644
--- a/zellij-utils/src/errors.rs
+++ b/zellij-utils/src/errors.rs
@@ -246,6 +246,7 @@ pub enum ScreenContext {
SwitchFocus,
FocusNextPane,
FocusPreviousPane,
+ FocusLastPane,
FocusPaneAt,
MoveFocusLeft,
MoveFocusLeftOrPreviousTab,
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs
index cf5fc448..40158b8b 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -158,6 +158,8 @@ pub enum Action {
/// Switch focus to next pane in specified direction.
FocusNextPane,
FocusPreviousPane,
+ /// Switch focus to the last focused pane.
+ FocusLastPane,
/// Move the focus pane in specified direction.
SwitchFocus,
MoveFocus {
@@ -828,6 +830,7 @@ impl Action {
))?;
Ok(vec![Action::FocusPaneByPaneId { pane_id }])
},
+ CliAction::FocusLastPane => Ok(vec![Action::FocusLastPane]),
CliAction::MoveFocus { direction } => Ok(vec![Action::MoveFocus { direction }]),
CliAction::MoveFocusOrTab { direction } => {
Ok(vec![Action::MoveFocusOrTab { direction }])
diff --git a/zellij-utils/src/ipc/protobuf_conversion.rs b/zellij-utils/src/ipc/protobuf_conversion.rs
index d1bfb7d4..c4941676 100644
--- a/zellij-utils/src/ipc/protobuf_conversion.rs
+++ b/zellij-utils/src/ipc/protobuf_conversion.rs
@@ -915,6 +915,7 @@ impl From<crate::input::actions::Action>
EditFileAction,
EditScrollbackAction,
EditScrollbackByPaneIdAction,
+ FocusLastPaneAction,
FocusNextPaneAction,
FocusPaneByPaneIdAction,
FocusPluginPaneWithIdAction,
@@ -1090,6 +1091,9 @@ impl From<crate::input::actions::Action>
crate::input::actions::Action::FocusPreviousPane => {
ActionType::FocusPreviousPane(FocusPreviousPaneAction {})
},
+ crate::input::actions::Action::FocusLastPane => {
+ ActionType::FocusLastPane(FocusLastPaneAction {})
+ },
crate::input::actions::Action::SwitchFocus => {
ActionType::SwitchFocus(SwitchFocusAction {})
},
@@ -1961,6 +1965,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action>
ActionType::FocusPreviousPane(_) => {
Ok(crate::input::actions::Action::FocusPreviousPane)
},
+ ActionType::FocusLastPane(_) => Ok(crate::input::actions::Action::FocusLastPane),
ActionType::SwitchFocus(_) => Ok(crate::input::actions::Action::SwitchFocus),
ActionType::MoveFocus(move_focus_action) => {
Ok(crate::input::actions::Action::MoveFocus {
diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs
index 0164ef92..14695f68 100644
--- a/zellij-utils/src/kdl/mod.rs
+++ b/zellij-utils/src/kdl/mod.rs
@@ -48,6 +48,7 @@ macro_rules! parse_kdl_action_arguments {
"Quit" => Ok(Action::Quit),
"FocusNextPane" => Ok(Action::FocusNextPane),
"FocusPreviousPane" => Ok(Action::FocusPreviousPane),
+ "FocusLastPane" => Ok(Action::FocusLastPane),
"SwitchFocus" => Ok(Action::SwitchFocus),
"EditScrollback" => Ok(Action::EditScrollback { ansi: false }),
"ScrollUp" => Ok(Action::ScrollUp),
@@ -660,6 +661,7 @@ impl Action {
},
Action::FocusNextPane => Some(KdlNode::new("FocusNextPane")),
Action::FocusPreviousPane => Some(KdlNode::new("FocusPreviousPane")),
+ Action::FocusLastPane => Some(KdlNode::new("FocusLastPane")),
Action::SwitchFocus => Some(KdlNode::new("SwitchFocus")),
Action::MoveFocus { direction } => {
let mut node = KdlNode::new("MoveFocus");
@@ -1512,6 +1514,9 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
"FocusPreviousPane" => {
parse_kdl_action_arguments!(action_name, action_arguments, kdl_action)
},
+ "FocusLastPane" => {
+ parse_kdl_action_arguments!(action_name, action_arguments, kdl_action)
+ },
"SwitchFocus" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
"EditScrollback" => {
let ansi = crate::kdl_get_bool_property_or_child_value!(kdl_action, "ansi")
@@ -6729,6 +6734,7 @@ fn keybinds_to_string_with_all_actions() {
config_key_2 "config_value_2";
};
}
+ bind "Ctrl Alt k" { FocusLastPane; }
}
}"#;
let document: KdlDocument = fake_config.parse().unwrap();
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap
index 43e85faa..b0119a63 100644
--- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap
@@ -1,6 +1,5 @@
---
source: zellij-utils/src/kdl/mod.rs
-assertion_line: 2731
expression: serialized.to_string()
---
keybinds clear-defaults=true {
@@ -47,6 +46,7 @@ keybinds clear-defaults=true {
}
bind "Alt j" { GoToPreviousTab; }
bind "Ctrl k" { MovePane "right"; }
+ bind "Ctrl Alt k" { FocusLastPane; }
bind "Alt k" { CloseTab; }
bind "Ctrl l" { MovePaneBackwards; }
bind "Alt l" { GoToTab 1; }
@@ -127,4 +127,3 @@ keybinds clear-defaults=true {
bind "Alt z" { SearchInput 0; }
}
}
-
diff --git a/zellij-utils/src/plugin_api/action.proto b/zellij-utils/src/plugin_api/action.proto
index 7eedf393..68c859b8 100644
--- a/zellij-utils/src/plugin_api/action.proto
+++ b/zellij-utils/src/plugin_api/action.proto
@@ -493,6 +493,7 @@ enum ActionName {
SetDarkTheme = 101;
SetLightTheme = 102;
ToggleTheme = 103;
+ FocusLastPane = 104;
}
message Position {
diff --git a/zellij-utils/src/plugin_api/action.rs b/zellij-utils/src/plugin_api/action.rs
index 34b3adce..f4e35d84 100644
--- a/zellij-utils/src/plugin_api/action.rs
+++ b/zellij-utils/src/plugin_api/action.rs
@@ -168,6 +168,10 @@ impl TryFrom<ProtobufAction> for Action {
Some(_) => Err("FocusPreviousPane should not have a payload"),
None => Ok(Action::FocusPreviousPane),
},
+ Some(ProtobufActionName::FocusLastPane) => match protobuf_action.optional_payload {
+ Some(_) => Err("FocusLastPane should not have a payload"),
+ None => Ok(Action::FocusLastPane),
+ },
Some(ProtobufActionName::SwitchFocus) => match protobuf_action.optional_payload {
Some(_) => Err("SwitchFocus should not have a payload"),
None => Ok(Action::SwitchFocus),
@@ -1173,6 +1177,10 @@ impl TryFrom<Action> for ProtobufAction {
name: ProtobufActionName::FocusPreviousPane as i32,
optional_payload: None,
}),
+ Action::FocusLastPane => Ok(ProtobufAction {
+ name: ProtobufActionName::FocusLastPane as i32,
+ optional_payload: None,
+ }),
Action::SwitchFocus => Ok(ProtobufAction {
name: ProtobufActionName::SwitchFocus as i32,
optional_payload: None,
diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto
index c65bcbfd..82f4d835 100644
--- a/zellij-utils/src/plugin_api/plugin_command.proto
+++ b/zellij-utils/src/plugin_api/plugin_command.proto
@@ -213,6 +213,7 @@ enum CommandName {
NewTabUnfocused = 220;
NewTiledPaneInTab = 221;
ExitMobileMode = 222;
+ FocusLastPane = 223;
}
message PluginCommand {
diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs
index 42b0774f..546c3586 100644
--- a/zellij-utils/src/plugin_api/plugin_command.rs
+++ b/zellij-utils/src/plugin_api/plugin_command.rs
@@ -980,6 +980,12 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
}
Ok(PluginCommand::FocusPreviousPane)
},
+ Some(CommandName::FocusLastPane) => {
+ if protobuf_plugin_command.payload.is_some() {
+ return Err("FocusLastPane should not have a payload");
+ }
+ Ok(PluginCommand::FocusLastPane)
+ },
Some(CommandName::MoveFocus) => match protobuf_plugin_command.payload {
Some(Payload::MoveFocusPayload(move_payload)) => match move_payload.direction {
Some(direction) => Ok(PluginCommand::MoveFocus(direction.try_into()?)),
@@ -3005,6 +3011,10 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
name: CommandName::FocusPreviousPane as i32,
payload: None,
}),
+ PluginCommand::FocusLastPane => Ok(ProtobufPluginCommand {
+ name: CommandName::FocusLastPane as i32,
+ payload: None,
+ }),
PluginCommand::MoveFocus(direction) => Ok(ProtobufPluginCommand {
name: CommandName::MoveFocus as i32,
payload: Some(Payload::MoveFocusPayload(MovePayload {