Files
improvise/src/ui/help.rs
Edward Langley 37584670eb feat: group-aware grid rendering and hide/show item
Builds out two half-finished view features:

Group collapse:
- AxisEntry enum distinguishes GroupHeader from DataItem on grid axes
- expand_category() emits group headers and filters collapsed items
- Grid renders inline group header rows with ▼/▶ indicator
- `z` keybinding toggles collapse of nearest group above cursor

Hide/show item:
- Restore show_item() (was commented out alongside hide_item)
- Add HideItem / ShowItem commands and dispatch
- `H` keybinding hides the current row item
- `:show-item <cat> <item>` command to restore hidden items
- Restore silenced test assertions for hide/show round-trip

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 00:07:11 -07:00

126 lines
4.7 KiB
Rust

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 <path.json>", "Open JSON import wizard", key),
(" :export [path.csv]", "Export active view to CSV", key),
(" :add-cat <name>", "Add a category", key),
(
" :add-item <cat> <item>",
"Add one item to a category",
key,
),
(
" :add-items <cat> a b c…",
"Add multiple items at once",
key,
),
(" :formula <cat> <Name=expr>", "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);
}
}
}
}
}