pub mod calendar;
pub mod date_picker;
mod debug;
pub mod description_textarea;
pub mod dialogs;
mod header;
mod help;
pub mod input;
mod layout;
pub mod search;
mod sidebar;
mod status_bar;
pub mod tag_input;
mod task_detail;
pub mod task_list;
pub mod theme;
use ratatui::{
layout::{Alignment, Rect},
style::Style,
text::{Line, Span},
widgets::{Block, Paragraph, Widget},
Frame,
};
use crate::app::{App, View};
pub const MIN_WIDTH: u16 = 100;
pub const MIN_HEIGHT: u16 = 20;
pub fn draw(frame: &mut Frame, app: &App) {
let area = frame.area();
let background = Block::default().style(Style::default().bg(theme::BG_DARK));
background.render(area, frame.buffer_mut());
if area.width < MIN_WIDTH || area.height < MIN_HEIGHT {
render_size_warning(frame, area);
return;
}
match app.current_view {
View::Main => layout::render_main_view(frame, app, frame.area()),
View::Help => help::render_help(frame, app, frame.area()),
View::DebugLogs => debug::render_debug_logs(frame, app, frame.area()),
View::Search => search::render_search_with_context(
frame,
&app.input_buffer,
app.input_cursor,
&app.search_results,
app.selected_search_index,
frame.area(),
Some(app.selected_project_name()),
),
View::TaskDetail => task_detail::render_task_detail(frame, app, frame.area()),
View::Calendar => calendar::render_calendar(frame, app, frame.area()),
}
if let Some(ref dialog) = app.dialog {
dialog.render(frame);
}
}
fn render_size_warning(frame: &mut Frame, area: Rect) {
use theme::{icons, WARNING, SUCCESS, ERROR, TEXT_MUTED, PRIMARY_LIGHT};
let width_ok = area.width >= MIN_WIDTH;
let height_ok = area.height >= MIN_HEIGHT;
let mut lines = vec![
Line::from(""),
Line::from(vec![
Span::styled(format!("{} ", icons::WARNING_ICON), Style::default().fg(WARNING)),
Span::styled("Terminal Too Small", Style::default().fg(WARNING)),
]),
Line::from(""),
Line::from(vec![
Span::raw(" Width: "),
Span::styled(
format!("{:>3}", area.width),
Style::default().fg(if width_ok { SUCCESS } else { ERROR }),
),
Span::styled(format!(" / {} ", MIN_WIDTH), Style::default().fg(TEXT_MUTED)),
Span::styled(
if width_ok { icons::CHECK } else { icons::CROSS },
Style::default().fg(if width_ok { SUCCESS } else { ERROR }),
),
]),
Line::from(vec![
Span::raw(" Height: "),
Span::styled(
format!("{:>3}", area.height),
Style::default().fg(if height_ok { SUCCESS } else { ERROR }),
),
Span::styled(format!(" / {} ", MIN_HEIGHT), Style::default().fg(TEXT_MUTED)),
Span::styled(
if height_ok { icons::CHECK } else { icons::CROSS },
Style::default().fg(if height_ok { SUCCESS } else { ERROR }),
),
]),
Line::from(""),
Line::from(Span::styled(
"Please resize your terminal window.",
Style::default().fg(TEXT_MUTED),
)),
];
if area.width >= 40 && area.height >= 8 {
lines.push(Line::from(""));
lines.push(Line::from(Span::styled(
"Ratado",
Style::default().fg(PRIMARY_LIGHT),
)));
}
let line_count = lines.len() as u16;
let y_offset = area.height.saturating_sub(line_count) / 2;
let centered_area = Rect {
x: area.x,
y: area.y + y_offset,
width: area.width,
height: area.height.saturating_sub(y_offset),
};
let paragraph = Paragraph::new(lines).alignment(Alignment::Center);
frame.render_widget(paragraph, centered_area);
}