rab/agent/ui/components/
user_message.rs1use crate::agent::ui::theme::ThemeKey;
2use crate::agent::ui::theme::current_theme;
3use crate::tui::Component;
4use crate::tui::components::r#box::TuiBox;
5use crate::tui::components::markdown::{DefaultTextStyle, Markdown};
6const OSC133_ZONE_START: &str = "\x1b]133;A\x07";
7const OSC133_ZONE_END: &str = "\x1b]133;B\x07";
8const OSC133_ZONE_FINAL: &str = "\x1b]133;C\x07";
9
10pub struct UserMessageComponent {
13 box_component: TuiBox,
14 cached_lines: Option<Vec<String>>,
15 cached_width: usize,
16}
17
18impl UserMessageComponent {
19 pub fn new(text: impl Into<String>) -> Self {
20 let text = text.into();
21 let theme = current_theme();
22 let bg_ansi = theme.bg_ansi_key(ThemeKey::UserMessageBg).to_string();
23 drop(theme);
24
25 let mut msg_box = TuiBox::new(1, 1, Some(crate::tui::Style::new().bg(bg_ansi)));
26
27 let md_theme = crate::agent::ui::theme::get_markdown_theme();
29 let default_style = DefaultTextStyle {
30 color: Some(std::sync::Arc::new(|s: &str| -> String {
31 let t = current_theme();
32 t.fg_key(ThemeKey::UserMessageText, s)
33 })),
34 bold: false,
35 italic: false,
36 strikethrough: false,
37 underline: false,
38 };
39 let md = Markdown::new(text.clone(), 0, 0, md_theme, Some(default_style));
40 msg_box.add_child(std::boxed::Box::new(md));
41
42 Self {
43 box_component: msg_box,
44 cached_lines: None,
45 cached_width: 0,
46 }
47 }
48}
49
50impl Component for UserMessageComponent {
51 fn set_expanded(&mut self, _expanded: bool) {
52 }
54
55 fn render(&mut self, width: usize) -> Vec<String> {
56 if self.cached_width == width
57 && let Some(ref lines) = self.cached_lines
58 {
59 return lines.clone();
60 }
61
62 let mut lines = self.box_component.render(width);
63 if !lines.is_empty() {
64 lines[0] = format!("{}{}", OSC133_ZONE_START, &lines[0]);
65 if let Some(last) = lines.last_mut() {
66 last.push_str(OSC133_ZONE_END);
67 last.push_str(OSC133_ZONE_FINAL);
68 }
69 }
70
71 let result = lines.clone();
73 self.cached_lines = Some(lines);
74 self.cached_width = width;
75 result
76 }
77
78 fn invalidate(&mut self) {
79 self.cached_lines = None;
80 self.box_component.invalidate();
81 }
82}