aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-utils/src
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2026-06-29 18:10:59 +0200
committerGitHub <noreply@github.com>2026-06-29 18:10:59 +0200
commit245f5141c9bb7eb91f003efac4defc94470243fd (patch)
tree52ae6d003366f97e41c8a1450602aef63c8fdca2 /zellij-utils/src
parentfeat: reload config on theme change (#5135) (diff)
downloadsl0ppij-245f5141c9bb7eb91f003efac4defc94470243fd.tar.gz
sl0ppij-245f5141c9bb7eb91f003efac4defc94470243fd.tar.xz
sl0ppij-245f5141c9bb7eb91f003efac4defc94470243fd.zip
Title frames (#5318)
* initial implementation * improve single line title design * hint text plugin infra * add proto regen task * display alt text in status bar * don't display text with full frames * style alt hover text * fix + style control own pane hover * make multiplayer indications responsive * update snapshots * plugin api * single pane as tab * better scroll styling * fix tests * tab-bar plus hover * plugin surface tests * integration tests * adjust all integration tests * reduce hover noise * add pane id to in place * more integration tests * style: rustfmt * update plugin assets * update assets and snapshots * add pr
Diffstat (limited to 'zellij-utils/src')
-rw-r--r--zellij-utils/src/cli.rs19
-rw-r--r--zellij-utils/src/client_server_contract/common_types.proto5
-rw-r--r--zellij-utils/src/data.rs15
-rw-r--r--zellij-utils/src/errors.rs2
-rw-r--r--zellij-utils/src/input/actions.rs111
-rw-r--r--zellij-utils/src/input/mod.rs1
-rw-r--r--zellij-utils/src/input/options.rs78
-rw-r--r--zellij-utils/src/ipc/protobuf_conversion.rs31
-rw-r--r--zellij-utils/src/ipc/tests/roundtrip_tests.rs23
-rw-r--r--zellij-utils/src/kdl/mod.rs79
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap7
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap7
-rw-r--r--zellij-utils/src/plugin_api/action.proto7
-rw-r--r--zellij-utils/src/plugin_api/action.rs42
-rw-r--r--zellij-utils/src/plugin_api/event.proto29
-rw-r--r--zellij-utils/src/plugin_api/event.rs176
-rw-r--r--zellij-utils/src/plugin_api/mod.rs1
-rw-r--r--zellij-utils/src/plugin_api/pane_frame_style.proto9
-rw-r--r--zellij-utils/src/plugin_api/pane_frame_style.rs49
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.proto14
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.rs110
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap1
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap1
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap1
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap1
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap1
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap1
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap1
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap1
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap1
30 files changed, 808 insertions, 16 deletions
diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs
index a00c4d05..ee2b6da1 100644
--- a/zellij-utils/src/cli.rs
+++ b/zellij-utils/src/cli.rs
@@ -2,7 +2,10 @@ use crate::data::{Direction, InputMode, Resize, UnblockCondition};
use crate::setup::Setup;
use crate::{
consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV},
- input::{layout::PluginUserConfiguration, options::Options},
+ input::{
+ layout::PluginUserConfiguration,
+ options::{Options, PaneFrameStyle},
+ },
};
use clap::{ArgEnum, Args, Parser, Subcommand};
use serde::{Deserialize, Serialize};
@@ -884,6 +887,10 @@ pub enum CliAction {
},
/// Toggle frames around panes in the UI
TogglePaneFrames,
+ SetPaneFrameStyle {
+ #[clap(arg_enum, value_parser)]
+ style: PaneFrameStyle,
+ },
/// Toggle between sending text commands to all panes on the current tab and normal mode.
ToggleActiveSyncTab {
/// Target a specific tab by ID
@@ -934,6 +941,16 @@ pub enum CliAction {
)]
close_replaced_pane: bool,
+ /// The pane to replace when opening in place, eg. terminal_1, plugin_2 or 3 (only
+ /// effective with --in-place; defaults to the focused pane)
+ #[clap(
+ long,
+ value_parser,
+ requires("in-place"),
+ conflicts_with("near-current-pane")
+ )]
+ pane_id: Option<String>,
+
/// Name of the new pane
#[clap(short, long, value_parser)]
name: Option<String>,
diff --git a/zellij-utils/src/client_server_contract/common_types.proto b/zellij-utils/src/client_server_contract/common_types.proto
index 718cc279..6a7809c8 100644
--- a/zellij-utils/src/client_server_contract/common_types.proto
+++ b/zellij-utils/src/client_server_contract/common_types.proto
@@ -279,6 +279,7 @@ message Action {
ToggleThemeAction toggle_theme = 139;
ToggleMobileModeAction toggle_mobile_mode = 140;
FocusLastPaneAction focus_last_pane = 141;
+ SetPaneFrameStyleAction set_pane_frame_style = 142;
}
}
@@ -307,6 +308,9 @@ message HalfPageScrollUpAction {}
message HalfPageScrollDownAction {}
message ToggleFocusFullscreenAction {}
message TogglePaneFramesAction {}
+message SetPaneFrameStyleAction {
+ string style = 1;
+}
message ToggleActiveSyncTabAction {}
message TogglePaneEmbedOrFloatingAction {}
message ToggleFloatingPanesAction {}
@@ -1183,6 +1187,7 @@ message Options {
optional MobileLayout mobile_layout = 48;
optional uint32 mobile_threshold_cols = 49;
optional uint32 mobile_threshold_rows = 50;
+ optional string pane_frame_style = 51;
}
enum OnForceClose {
diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs
index c11f777f..758e2ab3 100644
--- a/zellij-utils/src/data.rs
+++ b/zellij-utils/src/data.rs
@@ -5,6 +5,7 @@ use crate::input::keybinds::Keybinds;
use crate::input::layout::{
Layout, PercentOrFixed, Run, RunPlugin, RunPluginLocation, RunPluginOrAlias,
};
+pub use crate::input::options::PaneFrameStyle;
use crate::pane_size::{PaneGeom, Size};
use crate::position::Position;
use crate::shared::{colors as default_colors, eightbit_to_rgb};
@@ -938,6 +939,12 @@ impl From<Metadata> for FileMetadata {
}
}
+#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct StyledText {
+ pub text: String,
+ pub indices: Vec<Vec<usize>>,
+}
+
/// These events can be subscribed to with subscribe method exported by `zellij-tile`.
/// Once subscribed to, they will trigger the `update` method of the `ZellijPlugin` trait.
#[derive(Debug, Clone, PartialEq, EnumDiscriminants, Display, Serialize, Deserialize)]
@@ -1031,6 +1038,8 @@ pub enum Event {
/// The host terminal indicated its color palette theme mode (CSI 2031 / DSR 997).
HostTerminalThemeChanged(HostTerminalThemeMode),
SoftKeyboardVisibilityChanged(bool),
+ HintText(BTreeMap<usize, StyledText>),
+ ActivePaneScroll(Option<(usize, usize)>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
@@ -1741,6 +1750,7 @@ pub struct ModeInfo {
pub web_server_ip: Option<IpAddr>,
pub web_server_port: Option<u16>,
pub web_server_capability: Option<bool>,
+ pub pane_frame_style: Option<PaneFrameStyle>,
}
impl ModeInfo {
@@ -3378,6 +3388,10 @@ pub enum PluginCommand {
NewTiledPaneInTab {
tab_position: usize,
},
+ ToggleFloatingPanes {
+ tab_id: Option<u64>,
+ },
+ NewPane,
GoToNextTab,
GoToPreviousTab,
Resize(Resize),
@@ -3403,6 +3417,7 @@ pub enum PluginCommand {
PageScrollDown,
ToggleFocusFullscreen,
TogglePaneFrames,
+ SetPaneFrameStyle(PaneFrameStyle),
TogglePaneEmbedOrEject,
UndoRenamePane,
CloseFocus,
diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs
index c9e8e25f..8ffe0907 100644
--- a/zellij-utils/src/errors.rs
+++ b/zellij-utils/src/errors.rs
@@ -282,6 +282,7 @@ pub enum ScreenContext {
ToggleActiveSyncTab,
ToggleActiveTerminalFullscreen,
TogglePaneFrames,
+ SetPaneFrameStyle,
SetSelectable,
ShowPluginCursor,
SetInvisibleBorders,
@@ -466,6 +467,7 @@ pub enum ScreenContext {
MoveTabWithTabId,
PluginSubscribedToAnsiPaneContents,
UpdateBackgroundPluginSubscriptions,
+ ClearHintTextCache,
BroadcastModeUpdate,
EnterMobileMode,
ExitMobileMode,
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs
index 40158b8b..757e99e4 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -14,7 +14,7 @@ use crate::data::{FloatingPaneCoordinates, InputMode};
use crate::home::{find_default_config_dir, get_layout_dir};
use crate::input::config::{Config, ConfigError, KdlError};
use crate::input::mouse::MouseEvent;
-use crate::input::options::OnForceClose;
+use crate::input::options::{OnForceClose, PaneFrameStyle};
use miette::{NamedSource, Report};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
@@ -218,6 +218,7 @@ pub enum Action {
ToggleFocusFullscreen,
/// Toggle frames around panes in the UI
TogglePaneFrames,
+ SetPaneFrameStyle(PaneFrameStyle),
/// Toggle between sending text commands to all panes on the current tab and normal mode.
ToggleActiveSyncTab,
/// Open a new pane in the specified direction (relative to focus).
@@ -1007,6 +1008,7 @@ impl Action {
None => Ok(vec![Action::ToggleFocusFullscreen]),
},
CliAction::TogglePaneFrames => Ok(vec![Action::TogglePaneFrames]),
+ CliAction::SetPaneFrameStyle { style } => Ok(vec![Action::SetPaneFrameStyle(style)]),
CliAction::ToggleActiveSyncTab { tab_id } => match tab_id {
Some(id) => Ok(vec![Action::ToggleActiveSyncTabByTabId { id: id as u64 }]),
None => Ok(vec![Action::ToggleActiveSyncTab]),
@@ -1019,6 +1021,7 @@ impl Action {
floating,
in_place,
close_replaced_pane,
+ pane_id,
name,
close_on_exit,
start_suspended,
@@ -1039,6 +1042,18 @@ impl Action {
borderless,
tab_id,
} => {
+ let pane_id_to_replace = match pane_id {
+ Some(pane_id_str) => match PaneId::from_str(&pane_id_str) {
+ Ok(parsed_pane_id) => Some(parsed_pane_id),
+ Err(_e) => {
+ return Err(format!(
+ "Malformed pane id: {}, expecting either a bare integer (eg. 1), a terminal pane id (eg. terminal_1) or a plugin pane id (eg. plugin_1)",
+ pane_id_str
+ ))
+ },
+ },
+ None => None,
+ };
let current_dir = get_current_dir();
// cwd should only be specified in a plugin alias if it was explicitly given to us,
// otherwise the current_dir might override a cwd defined in the alias itself
@@ -1087,7 +1102,7 @@ impl Action {
))
} else if in_place {
NewPanePlacement::InPlace {
- pane_id_to_replace: None,
+ pane_id_to_replace,
close_replaced_pane,
borderless,
}
@@ -1197,7 +1212,7 @@ impl Action {
command: Some(run_command_action),
pane_name: name,
near_current_pane,
- pane_id_to_replace: None, // TODO: support this
+ pane_id_to_replace,
close_replaced_pane,
tab_id,
}])
@@ -1234,7 +1249,7 @@ impl Action {
command: None,
pane_name: name,
near_current_pane,
- pane_id_to_replace: None, // TODO: support this
+ pane_id_to_replace,
close_replaced_pane,
tab_id,
}])
@@ -3575,6 +3590,7 @@ mod tests {
floating: false,
in_place: false,
close_replaced_pane: false,
+ pane_id: None,
name: None,
close_on_exit: false,
start_suspended: false,
@@ -3617,6 +3633,7 @@ mod tests {
floating: false,
in_place: false,
close_replaced_pane: false,
+ pane_id: None,
name: None,
close_on_exit: false,
start_suspended: false,
@@ -3650,6 +3667,87 @@ mod tests {
}
#[test]
+ fn test_new_in_place_pane_with_pane_id_to_replace() {
+ let cli_action = CliAction::NewPane {
+ direction: None,
+ command: vec![],
+ plugin: None,
+ cwd: None,
+ floating: false,
+ in_place: true,
+ close_replaced_pane: true,
+ pane_id: Some("terminal_4".to_string()),
+ name: None,
+ close_on_exit: false,
+ start_suspended: false,
+ configuration: None,
+ skip_plugin_cache: false,
+ x: None,
+ y: None,
+ width: None,
+ height: None,
+ pinned: None,
+ stacked: false,
+ blocking: false,
+ block_until_exit_success: false,
+ block_until_exit_failure: false,
+ block_until_exit: false,
+ unblock_condition: None,
+ near_current_pane: false,
+ borderless: None,
+ tab_id: None,
+ };
+ let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None);
+ assert!(result.is_ok());
+ let actions = result.unwrap();
+ assert_eq!(actions.len(), 1);
+ match &actions[0] {
+ Action::NewInPlacePane {
+ pane_id_to_replace, ..
+ } => {
+ assert_eq!(*pane_id_to_replace, Some(PaneId::Terminal(4)));
+ },
+ _ => panic!("Expected NewInPlacePane action"),
+ }
+ }
+
+ #[test]
+ fn test_new_in_place_pane_with_malformed_pane_id() {
+ let cli_action = CliAction::NewPane {
+ direction: None,
+ command: vec![],
+ plugin: None,
+ cwd: None,
+ floating: false,
+ in_place: true,
+ close_replaced_pane: false,
+ pane_id: Some("not_a_pane".to_string()),
+ name: None,
+ close_on_exit: false,
+ start_suspended: false,
+ configuration: None,
+ skip_plugin_cache: false,
+ x: None,
+ y: None,
+ width: None,
+ height: None,
+ pinned: None,
+ stacked: false,
+ blocking: false,
+ block_until_exit_success: false,
+ block_until_exit_failure: false,
+ block_until_exit: false,
+ unblock_condition: None,
+ near_current_pane: false,
+ borderless: None,
+ tab_id: None,
+ };
+ let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None);
+ assert!(result.is_err());
+ assert!(result.unwrap_err().contains("Malformed pane id"));
+ }
+
+ #[test]
fn test_new_pane_floating_with_tab_id() {
let cli_action = CliAction::NewPane {
direction: None,
@@ -3659,6 +3757,7 @@ mod tests {
floating: true,
in_place: false,
close_replaced_pane: false,
+ pane_id: None,
name: None,
close_on_exit: false,
start_suspended: false,
@@ -3701,6 +3800,7 @@ mod tests {
floating: false,
in_place: false,
close_replaced_pane: false,
+ pane_id: None,
name: None,
close_on_exit: false,
start_suspended: false,
@@ -3743,6 +3843,7 @@ mod tests {
floating: false,
in_place: false,
close_replaced_pane: false,
+ pane_id: None,
name: None,
close_on_exit: false,
start_suspended: false,
@@ -3847,6 +3948,7 @@ mod tests {
floating: false,
in_place: false,
close_replaced_pane: false,
+ pane_id: None,
name: None,
close_on_exit: false,
start_suspended: false,
@@ -3889,6 +3991,7 @@ mod tests {
floating: true,
in_place: false,
close_replaced_pane: false,
+ pane_id: None,
name: None,
close_on_exit: false,
start_suspended: false,
diff --git a/zellij-utils/src/input/mod.rs b/zellij-utils/src/input/mod.rs
index c103e580..0934287e 100644
--- a/zellij-utils/src/input/mod.rs
+++ b/zellij-utils/src/input/mod.rs
@@ -54,6 +54,7 @@ mod not_wasm {
web_server_ip: None,
web_server_port: None,
web_server_capability: None,
+ pane_frame_style: None,
}
}
diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs
index 8f91cb47..a1cfdd67 100644
--- a/zellij-utils/src/input/options.rs
+++ b/zellij-utils/src/input/options.rs
@@ -93,6 +93,56 @@ impl FromStr for OnForceClose {
}
}
+#[derive(ArgEnum, Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum PaneFrameStyle {
+ Full,
+ Titles,
+ None,
+}
+
+impl Default for PaneFrameStyle {
+ fn default() -> Self {
+ PaneFrameStyle::Titles
+ }
+}
+
+impl PaneFrameStyle {
+ pub fn draws_full_frames(&self) -> bool {
+ matches!(self, PaneFrameStyle::Full)
+ }
+
+ pub fn draws_titles(&self) -> bool {
+ matches!(self, PaneFrameStyle::Titles)
+ }
+
+ pub fn from_options(options: &Options) -> Self {
+ if options.pane_frames == Some(false) {
+ return PaneFrameStyle::None;
+ }
+ match options.pane_frame_style {
+ Some(PaneFrameStyle::Full) => PaneFrameStyle::Full,
+ _ => PaneFrameStyle::Titles,
+ }
+ }
+}
+
+impl FromStr for PaneFrameStyle {
+ type Err = Box<dyn std::error::Error>;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s.trim().to_lowercase().as_str() {
+ "full" => Ok(PaneFrameStyle::Full),
+ "titles" => Ok(PaneFrameStyle::Titles),
+ "none" => Ok(PaneFrameStyle::None),
+ e => Err(format!(
+ "Unknown pane frame style: '{}' (expected 'full', 'titles' or 'none')",
+ e
+ )
+ .into()),
+ }
+ }
+}
+
#[derive(Clone, Default, Debug, PartialEq, Deserialize, Serialize, Args)]
/// Options that can be set either through the config file,
/// or cli flags - cli flags should take precedence over the config file
@@ -146,6 +196,9 @@ pub struct Options {
#[serde(default)]
/// Set display of the pane frames (true or false)
pub pane_frames: Option<bool>,
+ #[clap(long, arg_enum, hide_possible_values = true, value_parser)]
+ #[serde(default)]
+ pub pane_frame_style: Option<PaneFrameStyle>,
#[clap(long, value_parser)]
#[serde(default)]
/// Mirror session when multiple users are connected (true or false)
@@ -393,6 +446,7 @@ impl Options {
pub fn merge(&self, other: Options) -> Options {
let mouse_mode = other.mouse_mode.or(self.mouse_mode);
let pane_frames = other.pane_frames.or(self.pane_frames);
+ let pane_frame_style = other.pane_frame_style.or(self.pane_frame_style);
let auto_layout = other.auto_layout.or(self.auto_layout);
let mirror_session = other.mirror_session.or(self.mirror_session);
let simplified_ui = other.simplified_ui.or(self.simplified_ui);
@@ -475,6 +529,7 @@ impl Options {
theme_dir,
mouse_mode,
pane_frames,
+ pane_frame_style,
mirror_session,
on_force_close,
scroll_buffer_size,
@@ -534,6 +589,7 @@ impl Options {
let simplified_ui = merge_bool(other.simplified_ui, self.simplified_ui);
let mouse_mode = merge_bool(other.mouse_mode, self.mouse_mode);
let pane_frames = merge_bool(other.pane_frames, self.pane_frames);
+ let pane_frame_style = other.pane_frame_style.or(self.pane_frame_style);
let auto_layout = merge_bool(other.auto_layout, self.auto_layout);
let mirror_session = merge_bool(other.mirror_session, self.mirror_session);
let session_serialization =
@@ -616,6 +672,7 @@ impl Options {
theme_dir,
mouse_mode,
pane_frames,
+ pane_frame_style,
mirror_session,
on_force_close,
scroll_buffer_size,
@@ -675,6 +732,27 @@ mod tests {
const TALL_NARROW: (usize, usize) = (40, 200);
const LARGE: (usize, usize) = (200, 200);
+ #[test]
+ fn pane_frame_style_from_str_accepts_all_variants() {
+ assert_eq!(
+ "full".parse::<PaneFrameStyle>().unwrap(),
+ PaneFrameStyle::Full
+ );
+ assert_eq!(
+ "titles".parse::<PaneFrameStyle>().unwrap(),
+ PaneFrameStyle::Titles
+ );
+ assert_eq!(
+ "none".parse::<PaneFrameStyle>().unwrap(),
+ PaneFrameStyle::None
+ );
+ assert_eq!(
+ "NONE".parse::<PaneFrameStyle>().unwrap(),
+ PaneFrameStyle::None
+ );
+ assert!("bogus".parse::<PaneFrameStyle>().is_err());
+ }
+
fn route(
layout: MobileLayoutConfiguration,
is_web: bool,
diff --git a/zellij-utils/src/ipc/protobuf_conversion.rs b/zellij-utils/src/ipc/protobuf_conversion.rs
index c4941676..f1ad8118 100644
--- a/zellij-utils/src/ipc/protobuf_conversion.rs
+++ b/zellij-utils/src/ipc/protobuf_conversion.rs
@@ -1,3 +1,5 @@
+use std::str::FromStr;
+
use crate::{
client_server_contract::client_server_contract::{
client_to_server_msg, server_to_client_msg, ActionMsg, AttachClientMsg,
@@ -766,6 +768,11 @@ impl From<crate::input::options::Options>
}),
mobile_threshold_cols: options.mobile_threshold_cols.map(|v| v as u32),
mobile_threshold_rows: options.mobile_threshold_rows.map(|v| v as u32),
+ pane_frame_style: options.pane_frame_style.map(|s| match s {
+ crate::input::options::PaneFrameStyle::Full => "full".to_owned(),
+ crate::input::options::PaneFrameStyle::Titles => "titles".to_owned(),
+ crate::input::options::PaneFrameStyle::None => "none".to_owned(),
+ }),
}
}
}
@@ -798,6 +805,12 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Options>
theme_dir: options.theme_dir.map(std::path::PathBuf::from),
mouse_mode: options.mouse_mode,
pane_frames: options.pane_frames,
+ pane_frame_style: options.pane_frame_style.as_deref().and_then(|s| match s {
+ "full" => Some(crate::input::options::PaneFrameStyle::Full),
+ "titles" => Some(crate::input::options::PaneFrameStyle::Titles),
+ "none" => Some(crate::input::options::PaneFrameStyle::None),
+ _ => None,
+ }),
mirror_session: options.mirror_session,
on_force_close: options
.on_force_close
@@ -999,6 +1012,7 @@ impl From<crate::input::actions::Action>
SetLightThemeAction,
SetPaneBorderlessAction,
SetPaneColorAction,
+ SetPaneFrameStyleAction,
ShowFloatingPanesAction,
SkipConfirmAction,
StackPanesAction,
@@ -1177,6 +1191,16 @@ impl From<crate::input::actions::Action>
crate::input::actions::Action::TogglePaneFrames => {
ActionType::TogglePaneFrames(TogglePaneFramesAction {})
},
+ crate::input::actions::Action::SetPaneFrameStyle(style) => {
+ let style = match style {
+ crate::input::options::PaneFrameStyle::Full => "full",
+ crate::input::options::PaneFrameStyle::Titles => "titles",
+ crate::input::options::PaneFrameStyle::None => "none",
+ };
+ ActionType::SetPaneFrameStyle(SetPaneFrameStyleAction {
+ style: style.to_owned(),
+ })
+ },
crate::input::actions::Action::ToggleActiveSyncTab => {
ActionType::ToggleActiveSyncTab(ToggleActiveSyncTabAction {})
},
@@ -2037,6 +2061,13 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action>
Ok(crate::input::actions::Action::ToggleFocusFullscreen)
},
ActionType::TogglePaneFrames(_) => Ok(crate::input::actions::Action::TogglePaneFrames),
+ ActionType::SetPaneFrameStyle(set_pane_frame_style_action) => {
+ let style = crate::input::options::PaneFrameStyle::from_str(
+ &set_pane_frame_style_action.style,
+ )
+ .map_err(|e| anyhow!("{}", e))?;
+ Ok(crate::input::actions::Action::SetPaneFrameStyle(style))
+ },
ActionType::ToggleActiveSyncTab(_) => {
Ok(crate::input::actions::Action::ToggleActiveSyncTab)
},
diff --git a/zellij-utils/src/ipc/tests/roundtrip_tests.rs b/zellij-utils/src/ipc/tests/roundtrip_tests.rs
index 280805ab..8ea01a5f 100644
--- a/zellij-utils/src/ipc/tests/roundtrip_tests.rs
+++ b/zellij-utils/src/ipc/tests/roundtrip_tests.rs
@@ -13,7 +13,9 @@ use crate::input::layout::{
TiledPaneLayout,
};
use crate::input::mouse::{MouseEvent, MouseEventType};
-use crate::input::options::{Clipboard, MobileLayoutConfiguration, OnForceClose, Options};
+use crate::input::options::{
+ Clipboard, MobileLayoutConfiguration, OnForceClose, Options, PaneFrameStyle,
+};
use crate::ipc::{
ClientToServerMsg, ColorRegister, ExitReason, PaneReference, PixelDimensions, ResizeCause,
ServerToClientMsg,
@@ -444,6 +446,7 @@ fn test_client_messages() {
theme_dir: Some(PathBuf::from("theme_dir")),
mouse_mode: Some(true),
pane_frames: Some(true),
+ pane_frame_style: Some(PaneFrameStyle::Full),
mirror_session: Some(true),
on_force_close: Some(OnForceClose::Quit),
scroll_buffer_size: Some(100000),
@@ -3536,6 +3539,24 @@ fn test_client_messages() {
client_id: Some(100),
is_cli_client: true,
});
+ test_client_roundtrip!(ClientToServerMsg::Action {
+ action: Action::SetPaneFrameStyle(PaneFrameStyle::Full),
+ terminal_id: Some(1),
+ client_id: Some(100),
+ is_cli_client: true,
+ });
+ test_client_roundtrip!(ClientToServerMsg::Action {
+ action: Action::SetPaneFrameStyle(PaneFrameStyle::Titles),
+ terminal_id: Some(1),
+ client_id: Some(100),
+ is_cli_client: true,
+ });
+ test_client_roundtrip!(ClientToServerMsg::Action {
+ action: Action::SetPaneFrameStyle(PaneFrameStyle::None),
+ terminal_id: Some(1),
+ client_id: Some(100),
+ is_cli_client: true,
+ });
test_client_roundtrip!(ClientToServerMsg::ForwardedReplyFromHost {
token: 0,
reply_bytes: vec![],
diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs
index 14695f68..42ce65c5 100644
--- a/zellij-utils/src/kdl/mod.rs
+++ b/zellij-utils/src/kdl/mod.rs
@@ -12,7 +12,7 @@ use crate::input::keybinds::Keybinds;
use crate::input::layout::{
Layout, PercentOrFixed, PluginUserConfiguration, RunPlugin, RunPluginOrAlias, TabLayoutInfo,
};
-use crate::input::options::{Clipboard, OnForceClose, Options};
+use crate::input::options::{Clipboard, OnForceClose, Options, PaneFrameStyle};
use crate::input::permission::{GrantedPermission, PermissionCache};
use crate::input::plugins::PluginAliases;
use crate::input::theme::{FrameConfig, Theme, Themes, UiConfig};
@@ -455,6 +455,16 @@ impl Action {
) -> Result<Self, ConfigError> {
match action_name {
"WriteChars" => Ok(Action::WriteChars { chars: string }),
+ "SetPaneFrameStyle" => {
+ let style = PaneFrameStyle::from_str(string.as_str()).map_err(|e| {
+ ConfigError::new_kdl_error(
+ format!("{}", e),
+ action_node.span().offset(),
+ action_node.span().len(),
+ )
+ })?;
+ Ok(Action::SetPaneFrameStyle(style))
+ },
"SwitchToMode" => match InputMode::from_str(string.as_str()) {
Ok(input_mode) => Ok(Action::SwitchToMode { input_mode }),
Err(_e) => {
@@ -734,6 +744,16 @@ impl Action {
Action::HalfPageScrollDown => Some(KdlNode::new("HalfPageScrollDown")),
Action::ToggleFocusFullscreen => Some(KdlNode::new("ToggleFocusFullscreen")),
Action::TogglePaneFrames => Some(KdlNode::new("TogglePaneFrames")),
+ Action::SetPaneFrameStyle(style) => {
+ let mut node = KdlNode::new("SetPaneFrameStyle");
+ let style = match style {
+ PaneFrameStyle::Full => "full",
+ PaneFrameStyle::Titles => "titles",
+ PaneFrameStyle::None => "none",
+ };
+ node.push(style);
+ Some(node)
+ },
Action::ToggleActiveSyncTab => Some(KdlNode::new("ToggleActiveSyncTab")),
Action::NewPane {
direction,
@@ -1652,6 +1672,11 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
action_arguments,
kdl_action
),
+ "SetPaneFrameStyle" => parse_kdl_action_char_or_string_arguments!(
+ action_name,
+ action_arguments,
+ kdl_action
+ ),
"Search" => parse_kdl_action_char_or_string_arguments!(
action_name,
action_arguments,
@@ -2683,6 +2708,16 @@ impl Options {
.map(|(string, _entry)| PathBuf::from(string));
let pane_frames =
kdl_property_first_arg_as_bool_or_error!(kdl_options, "pane_frames").map(|(v, _)| v);
+ let pane_frame_style =
+ match kdl_property_first_arg_as_string_or_error!(kdl_options, "pane_frame_style") {
+ Some((string, entry)) => Some(PaneFrameStyle::from_str(string).map_err(|_| {
+ kdl_parsing_error!(
+ format!("Invalid value for pane_frame_style: '{}'", string),
+ entry
+ )
+ })?),
+ None => None,
+ };
let auto_layout =
kdl_property_first_arg_as_bool_or_error!(kdl_options, "auto_layout").map(|(v, _)| v);
let theme = kdl_property_first_arg_as_string_or_error!(kdl_options, "theme")
@@ -2881,6 +2916,7 @@ impl Options {
theme_dir,
mouse_mode,
pane_frames,
+ pane_frame_style,
mirror_session,
on_force_close,
scroll_buffer_size,
@@ -3295,6 +3331,44 @@ impl Options {
None
}
}
+ fn pane_frame_style_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// Set the pane frame style when pane_frames is enabled",
+ "// Options:",
+ "// - full",
+ "// - titles (default)",
+ "// ",
+ );
+
+ let style_as_str = |style: &PaneFrameStyle| -> &'static str {
+ match style {
+ PaneFrameStyle::Full => "full",
+ PaneFrameStyle::Titles => "titles",
+ PaneFrameStyle::None => "none",
+ }
+ };
+
+ let create_node = |node_value: &str| -> KdlNode {
+ let mut node = KdlNode::new("pane_frame_style");
+ node.push(node_value.to_owned());
+ node
+ };
+ if let Some(pane_frame_style) = &self.pane_frame_style {
+ let mut node = create_node(style_as_str(pane_frame_style));
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node("titles");
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
fn mirror_session_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
let comment_text = format!(
"{}\n{}\n{}\n{}\n{}\n{}\n{}",
@@ -4419,6 +4493,9 @@ impl Options {
if let Some(pane_frames) = self.pane_frames_to_kdl(add_comments) {
nodes.push(pane_frames);
}
+ if let Some(pane_frame_style) = self.pane_frame_style_to_kdl(add_comments) {
+ nodes.push(pane_frame_style);
+ }
if let Some(mirror_session) = self.mirror_session_to_kdl(add_comments) {
nodes.push(mirror_session);
}
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap
index 5dbede33..2d0063ce 100644
--- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap
@@ -359,6 +359,13 @@ web_client {
//
// pane_frames false
+// Set the pane frame style when pane_frames is enabled
+// Options:
+// - full
+// - titles (default)
+//
+// pane_frame_style "titles"
+
// When attaching to an existing session with other users,
// should the session be mirrored (true)
// or should each user have their own cursor (false)
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap
index 347a2074..a69641fe 100644
--- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap
@@ -77,6 +77,13 @@ mouse_mode false
//
pane_frames false
+// Set the pane frame style when pane_frames is enabled
+// Options:
+// - full
+// - titles (default)
+//
+// pane_frame_style "titles"
+
// When attaching to an existing session with other users,
// should the session be mirrored (true)
// or should each user have their own cursor (false)
diff --git a/zellij-utils/src/plugin_api/action.proto b/zellij-utils/src/plugin_api/action.proto
index 68c859b8..f9370961 100644
--- a/zellij-utils/src/plugin_api/action.proto
+++ b/zellij-utils/src/plugin_api/action.proto
@@ -2,6 +2,7 @@ syntax = "proto3";
import "input_mode.proto";
import "resize.proto";
+import "pane_frame_style.proto";
package api.action;
@@ -245,9 +246,14 @@ message Action {
ShowFloatingPanesPayload show_floating_panes_payload = 58;
HideFloatingPanesPayload hide_floating_panes_payload = 59;
AreFloatingPanesVisiblePayload are_floating_panes_visible_payload = 60;
+ SetPaneFrameStylePayload set_pane_frame_style_payload = 61;
}
}
+message SetPaneFrameStylePayload {
+ pane_frame_style.PaneFrameStyle pane_frame_style = 1;
+}
+
message CliPipePayload {
optional string name = 1;
string payload = 2;
@@ -494,6 +500,7 @@ enum ActionName {
SetLightTheme = 102;
ToggleTheme = 103;
FocusLastPane = 104;
+ SetPaneFrameStyle = 105;
}
message Position {
diff --git a/zellij-utils/src/plugin_api/action.rs b/zellij-utils/src/plugin_api/action.rs
index f4e35d84..209bff3f 100644
--- a/zellij-utils/src/plugin_api/action.rs
+++ b/zellij-utils/src/plugin_api/action.rs
@@ -58,6 +58,7 @@ pub use super::generated_api::api::{
ScrollAtPayload,
SearchDirection as ProtobufSearchDirection,
SearchOption as ProtobufSearchOption,
+ SetPaneFrameStylePayload,
ShowFloatingPanesPayload,
SplitDirection as ProtobufSplitDirection,
SplitSize as ProtobufSplitSize,
@@ -74,6 +75,7 @@ pub use super::generated_api::api::{
WritePayload,
},
input_mode::InputMode as ProtobufInputMode,
+ pane_frame_style::PaneFrameStyle as ProtobufPaneFrameStyle,
resize::{Resize as ProtobufResize, ResizeDirection as ProtobufResizeDirection},
};
use crate::data::{
@@ -300,6 +302,17 @@ impl TryFrom<ProtobufAction> for Action {
Some(_) => Err("TogglePaneFrames should not have a payload"),
None => Ok(Action::TogglePaneFrames),
},
+ Some(ProtobufActionName::SetPaneFrameStyle) => match protobuf_action.optional_payload {
+ Some(OptionalPayload::SetPaneFrameStylePayload(payload)) => {
+ match ProtobufPaneFrameStyle::from_i32(payload.pane_frame_style) {
+ Some(protobuf_pane_frame_style) => Ok(Action::SetPaneFrameStyle(
+ protobuf_pane_frame_style.try_into()?,
+ )),
+ None => Err("Malformed payload for Action::SetPaneFrameStyle"),
+ }
+ },
+ _ => Err("Wrong payload for Action::SetPaneFrameStyle"),
+ },
Some(ProtobufActionName::ToggleActiveSyncTab) => {
match protobuf_action.optional_payload {
Some(_) => Err("ToggleActiveSyncTab should not have a payload"),
@@ -1301,6 +1314,15 @@ impl TryFrom<Action> for ProtobufAction {
name: ProtobufActionName::TogglePaneFrames as i32,
optional_payload: None,
}),
+ Action::SetPaneFrameStyle(pane_frame_style) => Ok(ProtobufAction {
+ name: ProtobufActionName::SetPaneFrameStyle as i32,
+ optional_payload: Some(OptionalPayload::SetPaneFrameStylePayload(
+ SetPaneFrameStylePayload {
+ pane_frame_style: ProtobufPaneFrameStyle::try_from(pane_frame_style)?
+ as i32,
+ },
+ )),
+ }),
Action::ToggleActiveSyncTab => Ok(ProtobufAction {
name: ProtobufActionName::ToggleActiveSyncTab as i32,
optional_payload: None,
@@ -3235,3 +3257,23 @@ impl TryFrom<SwapFloatingLayout> for ProtobufSwapFloatingLayout {
})
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::input::options::PaneFrameStyle;
+
+ #[test]
+ fn set_pane_frame_style_action_protobuf_round_trip() {
+ for style in [
+ PaneFrameStyle::Full,
+ PaneFrameStyle::Titles,
+ PaneFrameStyle::None,
+ ] {
+ let original = Action::SetPaneFrameStyle(style);
+ let protobuf: ProtobufAction = original.clone().try_into().expect("encode");
+ let decoded: Action = protobuf.try_into().expect("decode");
+ assert_eq!(original, decoded);
+ }
+ }
+}
diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto
index 2567c657..d3148edf 100644
--- a/zellij-utils/src/plugin_api/event.proto
+++ b/zellij-utils/src/plugin_api/event.proto
@@ -70,6 +70,8 @@ enum EventType {
CommandChanged = 45;
HostTerminalThemeChanged = 46;
SoftKeyboardVisibilityChanged = 47;
+ HintText = 48;
+ ActivePaneScroll = 49;
}
message EventNameList {
@@ -119,9 +121,29 @@ message Event {
CommandChangedPayload command_changed_payload = 39;
HostTerminalThemeChangedPayload host_terminal_theme_changed_payload = 40;
SoftKeyboardVisibilityChangedPayload soft_keyboard_visibility_changed_payload = 41;
+ HintTextPayload hint_text_payload = 42;
+ ActivePaneScrollPayload active_pane_scroll_payload = 43;
}
}
+message HintTextPayload {
+ map<uint32, StyledText> hint_text = 1;
+}
+
+message ActivePaneScrollPayload {
+ optional uint32 position = 1;
+ optional uint32 length = 2;
+}
+
+message StyledText {
+ string text = 1;
+ repeated StyledTextIndices indices = 2;
+}
+
+message StyledTextIndices {
+ repeated uint32 indices = 1;
+}
+
message HostTerminalThemeChangedPayload {
HostTerminalThemeIndication mode = 1;
}
@@ -488,6 +510,7 @@ message ModeUpdatePayload {
optional string web_server_ip = 13;
optional uint32 web_server_port = 14;
optional bool web_server_capability = 15;
+ optional PaneFrameStyle pane_frame_style = 16;
}
enum WebSharing {
@@ -496,6 +519,12 @@ enum WebSharing {
Disabled = 2;
}
+enum PaneFrameStyle {
+ Full = 0;
+ Titles = 1;
+ None = 2;
+}
+
message InputModeKeybinds {
input_mode.InputMode mode = 1;
repeated KeyBind key_bind = 2;
diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs
index 5ba106d0..190552d4 100644
--- a/zellij-utils/src/plugin_api/event.rs
+++ b/zellij-utils/src/plugin_api/event.rs
@@ -4,6 +4,7 @@ pub use super::generated_api::api::{
event::Payload as ProtobufEventPayload,
layout_parsing_error::ErrorType as ProtobufLayoutParsingErrorType,
pane_scrollback_response, ActionCompletePayload as ProtobufActionCompletePayload,
+ ActivePaneScrollPayload as ProtobufActivePaneScrollPayload,
AvailableLayoutInfoPayload as ProtobufAvailableLayoutInfoPayload,
ClientInfo as ProtobufClientInfo, ClientPaneHistory as ProtobufClientPaneHistory,
ClientTabHistory as ProtobufClientTabHistory,
@@ -11,6 +12,7 @@ pub use super::generated_api::api::{
CopyDestination as ProtobufCopyDestination, CwdChangedPayload as ProtobufCwdChangedPayload,
Event as ProtobufEvent, EventNameList as ProtobufEventNameList,
EventType as ProtobufEventType, FileMetadata as ProtobufFileMetadata,
+ HintTextPayload as ProtobufHintTextPayload,
HostTerminalThemeChangedPayload as ProtobufHostTerminalThemeChangedPayload,
HostTerminalThemeIndication as ProtobufHostTerminalThemeIndication,
InputModeKeybinds as ProtobufInputModeKeybinds, KdlError as ProtobufKdlError,
@@ -19,14 +21,16 @@ pub use super::generated_api::api::{
LayoutParsingError as ProtobufLayoutParsingError,
LayoutWithError as ProtobufLayoutWithError, ModeUpdatePayload as ProtobufModeUpdatePayload,
PaneContents as ProtobufPaneContents, PaneContentsEntry as ProtobufPaneContentsEntry,
- PaneId as ProtobufPaneId, PaneInfo as ProtobufPaneInfo,
- PaneManifest as ProtobufPaneManifest, PaneMetadata as ProtobufPaneMetadata,
+ PaneFrameStyle as ProtobufPaneFrameStyle, PaneId as ProtobufPaneId,
+ PaneInfo as ProtobufPaneInfo, PaneManifest as ProtobufPaneManifest,
+ PaneMetadata as ProtobufPaneMetadata,
PaneRenderReportPayload as ProtobufPaneRenderReportPayload,
PaneScrollbackResponse as ProtobufPaneScrollbackResponse, PaneType as ProtobufPaneType,
PluginConfigurationChangedPayload as ProtobufPluginConfigurationChangedPayload,
PluginInfo as ProtobufPluginInfo, ResurrectableSession as ProtobufResurrectableSession,
SelectedText as ProtobufSelectedText, SessionManifest as ProtobufSessionManifest,
SoftKeyboardVisibilityChangedPayload as ProtobufSoftKeyboardVisibilityChangedPayload,
+ StyledText as ProtobufStyledText, StyledTextIndices as ProtobufStyledTextIndices,
SyntaxError as ProtobufSyntaxError, TabInfo as ProtobufTabInfo,
TabMetadata as ProtobufTabMetadata, UserActionPayload as ProtobufUserActionPayload,
WebServerStatusPayload as ProtobufWebServerStatusPayload, WebSharing as ProtobufWebSharing,
@@ -41,8 +45,8 @@ use crate::data::{
ClientId, ClientInfo, CopyDestination, Event, EventType, FileMetadata, HostTerminalThemeMode,
InputMode, KeyWithModifier, LayoutInfo, LayoutMetadata, ModeInfo, Mouse, PaneContents, PaneId,
PaneInfo, PaneManifest, PaneMetadata, PaneScrollbackResponse, PermissionStatus,
- PluginCapabilities, PluginInfo, SelectedText, SessionInfo, Style, TabInfo, TabMetadata,
- WebServerStatus, WebSharing,
+ PluginCapabilities, PluginInfo, SelectedText, SessionInfo, Style, StyledText, TabInfo,
+ TabMetadata, WebServerStatus, WebSharing,
};
use crate::errors::prelude::*;
@@ -597,6 +601,28 @@ impl TryFrom<ProtobufEvent> for Event {
_ => Err("Malformed payload for SoftKeyboardVisibilityChanged Event"),
}
},
+ Some(ProtobufEventType::HintText) => match protobuf_event.payload {
+ Some(ProtobufEventPayload::HintTextPayload(p)) => {
+ let mut hint_text = BTreeMap::new();
+ for (width, styled_text) in p.hint_text {
+ hint_text.insert(width as usize, StyledText::from(styled_text));
+ }
+ Ok(Event::HintText(hint_text))
+ },
+ _ => Err("Malformed payload for HintText Event"),
+ },
+ Some(ProtobufEventType::ActivePaneScroll) => match protobuf_event.payload {
+ Some(ProtobufEventPayload::ActivePaneScrollPayload(p)) => {
+ let scroll = match (p.position, p.length) {
+ (Some(position), Some(length)) => {
+ Some((position as usize, length as usize))
+ },
+ _ => None,
+ };
+ Ok(Event::ActivePaneScroll(scroll))
+ },
+ _ => Err("Malformed payload for ActivePaneScroll Event"),
+ },
None => Err("Unknown Protobuf Event"),
}
}
@@ -1147,6 +1173,35 @@ impl TryFrom<Event> for ProtobufEvent {
)),
})
},
+ Event::HintText(hint_text) => {
+ let mut proto_hint_text = HashMap::new();
+ for (width, styled_text) in hint_text {
+ proto_hint_text.insert(width as u32, ProtobufStyledText::from(styled_text));
+ }
+ let payload = ProtobufHintTextPayload {
+ hint_text: proto_hint_text,
+ };
+ Ok(ProtobufEvent {
+ name: ProtobufEventType::HintText as i32,
+ payload: Some(event::Payload::HintTextPayload(payload)),
+ })
+ },
+ Event::ActivePaneScroll(scroll) => {
+ let payload = match scroll {
+ Some((position, length)) => ProtobufActivePaneScrollPayload {
+ position: Some(position as u32),
+ length: Some(length as u32),
+ },
+ None => ProtobufActivePaneScrollPayload {
+ position: None,
+ length: None,
+ },
+ };
+ Ok(ProtobufEvent {
+ name: ProtobufEventType::ActivePaneScroll as i32,
+ payload: Some(event::Payload::ActivePaneScrollPayload(payload)),
+ })
+ },
Event::InitialKeybinds(keybinds) => {
let mut protobuf_keybinds: Vec<ProtobufInputModeKeybinds> = vec![];
for (input_mode, input_mode_keybinds) in keybinds {
@@ -1960,6 +2015,11 @@ impl TryFrom<ProtobufModeUpdatePayload> for ModeInfo {
let web_server_capability = protobuf_mode_update_payload.web_server_capability;
+ let pane_frame_style = protobuf_mode_update_payload
+ .pane_frame_style
+ .and_then(|p| ProtobufPaneFrameStyle::from_i32(p))
+ .map(|p| p.into());
+
let mode_info = ModeInfo {
mode: current_mode,
keybinds,
@@ -1976,6 +2036,7 @@ impl TryFrom<ProtobufModeUpdatePayload> for ModeInfo {
web_server_ip,
web_server_port,
web_server_capability,
+ pane_frame_style,
};
Ok(mode_info)
}
@@ -2000,6 +2061,10 @@ impl TryFrom<ModeInfo> for ProtobufModeUpdatePayload {
let web_server_ip = mode_info.web_server_ip.map(|i| format!("{}", i));
let web_server_port = mode_info.web_server_port.map(|p| p as u32);
let web_server_capability = mode_info.web_server_capability;
+ let pane_frame_style = mode_info.pane_frame_style.map(|p| {
+ let protobuf_pane_frame_style: ProtobufPaneFrameStyle = p.into();
+ protobuf_pane_frame_style as i32
+ });
let mut protobuf_input_mode_keybinds: Vec<ProtobufInputModeKeybinds> = vec![];
for (input_mode, input_mode_keybinds) in mode_info.keybinds {
let mode: ProtobufInputMode = input_mode.try_into()?;
@@ -2040,6 +2105,7 @@ impl TryFrom<ModeInfo> for ProtobufModeUpdatePayload {
web_server_ip,
web_server_port,
web_server_capability,
+ pane_frame_style,
})
}
}
@@ -2127,6 +2193,8 @@ impl TryFrom<ProtobufEventType> for EventType {
ProtobufEventType::SoftKeyboardVisibilityChanged => {
EventType::SoftKeyboardVisibilityChanged
},
+ ProtobufEventType::HintText => EventType::HintText,
+ ProtobufEventType::ActivePaneScroll => EventType::ActivePaneScroll,
})
}
}
@@ -2184,10 +2252,40 @@ impl TryFrom<EventType> for ProtobufEventType {
EventType::SoftKeyboardVisibilityChanged => {
ProtobufEventType::SoftKeyboardVisibilityChanged
},
+ EventType::HintText => ProtobufEventType::HintText,
+ EventType::ActivePaneScroll => ProtobufEventType::ActivePaneScroll,
})
}
}
+impl From<StyledText> for ProtobufStyledText {
+ fn from(styled_text: StyledText) -> Self {
+ ProtobufStyledText {
+ text: styled_text.text,
+ indices: styled_text
+ .indices
+ .into_iter()
+ .map(|inner| ProtobufStyledTextIndices {
+ indices: inner.into_iter().map(|i| i as u32).collect(),
+ })
+ .collect(),
+ }
+ }
+}
+
+impl From<ProtobufStyledText> for StyledText {
+ fn from(styled_text: ProtobufStyledText) -> Self {
+ StyledText {
+ text: styled_text.text,
+ indices: styled_text
+ .indices
+ .into_iter()
+ .map(|inner| inner.indices.into_iter().map(|i| i as usize).collect())
+ .collect(),
+ }
+ }
+}
+
impl From<HostTerminalThemeMode> for ProtobufHostTerminalThemeIndication {
fn from(mode: HostTerminalThemeMode) -> Self {
match mode {
@@ -2361,6 +2459,7 @@ fn serialize_mode_update_event_with_non_default_values() {
web_server_ip: IpAddr::from_str("127.0.0.1").ok(),
web_server_port: Some(8082),
web_server_capability: Some(true),
+ pane_frame_style: Some(crate::input::options::PaneFrameStyle::Titles),
});
let protobuf_event: ProtobufEvent = mode_update_event.clone().try_into().unwrap();
let serialized_protobuf_event = protobuf_event.encode_to_vec();
@@ -2463,6 +2562,55 @@ fn serialize_pane_update_event() {
}
#[test]
+fn serialize_hint_text_event() {
+ use prost::Message;
+ let hint_text_event = Event::HintText(BTreeMap::from([
+ (
+ 10,
+ StyledText {
+ text: "group".to_owned(),
+ indices: vec![vec![0]],
+ },
+ ),
+ (
+ 42,
+ StyledText {
+ text: "resize".to_owned(),
+ indices: vec![vec![0, 1], vec![3]],
+ },
+ ),
+ ]));
+ let protobuf_event: ProtobufEvent = hint_text_event.clone().try_into().unwrap();
+ let serialized_protobuf_event = protobuf_event.encode_to_vec();
+ let deserialized_protobuf_event: ProtobufEvent =
+ Message::decode(serialized_protobuf_event.as_slice()).unwrap();
+ let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
+ assert_eq!(
+ hint_text_event, deserialized_event,
+ "Event properly serialized/deserialized without change"
+ );
+}
+
+#[test]
+fn serialize_active_pane_scroll_event() {
+ use prost::Message;
+ for active_pane_scroll_event in [
+ Event::ActivePaneScroll(Some((3, 12))),
+ Event::ActivePaneScroll(None),
+ ] {
+ let protobuf_event: ProtobufEvent = active_pane_scroll_event.clone().try_into().unwrap();
+ let serialized_protobuf_event = protobuf_event.encode_to_vec();
+ let deserialized_protobuf_event: ProtobufEvent =
+ Message::decode(serialized_protobuf_event.as_slice()).unwrap();
+ let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
+ assert_eq!(
+ active_pane_scroll_event, deserialized_event,
+ "Event properly serialized/deserialized without change"
+ );
+ }
+}
+
+#[test]
fn serialize_key_event() {
use crate::data::BareKey;
use prost::Message;
@@ -2946,6 +3094,26 @@ impl Into<WebSharing> for ProtobufWebSharing {
}
}
+impl Into<ProtobufPaneFrameStyle> for crate::input::options::PaneFrameStyle {
+ fn into(self) -> ProtobufPaneFrameStyle {
+ match self {
+ crate::input::options::PaneFrameStyle::Full => ProtobufPaneFrameStyle::Full,
+ crate::input::options::PaneFrameStyle::Titles => ProtobufPaneFrameStyle::Titles,
+ crate::input::options::PaneFrameStyle::None => ProtobufPaneFrameStyle::None,
+ }
+ }
+}
+
+impl Into<crate::input::options::PaneFrameStyle> for ProtobufPaneFrameStyle {
+ fn into(self) -> crate::input::options::PaneFrameStyle {
+ match self {
+ ProtobufPaneFrameStyle::Full => crate::input::options::PaneFrameStyle::Full,
+ ProtobufPaneFrameStyle::Titles => crate::input::options::PaneFrameStyle::Titles,
+ ProtobufPaneFrameStyle::None => crate::input::options::PaneFrameStyle::None,
+ }
+ }
+}
+
impl TryFrom<WebServerStatus> for ProtobufWebServerStatusPayload {
type Error = &'static str;
fn try_from(web_server_status: WebServerStatus) -> Result<Self, &'static str> {
diff --git a/zellij-utils/src/plugin_api/mod.rs b/zellij-utils/src/plugin_api/mod.rs
index 0e26485e..918ad4ae 100644
--- a/zellij-utils/src/plugin_api/mod.rs
+++ b/zellij-utils/src/plugin_api/mod.rs
@@ -5,6 +5,7 @@ pub mod file;
pub mod input_mode;
pub mod key;
pub mod message;
+pub mod pane_frame_style;
pub mod pipe_message;
pub mod plugin_command;
pub mod plugin_ids;
diff --git a/zellij-utils/src/plugin_api/pane_frame_style.proto b/zellij-utils/src/plugin_api/pane_frame_style.proto
new file mode 100644
index 00000000..0acd8bee
--- /dev/null
+++ b/zellij-utils/src/plugin_api/pane_frame_style.proto
@@ -0,0 +1,9 @@
+syntax = "proto3";
+
+package api.pane_frame_style;
+
+enum PaneFrameStyle {
+ Full = 0;
+ Titles = 1;
+ None = 2;
+}
diff --git a/zellij-utils/src/plugin_api/pane_frame_style.rs b/zellij-utils/src/plugin_api/pane_frame_style.rs
new file mode 100644
index 00000000..392f2e89
--- /dev/null
+++ b/zellij-utils/src/plugin_api/pane_frame_style.rs
@@ -0,0 +1,49 @@
+pub use super::generated_api::api::pane_frame_style::PaneFrameStyle as ProtobufPaneFrameStyle;
+use crate::input::options::PaneFrameStyle;
+
+use std::convert::TryFrom;
+
+impl TryFrom<ProtobufPaneFrameStyle> for PaneFrameStyle {
+ type Error = &'static str;
+ fn try_from(protobuf_pane_frame_style: ProtobufPaneFrameStyle) -> Result<Self, &'static str> {
+ match protobuf_pane_frame_style {
+ ProtobufPaneFrameStyle::Full => Ok(PaneFrameStyle::Full),
+ ProtobufPaneFrameStyle::Titles => Ok(PaneFrameStyle::Titles),
+ ProtobufPaneFrameStyle::None => Ok(PaneFrameStyle::None),
+ }
+ }
+}
+
+impl TryFrom<PaneFrameStyle> for ProtobufPaneFrameStyle {
+ type Error = &'static str;
+ fn try_from(pane_frame_style: PaneFrameStyle) -> Result<Self, &'static str> {
+ Ok(match pane_frame_style {
+ PaneFrameStyle::Full => ProtobufPaneFrameStyle::Full,
+ PaneFrameStyle::Titles => ProtobufPaneFrameStyle::Titles,
+ PaneFrameStyle::None => ProtobufPaneFrameStyle::None,
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn pane_frame_style_roundtrips_through_protobuf() {
+ for style in [
+ PaneFrameStyle::Full,
+ PaneFrameStyle::Titles,
+ PaneFrameStyle::None,
+ ] {
+ let protobuf: ProtobufPaneFrameStyle = style.try_into().unwrap();
+ let roundtripped: PaneFrameStyle = protobuf.try_into().unwrap();
+ assert_eq!(style, roundtripped);
+ }
+ }
+
+ #[test]
+ fn unknown_pane_frame_style_value_is_rejected() {
+ assert!(ProtobufPaneFrameStyle::from_i32(99).is_none());
+ }
+}
diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto
index 82f4d835..8d31727d 100644
--- a/zellij-utils/src/plugin_api/plugin_command.proto
+++ b/zellij-utils/src/plugin_api/plugin_command.proto
@@ -9,6 +9,7 @@ import "resize.proto";
import "plugin_permission.proto";
import "input_mode.proto";
import "key.proto";
+import "pane_frame_style.proto";
package api.plugin_command;
@@ -214,6 +215,9 @@ enum CommandName {
NewTiledPaneInTab = 221;
ExitMobileMode = 222;
FocusLastPane = 223;
+ SetPaneFrameStyle = 224;
+ ToggleFloatingPanes = 225;
+ NewPane = 226;
}
message PluginCommand {
@@ -373,9 +377,19 @@ message PluginCommand {
PaneId set_shadow_focus_payload = 167;
NewTabUnfocusedPayload new_tab_unfocused_payload = 168;
NewTiledPaneInTabPayload new_tiled_pane_in_tab_payload = 169;
+ SetPaneFrameStylePayload set_pane_frame_style_payload = 170;
+ ToggleFloatingPanesPayload toggle_floating_panes_payload = 171;
}
}
+message SetPaneFrameStylePayload {
+ pane_frame_style.PaneFrameStyle pane_frame_style = 1;
+}
+
+message ToggleFloatingPanesPayload {
+ optional uint64 tab_id = 1;
+}
+
message NewTabPayload {
optional string name = 1;
optional string cwd = 2;
diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs
index 546c3586..38c42565 100644
--- a/zellij-utils/src/plugin_api/plugin_command.rs
+++ b/zellij-utils/src/plugin_api/plugin_command.rs
@@ -6,6 +6,7 @@ pub use super::generated_api::api::{
SessionManifest as ProtobufSessionManifest,
},
input_mode::InputMode as ProtobufInputMode,
+ pane_frame_style::PaneFrameStyle as ProtobufPaneFrameStyle,
plugin_command::{
break_panes_to_new_tab_response, break_panes_to_tab_with_id_response,
break_panes_to_tab_with_index_response, delete_layout_response, dump_layout_response,
@@ -120,16 +121,18 @@ pub use super::generated_api::api::{
SaveSessionResponse as ProtobufSaveSessionResponse, ScrollDownInPaneIdPayload,
ScrollToBottomInPaneIdPayload, ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload,
SessionListSnapshot as ProtobufSessionListSnapshot, SetFloatingPanePinnedPayload,
- SetPaneBorderlessPayload, SetPaneColorPayload, SetPaneRegexHighlightsPayload,
- SetSelfMouseSelectionSupportPayload,
+ SetPaneBorderlessPayload, SetPaneColorPayload,
+ SetPaneFrameStylePayload as ProtobufSetPaneFrameStylePayload,
+ SetPaneRegexHighlightsPayload, SetSelfMouseSelectionSupportPayload,
SetSoftKeyboardPayload as ProtobufSetSoftKeyboardPayload,
SetTabFitPayload as ProtobufSetTabFitPayload, SetTimeoutPayload, ShowCursorPayload,
ShowFloatingPanesPayload as ProtobufShowFloatingPanesPayload,
ShowFloatingPanesResponse as ProtobufShowFloatingPanesResponse, ShowPaneWithIdPayload,
Size as ProtobufSize, StackPanesPayload, SubscribePayload, SwitchSessionPayload,
- SwitchTabToIdPayload, SwitchTabToPayload, TogglePaneBorderlessPayload,
- TogglePaneEmbedOrEjectForPaneIdPayload, TogglePaneIdFullscreenPayload, UnsubscribePayload,
- WebRequestPayload, WriteCharsToPaneIdPayload, WriteToPaneIdPayload,
+ SwitchTabToIdPayload, SwitchTabToPayload, ToggleFloatingPanesPayload,
+ TogglePaneBorderlessPayload, TogglePaneEmbedOrEjectForPaneIdPayload,
+ TogglePaneIdFullscreenPayload, UnsubscribePayload, WebRequestPayload,
+ WriteCharsToPaneIdPayload, WriteToPaneIdPayload,
},
plugin_permission::PermissionType as ProtobufPermissionType,
resize::ResizeAction as ProtobufResizeAction,
@@ -940,6 +943,20 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
},
_ => Err("Mismatched payload for NewTiledPaneInTab"),
},
+ Some(CommandName::ToggleFloatingPanes) => match protobuf_plugin_command.payload {
+ Some(Payload::ToggleFloatingPanesPayload(payload)) => {
+ Ok(PluginCommand::ToggleFloatingPanes {
+ tab_id: payload.tab_id,
+ })
+ },
+ _ => Ok(PluginCommand::ToggleFloatingPanes { tab_id: None }),
+ },
+ Some(CommandName::NewPane) => {
+ if protobuf_plugin_command.payload.is_some() {
+ return Err("NewPane should not have a payload");
+ }
+ Ok(PluginCommand::NewPane)
+ },
Some(CommandName::GoToNextTab) => {
if protobuf_plugin_command.payload.is_some() {
return Err("GoToNextTab should not have a payload");
@@ -1099,6 +1116,17 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
}
Ok(PluginCommand::TogglePaneFrames)
},
+ Some(CommandName::SetPaneFrameStyle) => match protobuf_plugin_command.payload {
+ Some(Payload::SetPaneFrameStylePayload(payload)) => {
+ match ProtobufPaneFrameStyle::from_i32(payload.pane_frame_style) {
+ Some(protobuf_pane_frame_style) => Ok(PluginCommand::SetPaneFrameStyle(
+ protobuf_pane_frame_style.try_into()?,
+ )),
+ None => Err("Malformed SetPaneFrameStyle payload"),
+ }
+ },
+ _ => Err("Mismatched payload for SetPaneFrameStyle"),
+ },
Some(CommandName::TogglePaneEmbedOrEject) => {
if protobuf_plugin_command.payload.is_some() {
return Err("TogglePaneEmbedOrEject should not have a payload");
@@ -2983,6 +3011,16 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
},
)),
}),
+ PluginCommand::ToggleFloatingPanes { tab_id } => Ok(ProtobufPluginCommand {
+ name: CommandName::ToggleFloatingPanes as i32,
+ payload: Some(Payload::ToggleFloatingPanesPayload(
+ ToggleFloatingPanesPayload { tab_id },
+ )),
+ }),
+ PluginCommand::NewPane => Ok(ProtobufPluginCommand {
+ name: CommandName::NewPane as i32,
+ payload: None,
+ }),
PluginCommand::GoToNextTab => Ok(ProtobufPluginCommand {
name: CommandName::GoToNextTab as i32,
payload: None,
@@ -3093,6 +3131,15 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
name: CommandName::TogglePaneFrames as i32,
payload: None,
}),
+ PluginCommand::SetPaneFrameStyle(pane_frame_style) => Ok(ProtobufPluginCommand {
+ name: CommandName::SetPaneFrameStyle as i32,
+ payload: Some(Payload::SetPaneFrameStylePayload(
+ ProtobufSetPaneFrameStylePayload {
+ pane_frame_style: ProtobufPaneFrameStyle::try_from(pane_frame_style)?
+ as i32,
+ },
+ )),
+ }),
PluginCommand::TogglePaneEmbedOrEject => Ok(ProtobufPluginCommand {
name: CommandName::TogglePaneEmbedOrEject as i32,
payload: None,
@@ -5265,4 +5312,57 @@ mod tests {
other => panic!("expected SetTabFit, got {:?}", other),
}
}
+
+ #[test]
+ fn set_pane_frame_style_protobuf_round_trip() {
+ use crate::input::options::PaneFrameStyle;
+ for style in [
+ PaneFrameStyle::Full,
+ PaneFrameStyle::Titles,
+ PaneFrameStyle::None,
+ ] {
+ let original = PluginCommand::SetPaneFrameStyle(style);
+ let protobuf: ProtobufPluginCommand = original.try_into().expect("encode");
+ let decoded: PluginCommand = protobuf.try_into().expect("decode");
+ match decoded {
+ PluginCommand::SetPaneFrameStyle(decoded_style) => {
+ assert_eq!(decoded_style, style)
+ },
+ other => panic!("expected SetPaneFrameStyle, got {:?}", other),
+ }
+ }
+ }
+
+ #[test]
+ fn new_pane_protobuf_round_trip() {
+ let original = PluginCommand::NewPane;
+ let protobuf: ProtobufPluginCommand = original.try_into().expect("encode");
+ let decoded: PluginCommand = protobuf.try_into().expect("decode");
+ match decoded {
+ PluginCommand::NewPane => {},
+ other => panic!("expected NewPane, got {:?}", other),
+ }
+ }
+
+ #[test]
+ fn toggle_floating_panes_focused_tab_protobuf_round_trip() {
+ let original = PluginCommand::ToggleFloatingPanes { tab_id: None };
+ let protobuf: ProtobufPluginCommand = original.try_into().expect("encode");
+ let decoded: PluginCommand = protobuf.try_into().expect("decode");
+ match decoded {
+ PluginCommand::ToggleFloatingPanes { tab_id } => assert_eq!(tab_id, None),
+ other => panic!("expected ToggleFloatingPanes, got {:?}", other),
+ }
+ }
+
+ #[test]
+ fn toggle_floating_panes_with_tab_id_protobuf_round_trip() {
+ let original = PluginCommand::ToggleFloatingPanes { tab_id: Some(2) };
+ let protobuf: ProtobufPluginCommand = original.try_into().expect("encode");
+ let decoded: PluginCommand = protobuf.try_into().expect("decode");
+ match decoded {
+ PluginCommand::ToggleFloatingPanes { tab_id } => assert_eq!(tab_id, Some(2)),
+ other => panic!("expected ToggleFloatingPanes, got {:?}", other),
+ }
+ }
}
diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap
index 0d51e89b..4d16821f 100644
--- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap
@@ -17,6 +17,7 @@ Options {
theme_dir: None,
mouse_mode: None,
pane_frames: None,
+ pane_frame_style: None,
mirror_session: None,
on_force_close: None,
scroll_buffer_size: None,
diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap
index 1b0c154a..d8378848 100644
--- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap
@@ -17,6 +17,7 @@ Options {
pane_frames: Some(
true,
),
+ pane_frame_style: None,
mirror_session: None,
on_force_close: None,
scroll_buffer_size: None,
diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap
index 08b89aac..79a656a5 100644
--- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap
@@ -15,6 +15,7 @@ Options {
theme_dir: None,
mouse_mode: None,
pane_frames: None,
+ pane_frame_style: None,
mirror_session: None,
on_force_close: None,
scroll_buffer_size: None,
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 a8a144a8..2f8c1964 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
@@ -5993,6 +5993,7 @@ Config {
theme_dir: None,
mouse_mode: None,
pane_frames: None,
+ pane_frame_style: None,
mirror_session: None,
on_force_close: None,
scroll_buffer_size: None,
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 4792ab0c..e4a22a82 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
@@ -5993,6 +5993,7 @@ Config {
theme_dir: None,
mouse_mode: None,
pane_frames: None,
+ pane_frame_style: None,
mirror_session: None,
on_force_close: None,
scroll_buffer_size: None,
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 8f5baed6..f703371c 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
@@ -102,6 +102,7 @@ Config {
theme_dir: None,
mouse_mode: None,
pane_frames: None,
+ pane_frame_style: None,
mirror_session: None,
on_force_close: None,
scroll_buffer_size: None,
diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap
index 40822e6e..0830a4b6 100644
--- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap
@@ -17,6 +17,7 @@ Options {
pane_frames: Some(
false,
),
+ pane_frame_style: None,
mirror_session: None,
on_force_close: None,
scroll_buffer_size: None,
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 2cb1c9af..e8e1b3ee 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
@@ -5993,6 +5993,7 @@ Config {
theme_dir: None,
mouse_mode: None,
pane_frames: None,
+ pane_frame_style: None,
mirror_session: None,
on_force_close: None,
scroll_buffer_size: None,
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 431c8fc4..ff3a4fda 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
@@ -5993,6 +5993,7 @@ Config {
theme_dir: None,
mouse_mode: None,
pane_frames: None,
+ pane_frame_style: None,
mirror_session: None,
on_force_close: None,
scroll_buffer_size: None,