diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-09-11 00:55:19 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-11 00:55:19 +0300 |
| commit | 58271459401fa461707c042f7f03867ddcfff3ea (patch) | |
| tree | d8ae601d636858d9fafba62332870144059b2a3b /src | |
| parent | 043b804c44665b07bd503fdad896bbe0a29311a0 (diff) | |
feat: support conditional sidebar token hiding (#3925)
Diffstat (limited to 'src')
| -rw-r--r-- | src/app/mod.rs | 1 | ||||
| -rw-r--r-- | src/client/shell/config.rs | 5 | ||||
| -rw-r--r-- | src/config/sidebar.rs | 8 | ||||
| -rw-r--r-- | src/config/sidebar/rules.rs | 16 | ||||
| -rw-r--r-- | src/ui/sidebar/tokens.rs | 65 |
5 files changed, 84 insertions, 11 deletions
diff --git a/src/app/mod.rs b/src/app/mod.rs index b327b5bb..4ac109c7 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1848,6 +1848,7 @@ mod tests { assert_eq!( app.state.sidebar_agents.rows[0][0] .style_for_value("90") + .unwrap() .bold, Some(true) ); diff --git a/src/client/shell/config.rs b/src/client/shell/config.rs index 36e1051f..222e027e 100644 --- a/src/client/shell/config.rs +++ b/src/client/shell/config.rs @@ -476,7 +476,10 @@ mod tests { ); assert_eq!(shell.agents.row_gap, 2); assert_eq!( - shell.agents.rows[0][0].style_for_value("Local").bold, + shell.agents.rows[0][0] + .style_for_value("Local") + .unwrap() + .bold, Some(true) ); let previous = shell.agents.clone(); diff --git a/src/config/sidebar.rs b/src/config/sidebar.rs index 0b489765..9a4cb5e0 100644 --- a/src/config/sidebar.rs +++ b/src/config/sidebar.rs @@ -139,10 +139,10 @@ pub enum SpaceSidebarToken { } impl AgentSidebarToken { - pub(crate) fn style_for_value(&self, value: &str) -> SidebarTokenStyle { + pub(crate) fn style_for_value(&self, value: &str) -> Option<SidebarTokenStyle> { match self { Self::Styled { style, rules, .. } => rules::matching_style(rules, *style, value), - _ => SidebarTokenStyle::default(), + _ => Some(SidebarTokenStyle::default()), } } @@ -155,10 +155,10 @@ impl AgentSidebarToken { } impl SpaceSidebarToken { - pub(crate) fn style_for_value(&self, value: &str) -> SidebarTokenStyle { + pub(crate) fn style_for_value(&self, value: &str) -> Option<SidebarTokenStyle> { match self { Self::Styled { style, rules, .. } => rules::matching_style(rules, *style, value), - _ => SidebarTokenStyle::default(), + _ => Some(SidebarTokenStyle::default()), } } diff --git a/src/config/sidebar/rules.rs b/src/config/sidebar/rules.rs index 3ee72e56..049e0979 100644 --- a/src/config/sidebar/rules.rs +++ b/src/config/sidebar/rules.rs @@ -8,6 +8,7 @@ pub struct SidebarTokenRule { condition: Condition, ignore_case: bool, style: SidebarTokenStyle, + hide: Option<bool>, } // Deserialization rejects non-finite thresholds, so equality is reflexive. @@ -43,6 +44,8 @@ struct RawRule { bold: Option<bool>, #[serde(skip_serializing_if = "Option::is_none")] dim: Option<bool>, + #[serde(skip_serializing_if = "Option::is_none")] + hide: Option<bool>, } impl TryFrom<RawRule> for SidebarTokenRule { @@ -91,6 +94,7 @@ impl TryFrom<RawRule> for SidebarTokenRule { Ok(Self { condition, ignore_case: raw.ignore_case.unwrap_or(false), + hide: raw.hide, style: SidebarTokenStyle { fg: raw.fg, bold: raw.bold, @@ -107,6 +111,7 @@ impl From<SidebarTokenRule> for RawRule { fg: rule.style.fg, bold: rule.style.bold, dim: rule.style.dim, + hide: rule.hide, ..Self::default() }; match rule.condition { @@ -171,18 +176,21 @@ pub(super) fn matching_style( rules: &[SidebarTokenRule], base: SidebarTokenStyle, value: &str, -) -> SidebarTokenStyle { +) -> Option<SidebarTokenStyle> { let mut numeric = None; for rule in rules { if rule.matches(value, &mut numeric) { - return SidebarTokenStyle { + if rule.hide == Some(true) { + return None; + } + return Some(SidebarTokenStyle { fg: rule.style.fg.or(base.fg), bold: rule.style.bold.or(base.bold), dim: rule.style.dim.or(base.dim), - }; + }); } } - base + Some(base) } #[cfg(test)] diff --git a/src/ui/sidebar/tokens.rs b/src/ui/sidebar/tokens.rs index d9cefe99..c1e38aa9 100644 --- a/src/ui/sidebar/tokens.rs +++ b/src/ui/sidebar/tokens.rs @@ -112,7 +112,7 @@ pub(crate) fn agent_rows( }?; let style = kind .text_value() - .map_or(style, |value| configured.style_for_value(value)); + .map_or(Some(style), |value| configured.style_for_value(value))?; Some(ResolvedToken::new(kind, style)) }) .collect::<Vec<_>>(); @@ -168,7 +168,7 @@ pub(crate) fn space_rows( }?; let style = kind .text_value() - .map_or(style, |value| configured.style_for_value(value)); + .map_or(Some(style), |value| configured.style_for_value(value))?; Some(ResolvedToken::new(kind, style)) }) .collect::<Vec<_>>(); @@ -339,6 +339,67 @@ rows = [[{ token = "$load", rules = [{ lt = 50, dim = true }] }]] } #[test] + fn conditional_hide_removes_tokens_and_empty_rows() { + let config: crate::config::SidebarConfig = toml::from_str( + r##" +[agents] +rows = [[{ token = "machine", fg = "#61afef", rules = [{ equals = "Local", hide = true }] }, "agent"]] +[agents.rows_by_agent] +pi = [[{ token = "$load", rules = [{ lt = 50, hide = true }] }], ["agent"]] +[spaces] +rows = [[{ token = "$load", rules = [{ lt = 50, hide = true }] }], ["workspace"]] +"##, + ).unwrap(); + let encoded = toml::to_string(&config).unwrap(); + assert!(encoded.contains("hide = true")); + let config: crate::config::SidebarConfig = toml::from_str(&encoded).unwrap(); + let mut entry = entry(); + entry.canonical_agent = None; + for (machine, count) in [("Local", 1), ("Remote", 2)] { + let mut ctx = context(&entry); + ctx.machine = Some(machine); + let rows = agent_rows(&config.agents, ctx, "working"); + assert_eq!(rows[0].len(), count); + assert_eq!( + rows[0].last().unwrap().kind, + ResolvedTokenKind::Agent("pi".into()) + ); + } + entry.canonical_agent = Some(crate::detect::Agent::Pi); + for (value, count) in [("20", 1), ("90", 2)] { + entry.tokens.insert("load".into(), value.into()); + assert_eq!( + agent_rows(&config.agents, context(&entry), "working").len(), + count + ); + let rows = space_rows( + &config.spaces, + SpaceTokenContext { + workspace: "repo", + branch: None, + state_text: "working", + ahead_behind: None, + suppress_git_details: false, + tokens: &entry.tokens, + }, + ); + assert_eq!(rows.len(), count); + } + } + + #[test] + fn conditional_hide_preserves_first_match_wins() { + for first in ["hide = false", "bold = true"] { + let config: AgentsSidebarConfig = toml::from_str(&format!( + "rows = [[{{ token = 'agent', rules = [{{ equals = 'pi', {first} }}, {{ contains = '', hide = true }}] }}]]" + )).unwrap(); + let entry = entry(); + let rows = agent_rows(&config, context(&entry), "working"); + assert_eq!(rows[0][0].kind, ResolvedTokenKind::Agent("pi".into())); + } + } + + #[test] fn missing_custom_tokens_elide_rows_and_separators() { let entry = entry(); let config = AgentsSidebarConfig { |
