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>
126 lines
4.7 KiB
Rust
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|