pub mod detail;
pub mod header;
pub mod main_list;
use crate::tui::commands;
use crate::tui::state::{AppState, InputMode};
use ratatui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
widgets::{Block, Borders, Clear, List, ListItem, ListState, Paragraph},
};
pub fn draw_main_ui<B: Backend>(f: &mut ratatui::Frame<B>, app: &mut AppState) {
let size = f.size();
f.render_widget(Block::default().style(app.theme.background()), size);
let header_h = 4u16.min(size.height);
let mut detail_h = (size.height / 3).max(7);
detail_h = detail_h.min(size.height.saturating_sub(header_h));
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(header_h),
Constraint::Min(0),
Constraint::Length(detail_h),
])
.split(size);
header::draw(f, chunks[0], app);
main_list::draw(f, chunks[1], app);
detail::draw(f, chunks[2], app);
draw_overlays(f, size, app);
}
fn draw_overlays<B: Backend>(f: &mut ratatui::Frame<B>, size: Rect, app: &mut AppState) {
if matches!(app.input_mode, InputMode::Command) {
let total = app.command_suggestions.len();
let rows = total.min(commands::MAX_VISIBLE_SUGGESTIONS) as u16;
let overlay_h = (rows + 5).min(size.height);
let overlay = Rect {
x: size.x,
y: size.y + size.height.saturating_sub(overlay_h),
width: size.width,
height: overlay_h,
};
f.render_widget(Clear, overlay);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
.split(overlay);
let prompt = Paragraph::new(format!(":{}", app.command_input))
.style(app.theme.text())
.block(
Block::default()
.borders(Borders::ALL)
.border_style(app.theme.border())
.title("Command"),
);
f.render_widget(prompt, chunks[0]);
let visible = chunks[1].height.saturating_sub(2) as usize;
let (start, highlight_row) =
commands::suggestion_window(app.command_selected, total, visible);
let items: Vec<ListItem> = app
.command_suggestions
.iter()
.skip(start)
.take(visible)
.map(|s| ListItem::new(s.clone()))
.collect();
let mut state = ListState::default();
if !items.is_empty() {
state.select(Some(highlight_row));
}
let list = List::new(items)
.block(
Block::default()
.borders(Borders::ALL)
.border_style(app.theme.border())
.title("Suggestions (Tab to complete)"),
)
.style(app.theme.text())
.highlight_style(app.theme.list_highlight())
.highlight_symbol("ยป ");
f.render_stateful_widget(list, chunks[1], &mut state);
} else if matches!(app.input_mode, InputMode::Search) {
let overlay_h = 3u16.min(size.height);
let overlay = Rect {
x: size.x,
y: size.y + size.height.saturating_sub(overlay_h),
width: size.width,
height: overlay_h,
};
f.render_widget(Clear, overlay);
let prompt = Paragraph::new(format!("/{}", app.command_input))
.style(app.theme.text())
.block(
Block::default()
.borders(Borders::ALL)
.border_style(app.theme.border())
.title("Filter (Enter: apply, Esc: cancel)"),
);
f.render_widget(prompt, overlay);
}
}