ghostscope_ui/components/loading/
progress.rs

1use ratatui::{
2    style::{Color, Style},
3    text::{Line, Span},
4    widgets::{Block, Borders, Gauge, Paragraph},
5    Frame,
6};
7
8use super::{LoadingProgress, ModuleState};
9
10/// Progress bar component for loading
11pub struct ProgressRenderer;
12
13impl ProgressRenderer {
14    /// Render overall progress bar
15    pub fn render_progress_bar(
16        f: &mut Frame,
17        area: ratatui::layout::Rect,
18        progress: &LoadingProgress,
19    ) {
20        let ratio = progress.progress_ratio();
21        let completed = progress.completed_count;
22        let total = progress.total_modules();
23
24        let progress_bar = Gauge::default()
25            .block(Block::default().borders(Borders::NONE))
26            .gauge_style(Style::default().fg(Color::Cyan))
27            .ratio(ratio)
28            .label(format!(
29                "{completed}/{total} modules ({}%)",
30                (ratio * 100.0) as u8
31            ));
32
33        f.render_widget(progress_bar, area);
34    }
35
36    /// Render recently loaded modules list
37    pub fn render_recent_modules(
38        f: &mut Frame,
39        area: ratatui::layout::Rect,
40        progress: &LoadingProgress,
41        max_items: usize,
42    ) {
43        let recent = progress.recently_finished(max_items);
44        let mut lines = Vec::new();
45
46        for module in recent {
47            let path = if module.path.len() > 50 {
48                format!("...{}", &module.path[module.path.len() - 47..])
49            } else {
50                module.path.clone()
51            };
52
53            match &module.state {
54                ModuleState::Completed => {
55                    if let Some(stats) = &module.stats {
56                        let load_time = module.load_time.unwrap_or(0.0);
57                        let line = Line::from(vec![
58                            Span::styled("✅ ", Style::default().fg(Color::Green)),
59                            Span::styled(path, Style::default().fg(Color::White)),
60                            Span::styled(
61                                format!(
62                                    "  📈 Functions: {} | Variables: {} | Types: {} | Time: {:.1}s",
63                                    stats.functions, stats.variables, stats.types, load_time
64                                ),
65                                Style::default().fg(Color::Gray),
66                            ),
67                        ]);
68                        lines.push(line);
69                    }
70                }
71                ModuleState::Failed(error) => {
72                    let load_time = module.load_time.unwrap_or(0.0);
73                    let line = Line::from(vec![
74                        Span::styled("✗ ", Style::default().fg(Color::Red)),
75                        Span::styled(path, Style::default().fg(Color::White)),
76                        Span::styled(
77                            format!("  ❌ Failed: {error} | Time: {load_time:.1}s"),
78                            Style::default().fg(Color::Red),
79                        ),
80                    ]);
81                    lines.push(line);
82                }
83                _ => {} // Skip queued and loading states
84            }
85        }
86
87        if lines.is_empty() {
88            lines.push(Line::from(Span::styled(
89                "No modules processed yet...",
90                Style::default().fg(Color::DarkGray),
91            )));
92        }
93
94        let title = if progress.failed_count > 0 {
95            format!("📁 Recently processed: ({} failed)", progress.failed_count)
96        } else {
97            "📁 Recently loaded:".to_string()
98        };
99
100        let paragraph = Paragraph::new(lines).block(
101            Block::default()
102                .title(title)
103                .borders(Borders::NONE)
104                .title_style(Style::default().fg(Color::Yellow)),
105        );
106
107        f.render_widget(paragraph, area);
108    }
109
110    /// Render current loading status
111    pub fn render_current_status(
112        f: &mut Frame,
113        area: ratatui::layout::Rect,
114        progress: &LoadingProgress,
115    ) {
116        let status_line = if let Some(current) = &progress.current_loading {
117            let path = if current.len() > 60 {
118                format!("...{}", &current[current.len() - 57..])
119            } else {
120                current.clone()
121            };
122            Line::from(vec![
123                Span::styled("⏳ Loading: ", Style::default().fg(Color::Yellow)),
124                Span::styled(path, Style::default().fg(Color::White)),
125            ])
126        } else {
127            Line::from(Span::styled(
128                "Waiting for next module...",
129                Style::default().fg(Color::DarkGray),
130            ))
131        };
132
133        let paragraph = Paragraph::new(status_line);
134        f.render_widget(paragraph, area);
135    }
136
137    /// Render total statistics
138    pub fn render_stats(f: &mut Frame, area: ratatui::layout::Rect, progress: &LoadingProgress) {
139        let stats = progress.total_stats();
140        let elapsed = progress.elapsed_time();
141
142        let stats_line = Line::from(vec![
143            Span::styled("⏱️ Elapsed: ", Style::default().fg(Color::DarkGray)),
144            Span::styled(format!("{elapsed:.1}s"), Style::default().fg(Color::White)),
145            Span::styled(" | ", Style::default().fg(Color::DarkGray)),
146            Span::styled("📊 Total: ", Style::default().fg(Color::DarkGray)),
147            Span::styled(
148                format!(
149                    "{} functions | {} variables | {} types",
150                    stats.functions, stats.variables, stats.types
151                ),
152                Style::default().fg(Color::Cyan),
153            ),
154        ]);
155
156        let paragraph = Paragraph::new(stats_line);
157        f.render_widget(paragraph, area);
158    }
159}