Skip to main content

stynx_code_tui/widgets/
thinking_panel.rs

1use ratatui::{
2    buffer::Buffer,
3    layout::Rect,
4    style::{Modifier, Style},
5    text::{Line, Span},
6    widgets::{Paragraph, Widget, Wrap},
7};
8
9use crate::theme;
10use crate::widgets::spinner::FRAMES;
11
12pub struct ThinkingPanel<'a> {
13    pub text: &'a str,
14    pub spinner_frame: usize,
15}
16
17impl<'a> ThinkingPanel<'a> {
18    pub fn new(text: &'a str, spinner_frame: usize) -> Self {
19        Self { text, spinner_frame }
20    }
21}
22
23impl<'a> Widget for ThinkingPanel<'a> {
24    fn render(self, area: Rect, buf: &mut Buffer) {
25        if self.text.trim().is_empty() || area.height == 0 {
26            return;
27        }
28
29        // Subtle backdrop so the panel reads as a distinct surface.
30        for y in area.y..area.y + area.height {
31            for x in area.x..area.x + area.width {
32                buf[(x, y)].set_style(Style::default().bg(theme::BACKGROUND_PANEL()));
33            }
34        }
35
36        let visible_body = (area.height as usize).saturating_sub(1);
37        let mut body_lines: Vec<&str> = self
38            .text
39            .lines()
40            .filter(|l| !l.trim().is_empty())
41            .collect();
42        if body_lines.len() > visible_body {
43            body_lines = body_lines[body_lines.len() - visible_body..].to_vec();
44        }
45
46        let frame = FRAMES[self.spinner_frame % FRAMES.len()];
47
48        let mut lines: Vec<Line<'static>> = Vec::new();
49        lines.push(Line::from(vec![
50            Span::styled(
51                format!(" {frame}  "),
52                Style::default()
53                    .fg(theme::PRIMARY())
54                    .bg(theme::BACKGROUND_PANEL())
55                    .add_modifier(Modifier::BOLD),
56            ),
57            Span::styled(
58                "thinking",
59                Style::default()
60                    .fg(theme::TEXT_MUTED())
61                    .bg(theme::BACKGROUND_PANEL())
62                    .add_modifier(Modifier::ITALIC),
63            ),
64        ]));
65        for raw in body_lines {
66            lines.push(Line::from(vec![
67                Span::styled(
68                    "     ",
69                    Style::default().bg(theme::BACKGROUND_PANEL()),
70                ),
71                Span::styled(
72                    raw.trim_end().to_string(),
73                    Style::default()
74                        .fg(theme::TEXT_MUTED())
75                        .bg(theme::BACKGROUND_PANEL())
76                        .add_modifier(Modifier::ITALIC | Modifier::DIM),
77                ),
78            ]));
79        }
80
81        Paragraph::new(lines)
82            .style(Style::default().bg(theme::BACKGROUND_PANEL()))
83            .wrap(Wrap { trim: false })
84            .render(area, buf);
85    }
86}