Repository: fujiapple852/trippy (language: rust)

Issue:
[BREAKING CHANGE] Replace `toggle-privacy` key binding with `expand-privacy` and  `contract-privacy`
Add `expand-privacy` and `contract-privacy` key bindings which increase and decrease `tui-privacy-max-ttl`.  The value is clamped in the range 0 (i.e. off) and the current maximum ttl of the trace.

## Deprecation

This is a breaking change as the existing `toggle-privacy` key binding has been deprecated.

If used from a configuration file it will result in an error:

```shell
$ sudo ./target/debug/trip 1.1.1.1
Error: toggle-privacy in [bindings] section is deprecated, use expand-privacy and contract-privacy instead
```

If used as a command line parameter (unlikely) it will result in an error:

```shell
$ sudo ./target/debug/trip 1.1.1.1 --tui-key-bindings toggle-privacy=o
error: invalid value 'toggle-privacy=o' for '--tui-key-bindings <TUI_KEY_BINDINGS>': toggle-privacy is deprecated, use expand-privacy and contract-privacy instead

For more information, try '--help'.
```


Discussion / hints:
Description of the feature from the 0.9.0 release note [here](https://github.com/fujiapple852/trippy/blob/master/RELEASES.md#hop-privacy).

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 31d0cbf97..e1268b232 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 - [BREAKING CHANGE] Remove `Timestamp` from all `DnsEntry`
   variants ([#1296](https://github.com/fujiapple852/trippy/issues/1296))
+- [BREAKING CHANGE] Replace `toggle-privacy` key binding with `expand-privacy` and
+  `contract-privacy` ([#1347](https://github.com/fujiapple852/trippy/issues/1347))
 
 ### Fixed
 
diff --git a/README.md b/README.md
index c36049e29..8a7fc7a8f 100644
--- a/README.md
+++ b/README.md
@@ -857,7 +857,8 @@ command line option or in the `bindings` section of the configuration file.
 | `toggle-chart`             | Toggle the chart                                | `c`      |
 | `toggle-map`               | Toggle the GeoIp map                            | `m`      |
 | `toggle-flows`             | Toggle the flows                                | `f`      |
-| `toggle-privacy`           | Toggle the hop privacy                          | `p`      |
+| `expand-privacy`           | Expand hop privacy                              | `p`      |
+| `contract-privacy`         | Contract hop privacy                            | `o`      |
 | `expand-hosts`             | Expand the hosts shown per hop                  | `]`      |
 | `expand-hosts-max`         | Expand the hosts shown per hop to the maximum   | `}`      |
 | `contract-hosts`           | Contract the hosts shown per hop                | `[`      |
diff --git a/RELEASES.md b/RELEASES.md
index 892bb2451..6d3f7f8b8 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -37,6 +37,30 @@ samples-chart-lost-color = "blue"
 
 See [1247](https://github.com/fujiapple852/trippy/issues/1247) for more details.
 
+### Adjustable Hop Privacy Mode Settings
+
+Trippy has a privacy feature which can be used to hide sensitive information, such as IP address and GeoIp, for all hops
+up to a configurable `tui-privacy-max-ttl`.
+
+Today, the privacy feature can be toggled on and off from within the TUI using the `toggle-privacy` TUI command (bound
+to the `p` key by default), but only when `tui-privacy-max-ttl` is set to be greater than the default value of 0. If
+`tui-privacy-max-ttl` is set to be greater than 0, then the privacy mode will be enabled by default when Trippy
+starts. This behaviour is inconvenient for users who wish to enable privacy mode _after_ starting Trippy, as there is no
+way to enable it without first setting `tui-privacy-max-ttl` to be greater than 0 and restarting Trippy.
+
+Starting from this release, the `toggle-privacy` TUI command has been deprecated and replaced with the `expand-privacy`
+(bound to the `p` key by default) and `contract-privacy` (bound to the `o` key by default) TUI commands. The
+`expand-privacy` command will increase the `tui-privacy-max-ttl` value up to the maximum number of hops in the current
+trace, and the `contract-privacy` command will decrease the `tui-privacy-max-ttl` value down to 0.
+
+This allows users to adjust the number of hop that will be hidden at any time during a trace without needing to restart
+Trippy.
+
+See the [Key Bindings Reference](https://github.com/fujiapple852/trippy#key-bindings-reference) for details of how to
+adjust the default key bindings.
+
+See [#1347](https://github.com/fujiapple852/trippy/issues/1347) for more details.
+
 ### Thanks
 
 My thanks to all Trippy contributors, package maintainers and community members.
diff --git a/crates/trippy-tui/src/config.rs b/crates/trippy-tui/src/config.rs
index 01fb1521a..727e95d2c 100644
--- a/crates/trippy-tui/src/config.rs
+++ b/crates/trippy-tui/src/config.rs
@@ -20,7 +20,7 @@ mod constants;
 mod file;
 mod theme;
 
-use crate::config::file::ConfigTui;
+use crate::config::file::{ConfigBindings, ConfigTui};
 pub use binding::{TuiBindings, TuiCommandItem, TuiKeyBinding};
 pub use cmd::Args;
 pub use columns::{TuiColumn, TuiColumns};
@@ -364,7 +364,7 @@ impl TrippyConfig {
         let cfg_file_tui = cfg_file.tui.unwrap_or_default();
         let cfg_file_dns = cfg_file.dns.unwrap_or_default();
         let cfg_file_report = cfg_file.report.unwrap_or_default();
-        validate_deprecated(&cfg_file_tui)?;
+        validate_deprecated(&cfg_file_tui, &cfg_file_tui_bindings)?;
         let mode = cfg_layer(args.mode, cfg_file_trace.mode, constants::DEFAULT_MODE);
         let unprivileged = cfg_layer_bool_flag(
             args.unprivileged,
@@ -811,11 +811,16 @@ const fn cfg_layer_bool_flag(fst: bool, snd: Option<bool>, default: bool) -> boo
 }
 
 /// Check for deprecated fields.
-fn validate_deprecated(cfg_file_tui: &ConfigTui) -> anyhow::Result<()> {
+fn validate_deprecated(
+    cfg_file_tui: &ConfigTui,
+    cfg_file_tui_bindings: &ConfigBindings,
+) -> anyhow::Result<()> {
     if cfg_file_tui.deprecated_tui_max_samples.is_some() {
         Err(anyhow!("tui-max-samples in [tui] section is deprecated, use max-samples in [strategy] section instead"))
     } else if cfg_file_tui.deprecated_tui_max_flows.is_some() {
         Err(anyhow!("tui-max-flows in [tui] section is deprecated, use max-flows in [strategy] section instead"))
+    } else if cfg_file_tui_bindings.deprecated_toggle_privacy.is_some() {
+        Err(anyhow!("toggle-privacy in [bindings] section is deprecated, use expand-privacy and contract-privacy instead"))
     } else {
         Ok(())
     }
@@ -1646,6 +1651,7 @@ mod tests {
 
     #[test_case("trip example.com --tui-max-samples foo", Err(anyhow!("error: unexpected argument '--tui-max-samples' found")); "deprecated tui max samples")]
     #[test_case("trip example.com --tui-max-flows foo", Err(anyhow!("error: unexpected argument '--tui-max-flows' found")); "deprecated tui max flows")]
+    #[test_case("trip example.com --tui-key-bindings toggle-privacy=o", Err(anyhow!("error: invalid value 'toggle-privacy=o' for '--tui-key-bindings <TUI_KEY_BINDINGS>': toggle-privacy is deprecated, use expand-privacy and contract-privacy instead")); "deprecated toggle-privacy key binding")]
     fn test_deprecated(cmd: &str, expected: anyhow::Result<TrippyConfig>) {
         compare_lines(parse_config(cmd), expected, Some(0));
     }
diff --git a/crates/trippy-tui/src/config/binding.rs b/crates/trippy-tui/src/config/binding.rs
index c0be2f42c..4658849e7 100644
--- a/crates/trippy-tui/src/config/binding.rs
+++ b/crates/trippy-tui/src/config/binding.rs
@@ -33,7 +33,8 @@ pub struct TuiBindings {
     pub toggle_chart: TuiKeyBinding,
     pub toggle_map: TuiKeyBinding,
     pub toggle_flows: TuiKeyBinding,
-    pub toggle_privacy: TuiKeyBinding,
+    pub expand_privacy: TuiKeyBinding,
+    pub contract_privacy: TuiKeyBinding,
     pub expand_hosts: TuiKeyBinding,
     pub contract_hosts: TuiKeyBinding,
     pub expand_hosts_max: TuiKeyBinding,
@@ -77,7 +78,8 @@ impl Default for TuiBindings {
             toggle_chart: TuiKeyBinding::new(KeyCode::Char('c')),
             toggle_map: TuiKeyBinding::new(KeyCode::Char('m')),
             toggle_flows: TuiKeyBinding::new(KeyCode::Char('f')),
-            toggle_privacy: TuiKeyBinding::new(KeyCode::Char('p')),
+            expand_privacy: TuiKeyBinding::new(KeyCode::Char('p')),
+            contract_privacy: TuiKeyBinding::new(KeyCode::Char('o')),
             expand_hosts: TuiKeyBinding::new(KeyCode::Char(']')),
             contract_hosts: TuiKeyBinding::new(KeyCode::Char('[')),
             expand_hosts_max: TuiKeyBinding::new(KeyCode::Char('}')),
@@ -135,7 +137,8 @@ impl TuiBindings {
             (self.toggle_chart, TuiCommandItem::ToggleChart),
             (self.toggle_map, TuiCommandItem::ToggleMap),
             (self.toggle_flows, TuiCommandItem::ToggleFlows),
-            (self.toggle_privacy, TuiCommandItem::TogglePrivacy),
+            (self.expand_privacy, TuiCommandItem::ExpandPrivacy),
+            (self.contract_privacy, TuiCommandItem::ContractPrivacy),
             (self.expand_hosts, TuiCommandItem::ExpandHosts),
             (self.expand_hosts_max, TuiCommandItem::ExpandHostsMax),
             (self.contract_hosts, TuiCommandItem::ContractHosts),
@@ -263,10 +266,14 @@ impl From<(HashMap<TuiCommandItem, TuiKeyBinding>, ConfigBindings)> for TuiBindi
                 .get(&TuiCommandItem::ToggleFlows)
                 .or(cfg.toggle_flows.as_ref())
                 .unwrap_or(&Self::default().toggle_flows),
-            toggle_privacy: *cmd_items
-                .get(&TuiCommandItem::ToggleFlows)
-                .or(cfg.toggle_privacy.as_ref())
-                .unwrap_or(&Self::default().toggle_privacy),
+            expand_privacy: *cmd_items
+                .get(&TuiCommandItem::ExpandPrivacy)
+                .or(cfg.expand_privacy.as_ref())
+                .unwrap_or(&Self::default().expand_privacy),
+            contract_privacy: *cmd_items
+                .get(&TuiCommandItem::ContractPrivacy)
+                .or(cfg.contract_privacy.as_ref())
+                .unwrap_or(&Self::default().contract_privacy),
             toggle_map: *cmd_items
                 .get(&TuiCommandItem::ToggleMap)
                 .or(cfg.toggle_map.as_ref())
@@ -589,7 +596,14 @@ pub enum TuiCommandItem {
     /// Toggle the flows panel.
     ToggleFlows,
     /// Toggle hop privacy mode.
-    TogglePrivacy,
+    ///
+    /// Deprecated: use `ExpandPrivacy` and `ContractPrivacy` instead.
+    #[strum(serialize = "toggle-privacy")]
+    DeprecatedTogglePrivacy,
+    /// Expand hop privacy.
+    ExpandPrivacy,
+    /// Contract hop privacy.
+    ContractPrivacy,
     /// Expand hosts.
     ExpandHosts,
     /// Expand hosts to max.
diff --git a/crates/trippy-tui/src/config/cmd.rs b/crates/trippy-tui/src/config/cmd.rs
index 2d5de8d9f..647714fa5 100644
--- a/crates/trippy-tui/src/config/cmd.rs
+++ b/crates/trippy-tui/src/config/cmd.rs
@@ -292,6 +292,11 @@ fn parse_tui_binding_value(value: &str) -> anyhow::Result<(TuiCommandItem, TuiKe
         .ok_or_else(|| anyhow!("invalid binding value: expected format `item=value`"))?;
     let item = TuiCommandItem::try_from(&value[..pos])?;
     let binding = TuiKeyBinding::try_from(&value[pos + 1..])?;
+    if item == TuiCommandItem::DeprecatedTogglePrivacy {
+        return Err(anyhow!(
+            "toggle-privacy is deprecated, use expand-privacy and contract-privacy instead"
+        ));
+    }
     Ok((item, binding))
 }
 
diff --git a/crates/trippy-tui/src/config/file.rs b/crates/trippy-tui/src/config/file.rs
index 9edafab6b..73447a562 100644
--- a/crates/trippy-tui/src/config/file.rs
+++ b/crates/trippy-tui/src/config/file.rs
@@ -377,7 +377,10 @@ pub struct ConfigBindings {
     pub toggle_freeze: Option<TuiKeyBinding>,
     pub toggle_chart: Option<TuiKeyBinding>,
     pub toggle_flows: Option<TuiKeyBinding>,
-    pub toggle_privacy: Option<TuiKeyBinding>,
+    #[serde(rename = "toggle-privacy")]
+    pub deprecated_toggle_privacy: Option<TuiKeyBinding>,
+    pub expand_privacy: Option<TuiKeyBinding>,
+    pub contract_privacy: Option<TuiKeyBinding>,
     pub toggle_map: Option<TuiKeyBinding>,
     pub expand_hosts: Option<TuiKeyBinding>,
     pub contract_hosts: Option<TuiKeyBinding>,
@@ -419,7 +422,9 @@ impl Default for ConfigBindings {
             toggle_freeze: Some(bindings.toggle_freeze),
             toggle_chart: Some(bindings.toggle_chart),
             toggle_flows: Some(bindings.toggle_flows),
-            toggle_privacy: Some(bindings.toggle_privacy),
+            deprecated_toggle_privacy: None,
+            expand_privacy: Some(bindings.expand_privacy),
+            contract_privacy: Some(bindings.contract_privacy),
             toggle_map: Some(bindings.toggle_map),
             expand_hosts: Some(bindings.expand_hosts),
             contract_hosts: Some(bindings.contract_hosts),
diff --git a/crates/trippy-tui/src/frontend.rs b/crates/trippy-tui/src/frontend.rs
index 4d1f4e3c3..3f1011611 100644
--- a/crates/trippy-tui/src/frontend.rs
+++ b/crates/trippy-tui/src/frontend.rs
@@ -195,8 +195,10 @@ fn run_app<B: Backend>(
                         app.toggle_map();
                     } else if bindings.toggle_flows.check(key) {
                         app.toggle_flows();
-                    } else if bindings.toggle_privacy.check(key) {
-                        app.toggle_privacy();
+                    } else if bindings.expand_privacy.check(key) {
+                        app.expand_privacy();
+                    } else if bindings.contract_privacy.check(key) {
+                        app.contract_privacy();
                     } else if bindings.contract_hosts_min.check(key) {
                         app.contract_hosts_min();
                     } else if bindings.expand_hosts_max.check(key) {
diff --git a/crates/trippy-tui/src/frontend/binding.rs b/crates/trippy-tui/src/frontend/binding.rs
index 8bdd5123a..8e0189b98 100644
--- a/crates/trippy-tui/src/frontend/binding.rs
+++ b/crates/trippy-tui/src/frontend/binding.rs
@@ -29,7 +29,8 @@ pub struct Bindings {
     pub toggle_chart: KeyBinding,
     pub toggle_map: KeyBinding,
     pub toggle_flows: KeyBinding,
-    pub toggle_privacy: KeyBinding,
+    pub expand_privacy: KeyBinding,
+    pub contract_privacy: KeyBinding,
     pub expand_hosts: KeyBinding,
     pub contract_hosts: KeyBinding,
     pub expand_hosts_max: KeyBinding,
@@ -70,7 +71,8 @@ impl From<TuiBindings> for Bindings {
             toggle_chart: KeyBinding::from(value.toggle_chart),
             toggle_map: KeyBinding::from(value.toggle_map),
             toggle_flows: KeyBinding::from(value.toggle_flows),
-            toggle_privacy: KeyBinding::from(value.toggle_privacy),
+            expand_privacy: KeyBinding::from(value.expand_privacy),
+            contract_privacy: KeyBinding::from(value.contract_privacy),
             expand_hosts: KeyBinding::from(value.expand_hosts),
             contract_hosts: KeyBinding::from(value.contract_hosts),
             expand_hosts_max: KeyBinding::from(value.expand_hosts_max),
diff --git a/crates/trippy-tui/src/frontend/render/header.rs b/crates/trippy-tui/src/frontend/render/header.rs
index eed331285..4bae1ab1f 100644
--- a/crates/trippy-tui/src/frontend/render/header.rs
+++ b/crates/trippy-tui/src/frontend/render/header.rs
@@ -82,11 +82,7 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
         .tui_config
         .max_addrs
         .map_or_else(|| String::from(t!("auto")), |m| m.to_string());
-    let privacy = if app.hide_private_hops && app.tui_config.privacy_max_ttl > 0 {
-        t!("on")
-    } else {
-        t!("off")
-    };
+    let privacy = app.tui_config.privacy_max_ttl;
     let source = render_source(app);
     let dest = render_destination(app);
     let target = format!("{source} -> {dest}");
diff --git a/crates/trippy-tui/src/frontend/render/settings.rs b/crates/trippy-tui/src/frontend/render/settings.rs
index 1f3edd447..6beff5108 100644
--- a/crates/trippy-tui/src/frontend/render/settings.rs
+++ b/crates/trippy-tui/src/frontend/render/settings.rs
@@ -371,7 +371,8 @@ fn format_binding_settings(app: &TuiApp) -> Vec<SettingsItem> {
         SettingsItem::new("toggle-chart", format!("{}", binds.toggle_chart)),
         SettingsItem::new("toggle-map", format!("{}", binds.toggle_map)),
         SettingsItem::new("toggle-flows", format!("{}", binds.toggle_flows)),
-        SettingsItem::new("toggle-privacy", format!("{}", binds.toggle_privacy)),
+        SettingsItem::new("expand-privacy", format!("{}", binds.expand_privacy)),
+        SettingsItem::new("contract-privacy", format!("{}", binds.contract_privacy)),
         SettingsItem::new("expand-hosts", format!("{}", binds.expand_hosts)),
         SettingsItem::new("expand-hosts-max", format!("{}", binds.expand_hosts_max)),
         SettingsItem::new("contract-hosts", format!("{}", binds.contract_hosts)),
@@ -523,7 +524,7 @@ pub fn settings_tabs() -> [(String, usize); 7] {
         (t!("settings_tab_trace_title").to_string(), 17),
         (t!("settings_tab_dns_title").to_string(), 5),
         (t!("settings_tab_geoip_title").to_string(), 1),
-        (t!("settings_tab_bindings_title").to_string(), 36),
+        (t!("settings_tab_bindings_title").to_string(), 37),
         (t!("settings_tab_theme_title").to_string(), 31),
         (t!("settings_tab_columns_title").to_string(), 0),
     ]
diff --git a/crates/trippy-tui/src/frontend/render/table.rs b/crates/trippy-tui/src/frontend/render/table.rs
index 17679a3a0..88842b260 100644
--- a/crates/trippy-tui/src/frontend/render/table.rs
+++ b/crates/trippy-tui/src/frontend/render/table.rs
@@ -262,7 +262,7 @@ fn render_hostname(
     geoip_lookup: &GeoIpLookup,
 ) -> (Cell<'static>, u16) {
     let (hostname, count) = if hop.total_recv() > 0 {
-        if app.hide_private_hops && app.tui_config.privacy_max_ttl >= hop.ttl() {
+        if app.tui_config.privacy_max_ttl >= hop.ttl() {
             (format!("**{}**", t!("hidden")), 1)
         } else {
             match app.tui_config.max_addrs {
@@ -512,7 +512,7 @@ fn render_hostname_with_details(
     config: &TuiConfig,
 ) -> (Cell<'static>, u16) {
     let rendered = if hop.total_recv() > 0 {
-        if app.hide_private_hops && config.privacy_max_ttl >= hop.ttl() {
+        if config.privacy_max_ttl >= hop.ttl() {
             format!("**{}**", t!("hidden"))
         } else {
             let index = app.selected_hop_address;
diff --git a/crates/trippy-tui/src/frontend/render/world.rs b/crates/trippy-tui/src/frontend/render/world.rs
index 4b23df9d5..1c09ec207 100644
--- a/crates/trippy-tui/src/frontend/render/world.rs
+++ b/crates/trippy-tui/src/frontend/render/world.rs
@@ -51,7 +51,7 @@ fn render_map_canvas(f: &mut Frame<'_>, app: &TuiApp, rect: Rect, entries: &[Map
                     .hops
                     .iter()
                     .any(|hop| *hop > app.tui_config.privacy_max_ttl);
-                if !app.hide_private_hops || any_show {
+                if any_show {
                     render_map_canvas_pin(ctx, entry);
                     render_map_canvas_radius(ctx, entry, theme.map_radius);
                     render_map_canvas_selected(
@@ -146,7 +146,7 @@ fn render_map_info_panel(f: &mut Frame<'_>, app: &TuiApp, rect: Rect, entries: &
             }
         })
         .collect::<Vec<_>>();
-    let info = if app.hide_private_hops && app.tui_config.privacy_max_ttl >= selected_hop.ttl() {
+    let info = if app.tui_config.privacy_max_ttl >= selected_hop.ttl() {
         format!("**{}**", t!("hidden"))
     } else {
         match locations.as_slice() {
diff --git a/crates/trippy-tui/src/frontend/tui_app.rs b/crates/trippy-tui/src/frontend/tui_app.rs
index 17b59afc3..5947bf3ec 100644
--- a/crates/trippy-tui/src/frontend/tui_app.rs
+++ b/crates/trippy-tui/src/frontend/tui_app.rs
@@ -38,8 +38,6 @@ pub struct TuiApp {
     pub show_settings: bool,
     pub show_hop_details: bool,
     pub show_flows: bool,
-    /// Whether private hops should be shown or not.
-    pub hide_private_hops: bool,
     pub show_chart: bool,
     pub show_map: bool,
     pub frozen_start: Option<SystemTime>,
@@ -70,7 +68,6 @@ impl TuiApp {
             show_settings: false,
             show_hop_details: false,
             show_flows: false,
-            hide_private_hops: true,
             show_chart: false,
             show_map: false,
             frozen_start: None,
@@ -382,8 +379,17 @@ impl TuiApp {
         }
     }
 
-    pub fn toggle_privacy(&mut self) {
-        self.hide_private_hops = !self.hide_private_hops;
+    pub fn expand_privacy(&mut self) {
+        let hop_count = self.tracer_data().hops_for_flow(self.selected_flow).len();
+        if usize::from(self.tui_config.privacy_max_ttl) < hop_count {
+            self.tui_config.privacy_max_ttl += 1;
+        }
+    }
+
+    pub fn contract_privacy(&mut self) {
+        if self.tui_config.privacy_max_ttl > 0 {
+            self.tui_config.privacy_max_ttl -= 1;
+        }
     }
 
     pub fn toggle_asinfo(&mut self) {
diff --git a/trippy-config-sample.toml b/trippy-config-sample.toml
index 355a54304..859a99925 100644
--- a/trippy-config-sample.toml
+++ b/trippy-config-sample.toml
@@ -324,7 +324,7 @@ tui-preserve-screen = false
 # The Tui refresh rate [default: 100ms]
 tui-refresh-rate = "100ms"
 
-# The maximum ttl of hops which will be masked for privacy [default: 1]
+# The maximum ttl of hops which will be masked for privacy [default: 0]
 tui-privacy-max-ttl = 0
 
 # The locale to use for Tui [default: auto]
@@ -408,7 +408,8 @@ toggle-freeze = "ctrl+f"
 toggle-chart = "c"
 toggle-map = "m"
 toggle-flows = "f"
-toggle-privacy = "p"
+expand-privacy = "p"
+contract-privacy = "o"
 expand-hosts = "]"
 expand-hosts-max = "}"
 contract-hosts = "["


Test patch:
diff --git a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@tui_binding_commands_match.snap b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@tui_binding_commands_match.snap
index 9de8c55c1..21d0a648a 100644
--- a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@tui_binding_commands_match.snap
+++ b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@tui_binding_commands_match.snap
@@ -1,4 +1,4 @@
 ---
 source: crates/trippy-tui/src/print.rs
 ---
-TUIbindingcommands:toggle-help,toggle-help-alt,toggle-settings,toggle-settings-tui,toggle-settings-trace,toggle-settings-dns,toggle-settings-geoip,toggle-settings-bindings,toggle-settings-theme,toggle-settings-columns,next-hop,previous-hop,next-trace,previous-trace,next-hop-address,previous-hop-address,address-mode-ip,address-mode-host,address-mode-both,toggle-freeze,toggle-chart,toggle-map,toggle-flows,toggle-privacy,expand-hosts,expand-hosts-max,contract-hosts,contract-hosts-min,chart-zoom-in,chart-zoom-out,clear-trace-data,clear-dns-cache,clear-selection,toggle-as-info,toggle-hop-details,quit
+TUIbindingcommands:toggle-help,toggle-help-alt,toggle-settings,toggle-settings-tui,toggle-settings-trace,toggle-settings-dns,toggle-settings-geoip,toggle-settings-bindings,toggle-settings-theme,toggle-settings-columns,next-hop,previous-hop,next-trace,previous-trace,next-hop-address,previous-hop-address,address-mode-ip,address-mode-host,address-mode-both,toggle-freeze,toggle-chart,toggle-map,toggle-flows,toggle-privacy,expand-privacy,contract-privacy,expand-hosts,expand-hosts-max,contract-hosts,contract-hosts-min,chart-zoom-in,chart-zoom-out,clear-trace-data,clear-dns-cache,clear-selection,toggle-as-info,toggle-hop-details,quit