Skip to main content

matrixcode_tui/workflow/
progress.rs

1//! Compact Progress View
2//!
3//! Alternative compact display for workflow execution progress
4
5use crate::workflow::types::{WorkflowViewState, node_type_icon};
6use ratatui::{
7    buffer::Buffer,
8    layout::Rect,
9    style::{Color, Style},
10};
11
12/// Render compact progress view
13pub fn render_progress_view(state: &WorkflowViewState, area: Rect, buf: &mut Buffer) {
14    if state.workflow_def.is_none() {
15        buf.set_string(area.x, area.y, "No workflow running", Style::default());
16        return;
17    }
18
19    let def = state.workflow_def.as_ref().unwrap();
20
21    // Title line
22    let status_label = if let Some(ctx) = &state.context {
23        match ctx.status {
24            matrixcode_core::workflow::WorkflowStatus::Running => "⟳ running",
25            matrixcode_core::workflow::WorkflowStatus::Completed => "✓ completed",
26            matrixcode_core::workflow::WorkflowStatus::Failed => "✗ failed",
27            matrixcode_core::workflow::WorkflowStatus::Paused => "⏸ paused",
28            _ => "○ pending",
29        }
30    } else {
31        "○ pending"
32    };
33
34    let title = format!("{} [{}]", def.name, status_label);
35    let title_len = title.chars().count() as u16;
36    buf.set_string(
37        area.x,
38        area.y,
39        &title,
40        Style::default().fg(Color::White).bold(),
41    );
42
43    // Progress bar (if we have width)
44    if area.width > 30 {
45        let (completed, total) = state.progress();
46        let bar_start = area.x + title_len + 2;
47        let bar_width = area
48            .width
49            .saturating_sub(bar_start - area.x)
50            .saturating_sub(10);
51
52        if bar_width > 0 {
53            let filled = if total > 0 {
54                ((bar_width as usize) * completed) / total
55            } else {
56                0
57            };
58
59            buf.set_string(bar_start, area.y, " [", Style::default().fg(Color::Gray));
60            for i in 0..bar_width as usize {
61                let ch = if i < filled { "█" } else { "░" };
62                let color = if i < filled {
63                    Color::Green
64                } else {
65                    Color::Gray
66                };
67                buf.set_string(
68                    bar_start + 1 + i as u16,
69                    area.y,
70                    ch,
71                    Style::default().fg(color),
72                );
73            }
74            buf.set_string(
75                bar_start + 1 + bar_width,
76                area.y,
77                "] ",
78                Style::default().fg(Color::Gray),
79            );
80            buf.set_string(
81                bar_start + 3 + bar_width,
82                area.y,
83                format!("{}/{}", completed, total),
84                Style::default().fg(Color::Gray),
85            );
86        }
87    }
88
89    // Node status line
90    if area.height > 1 {
91        let node_y = area.y + 1;
92        let mut x = area.x;
93
94        buf.set_string(x, node_y, "Nodes: ", Style::default().fg(Color::Gray));
95        x += 7;
96
97        for node in &def.nodes {
98            if x >= area.right() {
99                break;
100            }
101            let status = state.get_node_status(&node.id);
102            let type_icon = node_type_icon(&node.node_type);
103            let status_icon = status.icon();
104
105            let color = match status.color() {
106                "gray" => Color::Gray,
107                "yellow" => Color::Yellow,
108                "green" => Color::Green,
109                "red" => Color::Red,
110                "blue" => Color::Blue,
111                _ => Color::Reset,
112            };
113
114            buf.set_string(
115                x,
116                node_y,
117                format!("{}{}", type_icon, status_icon),
118                Style::default().fg(color),
119            );
120            x += 3;
121        }
122    }
123}
124
125/// Render inline progress indicator (for status line)
126pub fn render_inline_progress(state: &WorkflowViewState, buf: &mut Buffer, x: u16, y: u16) -> u16 {
127    if state.workflow_def.is_none() {
128        return x;
129    }
130
131    let (completed, total) = state.progress();
132    let text = format!(" wf:{}/{:02}", completed, total);
133    let text_len = text.len() as u16;
134
135    let color = if completed == total && total > 0 {
136        Color::Green
137    } else if let Some(ctx) = &state.context {
138        match ctx.status {
139            matrixcode_core::workflow::WorkflowStatus::Failed => Color::Red,
140            matrixcode_core::workflow::WorkflowStatus::Running => Color::Yellow,
141            _ => Color::Gray,
142        }
143    } else {
144        Color::Gray
145    };
146
147    buf.set_string(x, y, &text, Style::default().fg(color));
148    x + text_len
149}