Skip to main content

vtcode_config/
terminal_title.rs

1use serde::{Deserialize, Serialize};
2
3pub const DEFAULT_TERMINAL_TITLE_ITEMS: &[&str] = &["spinner", "project"];
4
5#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
6#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
7pub struct TerminalTitleConfig {
8    /// Ordered title items; `None` resolves to `["spinner", "project"]`
9    /// (spinner enabled by default), and an empty array disables VT
10    /// Code-managed titles.
11    #[serde(default)]
12    pub items: Option<Vec<String>>,
13}
14
15impl TerminalTitleConfig {
16    #[must_use]
17    fn effective_items(&self) -> Vec<String> {
18        self.items
19            .clone()
20            .unwrap_or_else(|| DEFAULT_TERMINAL_TITLE_ITEMS.iter().map(|item| (*item).to_string()).collect())
21    }
22
23    #[must_use]
24    fn manages_title(&self) -> bool {
25        self.items.as_ref().is_none_or(|items| !items.is_empty())
26    }
27}
28
29#[cfg(test)]
30mod tests {
31    use super::{DEFAULT_TERMINAL_TITLE_ITEMS, TerminalTitleConfig};
32
33    #[test]
34    fn shipped_default_items_lead_with_the_spinner() {
35        // The shipped binary must always manage the title with the activity
36        // spinner first; single source in `data/default_config.toml` mirrors it.
37        assert_eq!(DEFAULT_TERMINAL_TITLE_ITEMS, ["spinner", "project"]);
38    }
39
40    #[test]
41    fn serde_defaults_when_terminal_title_table_is_missing() {
42        let config: TerminalTitleConfig = toml::from_str("").expect("config should parse");
43
44        assert_eq!(config.items, None);
45    }
46
47    #[test]
48    fn serde_preserves_explicit_empty_items() {
49        let config: TerminalTitleConfig = toml::from_str("items = []").expect("config should parse");
50
51        assert_eq!(config.items, Some(Vec::new()));
52    }
53
54    #[test]
55    fn serde_preserves_valid_item_list() {
56        let config: TerminalTitleConfig =
57            toml::from_str("items = [\"spinner\", \"project\", \"model\"]").expect("config should parse");
58
59        assert_eq!(config.items, Some(vec!["spinner".to_string(), "project".to_string(), "model".to_string()]));
60    }
61
62    #[test]
63    fn missing_items_uses_default_terminal_title_items() {
64        let config = TerminalTitleConfig::default();
65
66        assert_eq!(
67            config.effective_items(),
68            DEFAULT_TERMINAL_TITLE_ITEMS
69                .iter()
70                .map(|item| (*item).to_string())
71                .collect::<Vec<_>>()
72        );
73        assert!(config.manages_title());
74    }
75
76    #[test]
77    fn explicit_empty_items_disables_terminal_title_management() {
78        let config = TerminalTitleConfig { items: Some(Vec::new()) };
79
80        assert!(config.effective_items().is_empty());
81        assert!(!config.manages_title());
82    }
83
84    #[test]
85    fn explicit_items_are_preserved() {
86        let config = TerminalTitleConfig {
87            items: Some(vec!["project".to_string(), "model".to_string()]),
88        };
89
90        assert_eq!(config.effective_items(), vec!["project".to_string(), "model".to_string()]);
91        assert!(config.manages_title());
92    }
93}