From 12793893c330d94dfa4a9b93c11da13eadddf18f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Sun, 9 Nov 2025 07:08:24 +0100 Subject: [PATCH] Fix: Propagate user exit signal to break animation loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **CRITICAL BUG FIX**: When using -l (loop), the outer loop in main.rs was restarting the animation even after user pressed exit keys. Changes: - render() now returns Result instead of Result<()> - Returns true when user presses exit key (q/ESC/Ctrl+C) - Returns false when animation completes naturally - main.rs checks return value and breaks loop on user exit This fixes the infinite loop issue where pressing q/ESC/Ctrl+C had no effect when using the -l flag. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/animation/mod.rs | 2 +- src/animation/renderer.rs | 16 +++++++--------- src/main.rs | 8 +++++++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/animation/mod.rs b/src/animation/mod.rs index b03c34d..a55e56e 100644 --- a/src/animation/mod.rs +++ b/src/animation/mod.rs @@ -43,7 +43,7 @@ impl AnimationEngine { self } - pub async fn run(&self, terminal: &mut TerminalManager) -> Result<()> { + pub async fn run(&self, terminal: &mut TerminalManager) -> Result { let renderer = renderer::Renderer::new( &self.ascii_art, self.duration_ms, diff --git a/src/animation/renderer.rs b/src/animation/renderer.rs index a17a5d3..245e461 100644 --- a/src/animation/renderer.rs +++ b/src/animation/renderer.rs @@ -33,7 +33,7 @@ impl<'a> Renderer<'a> { } } - pub async fn render(&self, terminal: &mut TerminalManager) -> Result<()> { + pub async fn render(&self, terminal: &mut TerminalManager) -> Result { let mut timeline = Timeline::new(self.timeline.duration_ms(), self.timeline.fps()); timeline.start(); @@ -67,7 +67,7 @@ impl<'a> Renderer<'a> { loop { // Check for exit FIRST if should_exit.load(Ordering::Relaxed) { - break; + return Ok(true); // User requested exit } let frame_start = std::time::Instant::now(); @@ -78,7 +78,7 @@ impl<'a> Renderer<'a> { // Check again before rendering if should_exit.load(Ordering::Relaxed) { - break; + return Ok(true); // User requested exit } // Apply effect @@ -93,7 +93,7 @@ impl<'a> Renderer<'a> { // Check before terminal operations if should_exit.load(Ordering::Relaxed) { - break; + return Ok(true); // User requested exit } // Render to terminal @@ -129,12 +129,12 @@ impl<'a> Renderer<'a> { // Check if user wants to exit if should_exit.load(Ordering::Relaxed) { - break; + return Ok(true); // User requested exit } // Check if animation is complete before advancing if timeline.is_complete() { - break; + return Ok(false); // Animation completed naturally } // Advance to next frame and wait @@ -150,7 +150,7 @@ impl<'a> Renderer<'a> { while remaining > Duration::ZERO { if should_exit.load(Ordering::Relaxed) { - break; + return Ok(true); // User requested exit during sleep } let sleep_time = remaining.min(chunk_duration); sleep(sleep_time).await; @@ -158,8 +158,6 @@ impl<'a> Renderer<'a> { } } } - - Ok(()) } fn apply_colors(&self, text: &str, progress: f64) -> String { diff --git a/src/main.rs b/src/main.rs index 4bb263f..0be5802 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,8 +61,14 @@ async fn run_piglet(args: PigletCli) -> Result<()> { // Run animation loop { - animation_engine.run(&mut terminal).await?; + let user_exited = 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; }