cli_tutor/ui/
help_overlay.rs1use crate::app::App;
2use ratatui::{
3 layout::{Constraint, Direction, Layout, Rect},
4 style::{Color, Modifier, Style},
5 text::{Line, Span},
6 widgets::{Block, Borders, Clear, Paragraph},
7 Frame,
8};
9
10pub fn render(_app: &App, frame: &mut Frame, area: Rect) {
11 let overlay = centered_rect(60, 70, area);
12 frame.render_widget(Clear, overlay);
13
14 let lines = vec![
15 Line::from(Span::styled(
16 "Keyboard Reference",
17 Style::default()
18 .add_modifier(Modifier::BOLD)
19 .fg(Color::Cyan),
20 )),
21 Line::from(""),
22 key_line("↑ / ↓", "Navigate module list (Intro/Examples views)"),
23 key_line("Tab / Esc", "Cycle views: Intro → Examples → Exercise"),
24 key_line("Enter", "Submit command"),
25 key_line("↑ / ↓", "Scroll output (Exercise view)"),
26 key_line("← / →", "Move cursor in input"),
27 Line::from(""),
28 Line::from(Span::styled(
29 " Exercise shortcuts (Ctrl+key)",
30 Style::default()
31 .fg(Color::Cyan)
32 .add_modifier(Modifier::BOLD),
33 )),
34 Line::from(""),
35 key_line("Ctrl+N / Ctrl+P", "Next / previous exercise"),
36 key_line("Ctrl+T", "Reveal next hint"),
37 key_line("Ctrl+S", "Show/hide solution"),
38 key_line("Ctrl+F", "Toggle file viewer"),
39 key_line("Ctrl+R", "Reset exercise"),
40 key_line("Ctrl+L", "Clear output panel"),
41 Line::from(""),
42 key_line("q", "Quit (Intro/Examples)"),
43 key_line("Ctrl+C", "Quit (anywhere)"),
44 Line::from(""),
45 Line::from(Span::styled(
46 "Press Esc to close",
47 Style::default().fg(Color::DarkGray),
48 )),
49 ];
50
51 let para = Paragraph::new(lines).block(
52 Block::default()
53 .borders(Borders::ALL)
54 .title(" Help ")
55 .style(Style::default().bg(Color::Black)),
56 );
57
58 frame.render_widget(para, overlay);
59}
60
61fn key_line(key: &str, desc: &str) -> Line<'static> {
62 Line::from(vec![
63 Span::styled(
64 format!(" {:<15}", key),
65 Style::default()
66 .fg(Color::Yellow)
67 .add_modifier(Modifier::BOLD),
68 ),
69 Span::raw(desc.to_string()),
70 ])
71}
72
73fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
74 let vert = Layout::default()
75 .direction(Direction::Vertical)
76 .constraints([
77 Constraint::Percentage((100 - percent_y) / 2),
78 Constraint::Percentage(percent_y),
79 Constraint::Percentage((100 - percent_y) / 2),
80 ])
81 .split(r);
82 Layout::default()
83 .direction(Direction::Horizontal)
84 .constraints([
85 Constraint::Percentage((100 - percent_x) / 2),
86 Constraint::Percentage(percent_x),
87 Constraint::Percentage((100 - percent_x) / 2),
88 ])
89 .split(vert[1])[1]
90}