use crate::color::Color;
use std::io::{self, Write};
use std::thread;
use std::time::Duration;
pub trait Shimmer {
fn shimmer(&self, color: Color, background: Option<Color>) -> ShimmerText;
fn shimmer_gradient(
&self,
start_color: Color,
end_color: Color,
background: Option<Color>,
) -> ShimmerText;
fn shine(&self, color: Color) -> ShimmerText;
fn glow(&self, color: Color, intensity: u8) -> ShimmerText;
}
#[derive(Clone)]
pub struct ShimmerText {
text: String,
color: Color,
background: Option<Color>,
gradient: Option<(Color, Color)>,
animation_type: AnimationType,
speed: Duration,
intensity: u8,
}
#[derive(Clone, Copy)]
enum AnimationType {
Shimmer,
Shine,
Glow,
Gradient,
}
impl ShimmerText {
fn new(text: String, color: Color, animation_type: AnimationType) -> Self {
Self {
text,
color,
background: None,
gradient: None,
animation_type,
speed: Duration::from_millis(150), intensity: 255,
}
}
pub fn speed(mut self, millis: u64) -> Self {
self.speed = Duration::from_millis(millis);
self
}
pub fn background(mut self, color: Color) -> Self {
self.background = Some(color);
self
}
pub fn intensity(mut self, intensity: u8) -> Self {
self.intensity = intensity;
self
}
pub fn animate(&self, duration_secs: u64) {
let total_frames = (duration_secs * 1000) / self.speed.as_millis() as u64;
for frame in 0..total_frames {
print!("\r\x1b[K");
match self.animation_type {
AnimationType::Shimmer => self.render_shimmer_frame(frame),
AnimationType::Shine => self.render_shine_frame(frame),
AnimationType::Glow => self.render_glow_frame(frame),
AnimationType::Gradient => self.render_gradient_frame(frame),
}
io::stdout().flush().unwrap();
thread::sleep(self.speed);
}
println!();
}
pub fn static_render(&self) -> String {
match self.animation_type {
AnimationType::Shimmer => self.render_shimmer_static(),
AnimationType::Shine => self.render_shine_static(),
AnimationType::Glow => self.render_glow_static(),
AnimationType::Gradient => self.render_gradient_static(),
}
}
fn render_shimmer_frame(&self, frame: u64) {
let text_len = self.visible_char_count();
if text_len == 0 {
return;
}
let highlight_pos = (frame as usize) % (text_len + 6);
let mut result = String::new();
if let Some(bg) = self.background {
result.push_str(&format!("\x1b[48;2;{};{};{}m", bg.r, bg.g, bg.b));
}
let mut visible_index = 0;
for ch in self.text.chars() {
if ch == '\n' {
result.push(ch);
continue;
}
let is_highlight = visible_index >= highlight_pos.saturating_sub(3)
&& visible_index <= highlight_pos.saturating_add(3);
if is_highlight {
let distance = visible_index.abs_diff(highlight_pos) as f32;
let brightness_factor = 2.2 - (distance * 0.15); let bright_color = self.brighten_color(self.color, brightness_factor);
result.push_str(&format!(
"\x1b[38;2;{};{};{}m\x1b[1m",
bright_color.r, bright_color.g, bright_color.b
));
} else {
result.push_str(&format!(
"\x1b[38;2;{};{};{}m",
self.color.r, self.color.g, self.color.b
));
}
result.push(ch);
visible_index += 1;
}
result.push_str("\x1b[0m");
print!("{}", result);
}
fn render_shine_frame(&self, frame: u64) {
let text_len = self.visible_char_count();
if text_len == 0 {
return;
}
let wave_pos =
(frame as f64 * 0.3).sin() * (text_len as f64 / 2.0) + (text_len as f64 / 2.0);
let mut result = String::new();
if let Some(bg) = self.background {
result.push_str(&format!("\x1b[48;2;{};{};{}m", bg.r, bg.g, bg.b));
}
let mut visible_index = 0;
for ch in self.text.chars() {
if ch == '\n' {
result.push(ch);
continue;
}
let distance = (visible_index as f64 - wave_pos).abs();
let brightness = if distance < 3.0 {
1.0 + (0.8 * (1.0 - distance / 3.0))
} else {
1.0
};
let bright_color = self.brighten_color(self.color, brightness as f32);
result.push_str(&format!(
"\x1b[38;2;{};{};{}m",
bright_color.r, bright_color.g, bright_color.b
));
result.push(ch);
visible_index += 1;
}
result.push_str("\x1b[0m");
print!("{}", result);
}
fn render_glow_frame(&self, frame: u64) {
let pulse = ((frame as f64 * 0.3).sin() + 1.0) / 2.0; let glow_intensity = 0.7 + (pulse * 0.6);
let glowing_color = self.brighten_color(self.color, glow_intensity as f32);
let mut result = String::new();
if let Some(bg) = self.background {
result.push_str(&format!("\x1b[48;2;{};{};{}m", bg.r, bg.g, bg.b));
}
result.push_str(&format!(
"\x1b[38;2;{};{};{}m{}\x1b[0m",
glowing_color.r, glowing_color.g, glowing_color.b, self.text
));
print!("{}", result);
}
fn render_gradient_frame(&self, frame: u64) {
if let Some((start, end)) = self.gradient {
let shift = (frame as f64 * 0.05).sin() * 0.3 + 0.5;
let text_len = self.visible_char_count();
let mut result = String::new();
if let Some(bg) = self.background {
result.push_str(&format!("\x1b[48;2;{};{};{}m", bg.r, bg.g, bg.b));
}
let mut visible_index = 0;
for ch in self.text.chars() {
if ch == '\n' {
result.push(ch);
continue;
}
let progress = if text_len > 1 {
(visible_index as f64 / (text_len - 1) as f64 + shift) % 1.0
} else {
shift
};
let color = start.interpolate(&end, progress as f32);
result.push_str(&format!("\x1b[38;2;{};{};{}m", color.r, color.g, color.b));
result.push(ch);
visible_index += 1;
}
result.push_str("\x1b[0m");
print!("{}", result);
}
}
fn render_shimmer_static(&self) -> String {
format!(
"\x1b[38;2;{};{};{}m\x1b[1m{}\x1b[0m",
self.color.r, self.color.g, self.color.b, self.text
)
}
fn render_shine_static(&self) -> String {
let bright_color = self.brighten_color(self.color, 1.3);
format!(
"\x1b[38;2;{};{};{}m{}\x1b[0m",
bright_color.r, bright_color.g, bright_color.b, self.text
)
}
fn render_glow_static(&self) -> String {
let glowing_color = self.brighten_color(self.color, 1.2);
format!(
"\x1b[38;2;{};{};{}m{}\x1b[0m",
glowing_color.r, glowing_color.g, glowing_color.b, self.text
)
}
fn render_gradient_static(&self) -> String {
if let Some((start, end)) = self.gradient {
let text_len = self.visible_char_count();
let mut result = String::new();
let mut visible_index = 0;
for ch in self.text.chars() {
if ch == '\n' {
result.push(ch);
continue;
}
let progress = if text_len > 1 {
visible_index as f32 / (text_len - 1) as f32
} else {
0.0
};
let color = start.interpolate(&end, progress);
result.push_str(&format!("\x1b[38;2;{};{};{}m", color.r, color.g, color.b));
result.push(ch);
visible_index += 1;
}
result.push_str("\x1b[0m");
result
} else {
self.text.clone()
}
}
fn visible_char_count(&self) -> usize {
self.text.chars().filter(|&c| c != '\n').count()
}
fn brighten_color(&self, color: Color, factor: f32) -> Color {
let factor = factor.clamp(0.0, 2.0);
Color {
r: ((color.r as f32 * factor).min(255.0)) as u8,
g: ((color.g as f32 * factor).min(255.0)) as u8,
b: ((color.b as f32 * factor).min(255.0)) as u8,
}
}
}
impl Shimmer for str {
fn shimmer(&self, color: Color, background: Option<Color>) -> ShimmerText {
let mut shimmer = ShimmerText::new(self.to_string(), color, AnimationType::Shimmer);
if let Some(bg) = background {
shimmer = shimmer.background(bg);
}
shimmer
}
fn shimmer_gradient(
&self,
start_color: Color,
end_color: Color,
background: Option<Color>,
) -> ShimmerText {
let mut shimmer = ShimmerText::new(self.to_string(), start_color, AnimationType::Gradient);
shimmer.gradient = Some((start_color, end_color));
if let Some(bg) = background {
shimmer = shimmer.background(bg);
}
shimmer
}
fn shine(&self, color: Color) -> ShimmerText {
ShimmerText::new(self.to_string(), color, AnimationType::Shine)
}
fn glow(&self, color: Color, intensity: u8) -> ShimmerText {
ShimmerText::new(self.to_string(), color, AnimationType::Glow).intensity(intensity)
}
}
impl Shimmer for String {
fn shimmer(&self, color: Color, background: Option<Color>) -> ShimmerText {
let mut shimmer = ShimmerText::new(self.clone(), color, AnimationType::Shimmer);
if let Some(bg) = background {
shimmer = shimmer.background(bg);
}
shimmer
}
fn shimmer_gradient(
&self,
start_color: Color,
end_color: Color,
background: Option<Color>,
) -> ShimmerText {
let mut shimmer = ShimmerText::new(self.clone(), start_color, AnimationType::Gradient);
shimmer.gradient = Some((start_color, end_color));
if let Some(bg) = background {
shimmer = shimmer.background(bg);
}
shimmer
}
fn shine(&self, color: Color) -> ShimmerText {
ShimmerText::new(self.clone(), color, AnimationType::Shine)
}
fn glow(&self, color: Color, intensity: u8) -> ShimmerText {
ShimmerText::new(self.clone(), color, AnimationType::Glow).intensity(intensity)
}
}
impl std::fmt::Display for ShimmerText {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.static_render())
}
}