- export_csv writes through csv::Writer (RFC 4180) and honors the view's
number format (improvise-1cz)
- malformed numbers yield CellValue::Error naming the text instead of
silent 0.0 (improvise-4yc)
- formula [Category] suffix moved into the pest grammar; walker extracts
it structurally instead of rfind(" [") string-scanning (improvise-zgb)
- parse errors now report line, column, and the offending line's text
(improvise-6kj)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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)
Improved numeric parsing in Markdown files to surface errors instead of
defaulting to 0.0.
`export_csv` now uses `csv::Writer` to ensure RFC 4180 compliance (e.g.,
quoting fields containing commas).
`export_csv` now correctly respects the view's number format.
Add regression tests for numeric error handling and RFC 4180 compliance.
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)
`records_display` now returns `None` for out-of-bounds columns.
Added `try_axis_of` to `View` for non-panicking axis retrieval.
`cycle_axis` now uses `try_axis_of` to avoid panicking on unknown
categories.
Add regression tests for out-of-bounds access and unknown category cycling.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
Use `FLOAT_EQ_EPSILON` for equality/inequality operators and
division-by-zero guards in formula evaluation to ensure consistent
semantics.
Replace `Vec` with `IndexSet` for stem collection in `recompute_formulas`
to improve performance from O(n²) to O(n).
Add regression tests for epsilon-based comparison and stem collection
performance.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
Update Cargo.lock with new versions and additional dependencies.
This includes updates to bitflags, chrono, and hashbrown, along with the
addition of new packages such as approx, critical-section, and the futures
suite.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
Adds pkgs.global and pkgs.universal-ctags to the development shell.
This update also includes the corresponding changes in flake.lock.
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>
Captures three insights from 2026-04-16 deep review session:
- review-methodology-scoped-explore-agents
- compiler-exhaustiveness-theme
- agent-issue-drift-pattern
Co-Authored-By: Claude Sonnet 4.6 <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)
Reflect improvise-8zh: `persistence/` and `import/` now live in the
`improvise-io` sub-crate under `crates/`.
- Sub-crate list expanded to describe improvise-io's scope, its
dependencies (improvise-core, improvise-formula), and the
standalone-build guarantee.
- File Inventory reorganized: "Import layer" + persistence entries
collapsed into a single "I/O crate layers" block with paths rooted at
`crates/improvise-io/src/`.
- Updated line/test counts to current contents.
- Top-level block trimmed (persistence/format no longer live there) and
lib.rs annotated as a re-export facade.
No consumer-facing path changes; `crate::persistence::*` and
`crate::import::*` still resolve via the main crate's re-exports.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Lay groundwork for Phase 3 of the workspace split (improvise-8zh): a new
`crates/improvise-io/` sub-crate that will house the persistence and
import layers once the files move in the next commit.
Only scaffolding here:
- New `crates/improvise-io/Cargo.toml` declares deps on improvise-core,
improvise-formula, and the external crates persistence+import already
use: anyhow, chrono, csv, flate2, indexmap, pest, pest_derive, serde,
serde_json. Dev-deps: pest_meta, proptest, tempfile.
- New `crates/improvise-io/src/lib.rs` re-exports the core modules under
their conventional names (`format`, `model`, `view`, `workbook`,
`formula`) so in the next commit the moved code's `crate::model::*`,
`crate::view::*`, `crate::workbook::*`, `crate::format::*`, and
`crate::formula::*` paths resolve unchanged.
- Root `Cargo.toml` adds the new crate to workspace members and the main
crate's `[dependencies]`, ready to receive the move.
No source files change yet; cargo check --workspace still compiles as
before.
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)
Reflect Phase B of improvise-36h: `model/`, `view/`, `workbook.rs`, and
`format.rs` now live in the `improvise-core` sub-crate under `crates/`.
- Sub-crate list expanded to describe improvise-core's scope, its
dependency on improvise-formula, and the standalone-build guarantee.
- File Inventory reorganized into a single "Core crate layers" block
covering model/view/workbook/format, with paths rooted at
`crates/improvise-core/src/`.
- Updated line/test counts to match current contents (Phase A + merge
with main brought in records mode, IndexMap-backed DataStore, etc).
No architectural change; the main crate's re-exports keep every
`crate::model`/`crate::view`/`crate::workbook`/`crate::format` path
resolving unchanged, so no "How to Find Things" table edits are needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Lay the groundwork for Phase B of improvise-36h: a new
`crates/improvise-core/` sub-crate that will house the pure-data core
(model, view, workbook, format) once the files move in the next commit.
Only scaffolding here:
- New `crates/improvise-core/Cargo.toml` mirroring improvise-formula's
structure; declares deps on improvise-formula, anyhow, indexmap, serde.
- New `crates/improvise-core/src/lib.rs` with the single re-export
`pub use improvise_formula as formula;` so that in the next commit the
moved code's `crate::formula::*` paths resolve unchanged.
- Root `Cargo.toml` adds the new crate to the workspace members and the
main crate's `[dependencies]`, ready to receive the move.
No source files change yet; cargo check --workspace still compiles as
before.
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)