use crate::color; use crate::section::{RenderContext, SectionOutput}; use crate::shell; use std::time::Duration; /// Render a custom command section by ID. pub fn render(id: &str, ctx: &RenderContext) -> Option { let cmd_cfg = ctx.config.custom.iter().find(|c| c.id == id)?; let ttl = Duration::from_secs(cmd_cfg.ttl); let timeout = Duration::from_millis(ctx.config.global.shell_timeout_ms); let cache_key = format!("custom_{id}"); let cached = ctx.cache.get(&cache_key, ttl); let output_str = cached.or_else(|| { let result = if let Some(ref exec) = cmd_cfg.exec { if exec.is_empty() { return None; } let args: Vec<&str> = exec[1..].iter().map(|s| s.as_str()).collect(); shell::exec_with_timeout(&exec[0], &args, None, timeout) } else if let Some(ref command) = cmd_cfg.command { shell::exec_with_timeout("sh", &["-c", command], None, timeout) } else { None }; if let Some(ref val) = result { ctx.cache.set(&cache_key, val); } result })?; if output_str.is_empty() { return None; } let label = cmd_cfg.label.as_deref().unwrap_or(""); let raw = if label.is_empty() { output_str.clone() } else { format!("{label}: {output_str}") }; let ansi = if ctx.color_enabled { if let Some(ref color_cfg) = cmd_cfg.color { if let Some(matched_color) = color_cfg.match_map.get(&output_str) { let c = color::resolve_color(matched_color, ctx.theme, &ctx.config.colors); format!("{c}{raw}{}", color::RESET) } else if let Some(ref default_c) = cmd_cfg.default_color { let c = color::resolve_color(default_c, ctx.theme, &ctx.config.colors); format!("{c}{raw}{}", color::RESET) } else { raw.clone() } } else if let Some(ref default_c) = cmd_cfg.default_color { let c = color::resolve_color(default_c, ctx.theme, &ctx.config.colors); format!("{c}{raw}{}", color::RESET) } else { raw.clone() } } else { raw.clone() }; Some(SectionOutput { raw, ansi }) }