use ratatui::{ layout::{Constraint, Direction, Layout, Rect, Alignment}, style::{Color, Modifier, Style}, text::{Line, Span}, widgets::{Block, Borders, List, ListItem, Paragraph, Clear}, Frame, }; use crate::{state::{AppState, TimeItem}, app::{App, Screen}}; const ACTIVE_COLOR: Color = Color::Green; const INACTIVE_COLOR: Color = Color::Yellow; pub fn render(frame: &mut Frame, app: &App) { match app.current_screen { Screen::Main => render_main(frame, app), Screen::Help => render_help(frame), Screen::ConfigHelp => render_config_help(frame), } } fn render_main(frame: &mut Frame, app: &App) { let chunks = Layout::default() .direction(Direction::Vertical) .constraints([ Constraint::Ratio(1, 3), Constraint::Ratio(1, 3), Constraint::Ratio(1, 3), ]) .split(frame.size()); render_section( frame, chunks[0], "Permanent Items", &app.state.permanent_items, app.state.current_pane == 0, app.state.selected_indices[0], &app.state, ); render_section( frame, chunks[1], "Recurring Items", &app.state.recurring_items, app.state.current_pane == 1, app.state.selected_indices[1], &app.state, ); render_section( frame, chunks[2], "Recent Items", &app.state.recent_items, app.state.current_pane == 2, app.state.selected_indices[2], &app.state, ); if app.config.show_help_hint { let help_hint = Paragraph::new("(?) for help") .alignment(Alignment::Right) .style(Style::default().fg(Color::DarkGray)); let help_area = Rect { x: frame.size().width.saturating_sub(12), y: frame.size().height.saturating_sub(1), width: 12, height: 1, }; frame.render_widget(help_hint, help_area); } } fn render_section( frame: &mut Frame, area: Rect, title: &str, items: &[TimeItem], is_active: bool, selected: usize, state: &AppState, ) { let border_color = if is_active { ACTIVE_COLOR } else { INACTIVE_COLOR }; let block = Block::default() .borders(Borders::ALL) .title(title) .style(Style::default().fg(border_color)); let items: Vec = items .iter() .enumerate() .map(|(i, item)| { let is_running = state .active_timer .as_ref() .map(|(active, _)| active.name == item.name) .unwrap_or(false); let style = if is_running { Style::default().fg(ACTIVE_COLOR).add_modifier(Modifier::BOLD) } else if i == selected && is_active { Style::default().fg(border_color).add_modifier(Modifier::REVERSED) } else { Style::default() }; let mut line = item.name.clone(); if !item.tags.is_empty() { line.push_str(" ["); line.push_str(&item.tags.join(", ")); line.push(']'); } ListItem::new(Line::from(vec![Span::styled(line, style)])) }) .collect(); let list = List::new(items).block(block); frame.render_widget(list, area); } fn render_help(frame: &mut Frame) { let area = centered_rect(60, 20, frame.size()); frame.render_widget(Clear, area); let help_text = vec![ "WAT - Watson Time Tracker Interface", "", "This tool helps you track time using Watson with a convenient interface.", "", "The interface is divided into three sections:", "1. Permanent Items: Configured tasks that are always available", "2. Recurring Items: Frequently used tasks that you might return to", "3. Recent Items: One-off tasks, showing the last 20 used", "", "Navigation:", "- Use j/k or ↑/↓ to move selection up/down", "- Use h/l or ←/→ to switch between panes", "- Press Enter to start/stop time tracking", "", "Help Pages:", "c - Configuration help", "", "Main Commands:", "j/k, arrows - Navigate", "h/l, arrows - Switch panes", "Enter - Start/stop timer", "Ctrl+e - Edit tasks config", "c - Edit app config", "q - Quit", "? (or ESC) - Exit help", ]; let text = help_text.join("\n"); let block = Block::default() .title("Help") .borders(Borders::ALL) .style(Style::default().fg(Color::White)); let paragraph = Paragraph::new(text) .block(block) .style(Style::default().fg(Color::White)); frame.render_widget(paragraph, area); } fn render_config_help(frame: &mut Frame) { let area = centered_rect(60, 15, frame.size()); frame.render_widget(Clear, area); let help_text = vec![ "WAT Configuration", "", "The configuration file is in YAML format and supports these options:", "", "show_help_hint: true/false", " Controls visibility of the help hint in the main interface", "", "Example configuration:", "show_help_hint: true", "", "", "Commands:", "q (or ESC) - Return to main help", ]; let text = help_text.join("\n"); let block = Block::default() .title("Configuration Help") .borders(Borders::ALL) .style(Style::default().fg(Color::White)); let paragraph = Paragraph::new(text) .block(block) .style(Style::default().fg(Color::White)); frame.render_widget(paragraph, area); } fn centered_rect(width: u16, height: u16, r: Rect) -> Rect { let x = (r.width.saturating_sub(width)) / 2; let y = (r.height.saturating_sub(height)) / 2; Rect { x: r.x + x, y: r.y + y, width: width.min(r.width), height: height.min(r.height), } }