- commit
- 6ae343554e1df0ac291f2c4d14267551d79aca71
- parent
- 49cd2374082194399e1d304c3156cfe86ff01070
- author
- Dami <dami.etoile@gmail.com>
| M | docs/ | 1 | + |
| A | docs/ | 123 | ++++++++++++++++++++++++++++++ |
@@ -13,6 +13,7 @@
1313 **Working on 012**1414 1515 - [Architecture](architecture.md): how the engine and UI fit together16+- [Extending 012](extending.md): the patterns new features and scale follow1617 - [UX and visual bar](UX.md): the rules every change follows1718 - [Testing](testing.md): unit, end-to-end through libghostty, golden screens, the excelize oracle1819 - [Limits](limits.md): measured bounds of support and known bottlenecks@@ -0,0 +1,123 @@
1+# Extending 0122+3+012 grows along two axes: **breadth** (more functions, commands, formats,4+charts, overlays, remote sources) and **size** (more cells, sheets and5+formulas). Each axis has a few patterns. New work follows them; code that6+doesn't yet is listed under [Gaps](#gaps).7+8+## Breadth: one entry, every surface9+10+### 1. Registries are the single source11+12+A capability is one entry in a table, in one file, and every surface that13+shows it is derived from the table rather than listing it again.14+15+| Registry | Entry | Derived from it |16+|---|---|---|17+| `sheet.FuncDef` | a function: name, signature, description, arity, eval | parsing, autocomplete, argument hints, help, [functions.md](functions.md) |18+| `ui.command` | an action: id, title, description, run, enabled, checked | key bindings, menu bar, context menus, palette, shortcuts help |19+| file formats | a format: name, extensions, importer, exporter | import picker, File > Download, command line, detection |20+| chart types | a type: name, text renderer, image renderer | chart editor, Insert > Chart |21+| theme roles | a role: dark and light styles on the 16 ANSI colors | every style in the UI |22+23+Adding a function, command, format or chart type means adding an entry24+(and its file), not editing switch statements elsewhere. When a list has to25+be kept in sync by hand, turn it into a registry.26+27+### 2. Components own their state28+29+UI features that take over input (menus, palette, find bar, pickers, chart30+editor, sheet tabs, import flow) are components: a type with its own state,31+key and mouse handling, layout and status line, owned by `Model` and driven32+through the `overlay` interface or its siblings. `Model` routes messages and33+composes the screen; it doesn't hold a component's fields. Overlays are34+composited over the finished grid, so a new one never shifts the layout.35+36+### 3. Actions go through commands37+38+Every user action is a registered command, whatever reaches it (key, menu,39+mouse, palette). This keeps discoverability automatic, and gives one place40+to add a command log for macros and replay later.41+42+### 4. Side effects stay at the edges43+44+`internal/sheet` does no I/O, network or terminal work. Anything outside the45+process is a small interface the engine asks (`RemoteSource`) and the UI46+fulfils asynchronously with `tea.Cmd`, answering from a cache. Tests47+substitute fakes (`jev.Client`, the fake TypeSafe server, `demos/fakejev`).48+New remote function families (other models, web lookups, databases) plug in49+the same way.50+51+## Size: narrow paths for hot data52+53+### 5. One mutation path54+55+Every change to a workbook goes through `Batch(Change{...}, fn)` and the56+history `step`: snapshot what changes, apply, mark dirty, recalculate. Undo,57+telemetry, recalculation and the file's dirty flag hang off that one path,58+and so will anything later that needs to see every change (macros, a59+command log, live sharing). New mutations use it; they never write cells60+around it.61+62+### 6. Storage behind a small API63+64+Code reads and writes cells through a narrow API (get, set, iterate a65+range, iterate a column), not the map underneath. That lets the store change66+shape (compact column blocks, side tables for formulas and formats; see67+[limits.md](limits.md)) without touching the rest of the engine.68+69+### 7. Read ranges as ranges70+71+Formulas read single cells through `lookup` and ranges through range72+iteration, so aggregates (`SUM(A:A)`, running totals) can be served from73+column blocks, prefix sums or cached aggregates instead of one lookup per74+cell.75+76+### 8. Work proportional to what changed or what's visible77+78+Recalculation touches the cells an edit affects (dependency indexes by79+column and by sheet), rendering touches only the visible cells, and status80+statistics are cached against a version counter. A feature that scans the81+whole sheet per keystroke or per frame needs a cache or an index.82+83+### 9. Additive, versioned files84+85+The `.012` format only gains optional fields, and the version rises only when86+a file uses a feature older builds can't read (names, freeze and filters in87+v3, several sheets in v4). Old files always load. Settings that older88+builds can safely ignore (decimal arithmetic) don't raise the version.89+90+## Keeping it honest91+92+### 10. Measure at the seams93+94+Every seam above has a telemetry span and a `-tags stress` benchmark95+(`make stress`, `make stress-report`), and [limits.md](limits.md) records96+the bounds. A change to a seam reports before and after numbers.97+98+### 11. Shape limits99+100+`make lint`: no function over cognitive complexity 25, no Go file over 500101+lines. A package that keeps growing past a few thousand lines gets split102+along a boundary where dependencies point one way.103+104+### 12. Every feature at three levels105+106+Unit tests against the engine or model, an e2e test through libghostty, and107+a golden screen reviewed in the gallery for anything visible (see108+[testing.md](testing.md)).109+110+## Gaps111+112+Where the code doesn't follow the patterns yet:113+114+- File formats are a closed `Kind` list with switches in `Import` and115+ `Export` (pattern 1).116+- Chart types are switches on `sheet.ChartType` in `internal/chart`117+ (pattern 1).118+- `sheet.Remote` is a package global; it belongs to the workbook (pattern 4).119+- Cells are a bare `map[Addr]*Cell` used directly across the engine120+ (pattern 6).121+- Aggregates read ranges cell by cell through `lookup` (pattern 7).122+- Undo history is capped by step count, not bytes (pattern 8).123+- `Model` holds many components' fields directly (pattern 2).