aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2024-07-17 16:08:31 +0200
committerGitHub <noreply@github.com>2024-07-17 16:08:31 +0200
commit84ff29dd024c73e94f3e65d56d907ae903fe3752 (patch)
treeb3f01370b3b1af71f018eeb27a1437b75e959721
parentdocs(changelog): scroll region fix (diff)
downloadsl0ppij-84ff29dd024c73e94f3e65d56d907ae903fe3752.tar.gz
sl0ppij-84ff29dd024c73e94f3e65d56d907ae903fe3752.tar.xz
sl0ppij-84ff29dd024c73e94f3e65d56d907ae903fe3752.zip
feat(ui): configuration screen + configure non-colliding keys at runtime (#3502)
* rebind => reconfigure * persist keybinds and mode info to new tabs * add configuration plugin * make tests pass * remove warnings * style(fmt): rustfmt
-rw-r--r--Cargo.lock9
-rw-r--r--Cargo.toml1
-rw-r--r--default-plugins/configuration/.cargo/config.toml2
-rw-r--r--default-plugins/configuration/.gitignore1
-rw-r--r--default-plugins/configuration/Cargo.toml11
l---------default-plugins/configuration/LICENSE.md1
-rw-r--r--default-plugins/configuration/src/main.rs1987
-rw-r--r--default-plugins/fixture-plugin-for-tests/src/main.rs4
-rw-r--r--default-plugins/status-bar/src/one_line_ui.rs20
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap4
-rw-r--r--xtask/src/main.rs1
-rw-r--r--zellij-server/src/lib.rs69
-rw-r--r--zellij-server/src/plugins/mod.rs16
-rw-r--r--zellij-server/src/plugins/plugin_loader.rs24
-rw-r--r--zellij-server/src/plugins/plugin_map.rs8
-rw-r--r--zellij-server/src/plugins/unit/plugin_tests.rs10
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap4
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__reconfigure_plugin_command.snap11
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap4
-rw-r--r--zellij-server/src/plugins/wasm_bridge.rs66
-rw-r--r--zellij-server/src/plugins/zellij_exports.rs11
-rw-r--r--zellij-server/src/route.rs46
-rw-r--r--zellij-server/src/screen.rs49
-rw-r--r--zellij-server/src/unit/screen_tests.rs12
-rw-r--r--zellij-tile/src/shim.rs4
-rw-r--r--zellij-utils/assets/config/default.kdl9
-rwxr-xr-xzellij-utils/assets/plugins/configuration.wasmbin0 -> 840947 bytes
-rw-r--r--zellij-utils/assets/prost/api.plugin_command.rs8
-rw-r--r--zellij-utils/assets/prost/api.plugin_permission.rs6
-rw-r--r--zellij-utils/src/consts.rs1
-rw-r--r--zellij-utils/src/data.rs9
-rw-r--r--zellij-utils/src/errors.rs5
-rw-r--r--zellij-utils/src/input/actions.rs10
-rw-r--r--zellij-utils/src/input/mod.rs3
-rw-r--r--zellij-utils/src/input/plugins.rs1
-rw-r--r--zellij-utils/src/kdl/mod.rs4
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.proto4
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.rs14
-rw-r--r--zellij-utils/src/plugin_api/plugin_permission.proto2
-rw-r--r--zellij-utils/src/plugin_api/plugin_permission.rs4
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap172
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap172
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap14
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap172
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap172
46 files changed, 3066 insertions, 95 deletions
diff --git a/Cargo.lock b/Cargo.lock
index cfb94204..6588cffb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -750,6 +750,15 @@ dependencies = [
]
[[package]]
+name = "configuration"
+version = "0.1.0"
+dependencies = [
+ "ansi_term",
+ "chrono",
+ "zellij-tile",
+]
+
+[[package]]
name = "console"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 26987d01..63a663fe 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,6 +37,7 @@ members = [
"default-plugins/tab-bar",
"default-plugins/fixture-plugin-for-tests",
"default-plugins/session-manager",
+ "default-plugins/configuration",
"zellij-client",
"zellij-server",
"zellij-utils",
diff --git a/default-plugins/configuration/.cargo/config.toml b/default-plugins/configuration/.cargo/config.toml
new file mode 100644
index 00000000..bc255e30
--- /dev/null
+++ b/default-plugins/configuration/.cargo/config.toml
@@ -0,0 +1,2 @@
+[build]
+target = "wasm32-wasi" \ No newline at end of file
diff --git a/default-plugins/configuration/.gitignore b/default-plugins/configuration/.gitignore
new file mode 100644
index 00000000..ea8c4bf7
--- /dev/null
+++ b/default-plugins/configuration/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/default-plugins/configuration/Cargo.toml b/default-plugins/configuration/Cargo.toml
new file mode 100644
index 00000000..52242f3d
--- /dev/null
+++ b/default-plugins/configuration/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "configuration"
+version = "0.1.0"
+authors = ["Aram Drevekenin <aram@poor.dev>"]
+edition = "2021"
+license = "MIT"
+
+[dependencies]
+ansi_term = "0.12.1"
+zellij-tile = { path = "../../zellij-tile" }
+chrono = "0.4.0"
diff --git a/default-plugins/configuration/LICENSE.md b/default-plugins/configuration/LICENSE.md
new file mode 120000
index 00000000..f0608a63
--- /dev/null
+++ b/default-plugins/configuration/LICENSE.md
@@ -0,0 +1 @@
+../../LICENSE.md \ No newline at end of file
diff --git a/default-plugins/configuration/src/main.rs b/default-plugins/configuration/src/main.rs
new file mode 100644
index 00000000..db0219c5
--- /dev/null
+++ b/default-plugins/configuration/src/main.rs
@@ -0,0 +1,1987 @@
+use zellij_tile::prelude::*;
+
+use std::collections::{BTreeMap, BTreeSet};
+
+struct State {
+ userspace_configuration: BTreeMap<String, String>,
+ selected_index: Option<usize>,
+ selected_primary_key_index: usize,
+ selected_secondary_key_index: usize,
+ remapping_leaders: bool,
+ primary_modifier: BTreeSet<KeyModifier>,
+ secondary_modifier: BTreeSet<KeyModifier>,
+ possible_modifiers: Vec<KeyModifier>,
+ browsing_secondary_modifier: bool,
+ mode_color_index: usize,
+ preset_color_index: usize,
+ primary_leader_key_color_index: usize,
+ secondary_leader_key_color_index: usize,
+}
+
+impl Default for State {
+ fn default() -> Self {
+ let mut primary_modifier = BTreeSet::new();
+ primary_modifier.insert(KeyModifier::Ctrl);
+ let mut secondary_modifier = BTreeSet::new();
+ secondary_modifier.insert(KeyModifier::Alt);
+ State {
+ userspace_configuration: BTreeMap::new(),
+ selected_index: None,
+ selected_primary_key_index: 0,
+ selected_secondary_key_index: 0,
+ remapping_leaders: false,
+ primary_modifier,
+ secondary_modifier,
+ possible_modifiers: vec![
+ KeyModifier::Ctrl,
+ KeyModifier::Alt,
+ KeyModifier::Super,
+ KeyModifier::Shift,
+ ],
+ browsing_secondary_modifier: false,
+ primary_leader_key_color_index: 3,
+ secondary_leader_key_color_index: 0,
+ mode_color_index: 2,
+ preset_color_index: 1,
+ }
+ }
+}
+
+register_plugin!(State);
+
+impl ZellijPlugin for State {
+ fn load(&mut self, configuration: BTreeMap<String, String>) {
+ self.userspace_configuration = configuration;
+ // we need the ReadApplicationState permission to receive the ModeUpdate and TabUpdate
+ // events
+ // we need the RunCommands permission to run "cargo test" in a floating window
+ request_permission(&[
+ PermissionType::ReadApplicationState,
+ PermissionType::RunCommands,
+ PermissionType::ReadCliPipes,
+ PermissionType::MessageAndLaunchOtherPlugins,
+ PermissionType::Reconfigure,
+ PermissionType::ChangeApplicationState,
+ ]);
+ subscribe(&[
+ EventType::ModeUpdate,
+ EventType::TabUpdate,
+ EventType::Key,
+ EventType::Timer,
+ EventType::PermissionRequestResult,
+ ]);
+ }
+ fn update(&mut self, event: Event) -> bool {
+ let mut should_render = false;
+ match event {
+ Event::PermissionRequestResult(_) => {
+ should_render = true;
+ },
+ Event::Key(key) => {
+ if self.remapping_leaders {
+ should_render = self.handle_remapping_screen_key(key);
+ } else {
+ should_render = self.handle_main_screen_key(key);
+ }
+ },
+ _ => (),
+ };
+ should_render
+ }
+ fn render(&mut self, rows: usize, cols: usize) {
+ if self.remapping_leaders {
+ self.render_remapping_leaders_screen(rows, cols);
+ } else {
+ self.render_main_screen(rows, cols);
+ }
+ }
+}
+
+impl State {
+ fn handle_remapping_screen_key(&mut self, key: KeyWithModifier) -> bool {
+ let mut should_render = false;
+ if self.browsing_secondary_modifier {
+ if key.bare_key == BareKey::Left && key.has_no_modifiers() {
+ self.browsing_secondary_modifier = false;
+ self.selected_primary_key_index = self.selected_secondary_key_index;
+ should_render = true;
+ } else if key.bare_key == BareKey::Right && key.has_no_modifiers() {
+ self.browsing_secondary_modifier = false;
+ self.selected_primary_key_index = self.selected_secondary_key_index;
+ should_render = true;
+ } else if key.bare_key == BareKey::Down && key.has_no_modifiers() {
+ if self.selected_secondary_key_index
+ < self.possible_modifiers.len().saturating_sub(1)
+ {
+ self.selected_secondary_key_index += 1;
+ } else {
+ self.selected_secondary_key_index = 0;
+ }
+ should_render = true;
+ } else if key.bare_key == BareKey::Up && key.has_no_modifiers() {
+ if self.selected_secondary_key_index > 0 {
+ self.selected_secondary_key_index -= 1;
+ } else {
+ self.selected_secondary_key_index =
+ self.possible_modifiers.len().saturating_sub(1);
+ }
+ should_render = true;
+ } else if key.bare_key == BareKey::Char(' ') && key.has_no_modifiers() {
+ if let Some(selected_modifier) = self
+ .possible_modifiers
+ .get(self.selected_secondary_key_index)
+ {
+ if self.secondary_modifier.contains(selected_modifier) {
+ self.secondary_modifier.remove(selected_modifier);
+ } else {
+ self.secondary_modifier.insert(*selected_modifier);
+ }
+ should_render = true;
+ }
+ }
+ } else {
+ if key.bare_key == BareKey::Left && key.has_no_modifiers() {
+ self.browsing_secondary_modifier = true;
+ self.selected_secondary_key_index = self.selected_primary_key_index;
+ should_render = true;
+ } else if key.bare_key == BareKey::Right && key.has_no_modifiers() {
+ self.browsing_secondary_modifier = true;
+ self.selected_secondary_key_index = self.selected_primary_key_index;
+ should_render = true;
+ } else if key.bare_key == BareKey::Down && key.has_no_modifiers() {
+ if self.selected_primary_key_index < self.possible_modifiers.len().saturating_sub(1)
+ {
+ self.selected_primary_key_index += 1;
+ } else {
+ self.selected_primary_key_index = 0;
+ }
+ should_render = true;
+ } else if key.bare_key == BareKey::Up && key.has_no_modifiers() {
+ if self.selected_primary_key_index > 0 {
+ self.selected_primary_key_index -= 1;
+ } else {
+ self.selected_primary_key_index =
+ self.possible_modifiers.len().saturating_sub(1);
+ }
+ should_render = true;
+ } else if key.bare_key == BareKey::Char(' ') && key.has_no_modifiers() {
+ if let Some(selected_modifier) =
+ self.possible_modifiers.get(self.selected_primary_key_index)
+ {
+ if self.primary_modifier.contains(selected_modifier) {
+ self.primary_modifier.remove(selected_modifier);
+ } else {
+ self.primary_modifier.insert(*selected_modifier);
+ }
+ should_render = true;
+ }
+ }
+ }
+ if key.bare_key == BareKey::Enter {
+ self.remapping_leaders = false;
+ should_render = true;
+ }
+ should_render
+ }
+ fn handle_main_screen_key(&mut self, key: KeyWithModifier) -> bool {
+ let mut should_render = false;
+ if key.bare_key == BareKey::Down && key.has_no_modifiers() {
+ if self.selected_index.is_none() {
+ self.selected_index = Some(0);
+ } else if self.selected_index < Some(1) {
+ self.selected_index = Some(1);
+ } else {
+ self.selected_index = None;
+ }
+ should_render = true;
+ } else if key.bare_key == BareKey::Up && key.has_no_modifiers() {
+ if self.selected_index.is_none() {
+ self.selected_index = Some(1);
+ } else if self.selected_index == Some(1) {
+ self.selected_index = Some(0);
+ } else {
+ self.selected_index = None;
+ }
+ should_render = true;
+ } else if key.bare_key == BareKey::Enter && key.has_no_modifiers() {
+ if let Some(selected) = self.selected_index.take() {
+ if selected == 0 {
+ // TODO: these should be part of a "transaction" when they are
+ // implemented
+ reconfigure(default_keybinds(
+ self.primary_modifier
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join(" "),
+ self.secondary_modifier
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join(" "),
+ ));
+ switch_to_input_mode(&InputMode::Normal);
+ } else if selected == 1 {
+ // TODO: these should be part of a "transaction" when they are
+ // implemented
+ reconfigure(unlock_first_keybinds(
+ self.primary_modifier
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join(" "),
+ self.secondary_modifier
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join(" "),
+ ));
+ switch_to_input_mode(&InputMode::Locked);
+ }
+ should_render = true;
+ }
+ } else if key.bare_key == BareKey::Char('l') && key.has_no_modifiers() {
+ self.remapping_leaders = true;
+ should_render = true;
+ } else if key.bare_key == BareKey::Esc && key.has_no_modifiers() {
+ close_self();
+ should_render = true;
+ }
+ should_render
+ }
+ fn render_selection_keymap(&self, rows: usize, cols: usize) {
+ let widths = self.remapping_screen_widths();
+ if cols >= widths.0 {
+ let mut x = cols.saturating_sub(10) / 2;
+ let mut y = rows.saturating_sub(7) / 2;
+ if self.browsing_secondary_modifier {
+ x += 31;
+ y += self.selected_secondary_key_index;
+ } else {
+ y += self.selected_primary_key_index;
+ }
+ let text = "<←↓↑→> / <SPACE> ";
+ let text_len = text.chars().count();
+ let text = Text::new(text)
+ .color_range(2, 1..5)
+ .color_range(2, 10..15)
+ .selected();
+ print_text_with_coordinates(text, x.saturating_sub(text_len), y + 3, None, None);
+ }
+ }
+ fn render_remapping_screen_title(&self, rows: usize, cols: usize) {
+ let widths = self.remapping_screen_widths();
+ let screen_width = if cols >= widths.0 {
+ widths.0
+ } else if cols >= widths.1 {
+ widths.1
+ } else {
+ widths.2
+ };
+ let leader_keys_text = if cols >= widths.0 {
+ "Adjust leader keys for the presets in the previous screen:"
+ } else {
+ "Adjust leader keys:"
+ };
+ let base_x = cols.saturating_sub(screen_width) / 2;
+ let base_y = rows.saturating_sub(7) / 2;
+ print_text_with_coordinates(
+ Text::new(leader_keys_text).color_range(2, ..),
+ base_x,
+ base_y,
+ None,
+ None,
+ );
+ }
+ fn render_primary_modifier_selector(&self, rows: usize, cols: usize) {
+ let widths = self.remapping_screen_widths();
+ let screen_width = if cols >= widths.0 {
+ widths.0
+ } else if cols >= widths.1 {
+ widths.1
+ } else {
+ widths.2
+ };
+ self.render_remapping_screen_title(rows, cols);
+ let base_x = cols.saturating_sub(screen_width) / 2;
+ let base_y = rows.saturating_sub(7) / 2;
+ let primary_modifier_key_text = self.primary_modifier_text();
+ let (primary_modifier_text, primary_modifier_start_position) = if cols >= widths.0 {
+ (format!("Primary: {}", primary_modifier_key_text), 9)
+ } else {
+ (format!("{}", primary_modifier_key_text), 0)
+ };
+ print_text_with_coordinates(
+ Text::new(primary_modifier_text).color_range(
+ self.primary_leader_key_color_index,
+ primary_modifier_start_position..,
+ ),
+ base_x,
+ base_y + 2,
+ None,
+ None,
+ );
+ print_nested_list_with_coordinates(
+ self.possible_modifiers
+ .iter()
+ .enumerate()
+ .map(|(i, m)| {
+ let item = if self.primary_modifier.contains(m) {
+ NestedListItem::new(m.to_string())
+ .color_range(self.primary_leader_key_color_index, ..)
+ } else {
+ NestedListItem::new(m.to_string())
+ };
+ if !self.browsing_secondary_modifier && self.selected_primary_key_index == i {
+ item.selected()
+ } else {
+ item
+ }
+ })
+ .collect(),
+ base_x,
+ base_y + 3,
+ Some(screen_width / 2),
+ None,
+ );
+ }
+ fn render_secondary_modifier_selector(&mut self, rows: usize, cols: usize) {
+ let widths = self.remapping_screen_widths();
+ let screen_width = if cols >= widths.0 {
+ widths.0
+ } else if cols >= widths.1 {
+ widths.1
+ } else {
+ widths.2
+ };
+ let base_x = cols.saturating_sub(screen_width) / 2;
+ let base_y = rows.saturating_sub(7) / 2;
+ let secondary_modifier_key_text = self.secondary_modifier_text();
+ let (secondary_modifier_text, secondary_modifier_start_position) = if cols >= widths.0 {
+ (format!("Secondary: {}", secondary_modifier_key_text), 10)
+ } else {
+ (format!("{}", secondary_modifier_key_text), 0)
+ };
+ let secondary_modifier_menu_x_coords = base_x + (screen_width / 2);
+ print_text_with_coordinates(
+ Text::new(secondary_modifier_text).color_range(
+ self.secondary_leader_key_color_index,
+ secondary_modifier_start_position..,
+ ),
+ secondary_modifier_menu_x_coords,
+ base_y + 2,
+ None,
+ None,
+ );
+ print_nested_list_with_coordinates(
+ self.possible_modifiers
+ .iter()
+ .enumerate()
+ .map(|(i, m)| {
+ let item = if self.secondary_modifier.contains(m) {
+ NestedListItem::new(m.to_string())
+ .color_range(self.secondary_leader_key_color_index, ..)
+ } else {
+ NestedListItem::new(m.to_string())
+ };
+ if self.browsing_secondary_modifier && self.selected_secondary_key_index == i {
+ item.selected()
+ } else {
+ item
+ }
+ })
+ .collect(),
+ secondary_modifier_menu_x_coords,
+ base_y + 3,
+ Some(screen_width / 2),
+ None,
+ );
+ }
+ fn render_remapping_leaders_screen(&mut self, rows: usize, cols: usize) {
+ self.render_remapping_screen_title(rows, cols);
+ self.render_primary_modifier_selector(rows, cols);
+ self.render_secondary_modifier_selector(rows, cols);
+ self.render_selection_keymap(rows, cols);
+ self.render_help_text_remapping(rows, cols);
+ }
+ fn render_override_title(&self, rows: usize, cols: usize, primary_modifier_key_text: &str) {
+ let widths = self.main_screen_widths(primary_modifier_key_text);
+ if cols >= widths.0 {
+ let title_text = "Override keybindings with one of the following presets:";
+ let left_padding = cols.saturating_sub(widths.0) / 2;
+ print_text_with_coordinates(
+ Text::new(title_text).color_range(2, ..),
+ left_padding,
+ rows.saturating_sub(15) / 2,
+ None,
+ None,
+ );
+ } else {
+ let title_text = "Override keybindings:";
+ let left_padding = if cols >= widths.1 {
+ cols.saturating_sub(widths.1) / 2
+ } else {
+ cols.saturating_sub(widths.2) / 2
+ };
+ print_text_with_coordinates(
+ Text::new(title_text).color_range(2, ..),
+ left_padding,
+ rows.saturating_sub(15) / 2,
+ None,
+ None,
+ );
+ }
+ }
+ fn render_first_bulletin(&self, rows: usize, cols: usize, primary_modifier_key_text: &str) {
+ let widths = self.main_screen_widths(primary_modifier_key_text);
+ let primary_modifier_key_text_len = primary_modifier_key_text.chars().count();
+ let default_text = "1. Default";
+ let (mut list_items, max_width) = if cols >= widths.0 {
+ let list_items = vec![
+ NestedListItem::new(default_text).color_range(self.preset_color_index, ..),
+ NestedListItem::new("All modes available directly from the base mode, eg.:")
+ .indent(1),
+ NestedListItem::new(format!(
+ "{} p - to enter PANE mode",
+ primary_modifier_key_text
+ ))
+ .color_range(
+ self.primary_leader_key_color_index,
+ ..primary_modifier_key_text_len + 3,
+ )
+ .color_range(
+ self.mode_color_index,
+ primary_modifier_key_text_len + 14..primary_modifier_key_text_len + 18,
+ )
+ .indent(1),
+ NestedListItem::new(format!(
+ "{} t - to enter TAB mode.",
+ primary_modifier_key_text
+ ))
+ .color_range(
+ self.primary_leader_key_color_index,
+ ..primary_modifier_key_text_len + 3,
+ )
+ .color_range(
+ self.mode_color_index,
+ primary_modifier_key_text_len + 14..primary_modifier_key_text_len + 17,
+ )
+ .indent(1),
+ ];
+ let max_width = widths.0;
+ (list_items, max_width)
+ } else if cols >= widths.1 {
+ let list_items = vec![
+ NestedListItem::new(default_text).color_range(self.preset_color_index, ..),
+ NestedListItem::new("Modes available directly, eg.:").indent(1),
+ NestedListItem::new(format!(
+ "{} p - to enter PANE mode",
+ primary_modifier_key_text
+ ))
+ .indent(1)
+ .color_range(
+ self.primary_leader_key_color_index,
+ ..primary_modifier_key_text_len + 3,
+ )
+ .color_range(
+ self.mode_color_index,
+ primary_modifier_key_text_len + 14..primary_modifier_key_text_len + 18,
+ ),
+ NestedListItem::new(format!(
+ "{} t - to enter TAB mode.",
+ primary_modifier_key_text
+ ))
+ .indent(1)
+ .color_range(
+ self.primary_leader_key_color_index,
+ ..primary_modifier_key_text_len + 3,
+ )
+ .color_range(
+ self.mode_color_index,
+ primary_modifier_key_text_len + 14..primary_modifier_key_text_len + 17,
+ ),
+ ];
+ let max_width = widths.1;
+ (list_items, max_width)
+ } else {
+ let list_items = vec![
+ NestedListItem::new(default_text).color_range(self.preset_color_index, ..),
+ NestedListItem::new("Directly, eg.:").indent(1),
+ NestedListItem::new(format!("{} p - PANE mode", primary_modifier_key_text))
+ .color_range(
+ self.primary_leader_key_color_index,
+ ..primary_modifier_key_text_len + 3,
+ )
+ .color_range(
+ self.mode_color_index,
+ primary_modifier_key_text_len + 5..primary_modifier_key_text_len + 10,
+ )
+ .indent(1),
+ NestedListItem::new(format!("{} t - TAB mode.", primary_modifier_key_text))
+ .color_range(
+ self.primary_leader_key_color_index,
+ ..primary_modifier_key_text_len + 3,
+ )
+ .color_range(
+ self.mode_color_index,
+ primary_modifier_key_text_len + 5..primary_modifier_key_text_len + 9,
+ )
+ .indent(1),
+ ];
+ let max_width = widths.2;
+ (list_items, max_width)
+ };
+ if self.selected_index == Some(0) {
+ list_items = list_items.drain(..).map(|i| i.selected()).collect();
+ }
+ let left_padding = cols.saturating_sub(max_width) / 2;
+ let top_coordinates = if rows > 14 {
+ (rows.saturating_sub(15) / 2) + 2
+ } else {
+ (rows.saturating_sub(15) / 2) + 1
+ };
+ print_nested_list_with_coordinates(
+ list_items,
+ left_padding,
+ top_coordinates,
+ Some(max_width),
+ None,
+ );
+ }
+ fn render_second_bulletin(&self, rows: usize, cols: usize, primary_modifier_key_text: &str) {
+ let unlock_first_text = "2. Unlock First (non-colliding)";
+ let widths = self.main_screen_widths(primary_modifier_key_text);
+ let primary_modifier_key_text_len = primary_modifier_key_text.chars().count();
+ let (mut list_items, max_width) = if cols >= widths.0 {
+ let list_items = vec![
+ NestedListItem::new(unlock_first_text).color_range(self.preset_color_index, ..),
+ NestedListItem::new(format!(
+ "Single key modes available after unlocking with {} g, eg.:",
+ primary_modifier_key_text
+ ))
+ .indent(1),
+ NestedListItem::new(format!(
+ "{} g + p to enter PANE mode",
+ primary_modifier_key_text
+ ))
+ .indent(1)
+ .color_range(
+ self.primary_leader_key_color_index,
+ ..primary_modifier_key_text_len + 3,
+ )
+ .color_range(
+ self.primary_leader_key_color_index,
+ primary_modifier_key_text_len + 5..primary_modifier_key_text_len + 7,
+ )
+ .color_range(
+ self.mode_color_index,
+ primary_modifier_key_text_len + 16..primary_modifier_key_text_len + 21,
+ ),
+ NestedListItem::new(format!(
+ "{} g + t to enter TAB mode.",
+ primary_modifier_key_text
+ ))
+ .indent(1)
+ .color_range(
+ self.primary_leader_key_color_index,
+ ..primary_modifier_key_text_len + 3,
+ )
+ .color_range(
+ self.primary_leader_key_color_index,
+ primary_modifier_key_text_len + 5..primary_modifier_key_text_len + 7,
+ )
+ .color_range(
+ self.mode_color_index,
+ primary_modifier_key_text_len + 16..primary_modifier_key_text_len + 20,
+ ),
+ ];
+ let max_width = widths.0;
+ (list_items, max_width)
+ } else if cols >= widths.1 {
+ let list_items = vec![
+ NestedListItem::new(unlock_first_text).color_range(self.preset_color_index, ..),
+ NestedListItem::new(format!(
+ "Single key modes after {} g, eg.:",
+ primary_modifier_key_text
+ ))
+ .indent(1),
+ NestedListItem::new(format!(
+ "{} g + p to enter PANE mode",
+ primary_modifier_key_text
+ ))
+ .color_range(
+ self.primary_leader_key_color_index,
+ ..primary_modifier_key_text_len + 3,
+ )
+ .color_range(
+ self.primary_leader_key_color_index,
+ primary_modifier_key_text_len + 5..primary_modifier_key_text_len + 7,
+ )
+ .color_range(
+ self.mode_color_index,
+ primary_modifier_key_text_len + 16..primary_modifier_key_text_len + 21,
+ )
+ .indent(1),
+ NestedListItem::new(format!(
+ "{} g + t to enter TAB mode.",
+ primary_modifier_key_text
+ ))
+ .color_range(
+ self.primary_leader_key_color_index,
+ ..primary_modifier_key_text_len + 3,
+ )
+ .color_range(
+ self.primary_leader_key_color_index,
+ primary_modifier_key_text_len + 5..primary_modifier_key_text_len + 7,
+ )
+ .color_range(
+ self.mode_color_index,
+ primary_modifier_key_text_len + 16..primary_modifier_key_text_len + 20,
+ )
+ .indent(1),
+ ];
+ let max_width = widths.1;
+ (list_items, max_width)
+ } else {
+ let list_items = vec![
+ NestedListItem::new("2. Unlock First").color_range(self.preset_color_index, ..),
+ NestedListItem::new(format!(
+ "{} g + single key, eg.:",
+ primary_modifier_key_text
+ ))
+ .indent(1),
+ NestedListItem::new(format!("{} g + p PANE mode", primary_modifier_key_text))
+ .color_range(
+ self.primary_leader_key_color_index,
+ ..primary_modifier_key_text_len + 3,
+ )
+ .color_range(
+ self.primary_leader_key_color_index,
+ primary_modifier_key_text_len + 5..primary_modifier_key_text_len + 7,
+ )
+ .color_range(
+ self.mode_color_index,
+ primary_modifier_key_text_len + 7..primary_modifier_key_text_len + 11,
+ )
+ .indent(1),
+ NestedListItem::new(format!("{} g + t TAB mode.", primary_modifier_key_text))
+ .color_range(
+ self.primary_leader_key_color_index,
+ ..primary_modifier_key_text_len + 3,
+ )
+ .color_range(
+ self.primary_leader_key_color_index,
+ primary_modifier_key_text_len + 5..primary_modifier_key_text_len + 7,
+ )
+ .color_range(
+ self.mode_color_index,
+ primary_modifier_key_text_len + 7..primary_modifier_key_text_len + 10,
+ )
+ .indent(1),
+ ];
+ let max_width = widths.2;
+ (list_items, max_width)
+ };
+ if self.selected_index == Some(1) {
+ list_items = list_items.drain(..).map(|i| i.selected()).collect();
+ }
+ let left_padding = cols.saturating_sub(max_width) / 2;
+ let top_coordinates = if rows > 14 {
+ (rows.saturating_sub(15) / 2) + 7
+ } else {
+ (rows.saturating_sub(15) / 2) + 5
+ };
+ print_nested_list_with_coordinates(
+ list_items,
+ left_padding,
+ top_coordinates,
+ Some(max_width),
+ None,
+ );
+ }
+ fn render_leader_keys_indication(
+ &self,
+ rows: usize,
+ cols: usize,
+ primary_modifier_key_text: &str,
+ secondary_modifier_key_text: &str,
+ ) {
+ let widths = self.main_screen_widths(primary_modifier_key_text);
+ let primary_modifier_key_text_len = primary_modifier_key_text.chars().count();
+ let secondary_modifier_key_text_len = secondary_modifier_key_text.chars().count();
+ let top_coordinates = if rows > 14 {
+ (rows.saturating_sub(15) / 2) + 12
+ } else {
+ (rows.saturating_sub(15) / 2) + 9
+ };
+
+ if cols >= widths.0 {
+ let leader_key_text = format!(
+ "Leader keys: {} - modes, {} - quicknav and shortcuts.",
+ primary_modifier_key_text, secondary_modifier_key_text
+ );
+ let left_padding = cols.saturating_sub(widths.0) / 2;
+ print_text_with_coordinates(
+ Text::new(leader_key_text)
+ .color_range(2, ..12)
+ .color_range(
+ self.primary_leader_key_color_index,
+ 13..primary_modifier_key_text_len + 14,
+ )
+ .color_range(
+ self.secondary_leader_key_color_index,
+ primary_modifier_key_text_len + 23
+ ..primary_modifier_key_text_len + 23 + secondary_modifier_key_text_len,
+ ),
+ left_padding,
+ top_coordinates,
+ None,
+ None,
+ )
+ } else {
+ let leader_key_text = format!(
+ "Leaders: {}, {}",
+ primary_modifier_key_text, secondary_modifier_key_text
+ );
+ let left_padding = if cols >= widths.1 {
+ cols.saturating_sub(widths.1) / 2
+ } else {
+ cols.saturating_sub(widths.2) / 2
+ };
+ print_text_with_coordinates(
+ Text::new(leader_key_text)
+ .color_range(2, ..8)
+ .color_range(
+ self.primary_leader_key_color_index,
+ 9..primary_modifier_key_text_len + 10,
+ )
+ .color_range(
+ self.secondary_leader_key_color_index,
+ primary_modifier_key_text_len + 11
+ ..primary_modifier_key_text_len + 12 + secondary_modifier_key_text_len,
+ ),
+ left_padding,
+ top_coordinates,
+ None,
+ None,
+ )
+ };
+ }
+ fn render_warning_if_needed(&self, rows: usize, cols: usize, primary_modifier_key_text: &str) {
+ let widths = self.main_screen_widths(primary_modifier_key_text);
+ let top_coordinates = if rows > 14 {
+ (rows.saturating_sub(15) / 2) + 14
+ } else {
+ (rows.saturating_sub(15) / 2) + 10
+ };
+ let left_padding = if cols >= widths.0 {
+ cols.saturating_sub(widths.0) / 2
+ } else if cols >= widths.1 {
+ cols.saturating_sub(widths.1) / 2
+ } else {
+ cols.saturating_sub(widths.2) / 2
+ };
+ if let Some(warning_text) = self.warning_text(cols) {
+ print_text_with_coordinates(
+ Text::new(warning_text).color_range(3, ..),
+ left_padding,
+ top_coordinates,
+ None,
+ None,
+ );
+ }
+ }
+ fn render_help_text_main(&self, rows: usize, cols: usize, primary_modifier_key_text: &str) {
+ let widths = self.main_screen_widths(primary_modifier_key_text);
+ if cols >= widths.0 {
+ let help_text = "Help: <↓↑/ENTER> - navigate/select, <l> - leaders, <ESC> - close";
+ print_text_with_coordinates(
+ Text::new(help_text)
+ .color_range(2, 6..16)
+ .color_range(2, 36..39)
+ .color_range(2, 51..56),
+ 0,
+ rows,
+ None,
+ None,
+ );
+ } else {
+ let help_text = "Help: <↓↑> / <ENTER> / <l> / <ESC>";
+ print_text_with_coordinates(
+ Text::new(help_text)
+ .color_range(2, 6..10)
+ .color_range(2, 13..20)
+ .color_range(2, 23..26)
+ .color_range(2, 29..34),
+ 0,
+ rows,
+ None,
+ None,
+ );
+ }
+ }
+ fn render_help_text_remapping(&self, rows: usize, cols: usize) {
+ let widths = self.remapping_screen_widths();
+ if cols >= widths.0 {
+ let help_text = "Help: <ENTER> - when done";
+ print_text_with_coordinates(
+ Text::new(help_text).color_range(2, 6..13),
+ 0,
+ rows,
+ None,
+ None,
+ );
+ } else {
+ let help_text = "Help: <ENTER> / <←↓↑→> / <SPACE>";
+ print_text_with_coordinates(
+ Text::new(help_text)
+ .color_range(2, 6..13)
+ .color_range(2, 16..22)
+ .color_range(2, 25..32),
+ 0,
+ rows,
+ None,
+ None,
+ );
+ }
+ }
+ fn primary_modifier_text(&self) -> String {
+ if self.primary_modifier.is_empty() {
+ "<UNBOUND>".to_owned()
+ } else {
+ self.primary_modifier
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join("-")
+ }
+ }
+ fn secondary_modifier_text(&self) -> String {
+ if self.secondary_modifier.is_empty() {
+ "<UNBOUND>".to_owned()
+ } else {
+ self.secondary_modifier
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join("-")
+ }
+ }
+ fn main_screen_widths(&self, primary_modifier_text: &str) -> (usize, usize, usize) {
+ let primary_modifier_key_text_len = primary_modifier_text.chars().count();
+ let full_width = 61 + primary_modifier_key_text_len;
+ let mid_width = 36 + primary_modifier_key_text_len;
+ let min_width = 26 + primary_modifier_key_text_len;
+ (full_width, mid_width, min_width)
+ }
+ fn remapping_screen_widths(&self) -> (usize, usize, usize) {
+ let full_width = 62;
+ let mid_width = 42;
+ let min_width = 30;
+ (full_width, mid_width, min_width)
+ }
+ fn render_main_screen(&mut self, rows: usize, cols: usize) {
+ let primary_modifier_key_text = self.primary_modifier_text();
+ let secondary_modifier_key_text = self.secondary_modifier_text();
+ self.render_override_title(rows, cols, &primary_modifier_key_text);
+ self.render_first_bulletin(rows, cols, &primary_modifier_key_text);
+ self.render_second_bulletin(rows, cols, &primary_modifier_key_text);
+ self.render_leader_keys_indication(
+ rows,
+ cols,
+ &primary_modifier_key_text,
+ &secondary_modifier_key_text,
+ );
+ self.render_warning_if_needed(rows, cols, &primary_modifier_key_text);
+ self.render_help_text_main(rows, cols, &primary_modifier_key_text);
+ }
+ fn warning_text(&self, max_width: usize) -> Option<String> {
+ if self.needs_kitty_support() {
+ // TODO: some widget to test support by detecting pressed keys
+ if max_width >= 38 {
+ Some(String::from("Warning: requires supporting terminal."))
+ } else {
+ Some(String::from("Requires supporting terminal"))
+ }
+ } else if self.primary_modifier.is_empty() && self.secondary_modifier.is_empty() {
+ if max_width >= 49 {
+ Some(String::from(
+ "Warning: no leaders defined. UI will be disabled.",
+ ))
+ } else {
+ Some(String::from("No leaders. UI will be disabled."))
+ }
+ } else {
+ None
+ }
+ }
+ fn needs_kitty_support(&self) -> bool {
+ self.primary_modifier.len() > 1
+ || self.secondary_modifier.len() > 1
+ || self.primary_modifier.contains(&KeyModifier::Super)
+ || self.secondary_modifier.contains(&KeyModifier::Super)
+ }
+}
+
+fn unlock_first_keybinds(primary_modifier: String, secondary_modifier: String) -> String {
+ format!(
+ r#"
+default_mode "locked"
+keybinds clear-defaults=true {{
+ normal {{
+ }}
+ locked {{
+ bind "{primary_modifier} g" {{ SwitchToMode "Normal"; }}
+ }}
+ resize {{
+ bind "r" {{ SwitchToMode "Normal"; }}
+ bind "h" "Left" {{ Resize "Increase Left"; }}
+ bind "j" "Down" {{ Resize "Increase Down"; }}
+ bind "k" "Up" {{ Resize "Increase Up"; }}
+ bind "l" "Right" {{ Resize "Increase Right"; }}
+ bind "H" {{ Resize "Decrease Left"; }}
+ bind "J" {{ Resize "Decrease Down"; }}
+ bind "K" {{ Resize "Decrease Up"; }}
+ bind "L" {{ Resize "Decrease Right"; }}
+ bind "=" "+" {{ Resize "Increase"; }}
+ bind "-" {{ Resize "Decrease"; }}
+ }}
+ pane {{
+ bind "p" {{ SwitchToMode "Normal"; }}
+ bind "h" "Left" {{ MoveFocus "Left"; }}
+ bind "l" "Right" {{ MoveFocus "Right"; }}
+ bind "j" "Down" {{ MoveFocus "Down"; }}
+ bind "k" "Up" {{ MoveFocus "Up"; }}
+ bind "Tab" {{ SwitchFocus; }}
+ bind "n" {{ NewPane; SwitchToMode "Locked"; }}
+ bind "d" {{ NewPane "Down"; SwitchToMode "Locked"; }}
+ bind "r" {{ NewPane "Right"; SwitchToMode "Locked"; }}
+ bind "x" {{ CloseFocus; SwitchToMode "Locked"; }}
+ bind "f" {{ ToggleFocusFullscreen; SwitchToMode "Locked"; }}
+ bind "z" {{ TogglePaneFrames; SwitchToMode "Locked"; }}
+ bind "w" {{ ToggleFloatingPanes; SwitchToMode "Locked"; }}
+ bind "e" {{ TogglePaneEmbedOrFloating; SwitchToMode "Locked"; }}
+ bind "c" {{ SwitchToMode "RenamePane"; PaneNameInput 0;}}
+ }}
+ move {{
+ bind "m" {{ SwitchToMode "Normal"; }}
+ bind "n" "Tab" {{ MovePane; }}
+ bind "p" {{ MovePaneBackwards; }}
+ bind "h" "Left" {{ MovePane "Left"; }}
+ bind "j" "Down" {{ MovePane "Down"; }}
+ bind "k" "Up" {{ MovePane "Up"; }}
+ bind "l" "Right" {{ MovePane "Right"; }}
+ }}
+ tab {{
+ bind "t" {{ SwitchToMode "Normal"; }}
+ bind "r" {{ SwitchToMode "RenameTab"; TabNameInput 0; }}
+ bind "h" "Left" "Up" "k" {{ GoToPreviousTab; }}
+ bind "l" "Right" "Down" "j" {{ GoToNextTab; }}
+ bind "n" {{ NewTab; SwitchToMode "Locked"; }}
+ bind "x" {{ CloseTab; SwitchToMode "Locked"; }}
+ bind "s" {{ ToggleActiveSyncTab; SwitchToMode "Locked"; }}
+ bind "b" {{ BreakPane; SwitchToMode "Locked"; }}
+ bind "]" {{ BreakPaneRight; SwitchToMode "Locked"; }}
+ bind "[" {{ BreakPaneLeft; SwitchToMode "Locked"; }}
+ bind "1" {{ GoToTab 1; SwitchToMode "Locked"; }}
+ bind "2" {{ GoToTab 2; SwitchToMode "Locked"; }}
+ bind "3" {{ GoToTab 3; SwitchToMode "Locked"; }}
+ bind "4" {{ GoToTab 4; SwitchToMode "Locked"; }}
+ bind "5" {{ GoToTab 5; SwitchToMode "Locked"; }}
+ bind "6" {{ GoToTab 6; SwitchToMode "Locked"; }}
+ bind "7" {{ GoToTab 7; SwitchToMode "Locked"; }}
+ bind "8" {{ GoToTab 8; SwitchToMode "Locked"; }}
+ bind "9" {{ GoToTab 9; SwitchToMode "Locked"; }}
+ bind "Tab" {{ ToggleTab; }}
+ }}
+ scroll {{
+ bind "s" {{ SwitchToMode "Normal"; }}
+ bind "e" {{ EditScrollback; SwitchToMode "Locked"; }}
+ bind "f" {{ SwitchToMode "EnterSearch"; SearchInput 0; }}
+ bind "Ctrl c" {{ ScrollToBottom; SwitchToMode "Locked"; }}
+ bind "j" "Down" {{ ScrollDown; }}
+ bind "k" "Up" {{ ScrollUp; }}
+ bind "Ctrl f" "PageDown" "Right" "l" {{ PageScrollDown; }}
+ bind "Ctrl b" "PageUp" "Left" "h" {{ PageScrollUp; }}
+ bind "d" {{ HalfPageScrollDown; }}
+ bind "u" {{ HalfPageScrollUp; }}
+ }}
+ search {{
+ bind "Ctrl c" {{ ScrollToBottom; SwitchToMode "Locked"; }}
+ bind "j" "Down" {{ ScrollDown; }}
+ bind "k" "Up" {{ ScrollUp; }}
+ bind "Ctrl f" "PageDown" "Right" "l" {{ PageScrollDown; }}
+ bind "Ctrl b" "PageUp" "Left" "h" {{ PageScrollUp; }}
+ bind "d" {{ HalfPageScrollDown; }}
+ bind "u" {{ HalfPageScrollUp; }}
+ bind "n" {{ Search "down"; }}
+ bind "p" {{ Search "up"; }}
+ bind "c" {{ SearchToggleOption "CaseSensitivity"; }}
+ bind "w" {{ SearchToggleOption "Wrap"; }}
+ bind "o" {{ SearchToggleOption "WholeWord"; }}
+ }}
+ entersearch {{
+ bind "Ctrl c" "Esc" {{ SwitchToMode "Scroll"; }}
+ bind "Enter" {{ SwitchToMode "Search"; }}
+ }}
+ renametab {{
+ bind "Ctrl c" "Enter" {{ SwitchToMode "Locked"; }}
+ bind "Esc" {{ UndoRenameTab; SwitchToMode "Tab"; }}
+ }}
+ renamepane {{
+ bind "Ctrl c" "Enter" {{ SwitchToMode "Locked"; }}
+ bind "Esc" {{ UndoRenamePane; SwitchToMode "Pane"; }}
+ }}
+ session {{
+ bind "o" {{ SwitchToMode "Normal"; }}
+ bind "{primary_modifier} s" {{ SwitchToMode "Scroll"; }}
+ bind "d" {{ Detach; }}
+ bind "w" {{
+ LaunchOrFocusPlugin "session-manager" {{
+ floating true
+ move_to_focused_tab true
+ }};
+ SwitchToMode "Locked"
+ }}
+ bind "c" {{
+ LaunchOrFocusPlugin "configuration" {{
+ floating true
+ move_to_focused_tab true
+ }};
+ SwitchToMode "Locked"
+ }}
+ }}
+ shared_except "locked" "renametab" "renamepane" {{
+ bind "{primary_modifier} g" {{ SwitchToMode "Locked"; }}
+ bind "{primary_modifier} q" {{ Quit; }}
+ }}
+ shared_among "normal" "locked" {{
+ bind "{secondary_modifier} n" {{ NewPane; }}
+ bind "{secondary_modifier} f" {{ ToggleFloatingPanes; }}
+ bind "{secondary_modifier} i" {{ MoveTab "Left"; }}
+ bind "{secondary_modifier} o" {{ MoveTab "Right"; }}
+ bind "{secondary_modifier} h" "{secondary_modifier} Left" {{ MoveFocusOrTab "Left"; }}
+ bind "{secondary_modifier} l" "{secondary_modifier} Right" {{ MoveFocusOrTab "Right"; }}
+ bind "{secondary_modifier} j" "{secondary_modifier} Down" {{ MoveFocus "Down"; }}
+ bind "{secondary_modifier} k" "{secondary_modifier} Up" {{ MoveFocus "Up"; }}
+ bind "{secondary_modifier} =" "{secondary_modifier} +" {{ Resize "Increase"; }}
+ bind "{secondary_modifier} -" {{ Resize "Decrease"; }}
+ bind "{secondary_modifier} [" {{ PreviousSwapLayout; }}
+ bind "{secondary_modifier} ]" {{ NextSwapLayout; }}
+ }}
+ shared_except "locked" "renametab" "renamepane" {{
+ bind "Enter" {{ SwitchToMode "Locked"; }}
+ }}
+ shared_except "pane" "locked" "renametab" "renamepane" "entersearch" {{
+ bind "p" {{ SwitchToMode "Pane"; }}
+ }}
+ shared_except "resize" "locked" "renametab" "renamepane" "entersearch" {{
+ bind "r" {{ SwitchToMode "Resize"; }}
+ }}
+ shared_except "scroll" "locked" "renametab" "renamepane" "entersearch" {{
+ bind "s" {{ SwitchToMode "Scroll"; }}
+ }}
+ shared_except "session" "locked" "renametab" "renamepane" "entersearch" {{
+ bind "o" {{ SwitchToMode "Session"; }}
+ }}
+ shared_except "tab" "locked" "renametab" "renamepane" "entersearch" {{
+ bind "t" {{ SwitchToMode "Tab"; }}
+ }}
+ shared_except "move" "locked" "renametab" "renamepane" "entersearch" {{
+ bind "m" {{ SwitchToMode "Move"; }}
+ }}
+}}"#
+ )
+}
+
+fn default_keybinds(primary_modifier: String, secondary_modifier: String) -> String {
+ if primary_modifier.is_empty() && secondary_modifier.is_empty() {
+ return default_keybinds_no_modifiers();
+ } else if primary_modifier == secondary_modifier {
+ return non_colliding_default_keybinds(primary_modifier, secondary_modifier);
+ } else if primary_modifier.is_empty() {
+ return default_keybinds_no_primary_modifier(secondary_modifier);
+ } else if secondary_modifier.is_empty() {
+ return default_keybinds_no_secondary_modifier(primary_modifier);
+ }
+ format!(
+ r#"
+default_mode "normal"
+keybinds clear-defaults=true {{
+ normal {{}}
+ locked {{
+ bind "{primary_modifier} g" {{ SwitchToMode "Normal"; }}
+ }}
+ resize {{
+ bind "{primary_modifier} n" {{ SwitchToMode "Normal"; }}
+ bind "h" "Left" {{ Resize "Increase Left"; }}
+ bind "j" "Down" {{ Resize "Increase Down"; }}
+ bind "k" "Up" {{ Resize "Increase Up"; }}
+ bind "l" "Right" {{ Resize "Increase Right"; }}
+ bind "H" {{ Resize "Decrease Left"; }}
+ bind "J" {{ Resize "Decrease Down"; }}
+ bind "K" {{ Resize "Decrease Up"; }}
+ bind "L" {{ Resize "Decrease Right"; }}
+ bind "=" "+" {{ Resize "Increase"; }}
+ bind "-" {{ Resize "Decrease"; }}
+ }}
+ pane {{
+ bind "{primary_modifier} p" {{ SwitchToMode "Normal"; }}
+ bind "h" "Left" {{ MoveFocus "Left"; }}
+ bind "l" "Right" {{ MoveFocus "Right"; }}
+ bind "j" "Down" {{ MoveFocus "Down"; }}
+ bind "k" "Up" {{ MoveFocus "Up"; }}
+ bind "p" {{ SwitchFocus; }}
+ bind "n" {{ NewPane; SwitchToMode "Normal"; }}
+ bind "d" {{ NewPane "Down"; SwitchToMode "Normal"; }}
+ bind "r" {{ NewPane "Right"; SwitchToMode "Normal"; }}
+ bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
+ bind "f" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
+ bind "z" {{ TogglePaneFrames; SwitchToMode "Normal"; }}
+ bind "w" {{ ToggleFloatingPanes; SwitchToMode "Normal"; }}
+ bind "e" {{ TogglePaneEmbedOrFloating; SwitchToMode "Normal"; }}
+ bind "c" {{ SwitchToMode "RenamePane"; PaneNameInput 0;}}
+ }}
+ move {{
+ bind "{primary_modifier} h" {{ SwitchToMode "Normal"; }}
+ bind "n" "Tab" {{ MovePane; }}
+ bind "p" {{ MovePaneBackwards; }}
+ bind "h" "Left" {{ MovePane "Left"; }}
+ bind "j" "Down" {{ MovePane "Down"; }}
+ bind "k" "Up" {{ MovePane "Up"; }}
+ bind "l" "Right" {{ MovePane "Right"; }}
+ }}
+ tab {{
+ bind "{primary_modifier} t" {{ SwitchToMode "Normal"; }}
+ bind "r" {{ SwitchToMode "RenameTab"; TabNameInput 0; }}
+ bind "h" "Left" "Up" "k" {{ GoToPreviousTab; }}
+ bind "l" "Right" "Down" "j" {{ GoToNextTab; }}
+ bind "n" {{ NewTab; SwitchToMode "Normal"; }}
+ bind "x" {{ CloseTab; SwitchToMode "Normal"; }}
+ bind "s" {{ ToggleActiveSyncTab; SwitchToMode "Normal"; }}
+ bind "b" {{ BreakPane; SwitchToMode "Normal"; }}
+ bind "]" {{ BreakPaneRight; SwitchToMode "Normal"; }}
+ bind "[" {{ BreakPaneLeft; SwitchToMode "Normal"; }}
+ bind "1" {{ GoToTab 1; SwitchToMode "Normal"; }}
+ bind "2" {{ GoToTab 2; SwitchToMode "Normal"; }}
+ bind "3" {{ GoToTab 3; SwitchToMode "Normal"; }}
+ bind "4" {{ GoToTab 4; SwitchToMode "Normal"; }}
+ bind "5" {{ GoToTab 5; SwitchToMode "Normal"; }}
+ bind "6" {{ GoToTab 6; SwitchToMode "Normal"; }}
+ bind "7" {{ GoToTab 7; SwitchToMode "Normal"; }}
+ bind "8" {{ GoToTab 8; SwitchToMode "Normal"; }}
+ bind "9" {{ GoToTab 9; SwitchToMode "Normal"; }}
+ bind "Tab" {{ ToggleTab; }}
+ }}
+ scroll {{
+ bind "{primary_modifier} s" {{ SwitchToMode "Normal"; }}
+ bind "e" {{ EditScrollback; SwitchToMode "Normal"; }}
+ bind "s" {{ SwitchToMode "EnterSearch"; SearchInput 0; }}
+ bind "Ctrl c" {{ ScrollToBottom; SwitchToMode "Normal"; }}
+ bind "j" "Down" {{ ScrollDown; }}
+ bind "k" "Up" {{ ScrollUp; }}
+ bind "Ctrl f" "PageDown" "Right" "l" {{ PageScrollDown; }}
+ bind "Ctrl b" "PageUp" "Left" "h" {{ PageScrollUp; }}
+ bind "d" {{ HalfPageScrollDown; }}
+ bind "u" {{ HalfPageScrollUp; }}
+ }}
+ search {{
+ bind "{primary_modifier} s" {{ SwitchToMode "Normal"; }}
+ bind "Ctrl c" {{ ScrollToBottom; SwitchToMode "Normal"; }}
+ bind "j" "Down" {{ ScrollDown; }}
+ bind "k" "Up" {{ ScrollUp; }}
+ bind "Ctrl f" "PageDown" "Right" "l" {{ PageScrollDown; }}
+ bind "Ctrl b" "PageUp" "Left" "h" {{ PageScrollUp; }}
+ bind "d" {{ HalfPageScrollDown; }}
+ bind "u" {{ HalfPageScrollUp; }}
+ bind "n" {{ Search "down"; }}
+ bind "p" {{ Search "up"; }}
+ bind "c" {{ SearchToggleOption "CaseSensitivity"; }}
+ bind "w" {{ SearchToggleOption "Wrap"; }}
+ bind "o" {{ SearchToggleOption "WholeWord"; }}
+ }}
+ entersearch {{
+ bind "Ctrl c" "Esc" {{ SwitchToMode "Scroll"; }}
+ bind "Enter" {{ SwitchToMode "Search"; }}
+ }}
+ renametab {{
+ bind "Ctrl c" {{ SwitchToMode "Normal"; }}
+ bind "Esc" {{ UndoRenameTab; SwitchToMode "Tab"; }}
+ }}
+ renamepane {{
+ bind "Ctrl c" {{ SwitchToMode "Normal"; }}
+ bind "Esc" {{ UndoRenamePane; SwitchToMode "Pane"; }}
+ }}
+ session {{
+ bind "{primary_modifier} o" {{ SwitchToMode "Normal"; }}
+ bind "{primary_modifier} s" {{ SwitchToMode "Scroll"; }}
+ bind "d" {{ Detach; }}
+ bind "w" {{
+ LaunchOrFocusPlugin "session-manager" {{
+ floating true
+ move_to_focused_tab true
+ }};
+ SwitchToMode "Normal"
+ }}
+ bind "c" {{
+ LaunchOrFocusPlugin "configuration" {{
+ floating true
+ move_to_focused_tab true
+ }};
+ SwitchToMode "Normal"
+ }}
+ }}
+ tmux {{
+ bind "[" {{ SwitchToMode "Scroll"; }}
+ bind "{primary_modifier} b" {{ Write 2; SwitchToMode "Normal"; }}
+ bind "\"" {{ NewPane "Down"; SwitchToMode "Normal"; }}
+ bind "%" {{ NewPane "Right"; SwitchToMode "Normal"; }}
+ bind "z" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
+ bind "c" {{ NewTab; SwitchToMode "Normal"; }}
+ bind "," {{ SwitchToMode "RenameTab"; }}
+ bind "p" {{ GoToPreviousTab; SwitchToMode "Normal"; }}
+ bind "n" {{ GoToNextTab; SwitchToMode "Normal"; }}
+ bind "Left" {{ MoveFocus "Left"; SwitchToMode "Normal"; }}
+ bind "Right" {{ MoveFocus "Right"; SwitchToMode "Normal"; }}
+ bind "Down" {{ MoveFocus "Down"; SwitchToMode "Normal"; }}
+ bind "Up" {{ MoveFocus "Up"; SwitchToMode "Normal"; }}
+ bind "h" {{ MoveFocus "Left"; SwitchToMode "Normal"; }}
+ bind "l" {{ MoveFocus "Right"; SwitchToMode "Normal"; }}
+ bind "j" {{ MoveFocus "Down"; SwitchToMode "Normal"; }}
+ bind "k" {{ MoveFocus "Up"; SwitchToMode "Normal"; }}
+ bind "o" {{ FocusNextPane; }}
+ bind "d" {{ Detach; }}
+ bind "Space" {{ NextSwapLayout; }}
+ bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
+ }}
+ shared_except "locked" {{
+ bind "{primary_modifier} g" {{ SwitchToMode "Locked"; }}
+ bind "{primary_modifier} q" {{ Quit; }}
+ bind "{secondary_modifier} f" {{ ToggleFloatingPanes; }}
+ bind "{secondary_modifier} n" {{ NewPane; }}
+ bind "{secondary_modifier} i" {{ MoveTab "Left"; }}
+ bind "{secondary_modifier} o" {{ MoveTab "Right"; }}
+ bind "{secondary_modifier} h" "{secondary_modifier} Left" {{ MoveFocusOrTab "Left"; }}
+ bind "{secondary_modifier} l" "{secondary_modifier} Right" {{ MoveFocusOrTab "Right"; }}
+ bind "{secondary_modifier} j" "{secondary_modifier} Down" {{ MoveFocus "Down"; }}
+ bind "{secondary_modifier} k" "{secondary_modifier} Up" {{ MoveFocus "Up"; }}
+ bind "{secondary_modifier} =" "{secondary_modifier} +" {{ Resize "Increase"; }}
+ bind "{secondary_modifier} -" {{ Resize "Decrease"; }}
+ bind "{secondary_modifier} [" {{ PreviousSwapLayout; }}
+ bind "{secondary_modifier} ]" {{ NextSwapLayout; }}
+ }}
+ shared_except "normal" "locked" {{
+ bind "Enter" "Esc" {{ SwitchToMode "Normal"; }}
+ }}
+ shared_except "pane" "locked" {{
+ bind "{primary_modifier} p" {{ SwitchToMode "Pane"; }}
+ }}
+ shared_except "resize" "locked" {{
+ bind "{primary_modifier} n" {{ SwitchToMode "Resize"; }}
+ }}
+ shared_except "scroll" "locked" {{
+ bind "{primary_modifier} s" {{ SwitchToMode "Scroll"; }}
+ }}
+ shared_except "session" "locked" {{
+ bind "{primary_modifier} o" {{ SwitchToMode "Session"; }}
+ }}
+ shared_except "tab" "locked" {{
+ bind "{primary_modifier} t" {{ SwitchToMode "Tab"; }}
+ }}
+ shared_except "move" "locked" {{
+ bind "{primary_modifier} h" {{ SwitchToMode "Move"; }}
+ }}
+ shared_except "tmux" "locked" {{
+ bind "{primary_modifier} b" {{ SwitchToMode "Tmux"; }}
+ }}
+}}
+"#
+ )
+}
+
+fn default_keybinds_no_primary_modifier(secondary_modifier: String) -> String {
+ format!(
+ r#"
+default_mode "normal"
+keybinds clear-defaults=true {{
+ normal {{}}
+ locked {{}}
+ resize {{
+ bind "h" "Left" {{ Resize "Increase Left"; }}
+ bind "j" "Down" {{ Resize "Increase Down"; }}
+ bind "k" "Up" {{ Resize "Increase Up"; }}
+ bind "l" "Right" {{ Resize "Increase Right"; }}
+ bind "H" {{ Resize "Decrease Left"; }}
+ bind "J" {{ Resize "Decrease Down"; }}
+ bind "K" {{ Resize "Decrease Up"; }}
+ bind "L" {{ Resize "Decrease Right"; }}
+ bind "=" "+" {{ Resize "Increase"; }}
+ bind "-" {{ Resize "Decrease"; }}
+ }}
+ pane {{
+ bind "h" "Left" {{ MoveFocus "Left"; }}
+ bind "l" "Right" {{ MoveFocus "Right"; }}
+ bind "j" "Down" {{ MoveFocus "Down"; }}
+ bind "k" "Up" {{ MoveFocus "Up"; }}
+ bind "p" {{ SwitchFocus; }}
+ bind "n" {{ NewPane; SwitchToMode "Normal"; }}
+ bind "d" {{ NewPane "Down"; SwitchToMode "Normal"; }}
+ bind "r" {{ NewPane "Right"; SwitchToMode "Normal"; }}
+ bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
+ bind "f" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
+ bind "z" {{ TogglePaneFrames; SwitchToMode "Normal"; }}
+ bind "w" {{ ToggleFloatingPanes; SwitchToMode "Normal"; }}
+ bind "e" {{ TogglePaneEmbedOrFloating; SwitchToMode "Normal"; }}
+ bind "c" {{ SwitchToMode "RenamePane"; PaneNameInput 0;}}
+ }}
+ move {{
+ bind "n" "Tab" {{ MovePane; }}
+ bind "p" {{ MovePaneBackwards; }}
+ bind "h" "Left" {{ MovePane "Left"; }}
+ bind "j" "Down" {{ MovePane "Down"; }}
+ bind "k" "Up" {{ MovePane "Up"; }}
+ bind "l" "Right" {{ MovePane "Right"; }}
+ }}
+ tab {{
+ bind "r" {{ SwitchToMode "RenameTab"; TabNameInput 0; }}
+ bind "h" "Left" "Up" "k" {{ GoToPreviousTab; }}
+ bind "l" "Right" "Down" "j" {{ GoToNextTab; }}
+ bind "n" {{ NewTab; SwitchToMode "Normal"; }}
+ bind "x" {{ CloseTab; SwitchToMode "Normal"; }}
+ bind "s" {{ ToggleActiveSyncTab; SwitchToMode "Normal"; }}
+ bind "b" {{ BreakPane; SwitchToMode "Normal"; }}
+ bind "]" {{ BreakPaneRight; SwitchToMode "Normal"; }}
+ bind "[" {{ BreakPaneLeft; SwitchToMode "Normal"; }}
+ bind "1" {{ GoToTab 1; SwitchToMode "Normal"; }}
+ bind "2" {{ GoToTab 2; SwitchToMode "Normal"; }}
+ bind "3" {{ GoToTab 3; SwitchToMode "Normal"; }}
+ bind "4" {{ GoToTab 4; SwitchToMode "Normal"; }}
+ bind "5" {{ GoToTab 5; SwitchToMode "Normal"; }}
+ bind "6" {{ GoToTab 6; SwitchToMode "Normal"; }}
+ bind "7" {{ GoToTab 7; SwitchToMode "Normal"; }}
+ bind "8" {{ GoToTab 8; SwitchToMode "Normal"; }}
+ bind "9" {{ GoToTab 9; SwitchToMode "Normal"; }}
+ bind "Tab" {{ ToggleTab; }}
+ }}
+ scroll {{
+ bind "e" {{ EditScrollback; SwitchToMode "Normal"; }}
+ bind "s" {{ SwitchToMode "EnterSearch"; SearchInput 0; }}
+ bind "j" "Down" {{ ScrollDown; }}
+ bind "k" "Up" {{ ScrollUp; }}
+ bind "d" {{ HalfPageScrollDown; }}
+ bind "u" {{ HalfPageScrollUp; }}
+ }}
+ search {{
+ bind "Ctrl c" {{ ScrollToBottom; SwitchToMode "Normal"; }}
+ bind "j" "Down" {{ ScrollDown; }}
+ bind "k" "Up" {{ ScrollUp; }}
+ bind "Ctrl f" "PageDown" "Right" "l" {{ PageScrollDown; }}
+ bind "Ctrl b" "PageUp" "Left" "h" {{ PageScrollUp; }}
+ bind "d" {{ HalfPageScrollDown; }}
+ bind "u" {{ HalfPageScrollUp; }}
+ bind "n" {{ Search "down"; }}
+ bind "p" {{ Search "up"; }}
+ bind "c" {{ SearchToggleOption "CaseSensitivity"; }}
+ bind "w" {{ SearchToggleOption "Wrap"; }}
+ bind "o" {{ SearchToggleOption "WholeWord"; }}
+ }}
+ entersearch {{
+ bind "Ctrl c" "Esc" {{ SwitchToMode "Scroll"; }}
+ bind "Enter" {{ SwitchToMode "Search"; }}
+ }}
+ renametab {{
+ bind "Ctrl c" {{ SwitchToMode "Normal"; }}
+ bind "Esc" {{ UndoRenameTab; SwitchToMode "Tab"; }}
+ }}
+ renamepane {{
+ bind "Ctrl c" {{ SwitchToMode "Normal"; }}
+ bind "Esc" {{ UndoRenamePane; SwitchToMode "Pane"; }}
+ }}
+ session {{
+ bind "d" {{ Detach; }}
+ bind "w" {{
+ LaunchOrFocusPlugin "session-manager" {{
+ floating true
+ move_to_focused_tab true
+ }};
+ SwitchToMode "Normal"
+ }}
+ bind "c" {{
+ LaunchOrFocusPlugin "configuration" {{
+ floating true
+ move_to_focused_tab true
+ }};
+ SwitchToMode "Normal"
+ }}
+ }}
+ tmux {{
+ bind "[" {{ SwitchToMode "Scroll"; }}
+ bind "\"" {{ NewPane "Down"; SwitchToMode "Normal"; }}
+ bind "%" {{ NewPane "Right"; SwitchToMode "Normal"; }}
+ bind "z" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
+ bind "c" {{ NewTab; SwitchToMode "Normal"; }}
+ bind "," {{ SwitchToMode "RenameTab"; }}
+ bind "p" {{ GoToPreviousTab; SwitchToMode "Normal"; }}
+ bind "n" {{ GoToNextTab; SwitchToMode "Normal"; }}
+ bind "Left" {{ MoveFocus "Left"; SwitchToMode "Normal"; }}
+ bind "Right" {{ MoveFocus "Right"; SwitchToMode "Normal"; }}
+ bind "Down" {{ MoveFocus "Down"; SwitchToMode "Normal"; }}
+ bind "Up" {{ MoveFocus "Up"; SwitchToMode "Normal"; }}
+ bind "h" {{ MoveFocus "Left"; SwitchToMode "Normal"; }}
+ bind "l" {{ MoveFocus "Right"; SwitchToMode "Normal"; }}
+ bind "j" {{ MoveFocus "Down"; SwitchToMode "Normal"; }}
+ bind "k" {{ MoveFocus "Up"; SwitchToMode "Normal"; }}
+ bind "o" {{ FocusNextPane; }}
+ bind "d" {{ Detach; }}
+ bind "Space" {{ NextSwapLayout; }}
+ bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
+ }}
+ shared_except "locked" {{
+ bind "{secondary_modifier} n" {{ NewPane; }}
+ bind "{secondary_modifier} f" {{ ToggleFloatingPanes; }}
+ bind "{secondary_modifier} i" {{ MoveTab "Left"; }}
+ bind "{secondary_modifier} o" {{ MoveTab "Right"; }}
+ bind "{secondary_modifier} h" "{secondary_modifier} Left" {{ MoveFocusOrTab "Left"; }}
+ bind "{secondary_modifier} l" "{secondary_modifier} Right" {{ MoveFocusOrTab "Right"; }}
+ bind "{secondary_modifier} j" "{secondary_modifier} Down" {{ MoveFocus "Down"; }}
+ bind "{secondary_modifier} k" "{secondary_modifier} Up" {{ MoveFocus "Up"; }}
+ bind "{secondary_modifier} =" "{secondary_modifier} +" {{ Resize "Increase"; }}
+ bind "{secondary_modifier} -" {{ Resize "Decrease"; }}
+ bind "{secondary_modifier} [" {{ PreviousSwapLayout; }}
+ bind "{secondary_modifier} ]" {{ NextSwapLayout; }}
+ }}
+ shared_except "normal" "locked" {{
+ bind "Enter" "Esc" {{ SwitchToMode "Normal"; }}
+ }}
+}}
+"#
+ )
+}
+
+fn default_keybinds_no_secondary_modifier(primary_modifier: String) -> String {
+ format!(
+ r#"
+default_mode "normal"
+keybinds clear-defaults=true {{
+ normal {{}}
+ locked {{
+ bind "{primary_modifier} g" {{ SwitchToMode "Normal"; }}
+ }}
+ resize {{
+ bind "{primary_modifier} n" {{ SwitchToMode "Normal"; }}
+ bind "h" "Left" {{ Resize "Increase Left"; }}
+ bind "j" "Down" {{ Resize "Increase Down"; }}
+ bind "k" "Up" {{ Resize "Increase Up"; }}
+ bind "l" "Right" {{ Resize "Increase Right"; }}
+ bind "H" {{ Resize "Decrease Left"; }}
+ bind "J" {{ Resize "Decrease Down"; }}
+ bind "K" {{ Resize "Decrease Up"; }}
+ bind "L" {{ Resize "Decrease Right"; }}
+ bind "=" "+" {{ Resize "Increase"; }}
+ bind "-" {{ Resize "Decrease"; }}
+ }}
+ pane {{
+ bind "{primary_modifier} p" {{ SwitchToMode "Normal"; }}
+ bind "h" "Left" {{ MoveFocus "Left"; }}
+ bind "l" "Right" {{ MoveFocus "Right"; }}
+ bind "j" "Down" {{ MoveFocus "Down"; }}
+ bind "k" "Up" {{ MoveFocus "Up"; }}
+ bind "p" {{ SwitchFocus; }}
+ bind "n" {{ NewPane; SwitchToMode "Normal"; }}
+ bind "d" {{ NewPane "Down"; SwitchToMode "Normal"; }}
+ bind "r" {{ NewPane "Right"; SwitchToMode "Normal"; }}
+ bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
+ bind "f" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
+ bind "z" {{ TogglePaneFrames; SwitchToMode "Normal"; }}
+ bind "w" {{ ToggleFloatingPanes; SwitchToMode "Normal"; }}
+ bind "e" {{ TogglePaneEmbedOrFloating; SwitchToMode "Normal"; }}
+ bind "c" {{ SwitchToMode "RenamePane"; PaneNameInput 0;}}
+ }}
+ move {{
+ bind "{primary_modifier} h" {{ SwitchToMode "Normal"; }}
+ bind "n" "Tab" {{ MovePane; }}
+ bind "p" {{ MovePaneBackwards; }}
+ bind "h" "Left" {{ MovePane "Left"; }}
+ bind "j" "Down" {{ MovePane "Down"; }}
+ bind "k" "Up" {{ MovePane "Up"; }}
+ bind "l" "Right" {{ MovePane "Right"; }}
+ }}
+ tab {{
+ bind "{primary_modifier} t" {{ SwitchToMode "Normal"; }}
+ bind "r" {{ SwitchToMode "RenameTab"; TabNameInput 0; }}
+ bind "h" "Left" "Up" "k" {{ GoToPreviousTab; }}
+ bind "l" "Right" "Down" "j" {{ GoToNextTab; }}
+ bind "n" {{ NewTab; SwitchToMode "Normal"; }}
+ bind "x" {{ CloseTab; SwitchToMode "Normal"; }}
+ bind "s" {{ ToggleActiveSyncTab; SwitchToMode "Normal"; }}
+ bind "b" {{ BreakPane; SwitchToMode "Normal"; }}
+ bind "]" {{ BreakPaneRight; SwitchToMode "Normal"; }}
+ bind "[" {{ BreakPaneLeft; SwitchToMode "Normal"; }}
+ bind "1" {{ GoToTab 1; SwitchToMode "Normal"; }}
+ bind "2" {{ GoToTab 2; SwitchToMode "Normal"; }}
+ bind "3" {{ GoToTab 3; SwitchToMode "Normal"; }}
+ bind "4" {{ GoToTab 4; SwitchToMode "Normal"; }}
+ bind "5" {{ GoToTab 5; SwitchToMode "Normal"; }}
+ bind "6" {{ GoToTab 6; SwitchToMode "Normal"; }}
+ bind "7" {{ GoToTab 7; SwitchToMode "Normal"; }}
+ bind "8" {{ GoToTab 8; SwitchToMode "Normal"; }}
+ bind "9" {{ GoToTab 9; SwitchToMode "Normal"; }}
+ bind "Tab" {{ ToggleTab; }}
+ }}
+ scroll {{
+ bind "{primary_modifier} s" {{ SwitchToMode "Normal"; }}
+ bind "e" {{ EditScrollback; SwitchToMode "Normal"; }}
+ bind "s" {{ SwitchToMode "EnterSearch"; SearchInput 0; }}
+ bind "Ctrl c" {{ ScrollToBottom; SwitchToMode "Normal"; }}
+ bind "j" "Down" {{ ScrollDown; }}
+ bind "k" "Up" {{ ScrollUp; }}
+ bind "Ctrl f" "PageDown" "Right" "l" {{ PageScrollDown; }}
+ bind "Ctrl b" "PageUp" "Left" "h" {{ PageScrollUp; }}
+ bind "d" {{ HalfPageScrollDown; }}
+ bind "u" {{ HalfPageScrollUp; }}
+ }}
+ search {{
+ bind "{primary_modifier} s" {{ SwitchToMode "Normal"; }}
+ bind "Ctrl c" {{ ScrollToBottom; SwitchToMode "Normal"; }}
+ bind "j" "Down" {{ ScrollDown; }}
+ bind "k" "Up" {{ ScrollUp; }}
+ bind "Ctrl f" "PageDown" "Right" "l" {{ PageScrollDown; }}
+ bind "Ctrl b" "PageUp" "Left" "h" {{ PageScrollUp; }}
+ bind "d" {{ HalfPageScrollDown; }}
+ bind "u" {{ HalfPageScrollUp; }}
+ bind "n" {{ Search "down"; }}
+ bind "p" {{ Search "up"; }}
+ bind "c" {{ SearchToggleOption "CaseSensitivity"; }}
+ bind "w" {{ SearchToggleOption "Wrap"; }}
+ bind "o" {{ SearchToggleOption "WholeWord"; }}
+ }}
+ entersearch {{
+ bind "Ctrl c" "Esc" {{ SwitchToMode "Scroll"; }}
+ bind "Enter" {{ SwitchToMode "Search"; }}
+ }}
+ renametab {{
+ bind "Ctrl c" {{ SwitchToMode "Normal"; }}
+ bind "Esc" {{ UndoRenameTab; SwitchToMode "Tab"; }}
+ }}
+ renamepane {{
+ bind "Ctrl c" {{ SwitchToMode "Normal"; }}
+ bind "Esc" {{ UndoRenamePane; SwitchToMode "Pane"; }}
+ }}
+ session {{
+ bind "{primary_modifier} o" {{ SwitchToMode "Normal"; }}
+ bind "{primary_modifier} s" {{ SwitchToMode "Scroll"; }}
+ bind "d" {{ Detach; }}
+ bind "w" {{
+ LaunchOrFocusPlugin "session-manager" {{
+ floating true
+ move_to_focused_tab true
+ }};
+ SwitchToMode "Normal"
+ }}
+ bind "c" {{
+ LaunchOrFocusPlugin "configuration" {{
+ floating true
+ move_to_focused_tab true
+ }};
+ SwitchToMode "Normal"
+ }}
+ }}
+ tmux {{
+ bind "[" {{ SwitchToMode "Scroll"; }}
+ bind "{primary_modifier} b" {{ Write 2; SwitchToMode "Normal"; }}
+ bind "\"" {{ NewPane "Down"; SwitchToMode "Normal"; }}
+ bind "%" {{ NewPane "Right"; SwitchToMode "Normal"; }}
+ bind "z" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
+ bind "c" {{ NewTab; SwitchToMode "Normal"; }}
+ bind "," {{ SwitchToMode "RenameTab"; }}
+ bind "p" {{ GoToPreviousTab; SwitchToMode "Normal"; }}
+ bind "n" {{ GoToNextTab; SwitchToMode "Normal"; }}
+ bind "Left" {{ MoveFocus "Left"; SwitchToMode "Normal"; }}
+ bind "Right" {{ MoveFocus "Right"; SwitchToMode "Normal"; }}
+ bind "Down" {{ MoveFocus "Down"; SwitchToMode "Normal"; }}
+ bind "Up" {{ MoveFocus "Up"; SwitchToMode "Normal"; }}
+ bind "h" {{ MoveFocus "Left"; SwitchToMode "Normal"; }}
+ bind "l" {{ MoveFocus "Right"; SwitchToMode "Normal"; }}
+ bind "j" {{ MoveFocus "Down"; SwitchToMode "Normal"; }}
+ bind "k" {{ MoveFocus "Up"; SwitchToMode "Normal"; }}
+ bind "o" {{ FocusNextPane; }}
+ bind "d" {{ Detach; }}
+ bind "Space" {{ NextSwapLayout; }}
+ bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
+ }}
+ shared_except "locked" {{
+ bind "{primary_modifier} g" {{ SwitchToMode "Locked"; }}
+ bind "{primary_modifier} q" {{ Quit; }}
+ }}
+ shared_except "normal" "locked" {{
+ bind "Enter" "Esc" {{ SwitchToMode "Normal"; }}
+ }}
+ shared_except "pane" "locked" {{
+ bind "{primary_modifier} p" {{ SwitchToMode "Pane"; }}
+ }}
+ shared_except "resize" "locked" {{
+ bind "{primary_modifier} n" {{ SwitchToMode "Resize"; }}
+ }}
+ shared_except "scroll" "locked" {{
+ bind "{primary_modifier} s" {{ SwitchToMode "Scroll"; }}
+ }}
+ shared_except "session" "locked" {{
+ bind "{primary_modifier} o" {{ SwitchToMode "Session"; }}
+ }}
+ shared_except "tab" "locked" {{
+ bind "{primary_modifier} t" {{ SwitchToMode "Tab"; }}
+ }}
+ shared_except "move" "locked" {{
+ bind "{primary_modifier} h" {{ SwitchToMode "Move"; }}
+ }}
+ shared_except "tmux" "locked" {{
+ bind "{primary_modifier} b" {{ SwitchToMode "Tmux"; }}
+ }}
+}}
+"#
+ )
+}
+
+fn default_keybinds_no_modifiers() -> String {
+ format!(
+ r#"
+default_mode "normal"
+keybinds clear-defaults=true {{
+ normal {{}}
+ locked {{}}
+ resize {{
+ bind "h" "Left" {{ Resize "Increase Left"; }}
+ bind "j" "Down" {{ Resize "Increase Down"; }}
+ bind "k" "Up" {{ Resize "Increase Up"; }}
+ bind "l" "Right" {{ Resize "Increase Right"; }}
+ bind "H" {{ Resize "Decrease Left"; }}
+ bind "J" {{ Resize "Decrease Down"; }}
+ bind "K" {{ Resize "Decrease Up"; }}
+ bind "L" {{ Resize "Decrease Right"; }}
+ bind "=" "+" {{ Resize "Increase"; }}
+ bind "-" {{ Resize "Decrease"; }}
+ }}
+ pane {{
+ bind "h" "Left" {{ MoveFocus "Left"; }}
+ bind "l" "Right" {{ MoveFocus "Right"; }}
+ bind "j" "Down" {{ MoveFocus "Down"; }}
+ bind "k" "Up" {{ MoveFocus "Up"; }}
+ bind "p" {{ SwitchFocus; }}
+ bind "n" {{ NewPane; SwitchToMode "Normal"; }}
+ bind "d" {{ NewPane "Down"; SwitchToMode "Normal"; }}
+ bind "r" {{ NewPane "Right"; SwitchToMode "Normal"; }}
+ bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
+ bind "f" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
+ bind "z" {{ TogglePaneFrames; SwitchToMode "Normal"; }}
+ bind "w" {{ ToggleFloatingPanes; SwitchToMode "Normal"; }}
+ bind "e" {{ TogglePaneEmbedOrFloating; SwitchToMode "Normal"; }}
+ bind "c" {{ SwitchToMode "RenamePane"; PaneNameInput 0;}}
+ }}
+ move {{
+ bind "n" "Tab" {{ MovePane; }}
+ bind "p" {{ MovePaneBackwards; }}
+ bind "h" "Left" {{ MovePane "Left"; }}
+ bind "j" "Down" {{ MovePane "Down"; }}
+ bind "k" "Up" {{ MovePane "Up"; }}
+ bind "l" "Right" {{ MovePane "Right"; }}
+ }}
+ tab {{
+ bind "r" {{ SwitchToMode "RenameTab"; TabNameInput 0; }}
+ bind "h" "Left" "Up" "k" {{ GoToPreviousTab; }}
+ bind "l" "Right" "Down" "j" {{ GoToNextTab; }}
+ bind "n" {{ NewTab; SwitchToMode "Normal"; }}
+ bind "x" {{ CloseTab; SwitchToMode "Normal"; }}
+ bind "s" {{ ToggleActiveSyncTab; SwitchToMode "Normal"; }}
+ bind "b" {{ BreakPane; SwitchToMode "Normal"; }}
+ bind "]" {{ BreakPaneRight; SwitchToMode "Normal"; }}
+ bind "[" {{ BreakPaneLeft; SwitchToMode "Normal"; }}
+ bind "1" {{ GoToTab 1; SwitchToMode "Normal"; }}
+ bind "2" {{ GoToTab 2; SwitchToMode "Normal"; }}
+ bind "3" {{ GoToTab 3; SwitchToMode "Normal"; }}
+ bind "4" {{ GoToTab 4; SwitchToMode "Normal"; }}
+ bind "5" {{ GoToTab 5; SwitchToMode "Normal"; }}
+ bind "6" {{ GoToTab 6; SwitchToMode "Normal"; }}
+ bind "7" {{ GoToTab 7; SwitchToMode "Normal"; }}
+ bind "8" {{ GoToTab 8; SwitchToMode "Normal"; }}
+ bind "9" {{ GoToTab 9; SwitchToMode "Normal"; }}
+ bind "Tab" {{ ToggleTab; }}
+ }}
+ scroll {{
+ bind "e" {{ EditScrollback; SwitchToMode "Normal"; }}
+ bind "s" {{ SwitchToMode "EnterSearch"; SearchInput 0; }}
+ bind "Ctrl c" {{ ScrollToBottom; SwitchToMode "Normal"; }}
+ bind "j" "Down" {{ ScrollDown; }}
+ bind "k" "Up" {{ ScrollUp; }}
+ bind "Ctrl f" "PageDown" "Right" "l" {{ PageScrollDown; }}
+ bind "Ctrl b" "PageUp" "Left" "h" {{ PageScrollUp; }}
+ bind "d" {{ HalfPageScrollDown; }}
+ bind "u" {{ HalfPageScrollUp; }}
+ }}
+ search {{
+ bind "Ctrl c" {{ ScrollToBottom; SwitchToMode "Normal"; }}
+ bind "j" "Down" {{ ScrollDown; }}
+ bind "k" "Up" {{ ScrollUp; }}
+ bind "Ctrl f" "PageDown" "Right" "l" {{ PageScrollDown; }}
+ bind "Ctrl b" "PageUp" "Left" "h" {{ PageScrollUp; }}
+ bind "d" {{ HalfPageScrollDown; }}
+ bind "u" {{ HalfPageScrollUp; }}
+ bind "n" {{ Search "down"; }}
+ bind "p" {{ Search "up"; }}
+ bind "c" {{ SearchToggleOption "CaseSensitivity"; }}
+ bind "w" {{ SearchToggleOption "Wrap"; }}
+ bind "o" {{ SearchToggleOption "WholeWord"; }}
+ }}
+ entersearch {{
+ bind "Ctrl c" "Esc" {{ SwitchToMode "Scroll"; }}
+ bind "Enter" {{ SwitchToMode "Search"; }}
+ }}
+ renametab {{
+ bind "Ctrl c" {{ SwitchToMode "Normal"; }}
+ bind "Esc" {{ UndoRenameTab; SwitchToMode "Tab"; }}
+ }}
+ renamepane {{
+ bind "Ctrl c" {{ SwitchToMode "Normal"; }}
+ bind "Esc" {{ UndoRenamePane; SwitchToMode "Pane"; }}
+ }}
+ session {{
+ bind "d" {{ Detach; }}
+ bind "w" {{
+ LaunchOrFocusPlugin "session-manager" {{
+ floating true
+ move_to_focused_tab true
+ }};
+ SwitchToMode "Normal"
+ }}
+ bind "c" {{
+ LaunchOrFocusPlugin "configuration" {{
+ floating true
+ move_to_focused_tab true
+ }};
+ SwitchToMode "Normal"
+ }}
+ }}
+ tmux {{
+ bind "[" {{ SwitchToMode "Scroll"; }}
+ bind "\"" {{ NewPane "Down"; SwitchToMode "Normal"; }}
+ bind "%" {{ NewPane "Right"; SwitchToMode "Normal"; }}
+ bind "z" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
+ bind "c" {{ NewTab; SwitchToMode "Normal"; }}
+ bind "," {{ SwitchToMode "RenameTab"; }}
+ bind "p" {{ GoToPreviousTab; SwitchToMode "Normal"; }}
+ bind "n" {{ GoToNextTab; SwitchToMode "Normal"; }}
+ bind "Left" {{ MoveFocus "Left"; SwitchToMode "Normal"; }}
+ bind "Right" {{ MoveFocus "Right"; SwitchToMode "Normal"; }}
+ bind "Down" {{ MoveFocus "Down"; SwitchToMode "Normal"; }}
+ bind "Up" {{ MoveFocus "Up"; SwitchToMode "Normal"; }}
+ bind "h" {{ MoveFocus "Left"; SwitchToMode "Normal"; }}
+ bind "l" {{ MoveFocus "Right"; SwitchToMode "Normal"; }}
+ bind "j" {{ MoveFocus "Down"; SwitchToMode "Normal"; }}
+ bind "k" {{ MoveFocus "Up"; SwitchToMode "Normal"; }}
+ bind "o" {{ FocusNextPane; }}
+ bind "d" {{ Detach; }}
+ bind "Space" {{ NextSwapLayout; }}
+ bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
+ }}
+ shared_except "normal" "locked" {{
+ bind "Enter" "Esc" {{ SwitchToMode "Normal"; }}
+ }}
+}}
+"#
+ )
+}
+
+fn non_colliding_default_keybinds(primary_modifier: String, secondary_modifier: String) -> String {
+ format!(
+ r#"
+default_mode "normal"
+keybinds clear-defaults=true {{
+ normal {{}}
+ locked {{
+ bind "{primary_modifier} g" {{ SwitchToMode "Normal"; }}
+ }}
+ resize {{
+ bind "{primary_modifier} r" {{ SwitchToMode "Normal"; }}
+ bind "h" "Left" {{ Resize "Increase Left"; }}
+ bind "j" "Down" {{ Resize "Increase Down"; }}
+ bind "k" "Up" {{ Resize "Increase Up"; }}
+ bind "l" "Right" {{ Resize "Increase Right"; }}
+ bind "H" {{ Resize "Decrease Left"; }}
+ bind "J" {{ Resize "Decrease Down"; }}
+ bind "K" {{ Resize "Decrease Up"; }}
+ bind "L" {{ Resize "Decrease Right"; }}
+ bind "=" "+" {{ Resize "Increase"; }}
+ bind "-" {{ Resize "Decrease"; }}
+ }}
+ pane {{
+ bind "{primary_modifier} p" {{ SwitchToMode "Normal"; }}
+ bind "h" "Left" {{ MoveFocus "Left"; }}
+ bind "l" "Right" {{ MoveFocus "Right"; }}
+ bind "j" "Down" {{ MoveFocus "Down"; }}
+ bind "k" "Up" {{ MoveFocus "Up"; }}
+ bind "p" {{ SwitchFocus; }}
+ bind "n" {{ NewPane; SwitchToMode "Normal"; }}
+ bind "d" {{ NewPane "Down"; SwitchToMode "Normal"; }}
+ bind "r" {{ NewPane "Right"; SwitchToMode "Normal"; }}
+ bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
+ bind "f" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
+ bind "z" {{ TogglePaneFrames; SwitchToMode "Normal"; }}
+ bind "w" {{ ToggleFloatingPanes; SwitchToMode "Normal"; }}
+ bind "e" {{ TogglePaneEmbedOrFloating; SwitchToMode "Normal"; }}
+ bind "c" {{ SwitchToMode "RenamePane"; PaneNameInput 0;}}
+ }}
+ move {{
+ bind "{primary_modifier} m" {{ SwitchToMode "Normal"; }}
+ bind "n" "Tab" {{ MovePane; }}
+ bind "p" {{ MovePaneBackwards; }}
+ bind "h" "Left" {{ MovePane "Left"; }}
+ bind "j" "Down" {{ MovePane "Down"; }}
+ bind "k" "Up" {{ MovePane "Up"; }}
+ bind "l" "Right" {{ MovePane "Right"; }}
+ }}
+ tab {{
+ bind "{primary_modifier} t" {{ SwitchToMode "Normal"; }}
+ bind "r" {{ SwitchToMode "RenameTab"; TabNameInput 0; }}
+ bind "h" "Left" "Up" "k" {{ GoToPreviousTab; }}
+ bind "l" "Right" "Down" "j" {{ GoToNextTab; }}
+ bind "n" {{ NewTab; SwitchToMode "Normal"; }}
+ bind "x" {{ CloseTab; SwitchToMode "Normal"; }}
+ bind "s" {{ ToggleActiveSyncTab; SwitchToMode "Normal"; }}
+ bind "b" {{ BreakPane; SwitchToMode "Normal"; }}
+ bind "]" {{ BreakPaneRight; SwitchToMode "Normal"; }}
+ bind "[" {{ BreakPaneLeft; SwitchToMode "Normal"; }}
+ bind "1" {{ GoToTab 1; SwitchToMode "Normal"; }}
+ bind "2" {{ GoToTab 2; SwitchToMode "Normal"; }}
+ bind "3" {{ GoToTab 3; SwitchToMode "Normal"; }}
+ bind "4" {{ GoToTab 4; SwitchToMode "Normal"; }}
+ bind "5" {{ GoToTab 5; SwitchToMode "Normal"; }}
+ bind "6" {{ GoToTab 6; SwitchToMode "Normal"; }}
+ bind "7" {{ GoToTab 7; SwitchToMode "Normal"; }}
+ bind "8" {{ GoToTab 8; SwitchToMode "Normal"; }}
+ bind "9" {{ GoToTab 9; SwitchToMode "Normal"; }}
+ bind "Tab" {{ ToggleTab; }}
+ }}
+ scroll {{
+ bind "{primary_modifier} s" {{ SwitchToMode "Normal"; }}
+ bind "e" {{ EditScrollback; SwitchToMode "Normal"; }}
+ bind "s" {{ SwitchToMode "EnterSearch"; SearchInput 0; }}
+ bind "Ctrl c" {{ ScrollToBottom; SwitchToMode "Normal"; }}
+ bind "j" "Down" {{ ScrollDown; }}
+ bind "k" "Up" {{ ScrollUp; }}
+ bind "Ctrl f" "PageDown" "Right" "l" {{ PageScrollDown; }}
+ bind "Ctrl b" "PageUp" "Left" "h" {{ PageScrollUp; }}
+ bind "d" {{ HalfPageScrollDown; }}
+ bind "u" {{ HalfPageScrollUp; }}
+ }}
+ search {{
+ bind "{primary_modifier} s" {{ SwitchToMode "Normal"; }}
+ bind "Ctrl c" {{ ScrollToBottom; SwitchToMode "Normal"; }}
+ bind "j" "Down" {{ ScrollDown; }}
+ bind "k" "Up" {{ ScrollUp; }}
+ bind "Ctrl f" "PageDown" "Right" "l" {{ PageScrollDown; }}
+ bind "Ctrl b" "PageUp" "Left" "h" {{ PageScrollUp; }}
+ bind "d" {{ HalfPageScrollDown; }}
+ bind "u" {{ HalfPageScrollUp; }}
+ bind "n" {{ Search "down"; }}
+ bind "p" {{ Search "up"; }}
+ bind "c" {{ SearchToggleOption "CaseSensitivity"; }}
+ bind "w" {{ SearchToggleOption "Wrap"; }}
+ bind "o" {{ SearchToggleOption "WholeWord"; }}
+ }}
+ entersearch {{
+ bind "Ctrl c" "Esc" {{ SwitchToMode "Scroll"; }}
+ bind "Enter" {{ SwitchToMode "Search"; }}
+ }}
+ renametab {{
+ bind "Ctrl c" {{ SwitchToMode "Normal"; }}
+ bind "Esc" {{ UndoRenameTab; SwitchToMode "Tab"; }}
+ }}
+ renamepane {{
+ bind "Ctrl c" {{ SwitchToMode "Normal"; }}
+ bind "Esc" {{ UndoRenamePane; SwitchToMode "Pane"; }}
+ }}
+ session {{
+ bind "{primary_modifier} o" {{ SwitchToMode "Normal"; }}
+ bind "{primary_modifier} s" {{ SwitchToMode "Scroll"; }}
+ bind "d" {{ Detach; }}
+ bind "w" {{
+ LaunchOrFocusPlugin "session-manager" {{
+ floating true
+ move_to_focused_tab true
+ }};
+ SwitchToMode "Normal"
+ }}
+ bind "c" {{
+ LaunchOrFocusPlugin "configuration" {{
+ floating true
+ move_to_focused_tab true
+ }};
+ SwitchToMode "Normal"
+ }}
+ }}
+ tmux {{
+ bind "[" {{ SwitchToMode "Scroll"; }}
+ bind "{primary_modifier} b" {{ Write 2; SwitchToMode "Normal"; }}
+ bind "\"" {{ NewPane "Down"; SwitchToMode "Normal"; }}
+ bind "%" {{ NewPane "Right"; SwitchToMode "Normal"; }}
+ bind "z" {{ ToggleFocusFullscreen; SwitchToMode "Normal"; }}
+ bind "c" {{ NewTab; SwitchToMode "Normal"; }}
+ bind "," {{ SwitchToMode "RenameTab"; }}
+ bind "p" {{ GoToPreviousTab; SwitchToMode "Normal"; }}
+ bind "n" {{ GoToNextTab; SwitchToMode "Normal"; }}
+ bind "Left" {{ MoveFocus "Left"; SwitchToMode "Normal"; }}
+ bind "Right" {{ MoveFocus "Right"; SwitchToMode "Normal"; }}
+ bind "Down" {{ MoveFocus "Down"; SwitchToMode "Normal"; }}
+ bind "Up" {{ MoveFocus "Up"; SwitchToMode "Normal"; }}
+ bind "h" {{ MoveFocus "Left"; SwitchToMode "Normal"; }}
+ bind "l" {{ MoveFocus "Right"; SwitchToMode "Normal"; }}
+ bind "j" {{ MoveFocus "Down"; SwitchToMode "Normal"; }}
+ bind "k" {{ MoveFocus "Up"; SwitchToMode "Normal"; }}
+ bind "o" {{ FocusNextPane; }}
+ bind "d" {{ Detach; }}
+ bind "Space" {{ NextSwapLayout; }}
+ bind "x" {{ CloseFocus; SwitchToMode "Normal"; }}
+ }}
+ shared_except "locked" {{
+ bind "{primary_modifier} g" {{ SwitchToMode "Locked"; }}
+ bind "{primary_modifier} q" {{ Quit; }}
+ bind "{secondary_modifier} f" {{ ToggleFloatingPanes; }}
+ bind "{secondary_modifier} n" {{ NewPane; }}
+ bind "{secondary_modifier} i" {{ MoveTab "Left"; }}
+ bind "{secondary_modifier} o" {{ MoveTab "Right"; }}
+ bind "{secondary_modifier} h" "{secondary_modifier} Left" {{ MoveFocusOrTab "Left"; }}
+ bind "{secondary_modifier} l" "{secondary_modifier} Right" {{ MoveFocusOrTab "Right"; }}
+ bind "{secondary_modifier} j" "{secondary_modifier} Down" {{ MoveFocus "Down"; }}
+ bind "{secondary_modifier} k" "{secondary_modifier} Up" {{ MoveFocus "Up"; }}
+ bind "{secondary_modifier} =" "{secondary_modifier} +" {{ Resize "Increase"; }}
+ bind "{secondary_modifier} -" {{ Resize "Decrease"; }}
+ bind "{secondary_modifier} [" {{ PreviousSwapLayout; }}
+ bind "{secondary_modifier} ]" {{ NextSwapLayout; }}
+ }}
+ shared_except "normal" "locked" {{
+ bind "Enter" "Esc" {{ SwitchToMode "Normal"; }}
+ }}
+ shared_except "pane" "locked" {{
+ bind "{primary_modifier} p" {{ SwitchToMode "Pane"; }}
+ }}
+ shared_except "resize" "locked" {{
+ bind "{primary_modifier} r" {{ SwitchToMode "Resize"; }}
+ }}
+ shared_except "scroll" "locked" {{
+ bind "{primary_modifier} s" {{ SwitchToMode "Scroll"; }}
+ }}
+ shared_except "session" "locked" "tab" {{
+ bind "{primary_modifier} o" {{ SwitchToMode "Session"; }}
+ }}
+ shared_except "tab" "locked" {{
+ bind "{primary_modifier} t" {{ SwitchToMode "Tab"; }}
+ }}
+ shared_except "move" "locked" {{
+ bind "{primary_modifier} m" {{ SwitchToMode "Move"; }}
+ }}
+ shared_except "tmux" "locked" {{
+ bind "{primary_modifier} b" {{ SwitchToMode "Tmux"; }}
+ }}
+}}
+"#
+ )
+}
diff --git a/default-plugins/fixture-plugin-for-tests/src/main.rs b/default-plugins/fixture-plugin-for-tests/src/main.rs
index b6cac35b..918aba72 100644
--- a/default-plugins/fixture-plugin-for-tests/src/main.rs
+++ b/default-plugins/fixture-plugin-for-tests/src/main.rs
@@ -56,7 +56,7 @@ impl ZellijPlugin for State {
PermissionType::WebAccess,
PermissionType::ReadCliPipes,
PermissionType::MessageAndLaunchOtherPlugins,
- PermissionType::RebindKeys,
+ PermissionType::Reconfigure,
]);
self.configuration = configuration;
subscribe(&[
@@ -320,7 +320,7 @@ impl ZellijPlugin for State {
);
},
BareKey::Char('0') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
- rebind_keys(
+ reconfigure(
"
keybinds {
locked {
diff --git a/default-plugins/status-bar/src/one_line_ui.rs b/default-plugins/status-bar/src/one_line_ui.rs
index 87b1e2fd..7e3b8a4d 100644
--- a/default-plugins/status-bar/src/one_line_ui.rs
+++ b/default-plugins/status-bar/src/one_line_ui.rs
@@ -1265,6 +1265,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<KeyWithModifier
]} 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("Configure"), s("Config"), configuration_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, &[
@@ -1355,6 +1356,25 @@ fn session_manager_key(keymap: &[(KeyWithModifier, Vec<Action>)]) -> Vec<KeyWith
}
}
+fn configuration_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("configuration"))
+ .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();
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 da8650ae..b2401f43 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: 1050
+assertion_line: 1064
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
@@ -26,4 +26,4 @@ expression: last_snapshot
│ ││ │
│ ││ │
└────────────────────────────────────────────────┘└────────────────────────────────────────────────┘
- Ctrl + g  p  t  n  h  s  o  q  Alt +  <n> New Pane  <←↓↑→> Change Focus 
+ Ctrl + g  p  t  n  h  s  o  q  Alt +  <n> New  <←↓↑→> Focus  <f> Floating 
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 197fd571..623bfb73 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,6 +1,6 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 2349
+assertion_line: 2372
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1  Alt <[]>  VERTICAL 
@@ -26,4 +26,4 @@ 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 +  <n> New  <←↓↑→> Focus 
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 1f76fb4e..28bd7b3c 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -36,6 +36,7 @@ lazy_static::lazy_static! {
WorkspaceMember{crate_name: "default-plugins/tab-bar", build: true},
WorkspaceMember{crate_name: "default-plugins/fixture-plugin-for-tests", build: true},
WorkspaceMember{crate_name: "default-plugins/session-manager", build: true},
+ WorkspaceMember{crate_name: "default-plugins/configuration", build: true},
WorkspaceMember{crate_name: "zellij-utils", build: false},
WorkspaceMember{crate_name: "zellij-tile-utils", build: false},
WorkspaceMember{crate_name: "zellij-tile", build: false},
diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs
index 9a3231b3..fbde2f86 100644
--- a/zellij-server/src/lib.rs
+++ b/zellij-server/src/lib.rs
@@ -97,7 +97,7 @@ pub enum ServerInstruction {
DisconnectAllClientsExcept(ClientId),
ChangeMode(ClientId, InputMode),
ChangeModeForAllClients(InputMode),
- RebindKeys(ClientId, String), // String -> stringified keybindings
+ Reconfigure(ClientId, String), // String -> stringified configuration
}
impl From<&ServerInstruction> for ServerContext {
@@ -129,7 +129,7 @@ impl From<&ServerInstruction> for ServerContext {
ServerInstruction::ChangeModeForAllClients(..) => {
ServerContext::ChangeModeForAllClients
},
- ServerInstruction::RebindKeys(..) => ServerContext::RebindKeys,
+ ServerInstruction::Reconfigure(..) => ServerContext::Reconfigure,
}
}
}
@@ -149,7 +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,
+ pub default_mode: HashMap<ClientId, InputMode>,
screen_thread: Option<thread::JoinHandle<()>>,
pty_thread: Option<thread::JoinHandle<()>>,
plugin_thread: Option<thread::JoinHandle<()>>,
@@ -570,6 +570,10 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
default_mode,
&attrs,
session_data.capabilities,
+ session_data
+ .client_keybinds
+ .get(&client_id)
+ .unwrap_or(&session_data.client_attributes.keybinds),
Some(default_mode),
);
session_data
@@ -911,24 +915,57 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
.unwrap()
.change_mode_for_all_clients(input_mode);
},
- ServerInstruction::RebindKeys(client_id, new_keybinds) => {
+ ServerInstruction::Reconfigure(client_id, new_config) => {
+ let mut new_default_mode = None;
+ match Options::from_string(&new_config) {
+ Ok(mut new_config_options) => {
+ if let Some(default_mode) = new_config_options.default_mode.take() {
+ new_default_mode = Some(default_mode);
+ session_data
+ .write()
+ .unwrap()
+ .as_mut()
+ .unwrap()
+ .default_mode
+ .insert(client_id, default_mode);
+ }
+ },
+ Err(e) => {
+ log::error!("Failed to parse config: {}", e);
+ },
+ }
+
let new_keybinds = session_data
.write()
.unwrap()
.as_mut()
.unwrap()
- .rebind_keys(client_id, new_keybinds)
+ .rebind_keys(client_id, new_config)
.clone();
- if let Some(new_keybinds) = new_keybinds {
- session_data
- .write()
- .unwrap()
- .as_ref()
- .unwrap()
- .senders
- .send_to_screen(ScreenInstruction::RebindKeys(new_keybinds, client_id))
- .unwrap();
- }
+ session_data
+ .write()
+ .unwrap()
+ .as_ref()
+ .unwrap()
+ .senders
+ .send_to_screen(ScreenInstruction::Reconfigure {
+ client_id,
+ keybinds: new_keybinds.clone(),
+ default_mode: new_default_mode,
+ })
+ .unwrap();
+ session_data
+ .write()
+ .unwrap()
+ .as_ref()
+ .unwrap()
+ .senders
+ .send_to_plugin(PluginInstruction::Reconfigure {
+ client_id,
+ keybinds: new_keybinds,
+ default_mode: new_default_mode,
+ })
+ .unwrap();
},
}
}
@@ -1167,7 +1204,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,
+ default_mode: HashMap::new(),
}
}
diff --git a/zellij-server/src/plugins/mod.rs b/zellij-server/src/plugins/mod.rs
index 639a389e..223ecb55 100644
--- a/zellij-server/src/plugins/mod.rs
+++ b/zellij-server/src/plugins/mod.rs
@@ -31,6 +31,7 @@ use zellij_utils::{
errors::{prelude::*, ContextType, PluginContext},
input::{
command::TerminalAction,
+ keybinds::Keybinds,
layout::{FloatingPaneLayout, Layout, Run, RunPlugin, RunPluginOrAlias, TiledPaneLayout},
plugins::PluginAliases,
},
@@ -142,6 +143,11 @@ pub enum PluginInstruction {
message: MessageToPlugin,
},
UnblockCliPipes(Vec<PluginRenderAsset>),
+ Reconfigure {
+ client_id: ClientId,
+ keybinds: Option<Keybinds>,
+ default_mode: Option<InputMode>,
+ },
WatchFilesystem,
Exit,
}
@@ -182,6 +188,7 @@ impl From<&PluginInstruction> for PluginContext {
PluginInstruction::WatchFilesystem => PluginContext::WatchFilesystem,
PluginInstruction::KeybindPipe { .. } => PluginContext::KeybindPipe,
PluginInstruction::DumpLayoutToPlugin(..) => PluginContext::DumpLayoutToPlugin,
+ PluginInstruction::Reconfigure { .. } => PluginContext::Reconfigure,
}
}
}
@@ -734,6 +741,15 @@ pub(crate) fn plugin_thread_main(
.context("failed to unblock input pipe");
}
},
+ PluginInstruction::Reconfigure {
+ client_id,
+ keybinds,
+ default_mode,
+ } => {
+ wasm_bridge
+ .reconfigure(client_id, keybinds, default_mode)
+ .non_fatal();
+ },
PluginInstruction::WatchFilesystem => {
wasm_bridge.start_fs_watcher_if_not_started();
},
diff --git a/zellij-server/src/plugins/plugin_loader.rs b/zellij-server/src/plugins/plugin_loader.rs
index 6ed36c05..51ca3e16 100644
--- a/zellij-server/src/plugins/plugin_loader.rs
+++ b/zellij-server/src/plugins/plugin_loader.rs
@@ -29,6 +29,7 @@ use zellij_utils::{
data::{InputMode, PluginCapabilities},
errors::prelude::*,
input::command::TerminalAction,
+ input::keybinds::Keybinds,
input::layout::Layout,
input::plugins::PluginConfig,
ipc::ClientAttributes,
@@ -69,6 +70,7 @@ pub struct PluginLoader<'a> {
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
default_mode: InputMode,
+ keybinds: Option<Keybinds>,
}
impl<'a> PluginLoader<'a> {
@@ -89,6 +91,7 @@ impl<'a> PluginLoader<'a> {
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
default_mode: InputMode,
+ keybinds: &HashMap<ClientId, Keybinds>,
) -> Result<()> {
let err_context = || format!("failed to reload plugin {plugin_id} from memory");
let mut connected_clients: Vec<ClientId> =
@@ -97,6 +100,7 @@ impl<'a> PluginLoader<'a> {
return Err(anyhow!("No connected clients, cannot reload plugin"));
}
let first_client_id = connected_clients.remove(0);
+ let keybinds = keybinds.get(&first_client_id).cloned();
let mut plugin_loader = PluginLoader::new_from_existing_plugin_attributes(
&plugin_cache,
@@ -115,6 +119,7 @@ impl<'a> PluginLoader<'a> {
default_layout,
layout_dir,
default_mode,
+ keybinds,
)?;
plugin_loader
.load_module_from_memory()
@@ -152,6 +157,7 @@ impl<'a> PluginLoader<'a> {
skip_cache: bool,
layout_dir: Option<PathBuf>,
default_mode: InputMode,
+ keybinds: Option<Keybinds>,
) -> Result<()> {
let err_context = || format!("failed to start plugin {plugin_id} for client {client_id}");
let mut plugin_loader = PluginLoader::new(
@@ -173,6 +179,7 @@ impl<'a> PluginLoader<'a> {
default_layout,
layout_dir,
default_mode,
+ keybinds,
)?;
if skip_cache {
plugin_loader
@@ -226,6 +233,7 @@ impl<'a> PluginLoader<'a> {
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
default_mode: InputMode,
+ keybinds: Option<Keybinds>,
) -> Result<()> {
let mut new_plugins = HashSet::new();
for plugin_id in plugin_map.lock().unwrap().plugin_ids() {
@@ -249,6 +257,7 @@ impl<'a> PluginLoader<'a> {
default_layout.clone(),
layout_dir.clone(),
default_mode,
+ keybinds.clone(),
)?;
plugin_loader
.load_module_from_memory()
@@ -278,6 +287,7 @@ impl<'a> PluginLoader<'a> {
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
default_mode: InputMode,
+ keybinds: &HashMap<ClientId, Keybinds>,
) -> Result<()> {
let err_context = || format!("failed to reload plugin id {plugin_id}");
@@ -287,6 +297,7 @@ impl<'a> PluginLoader<'a> {
return Err(anyhow!("No connected clients, cannot reload plugin"));
}
let first_client_id = connected_clients.remove(0);
+ let keybinds = keybinds.get(&first_client_id).cloned();
let mut plugin_loader = PluginLoader::new_from_existing_plugin_attributes(
&plugin_cache,
@@ -305,6 +316,7 @@ impl<'a> PluginLoader<'a> {
default_layout,
layout_dir,
default_mode,
+ keybinds,
)?;
plugin_loader
.compile_module()
@@ -338,6 +350,7 @@ impl<'a> PluginLoader<'a> {
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
default_mode: InputMode,
+ keybinds: Option<Keybinds>,
) -> Result<Self> {
let plugin_own_data_dir = ZELLIJ_SESSION_CACHE_DIR
.join(Url::from(&plugin.location).to_string())
@@ -366,6 +379,7 @@ impl<'a> PluginLoader<'a> {
default_layout,
layout_dir,
default_mode,
+ keybinds,
})
}
pub fn new_from_existing_plugin_attributes(
@@ -385,6 +399,7 @@ impl<'a> PluginLoader<'a> {
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
default_mode: InputMode,
+ keybinds: Option<Keybinds>,
) -> Result<Self> {
let err_context = || "Failed to find existing plugin";
let (running_plugin, _subscriptions, _workers) = {
@@ -420,6 +435,7 @@ impl<'a> PluginLoader<'a> {
default_layout,
layout_dir,
default_mode,
+ keybinds,
)
}
pub fn new_from_different_client_id(
@@ -439,6 +455,7 @@ impl<'a> PluginLoader<'a> {
default_layout: Box<Layout>,
layout_dir: Option<PathBuf>,
default_mode: InputMode,
+ keybinds: Option<Keybinds>,
) -> Result<Self> {
let err_context = || "Failed to find existing plugin";
let running_plugin = {
@@ -475,6 +492,7 @@ impl<'a> PluginLoader<'a> {
default_layout,
layout_dir,
default_mode,
+ keybinds,
)
}
pub fn load_module_from_memory(&mut self) -> Result<Module> {
@@ -731,6 +749,7 @@ impl<'a> PluginLoader<'a> {
self.default_layout.clone(),
self.layout_dir.clone(),
self.default_mode,
+ self.keybinds.clone(),
)?;
plugin_loader_for_client
.load_module_from_memory()
@@ -832,6 +851,11 @@ impl<'a> PluginLoader<'a> {
layout_dir: self.layout_dir.clone(),
default_mode: self.default_mode.clone(),
subscriptions: Arc::new(Mutex::new(HashSet::new())),
+ keybinds: self
+ .keybinds
+ .as_ref()
+ .unwrap_or_else(|| &self.client_attributes.keybinds)
+ .clone(),
stdin_pipe,
stdout_pipe,
};
diff --git a/zellij-server/src/plugins/plugin_map.rs b/zellij-server/src/plugins/plugin_map.rs
index c5ac6d5f..6390841a 100644
--- a/zellij-server/src/plugins/plugin_map.rs
+++ b/zellij-server/src/plugins/plugin_map.rs
@@ -22,6 +22,7 @@ use zellij_utils::{
data::InputMode,
data::PluginCapabilities,
input::command::TerminalAction,
+ input::keybinds::Keybinds,
input::layout::{Layout, PluginUserConfiguration, RunPlugin, RunPluginLocation},
input::plugins::PluginConfig,
ipc::ClientAttributes,
@@ -289,6 +290,7 @@ pub struct PluginEnv {
pub subscriptions: Arc<Mutex<Subscriptions>>,
pub stdin_pipe: Arc<Mutex<VecDeque<u8>>>,
pub stdout_pipe: Arc<Mutex<VecDeque<u8>>>,
+ pub keybinds: Keybinds,
}
#[derive(Clone)]
@@ -424,4 +426,10 @@ impl RunningPlugin {
false
}
}
+ pub fn update_keybinds(&mut self, keybinds: Keybinds) {
+ self.store.data_mut().keybinds = keybinds;
+ }
+ pub fn update_default_mode(&mut self, default_mode: InputMode) {
+ self.store.data_mut().default_mode = default_mode;
+ }
}
diff --git a/zellij-server/src/plugins/unit/plugin_tests.rs b/zellij-server/src/plugins/unit/plugin_tests.rs
index c1f4fcd5..5ef220db 100644
--- a/zellij-server/src/plugins/unit/plugin_tests.rs
+++ b/zellij-server/src/plugins/unit/plugin_tests.rs
@@ -6492,7 +6492,7 @@ pub fn disconnect_other_clients_plugins_command() {
#[test]
#[ignore]
-pub fn rebind_keys_plugin_command() {
+pub fn reconfigure_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
@@ -6527,7 +6527,7 @@ pub fn rebind_keys_plugin_command() {
let received_server_instruction = Arc::new(Mutex::new(vec![]));
let server_thread = log_actions_in_thread!(
received_server_instruction,
- ServerInstruction::RebindKeys,
+ ServerInstruction::Reconfigure,
server_receiver,
1
);
@@ -6555,20 +6555,20 @@ pub fn rebind_keys_plugin_command() {
std::thread::sleep(std::time::Duration::from_millis(500));
teardown();
server_thread.join().unwrap(); // this might take a while if the cache is cold
- let rebind_keys_event = received_server_instruction
+ let reconfigure_event = received_server_instruction
.lock()
.unwrap()
.iter()
.rev()
.find_map(|i| {
- if let ServerInstruction::RebindKeys(..) = i {
+ if let ServerInstruction::Reconfigure(..) = i {
Some(i.clone())
} else {
None
}
})
.clone();
- assert_snapshot!(format!("{:#?}", rebind_keys_event));
+ assert_snapshot!(format!("{:#?}", reconfigure_event));
}
#[test]
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap
index 465f1088..28c7dedc 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap
@@ -1,6 +1,6 @@
---
source: zellij-server/src/plugins/./unit/plugin_tests.rs
-assertion_line: 5500
+assertion_line: 5505
expression: "format!(\"{:#?}\", permissions)"
---
Some(
@@ -14,6 +14,6 @@ Some(
WebAccess,
ReadCliPipes,
MessageAndLaunchOtherPlugins,
- RebindKeys,
+ Reconfigure,
],
)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__reconfigure_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__reconfigure_plugin_command.snap
new file mode 100644
index 00000000..3f788d7d
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__reconfigure_plugin_command.snap
@@ -0,0 +1,11 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 6571
+expression: "format!(\"{:#?}\", reconfigure_event)"
+---
+Some(
+ Reconfigure(
+ 1,
+ "\n keybinds {\n locked {\n bind \"a\" { NewTab; }\n }\n }\n ",
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap
index a0fec410..75f47da7 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap
@@ -1,6 +1,6 @@
---
source: zellij-server/src/plugins/./unit/plugin_tests.rs
-assertion_line: 5409
+assertion_line: 5414
expression: "format!(\"{:#?}\", new_tab_event)"
---
Some(
@@ -16,6 +16,6 @@ Some(
WebAccess,
ReadCliPipes,
MessageAndLaunchOtherPlugins,
- RebindKeys,
+ Reconfigure,
],
)
diff --git a/zellij-server/src/plugins/wasm_bridge.rs b/zellij-server/src/plugins/wasm_bridge.rs
index 2abc22d3..00a7795b 100644
--- a/zellij-server/src/plugins/wasm_bridge.rs
+++ b/zellij-server/src/plugins/wasm_bridge.rs
@@ -21,6 +21,7 @@ use zellij_utils::async_std::task::{self, JoinHandle};
use zellij_utils::consts::ZELLIJ_CACHE_DIR;
use zellij_utils::data::{InputMode, PermissionStatus, PermissionType, PipeMessage, PipeSource};
use zellij_utils::downloader::Downloader;
+use zellij_utils::input::keybinds::Keybinds;
use zellij_utils::input::permission::PermissionCache;
use zellij_utils::notify_debouncer_full::{notify::RecommendedWatcher, Debouncer, FileIdMap};
use zellij_utils::plugin_api::event::ProtobufEvent;
@@ -103,6 +104,7 @@ pub struct WasmBridge {
pending_pipes: PendingPipes,
layout_dir: Option<PathBuf>,
default_mode: InputMode,
+ keybinds: HashMap<ClientId, Keybinds>,
}
impl WasmBridge {
@@ -149,6 +151,7 @@ impl WasmBridge {
pending_pipes: Default::default(),
layout_dir,
default_mode,
+ keybinds: HashMap::new(),
}
}
pub fn load_plugin(
@@ -206,6 +209,7 @@ impl WasmBridge {
let default_layout = self.default_layout.clone();
let layout_dir = self.layout_dir.clone();
let default_mode = self.default_mode;
+ let keybinds = self.keybinds.get(&client_id).cloned();
async move {
let _ = senders.send_to_background_jobs(
BackgroundJob::AnimatePluginLoading(plugin_id),
@@ -254,6 +258,7 @@ impl WasmBridge {
skip_cache,
layout_dir,
default_mode,
+ keybinds,
) {
Ok(_) => handle_plugin_successful_loading(&senders, plugin_id),
Err(e) => handle_plugin_loading_failure(
@@ -346,6 +351,7 @@ impl WasmBridge {
let default_layout = self.default_layout.clone();
let layout_dir = self.layout_dir.clone();
let default_mode = self.default_mode;
+ let keybinds = self.keybinds.clone();
async move {
match PluginLoader::reload_plugin(
first_plugin_id,
@@ -364,6 +370,7 @@ impl WasmBridge {
default_layout.clone(),
layout_dir.clone(),
default_mode,
+ &keybinds,
) {
Ok(_) => {
handle_plugin_successful_loading(&senders, first_plugin_id);
@@ -390,6 +397,7 @@ impl WasmBridge {
default_layout.clone(),
layout_dir.clone(),
default_mode,
+ &keybinds,
) {
Ok(_) => handle_plugin_successful_loading(&senders, *plugin_id),
Err(e) => handle_plugin_loading_failure(
@@ -443,6 +451,7 @@ impl WasmBridge {
self.default_layout.clone(),
self.layout_dir.clone(),
self.default_mode,
+ self.keybinds.get(&client_id).cloned(),
) {
Ok(_) => {
let _ = self
@@ -795,6 +804,51 @@ impl WasmBridge {
.unwrap()
.run_plugin_of_plugin_id(plugin_id)
}
+
+ pub fn reconfigure(
+ &mut self,
+ client_id: ClientId,
+ keybinds: Option<Keybinds>,
+ default_mode: Option<InputMode>,
+ ) -> Result<()> {
+ let plugins_to_reconfigure: Vec<Arc<Mutex<RunningPlugin>>> = self
+ .plugin_map
+ .lock()
+ .unwrap()
+ .running_plugins()
+ .iter()
+ .cloned()
+ .filter_map(|(_plugin_id, c_id, running_plugin)| {
+ if c_id == client_id {
+ Some(running_plugin.clone())
+ } else {
+ None
+ }
+ })
+ .collect();
+ if let Some(default_mode) = default_mode.as_ref() {
+ self.default_mode = *default_mode;
+ }
+ if let Some(keybinds) = keybinds.as_ref() {
+ self.keybinds.insert(client_id, keybinds.clone());
+ }
+ for running_plugin in plugins_to_reconfigure {
+ task::spawn({
+ let running_plugin = running_plugin.clone();
+ let keybinds = keybinds.clone();
+ async move {
+ let mut running_plugin = running_plugin.lock().unwrap();
+ if let Some(keybinds) = keybinds {
+ running_plugin.update_keybinds(keybinds);
+ }
+ if let Some(default_mode) = default_mode {
+ running_plugin.update_default_mode(default_mode);
+ }
+ }
+ });
+ }
+ Ok(())
+ }
fn apply_cached_events_and_resizes_for_plugin(
&mut self,
plugin_id: PluginId,
@@ -1288,6 +1342,18 @@ pub fn apply_event_to_plugin(
let err_context = || format!("Failed to apply event to plugin {plugin_id}");
match check_event_permission(running_plugin.store.data(), event) {
(PermissionStatus::Granted, _) => {
+ let mut event = event.clone();
+ if let Event::ModeUpdate(mode_info) = &mut event {
+ // we do this because there can be some cases where this event arrives here with
+ // the wrong keybindings or default mode (for example: when triggered from the CLI,
+ // where we do not know the target client_id and thus don't know if their keybindings are the
+ // default or if they have changed at runtime), the keybindings in running_plugin
+ // should always be up-to-date. Ideally, we would have changed the keybindings in
+ // ModeInfo to an Option, but alas - this is already part of our contract and that
+ // would be a breaking change.
+ mode_info.keybinds = running_plugin.store.data().keybinds.to_keybinds_vec();
+ mode_info.base_mode = Some(running_plugin.store.data().default_mode);
+ }
let protobuf_event: ProtobufEvent = event
.clone()
.try_into()
diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs
index a517df06..21b8b01d 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.keybinds.clone(),
$env.default_mode.clone(),
) {
log::error!("{}: {:?}", $error_message(), e);
@@ -244,7 +245,7 @@ fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) {
PluginCommand::WatchFilesystem => watch_filesystem(env),
PluginCommand::DumpSessionLayout => dump_session_layout(env),
PluginCommand::CloseSelf => close_self(env),
- PluginCommand::RebindKeys(new_keybinds) => rebind_keys(env, new_keybinds)?,
+ PluginCommand::Reconfigure(new_config) => reconfigure(env, new_config)?,
},
(PermissionStatus::Denied, permission) => {
log::error!(
@@ -781,11 +782,11 @@ fn close_self(env: &PluginEnv) {
.non_fatal();
}
-fn rebind_keys(env: &PluginEnv, new_keybinds: String) -> Result<()> {
- let err_context = || "Failed to rebind keys";
+fn reconfigure(env: &PluginEnv, new_config: String) -> Result<()> {
+ let err_context = || "Failed to reconfigure";
let client_id = env.client_id;
env.senders
- .send_to_server(ServerInstruction::RebindKeys(client_id, new_keybinds))
+ .send_to_server(ServerInstruction::Reconfigure(client_id, new_config))
.with_context(err_context)?;
Ok(())
}
@@ -1446,7 +1447,7 @@ fn check_command_permission(
| PluginCommand::CliPipeOutput(..) => PermissionType::ReadCliPipes,
PluginCommand::MessageToPlugin(..) => PermissionType::MessageAndLaunchOtherPlugins,
PluginCommand::DumpSessionLayout => PermissionType::ReadApplicationState,
- PluginCommand::RebindKeys(..) => PermissionType::RebindKeys,
+ PluginCommand::Reconfigure(..) => PermissionType::Reconfigure,
_ => return (PermissionStatus::Granted, None),
};
diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs
index 3092310a..ab5db049 100644
--- a/zellij-server/src/route.rs
+++ b/zellij-server/src/route.rs
@@ -19,6 +19,7 @@ use zellij_utils::{
actions::{Action, SearchDirection, SearchOption},
command::TerminalAction,
get_mode_info,
+ keybinds::Keybinds,
layout::Layout,
},
ipc::{
@@ -38,6 +39,7 @@ pub(crate) fn route_action(
default_shell: Option<TerminalAction>,
default_layout: Box<Layout>,
mut seen_cli_pipes: Option<&mut HashSet<String>>,
+ client_keybinds: Keybinds,
default_mode: InputMode,
) -> Result<bool> {
let mut should_break = false;
@@ -99,7 +101,13 @@ pub(crate) fn route_action(
.send_to_plugin(PluginInstruction::Update(vec![(
None,
Some(client_id),
- Event::ModeUpdate(get_mode_info(mode, attrs, capabilities, Some(default_mode))),
+ Event::ModeUpdate(get_mode_info(
+ mode,
+ attrs,
+ capabilities,
+ &client_keybinds,
+ Some(default_mode),
+ )),
)]))
.with_context(err_context)?;
senders
@@ -107,7 +115,13 @@ pub(crate) fn route_action(
.with_context(err_context)?;
senders
.send_to_screen(ScreenInstruction::ChangeMode(
- get_mode_info(mode, attrs, capabilities, Some(default_mode)),
+ get_mode_info(
+ mode,
+ attrs,
+ capabilities,
+ &client_keybinds,
+ Some(default_mode),
+ ),
client_id,
))
.with_context(err_context)?;
@@ -349,6 +363,7 @@ pub(crate) fn route_action(
input_mode,
attrs,
capabilities,
+ &client_keybinds,
Some(default_mode),
)),
)]))
@@ -363,6 +378,7 @@ pub(crate) fn route_action(
input_mode,
attrs,
capabilities,
+ &client_keybinds,
Some(default_mode),
)))
.with_context(err_context)?;
@@ -1025,7 +1041,20 @@ pub(crate) fn route_thread_main(
rlocked_sessions.default_shell.clone(),
rlocked_sessions.layout.clone(),
Some(&mut seen_cli_pipes),
- rlocked_sessions.default_mode,
+ rlocked_sessions
+ .client_keybinds
+ .get(&client_id)
+ .unwrap_or(
+ &rlocked_sessions
+ .client_attributes
+ .keybinds,
+ )
+ .clone(),
+ rlocked_sessions
+ .default_mode
+ .get(&client_id)
+ .unwrap_or(&InputMode::Normal)
+ .clone(),
)? {
should_break = true;
}
@@ -1050,7 +1079,16 @@ pub(crate) fn route_thread_main(
rlocked_sessions.default_shell.clone(),
rlocked_sessions.layout.clone(),
Some(&mut seen_cli_pipes),
- rlocked_sessions.default_mode,
+ rlocked_sessions
+ .client_keybinds
+ .get(&client_id)
+ .unwrap_or(&rlocked_sessions.client_attributes.keybinds)
+ .clone(),
+ rlocked_sessions
+ .default_mode
+ .get(&client_id)
+ .unwrap_or(&InputMode::Normal)
+ .clone(),
)? {
should_break = true;
}
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index e61b5d19..189c2571 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -361,7 +361,11 @@ pub enum ScreenInstruction {
DumpLayoutToHd,
RenameSession(String, ClientId), // String -> new name
ListClientsMetadata(Option<PathBuf>, ClientId), // Option<PathBuf> - default shell
- RebindKeys(Keybinds, ClientId),
+ Reconfigure {
+ client_id: ClientId,
+ keybinds: Option<Keybinds>,
+ default_mode: Option<InputMode>,
+ },
}
impl From<&ScreenInstruction> for ScreenContext {
@@ -546,7 +550,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::DumpLayoutToHd => ScreenContext::DumpLayoutToHd,
ScreenInstruction::RenameSession(..) => ScreenContext::RenameSession,
ScreenInstruction::ListClientsMetadata(..) => ScreenContext::ListClientsMetadata,
- ScreenInstruction::RebindKeys(..) => ScreenContext::RebindKeys,
+ ScreenInstruction::Reconfigure { .. } => ScreenContext::Reconfigure,
}
}
}
@@ -1214,7 +1218,7 @@ impl Screen {
let tab_name = tab_name.unwrap_or_else(|| String::new());
let position = self.tabs.len();
- let tab = Tab::new(
+ let mut tab = Tab::new(
tab_index,
position,
tab_name,
@@ -1245,6 +1249,9 @@ impl Screen {
self.styled_underlines,
self.explicitly_disable_kitty_keyboard_protocol,
);
+ for (client_id, mode_info) in &self.mode_info {
+ tab.change_mode_info(mode_info.clone(), *client_id);
+ }
self.tabs.insert(tab_index, tab);
Ok(())
}
@@ -2151,17 +2158,30 @@ impl Screen {
}
Ok(())
}
- pub fn rebind_keys(&mut self, new_keybinds: Keybinds, client_id: ClientId) -> Result<()> {
+ pub fn reconfigure_mode_info(
+ &mut self,
+ new_keybinds: Option<Keybinds>,
+ new_default_mode: Option<InputMode>,
+ client_id: ClientId,
+ ) -> Result<()> {
if self.connected_clients_contains(&client_id) {
+ let should_update_mode_info = new_keybinds.is_some() || new_default_mode.is_some();
let mode_info = self
.mode_info
.entry(client_id)
.or_insert_with(|| self.default_mode_info.clone());
- mode_info.update_keybinds(new_keybinds);
- for tab in self.tabs.values_mut() {
- tab.change_mode_info(mode_info.clone(), client_id);
- tab.mark_active_pane_for_rerender(client_id);
- tab.update_input_modes()?;
+ if let Some(new_keybinds) = new_keybinds {
+ mode_info.update_keybinds(new_keybinds);
+ }
+ if let Some(new_default_mode) = new_default_mode {
+ mode_info.update_default_mode(new_default_mode);
+ }
+ if should_update_mode_info {
+ for tab in self.tabs.values_mut() {
+ tab.change_mode_info(mode_info.clone(), client_id);
+ tab.mark_active_pane_for_rerender(client_id);
+ tab.update_input_modes()?;
+ }
}
} else {
log::error!("Could not find client_id {client_id} to rebind keys");
@@ -2363,6 +2383,7 @@ pub(crate) fn screen_thread_main(
// ¯\_(ツ)_/¯
arrow_fonts: !arrow_fonts,
},
+ &client_attributes.keybinds,
config_options.default_mode,
),
draw_pane_frames,
@@ -4028,8 +4049,14 @@ pub(crate) fn screen_thread_main(
}
screen.unblock_input()?;
},
- ScreenInstruction::RebindKeys(new_keybinds, client_id) => {
- screen.rebind_keys(new_keybinds, client_id).non_fatal();
+ ScreenInstruction::Reconfigure {
+ client_id,
+ keybinds,
+ default_mode,
+ } => {
+ screen
+ .reconfigure_mode_info(keybinds, default_mode, client_id)
+ .non_fatal();
},
}
}
diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs
index 5f7f0a66..09d2d794 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 client_keybinds = session_metadata.client_keybinds.clone();
let default_mode = session_metadata.default_mode.clone();
let capabilities = PluginCapabilities::default();
let client_attributes = ClientAttributes::default();
@@ -130,7 +131,14 @@ fn send_cli_action_to_server(
default_shell.clone(),
default_layout.clone(),
None,
- default_mode,
+ client_keybinds
+ .get(&client_id)
+ .unwrap_or(&session_metadata.client_attributes.keybinds)
+ .clone(),
+ default_mode
+ .get(&client_id)
+ .unwrap_or(&InputMode::Normal)
+ .clone(),
)
.unwrap();
}
@@ -578,7 +586,7 @@ impl MockScreen {
layout,
client_input_modes: HashMap::new(),
client_keybinds: HashMap::new(),
- default_mode: InputMode::Normal,
+ default_mode: HashMap::new(),
};
let os_input = FakeInputOutput::default();
diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs
index 01049b37..d46b904a 100644
--- a/zellij-tile/src/shim.rs
+++ b/zellij-tile/src/shim.rs
@@ -809,8 +809,8 @@ pub fn dump_session_layout() {
}
/// Rebind keys for the current user
-pub fn rebind_keys(keys: String) {
- let plugin_command = PluginCommand::RebindKeys(keys);
+pub fn reconfigure(new_config: String) {
+ let plugin_command = PluginCommand::Reconfigure(new_config);
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
object_to_stdout(&protobuf_plugin_command.encode_to_vec());
unsafe { host_run_plugin_command() };
diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl
index e830cd09..95f42fe4 100644
--- a/zellij-utils/assets/config/default.kdl
+++ b/zellij-utils/assets/config/default.kdl
@@ -120,6 +120,13 @@ keybinds {
};
SwitchToMode "Normal"
}
+ bind "c" {
+ LaunchOrFocusPlugin "configuration" {
+ floating true
+ move_to_focused_tab true
+ };
+ SwitchToMode "Normal"
+ }
}
tmux {
bind "[" { SwitchToMode "Scroll"; }
@@ -147,6 +154,7 @@ keybinds {
shared_except "locked" {
bind "Ctrl g" { SwitchToMode "Locked"; }
bind "Ctrl q" { Quit; }
+ bind "Alt f" { ToggleFloatingPanes; }
bind "Alt n" { NewPane; }
bind "Alt i" { MoveTab "Left"; }
bind "Alt o" { MoveTab "Right"; }
@@ -197,6 +205,7 @@ plugins {
filepicker location="zellij:strider" {
cwd "/"
}
+ configuration location="zellij:configuration"
}
// Choose what to do when zellij receives SIGTERM, SIGINT, SIGQUIT or SIGHUP
diff --git a/zellij-utils/assets/plugins/configuration.wasm b/zellij-utils/assets/plugins/configuration.wasm
new file mode 100755
index 00000000..581dbfb1
--- /dev/null
+++ b/zellij-utils/assets/plugins/configuration.wasm
Binary files differ
diff --git a/zellij-utils/assets/prost/api.plugin_command.rs b/zellij-utils/assets/prost/api.plugin_command.rs
index 393f2e0c..1e57602e 100644
--- a/zellij-utils/assets/prost/api.plugin_command.rs
+++ b/zellij-utils/assets/prost/api.plugin_command.rs
@@ -119,7 +119,7 @@ pub mod plugin_command {
#[prost(message, tag = "62")]
NewTabsWithLayoutInfoPayload(super::NewTabsWithLayoutInfoPayload),
#[prost(string, tag = "63")]
- RebindKeysPayload(::prost::alloc::string::String),
+ ReconfigurePayload(::prost::alloc::string::String),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
@@ -433,7 +433,7 @@ pub enum CommandName {
DumpSessionLayout = 84,
CloseSelf = 85,
NewTabsWithLayoutInfo = 86,
- RebindKeys = 87,
+ Reconfigure = 87,
}
impl CommandName {
/// String value of the enum field names used in the ProtoBuf definition.
@@ -529,7 +529,7 @@ impl CommandName {
CommandName::DumpSessionLayout => "DumpSessionLayout",
CommandName::CloseSelf => "CloseSelf",
CommandName::NewTabsWithLayoutInfo => "NewTabsWithLayoutInfo",
- CommandName::RebindKeys => "RebindKeys",
+ CommandName::Reconfigure => "Reconfigure",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
@@ -622,7 +622,7 @@ impl CommandName {
"DumpSessionLayout" => Some(Self::DumpSessionLayout),
"CloseSelf" => Some(Self::CloseSelf),
"NewTabsWithLayoutInfo" => Some(Self::NewTabsWithLayoutInfo),
- "RebindKeys" => Some(Self::RebindKeys),
+ "Reconfigure" => Some(Self::Reconfigure),
_ => None,
}
}
diff --git a/zellij-utils/assets/prost/api.plugin_permission.rs b/zellij-utils/assets/prost/api.plugin_permission.rs
index 50bc853f..013732d9 100644
--- a/zellij-utils/assets/prost/api.plugin_permission.rs
+++ b/zellij-utils/assets/prost/api.plugin_permission.rs
@@ -10,7 +10,7 @@ pub enum PermissionType {
WebAccess = 6,
ReadCliPipes = 7,
MessageAndLaunchOtherPlugins = 8,
- RebindKeys = 9,
+ Reconfigure = 9,
}
impl PermissionType {
/// String value of the enum field names used in the ProtoBuf definition.
@@ -30,7 +30,7 @@ impl PermissionType {
PermissionType::MessageAndLaunchOtherPlugins => {
"MessageAndLaunchOtherPlugins"
}
- PermissionType::RebindKeys => "RebindKeys",
+ PermissionType::Reconfigure => "Reconfigure",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
@@ -45,7 +45,7 @@ impl PermissionType {
"WebAccess" => Some(Self::WebAccess),
"ReadCliPipes" => Some(Self::ReadCliPipes),
"MessageAndLaunchOtherPlugins" => Some(Self::MessageAndLaunchOtherPlugins),
- "RebindKeys" => Some(Self::RebindKeys),
+ "Reconfigure" => Some(Self::Reconfigure),
_ => None,
}
}
diff --git a/zellij-utils/src/consts.rs b/zellij-utils/src/consts.rs
index 05794495..1c11815c 100644
--- a/zellij-utils/src/consts.rs
+++ b/zellij-utils/src/consts.rs
@@ -110,6 +110,7 @@ mod not_wasm {
add_plugin!(assets, "tab-bar.wasm");
add_plugin!(assets, "strider.wasm");
add_plugin!(assets, "session-manager.wasm");
+ add_plugin!(assets, "configuration.wasm");
assets
};
}
diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs
index 54c056e3..1a3c9881 100644
--- a/zellij-utils/src/data.rs
+++ b/zellij-utils/src/data.rs
@@ -942,7 +942,7 @@ pub enum Permission {
WebAccess,
ReadCliPipes,
MessageAndLaunchOtherPlugins,
- RebindKeys,
+ Reconfigure,
}
impl PermissionType {
@@ -963,7 +963,7 @@ impl PermissionType {
PermissionType::MessageAndLaunchOtherPlugins => {
"Send messages to and launch other plugins".to_owned()
},
- PermissionType::RebindKeys => "Rebind keys".to_owned(),
+ PermissionType::Reconfigure => "Change Zellij runtime configuration".to_owned(),
}
}
}
@@ -1164,6 +1164,9 @@ impl ModeInfo {
pub fn update_keybinds(&mut self, keybinds: Keybinds) {
self.keybinds = keybinds.to_keybinds_vec();
}
+ pub fn update_default_mode(&mut self, new_default_mode: InputMode) {
+ self.base_mode = Some(new_default_mode);
+ }
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
@@ -1767,5 +1770,5 @@ pub enum PluginCommand {
DumpSessionLayout,
CloseSelf,
NewTabsWithLayoutInfo(LayoutInfo),
- RebindKeys(String), // String -> stringified keybindings
+ Reconfigure(String), // String -> stringified configuration
}
diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs
index 3656d8ad..8700dd15 100644
--- a/zellij-utils/src/errors.rs
+++ b/zellij-utils/src/errors.rs
@@ -353,7 +353,7 @@ pub enum ScreenContext {
RenameSession,
DumpLayoutToPlugin,
ListClientsMetadata,
- RebindKeys,
+ Reconfigure,
}
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
@@ -409,6 +409,7 @@ pub enum PluginContext {
KeybindPipe,
DumpLayoutToPlugin,
ListClientsMetadata,
+ Reconfigure,
}
/// Stack call representations corresponding to the different types of [`ClientInstruction`]s.
@@ -457,7 +458,7 @@ pub enum ServerContext {
DisconnectAllClientsExcept,
ChangeMode,
ChangeModeForAllClients,
- RebindKeys,
+ Reconfigure,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs
index 5d6d99de..32a8a828 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -712,16 +712,6 @@ impl Action {
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, ..) => {
diff --git a/zellij-utils/src/input/mod.rs b/zellij-utils/src/input/mod.rs
index dd17ac8c..296b5313 100644
--- a/zellij-utils/src/input/mod.rs
+++ b/zellij-utils/src/input/mod.rs
@@ -33,9 +33,10 @@ mod not_wasm {
mode: InputMode,
attributes: &ClientAttributes,
capabilities: PluginCapabilities,
+ keybinds: &Keybinds,
base_mode: Option<InputMode>,
) -> ModeInfo {
- let keybinds = attributes.keybinds.to_keybinds_vec();
+ let keybinds = keybinds.to_keybinds_vec();
let session_name = envs::get_session_name().ok();
ModeInfo {
diff --git a/zellij-utils/src/input/plugins.rs b/zellij-utils/src/input/plugins.rs
index ec68bd59..5a2ac645 100644
--- a/zellij-utils/src/input/plugins.rs
+++ b/zellij-utils/src/input/plugins.rs
@@ -65,6 +65,7 @@ impl PluginConfig {
|| tag == "compact-bar"
|| tag == "strider"
|| tag == "session-manager"
+ || tag == "configuration"
{
Some(PluginConfig {
path: PathBuf::from(&tag),
diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs
index a69b6b02..02ef0ab8 100644
--- a/zellij-utils/src/kdl/mod.rs
+++ b/zellij-utils/src/kdl/mod.rs
@@ -1649,6 +1649,10 @@ impl Options {
support_kitty_keyboard_protocol,
})
}
+ pub fn from_string(stringified_keybindings: &String) -> Result<Self, ConfigError> {
+ let document: KdlDocument = stringified_keybindings.parse()?;
+ Options::from_kdl(&document)
+ }
}
impl Layout {
diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto
index 14ab3045..5442909e 100644
--- a/zellij-utils/src/plugin_api/plugin_command.proto
+++ b/zellij-utils/src/plugin_api/plugin_command.proto
@@ -98,7 +98,7 @@ enum CommandName {
DumpSessionLayout = 84;
CloseSelf = 85;
NewTabsWithLayoutInfo = 86;
- RebindKeys = 87;
+ Reconfigure = 87;
}
message PluginCommand {
@@ -156,7 +156,7 @@ message PluginCommand {
KillSessionsPayload kill_sessions_payload = 60;
string scan_host_folder_payload = 61;
NewTabsWithLayoutInfoPayload new_tabs_with_layout_info_payload = 62;
- string rebind_keys_payload = 63;
+ string reconfigure_payload = 63;
}
}
diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs
index aeafc1d4..a6514b1b 100644
--- a/zellij-utils/src/plugin_api/plugin_command.rs
+++ b/zellij-utils/src/plugin_api/plugin_command.rs
@@ -888,11 +888,11 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
},
_ => Err("Mismatched payload for NewTabsWithLayoutInfo"),
},
- Some(CommandName::RebindKeys) => match protobuf_plugin_command.payload {
- Some(Payload::RebindKeysPayload(rebind_keys_payload)) => {
- Ok(PluginCommand::RebindKeys(rebind_keys_payload))
+ Some(CommandName::Reconfigure) => match protobuf_plugin_command.payload {
+ Some(Payload::ReconfigurePayload(reconfigure_payload)) => {
+ Ok(PluginCommand::Reconfigure(reconfigure_payload))
},
- _ => Err("Mismatched payload for RebindKeys"),
+ _ => Err("Mismatched payload for Reconfigure"),
},
None => Err("Unrecognized plugin command"),
}
@@ -1426,9 +1426,9 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
)),
})
},
- PluginCommand::RebindKeys(rebind_keys_payload) => Ok(ProtobufPluginCommand {
- name: CommandName::RebindKeys as i32,
- payload: Some(Payload::RebindKeysPayload(rebind_keys_payload)),
+ PluginCommand::Reconfigure(reconfigure_payload) => Ok(ProtobufPluginCommand {
+ name: CommandName::Reconfigure as i32,
+ payload: Some(Payload::ReconfigurePayload(reconfigure_payload)),
}),
}
}
diff --git a/zellij-utils/src/plugin_api/plugin_permission.proto b/zellij-utils/src/plugin_api/plugin_permission.proto
index cc1fda6a..399ebba5 100644
--- a/zellij-utils/src/plugin_api/plugin_permission.proto
+++ b/zellij-utils/src/plugin_api/plugin_permission.proto
@@ -12,5 +12,5 @@ enum PermissionType {
WebAccess = 6;
ReadCliPipes = 7;
MessageAndLaunchOtherPlugins = 8;
- RebindKeys = 9;
+ Reconfigure = 9;
}
diff --git a/zellij-utils/src/plugin_api/plugin_permission.rs b/zellij-utils/src/plugin_api/plugin_permission.rs
index 968de917..5ea70157 100644
--- a/zellij-utils/src/plugin_api/plugin_permission.rs
+++ b/zellij-utils/src/plugin_api/plugin_permission.rs
@@ -24,7 +24,7 @@ impl TryFrom<ProtobufPermissionType> for PermissionType {
ProtobufPermissionType::MessageAndLaunchOtherPlugins => {
Ok(PermissionType::MessageAndLaunchOtherPlugins)
},
- ProtobufPermissionType::RebindKeys => Ok(PermissionType::RebindKeys),
+ ProtobufPermissionType::Reconfigure => Ok(PermissionType::Reconfigure),
}
}
}
@@ -50,7 +50,7 @@ impl TryFrom<PermissionType> for ProtobufPermissionType {
PermissionType::MessageAndLaunchOtherPlugins => {
Ok(ProtobufPermissionType::MessageAndLaunchOtherPlugins)
},
- PermissionType::RebindKeys => Ok(ProtobufPermissionType::RebindKeys),
+ PermissionType::Reconfigure => Ok(ProtobufPermissionType::Reconfigure),
}
}
}
diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap
index 09e02f71..0f919210 100644
--- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/setup.rs
-assertion_line: 725
+assertion_line: 753
expression: "format!(\"{:#?}\", config)"
---
Config {
@@ -119,6 +119,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -554,6 +564,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -999,6 +1019,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -1592,6 +1622,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -2068,6 +2108,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -2443,6 +2493,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -2826,6 +2886,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -3226,6 +3296,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -3547,6 +3627,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -3856,6 +3946,34 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'c',
+ ),
+ key_modifiers: {},
+ }: [
+ LaunchOrFocusPlugin(
+ Alias(
+ PluginAlias {
+ name: "configuration",
+ configuration: Some(
+ PluginUserConfiguration(
+ {},
+ ),
+ ),
+ initial_cwd: None,
+ run_plugin: None,
+ },
+ ),
+ true,
+ true,
+ false,
+ false,
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'd',
),
key_modifiers: {},
@@ -3864,6 +3982,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -4240,6 +4368,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -4622,6 +4760,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -5066,6 +5214,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -5409,6 +5567,18 @@ Config {
),
initial_cwd: None,
},
+ "configuration": RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: Zellij(
+ PluginTag(
+ "configuration",
+ ),
+ ),
+ configuration: PluginUserConfiguration(
+ {},
+ ),
+ initial_cwd: None,
+ },
"filepicker": RunPlugin {
_allow_exec_host_cmd: false,
location: Zellij(
diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap
index 6a4cb7f9..04174c5f 100644
--- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/setup.rs
-assertion_line: 783
+assertion_line: 811
expression: "format!(\"{:#?}\", config)"
---
Config {
@@ -119,6 +119,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -554,6 +564,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -999,6 +1019,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -1592,6 +1622,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -2068,6 +2108,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -2443,6 +2493,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -2826,6 +2886,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -3226,6 +3296,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -3547,6 +3627,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -3856,6 +3946,34 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'c',
+ ),
+ key_modifiers: {},
+ }: [
+ LaunchOrFocusPlugin(
+ Alias(
+ PluginAlias {
+ name: "configuration",
+ configuration: Some(
+ PluginUserConfiguration(
+ {},
+ ),
+ ),
+ initial_cwd: None,
+ run_plugin: None,
+ },
+ ),
+ true,
+ true,
+ false,
+ false,
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'd',
),
key_modifiers: {},
@@ -3864,6 +3982,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -4240,6 +4368,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -4622,6 +4760,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -5066,6 +5214,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -5409,6 +5567,18 @@ Config {
),
initial_cwd: None,
},
+ "configuration": RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: Zellij(
+ PluginTag(
+ "configuration",
+ ),
+ ),
+ configuration: PluginUserConfiguration(
+ {},
+ ),
+ initial_cwd: None,
+ },
"filepicker": RunPlugin {
_allow_exec_host_cmd: false,
location: Zellij(
diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap
index d533e426..028feefe 100644
--- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/setup.rs
-assertion_line: 825
+assertion_line: 853
expression: "format!(\"{:#?}\", config)"
---
Config {
@@ -134,6 +134,18 @@ Config {
),
initial_cwd: None,
},
+ "configuration": RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: Zellij(
+ PluginTag(
+ "configuration",
+ ),
+ ),
+ configuration: PluginUserConfiguration(
+ {},
+ ),
+ initial_cwd: None,
+ },
"filepicker": RunPlugin {
_allow_exec_host_cmd: false,
location: Zellij(
diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap
index 2cb867e7..ef40f8c5 100644
--- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/setup.rs
-assertion_line: 811
+assertion_line: 839
expression: "format!(\"{:#?}\", config)"
---
Config {
@@ -119,6 +119,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -554,6 +564,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -999,6 +1019,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -1592,6 +1622,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -2068,6 +2108,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -2443,6 +2493,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -2826,6 +2886,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -3226,6 +3296,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -3547,6 +3627,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -3856,6 +3946,34 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'c',
+ ),
+ key_modifiers: {},
+ }: [
+ LaunchOrFocusPlugin(
+ Alias(
+ PluginAlias {
+ name: "configuration",
+ configuration: Some(
+ PluginUserConfiguration(
+ {},
+ ),
+ ),
+ initial_cwd: None,
+ run_plugin: None,
+ },
+ ),
+ true,
+ true,
+ false,
+ false,
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'd',
),
key_modifiers: {},
@@ -3864,6 +3982,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -4240,6 +4368,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -4622,6 +4760,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -5066,6 +5214,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -5713,6 +5871,18 @@ Config {
),
initial_cwd: None,
},
+ "configuration": RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: Zellij(
+ PluginTag(
+ "configuration",
+ ),
+ ),
+ configuration: PluginUserConfiguration(
+ {},
+ ),
+ initial_cwd: None,
+ },
"filepicker": RunPlugin {
_allow_exec_host_cmd: false,
location: Zellij(
diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap
index 808e9cfb..795d0cf9 100644
--- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/setup.rs
-assertion_line: 797
+assertion_line: 825
expression: "format!(\"{:#?}\", config)"
---
Config {
@@ -119,6 +119,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -554,6 +564,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -999,6 +1019,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -1592,6 +1622,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -2068,6 +2108,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -2443,6 +2493,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -2826,6 +2886,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -3226,6 +3296,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -3547,6 +3627,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -3856,6 +3946,34 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'c',
+ ),
+ key_modifiers: {},
+ }: [
+ LaunchOrFocusPlugin(
+ Alias(
+ PluginAlias {
+ name: "configuration",
+ configuration: Some(
+ PluginUserConfiguration(
+ {},
+ ),
+ ),
+ initial_cwd: None,
+ run_plugin: None,
+ },
+ ),
+ true,
+ true,
+ false,
+ false,
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'd',
),
key_modifiers: {},
@@ -3864,6 +3982,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -4240,6 +4368,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -4622,6 +4760,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -5066,6 +5214,16 @@ Config {
],
KeyWithModifier {
bare_key: Char(
+ 'f',
+ ),
+ key_modifiers: {
+ Alt,
+ },
+ }: [
+ ToggleFloatingPanes,
+ ],
+ KeyWithModifier {
+ bare_key: Char(
'g',
),
key_modifiers: {
@@ -5409,6 +5567,18 @@ Config {
),
initial_cwd: None,
},
+ "configuration": RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: Zellij(
+ PluginTag(
+ "configuration",
+ ),
+ ),
+ configuration: PluginUserConfiguration(
+ {},
+ ),
+ initial_cwd: None,
+ },
"filepicker": RunPlugin {
_allow_exec_host_cmd: false,
location: Zellij(