stynx-code-tui 3.3.1

Terminal user interface with ratatui for interactive sessions
Documentation
use ratatui::{
    buffer::Buffer,
    layout::Rect,
    style::{Modifier, Style},
    text::{Line, Span},
    widgets::{Clear, Paragraph, Widget},
};

use crate::state::{ToastKind, ToastState};
use crate::theme;

pub struct ToastStack<'a> {
    pub state: &'a ToastState,
}

impl<'a> ToastStack<'a> {
    pub fn new(state: &'a ToastState) -> Self { Self { state } }
}

impl<'a> Widget for ToastStack<'a> {
    fn render(self, area: Rect, buf: &mut Buffer) {
        if self.state.items.is_empty() {
            return;
        }

        let max_w = 50.min(area.width.saturating_sub(4)) as usize;
        let mut y = area.y + 1;
        let right_edge = area.x + area.width.saturating_sub(2);

        for toast in &self.state.items {
            if y >= area.y + area.height { break; }

            let (icon, color) = match toast.kind {
                ToastKind::Info => ("", theme::ACCENT()),
                ToastKind::Success => ("", theme::SUCCESS()),
                ToastKind::Warning => ("", theme::WARNING()),
                ToastKind::Error => ("", theme::ERROR()),
            };

            let trimmed: String = if toast.message.chars().count() > max_w.saturating_sub(4) {
                let mut s: String = toast.message.chars().take(max_w.saturating_sub(5)).collect();
                s.push('');
                s
            } else {
                toast.message.clone()
            };

            let text = format!(" {icon}  {trimmed} ");
            let width = text.chars().count() as u16;
            let x = right_edge.saturating_sub(width);

            let rect = Rect { x, y, width, height: 1 };
            Clear.render(rect, buf);

            let line = Line::from(vec![
                Span::styled(format!(" {icon}  "), Style::default().fg(color).bg(theme::BACKGROUND_ELEMENT()).add_modifier(Modifier::BOLD)),
                Span::styled(format!("{trimmed} "), Style::default().fg(theme::TEXT()).bg(theme::BACKGROUND_ELEMENT())),
            ]);
            Paragraph::new(line)
                .style(Style::default().bg(theme::BACKGROUND_ELEMENT()))
                .render(rect, buf);

            y += 1;
        }
    }
}