terminal_jarvis/progress_utils.rs
1// src/progress_utils.rs
2//
3// Progress indication utilities for Terminal Jarvis operations
4//
5// Provides spinner and progress bar utilities with theme integration
6// to enhance user experience during long-running operations.
7
8use crate::theme::theme_global_config;
9use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
10use std::time::Duration;
11use tokio::time::sleep;
12
13/// Progress utilities for enhancing user experience during operations
14///
15/// Provides themed progress indicators, spinners, and progress bars that integrate
16/// with Terminal Jarvis's theming system. Designed to make cold starts and long-running
17/// operations feel more responsive and professional.
18///
19/// All progress indicators respect the user's theme configuration and provide
20/// consistent visual feedback across different Terminal Jarvis operations.
21pub struct ProgressUtils;
22
23impl ProgressUtils {
24 /// Creates a themed spinner for indeterminate progress operations
25 ///
26 /// Displays an animated spinner with the specified message, using Terminal Jarvis
27 /// theme colors for consistency. Ideal for operations where progress cannot be
28 /// measured (e.g., network requests, tool initialization).
29 ///
30 /// # Arguments
31 ///
32 /// * `message` - The message to display alongside the spinner
33 ///
34 /// # Returns
35 ///
36 /// A configured ProgressBar in spinner mode
37 ///
38 /// # Example
39 ///
40 /// ```rust,ignore
41 /// let spinner = ProgressUtils::spinner("Initializing tool...");
42 /// // Perform long operation
43 /// spinner.finish_with_message("Tool ready!");
44 /// ```
45 pub fn spinner(message: &str) -> ProgressBar {
46 let pb = ProgressBar::new_spinner();
47
48 pb.set_style(
49 ProgressStyle::default_spinner()
50 .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"])
51 .template("{spinner:.cyan} {msg}")
52 .unwrap(),
53 );
54
55 pb.set_message(message.to_string());
56
57 pb.enable_steady_tick(Duration::from_millis(100));
58
59 pb
60 }
61
62 /// Creates a themed progress bar for determinate progress operations
63 ///
64 /// Displays a progress bar with percentage and ETA, using Terminal Jarvis
65 /// theme colors. Ideal for operations where total progress can be measured
66 /// (e.g., file downloads, batch processing).
67 ///
68 /// # Arguments
69 ///
70 /// * `total` - The total number of steps for the progress bar
71 /// * `message` - The message to display alongside the progress bar
72 ///
73 /// # Returns
74 ///
75 /// A configured ProgressBar with the specified total and message
76 ///
77 /// # Example
78 ///
79 /// ```rust,ignore
80 /// let pb = ProgressUtils::progress_bar(100, "Processing items...");
81 /// for i in 0..100 {
82 /// pb.inc(1);
83 /// // Perform work
84 /// }
85 /// pb.finish();
86 /// ```
87 #[allow(dead_code)]
88 pub fn progress_bar(total: u64, message: &str) -> ProgressBar {
89 let pb = ProgressBar::new(total);
90
91 pb.set_style(
92 ProgressStyle::default_bar()
93 .template("{msg} [{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {eta}")
94 .unwrap()
95 .progress_chars("██░"),
96 );
97
98 pb.set_message(message.to_string());
99
100 pb
101 }
102
103 /// Creates a multi-progress manager for concurrent operations
104 ///
105 /// Enables multiple progress indicators to run simultaneously without
106 /// interfering with each other's display. Useful for batch operations
107 /// or parallel task execution.
108 ///
109 /// # Returns
110 ///
111 /// A MultiProgress instance for managing multiple progress indicators
112 ///
113 /// # Example
114 ///
115 /// ```rust,ignore
116 /// let multi = ProgressUtils::multi_progress();
117 /// let pb1 = multi.add(ProgressBar::new(100));
118 /// let pb2 = multi.add(ProgressBar::new(50));
119 /// // Run concurrent operations
120 /// ```
121 #[allow(dead_code)]
122 pub fn multi_progress() -> MultiProgress {
123 MultiProgress::new()
124 }
125
126 /// Completes progress indicator with a success message
127 ///
128 /// Displays a success message and provides visual feedback that the operation
129 /// completed successfully. Includes a brief pause to ensure visibility.
130 ///
131 /// # Arguments
132 ///
133 /// * `pb` - The ProgressBar to complete
134 /// * `message` - Success message to display
135 ///
136 /// # Example
137 ///
138 /// ```rust,ignore
139 /// let spinner = ProgressUtils::spinner("Installing tool...");
140 /// // Perform installation
141 /// ProgressUtils::finish_with_success(&spinner, "Tool installed successfully!");
142 /// ```
143 pub fn finish_with_success(pb: &ProgressBar, message: &str) {
144 pb.finish_with_message(format!("SUCCESS: {message}"));
145
146 // Brief pause to let user see the success message before clearing
147 std::thread::sleep(std::time::Duration::from_millis(300));
148 }
149
150 /// Completes progress indicator with an error message
151 ///
152 /// Displays an error message and provides visual feedback that the operation
153 /// failed. Includes a brief pause to ensure visibility.
154 ///
155 /// # Arguments
156 ///
157 /// * `pb` - The ProgressBar to complete
158 /// * `message` - Error message to display
159 ///
160 /// # Example
161 ///
162 /// ```rust,ignore
163 /// let spinner = ProgressUtils::spinner("Connecting to service...");
164 /// // Attempt connection
165 /// if connection_failed {
166 /// ProgressUtils::finish_with_error(&spinner, "Connection failed");
167 /// }
168 /// ```
169 pub fn finish_with_error(pb: &ProgressBar, message: &str) {
170 pb.finish_with_message(format!("ERROR: {message}"));
171
172 // Brief pause to let user see the error message before clearing
173 std::thread::sleep(std::time::Duration::from_millis(500));
174 }
175
176 /// Simulate installation progress with realistic steps
177 pub async fn simulate_installation_progress(pb: &ProgressBar, tool_name: &str) {
178 let steps = vec![
179 ("Checking NPM registry", 200),
180 ("Downloading package metadata", 300),
181 ("Resolving dependencies", 400),
182 ("Downloading packages", 800),
183 ("Installing dependencies", 600),
184 ("Building native modules", 400),
185 ("Finalizing installation", 200),
186 ];
187
188 for (step, duration) in steps {
189 pb.set_message(format!("Installing {tool_name}: {step}"));
190
191 sleep(Duration::from_millis(duration)).await;
192 }
193 }
194
195 /// Simulate tool verification progress
196 pub async fn simulate_verification_progress(pb: &ProgressBar, tool_name: &str) {
197 let steps = vec![
198 ("Locating binary", 150),
199 ("Checking PATH", 100),
200 ("Verifying version", 200),
201 ("Testing functionality", 250),
202 ];
203
204 for (step, duration) in steps {
205 pb.set_message(format!("Verifying {tool_name}: {step}"));
206
207 sleep(Duration::from_millis(duration)).await;
208 }
209 }
210
211 /// Show a quick loading animation for brief operations
212 #[allow(dead_code)]
213 pub async fn quick_load(message: &str, duration_ms: u64) -> ProgressBar {
214 let pb = Self::spinner(message);
215
216 sleep(Duration::from_millis(duration_ms)).await;
217
218 pb
219 }
220
221 /// Create a styled info message
222 pub fn info_message(message: &str) {
223 let theme = theme_global_config::current_theme();
224
225 println!("{} {}", theme.accent("T.JARVIS:"), theme.primary(message));
226 }
227
228 /// Create a styled warning message
229 pub fn warning_message(message: &str) {
230 let theme = theme_global_config::current_theme();
231
232 println!(
233 "{} {}",
234 theme.secondary("⚠ ADVISORY:"),
235 theme.primary(message)
236 );
237 }
238
239 /// Create a styled error message
240 pub fn error_message(message: &str) {
241 let theme = theme_global_config::current_theme();
242
243 println!("{} {}", theme.accent("✗ SYSTEM:"), theme.primary(message));
244 }
245
246 /// Create a styled success message
247 pub fn success_message(message: &str) {
248 let theme = theme_global_config::current_theme();
249
250 println!("{} {}", theme.accent("✓ COMPLETE:"), theme.primary(message));
251 }
252}
253
254/// Progress context for coordinating long-running operations
255///
256/// Manages multiple related progress indicators and provides a unified interface
257/// for complex operations that involve multiple steps or parallel tasks.
258///
259/// Integrates with Terminal Jarvis theming and provides consistent progress
260/// reporting across different operation types.
261pub struct ProgressContext {
262 pub spinner: ProgressBar,
263
264 #[allow(dead_code)]
265 pub operation: String,
266}
267
268impl ProgressContext {
269 pub fn new(operation: &str) -> Self {
270 let spinner = ProgressUtils::spinner(operation);
271
272 Self {
273 spinner,
274 operation: operation.to_string(),
275 }
276 }
277
278 pub fn update_message(&self, message: &str) {
279 self.spinner.set_message(message.to_string());
280 }
281
282 pub fn finish_success(&self, message: &str) {
283 ProgressUtils::finish_with_success(&self.spinner, message);
284
285 // Clear the line after showing success message
286 print!("\x1b[2K\r");
287 }
288
289 pub fn finish_error(&self, message: &str) {
290 ProgressUtils::finish_with_error(&self.spinner, message);
291 // Keep error messages visible longer
292 }
293}
294
295impl Drop for ProgressContext {
296 fn drop(&mut self) {
297 if !self.spinner.is_finished() {
298 self.spinner.finish_and_clear();
299 }
300
301 // Ensure cursor is visible and terminal is clean
302 print!("\x1b[?25h"); // Show cursor
303
304 print!("\x1b[2K\r"); // Clear current line
305 }
306}