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>
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)
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)
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)
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>
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)
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)
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)
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)
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)
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)
Update application initialization and example data.
- `App::new` now recomputes formulas on startup to ensure formula-derived
values are available immediately.
- Updated `examples/demo.improv` to reflect changes in formula and category
structure.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
_Measure is now a virtual category (CategoryKind::VirtualMeasure),
created automatically in Model::new() alongside _Index and _Dim.
Formula targets are added as items automatically by add_formula.
Formula evaluation uses a fixed-point cache: recompute_formulas()
iterates evaluation of all formula cells until values stabilize,
resolving refs through the cache for formula values and raw data
aggregation for non-formula values. This fixes formulas that
reference other measures when hidden dimensions are present.
evaluate_aggregated now checks the formula cache instead of
recursively evaluating formulas, breaking the dependency between
formula evaluation and aggregation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement help page navigation with `help-page-next` and `help-page-prev`
commands.
- Added `HelpPageNextCmd` and `HelpPagePrevCmd` to `src/command/cmd.rs` .
- Registered help navigation commands in `CmdRegistry` .
- Updated `HelpCmd` to initialize the help page.
- Added unit tests for help navigation in `src/ui/app.rs` .
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
Refactor AppMode to use MinibufferConfig for all text-entry modes. Update
command implementations to use new mode constructors. Introduce
PanelContent trait and replace panel structs with content types. Adjust
rendering to use Panel::new and minibuffer configuration. Update imports
and add MinibufferConfig struct. No functional changes; all behavior
preserved.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (bartowski/nvidia_Nemotron-Cascade-2-30B-A3B-GGUF)
Expose CmdRegistry via KeymapSet registry method.
Update Z keybinding to use SaveAndQuit command.
Pass registry to App's CmdContext construction.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (bartowski/nvidia_Nemotron-Cascade-2-30B-A3B-GGUF)
Update UI components and effects to ensure layout is rebuilt when necessary.
- Rebuild layout in `EnterEditAtCursor` effect
- Use `Rc` for sharing records in `StartDrill` effect
- Improve formatting in `SetDrillPendingEdit` effect
- Update test case in `App` to use multi-line method calls
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
Refactor App and DrillState to use Rc for sharing records, and introduce
rebuild_layout() to manage GridLayout lifecycle. This ensures the layout is
consistently updated when the model or drill state changes.
- Use Rc<Vec<(CellKey, CellValue)>> for DrillState.records to allow cheap
cloning
- Add layout field to App
- Implement rebuild_layout() in App to refresh the GridLayout
- Update App::new and App::cmd_context to use the new layout management
- Ensure layout is rebuilt after applying effects and handling key events
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
Adds a `mode()` method to the `Panel` enum to map panels to their
corresponding `AppMode`. Simplifies `TogglePanelAndFocus` in `cmd.rs`
to use this method instead of a manual match block.
Also adds regression tests in `app.rs` to verify that viewport
scrolling now correctly handles small terminal heights.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-31B-it-GGUF:UD-Q5_K_XL)
Improve grid viewport calculations by using actual column widths instead of
rough estimates. This ensures that the viewport scrolls correctly when the
cursor moves past the visible area.
- Move column width and visible column calculations to public functions in
`src/ui/grid.rs`.
- Update `App::cmd_context` to use these precise calculations for `visible_cols`
.
- Add a regression test to verify that `col_offset` scrolls when cursor moves
past visible columns.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-31B-it-GGUF:UD-Q5_K_XL)
Add new navigation and editing capabilities to improve data entry and
browsing efficiency.
- Implement `PageScroll` command for PageUp/PageDown navigation.
- Implement `OpenRecordRow` command (Vim-style 'o') to add a row and enter edit
mode.
- Implement `CommitAndAdvanceRight` command for Excel-style Tab navigation.
- Add keybindings for Home, End, PageUp, PageDown, and Tab.
- Update UI hints to reflect new Tab functionality.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-31B-it-GGUF:UD-Q5_K_XL)
Refactor records mode to use synthetic CellKeys (_Index, _Dim) for all columns,
allowing uniform handling of display values and edits across both pivot and
records modes.
- Introduce `synthetic_record_info` to extract metadata from synthetic keys.
- Update `GridLayout::cell_key` to return synthetic keys in records mode.
- Add `GridLayout::resolve_display` to handle value resolution for synthetic
keys.
- Replace `records_col` and `records_value` in `CmdContext` with a unified
`display_value`.
- Update `EditOrDrill` and `AddRecordRow` to use synthetic key detection.
- Refactor `CommitCellEdit` to use a shared `commit_cell_value` helper.
BREAKING CHANGE: CmdContext fields `records_col` and `records_value` are replaced by
`display_value` .
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-31B-it-GGUF:UD-Q5_K_XL)
Update TogglePanelAndFocus and related components to use open/focused flags.
Changed TogglePanelAndFocus from currently_open to open+focused flags.
Parser accepts optional [open] [focused] arguments.
Interactive mode toggles between open+focus and closed/unfocused.
Keymap updates: F/C/V in panel modes close panels when focused.
Model initialization: Virtual categories _Index/_Dim default to None
axis, regular categories auto-assign Row/Column/Page.
App test context updated with visible_rows/visible_cols.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
Update status bar hints for new features.
Normal mode hint: Added R:records P:prune, removed F/C/V:panels
CategoryPanel hint: Added d:delete
These hints reflect the new keybindings for records mode toggle,
prune empty toggle, and category deletion.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
Add a tree-based category panel that supports expand/collapse of categories.
Introduces CatTreeEntry and build_cat_tree to render categories as
a collapsible tree. The category panel now displays categories with
expand indicators (▶/▼) and shows items under expanded categories.
CmdContext gains cat_tree_entry(), cat_at_cursor(), and cat_tree_len()
methods to work with the tree. App tracks expanded_cats in a HashSet.
Keymap updates: Enter in category panel now triggers filter-to-item.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
Introduce records-mode drill-down functionality that allows users to
edit individual records without immediately modifying the underlying model.
Key changes:
- Added DrillState struct to hold frozen records snapshot and pending edits
- New effects: StartDrill, ApplyAndClearDrill, SetDrillPendingEdit
- Extended CmdContext with records_col and records_value for records mode
- CommitCellEdit now stages edits in pending_edits when in records mode
- DrillIntoCell captures a snapshot before switching to drill view
- GridLayout supports frozen records for stable view during edits
- GridWidget renders with drill_state for pending edit display
In records mode, edits are staged and only applied to the model when
the user navigates away or commits. This prevents data loss and allows
batch editing of records.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
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)
Introduce a buffers HashMap to manage text input state across different
modes (command, edit, formula, category, export).
Changes:
- Added buffers field to GridWidget and updated constructor
- Updated draw_command_bar to use app.buffers instead of mode buffer
- Updated grid edit indicator to use buffers HashMap
- Added tests for command mode buffer behavior:
* command_mode_typing_appends_to_buffer
* command_mode_buffer_cleared_on_reentry
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
Remove the key_modifiers field from CmdContext struct and all its usages.
This simplifies the command context by removing unused modifier state.
The Cmd trait's execute method no longer receives key modifiers.
Changes:
- Removed KeyModifiers import from cmd.rs
- Removed key_modifiers field from CmdContext struct
- Removed file_path_set field from CmdContext (unused)
- Updated App::cmd_context to not populate key_modifiers
- Removed KeymapSet::registry() accessor
- Updated test code to match new struct layout
- Added documentation to Cmd::name() method
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
Refactor command system to pre-resolve cell key and grid dimensions
in CmdContext, eliminating repeated GridLayout construction.
Key changes:
- Add cell_key, row_count, col_count to CmdContext
- Replace generic CmdRegistry::register with
register/register_pure/register_nullary
- Cell commands (clear-cell, yank, paste) now take explicit CellKey
- Update keymap dispatch to use new interactive() method
- Special-case "search" buffer in SetBuffer effect
- Update tests to populate new context fields
This reduces code duplication and makes command execution more
efficient by computing layout once at context creation time.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
Refactor the keymap system to use string-based command names instead of
concrete command struct instantiations. This introduces a Binding enum that
can represent either a command lookup (name + args) or a prefix sub-keymap.
Key changes:
- Keymap now stores Binding enum instead of Arc<dyn Cmd>
- dispatch() accepts CmdRegistry to resolve commands at runtime
- Added bind_args() for commands with arguments
- KeymapSet now owns the command registry
- Removed PrefixKey struct, inlined its logic
- Updated all default keymap bindings to use string names
This enables more flexible command configuration and easier testing.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
Update headless command execution to use the new architecture:
- Removed CommandResult import (no longer needed)
- Headless mode now creates an App instance and uses cmd_context()
- Commands are parsed and executed via the registry, effects applied
through app.apply_effects() instead of command::dispatch()
- Made cmd_context() public so headless mode can access it
- Updated persistence save to use app.model instead of direct model
Tests updated to use ExecuteCommand instead of QuitCmd, with proper
buffer setup for command parsing.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
Remove unused imports for legacy code that is no longer needed.
Delete execute_command function that handled :q, :w, :import
commands via direct AppMode matching.
Delete handle_wizard_key function and associated fallback logic
for modes not yet migrated to keymaps. These are now handled
by the new keymap-based approach.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
Update handle_key to pass key code and modifiers to cmd_context.
Update keymap_set.dispatch to pass search_mode from App state.
Remove old-style panel handlers (handle_edit_key, handle_formula_edit_key,
handle_formula_panel_key, etc.) - approximately 500 lines.
Update handle_command_mode_key to use buffers map for command execution.
All other modes now handled via keymap dispatch.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
Add tile_cat_idx field to track selected tile category index.
Add buffers HashMap for named text buffers used in text-entry modes.
Update AppMode::TileSelect to remove nested cat_idx struct.
Update cmd_context to accept key and modifiers parameters.
Update cmd_context to populate new fields from App state.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
- Fixed transient keymap consumption logic: the transient keymap is now only
consumed when a match is found.
- Updated handle_key to retain transient keymap if no command matches, allowing
subsequent key presses to be processed by the main keymap set.
- Added issue noting the previous unintended consumption.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gpt-oss:20b)
- Updated imports to include Arc and removed KeyModifiers.
- Replaced pending_key with transient_keymap and keymap_set.
- Added KeymapSet for mode-specific keymaps.
- Removed legacy pending key logic and many helper methods.
- Updated tests to use new command execution pattern.
- Adjusted App struct and methods to align with new keymap system.
Co-Authored-By: fiddlerwoaroof/git-smart-commit (gpt-oss:20b)
Create keymap.rs with Keymap struct mapping (mode, key) to Cmd trait
objects. Wire into App::handle_key — keymap dispatch is tried first,
falling through to old handlers for unmigrated bindings. Normal mode
navigation, cell ops, mode switches, and Help mode are keymap-driven.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Define Effect trait in ui/effect.rs with concrete effect structs for
all model mutations, view changes, navigation, and app state updates.
Each effect implements apply(&self, &mut App). Add App::apply_effects
to apply a sequence of effects. No behavior change yet — existing
key handlers still work as before.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add key handling for ConfigureDates (space toggle components) and
DefineFormulas (n new, d delete, text input mode) wizard steps.
Render date component toggles, formula list with input area, and
sample formulas derived from detected measures.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>