diff options
| author | Aram Drevekenin <aram@poor.dev> | 2026-07-09 16:52:50 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-09 16:52:50 +0200 |
| commit | 4bfda976b110b5d3182fca004ee7031f6322253e (patch) | |
| tree | ff16cdbec15673861ee0907181a25f709565b4fd | |
| parent | fix: focus last pane correctness issues (#5344) (diff) | |
| download | sl0ppij-4bfda976b110b5d3182fca004ee7031f6322253e.tar.gz sl0ppij-4bfda976b110b5d3182fca004ee7031f6322253e.tar.xz sl0ppij-4bfda976b110b5d3182fca004ee7031f6322253e.zip | |
Allow creating panes/tabs from the CLI without changing focus to them (#5346)
* feat: allow opening panes and tabs from the cli without focusing them
* rustfmt
* fix flake
* rustfmt
* add pr
27 files changed, 797 insertions, 141 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a568431..e21895f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * fix(windows): route Windows Terminal through the VT input path (https://github.com/zellij-org/zellij/pull/5335) * fix(windows): backslashes dropped when quoting arguments passed to spawned processes (https://github.com/zellij-org/zellij/pull/5336) * fix: occasional high resource utilization and sluggishness on machines with tens of thousands of concurent processes (https://github.com/zellij-org/zellij/pull/5324) +* feat: allow opening panes/tabs from the CLI without changing focus (https://github.com/zellij-org/zellij/pull/5346) ## [0.44.3] - 2026-05-13 * fix(windows): bump windows-sys to 0.59 to align manifest with code, fixing source builds via `cargo install`/`cargo binstall` (https://github.com/zellij-org/zellij/pull/5139) diff --git a/default-plugins/status-bar/src/one_line_ui.rs b/default-plugins/status-bar/src/one_line_ui.rs index 6d00a6bf..ae57a223 100644 --- a/default-plugins/status-bar/src/one_line_ui.rs +++ b/default-plugins/status-bar/src/one_line_ui.rs @@ -1613,7 +1613,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<KeyWithModifier (s("Toggle Embed"), s("Embed"), single_action_key(&km, &[A::TogglePaneEmbedOrFloating, TO_NORMAL])), (s("Split Right"), s("Right"), single_action_key(&km, &[A::NewPane{direction: Some(Direction::Right), pane_name: None, start_suppressed: false}, TO_NORMAL])), (s("Split Down"), s("Down"), single_action_key(&km, &[A::NewPane{direction: Some(Direction::Down), pane_name: None, start_suppressed: false}, TO_NORMAL])), - (s("Stack"), s("Stack"), single_action_key(&km, &[A::NewStackedPane{command: None, pane_name: None, near_current_pane: false, tab_id: None}, TO_NORMAL])), + (s("Stack"), s("Stack"), single_action_key(&km, &[A::NewStackedPane{command: None, pane_name: None, near_current_pane: false, no_focus: false, tab_id: None}, TO_NORMAL])), (s("Select pane"), s("Select"), to_basemode_key), ]} else if mi.mode == IM::Tab { // With the default bindings, "Move focus" for tabs is tricky: It binds all the arrow keys diff --git a/src/main.rs b/src/main.rs index a5faf7d3..30c9a00e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,6 +50,7 @@ fn main() { block_until_exit_failure, block_until_exit, near_current_pane, + no_focus, borderless, tab_id, })) = opts.command @@ -94,6 +95,7 @@ fn main() { block_until_exit: false, unblock_condition, near_current_pane, + no_focus, borderless, tab_id, }; @@ -112,6 +114,7 @@ fn main() { width, height, pinned, + no_focus, borderless, tab_id, })) = opts.command @@ -146,6 +149,7 @@ fn main() { block_until_exit: false, unblock_condition, near_current_pane: false, + no_focus, borderless, tab_id, }; @@ -166,6 +170,7 @@ fn main() { height, pinned, near_current_pane, + no_focus, borderless, tab_id, })) = opts.command @@ -191,6 +196,7 @@ fn main() { height, pinned, near_current_pane, + no_focus, borderless, tab_id, }; @@ -286,6 +292,7 @@ fn main() { block_until_exit_success: false, block_until_exit_failure: false, block_until_exit: false, + no_focus: false, }; commands::send_action_to_session(new_layout_cli_action, Some(session_name), config); } else { diff --git a/zellij-integration-tests/src/runner.rs b/zellij-integration-tests/src/runner.rs index 76544ecd..cee765a9 100644 --- a/zellij-integration-tests/src/runner.rs +++ b/zellij-integration-tests/src/runner.rs @@ -145,6 +145,7 @@ fn new_pane_cli_action( block_until_exit, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: None, } diff --git a/zellij-integration-tests/tests/no_focus.rs b/zellij-integration-tests/tests/no_focus.rs new file mode 100644 index 00000000..5722a019 --- /dev/null +++ b/zellij-integration-tests/tests/no_focus.rs @@ -0,0 +1,155 @@ +#![cfg(unix)] + +use std::time::Instant; +use zellij_integration_tests::{ + claim_first_terminal_and_wait_for_prompt, default_timeout, start_zellij, FakePtyHandle, PROMPT, +}; +use zellij_utils::cli::CliAction; + +fn no_focus_new_pane_action(command: &[&str]) -> CliAction { + CliAction::NewPane { + direction: None, + command: command.iter().map(|part| part.to_string()).collect(), + plugin: None, + cwd: None, + floating: false, + in_place: false, + close_replaced_pane: false, + pane_id: None, + name: None, + close_on_exit: false, + start_suspended: false, + configuration: None, + skip_plugin_cache: false, + x: None, + y: None, + width: None, + height: None, + pinned: None, + stacked: false, + blocking: false, + block_until_exit_success: false, + block_until_exit_failure: false, + block_until_exit: false, + unblock_condition: None, + near_current_pane: false, + no_focus: true, + borderless: None, + tab_id: None, + } +} + +fn no_focus_new_tab_action() -> CliAction { + CliAction::NewTab { + name: None, + layout: None, + layout_string: None, + layout_dir: None, + cwd: None, + initial_command: vec![], + initial_plugin: None, + close_on_exit: false, + start_suspended: false, + block_until_exit_success: false, + block_until_exit_failure: false, + block_until_exit: false, + no_focus: true, + } +} + +fn wait_until_stdin_contains(handle: &FakePtyHandle, needle: &[u8]) { + let deadline = Instant::now() + default_timeout(); + loop { + let bytes = handle.stdin_bytes(); + if bytes.windows(needle.len()).any(|window| window == needle) { + return; + } + if Instant::now() >= deadline { + panic!( + "timed out waiting for pane to receive {:?}, received {:?}", + String::from_utf8_lossy(needle), + String::from_utf8_lossy(&bytes), + ); + } + std::thread::sleep(std::time::Duration::from_millis(10)); + } +} + +fn stdin_contains(handle: &FakePtyHandle, needle: &[u8]) -> bool { + handle + .stdin_bytes() + .windows(needle.len()) + .any(|window| window == needle) +} + +#[test] +fn new_pane_no_focus_leaves_input_on_the_original_pane() { + let mut zellij = start_zellij(); + let original_pane = claim_first_terminal_and_wait_for_prompt(&zellij); + + zellij.run_cli_action(no_focus_new_pane_action(&["unfocused-command"])); + let unfocused_pane = zellij.expect_pty_spawn(); + unfocused_pane.output(PROMPT); + zellij.wait_until( + "second pane appeared alongside the first", + |grid_snapshot| grid_snapshot.text.matches("$").count() >= 2, + ); + + let probe = b"nofocusprobe"; + zellij.send_stdin(probe); + wait_until_stdin_contains(&original_pane, probe); + assert!( + !stdin_contains(&unfocused_pane, probe), + "input leaked into the pane that was opened with --no-focus" + ); + zellij.quit(); +} + +#[test] +fn new_pane_without_no_focus_moves_input_to_the_new_pane() { + let mut zellij = start_zellij(); + let original_pane = claim_first_terminal_and_wait_for_prompt(&zellij); + + let mut focusing_action = no_focus_new_pane_action(&["focused-command"]); + if let CliAction::NewPane { no_focus, .. } = &mut focusing_action { + *no_focus = false; + } + zellij.run_cli_action(focusing_action); + let focused_pane = zellij.expect_pty_spawn(); + focused_pane.output(PROMPT); + zellij.wait_until( + "second pane appeared alongside the first", + |grid_snapshot| grid_snapshot.text.matches("$").count() >= 2, + ); + + let probe = b"focusprobe"; + zellij.send_stdin(probe); + wait_until_stdin_contains(&focused_pane, probe); + assert!( + !stdin_contains(&original_pane, probe), + "input reached the original pane even though the new pane should have taken focus" + ); + zellij.quit(); +} + +#[test] +fn new_tab_no_focus_leaves_focus_on_the_original_tab() { + let mut zellij = start_zellij(); + let original_pane = claim_first_terminal_and_wait_for_prompt(&zellij); + + zellij.run_cli_action(no_focus_new_tab_action()); + let new_tab_pane = zellij.expect_pty_spawn(); + new_tab_pane.output(PROMPT); + zellij.wait_until("a second tab exists in the tab bar", |grid_snapshot| { + grid_snapshot.text.contains("Tab #2") + }); + + let probe = b"nofocusprobe"; + zellij.send_stdin(probe); + wait_until_stdin_contains(&original_pane, probe); + assert!( + !stdin_contains(&new_tab_pane, probe), + "input leaked into the tab that was created with --no-focus" + ); + zellij.quit(); +} diff --git a/zellij-integration-tests/tests/plugins.rs b/zellij-integration-tests/tests/plugins.rs index 5b14c633..21990a94 100644 --- a/zellij-integration-tests/tests/plugins.rs +++ b/zellij-integration-tests/tests/plugins.rs @@ -22,9 +22,12 @@ fn load_plugins_in_background_on_startup() { let terminal = zellij.expect_pty_spawn(); terminal.output(b"$ "); - let grid_snapshot = zellij - .wait_until("background plugin requests permissions", |grid_snapshot| { - grid_snapshot.contains("Allow? (y/n)") && grid_snapshot.tab_bar_appears() + let grid_snapshot = + zellij.wait_until("background plugin requests permissions", |grid_snapshot| { + grid_snapshot.contains("Allow? (y/n)") + && grid_snapshot.tab_bar_appears() + && grid_snapshot.status_bar_appears() + && grid_snapshot.contains("STAGGERED") }); assert_snapshot!(normalized(&grid_snapshot)); zellij.send_stdin(b"y"); diff --git a/zellij-integration-tests/tests/single_pane_tab_title.rs b/zellij-integration-tests/tests/single_pane_tab_title.rs index 1fd1a553..cc70a163 100644 --- a/zellij-integration-tests/tests/single_pane_tab_title.rs +++ b/zellij-integration-tests/tests/single_pane_tab_title.rs @@ -37,6 +37,7 @@ fn replace_pane_in_place_with_held_command( block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: None, }); diff --git a/zellij-integration-tests/tests/snapshots/plugins__load_plugins_in_background_on_startup.snap b/zellij-integration-tests/tests/snapshots/plugins__load_plugins_in_background_on_startup.snap index 034a2c02..a77d1dd9 100644 --- a/zellij-integration-tests/tests/snapshots/plugins__load_plugins_in_background_on_startup.snap +++ b/zellij-integration-tests/tests/snapshots/plugins__load_plugins_in_background_on_startup.snap @@ -16,3 +16,4 @@ $ │ │ │ │ └──────────────────────────────────────────────────────────┘ + Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT ... diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index d9448641..ba0ba51c 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -649,64 +649,80 @@ impl TiledPanes { self.reset_boundaries(); } pub fn can_split_pane_horizontally(&mut self, client_id: ClientId) -> bool { - if let Some(active_pane_id) = &self.active_panes.get(&client_id) { - if let Some(active_pane) = self.panes.get_mut(active_pane_id) { - let mut full_pane_size = active_pane.position_and_size(); - - if full_pane_size.is_stacked() { - let Some(position_and_size_of_stack) = - StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) - .position_and_size_of_stack(&active_pane_id) - else { - log::error!("Failed to find position and size of stack"); - return false; - }; - full_pane_size = position_and_size_of_stack; - } + match self.active_panes.get(&client_id).copied() { + Some(active_pane_id) => self.can_split_pane_id_horizontally(active_pane_id), + None => false, + } + } + pub fn can_split_pane_id_horizontally(&mut self, active_pane_id: PaneId) -> bool { + if let Some(active_pane) = self.panes.get_mut(&active_pane_id) { + let mut full_pane_size = active_pane.position_and_size(); - if full_pane_size.rows.as_usize() < MIN_TERMINAL_HEIGHT * 2 { + if full_pane_size.is_stacked() { + let Some(position_and_size_of_stack) = + StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .position_and_size_of_stack(&active_pane_id) + else { + log::error!("Failed to find position and size of stack"); return false; - } else { - return split(SplitDirection::Horizontal, &full_pane_size).is_some(); - } + }; + full_pane_size = position_and_size_of_stack; + } + + if full_pane_size.rows.as_usize() < MIN_TERMINAL_HEIGHT * 2 { + return false; + } else { + return split(SplitDirection::Horizontal, &full_pane_size).is_some(); } } false } pub fn can_split_pane_vertically(&mut self, client_id: ClientId) -> bool { - if let Some(active_pane_id) = &self.active_panes.get(&client_id) { - if let Some(active_pane) = self.panes.get_mut(active_pane_id) { - let mut full_pane_size = active_pane.position_and_size(); - - if full_pane_size.is_stacked() { - let Some(position_and_size_of_stack) = - StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) - .position_and_size_of_stack(&active_pane_id) - else { - log::error!("Failed to find position and size of stack"); - return false; - }; - full_pane_size = position_and_size_of_stack; - } + match self.active_panes.get(&client_id).copied() { + Some(active_pane_id) => self.can_split_pane_id_vertically(active_pane_id), + None => false, + } + } + pub fn can_split_pane_id_vertically(&mut self, active_pane_id: PaneId) -> bool { + if let Some(active_pane) = self.panes.get_mut(&active_pane_id) { + let mut full_pane_size = active_pane.position_and_size(); - if full_pane_size.cols.as_usize() < MIN_TERMINAL_WIDTH * 2 { + if full_pane_size.is_stacked() { + let Some(position_and_size_of_stack) = + StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .position_and_size_of_stack(&active_pane_id) + else { + log::error!("Failed to find position and size of stack"); return false; - } - return split(SplitDirection::Vertical, &full_pane_size).is_some(); + }; + full_pane_size = position_and_size_of_stack; } + + if full_pane_size.cols.as_usize() < MIN_TERMINAL_WIDTH * 2 { + return false; + } + return split(SplitDirection::Vertical, &full_pane_size).is_some(); } false } pub fn split_pane_horizontally( &mut self, pid: PaneId, - mut new_pane: Box<dyn Pane>, + new_pane: Box<dyn Pane>, client_id: ClientId, ) { - let active_pane_id = &self.active_panes.get(&client_id).unwrap(); + let active_pane_id = *self.active_panes.get(&client_id).unwrap(); + self.split_pane_id_horizontally(pid, new_pane, active_pane_id); + } + pub fn split_pane_id_horizontally( + &mut self, + pid: PaneId, + mut new_pane: Box<dyn Pane>, + active_pane_id: PaneId, + ) { let mut full_pane_size = self .panes - .get(active_pane_id) + .get(&active_pane_id) .map(|p| p.position_and_size()) .unwrap(); if full_pane_size.is_stacked() { @@ -721,7 +737,7 @@ impl TiledPanes { }, } } - let active_pane = self.panes.get_mut(active_pane_id).unwrap(); + let active_pane = self.panes.get_mut(&active_pane_id).unwrap(); if let Some((top_winsize, bottom_winsize)) = split(SplitDirection::Horizontal, &full_pane_size) { @@ -745,13 +761,21 @@ impl TiledPanes { pub fn split_pane_vertically( &mut self, pid: PaneId, - mut new_pane: Box<dyn Pane>, + new_pane: Box<dyn Pane>, client_id: ClientId, ) { - let active_pane_id = &self.active_panes.get(&client_id).unwrap(); + let active_pane_id = *self.active_panes.get(&client_id).unwrap(); + self.split_pane_id_vertically(pid, new_pane, active_pane_id); + } + pub fn split_pane_id_vertically( + &mut self, + pid: PaneId, + mut new_pane: Box<dyn Pane>, + active_pane_id: PaneId, + ) { let mut full_pane_size = self .panes - .get(active_pane_id) + .get(&active_pane_id) .map(|p| p.position_and_size()) .unwrap(); if full_pane_size.is_stacked() { @@ -766,7 +790,7 @@ impl TiledPanes { }, } } - let active_pane = self.panes.get_mut(active_pane_id).unwrap(); + let active_pane = self.panes.get_mut(&active_pane_id).unwrap(); if let Some((left_winsize, right_winsize)) = split(SplitDirection::Vertical, &full_pane_size) { diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs index 4dd559ed..d850145c 100644 --- a/zellij-server/src/plugins/zellij_exports.rs +++ b/zellij-server/src/plugins/zellij_exports.rs @@ -1197,6 +1197,7 @@ fn open_plugin_pane_floating( skip_cache: false, cwd: Some(env.plugin_cwd.clone()), coordinates: floating_pane_coordinates, + no_focus: false, tab_id: None, }; let error_msg = || format!("Failed to open floating plugin pane"); @@ -1437,6 +1438,7 @@ fn open_file(env: &PluginEnv, file_to_open: FileToOpen, context: BTreeMap<String start_suppressed, coordinates: None, near_current_pane: false, + no_focus: false, tab_id: None, }; let result = apply_action!(action, error_msg, env); @@ -1532,6 +1534,7 @@ fn open_file_floating( start_suppressed, coordinates: floating_pane_coordinates, near_current_pane: false, + no_focus: false, tab_id: None, }; let result = apply_action!(action, error_msg, env); @@ -1573,6 +1576,7 @@ fn open_file_in_place( start_suppressed, coordinates: None, near_current_pane: false, + no_focus: false, tab_id: None, }; let result = apply_action!(action, error_msg, env); @@ -1733,6 +1737,7 @@ fn open_terminal(env: &PluginEnv, cwd: PathBuf) { command: run_command_action, pane_name: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: None, }; @@ -1810,6 +1815,7 @@ fn open_terminal_floating( pane_name: None, coordinates: floating_pane_coordinates, near_current_pane: false, + no_focus: false, tab_id: None, }; let result = apply_action!(action, error_msg, env); @@ -1884,6 +1890,7 @@ fn open_terminal_in_place(env: &PluginEnv, cwd: PathBuf) { command: run_command_action, pane_name: None, near_current_pane: false, + no_focus: false, pane_id_to_replace: None, close_replaced_pane: false, tab_id: None, @@ -2170,6 +2177,7 @@ fn open_command_pane( command: Some(run_command_action), pane_name: name, near_current_pane: false, + no_focus: false, borderless: None, tab_id: None, }; @@ -2279,6 +2287,7 @@ fn open_command_pane_floating( pane_name: name, coordinates: floating_pane_coordinates, near_current_pane: false, + no_focus: false, tab_id: None, }; let result = apply_action!(action, error_msg, env); @@ -2388,6 +2397,7 @@ fn open_command_pane_in_place( command: Some(run_command_action), pane_name: name, near_current_pane: false, + no_focus: false, pane_id_to_replace: None, close_replaced_pane: false, tab_id: None, @@ -2872,6 +2882,7 @@ fn new_tiled_pane_in_tab(env: &PluginEnv, tab_position: usize) { command: run_command_action, pane_name: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: Some(tab_position), }; @@ -4730,9 +4741,9 @@ fn try_edit_layout( start_suppressed: false, coordinates: None, near_current_pane: true, + no_focus: false, tab_id: None, }; - // Route the action - this is fallible route_action( action, diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index 13b06b57..68a2371b 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -39,7 +39,9 @@ pub type TabIndex = u32; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ClientTabIndexOrPaneId { ClientId(ClientId), + ClientIdNoFocus(ClientId), TabIndex(usize), + TabIndexNoFocus(usize), PaneId(PaneId), } @@ -1025,13 +1027,14 @@ impl Pty { // returns the terminal id let terminal_action = match client_or_tab_index { - ClientTabIndexOrPaneId::ClientId(client_id) => { + ClientTabIndexOrPaneId::ClientId(client_id) + | ClientTabIndexOrPaneId::ClientIdNoFocus(client_id) => { let mut terminal_action = terminal_action.unwrap_or_else(|| self.get_default_terminal(None, None)); self.fill_cwd(&mut terminal_action, client_id); terminal_action }, - ClientTabIndexOrPaneId::TabIndex(_) => { + ClientTabIndexOrPaneId::TabIndex(_) | ClientTabIndexOrPaneId::TabIndexNoFocus(_) => { terminal_action.unwrap_or_else(|| self.get_default_terminal(None, None)) }, ClientTabIndexOrPaneId::PaneId(pane_id) => { diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 529a93b9..08200abb 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -193,6 +193,28 @@ impl Drop for NotificationEnd { // otherwise blocking-CLI actions // (`wait_forever=true`) park this function while still holding the guard, // deadlocking concurrent `session_data.write()`s. +fn new_pane_routing( + no_focus: bool, + near_current_pane: bool, + tab_id: Option<usize>, + pane_id: Option<PaneId>, + client_id: ClientId, +) -> ClientTabIndexOrPaneId { + if let Some(tab_id) = tab_id { + if no_focus { + ClientTabIndexOrPaneId::TabIndexNoFocus(tab_id) + } else { + ClientTabIndexOrPaneId::TabIndex(tab_id) + } + } else if (no_focus || near_current_pane) && pane_id.is_some() { + ClientTabIndexOrPaneId::PaneId(pane_id.unwrap()) + } else if no_focus { + ClientTabIndexOrPaneId::ClientIdNoFocus(client_id) + } else { + ClientTabIndexOrPaneId::ClientId(client_id) + } +} + pub(crate) fn route_action( action: Action, client_id: ClientId, @@ -635,6 +657,7 @@ pub(crate) fn route_action( command, unblock_condition, near_current_pane, + no_focus, tab_id, } => { let command = command @@ -667,13 +690,8 @@ pub(crate) fn route_action( _ => pane_id, }; - let client_tab_index_or_paneid = if let Some(tab_id) = tab_id { - ClientTabIndexOrPaneId::TabIndex(tab_id) - } else if near_current_pane && pane_id.is_some() { - ClientTabIndexOrPaneId::PaneId(pane_id.unwrap()) - } else { - ClientTabIndexOrPaneId::ClientId(client_id) - }; + let client_tab_index_or_paneid = + new_pane_routing(no_focus, near_current_pane, tab_id, pane_id, client_id); senders .send_to_pty(PtyInstruction::SpawnTerminal( command, @@ -696,18 +714,14 @@ pub(crate) fn route_action( start_suppressed, coordinates: floating_pane_coordinates, near_current_pane, + no_focus, tab_id, } => { let title = format!("Editing: {}", open_file_payload.path.display()); let open_file = TerminalAction::OpenFile(open_file_payload); let pty_instr = if should_open_in_place { - let client_tab_index_or_paneid = if let Some(tab_id) = tab_id { - ClientTabIndexOrPaneId::TabIndex(tab_id) - } else if near_current_pane && pane_id.is_some() { - ClientTabIndexOrPaneId::PaneId(pane_id.unwrap()) - } else { - ClientTabIndexOrPaneId::ClientId(client_id) - }; + let client_tab_index_or_paneid = + new_pane_routing(no_focus, near_current_pane, tab_id, pane_id, client_id); PtyInstruction::SpawnInPlaceTerminal( Some(open_file), Some(title), @@ -716,11 +730,8 @@ pub(crate) fn route_action( Some(NotificationEnd::new(completion_tx)), ) } else { - let client_tab_index_or_paneid = if let Some(tab_id) = tab_id { - ClientTabIndexOrPaneId::TabIndex(tab_id) - } else { - ClientTabIndexOrPaneId::ClientId(client_id) - }; + let client_tab_index_or_paneid = + new_pane_routing(no_focus, near_current_pane, tab_id, pane_id, client_id); PtyInstruction::SpawnTerminal( Some(open_file), Some(title), @@ -760,18 +771,14 @@ pub(crate) fn route_action( pane_name: name, coordinates: floating_pane_coordinates, near_current_pane, + no_focus, tab_id, } => { let run_cmd = run_command .map(|cmd| TerminalAction::RunCommand(cmd.into())) .or_else(|| default_shell.clone()); - let client_tab_index_or_paneid = if let Some(tab_id) = tab_id { - ClientTabIndexOrPaneId::TabIndex(tab_id) - } else if near_current_pane && pane_id.is_some() { - ClientTabIndexOrPaneId::PaneId(pane_id.unwrap()) - } else { - ClientTabIndexOrPaneId::ClientId(client_id) - }; + let client_tab_index_or_paneid = + new_pane_routing(no_focus, near_current_pane, tab_id, pane_id, client_id); senders .send_to_pty(PtyInstruction::SpawnTerminal( run_cmd, @@ -788,6 +795,7 @@ pub(crate) fn route_action( command: run_command, pane_name: name, near_current_pane, + no_focus, pane_id_to_replace, close_replaced_pane, tab_id, @@ -799,11 +807,17 @@ pub(crate) fn route_action( .and_then(|pane_id_to_replace| pane_id_to_replace.try_into().ok()); let pane_id = explicit_pane_id_to_replace.or(pane_id); let client_tab_index_or_paneid = if let Some(tab_id) = tab_id { - ClientTabIndexOrPaneId::TabIndex(tab_id) - } else if let Some(pane_id) = - pane_id.filter(|_| explicit_pane_id_to_replace.is_some() || near_current_pane) + if no_focus { + ClientTabIndexOrPaneId::TabIndexNoFocus(tab_id) + } else { + ClientTabIndexOrPaneId::TabIndex(tab_id) + } + } else if let Some(pane_id) = pane_id + .filter(|_| explicit_pane_id_to_replace.is_some() || near_current_pane || no_focus) { ClientTabIndexOrPaneId::PaneId(pane_id) + } else if no_focus { + ClientTabIndexOrPaneId::ClientIdNoFocus(client_id) } else { ClientTabIndexOrPaneId::ClientId(client_id) }; @@ -821,6 +835,7 @@ pub(crate) fn route_action( command: run_command, pane_name: name, near_current_pane, + no_focus, tab_id, } => { let run_cmd = run_command @@ -833,9 +848,13 @@ pub(crate) fn route_action( pane_id_to_stack_under: None, borderless: None, }, - ClientTabIndexOrPaneId::TabIndex(tab_id), + if no_focus { + ClientTabIndexOrPaneId::TabIndexNoFocus(tab_id) + } else { + ClientTabIndexOrPaneId::TabIndex(tab_id) + }, ) - } else if near_current_pane && pane_id.is_some() { + } else if (no_focus || near_current_pane) && pane_id.is_some() { let pane_id = pane_id.unwrap(); ( NewPanePlacement::Stacked { @@ -850,7 +869,11 @@ pub(crate) fn route_action( pane_id_to_stack_under: None, borderless: None, }, - ClientTabIndexOrPaneId::ClientId(client_id), + if no_focus { + ClientTabIndexOrPaneId::ClientIdNoFocus(client_id) + } else { + ClientTabIndexOrPaneId::ClientId(client_id) + }, ) }; senders @@ -870,19 +893,15 @@ pub(crate) fn route_action( command: run_command, pane_name: name, near_current_pane, + no_focus, borderless, tab_id, } => { let run_cmd = run_command .map(|cmd| TerminalAction::RunCommand(cmd.into())) .or_else(|| default_shell.clone()); - let client_tab_index_or_paneid = if let Some(tab_id) = tab_id { - ClientTabIndexOrPaneId::TabIndex(tab_id) - } else if near_current_pane && pane_id.is_some() { - ClientTabIndexOrPaneId::PaneId(pane_id.unwrap()) - } else { - ClientTabIndexOrPaneId::ClientId(client_id) - }; + let client_tab_index_or_paneid = + new_pane_routing(no_focus, near_current_pane, tab_id, pane_id, client_id); senders .send_to_pty(PtyInstruction::SpawnTerminal( run_cmd, @@ -935,13 +954,11 @@ pub(crate) fn route_action( Action::Run { command, near_current_pane, + no_focus, } => { let run_cmd = Some(TerminalAction::RunCommand(command.clone().into())); - let client_tab_index_or_paneid = if near_current_pane && pane_id.is_some() { - ClientTabIndexOrPaneId::PaneId(pane_id.unwrap()) - } else { - ClientTabIndexOrPaneId::ClientId(client_id) - }; + let client_tab_index_or_paneid = + new_pane_routing(no_focus, near_current_pane, None, pane_id, client_id); senders .send_to_pty(PtyInstruction::SpawnTerminal( run_cmd, @@ -1321,6 +1338,7 @@ pub(crate) fn route_action( pane_name: name, skip_cache, cwd, + no_focus, tab_id, } => { senders @@ -1332,6 +1350,7 @@ pub(crate) fn route_action( client_id, Some(NotificationEnd::new(completion_tx)), tab_id, + no_focus, )) .with_context(err_context)?; }, @@ -1341,6 +1360,7 @@ pub(crate) fn route_action( skip_cache, cwd, coordinates: floating_pane_coordinates, + no_focus, tab_id, } => { senders @@ -1353,6 +1373,7 @@ pub(crate) fn route_action( client_id, Some(NotificationEnd::new(completion_tx)), tab_id, + no_focus, )) .with_context(err_context)?; }, @@ -1361,6 +1382,7 @@ pub(crate) fn route_action( pane_name: name, skip_cache, close_replaced_pane, + no_focus, tab_id, } => { if let Some(pane_id) = pane_id { @@ -1374,6 +1396,7 @@ pub(crate) fn route_action( client_id, Some(NotificationEnd::new(completion_tx)), tab_id, + no_focus, )) .with_context(err_context)?; } else { @@ -1420,6 +1443,7 @@ pub(crate) fn route_action( close_replaced_pane, skip_cache, cwd, + no_focus, tab_id, } => { senders @@ -1434,6 +1458,7 @@ pub(crate) fn route_action( client_id, Some(NotificationEnd::new(completion_tx)), tab_id, + no_focus, )) .with_context(err_context)?; }, diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 34c9ced5..773e390b 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -609,6 +609,7 @@ pub enum ScreenInstruction { ClientId, Option<NotificationEnd>, Option<usize>, // tab_id + bool, ), // Option<String> is // optional pane title, bool is skip cache, Option<PathBuf> is an optional cwd NewFloatingPluginPane( @@ -620,6 +621,7 @@ pub enum ScreenInstruction { ClientId, Option<NotificationEnd>, Option<usize>, // tab_id + bool, ), // Option<String> is an // optional pane title, bool // is skip cache, Option<PathBuf> is an optional cwd @@ -632,6 +634,7 @@ pub enum ScreenInstruction { ClientId, Option<NotificationEnd>, Option<usize>, // tab_id + bool, ), // Option<String> is an // optional pane title, first bool is skip cache, second bool is close_replaced_pane StartOrReloadPluginPane(RunPluginOrAlias, Option<String>, Option<NotificationEnd>), @@ -678,6 +681,7 @@ pub enum ScreenInstruction { ClientId, Option<NotificationEnd>, Option<usize>, // tab_id + bool, ), // bools are: should_float, should_open_in_place, close_replaced_pane, Option<PaneId> is the pane id to replace, Option<PathBuf> is an optional cwd, bool after is skip_cache SuppressPane(PaneId, ClientId), UnsuppressPane(PaneId, bool), // bool -> should float if hidden @@ -4917,7 +4921,8 @@ impl Screen { } }; match client_id_tab_index_or_pane_id { - ClientTabIndexOrPaneId::ClientId(client_id) => { + ClientTabIndexOrPaneId::ClientId(client_id) + | ClientTabIndexOrPaneId::ClientIdNoFocus(client_id) => { active_tab!(self, client_id, |tab: &mut Tab| { match tab.get_active_pane_id(client_id) { Some(pane_id) => { @@ -4951,7 +4956,8 @@ impl Screen { }, }; }, - ClientTabIndexOrPaneId::TabIndex(_tab_index) => { + ClientTabIndexOrPaneId::TabIndex(_tab_index) + | ClientTabIndexOrPaneId::TabIndexNoFocus(_tab_index) => { log::error!("Cannot replace pane with tab index"); }, } @@ -6384,13 +6390,16 @@ pub(crate) fn screen_thread_main( let blocking_notification = if set_blocking { completion_tx } else { None }; match client_or_tab_index { - ClientTabIndexOrPaneId::ClientId(client_id) => { + ClientTabIndexOrPaneId::ClientId(client_id) + | ClientTabIndexOrPaneId::ClientIdNoFocus(client_id) => { + let should_focus_pane = + matches!(client_or_tab_index, ClientTabIndexOrPaneId::ClientId(_)); active_tab_and_connected_client_id_with_first_tab_fallback!(screen, client_id, |tab: &mut Tab, client_id: Option<ClientId>| { tab.new_pane(pid, initial_pane_title, invoked_with, start_suppressed, - true, + should_focus_pane, new_pane_placement, client_id, blocking_notification @@ -6410,7 +6419,10 @@ pub(crate) fn screen_thread_main( ) } }, - ClientTabIndexOrPaneId::TabIndex(tab_index) => { + ClientTabIndexOrPaneId::TabIndex(tab_index) + | ClientTabIndexOrPaneId::TabIndexNoFocus(tab_index) => { + let should_focus_pane = + matches!(client_or_tab_index, ClientTabIndexOrPaneId::TabIndex(_)); // Some placements (directional split, stacked without a // target pane) need a client_id to know which pane to // split relative to. Only resolve one when required. @@ -6440,7 +6452,7 @@ pub(crate) fn screen_thread_main( initial_pane_title, invoked_with, start_suppressed, - true, + should_focus_pane, new_pane_placement, client_id, blocking_notification, @@ -6459,16 +6471,46 @@ pub(crate) fn screen_thread_main( let should_focus_pane = false; for tab in all_tabs.values_mut() { if tab.has_pane_with_pid(&pane_id) { - tab.new_pane( - pid, - initial_pane_title, - invoked_with, - start_suppressed, - should_focus_pane, - new_pane_placement, - None, - blocking_notification, // TODO: is this correct? - )?; + match new_pane_placement { + NewPanePlacement::Tiled { + direction: Some(direction), + borderless, + } => { + if direction == Direction::Left + || direction == Direction::Right + { + tab.vertical_split_of_pane_id( + pid, + initial_pane_title, + invoked_with, + pane_id, + blocking_notification, + borderless, + )?; + } else { + tab.horizontal_split_of_pane_id( + pid, + initial_pane_title, + invoked_with, + pane_id, + blocking_notification, + borderless, + )?; + } + }, + _ => { + tab.new_pane( + pid, + initial_pane_title, + invoked_with, + start_suppressed, + should_focus_pane, + new_pane_placement, + None, + blocking_notification, + )?; + }, + } if let Some(hold_for_command) = hold_for_command { let is_first_run = true; tab.hold_pane(pid, None, is_first_run, hold_for_command); @@ -6496,12 +6538,14 @@ pub(crate) fn screen_thread_main( }, ScreenInstruction::OpenInPlaceEditor(pid, client_tab_index_or_pane_id) => { match client_tab_index_or_pane_id { - ClientTabIndexOrPaneId::ClientId(client_id) => { + ClientTabIndexOrPaneId::ClientId(client_id) + | ClientTabIndexOrPaneId::ClientIdNoFocus(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .replace_active_pane_with_editor_pane(pid, client_id), ?); screen.log_and_report_session_state()?; }, - ClientTabIndexOrPaneId::TabIndex(_tab_index) => { + ClientTabIndexOrPaneId::TabIndex(_tab_index) + | ClientTabIndexOrPaneId::TabIndexNoFocus(_tab_index) => { log::error!("Cannot OpenInPlaceEditor with a TabIndex"); }, ClientTabIndexOrPaneId::PaneId(pane_id_to_replace) => { @@ -8478,12 +8522,14 @@ pub(crate) fn screen_thread_main( client_id, completion_tx, explicit_tab_id, + no_focus, ) => { let tab_index = explicit_tab_id .unwrap_or_else(|| *screen.active_tab_ids.values().next().unwrap_or(&1)); let size = Size::default(); let should_float = Some(false); let should_be_opened_in_place = false; + let should_focus_plugin = if no_focus { Some(false) } else { None }; screen .bus .senders @@ -8499,7 +8545,7 @@ pub(crate) fn screen_thread_main( size, skip_cache, cwd, - None, + should_focus_plugin, None, completion_tx, ))?; @@ -8513,6 +8559,7 @@ pub(crate) fn screen_thread_main( client_id, completion_tx, explicit_tab_id, + no_focus, ) => { let resolved_tab_index = explicit_tab_id.or_else(|| screen.active_tab_ids.values().next().copied()); @@ -8521,6 +8568,7 @@ pub(crate) fn screen_thread_main( let size = Size::default(); let should_float = Some(true); let should_be_opened_in_place = false; + let should_focus_plugin = if no_focus { Some(false) } else { None }; screen .bus .senders @@ -8536,7 +8584,7 @@ pub(crate) fn screen_thread_main( size, skip_cache, cwd, - None, + should_focus_plugin, floating_pane_coordinates, completion_tx, ))?; @@ -8557,6 +8605,7 @@ pub(crate) fn screen_thread_main( client_id, completion_tx, explicit_tab_id, + no_focus, ) => { let resolved_tab_index = explicit_tab_id.or_else(|| screen.active_tab_ids.values().next().copied()); @@ -8565,6 +8614,7 @@ pub(crate) fn screen_thread_main( let size = Size::default(); let should_float = None; let should_be_in_place = true; + let should_focus_plugin = if no_focus { Some(false) } else { None }; screen .bus .senders @@ -8580,7 +8630,7 @@ pub(crate) fn screen_thread_main( size, skip_cache, None, - None, + should_focus_plugin, None, completion_tx, ))?; @@ -8891,6 +8941,7 @@ pub(crate) fn screen_thread_main( client_id, completion_tx, explicit_tab_id, + no_focus, ) => match pane_id_to_replace { Some(pane_id_to_replace) => { let resolved_tab_index = @@ -8913,7 +8964,7 @@ pub(crate) fn screen_thread_main( size, skip_cache, cwd, - None, + if no_focus { Some(false) } else { None }, None, completion_tx, ))?; @@ -8957,7 +9008,7 @@ pub(crate) fn screen_thread_main( Size::default(), skip_cache, cwd, - None, + if no_focus { Some(false) } else { None }, None, completion_tx, ))?; diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index d3bc154f..3189e69a 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -68,8 +68,8 @@ use zellij_utils::{ input::{ command::TerminalAction, layout::{ - FloatingPaneLayout, Run, RunPluginOrAlias, SwapFloatingLayout, SwapTiledLayout, - TiledPaneLayout, + FloatingPaneLayout, Run, RunPluginOrAlias, SplitDirection, SwapFloatingLayout, + SwapTiledLayout, TiledPaneLayout, }, options::PaneFrameStyle, parse_keys, @@ -2393,20 +2393,45 @@ impl Tab { direction: Some(direction), borderless, } => { - if let Some(client_id) = client_id { - if direction == Direction::Left || direction == Direction::Right { - self.vertical_split( + let is_vertical = direction == Direction::Left || direction == Direction::Right; + if should_focus_pane { + if let Some(client_id) = client_id { + if is_vertical { + self.vertical_split( + pid, + initial_pane_title, + client_id, + blocking_notification, + borderless, + )?; + } else { + self.horizontal_split( + pid, + initial_pane_title, + client_id, + blocking_notification, + borderless, + )?; + } + } + } else if let Some(target_pane_id) = + client_id.and_then(|client_id| self.tiled_panes.get_active_pane_id(client_id)) + { + if is_vertical { + self.vertical_split_of_pane_id( pid, initial_pane_title, - client_id, + invoked_with, + target_pane_id, blocking_notification, borderless, )?; } else { - self.horizontal_split( + self.horizontal_split_of_pane_id( pid, initial_pane_title, - client_id, + invoked_with, + target_pane_id, blocking_notification, borderless, )?; @@ -2559,7 +2584,7 @@ impl Tab { if self.floating_panes.panes_are_visible() { self.add_floating_pane(new_pane, pid, None, false, client_id) } else { - self.add_tiled_pane(new_pane, pid, false, client_id) + self.add_tiled_pane(new_pane, pid, false, None) } } } @@ -2663,7 +2688,8 @@ impl Tab { .insert(pid, (is_scrollback_editor, new_pane)); Ok(()) } else { - self.add_tiled_pane(new_pane, pid, false, client_id) + let focus_client_id = if should_focus_pane { client_id } else { None }; + self.add_tiled_pane(new_pane, pid, false, focus_client_id) } } pub fn new_floating_pane( @@ -2918,11 +2944,14 @@ impl Tab { Ok(()) } else { if let Some(pane_id_to_stack_under) = pane_id_to_stack_under { - // TODO: also focus pane if should_focus_pane? in cases where we did this from the CLI in an unfocused - // pane... - self.add_stacked_pane_to_pane_id(new_pane, pid, pane_id_to_stack_under) + self.add_stacked_pane_to_pane_id( + new_pane, + pid, + pane_id_to_stack_under, + should_focus_pane, + ) } else if let Some(client_id) = client_id { - self.add_stacked_pane_to_active_pane(new_pane, pid, client_id) + self.add_stacked_pane_to_active_pane(new_pane, pid, client_id, should_focus_pane) } else { log::error!("Must have client id or pane id to stack pane"); return Ok(()); @@ -3393,6 +3422,125 @@ impl Tab { Ok(()) } + pub fn horizontal_split_of_pane_id( + &mut self, + pid: PaneId, + initial_pane_title: Option<String>, + invoked_with: Option<Run>, + target_pane_id: PaneId, + completion_tx: Option<NotificationEnd>, + borderless: Option<bool>, + ) -> Result<()> { + self.split_of_pane_id( + pid, + initial_pane_title, + invoked_with, + target_pane_id, + SplitDirection::Horizontal, + completion_tx, + borderless, + ) + } + pub fn vertical_split_of_pane_id( + &mut self, + pid: PaneId, + initial_pane_title: Option<String>, + invoked_with: Option<Run>, + target_pane_id: PaneId, + completion_tx: Option<NotificationEnd>, + borderless: Option<bool>, + ) -> Result<()> { + self.split_of_pane_id( + pid, + initial_pane_title, + invoked_with, + target_pane_id, + SplitDirection::Vertical, + completion_tx, + borderless, + ) + } + fn split_of_pane_id( + &mut self, + pid: PaneId, + initial_pane_title: Option<String>, + invoked_with: Option<Run>, + target_pane_id: PaneId, + split_direction: SplitDirection, + completion_tx: Option<NotificationEnd>, + borderless: Option<bool>, + ) -> Result<()> { + let err_context = + || format!("failed to split pane {target_pane_id:?} without changing focus"); + if self.floating_panes.panes_are_visible() { + return Ok(()); + } + if !self.tiled_panes.panes_contain(&target_pane_id) { + return Ok(()); + } + self.close_down_to_max_terminals() + .with_context(err_context)?; + if self.tiled_panes.fullscreen_is_active() { + self.tiled_panes.unset_fullscreen(); + } + self.dissolve_stack_lists_for_classic_mutation(); + let can_split = match split_direction { + SplitDirection::Horizontal => self + .tiled_panes + .can_split_pane_id_horizontally(target_pane_id), + SplitDirection::Vertical => self + .tiled_panes + .can_split_pane_id_vertically(target_pane_id), + }; + if !can_split { + self.senders + .send_to_pty(PtyInstruction::ClosePane(pid, completion_tx)) + .with_context(err_context)?; + return Ok(()); + } + if let PaneId::Terminal(term_pid) = pid { + let next_terminal_position = self.get_next_terminal_position(); + let mut new_terminal = TerminalPane::new( + term_pid, + PaneGeom::default(), + self.style, + next_terminal_position, + String::new(), + self.link_handler.clone(), + self.character_cell_size.clone(), + self.sixel_image_store.clone(), + self.terminal_emulator_colors.clone(), + self.terminal_emulator_color_codes.clone(), + initial_pane_title, + invoked_with, + self.debug, + self.arrow_fonts, + self.styled_underlines, + self.osc8_hyperlinks, + self.explicitly_disable_kitty_keyboard_protocol, + completion_tx, + ); + if let Some(borderless) = borderless { + new_terminal.set_borderless(borderless); + } + match split_direction { + SplitDirection::Horizontal => self.tiled_panes.split_pane_id_horizontally( + pid, + Box::new(new_terminal), + target_pane_id, + ), + SplitDirection::Vertical => self.tiled_panes.split_pane_id_vertically( + pid, + Box::new(new_terminal), + target_pane_id, + ), + } + self.set_should_clear_display_before_rendering(); + self.swap_layouts.set_is_tiled_damaged(); + } + Ok(()) + } + pub fn get_active_pane(&self, client_id: ClientId) -> Option<&dyn Pane> { self.get_active_pane_id(client_id).and_then(|ap| { if self.floating_panes.panes_are_visible() { @@ -6654,6 +6802,7 @@ impl Tab { pane: Box<dyn Pane>, pane_id: PaneId, root_pane_id: PaneId, + should_focus: bool, ) -> Result<()> { if self.tiled_panes.fullscreen_is_active() { self.tiled_panes.unset_fullscreen(); @@ -6662,8 +6811,9 @@ impl Tab { self.tiled_panes .add_pane_to_stack_of_pane_id(pane_id, pane, root_pane_id); self.set_should_clear_display_before_rendering(); - self.tiled_panes.expand_pane_in_stack(pane_id); // so that it will get focused by all - // clients + if should_focus { + self.tiled_panes.expand_pane_in_stack(pane_id); + } self.swap_layouts.set_is_tiled_damaged(); Ok(()) } @@ -6672,6 +6822,7 @@ impl Tab { pane: Box<dyn Pane>, pane_id: PaneId, client_id: ClientId, + should_focus: bool, ) -> Result<()> { if self.tiled_panes.fullscreen_is_active() { self.tiled_panes.unset_fullscreen(); @@ -6679,7 +6830,9 @@ impl Tab { self.dissolve_stack_lists_for_classic_mutation(); self.tiled_panes .add_pane_to_stack_of_active_pane(pane_id, pane, client_id); - self.tiled_panes.focus_pane(pane_id, client_id); + if should_focus { + self.tiled_panes.focus_pane(pane_id, client_id); + } self.swap_layouts.set_is_tiled_damaged(); Ok(()) } diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index a45ec812..17c9ac36 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -3252,6 +3252,7 @@ pub fn send_cli_new_pane_action_with_default_parameters() { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: Some(false), tab_id: None, }; @@ -3307,6 +3308,7 @@ pub fn send_cli_new_pane_action_with_split_direction() { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: Some(false), tab_id: None, }; @@ -3362,6 +3364,7 @@ pub fn send_cli_new_pane_action_with_command_and_cwd() { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: Some(false), tab_id: None, }; @@ -3428,6 +3431,7 @@ pub fn send_cli_new_pane_action_with_floating_pane_and_coordinates() { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: Some(false), tab_id: None, }; @@ -3472,6 +3476,7 @@ pub fn send_cli_edit_action_with_default_parameters() { pinned: None, borderless: Some(false), near_current_pane: false, + no_focus: false, tab_id: None, }; send_cli_action_to_server(&session_metadata, cli_edit_action, client_id); @@ -3515,6 +3520,7 @@ pub fn send_cli_edit_action_with_line_number() { pinned: None, borderless: Some(false), near_current_pane: false, + no_focus: false, tab_id: None, }; send_cli_action_to_server(&session_metadata, cli_edit_action, client_id); @@ -3558,6 +3564,7 @@ pub fn send_cli_edit_action_with_split_direction() { pinned: None, borderless: Some(false), near_current_pane: false, + no_focus: false, tab_id: None, }; send_cli_action_to_server(&session_metadata, cli_edit_action, client_id); @@ -3759,6 +3766,7 @@ pub fn send_cli_new_tab_action_default_params() { block_until_exit: false, block_until_exit_success: false, block_until_exit_failure: false, + no_focus: false, }; send_cli_action_to_server(&session_metadata, new_tab_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); @@ -3807,6 +3815,7 @@ pub fn send_cli_new_tab_action_with_name_and_layout() { block_until_exit: false, block_until_exit_success: false, block_until_exit_failure: false, + no_focus: false, }; send_cli_action_to_server(&session_metadata, new_tab_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); @@ -5250,6 +5259,7 @@ pub fn send_cli_new_pane_in_place_with_close_replaced_pane() { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: None, }; @@ -5301,6 +5311,7 @@ pub fn send_cli_edit_in_place_with_close_replaced_pane() { height: None, pinned: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: None, }; @@ -7853,6 +7864,7 @@ pub fn send_cli_new_tab_action_with_layout_string() { block_until_exit: false, block_until_exit_success: false, block_until_exit_failure: false, + no_focus: false, }; send_cli_action_to_server(&session_metadata, new_tab_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); @@ -7907,6 +7919,7 @@ pub fn send_cli_new_tab_action_with_layout_string_and_name() { block_until_exit: false, block_until_exit_success: false, block_until_exit_failure: false, + no_focus: false, }; send_cli_action_to_server(&session_metadata, new_tab_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); @@ -7977,6 +7990,7 @@ pub fn send_cli_new_pane_action_with_tab_id() { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: Some(false), tab_id: Some(0), }; @@ -8039,6 +8053,7 @@ pub fn send_cli_new_floating_pane_action_with_tab_id() { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: Some(0), }; @@ -8088,6 +8103,7 @@ pub fn send_cli_edit_action_with_tab_id() { height: None, pinned: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: Some(0), }; @@ -8149,6 +8165,7 @@ pub fn send_cli_new_pane_action_with_tab_id_and_direction() { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: Some(false), tab_id: Some(0), }; @@ -8210,6 +8227,7 @@ pub fn send_cli_new_pane_action_with_tab_id_and_stacked() { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: Some(0), }; diff --git a/zellij-utils/assets/prost_ipc/client_server_contract.rs b/zellij-utils/assets/prost_ipc/client_server_contract.rs index 6b01f7fc..79b0b95a 100644 --- a/zellij-utils/assets/prost_ipc/client_server_contract.rs +++ b/zellij-utils/assets/prost_ipc/client_server_contract.rs @@ -848,6 +848,8 @@ pub struct EditFileAction { pub close_replaced_pane: bool, #[prost(uint32, optional, tag="9")] pub tab_id: ::core::option::Option<u32>, + #[prost(bool, tag="10")] + pub no_focus: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -862,6 +864,8 @@ pub struct NewFloatingPaneAction { pub near_current_pane: bool, #[prost(uint32, optional, tag="8")] pub tab_id: ::core::option::Option<u32>, + #[prost(bool, tag="9")] + pub no_focus: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -878,6 +882,8 @@ pub struct NewTiledPaneAction { pub borderless: ::core::option::Option<bool>, #[prost(uint32, optional, tag="9")] pub tab_id: ::core::option::Option<u32>, + #[prost(bool, tag="10")] + pub no_focus: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -894,6 +900,8 @@ pub struct NewInPlacePaneAction { pub close_replaced_pane: bool, #[prost(uint32, optional, tag="6")] pub tab_id: ::core::option::Option<u32>, + #[prost(bool, tag="7")] + pub no_focus: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -906,6 +914,8 @@ pub struct NewStackedPaneAction { pub near_current_pane: bool, #[prost(uint32, optional, tag="4")] pub tab_id: ::core::option::Option<u32>, + #[prost(bool, tag="5")] + pub no_focus: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -922,6 +932,8 @@ pub struct NewBlockingPaneAction { pub near_current_pane: bool, #[prost(uint32, optional, tag="6")] pub tab_id: ::core::option::Option<u32>, + #[prost(bool, tag="7")] + pub no_focus: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -984,6 +996,8 @@ pub struct RunAction { pub command: ::core::option::Option<RunCommandAction>, #[prost(bool, tag="2")] pub near_current_pane: bool, + #[prost(bool, tag="3")] + pub no_focus: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1020,6 +1034,8 @@ pub struct LaunchPluginAction { pub close_replaced_pane: bool, #[prost(uint32, optional, tag="7")] pub tab_id: ::core::option::Option<u32>, + #[prost(bool, tag="8")] + pub no_focus: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1064,6 +1080,8 @@ pub struct NewTiledPluginPaneAction { pub cwd: ::core::option::Option<::prost::alloc::string::String>, #[prost(uint32, optional, tag="5")] pub tab_id: ::core::option::Option<u32>, + #[prost(bool, tag="6")] + pub no_focus: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1080,6 +1098,8 @@ pub struct NewFloatingPluginPaneAction { pub coordinates: ::core::option::Option<FloatingPaneCoordinates>, #[prost(uint32, optional, tag="6")] pub tab_id: ::core::option::Option<u32>, + #[prost(bool, tag="7")] + pub no_focus: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1094,6 +1114,8 @@ pub struct NewInPlacePluginPaneAction { pub close_replaced_pane: bool, #[prost(uint32, optional, tag="5")] pub tab_id: ::core::option::Option<u32>, + #[prost(bool, tag="6")] + pub no_focus: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index ee2b6da1..e09755e0 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -520,6 +520,11 @@ pub enum Sessions { /// if set, will open the pane near the current one rather than following the user's focus #[clap(long)] near_current_pane: bool, + #[clap( + long, + help = "if set, will open the pane without changing the focus of any client, placing it relative to the pane the command was issued from" + )] + no_focus: bool, /// start this pane without a border (warning: will make it impossible to move with the /// mouse) #[clap(short, long, value_parser)] @@ -588,6 +593,11 @@ pub enum Sessions { /// Whether to pin a floating pane so that it is always on top #[clap(long, requires("floating"))] pinned: Option<bool>, + #[clap( + long, + help = "if set, will open the plugin pane without changing the focus of any client, placing it relative to the pane the command was issued from" + )] + no_focus: bool, /// start this pane without a border (warning: will make it impossible to move with the /// mouse) #[clap(short, long, value_parser)] @@ -657,6 +667,11 @@ pub enum Sessions { /// if set, will open the pane near the current one rather than following the user's focus #[clap(long)] near_current_pane: bool, + #[clap( + long, + help = "if set, will open the pane without changing the focus of any client, placing it relative to the pane the command was issued from" + )] + no_focus: bool, /// start this pane without a border (warning: will make it impossible to move with the /// mouse) #[clap(short, long, value_parser)] @@ -1050,6 +1065,11 @@ pub enum CliAction { /// if set, will open the pane near the current one rather than following the user's focus #[clap(long)] near_current_pane: bool, + #[clap( + long, + help = "if set, will open the pane without changing the focus of any client, placing it relative to the pane the command was issued from" + )] + no_focus: bool, /// start this pane without a border (warning: will make it impossible to move with the /// mouse) #[clap(long, value_parser)] @@ -1123,6 +1143,11 @@ pub enum CliAction { /// if set, will open the pane near the current one rather than following the user's focus #[clap(long)] near_current_pane: bool, + #[clap( + long, + help = "if set, will open the pane without changing the focus of any client, placing it relative to the pane the command was issued from" + )] + no_focus: bool, /// start this pane without a border (warning: will make it impossible to move with the /// mouse) #[clap(short, long, value_parser)] @@ -1335,6 +1360,12 @@ pub enum CliAction { conflicts_with("block-until-exit-failure") )] block_until_exit: bool, + + #[clap( + long, + help = "if set, will create the tab without changing the focus of any client" + )] + no_focus: bool, }, /// Move the focused tab in the specified direction. [right|left] MoveTab { @@ -1437,6 +1468,11 @@ pub enum CliAction { configuration: Option<PluginUserConfiguration>, #[clap(short, long, value_parser)] skip_plugin_cache: bool, + #[clap( + long, + help = "if set, will open the plugin pane without changing the focus of any client" + )] + no_focus: bool, /// Target a specific tab by ID #[clap(long, value_parser, conflicts_with("in-place"))] tab_id: Option<usize>, diff --git a/zellij-utils/src/client_server_contract/common_types.proto b/zellij-utils/src/client_server_contract/common_types.proto index 14f8eda0..4c2f8395 100644 --- a/zellij-utils/src/client_server_contract/common_types.proto +++ b/zellij-utils/src/client_server_contract/common_types.proto @@ -469,6 +469,7 @@ message EditFileAction { bool near_current_pane = 7; bool close_replaced_pane = 8; optional uint32 tab_id = 9; + bool no_focus = 10; } message NewFloatingPaneAction { @@ -477,6 +478,7 @@ message NewFloatingPaneAction { optional FloatingPaneCoordinates coordinates = 3; bool near_current_pane = 7; optional uint32 tab_id = 8; + bool no_focus = 9; } message NewTiledPaneAction { @@ -486,6 +488,7 @@ message NewTiledPaneAction { bool near_current_pane = 7; optional bool borderless = 8; optional uint32 tab_id = 9; + bool no_focus = 10; } message NewInPlacePaneAction { @@ -495,6 +498,7 @@ message NewInPlacePaneAction { optional PaneId pane_id_to_replace = 4; bool close_replaced_pane = 5; optional uint32 tab_id = 6; + bool no_focus = 7; } message NewStackedPaneAction { @@ -502,6 +506,7 @@ message NewStackedPaneAction { optional string pane_name = 2; bool near_current_pane = 3; optional uint32 tab_id = 4; + bool no_focus = 5; } message NewBlockingPaneAction { @@ -511,6 +516,7 @@ message NewBlockingPaneAction { optional UnblockCondition unblock_condition = 4; bool near_current_pane = 5; optional uint32 tab_id = 6; + bool no_focus = 7; } message PaneNameInputAction { @@ -549,6 +555,7 @@ message MoveTabAction { message RunAction { RunCommandAction command = 1; bool near_current_pane = 2; + bool no_focus = 3; } message LaunchOrFocusPluginAction { @@ -569,6 +576,7 @@ message LaunchPluginAction { optional string cwd = 5; bool close_replaced_pane = 6; optional uint32 tab_id = 7; + bool no_focus = 8; } message MouseEventAction { @@ -597,6 +605,7 @@ message NewTiledPluginPaneAction { bool skip_cache = 3; optional string cwd = 4; optional uint32 tab_id = 5; + bool no_focus = 6; } message NewFloatingPluginPaneAction { @@ -606,6 +615,7 @@ message NewFloatingPluginPaneAction { optional string cwd = 4; optional FloatingPaneCoordinates coordinates = 5; optional uint32 tab_id = 6; + bool no_focus = 7; } message NewInPlacePluginPaneAction { @@ -614,6 +624,7 @@ message NewInPlacePluginPaneAction { bool skip_cache = 3; bool close_replaced_pane = 4; optional uint32 tab_id = 5; + bool no_focus = 6; } message StartOrReloadPluginAction { diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 757e99e4..c0e953b6 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -235,6 +235,7 @@ pub enum Action { command: Option<RunCommandAction>, unblock_condition: Option<UnblockCondition>, near_current_pane: bool, + no_focus: bool, tab_id: Option<usize>, }, /// Open the file in a new pane using the default editor @@ -248,6 +249,7 @@ pub enum Action { start_suppressed: bool, coordinates: Option<FloatingPaneCoordinates>, near_current_pane: bool, + no_focus: bool, tab_id: Option<usize>, }, /// Open a new floating pane @@ -257,6 +259,7 @@ pub enum Action { pane_name: Option<String>, coordinates: Option<FloatingPaneCoordinates>, near_current_pane: bool, + no_focus: bool, tab_id: Option<usize>, }, /// Open a new tiled (embedded, non-floating) pane @@ -266,6 +269,7 @@ pub enum Action { command: Option<RunCommandAction>, pane_name: Option<String>, near_current_pane: bool, + no_focus: bool, borderless: Option<bool>, tab_id: Option<usize>, }, @@ -275,6 +279,7 @@ pub enum Action { command: Option<RunCommandAction>, pane_name: Option<String>, near_current_pane: bool, + no_focus: bool, pane_id_to_replace: Option<PaneId>, close_replaced_pane: bool, tab_id: Option<usize>, @@ -284,6 +289,7 @@ pub enum Action { command: Option<RunCommandAction>, pane_name: Option<String>, near_current_pane: bool, + no_focus: bool, tab_id: Option<usize>, }, /// Embed focused pane in tab if floating or float focused pane if embedded @@ -347,6 +353,7 @@ pub enum Action { Run { command: RunCommandAction, near_current_pane: bool, + no_focus: bool, }, /// Set pane default foreground/background color SetPaneColor { @@ -388,6 +395,7 @@ pub enum Action { close_replaced_pane: bool, skip_cache: bool, cwd: Option<PathBuf>, + no_focus: bool, tab_id: Option<usize>, }, MouseEvent { @@ -434,6 +442,7 @@ pub enum Action { pane_name: Option<String>, skip_cache: bool, cwd: Option<PathBuf>, + no_focus: bool, tab_id: Option<usize>, }, /// Returns: Created pane ID (format: plugin_<id>) @@ -443,6 +452,7 @@ pub enum Action { skip_cache: bool, cwd: Option<PathBuf>, coordinates: Option<FloatingPaneCoordinates>, + no_focus: bool, tab_id: Option<usize>, }, /// Returns: Created pane ID (format: plugin_<id>) @@ -451,6 +461,7 @@ pub enum Action { pane_name: Option<String>, skip_cache: bool, close_replaced_pane: bool, + no_focus: bool, tab_id: Option<usize>, }, StartOrReloadPlugin { @@ -1039,6 +1050,7 @@ impl Action { block_until_exit, unblock_condition, near_current_pane, + no_focus, borderless, tab_id, } => { @@ -1124,6 +1136,7 @@ impl Action { command, unblock_condition, near_current_pane, + no_focus, tab_id, }]) } else if let Some(plugin) = plugin { @@ -1156,6 +1169,7 @@ impl Action { coordinates: FloatingPaneCoordinates::new( x, y, width, height, pinned, borderless, ), + no_focus, tab_id, }]) } else if in_place { @@ -1164,6 +1178,7 @@ impl Action { pane_name: name, skip_cache: skip_plugin_cache, close_replaced_pane, + no_focus, tab_id, }]) } else { @@ -1180,6 +1195,7 @@ impl Action { pane_name: name, skip_cache: skip_plugin_cache, cwd, + no_focus, tab_id, }]) } @@ -1205,6 +1221,7 @@ impl Action { x, y, width, height, pinned, borderless, ), near_current_pane, + no_focus, tab_id, }]) } else if in_place { @@ -1212,6 +1229,7 @@ impl Action { command: Some(run_command_action), pane_name: name, near_current_pane, + no_focus, pane_id_to_replace, close_replaced_pane, tab_id, @@ -1221,6 +1239,7 @@ impl Action { command: Some(run_command_action), pane_name: name, near_current_pane, + no_focus, tab_id, }]) } else { @@ -1229,6 +1248,7 @@ impl Action { command: Some(run_command_action), pane_name: name, near_current_pane, + no_focus, borderless, tab_id, }]) @@ -1242,6 +1262,7 @@ impl Action { x, y, width, height, pinned, borderless, ), near_current_pane, + no_focus, tab_id, }]) } else if in_place { @@ -1249,6 +1270,7 @@ impl Action { command: None, pane_name: name, near_current_pane, + no_focus, pane_id_to_replace, close_replaced_pane, tab_id, @@ -1258,6 +1280,7 @@ impl Action { command: None, pane_name: name, near_current_pane, + no_focus, tab_id, }]) } else { @@ -1266,6 +1289,7 @@ impl Action { command: None, pane_name: name, near_current_pane, + no_focus, borderless, tab_id, }]) @@ -1286,6 +1310,7 @@ impl Action { height, pinned, near_current_pane, + no_focus, borderless, tab_id, } => { @@ -1311,6 +1336,7 @@ impl Action { x, y, width, height, pinned, borderless, ), near_current_pane, + no_focus, tab_id, }]) }, @@ -1414,6 +1440,7 @@ impl Action { block_until_exit_success, block_until_exit_failure, block_until_exit, + no_focus, } => { let current_dir = get_current_dir(); let cwd = cwd @@ -1520,8 +1547,8 @@ impl Action { .any(|(_, layout, _)| layout.focus.unwrap_or(false)); for (tab_name, layout, floating_panes_layout) in tabs.drain(..) { let name = tab_name.or_else(|| name.clone()); - let should_change_focus_to_new_tab = - layout.focus.unwrap_or_else(|| { + let should_change_focus_to_new_tab = !no_focus + && layout.focus.unwrap_or_else(|| { if !has_focused_tab { has_focused_tab = true; true @@ -1546,7 +1573,7 @@ impl Action { let swap_tiled_layouts = Some(layout.swap_tiled_layouts.clone()); let swap_floating_layouts = Some(layout.swap_floating_layouts.clone()); let (layout, floating_panes_layout) = layout.new_tab(); - let should_change_focus_to_new_tab = true; + let should_change_focus_to_new_tab = !no_focus; Ok(vec![Action::NewTab { tiled_layout: Some(layout), floating_layouts: floating_panes_layout, @@ -1636,8 +1663,8 @@ impl Action { .any(|(_, layout, _)| layout.focus.unwrap_or(false)); for (tab_name, layout, floating_panes_layout) in tabs.drain(..) { let name = tab_name.or_else(|| name.clone()); - let should_change_focus_to_new_tab = - layout.focus.unwrap_or_else(|| { + let should_change_focus_to_new_tab = !no_focus + && layout.focus.unwrap_or_else(|| { if !has_focused_tab { has_focused_tab = true; true @@ -1662,7 +1689,7 @@ impl Action { let swap_tiled_layouts = Some(layout.swap_tiled_layouts.clone()); let swap_floating_layouts = Some(layout.swap_floating_layouts.clone()); let (layout, floating_panes_layout) = layout.new_tab(); - let should_change_focus_to_new_tab = true; + let should_change_focus_to_new_tab = !no_focus; Ok(vec![Action::NewTab { tiled_layout: Some(layout), floating_layouts: floating_panes_layout, @@ -1676,7 +1703,7 @@ impl Action { }]) } } else { - let should_change_focus_to_new_tab = true; + let should_change_focus_to_new_tab = !no_focus; Ok(vec![Action::NewTab { tiled_layout: None, floating_layouts: vec![], @@ -1854,6 +1881,7 @@ impl Action { close_replaced_pane, configuration, skip_plugin_cache, + no_focus, tab_id, } => { let current_dir = get_current_dir(); @@ -1870,6 +1898,7 @@ impl Action { close_replaced_pane, skip_cache: skip_plugin_cache, cwd: Some(current_dir), + no_focus, tab_id, }]) }, @@ -3474,6 +3503,7 @@ mod tests { block_until_exit: false, block_until_exit_success: false, block_until_exit_failure: false, + no_focus: false, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_ok()); @@ -3510,6 +3540,7 @@ mod tests { block_until_exit: false, block_until_exit_success: false, block_until_exit_failure: false, + no_focus: false, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_err()); @@ -3608,6 +3639,7 @@ mod tests { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: Some(3), }; @@ -3651,6 +3683,7 @@ mod tests { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: None, }; @@ -3694,6 +3727,7 @@ mod tests { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: None, }; @@ -3739,6 +3773,7 @@ mod tests { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: None, }; @@ -3775,6 +3810,7 @@ mod tests { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: Some(5), }; @@ -3818,6 +3854,7 @@ mod tests { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: Some(1), }; @@ -3861,6 +3898,7 @@ mod tests { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: Some(2), }; @@ -3892,6 +3930,7 @@ mod tests { height: None, pinned: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: Some(4), }; @@ -3923,6 +3962,7 @@ mod tests { height: None, pinned: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: None, }; @@ -3966,6 +4006,7 @@ mod tests { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: Some(2), }; @@ -4009,6 +4050,7 @@ mod tests { block_until_exit: false, unblock_condition: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: Some(1), }; diff --git a/zellij-utils/src/ipc/protobuf_conversion.rs b/zellij-utils/src/ipc/protobuf_conversion.rs index 1f625ddd..ebaae103 100644 --- a/zellij-utils/src/ipc/protobuf_conversion.rs +++ b/zellij-utils/src/ipc/protobuf_conversion.rs @@ -1225,6 +1225,7 @@ impl From<crate::input::actions::Action> start_suppressed, coordinates, near_current_pane, + no_focus, tab_id, .. } => ActionType::EditFile(EditFileAction { @@ -1237,12 +1238,14 @@ impl From<crate::input::actions::Action> coordinates: coordinates.map(|c| c.into()), near_current_pane, tab_id: tab_id.map(|t| t as u32), + no_focus, }), crate::input::actions::Action::NewFloatingPane { command, pane_name, coordinates, near_current_pane, + no_focus, tab_id, .. } => ActionType::NewFloatingPane(NewFloatingPaneAction { @@ -1251,12 +1254,14 @@ impl From<crate::input::actions::Action> coordinates: coordinates.map(|c| c.into()), near_current_pane, tab_id: tab_id.map(|t| t as u32), + no_focus, }), crate::input::actions::Action::NewTiledPane { direction, command, pane_name, near_current_pane, + no_focus, borderless, tab_id, .. @@ -1267,11 +1272,13 @@ impl From<crate::input::actions::Action> near_current_pane, borderless, tab_id: tab_id.map(|t| t as u32), + no_focus, }), crate::input::actions::Action::NewInPlacePane { command, pane_name, near_current_pane, + no_focus, pane_id_to_replace, close_replaced_pane, tab_id, @@ -1283,11 +1290,13 @@ impl From<crate::input::actions::Action> pane_id_to_replace: pane_id_to_replace.and_then(|p| p.try_into().ok()), close_replaced_pane, tab_id: tab_id.map(|t| t as u32), + no_focus, }), crate::input::actions::Action::NewStackedPane { command, pane_name, near_current_pane, + no_focus, tab_id, .. } => ActionType::NewStackedPane(NewStackedPaneAction { @@ -1295,6 +1304,7 @@ impl From<crate::input::actions::Action> pane_name, near_current_pane, tab_id: tab_id.map(|t| t as u32), + no_focus, }), crate::input::actions::Action::NewBlockingPane { placement, @@ -1302,6 +1312,7 @@ impl From<crate::input::actions::Action> command, unblock_condition, near_current_pane, + no_focus, tab_id, .. } => ActionType::NewBlockingPane(NewBlockingPaneAction { @@ -1311,6 +1322,7 @@ impl From<crate::input::actions::Action> unblock_condition: unblock_condition.map(|c| unblock_condition_to_proto_i32(c)), near_current_pane, tab_id: tab_id.map(|t| t as u32), + no_focus, }), crate::input::actions::Action::TogglePaneEmbedOrFloating => { ActionType::TogglePaneEmbedOrFloating(TogglePaneEmbedOrFloatingAction {}) @@ -1403,9 +1415,11 @@ impl From<crate::input::actions::Action> crate::input::actions::Action::Run { command, near_current_pane, + no_focus, } => ActionType::Run(RunAction { command: Some(command.into()), near_current_pane, + no_focus, }), crate::input::actions::Action::Detach => ActionType::Detach(DetachAction {}), crate::input::actions::Action::SetDarkTheme => { @@ -1458,6 +1472,7 @@ impl From<crate::input::actions::Action> close_replaced_pane, skip_cache, cwd, + no_focus, tab_id, .. } => ActionType::LaunchPlugin(LaunchPluginAction { @@ -1468,6 +1483,7 @@ impl From<crate::input::actions::Action> skip_cache, cwd: cwd.map(|p| p.to_string_lossy().to_string()), tab_id: tab_id.map(|t| t as u32), + no_focus, }), crate::input::actions::Action::MouseEvent { event } => { ActionType::MouseEvent(MouseEventAction { @@ -1528,6 +1544,7 @@ impl From<crate::input::actions::Action> pane_name, skip_cache, cwd, + no_focus, tab_id, .. } => ActionType::NewTiledPluginPane(NewTiledPluginPaneAction { @@ -1536,6 +1553,7 @@ impl From<crate::input::actions::Action> skip_cache, cwd: cwd.map(|p| p.to_string_lossy().to_string()), tab_id: tab_id.map(|t| t as u32), + no_focus, }), crate::input::actions::Action::NewFloatingPluginPane { plugin, @@ -1543,6 +1561,7 @@ impl From<crate::input::actions::Action> skip_cache, cwd, coordinates, + no_focus, tab_id, .. } => ActionType::NewFloatingPluginPane(NewFloatingPluginPaneAction { @@ -1552,12 +1571,14 @@ impl From<crate::input::actions::Action> cwd: cwd.map(|p| p.to_string_lossy().to_string()), coordinates: coordinates.map(|c| c.into()), tab_id: tab_id.map(|t| t as u32), + no_focus, }), crate::input::actions::Action::NewInPlacePluginPane { plugin, pane_name, skip_cache, close_replaced_pane, + no_focus, tab_id, .. } => ActionType::NewInPlacePluginPane(NewInPlacePluginPaneAction { @@ -1566,6 +1587,7 @@ impl From<crate::input::actions::Action> skip_cache, close_replaced_pane, tab_id: tab_id.map(|t| t as u32), + no_focus, }), crate::input::actions::Action::StartOrReloadPlugin { plugin } => { ActionType::StartOrReloadPlugin(StartOrReloadPluginAction { @@ -2099,6 +2121,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action> .map(|c| c.try_into()) .transpose()?, near_current_pane: edit_file_action.near_current_pane, + no_focus: edit_file_action.no_focus, tab_id: edit_file_action.tab_id.map(|t| t as usize), }), ActionType::NewFloatingPane(new_floating_action) => { @@ -2113,6 +2136,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action> .map(|c| c.try_into()) .transpose()?, near_current_pane: new_floating_action.near_current_pane, + no_focus: new_floating_action.no_focus, tab_id: new_floating_action.tab_id.map(|t| t as usize), }) }, @@ -2125,6 +2149,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action> command: new_tiled_action.command.map(|c| c.try_into()).transpose()?, pane_name: new_tiled_action.pane_name, near_current_pane: new_tiled_action.near_current_pane, + no_focus: new_tiled_action.no_focus, borderless: new_tiled_action.borderless, tab_id: new_tiled_action.tab_id.map(|t| t as usize), }) @@ -2137,6 +2162,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action> .transpose()?, pane_name: new_in_place_action.pane_name, near_current_pane: new_in_place_action.near_current_pane, + no_focus: new_in_place_action.no_focus, pane_id_to_replace: new_in_place_action .pane_id_to_replace .and_then(|p| p.try_into().ok()), @@ -2152,6 +2178,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action> .transpose()?, pane_name: new_stacked_action.pane_name, near_current_pane: new_stacked_action.near_current_pane, + no_focus: new_stacked_action.no_focus, tab_id: new_stacked_action.tab_id.map(|t| t as usize), }) }, @@ -2171,6 +2198,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action> .map(|c| proto_i32_to_unblock_condition(c)) .transpose()?, near_current_pane: new_blocking_action.near_current_pane, + no_focus: new_blocking_action.no_focus, tab_id: new_blocking_action.tab_id.map(|t| t as usize), }) }, @@ -2287,6 +2315,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action> .ok_or_else(|| anyhow!("Run missing command"))? .try_into()?, near_current_pane: run_action.near_current_pane, + no_focus: run_action.no_focus, }), ActionType::Detach(_) => Ok(crate::input::actions::Action::Detach), ActionType::SetDarkTheme(_) => Ok(crate::input::actions::Action::SetDarkTheme), @@ -2332,6 +2361,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action> close_replaced_pane: launch_plugin_action.close_replaced_pane, skip_cache: launch_plugin_action.skip_cache, cwd: launch_plugin_action.cwd.map(PathBuf::from), + no_focus: launch_plugin_action.no_focus, tab_id: launch_plugin_action.tab_id.map(|t| t as usize), }) }, @@ -2405,6 +2435,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action> pane_name: new_tiled_plugin_action.pane_name, skip_cache: new_tiled_plugin_action.skip_cache, cwd: new_tiled_plugin_action.cwd.map(PathBuf::from), + no_focus: new_tiled_plugin_action.no_focus, tab_id: new_tiled_plugin_action.tab_id.map(|t| t as usize), }) }, @@ -2421,6 +2452,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action> .coordinates .map(|c| c.try_into()) .transpose()?, + no_focus: new_floating_plugin_action.no_focus, tab_id: new_floating_plugin_action.tab_id.map(|t| t as usize), }) }, @@ -2433,6 +2465,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action> pane_name: new_in_place_plugin_action.pane_name, skip_cache: new_in_place_plugin_action.skip_cache, close_replaced_pane: new_in_place_plugin_action.close_replaced_pane, + no_focus: new_in_place_plugin_action.no_focus, tab_id: new_in_place_plugin_action.tab_id.map(|t| t as usize), }) }, diff --git a/zellij-utils/src/ipc/tests/roundtrip_tests.rs b/zellij-utils/src/ipc/tests/roundtrip_tests.rs index 4f9d57ac..db0b8fc1 100644 --- a/zellij-utils/src/ipc/tests/roundtrip_tests.rs +++ b/zellij-utils/src/ipc/tests/roundtrip_tests.rs @@ -1059,6 +1059,7 @@ fn test_client_messages() { start_suppressed: false, coordinates: None, near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -1084,6 +1085,7 @@ fn test_client_messages() { start_suppressed: false, coordinates: None, near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -1109,6 +1111,7 @@ fn test_client_messages() { start_suppressed: false, coordinates: None, near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -1134,6 +1137,7 @@ fn test_client_messages() { start_suppressed: true, coordinates: FloatingPaneCoordinates::new(None, None, None, None, None, Some(false)), near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -1166,6 +1170,7 @@ fn test_client_messages() { Some(false), ), near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -1198,6 +1203,7 @@ fn test_client_messages() { Some(false), ), near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -1230,6 +1236,7 @@ fn test_client_messages() { Some(false), ), near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -1262,6 +1269,7 @@ fn test_client_messages() { Some(false) ), near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -1274,6 +1282,7 @@ fn test_client_messages() { pane_name: None, coordinates: None, near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -1302,6 +1311,7 @@ fn test_client_messages() { Some(false), ), near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -1334,6 +1344,7 @@ fn test_client_messages() { Some(false), ), near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -1346,6 +1357,7 @@ fn test_client_messages() { direction: None, pane_name: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: None, }, @@ -1372,6 +1384,7 @@ fn test_client_messages() { direction: Some(Direction::Right), pane_name: Some("my_pane_name".to_owned()), near_current_pane: false, + no_focus: false, borderless: Some(true), tab_id: None, }, @@ -1397,6 +1410,7 @@ fn test_client_messages() { }), pane_name: Some("my_pane_name".to_owned()), near_current_pane: false, + no_focus: false, pane_id_to_replace: None, close_replaced_pane: false, tab_id: None, @@ -1410,6 +1424,7 @@ fn test_client_messages() { command: None, pane_name: None, near_current_pane: false, + no_focus: false, pane_id_to_replace: None, close_replaced_pane: false, tab_id: None, @@ -1436,6 +1451,7 @@ fn test_client_messages() { }), pane_name: Some("my_pane_name".to_owned()), near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -1447,6 +1463,7 @@ fn test_client_messages() { command: None, pane_name: None, near_current_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -2249,6 +2266,7 @@ fn test_client_messages() { use_terminal_title: false, }, near_current_pane: false, + no_focus: false, }, terminal_id: Some(1), client_id: Some(100), @@ -2296,6 +2314,7 @@ fn test_client_messages() { close_replaced_pane: false, skip_cache: true, cwd: None, + no_focus: true, tab_id: None, }, terminal_id: Some(1), @@ -2310,6 +2329,7 @@ fn test_client_messages() { close_replaced_pane: false, skip_cache: false, cwd: Some(PathBuf::from("/path/to/cwd")), + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -2483,6 +2503,7 @@ fn test_client_messages() { pane_name: Some("my_pane_name".to_owned()), skip_cache: false, cwd: Some(PathBuf::from("relative/path/to/cwd")), + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -2496,6 +2517,7 @@ fn test_client_messages() { skip_cache: true, cwd: Some(PathBuf::from("relative/path/to/cwd")), coordinates: None, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -2516,6 +2538,7 @@ fn test_client_messages() { Some(true), Some(false), ), + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -2528,6 +2551,7 @@ fn test_client_messages() { pane_name: Some("my_pane_name".to_owned()), skip_cache: true, close_replaced_pane: false, + no_focus: false, tab_id: None, }, terminal_id: Some(1), @@ -2540,6 +2564,7 @@ fn test_client_messages() { command: None, pane_name: None, near_current_pane: false, + no_focus: false, borderless: None, tab_id: Some(3), }, @@ -2553,6 +2578,7 @@ fn test_client_messages() { pane_name: None, coordinates: None, near_current_pane: false, + no_focus: false, tab_id: Some(5), }, terminal_id: Some(1), @@ -2564,6 +2590,7 @@ fn test_client_messages() { command: None, pane_name: None, near_current_pane: false, + no_focus: false, tab_id: Some(2), }, terminal_id: Some(1), @@ -2580,6 +2607,7 @@ fn test_client_messages() { command: None, unblock_condition: None, near_current_pane: false, + no_focus: false, tab_id: Some(1), }, terminal_id: Some(1), @@ -2601,6 +2629,7 @@ fn test_client_messages() { start_suppressed: false, coordinates: None, near_current_pane: false, + no_focus: false, tab_id: Some(4), }, terminal_id: Some(1), @@ -2612,6 +2641,7 @@ fn test_client_messages() { command: None, pane_name: None, near_current_pane: false, + no_focus: false, pane_id_to_replace: None, close_replaced_pane: false, tab_id: Some(7), @@ -2642,6 +2672,7 @@ fn test_client_messages() { close_replaced_pane: false, skip_cache: false, cwd: None, + no_focus: false, tab_id: Some(6), }, terminal_id: Some(1), @@ -2654,6 +2685,7 @@ fn test_client_messages() { pane_name: None, skip_cache: false, cwd: None, + no_focus: false, tab_id: Some(1), }, terminal_id: Some(1), @@ -2667,6 +2699,7 @@ fn test_client_messages() { skip_cache: false, cwd: None, coordinates: None, + no_focus: false, tab_id: Some(3), }, terminal_id: Some(1), @@ -2679,6 +2712,7 @@ fn test_client_messages() { pane_name: None, skip_cache: false, close_replaced_pane: false, + no_focus: false, tab_id: Some(2), }, terminal_id: Some(1), diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 30495d5f..94848e63 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -573,6 +573,7 @@ impl Action { command: None, pane_name: None, near_current_pane: false, + no_focus: false, tab_id: None, }); } else { @@ -2038,6 +2039,7 @@ impl TryFrom<(&KdlNode, &Options)> for Action { x, y, width, height, pinned, borderless, ), near_current_pane: false, + no_focus: false, tab_id: None, }) } else if in_place { @@ -2045,6 +2047,7 @@ impl TryFrom<(&KdlNode, &Options)> for Action { command: Some(run_command_action), pane_name: name, near_current_pane: false, + no_focus: false, pane_id_to_replace: None, close_replaced_pane, tab_id: None, @@ -2054,6 +2057,7 @@ impl TryFrom<(&KdlNode, &Options)> for Action { command: Some(run_command_action), pane_name: name, near_current_pane: false, + no_focus: false, tab_id: None, }) } else { @@ -2062,6 +2066,7 @@ impl TryFrom<(&KdlNode, &Options)> for Action { command: Some(run_command_action), pane_name: name, near_current_pane: false, + no_focus: false, borderless: None, tab_id: None, }) @@ -2171,6 +2176,7 @@ impl TryFrom<(&KdlNode, &Options)> for Action { skip_cache: skip_plugin_cache, cwd: None, // we explicitly do not send the current dir here so that it will be // filled from the active pane == better UX + no_focus: false, tab_id: None, }) }, diff --git a/zellij-utils/src/plugin_api/action.rs b/zellij-utils/src/plugin_api/action.rs index 209bff3f..f1d71222 100644 --- a/zellij-utils/src/plugin_api/action.rs +++ b/zellij-utils/src/plugin_api/action.rs @@ -355,6 +355,7 @@ impl TryFrom<ProtobufAction> for Action { start_suppressed: false, coordinates: None, near_current_pane, + no_focus: false, tab_id: None, }) }, @@ -371,6 +372,7 @@ impl TryFrom<ProtobufAction> for Action { pane_name, coordinates: None, near_current_pane, + no_focus: false, tab_id: None, }) } else { @@ -379,6 +381,7 @@ impl TryFrom<ProtobufAction> for Action { pane_name: None, coordinates: None, near_current_pane, + no_focus: false, tab_id: None, }) } @@ -401,6 +404,7 @@ impl TryFrom<ProtobufAction> for Action { command: Some(run_command_action), pane_name, near_current_pane, + no_focus: false, borderless, tab_id: None, }) @@ -410,6 +414,7 @@ impl TryFrom<ProtobufAction> for Action { command: None, pane_name: None, near_current_pane, + no_focus: false, borderless, tab_id: None, }) @@ -620,6 +625,7 @@ impl TryFrom<ProtobufAction> for Action { Ok(Action::Run { command: run_command_action, near_current_pane: false, + no_focus: false, }) }, _ => Err("Wrong payload for Action::Run"), @@ -723,6 +729,7 @@ impl TryFrom<ProtobufAction> for Action { close_replaced_pane: false, skip_cache: skip_plugin_cache, cwd: None, + no_focus: false, tab_id: None, }) }, @@ -843,6 +850,7 @@ impl TryFrom<ProtobufAction> for Action { pane_name, skip_cache: skip_plugin_cache, cwd: None, + no_focus: false, tab_id: None, }) }, @@ -869,6 +877,7 @@ impl TryFrom<ProtobufAction> for Action { skip_cache: skip_plugin_cache, cwd: None, coordinates: None, + no_focus: false, tab_id: None, }) }, @@ -1022,6 +1031,7 @@ impl TryFrom<ProtobufAction> for Action { command: None, pane_name: None, near_current_pane: false, + no_focus: false, tab_id: None, }), }, @@ -1044,6 +1054,7 @@ impl TryFrom<ProtobufAction> for Action { command, unblock_condition, near_current_pane, + no_focus: false, tab_id: None, }) }, @@ -1062,6 +1073,7 @@ impl TryFrom<ProtobufAction> for Action { command: Some(run_command_action), pane_name, near_current_pane, + no_focus: false, pane_id_to_replace, close_replaced_pane, tab_id: None, @@ -1071,6 +1083,7 @@ impl TryFrom<ProtobufAction> for Action { command: None, pane_name: payload.pane_name, near_current_pane, + no_focus: false, pane_id_to_replace, close_replaced_pane, tab_id: None, @@ -1598,6 +1611,7 @@ impl TryFrom<Action> for ProtobufAction { Action::Run { command: run_command_action, near_current_pane: _, + no_focus: _, } => { let run_command_action: ProtobufRunCommandAction = run_command_action.try_into()?; Ok(ProtobufAction { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap index 5a35fe4e..9b6ac7fe 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap @@ -1349,6 +1349,7 @@ Config { command: None, pane_name: None, near_current_pane: false, + no_focus: false, tab_id: None, }, SwitchToMode { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap index 83046edf..6c210eeb 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap @@ -1349,6 +1349,7 @@ Config { command: None, pane_name: None, near_current_pane: false, + no_focus: false, tab_id: None, }, SwitchToMode { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap index 7ae735ab..7f1625d3 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap @@ -1349,6 +1349,7 @@ Config { command: None, pane_name: None, near_current_pane: false, + no_focus: false, tab_id: None, }, SwitchToMode { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap index f0391d92..c7db95cd 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap @@ -1349,6 +1349,7 @@ Config { command: None, pane_name: None, near_current_pane: false, + no_focus: false, tab_id: None, }, SwitchToMode { |
