//! Overlay rendering: each overlay variant gets its own module with a //! `pub fn render(frame, area, block, state)` entry point, dispatched by //! the top-level `render_overlay` function in this module. pub mod bash; pub mod clear_confirm; pub mod diff; pub mod editor; pub mod effort; pub mod help; pub mod key_input; pub mod learning; pub mod loading; pub mod mcp; pub mod model_selector; pub mod quit_confirm; pub mod rewind; pub mod settings; pub mod todo; pub mod plan; pub mod usage; use crate::state::{AppStateRest, Overlay}; use ratatui::layout::Rect; use ratatui::style::{Modifier, Style}; use ratatui::text::Span; use ratatui::widgets::{Block, Borders, Clear}; use ratatui::Frame; use super::theme::Theme; use tracing::debug; /// Decorate an overlay block with a styled title and matching border color. pub fn overlay_block(block: Block<'static>, title: &str, color: ratatui::style::Color) -> Block<'static> { block .title(Span::styled( format!(" {title} "), Style::default() .fg(color) .add_modifier(Modifier::BOLD), )) .border_style(Style::default().fg(color)) } /// Compute a centered rectangle within `area` at the given percentage width and height. pub fn centered_rect(area: Rect, percent_x: u16, percent_y: u16) -> Rect { let x_pad = (area.width.saturating_sub(area.width * percent_x / 100)) / 2; let y_pad = (area.height.saturating_sub(area.height * percent_y / 100)) / 2; Rect { x: area.x.saturating_add(x_pad), y: area.y.saturating_add(y_pad), width: area.width.saturating_sub(x_pad * 2).max(40), height: area.height.saturating_sub(y_pad * 2).max(10), } } /// Render the active modal overlay as a centered panel. /// /// Dispatches to the appropriate overlay module's `render` function based on the /// `Overlay` variant. No-ops for `Overlay::None`. #[tracing::instrument(skip(frame, state))] pub fn render_overlay( frame: &mut Frame, area: Rect, overlay: Overlay, state: &AppStateRest, ) { debug!("Rendering overlay: {overlay:?}"); let overlay_area = centered_rect(area, 75, 70); frame.render_widget(Clear, overlay_area); let block = Block::default() .borders(Borders::ALL) .border_style(Style::default().fg(Theme::BORDER)) .style(Style::default().bg(Theme::BG)); match overlay { Overlay::None => {} Overlay::Help => { help::render(frame, overlay_area, block, state); } Overlay::Settings => { settings::render(frame, overlay_area, block, state); } Overlay::Bash => { bash::render(frame, overlay_area, block, state); } Overlay::QuitConfirm => { quit_confirm::render(frame, overlay_area, block, state); } Overlay::KeyInput => { key_input::render(frame, overlay_area, block, state); } Overlay::Editor => { editor::render(frame, overlay_area, block, state); } Overlay::Effort => { effort::render(frame, overlay_area, block, state); } Overlay::Mcp => { mcp::render(frame, overlay_area, block, state); } Overlay::Todo => { todo::render(frame, overlay_area, block, state); } Overlay::Plan => { plan::render(frame, overlay_area, block, state); } Overlay::Rewind => { rewind::render(frame, overlay_area, block, state); } Overlay::Learning => { learning::render(frame, overlay_area, block, state); } Overlay::Usage => { usage::render(frame, overlay_area, block, state); } Overlay::Loading => { loading::render(frame, overlay_area, block, state); } Overlay::ModelSelector => { model_selector::render(frame, overlay_area, block, state); } Overlay::ClearConfirm => { clear_confirm::render(frame, overlay_area, block, state); } Overlay::Diff => { diff::render(frame, overlay_area, block, state); } } }