Implemented `show_sublabel` to ensure the first rendered entry in a
scrolled viewport always shows its full group labels.
Add regression tests for scrolling behavior in multi-level headers.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
The import wizard now proactively checks the `MAX_CATEGORIES` limit during
the proposal and configuration steps.
Advancing is blocked with a descriptive message if the limit would be
exceeded.
Fixed UI rendering order in `ImportWizardWidget` so messages are correctly
displayed.
Add regression tests for category limit enforcement.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
`parse_csv` now supports `parse_csv_with_warnings` to detect and report
short rows.
Short rows are now padded with `Value::Null` instead of being silently
dropped.
`ImportJsonHeadless` now uses `parse_csv_with_warnings` and surfaces
warnings in the status message.
`ImportJsonHeadless` now reuses parsed JSON/CSV data via
`json_import_records` instead of re-parsing.
Add regression tests for short row handling and headless import.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
Step 4 of vb4. After steps 1–3, App owns model_state, view_state, and a
residue of five fields that don't belong to either slice. Add a top-level
doc on App explaining the slice structure and tag each residue field as
derived cache / runtime metadata / transient / config with a one-line
rationale. No behavior change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Step 3 of vb4. Populates ViewState with the 20 UI session fields (mode,
status_msg, wizard, search_query, search_mode, three panel-open flags,
three panel cursors, formula_cursor, yanked, tile_cat_idx, two view nav
stacks, drill_state, help_page, expanded_cats, buffers, transient_keymap)
and routes every read/write site through app.view_state.X. App now
contains only model_state, view_state, and the runtime/derived residue
(term dims, layout, last_autosave, abort_effects, keymap_set).
ViewState gets a manual Default impl mirroring the previous App::new
field initialisers; AppMode has no Default of its own so AppMode::Normal
is the explicit baseline. Effect::apply still takes &mut App; narrowing
remains step 5 (improvise-drg).
A structural test (app_view_state_owns_ui_session_fields) locks in the
20-field layout. 624 tests pass workspace-wide (+1 new). cargo clippy
--workspace --tests clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Step 2 of vb4. Populates ModelState with the document slice and routes
every read/write site (effects, draw, main, app methods, tests) through
app.model_state.X. App no longer owns workbook, file_path, or dirty
directly. Effect::apply signatures still take &mut App; narrowing happens
in step 5 (improvise-drg).
A structural test (app_model_state_owns_workbook_file_path_and_dirty)
locks in the field layout. ModelState now has a manual Default impl
that creates an "Untitled" Workbook so the existing constructibility
test keeps working.
623 tests pass workspace-wide (+1 new). cargo clippy --workspace --tests
clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Step 1 of vb4 (Split AppState into ModelState + ViewState): introduce the
named slice types in src/ui/app.rs as empty structs, with doc comments
pointing at the follow-up issues that fill them in. App still owns every
field directly; subsequent steps migrate the fields.
A structural test locks in that both types are constructible.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Update various commands to utilize the new AbortChain and CleanEmptyRecords
effects.
- CommitAndAdvance now pushes a mode change effect when aborting.
- ToggleRecordsMode now cleans up empty records upon exiting.
- EnterAdvance now emits AbortChain when at the bottom-right corner.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-26B-A4B-it-UD-Q5_K_XL.gguf)
Implement AbortChain and CleanEmptyRecords effects to allow
short-circuiting effect batches and purging cells with empty coordinates.
Update the App struct to support aborting effects during the application of
an effect batch.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-26B-A4B-it-UD-Q5_K_XL.gguf)
Relocate the two I/O module trees into the improvise-io sub-crate
scaffolded in the previous commit:
git mv src/persistence -> crates/improvise-io/src/persistence
git mv src/import -> crates/improvise-io/src/import
The grammar file `improv.pest` moves alongside `persistence/mod.rs`;
the `#[grammar = "persistence/improv.pest"]` attribute resolves relative
to the new crate root and keeps working unchanged.
No path edits inside the moved code: the `crate::model::*`,
`crate::view::*`, `crate::workbook::*`, `crate::format::*`, and
`crate::formula::*` imports inside persistence and import all continue
to resolve because improvise-io's lib.rs re-exports those modules from
improvise-core and improvise-formula, mirroring the pattern improvise-core
uses for `formula`. Verified no `crate::ui::*`, `crate::command::*`,
`crate::draw::*` imports exist in the moved code (per improvise-8zh
acceptance criterion #3).
Main-crate `src/lib.rs` now re-exports `import` and `persistence` from
improvise-io, keeping every `crate::persistence::*` and `crate::import::*`
path in the 4 consumer files (ui/app.rs, ui/effect.rs,
ui/import_wizard_ui.rs, main.rs) resolving unchanged — no downstream
edits needed.
`examples/gen-grammar.rs` had `include_str!("../src/persistence/improv.pest")`;
updated the relative path to the new location under
`crates/improvise-io/src/persistence/`.
Verification:
- cargo check --workspace --examples: clean
- cargo test --workspace: 616 passing (219 main + 190 core + 65 formula + 142 io)
- cargo clippy --workspace --tests: clean
- cargo build -p improvise-io: standalone build succeeds, confirming no
UI/command leakage into the IO crate (improvise-8zh acceptance #2, #3)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update keybindings for normal, records-normal, editing, and records-editing
modes to pass the appropriate mode names as arguments to the parameterized
commands.
This ensures that the correct mode is entered when using commands like
`edit-or-drill` , `enter-edit-at-cursor` , `commit-cell-edit` , and
`commit-and-advance-right` .
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-26B-A4B-it-UD-Q5_K_XL.gguf)
Make mode-related commands and effects mode-agnostic by passing the target
mode as an argument instead of inspecting the current application mode.
- `CommitAndAdvance` now accepts `edit_mode` .
- `EditOrDrill` now accepts `edit_mode` .
- `EnterEditAtCursorCmd` now accepts `target_mode` .
- `EnterEditAtCursor` effect now accepts `target_mode` .
Update the command registry to parse mode names from arguments and pass
them to the corresponding commands.
Add tests to verify the new mode-passing behavior.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-26B-A4B-it-UD-Q5_K_XL.gguf)
Relocate the four pure-data module trees into the improvise-core
sub-crate scaffolded in the previous commit. Phase A already made
these modules UI/IO-free; this commit is purely mechanical:
git mv src/format.rs -> crates/improvise-core/src/format.rs
git mv src/workbook.rs -> crates/improvise-core/src/workbook.rs
git mv src/model -> crates/improvise-core/src/model
git mv src/view -> crates/improvise-core/src/view
The moved code contains no path edits: the `crate::formula::*`,
`crate::model::*`, `crate::view::*`, `crate::workbook::*`,
`crate::format::*` imports inside the four trees all continue to
resolve because the new crate mirrors the same module layout and
re-exports improvise_formula under `formula` via its lib.rs.
Main-crate `src/lib.rs` flips from declaring these as owned modules
(`pub mod model;` etc.) to re-exporting them from improvise-core
(`pub use improvise_core::model;` etc.). This keeps every
`crate::model::*`, `crate::view::*`, `crate::workbook::*`,
`crate::format::*` path inside the 26 consumer files in src/ (ui,
command, persistence, import, draw, main) resolving unchanged — no
downstream edits needed.
Verification:
- cargo check --workspace: clean
- cargo test --workspace: 612 passing (357 main + 190 core + 65 formula)
- cargo clippy --workspace --tests: clean
- cargo build -p improvise-core: standalone build succeeds, confirming
zero UI/IO leakage into the core crate
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement a new "Records" mode for data entry.
- Add `RecordsNormal` and `RecordsEditing` to `AppMode` and `ModeKey` .
- `DataStore` now uses `IndexMap` and supports `sort_by_key()` to ensure
deterministic row order.
- `ToggleRecordsMode` command now sorts data and switches to
`RecordsNormal` .
- `EnterEditMode` command now respects records editing variants.
- `RecordsNormal` mode includes a new `o` keybinding to add a record row.
- `RecordsEditing` mode inherits from `Editing` and adds an `Esc` binding
to return to `RecordsNormal` .
- Added `SortData` effect to trigger data sorting.
- Updated UI to display "RECORDS" and "RECORDS INSERT" mode names and
styles.
- Updated keymaps, command registry, and view navigation to support these
new modes.
- Added comprehensive tests for records mode behavior, including sorting
and boundary conditions for Tab/Enter.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-26B-A4B-it-UD-Q5_K_XL.gguf)
Replace `HashMap` with `IndexMap` in `DataStore::cells` to preserve
insertion order. This allows records mode to display rows in the order they
were added. Update `remove` to use `shift_remove` to maintain order during
deletions.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-26B-A4B-it-UD-Q5_K_XL.gguf)
Introduce `ViewFrame` to store both the view name and the `AppMode` when
pushing to the navigation stack. Update `view_back_stack` and
`view_forward_stack` to use `ViewFrame` instead of `String` . Update
`CmdContext` and `Effect` implementations (SwitchView, ViewBack,
ViewForward) to handle the new `ViewFrame` structure. Add `is_editing()`
helper to `AppMode` .
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-26B-A4B-it-UD-Q5_K_XL.gguf)
Workbook::new was setting _Index=Row and _Dim=Column on the default view,
which forced a fresh workbook into records mode on every startup — records
mode auto-activates when both _Index and _Dim sit on axes. The result felt
like broken keybindings: the grid rendered as records instead of an
aggregated pivot, and every navigation key landed in an unexpected spot.
Main fixed this on the Model layer in 709f2df + 6d4b19a (improvise-kos);
the Workbook refactor accidentally reintroduced the pre-fix behavior.
Restore the post-kos defaults: all virtual categories start on Axis::None
(via on_category_added's "_"-prefix rule), then _Measure is bumped to
Axis::Page so aggregated pivot views show one measure at a time. _Index
and _Dim only move onto axes when the user explicitly enters records mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Model is now pure data (categories, cells, formulas, measure_agg) with
no references to view/. The Workbook struct owns the Model together
with views and the active view name, and is responsible for cross-slice
operations (add/remove category → notify views, view management).
- New: src/workbook.rs with Workbook wrapper and cross-slice helpers
(add_category, add_label_category, remove_category, create_view,
switch_view, delete_view, normalize_view_state).
- Model: strip view state and view-touching methods. recompute_formulas
remains on Model as a primitive; the view-derived none_cats list is
gathered at each call site (App::rebuild_layout, persistence::load)
so the view dependency is explicit, not hidden behind a wrapper.
- View: add View::none_cats() helper.
- CmdContext: add workbook and view fields so commands can reach both
slices without threading Model + View through every call.
- App: rename `model` field to `workbook`.
- Persistence (save/load/format_md/parse_md/export_csv): take/return
Workbook so the on-disk format carries model + views together.
- Widgets (GridWidget, TileBar, CategoryContent, ViewContent): take
explicit &Model + &View instead of routing through Model.
Tests updated throughout to reflect the new shape. View-management
tests that previously lived on Model continue to cover the same
behaviour via a build_workbook() helper in model/types.rs. All 573
tests pass; clippy is clean.
This is Phase A of improvise-36h. Phase B will mechanically extract
crates/improvise-core/ containing model/, view/, format.rs, workbook.rs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Virtual categories _Index and _Dim now default to None on new models
instead of being forced onto Row/Column. _Measure defaults to Page
(the natural home for measure filtering). Fixed page_coords builder
to skip Page categories with no items/selection, preventing empty-string
contamination of cell keys.
Made-with: Cursor
Ensure that virtual categories (_Index, _Dim, _Measure) are registered in
the default view with Axis::None. This prevents potential panics when
calling axis_of on these categories and allows users to move them to a
specific axis manually.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
The records mode column filter excluded all categories starting with '_',
which hid _Measure. Changed to explicitly exclude only _Index and _Dim,
making _Measure visible as a data column. Updated the blank-model editing
test to reflect the new column order (_Measure first, Value last).
Made-with: Cursor
Clean up formatting of a test assertion in app.rs to improve readability.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Simplify the commit_cell_value function by extracting its core logic into
specialized helper functions: commit_regular_cell_value, stage_drill_edit,
and commit_plain_records_edit. This improves readability and reduces
nesting.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Extract the hardcoded error message "Record coordinates cannot be empty"
into a public constant in effect.rs to avoid duplication and improve
maintainability.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Allow synthetic record edits to be applied directly to the model when not
in drill mode, whereas they remain staged in drill state when a drill
snapshot is active. This enables editing of records in plain records view.
Additionally, add validation to prevent creating records with empty
coordinates in both direct commits and when applying staged drill edits.
Includes regression tests for persistence in blank models, drill state
staging, and empty coordinate prevention.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Extract the formula AST and parser into a dedicated `improvise-formula`
crate and convert the project into a Cargo workspace.
The root crate now re-exports `improvise-formula` as `crate::formula` to
maintain backward compatibility for internal callers. The repository map is
updated to reflect the new crate structure.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Add helper methods to CmdContext to clarify layout state and synthetic
record presence. Update AddRecordRow to check for records mode generally
rather than requiring a synthetic record at the current cursor, which
allows adding the first row to an empty records view.
Includes a regression test for the empty records view scenario.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Update the loop in TileBar to use an iterator-based approach with
enumerate, take, and skip instead of indexing.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Clean up various test cases by simplifying Option checks, removing
redundant clones, using contains instead of any for DateComponent checks,
and removing unnecessary references in string formatting.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Simplify the return value of CommitFormula::execute to use a vec! macro and
move the page_cat_data helper function in navigation.rs for better
organization.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Replace modulo operations with is_multiple_of calls and simplify nested if
statements using let-chains in the grammar generator.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Consolidate if-let chain formatting by adding missing braces throughout the
project. This improves readability and ensures consistency across the
codebase.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
The render helper used in Grid tests previously did not recompute formulas,
meaning tests for computed values were either ignored or required manual
setup that didn't reflect actual application behavior.
Update the render helper to identify 'None' axis categories and trigger
recompute_formulas, and update all call sites to pass a mutable model.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
When drilling into a cell that is the target of a formula, the resulting
record set was empty because the formula target coordinate itself does not
exist in the raw data.
This change strips the '_Measure' coordinate from the drill key if the
value is a known formula target, allowing the underlying data records that
feed the formula to be discovered.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Replace nested if and if let blocks with combined if statements using let
chains. This reduces indentation and improves readability across the
project.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Disable the eval_formula method in Model. This may be a temporary change or
part of a larger refactor of formula evaluation.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Update the .improv file grammar to require version v2025-04-09 and remove
unnecessary blank line requirements at the start of files. Update
bank-info.improv to comply with the new version and reformat date entries
and categories.
BREAKING CHANGE: The .improv grammar now strictly requires the version line 'v2025-04-09'.
Existing files without this line will fail to parse.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
Move module declarations from src/main.rs to a new src/lib.rs to transition
the project to a library-first structure. This allows other tools and
examples to utilize the core logic.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
- gen is a reserved keyword (→ renamed module to generator)
- ref in patterns with implicit borrowing (→ removed ref)
- & pattern with implicit borrowing (→ added & to outer pattern)