Repository: fujiapple852/trippy (rust)

Issue:
Add information bar
Add a "information bar" at the bottom of the TUI to show dynamically configurable settings such as whether to display 
autonomous system (AS) information, whether to show hop details and so forth.

Once added, remove the corresponding information from the left hand side of the header and reduce the header size by one line to make it more compact.

This improves the overall look-and-feel, especially for non-`en` locales.

The TUI vertical height is net neural after this change.

Adds two new color theme items:

```
info-bar-bg-color = "white"
info-bar-text-color = "black"
```

Preview:

<img width="848" alt="Screenshot 2024-10-21 at 9 47 27 PM" src="https://github.com/user-attachments/assets/c94582d2-0347-499d-96bd-9d37519eca40">


Pull request description:
Add Information Bar
Closes #1349 

Patch:
diff --git a/crates/trippy-tui/locales/app.yml b/crates/trippy-tui/locales/app.yml
index 13cb70a..900ccfa 100644
--- a/crates/trippy-tui/locales/app.yml
+++ b/crates/trippy-tui/locales/app.yml
@@ -76,13 +76,6 @@ target:
   it: "Target"
   pt: "Alvo"
   zh: "目标"
-config:
-  en: "Config"
-  fr: "Config"
-  tr: "Konfigurasyon"
-  it: "Config"
-  pt: "Config"
-  zh: "配置"
 status:
   en: "Status"
   fr: "Statut"
@@ -90,41 +83,13 @@ status:
   it: "Stato"
   pt: "Estado"
   zh: "状态"
-protocol:
-  en: "protocol"
-  fr: "protocole"
-  tr: "protokol"
-  it: "protocollo"
-  pt: "protocolo"
-  zh: "协议"
-as-info:
-  en: "as-info"
-  fr: "info-as"
-  tr: "AS bilgisi"
-  it: "info-as"
-  pt: "info-as"
-  zh: "AS 信息"
 details:
-  en: "details"
-  fr: "détails"
+  en: "detail"
+  fr: "détail"
   tr: "ayrıntılar"
   it: "dettagli"
-  pt: "detalhes"
+  pt: "detalhe"
   zh: "详情"
-max-hosts:
-  en: "max-hosts"
-  fr: "nombre d'hôtes maximum"
-  tr: "maksimum hostlar"
-  it: "max-hosts"
-  pt: "max-hosts"
-  zh: "最大主机数"
-privacy:
-  en: "privacy"
-  fr: "confidentialité"
-  tr: "gizlilik"
-  it: "privacy"
-  pt: "privacidade"
-  zh: "隐私"
 privileged:
   en: "privileged"
   fr: "privilégié"
@@ -224,25 +189,25 @@ awaiting_data:
   pt: "Aguardando dados..."
   zh: "等待数据……"
 header_help:
-  en: "elp"
-  fr: "Aide"
-  tr: "Yardım"
-  it: "Aiuto"
-  pt: "Ajuda"
+  en: "help"
+  fr: "aide"
+  tr: "yardım"
+  it: "aiuto"
+  pt: "ajuda"
   zh: "帮助"
 header_settings:
-  en: "ettings"
-  fr: "Paramètres"
-  tr: "Ayarlar"
-  it: "Impostazioni"
-  pt: "Configurações"
+  en: "settings"
+  fr: "paramètres"
+  tr: "ayarlar"
+  it: "impostazioni"
+  pt: "configurações"
   zh: "设置"
 header_quit:
-  en: "uit"
-  fr: "Quitter"
-  tr: "Çıkış"
-  it: "Uscita"
-  pt: "Sair"
+  en: "quit"
+  fr: "quitter"
+  tr: "cıkış"
+  it: "uscita"
+  pt: "sair"
   zh: "退出"
 title_hops:
   en: "Hops"
diff --git a/crates/trippy-tui/src/app.rs b/crates/trippy-tui/src/app.rs
index cae4c70..77a09ec 100644
--- a/crates/trippy-tui/src/app.rs
+++ b/crates/trippy-tui/src/app.rs
@@ -16,9 +16,10 @@ use trippy_privilege::Privilege;
 /// Run the trippy application.
 pub fn run_trippy(cfg: &TrippyConfig, pid: u16) -> anyhow::Result<()> {
     set_locale(cfg.tui_locale.as_deref());
+    let locale = locale();
     let _guard = configure_logging(cfg);
     let resolver = start_dns_resolver(cfg)?;
-    let geoip_lookup = create_geoip_lookup(cfg, locale())?;
+    let geoip_lookup = create_geoip_lookup(cfg, &locale)?;
     let addrs = resolve_targets(cfg, &resolver)?;
     if addrs.is_empty() {
         return Err(anyhow!(
@@ -29,7 +30,7 @@ pub fn run_trippy(cfg: &TrippyConfig, pid: u16) -> anyhow::Result<()> {
     }
     let traces = start_tracers(cfg, &addrs, pid)?;
     Privilege::drop_privileges()?;
-    run_frontend(cfg, resolver, geoip_lookup, traces)
+    run_frontend(cfg, locale, resolver, geoip_lookup, traces)
 }
 
 /// Start all tracers.
@@ -87,12 +88,18 @@ fn start_tracer(
 /// Run the TUI, stream or report.
 fn run_frontend(
     args: &TrippyConfig,
+    locale: String,
     resolver: DnsResolver,
     geoip_lookup: GeoIpLookup,
     traces: Vec<TraceInfo>,
 ) -> anyhow::Result<()> {
     match args.mode {
-        Mode::Tui => frontend::run_frontend(traces, make_tui_config(args), resolver, geoip_lookup)?,
+        Mode::Tui => frontend::run_frontend(
+            traces,
+            make_tui_config(args, locale),
+            resolver,
+            geoip_lookup,
+        )?,
         Mode::Stream => report::stream::report(&traces[0], &resolver)?,
         Mode::Csv => report::csv::report(&traces[0], args.report_cycles, &resolver)?,
         Mode::Json => report::json::report(&traces[0], args.report_cycles, &resolver)?,
@@ -141,9 +148,9 @@ fn start_dns_resolver(cfg: &TrippyConfig) -> anyhow::Result<DnsResolver> {
     ))?)
 }
 
-fn create_geoip_lookup(cfg: &TrippyConfig, locale: String) -> anyhow::Result<GeoIpLookup> {
+fn create_geoip_lookup(cfg: &TrippyConfig, locale: &str) -> anyhow::Result<GeoIpLookup> {
     if let Some(path) = cfg.geoip_mmdb_file.as_ref() {
-        GeoIpLookup::from_file(path, locale)
+        GeoIpLookup::from_file(path, String::from(locale))
     } else {
         Ok(GeoIpLookup::empty())
     }
@@ -192,7 +199,7 @@ fn configure_logging(cfg: &TrippyConfig) -> Option<FlushGuard> {
 }
 
 /// Make the TUI configuration.
-fn make_tui_config(args: &TrippyConfig) -> TuiConfig {
+fn make_tui_config(args: &TrippyConfig, locale: String) -> TuiConfig {
     TuiConfig::new(
         args.tui_refresh_rate,
         args.tui_privacy_max_ttl,
@@ -208,6 +215,7 @@ fn make_tui_config(args: &TrippyConfig) -> TuiConfig {
         &args.tui_custom_columns,
         args.geoip_mmdb_file.clone(),
         args.dns_resolve_all,
+        locale,
     )
 }
 
diff --git a/crates/trippy-tui/src/config/file.rs b/crates/trippy-tui/src/config/file.rs
index 73447a5..13e6e6a 100644
--- a/crates/trippy-tui/src/config/file.rs
+++ b/crates/trippy-tui/src/config/file.rs
@@ -310,6 +310,8 @@ pub struct ConfigThemeColors {
     pub map_info_panel_border_color: Option<TuiColor>,
     pub map_info_panel_bg_color: Option<TuiColor>,
     pub map_info_panel_text_color: Option<TuiColor>,
+    pub info_bar_bg_color: Option<TuiColor>,
+    pub info_bar_text_color: Option<TuiColor>,
 }
 
 impl Default for ConfigThemeColors {
@@ -348,6 +350,8 @@ impl Default for ConfigThemeColors {
             map_info_panel_border_color: Some(theme.map_info_panel_border),
             map_info_panel_bg_color: Some(theme.map_info_panel_bg),
             map_info_panel_text_color: Some(theme.map_info_panel_text),
+            info_bar_bg_color: Some(theme.info_bar_bg),
+            info_bar_text_color: Some(theme.info_bar_text),
         }
     }
 }
diff --git a/crates/trippy-tui/src/config/theme.rs b/crates/trippy-tui/src/config/theme.rs
index 2a60522..dcf2f3b 100644
--- a/crates/trippy-tui/src/config/theme.rs
+++ b/crates/trippy-tui/src/config/theme.rs
@@ -77,6 +77,10 @@ pub struct TuiTheme {
     pub map_info_panel_bg: TuiColor,
     /// The color of text in the map info panel.
     pub map_info_panel_text: TuiColor,
+    /// The color of the info bar background.
+    pub info_bar_bg: TuiColor,
+    /// The color of the info bar text.
+    pub info_bar_text: TuiColor,
 }
 
 impl Default for TuiTheme {
@@ -114,6 +118,8 @@ impl Default for TuiTheme {
             map_info_panel_border: TuiColor::Gray,
             map_info_panel_bg: TuiColor::Black,
             map_info_panel_text: TuiColor::Gray,
+            info_bar_bg: TuiColor::White,
+            info_bar_text: TuiColor::Black,
         }
     }
 }
@@ -251,6 +257,14 @@ impl From<(HashMap<TuiThemeItem, TuiColor>, ConfigThemeColors)> for TuiTheme {
                 .get(&TuiThemeItem::MapInfoPanelTextColor)
                 .or(cfg.map_info_panel_text_color.as_ref())
                 .unwrap_or(&Self::default().map_info_panel_text),
+            info_bar_bg: *color_map
+                .get(&TuiThemeItem::InfoBarBgColor)
+                .or(cfg.info_bar_bg_color.as_ref())
+                .unwrap_or(&Self::default().info_bar_bg),
+            info_bar_text: *color_map
+                .get(&TuiThemeItem::InfoBarTextColor)
+                .or(cfg.info_bar_text_color.as_ref())
+                .unwrap_or(&Self::default().info_bar_text),
         }
     }
 }
@@ -324,6 +338,10 @@ pub enum TuiThemeItem {
     MapInfoPanelBgColor,
     /// The color of text in the map info panel.
     MapInfoPanelTextColor,
+    /// The color of the info bar background.
+    InfoBarBgColor,
+    /// The color of the info bar text.
+    InfoBarTextColor,
 }
 
 /// A TUI color.
diff --git a/crates/trippy-tui/src/frontend/config.rs b/crates/trippy-tui/src/frontend/config.rs
index cd8d4dd..39d6c9a 100644
--- a/crates/trippy-tui/src/frontend/config.rs
+++ b/crates/trippy-tui/src/frontend/config.rs
@@ -34,6 +34,8 @@ pub struct TuiConfig {
     pub tui_columns: Columns,
     pub geoip_mmdb_file: Option<String>,
     pub dns_resolve_all: bool,
+    /// The current locale.
+    pub locale: String,
 }
 
 impl TuiConfig {
@@ -51,9 +53,9 @@ impl TuiConfig {
         tui_theme: TuiTheme,
         tui_bindings: &TuiBindings,
         tui_columns: &TuiColumns,
-
         geoip_mmdb_file: Option<String>,
         dns_resolve_all: bool,
+        locale: String,
     ) -> Self {
         Self {
             refresh_rate,
@@ -70,6 +72,7 @@ impl TuiConfig {
             tui_columns: Columns::from(tui_columns.clone()),
             geoip_mmdb_file,
             dns_resolve_all,
+            locale,
         }
     }
 }
diff --git a/crates/trippy-tui/src/frontend/render.rs b/crates/trippy-tui/src/frontend/render.rs
index 82b2c22..8a77dc7 100644
--- a/crates/trippy-tui/src/frontend/render.rs
+++ b/crates/trippy-tui/src/frontend/render.rs
@@ -1,4 +1,5 @@
 pub mod app;
+pub mod bar;
 pub mod body;
 pub mod bsod;
 pub mod chart;
diff --git a/crates/trippy-tui/src/frontend/render/app.rs b/crates/trippy-tui/src/frontend/render/app.rs
index 0e93d0a..439e868 100644
--- a/crates/trippy-tui/src/frontend/render/app.rs
+++ b/crates/trippy-tui/src/frontend/render/app.rs
@@ -1,4 +1,4 @@
-use crate::frontend::render::{body, flows, footer, header, help, settings, tabs};
+use crate::frontend::render::{bar, body, flows, footer, header, help, settings, tabs};
 use crate::frontend::tui_app::TuiApp;
 use ratatui::layout::{Constraint, Direction, Layout};
 use ratatui::Frame;
@@ -25,14 +25,15 @@ use ratatui::Frame;
 /// |     History     |    Frequency     |
 /// |                 |                  |
 ///  ------------------------------------
+///  ====== info configuration bar ======
 ///
-/// - Header: the title, configuration, destination, clock and keyboard controls
-/// - Tab: a tab for each target being traced (shown if > 1 target requested, can't be used with
-///   flows)
+/// - Header: the title, target, clock and basic keyboard controls
+/// - Tab: a tab for each target (shown if > 1 target requested, can't be used with flows)
 /// - Flows: a navigable chart of individual trace flows (toggled on/off, can't be used with tabs)
 /// - Hops: a table where each row represents a single hop (time-to-live) in the trace
 /// - History: a graph of historic round-trip ping samples for the target host
 /// - Frequency: a histogram of sample frequencies by round-trip time for the target host
+/// - Info bar: a bar showing the current value for configurable items
 ///
 /// On startup a splash screen is shown in place of the hops table, until the completion of the
 /// first round.
@@ -53,13 +54,16 @@ pub fn render(f: &mut Frame<'_>, app: &mut TuiApp) {
         tabs::render(f, chunks[1], app);
         body::render(f, chunks[2], app);
         footer::render(f, chunks[3], app);
+        bar::render(f, chunks[4], app);
     } else if app.show_flows {
         flows::render(f, chunks[1], app);
         body::render(f, chunks[2], app);
         footer::render(f, chunks[3], app);
+        bar::render(f, chunks[4], app);
     } else {
         body::render(f, chunks[1], app);
         footer::render(f, chunks[2], app);
+        bar::render(f, chunks[3], app);
     }
     if app.show_settings {
         settings::render(f, app);
@@ -68,22 +72,25 @@ pub fn render(f: &mut Frame<'_>, app: &mut TuiApp) {
     }
 }
 
-const LAYOUT_WITHOUT_TABS: [Constraint; 3] = [
-    Constraint::Length(5),
+const LAYOUT_WITHOUT_TABS: [Constraint; 4] = [
+    Constraint::Length(4),
     Constraint::Min(10),
     Constraint::Length(6),
+    Constraint::Length(1),
 ];
 
-const LAYOUT_WITH_TABS: [Constraint; 4] = [
-    Constraint::Length(5),
+const LAYOUT_WITH_TABS: [Constraint; 5] = [
+    Constraint::Length(4),
     Constraint::Length(3),
     Constraint::Min(10),
     Constraint::Length(6),
+    Constraint::Length(1),
 ];
 
-const LAYOUT_WITH_FLOWS: [Constraint; 4] = [
-    Constraint::Length(5),
+const LAYOUT_WITH_FLOWS: [Constraint; 5] = [
+    Constraint::Length(4),
     Constraint::Length(6),
     Constraint::Min(10),
     Constraint::Length(6),
+    Constraint::Length(1),
 ];
diff --git a/crates/trippy-tui/src/frontend/render/bar.rs b/crates/trippy-tui/src/frontend/render/bar.rs
new file mode 100644
index 0000000..8e37740
--- /dev/null
+++ b/crates/trippy-tui/src/frontend/render/bar.rs
@@ -0,0 +1,126 @@
+use crate::config::AddressMode;
+use crate::frontend::tui_app::TuiApp;
+use crate::t;
+use ratatui::layout::{Alignment, Rect};
+use ratatui::prelude::{Line, Span, Style};
+use ratatui::widgets::Paragraph;
+use ratatui::Frame;
+use std::borrow::Cow;
+use std::net::IpAddr;
+use trippy_core::{PrivilegeMode, Protocol};
+use trippy_dns::ResolveMethod;
+
+pub fn render(f: &mut Frame<'_>, rect: Rect, app: &TuiApp) {
+    let protocol = Span::raw(match app.tracer_config().data.protocol() {
+        Protocol::Icmp => format!(
+            "{}/{}",
+            fmt_target_family(app.tracer_config().data.target_addr()),
+            t!("ICMP"),
+        ),
+        Protocol::Udp => format!(
+            "{}/{}/{}",
+            fmt_target_family(app.tracer_config().data.target_addr()),
+            t!("UDP"),
+            app.tracer_config().data.multipath_strategy(),
+        ),
+        Protocol::Tcp => format!(
+            "{}/{}",
+            fmt_target_family(app.tracer_config().data.target_addr()),
+            t!("TCP"),
+        ),
+    });
+
+    let privilege_mode = Span::raw(fmt_privilege_mode(
+        app.tracer_config().data.privilege_mode(),
+    ));
+
+    let as_mode = match app.resolver.config().resolve_method {
+        ResolveMethod::System => Span::raw("□ ASN"),
+        ResolveMethod::Resolv | ResolveMethod::Google | ResolveMethod::Cloudflare => {
+            if app.tui_config.lookup_as_info {
+                Span::raw("■ ASN")
+            } else {
+                Span::raw("□ ASN")
+            }
+        }
+    };
+
+    let details = if app.show_hop_details {
+        Span::raw(format!("■ {}", t!("details")))
+    } else {
+        Span::raw(format!("□ {}", t!("details")))
+    };
+
+    let max_hosts = if let Some(m) = app.tui_config.max_addrs {
+        Span::raw(format!("%:{m:2}"))
+    } else {
+        Span::raw("%: -")
+    };
+
+    let privacy = if app.tui_config.privacy_max_ttl > 0 {
+        Span::raw(format!("»:{:2}", app.tui_config.privacy_max_ttl))
+    } else {
+        Span::raw("»: -")
+    };
+
+    let address_mode = match app.tui_config.address_mode {
+        AddressMode::Ip => Span::raw(" ip "),
+        AddressMode::Host => Span::raw("host"),
+        AddressMode::Both => Span::raw("both"),
+    };
+
+    let locale = Span::raw(app.tui_config.locale.as_str());
+
+    // these are configuration items that cannot be changed at runtime.
+    let left_line = Line::from(vec![
+        Span::raw(" ["),
+        protocol,
+        Span::raw("] ["),
+        privilege_mode,
+        Span::raw("] ["),
+        locale,
+        Span::raw("]"),
+    ]);
+
+    // these are configuration items that can be toggled at runtime.
+    let right_line = Line::from(vec![
+        Span::raw(" ["),
+        as_mode,
+        Span::raw("] ["),
+        details,
+        Span::raw("] ["),
+        address_mode,
+        Span::raw("] ["),
+        privacy,
+        Span::raw("] ["),
+        max_hosts,
+        Span::raw("] "),
+    ]);
+
+    let bar_style = Style::default()
+        .bg(app.tui_config.theme.info_bar_bg)
+        .fg(app.tui_config.theme.info_bar_text);
+    let left = Paragraph::new(left_line)
+        .style(bar_style)
+        .alignment(Alignment::Left);
+    let right = Paragraph::new(right_line)
+        .style(bar_style)
+        .alignment(Alignment::Right);
+
+    f.render_widget(right, rect);
+    f.render_widget(left, rect);
+}
+
+fn fmt_privilege_mode(privilege_mode: PrivilegeMode) -> Cow<'static, str> {
+    match privilege_mode {
+        PrivilegeMode::Privileged => t!("privileged"),
+        PrivilegeMode::Unprivileged => t!("unprivileged"),
+    }
+}
+
+const fn fmt_target_family(target: IpAddr) -> &'static str {
+    match target {
+        IpAddr::V4(_) => "IPv4",
+        IpAddr::V6(_) => "IPv6",
+    }
+}
diff --git a/crates/trippy-tui/src/frontend/render/header.rs b/crates/trippy-tui/src/frontend/render/header.rs
index 4bae1ab..a9e836a 100644
--- a/crates/trippy-tui/src/frontend/render/header.rs
+++ b/crates/trippy-tui/src/frontend/render/header.rs
@@ -7,13 +7,11 @@ use ratatui::style::{Modifier, Style};
 use ratatui::text::{Line, Span};
 use ratatui::widgets::{Block, BorderType, Borders, Paragraph};
 use ratatui::Frame;
-use std::borrow::Cow;
-use std::net::IpAddr;
 use std::time::Duration;
-use trippy_core::{Hop, PortDirection, PrivilegeMode, Protocol};
-use trippy_dns::{ResolveMethod, Resolver};
+use trippy_core::{Hop, PortDirection};
+use trippy_dns::Resolver;
 
-/// Render the title, config, target, clock and keyboard controls.
+/// Render the title, target, clock and basic keyboard controls.
 #[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
 pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
     let header_block = Block::default()
@@ -29,60 +27,74 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
         );
     let now = chrono::Local::now().to_rfc3339_opts(SecondsFormat::Secs, true);
     let clock_span = Line::from(Span::raw(now));
-    let help_span = Line::from(vec![
-        Span::styled("h", Style::default().add_modifier(Modifier::BOLD)),
-        Span::raw(t!("header_help")),
-        Span::styled(" s", Style::default().add_modifier(Modifier::BOLD)),
-        Span::raw(t!("header_settings")),
-        Span::styled(" q", Style::default().add_modifier(Modifier::BOLD)),
-        Span::raw(t!("header_quit")),
-    ]);
+    let bold = Style::default().add_modifier(Modifier::BOLD);
+
+    let help_binding = app.tui_config.bindings.toggle_help.to_string();
+    let header_help = t!("header_help");
+    let help_line_help = if header_help.starts_with(&help_binding) {
+        vec![
+            Span::styled(&help_binding, bold),
+            Span::raw(&header_help[help_binding.len()..]),
+        ]
+    } else {
+        vec![
+            Span::raw("["),
+            Span::styled(help_binding, bold),
+            Span::raw("]"),
+            Span::raw(header_help),
+        ]
+    };
+
+    let settings_binding = app.tui_config.bindings.toggle_settings.to_string();
+    let header_settings = t!("header_settings");
+    let help_line_settings = if header_settings.starts_with(&settings_binding) {
+        vec![
+            Span::styled(&settings_binding, bold),
+            Span::raw(&header_settings[settings_binding.len()..]),
+        ]
+    } else {
+        vec![
+            Span::raw("["),
+            Span::styled(settings_binding, bold),
+            Span::raw("]"),
+            Span::raw(header_settings),
+        ]
+    };
+
+    let quit_binding = app.tui_config.bindings.quit.to_string();
+    let header_quit = t!("header_quit");
+    let help_line_quit = if header_quit.starts_with(&quit_binding) {
+        vec![
+            Span::styled(&quit_binding, bold),
+            Span::raw(&header_quit[quit_binding.len()..]),
+        ]
+    } else {
+        vec![
+            Span::raw("["),
+            Span::styled(quit_binding, bold),
+            Span::raw("]"),
+            Span::raw(header_quit),
+        ]
+    };
+
+    let help_span = Line::from(
+        [
+            help_line_help,
+            vec![Span::raw(" ")],
+            help_line_settings,
+            vec![Span::raw(" ")],
+            help_line_quit,
+        ]
+        .into_iter()
+        .flatten()
+        .collect::<Vec<_>>(),
+    );
     let right_line = vec![clock_span, help_span];
     let right = Paragraph::new(right_line)
         .style(Style::default())
         .block(header_block.clone())
         .alignment(Alignment::Right);
-    let protocol = match app.tracer_config().data.protocol() {
-        Protocol::Icmp => format!(
-            "{}({}, {})",
-            t!("icmp"),
-            fmt_target_family(app.tracer_config().data.target_addr()),
-            fmt_privilege_mode(app.tracer_config().data.privilege_mode())
-        ),
-        Protocol::Udp => format!(
-            "{}({}, {}, {})",
-            t!("udp"),
-            fmt_target_family(app.tracer_config().data.target_addr()),
-            app.tracer_config().data.multipath_strategy(),
-            fmt_privilege_mode(app.tracer_config().data.privilege_mode())
-        ),
-        Protocol::Tcp => format!(
-            "{}({}, {})",
-            t!("tcp"),
-            fmt_target_family(app.tracer_config().data.target_addr()),
-            fmt_privilege_mode(app.tracer_config().data.privilege_mode())
-        ),
-    };
-    let details = if app.show_hop_details {
-        String::from(t!("on"))
-    } else {
-        String::from(t!("off"))
-    };
-    let as_info = match app.resolver.config().resolve_method {
-        ResolveMethod::System => String::from(t!("na")),
-        ResolveMethod::Resolv | ResolveMethod::Google | ResolveMethod::Cloudflare => {
-            if app.tui_config.lookup_as_info {
-                String::from(t!("on"))
-            } else {
-                String::from(t!("off"))
-            }
-        }
-    };
-    let max_hosts = app
-        .tui_config
-        .max_addrs
-        .map_or_else(|| String::from(t!("auto")), |m| m.to_string());
-    let privacy = app.tui_config.privacy_max_ttl;
+
     let source = render_source(app);
     let dest = render_destination(app);
     let target = format!("{source} -> {dest}");
@@ -113,20 +125,6 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
             ),
             Span::raw(target),
         ]),
-        Line::from(vec![
-            Span::styled(
-                format!("{}: ", t!("config")),
-                Style::default().add_modifier(Modifier::BOLD),
-            ),
-            Span::raw(format!(
-                "{}={protocol} {}={as_info} {}={details} {}={max_hosts}, {}={privacy}",
-                t!("protocol"),
-                t!("as-info"),
-                t!("details"),
-                t!("max-hosts"),
-                t!("privacy")
-            )),
-        ]),
         Line::from(vec![
             Span::styled(
                 format!("{}: ", t!("status")),
@@ -145,20 +143,6 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
     f.render_widget(left, rect);
 }
 
-fn fmt_privilege_mode(privilege_mode: PrivilegeMode) -> Cow<'static, str> {
-    match privilege_mode {
-        PrivilegeMode::Privileged => t!("privileged"),
-        PrivilegeMode::Unprivileged => t!("unprivileged"),
-    }
-}
-
-const fn fmt_target_family(target: IpAddr) -> &'static str {
-    match target {
-        IpAddr::V4(_) => "v4",
-        IpAddr::V6(_) => "v6",
-    }
-}
-
 /// Render the source address of the trace.
 fn render_source(app: &TuiApp) -> String {
     if let Some(src_addr) = app.tracer_config().data.source_addr() {
diff --git a/crates/trippy-tui/src/frontend/render/settings.rs b/crates/trippy-tui/src/frontend/render/settings.rs
index 5174f80..a147acf 100644
--- a/crates/trippy-tui/src/frontend/render/settings.rs
+++ b/crates/trippy-tui/src/frontend/render/settings.rs
@@ -502,6 +502,8 @@ fn format_theme_settings(app: &TuiApp) -> Vec<SettingsItem> {
             "map-info-panel-text-color",
             theme::fmt_color(theme.map_info_panel_text),
         ),
+        SettingsItem::new("info-bar-bg-color", theme::fmt_color(theme.info_bar_bg)),
+        SettingsItem::new("info-bar-text-color", theme::fmt_color(theme.info_bar_text)),
     ]
 }
 
@@ -525,7 +527,7 @@ pub fn settings_tabs() -> [(String, usize); 7] {
         (t!("settings_tab_dns_title").to_string(), 5),
         (t!("settings_tab_geoip_title").to_string(), 1),
         (t!("settings_tab_bindings_title").to_string(), 37),
-        (t!("settings_tab_theme_title").to_string(), 31),
+        (t!("settings_tab_theme_title").to_string(), 33),
         (t!("settings_tab_columns_title").to_string(), 0),
     ]
 }
diff --git a/crates/trippy-tui/src/frontend/theme.rs b/crates/trippy-tui/src/frontend/theme.rs
index fc6929d..961588b 100644
--- a/crates/trippy-tui/src/frontend/theme.rs
+++ b/crates/trippy-tui/src/frontend/theme.rs
@@ -74,6 +74,10 @@ pub struct Theme {
     pub map_info_panel_bg: Color,
     /// The color of text in the map info panel.
     pub map_info_panel_text: Color,
+    /// The color of the info bar background.
+    pub info_bar_bg: Color,
+    /// The color of the info bar text.
+    pub info_bar_text: Color,
 }
 
 impl From<TuiTheme> for Theme {
@@ -111,6 +115,8 @@ impl From<TuiTheme> for Theme {
             map_info_panel_border: Color::from(value.map_info_panel_border),
             map_info_panel_bg: Color::from(value.map_info_panel_bg),
             map_info_panel_text: Color::from(value.map_info_panel_text),
+            info_bar_bg: Color::from(value.info_bar_bg),
+            info_bar_text: Color::from(value.info_bar_text),
         }
     }
 }
diff --git a/trippy-config-sample.toml b/trippy-config-sample.toml
index 859a999..9965738 100644
--- a/trippy-config-sample.toml
+++ b/trippy-config-sample.toml
@@ -330,7 +330,7 @@ tui-privacy-max-ttl = 0
 # The locale to use for Tui [default: auto]
 #tui-locale = "en-US"
 
-# Tui color theme configure.
+# Tui color theme configuraton.
 #
 # The supported ANSI color values are:
 #   Black, Red, Green, Yellow, Blue, Magenta, Cyan, Gray, DarkGray, LightRed,
@@ -376,6 +376,8 @@ map-selected-color = "green"
 map-info-panel-border-color = "gray"
 map-info-panel-bg-color = "black"
 map-info-panel-text-color = "gray"
+info-bar-bg-color = "white"
+info-bar-text-color = "black"
 
 
 # Tui key bindings Configuration.


Test patch:
diff --git a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@tui_theme_items_match.snap b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@tui_theme_items_match.snap
index 58b7628..7df61cd 100644
--- a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@tui_theme_items_match.snap
+++ b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@tui_theme_items_match.snap
@@ -1,4 +1,4 @@
 ---
 source: crates/trippy-tui/src/print.rs
 ---
-TUIthemecoloritems:bg-color,border-color,text-color,tab-text-color,hops-table-header-bg-color,hops-table-header-text-color,hops-table-row-active-text-color,hops-table-row-inactive-text-color,hops-chart-selected-color,hops-chart-unselected-color,hops-chart-axis-color,frequency-chart-bar-color,frequency-chart-text-color,flows-chart-bar-selected-color,flows-chart-bar-unselected-color,flows-chart-text-current-color,flows-chart-text-non-current-color,samples-chart-color,samples-chart-lost-color,help-dialog-bg-color,help-dialog-text-color,settings-tab-text-color,settings-dialog-bg-color,settings-table-header-text-color,settings-table-header-bg-color,settings-table-row-text-color,map-world-color,map-radius-color,map-selected-color,map-info-panel-border-color,map-info-panel-bg-color,map-info-panel-text-color
+TUIthemecoloritems:bg-color,border-color,text-color,tab-text-color,hops-table-header-bg-color,hops-table-header-text-color,hops-table-row-active-text-color,hops-table-row-inactive-text-color,hops-chart-selected-color,hops-chart-unselected-color,hops-chart-axis-color,frequency-chart-bar-color,frequency-chart-text-color,flows-chart-bar-selected-color,flows-chart-bar-unselected-color,flows-chart-text-current-color,flows-chart-text-non-current-color,samples-chart-color,samples-chart-lost-color,help-dialog-bg-color,help-dialog-text-color,settings-tab-text-color,settings-dialog-bg-color,settings-table-header-text-color,settings-table-header-bg-color,settings-table-row-text-color,map-world-color,map-radius-color,map-selected-color,map-info-panel-border-color,map-info-panel-bg-color,map-info-panel-text-color,info-bar-bg-color,info-bar-text-color