Column and row formats and widths live in formatting.md, tabs in sheets-and-tabs.md; editing.md and keys.md link to them. The format page says once what isn't saved (results, pivots, spills) instead of a sentence under pivot tables. arrays.md gives spilled cells a table and a section on when an array can't spill. Links that named a file (data.md, formulas.md) name the page they lead to.
- commit
- 5f1e569a70adc15e8e8ff41277cf346069ccd6a9
- parent
- 6cea7499f72116a27c5df786558f65d8ff1788cd
- author
- Dami <dami.etoile@gmail.com>
| M | CLAUDE.md | 5 | ++- |
| M | ROADMAP.md | 58 | +++++++++++++++--------------- |
| M | docs/ | 20 | +++++----- |
| M | docs/ | 12 | +++--- |
| M | docs/ | 4 | +- |
| M | docs/ | 2 | +- |
| M | docs/ | 8 | ++-- |
| M | docs/ | 6 | ++-- |
| M | docs/ | 4 | +- |
| M | docs/ | 2 | +- |
| M | docs/ | 13 | ++++--- |
| M | docs/ | 2 | +- |
| M | docs/ | 38 | +++++++++++--------- |
| M | docs/ | 2 | +- |
| M | docs/ | 2 | +- |
| M | docs/ | 2 | +- |
| M | docs/ | 33 | ++++++++--------- |
| M | docs/ | 2 | +- |
| M | docs/ | 11 | +----- |
| M | docs/ | 2 | +- |
| M | docs/ | 2 | +- |
| M | docs/ | 4 | +- |
| M | docs/ | 2 | +- |
@@ -1,7 +1,8 @@
11 # Working on 01222 3-Read [docs/UX.md](docs/contributing/ux.md), [docs/extending.md](docs/contributing/extending.md) and4-[docs/testing.md](docs/contributing/testing.md) before changing code.3+Read [UX](docs/contributing/ux.md), [extending](docs/contributing/extending.md)4+and [testing](docs/contributing/testing.md) in `docs/contributing/`5+before changing code.56 67 ## No broken windows78 @@ -4,7 +4,7 @@ Principles: inside the grid 012 behaves like Google Sheets; 1-2-3 is the
44 visual identity around it. The main binary stays pure Go55 (`CGO_ENABLED=0`), and every feature ships with unit tests, a libghostty66 e2e test and reviewed golden screens. Visual and UX quality keep pace7-with features: see [docs/UX.md](docs/contributing/ux.md).7+with features: see [UX and visual bar](docs/contributing/ux.md).88 99 This page is what's ahead. What has shipped is listed once, at the end,1010 with the doc that describes it; when an item ships, it moves there as@@ -20,7 +20,7 @@ Sizes: S (a day or two), M (about a week), L (weeks).
2020 |---|---|---|2121 | Open 012's XLSX output in Excel and LibreOffice before releases, as was done for Google Sheets | The writer is otherwise checked by excelize and 012's reader | S |2222 | Conditional formatting and validation, what's left: data bars and icon sets, "top 10", "duplicate values", date periods (this week, last month), validation on pastes and fills, rules moving with cut and paste, a dropdown's chip look, and custom checkbox values | Excel's other rule types come in as notes | M |23-| Remaining overlays get narrow hosts: filter picker, sort and choice bars, chart editor, shortcuts, named ranges, cell entry and prompts | Components testable without the model ([architecture.md](docs/contributing/architecture.md#the-ui)) | M |23+| Remaining overlays get narrow hosts: filter picker, sort and choice bars, chart editor, shortcuts, named ranges, cell entry and prompts | Components testable without the model ([Architecture](docs/contributing/architecture.md#the-ui)) | M |2424 2525 ### 2. Spreadsheet features Sheets users reach for2626 @@ -33,7 +33,7 @@ Sizes: S (a day or two), M (about a week), L (weeks).
3333 3434 The grid is Excel's, 1,048,576 x 16,384, and memory is bounded by the3535 `max-cells` budget rather than the grid. The budget rises in measured36-steps; see [docs/limits.md](docs/contributing/limits.md#what-would-raise-the-bounds).36+steps; see [Bounds of support](docs/contributing/limits.md#what-would-raise-the-bounds).3737 3838 | Item | Result | Size |3939 |---|---|---|@@ -79,44 +79,44 @@ by a gateway dialing the iroh ticket.
7979 8080 **Engine**8181 82-- Undo and redo for every change, across sheets: [data.md](docs/sheets/editing.md#undo)83-- Copy, cut, paste and fill with relative and `$absolute` references; insert and delete rows and columns: [data.md](docs/sheets/editing.md#copy-paste-and-fill)84-- Entries detected as typed (currency, percent, dates, times) and number formats: [formulas.md](docs/formulas/README.md#what-you-type)85-- Named ranges; several sheets with references between them, hidden sheets: [formulas.md](docs/formulas/references.md)86-- Sheets' everyday, math, text, lookup, date and finance functions, checked against excelize: [functions.md](docs/reference/functions.md), [testing.md](docs/contributing/testing.md#the-excelize-oracle)87-- Dynamic arrays that spill (FILTER, SORT, UNIQUE, SEQUENCE and more, ARRAYFORMULA), LET and LAMBDA, SPLIT and the REGEX functions: [formulas.md](docs/formulas/arrays.md)88-- Opt-in decimal arithmetic for money: [formulas.md](docs/formulas/decimal.md)89-- An Excel-sized grid with a `max-cells` budget; operations cost the data, not the grid: [limits.md](docs/contributing/limits.md#sheet-size)82+- Undo and redo for every change, across sheets: [Editing](docs/sheets/editing.md#undo)83+- Copy, cut, paste and fill with relative and `$absolute` references; insert and delete rows and columns: [Editing](docs/sheets/editing.md#copy-paste-and-fill)84+- Entries detected as typed (currency, percent, dates, times) and number formats: [Formulas](docs/formulas/README.md#what-you-type)85+- Named ranges; several sheets with references between them, hidden sheets: [References](docs/formulas/references.md)86+- Sheets' everyday, math, text, lookup, date and finance functions, checked against excelize: [Functions](docs/reference/functions.md), [Testing](docs/contributing/testing.md#the-excelize-oracle)87+- Dynamic arrays that spill (FILTER, SORT, UNIQUE, SEQUENCE and more, ARRAYFORMULA), LET and LAMBDA, SPLIT and the REGEX functions: [Arrays and spills](docs/formulas/arrays.md)88+- Opt-in decimal arithmetic for money: [Decimal arithmetic](docs/formulas/decimal.md)89+- An Excel-sized grid with a `max-cells` budget; operations cost the data, not the grid: [Bounds of support](docs/contributing/limits.md#sheet-size)9090 9191 **Finding and using features**9292 93-- Sheets-structured menus, a command palette and a key for every command: [keys.md](docs/reference/keys.md)94-- Formula suggestions, argument hints and pointing at cells and sheets: [formulas.md](docs/formulas/building.md)95-- Tracing precedents and dependents, hidden sheets explained: [formulas.md](docs/formulas/building.md)96-- An optional vim keymap with a `:` command line: [keys.md](docs/reference/keys.md#vim-keys)93+- Sheets-structured menus, a command palette and a key for every command: [Keys and mouse](docs/reference/keys.md)94+- Formula suggestions, argument hints and pointing at cells and sheets: [Building formulas](docs/formulas/building.md)95+- Tracing precedents and dependents, hidden sheets explained: [Building formulas](docs/formulas/building.md)96+- An optional vim keymap with a `:` command line: [Keys and mouse](docs/reference/keys.md#vim-keys)9797 9898 **Data tools**9999 100-- Freeze, multi-column sort, filters with value pickers and conditions: [data.md](docs/sheets/sort-filter.md#freeze)101-- Find and replace, with regular expressions, across sheets: [data.md](docs/sheets/find-replace.md)102-- Conditional formatting (single-color rules, color scales) and data validation (dropdowns, checkboxes, bounds): [data.md](docs/sheets/rules.md#conditional-formatting)103-- Pivot tables and frequency tables, live, with subtotals and renamed values: [data.md](docs/sheets/pivots.md)104-- Notes on cells: [data.md](docs/sheets/notes-protection.md#notes)105-- Protected sheets and ranges that warn on edit: [data.md](docs/sheets/notes-protection.md#protected-sheets-and-ranges)106-- Charts (column, bar, line, area, pie, scatter; stacking, trend lines, axis and legend options), as images or text: [charts.md](docs/sheets/charts.md)107-- Macros, recorded or written in Starlark: [macros.md](docs/sheets/macros.md)108-- JEV functions, with the API key in the OS keychain: [jev.md](docs/formulas/jev.md)100+- Freeze, multi-column sort, filters with value pickers and conditions: [Freeze, sort and filter](docs/sheets/sort-filter.md#freeze)101+- Find and replace, with regular expressions, across sheets: [Find and replace](docs/sheets/find-replace.md)102+- Conditional formatting (single-color rules, color scales) and data validation (dropdowns, checkboxes, bounds): [Conditional formatting and data validation](docs/sheets/rules.md#conditional-formatting)103+- Pivot tables and frequency tables, live, with subtotals and renamed values: [Pivot tables](docs/sheets/pivots.md)104+- Notes on cells: [Notes and protection](docs/sheets/notes-protection.md#notes)105+- Protected sheets and ranges that warn on edit: [Notes and protection](docs/sheets/notes-protection.md#protected-sheets-and-ranges)106+- Charts (column, bar, line, area, pie, scatter; stacking, trend lines, axis and legend options), as images or text: [Charts](docs/sheets/charts.md)107+- Macros, recorded or written in Starlark: [Macros](docs/sheets/macros.md)108+- JEV functions, with the API key in the OS keychain: [JEV functions](docs/formulas/jev.md)109109 110110 **The terminal**111111 112-- System clipboard, hyperlinks, curly error underlines, cursor shapes, progress and notifications, light and dark: [charts.md](docs/terminal/README.md#terminal-features-012-uses)113-- Color schemes and a config file: [themes.md](docs/terminal/themes.md), [config.md](docs/reference/config.md)114-- `012 serve` over SSH, with files on the ssh command line and recovery of unsaved work: [ssh.md](docs/terminal/ssh.md)112+- System clipboard, hyperlinks, curly error underlines, cursor shapes, progress and notifications, light and dark: [The terminal](docs/terminal/README.md#terminal-features-012-uses)113+- Color schemes and a config file: [Themes](docs/terminal/themes.md), [Configuration](docs/reference/config.md)114+- `012 serve` over SSH, with files on the ssh command line and recovery of unsaved work: [Serving over SSH](docs/terminal/ssh.md)115115 116116 **Files**117117 118-- Import CSV, TSV, XLSX, SQLite, Parquet and Lotus `.wk1`; export CSV, TSV, XLSX and SQLite; import locations; save-as and overwrite checks: [files.md](docs/files/README.md)118+- Import CSV, TSV, XLSX, SQLite, Parquet and Lotus `.wk1`; export CSV, TSV, XLSX and SQLite; import locations; save-as and overwrite checks: [Files](docs/files/README.md)119119 120120 **Upkeep**121121 122-- `make check` before every push; demo tapes (`make demos`); version tags from v0.1.0: [testing.md](docs/contributing/testing.md)122+- `make check` before every push; demo tapes (`make demos`); version tags from v0.1.0: [Testing](docs/contributing/testing.md)@@ -128,7 +128,7 @@ style.
128128 - **Functions.** One table (`FuncDef`, in `internal/functions`) holds129129 every function's name, signature, description and arity; it drives130130 parsing, evaluation, autocomplete, in-app help and131- [functions.md](../reference/functions.md). See [Functions](#functions).131+ [Functions](../reference/functions.md). See [Functions](#functions).132132 - **Undo.** Every mutation goes through a small set of paths that snapshot133133 the cells, widths, names, charts, view state and sheet list they change,134134 on any sheet, so a step can be reversed exactly; multi-cell operations@@ -186,7 +186,7 @@ style.
186186 (one file per category: `everyday.go`, `math.go`, `stats.go`,187187 `logic.go`, `text.go`, `regex.go`, `lookup.go`, `dynamic.go`,188188 `lambda.go`, `date.go`, `finance.go`, `link.go`, `jev.go`, which189-[functions.md](../reference/functions.md) is grouped by), the evaluation of formulas189+[Functions](../reference/functions.md) is grouped by), the evaluation of formulas190190 (`eval.go`: operators and calls; arrays in `array.go`, functions mapped191191 over them in `lift.go`, the names LET and LAMBDA bind in `scope.go`), the helpers192192 functions share (arguments and blocks of cells in `args.go`, criteria@@ -260,7 +260,7 @@ instead:
260260 The engine fills a chunk from the occupancy index without a call per cell261261 (`colFill`, `rangeFill`), and keeps each sheet's reader between262262 recalculations, so the buffers are made once. See263-[limits.md](limits.md#the-function-library) for its costs.263+[Bounds of support](limits.md#the-function-library) for its costs.264264 265265 What stays in the engine is what needs cells or the workbook: the266266 running aggregates' storage and extension (`rangememo.go`), the links a@@ -270,7 +270,7 @@ setting (`SetDecimal`, which marks formulas with `functions.Decimalize`).
270270 271271 ### Where the engine can grow272272 273-The package boundaries leave the bounds in [limits.md](limits.md) room to273+The package boundaries leave the bounds in [Bounds of support](limits.md) room to274274 move without touching callers:275275 276276 - **Storage.** Compact cell storage (column blocks of values, formulas and@@ -295,7 +295,7 @@ move without touching callers:
295295 296296 `internal/ui` is a Bubble Tea program. Inside the grid it follows Google297297 Sheets; around it, the control panel keeps a 1-2-3 look. See298-[UX.md](ux.md) for the rules every change follows.298+[UX and visual bar](ux.md) for the rules every change follows.299299 300300 **Commands.** Every action is a registered command with a title and301301 description (`commands.go`), reached from key bindings, the menu bar,@@ -479,7 +479,7 @@ file names. Everything a model knows lives in the model, so sessions
479479 share nothing but read-only tables (the command and function480480 registries), the process-wide telemetry and, with JEV on, the HTTP481481 client; each gets its own `jev.Cache`, whose queue belongs to that482-session's program. See [ssh.md](../terminal/ssh.md).482+session's program. See [Serving over SSH](../terminal/ssh.md).483483 484484 **Unsaved work.** `Server.Shutdown` closes a channel every session485485 watches; a session quitting on it, or on its idle timeout, asks its@@ -504,7 +504,7 @@ config directory; the same directory holds `themes/` and 012 serve's host
504504 key. Every option is one entry in `config.Options`: name, type, default,505505 environment variables, flag, whether Reload config applies it, and a506506 description. Parsing and validation, the flags `cmd/012` accepts, `012507-config`'s listing and default file, and [config.md](../reference/config.md)'s507+config`'s listing and default file, and [Configuration](../reference/config.md)'s508508 reference (checked by a test) all come from that table. Values are layered defaults < file < environment < flags, each509509 remembering its source; problems are warnings, never fatal. The UI gets a510510 `ui.Settings` (the config, a reload function, the credential store, a JEV@@ -550,7 +550,7 @@ color scheme (`theme.Palette`: VHS's embedded catalog, or a Ghostty,
550550 kitty or VHS JSON file in `themes/`) is drawn by `theme.FromPalette`,551551 which maps each ANSI color to the scheme's, adds full-width bands for the552552 bars and a screen background, and corrects contrast (see553-[themes.md](../terminal/themes.md)). Bands are applied by `theme.Fill`, one pass over553+[Themes](../terminal/themes.md)). Bands are applied by `theme.Fill`, one pass over554554 a rendered line's escape sequences that sets the band's colors at the555555 start and after each reset and pads to the width; `View` fills the bar556556 lines before overlays are composited and the whole screen after, so bars@@ -561,7 +561,7 @@ when there is one, and from the terminal's palette otherwise.
561561 562562 `internal/telemetry` is off unless asked for. When on, spans, events and563563 per-second frame summaries go to a JSON log, an OTLP/HTTP collector, or564-both (see [observability.md](observability.md)). The OTLP exporter uses564+both (see [Observability](observability.md)). The OTLP exporter uses565565 only the standard library: bounded queues per signal, drained by one566566 goroutine in batches; when a queue is full new items are dropped and567567 counted, so the UI never waits on the network. Operation durations are@@ -573,4 +573,4 @@ command that caused them.
573573 574574 ## Testing575575 576-See [testing.md](testing.md).576+See [Testing](testing.md).@@ -19,12 +19,12 @@ shows it is derived from the table rather than listing it again.
1919 2020 | Registry | Entry | Derived from it |2121 |---|---|---|22-| `functions.FuncDef` (one file per category in `internal/functions`) | a function: name, signature, description, arity, eval, result format, decimal twin | parsing, autocomplete, argument hints, help, [functions.md](../reference/functions.md) |22+| `functions.FuncDef` (one file per category in `internal/functions`) | a function: name, signature, description, arity, eval, result format, decimal twin | parsing, autocomplete, argument hints, help, [Functions](../reference/functions.md) |2323 | `ui.command` | an action: id, title, description, run, enabled, checked, what it edits, and how macros treat it | key bindings (Sheets and vim), menu bar, context menus, palette, shortcuts help, the `:` command line and its completions, macro recording and `run()` in scripts |2424 | `fileio` formats (`formats.go`) | a format: name, extensions, labels, traits, importer, exporter | `Import`, `Export`, detection, import picker, File > Download, command line |2525 | `chart.types`, with `sheet.ChartTypes` | a type: name and order (sheet, saved in files), a layout drawing text and image and the series its legend lists (chart) | `chart.Draw`, `chart.Image`, chart editor, Insert > Chart |2626 | theme roles (`theme.Theme`) | a role: dark and light styles on the 16 ANSI colors, with a contrast minimum for schemes (`minContrast`) | every style in the UI, drawn in the terminal's palette or any color scheme (`FromPalette`); `TestEveryThemeReadable` checks each role under every built-in scheme |27-| `config.Options` | an option: name, type, default, environment variables, flag, live or not, description, check | parsing and warnings, flags, `012 config` and its default file, [config.md](../reference/config.md), Reload config |27+| `config.Options` | an option: name, type, default, environment variables, flag, live or not, description, check | parsing and warnings, flags, `012 config` and its default file, [Configuration](../reference/config.md), Reload config |2828 2929 Adding a function, command, format, chart type, option or role means3030 adding an entry (and its file), not editing switch statements elsewhere.@@ -119,7 +119,7 @@ Code reads and writes cells through a narrow API (`cellStore` in
119119 `internal/sheet/store.go`: get, set, delete, count, iterate everything or120120 a range, a column being a range), not the map underneath. That lets the121121 store change shape (compact column blocks, side tables for formulas and122-formats; see [limits.md](limits.md)) without touching the rest of the122+formats; see [Bounds of support](limits.md)) without touching the rest of the123123 engine. Beside the map, occupancy indexes (`occupancy.go`) say which rows124124 of each column hold a cell, and which hold contents, so a range yields its125125 cells in row order at the cost of what it holds, and the used range, data@@ -175,7 +175,7 @@ builds can safely ignore (decimal arithmetic) don't raise the version.
175175 ### 10. Measure at the seams176176 177177 Every seam above has a telemetry span and a `-tags stress` benchmark178-(`make stress`, `make stress-report`), and [limits.md](limits.md) records178+(`make stress`, `make stress-report`), and [Bounds of support](limits.md) records179179 the bounds. A change to a seam comes with its numbers, measured against180180 the code it replaces.181181 @@ -185,7 +185,7 @@ causes; hand work on another goroutine `m.spans.Parent()` (in the
185185 `context.Context` if the API already takes one); and give a workbook a186186 new owner's trace with `SetTrace`. Never keep a "current span" in a187187 global: `012 serve` runs many programs in one process (see188-[observability.md](observability.md#nested-spans)).188+[Observability](observability.md#nested-spans)).189189 190190 ### 11. Shape limits191191 @@ -201,7 +201,7 @@ aliases so callers don't change.
201201 202202 Unit tests against the engine or model, an e2e test through libghostty, and203203 a golden screen reviewed in the gallery for anything visible (see204-[testing.md](testing.md)).204+[Testing](testing.md)).205205 206206 ## Gaps207207 @@ -9,7 +9,7 @@ What 012 handles well, where it degrades, and why, measured. Numbers come
99 from `make stress` (synthetic worst cases and real datasets, see1010 [Measuring](#measuring)), `make stress-e2e` (key press to screen through a1111 real terminal emulator), and the telemetry described in12-[observability.md](observability.md), which tracks how they move.12+[Observability](observability.md), which tracks how they move.1313 1414 Machine: Apple M5 Pro (18 cores), 48 GB, macOS, Go 1.27.1, arm64. Times1515 are single-threaded: the engine and the UI run on one goroutine. Figures@@ -150,7 +150,7 @@ on an empty sheet over 30 s, within the noise of measuring it.
150150 ## The function library151151 152152 Functions live in `internal/functions`, behind a `Book` interface whose153-methods pass only values (see [architecture.md](architecture.md#functions)):153+methods pass only values (see [Architecture](architecture.md#functions)):154154 SUM-like functions have the engine add a range up (`Fold`), other range155155 walks read chunks into buffers the `Reader` reuses (`Scan`), and lookups156156 walk positions with a cursor, so no function makes a call or an@@ -15,7 +15,7 @@ runs. There are two paths that share one set of numbers:
1515 OTLP (or the collector tails its log file), the stress runs load into1616 ClickHouse, and dashboards sit on top.1717 18-The measured bounds these tools track are in [limits.md](limits.md).18+The measured bounds these tools track are in [Bounds of support](limits.md).1919 2020 ## Turning it on2121 @@ -109,15 +109,15 @@ shows go there, and GIFs stay under 1 MB.
109109 110110 `make lint` runs `scripts/doclint`, which flags wording that narrates111111 history (see [CLAUDE.md](../../CLAUDE.md)), and `scripts/doccheck`: relative112-links and anchors resolve, [docs/README.md](../README.md) links every doc,112+links and anchors resolve, [012 documentation](../README.md) links every doc,113113 every file in `docs/media` is shown by a doc and made by a tape, and114114 every tape records something a doc shows. Unit tests check what the docs115115 say about the code: menu paths lead to menu items, command ids and keys116-exist and every bound key is in [keys.md](../reference/keys.md)116+exist and every bound key is in [Keys and mouse](../reference/keys.md)117117 (`internal/ui/docs_test.go`), settings and variables are options118-(`internal/config/docs_test.go`), and [files.md](../files/README.md) names every118+(`internal/config/docs_test.go`), and [Files](../files/README.md) names every119119 field of the format (`internal/sheet/file_doc_test.go`).120-[functions.md](../reference/functions.md) and [config.md](../reference/config.md)'s reference are120+[Functions](../reference/functions.md) and [Configuration](../reference/config.md)'s reference are121121 generated, and their tests fail when they're stale.122122 123123 ## No broken windows@@ -50,7 +50,7 @@ follow-up task.
5050 `lipgloss.NewStyle()` in views. Need a new role? Add it to `theme` with a5151 comment saying what it's for, in both the dark and light variants.5252 - Use the 16 ANSI colors so the user's terminal palette applies. No53- hard-coded RGB. Color schemes ([themes.md](../terminal/themes.md)) are data: they53+ hard-coded RGB. Color schemes ([Themes](../terminal/themes.md)) are data: they5454 map the same roles to their colors and correct contrast, and5555 `TestEveryThemeReadable` must pass for a new role.5656 - Bars (menu bar, formula bar, context line, column headers, status line)@@ -91,9 +91,9 @@ follow-up task.
9191 committing. Screens are drawn from libghostty's cell grid, so they9292 match what a terminal shows.9393 - [ ] The feature's doc updated in place, where a reader looks for it,94- and its keys in [keys.md](../reference/keys.md) (tests check every bound key is94+ and its keys in [Keys and mouse](../reference/keys.md) (tests check every bound key is9595 there); a headline feature gets a tape in `demos/` and a place in96- the README ([testing.md](testing.md#demo-recordings)).96+ the README ([Testing](testing.md#demo-recordings)).9797 9898 ## Review checklist for screenshots9999 @@ -48,12 +48,12 @@ line lists the removed charts first, then which were re-pointed and
4848 which kept their range, and undo brings the removed ones back.4949 5050 **Size.** A sheet is 1,048,576 rows by 16,384 columns (A to XFD), as in51-Excel. Imports keep at most `max-cells` cells ([config.md](../reference/config.md#max-cells),51+Excel. Imports keep at most `max-cells` cells ([Configuration](../reference/config.md#max-cells),5252 two million by default, about 600 MB): whole rows, as many as fit, and5353 the context line says how many rows were left out, e.g. `only the first5454 166,666 rows fit in max-cells (2,000,000 cells); 12,000 rows left out`.5555 Data past the grid's edges is left out the same way. WK1 files keep their56-own 8,192 by 256. See [limits.md](../contributing/limits.md#imports) for speeds.56+own 8,192 by 256. See [Bounds of support](../contributing/limits.md#imports) for speeds.5757 5858 ## More5959 @@ -60,7 +60,7 @@ opposed to the sheet's AutoFilter) aren't read.
6060 6161 ## Arrays in Excel6262 63-A formula whose array spills (see [formulas.md](../formulas/arrays.md))63+A formula whose array spills (see [Arrays and spills](../formulas/arrays.md))6464 is written as Excel writes a dynamic array formula: an array formula over6565 the cells it spills into (`<f t="array" ref="C1:C9">`) on a cell whose6666 metadata marks it dynamic (`cm="1"`, defined in `xl/metadata.xml`), with@@ -24,11 +24,16 @@ object:
2424 ```2525 2626 A formatted cell has its `input` and the formatting that isn't the27-default: `format` (as `number_format` in [macros](../reference/macro-api.md#cells) names28-it: `currency`, `percent`, `date`, ...), `decimals`, `pattern` for a27+default: `format` (as `number_format` in28+[macros](../reference/macro-api.md#cells) names it: `currency`, `percent`, `date`, ...), `decimals`, `pattern` for a2929 custom format, `bold`, `italic`, `underline`, `strikethrough` and3030 `align` (`left`, `center`, `right`).3131 32+Only what's typed is saved. What 012 computes is computed again when the33+file opens: formula results, [pivot tables](#pivot-tables)' results, and34+the arrays formulas [spill](../formulas/arrays.md), whose cells are kept35+only for their formatting and notes.36+3237 ## Versions3338 3439 012 writes the lowest version that holds the workbook, so older builds@@ -58,7 +63,7 @@ save), so they raise no version:
5863 | `protected` | a sheet | [Protected ranges](../sheets/notes-protection.md#protected-sheets-and-ranges): `{"range":"B2:C9","description":"Totals"}`, or `{"sheet":true}` |5964 | `conditionalFormats`, `validations` | a sheet | [Rules](#conditional-formats-and-data-validation), one per line |6065 | `arithmetic` | the workbook | `decimal` for [decimal arithmetic](../formulas/decimal.md) |61-| `macros`, `macroOrigin` | the workbook | Macros as Starlark scripts, and the computer they were made or trusted on: see [macros.md](../reference/macro-api.md#in-the-file). Opening a file never runs them |66+| `macros`, `macroOrigin` | the workbook | Macros as Starlark scripts, and the computer they were made or trusted on: see [Macro scripting API](../reference/macro-api.md#in-the-file). Opening a file never runs them |6267 6368 ## Column and row formats6469 @@ -107,8 +112,6 @@ A pivot's sheet has a `pivot` field, on one line after its cells,
107112 holding the definition. The results are never saved; they are computed108113 again when the file opens, so the file stays small and can't disagree109114 with its data.110-Arrays that formulas spill are kept the same way: the file keeps the formula,111-and the cells it spills into only for their formatting and notes.112115 113116 ```json114117 {@@ -16,4 +16,4 @@ exists asks the same way: Enter replaces it, Esc cancels, and cancelling
1616 Under `012 serve`, a session that idles out or is ended by the server1717 stopping keeps its unsaved changes in `.012-recovery/` in the served1818 directory, and the next session opening that file offers them back: see19-[ssh.md](../terminal/ssh.md#unsaved-work).19+[Serving over SSH](../terminal/ssh.md#unsaved-work).@@ -33,18 +33,27 @@ right and below, as in Sheets:
3333 `=IFERROR(FILTER(A2:A99, B2:B99="x"), "none")` spills or says none, and3434 `=INDEX(A2:C9, 0, 2)` spills the second column.3535 36-The context line says where a spilled cell's value comes from37-("Spilled from B2"), and on the formula's own cell where it spills38-("Spills into B2:C9"); spilled values are drawn in a color of their own,39-and the formula bar shows a spilled cell's formula dimmed. A spilled cell40-can't be typed into, cleared, pasted over or filled: the context line41-names the formula to edit instead. Selecting the formula with its spill42-and pressing Del clears it, formatting spilled cells keeps the43-formatting, and inserting or deleting rows and columns through a spill44-spills it again. Copying spilled cells pastes their values. Conditional45-formats color spilled cells by their values, and data validation marks46-spilled values it doesn't accept, but never stops an array from47-spilling: rules judge what's typed, and a spilled value isn't.36+## Spilled cells37+38+Spilled values are drawn in a color of their own. The context line says39+where a spilled cell's value comes from ("Spilled from B2"), and on the40+formula's own cell where it spills ("Spills into B2:C9"); the formula41+bar shows a spilled cell's formula dimmed.42+43+| Doing this to spilled cells | Does |44+|---|---|45+| Typing, clearing, pasting over or filling one | Nothing: the context line names the formula to edit instead |46+| Del on the formula with its spill selected | Clears the formula |47+| Formatting them | Keeps the formatting, which the file saves |48+| Inserting or deleting rows or columns through the spill | Spills it again |49+| Copying them | Pastes their values |50+| Conditional formats and data validation on them | Color them by their values, and mark values a rule doesn't accept; a rule never stops an array from spilling |51+52+Formulas reading spilled cells recalculate when the array changes, on53+any sheet, and undo brings back what an array spilled with the formula.54+The file keeps only the formula ([format](../files/format.md)).55+56+## When an array can't spill4857 4958 When a cell in the way of an array isn't empty, the formula shows5059 `#REF!` and says why, as Sheets does: "Array result was not expanded@@ -52,11 +61,6 @@ because it would overwrite data in C3". Clearing that cell lets the
5261 array spill. The same goes for an array that would pass the sheet's5362 edge, or write more cells than `max-cells` allows.5463 55-Formulas reading spilled cells recalculate when the array changes, on56-any sheet, and undo brings back what an array spilled with the formula.57-Files keep only the formula: its array is computed again when the file58-opens.59-6064 ## Names in a formula: LET and LAMBDA6165 6266 `=LET(total, SUM(B2:B99), count, COUNT(B2:B99), total/count)` names@@ -73,7 +73,7 @@ key would come from, without showing it.
7373 The service's address and model come from the config file or the7474 environment only: `jev-base-url` (or `TYPESAFE_BASE_URL`), which must be7575 https unless it's this machine, and `jev-model` (or76-`TYPESAFE_DEFAULT_MODEL`). See [config.md](../reference/config.md).76+`TYPESAFE_DEFAULT_MODEL`). See [Configuration](../reference/config.md).7777 7878 **012 doesn't read `.env` files.** A `.env` next to a downloaded sheet7979 could set `TYPESAFE_BASE_URL` and send your key to someone else's server.@@ -28,7 +28,7 @@ sidebar_position: 2
2828 - Each sheet is 16,384 columns (A to XFD) by 1,048,576 rows, Excel's size;2929 a reference past them (`XFE1`, `A1048577`) reads as a name. Formulas cost3030 what their ranges hold, not their size: `SUM(A:A)` over ten numbers reads31- ten cells, and `ROWS(A:A)` is still 1,048,576. See [limits.md](../contributing/limits.md)31+ ten cells, and `ROWS(A:A)` is still 1,048,576. See [Bounds of support](../contributing/limits.md)3232 for what that means in practice.3333 3434 ## A range where one value is wanted@@ -57,7 +57,7 @@ credential store or comes from a command; see [JEV setup](../formulas/jev.md#set
5757 In the app, File > Settings has **Theme** (a picker with live preview),5858 **Open config file** (in your editor, reloading when it closes),5959 **Reload config** and **JEV API key** (stored, then checked with one test60-call: see [jev.md](../formulas/jev.md#setup)). Reloading applies the options marked60+call: see [JEV functions](../formulas/jev.md#setup)). Reloading applies the options marked6161 "File > Settings > Reload config" below; the rest take effect on the next6262 start.6363 @@ -28,7 +28,7 @@ mouse and the vim keymap added.
2828 2929 | Key | Action |3030 |---|---|31-| Type | Replace the cell: `=` starts a formula, `'` forces text, and `$1,200`, `12%`, `9/26/2026` or `14:30` are numbers that keep their format ([formulas.md](../formulas/README.md#what-you-type)) |31+| Type | Replace the cell: `=` starts a formula, `'` forces text, and `$1,200`, `12%`, `9/26/2026` or `14:30` are numbers that keep their format ([Formulas](../formulas/README.md#what-you-type)) |3232 | Enter, Tab | Accept and move down or right; Enter goes back to the column a run of Tabs began in |3333 | Enter, F2, double-click | Edit the cell |3434 | Esc | Cancel the entry |@@ -58,7 +58,7 @@ mouse and the vim keymap added.
5858 | Ctrl+D, Ctrl+R | Fill down, fill right; a series started in the top rows (1, 2 over blanks) continues |5959 | Ctrl+Alt+=, Ctrl+Alt+- | Insert rows above, delete the selected rows (columns when whole columns are selected) |6060 61-[data.md](../sheets/editing.md#copy-paste-and-fill) says how formats travel with61+[Editing](../sheets/editing.md#copy-paste-and-fill) says how formats travel with6262 copies and which series the fill handle continues.6363 6464 ## Formatting@@ -89,20 +89,20 @@ The rest of the formats are in the Format menu.
8989 9090 | Key | Action | More |9191 |---|---|---|92-| Ctrl+F, Ctrl+H | Find; find and replace | [data.md](../sheets/find-replace.md) |92+| Ctrl+F, Ctrl+H | Find; find and replace | [Find and replace](../sheets/find-replace.md) |9393 | Alt+Down | Open the active cell's dropdown (type to search, Enter picks); elsewhere, with a filter on (Data > Create a filter), the column's filter (Space checks values, type to search, or pick a condition) | [validation](../sheets/rules.md#data-validation), [filter](../sheets/sort-filter.md#filter) |94-| Space | Check or uncheck the selected checkboxes (Insert > Checkbox); elsewhere Space starts an entry | [data.md](../sheets/rules.md#data-validation) |95-| Alt+, Alt+. | Trace precedents, dependents: highlight the cells a formula reads, or the formulas that read the cell, and jump to the first; again for the next, Esc to go back (Excel's Ctrl+[ and Ctrl+], which terminals send as Esc) | [formulas.md](../formulas/building.md) |96-| Alt+Shift+F | Frequency table of the active column on a new sheet, as VisiData's Shift+F | [data.md](../sheets/pivots.md#frequency-tables) |97-| Shift+F2 | Add or edit the active cell's note; Alt+Enter or Shift+Enter starts a new line | [data.md](../sheets/notes-protection.md#notes) |94+| Space | Check or uncheck the selected checkboxes (Insert > Checkbox); elsewhere Space starts an entry | [Conditional formatting and data validation](../sheets/rules.md#data-validation) |95+| Alt+, Alt+. | Trace precedents, dependents: highlight the cells a formula reads, or the formulas that read the cell, and jump to the first; again for the next, Esc to go back (Excel's Ctrl+[ and Ctrl+], which terminals send as Esc) | [Building formulas](../formulas/building.md) |96+| Alt+Shift+F | Frequency table of the active column on a new sheet, as VisiData's Shift+F | [Pivot tables](../sheets/pivots.md#frequency-tables) |97+| Shift+F2 | Add or edit the active cell's note; Alt+Enter or Shift+Enter starts a new line | [Notes and protection](../sheets/notes-protection.md#notes) |9898 9999 Tools with no key of their own have their keys on screen while they're100100 open, and in their docs: sorting by several columns101-([data.md](../sheets/sort-filter.md#sort)), conditional formatting and data validation101+([Freeze, sort and filter](../sheets/sort-filter.md#sort)), conditional formatting and data validation102102 ([rules panel](../sheets/rules.md#conditional-formatting)), pivot tables103103 ([pivot editor](../sheets/pivots.md)), protected ranges104-([data.md](../sheets/notes-protection.md#protected-sheets-and-ranges)) and charts105-([charts.md](../sheets/charts.md)). In Data > Named ranges, Enter goes to a104+([Notes and protection](../sheets/notes-protection.md#protected-sheets-and-ranges)) and charts105+([Charts](../sheets/charts.md)). In Data > Named ranges, Enter goes to a106106 range, F2 renames or repoints it and Ctrl+D deletes it.107107 108108 ## Files, menus and help@@ -119,7 +119,7 @@ range, F2 renames or repoints it and Ctrl+D deletes it.
119119 120120 | Key | Action |121121 |---|---|122-| Ctrl+Alt+Shift+0 to 9 | Run the macro with that shortcut ([macros.md](../sheets/macros.md#running)) |122+| Ctrl+Alt+Shift+0 to 9 | Run the macro with that shortcut ([Macros](../sheets/macros.md#running)) |123123 | Esc | Stop a macro while it runs (the mode indicator says CMD) |124124 125125 ## Vim keys@@ -127,7 +127,7 @@ range, F2 renames or repoints it and Ctrl+D deletes it.
127127 File > Settings > Vim keys (also `:settings.vim` or the palette) turns128128 on a vim keymap in the spirit of sc-im and VisiData. It is off by129129 default and belongs to you, not the sheet: it's kept in the config file130-as `keymap = vim` ([config.md](config.md#keymap)), never in `.012` files.130+as `keymap = vim` ([Configuration](config.md#keymap)), never in `.012` files.131131 The mode indicator says **NORMAL** where it would say READY, **VISUAL**132132 while selecting with `v` or `V`, and **COMMAND** on the `:` line; typing133133 into a cell is ENTER or EDIT, as always, and Enter, Tab or Esc there@@ -204,12 +204,9 @@ line isn't a command itself (`:fill d` Enter fills down).
204204 | Cmd- or Ctrl-click a link | Open a URL in a cell or a `=HYPERLINK(url, [label])` |205205 | Hover a cell with a note | Show the note beside it |206206 207-The sheet tabs at the left of the status line take the mouse too: click208-a tab to show its sheet, double-click to rename it, right-click for its209-menu (rename, duplicate, delete, hide, move left or right), drag it onto210-another tab to move it there, and click `+` to add a sheet. When the211-tabs don't all fit, `‹` and `›` step through them. While typing a212-formula, clicking a cell or a tab points at it, as in Sheets.207+The sheet tabs take the mouse too: see [sheets and tabs](../sheets/sheets-and-tabs.md).208+While typing a formula, clicking a cell or a tab points at it, as in209+Sheets.213210 214211 The pointer changes shape over cells, resize handles and the formula bar215212 in terminals that support it (OSC 22). In Ghostty and xterm, Shift+click@@ -99,7 +99,7 @@ menus, help, undo or macros. The ids are those in the recorded scripts; a few:
9999 | `column.width` (answer: the width), `column.reset` | Column widths |100100 | `data.sort_sheet_az`, `data.sort_range_az`, `data.sort_range_za`, `data.filter`, `data.filter_remove` | Sorting and filters |101101 | `data.define_name` (answer: the name) | Name the selection |102-| `format.conditional_add`, `data.validation_add` (answer: the rule as a line of the file, see [files.md](../files/format.md#conditional-formats-and-data-validation)), `insert.checkbox`, `format.conditional_clear`, `data.validation_clear`, `data.checkbox_toggle` | Conditional formats, data validation and checkboxes |102+| `format.conditional_add`, `data.validation_add` (answer: the rule as a line of the file, see [The .012 format](../files/format.md#conditional-formats-and-data-validation)), `insert.checkbox`, `format.conditional_clear`, `data.validation_clear`, `data.checkbox_toggle` | Conditional formats, data validation and checkboxes |103103 | `sheet.new`, `sheet.duplicate`, `sheet.rename` (answer: the name), `sheet.delete` (answer: `"enter"` when it asks), `sheet.hide`, `sheet.unhide` (answer: the sheet's name) | Sheets |104104 | `view.freeze_rows1`, `view.freeze_cols1`, `view.freeze_rows0`, ... | Frozen panes |105105 @@ -59,15 +59,8 @@ and the context line says what was undone, e.g. `Undid: clear B3:B5`.
5959 Insert and delete rows and columns from the Insert and Edit menus, the6060 right-click menus, or Ctrl+Alt+= and Ctrl+Alt+- (rows, or columns when whole6161 columns are selected). Formulas, names, charts, filters and widths follow.62-Drag a column header's edge to resize it, or use Format > Column width.63-64-Formatting whole columns or rows (select them with Ctrl+Space or65-Shift+Space, or click their headers) keeps the format on the column or66-row, as Sheets does, rather than on each of their million cells: every67-cell of it shows the format unless it has its own, and a cell typed into68-later takes it. A cell's format comes from the cell, else its row, else69-its column, else the whole sheet's (Ctrl+A twice, then a format); the70-number format and the text style fall back separately.62+Widths and the formats of whole columns and rows are in63+[formatting](formatting.md).7164 7265 ## Links7366 @@ -114,7 +114,7 @@ and mark the file modified, like any edit.
114114 115115 Opening a file never runs its macros. The file remembers which computer116116 its macros were made or trusted on (a random id kept in 012's117-config directory, the one [config.md](../reference/config.md) describes). Running117+config directory, the one [Configuration](../reference/config.md) describes). Running118118 a macro from a file made elsewhere asks once, on the context line:119119 120120 ```@@ -41,7 +41,7 @@ over a rule's colors; its text styles show through them.
4141 4242 Colors are named (red, yellow, green, cyan, blue, magenta) rather than4343 picked from a color wheel, because each is one of the terminal's 16 ANSI44-colors: the terminal's palette or the color scheme ([themes.md](../terminal/themes.md))44+colors: the terminal's palette or the color scheme ([Themes](../terminal/themes.md))4545 decides what they look like, and a fill's text is drawn in whichever of4646 black or white reads on it. A text color that wouldn't read on a rule's4747 fill takes the fill's ink instead. Color scales blend the scheme's colors@@ -38,7 +38,7 @@ there's no command.
3838 | `--host-key` | `<config dir>/012/ssh_host_ed25519_key` | The server's key, generated on first run |3939 | `--idle-timeout` | `30m` | End a session after this long without input, keeping its unsaved changes; `0` never does |4040 | `--max-sessions` | `8` | Sessions at once; more are turned away with a message |41-| `--log`, `--otlp` | off | Telemetry, as for the app ([observability.md](../contributing/observability.md)) |41+| `--log`, `--otlp` | off | Telemetry, as for the app ([Observability](../contributing/observability.md)) |4242 4343 The config directory is `os.UserConfigDir()`: `~/Library/Application4444 Support` on macOS, `~/.config` on Linux. On start, 012 prints the served@@ -194,4 +194,4 @@ check protects the local app from other programs writing the file.
194194 300 B per cell of the sheets it opens; two sessions opening one file195195 hold two copies. With 50 sessions typing at once on loopback, frames196196 still arrive within one frame interval (p95 16.6 ms), the same as with197- 10: see [limits.md](../contributing/limits.md#serving-over-ssh).197+ 10: see [Bounds of support](../contributing/limits.md#serving-over-ssh).@@ -100,7 +100,7 @@ for cell text, text on the bars, the selection, the headers and every
100100 other role (`TestEveryThemeReadable`). Charts, as text and as kitty101101 images, use the scheme's colors too.102102 103-Conditional formats ([data.md](../sheets/rules.md#conditional-formatting)) name103+Conditional formats ([Conditional formatting and data validation](../sheets/rules.md#conditional-formatting)) name104104 their colors (red, yellow, green, cyan, blue, magenta) rather than105105 giving RGB, and each is an ANSI slot, so they follow the theme too: a106106 text color is the slot on the cell (`RuleText`), a fill the slot as a