1 Commits

Author SHA1 Message Date
dependabot[bot]
68131bc92f Bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-09 03:33:45 +00:00
10 changed files with 21 additions and 740 deletions

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
@@ -61,7 +61,7 @@ jobs:
rust: [stable, beta]
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@master
@@ -116,7 +116,7 @@ jobs:
target: aarch64-apple-darwin
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
@@ -159,7 +159,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Run cargo-audit
uses: rustsec/audit-check@v1

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

View File

@@ -5,9 +5,6 @@ on:
tags:
- 'v*.*.*'
permissions:
contents: write
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
@@ -111,7 +108,7 @@ jobs:
target: aarch64-apple-darwin
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
@@ -151,14 +148,11 @@ jobs:
if: matrix.os == 'macos-latest'
run: strip target/${{ matrix.target }}/release/piglet
- name: Rename binary
run: |
cp target/${{ matrix.target }}/release/piglet piglet-${{ matrix.target }}
- name: Upload release binary
uses: softprops/action-gh-release@v1
with:
files: piglet-${{ matrix.target }}
files: target/${{ matrix.target }}/release/piglet
name: piglet-${{ matrix.target }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -168,7 +162,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Run cargo-audit
uses: rustsec/audit-check@v1
@@ -28,7 +28,7 @@ jobs:
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Dependency Review
uses: actions/dependency-review-action@v4

View File

@@ -21,8 +21,8 @@ palette = "0.7"
# Terminal manipulation
crossterm = "0.27"
# Async runtime (for timing and signal handling)
tokio = { version = "1.35", features = ["time", "rt-multi-thread", "macros", "signal", "sync"] }
# Async runtime (for timing)
tokio = { version = "1.35", features = ["time", "rt-multi-thread", "macros"] }
# Process execution
which = "5.0"

View File

@@ -393,592 +393,6 @@ impl Effect for GradientFlow {
}
}
// Phase 1: High-Impact Effects from Animista
// Shake effect - horizontal vibration
pub struct Shake;
impl Effect for Shake {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Fast oscillation that decreases over time
let frequency = 20.0;
let amplitude = 10.0 * (1.0 - progress);
let offset_x = (progress * frequency * std::f64::consts::PI * 2.0).sin() * amplitude;
EffectResult::new(ascii_art.render()).with_offset(offset_x as i32, 0)
}
fn name(&self) -> &str {
"shake"
}
}
// Wobble effect - rotation wobble (simulated with offset variations)
pub struct Wobble;
impl Effect for Wobble {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Wobble with decreasing amplitude
let angle = progress * std::f64::consts::PI * 4.0;
let amplitude = 15.0 * (1.0 - progress);
let offset_x = (angle.sin() * amplitude) as i32;
let offset_y = (angle.cos() * amplitude * 0.3) as i32;
EffectResult::new(ascii_art.render()).with_offset(offset_x, offset_y)
}
fn name(&self) -> &str {
"wobble"
}
}
// Vibrate effect - rapid small movements
pub struct Vibrate;
impl Effect for Vibrate {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Very fast, small vibrations
let frequency = 50.0;
let amplitude = 3.0;
let offset_x = (progress * frequency * std::f64::consts::PI).sin() * amplitude;
let offset_y = (progress * frequency * std::f64::consts::PI * 1.3).cos() * amplitude;
EffectResult::new(ascii_art.render()).with_offset(offset_x as i32, offset_y as i32)
}
fn name(&self) -> &str {
"vibrate"
}
}
// Heartbeat effect - pulsing scale with heartbeat rhythm
pub struct Heartbeat;
impl Effect for Heartbeat {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Two-beat pulse pattern like a heartbeat
let beat_progress = (progress * 2.0) % 1.0;
let scale = if beat_progress < 0.3 {
1.0 + (beat_progress / 0.3) * 0.15
} else if beat_progress < 0.4 {
1.15 - ((beat_progress - 0.3) / 0.1) * 0.15
} else if beat_progress < 0.6 {
1.0 + ((beat_progress - 0.4) / 0.2) * 0.1
} else if beat_progress < 0.7 {
1.1 - ((beat_progress - 0.6) / 0.1) * 0.1
} else {
1.0
};
let scaled = ascii_art.scale(scale);
EffectResult::new(scaled.render()).with_scale(scale)
}
fn name(&self) -> &str {
"heartbeat"
}
}
// Flip horizontal - flip text horizontally
pub struct FlipHorizontal;
impl Effect for FlipHorizontal {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Scale horizontally from 1 to -1 (flip)
let scale = 1.0 - (progress * 2.0);
if scale <= 0.0 {
// Show reversed text when flipped
let lines: Vec<String> = ascii_art
.get_lines()
.iter()
.map(|line| line.chars().rev().collect())
.collect();
EffectResult::new(lines.join("\n"))
} else {
let scaled = ascii_art.scale(scale);
EffectResult::new(scaled.render()).with_scale(scale)
}
}
fn name(&self) -> &str {
"flip-horizontal"
}
}
// Flip vertical - flip text vertically
pub struct FlipVertical;
impl Effect for FlipVertical {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Scale vertically with midpoint flip
let scale = 1.0 - (progress * 2.0).min(1.0);
if progress > 0.5 {
// Show reversed lines when flipped
let mut lines: Vec<String> = ascii_art
.get_lines()
.iter()
.map(|s| s.to_string())
.collect();
lines.reverse();
let result_scale = (progress - 0.5) * 2.0;
let scaled = AsciiArt::new(lines.join("\n")).scale(result_scale);
EffectResult::new(scaled.render()).with_scale(result_scale)
} else {
let scaled = ascii_art.scale(scale.max(0.1));
EffectResult::new(scaled.render()).with_scale(scale.max(0.1))
}
}
fn name(&self) -> &str {
"flip-vertical"
}
}
// Swing effect - pendulum motion
pub struct Swing;
impl Effect for Swing {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Pendulum swing with decreasing amplitude
let swings = 2.0;
let angle = (progress * swings * std::f64::consts::PI * 2.0).sin() * (1.0 - progress);
let offset_x = (angle * 20.0) as i32;
let offset_y = (angle.abs() * 5.0) as i32;
EffectResult::new(ascii_art.render()).with_offset(offset_x, -offset_y)
}
fn name(&self) -> &str {
"swing"
}
}
// Sway effect - gentle swaying motion
pub struct Sway;
impl Effect for Sway {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Smooth, gentle sway
let angle = (progress * std::f64::consts::PI * 2.0).sin();
let offset_x = (angle * 8.0) as i32;
let offset_y = (angle.abs() * 2.0) as i32;
EffectResult::new(ascii_art.render()).with_offset(offset_x, offset_y)
}
fn name(&self) -> &str {
"sway"
}
}
// Roll-in effect - roll in from left with rotation
pub struct RollIn;
impl Effect for RollIn {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Slide in from left while appearing to roll
let offset_x = ((1.0 - progress) * -(ascii_art.width() as f64 + 20.0)) as i32;
let rotation_effect = ((1.0 - progress) * 5.0) as i32;
let offset_y = (rotation_effect as f64 * (progress * std::f64::consts::PI).sin()) as i32;
EffectResult::new(ascii_art.render()).with_offset(offset_x, offset_y)
}
fn name(&self) -> &str {
"roll-in"
}
}
// Roll-out effect - roll out to right with rotation
pub struct RollOut;
impl Effect for RollOut {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Slide out to right while appearing to roll
let offset_x = (progress * (ascii_art.width() as f64 + 20.0)) as i32;
let rotation_effect = (progress * 5.0) as i32;
let offset_y = (rotation_effect as f64 * (progress * std::f64::consts::PI).sin()) as i32;
EffectResult::new(ascii_art.render()).with_offset(offset_x, offset_y)
}
fn name(&self) -> &str {
"roll-out"
}
}
// Phase 2: Specialty & Combination Effects
// Puff-in effect - scale up from tiny with fade in
pub struct PuffIn;
impl Effect for PuffIn {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Start very small and expand while fading in
let scale = 0.1 + (progress * 0.9);
let opacity = progress;
let scaled = ascii_art.scale(scale);
EffectResult::new(scaled.render())
.with_scale(scale)
.with_opacity(opacity)
}
fn name(&self) -> &str {
"puff-in"
}
}
// Puff-out effect - scale down to tiny with fade out
pub struct PuffOut;
impl Effect for PuffOut {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Shrink down while fading out
let scale = 1.0 - (progress * 0.9);
let opacity = 1.0 - progress;
let scaled = ascii_art.scale(scale.max(0.1));
EffectResult::new(scaled.render())
.with_scale(scale)
.with_opacity(opacity)
}
fn name(&self) -> &str {
"puff-out"
}
}
// Slide-rotate horizontal - slide from left with rotation
pub struct SlideRotateHor;
impl Effect for SlideRotateHor {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Slide in from left while rotating
let offset_x = ((1.0 - progress) * -(ascii_art.width() as f64 + 10.0)) as i32;
let rotation_progress = 1.0 - progress;
let offset_y =
(rotation_progress * 10.0 * (rotation_progress * std::f64::consts::PI).sin()) as i32;
EffectResult::new(ascii_art.render()).with_offset(offset_x, offset_y)
}
fn name(&self) -> &str {
"slide-rotate-hor"
}
}
// Slide-rotate vertical - slide from top with rotation
pub struct SlideRotateVer;
impl Effect for SlideRotateVer {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Slide in from top while rotating
let offset_y = ((1.0 - progress) * -(ascii_art.height() as f64 + 5.0)) as i32;
let rotation_progress = 1.0 - progress;
let offset_x =
(rotation_progress * 15.0 * (rotation_progress * std::f64::consts::PI).cos()) as i32;
EffectResult::new(ascii_art.render()).with_offset(offset_x, offset_y)
}
fn name(&self) -> &str {
"slide-rotate-ver"
}
}
// Flicker effect - random flickering opacity
pub struct Flicker;
impl Effect for Flicker {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Fast flickering that stabilizes
let flicker_speed = 30.0;
let stability = progress; // Gets more stable over time
let flicker = ((progress * flicker_speed).sin() + 1.0) / 2.0;
let opacity = stability + (1.0 - stability) * flicker;
EffectResult::new(ascii_art.render()).with_opacity(opacity)
}
fn name(&self) -> &str {
"flicker"
}
}
// Tracking-in effect - letters expand from center
pub struct TrackingIn;
impl Effect for TrackingIn {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Simulate letter spacing by adding spaces between characters
let spacing = ((1.0 - progress) * 3.0) as usize;
if spacing == 0 {
EffectResult::new(ascii_art.render())
} else {
let lines: Vec<String> = ascii_art
.get_lines()
.iter()
.map(|line| {
line.chars()
.map(|c| {
if c == ' ' {
" ".repeat(spacing + 1)
} else {
format!("{}{}", c, " ".repeat(spacing))
}
})
.collect::<String>()
})
.collect();
EffectResult::new(lines.join("\n"))
}
}
fn name(&self) -> &str {
"tracking-in"
}
}
// Tracking-out effect - letters contract to center
pub struct TrackingOut;
impl Effect for TrackingOut {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Simulate letter spacing by adding spaces between characters
let spacing = (progress * 3.0) as usize;
if spacing == 0 {
EffectResult::new(ascii_art.render())
} else {
let lines: Vec<String> = ascii_art
.get_lines()
.iter()
.map(|line| {
line.chars()
.map(|c| {
if c == ' ' {
" ".repeat(spacing + 1)
} else {
format!("{}{}", c, " ".repeat(spacing))
}
})
.collect::<String>()
})
.collect();
EffectResult::new(lines.join("\n"))
}
}
fn name(&self) -> &str {
"tracking-out"
}
}
// Bounce-top effect - bounce down from top
pub struct BounceTop;
impl Effect for BounceTop {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Bounce from top with easing
let bounces = 2.0;
let bounce_height = ascii_art.height() as f64 + 10.0;
let base_offset = (1.0 - progress) * bounce_height;
let bounce_factor =
(progress * bounces * std::f64::consts::PI).sin().abs() * (1.0 - progress);
let offset_y = -(base_offset + bounce_factor * 5.0) as i32;
EffectResult::new(ascii_art.render()).with_offset(0, offset_y)
}
fn name(&self) -> &str {
"bounce-top"
}
}
// Bounce-bottom effect - bounce up from bottom
pub struct BounceBottom;
impl Effect for BounceBottom {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Bounce from bottom with easing
let bounces = 2.0;
let bounce_height = ascii_art.height() as f64 + 10.0;
let base_offset = (1.0 - progress) * bounce_height;
let bounce_factor =
(progress * bounces * std::f64::consts::PI).sin().abs() * (1.0 - progress);
let offset_y = (base_offset + bounce_factor * 5.0) as i32;
EffectResult::new(ascii_art.render()).with_offset(0, offset_y)
}
fn name(&self) -> &str {
"bounce-bottom"
}
}
// Tilt-in effect - tilt in with perspective simulation
pub struct TiltIn;
impl Effect for TiltIn {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Simulate tilting in with combined scale and offset
let tilt_progress = 1.0 - progress;
let scale = 0.5 + (progress * 0.5);
let offset_x = (tilt_progress * 20.0 * (tilt_progress * std::f64::consts::PI).sin()) as i32;
let offset_y = -(tilt_progress * 15.0) as i32;
let scaled = ascii_art.scale(scale);
EffectResult::new(scaled.render())
.with_scale(scale)
.with_offset(offset_x, offset_y)
}
fn name(&self) -> &str {
"tilt-in"
}
}
// Phase 3: Additional Slide, Blink, Focus, and Shadow Effects
// Slide-out-top effect - slide out to top
pub struct SlideOutTop;
impl Effect for SlideOutTop {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
let offset_y = -(progress * (ascii_art.height() as f64 + 10.0)) as i32;
EffectResult::new(ascii_art.render()).with_offset(0, offset_y)
}
fn name(&self) -> &str {
"slide-out-top"
}
}
// Slide-out-bottom effect - slide out to bottom
pub struct SlideOutBottom;
impl Effect for SlideOutBottom {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
let offset_y = (progress * (ascii_art.height() as f64 + 10.0)) as i32;
EffectResult::new(ascii_art.render()).with_offset(0, offset_y)
}
fn name(&self) -> &str {
"slide-out-bottom"
}
}
// Slide-out-left effect - slide out to left
pub struct SlideOutLeft;
impl Effect for SlideOutLeft {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
let offset_x = -(progress * (ascii_art.width() as f64 + 10.0)) as i32;
EffectResult::new(ascii_art.render()).with_offset(offset_x, 0)
}
fn name(&self) -> &str {
"slide-out-left"
}
}
// Slide-out-right effect - slide out to right
pub struct SlideOutRight;
impl Effect for SlideOutRight {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
let offset_x = (progress * (ascii_art.width() as f64 + 10.0)) as i32;
EffectResult::new(ascii_art.render()).with_offset(offset_x, 0)
}
fn name(&self) -> &str {
"slide-out-right"
}
}
// Blink effect - rapid on/off blinking
pub struct Blink;
impl Effect for Blink {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Blink 3 times during animation
let blinks = 6.0;
let blink_state = ((progress * blinks).floor() % 2.0) as i32;
let opacity = if blink_state == 0 { 1.0 } else { 0.0 };
EffectResult::new(ascii_art.render()).with_opacity(opacity)
}
fn name(&self) -> &str {
"blink"
}
}
// Focus-in effect - simulate coming into focus with scale and opacity
pub struct FocusIn;
impl Effect for FocusIn {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Start blurry (small scale, low opacity) and come into focus
let scale = 0.7 + (progress * 0.3);
let opacity = progress.powf(0.5);
let scaled = ascii_art.scale(scale);
EffectResult::new(scaled.render())
.with_scale(scale)
.with_opacity(opacity)
}
fn name(&self) -> &str {
"focus-in"
}
}
// Blur-out effect - simulate going out of focus
pub struct BlurOut;
impl Effect for BlurOut {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Go out of focus (reduce scale, reduce opacity)
let scale = 1.0 - (progress * 0.3);
let opacity = (1.0 - progress).powf(0.5);
let scaled = ascii_art.scale(scale);
EffectResult::new(scaled.render())
.with_scale(scale)
.with_opacity(opacity)
}
fn name(&self) -> &str {
"blur-out"
}
}
// Shadow-drop effect - drop down with shadow simulation
pub struct ShadowDrop;
impl Effect for ShadowDrop {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Drop down from above with increasing shadow (opacity)
let drop_distance = 20.0;
let offset_y = -((1.0 - progress) * drop_distance) as i32;
let opacity = 0.3 + (progress * 0.7); // Start semi-transparent
EffectResult::new(ascii_art.render())
.with_offset(0, offset_y)
.with_opacity(opacity)
}
fn name(&self) -> &str {
"shadow-drop"
}
}
// Shadow-pop effect - pop forward with shadow simulation
pub struct ShadowPop;
impl Effect for ShadowPop {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Scale up quickly then settle, simulating popping forward
let pop_scale = if progress < 0.5 {
1.0 + (progress * 2.0) * 0.3
} else {
1.3 - ((progress - 0.5) * 2.0) * 0.3
};
let scaled = ascii_art.scale(pop_scale);
EffectResult::new(scaled.render()).with_scale(pop_scale)
}
fn name(&self) -> &str {
"shadow-pop"
}
}
// Rotate-center effect - rotate around center point
pub struct RotateCenter;
impl Effect for RotateCenter {
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
// Simulate rotation with alternating line offsets
let rotations = 1.0;
let angle = progress * rotations * std::f64::consts::PI * 2.0;
let max_offset = 5.0;
let lines: Vec<String> = ascii_art
.get_lines()
.iter()
.enumerate()
.map(|(i, line)| {
let line_factor = (i as f64 / ascii_art.get_lines().len().max(1) as f64) - 0.5;
let offset = (angle.sin() * line_factor * max_offset) as i32;
if offset > 0 {
format!("{}{}", " ".repeat(offset as usize), line)
} else if offset < 0 {
line.chars().skip(offset.unsigned_abs() as usize).collect()
} else {
line.to_string()
}
})
.collect();
EffectResult::new(lines.join("\n"))
}
fn name(&self) -> &str {
"rotate-center"
}
}
/// Get effect by name
pub fn get_effect(name: &str) -> Result<Box<dyn Effect>> {
match name {
@@ -1003,36 +417,6 @@ pub fn get_effect(name: &str) -> Result<Box<dyn Effect>> {
"gradient-flow" => Ok(Box::new(GradientFlow)),
"rotate-in" => Ok(Box::new(RotateIn)),
"rotate-out" => Ok(Box::new(RotateOut)),
"shake" => Ok(Box::new(Shake)),
"wobble" => Ok(Box::new(Wobble)),
"vibrate" => Ok(Box::new(Vibrate)),
"heartbeat" => Ok(Box::new(Heartbeat)),
"flip-horizontal" => Ok(Box::new(FlipHorizontal)),
"flip-vertical" => Ok(Box::new(FlipVertical)),
"swing" => Ok(Box::new(Swing)),
"sway" => Ok(Box::new(Sway)),
"roll-in" => Ok(Box::new(RollIn)),
"roll-out" => Ok(Box::new(RollOut)),
"puff-in" => Ok(Box::new(PuffIn)),
"puff-out" => Ok(Box::new(PuffOut)),
"slide-rotate-hor" => Ok(Box::new(SlideRotateHor)),
"slide-rotate-ver" => Ok(Box::new(SlideRotateVer)),
"flicker" => Ok(Box::new(Flicker)),
"tracking-in" => Ok(Box::new(TrackingIn)),
"tracking-out" => Ok(Box::new(TrackingOut)),
"bounce-top" => Ok(Box::new(BounceTop)),
"bounce-bottom" => Ok(Box::new(BounceBottom)),
"tilt-in" => Ok(Box::new(TiltIn)),
"slide-out-top" => Ok(Box::new(SlideOutTop)),
"slide-out-bottom" => Ok(Box::new(SlideOutBottom)),
"slide-out-left" => Ok(Box::new(SlideOutLeft)),
"slide-out-right" => Ok(Box::new(SlideOutRight)),
"blink" => Ok(Box::new(Blink)),
"focus-in" => Ok(Box::new(FocusIn)),
"blur-out" => Ok(Box::new(BlurOut)),
"shadow-drop" => Ok(Box::new(ShadowDrop)),
"shadow-pop" => Ok(Box::new(ShadowPop)),
"rotate-center" => Ok(Box::new(RotateCenter)),
_ => bail!("Unknown effect: {}", name),
}
}
@@ -1062,35 +446,5 @@ pub fn list_effects() -> Vec<&'static str> {
"gradient-flow",
"rotate-in",
"rotate-out",
"shake",
"wobble",
"vibrate",
"heartbeat",
"flip-horizontal",
"flip-vertical",
"swing",
"sway",
"roll-in",
"roll-out",
"puff-in",
"puff-out",
"slide-rotate-hor",
"slide-rotate-ver",
"flicker",
"tracking-in",
"tracking-out",
"bounce-top",
"bounce-bottom",
"tilt-in",
"slide-out-top",
"slide-out-bottom",
"slide-out-left",
"slide-out-right",
"blink",
"focus-in",
"blur-out",
"shadow-drop",
"shadow-pop",
"rotate-center",
]
}

View File

@@ -43,7 +43,7 @@ impl AnimationEngine {
self
}
pub async fn run(&self, terminal: &mut TerminalManager) -> Result<bool> {
pub async fn run(&self, terminal: &mut TerminalManager) -> Result<()> {
let renderer = renderer::Renderer::new(
&self.ascii_art,
self.duration_ms,

View File

@@ -2,12 +2,6 @@ use crate::animation::{easing::EasingFunction, effects::Effect, timeline::Timeli
use crate::color::{apply, ColorEngine};
use crate::utils::{ansi, ascii::AsciiArt, terminal::TerminalManager};
use anyhow::Result;
use crossterm::event::{self, Event, KeyCode, KeyModifiers};
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use std::time::Duration;
use tokio::time::sleep;
pub struct Renderer<'a> {
@@ -36,52 +30,17 @@ impl<'a> Renderer<'a> {
}
}
pub async fn render(&self, terminal: &mut TerminalManager) -> Result<bool> {
pub async fn render(&self, terminal: &mut TerminalManager) -> Result<()> {
let mut timeline = Timeline::new(self.timeline.duration_ms(), self.timeline.fps());
timeline.start();
// Spawn background thread to listen for exit keys
let should_exit = Arc::new(AtomicBool::new(false));
let should_exit_clone = should_exit.clone();
std::thread::spawn(move || loop {
if let Ok(true) = event::poll(Duration::from_millis(100)) {
if let Ok(Event::Key(key)) = event::read() {
match key.code {
KeyCode::Char('q') | KeyCode::Esc => {
should_exit_clone.store(true, Ordering::Relaxed);
break;
}
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
should_exit_clone.store(true, Ordering::Relaxed);
break;
}
_ => {}
}
}
}
if should_exit_clone.load(Ordering::Relaxed) {
break;
}
});
loop {
// Check for exit FIRST
if should_exit.load(Ordering::Relaxed) {
return Ok(true); // User requested exit
}
let frame_start = std::time::Instant::now();
// Calculate progress with easing
let linear_progress = timeline.progress();
let eased_progress = self.easing.ease(linear_progress);
// Check again before rendering
if should_exit.load(Ordering::Relaxed) {
return Ok(true); // User requested exit
}
// Apply effect
let effect_result = self.effect.apply(self.ascii_art, eased_progress);
@@ -92,11 +51,6 @@ impl<'a> Renderer<'a> {
effect_result.text.clone()
};
// Check before terminal operations
if should_exit.load(Ordering::Relaxed) {
return Ok(true); // User requested exit
}
// Render to terminal
terminal.clear()?;
terminal.refresh_size()?;
@@ -128,14 +82,9 @@ impl<'a> Renderer<'a> {
}
}
// Check if user wants to exit
if should_exit.load(Ordering::Relaxed) {
return Ok(true); // User requested exit
}
// Check if animation is complete before advancing
if timeline.is_complete() {
return Ok(false); // Animation completed naturally
break;
}
// Advance to next frame and wait
@@ -144,21 +93,11 @@ impl<'a> Renderer<'a> {
let elapsed = frame_start.elapsed();
if elapsed < frame_duration {
let sleep_duration = frame_duration - elapsed;
// Break sleep into small chunks to check should_exit frequently
let chunk_duration = Duration::from_millis(5);
let mut remaining = sleep_duration;
while remaining > Duration::ZERO {
if should_exit.load(Ordering::Relaxed) {
return Ok(true); // User requested exit during sleep
}
let sleep_time = remaining.min(chunk_duration);
sleep(sleep_time).await;
remaining = remaining.saturating_sub(sleep_time);
}
sleep(frame_duration - elapsed).await;
}
}
Ok(())
}
fn apply_colors(&self, text: &str, progress: f64) -> String {

View File

@@ -61,14 +61,8 @@ async fn run_piglet(args: PigletCli) -> Result<()> {
// Run animation
loop {
let user_exited = animation_engine.run(&mut terminal).await?;
animation_engine.run(&mut terminal).await?;
// If user pressed exit key, stop looping
if user_exited {
break;
}
// If not looping, stop after one animation
if !args.loop_animation {
break;
}