chore: TUI help menu 81491563
Steve · 2026-02-18 23:22 1 file(s) · +90 −24
src/bin/tui.rs +90 −24
5 5
    layout::{Constraint, Layout},
6 6
    style::{Color, Modifier, Style},
7 7
    text::{Line, Span, Text},
8 -
    widgets::{Block, Borders, List, ListItem, ListState, Paragraph},
8 +
    widgets::{Block, Borders, Clear, List, ListItem, ListState, Paragraph, Widget},
9 9
};
10 10
use sipp_rust::db::{self, Snippet};
11 11
use std::time::{Duration, Instant};
27 27
    status_message: Option<(String, Instant)>,
28 28
    focus: Focus,
29 29
    content_scroll: u16,
30 +
    show_help: bool,
30 31
    syntax_set: SyntaxSet,
31 32
    theme: Theme,
32 33
}
49 50
            status_message: None,
50 51
            focus: Focus::List,
51 52
            content_scroll: 0,
53 +
            show_help: false,
52 54
            syntax_set,
53 55
            theme,
54 56
        }
231 233
                    .style(Style::default().fg(Color::Green).add_modifier(Modifier::BOLD));
232 234
                frame.render_widget(status, outer[1]);
233 235
            }
236 +
237 +
            if app.show_help {
238 +
                let area = frame.area();
239 +
                let popup_width = 40u16.min(area.width.saturating_sub(4));
240 +
                let popup_height = 14u16.min(area.height.saturating_sub(4));
241 +
                let popup_area = ratatui::layout::Rect {
242 +
                    x: (area.width.saturating_sub(popup_width)) / 2,
243 +
                    y: (area.height.saturating_sub(popup_height)) / 2,
244 +
                    width: popup_width,
245 +
                    height: popup_height,
246 +
                };
247 +
248 +
                let help_text = Text::from(vec![
249 +
                    Line::from(""),
250 +
                    Line::from(vec![
251 +
                        Span::styled("  j/↓  ", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)),
252 +
                        Span::raw("Move down / Scroll down"),
253 +
                    ]),
254 +
                    Line::from(vec![
255 +
                        Span::styled("  k/↑  ", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)),
256 +
                        Span::raw("Move up / Scroll up"),
257 +
                    ]),
258 +
                    Line::from(vec![
259 +
                        Span::styled("  Enter", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)),
260 +
                        Span::raw("  Focus content pane"),
261 +
                    ]),
262 +
                    Line::from(vec![
263 +
                        Span::styled("  Esc  ", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)),
264 +
                        Span::raw("Back / Quit"),
265 +
                    ]),
266 +
                    Line::from(vec![
267 +
                        Span::styled("  y    ", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)),
268 +
                        Span::raw("Copy snippet"),
269 +
                    ]),
270 +
                    Line::from(vec![
271 +
                        Span::styled("  q    ", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)),
272 +
                        Span::raw("Quit"),
273 +
                    ]),
274 +
                    Line::from(vec![
275 +
                        Span::styled("  ?    ", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)),
276 +
                        Span::raw("Toggle this help"),
277 +
                    ]),
278 +
                    Line::from(""),
279 +
                    Line::from(Span::styled(
280 +
                        "  Press any key to close",
281 +
                        Style::default().fg(Color::DarkGray),
282 +
                    )),
283 +
                ]);
284 +
285 +
                Clear.render(popup_area, frame.buffer_mut());
286 +
                let help = Paragraph::new(help_text).block(
287 +
                    Block::default()
288 +
                        .title(" Keybindings ")
289 +
                        .borders(Borders::ALL)
290 +
                        .border_style(Style::default().fg(Color::Yellow)),
291 +
                );
292 +
                frame.render_widget(help, popup_area);
293 +
            }
234 294
        })?;
235 295
236 296
        if event::poll(Duration::from_millis(100))? {
237 297
            if let Event::Key(key) = event::read()? {
238 -
                match app.focus {
239 -
                    Focus::List => match key.code {
240 -
                        KeyCode::Char('q') => app.should_quit = true,
241 -
                        KeyCode::Char('j') | KeyCode::Down => app.move_down(),
242 -
                        KeyCode::Char('k') | KeyCode::Up => app.move_up(),
243 -
                        KeyCode::Char('y') => app.copy_selected(),
244 -
                        KeyCode::Enter => {
245 -
                            if app.selected_snippet().is_some() {
246 -
                                app.focus = Focus::Content;
298 +
                if app.show_help {
299 +
                    app.show_help = false;
300 +
                } else {
301 +
                    match app.focus {
302 +
                        Focus::List => match key.code {
303 +
                            KeyCode::Char('q') | KeyCode::Esc => app.should_quit = true,
304 +
                            KeyCode::Char('j') | KeyCode::Down => app.move_down(),
305 +
                            KeyCode::Char('k') | KeyCode::Up => app.move_up(),
306 +
                            KeyCode::Char('y') => app.copy_selected(),
307 +
                            KeyCode::Char('?') => app.show_help = true,
308 +
                            KeyCode::Enter => {
309 +
                                if app.selected_snippet().is_some() {
310 +
                                    app.focus = Focus::Content;
311 +
                                }
312 +
                            }
313 +
                            _ => {}
314 +
                        },
315 +
                        Focus::Content => match key.code {
316 +
                            KeyCode::Char(' ') | KeyCode::Esc | KeyCode::Char('q') => {
317 +
                                app.focus = Focus::List;
318 +
                            }
319 +
                            KeyCode::Char('j') | KeyCode::Down => {
320 +
                                app.scroll_down(content_line_count);
247 321
                            }
248 -
                        }
249 -
                        _ => {}
250 -
                    },
251 -
                    Focus::Content => match key.code {
252 -
                        KeyCode::Char(' ') | KeyCode::Esc | KeyCode::Char('q') => {
253 -
                            app.focus = Focus::List;
254 -
                        }
255 -
                        KeyCode::Char('j') | KeyCode::Down => {
256 -
                            app.scroll_down(content_line_count);
257 -
                        }
258 -
                        KeyCode::Char('k') | KeyCode::Up => app.scroll_up(),
259 -
                        KeyCode::Char('y') => app.copy_selected(),
260 -
                        _ => {}
261 -
                    },
322 +
                            KeyCode::Char('k') | KeyCode::Up => app.scroll_up(),
323 +
                            KeyCode::Char('y') => app.copy_selected(),
324 +
                            KeyCode::Char('?') => app.show_help = true,
325 +
                            _ => {}
326 +
                        },
327 +
                    }
262 328
                }
263 329
            }
264 330
        }