docs: expand README with concrete details

Add dedicated sections for the data model (formula examples), views and
axes (tile mode, records mode, drill-down), the .improv file format
(annotated example), import wizard and headless scripting, and the
command/effect architecture. Link docs/design-notes.md from the "Why"
section. Update build instructions to use `nix build .`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Edward Langley
2026-04-09 00:11:44 -07:00
parent 4751b800fd
commit d3d1df0be2

136
README.md
View File

@ -17,18 +17,20 @@ attempt to bring that model to a modern terminal, with a formula language that
references category and item names, views that can be rearranged with a single
keystroke, and a plain-text file format you can diff in git.
See [docs/design-notes.md](docs/design-notes.md) for the original product
vision and non-goals.
## Quick start
```sh
nix develop
cargo build --release
./target/release/improvise examples/demo.improv
nix build .
./result/bin/improvise examples/demo.improv
```
Or import your own CSV:
```sh
./target/release/improvise import path/to/data.csv
./result/bin/improvise import path/to/data.csv
```
## Key bindings to try first
@ -47,13 +49,12 @@ Or import your own CSV:
## Installation
### From source with Nix (preferred)
### With Nix (preferred)
```sh
nix develop
cargo build --release
# optionally install to your PATH:
cargo install --path .
nix build .
# or install into your profile:
nix profile install .
```
### From crates.io
@ -67,22 +68,109 @@ cargo install improvise
See the [GitHub releases page](https://github.com/fiddlerwoaroof/improvise/releases)
for prebuilt binaries (Linux x86_64, macOS Intel and Apple Silicon).
## What's interesting about the codebase
## The data model
The data model stores every cell at the intersection of named categories and
items — `(Region=East, Measure=Revenue)`rather than at a grid address like
`B3`. A view is a configuration that assigns each category to a row, column,
page, or hidden axis, and the grid layout is computed as a pure function from
`(Model, View)` to `GridLayout`. Transposing a pivot is just swapping the row
and column assignments; records mode is just assigning the virtual `_Index` and
`_Dim` categories to particular axes. The formula language references dimension
names (`Profit = Revenue - Cost`), so formulas survive any rearrangement
without rewriting. All user actions go through a command/effect pipeline:
commands are pure functions that read an immutable context and return a list of
effects, which are the only things that mutate state. The same command registry
serves both the interactive TUI and the headless `cmd`/`script` CLI
subcommands. Models persist to a markdown-like `.improv` format that is
human-readable, git-diffable, and optionally gzip-compressed.
Every cell lives at the intersection of named categories and items —
`(Region=East, Measure=Revenue)`not at a grid address like `B3`. A model
can have up to 12 categories, each with an ordered list of items that can be
organized into collapsible groups (e.g. months grouped into quarters).
Formulas reference dimension names, not cell addresses:
```
Profit = Revenue - Cost
Tax = Revenue * 0.08
Margin = IF(Revenue > 0, Profit / Revenue, 0)
BigDeal = Sum(Revenue WHERE Region = "West")
```
The formula language supports `+` `-` `*` `/` `^`, comparisons, `IF`, and
aggregation functions (`Sum`, `Avg`, `Min`, `Max`, `Count`) with optional
`WHERE` clauses. Formulas apply uniformly across all intersections — no
copying, no dragging, no `$A$1` anchoring.
## Views and axes
A view assigns each category to one of four axes: **row**, **column**,
**page** (slicer), or **hidden**. The grid layout is a pure function of
`(Model, View)``GridLayout` — transposing is just swapping row and column
assignments, and it happens in one keystroke (`t`). Page-axis categories act
as filters: `[` and `]` cycle through items.
Press `T` to enter tile mode, where each category appears as a tile in the
tile bar. Move between tiles with `h`/`l`, then press `Space` to cycle axes
or `r`/`c`/`p` to set one directly.
Records mode (`R`) flips to a long-format view by assigning the virtual
`_Index` and `_Dim` categories to row and column axes. Drill-down (`>`) on an
aggregated cell captures a snapshot; edits accumulate in a staging area and
commit atomically on exit.
## File format
Models persist to a plain-text `.improv` format that reads like markdown and
diffs cleanly in git:
```
# Sales 2025
## Category: Region
- North
- South
- East [Coastal]
- West [Coastal]
> Coastal
## Category: Measure
- Revenue
- Cost
- Profit
## Formulas
- Profit = Revenue - Cost [Measure]
## Data
Region=East, Measure=Revenue = 1200
Region=East, Measure=Cost = 800
## View: Default (active)
Region: row
Measure: column
format: ,.2f
```
Gzip-compressed `.improv.gz` is also supported. Legacy JSON is auto-detected
for backward compatibility.
## Import and scripting
The import wizard analyzes CSV columns and proposes each as a category
(string-valued), measure (numeric), time dimension (date-like, with optional
year/month/quarter extraction), or skip. Multiple CSVs merge automatically
with a synthetic "File" category.
All model mutations go through a typed command registry, so the same
operations that back the TUI work headless:
```sh
# single commands
improvise cmd 'add-cat Region' 'add-item Region East' -f model.improv
# script file (one command per line, # comments)
improvise script setup.txt -f model.improv
```
## What's interesting about the architecture
All user actions flow through a two-phase command/effect pipeline. Commands
are pure functions: they receive an immutable `CmdContext` (model, layout,
cursor position, mode) and return a list of effects. Effects are the only
things that mutate app state, and each one is a small, debuggable struct.
This means commands are testable without a terminal, effects can be logged or
replayed, and the 40+ commands and 50+ effect types are all polymorphic —
dispatched through trait objects and a registry, not a central match block.
The keybinding system gives each of the 14 modes its own keymap, with
Emacs-style prefix keys for multi-stroke sequences.
## Expectations