aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2024-07-05 15:13:51 +0200
committerGitHub <noreply@github.com>2024-07-05 15:13:51 +0200
commita6d6c0e4ff03c35cafb6f04363ecab856ee3682d (patch)
treeeadf1c24c4c6a1e4b7499b3cd047351fd29e52de
parentdocs(changelog): wasmer -> wasmtime (diff)
downloadsl0ppij-a6d6c0e4ff03c35cafb6f04363ecab856ee3682d.tar.gz
sl0ppij-a6d6c0e4ff03c35cafb6f04363ecab856ee3682d.tar.xz
sl0ppij-a6d6c0e4ff03c35cafb6f04363ecab856ee3682d.zip
feat(ui): status bar redesign (#3475)
* work * work * working * get default mode from server and some ui responsiveness * work * finish design and get tests to pass * get e2e tests to pass * add classic layout * add classic layout assets * fix e2e tests * style(fmt): rustfmt * fix plugin system test * style(fmt): some cleanups
-rw-r--r--Cargo.lock1
-rw-r--r--default-plugins/status-bar/Cargo.toml1
-rw-r--r--default-plugins/status-bar/src/first_line.rs47
-rw-r--r--default-plugins/status-bar/src/main.rs30
-rw-r--r--default-plugins/status-bar/src/one_line_ui.rs1440
-rw-r--r--default-plugins/tab-bar/src/line.rs211
-rw-r--r--default-plugins/tab-bar/src/main.rs17
-rw-r--r--default-plugins/tab-bar/src/tab.rs2
-rw-r--r--src/tests/e2e/cases.rs132
-rw-r--r--src/tests/e2e/remote_runner.rs4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__bracketed_paste.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_pane.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__lock_mode.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_left.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_left_until_it_wraps_around.snap5
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_right.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_right_until_it_wraps_around.snap5
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__open_new_tab.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session_with_viewport_serialization.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane.snap10
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap10
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap8
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__starts_with_one_terminal.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__status_bar_loads_custom_keybindings.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_floating_panes.snap10
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_pane_fullscreen.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_pane.snap6
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_tab.snap6
-rw-r--r--src/tests/e2e/steps.rs23
-rw-r--r--zellij-server/src/lib.rs12
-rw-r--r--zellij-server/src/plugins/mod.rs6
-rw-r--r--zellij-server/src/plugins/plugin_loader.rs19
-rw-r--r--zellij-server/src/plugins/plugin_map.rs2
-rw-r--r--zellij-server/src/plugins/unit/plugin_tests.rs7
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap5
-rw-r--r--zellij-server/src/plugins/wasm_bridge.rs11
-rw-r--r--zellij-server/src/plugins/zellij_exports.rs1
-rw-r--r--zellij-server/src/route.rs17
-rw-r--r--zellij-server/src/screen.rs1
-rw-r--r--zellij-server/src/unit/screen_tests.rs4
-rw-r--r--zellij-utils/assets/layouts/classic.kdl13
-rw-r--r--zellij-utils/assets/layouts/classic.swap.kdl104
-rw-r--r--zellij-utils/assets/layouts/default.kdl2
-rw-r--r--zellij-utils/assets/layouts/default.swap.kdl2
-rw-r--r--zellij-utils/assets/prost/api.event.rs2
-rw-r--r--zellij-utils/src/data.rs1
-rw-r--r--zellij-utils/src/input/actions.rs31
-rw-r--r--zellij-utils/src/input/layout.rs17
-rw-r--r--zellij-utils/src/input/mod.rs2
-rw-r--r--zellij-utils/src/plugin_api/event.proto1
-rw-r--r--zellij-utils/src/plugin_api/event.rs9
-rw-r--r--zellij-utils/src/setup.rs14
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap18
71 files changed, 2208 insertions, 225 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2803fb4c..cfb94204 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3811,6 +3811,7 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
+ "unicode-width",
"zellij-tile",
"zellij-tile-utils",
]
diff --git a/default-plugins/status-bar/Cargo.toml b/default-plugins/status-bar/Cargo.toml
index a2ca3175..c1492c19 100644
--- a/default-plugins/status-bar/Cargo.toml
+++ b/default-plugins/status-bar/Cargo.toml
@@ -13,6 +13,7 @@ rand = "0.8.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0.30"
+unicode-width = "0.1.8"
zellij-tile = { path = "../../zellij-tile" }
zellij-tile-utils = { path = "../../zellij-tile-utils" }
diff --git a/default-plugins/status-bar/src/first_line.rs b/default-plugins/status-bar/src/first_line.rs
index 1fffd755..ee9b9478 100644
--- a/default-plugins/status-bar/src/first_line.rs
+++ b/default-plugins/status-bar/src/first_line.rs
@@ -8,14 +8,16 @@ use crate::{
};
use crate::{ColoredElements, LinePart};
-struct KeyShortcut {
- mode: KeyMode,
- action: KeyAction,
- key: Option<KeyWithModifier>,
+#[derive(Debug)]
+pub struct KeyShortcut {
+ pub mode: KeyMode,
+ pub action: KeyAction,
+ pub key: Option<KeyWithModifier>,
}
-#[derive(PartialEq)]
-enum KeyAction {
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum KeyAction {
+ Unlock,
Lock,
Pane,
Tab,
@@ -27,7 +29,8 @@ enum KeyAction {
Tmux,
}
-enum KeyMode {
+#[derive(Debug, Copy, Clone)]
+pub enum KeyMode {
Unselected,
UnselectedAlternate,
Selected,
@@ -42,6 +45,7 @@ impl KeyShortcut {
pub fn full_text(&self) -> String {
match self.action {
KeyAction::Lock => String::from("LOCK"),
+ KeyAction::Unlock => String::from("UNLOCK"),
KeyAction::Pane => String::from("PANE"),
KeyAction::Tab => String::from("TAB"),
KeyAction::Resize => String::from("RESIZE"),
@@ -82,6 +86,35 @@ impl KeyShortcut {
};
format!("{}", key)
}
+ pub fn get_key(&self) -> Option<KeyWithModifier> {
+ self.key.clone()
+ }
+ pub fn get_mode(&self) -> KeyMode {
+ self.mode
+ }
+ pub fn get_action(&self) -> KeyAction {
+ self.action
+ }
+ pub fn is_selected(&self) -> bool {
+ match self.mode {
+ KeyMode::Selected => true,
+ _ => false,
+ }
+ }
+ pub fn short_text(&self) -> String {
+ match self.action {
+ KeyAction::Lock => String::from("Lo"),
+ KeyAction::Unlock => String::from("Un"),
+ KeyAction::Pane => String::from("Pa"),
+ KeyAction::Tab => String::from("Ta"),
+ KeyAction::Resize => String::from("Re"),
+ KeyAction::Search => String::from("Se"),
+ KeyAction::Quit => String::from("Qu"),
+ KeyAction::Session => String::from("Se"),
+ KeyAction::Move => String::from("Mo"),
+ KeyAction::Tmux => String::from("Tm"),
+ }
+ }
}
/// Generate long mode shortcut tile.
diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs
index 6fed529f..7db9b09d 100644
--- a/default-plugins/status-bar/src/main.rs
+++ b/default-plugins/status-bar/src/main.rs
@@ -1,4 +1,5 @@
mod first_line;
+mod one_line_ui;
mod second_line;
mod tip;
@@ -15,6 +16,7 @@ use zellij_tile::prelude::*;
use zellij_tile_utils::{palette_match, style};
use first_line::first_line;
+use one_line_ui::one_line_ui;
use second_line::{
floating_panes_are_visible, fullscreen_panes_to_hide, keybinds,
locked_floating_panes_are_visible, locked_fullscreen_panes_to_hide, system_clipboard_error,
@@ -35,6 +37,8 @@ struct State {
mode_info: ModeInfo,
text_copy_destination: Option<CopyDestination>,
display_system_clipboard_failure: bool,
+ classic_ui: bool,
+ base_mode_is_locked: bool,
}
register_plugin!(State);
@@ -177,9 +181,13 @@ fn color_elements(palette: Palette, different_color_alternates: bool) -> Colored
}
impl ZellijPlugin for State {
- fn load(&mut self, _configuration: BTreeMap<String, String>) {
+ fn load(&mut self, configuration: BTreeMap<String, String>) {
// TODO: Should be able to choose whether to use the cache through config.
self.tip_name = get_cached_tip_name();
+ self.classic_ui = configuration
+ .get("classic")
+ .map(|c| c == "true")
+ .unwrap_or(false);
set_selectable(false);
subscribe(&[
EventType::ModeUpdate,
@@ -198,6 +206,7 @@ impl ZellijPlugin for State {
should_render = true;
}
self.mode_info = mode_info;
+ self.base_mode_is_locked = self.mode_info.base_mode == Some(InputMode::Locked);
},
Event::TabUpdate(tabs) => {
if self.tabs != tabs {
@@ -244,6 +253,21 @@ impl ZellijPlugin for State {
""
};
+ if rows == 1 && !self.classic_ui {
+ let active_tab = self.tabs.iter().find(|t| t.active);
+ print!(
+ "{}",
+ one_line_ui(
+ &self.mode_info,
+ active_tab,
+ cols,
+ separator,
+ self.base_mode_is_locked
+ )
+ );
+ return;
+ }
+
let active_tab = self.tabs.iter().find(|t| t.active);
let first_line = first_line(&self.mode_info, active_tab, cols, separator);
let second_line = self.second_line(cols);
@@ -418,10 +442,6 @@ pub fn style_key_with_modifier(
let common_modifiers = get_common_modifiers(keyvec.iter().collect());
- // let modifier_str = match get_common_modifier(keyvec.iter().collect()) {
- // Some(modifier) => modifier,
- // None => "".to_string(),
- // };
let no_common_modifier = common_modifiers.is_empty();
let modifier_str = common_modifiers
.iter()
diff --git a/default-plugins/status-bar/src/one_line_ui.rs b/default-plugins/status-bar/src/one_line_ui.rs
new file mode 100644
index 00000000..87b1e2fd
--- /dev/null
+++ b/default-plugins/status-bar/src/one_line_ui.rs
@@ -0,0 +1,1440 @@
+use ansi_term::{ANSIString, ANSIStrings};
+use ansi_term::{
+ Color::{Fixed, RGB},
+ Style,
+};
+use std::collections::HashMap;
+use zellij_tile::prelude::actions::Action;
+use zellij_tile::prelude::*;
+use zellij_tile_utils::palette_match;
+
+use crate::first_line::{to_char, KeyAction, KeyMode, KeyShortcut};
+use crate::{action_key, action_key_group, color_elements, MORE_MSG, TO_NORMAL};
+use crate::{ColoredElements, LinePart};
+use unicode_width::UnicodeWidthStr;
+
+pub fn one_line_ui(
+ help: &ModeInfo,
+ tab_info: Option<&TabInfo>,
+ mut max_len: usize,
+ separator: &str,
+ base_mode_is_locked: bool,
+) -> LinePart {
+ let mut line_part_to_render = LinePart::default();
+ let mut append = |line_part: &LinePart, max_len: &mut usize| {
+ line_part_to_render.append(line_part);
+ *max_len = max_len.saturating_sub(line_part.len);
+ };
+
+ render_mode_key_indicators(help, max_len, separator, base_mode_is_locked)
+ .map(|mode_key_indicators| append(&mode_key_indicators, &mut max_len))
+ .and_then(|_| match help.mode {
+ InputMode::Normal | InputMode::Locked => render_secondary_info(help, tab_info, max_len)
+ .map(|secondary_info| append(&secondary_info, &mut max_len)),
+ _ => add_keygroup_separator(help, max_len)
+ .map(|key_group_separator| append(&key_group_separator, &mut max_len))
+ .and_then(|_| keybinds(help, max_len))
+ .map(|keybinds| append(&keybinds, &mut max_len)),
+ });
+ line_part_to_render
+}
+
+fn to_base_mode(base_mode: InputMode) -> Action {
+ Action::SwitchToMode(base_mode)
+}
+
+fn base_mode_locked_mode_indicators(help: &ModeInfo) -> HashMap<InputMode, Vec<KeyShortcut>> {
+ let locked_binds = &help.get_keybinds_for_mode(InputMode::Locked);
+ let normal_binds = &help.get_keybinds_for_mode(InputMode::Normal);
+ let pane_binds = &help.get_keybinds_for_mode(InputMode::Pane);
+ let tab_binds = &help.get_keybinds_for_mode(InputMode::Tab);
+ let resize_binds = &help.get_keybinds_for_mode(InputMode::Resize);
+ let move_binds = &help.get_keybinds_for_mode(InputMode::Move);
+ let scroll_binds = &help.get_keybinds_for_mode(InputMode::Scroll);
+ let session_binds = &help.get_keybinds_for_mode(InputMode::Session);
+ HashMap::from([
+ (
+ InputMode::Locked,
+ vec![KeyShortcut::new(
+ KeyMode::Unselected,
+ KeyAction::Unlock,
+ to_char(action_key(
+ locked_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ )],
+ ),
+ (
+ InputMode::Normal,
+ vec![
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Unlock,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Locked)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::UnselectedAlternate,
+ KeyAction::Pane,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Pane)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::Unselected,
+ KeyAction::Tab,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Tab)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::UnselectedAlternate,
+ KeyAction::Resize,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Resize)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::Unselected,
+ KeyAction::Move,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Move)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::UnselectedAlternate,
+ KeyAction::Search,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Scroll)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::Unselected,
+ KeyAction::Session,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Session)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::UnselectedAlternate,
+ KeyAction::Quit,
+ to_char(action_key(normal_binds, &[Action::Quit])),
+ ),
+ ],
+ ),
+ (
+ InputMode::Pane,
+ vec![
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Unlock,
+ to_char(action_key(
+ pane_binds,
+ &[Action::SwitchToMode(InputMode::Locked)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Pane,
+ to_char(action_key(
+ pane_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ ),
+ ],
+ ),
+ (
+ InputMode::Tab,
+ vec![
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Unlock,
+ to_char(action_key(
+ tab_binds,
+ &[Action::SwitchToMode(InputMode::Locked)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Tab,
+ to_char(action_key(
+ tab_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ ),
+ ],
+ ),
+ (
+ InputMode::Resize,
+ vec![
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Unlock,
+ to_char(action_key(
+ resize_binds,
+ &[Action::SwitchToMode(InputMode::Locked)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Resize,
+ to_char(action_key(
+ resize_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ ),
+ ],
+ ),
+ (
+ InputMode::Move,
+ vec![
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Unlock,
+ to_char(action_key(
+ move_binds,
+ &[Action::SwitchToMode(InputMode::Locked)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Move,
+ to_char(action_key(
+ move_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ ),
+ ],
+ ),
+ (
+ InputMode::Scroll,
+ vec![
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Unlock,
+ to_char(action_key(
+ scroll_binds,
+ &[Action::SwitchToMode(InputMode::Locked)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Search,
+ to_char(action_key(
+ scroll_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ ),
+ ],
+ ),
+ (
+ InputMode::Session,
+ vec![
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Unlock,
+ to_char(action_key(
+ session_binds,
+ &[Action::SwitchToMode(InputMode::Locked)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Session,
+ to_char(action_key(
+ session_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ ),
+ ],
+ ),
+ ])
+}
+
+fn base_mode_normal_mode_indicators(help: &ModeInfo) -> HashMap<InputMode, Vec<KeyShortcut>> {
+ let locked_binds = &help.get_keybinds_for_mode(InputMode::Locked);
+ let normal_binds = &help.get_keybinds_for_mode(InputMode::Normal);
+ let pane_binds = &help.get_keybinds_for_mode(InputMode::Pane);
+ let tab_binds = &help.get_keybinds_for_mode(InputMode::Tab);
+ let resize_binds = &help.get_keybinds_for_mode(InputMode::Resize);
+ let move_binds = &help.get_keybinds_for_mode(InputMode::Move);
+ let scroll_binds = &help.get_keybinds_for_mode(InputMode::Scroll);
+ let session_binds = &help.get_keybinds_for_mode(InputMode::Session);
+ HashMap::from([
+ (
+ InputMode::Locked,
+ vec![KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Lock,
+ to_char(action_key(
+ locked_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ )],
+ ),
+ (
+ InputMode::Normal,
+ vec![
+ KeyShortcut::new(
+ KeyMode::Unselected,
+ KeyAction::Lock,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Locked)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::UnselectedAlternate,
+ KeyAction::Pane,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Pane)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::Unselected,
+ KeyAction::Tab,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Tab)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::UnselectedAlternate,
+ KeyAction::Resize,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Resize)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::Unselected,
+ KeyAction::Move,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Move)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::UnselectedAlternate,
+ KeyAction::Search,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Scroll)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::Unselected,
+ KeyAction::Session,
+ to_char(action_key(
+ normal_binds,
+ &[Action::SwitchToMode(InputMode::Session)],
+ )),
+ ),
+ KeyShortcut::new(
+ KeyMode::UnselectedAlternate,
+ KeyAction::Quit,
+ to_char(action_key(normal_binds, &[Action::Quit])),
+ ),
+ ],
+ ),
+ (
+ InputMode::Pane,
+ vec![KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Pane,
+ to_char(action_key(
+ pane_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ )],
+ ),
+ (
+ InputMode::Tab,
+ vec![KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Tab,
+ to_char(action_key(
+ tab_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ )],
+ ),
+ (
+ InputMode::Resize,
+ vec![KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Resize,
+ to_char(action_key(
+ resize_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ )],
+ ),
+ (
+ InputMode::Move,
+ vec![KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Move,
+ to_char(action_key(
+ move_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ )],
+ ),
+ (
+ InputMode::Scroll,
+ vec![KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Search,
+ to_char(action_key(
+ scroll_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ )],
+ ),
+ (
+ InputMode::Session,
+ vec![KeyShortcut::new(
+ KeyMode::Selected,
+ KeyAction::Session,
+ to_char(action_key(
+ session_binds,
+ &[Action::SwitchToMode(InputMode::Normal)],
+ )),
+ )],
+ ),
+ ])
+}
+
+fn render_mode_key_indicators(
+ help: &ModeInfo,
+ max_len: usize,
+ separator: &str,
+ base_mode_is_locked: bool,
+) -> Option<LinePart> {
+ let mut line_part_to_render = LinePart::default();
+ let supports_arrow_fonts = !help.capabilities.arrow_fonts;
+ let colored_elements = color_elements(help.style.colors, !supports_arrow_fonts);
+ let default_keys = if base_mode_is_locked {
+ base_mode_locked_mode_indicators(help)
+ } else {
+ base_mode_normal_mode_indicators(help)
+ };
+ match common_modifiers_in_all_modes(&default_keys) {
+ Some(modifiers) => {
+ if let Some(default_keys) = default_keys.get(&help.mode) {
+ let keys_without_common_modifiers: Vec<KeyShortcut> = default_keys
+ .iter()
+ .map(|key_shortcut| {
+ let key = key_shortcut
+ .get_key()
+ .map(|k| k.strip_common_modifiers(&modifiers));
+ let mode = key_shortcut.get_mode();
+ let action = key_shortcut.get_action();
+ KeyShortcut::new(mode, action, key)
+ })
+ .collect();
+ render_common_modifiers(
+ &colored_elements,
+ help,
+ &modifiers,
+ &mut line_part_to_render,
+ separator,
+ );
+
+ let full_shortcut_list =
+ full_inline_keys_modes_shortcut_list(&keys_without_common_modifiers, help);
+
+ if line_part_to_render.len + full_shortcut_list.len <= max_len {
+ line_part_to_render.append(&full_shortcut_list);
+ } else {
+ let shortened_shortcut_list = shortened_inline_keys_modes_shortcut_list(
+ &keys_without_common_modifiers,
+ help,
+ );
+ if line_part_to_render.len + shortened_shortcut_list.len <= max_len {
+ line_part_to_render.append(&shortened_shortcut_list);
+ }
+ }
+ }
+ },
+ None => {
+ if let Some(default_keys) = default_keys.get(&help.mode) {
+ let full_shortcut_list = full_modes_shortcut_list(&default_keys, help);
+ if line_part_to_render.len + full_shortcut_list.len <= max_len {
+ line_part_to_render.append(&full_shortcut_list);
+ } else {
+ let shortened_shortcut_list =
+ shortened_modes_shortcut_list(&default_keys, help);
+ if line_part_to_render.len + shortened_shortcut_list.len <= max_len {
+ line_part_to_render.append(&shortened_shortcut_list);
+ }
+ }
+ }
+ },
+ }
+ if line_part_to_render.len <= max_len {
+ Some(line_part_to_render)
+ } else {
+ None
+ }
+}
+
+fn full_inline_keys_modes_shortcut_list(
+ keys_without_common_modifiers: &Vec<KeyShortcut>,
+ help: &ModeInfo,
+) -> LinePart {
+ let mut full_shortcut_list = LinePart::default();
+ for key in keys_without_common_modifiers {
+ let is_selected = key.is_selected();
+ let shortcut = add_shortcut_with_inline_key(
+ help,
+ &key.full_text(),
+ key.key
+ .as_ref()
+ .map(|k| vec![k.clone()])
+ .unwrap_or_else(|| vec![]),
+ is_selected,
+ );
+ full_shortcut_list.append(&shortcut);
+ }
+ full_shortcut_list
+}
+
+fn shortened_inline_keys_modes_shortcut_list(
+ keys_without_common_modifiers: &Vec<KeyShortcut>,
+ help: &ModeInfo,
+) -> LinePart {
+ let mut shortened_shortcut_list = LinePart::default();
+ for key in keys_without_common_modifiers {
+ let is_selected = key.is_selected();
+ let shortcut = add_shortcut_with_key_only(
+ help,
+ key.key
+ .as_ref()
+ .map(|k| vec![k.clone()])
+ .unwrap_or_else(|| vec![]),
+ is_selected,
+ );
+ shortened_shortcut_list.append(&shortcut);
+ }
+ shortened_shortcut_list
+}
+
+fn full_modes_shortcut_list(default_keys: &Vec<KeyShortcut>, help: &ModeInfo) -> LinePart {
+ let mut full_shortcut_list = LinePart::default();
+ for key in default_keys {
+ let is_selected = key.is_selected();
+ full_shortcut_list.append(&add_shortcut(
+ help,
+ &key.full_text(),
+ &key.key
+ .as_ref()
+ .map(|k| vec![k.clone()])
+ .unwrap_or_else(|| vec![]),
+ is_selected,
+ Some(3),
+ ));
+ }
+ full_shortcut_list
+}
+
+fn shortened_modes_shortcut_list(default_keys: &Vec<KeyShortcut>, help: &ModeInfo) -> LinePart {
+ let mut shortened_shortcut_list = LinePart::default();
+ for key in default_keys {
+ let is_selected = key.is_selected();
+ shortened_shortcut_list.append(&add_shortcut(
+ help,
+ &key.short_text(),
+ &key.key
+ .as_ref()
+ .map(|k| vec![k.clone()])
+ .unwrap_or_else(|| vec![]),
+ is_selected,
+ Some(3),
+ ));
+ }
+ shortened_shortcut_list
+}
+
+fn common_modifiers_in_all_modes(
+ key_shortcuts: &HashMap<InputMode, Vec<KeyShortcut>>,
+) -> Option<Vec<KeyModifier>> {
+ let Some(mut common_modifiers) = key_shortcuts.iter().next().and_then(|k| {
+ k.1.iter()
+ .next()
+ .and_then(|k| k.get_key().map(|k| k.key_modifiers.clone()))
+ }) else {
+ return None;
+ };
+ for (_mode, key_shortcuts) in key_shortcuts {
+ if key_shortcuts.is_empty() {
+ return None;
+ }
+ let Some(mut common_modifiers_for_mode) = key_shortcuts
+ .iter()
+ .next()
+ .unwrap()
+ .get_key()
+ .map(|k| k.key_modifiers.clone())
+ else {
+ return None;
+ };
+ for key in key_shortcuts {
+ let Some(key) = key.get_key() else {
+ return None;
+ };
+ common_modifiers_for_mode = common_modifiers_for_mode
+ .intersection(&key.key_modifiers)
+ .cloned()
+ .collect();
+ }
+ common_modifiers = common_modifiers
+ .intersection(&common_modifiers_for_mode)
+ .cloned()
+ .collect();
+ }
+ if common_modifiers.is_empty() {
+ return None;
+ }
+ Some(common_modifiers.into_iter().collect())
+}
+
+fn render_common_modifiers(
+ palette: &ColoredElements,
+ mode_info: &ModeInfo,
+ common_modifiers: &Vec<KeyModifier>,
+ line_part_to_render: &mut LinePart,
+ separator: &str,
+) {
+ let prefix_text = if mode_info.capabilities.arrow_fonts {
+ // Add extra space in simplified ui
+ format!(
+ " {} + ",
+ common_modifiers
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join("-")
+ )
+ } else {
+ format!(
+ " {} +",
+ common_modifiers
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join("-")
+ )
+ };
+
+ let suffix_separator = palette.superkey_suffix_separator.paint(separator);
+ line_part_to_render.part = format!(
+ "{}{}{}",
+ line_part_to_render.part,
+ serialize_text(&Text::new(&prefix_text)),
+ suffix_separator
+ );
+ line_part_to_render.len += prefix_text.chars().count() + separator.chars().count();
+}
+
+fn render_secondary_info(
+ help: &ModeInfo,
+ tab_info: Option<&TabInfo>,
+ max_len: usize,
+) -> Option<LinePart> {
+ let mut secondary_info = LinePart::default();
+ let supports_arrow_fonts = !help.capabilities.arrow_fonts;
+ let colored_elements = color_elements(help.style.colors, !supports_arrow_fonts);
+ let secondary_keybinds = secondary_keybinds(&help, tab_info, max_len);
+ secondary_info.append(&secondary_keybinds);
+ let remaining_space = max_len.saturating_sub(secondary_info.len).saturating_sub(1); // 1 for the end padding of the line
+ let mut padding = String::new();
+ let mut padding_len = 0;
+ for _ in 0..remaining_space {
+ padding.push_str(&ANSIStrings(&[colored_elements.superkey_prefix.paint(" ")]).to_string());
+ padding_len += 1;
+ }
+ secondary_info.part = format!("{}{}", padding, secondary_info.part);
+ secondary_info.len += padding_len;
+ if secondary_info.len <= max_len {
+ Some(secondary_info)
+ } else {
+ None
+ }
+}
+
+fn secondary_keybinds(help: &ModeInfo, tab_info: Option<&TabInfo>, max_len: usize) -> LinePart {
+ let mut secondary_info = LinePart::default();
+ let binds = &help.get_mode_keybinds();
+ // New Pane
+ let new_pane_action_key = action_key(binds, &[Action::NewPane(None, None)]);
+ let mut new_pane_key_to_display = new_pane_action_key
+ .iter()
+ .find(|k| k.is_key_with_alt_modifier(BareKey::Char('n')))
+ .or_else(|| new_pane_action_key.iter().next());
+ let new_pane_key_to_display =
+ if let Some(new_pane_key_to_display) = new_pane_key_to_display.take() {
+ vec![new_pane_key_to_display.clone()]
+ } else {
+ vec![]
+ };
+
+ // Move focus
+ let mut move_focus_shortcuts: Vec<KeyWithModifier> = vec![];
+
+ // Left
+ let move_focus_left_action_key = action_key(binds, &[Action::MoveFocusOrTab(Direction::Left)]);
+ let move_focus_left_key = move_focus_left_action_key
+ .iter()
+ .find(|k| k.is_key_with_alt_modifier(BareKey::Left))
+ .or_else(|| move_focus_left_action_key.iter().next());
+ if let Some(move_focus_left_key) = move_focus_left_key {
+ move_focus_shortcuts.push(move_focus_left_key.clone());
+ }
+ // Down
+ let move_focus_left_action_key = action_key(binds, &[Action::MoveFocus(Direction::Down)]);
+ let move_focus_left_key = move_focus_left_action_key
+ .iter()
+ .find(|k| k.is_key_with_alt_modifier(BareKey::Down))
+ .or_else(|| move_focus_left_action_key.iter().next());
+ if let Some(move_focus_left_key) = move_focus_left_key {
+ move_focus_shortcuts.push(move_focus_left_key.clone());
+ }
+ // Up
+ let move_focus_left_action_key = action_key(binds, &[Action::MoveFocus(Direction::Up)]);
+ let move_focus_left_key = move_focus_left_action_key
+ .iter()
+ .find(|k| k.is_key_with_alt_modifier(BareKey::Up))
+ .or_else(|| move_focus_left_action_key.iter().next());
+ if let Some(move_focus_left_key) = move_focus_left_key {
+ move_focus_shortcuts.push(move_focus_left_key.clone());
+ }
+ // Right
+ let move_focus_left_action_key = action_key(binds, &[Action::MoveFocusOrTab(Direction::Right)]);
+ let move_focus_left_key = move_focus_left_action_key
+ .iter()
+ .find(|k| k.is_key_with_alt_modifier(BareKey::Right))
+ .or_else(|| move_focus_left_action_key.iter().next());
+ if let Some(move_focus_left_key) = move_focus_left_key {
+ move_focus_shortcuts.push(move_focus_left_key.clone());
+ }
+
+ let toggle_floating_action_key = action_key(binds, &[Action::ToggleFloatingPanes]);
+ let mut toggle_floating_action_key = toggle_floating_action_key
+ .iter()
+ .find(|k| k.is_key_with_alt_modifier(BareKey::Char('f')))
+ .or_else(|| toggle_floating_action_key.iter().next());
+ let toggle_floating_key_to_display =
+ if let Some(toggle_floating_key_to_display) = toggle_floating_action_key.take() {
+ vec![toggle_floating_key_to_display.clone()]
+ } else {
+ vec![]
+ };
+ let are_floating_panes_visible = tab_info
+ .map(|t| t.are_floating_panes_visible)
+ .unwrap_or(false);
+
+ let common_modifiers = get_common_modifiers(
+ [
+ new_pane_key_to_display.clone(),
+ move_focus_shortcuts.clone(),
+ toggle_floating_key_to_display.clone(),
+ ]
+ .iter()
+ .flatten()
+ .collect(),
+ );
+ let no_common_modifier = common_modifiers.is_empty();
+
+ if no_common_modifier {
+ secondary_info.append(&add_shortcut(
+ help,
+ "New Pane",
+ &new_pane_key_to_display,
+ false,
+ Some(0),
+ ));
+ secondary_info.append(&add_shortcut(
+ help,
+ "Change Focus",
+ &move_focus_shortcuts,
+ false,
+ Some(0),
+ ));
+ secondary_info.append(&add_shortcut(
+ help,
+ "Floating",
+ &toggle_floating_key_to_display,
+ are_floating_panes_visible,
+ Some(0),
+ ));
+ } else {
+ let modifier_str = text_as_line_part_with_emphasis(
+ format!(
+ "{} + ",
+ common_modifiers
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join("-")
+ ),
+ 0,
+ );
+ secondary_info.append(&modifier_str);
+ let new_pane_key_to_display: Vec<KeyWithModifier> = new_pane_key_to_display
+ .iter()
+ .map(|k| k.strip_common_modifiers(&common_modifiers))
+ .collect();
+ let move_focus_shortcuts: Vec<KeyWithModifier> = move_focus_shortcuts
+ .iter()
+ .map(|k| k.strip_common_modifiers(&common_modifiers))
+ .collect();
+ let toggle_floating_key_to_display: Vec<KeyWithModifier> = toggle_floating_key_to_display
+ .iter()
+ .map(|k| k.strip_common_modifiers(&common_modifiers))
+ .collect();
+ secondary_info.append(&add_shortcut_with_inline_key(
+ help,
+ "New Pane",
+ new_pane_key_to_display,
+ false,
+ ));
+ secondary_info.append(&add_shortcut_with_inline_key(
+ help,
+ "Change Focus",
+ move_focus_shortcuts,
+ false,
+ ));
+ secondary_info.append(&add_shortcut_with_inline_key(
+ help,
+ "Floating",
+ toggle_floating_key_to_display,
+ are_floating_panes_visible,
+ ));
+ }
+
+ if secondary_info.len <= max_len {
+ secondary_info
+ } else {
+ let mut short_line = LinePart::default();
+ if no_common_modifier {
+ short_line.append(&add_shortcut(
+ help,
+ "New",
+ &new_pane_key_to_display,
+ false,
+ Some(0),
+ ));
+ short_line.append(&add_shortcut(
+ help,
+ "Focus",
+ &move_focus_shortcuts,
+ false,
+ Some(0),
+ ));
+ short_line.append(&add_shortcut(
+ help,
+ "Floating",
+ &toggle_floating_key_to_display,
+ are_floating_panes_visible,
+ Some(0),
+ ));
+ } else {
+ let modifier_str = text_as_line_part_with_emphasis(
+ format!(
+ "{} + ",
+ common_modifiers
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join("-")
+ ),
+ 0,
+ );
+ short_line.append(&modifier_str);
+ let new_pane_key_to_display: Vec<KeyWithModifier> = new_pane_key_to_display
+ .iter()
+ .map(|k| k.strip_common_modifiers(&common_modifiers))
+ .collect();
+ let move_focus_shortcuts: Vec<KeyWithModifier> = move_focus_shortcuts
+ .iter()
+ .map(|k| k.strip_common_modifiers(&common_modifiers))
+ .collect();
+ let toggle_floating_key_to_display: Vec<KeyWithModifier> =
+ toggle_floating_key_to_display
+ .iter()
+ .map(|k| k.strip_common_modifiers(&common_modifiers))
+ .collect();
+ short_line.append(&add_shortcut_with_inline_key(
+ help,
+ "New",
+ new_pane_key_to_display,
+ false,
+ ));
+ short_line.append(&add_shortcut_with_inline_key(
+ help,
+ "Focus",
+ move_focus_shortcuts,
+ false,
+ ));
+ short_line.append(&add_shortcut_with_inline_key(
+ help,
+ "Floating",
+ toggle_floating_key_to_display,
+ are_floating_panes_visible,
+ ));
+ }
+ short_line
+ }
+}
+
+fn text_as_line_part_with_emphasis(text: String, emphases_index: usize) -> LinePart {
+ let part = serialize_text(&Text::new(&text).color_range(emphases_index, ..));
+ LinePart {
+ part,
+ len: text.width(),
+ }
+}
+
+fn keybinds(help: &ModeInfo, max_width: usize) -> Option<LinePart> {
+ let full_shortcut_list = full_shortcut_list(help);
+ if full_shortcut_list.len <= max_width {
+ return Some(full_shortcut_list);
+ }
+ let shortened_shortcut_list = shortened_shortcut_list(help);
+ if shortened_shortcut_list.len <= max_width {
+ return Some(shortened_shortcut_list);
+ }
+ Some(best_effort_shortcut_list(help, max_width))
+}
+
+fn add_shortcut(
+ help: &ModeInfo,
+ text: &str,
+ keys: &Vec<KeyWithModifier>,
+ selected: bool,
+ key_color_index: Option<usize>,
+) -> LinePart {
+ let mut ret = LinePart::default();
+ if keys.is_empty() {
+ return ret;
+ }
+
+ ret.append(&style_key_with_modifier(&keys, key_color_index)); // TODO: alternate
+ //
+ let ribbon = if selected {
+ serialize_ribbon(&Text::new(format!("{}", text)).selected())
+ } else {
+ serialize_ribbon(&Text::new(format!("{}", text)))
+ };
+ ret.part = format!("{}{}", ret.part, ribbon);
+ let supports_arrow_fonts = !help.capabilities.arrow_fonts;
+ ret.len += if supports_arrow_fonts {
+ text.width() + 4 // padding and arrow fonts
+ } else {
+ text.width() + 2 // padding
+ };
+ ret
+}
+
+fn add_shortcut_with_inline_key(
+ help: &ModeInfo,
+ text: &str,
+ key: Vec<KeyWithModifier>,
+ is_selected: bool,
+) -> LinePart {
+ let capabilities = help.capabilities;
+
+ let mut ret = LinePart::default();
+ if key.is_empty() {
+ return ret;
+ }
+
+ let key_separator = match key
+ .iter()
+ .map(|k| k.to_string())
+ .collect::<Vec<_>>()
+ .join("")
+ .as_str()
+ {
+ "HJKL" => "",
+ "hjkl" => "",
+ "←↓↑→" => "",
+ "←→" => "",
+ "↓↑" => "",
+ "[]" => "",
+ _ => "|",
+ };
+
+ let key_string = format!(
+ "{}",
+ key.iter()
+ .map(|k| k.to_string())
+ .collect::<Vec<_>>()
+ .join(key_separator)
+ );
+
+ let ribbon = if is_selected {
+ serialize_ribbon(
+ &Text::new(format!("<{}> {}", key_string, text))
+ .color_range(0, 1..key_string.width() + 1)
+ .selected(),
+ )
+ } else {
+ serialize_ribbon(
+ &Text::new(format!("<{}> {}", key_string, text))
+ .color_range(0, 1..key_string.width() + 1),
+ )
+ };
+ ret.part = ribbon;
+ let supports_arrow_fonts = !capabilities.arrow_fonts;
+ ret.len += if supports_arrow_fonts {
+ text.width() + key_string.width() + 7 // padding, group boundaries and arrow fonts
+ } else {
+ text.width() + key_string.width() + 5 // padding and group boundaries
+ };
+
+ ret
+}
+
+fn add_shortcut_with_key_only(
+ help: &ModeInfo,
+ key: Vec<KeyWithModifier>,
+ is_selected: bool,
+) -> LinePart {
+ let mut ret = LinePart::default();
+ if key.is_empty() {
+ return ret;
+ }
+
+ let key_string = format!(
+ "{}",
+ key.iter()
+ .map(|k| k.to_string())
+ .collect::<Vec<_>>()
+ .join("-")
+ );
+
+ let ribbon = if is_selected {
+ serialize_ribbon(
+ &Text::new(format!("{}", key_string))
+ .color_range(0, ..)
+ .selected(),
+ )
+ } else {
+ serialize_ribbon(&Text::new(format!("{}", key_string)).color_range(0, ..))
+ };
+ ret.part = ribbon;
+ let supports_arrow_fonts = !help.capabilities.arrow_fonts;
+ ret.len += if supports_arrow_fonts {
+ key_string.width() + 4 // 4 => arrow fonts + padding
+ } else {
+ key_string.width() + 2 // 2 => padding
+ };
+ ret
+}
+
+fn add_keygroup_separator(help: &ModeInfo, max_len: usize) -> Option<LinePart> {
+ let supports_arrow_fonts = !help.capabilities.arrow_fonts;
+ let separator = if supports_arrow_fonts {
+ crate::ARROW_SEPARATOR
+ } else {
+ " "
+ };
+ let palette = help.style.colors;
+
+ let mut ret = LinePart::default();
+
+ let separator_color = palette_match!(palette.orange);
+ let bg_color = palette_match!(palette.black);
+ let mut bits: Vec<ANSIString> = vec![];
+ let mode_help_text = match help.mode {
+ InputMode::RenamePane => Some("RENAMING PANE"),
+ InputMode::RenameTab => Some("RENAMING TAB"),
+ InputMode::EnterSearch => Some("ENTERING SEARCH TERM"),
+ InputMode::Search => Some("SEARCHING"),
+ _ => None,
+ };
+ if let Some(mode_help_text) = mode_help_text {
+ bits.push(
+ Style::new()
+ .fg(separator_color)
+ .bold()
+ .paint(format!(" {} ", mode_help_text)),
+ );
+ ret.len += mode_help_text.width() + 2; // 2 => padding
+ }
+ bits.push(
+ Style::new()
+ .fg(bg_color)
+ .on(separator_color)
+ .bold()
+ .paint(format!("{}", separator)),
+ );
+ bits.push(
+ Style::new()
+ .fg(separator_color)
+ .on(separator_color)
+ .bold()
+ .paint(format!(" ")),
+ );
+ bits.push(
+ Style::new()
+ .fg(separator_color)
+ .on(bg_color)
+ .bold()
+ .paint(format!("{}", separator)),
+ );
+ ret.part = format!("{}{}", ret.part, ANSIStrings(&bits));
+ ret.len += 3; // padding and arrow fonts
+
+ if ret.len <= max_len {
+ Some(ret)
+ } else {
+ None
+ }
+}
+
+fn full_shortcut_list(help: &ModeInfo) -> LinePart {
+ match help.mode {
+ InputMode::Normal => LinePart::default(),
+ InputMode::Locked => LinePart::default(),
+ _ => full_shortcut_list_nonstandard_mode(help),
+ }
+}
+
+fn full_shortcut_list_nonstandard_mode(help: &ModeInfo) -> LinePart {
+ let mut line_part = LinePart::default();
+ let keys_and_hints = get_keys_and_hints(help);
+
+ for (long, _short, keys) in keys_and_hints.into_iter() {
+ line_part.append(&add_shortcut(help, &long, &keys.to_vec(), false, Some(2)));
+ }
+ line_part
+}
+
+#[rustfmt::skip]
+fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<KeyWithModifier>)> {
+ use Action as A;
+ use InputMode as IM;
+ use Direction as Dir;
+ use actions::SearchDirection as SDir;
+ use actions::SearchOption as SOpt;
+
+ let mut old_keymap = mi.get_mode_keybinds();
+ let s = |string: &str| string.to_string();
+
+ // Find a keybinding to get back to "Normal" input mode. In this case we prefer '\n' over other
+ // choices. Do it here before we dedupe the keymap below!
+ let base_mode = mi.base_mode;
+ let to_basemode_keys = base_mode.map(|b| action_key(&old_keymap, &[to_base_mode(b)])).unwrap_or_else(|| action_key(&old_keymap, &[TO_NORMAL]));
+ let to_basemode_key = if to_basemode_keys.contains(&KeyWithModifier::new(BareKey::Enter)) {
+ vec![KeyWithModifier::new(BareKey::Enter)]
+ } else {
+ // Yield `vec![key]` if `to_normal_keys` has at least one key, or an empty vec otherwise.
+ to_basemode_keys.into_iter().take(1).collect()
+ };
+
+ // Sort and deduplicate the keybindings first. We sort after the `Key`s, and deduplicate by
+ // their `Action` vectors. An unstable sort is fine here because if the user maps anything to
+ // the same key again, anything will happen...
+ old_keymap.sort_unstable_by(|(keya, _), (keyb, _)| keya.partial_cmp(keyb).unwrap());
+
+ let mut known_actions: Vec<Vec<Action>> = vec![];
+ let mut km = vec![];
+ for (key, acvec) in old_keymap {
+ if known_actions.contains(&acvec) {
+ // This action is known already
+ continue;
+ } else {
+ known_actions.push(acvec.to_vec());
+ km.push((key, acvec));
+ }
+ }
+
+ if mi.mode == IM::Pane { vec![
+ (s("New"), s("New"), single_action_key(&km, &[A::NewPane(None, None), TO_NORMAL])),
+ (s("Change Focus"), s("Move"),
+ action_key_group(&km, &[&[A::MoveFocus(Dir::Left)], &[A::MoveFocus(Dir::Down)],
+ &[A::MoveFocus(Dir::Up)], &[A::MoveFocus(Dir::Right)]])),
+ (s("Close"), s("Close"), single_action_key(&km, &[A::CloseFocus, TO_NORMAL])),
+ (s("Rename"), s("Rename"),
+ single_action_key(&km, &[A::SwitchToMode(IM::RenamePane), A::PaneNameInput(vec![0])])),
+ (s("Toggle Fullscreen"), s("Fullscreen"), single_action_key(&km, &[A::ToggleFocusFullscreen, TO_NORMAL])),
+ (s("Toggle Floating"), s("Floating"),
+ single_action_key(&km, &[A::ToggleFloatingPanes, TO_NORMAL])),
+ (s("Toggle Embed"), s("Embed"), single_action_key(&km, &[A::TogglePaneEmbedOrFloating, 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
+ // to moving tabs focus (left/up go left, right/down go right). Since we sort the keys
+ // above and then dedpulicate based on the actions, we will end up with LeftArrow for
+ // "left" and DownArrow for "right". What we really expect is to see LeftArrow and
+ // RightArrow.
+ // FIXME: So for lack of a better idea we just check this case manually here.
+ let old_keymap = mi.get_mode_keybinds();
+ let focus_keys_full: Vec<KeyWithModifier> = action_key_group(&old_keymap,
+ &[&[A::GoToPreviousTab], &[A::GoToNextTab]]);
+ let focus_keys = if focus_keys_full.contains(&KeyWithModifier::new(BareKey::Left))
+ && focus_keys_full.contains(&KeyWithModifier::new(BareKey::Right)) {
+ vec![KeyWithModifier::new(BareKey::Left), KeyWithModifier::new(BareKey::Right)]
+ } else {
+ action_key_group(&km, &[&[A::GoToPreviousTab], &[A::GoToNextTab]])
+ };
+
+ vec![
+ (s("New"), s("New"), single_action_key(&km, &[A::NewTab(None, vec![], None, None, None), TO_NORMAL])),
+ (s("Change focus"), s("Move"), focus_keys),
+ (s("Close"), s("Close"), single_action_key(&km, &[A::CloseTab, TO_NORMAL])),
+ (s("Rename"), s("Rename"),
+ single_action_key(&km, &[A::SwitchToMode(IM::RenameTab), A::TabNameInput(vec![0])])),
+ (s("Sync"), s("Sync"), single_action_key(&km, &[A::ToggleActiveSyncTab, TO_NORMAL])),
+ (s("Break pane to new tab"), s("Break out"), single_action_key(&km, &[A::BreakPane, TO_NORMAL])),
+ (s("Break pane left/right"), s("Break"), action_key_group(&km, &[
+ &[Action::BreakPaneLeft, TO_NORMAL],
+ &[Action::BreakPaneRight, TO_NORMAL],
+ ])),
+ (s("Toggle"), s("Toggle"), single_action_key(&km, &[A::ToggleTab])),
+ (s("Select pane"), s("Select"), to_basemode_key),
+ ]} else if mi.mode == IM::Resize { vec![
+ (s("Increase/Decrease size"), s("Increase/Decrease"),
+ action_key_group(&km, &[
+ &[A::Resize(Resize::Increase, None)],
+ &[A::Resize(Resize::Decrease, None)]
+ ])),
+ (s("Increase to"), s("Increase"), action_key_group(&km, &[
+ &[A::Resize(Resize::Increase, Some(Dir::Left))],
+ &[A::Resize(Resize::Increase, Some(Dir::Down))],
+ &[A::Resize(Resize::Increase, Some(Dir::Up))],
+ &[A::Resize(Resize::Increase, Some(Dir::Right))]
+ ])),
+ (s("Decrease from"), s("Decrease"), action_key_group(&km, &[
+ &[A::Resize(Resize::Decrease, Some(Dir::Left))],
+ &[A::Resize(Resize::Decrease, Some(Dir::Down))],
+ &[A::Resize(Resize::Decrease, Some(Dir::Up))],
+ &[A::Resize(Resize::Decrease, Some(Dir::Right))]
+ ])),
+ (s("Select pane"), s("Select"), to_basemode_key),
+ ]} else if mi.mode == IM::Move { vec![
+ (s("Switch Location"), s("Move"), action_key_group(&km, &[
+ &[Action::MovePane(Some(Dir::Left))], &[Action::MovePane(Some(Dir::Down))],
+ &[Action::MovePane(Some(Dir::Up))], &[Action::MovePane(Some(Dir::Right))]])),
+ ]} else if mi.mode == IM::Scroll { vec![
+ (s("Enter search term"), s("Search"),
+ action_key(&km, &[A::SwitchToMode(IM::EnterSearch), A::SearchInput(vec![0])])),
+ (s("Scroll"), s("Scroll"),
+ action_key_group(&km, &[&[Action::ScrollDown], &[Action::ScrollUp]])),
+ (s("Scroll page"), s("Scroll"),
+ action_key_group(&km, &[&[Action::PageScrollDown], &[Action::PageScrollUp]])),
+ (s("Scroll half page"), s("Scroll"),
+ action_key_group(&km, &[&[Action::HalfPageScrollDown], &[Action::HalfPageScrollUp]])),
+ (s("Edit scrollback in default editor"), s("Edit"),
+ single_action_key(&km, &[Action::EditScrollback, TO_NORMAL])),
+ (s("Select pane"), s("Select"), to_basemode_key),
+ ]} else if mi.mode == IM::EnterSearch { vec![
+ (s("When done"), s("Done"), action_key(&km, &[A::SwitchToMode(IM::Search)])),
+ (s("Cancel"), s("Cancel"),
+ action_key(&km, &[A::SearchInput(vec![27]), A::SwitchToMode(IM::Scroll)])),
+ ]} else if mi.mode == IM::Search { vec![
+ (s("Enter Search term"), s("Search"),
+ action_key(&km, &[A::SwitchToMode(IM::EnterSearch), A::SearchInput(vec![0])])),
+ (s("Scroll"), s("Scroll"),
+ action_key_group(&km, &[&[Action::ScrollDown], &[Action::ScrollUp]])),
+ (s("Scroll page"), s("Scroll"),
+ action_key_group(&km, &[&[Action::PageScrollDown], &[Action::PageScrollUp]])),
+ (s("Scroll half page"), s("Scroll"),
+ action_key_group(&km, &[&[Action::HalfPageScrollDown], &[Action::HalfPageScrollUp]])),
+ (s("Search down"), s("Down"), action_key(&km, &[A::Search(SDir::Down)])),
+ (s("Search up"), s("Up"), action_key(&km, &[A::Search(SDir::Up)])),
+ (s("Case sensitive"), s("Case"),
+ action_key(&km, &[A::SearchToggleOption(SOpt::CaseSensitivity)])),
+ (s("Wrap"), s("Wrap"),
+ action_key(&km, &[A::SearchToggleOption(SOpt::Wrap)])),
+ (s("Whole words"), s("Whole"),
+ action_key(&km, &[A::SearchToggleOption(SOpt::WholeWord)])),
+ ]} else if mi.mode == IM::Session { vec![
+ (s("Detach"), s("Detach"), action_key(&km, &[Action::Detach])),
+ (s("Session Manager"), s("Manager"), session_manager_key(&km)),
+ (s("Select pane"), s("Select"), to_basemode_key),
+ ]} else if mi.mode == IM::Tmux { vec![
+ (s("Move focus"), s("Move"), action_key_group(&km, &[
+ &[A::MoveFocus(Dir::Left)], &[A::MoveFocus(Dir::Down)],
+ &[A::MoveFocus(Dir::Up)], &[A::MoveFocus(Dir::Right)]])),
+ (s("Split down"), s("Down"), action_key(&km, &[A::NewPane(Some(Dir::Down), None), TO_NORMAL])),
+ (s("Split right"), s("Right"), action_key(&km, &[A::NewPane(Some(Dir::Right), None), TO_NORMAL])),
+ (s("Fullscreen"), s("Fullscreen"), action_key(&km, &[A::ToggleFocusFullscreen, TO_NORMAL])),
+ (s("New tab"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None, None, None), TO_NORMAL])),
+ (s("Rename tab"), s("Rename"),
+ action_key(&km, &[A::SwitchToMode(IM::RenameTab), A::TabNameInput(vec![0])])),
+ (s("Previous Tab"), s("Previous"), action_key(&km, &[A::GoToPreviousTab, TO_NORMAL])),
+ (s("Next Tab"), s("Next"), action_key(&km, &[A::GoToNextTab, TO_NORMAL])),
+ (s("Select pane"), s("Select"), to_basemode_key),
+ ]} else if matches!(mi.mode, IM::RenamePane | IM::RenameTab) { vec![
+ (s("When done"), s("Done"), to_basemode_key),
+ ]} else { vec![] }
+}
+
+fn shortened_shortcut_list_nonstandard_mode(help: &ModeInfo) -> LinePart {
+ let mut line_part = LinePart::default();
+ let keys_and_hints = get_keys_and_hints(help);
+
+ for (_, short, keys) in keys_and_hints.into_iter() {
+ line_part.append(&add_shortcut(help, &short, &keys.to_vec(), false, Some(2)));
+ }
+ line_part
+}
+
+fn shortened_shortcut_list(help: &ModeInfo) -> LinePart {
+ match help.mode {
+ InputMode::Normal => LinePart::default(),
+ InputMode::Locked => LinePart::default(),
+ _ => shortened_shortcut_list_nonstandard_mode(help),
+ }
+}
+
+fn best_effort_shortcut_list(help: &ModeInfo, max_len: usize) -> LinePart {
+ let mut line_part = LinePart::default();
+ let keys_and_hints = get_keys_and_hints(help);
+ for (_, short, keys) in keys_and_hints.into_iter() {
+ let shortcut = add_shortcut(help, &short, &keys.to_vec(), false, Some(2));
+ if line_part.len + shortcut.len + MORE_MSG.chars().count() > max_len {
+ line_part.part = format!("{}{}", line_part.part, MORE_MSG);
+ line_part.len += MORE_MSG.chars().count();
+ break;
+ } else {
+ line_part.append(&shortcut);
+ }
+ }
+ line_part
+}
+
+fn single_action_key(
+ keymap: &[(KeyWithModifier, Vec<Action>)],
+ action: &[Action],
+) -> Vec<KeyWithModifier> {
+ let mut matching = keymap.iter().find_map(|(key, acvec)| {
+ if acvec.iter().next() == action.iter().next() {
+ Some(key.clone())
+ } else {
+ None
+ }
+ });
+ if let Some(matching) = matching.take() {
+ vec![matching]
+ } else {
+ vec![]
+ }
+}
+
+fn session_manager_key(keymap: &[(KeyWithModifier, Vec<Action>)]) -> Vec<KeyWithModifier> {
+ let mut matching = keymap.iter().find_map(|(key, acvec)| {
+ let has_match = acvec
+ .iter()
+ .find(|a| a.launches_plugin("session-manager"))
+ .is_some();
+ if has_match {
+ Some(key.clone())
+ } else {
+ None
+ }
+ });
+ if let Some(matching) = matching.take() {
+ vec![matching]
+ } else {
+ vec![]
+ }
+}
+
+fn style_key_with_modifier(keyvec: &[KeyWithModifier], color_index: Option<usize>) -> LinePart {
+ if keyvec.is_empty() {
+ return LinePart::default();
+ }
+
+ let common_modifiers = get_common_modifiers(keyvec.iter().collect());
+
+ let no_common_modifier = common_modifiers.is_empty();
+ let modifier_str = common_modifiers
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join("-");
+
+ // Prints the keys
+ let key = keyvec
+ .iter()
+ .map(|key| {
+ if no_common_modifier || keyvec.len() == 1 {
+ format!("{}", key)
+ } else {
+ format!("{}", key.strip_common_modifiers(&common_modifiers))
+ }
+ })
+ .collect::<Vec<String>>();
+
+ // Special handling of some pre-defined keygroups
+ let key_string = key.join("");
+ let key_separator = match &key_string[..] {
+ "HJKL" => "",
+ "hjkl" => "",
+ "←↓↑→" => "",
+ "←→" => "",
+ "↓↑" => "",
+ "[]" => "",
+ _ => "|",
+ };
+
+ if no_common_modifier || key.len() == 1 {
+ let key_string_text = format!(" {} ", key.join(key_separator));
+ let text = if let Some(color_index) = color_index {
+ Text::new(&key_string_text).color_range(color_index, ..)
+ } else {
+ Text::new(&key_string_text)
+ };
+ LinePart {
+ part: serialize_text(&text),
+ len: key_string_text.width(),
+ }
+ } else {
+ let key_string_without_modifier = format!("{}", key.join(key_separator));
+ let key_string_text = format!(" {} <{}> ", modifier_str, key_string_without_modifier);
+ let text = if let Some(color_index) = color_index {
+ Text::new(&key_string_text)
+ .color_range(color_index, ..modifier_str.width() + 1)
+ .color_range(
+ color_index,
+ modifier_str.width() + 3
+ ..modifier_str.width() + 3 + key_string_without_modifier.width(),
+ )
+ } else {
+ Text::new(&key_string_text)
+ };
+ LinePart {
+ part: serialize_text(&text),
+ len: key_string_text.width(),
+ }
+ }
+}
+
+fn get_common_modifiers(mut keyvec: Vec<&KeyWithModifier>) -> Vec<KeyModifier> {
+ if keyvec.is_empty() {
+ return vec![];
+ }
+ let mut common_modifiers = keyvec.pop().unwrap().key_modifiers.clone();
+ for key in keyvec {
+ common_modifiers = common_modifiers
+ .intersection(&key.key_modifiers)
+ .cloned()
+ .collect();
+ }
+ common_modifiers.into_iter().collect()
+}
diff --git a/default-plugins/tab-bar/src/line.rs b/default-plugins/tab-bar/src/line.rs
index 668507a0..349981f4 100644
--- a/default-plugins/tab-bar/src/line.rs
+++ b/default-plugins/tab-bar/src/line.rs
@@ -1,9 +1,14 @@
use ansi_term::ANSIStrings;
+use ansi_term::{
+ Color::{Fixed, RGB},
+ Style,
+};
use unicode_width::UnicodeWidthStr;
use crate::{LinePart, ARROW_SEPARATOR};
+use zellij_tile::prelude::actions::Action;
use zellij_tile::prelude::*;
-use zellij_tile_utils::style;
+use zellij_tile_utils::{palette_match, style};
fn get_current_title_len(current_title: &[LinePart]) -> usize {
current_title.iter().map(|p| p.len).sum()
@@ -224,6 +229,9 @@ pub fn tab_line(
palette: Palette,
capabilities: PluginCapabilities,
hide_session_name: bool,
+ tab_info: Option<&TabInfo>,
+ mode_info: &ModeInfo,
+ hide_swap_layout_indicator: bool,
) -> Vec<LinePart> {
let mut tabs_after_active = all_tabs.split_off(active_tab_index);
let mut tabs_before_active = all_tabs;
@@ -236,10 +244,26 @@ pub fn tab_line(
true => tab_line_prefix(None, palette, cols),
false => tab_line_prefix(session_name, palette, cols),
};
- let prefix_len = get_current_title_len(&prefix);
+
+ let mut swap_layout_indicator = if hide_swap_layout_indicator {
+ None
+ } else {
+ tab_info.and_then(|tab_info| {
+ swap_layout_status(
+ cols,
+ &tab_info.active_swap_layout_name,
+ tab_info.is_swap_layout_dirty,
+ mode_info,
+ !capabilities.arrow_fonts,
+ )
+ })
+ };
+
+ let non_tab_len =
+ get_current_title_len(&prefix) + swap_layout_indicator.as_ref().map(|s| s.len).unwrap_or(0);
// if active tab alone won't fit in cols, don't draw any tabs
- if prefix_len + active_tab.len > cols {
+ if non_tab_len + active_tab.len > cols {
return prefix;
}
@@ -249,10 +273,189 @@ pub fn tab_line(
&mut tabs_before_active,
&mut tabs_after_active,
&mut tabs_to_render,
- cols.saturating_sub(prefix_len),
+ cols.saturating_sub(non_tab_len),
palette,
capabilities,
);
prefix.append(&mut tabs_to_render);
+
+ if let Some(mut swap_layout_indicator) = swap_layout_indicator.take() {
+ let remaining_space = cols
+ .saturating_sub(prefix.iter().fold(0, |len, part| len + part.len))
+ .saturating_sub(swap_layout_indicator.len);
+ let mut padding = String::new();
+ let mut padding_len = 0;
+ for _ in 0..remaining_space {
+ padding.push_str(" ");
+ padding_len += 1;
+ }
+ swap_layout_indicator.part = format!("{}{}", padding, swap_layout_indicator.part);
+ swap_layout_indicator.len += padding_len;
+ prefix.push(swap_layout_indicator);
+ }
+
prefix
}
+
+fn swap_layout_status(
+ cols: usize,
+ swap_layout_name: &Option<String>,
+ is_swap_layout_dirty: bool,
+ mode_info: &ModeInfo,
+ supports_arrow_fonts: bool,
+) -> Option<LinePart> {
+ match swap_layout_name {
+ Some(swap_layout_name) => {
+ let mode_keybinds = mode_info.get_mode_keybinds();
+ let prev_next_keys = action_key_group(
+ &mode_keybinds,
+ &[&[Action::PreviousSwapLayout], &[Action::NextSwapLayout]],
+ );
+ let mut text = style_key_with_modifier(&prev_next_keys, Some(0));
+ text.append(&ribbon_as_line_part(
+ &swap_layout_name.to_uppercase(),
+ !is_swap_layout_dirty,
+ supports_arrow_fonts,
+ ));
+ Some(text)
+ },
+ None => None,
+ }
+}
+
+pub fn ribbon_as_line_part(text: &str, is_selected: bool, supports_arrow_fonts: bool) -> LinePart {
+ let ribbon_text = if is_selected {
+ Text::new(text).selected()
+ } else {
+ Text::new(text)
+ };
+ let part = serialize_ribbon(&ribbon_text);
+ let mut len = text.width() + 2;
+ if supports_arrow_fonts {
+ len += 2;
+ };
+ LinePart {
+ part,
+ len,
+ tab_index: None,
+ }
+}
+
+pub fn style_key_with_modifier(keyvec: &[KeyWithModifier], color_index: Option<usize>) -> LinePart {
+ if keyvec.is_empty() {
+ return LinePart::default();
+ }
+
+ let common_modifiers = get_common_modifiers(keyvec.iter().collect());
+
+ let no_common_modifier = common_modifiers.is_empty();
+ let modifier_str = common_modifiers
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join("-");
+
+ // Prints the keys
+ let key = keyvec
+ .iter()
+ .map(|key| {
+ if no_common_modifier || keyvec.len() == 1 {
+ format!("{}", key)
+ } else {
+ format!("{}", key.strip_common_modifiers(&common_modifiers))
+ }
+ })
+ .collect::<Vec<String>>();
+
+ // Special handling of some pre-defined keygroups
+ let key_string = key.join("");
+ let key_separator = match &key_string[..] {
+ "HJKL" => "",
+ "hjkl" => "",
+ "←↓↑→" => "",
+ "←→" => "",
+ "↓↑" => "",
+ "[]" => "",
+ _ => "|",
+ };
+
+ if no_common_modifier || key.len() == 1 {
+ let key_string_text = format!(" {} ", key.join(key_separator));
+ let text = if let Some(color_index) = color_index {
+ Text::new(&key_string_text).color_range(color_index, ..)
+ } else {
+ Text::new(&key_string_text)
+ };
+ LinePart {
+ part: serialize_text(&text),
+ len: key_string_text.width(),
+ ..Default::default()
+ }
+ } else {
+ let key_string_without_modifier = format!("{}", key.join(key_separator));
+ let key_string_text = format!(" {} <{}> ", modifier_str, key_string_without_modifier);
+ let text = if let Some(color_index) = color_index {
+ Text::new(&key_string_text)
+ .color_range(color_index, ..modifier_str.width() + 1)
+ .color_range(
+ color_index,
+ modifier_str.width() + 3
+ ..modifier_str.width() + 3 + key_string_without_modifier.width(),
+ )
+ } else {
+ Text::new(&key_string_text)
+ };
+ LinePart {
+ part: serialize_text(&text),
+ len: key_string_text.width(),
+ ..Default::default()
+ }
+ }
+}
+
+pub fn get_common_modifiers(mut keyvec: Vec<&KeyWithModifier>) -> Vec<KeyModifier> {
+ if keyvec.is_empty() {
+ return vec![];
+ }
+ let mut common_modifiers = keyvec.pop().unwrap().key_modifiers.clone();
+ for key in keyvec {
+ common_modifiers = common_modifiers
+ .intersection(&key.key_modifiers)
+ .cloned()
+ .collect();
+ }
+ common_modifiers.into_iter().collect()
+}
+
+pub fn action_key_group(
+ keymap: &[(KeyWithModifier, Vec<Action>)],
+ actions: &[&[Action]],
+) -> Vec<KeyWithModifier> {
+ let mut ret = vec![];
+ for action in actions {
+ ret.extend(action_key(keymap, action));
+ }
+ ret
+}
+
+pub fn action_key(
+ keymap: &[(KeyWithModifier, Vec<Action>)],
+ action: &[Action],
+) -> Vec<KeyWithModifier> {
+ keymap
+ .iter()
+ .filter_map(|(key, acvec)| {
+ let matching = acvec
+ .iter()
+ .zip(action)
+ .filter(|(a, b)| a.shallow_eq(b))
+ .count();
+
+ if matching == acvec.len() && matching == action.len() {
+ Some(key.clone())
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<KeyWithModifier>>()
+}
diff --git a/default-plugins/tab-bar/src/main.rs b/default-plugins/tab-bar/src/main.rs
index 20855825..8230467d 100644
--- a/default-plugins/tab-bar/src/main.rs
+++ b/default-plugins/tab-bar/src/main.rs
@@ -18,12 +18,20 @@ pub struct LinePart {
tab_index: Option<usize>,
}
+impl LinePart {
+ pub fn append(&mut self, to_append: &LinePart) {
+ self.part.push_str(&to_append.part);
+ self.len += to_append.len;
+ }
+}
+
#[derive(Default)]
struct State {
tabs: Vec<TabInfo>,
active_tab_idx: usize,
mode_info: ModeInfo,
tab_line: Vec<LinePart>,
+ hide_swap_layout_indication: bool,
}
static ARROW_SEPARATOR: &str = "";
@@ -31,7 +39,11 @@ static ARROW_SEPARATOR: &str = "";
register_plugin!(State);
impl ZellijPlugin for State {
- fn load(&mut self, _configuration: BTreeMap<String, String>) {
+ fn load(&mut self, configuration: BTreeMap<String, String>) {
+ self.hide_swap_layout_indication = configuration
+ .get("hide_swap_layout_indication")
+ .map(|s| s == "true")
+ .unwrap_or(false);
set_selectable(false);
subscribe(&[
EventType::TabUpdate,
@@ -120,6 +132,9 @@ impl ZellijPlugin for State {
self.mode_info.style.colors,
self.mode_info.capabilities,
self.mode_info.style.hide_session_name,
+ self.tabs.iter().find(|t| t.active),
+ &self.mode_info,
+ self.hide_swap_layout_indication,
);
let output = self
diff --git a/default-plugins/tab-bar/src/tab.rs b/default-plugins/tab-bar/src/tab.rs
index 4b2ae777..5044ac87 100644
--- a/default-plugins/tab-bar/src/tab.rs
+++ b/default-plugins/tab-bar/src/tab.rs
@@ -51,7 +51,7 @@ pub fn render_tab(
let right_separator = style!(background_color, foreground_color).paint(separator);
let tab_styled_text = if !focused_clients.is_empty() {
let (cursor_section, extra_length) = cursors(focused_clients, palette);
- tab_text_len += extra_length;
+ tab_text_len += extra_length + 2; // 2 for cursor_beginning and cursor_end
let mut s = String::new();
let cursor_beginning = style!(foreground_color, background_color)
.bold()
diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs
index 9687d53c..eec66096 100644
--- a/src/tests/e2e/cases.rs
+++ b/src/tests/e2e/cases.rs
@@ -100,7 +100,7 @@ fn account_for_races_in_snapshot(snapshot: String) -> String {
// to fix this, we should set plugins as unselectable in the layout (before they are loaded),
// once that happens, we should be able to remove this hack (and adjust the snapshots for the
// trailing spaces that we had to get rid of here)
- let base_replace = Regex::new(r" BASE \s*\n").unwrap();
+ let base_replace = Regex::new(r"Alt <\[\]>  BASE \s*\n").unwrap();
let eol_arrow_replace = Regex::new(r"\s*\n").unwrap();
let snapshot = base_replace.replace_all(&snapshot, "\n").to_string();
let snapshot = eol_arrow_replace.replace_all(&snapshot, "\n").to_string();
@@ -134,6 +134,7 @@ pub fn starts_with_one_terminal() {
},
});
if runner.test_timed_out && test_attempts > 0 {
+ test_attempts -= 1;
continue;
} else {
break last_snapshot;
@@ -175,7 +176,8 @@ pub fn split_terminals_vertically() {
name: "Wait for new pane to appear",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2) && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
step_is_complete = true;
}
@@ -219,12 +221,7 @@ pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() {
name: "Make sure only one pane appears",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(3, 2)
- //two empty lines at the bottom to make sure there is no plugin output
- && remote_terminal
- .current_snapshot()
- .ends_with(" \n ")
- {
+ if remote_terminal.cursor_position_is(3, 2) {
// ... is the truncated tip line
step_is_complete = true;
}
@@ -272,7 +269,9 @@ pub fn scrolling_inside_a_pane() {
name: "Fill terminal with text",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2)
+ && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
remote_terminal.load_fixture("e2e/scrolling_inside_a_pane");
step_is_complete = true;
@@ -284,7 +283,7 @@ pub fn scrolling_inside_a_pane() {
name: "Scroll up inside pane",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 20)
+ if remote_terminal.cursor_position_is(63, 21)
&& remote_terminal.snapshot_contains("line21")
{
// all lines have been written to the pane
@@ -301,9 +300,9 @@ pub fn scrolling_inside_a_pane() {
name: "Wait for scroll to finish",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 20)
+ if remote_terminal.cursor_position_is(63, 21)
&& remote_terminal.snapshot_contains("line3 ")
- && remote_terminal.snapshot_contains("SCROLL: 1/4")
+ && remote_terminal.snapshot_contains("SCROLL: 1/3")
{
// keyboard scrolls up 1 line, scrollback is 4 lines: cat command + 2 extra lines from fixture + prompt
step_is_complete = true;
@@ -352,7 +351,9 @@ pub fn toggle_pane_fullscreen() {
name: "Change newly opened pane to be fullscreen",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2)
+ && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
remote_terminal.send_key(&PANE_MODE);
std::thread::sleep(std::time::Duration::from_millis(100));
@@ -415,7 +416,9 @@ pub fn open_new_tab() {
name: "Open new tab",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2)
+ && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
remote_terminal.send_key(&TAB_MODE);
std::thread::sleep(std::time::Duration::from_millis(100));
@@ -431,7 +434,6 @@ pub fn open_new_tab() {
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.cursor_position_is(3, 2)
- && remote_terminal.tip_appears()
&& remote_terminal.snapshot_contains("Tab #2")
&& remote_terminal.status_bar_appears()
{
@@ -482,7 +484,9 @@ pub fn close_tab() {
name: "Open new tab",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2)
+ && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
remote_terminal.send_key(&TAB_MODE);
std::thread::sleep(std::time::Duration::from_millis(100));
@@ -497,7 +501,6 @@ pub fn close_tab() {
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.cursor_position_is(3, 2)
- && remote_terminal.tip_appears()
&& remote_terminal.snapshot_contains("Tab #2")
&& remote_terminal.status_bar_appears()
{
@@ -675,7 +678,9 @@ pub fn close_pane() {
name: "Close pane",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2)
+ && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
remote_terminal.send_key(&PANE_MODE);
std::thread::sleep(std::time::Duration::from_millis(100));
@@ -690,7 +695,8 @@ pub fn close_pane() {
name: "Wait for pane to close",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(3, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(3, 2) && remote_terminal.status_bar_appears()
+ {
// cursor is in the original pane
step_is_complete = true;
}
@@ -820,7 +826,9 @@ pub fn typing_exit_closes_pane() {
name: "Type exit",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2)
+ && remote_terminal.status_bar_appears()
+ {
remote_terminal.send_key("e".as_bytes());
std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key("x".as_bytes());
@@ -840,8 +848,8 @@ pub fn typing_exit_closes_pane() {
name: "Wait for pane to close",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- // if remote_terminal.cursor_position_is(3, 2) && remote_terminal.tip_appears() {
- if remote_terminal.cursor_position_is(3, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(3, 2) && remote_terminal.status_bar_appears()
+ {
// cursor is in the original pane
step_is_complete = true;
}
@@ -889,7 +897,9 @@ pub fn resize_pane() {
name: "Resize pane",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2)
+ && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
remote_terminal.send_key(&RESIZE_MODE);
std::thread::sleep(std::time::Duration::from_millis(100));
@@ -906,7 +916,8 @@ pub fn resize_pane() {
name: "Wait for pane to be resized",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(57, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(57, 2) && remote_terminal.status_bar_appears()
+ {
// pane has been resized
step_is_complete = true;
}
@@ -952,7 +963,7 @@ pub fn lock_mode() {
name: "Send keys that should not be intercepted by the app",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.snapshot_contains("INTERFACE LOCKED") {
+ if !remote_terminal.snapshot_contains("PANE") {
remote_terminal.send_key(&TAB_MODE);
std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE);
@@ -1017,7 +1028,9 @@ pub fn resize_terminal_window() {
name: "Change terminal window size",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2)
+ && remote_terminal.status_bar_appears()
+ {
// new pane has been opened and focused
remote_terminal.change_size(100, 24);
step_is_complete = true;
@@ -1031,7 +1044,7 @@ pub fn resize_terminal_window() {
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.cursor_position_is(53, 2)
- && remote_terminal.tip_appears()
+ && remote_terminal.status_bar_appears()
&& remote_terminal.snapshot_contains("Ctrl +")
{
// size has been changed
@@ -1081,7 +1094,9 @@ pub fn detach_and_attach_session() {
name: "Send some text to the active pane",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2)
+ && remote_terminal.status_bar_appears()
+ {
// new pane has been opened and focused
remote_terminal.send_key("I am some text".as_bytes());
step_is_complete = true;
@@ -1275,7 +1290,9 @@ pub fn status_bar_loads_custom_keybindings() {
name: "Wait for app to load",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(3, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(3, 2)
+ && remote_terminal.snapshot_contains("LOCK")
+ {
step_is_complete = true;
}
step_is_complete
@@ -1323,7 +1340,9 @@ fn focus_pane_with_mouse() {
name: "Click left pane",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2)
+ && remote_terminal.status_bar_appears()
+ {
remote_terminal.send_key(&sgr_mouse_report(Position::new(5, 2), 0));
step_is_complete = true;
}
@@ -1335,7 +1354,8 @@ fn focus_pane_with_mouse() {
name: "Wait for left pane to be focused",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(3, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(3, 2) && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
step_is_complete = true;
}
@@ -1383,7 +1403,9 @@ pub fn scrolling_inside_a_pane_with_mouse() {
name: "Fill terminal with text",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2)
+ && remote_terminal.status_bar_appears()
+ {
remote_terminal.load_fixture("e2e/scrolling_inside_a_pane");
step_is_complete = true;
}
@@ -1394,7 +1416,7 @@ pub fn scrolling_inside_a_pane_with_mouse() {
name: "Scroll up inside pane",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 20)
+ if remote_terminal.cursor_position_is(63, 21)
&& remote_terminal.snapshot_contains("line21")
{
// all lines have been written to the pane
@@ -1409,11 +1431,10 @@ pub fn scrolling_inside_a_pane_with_mouse() {
name: "Wait for scroll to finish",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 20)
+ if remote_terminal.cursor_position_is(63, 21)
&& remote_terminal.snapshot_contains("line1 ")
- && remote_terminal.snapshot_contains("SCROLL: 3/4")
+ && remote_terminal.snapshot_contains("SCROLL: 3/3")
{
- // mouse wheel scrolls up 3 lines, scrollback is 4 lines: cat command + 2 extra lines from fixture + prompt
step_is_complete = true;
}
step_is_complete
@@ -1460,7 +1481,8 @@ pub fn start_without_pane_frames() {
name: "Wait for new pane to appear",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(62, 1) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(62, 1) && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
step_is_complete = true;
}
@@ -1529,7 +1551,9 @@ pub fn mirrored_sessions() {
name: "Open new tab (second user)",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2)
+ && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
remote_terminal.send_key(&TAB_MODE);
std::thread::sleep(std::time::Duration::from_millis(100));
@@ -1544,7 +1568,6 @@ pub fn mirrored_sessions() {
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.cursor_position_is(3, 2)
- && remote_terminal.tip_appears()
&& remote_terminal.snapshot_contains("Tab #2")
&& remote_terminal.status_bar_appears()
{
@@ -1559,7 +1582,7 @@ pub fn mirrored_sessions() {
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.cursor_position_is(3, 2)
- && remote_terminal.tip_appears()
+ && remote_terminal.status_bar_appears()
&& remote_terminal.snapshot_contains("Tab #2")
{
// cursor is in the newly opened second pane
@@ -1584,7 +1607,7 @@ pub fn mirrored_sessions() {
});
second_runner.run_all_steps();
- if first_runner.test_timed_out || second_runner.test_timed_out {
+ if (first_runner.test_timed_out || second_runner.test_timed_out) && test_attempts >= 0 {
test_attempts -= 1;
continue;
}
@@ -1674,7 +1697,7 @@ pub fn multiple_users_in_same_pane_and_tab() {
});
second_runner.run_all_steps();
- if first_runner.test_timed_out || second_runner.test_timed_out {
+ if (first_runner.test_timed_out || second_runner.test_timed_out) && test_attempts >= 0 {
test_attempts -= 1;
continue;
}
@@ -1767,7 +1790,7 @@ pub fn multiple_users_in_different_panes_and_same_tab() {
});
second_runner.run_all_steps();
- if first_runner.test_timed_out || second_runner.test_timed_out {
+ if (first_runner.test_timed_out || second_runner.test_timed_out) && test_attempts >= 0 {
test_attempts -= 1;
continue;
}
@@ -1776,7 +1799,8 @@ pub fn multiple_users_in_different_panes_and_same_tab() {
name: "take snapshot after",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2) && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
step_is_complete = true;
}
@@ -1790,6 +1814,7 @@ pub fn multiple_users_in_different_panes_and_same_tab() {
let mut step_is_complete = false;
if remote_terminal.cursor_position_is(3, 2)
&& remote_terminal.snapshot_contains("││$")
+ && remote_terminal.tab_bar_appears()
{
// cursor is back in the first tab
step_is_complete = true;
@@ -1847,7 +1872,9 @@ pub fn multiple_users_in_different_tabs() {
name: "Open new tab",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(3, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(3, 2)
+ && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
remote_terminal.send_key(&TAB_MODE);
std::thread::sleep(std::time::Duration::from_millis(100));
@@ -1859,7 +1886,7 @@ pub fn multiple_users_in_different_tabs() {
});
second_runner.run_all_steps();
- if first_runner.test_timed_out || second_runner.test_timed_out {
+ if (first_runner.test_timed_out || second_runner.test_timed_out) && test_attempts >= 0 {
test_attempts -= 1;
continue;
}
@@ -1869,7 +1896,6 @@ pub fn multiple_users_in_different_tabs() {
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.cursor_position_is(3, 2)
- && remote_terminal.tip_appears()
&& remote_terminal.snapshot_contains("Tab #1 [ ]")
&& remote_terminal.snapshot_contains("Tab #2")
&& remote_terminal.status_bar_appears()
@@ -1887,7 +1913,6 @@ pub fn multiple_users_in_different_tabs() {
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.cursor_position_is(3, 2)
- && remote_terminal.tip_appears()
&& remote_terminal.snapshot_contains("Tab #2 [ ]")
&& remote_terminal.status_bar_appears()
{
@@ -2006,9 +2031,7 @@ pub fn toggle_floating_panes() {
name: "Wait for new pane to appear",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(33, 7)
- && remote_terminal.snapshot_contains("FLOATING PANES VISIBLE")
- {
+ if remote_terminal.cursor_position_is(33, 8) {
// cursor is in the newly opened second pane
step_is_complete = true;
}
@@ -2057,7 +2080,8 @@ pub fn tmux_mode() {
name: "Wait for new pane to appear",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ if remote_terminal.cursor_position_is(63, 2) && remote_terminal.status_bar_appears()
+ {
// cursor is in the newly opened second pane
step_is_complete = true;
}
@@ -2163,7 +2187,7 @@ pub fn undo_rename_tab() {
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.snapshot_contains("Tab #1")
- && remote_terminal.snapshot_contains("Tip:")
+ && remote_terminal.snapshot_contains("LOCK")
{
step_is_complete = true
}
@@ -2220,7 +2244,7 @@ pub fn undo_rename_pane() {
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.snapshot_contains("Pane #1")
- && remote_terminal.snapshot_contains("Tip:")
+ && remote_terminal.snapshot_contains("LOCK")
{
step_is_complete = true
}
diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs
index b3c61fa4..49cdf64e 100644
--- a/src/tests/e2e/remote_runner.rs
+++ b/src/tests/e2e/remote_runner.rs
@@ -351,10 +351,6 @@ impl RemoteTerminal {
pub fn cursor_position_is(&self, x: usize, y: usize) -> bool {
x == self.cursor_x && y == self.cursor_y
}
- pub fn tip_appears(&self) -> bool {
- let snapshot = self.last_snapshot.lock().unwrap();
- snapshot.contains("Tip:") || snapshot.contains("QuickNav:")
- }
pub fn status_bar_appears(&self) -> bool {
self.last_snapshot.lock().unwrap().contains("Ctrl +")
}
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__bracketed_paste.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__bracketed_paste.snap
index 55cb8d8a..77031624 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__bracketed_paste.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__bracketed_paste.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1676
+assertion_line: 1975
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap
index 713fac4d..98b108ed 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 198
+assertion_line: 243
expression: last_snapshot
---
Zellij
@@ -20,6 +20,6 @@ expression: last_snapshot
│ │
│ │
│ │
+│ │
└──────┘
-
-
+ Ctrl +
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_pane.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_pane.snap
index d7d6423f..8ad2ef36 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_pane.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_pane.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 557
+assertion_line: 707
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap
index 7140fc40..705a746e 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 975
+assertion_line: 1138
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ ││ │
│ ││ │
│ ││ │
+│ ││ │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap
index 2ca3fa89..173f8914 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1071
+assertion_line: 1352
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ ││ │
│ ││ │
│ ││ │
+│ ││ │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__lock_mode.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__lock_mode.snap
index e6d38062..9fe1892d 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__lock_mode.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__lock_mode.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 836
+assertion_line: 985
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <> PANE  <> TAB  <> RESIZE  <> MOVE  <> SEARCH  <> SESSION  <> QUIT 
- -- INTERFACE LOCKED --
+ Ctrl + <g> LOCK 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap
index 9a9efdfc..2fac31b0 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1354
+assertion_line: 1625
expression: second_runner_snapshot
---
Zellij (mirrored_sessions)  Tab #1  Tab #2 
@@ -24,6 +24,6 @@ expression: second_runner_snapshot
│ ││ │
│ ││ │
│ ││ │
+│ ││ │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- <n> New / <←→> Move / <x> Close / <r> Rename / <s> Sync / <b> Break out / <[]> Break / <TAB> Toggle ...
+ Ctrl + <t> TAB   n  New  ←→  Move  x  Close  r  Rename  s  Sync  b  Break out  []  Break  ...
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap
index 3546357c..9fb84667 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1340
+assertion_line: 1624
expression: first_runner_snapshot
---
Zellij (mirrored_sessions)  Tab #1  Tab #2 
@@ -24,6 +24,6 @@ expression: first_runner_snapshot
│ ││ │
│ ││ │
│ ││ │
+│ ││ │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_left.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_left.snap
index 2e3add78..c0d32cdd 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_left.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_left.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 531
+assertion_line: 558
expression: account_for_races_in_snapshot(last_snapshot)
---
Zellij (e2e-test)  Tab #1  Tab #3  Tab #2 
@@ -24,6 +24,6 @@ expression: account_for_races_in_snapshot(last_snapshot)
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_left_until_it_wraps_around.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_left_until_it_wraps_around.snap
index 9479e355..b7898ffb 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_left_until_it_wraps_around.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_left_until_it_wraps_around.snap
@@ -1,5 +1,6 @@
---
source: src/tests/e2e/cases.rs
+assertion_line: 620
expression: account_for_races_in_snapshot(last_snapshot)
---
Zellij (e2e-test)  Tab #2  Tab #1  Tab #3 
@@ -23,6 +24,6 @@ expression: account_for_races_in_snapshot(last_snapshot)
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_right.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_right.snap
index 2aee51c5..dfe09d2e 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_right.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_right.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 624
+assertion_line: 592
expression: account_for_races_in_snapshot(last_snapshot)
---
Zellij (e2e-test)  Tab #1  Tab #3  Tab #2 
@@ -24,6 +24,6 @@ expression: account_for_races_in_snapshot(last_snapshot)
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_right_until_it_wraps_around.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_right_until_it_wraps_around.snap
index 9c2303a5..0baedb2a 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_right_until_it_wraps_around.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__move_tab_to_right_until_it_wraps_around.snap
@@ -1,5 +1,6 @@
---
source: src/tests/e2e/cases.rs
+assertion_line: 644
expression: account_for_races_in_snapshot(last_snapshot)
---
Zellij (e2e-test)  Tab #3  Tab #2  Tab #1 
@@ -23,6 +24,6 @@ expression: account_for_races_in_snapshot(last_snapshot)
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap
index 3c404b66..daf4e132 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1523
+assertion_line: 1809
expression: second_runner_snapshot
---
Zellij (multiple_users_in_same_pane_and_tab)  Tab #1 [ ]
@@ -24,6 +24,6 @@ expression: second_runner_snapshot
│ ││ │
│ ││ │
│ ││ │
+│ ││ │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap
index 189c1f76..ee6b7f0c 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1522
+assertion_line: 1808
expression: first_runner_snapshot
---
Zellij (multiple_users_in_same_pane_and_tab)  Tab #1 [ ]
@@ -24,6 +24,6 @@ expression: first_runner_snapshot
│ ││ │
│ ││ │
│ ││ │
+│ ││ │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap
index 4cad75c0..dc5de24b 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1620
+assertion_line: 1907
expression: second_runner_snapshot
---
Zellij (multiple_users_in_different_tabs)  Tab #1 [ ] Tab #2 
@@ -24,6 +24,6 @@ expression: second_runner_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap
index 389e4ce7..c6ef6230 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1619
+assertion_line: 1906
expression: first_runner_snapshot
---
Zellij (multiple_users_in_different_tabs)  Tab #1  Tab #2 [ ]
@@ -24,6 +24,6 @@ expression: first_runner_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap
index 4aaf8ead..3049701b 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1431
+assertion_line: 1716
expression: second_runner_snapshot
---
Zellij (multiple_users_in_same_pane_and_tab)  Tab #1 [ ]
@@ -24,6 +24,6 @@ expression: second_runner_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap
index 7bd9cdd6..e3830eb6 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1430
+assertion_line: 1718
expression: first_runner_snapshot
---
Zellij (multiple_users_in_same_pane_and_tab)  Tab #1 [ ]
@@ -24,6 +24,6 @@ expression: first_runner_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__open_new_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__open_new_tab.snap
index 279a9f48..93be29fc 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__open_new_tab.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__open_new_tab.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 416
+assertion_line: 452
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1  Tab #2 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session.snap
index 5308c782..857f417d 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1171
+assertion_line: 1191
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1  Tab #2  Tab #3  Tab #4 
@@ -25,5 +25,5 @@ expression: last_snapshot
│ └────────────────────────────────────────────────────────┘ │
│ ││ │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  BASE 
(FLOATING PANES VISIBLE): Press Ctrl p, <w> to hide.
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session_with_viewport_serialization.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session_with_viewport_serialization.snap
index 8c0477f8..dff8858b 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session_with_viewport_serialization.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session_with_viewport_serialization.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1232
+assertion_line: 1252
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1  Tab #2  Tab #3  Tab #4 
@@ -25,5 +25,5 @@ expression: last_snapshot
│ └────────────────────────────────────────────────────────┘ │
│ ││ │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  BASE 
(FLOATING PANES VISIBLE): Press Ctrl p, <w> to hide.
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap
index 76421628..79b5028a 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 765
+assertion_line: 923
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ ││ │
│ ││ │
│ ││ │
+│ ││ │
└────────────────────────────────────────────────────┘└────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap
index ef696f27..da8650ae 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 889
+assertion_line: 1050
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ ││ │
│ ││ │
│ ││ │
+│ ││ │
└────────────────────────────────────────────────┘└────────────────────────────────────────────────┘
- Ctrl + g  p  t  n  h  s  o  q  Alt + <[]>
- QuickNav: Alt + <n> / Alt + <←↓↑→> or Alt + <hjkl> / Alt + <+|->
+ Ctrl + g  p  t  n  h  s  o  q  Alt +  <n> New Pane  <←↓↑→> Change Focus 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane.snap
index e1d36e7c..33f36714 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane.snap
@@ -1,11 +1,12 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 305
+assertion_line: 323
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
-┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────── SCROLL: 1/4 ┐
-│$ ││line3 │
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────── SCROLL: 1/3 ┐
+│$ ││line2 │
+│ ││line3 │
│ ││line4 │
│ ││line5 │
│ ││line6 │
@@ -25,5 +26,4 @@ expression: last_snapshot
│ ││line20 │
│ ││li█e21 │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- <s> Search / <↓↑> Scroll / <PgDn|PgUp> Scroll / <d|u> Scroll / <e> Edit / <ENTER> Select
+ Ctrl + <s> SEARCH   s  Search  ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  e  Edit  ENTER  Select 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap
index dfbbc98f..a8ec256e 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap
@@ -1,11 +1,12 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1147
+assertion_line: 1428
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
-┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────── SCROLL: 3/4 ┐
-│$ ││line1 │
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────── SCROLL: 3/3 ┐
+│$ ││$ cat /usr/src/zellij/fixtures/e2e/scrolling_inside_a_pane│
+│ ││line1 │
│ ││line2 │
│ ││line3 │
│ ││line4 │
@@ -25,5 +26,4 @@ expression: last_snapshot
│ ││line18 │
│ ││li█e19 │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap
index 4d255521..197fd571 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap
@@ -1,9 +1,9 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 2175
+assertion_line: 2349
expression: last_snapshot
---
- Zellij (e2e-test)  Tab #1 
+ Zellij (e2e-test)  Tab #1  Alt <[]>  VERTICAL 
┌ Pane #1 ────────────────────────────────────────────────────────────────┐┌ /usr/src/zellij/fixtures/append-echo-script.sh ─────────────────────────┐
│$ /usr/src/zellij/x86_64-unknown-linux-musl/release/zellij run -s -- "/us││foo │
│r/src/zellij/fixtures/append-echo-script.sh" ││foo │
@@ -24,6 +24,6 @@ expression: last_snapshot
│ ││ │
│ ││ │
│ ││ │
+│ ││ │
└─────────────────────────────────────────────────────────────────────────┘└ [ EXIT CODE: 0 ] <ENTER> re-run, <ESC> drop to shell, <Ctrl-c> exit ────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  Alt + <[]>  VERTICAL 
- Tip: Alt + <n> => open new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate between panes. Alt + <+|-> => increase/decrease pane size.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  Alt +  <n> New  <←↓↑→> Focus 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap
index 39f7de54..1ca3ee0a 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 168
+assertion_line: 194
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ ││ │
│ ││ │
│ ││ │
+│ ││ │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap
index b8fdc86d..2d048e02 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1326
+assertion_line: 1476
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -25,5 +25,5 @@ $ │
│
│
│
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ │
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__starts_with_one_terminal.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__starts_with_one_terminal.snap
index b76d31ec..a78c91c2 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__starts_with_one_terminal.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__starts_with_one_terminal.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 120
+assertion_line: 145
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__status_bar_loads_custom_keybindings.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__status_bar_loads_custom_keybindings.snap
index a0fe5fbd..93be704b 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__status_bar_loads_custom_keybindings.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__status_bar_loads_custom_keybindings.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1266
+assertion_line: 1293
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- <F1> LOCK  <F2> PANE  <F3> TAB  <F4> RESIZE  <F5> MOVE  <F6> SEARCH  <Alt F7> SESSION  <Ctrl F8> QUIT 
- Tip: UNBOUND => open new pane. UNBOUND => navigate between panes. UNBOUND => increase/decrease pane size.
+ F1  LOCK  F2  PANE  F3  TAB  F4  RESIZE  F5  MOVE  F6  SEARCH  Alt F7  SESSION  Ctrl F8  QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap
index d3e715c7..dc180a12 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1772
+assertion_line: 2070
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ ││ │
│ ││ │
│ ││ │
+│ ││ │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_floating_panes.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_floating_panes.snap
index 3007e299..d5231a04 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_floating_panes.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_floating_panes.snap
@@ -1,14 +1,15 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1984
+assertion_line: 2027
expression: last_snapshot
---
- Zellij (e2e-test)  Tab #1 
+ Zellij (e2e-test)  Tab #1  Alt <[]>  STAGGERED 
┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│$ │
│ │
│ │
│ │
+│ │
│ ┌ Pane #2 ─────────────────────────────────────────────────┐ │
│ │$ █ │ │
│ │ │ │
@@ -18,12 +19,11 @@ expression: last_snapshot
│ │ │ │
│ │ │ │
│ │ │ │
+│ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ │
│ │
│ │
-│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- (FLOATING PANES VISIBLE): Press Ctrl p, <w> to hide.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_pane_fullscreen.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_pane_fullscreen.snap
index 94d026ae..a81f9814 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_pane_fullscreen.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_pane_fullscreen.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 351
+assertion_line: 386
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- (FULLSCREEN): + 1 hidden panes
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap
index 2907e211..33ffd4b5 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 702
+assertion_line: 858
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_pane.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_pane.snap
index ca77b6b9..859a9763 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_pane.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_pane.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1921
+assertion_line: 2240
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_tab.snap
index 453521e5..7fa61630 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_tab.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_tab.snap
@@ -1,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1871
+assertion_line: 2183
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -24,6 +24,6 @@ expression: last_snapshot
│ │
│ │
│ │
+│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/src/tests/e2e/steps.rs b/src/tests/e2e/steps.rs
index 85009e19..91cc0e5b 100644
--- a/src/tests/e2e/steps.rs
+++ b/src/tests/e2e/steps.rs
@@ -9,7 +9,7 @@ pub fn new_tab() -> Step {
name: "Open new tab",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.tip_appears() && remote_terminal.status_bar_appears() {
+ if remote_terminal.status_bar_appears() {
remote_terminal.send_key(&TAB_MODE);
std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE);
@@ -24,9 +24,7 @@ pub fn check_second_tab_opened() -> Step {
Step {
name: "Check second tab opened",
instruction: |remote_terminal: RemoteTerminal| -> bool {
- remote_terminal.status_bar_appears()
- && remote_terminal.tip_appears()
- && remote_terminal.snapshot_contains("Tab #2")
+ remote_terminal.status_bar_appears() && remote_terminal.snapshot_contains("Tab #2")
},
}
}
@@ -36,7 +34,7 @@ pub fn move_tab_left() -> Step {
name: "Move tab left",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.tip_appears() && remote_terminal.status_bar_appears() {
+ if remote_terminal.status_bar_appears() {
remote_terminal.send_key(&MOVE_TAB_LEFT);
std::thread::sleep(std::time::Duration::from_millis(100));
step_is_complete = true;
@@ -51,7 +49,6 @@ pub fn check_third_tab_moved_left() -> Step {
name: "Check third tab is in the middle",
instruction: |remote_terminal: RemoteTerminal| -> bool {
remote_terminal.status_bar_appears()
- && remote_terminal.tip_appears()
&& remote_terminal.snapshot_contains("Tab #1  Tab #3  Tab #2")
},
}
@@ -62,7 +59,7 @@ pub fn type_second_tab_content() -> Step {
name: "Type second tab content",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.tip_appears() && remote_terminal.status_bar_appears() {
+ if remote_terminal.status_bar_appears() {
remote_terminal.send_key(&SECOND_TAB_CONTENT);
step_is_complete = true;
}
@@ -75,9 +72,7 @@ pub fn check_third_tab_opened() -> Step {
Step {
name: "Check third tab opened",
instruction: |remote_terminal: RemoteTerminal| -> bool {
- remote_terminal.status_bar_appears()
- && remote_terminal.tip_appears()
- && remote_terminal.snapshot_contains("Tab #3")
+ remote_terminal.status_bar_appears() && remote_terminal.snapshot_contains("Tab #3")
},
}
}
@@ -87,7 +82,7 @@ pub fn switch_focus_to_left_tab() -> Step {
name: "Move focus to tab on the left",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.tip_appears() && remote_terminal.status_bar_appears() {
+ if remote_terminal.status_bar_appears() {
remote_terminal.send_key(&MOVE_FOCUS_LEFT_IN_NORMAL_MODE);
step_is_complete = true;
}
@@ -101,7 +96,6 @@ pub fn check_focus_on_second_tab() -> Step {
name: "Check focus is on the second tab",
instruction: |remote_terminal: RemoteTerminal| -> bool {
remote_terminal.status_bar_appears()
- && remote_terminal.tip_appears()
&& remote_terminal.snapshot_contains("Tab #2 content")
},
}
@@ -112,7 +106,7 @@ pub fn move_tab_right() -> Step {
name: "Move tab right",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.tip_appears() && remote_terminal.status_bar_appears() {
+ if remote_terminal.status_bar_appears() {
remote_terminal.send_key(&MOVE_TAB_RIGHT);
step_is_complete = true;
}
@@ -126,7 +120,6 @@ pub fn check_third_tab_moved_to_beginning() -> Step {
name: "Check third tab moved to beginning",
instruction: |remote_terminal: RemoteTerminal| -> bool {
remote_terminal.status_bar_appears()
- && remote_terminal.tip_appears()
&& remote_terminal.snapshot_contains("Tab #3  Tab #1  Tab #2")
},
}
@@ -137,7 +130,6 @@ pub fn check_third_tab_is_left_wrapped() -> Step {
name: "Check third tab is in last position",
instruction: |remote_terminal: RemoteTerminal| -> bool {
remote_terminal.status_bar_appears()
- && remote_terminal.tip_appears()
&& remote_terminal.snapshot_contains("Tab #2  Tab #1  Tab #3")
},
}
@@ -148,7 +140,6 @@ pub fn check_third_tab_is_right_wrapped() -> Step {
name: "Check third tab is in last position",
instruction: |remote_terminal: RemoteTerminal| -> bool {
remote_terminal.status_bar_appears()
- && remote_terminal.tip_appears()
&& remote_terminal.snapshot_contains("Tab #3  Tab #2  Tab #1")
},
}
diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs
index aaedabd7..9a3231b3 100644
--- a/zellij-server/src/lib.rs
+++ b/zellij-server/src/lib.rs
@@ -149,6 +149,7 @@ pub(crate) struct SessionMetaData {
pub config_options: Box<Options>,
pub client_keybinds: HashMap<ClientId, Keybinds>,
pub client_input_modes: HashMap<ClientId, InputMode>,
+ pub default_mode: InputMode,
screen_thread: Option<thread::JoinHandle<()>>,
pty_thread: Option<thread::JoinHandle<()>>,
plugin_thread: Option<thread::JoinHandle<()>>,
@@ -565,7 +566,12 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
.send_to_plugin(PluginInstruction::AddClient(client_id))
.unwrap();
let default_mode = options.default_mode.unwrap_or_default();
- let mode_info = get_mode_info(default_mode, &attrs, session_data.capabilities);
+ let mode_info = get_mode_info(
+ default_mode,
+ &attrs,
+ session_data.capabilities,
+ Some(default_mode),
+ );
session_data
.senders
.send_to_screen(ScreenInstruction::ChangeMode(mode_info.clone(), client_id))
@@ -999,6 +1005,8 @@ fn init_session(
.clone()
.unwrap_or_else(|| get_default_shell());
+ let default_mode = config_options.default_mode.unwrap_or_default();
+
let pty_thread = thread::Builder::new()
.name("pty".to_string())
.spawn({
@@ -1089,6 +1097,7 @@ fn init_session(
client_attributes,
default_shell,
plugin_aliases,
+ default_mode,
)
.fatal()
}
@@ -1158,6 +1167,7 @@ fn init_session(
plugin_thread: Some(plugin_thread),
pty_writer_thread: Some(pty_writer_thread),
background_jobs_thread: Some(background_jobs_thread),
+ default_mode,
}
}
diff --git a/zellij-server/src/plugins/mod.rs b/zellij-server/src/plugins/mod.rs
index 7b9f7550..639a389e 100644
--- a/zellij-server/src/plugins/mod.rs
+++ b/zellij-server/src/plugins/mod.rs
@@ -25,8 +25,8 @@ use wasm_bridge::WasmBridge;
use zellij_utils::{
async_std::{channel, future::timeout, task},
data::{
- Event, EventType, MessageToPlugin, PermissionStatus, PermissionType, PipeMessage,
- PipeSource, PluginCapabilities,
+ Event, EventType, InputMode, MessageToPlugin, PermissionStatus, PermissionType,
+ PipeMessage, PipeSource, PluginCapabilities,
},
errors::{prelude::*, ContextType, PluginContext},
input::{
@@ -198,6 +198,7 @@ pub(crate) fn plugin_thread_main(
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
plugin_aliases: Box<PluginAliases>,
+ default_mode: InputMode,
) -> Result<()> {
info!("Wasm main thread starts");
let plugin_dir = data_dir.join("plugins/");
@@ -219,6 +220,7 @@ pub(crate) fn plugin_thread_main(
default_shell,
layout.clone(),
layout_dir,
+ default_mode,
);
loop {
diff --git a/zellij-server/src/plugins/plugin_loader.rs b/zellij-server/src/plugins/plugin_loader.rs
index 0a839b65..6ed36c05 100644
--- a/zellij-server/src/plugins/plugin_loader.rs
+++ b/zellij-server/src/plugins/plugin_loader.rs
@@ -26,7 +26,7 @@ use crate::{
use zellij_utils::plugin_api::action::ProtobufPluginConfiguration;
use zellij_utils::{
consts::{ZELLIJ_CACHE_DIR, ZELLIJ_SESSION_CACHE_DIR, ZELLIJ_TMP_DIR},
- data::PluginCapabilities,
+ data::{InputMode, PluginCapabilities},
errors::prelude::*,
input::command::TerminalAction,
input::layout::Layout,
@@ -68,6 +68,7 @@ pub struct PluginLoader<'a> {
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
+ default_mode: InputMode,
}
impl<'a> PluginLoader<'a> {
@@ -87,6 +88,7 @@ impl<'a> PluginLoader<'a> {
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
+ default_mode: InputMode,
) -> Result<()> {
let err_context = || format!("failed to reload plugin {plugin_id} from memory");
let mut connected_clients: Vec<ClientId> =
@@ -112,6 +114,7 @@ impl<'a> PluginLoader<'a> {
default_shell,
default_layout,
layout_dir,
+ default_mode,
)?;
plugin_loader
.load_module_from_memory()
@@ -148,6 +151,7 @@ impl<'a> PluginLoader<'a> {
default_layout: Box<Layout>,
skip_cache: bool,
layout_dir: Option<PathBuf>,
+ default_mode: InputMode,
) -> Result<()> {
let err_context = || format!("failed to start plugin {plugin_id} for client {client_id}");
let mut plugin_loader = PluginLoader::new(
@@ -168,6 +172,7 @@ impl<'a> PluginLoader<'a> {
default_shell,
default_layout,
layout_dir,
+ default_mode,
)?;
if skip_cache {
plugin_loader
@@ -220,6 +225,7 @@ impl<'a> PluginLoader<'a> {
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
+ default_mode: InputMode,
) -> Result<()> {
let mut new_plugins = HashSet::new();
for plugin_id in plugin_map.lock().unwrap().plugin_ids() {
@@ -242,6 +248,7 @@ impl<'a> PluginLoader<'a> {
default_shell.clone(),
default_layout.clone(),
layout_dir.clone(),
+ default_mode,
)?;
plugin_loader
.load_module_from_memory()
@@ -270,6 +277,7 @@ impl<'a> PluginLoader<'a> {
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
+ default_mode: InputMode,
) -> Result<()> {
let err_context = || format!("failed to reload plugin id {plugin_id}");
@@ -296,6 +304,7 @@ impl<'a> PluginLoader<'a> {
default_shell,
default_layout,
layout_dir,
+ default_mode,
)?;
plugin_loader
.compile_module()
@@ -328,6 +337,7 @@ impl<'a> PluginLoader<'a> {
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
+ default_mode: InputMode,
) -> Result<Self> {
let plugin_own_data_dir = ZELLIJ_SESSION_CACHE_DIR
.join(Url::from(&plugin.location).to_string())
@@ -355,6 +365,7 @@ impl<'a> PluginLoader<'a> {
default_shell,
default_layout,
layout_dir,
+ default_mode,
})
}
pub fn new_from_existing_plugin_attributes(
@@ -373,6 +384,7 @@ impl<'a> PluginLoader<'a> {
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
+ default_mode: InputMode,
) -> Result<Self> {
let err_context = || "Failed to find existing plugin";
let (running_plugin, _subscriptions, _workers) = {
@@ -407,6 +419,7 @@ impl<'a> PluginLoader<'a> {
default_shell,
default_layout,
layout_dir,
+ default_mode,
)
}
pub fn new_from_different_client_id(
@@ -425,6 +438,7 @@ impl<'a> PluginLoader<'a> {
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
+ default_mode: InputMode,
) -> Result<Self> {
let err_context = || "Failed to find existing plugin";
let running_plugin = {
@@ -460,6 +474,7 @@ impl<'a> PluginLoader<'a> {
default_shell,
default_layout,
layout_dir,
+ default_mode,
)
}
pub fn load_module_from_memory(&mut self) -> Result<Module> {
@@ -715,6 +730,7 @@ impl<'a> PluginLoader<'a> {
self.default_shell.clone(),
self.default_layout.clone(),
self.layout_dir.clone(),
+ self.default_mode,
)?;
plugin_loader_for_client
.load_module_from_memory()
@@ -814,6 +830,7 @@ impl<'a> PluginLoader<'a> {
input_pipes_to_unblock: Arc::new(Mutex::new(HashSet::new())),
input_pipes_to_block: Arc::new(Mutex::new(HashSet::new())),
layout_dir: self.layout_dir.clone(),
+ default_mode: self.default_mode.clone(),
subscriptions: Arc::new(Mutex::new(HashSet::new())),
stdin_pipe,
stdout_pipe,
diff --git a/zellij-server/src/plugins/plugin_map.rs b/zellij-server/src/plugins/plugin_map.rs
index fb41c89e..c5ac6d5f 100644
--- a/zellij-server/src/plugins/plugin_map.rs
+++ b/zellij-server/src/plugins/plugin_map.rs
@@ -19,6 +19,7 @@ use crate::{thread_bus::ThreadSenders, ClientId};
use zellij_utils::async_channel::Sender;
use zellij_utils::{
data::EventType,
+ data::InputMode,
data::PluginCapabilities,
input::command::TerminalAction,
input::layout::{Layout, PluginUserConfiguration, RunPlugin, RunPluginLocation},
@@ -284,6 +285,7 @@ pub struct PluginEnv {
pub plugin_cwd: PathBuf,
pub input_pipes_to_unblock: Arc<Mutex<HashSet<String>>>,
pub input_pipes_to_block: Arc<Mutex<HashSet<String>>>,
+ pub default_mode: InputMode,
pub subscriptions: Arc<Mutex<Subscriptions>>,
pub stdin_pipe: Arc<Mutex<VecDeque<u8>>>,
pub stdout_pipe: Arc<Mutex<VecDeque<u8>>>,
diff --git a/zellij-server/src/plugins/unit/plugin_tests.rs b/zellij-server/src/plugins/unit/plugin_tests.rs
index 6743d5e7..c1f4fcd5 100644
--- a/zellij-server/src/plugins/unit/plugin_tests.rs
+++ b/zellij-server/src/plugins/unit/plugin_tests.rs
@@ -7,7 +7,8 @@ use std::path::PathBuf;
use tempfile::tempdir;
use wasmtime::Engine;
use zellij_utils::data::{
- BareKey, Event, KeyWithModifier, PermissionStatus, PermissionType, PluginCapabilities,
+ BareKey, Event, InputMode, KeyWithModifier, PermissionStatus, PermissionType,
+ PluginCapabilities,
};
use zellij_utils::errors::ErrorContext;
use zellij_utils::input::layout::{
@@ -280,6 +281,7 @@ fn create_plugin_thread(
client_attributes,
default_shell_action,
Box::new(plugin_aliases),
+ InputMode::Normal,
)
.expect("TEST")
})
@@ -359,6 +361,7 @@ fn create_plugin_thread_with_server_receiver(
client_attributes,
default_shell_action,
Box::new(PluginAliases::default()),
+ InputMode::Normal,
)
.expect("TEST");
})
@@ -444,6 +447,7 @@ fn create_plugin_thread_with_pty_receiver(
client_attributes,
default_shell_action,
Box::new(PluginAliases::default()),
+ InputMode::Normal,
)
.expect("TEST")
})
@@ -524,6 +528,7 @@ fn create_plugin_thread_with_background_jobs_receiver(
client_attributes,
default_shell_action,
Box::new(PluginAliases::default()),
+ InputMode::Normal,
)
.expect("TEST")
})
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap
index 8d699ae6..36de9362 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap
@@ -1,12 +1,15 @@
---
source: zellij-server/src/plugins/./unit/plugin_tests.rs
-assertion_line: 455
+assertion_line: 1043
expression: "format!(\"{:#?}\", switch_to_mode_event)"
---
Some(
ChangeMode(
ModeInfo {
mode: Tab,
+ base_mode: Some(
+ Normal,
+ ),
keybinds: [],
style: Style {
colors: Palette {
diff --git a/zellij-server/src/plugins/wasm_bridge.rs b/zellij-server/src/plugins/wasm_bridge.rs
index 764b757d..2abc22d3 100644
--- a/zellij-server/src/plugins/wasm_bridge.rs
+++ b/zellij-server/src/plugins/wasm_bridge.rs
@@ -19,7 +19,7 @@ use wasmtime::{Engine, Module};
use zellij_utils::async_channel::Sender;
use zellij_utils::async_std::task::{self, JoinHandle};
use zellij_utils::consts::ZELLIJ_CACHE_DIR;
-use zellij_utils::data::{PermissionStatus, PermissionType, PipeMessage, PipeSource};
+use zellij_utils::data::{InputMode, PermissionStatus, PermissionType, PipeMessage, PipeSource};
use zellij_utils::downloader::Downloader;
use zellij_utils::input::permission::PermissionCache;
use zellij_utils::notify_debouncer_full::{notify::RecommendedWatcher, Debouncer, FileIdMap};
@@ -102,6 +102,7 @@ pub struct WasmBridge {
HashMap<RunPluginLocation, HashMap<PluginUserConfiguration, Vec<(PluginId, ClientId)>>>,
pending_pipes: PendingPipes,
layout_dir: Option<PathBuf>,
+ default_mode: InputMode,
}
impl WasmBridge {
@@ -116,6 +117,7 @@ impl WasmBridge {
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
+ default_mode: InputMode,
) -> Self {
let plugin_map = Arc::new(Mutex::new(PluginMap::default()));
let connected_clients: Arc<Mutex<Vec<ClientId>>> = Arc::new(Mutex::new(vec![]));
@@ -146,6 +148,7 @@ impl WasmBridge {
cached_plugin_map: HashMap::new(),
pending_pipes: Default::default(),
layout_dir,
+ default_mode,
}
}
pub fn load_plugin(
@@ -202,6 +205,7 @@ impl WasmBridge {
let default_shell = self.default_shell.clone();
let default_layout = self.default_layout.clone();
let layout_dir = self.layout_dir.clone();
+ let default_mode = self.default_mode;
async move {
let _ = senders.send_to_background_jobs(
BackgroundJob::AnimatePluginLoading(plugin_id),
@@ -249,6 +253,7 @@ impl WasmBridge {
default_layout,
skip_cache,
layout_dir,
+ default_mode,
) {
Ok(_) => handle_plugin_successful_loading(&senders, plugin_id),
Err(e) => handle_plugin_loading_failure(
@@ -340,6 +345,7 @@ impl WasmBridge {
let default_shell = self.default_shell.clone();
let default_layout = self.default_layout.clone();
let layout_dir = self.layout_dir.clone();
+ let default_mode = self.default_mode;
async move {
match PluginLoader::reload_plugin(
first_plugin_id,
@@ -357,6 +363,7 @@ impl WasmBridge {
default_shell.clone(),
default_layout.clone(),
layout_dir.clone(),
+ default_mode,
) {
Ok(_) => {
handle_plugin_successful_loading(&senders, first_plugin_id);
@@ -382,6 +389,7 @@ impl WasmBridge {
default_shell.clone(),
default_layout.clone(),
layout_dir.clone(),
+ default_mode,
) {
Ok(_) => handle_plugin_successful_loading(&senders, *plugin_id),
Err(e) => handle_plugin_loading_failure(
@@ -434,6 +442,7 @@ impl WasmBridge {
self.default_shell.clone(),
self.default_layout.clone(),
self.layout_dir.clone(),
+ self.default_mode,
) {
Ok(_) => {
let _ = self
diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs
index 04569238..a517df06 100644
--- a/zellij-server/src/plugins/zellij_exports.rs
+++ b/zellij-server/src/plugins/zellij_exports.rs
@@ -61,6 +61,7 @@ macro_rules! apply_action {
$env.default_shell.clone(),
$env.default_layout.clone(),
None,
+ $env.default_mode.clone(),
) {
log::error!("{}: {:?}", $error_message(), e);
}
diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs
index 28dc4387..3092310a 100644
--- a/zellij-server/src/route.rs
+++ b/zellij-server/src/route.rs
@@ -13,7 +13,7 @@ use crate::{
use uuid::Uuid;
use zellij_utils::{
channels::SenderWithContext,
- data::{Direction, Event, PluginCapabilities, ResizeStrategy},
+ data::{Direction, Event, InputMode, PluginCapabilities, ResizeStrategy},
errors::prelude::*,
input::{
actions::{Action, SearchDirection, SearchOption},
@@ -38,6 +38,7 @@ pub(crate) fn route_action(
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
mut seen_cli_pipes: Option<&mut HashSet<String>>,
+ default_mode: InputMode,
) -> Result<bool> {
let mut should_break = false;
let err_context = || format!("failed to route action for client {client_id}");
@@ -98,7 +99,7 @@ pub(crate) fn route_action(
.send_to_plugin(PluginInstruction::Update(vec![(
None,
Some(client_id),
- Event::ModeUpdate(get_mode_info(mode, attrs, capabilities)),
+ Event::ModeUpdate(get_mode_info(mode, attrs, capabilities, Some(default_mode))),
)]))
.with_context(err_context)?;
senders
@@ -106,7 +107,7 @@ pub(crate) fn route_action(
.with_context(err_context)?;
senders
.send_to_screen(ScreenInstruction::ChangeMode(
- get_mode_info(mode, attrs, capabilities),
+ get_mode_info(mode, attrs, capabilities, Some(default_mode)),
client_id,
))
.with_context(err_context)?;
@@ -344,7 +345,12 @@ pub(crate) fn route_action(
.send_to_plugin(PluginInstruction::Update(vec![(
None,
None,
- Event::ModeUpdate(get_mode_info(input_mode, attrs, capabilities)),
+ Event::ModeUpdate(get_mode_info(
+ input_mode,
+ attrs,
+ capabilities,
+ Some(default_mode),
+ )),
)]))
.with_context(err_context)?;
@@ -357,6 +363,7 @@ pub(crate) fn route_action(
input_mode,
attrs,
capabilities,
+ Some(default_mode),
)))
.with_context(err_context)?;
},
@@ -1018,6 +1025,7 @@ pub(crate) fn route_thread_main(
rlocked_sessions.default_shell.clone(),
rlocked_sessions.layout.clone(),
Some(&mut seen_cli_pipes),
+ rlocked_sessions.default_mode,
)? {
should_break = true;
}
@@ -1042,6 +1050,7 @@ pub(crate) fn route_thread_main(
rlocked_sessions.default_shell.clone(),
rlocked_sessions.layout.clone(),
Some(&mut seen_cli_pipes),
+ rlocked_sessions.default_mode,
)? {
should_break = true;
}
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index 8e1e4002..e61b5d19 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -2363,6 +2363,7 @@ pub(crate) fn screen_thread_main(
// ¯\_(ツ)_/¯
arrow_fonts: !arrow_fonts,
},
+ config_options.default_mode,
),
draw_pane_frames,
auto_layout,
diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs
index 1fe7af29..5f7f0a66 100644
--- a/zellij-server/src/unit/screen_tests.rs
+++ b/zellij-server/src/unit/screen_tests.rs
@@ -114,6 +114,7 @@ fn send_cli_action_to_server(
let get_current_dir = || PathBuf::from(".");
let actions = Action::actions_from_cli(cli_action, Box::new(get_current_dir), None).unwrap();
let senders = session_metadata.senders.clone();
+ let default_mode = session_metadata.default_mode.clone();
let capabilities = PluginCapabilities::default();
let client_attributes = ClientAttributes::default();
let default_shell = None;
@@ -129,6 +130,7 @@ fn send_cli_action_to_server(
default_shell.clone(),
default_layout.clone(),
None,
+ default_mode,
)
.unwrap();
}
@@ -516,6 +518,7 @@ impl MockScreen {
layout,
client_input_modes: HashMap::new(),
client_keybinds: HashMap::new(),
+ default_mode: self.session_metadata.default_mode.clone(),
}
}
}
@@ -575,6 +578,7 @@ impl MockScreen {
layout,
client_input_modes: HashMap::new(),
client_keybinds: HashMap::new(),
+ default_mode: InputMode::Normal,
};
let os_input = FakeInputOutput::default();
diff --git a/zellij-utils/assets/layouts/classic.kdl b/zellij-utils/assets/layouts/classic.kdl
new file mode 100644
index 00000000..a6560d73
--- /dev/null
+++ b/zellij-utils/assets/layouts/classic.kdl
@@ -0,0 +1,13 @@
+layout {
+ pane size=1 borderless=true {
+ plugin location="tab-bar" {
+ hide_swap_layout_indication true
+ }
+ }
+ pane
+ pane size=2 borderless=true {
+ plugin location="status-bar" {
+ classic true
+ }
+ }
+}
diff --git a/zellij-utils/assets/layouts/classic.swap.kdl b/zellij-utils/assets/layouts/classic.swap.kdl
new file mode 100644
index 00000000..ae1091cb
--- /dev/null
+++ b/zellij-utils/assets/layouts/classic.swap.kdl
@@ -0,0 +1,104 @@
+tab_template name="ui" {
+ pane size=1 borderless=true {
+ plugin location="tab-bar" {
+ hide_swap_layout_indication true
+ }
+ }
+ children
+ pane size=2 borderless=true {
+ plugin location="status-bar" {
+ classic true
+ }
+ }
+}
+
+swap_tiled_layout name="vertical" {
+ ui max_panes=5 {
+ pane split_direction="vertical" {
+ pane
+ pane { children; }
+ }
+ }
+ ui max_panes=8 {
+ pane split_direction="vertical" {
+ pane { children; }
+ pane { pane; pane; pane; pane; }
+ }
+ }
+ ui max_panes=12 {
+ pane split_direction="vertical" {
+ pane { children; }
+ pane { pane; pane; pane; pane; }
+ pane { pane; pane; pane; pane; }
+ }
+ }
+}
+
+swap_tiled_layout name="horizontal" {
+ ui max_panes=5 {
+ pane
+ pane
+ }
+ ui max_panes=8 {
+ pane {
+ pane split_direction="vertical" { children; }
+ pane split_direction="vertical" { pane; pane; pane; pane; }
+ }
+ }
+ ui max_panes=12 {
+ pane {
+ pane split_direction="vertical" { children; }
+ pane split_direction="vertical" { pane; pane; pane; pane; }
+ pane split_direction="vertical" { pane; pane; pane; pane; }
+ }
+ }
+}
+
+swap_tiled_layout name="stacked" {
+ ui min_panes=5 {
+ pane split_direction="vertical" {
+ pane
+ pane stacked=true { children; }
+ }
+ }
+}
+
+swap_floating_layout name="staggered" {
+ floating_panes
+}
+
+swap_floating_layout name="enlarged" {
+ floating_panes max_panes=10 {
+ pane { x "5%"; y 1; width "90%"; height "90%"; }
+ pane { x "5%"; y 2; width "90%"; height "90%"; }
+ pane { x "5%"; y 3; width "90%"; height "90%"; }
+ pane { x "5%"; y 4; width "90%"; height "90%"; }
+ pane { x "5%"; y 5; width "90%"; height "90%"; }
+ pane { x "5%"; y 6; width "90%"; height "90%"; }
+ pane { x "5%"; y 7; width "90%"; height "90%"; }
+ pane { x "5%"; y 8; width "90%"; height "90%"; }
+ pane { x "5%"; y 9; width "90%"; height "90%"; }
+ pane focus=true { x 10; y 10; width "90%"; height "90%"; }
+ }
+}
+
+swap_floating_layout name="spread" {
+ floating_panes max_panes=1 {
+ pane {y "50%"; x "50%"; }
+ }
+ floating_panes max_panes=2 {
+ pane { x "1%"; y "25%"; width "45%"; }
+ pane { x "50%"; y "25%"; width "45%"; }
+ }
+ floating_panes max_panes=3 {
+ pane focus=true { y "55%"; width "45%"; height "45%"; }
+ pane { x "1%"; y "1%"; width "45%"; }
+ pane { x "50%"; y "1%"; width "45%"; }
+ }
+ floating_panes max_panes=4 {
+ pane { x "1%"; y "55%"; width "45%"; height "45%"; }
+ pane focus=true { x "50%"; y "55%"; width "45%"; height "45%"; }
+ pane { x "1%"; y "1%"; width "45%"; height "45%"; }
+ pane { x "50%"; y "1%"; width "45%"; height "45%"; }
+ }
+}
diff --git a/zellij-utils/assets/layouts/default.kdl b/zellij-utils/assets/layouts/default.kdl
index 460aee8a..8ce2ddf6 100644
--- a/zellij-utils/assets/layouts/default.kdl
+++ b/zellij-utils/assets/layouts/default.kdl
@@ -3,7 +3,7 @@ layout {
plugin location="tab-bar"
}
pane
- pane size=2 borderless=true {
+ pane size=1 borderless=true {
plugin location="status-bar"
}
}
diff --git a/zellij-utils/assets/layouts/default.swap.kdl b/zellij-utils/assets/layouts/default.swap.kdl
index 71a6dfe9..27475819 100644
--- a/zellij-utils/assets/layouts/default.swap.kdl
+++ b/zellij-utils/assets/layouts/default.swap.kdl
@@ -3,7 +3,7 @@ tab_template name="ui" {
plugin location="tab-bar"
}
children
- pane size=2 borderless=true {
+ pane size=1 borderless=true {
plugin location="status-bar"
}
}
diff --git a/zellij-utils/assets/prost/api.event.rs b/zellij-utils/assets/prost/api.event.rs
index 2a4f8427..6277b309 100644
--- a/zellij-utils/assets/prost/api.event.rs
+++ b/zellij-utils/assets/prost/api.event.rs
@@ -293,6 +293,8 @@ pub struct ModeUpdatePayload {
pub arrow_fonts_support: bool,
#[prost(string, optional, tag = "5")]
pub session_name: ::core::option::Option<::prost::alloc::string::String>,
+ #[prost(enumeration = "super::input_mode::InputMode", optional, tag = "6")]
+ pub base_mode: ::core::option::Option<i32>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs
index 602b1942..c7ebb300 100644
--- a/zellij-utils/src/data.rs
+++ b/zellij-utils/src/data.rs
@@ -1114,6 +1114,7 @@ pub type KeybindsVec = Vec<(InputMode, Vec<(KeyWithModifier, Vec<Action>)>)>;
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ModeInfo {
pub mode: InputMode,
+ pub base_mode: Option<InputMode>,
pub keybinds: KeybindsVec,
pub style: Style,
pub capabilities: PluginCapabilities,
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs
index cf2f3710..5d6d99de 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -709,6 +709,37 @@ impl Action {
CliAction::ListClients => Ok(vec![Action::ListClients]),
}
}
+ pub fn launches_plugin(&self, plugin_url: &str) -> bool {
+ match self {
+ Action::LaunchPlugin(run_plugin_or_alias, ..) => {
+ log::info!(
+ "1: {:?} == {:?}",
+ run_plugin_or_alias.location_string(),
+ plugin_url
+ );
+ eprintln!(
+ "1: {:?} == {:?}",
+ run_plugin_or_alias.location_string(),
+ plugin_url
+ );
+ &run_plugin_or_alias.location_string() == plugin_url
+ },
+ Action::LaunchOrFocusPlugin(run_plugin_or_alias, ..) => {
+ log::info!(
+ "2: {:?} == {:?}",
+ run_plugin_or_alias.location_string(),
+ plugin_url
+ );
+ eprintln!(
+ "2: {:?} == {:?}",
+ run_plugin_or_alias.location_string(),
+ plugin_url
+ );
+ &run_plugin_or_alias.location_string() == plugin_url
+ },
+ _ => false,
+ }
+ }
}
impl From<OnForceClose> for Action {
diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs
index 94122d9f..82207725 100644
--- a/zellij-utils/src/input/layout.rs
+++ b/zellij-utils/src/input/layout.rs
@@ -1121,6 +1121,7 @@ impl Layout {
available_layouts.push(LayoutInfo::BuiltIn("strider".to_owned()));
available_layouts.push(LayoutInfo::BuiltIn("disable-status-bar".to_owned()));
available_layouts.push(LayoutInfo::BuiltIn("compact".to_owned()));
+ available_layouts.push(LayoutInfo::BuiltIn("classic".to_owned()));
available_layouts.sort_by(|a, b| {
let a_name = a.name();
let b_name = b.name();
@@ -1359,6 +1360,14 @@ impl Layout {
Self::stringified_compact_swap_from_assets()?,
)),
)),
+ Some("classic") => Ok((
+ "Classic layout".into(),
+ Self::stringified_classic_from_assets()?,
+ Some((
+ "Classiclayout swap".into(),
+ Self::stringified_classic_swap_from_assets()?,
+ )),
+ )),
Some("welcome") => Ok((
"Welcome screen layout".into(),
Self::stringified_welcome_from_assets()?,
@@ -1395,6 +1404,14 @@ impl Layout {
Ok(String::from_utf8(setup::COMPACT_BAR_SWAP_LAYOUT.to_vec())?)
}
+ pub fn stringified_classic_from_assets() -> Result<String, ConfigError> {
+ Ok(String::from_utf8(setup::CLASSIC_LAYOUT.to_vec())?)
+ }
+
+ pub fn stringified_classic_swap_from_assets() -> Result<String, ConfigError> {
+ Ok(String::from_utf8(setup::CLASSIC_SWAP_LAYOUT.to_vec())?)
+ }
+
pub fn stringified_welcome_from_assets() -> Result<String, ConfigError> {
Ok(String::from_utf8(setup::WELCOME_LAYOUT.to_vec())?)
}
diff --git a/zellij-utils/src/input/mod.rs b/zellij-utils/src/input/mod.rs
index c85bebc1..dd17ac8c 100644
--- a/zellij-utils/src/input/mod.rs
+++ b/zellij-utils/src/input/mod.rs
@@ -33,12 +33,14 @@ mod not_wasm {
mode: InputMode,
attributes: &ClientAttributes,
capabilities: PluginCapabilities,
+ base_mode: Option<InputMode>,
) -> ModeInfo {
let keybinds = attributes.keybinds.to_keybinds_vec();
let session_name = envs::get_session_name().ok();
ModeInfo {
mode,
+ base_mode,
keybinds,
style: attributes.style,
capabilities,
diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto
index abc239c0..c48030aa 100644
--- a/zellij-utils/src/plugin_api/event.proto
+++ b/zellij-utils/src/plugin_api/event.proto
@@ -218,6 +218,7 @@ message ModeUpdatePayload {
style.Style style = 3;
bool arrow_fonts_support = 4;
optional string session_name = 5;
+ optional input_mode.InputMode base_mode = 6;
}
message InputModeKeybinds {
diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs
index e8966533..1e793e66 100644
--- a/zellij-utils/src/plugin_api/event.rs
+++ b/zellij-utils/src/plugin_api/event.rs
@@ -813,6 +813,9 @@ impl TryFrom<ProtobufModeUpdatePayload> for ModeInfo {
ProtobufInputMode::from_i32(protobuf_mode_update_payload.current_mode)
.ok_or("Malformed InputMode in the ModeUpdate Event")?
.try_into()?;
+ let base_mode: Option<InputMode> = protobuf_mode_update_payload
+ .base_mode
+ .and_then(|b_m| ProtobufInputMode::from_i32(b_m)?.try_into().ok());
let keybinds: Vec<(InputMode, Vec<(KeyWithModifier, Vec<Action>)>)> =
protobuf_mode_update_payload
.keybinds
@@ -851,6 +854,7 @@ impl TryFrom<ProtobufModeUpdatePayload> for ModeInfo {
style,
capabilities,
session_name,
+ base_mode,
};
Ok(mode_info)
}
@@ -860,6 +864,9 @@ impl TryFrom<ModeInfo> for ProtobufModeUpdatePayload {
type Error = &'static str;
fn try_from(mode_info: ModeInfo) -> Result<Self, &'static str> {
let current_mode: ProtobufInputMode = mode_info.mode.try_into()?;
+ let base_mode: Option<ProtobufInputMode> = mode_info
+ .base_mode
+ .and_then(|mode| ProtobufInputMode::try_from(mode).ok());
let style: ProtobufStyle = mode_info.style.try_into()?;
let arrow_fonts_support: bool = mode_info.capabilities.arrow_fonts;
let session_name = mode_info.session_name;
@@ -893,6 +900,7 @@ impl TryFrom<ModeInfo> for ProtobufModeUpdatePayload {
keybinds: protobuf_input_mode_keybinds,
arrow_fonts_support,
session_name,
+ base_mode: base_mode.map(|b_m| b_m as i32),
})
}
}
@@ -1112,6 +1120,7 @@ fn serialize_mode_update_event_with_non_default_values() {
},
capabilities: PluginCapabilities { arrow_fonts: false },
session_name: Some("my awesome test session".to_owned()),
+ base_mode: Some(InputMode::Locked),
});
let protobuf_event: ProtobufEvent = mode_update_event.clone().try_into().unwrap();
let serialized_protobuf_event = protobuf_event.encode_to_vec();
diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs
index 780530d7..941c351b 100644
--- a/zellij-utils/src/setup.rs
+++ b/zellij-utils/src/setup.rs
@@ -167,6 +167,18 @@ pub const COMPACT_BAR_SWAP_LAYOUT: &[u8] = include_bytes!(concat!(
"assets/layouts/compact.swap.kdl"
));
+pub const CLASSIC_LAYOUT: &[u8] = include_bytes!(concat!(
+ env!("CARGO_MANIFEST_DIR"),
+ "/",
+ "assets/layouts/classic.kdl"
+));
+
+pub const CLASSIC_SWAP_LAYOUT: &[u8] = include_bytes!(concat!(
+ env!("CARGO_MANIFEST_DIR"),
+ "/",
+ "assets/layouts/classic.swap.kdl"
+));
+
pub const WELCOME_LAYOUT: &[u8] = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/",
@@ -230,6 +242,7 @@ pub fn dump_specified_layout(layout: &str) -> std::io::Result<()> {
"default" => dump_asset(DEFAULT_LAYOUT),
"compact" => dump_asset(COMPACT_BAR_LAYOUT),
"disable-status" => dump_asset(NO_STATUS_LAYOUT),
+ "classic" => dump_asset(CLASSIC_LAYOUT),
custom => {
info!("Dump {custom} layout");
let custom = add_layout_ext(custom);
@@ -256,6 +269,7 @@ pub fn dump_specified_swap_layout(swap_layout: &str) -> std::io::Result<()> {
"strider" => dump_asset(STRIDER_SWAP_LAYOUT),
"default" => dump_asset(DEFAULT_SWAP_LAYOUT),
"compact" => dump_asset(COMPACT_BAR_SWAP_LAYOUT),
+ "classic" => dump_asset(CLASSIC_SWAP_LAYOUT),
not_found => Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Swap Layout not found for: {}", not_found),
diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap
index 6b6471ee..8f85ae3e 100644
--- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/setup.rs
-assertion_line: 699
+assertion_line: 740
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -69,7 +69,7 @@ Layout {
children: [],
split_size: Some(
Fixed(
- 2,
+ 1,
),
),
run: Some(
@@ -233,7 +233,7 @@ Layout {
children: [],
split_size: Some(
Fixed(
- 2,
+ 1,
),
),
run: Some(
@@ -455,7 +455,7 @@ Layout {
children: [],
split_size: Some(
Fixed(
- 2,
+ 1,
),
),
run: Some(
@@ -758,7 +758,7 @@ Layout {
children: [],
split_size: Some(
Fixed(
- 2,
+ 1,
),
),
run: Some(
@@ -903,7 +903,7 @@ Layout {
children: [],
split_size: Some(
Fixed(
- 2,
+ 1,
),
),
run: Some(
@@ -1125,7 +1125,7 @@ Layout {
children: [],
split_size: Some(
Fixed(
- 2,
+ 1,
),
),
run: Some(
@@ -1428,7 +1428,7 @@ Layout {
children: [],
split_size: Some(
Fixed(
- 2,
+ 1,
),
),
run: Some(
@@ -1592,7 +1592,7 @@ Layout {
children: [],
split_size: Some(
Fixed(
- 2,
+ 1,
),
),
run: Some(