1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
use ratatui::{
layout::{Constraint, Layout, Rect},
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Clear, Paragraph},
Frame,
};
use super::widgets::panel_contrast_fg;
use crate::{
app::state::{Palette, ToastKind, ToastNotification},
detect::AgentState,
};
pub(super) fn render_toast_notification(
frame: &mut Frame,
area: Rect,
toast: &ToastNotification,
offset_for_warning: bool,
p: &Palette,
) {
let dot_color = match toast.kind {
ToastKind::NeedsAttention => p.red,
ToastKind::Finished => p.blue,
ToastKind::UpdateInstalled => p.accent,
};
let content_width = (toast.title.len().max(toast.context.len()) as u16) + 4;
let width = content_width.saturating_add(2).min(area.width);
let height = 4u16.min(area.height);
let x = area.x + area.width.saturating_sub(width);
let y = area.y
+ area
.height
.saturating_sub(height + if offset_for_warning { 1 } else { 0 });
let toast_area = Rect::new(x, y, width, height);
frame.render_widget(Clear, toast_area);
let block = Block::default()
.borders(Borders::ALL)
.border_style(Style::default().fg(p.overlay0))
.style(Style::default().bg(p.panel_bg));
let inner = block.inner(toast_area);
frame.render_widget(block, toast_area);
if inner.height < 2 {
return;
}
let [title_row, context_row] =
Layout::vertical([Constraint::Length(1), Constraint::Length(1)]).areas(inner);
let title = Line::from(vec![
Span::styled("●", Style::default().fg(dot_color)),
Span::raw(" "),
Span::styled(
&toast.title,
Style::default().fg(p.text).add_modifier(Modifier::BOLD),
),
]);
let context = Line::from(vec![
Span::styled(" ", Style::default().fg(p.overlay0)),
Span::styled(&toast.context, Style::default().fg(p.overlay0)),
]);
frame.render_widget(Paragraph::new(title), title_row);
frame.render_widget(Paragraph::new(context), context_row);
}
pub(super) fn render_config_diagnostic(frame: &mut Frame, area: Rect, message: &str, p: &Palette) {
let text = format!(" config warning: {message} ");
let width = text.len() as u16 + 2;
let notif_area = Rect::new(
area.x + area.width.saturating_sub(width.min(area.width)),
area.y,
width.min(area.width),
1,
);
frame.render_widget(Clear, notif_area);
frame.render_widget(
Paragraph::new(Span::styled(
text,
Style::default()
.fg(panel_contrast_fg(p))
.bg(p.yellow)
.add_modifier(Modifier::BOLD),
)),
notif_area,
);
}
pub(super) fn state_dot(state: AgentState, seen: bool, p: &Palette) -> (&'static str, Style) {
match (state, seen) {
(AgentState::Blocked, _) => ("●", Style::default().fg(p.red)),
(AgentState::Working, _) => ("●", Style::default().fg(p.yellow)),
(AgentState::Idle, false) => ("●", Style::default().fg(p.teal)),
(AgentState::Idle, true) => ("○", Style::default().fg(p.green)),
(AgentState::Unknown, _) => ("·", Style::default().fg(p.overlay0)),
}
}
pub(super) fn agent_icon(
state: AgentState,
seen: bool,
tick: u32,
p: &Palette,
) -> (&'static str, Style) {
match (state, seen) {
(AgentState::Blocked, _) => ("◉", Style::default().fg(p.red)),
(AgentState::Working, _) => (super::spinner_frame(tick), Style::default().fg(p.yellow)),
(AgentState::Idle, false) => ("●", Style::default().fg(p.teal)),
(AgentState::Idle, true) => ("✓", Style::default().fg(p.green)),
(AgentState::Unknown, _) => ("○", Style::default().fg(p.overlay0)),
}
}
pub(super) fn state_label(state: AgentState, seen: bool) -> &'static str {
match (state, seen) {
(AgentState::Blocked, _) => "blocked",
(AgentState::Working, _) => "working",
(AgentState::Idle, false) => "done",
(AgentState::Idle, true) => "idle",
(AgentState::Unknown, _) => "idle",
}
}
pub(super) fn state_label_color(state: AgentState, seen: bool, p: &Palette) -> Color {
match (state, seen) {
(AgentState::Blocked, _) => p.red,
(AgentState::Working, _) => p.yellow,
(AgentState::Idle, false) => p.teal,
(AgentState::Idle, true) => p.green,
(AgentState::Unknown, _) => p.overlay0,
}
}
|