1use ratatui::style::Color;
2
3pub struct Theme {
6 pub brand: Color, pub coral: Color, pub agent: Color, pub planner: Color, pub verifier: Color, pub add: Color, pub rem: Color, pub gold: Color, pub steel: Color, pub supervised: Color, pub trusted: Color, pub autonomous: Color, pub bg: Color, pub panel: Color, pub line: Color, pub fg: Color, pub dim: Color, pub dimmer: Color, }
25
26pub const THEME_NAMES: &[&str] = &["captain", "midnight", "paper"];
28
29pub fn by_name(name: &str) -> Theme {
31 match name.trim().to_lowercase().as_str() {
32 "midnight" => THEME_MIDNIGHT,
33 "paper" => THEME_PAPER,
34 _ => THEME_CAPTAIN,
35 }
36}
37
38pub const THEME_CAPTAIN: Theme = Theme {
39 brand: Color::Rgb(0xf2, 0xa9, 0x3c),
40 coral: Color::Rgb(0xf0, 0x67, 0x4a),
41 agent: Color::Rgb(0x4e, 0xc9, 0xb0),
42 planner: Color::Rgb(0x6f, 0xa6, 0xe6),
43 verifier: Color::Rgb(0xc9, 0xa1, 0x4e),
44 add: Color::Rgb(0x74, 0xc2, 0x58),
45 rem: Color::Rgb(0xd9, 0x6a, 0x63),
46 gold: Color::Rgb(0xf2, 0xc9, 0x4c),
47 steel: Color::Rgb(0xb9, 0xb0, 0xa3),
48 supervised: Color::Rgb(0x74, 0xc2, 0x58),
49 trusted: Color::Rgb(0xf2, 0xa9, 0x3c),
50 autonomous: Color::Rgb(0xd9, 0x6a, 0x63),
51 bg: Color::Rgb(0x0e, 0x0b, 0x08),
52 panel: Color::Rgb(0x16, 0x12, 0x0d),
53 line: Color::Rgb(0x2c, 0x25, 0x1c),
54 fg: Color::Rgb(0xec, 0xe2, 0xcf),
55 dim: Color::Rgb(0x89, 0x7d, 0x6c),
56 dimmer: Color::Rgb(0x5c, 0x53, 0x46),
57};
58
59pub const THEME_MIDNIGHT: Theme = Theme {
61 brand: Color::Rgb(0x6f, 0xa6, 0xe6),
62 coral: Color::Rgb(0x9b, 0x7e, 0xd1),
63 agent: Color::Rgb(0x4e, 0xc9, 0xb0),
64 planner: Color::Rgb(0x6f, 0xa6, 0xe6),
65 verifier: Color::Rgb(0xc9, 0xa1, 0x4e),
66 add: Color::Rgb(0x74, 0xc2, 0x58),
67 rem: Color::Rgb(0xd9, 0x6a, 0x63),
68 gold: Color::Rgb(0xf2, 0xc9, 0x4c),
69 steel: Color::Rgb(0xb9, 0xb0, 0xa3),
70 supervised: Color::Rgb(0x74, 0xc2, 0x58),
71 trusted: Color::Rgb(0x6f, 0xa6, 0xe6),
72 autonomous: Color::Rgb(0xd9, 0x6a, 0x63),
73 bg: Color::Rgb(0x06, 0x08, 0x0e),
74 panel: Color::Rgb(0x0e, 0x12, 0x1a),
75 line: Color::Rgb(0x20, 0x26, 0x33),
76 fg: Color::Rgb(0xd6, 0xde, 0xeb),
77 dim: Color::Rgb(0x7a, 0x84, 0x99),
78 dimmer: Color::Rgb(0x4a, 0x52, 0x63),
79};
80
81pub const THEME_PAPER: Theme = Theme {
83 brand: Color::Rgb(0xa6, 0x5a, 0x1a),
84 coral: Color::Rgb(0xa6, 0x3a, 0x2a),
85 agent: Color::Rgb(0x2f, 0x7d, 0x67),
86 planner: Color::Rgb(0x2e, 0x5a, 0x9c),
87 verifier: Color::Rgb(0x7a, 0x5e, 0x2c),
88 add: Color::Rgb(0x3a, 0x7d, 0x2e),
89 rem: Color::Rgb(0xa6, 0x3a, 0x2a),
90 gold: Color::Rgb(0xa6, 0x80, 0x1a),
91 steel: Color::Rgb(0x5e, 0x55, 0x47),
92 supervised: Color::Rgb(0x3a, 0x7d, 0x2e),
93 trusted: Color::Rgb(0xa6, 0x5a, 0x1a),
94 autonomous: Color::Rgb(0xa6, 0x3a, 0x2a),
95 bg: Color::Rgb(0xf3, 0xee, 0xe1),
96 panel: Color::Rgb(0xe7, 0xe0, 0xcc),
97 line: Color::Rgb(0xc6, 0xbc, 0xa3),
98 fg: Color::Rgb(0x2a, 0x24, 0x18),
99 dim: Color::Rgb(0x6a, 0x60, 0x4c),
100 dimmer: Color::Rgb(0x9a, 0x90, 0x7c),
101};
102
103impl Theme {
104 pub fn autonomy_color(&self, level: &crate::event::AutonomyLevel) -> Color {
105 match level {
106 crate::event::AutonomyLevel::Supervised => self.supervised,
107 crate::event::AutonomyLevel::Trusted => self.trusted,
108 crate::event::AutonomyLevel::Autonomous => self.autonomous,
109 }
110 }
111
112 pub fn agent_color(&self, role: &str) -> Color {
113 match role {
114 "planner" => self.planner,
115 "coder" => self.agent,
116 "verifier" => self.verifier,
117 "swarm" => self.gold,
118 _ => self.steel,
119 }
120 }
121
122 pub fn spinner_frame(&self, _index: usize) -> &str {
123 const FRAMES: &[&str] = &["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
124 FRAMES[_index % FRAMES.len()]
125 }
126
127 pub fn flight_verb(&self, index: usize) -> &str {
128 const VERBS: &[&str] = &[
129 "Soaring", "Gliding", "Diving", "Scouting", "Perching", "Foraging", "Wheeling",
130 ];
131 VERBS[index % VERBS.len()]
132 }
133}
134
135impl Default for Theme {
136 fn default() -> Self {
137 THEME_CAPTAIN
138 }
139}
140
141pub const ASCII_SPARROW: &str = r#"
146 ^^
147 .-~~~-.
148 /__ \
149 | o ██ |
150 | v |
151 | . |
152 \ \__/ /
153 '-..-'
154 /| |\ ╤━o
155 '_| |_'
156"#;
157
158pub const ASCII_WORDMARK: &str = "S P A R R O W";
159
160pub fn ascii_sparrow_at_frame(frame: u64) -> String {
161 let blink = frame % 88 == 0;
162 let bob = frame % 60 == 0;
163 let mut art = ASCII_SPARROW.to_string();
164 if blink {
165 art = art.replace("| o ", "| - ");
167 }
168 if bob {
169 art.lines()
170 .map(|line| {
171 if line.is_empty() {
172 String::new()
173 } else {
174 format!(" {}", line)
175 }
176 })
177 .collect::<Vec<_>>()
178 .join("\n")
179 } else {
180 art
181 }
182}
183
184pub fn boot_sequence() -> Vec<String> {
185 vec![
186 format!("{}", ASCII_SPARROW),
187 format!(" {} ", ASCII_WORDMARK),
188 String::new(),
189 "one cli · grows with you".to_string(),
190 String::new(),
191 "boot: router · surfaces · sandbox · skills · memory · autonomy · ready".to_string(),
192 ]
193}