void-focus 0.3.0-alpha.6

A feature-rich terminal focus timer with task tracking
Documentation
use super::*;

pub(crate) fn draw_zen_dashboard(f: &mut Frame, app: &App, area: Rect) {
    let theme = &app.theme;
    let icons = app.icons;
    let t = &app.timer;
    let on_break = is_break_mode(t.mode);
    let mc = mode_color(theme, t.mode);

    let chunks = Layout::default()
        .constraints([
            Constraint::Min(1),
            Constraint::Length(1),
            Constraint::Length(if on_break { 3 } else { 0 }),
            Constraint::Length(2),
        ])
        .split(area);

    let cycle = t.config.long_break_every.max(1);
    let style = theme.scene_style(mc);
    let options = ZenSceneOptions {
        task_progress: app.active_task_progress(),
        sessions_done: t.completed_focus_sessions % cycle,
        sessions_total: cycle,
        pending_tasks: app.pending_task_count(),
        active_task_index: app.active_task_pending_index(),
        layout: crate::canvas_timer::SceneLayout::Zen,
    };
    draw_zen_canvas(f, chunks[0], t, &style, &options);

    let (main_time, tenths, _) = format_time_stack(t);
    let session_label = t.cycle_label();
    let mut overlay_lines = vec![
        Line::from(vec![
            Span::styled(
                main_time,
                Style::default().fg(theme.text).add_modifier(Modifier::BOLD),
            ),
            Span::styled(tenths, Style::default().fg(theme.dim)),
        ]),
        Line::from(Span::styled(session_label, Style::default().fg(theme.dim))),
    ];
    if let Some(id) = app.active_task {
        if let Some(task) = app.data.tasks.iter().find(|t| t.id == id) {
            overlay_lines.push(Line::from(Span::styled(
                format!("{} {}", icons.task_active, truncate(&task.title, 36)),
                Style::default()
                    .fg(theme.accent)
                    .add_modifier(Modifier::BOLD),
            )));
        }
    }
    let time_area = Rect {
        x: chunks[0].x,
        y: chunks[0].y + chunks[0].height / 2 - 2,
        width: chunks[0].width,
        height: overlay_lines.len() as u16,
    };
    f.render_widget(
        Paragraph::new(overlay_lines).alignment(Alignment::Center),
        time_area,
    );

    let task_line = if let Some(id) = app.active_task {
        app.data.tasks.iter().find(|t| t.id == id).map(|task| {
            Line::from(vec![
                Span::styled(
                    format!("{} ", icons.task_active),
                    Style::default().fg(theme.accent),
                ),
                Span::styled(
                    truncate(&task.title, 48),
                    Style::default().fg(theme.text).add_modifier(Modifier::BOLD),
                ),
                Span::styled(
                    format!(
                        "  {} {}",
                        task_status_icon(icons, task.status),
                        task.status.short_label()
                    ),
                    Style::default().fg(task_status_color(theme, task.status)),
                ),
            ])
        })
    } else {
        None
    };
    f.render_widget(
        Paragraph::new(task_line.unwrap_or_else(|| {
            Line::from(Span::styled(
                if app.queue_empty() {
                    format!("{} All tasks done — free focus", icons.check)
                } else {
                    format!("{} No active task — [f] on dashboard", icons.dot)
                },
                Style::default().fg(theme.dim),
            ))
        }))
        .alignment(Alignment::Center),
        chunks[1],
    );

    if on_break {
        draw_break_tip(f, chunks[2], t, mc, theme.text, theme.dim, icons.heart);
    }

    chrome::draw_footer(f, app, chunks[3]);
}