use ratatui::layout::Rect;
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::Paragraph;
use super::{borderless_panel, centered_in, to_color};
use crate::tui::theme::Theme;
const SHORTCUTS: &[(&str, &str)] = &[
("q", "quit"),
("Ctrl+C", "quit"),
("p", "pause / resume"),
("t", "themes"),
("Tab", "agent dashboard"),
("s", "sources (connect / health)"),
#[cfg(debug_assertions)]
("w", "walkable / approach / route debug"),
("?", "toggle this overlay"),
("\u{2191} \u{2193} j k", "switch floor"),
("PgUp / PgDn", "switch floor"),
("click agent", "pin tooltip"),
("Enter / Esc", "dismiss popup"),
];
pub(in crate::tui) fn paint_help_overlay(f: &mut ratatui::Frame<'_>, bounds: Rect, theme: &Theme) {
let area = centered_in(
bounds,
36 + 2 * super::PANEL_PAD_X,
SHORTCUTS.len() as u16 + 2 + 2 * super::PANEL_PAD_Y,
);
if area.width < 4 || area.height < 3 {
return;
}
let inner = borderless_panel(f, area, Some("? Keyboard"), theme);
let mut lines: Vec<Line> = Vec::with_capacity(SHORTCUTS.len() + 1);
lines.push(Line::from(""));
for (key, desc) in SHORTCUTS {
lines.push(Line::from(vec![
Span::raw(" "),
Span::styled(
format!("{key:<13}"),
Style::default()
.fg(to_color(theme.ui.neon_brand))
.add_modifier(Modifier::BOLD),
),
Span::styled(
desc.to_string(),
Style::default().fg(to_color(theme.ui.label_idle)),
),
]));
}
f.render_widget(Paragraph::new(lines), inner);
}
#[cfg(test)]
mod tests {
use super::*;
use ratatui::backend::TestBackend;
use ratatui::Terminal;
fn render_at(w: u16, h: u16) {
let mut term = Terminal::new(TestBackend::new(w, h)).unwrap();
term.draw(|f| {
paint_help_overlay(f, Rect::new(0, 0, w, h), &crate::tui::theme::NORMAL);
})
.unwrap();
}
#[test]
fn help_overlay_renders_without_panic_across_sizes() {
for (w, h) in [(200, 60), (40, 20), (24, 30), (10, 4), (4, 3), (2, 2)] {
render_at(w, h);
}
}
}