feat: add view history navigation and drill-into-cell

Add view navigation history with back/forward stacks (bound to < and >).

Introduce CategoryKind enum to distinguish regular categories from
virtual ones (_Index, _Dim) that are synthesized at query time.

Add DrillIntoCell command that creates a drill view showing raw data
for an aggregated cell, expanding categories on Axis::None into Row
and Column axes while filtering by the cell's fixed coordinates.

Virtual categories default to Axis::None and are automatically added
to all views when the model is initialized.

Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
This commit is contained in:
Edward Langley
2026-04-05 10:57:28 -07:00
parent b2d633eb7d
commit 67041dd4a5
7 changed files with 253 additions and 16 deletions

View File

@ -67,6 +67,11 @@ pub struct App {
pub yanked: Option<CellValue>,
/// Tile select cursor (which category index is highlighted)
pub tile_cat_idx: usize,
/// View navigation history: views visited before the current one.
/// Pushed on SwitchView, popped by `<` (back).
pub view_back_stack: Vec<String>,
/// Views that were "back-ed" from, available for forward navigation (`>`).
pub view_forward_stack: Vec<String>,
/// Named text buffers for text-entry modes
pub buffers: HashMap<String, String>,
/// Transient keymap for Emacs-style prefix key sequences (g→gg, y→yy, etc.)
@ -94,6 +99,8 @@ impl App {
dirty: false,
yanked: None,
tile_cat_idx: 0,
view_back_stack: Vec::new(),
view_forward_stack: Vec::new(),
buffers: HashMap::new(),
transient_keymap: None,
keymap_set: KeymapSet::default_keymaps(),
@ -125,6 +132,9 @@ impl App {
cell_key: layout.cell_key(sel_row, sel_col),
row_count: layout.row_count(),
col_count: layout.col_count(),
none_cats: layout.none_cats.clone(),
view_back_stack: self.view_back_stack.clone(),
view_forward_stack: self.view_forward_stack.clone(),
key_code: key,
}
}
@ -184,7 +194,7 @@ impl App {
/// Hint text for the status bar (context-sensitive)
pub fn hint_text(&self) -> &'static str {
match &self.mode {
AppMode::Normal => "hjkl:nav Enter:advance i:edit x:clear t:transpose /:search F/C/V:panels T:tiles [:]:page ::cmd",
AppMode::Normal => "hjkl:nav Enter:advance i:edit x:clear t:transpose /:search F/C/V:panels T:tiles [:]:page >:drill ::cmd",
AppMode::Editing { .. } => "Enter:commit Esc:cancel",
AppMode::FormulaPanel => "n:new d:delete jk:nav Esc:back",
AppMode::FormulaEdit { .. } => "Enter:save Esc:cancel — type: Name = expression",