diff options
| author | Aram Drevekenin <aram@poor.dev> | 2026-03-03 08:20:04 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-03 08:20:04 +0100 |
| commit | f618b0653abc918f9c34b5a4e8a06e4a11745b43 (patch) | |
| tree | b2a1cea091bd395fda4843733a081d1c87180823 /zellij-utils | |
| parent | feat(web): improve web client ui on mobile (#4723) (diff) | |
| download | sl0ppij-f618b0653abc918f9c34b5a4e8a06e4a11745b43.tar.gz sl0ppij-f618b0653abc918f9c34b5a4e8a06e4a11745b43.tar.xz sl0ppij-f618b0653abc918f9c34b5a4e8a06e4a11745b43.zip | |
Visual indication for terminal bells (#4781)
* initial implementation
* rustfmt
* allow disabling with config
* add missing kdl
* add pr
* fix tests
Diffstat (limited to 'zellij-utils')
22 files changed, 99 insertions, 0 deletions
diff --git a/zellij-utils/assets/prost/api.event.rs b/zellij-utils/assets/prost/api.event.rs index 3fac82af..3bab18af 100644 --- a/zellij-utils/assets/prost/api.event.rs +++ b/zellij-utils/assets/prost/api.event.rs @@ -589,6 +589,10 @@ pub struct TabInfo { pub selectable_floating_panes_count: u32, #[prost(uint32, tag="17")] pub tab_id: u32, + #[prost(bool, tag="18")] + pub has_bell_notification: bool, + #[prost(bool, tag="19")] + pub is_flashing_bell: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/zellij-utils/assets/prost_ipc/client_server_contract.rs b/zellij-utils/assets/prost_ipc/client_server_contract.rs index c61229e9..79a8b6b3 100644 --- a/zellij-utils/assets/prost_ipc/client_server_contract.rs +++ b/zellij-utils/assets/prost_ipc/client_server_contract.rs @@ -1799,6 +1799,8 @@ pub struct Options { pub mouse_hover_effects: ::core::option::Option<bool>, #[prost(uint64, optional, tag="42")] pub client_async_worker_tasks: ::core::option::Option<u64>, + #[prost(bool, optional, tag="43")] + pub visual_bell: ::core::option::Option<bool>, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] diff --git a/zellij-utils/src/client_server_contract/common_types.proto b/zellij-utils/src/client_server_contract/common_types.proto index 46f1b812..03602019 100644 --- a/zellij-utils/src/client_server_contract/common_types.proto +++ b/zellij-utils/src/client_server_contract/common_types.proto @@ -1098,6 +1098,7 @@ message Options { optional bool osc8_hyperlinks = 40; optional bool mouse_hover_effects = 41; optional uint64 client_async_worker_tasks = 42; + optional bool visual_bell = 43; } enum OnForceClose { diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index 74dfdfe7..66cf0815 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -2192,6 +2192,10 @@ pub struct TabInfo { pub selectable_floating_panes_count: usize, /// The stable identifier for this tab pub tab_id: usize, + /// Whether this tab has an active (persistent) bell notification + pub has_bell_notification: bool, + /// Whether this tab is currently flashing its bell (transient 400ms state) + pub is_flashing_bell: bool, } /// The `PaneManifest` contains a dictionary of panes, indexed by the tab position (0 indexed). diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index 6c85ad4e..2085e913 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -327,6 +327,7 @@ pub enum ScreenContext { SearchToggleWrap, AddRedPaneFrameColorOverride, ClearPaneFrameColorOverride, + SetTabBellFlash, PreviousSwapLayout, NextSwapLayout, OverrideLayout, @@ -581,6 +582,10 @@ pub enum BackgroundJobContext { HighlightPanesWithMessage, QueryZellijWebServerStatus, ClearHelpText, + FlashPaneBell, + StopFlashPaneBell, + FlashTabBell, + StopFlashTabBell, Exit, } diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs index ccba4d9c..dfbe089e 100644 --- a/zellij-utils/src/input/options.rs +++ b/zellij-utils/src/input/options.rs @@ -231,6 +231,12 @@ pub struct Options { #[serde(default)] pub mouse_hover_effects: Option<bool>, + /// Whether to show visual bell indicators (pane/tab frame flash and [!] suffix) + /// default is true + #[clap(long, value_parser)] + #[serde(default)] + pub visual_bell: Option<bool>, + // these are intentionally excluded from the CLI options as they must be specified in the // configuration file pub web_server_ip: Option<IpAddr>, @@ -336,6 +342,7 @@ impl Options { let show_release_notes = other.show_release_notes.or(self.show_release_notes); let advanced_mouse_actions = other.advanced_mouse_actions.or(self.advanced_mouse_actions); let mouse_hover_effects = other.mouse_hover_effects.or(self.mouse_hover_effects); + let visual_bell = other.visual_bell.or(self.visual_bell); let web_server_ip = other.web_server_ip.or(self.web_server_ip); let web_server_port = other.web_server_port.or(self.web_server_port); let web_server_cert = other @@ -388,6 +395,7 @@ impl Options { show_release_notes, advanced_mouse_actions, mouse_hover_effects, + visual_bell, web_server_ip, web_server_port, web_server_cert, @@ -461,6 +469,7 @@ impl Options { let show_release_notes = other.show_release_notes.or(self.show_release_notes); let advanced_mouse_actions = other.advanced_mouse_actions.or(self.advanced_mouse_actions); let mouse_hover_effects = other.mouse_hover_effects.or(self.mouse_hover_effects); + let visual_bell = other.visual_bell.or(self.visual_bell); let web_server_ip = other.web_server_ip.or(self.web_server_ip); let web_server_port = other.web_server_port.or(self.web_server_port); let web_server_cert = other @@ -513,6 +522,7 @@ impl Options { show_release_notes, advanced_mouse_actions, mouse_hover_effects, + visual_bell, web_server_ip, web_server_port, web_server_cert, diff --git a/zellij-utils/src/ipc/protobuf_conversion.rs b/zellij-utils/src/ipc/protobuf_conversion.rs index b6bfbf9c..91f64070 100644 --- a/zellij-utils/src/ipc/protobuf_conversion.rs +++ b/zellij-utils/src/ipc/protobuf_conversion.rs @@ -615,6 +615,7 @@ impl From<crate::input::options::Options> enforce_https_for_localhost: options.enforce_https_for_localhost, post_command_discovery_hook: options.post_command_discovery_hook, client_async_worker_tasks: options.client_async_worker_tasks.map(|v| v as u64), + visual_bell: options.visual_bell, } } } @@ -707,6 +708,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Options> enforce_https_for_localhost: options.enforce_https_for_localhost, post_command_discovery_hook: options.post_command_discovery_hook, client_async_worker_tasks: options.client_async_worker_tasks.map(|v| v as usize), + visual_bell: options.visual_bell, }) } } diff --git a/zellij-utils/src/ipc/tests/roundtrip_tests.rs b/zellij-utils/src/ipc/tests/roundtrip_tests.rs index 8cd0cc84..233a0e5d 100644 --- a/zellij-utils/src/ipc/tests/roundtrip_tests.rs +++ b/zellij-utils/src/ipc/tests/roundtrip_tests.rs @@ -473,6 +473,7 @@ fn test_client_messages() { post_command_discovery_hook: Some("post_command_discovery_hook".to_owned()), client_async_worker_tasks: Some(16), mouse_hover_effects: Some(false), + visual_bell: Some(true), }), layout: None, terminal_window_size: Size { rows: 80, cols: 42 }, diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index ced75920..12eadff6 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -2781,6 +2781,8 @@ impl Options { }, None => None, }; + let visual_bell = + kdl_property_first_arg_as_bool_or_error!(kdl_options, "visual_bell").map(|(v, _)| v); Ok(Options { simplified_ui, @@ -2818,6 +2820,7 @@ impl Options { show_release_notes, advanced_mouse_actions, mouse_hover_effects, + visual_bell, web_server_ip, web_server_port, web_server_cert, @@ -3949,6 +3952,33 @@ impl Options { None } } + fn visual_bell_to_kdl(&self, add_comments: bool) -> Option<KdlNode> { + let comment_text = format!( + "{}\n{}\n{}", + " ", + "// Whether to show visual bell indicators (pane/tab frame flash and [!] suffix)", + "// default is true", + ); + + let create_node = |node_value: bool| -> KdlNode { + let mut node = KdlNode::new("visual_bell"); + node.push(KdlValue::Bool(node_value)); + node + }; + if let Some(visual_bell) = self.visual_bell { + let mut node = create_node(visual_bell); + if add_comments { + node.set_leading(format!("{}\n", comment_text)); + } + Some(node) + } else if add_comments { + let mut node = create_node(true); + node.set_leading(format!("{}\n// ", comment_text)); + Some(node) + } else { + None + } + } fn web_server_ip_to_kdl(&self, add_comments: bool) -> Option<KdlNode> { let comment_text = format!( "{}\n{}\n{}\n{}", @@ -4183,6 +4213,9 @@ impl Options { if let Some(mouse_hover_effects) = self.mouse_hover_effects_to_kdl(add_comments) { nodes.push(mouse_hover_effects); } + if let Some(visual_bell) = self.visual_bell_to_kdl(add_comments) { + nodes.push(visual_bell); + } if let Some(web_server_ip) = self.web_server_ip_to_kdl(add_comments) { nodes.push(web_server_ip); } @@ -5659,6 +5692,8 @@ impl TabInfo { selectable_tiled_panes_count, selectable_floating_panes_count, tab_id, + has_bell_notification: false, + is_flashing_bell: false, }) } pub fn encode_to_kdl(&self) -> KdlDocument { @@ -6090,6 +6125,8 @@ fn serialize_and_deserialize_session_info_with_data() { selectable_tiled_panes_count: 10, selectable_floating_panes_count: 10, tab_id: 0, + is_flashing_bell: false, + has_bell_notification: false, }, TabInfo { position: 1, @@ -6109,6 +6146,8 @@ fn serialize_and_deserialize_session_info_with_data() { selectable_tiled_panes_count: 10, selectable_floating_panes_count: 10, tab_id: 1, + is_flashing_bell: false, + has_bell_notification: false, }, ], panes: PaneManifest { panes }, 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 26a1be67..da967bcb 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 @@ -536,6 +536,10 @@ web_client { // default is true // mouse_hover_effects false +// Whether to show visual bell indicators (pane/tab frame flash and [!] suffix) +// default is true +// visual_bell true + // The ip address the web server should listen on when it starts // Default: "127.0.0.1" // (Requires restart) 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 18293ffb..e6a0ddda 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 @@ -256,6 +256,10 @@ web_sharing "disabled" // default is true // mouse_hover_effects false +// Whether to show visual bell indicators (pane/tab frame flash and [!] suffix) +// default is true +// visual_bell true + // The ip address the web server should listen on when it starts // Default: "127.0.0.1" // (Requires restart) diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto index 5daf3ecd..5aa119a9 100644 --- a/zellij-utils/src/plugin_api/event.proto +++ b/zellij-utils/src/plugin_api/event.proto @@ -431,6 +431,8 @@ message TabInfo { uint32 selectable_tiled_panes_count = 15; uint32 selectable_floating_panes_count = 16; uint32 tab_id = 17; + bool has_bell_notification = 18; + bool is_flashing_bell = 19; } message ModeUpdatePayload { diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs index 431e5e0f..3b135b6d 100644 --- a/zellij-utils/src/plugin_api/event.rs +++ b/zellij-utils/src/plugin_api/event.rs @@ -1633,6 +1633,8 @@ impl TryFrom<ProtobufTabInfo> for TabInfo { selectable_floating_panes_count: protobuf_tab_info.selectable_floating_panes_count as usize, tab_id: protobuf_tab_info.tab_id as usize, + has_bell_notification: protobuf_tab_info.has_bell_notification, + is_flashing_bell: protobuf_tab_info.is_flashing_bell, }) } } @@ -1662,6 +1664,8 @@ impl TryFrom<TabInfo> for ProtobufTabInfo { selectable_tiled_panes_count: tab_info.selectable_tiled_panes_count as u32, selectable_floating_panes_count: tab_info.selectable_floating_panes_count as u32, tab_id: tab_info.tab_id as u32, + has_bell_notification: tab_info.has_bell_notification, + is_flashing_bell: tab_info.is_flashing_bell, }) } } @@ -2150,6 +2154,8 @@ fn serialize_tab_update_event_with_non_default_values() { selectable_tiled_panes_count: 10, selectable_floating_panes_count: 10, tab_id: 0, + has_bell_notification: false, + is_flashing_bell: false, }, TabInfo { position: 1, @@ -2169,6 +2175,8 @@ fn serialize_tab_update_event_with_non_default_values() { selectable_tiled_panes_count: 10, selectable_floating_panes_count: 10, tab_id: 1, + has_bell_notification: false, + is_flashing_bell: false, }, TabInfo::default(), ]); @@ -2443,6 +2451,8 @@ fn serialize_session_update_event_with_non_default_values() { selectable_tiled_panes_count: 10, selectable_floating_panes_count: 10, tab_id: 0, + has_bell_notification: false, + is_flashing_bell: false, }, TabInfo { position: 1, @@ -2462,6 +2472,8 @@ fn serialize_session_update_event_with_non_default_values() { selectable_tiled_panes_count: 10, selectable_floating_panes_count: 10, tab_id: 1, + has_bell_notification: false, + is_flashing_bell: false, }, TabInfo::default(), ]; 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 5c62f124..2522ab46 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 @@ -40,6 +40,7 @@ Options { show_release_notes: None, advanced_mouse_actions: None, mouse_hover_effects: None, + visual_bell: None, web_server_ip: None, web_server_port: None, web_server_cert: 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 7b18b100..9ad64f6f 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 @@ -40,6 +40,7 @@ Options { show_release_notes: None, advanced_mouse_actions: None, mouse_hover_effects: None, + visual_bell: None, web_server_ip: None, web_server_port: None, web_server_cert: 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 0561e8be..1c16a2fd 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 @@ -38,6 +38,7 @@ Options { show_release_notes: None, advanced_mouse_actions: None, mouse_hover_effects: None, + visual_bell: None, web_server_ip: None, web_server_port: None, web_server_cert: 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 d794c05f..1684ff37 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 @@ -6007,6 +6007,7 @@ Config { show_release_notes: None, advanced_mouse_actions: None, mouse_hover_effects: None, + visual_bell: None, web_server_ip: None, web_server_port: None, web_server_cert: 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 4151c52a..01603ece 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 @@ -6007,6 +6007,7 @@ Config { show_release_notes: None, advanced_mouse_actions: None, mouse_hover_effects: None, + visual_bell: None, web_server_ip: None, web_server_port: None, web_server_cert: 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 eb5db32b..3ced7620 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 @@ -125,6 +125,7 @@ Config { show_release_notes: None, advanced_mouse_actions: None, mouse_hover_effects: None, + visual_bell: None, web_server_ip: None, web_server_port: None, web_server_cert: 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 78d7c05a..994932a4 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 @@ -40,6 +40,7 @@ Options { show_release_notes: None, advanced_mouse_actions: None, mouse_hover_effects: None, + visual_bell: None, web_server_ip: None, web_server_port: None, web_server_cert: 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 c7e71adf..ac52b53f 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 @@ -6007,6 +6007,7 @@ Config { show_release_notes: None, advanced_mouse_actions: None, mouse_hover_effects: None, + visual_bell: None, web_server_ip: None, web_server_port: None, web_server_cert: 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 b5211847..927928e0 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 @@ -6007,6 +6007,7 @@ Config { show_release_notes: None, advanced_mouse_actions: None, mouse_hover_effects: None, + visual_bell: None, web_server_ip: None, web_server_port: None, web_server_cert: None, |
