scud/commands/spawn/tui/
header.rs

1//! Header view components for TUI monitor
2
3use ratatui::{
4    style::{Modifier, Style, Stylize},
5    text::{Line, Span},
6    widgets::{Block, Borders, Padding, Paragraph},
7    Frame,
8};
9
10use super::app::App;
11use super::theme::*;
12
13/// Render the main header bar with session name and status counts
14pub fn render_header(frame: &mut Frame, area: ratatui::layout::Rect, app: &App) {
15    let (starting, running, completed, failed) = app.status_counts();
16
17    // Swarm mode indicator
18    let swarm_indicator = if app.swarm_mode {
19        vec![
20            Span::styled("  ", Style::default()),
21            Span::styled(
22                "SWARM ",
23                Style::default()
24                    .fg(SWARM_PURPLE)
25                    .add_modifier(Modifier::BOLD),
26            ),
27        ]
28    } else {
29        vec![]
30    };
31
32    // Ralph mode indicator
33    let ralph_indicator = if app.ralph_mode {
34        vec![
35            Span::styled("  🔄 ", Style::default()),
36            Span::styled(
37                "RALPH ",
38                Style::default()
39                    .fg(RALPH_ORANGE)
40                    .add_modifier(Modifier::BOLD),
41            ),
42        ]
43    } else {
44        vec![]
45    };
46
47    // Status line with legend labels
48    let mut spans = vec![
49        Span::styled(" ", Style::default()),
50        Span::styled(
51            &app.session_name,
52            Style::default().fg(ACCENT).add_modifier(Modifier::BOLD),
53        ),
54    ];
55    spans.extend(swarm_indicator);
56    spans.extend(ralph_indicator);
57    spans.extend(vec![
58        Span::styled("    ", Style::default()),
59        // Gray = Starting/Waiting
60        Span::styled("â—‰ ", Style::default().fg(STATUS_STARTING)),
61        Span::styled("Starting ", Style::default().fg(TEXT_MUTED).dim()),
62        Span::styled(
63            format!("{}  ", starting),
64            Style::default().fg(STATUS_STARTING),
65        ),
66        // Green = Running
67        Span::styled("â—‰ ", Style::default().fg(STATUS_RUNNING)),
68        Span::styled("Running ", Style::default().fg(TEXT_MUTED).dim()),
69        Span::styled(
70            format!("{}  ", running),
71            Style::default().fg(STATUS_RUNNING),
72        ),
73        // Blue = Completed
74        Span::styled("â—‰ ", Style::default().fg(STATUS_COMPLETED)),
75        Span::styled("Done ", Style::default().fg(TEXT_MUTED).dim()),
76        Span::styled(
77            format!("{}  ", completed),
78            Style::default().fg(STATUS_COMPLETED),
79        ),
80        // Red = Failed
81        Span::styled("â—‰ ", Style::default().fg(STATUS_FAILED)),
82        Span::styled("Failed ", Style::default().fg(TEXT_MUTED).dim()),
83        Span::styled(format!("{}", failed), Style::default().fg(STATUS_FAILED)),
84    ]);
85    let status_line = Line::from(spans);
86
87    let header = Paragraph::new(status_line).block(
88        Block::default()
89            .borders(Borders::BOTTOM)
90            .border_style(Style::default().fg(BORDER_DEFAULT))
91            .style(Style::default().bg(BG_SECONDARY))
92            .padding(Padding::horizontal(1)),
93    );
94
95    frame.render_widget(header, area);
96}
97
98/// Render the fullscreen mode header (shows agent name)
99pub fn render_fullscreen_header(frame: &mut Frame, area: ratatui::layout::Rect, app: &App) {
100    let agent_name = app
101        .selected_agent()
102        .map(|a| format!("{}: {}", a.task_id, a.task_title))
103        .unwrap_or_else(|| "No agent".to_string());
104
105    let title = Line::from(vec![
106        Span::styled(" ", Style::default()),
107        Span::styled(
108            &agent_name,
109            Style::default().fg(ACCENT).add_modifier(Modifier::BOLD),
110        ),
111    ]);
112
113    let header = Paragraph::new(title).block(
114        Block::default()
115            .borders(Borders::BOTTOM)
116            .border_style(Style::default().fg(BORDER_ACTIVE))
117            .style(Style::default().bg(BG_SECONDARY)),
118    );
119
120    frame.render_widget(header, area);
121}