Skip to main content

flow_tui/views/
logs.rs

1use crate::app::App;
2use ratatui::{
3    layout::{Constraint, Direction, Layout},
4    style::{Modifier, Style},
5    text::{Line, Span},
6    widgets::{Block, Borders, List, ListItem, Paragraph},
7    Frame,
8};
9
10pub fn render(frame: &mut Frame, app: &App) {
11    let area = frame.area();
12    let theme = &app.tui_theme;
13
14    // Split main area into logs and footer
15    let chunks = Layout::default()
16        .direction(Direction::Vertical)
17        .constraints([
18            Constraint::Min(0),    // Logs
19            Constraint::Length(1), // Footer
20        ])
21        .split(area);
22
23    let logs_area = chunks[0];
24    let footer_area = chunks[1];
25
26    let block = Block::default()
27        .title(format!("Logs ({} messages)", app.log_messages.len()))
28        .borders(Borders::ALL)
29        .border_style(Style::default().fg(theme.border))
30        .title_style(
31            Style::default()
32                .fg(theme.primary)
33                .add_modifier(Modifier::BOLD),
34        );
35
36    let inner = block.inner(logs_area);
37    frame.render_widget(block, logs_area);
38
39    if app.log_messages.is_empty() {
40        return;
41    }
42
43    // Create list items from log messages
44    let items: Vec<ListItem> = app
45        .log_messages
46        .iter()
47        .enumerate()
48        .map(|(idx, msg)| {
49            // Color-code by message content (simple heuristic)
50            let color = if msg.to_lowercase().contains("error") {
51                theme.error
52            } else if msg.to_lowercase().contains("warn") {
53                theme.warning
54            } else if msg.to_lowercase().contains("debug") {
55                theme.muted
56            } else {
57                theme.foreground
58            };
59
60            ListItem::new(Line::from(vec![
61                Span::styled(format!("[{}] ", idx + 1), Style::default().fg(theme.muted)),
62                Span::styled(msg, Style::default().fg(color)),
63            ]))
64        })
65        .collect();
66
67    let list = List::new(items);
68    frame.render_widget(list, inner);
69
70    // Render footer with key hints
71    let footer_text = if let Some(status) = app.get_status_message() {
72        status.to_string()
73    } else {
74        " 1-4:views  r:refresh  t:theme  ?:help  q:quit".to_string()
75    };
76
77    let footer = Paragraph::new(footer_text).style(Style::default().fg(theme.muted));
78    frame.render_widget(footer, footer_area);
79}