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:
136
README.md
136
README.md
@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user