1use ratatui::{
2 buffer::Buffer,
3 layout::Rect,
4 style::Style,
5 text::{Line, Span},
6 widgets::{Block, Borders, Paragraph, Widget, Wrap},
7};
8
9use crate::explain::ExplainNode;
10use crate::ui::theme;
11
12pub struct ExplanationPanel<'a> {
13 pub nodes: &'a [ExplainNode],
14 pub error: Option<&'a str>,
15 pub scroll: u16,
16 pub focused: bool,
17}
18
19impl<'a> Widget for ExplanationPanel<'a> {
20 fn render(self, area: Rect, buf: &mut Buffer) {
21 let border_color = if self.focused {
22 theme::BLUE
23 } else {
24 theme::OVERLAY
25 };
26 let block = Block::default()
27 .borders(Borders::ALL)
28 .border_style(Style::default().fg(border_color))
29 .title(Span::styled(
30 " Explanation ",
31 Style::default().fg(theme::TEXT),
32 ));
33
34 if let Some(err) = self.error {
35 let lines = vec![Line::from(Span::styled(
36 err.to_string(),
37 Style::default().fg(theme::RED),
38 ))];
39 let paragraph = Paragraph::new(lines)
40 .block(block)
41 .style(Style::default().bg(theme::BASE));
42 paragraph.render(area, buf);
43 return;
44 }
45
46 if self.nodes.is_empty() {
47 let paragraph = Paragraph::new(Line::from(Span::styled(
48 "Enter a pattern to see its explanation",
49 Style::default().fg(theme::SUBTEXT),
50 )))
51 .block(block)
52 .style(Style::default().bg(theme::BASE));
53 paragraph.render(area, buf);
54 return;
55 }
56
57 let lines: Vec<Line> = self
58 .nodes
59 .iter()
60 .map(|node| {
61 let indent = " ".repeat(node.depth);
62 let bullet = if node.depth > 0 { "|- " } else { "" };
63 Line::from(Span::styled(
64 format!("{indent}{bullet}{}", node.description),
65 Style::default().fg(if node.depth == 0 {
66 theme::TEXT
67 } else {
68 theme::SUBTEXT
69 }),
70 ))
71 })
72 .collect();
73
74 let paragraph = Paragraph::new(lines)
75 .block(block)
76 .style(Style::default().bg(theme::BASE))
77 .wrap(Wrap { trim: false })
78 .scroll((self.scroll, 0));
79
80 paragraph.render(area, buf);
81 }
82}