@hej / sigit / commits / c385aa9

Add Banner role for colorized banner art rendering

Seto Elkahfi committed Apr 14, 2026 at 18:42 UTC c385aa96ddccc2d7deafeb398085a2c8091dfb59
1 file changed +27 -3
src/chat.rs
+27 -3
@@ -25,6 +25,8 @@ enum Role {
25 User,
26 Assistant,
27 System,
28 + /// Banner art — rendered with per-character digit colorization.
29 + Banner,
30 }
31
32 struct ChatMessage {
@@ -53,6 +55,13 @@ impl ChatMessage {
55 text: text.into(),
56 }
57 }
58 +
59 + fn banner(text: impl Into<String>) -> Self {
60 + Self {
61 + role: Role::Banner,
62 + text: text.into(),
63 + }
64 + }
65 }
66
67 // ── App state ────────────────────────────────────────────────────────────────
@@ -89,7 +98,7 @@ impl App {
98 fn new() -> Self {
99 let mut messages = Vec::new();
100 for line in BANNER_ART.lines() {
92 - messages.push(ChatMessage::system(line));
101 + messages.push(ChatMessage::banner(line));
102 }
103 messages.push(ChatMessage::system(""));
104 messages.push(ChatMessage::system(format!(
@@ -161,12 +170,16 @@ impl App {
170 }
171 }
172
173 +fn banner_char_color(_ch: char) -> Color {
174 + Color::White
175 +}
176 +
177 /// Estimate how many terminal rows a message takes once wrapped.
178 fn wrapped_line_count(text: &str, role: Role, width: usize) -> u16 {
179 let prefix_len = match role {
180 Role::User => 6, // "you > "
168 - Role::Assistant => 7, // "siGit > " — wait, that's 8. Let's just use 7 for "siGit> "
169 - Role::System => 0,
181 + Role::Assistant => 8, // "siGit > "
182 + Role::System | Role::Banner => 0,
183 };
184 let effective = if width > prefix_len {
185 width - prefix_len
@@ -343,6 +356,17 @@ fn render_chat_message<'a>(lines: &mut Vec<Line<'a>>, msg: &ChatMessage) {
356 )));
357 }
358 }
359 + Role::Banner => {
360 + for segment in &text_lines {
361 + let spans: Vec<Span<'_>> = segment
362 + .chars()
363 + .map(|ch| {
364 + Span::styled(ch.to_string(), Style::default().fg(banner_char_color(ch)))
365 + })
366 + .collect();
367 + lines.push(Line::from(spans));
368 + }
369 + }
370 }
371 }
372