use ratatui::style::{Color, Modifier, Style};
use unicode_segmentation::UnicodeSegmentation;
use crate::tui::ocean;
pub const HOT_TAIL_GRAPHEMES: usize = 12;
#[must_use]
pub fn split_hot_tail(text: &str, active: bool, n: usize) -> (&str, &str) {
if !active || n == 0 || text.is_empty() {
return (text, "");
}
let graphemes: Vec<&str> = text.graphemes(true).collect();
if graphemes.len() <= n {
return ("", text);
}
let split_at = graphemes.len() - n;
let mut byte = 0usize;
for g in graphemes.iter().take(split_at) {
byte += g.len();
}
(&text[..byte], &text[byte..])
}
#[must_use]
pub fn breath_luminance(elapsed_ms: u128, reduced_motion: bool) -> f32 {
if reduced_motion {
return 1.12;
}
let period = 1_500u128;
let phase = (elapsed_ms % period) as f32 / period as f32;
let s = (phase * std::f32::consts::TAU).sin();
1.135 + s * 0.085
}
#[must_use]
pub fn hot_tail_style(base_fg: Color, elapsed_ms: u128, reduced_motion: bool) -> Style {
let scale = breath_luminance(elapsed_ms, reduced_motion);
let fg = ocean::scale_color(base_fg, scale);
Style::default().fg(fg).add_modifier(Modifier::BOLD)
}
#[must_use]
#[allow(dead_code)]
pub fn settled_style(base_fg: Color) -> Style {
Style::default().fg(base_fg)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn split_hot_tail_on_grapheme_boundary() {
let text = "hello δΈη!";
let (settled, hot) = split_hot_tail(text, true, 3);
assert_eq!(hot.graphemes(true).count(), 3);
assert_eq!(format!("{settled}{hot}"), text);
}
#[test]
fn inactive_stream_has_no_hot_tail() {
let (settled, hot) = split_hot_tail("abcdef", false, 12);
assert_eq!(settled, "abcdef");
assert!(hot.is_empty());
}
}