use crate::theme; use rand::Rng; use ratatui::buffer::Buffer; use ratatui::layout::Rect; use ratatui::style::{Color, Style}; use ratatui::widgets::Widget; const CHARS: &[char] = &['·', '✦', '✧', '°', '•', '+', '⋆', '*']; struct Sparkle { x: u16, y: u16, char_idx: usize, life: u8, } #[derive(Default)] pub struct Sparkles { sparkles: Vec, } impl Sparkles { pub fn tick(&mut self, area: Rect) { let mut rng = rand::thread_rng(); for _ in 0..3 { if rng.gen_bool(0.6) { self.sparkles.push(Sparkle { x: rng.gen_range(0..area.width), y: rng.gen_range(0..area.height), char_idx: rng.gen_range(0..CHARS.len()), life: rng.gen_range(15..40), }); } } self.sparkles .iter_mut() .for_each(|s| s.life = s.life.saturating_sub(1)); self.sparkles.retain(|s| s.life > 0); } } impl Widget for &Sparkles { fn render(self, area: Rect, buf: &mut Buffer) { let colors = theme::get().sparkle.colors; for sp in &self.sparkles { let color = colors[sp.char_idx % colors.len()]; let intensity = (sp.life as f32 / 30.0).min(1.0); let r = (color.0 as f32 * intensity) as u8; let g = (color.1 as f32 * intensity) as u8; let b = (color.2 as f32 * intensity) as u8; if sp.x < area.width && sp.y < area.height { let x = area.x + sp.x; let y = area.y + sp.y; let ch = CHARS[sp.char_idx]; buf[(x, y)].set_char(ch).set_style(Style::new().fg(Color::Rgb(r, g, b))); } } } }