wisp/components/
thought_message.rs1use tui::{Line, Style, Theme, ViewContext};
2
3pub struct ThoughtMessage<'a> {
4 pub text: &'a str,
5}
6
7impl ThoughtMessage<'_> {
8 fn format_line(text: &str, theme: &Theme) -> Line {
9 let mut line = Line::default();
10 line.push_styled("│ ", theme.muted());
11 line.push_with_style(text, Style::fg(theme.muted()));
12 line
13 }
14
15 fn format_lines(text: &str, theme: &Theme) -> Vec<Line> {
16 text.lines()
17 .map(|line| Self::format_line(line, theme))
18 .collect()
19 }
20}
21
22impl ThoughtMessage<'_> {
23 pub fn render(&self, context: &ViewContext) -> Vec<Line> {
24 if self.text.is_empty() {
25 return vec![];
26 }
27
28 Self::format_lines(self.text, &context.theme)
29 }
30}
31
32#[cfg(test)]
33mod tests {
34 use super::*;
35
36 #[test]
37 fn renders_border_prefixed_thought_line() {
38 let component = ThoughtMessage { text: "check plan" };
39 let context = ViewContext::new((80, 24));
40 let lines = component.render(&context);
41 assert_eq!(lines.len(), 1);
42 assert!(lines[0].plain_text().starts_with("│ "));
43 assert!(lines[0].plain_text().contains("check plan"));
44 }
45
46 #[test]
47 fn prefixes_all_lines_with_border() {
48 let component = ThoughtMessage {
49 text: "line one\nline two",
50 };
51 let context = ViewContext::new((80, 24));
52 let lines = component.render(&context);
53 assert_eq!(lines.len(), 2);
54 assert!(lines[0].plain_text().starts_with("│ "));
55 assert!(lines[0].plain_text().contains("line one"));
56 assert!(lines[1].plain_text().starts_with("│ "));
57 assert!(lines[1].plain_text().contains("line two"));
58 }
59
60 #[test]
61 fn wrapped_continuation_rows_remain_muted() {
62 let component = ThoughtMessage {
63 text: "abcdefghijklmnopqrstuvwxyz",
64 };
65 let context = ViewContext::new((80, 24));
66 let lines = component.render(&context);
67 let wrapped = lines[0].soft_wrap(12);
68 assert!(wrapped.len() > 1);
69
70 for row in wrapped.iter().skip(1) {
71 assert!(!row.spans().is_empty());
72 assert!(
73 row.spans()
74 .iter()
75 .all(|span| span.style().fg == Some(context.theme.muted()))
76 );
77 }
78 }
79}