use ratatui::{ buffer::Buffer, layout::Rect, style::{Color, Modifier, Style}, widgets::{Block, Borders, Clear, Widget}, }; pub struct HelpWidget; impl Widget for HelpWidget { fn render(self, area: Rect, buf: &mut Buffer) { let popup_w = 66u16.min(area.width); let popup_h = 36u16.min(area.height); let x = area.x + area.width.saturating_sub(popup_w) / 2; let y = area.y + area.height.saturating_sub(popup_h) / 2; let popup_area = Rect::new(x, y, popup_w, popup_h); Clear.render(popup_area, buf); let block = Block::default() .borders(Borders::ALL) .title(" improvise — key reference (any key to close) ") .border_style(Style::default().fg(Color::Blue)); let inner = block.inner(popup_area); block.render(popup_area, buf); let head = Style::default() .fg(Color::Blue) .add_modifier(Modifier::BOLD); let key = Style::default().fg(Color::Cyan); let dim = Style::default().fg(Color::DarkGray); let norm = Style::default(); // (key_col, desc_col, style) let rows: &[(&str, &str, Style)] = &[ ("Navigation", "", head), (" hjkl / ↑↓←→", "Move cursor", key), (" gg / G", "First / last row", key), (" 0 / $", "First / last column", key), (" Ctrl+D / Ctrl+U", "Scroll ½-page down / up", key), (" [ / ]", "Cycle page-axis filter", key), ("", "", norm), ("Editing", "", head), (" i / a / Enter", "Enter Insert mode", key), (" Esc", "Return to Normal mode", key), (" x", "Clear cell", key), (" yy", "Yank (copy) cell value", key), (" p", "Paste yanked value", key), ("", "", norm), ("Search", "", head), (" /", "Enter search, highlight matches", key), (" n / N", "Next / previous match", key), (" Esc or Enter", "Exit search", key), ("", "", norm), ("Panels", "", head), (" F", "Toggle Formula panel (n:new d:del)", key), ( " C", "Toggle Category panel (n:new-cat a:add-items)", key, ), (" N", "New category quick-add (from anywhere)", key), ( " V", "Toggle View panel (n:new d:del Enter:switch)", key, ), (" Tab", "Focus next open panel", key), ("", "", norm), ("Pivot / Tiles / Groups", "", head), (" z", "Toggle collapse nearest group above cursor", key), ( " H", "Hide current row item (:show-item cat item to restore)", key, ), (" T", "Tile-select mode", key), (" ← h / → l", "Select previous/next tile", dim), (" Space / Enter", "Cycle axis (Row→Col→Page)", dim), (" r / c / p", "Set axis to Row / Col / Page", dim), ("", "", norm), ("Command line ( : )", "", head), ( " :q :q! :wq ZZ", "Quit / force-quit / save+quit", key, ), (" :w [path]", "Save (path optional)", key), (" :import ", "Open JSON import wizard", key), (" :export [path.csv]", "Export active view to CSV", key), (" :add-cat ", "Add a category", key), ( " :add-item ", "Add one item to a category", key, ), ( " :add-items a b c…", "Add multiple items at once", key, ), (" :formula ", "Add a formula", key), (" :add-view [name]", "Create a new view", key), ("", "", norm), (" ? or F1", "This help", key), (" Ctrl+S", "Save (same as :w)", key), ]; let key_col_w = 32usize; for (i, (k, d, style)) in rows.iter().enumerate() { if i >= inner.height as usize { break; } let y = inner.y + i as u16; if d.is_empty() { buf.set_string(inner.x, y, k, *style); } else { buf.set_string(inner.x, y, k, *style); let dx = inner.x + key_col_w as u16; if dx < inner.x + inner.width { buf.set_string(dx, y, d, norm); } } } } }