Skip to main content

rgx/ui/
explanation.rs

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}