carch_core/ui/widgets/
header.rs

1use chrono::Local;
2use ratatui::Frame;
3use ratatui::layout::{Alignment, Constraint, Rect};
4use ratatui::style::{Modifier, Style};
5use ratatui::text::{Line, Span, Text};
6use ratatui::widgets::{Block, BorderType, Borders, Paragraph};
7
8use crate::ui::state::App;
9
10pub fn render_header(f: &mut Frame, app: &App, area: Rect) {
11    let header_block = Block::default()
12        .borders(Borders::ALL)
13        .border_style(Style::default().fg(app.theme.primary))
14        .border_type(BorderType::Rounded);
15
16    let inner_area = header_block.inner(area);
17    f.render_widget(header_block, area);
18
19    let chunks = ratatui::layout::Layout::default()
20        .direction(ratatui::layout::Direction::Horizontal)
21        .constraints([
22            Constraint::Percentage(33),
23            Constraint::Percentage(34),
24            Constraint::Percentage(33),
25        ])
26        .split(inner_area);
27
28    let total_scripts = app.all_scripts.values().map(Vec::len).sum::<usize>();
29    let left_text = Text::from(Line::from(vec![
30        Span::styled("Carch", Style::default().fg(app.theme.accent).add_modifier(Modifier::BOLD)),
31        Span::raw(format!(" | Total Scripts: {total_scripts}")),
32    ]));
33    f.render_widget(Paragraph::new(left_text).alignment(Alignment::Left), chunks[0]);
34
35    let time_str = Local::now().format("%H:%M:%S").to_string();
36    let time_text = Text::from(Line::from(vec![Span::styled(
37        time_str,
38        Style::default().fg(app.theme.secondary),
39    )]));
40    f.render_widget(Paragraph::new(time_text).alignment(Alignment::Center), chunks[1]);
41
42    let breadcrumb = if let Some(script_idx) = app.scripts.state.selected() {
43        let script = &app.scripts.items[script_idx];
44        let category_scripts = app.scripts.items.len();
45        let script_pos = script_idx + 1;
46        Text::from(Line::from(vec![
47            Span::styled(&script.category, Style::default().fg(app.theme.accent)),
48            Span::raw(" > "),
49            Span::styled(
50                &script.name,
51                Style::default().fg(app.theme.accent).add_modifier(Modifier::BOLD),
52            ),
53            Span::raw(format!(" ({script_pos}/{category_scripts})")),
54        ]))
55    } else if let Some(category_idx) = app.categories.state.selected() {
56        let category = &app.categories.items[category_idx];
57        let category_scripts = app.all_scripts.get(category).map_or(0, |s| s.len());
58        Text::from(Line::from(vec![
59            Span::styled(
60                category,
61                Style::default().fg(app.theme.accent).add_modifier(Modifier::BOLD),
62            ),
63            Span::raw(format!(" ({category_scripts} scripts)")),
64        ]))
65    } else {
66        Text::from(Span::styled("Select a category", Style::default().fg(app.theme.secondary)))
67    };
68    f.render_widget(Paragraph::new(breadcrumb).alignment(Alignment::Right), chunks[2]);
69}