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