stynx_code_tui/widgets/
thinking_panel.rs1use 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 || area.width < 4 {
26 return;
27 }
28
29 let frame = FRAMES[self.spinner_frame % FRAMES.len()];
30
31 let visible_body = (area.height as usize).saturating_sub(1);
32 let body_lines: Vec<&str> = {
33 let mut v: Vec<&str> = self
34 .text
35 .lines()
36 .filter(|l| !l.trim().is_empty())
37 .collect();
38 if v.len() > visible_body {
39 v = v[v.len() - visible_body..].to_vec();
40 }
41 v
42 };
43
44 for y in area.y..area.y + area.height {
45 for x in area.x..area.x + area.width {
46 buf[(x, y)].set_style(Style::default().bg(theme::BACKGROUND()));
47 }
48 }
49
50 let accent = theme::IRIS();
51 let bar_col = theme::OVERLAY();
52
53 for y in area.y..area.y + area.height {
54 buf[(area.x, y)]
55 .set_char('▌')
56 .set_style(Style::default().fg(accent).bg(theme::BACKGROUND()));
57 }
58
59 let inner_x = area.x + 2;
60 let inner_width = area.width.saturating_sub(3) as usize;
61 let _ = bar_col;
62
63 let header = Line::from(vec![
64 Span::styled(
65 format!("{frame} "),
66 Style::default()
67 .fg(accent)
68 .add_modifier(Modifier::BOLD),
69 ),
70 Span::styled(
71 "thinking",
72 Style::default()
73 .fg(theme::SUBTLE())
74 .add_modifier(Modifier::ITALIC),
75 ),
76 ]);
77
78 let inner_area = Rect {
79 x: inner_x,
80 y: area.y,
81 width: inner_width as u16,
82 height: area.height,
83 };
84
85 let mut lines: Vec<Line<'static>> = vec![header];
86 for raw in body_lines {
87 let text = if raw.len() > inner_width {
88 format!("{}…", &raw[..inner_width.saturating_sub(1)])
89 } else {
90 raw.trim_end().to_string()
91 };
92 lines.push(Line::from(Span::styled(
93 text,
94 Style::default()
95 .fg(theme::MUTED())
96 .add_modifier(Modifier::ITALIC),
97 )));
98 }
99
100 Paragraph::new(lines)
101 .wrap(Wrap { trim: false })
102 .render(inner_area, buf);
103 }
104}