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>
Axis::Unassigned served two purposes that Option already covers:
1. "this category has no assignment yet" → None
2. "this category doesn't exist" → None
By removing the variant and changing axis_of to return Option<Axis>,
callers are forced by the compiler to handle the absent-category case
explicitly (via match or unwrap_or), rather than silently treating it
like a real axis value.
SetAxis { axis: String } also upgraded to SetAxis { axis: Axis }.
Previously, constructing SetAxis with an invalid string (e.g. "diagonal")
would compile and then silently fail at dispatch. Now the type only admits
valid axis values; the dispatch string-parser is gone.
Axis gains #[serde(rename_all = "lowercase")] so existing JSON command
files (smoke.jsonl, etc.) using "row"/"column"/"page" continue to work.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three bugs fixed, each with a failing regression test added first:
1. WHERE filter fallthrough: when the filter's category was absent from the
cell key, the formula was applied unconditionally. Now returns the raw
stored value (no formula applied) when the category is missing.
2. Agg inner/filter ignored: SUM(Revenue) was summing ALL cells in the
partial slice rather than constraining to the Revenue item. Now resolves
the inner Ref to its category and pins that coordinate before scanning.
3. Formula dedup by target only: add_formula and remove_formula keyed on
target name alone, so two formulas with the same item name in different
categories would collide. Both now key on (target, target_category).
RemoveFormula command updated to carry target_category.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>