5a34fafd3f
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/claude-agent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
149 lines
4.7 KiB
Rust
149 lines
4.7 KiB
Rust
#![allow(dead_code)]
|
|
|
|
use std::time::Instant;
|
|
|
|
use ratatui::{
|
|
Frame,
|
|
layout::Rect,
|
|
style::Style,
|
|
text::{Line, Span},
|
|
widgets::{Block, Borders, Clear, Paragraph},
|
|
};
|
|
|
|
use crate::application::notification_state::{
|
|
MAX_VISIBLE, Notification, NotificationManager, format_elapsed,
|
|
};
|
|
use crate::theme;
|
|
|
|
impl NotificationManager {
|
|
pub fn render(&self, frame: &mut Frame, area: Rect) {
|
|
let visible: Vec<&Notification> = self
|
|
.active()
|
|
.iter()
|
|
.rev()
|
|
.take(MAX_VISIBLE)
|
|
.collect::<Vec<_>>()
|
|
.into_iter()
|
|
.rev()
|
|
.collect();
|
|
|
|
if visible.is_empty() {
|
|
return;
|
|
}
|
|
|
|
let notif_width = 50u16.min(area.width.saturating_sub(4));
|
|
let notif_height = 3u16;
|
|
let spacing = 1u16;
|
|
let total_height = visible.len() as u16 * (notif_height + spacing);
|
|
|
|
let start_y = area.y + area.height.saturating_sub(total_height + 1);
|
|
let start_x = area.x + area.width.saturating_sub(notif_width + 2);
|
|
|
|
for (i, notif) in visible.iter().enumerate() {
|
|
let y = start_y + (i as u16) * (notif_height + spacing);
|
|
let notif_area = Rect::new(start_x, y, notif_width, notif_height);
|
|
|
|
frame.render_widget(Clear, notif_area);
|
|
|
|
let border_color = notif.kind.color();
|
|
let block = Block::default()
|
|
.borders(Borders::LEFT)
|
|
.border_style(Style::default().fg(border_color))
|
|
.style(Style::default().bg(theme::BG1));
|
|
|
|
let inner = block.inner(notif_area);
|
|
frame.render_widget(block, notif_area);
|
|
|
|
let elapsed = Instant::now().duration_since(notif.created_at).as_secs();
|
|
let timestamp = if elapsed == 0 {
|
|
"now".to_string()
|
|
} else {
|
|
format!("{}s", elapsed)
|
|
};
|
|
|
|
let mut lines = vec![Line::from(vec![
|
|
Span::styled(¬if.icon, Style::default().fg(border_color)),
|
|
Span::raw(" "),
|
|
Span::styled(
|
|
¬if.title,
|
|
Style::default()
|
|
.fg(theme::FG1)
|
|
.add_modifier(ratatui::style::Modifier::BOLD),
|
|
),
|
|
Span::raw(" "),
|
|
Span::styled(timestamp, Style::default().fg(theme::GRAY)),
|
|
])];
|
|
|
|
if let Some(detail) = ¬if.detail {
|
|
let max_len = inner.width.saturating_sub(2) as usize;
|
|
let d = if detail.len() > max_len {
|
|
format!("{}…", &detail[..max_len.saturating_sub(1)])
|
|
} else {
|
|
detail.clone()
|
|
};
|
|
lines.push(Line::from(Span::styled(
|
|
d,
|
|
Style::default().fg(theme::GRAY),
|
|
)));
|
|
}
|
|
|
|
let para = Paragraph::new(lines);
|
|
frame.render_widget(para, inner);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn render_notification_item(frame: &mut Frame, area: Rect, notif: &Notification) {
|
|
let border_color = notif.kind.color();
|
|
let block = Block::default()
|
|
.borders(Borders::LEFT)
|
|
.border_style(Style::default().fg(border_color))
|
|
.style(Style::default().bg(theme::BG1));
|
|
|
|
let inner = block.inner(area);
|
|
frame.render_widget(block, area);
|
|
|
|
let elapsed = Instant::now().duration_since(notif.created_at).as_secs();
|
|
let timestamp = format_elapsed(elapsed);
|
|
|
|
let mut lines = vec![Line::from(vec![
|
|
Span::styled(¬if.icon, Style::default().fg(border_color)),
|
|
Span::raw(" "),
|
|
Span::styled(
|
|
notif.kind.label(),
|
|
Style::default()
|
|
.fg(theme::FG1)
|
|
.add_modifier(ratatui::style::Modifier::BOLD),
|
|
),
|
|
Span::raw(" "),
|
|
Span::styled(timestamp, Style::default().fg(theme::GRAY)),
|
|
])];
|
|
|
|
if let Some(detail) = ¬if.detail {
|
|
let max_len = inner.width.saturating_sub(2) as usize;
|
|
let d = if detail.len() > max_len {
|
|
format!("{}…", &detail[..max_len.saturating_sub(1)])
|
|
} else {
|
|
detail.clone()
|
|
};
|
|
lines.push(Line::from(Span::styled(
|
|
d,
|
|
Style::default().fg(theme::GRAY),
|
|
)));
|
|
} else {
|
|
let max_len = inner.width.saturating_sub(2) as usize;
|
|
let title = if notif.title.len() > max_len {
|
|
format!("{}…", ¬if.title[..max_len.saturating_sub(1)])
|
|
} else {
|
|
notif.title.clone()
|
|
};
|
|
lines.push(Line::from(Span::styled(
|
|
title,
|
|
Style::default().fg(theme::GRAY),
|
|
)));
|
|
}
|
|
|
|
let para = Paragraph::new(lines);
|
|
frame.render_widget(para, inner);
|
|
}
|