use ratatui::Frame;
use ratatui::layout::Rect;
use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Clear, Paragraph};
use crate::app::{App, Level};
pub(super) fn draw_toast(f: &mut Frame, app: &App, buf_area: Rect) {
if app.prompt.is_open() || app.toast_remaining().is_none() {
return;
}
let msg = app.toast.text();
if msg.is_empty() {
return;
}
let level = app.toast.level();
let is_error = level == Level::Error;
let max_w = (buf_area.width / 2).max(10) as usize;
let content_max = max_w.saturating_sub(4).max(1);
let mut lines: Vec<String> = if is_error {
let mut out = Vec::new();
for raw in msg.lines() {
if raw.is_empty() {
out.push(String::new());
continue;
}
let mut buf = String::new();
let mut count = 0usize;
for ch in raw.chars() {
buf.push(ch);
count += 1;
if count >= content_max {
out.push(std::mem::take(&mut buf));
count = 0;
}
}
if !buf.is_empty() {
out.push(buf);
}
}
out
} else {
let first = msg.lines().next().unwrap_or("");
let s = if first.chars().count() > content_max {
let mut t: String = first.chars().take(content_max.saturating_sub(1)).collect();
t.push('…');
t
} else {
first.to_string()
};
vec![s]
};
let max_h = (buf_area.height / 2).max(3) as usize;
let hint_rows: usize = if is_error { 1 } else { 0 };
let body_cap = max_h.saturating_sub(2 + hint_rows).max(1);
if lines.len() > body_cap {
lines.truncate(body_cap);
if let Some(last) = lines.last_mut() {
let truncated: String = last
.chars()
.take(content_max.saturating_sub(1))
.collect::<String>();
*last = format!("{}…", truncated);
}
}
let body_w = lines.iter().map(|s| s.chars().count()).max().unwrap_or(0);
let hint_text = "[Esc] dismiss";
let inner_w = if is_error {
body_w.max(hint_text.chars().count())
} else {
body_w
};
let toast_w = (inner_w + 4) as u16;
let toast_h = (lines.len() + 2 + hint_rows) as u16;
if toast_w > buf_area.width || buf_area.height < toast_h {
return;
}
let area = Rect {
x: buf_area.x + buf_area.width - toast_w,
y: buf_area.y + buf_area.height - toast_h,
width: toast_w,
height: toast_h,
};
let bg = Style::default().bg(super::PANEL_BG);
let fg = match level {
Level::Info => Color::Reset,
Level::Warn => Color::Yellow,
Level::Error => Color::Red,
};
let block = Block::default()
.borders(Borders::ALL)
.border_style(bg.fg(Color::DarkGray))
.style(bg);
let inner = block.inner(area);
f.render_widget(Clear, area);
f.render_widget(block, area);
let mut rendered: Vec<Line> = lines
.into_iter()
.map(|l| {
Line::from(Span::styled(
format!(" {} ", l),
bg.fg(fg).add_modifier(Modifier::BOLD),
))
})
.collect();
if is_error {
rendered.push(Line::from(Span::styled(
format!(" {} ", hint_text),
bg.fg(Color::DarkGray),
)));
}
f.render_widget(Paragraph::new(rendered), inner);
}