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