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