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:
@ -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",
|
||||
|
||||
@ -112,10 +112,41 @@ impl Effect for DeleteView {
|
||||
pub struct SwitchView(pub String);
|
||||
impl Effect for SwitchView {
|
||||
fn apply(&self, app: &mut App) {
|
||||
let current = app.model.active_view.clone();
|
||||
if current != self.0 {
|
||||
app.view_back_stack.push(current);
|
||||
app.view_forward_stack.clear();
|
||||
}
|
||||
let _ = app.model.switch_view(&self.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Go back in view history (pop back stack, push current to forward stack).
|
||||
#[derive(Debug)]
|
||||
pub struct ViewBack;
|
||||
impl Effect for ViewBack {
|
||||
fn apply(&self, app: &mut App) {
|
||||
if let Some(prev) = app.view_back_stack.pop() {
|
||||
let current = app.model.active_view.clone();
|
||||
app.view_forward_stack.push(current);
|
||||
let _ = app.model.switch_view(&prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Go forward in view history (pop forward stack, push current to back stack).
|
||||
#[derive(Debug)]
|
||||
pub struct ViewForward;
|
||||
impl Effect for ViewForward {
|
||||
fn apply(&self, app: &mut App) {
|
||||
if let Some(next) = app.view_forward_stack.pop() {
|
||||
let current = app.model.active_view.clone();
|
||||
app.view_back_stack.push(current);
|
||||
let _ = app.model.switch_view(&next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SetAxis {
|
||||
pub category: String,
|
||||
|
||||
Reference in New Issue
Block a user