Skip to content

Commit 802777f

Browse files
committed
fix: Add missing animated_progress.py module for AI validation
1 parent dfa7d7f commit 802777f

1 file changed

Lines changed: 103 additions & 0 deletions

File tree

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Simple ASCII Animation Progress Indicator for Claude Code Operations
4+
5+
Provides a clean spinner animation during long-running Claude Code operations
6+
to give visual feedback that the process is working.
7+
"""
8+
9+
import threading
10+
import time
11+
import sys
12+
from typing import Optional
13+
14+
15+
class AnimatedProgress:
16+
"""Simple spinner animation for long-running operations"""
17+
18+
# Braille spinner - looks professional and works on most terminals
19+
SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
20+
21+
def __init__(self, message: str = "🧠 Thinking", update_interval: float = 0.5):
22+
"""
23+
Initialize the progress animation
24+
25+
Args:
26+
message: Base message to show (e.g. "🧠 Thinking")
27+
update_interval: How often to update animation (seconds)
28+
"""
29+
self.message = message
30+
self.update_interval = update_interval
31+
self.stop_event = threading.Event()
32+
self.animation_thread: Optional[threading.Thread] = None
33+
self.frame_index = 0
34+
35+
def start(self):
36+
"""Start the animation in a background thread"""
37+
if self.animation_thread and self.animation_thread.is_alive():
38+
return # Already running
39+
40+
self.stop_event.clear()
41+
self.frame_index = 0
42+
self.animation_thread = threading.Thread(target=self._animate, daemon=True)
43+
self.animation_thread.start()
44+
45+
def stop(self):
46+
"""Stop the animation and clean up"""
47+
if self.stop_event:
48+
self.stop_event.set()
49+
50+
if self.animation_thread and self.animation_thread.is_alive():
51+
self.animation_thread.join(timeout=1.0)
52+
53+
# Clear the line
54+
sys.stdout.write('\r' + ' ' * 50 + '\r')
55+
sys.stdout.flush()
56+
57+
def _animate(self):
58+
"""Internal animation loop - runs in background thread"""
59+
start_time = time.time()
60+
61+
while not self.stop_event.is_set():
62+
# Get current spinner frame
63+
frame = self.SPINNER_FRAMES[self.frame_index % len(self.SPINNER_FRAMES)]
64+
65+
# Calculate elapsed time
66+
elapsed = time.time() - start_time
67+
elapsed_str = f"{elapsed:.1f}s"
68+
69+
# Build the progress line
70+
progress_line = f"\r{frame} {self.message}... {elapsed_str}"
71+
72+
# Write without newline and flush immediately
73+
sys.stdout.write(progress_line)
74+
sys.stdout.flush()
75+
76+
# Wait for next frame or stop signal
77+
if self.stop_event.wait(self.update_interval):
78+
break
79+
80+
self.frame_index += 1
81+
82+
def __enter__(self):
83+
"""Context manager entry"""
84+
self.start()
85+
return self
86+
87+
def __exit__(self, exc_type, exc_val, exc_tb):
88+
"""Context manager exit"""
89+
self.stop()
90+
91+
92+
def test_animation():
93+
"""Test the animation for a few seconds"""
94+
print("Testing animated progress...")
95+
96+
with AnimatedProgress("🧠 Claude Code analyzing") as progress:
97+
time.sleep(10) # Simulate a long operation
98+
99+
print("✅ Animation test completed!")
100+
101+
102+
if __name__ == "__main__":
103+
test_animation()

0 commit comments

Comments
 (0)