Initial commit: Piglet - Animated figlet wrapper

Add complete Rust implementation with:
- 20+ motion effects (fade, slide, scale, typewriter, wave, bounce, etc.)
- 18+ easing functions (linear, quad, cubic, elastic, back, bounce)
- Color support (CSS4 colors, hex codes, gradients)
- Figlet integration with custom fonts and options
- Cross-platform support (Linux, macOS, Windows)
- Comprehensive CI/CD workflows
- Full test suite with integration tests
- Documentation (README.md, CLAUDE.md)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-09 01:53:26 +01:00
commit f6fac85bc4
25 changed files with 1890 additions and 0 deletions

63
src/color/apply.rs Normal file
View File

@@ -0,0 +1,63 @@
use crate::parser::color::Color;
use crossterm::style::Color as CrosstermColor;
pub fn apply_color_to_char(ch: char, color: Color) -> String {
use crossterm::style::Stylize;
let crossterm_color = CrosstermColor::Rgb {
r: color.r,
g: color.g,
b: color.b,
};
format!("{}", ch.to_string().with(crossterm_color))
}
pub fn apply_color_to_line(line: &str, colors: &[Color]) -> String {
if colors.is_empty() {
return line.to_string();
}
line.chars()
.enumerate()
.map(|(i, ch)| {
if ch.is_whitespace() {
ch.to_string()
} else {
let color = colors[i % colors.len()];
apply_color_to_char(ch, color)
}
})
.collect()
}
pub fn apply_gradient_to_text(text: &str, colors: &[Color]) -> String {
let lines: Vec<&str> = text.lines().collect();
let total_chars: usize = lines.iter().map(|l| l.chars().count()).sum();
if total_chars == 0 || colors.is_empty() {
return text.to_string();
}
let mut result = String::new();
let mut char_index = 0;
for (line_idx, line) in lines.iter().enumerate() {
for ch in line.chars() {
if ch.is_whitespace() {
result.push(ch);
} else {
let color_index = (char_index * colors.len()) / total_chars.max(1);
let color = colors[color_index.min(colors.len() - 1)];
result.push_str(&apply_color_to_char(ch, color));
char_index += 1;
}
}
if line_idx < lines.len() - 1 {
result.push('\n');
}
}
result
}

26
src/color/gradient.rs Normal file
View File

@@ -0,0 +1,26 @@
use crate::parser::gradient::Gradient;
use crate::parser::color::Color;
use anyhow::Result;
pub struct GradientEngine {
gradient: Gradient,
}
impl GradientEngine {
pub fn new(gradient: Gradient) -> Self {
Self { gradient }
}
pub fn from_string(gradient_str: &str) -> Result<Self> {
let gradient = Gradient::parse(gradient_str)?;
Ok(Self::new(gradient))
}
pub fn color_at(&self, t: f64) -> Color {
self.gradient.color_at(t)
}
pub fn colors(&self, steps: usize) -> Vec<Color> {
self.gradient.colors(steps)
}
}

21
src/color/palette.rs Normal file
View File

@@ -0,0 +1,21 @@
"#ffff00".to_string(),
]).unwrap()
}
/// Create ocean palette
pub fn ocean() -> Self {
Self::from_strings(&[
"#000080".to_string(),
"#0000ff".to_string(),
"#4169e1".to_string(),
"#87ceeb".to_string(),
"#add8e6".to_string(),
]).unwrap()
}
}
impl Default for ColorPalette {
fn default() -> Self {
Self::rainbow()
}
}