docs/ becomes getting-started, sheets, formulas, files, terminal, reference and contributing, each with a README.md index and a _category_.json, and every page front matter (title, sidebar_position). data.md splits into editing, sort-filter, find-replace, rules, pivots and notes-protection; formulas.md into the formulas README, references, building, arrays (the arrays and spills, LET and LAMBDA sections) and decimal; files.md into the files README, excel, saving and format; macros.md into sheets/macros and reference/macro-api; charts.md into sheets/charts, the links section of editing and the terminal README. New pages: install (the home for install details), the screen (from keys.md), formatting (formats had no page) and sheets and tabs. Links still point at the old paths; the next commit rewrites them.
- commit
- 6fc38a05ef8d87209a7262a06f420b44d5b2d91d
- parent
- ec9c57c306f55a6312e3472fa555e26384349d99
- author
- Dami <dami.etoile@gmail.com>
@@ -1,25 +1,20 @@
1-# 012 documentation2-3-**Using 012**1+---2+title: "012 documentation"3+sidebar_position: 14+---45 5-- [Keys and mouse](keys.md): every shortcut, the mouse, and what's on screen6-- [Entries and formulas](formulas.md): what you type, references, names, errors7-- [Functions](functions.md): all of them, generated from the engine8-- [Working with data](data.md): selection, copy and fill, undo, freeze, sort, filter, conditional formatting, data validation, pivot tables, find, notes, protection9-- [Charts, links and the terminal](charts.md): charts, hyperlinks, terminal features used10-- [Files](files.md): import and download, Excel files, saving, the `.012` format11-- [JEV functions](jev.md): asking a hosted model from formulas, and storing the API key12-- [Serving over SSH](ssh.md): `012 serve`, setup, the security model and limits13-- [Configuration](config.md): the config file, `012 config` and every option14-- [Themes](themes.md): terminal color schemes, built in or your own15-- [Macros](macros.md): recording, running and managing macros; the Starlark scripting API6+# 012 documentation167 17-**Working on 012**8+| Section | For |9+|---|---|10+| [Getting started](@/getting-started/README.md) | Installing and running 012, and the screen |11+| [Working with sheets](@/sheets/README.md) | Editing, formatting, sheets, sort and filter, find, rules, pivot tables, notes, charts, macros |12+| [Formulas](@/formulas/README.md) | Entries, references, building formulas, arrays, decimal arithmetic, JEV functions |13+| [Files](@/files/README.md) | Import and download, Excel files, saving, the `.012` format |14+| [The terminal](@/terminal/README.md) | Terminal features, themes, serving over SSH |15+| [Reference](@/reference/README.md) | Keys, functions, configuration, the macro API |16+| [Contributing](@/contributing/README.md) | Architecture, extending, the UX bar, testing, limits, observability |1817 19-- [Architecture](architecture.md): how the engine and UI fit together20-- [Extending 012](extending.md): the patterns new features and scale follow21-- [UX and visual bar](UX.md): the rules every change follows22-- [Testing](testing.md): unit, end-to-end through libghostty, golden screens, the excelize oracle23-- [Limits](limits.md): measured bounds of support and known bottlenecks24-- [Observability](observability.md): event logs, OTLP export, DuckDB queries, the local telemetry stack25-- [Roadmap](../ROADMAP.md)18+The [roadmap](@@/ROADMAP.md) says what's ahead. The docs are Markdown that19+reads on GitHub and builds with Docusaurus: each folder has a20+`README.md` and a `_category_.json`, and each page front matter.@@ -0,0 +1,20 @@
1+---2+title: "Contributing"3+sidebar_position: 14+---5+6+# Contributing7+8+How 012 is built and the bar every change meets. Start with the rules9+in [CLAUDE.md](@@/CLAUDE.md), then:10+11+| Page | For |12+|---|---|13+| [Architecture](@/contributing/architecture.md) | How the engine and UI fit together |14+| [Extending 012](@/contributing/extending.md) | The patterns new features and scale follow |15+| [UX and visual bar](@/contributing/ux.md) | The rules every change follows |16+| [Testing](@/contributing/testing.md) | Unit, end-to-end through libghostty, golden screens, the oracle, docs checks, demos |17+| [Bounds of support](@/contributing/limits.md) | Measured bounds and known bottlenecks |18+| [Observability](@/contributing/observability.md) | Event logs, OTLP export, DuckDB queries, the local telemetry stack |19+20+Plans are in the [roadmap](@@/ROADMAP.md).@@ -1,303 +0,0 @@
1-# Working with data2-3-## Selection4-5-Shift+arrows, a mouse drag or Shift+click select a range; Ctrl+arrows jump6-to the edge of the data (add Shift to select as you go); Ctrl+A selects the7-data around the active cell, then everything; Ctrl+Space and Shift+Space8-select whole columns and rows. The active cell stays distinct from the rest9-of the selection, and the status line shows Sum, Avg and Count, dropping the10-ones that don't fit on a narrow terminal.11-12-13-14-## Copy, paste and fill15-16-Ctrl+C, Ctrl+X and Ctrl+V work as in Sheets: relative references shift,17-absolute ones don't, cut and paste moves cells and the formulas that point18-at them follow. Ctrl+Shift+V pastes values only, keeping the destination's19-formats.20-21-Formats travel with the cells. A pasted or moved cell shows what its22-source showed, whether the format was the cell's own or came from its23-row, its column or the whole sheet, and pasting plain cells into a24-currency column leaves them plain. Whole columns or rows (Ctrl+Space,25-Shift+Space) copied and pasted at the top of a column, or the start of a26-row, take their column or row formats along, so a pasted column is27-currency all the way down, not only where it had data; cut and pasted,28-they move them, leaving the source columns plain. Pasted anywhere else,29-they paste as a block of the cells that hold something. A block cut and30-pasted leaves the cells it came from plain, as Sheets does, even where31-a formatted column or row crosses them. Vim's `yy` and `dd` copy whole32-rows, so `p` and `P` bring their row formats along. Formulas reading33-a column whose format changes, blank cells included, show the new format34-at once (`=B5*2` shows currency when column B becomes currency). Copies also go to the35-system clipboard as tab-separated text (OSC 52, so it works over SSH), and36-pasting tab-separated or multi-line text from the terminal fills a block.37-38-Ctrl+D and Ctrl+R fill down and right. The fill handle, shown when you hover39-the corner of the selection, can be dragged to continue a series: 1, 2, 3;40-2, 4, 6; Jan, Feb; Mon, Tue; dates by day or month; Item 1, Item 2. Ctrl+Enter41-while typing enters the same entry into every selected cell.42-43-## Undo44-45-Ctrl+Z undoes and Ctrl+Y or Ctrl+Shift+Z redoes, 100 steps deep, across46-every sheet: undo shows the sheet a step changed. Every change is covered:47-typing, clearing, paste, fill, sort, insert and delete, formats, widths,48-names, charts, freeze, filters, pivot tables, and adding, deleting,49-renaming, moving and duplicating sheets. A multi-cell change is one step,50-and the context line says what was undone, e.g. `Undid: clear B3:B5`.51-52-## Rows and columns53-54-Insert and delete rows and columns from the Insert and Edit menus, the55-right-click menus, or Ctrl+Alt+= and Ctrl+Alt+- (rows, or columns when whole56-columns are selected). Formulas, names, charts, filters and widths follow.57-Drag a column header's edge to resize it, or use Format > Column width.58-59-Formatting whole columns or rows (select them with Ctrl+Space or60-Shift+Space, or click their headers) keeps the format on the column or61-row, as Sheets does, rather than on each of their million cells: every62-cell of it shows the format unless it has its own, and a cell typed into63-later takes it. A cell's format comes from the cell, else its row, else64-its column, else the whole sheet's (Ctrl+A twice, then a format); the65-number format and the text style fall back separately.66-67-## Freeze68-69-View > Freeze keeps rows or columns on screen while the rest scrolls, marked70-by thin divider lines. The mouse and keys treat the frozen area as Sheets71-does.72-73-## Sort74-75-Data > Sort sheet sorts by the active column A to Z or Z to A, keeping frozen76-rows on top. Data > Sort range sorts the selection, or the block of data77-around the active cell; its advanced option opens a bar to sort by several78-columns (Left/Right picks a column, Space flips the order, Alt+A adds a79-column, Alt+H toggles the header row). The order follows Sheets: numbers,80-then text ignoring case, then booleans, then errors, with blanks last.81-82-## Filter83-84-Data > Create a filter puts a filter on the selection or the table around85-the active cell; headers show `▾`. Alt+Down or a click on `▾` opens the86-column's filter: check values in a list (Space checks one, typing87-narrows the list), or pick a condition such as "greater than" or "text88-contains". Filtered-out rows are hidden, not89-deleted: row numbers show the gap, navigation skips them, formulas still90-count them, and the status line says how many rows are hidden.91-92-## Conditional formatting93-94-Format > Conditional formatting opens the sheet's rules in a panel at the95-right of the grid, as Sheets' sidebar: `+ Add rule` adds one over the96-selection, Enter edits the highlighted rule, Del removes it and97-Shift+Up/Down moves it. In a rule's form, Up/Down pick a line,98-Left/Right change a choice, Space flips a check, text lines are typed99-into, Enter saves and Esc goes back without saving. Every change is an100-undo step.101-102-103-104-A rule applies to ranges (`A2:A100`, or several: `A2:A9,C2:C9`) and is105-one of:106-107-- **Single color**: cells that meet a condition get a text color, a fill,108- and bold, italic, underline or strikethrough. The conditions are109- Sheets': is empty, is not empty, text contains, does not contain,110- starts with, ends with, is exactly (ignoring case), date is, is before,111- is after (a date, or today, tomorrow, yesterday), greater than, greater112- than or equal to, less than, less than or equal to, is equal to, is not113- equal to, is between, is not between, and custom formula is. A value114- may be a formula (`=B2`), and a custom formula (`=$C2>100`) is written115- for the first cell of the first range: its relative references move116- with each cell, as a copied formula's would.117-- **Color scale**: every number in the ranges is shaded along two or three118- colors, from its minimum to its maximum; each point is the minimum or119- maximum value, a number, a percent of the way between them, or a120- percentile, and a midpoint is optional.121-122-Rules are tried in order, and the first that applies to a cell formats123-it, so a rule higher in the list wins. The pointer and the selection draw124-over a rule's colors; its text styles show through them.125-126-Colors are named (red, yellow, green, cyan, blue, magenta) rather than127-picked from a color wheel, because each is one of the terminal's 16 ANSI128-colors: the terminal's palette or the color scheme ([themes.md](themes.md))129-decides what they look like, and a fill's text is drawn in whichever of130-black or white reads on it. A text color that wouldn't read on a rule's131-fill takes the fill's ink instead. Color scales blend the scheme's colors132-(or the terminal's, as it reports them) and pick readable text for each133-shade.134-135-Rules only change how cells look, so they may go on a pivot table's136-results. They follow inserted and deleted rows and columns, and a rule137-whose cells are all deleted goes. Format > Clear conditional formats138-takes the selection out of every rule.139-140-## Data validation141-142-Data > Data validation opens the sheet's validation rules in the same143-panel; Insert > Dropdown opens it with a dropdown for the selection, and144-Insert > Checkbox makes the selection checkboxes at once. The criteria:145-146-| Criteria | Cells may hold | Shows |147-|---|---|---|148-| Dropdown | one of the items typed (`Yes, No, Maybe`), ignoring case | ▾ at the right of the cell |149-| Dropdown (from a range) | one of the values in a range, on any sheet (`Lists!A1:A20`) | ▾ |150-| Checkbox | TRUE or FALSE; a blank cell is unchecked | `[✓]` or `[ ]`, centered |151-| Number | a number between, not between, equal to, greater than ... values | |152-| Date | any date, or one on, after, before, between ... dates | |153-| Text length | text whose length is between, less than ... | |154-| Custom formula is | anything for which the formula (`=B2<=C2`) is TRUE | |155-156-Alt+Down, or a click on the ▾, opens a dropdown's list under the cell:157-type to search, Enter enters the item as if typed. Space, or a click on158-the box, checks or unchecks the selected checkboxes (all checked when159-the active one isn't, as in Sheets); Space anywhere else starts an entry160-as usual. When the active cell has no dropdown, Alt+Down opens the161-column's filter.162-163-Each rule either **rejects** an invalid entry or **shows a warning**.164-Rejected, the entry stays open with Sheets' message on the context line165-("Invalid entry in C2: Input must be a number between 0 and 80"), for166-fixing or Esc. Warned, it goes in, and the cell's text gets a dotted167-underline; on the active cell the context line says why ("Invalid:168-Input must be ..."). A rule's help text, when it has one, replaces its169-own words there. A formula is checked by the value it computes. Blanks170-are always valid.171-172-Each cell has one rule: a rule added over cells that had another takes173-them from it. Validation checks what's typed, picked from a dropdown and174-entered by macros; pastes, fills and imports aren't refused, and what175-they leave invalid is marked. Validation can't go on a pivot table's176-results, which take no entries. Data > Remove data validation takes the177-selection out of every rule.178-179-Adding a checkbox or a dropdown changes no cells, so on a protected range180-it doesn't ask first (on a protected sheet it does); checking a box or181-picking an item asks as typing does ([protection](#protected-sheets-and-ranges)).182-183-## Pivot tables184-185-186-187-Data > Pivot table summarizes the selection, or the table around the188-active cell, on a new sheet named `Pivot Table 1`, as Sheets' Insert >189-Pivot table does. The data's first row names the fields. The pivot editor190-opens docked at the right of the grid, with the results updating behind it:191-192-- **Rows** group the data by a field's values, one row per value; with two193- or more, the groups nest and each outer group gets a subtotal row.194-- **Columns** spread the groups across columns by a field's values; with195- two or more, each outer group gets a subtotal column (`East Total`)196- after its last column.197-- **Values** summarize a field for each group: SUM, COUNTA, COUNT,198- COUNTUNIQUE, AVERAGE, MAX or MIN (Sheets' "Summarize by"), shown as they199- are or as a share of their row, column or grand total ("Show as").200- A new value is summed when its field holds numbers, and counted201- otherwise. Its header is Sheets' `SUM of Units` until you rename it.202-- **Filters** leave out rows by a field's values or a condition, in the203- same picker as a filter's column.204-- **Grand total row** and **Grand total column** add Sheets' totals, and205- turn the subtotal rows and columns on and off with them.206-207-Up and Down pick a line. Space adds a field to the section it's on (pick208-it from the fields of the data, type to narrow the list), opens a filter's209-values, or flips a toggle; Space on the data range points at a new one on210-its sheet. On a row or column field, Left and Right order its groups A to211-Z, Z to A, or by a value's total, smallest or largest first, and212-Shift+Up and Shift+Down move it before or after the others, which changes213-how the groups nest. On a value, Left and Right change how it's214-summarized, S how it's shown, and R (or F2) renames it on the context215-line, the name heading its columns; an empty name goes back to Sheets'.216-A value keeps its name when its summary changes. Del removes a field. Every change is an217-undo step; Enter keeps them, Esc undoes them, and removes a pivot just218-created. Data > Edit pivot table opens the editor again.219-220-Groups follow Sheets: text ignoring case (`east` joins `East`), numbers221-and dates by value, with the source's format, so dates show as dates.222-Numbers and text never share a group: `1` and the text `'1` are two.223-Groups sort numbers first, then text, booleans and errors, and blank224-cells last; as in Sheets, the group of blank cells has a blank label,225-and its subtotal is just `Total`. Sorting an outer column field by a226-value's total orders whole outer groups, so their columns stay227-together. Rows blank across the whole range are228-left out, so a range can reach past the data. SUM, AVERAGE, MIN and MAX229-keep the column's number format: the column's own when it has one, else230-the format most of its numbers show in (ties go to the first), so one231-price typed as `$9,000` among `$2.50`s doesn't turn every total into232-whole dollars. A group's label shows in the format of its first cell. An233-error in a summed column shows as that error, as SUM would.234-235-The results are live: any change to the data, typed, pasted, filled or236-computed by a formula, recomputes the pivot, and rows or columns inserted237-inside the range widen it. Formulas can read the results (`='Pivot Table238-1'!B5`), and copying them pastes their values. The results themselves239-can't be edited, as in Sheets: typing, pasting, clearing, formatting,240-sorting or inserting rows or columns over them is refused with a note on241-the context line. A pivot shows `#REF!` in A1, explained on the context242-line, when its data's sheet is deleted or when its results would243-overwrite a cell typed next to them; undo or moving the cell brings it244-back. The sheet's columns widen to fit the results until you set a width245-yourself.246-247-## Frequency tables248-249-Data > Frequency table (column stats), or Alt+Shift+F (VisiData's250-Shift+F; plain Shift+F types an F), counts each value of the active251-column of the table around the active cell on a new sheet, `Frequency of252-<field>`: the value, how many rows have it and their percentage, most253-frequent first, with a Grand Total. Blank cells are counted too. It is a254-pivot table like any other, so it stays live, and the pivot editor255-changes it.256-257-## Find and replace258-259-Ctrl+F opens a find bar on the context line: matches highlight as you type,260-the active cell follows the current one, and Enter and Shift+Enter step261-through them. Ctrl+H adds a replacement field; Enter replaces and moves on,262-Ctrl+Enter or Alt+A replaces all as one undo step. Chips toggle match case (Alt+C),263-whole cell (Alt+W), regular expressions (Alt+R, with `$1` in replacements),264-searching formulas (Alt+=). The scope chip says where to search, as Sheets'265-"Search" choice: this sheet, all sheets, or the range selected when the bar266-opened; Alt+S goes through them. Searching all sheets steps from sheet to267-sheet in tab order, and replacing all across sheets is still one undo step.268-269-## Notes270-271-Insert > Note (Shift+F2, or Insert note on the right-click menu) adds a272-note to the active cell, as Sheets' notes: plain text typed on the context273-line, where Alt+Enter or Shift+Enter starts a new line (shown as ↵), and274-an empty note deletes it. A cell with a note shows ▝ in its top-right275-corner; while it is the active cell, the context line shows the note, and276-while the mouse is over it, a box beside it does. Delete notes (on the277-right-click menu) removes the notes of the selection.278-279-A note belongs to its cell: clearing the contents (Del) keeps it, it moves280-with the cell when rows or columns are inserted, deleted or sorted, and a281-copy or a cut takes it along, as in Sheets. Ctrl+Enter fills the entry282-into the selection and leaves each cell's note alone. Every change to a283-note is an undo step. Pivot table results can't take notes.284-285-## Protected sheets and ranges286-287-Data > Protect sheets and ranges works as Sheets' protection with "Show a288-warning when editing this range": it locks nothing, but an edit that289-touches a protected range, or anything on a protected sheet, asks first on290-the context line: Enter edits anyway, Esc cancels. The picker lists the291-sheet's protections; its first rows protect the selection or the whole292-sheet, with an optional description, Enter on a protection selects its293-range, and Ctrl+D removes it. Protect range and Remove protection on the294-right-click menu and in the palette do the same for the selection.295-296-What asks: typing into a cell, commands that change cells (clear, paste,297-fill, formats, sort, delete rows), pasting text from the terminal and the298-fill handle. Inserting rows or columns moves a protected range without299-asking, unless the whole sheet is protected. Macros that run don't ask, as300-Sheets' scripts don't. Protected ranges follow inserted and deleted rows301-and columns, save with the sheet, and adding or removing one is an undo302-step.303-@@ -1,353 +0,0 @@
1-# Files2-3-Spreadsheets save as `.012` files: JSON with one line per cell, every4-sheet in one file. Other formats come in through File > Import, File >5-Open or the command line (`012 sales.xlsx`), and go out through File >6-Download.7-8-9-10-## Import and download11-12-Imports run in the background with a progress bar; Esc cancels. Saving13-an imported sheet asks whether to save it as a `.012` file or download14-it back in its format.15-16-| Format | Import | Download |17-|---|---|---|18-| CSV, TSV | Delimiter (`,` `;` tab `\|`), UTF-8 BOM, UTF-16 and Windows-1252 detected; entries become numbers, dates, currency and percentages as if typed; formulas stay text | Values as shown, of the sheet shown, as Sheets' Download does; a selection of whole columns or rows downloads their data, not a million blank lines |19-| Excel `.xlsx` | Every sheet, with formulas, formats and most of what a sheet holds: see [Excel files](#excel-files) | The same |20-| SQLite | Pick a table or view, or type a query; a header row names the columns | The sheet shown or the selection as a table, first row as column names; a table of that name is replaced |21-| Parquet | Every column, with dates and timestamps; lists joined with commas | |22-| Lotus 1-2-3 `.wk1`, `.wks` | Numbers, labels with their alignment, formats, column widths, formulas translated (references, operators, `@SUM`, `@AVG`, `@IF`, `@ROUND`, `@PMT` and 60 more) or kept as values | |23-24-Formats other than XLSX import as one sheet named after the file (or the25-SQLite table).26-27-File > Import asks where the data goes, as Sheets' Import location does28-(a new, empty spreadsheet is simply replaced):29-30-| Location | Does |31-|---|---|32-| Insert new sheet(s) | Adds the file's sheets after the sheet shown. A name already taken gets a number (`Sales 2`) and the file's formulas follow it; named ranges come along unless their name is taken. One undo step, and the spreadsheet stays the file you're editing |33-| Replace current sheet | Puts the data in place of the sheet shown, keeping its name and position, so formulas and named ranges that read it read the new data. One undo step. Not offered for `.xlsx`, which holds several sheets |34-| Replace spreadsheet | Opens the file instead, as File > Open and the command line do, asking first when there are unsaved changes |35-36-Replacing the current sheet keeps the charts that fit the new data. A37-chart that drew a whole table is re-pointed to the table the file has at38-the same corner when it has as many columns (rows for a chart by row),39-so last month's chart draws this month's rows; a chart of part of a40-table, or of whole columns or rows, keeps its range. A chart whose table41-now has other columns, or whose range is empty, is removed. The context42-line lists the removed charts first, then which were re-pointed and43-which kept their range, and undo brings the removed ones back.44-45-**Size.** A sheet is 1,048,576 rows by 16,384 columns (A to XFD), as in46-Excel. Imports keep at most `max-cells` cells ([config.md](config.md#max-cells),47-two million by default, about 600 MB): whole rows, as many as fit, and48-the context line says how many rows were left out, e.g. `only the first49-166,666 rows fit in max-cells (2,000,000 cells); 12,000 rows left out`.50-Data past the grid's edges is left out the same way. WK1 files keep their51-own 8,192 by 256. See [limits.md](limits.md#imports) for speeds.52-53-## Excel files54-55-**Read:** every sheet, opening on the one Excel showed; values, formulas56-(references between sheets too, shared formulas), workbook named ranges,57-number formats, bold, italic, underline, strikethrough, alignment,58-column widths, column and row styles, frozen panes, [filters](#filters-in-excel),59-notes (Excel's notes, its legacy comments; threaded comments aren't60-read), [conditional formatting and data validation](#rules-in-excel),61-[array formulas](#arrays-in-excel),62-dates in the 1904 system, and hidden sheets (unless it's the one Excel63-showed).64-65-**Written:** the same, with formulas in Excel's syntax and their results66-cached, column and row formats as column and row styles, frozen rows and67-columns as frozen panes, a filter as Excel's with the rows it hides68-hidden, notes as Excel's notes (which Sheets reads as notes), and69-formulas that spill as Excel 365's [dynamic array formulas](#arrays-in-excel).70-71-What changes on the way:72-73-| | |74-|---|---|75-| Formulas 012 can't read (unknown functions) | Keep their values |76-| Excel pivot tables | Come in as the values they showed; 012's go out as their results, not a pivot |77-| Excel's sheet protection | Comes in unprotected, with a note saying so: Excel's protection locks cells where 012's only warns. Protected ranges aren't written |78-| Array formulas (Excel 365's dynamic arrays and older `{=...}` ones) | Come in as formulas that spill again, without the values Excel kept in the cells they spill into |79-| Functions only Sheets has (`SORTN`, `FLATTEN`, `SPLIT`, `REGEXMATCH`, `REGEXEXTRACT`) | Go out as values, counted with the others below |80-| JEV functions, `#AND#`, formulas naming a sheet that doesn't exist | Go out as values (their `#REF!`, for a missing sheet: Excel would refuse the reference). The download's result counts the formulas saved as values, with an example |81-| A sheet name Excel can't take as is (spaces at its ends, or the same as another's but for them and case) | Written as one it can: without the spaces, with a number when two would clash (`Plan (2)`); formulas and named ranges naming it name that |82-| Files past the reader's limits (a zip bomb, 1 GB in one part, 2 GB in all, cells past XFD1048576) | Refused, with a message saying which |83-84-### Filters in Excel85-86-A sheet's filter goes out as Excel's AutoFilter over the same range, and87-an AutoFilter comes in as a filter:88-89-| 012 | Excel |90-|---|---|91-| Filter by values: the values unchecked | The list of values shown (Excel keeps those checked), with blanks when they're shown. Values are compared as displayed, ignoring case |92-| Is empty | Blanks only |93-| Is not empty | Custom filter: does not equal a space |94-| Text contains, does not contain, starts with, ends with, is exactly | Custom filter: equals or does not equal `*text*`, `text*`, `*text`, `text`, with Excel's wildcards in the text escaped (`~*`) |95-| Greater than, less than, is equal to, is not equal to (and or equal to) | Custom filter with that operator; numbers typed as `$1,200` or `12%` go out as the number |96-97-The rows the filter hides are written hidden, since Excel shows a file's98-rows as saved rather than filtering again. A column with both unchecked99-values and a condition goes out as the list of values both let through,100-and comes back as that list. What 012's filters can't do is left out,101-with a note saying how many criteria and which: two conditions in one102-column (`and`, `or`), wildcards other than at the ends of the text (`a?c`,103-`a*c`), top 10, dynamic filters (above average, this month), dates104-grouped by year or month, and filtering by color or icon. The filter105-itself still comes in over its range. Filters of Excel tables (as106-opposed to the sheet's AutoFilter) aren't read.107-108-### Arrays in Excel109-110-A formula whose array spills (see [formulas.md](formulas.md#arrays-and-spills))111-is written as Excel writes a dynamic array formula: an array formula over112-the cells it spills into (`<f t="array" ref="C1:C9">`) on a cell whose113-metadata marks it dynamic (`cm="1"`, defined in `xl/metadata.xml`), with114-the values in the cells below and right of it, so Excel spills it the115-same and older Excels show the values. Formulas calling array functions116-(`FILTER`, `SORT`, `UNIQUE`, `SEQUENCE`, `LET`, `LAMBDA` and the like) or117-holding an array literal are written that way even when they compute one118-value, so Excel doesn't put its implicit intersection (`@`) in front of119-them. Excel gets its own names for the functions newer than Excel 2007120-(`_xlfn._xlws.FILTER`, `_xlfn.SEQUENCE`), `_xlpm.` before the names LET121-and LAMBDA bind, and the formula inside an `ARRAYFORMULA` around a whole122-formula, which a dynamic array formula computes over arrays anyway.123-What Excel can't hold goes out as the value it showed, counted in the124-download's note: functions only Sheets has (`SORTN`, `FLATTEN`, `SPLIT`,125-`REGEXMATCH`, and `REGEXEXTRACT`, whose Excel namesake returns the whole126-match where Sheets' returns the capture group), array literals holding127-references (Excel's hold only constants) and `ARRAYFORMULA` inside a128-formula.129-130-### Rules in Excel131-132-[Conditional formats](data.md#conditional-formatting) go out as Excel's133-conditional formatting, and [data validation](data.md#data-validation)134-as its data validation, and both come back:135-136-| Conditional format | Excel |137-|---|---|138-| Is empty, is not empty | `containsBlanks`, `notContainsBlanks` |139-| Text contains, does not contain, starts with, ends with | `containsText`, `notContainsText`, `beginsWith`, `endsWith` |140-| Text is exactly | `cellIs` equal to the text |141-| Date is today, tomorrow, yesterday | `timePeriod` |142-| Date is, is before, is after a date | an expression, `INT(A1)<DATE(2026,9,30)` |143-| Greater than ... is not between | `cellIs` with the operator; dates as Excel's serial numbers |144-| Custom formula is | `expression`, in Excel's syntax |145-| Color scale | `colorScale` with its points (`min`, `max`, `num`, `percent`, `percentile`) |146-| A rule's style | a differential style (dxf): bold, italic, underline, strikethrough, the text color and a solid fill |147-148-| Data validation | Excel |149-|---|---|150-| Dropdown, from a range | `list` of the items in quotes, or of the range (`Lists!$A$1:$A$20`) |151-| Checkbox | `list` of `TRUE,FALSE`, which comes back as a checkbox |152-| Number, date, text length | `decimal`, `date`, `textLength` with the operator; any date as a date greater than 0 |153-| Custom formula is | `custom` |154-| Reject the input, show a warning | error style `stop`, `warning`; help text as the prompt |155-156-Each single-color rule stops the ones after it (`stopIfTrue`) and its157-priority is its place, so Excel applies the first that matches, as 012158-does. Named colors go out in the colors of Sheets' palette, and Excel's159-colors (ARGB, theme and indexed) come in as the nearest named color by160-hue, grays as none; a color scale's gray or white point takes the nearest161-scale color. Validation in Excel 2010's extension (lists from other162-sheets) is read too; whole-number rules come in as number rules.163-164-Left out, with a note saying how many and which: conditional formats 012165-has no rule for (data bars, icon sets, top or bottom values, above or166-below average, duplicate or unique values, errors, date periods other167-than today, tomorrow and yesterday, and what Excel 2010's extension168-holds), rules whose style has only a number format or borders, time169-validation and lists from named ranges or formulas. Going out, a170-formula with no Excel equivalent (`#AND#`) leaves its rule out, and so171-do list items with commas or quotes, or longer than Excel's 255172-characters.173-174-## Saving175-176-Saves are atomic: 012 writes a temporary file and renames it. Saving over177-the open file when something else wrote it since it was opened or last178-saved (another program, or another session of [012 serve](ssh.md)) asks179-first: Enter overwrites, S saves under another name, Esc cancels. Save180-as (and `:w name`, `:wq name` with vim keys) onto another file that181-exists asks the same way: Enter replaces it, Esc cancels, and cancelling182-`:wq`'s question cancels its quit too.183-184-Under `012 serve`, a session that idles out or is ended by the server185-stopping keeps its unsaved changes in `.012-recovery/` in the served186-directory, and the next session opening that file offers them back: see187-[ssh.md](ssh.md#unsaved-work).188-189-## The .012 format190-191-`.012` files are JSON with one line per cell, keyed by address, so diffs192-read naturally and files merge reasonably in version control. A cell193-without formatting is just what was typed; a formatted cell is a small194-object:195-196-```json197-{198- "version": 2,199- "widths": {"A": 14},200- "cells": {201- "A1": "Rent",202- "B1": {"input":"1450","format":"currency","decimals":2},203- "B2": {"input":"95","note":"Due on the 1st"},204- "B3": "=SUM(B1:B2)"205- }206-}207-```208-209-A formatted cell has its `input` and the formatting that isn't the210-default: `format` (as `number_format` in [macros](macros.md#cells) names211-it: `currency`, `percent`, `date`, ...), `decimals`, `pattern` for a212-custom format, `bold`, `italic`, `underline`, `strikethrough` and213-`align` (`left`, `center`, `right`).214-215-### Versions216-217-012 writes the lowest version that holds the workbook, so older builds218-open what they can, and refuses a version newer than it knows rather219-than lose sheets or pivots. Version 1 files (cells as plain strings)220-still load.221-222-| Version | Written when | Adds |223-|---|---|---|224-| 2 | One sheet, using none of the below | `cells`, `widths` |225-| 3 | A sheet has named ranges, frozen panes or a filter | `names` (each name's range, `#REF!` once its cells were deleted), `freeze` (`rows`, `cols`), `filter` (its `range`, and `columns` by letter, each with `hidden` values or a `condition` and its `value`) |226-| 4 | Several sheets, or a formula naming a sheet | `sheets`, a list; see [Several sheets](#several-sheets) |227-| 5 | A pivot table | a sheet's `pivot`; see [Pivot tables](#pivot-tables) |228-229-### Fields that need no version230-231-Builds that don't know these fields ignore them (and drop them if they232-save), so they raise no version:233-234-| Field | On | Holds |235-|---|---|---|236-| `note` | a cell | Its [note](data.md#notes) |237-| `own` | a cell | `true` when its formatting is its own, not its column's or row's (Automatic in a currency column) |238-| `lines` | a sheet | [Column and row formats](#column-and-row-formats) |239-| `name`, `hidden` | a sheet | Its name when renamed; `true` when hidden (a file whose sheets are all hidden opens with the first one shown) |240-| `charts` | a sheet | One chart per line: `type` (`column`, `bar`, `line`, `pie`, `area`, `scatter`), `data`, `at`, `width`, `height`, `byRow`, `header`, `labels`, `title`, and options left out at their defaults: `stack` (`stacked`, `percent`), `trend`, `min`, `max`, `log`, `gridlines` (only when off), `legend` (`right`, `none`). A build that charts but lacks a chart's type refuses the file |241-| `protected` | a sheet | [Protected ranges](data.md#protected-sheets-and-ranges): `{"range":"B2:C9","description":"Totals"}`, or `{"sheet":true}` |242-| `conditionalFormats`, `validations` | a sheet | [Rules](#conditional-formats-and-data-validation), one per line |243-| `arithmetic` | the workbook | `decimal` for [decimal arithmetic](formulas.md#decimal-arithmetic) |244-| `macros`, `macroOrigin` | the workbook | Macros as Starlark scripts, and the computer they were made or trusted on: see [macros.md](macros.md#in-the-file). Opening a file never runs them |245-246-### Column and row formats247-248-[Formats of whole columns and rows](data.md#rows-and-columns) are kept249-in a `lines` field after the widths, runs of lines with the same250-formatting together; `A:XFD` is the whole sheet's format:251-252-```json253- "lines": {"A:XFD": {"italic":true}, "B:D": {"format":"currency","decimals":2}, "1:1": {"bold":true}},254-```255-256-### Several sheets257-258-In version 4 the sheets are a list, each with its name and the fields a259-version 3 file has at the top, and named ranges say their sheet. The260-workbook's settings (named ranges, decimal arithmetic, the sheet shown261-when saved as `active`) stay at the top:262-263-```json264-{265- "version": 4,266- "names": {"Rent": "Budget!B1"},267- "active": 1,268- "sheets": [269- {270- "name": "Budget",271- "cells": {272- "A1": "Rent",273- "B1": "1450"274- }275- },276- {277- "name": "Q3 plan",278- "hidden": true,279- "cells": {280- "A1": "=Budget!B1*3"281- }282- }283- ]284-}285-```286-287-### Pivot tables288-289-A pivot's sheet has a `pivot` field, on one line after its cells,290-holding the definition. The results are never saved; they are computed291-again when the file opens, so the file stays small and can't disagree292-with its data.293-Arrays that formulas spill are kept the same way: the file keeps the formula,294-and the cells it spills into only for their formatting and notes.295-296-```json297-{298- "name": "Pivot Table 1",299- "cells": {},300- "pivot": {"source":"Sales!A1:D200","rows":[{"column":"B"},{"column":"A","order":"desc","sortBy":1}],"columns":[{"column":"C"}],"values":[{"column":"D","summarize":"sum"},{"column":"D","summarize":"counta","showAs":"percent_of_total","name":"Share"}],"filters":[{"column":"A","hidden":["North"]}],"rowTotals":true,"columnTotals":false}301-}302-```303-304-- `source` is the data, with its sheet; `Sales!#REF!` once the range was305- deleted. The sheet is by name, as in formulas, and follows renames.306-- `rows` and `columns` are fields by column letter on the source sheet,307- with `order` `desc` for Z to A and `sortBy` the value, counting from 1,308- whose totals order the groups.309-- `values` summarize a column: `sum`, `counta`, `count`, `countunique`,310- `average`, `max`, `min`, or `rows` (every row, blank or not, which311- frequency tables use); `showAs` is `percent_of_row`,312- `percent_of_column` or `percent_of_total`; `name` replaces the header.313-- `filters` take a filter column's criteria: `hidden` values and a314- `condition` with its `value`.315-- `rowTotals` and `columnTotals` are the grand total row and column.316-317-### Conditional formats and data validation318-319-A sheet's rules are two lists after its cells and charts, one rule per320-line:321-322-```json323- "conditionalFormats": [324- {"ranges":"A2:A6","condition":"formula","values":["=$D2"],"text":"green","strikethrough":true},325- {"ranges":"C2:C6","scale":[{"type":"min","color":"green"},{"type":"percentile","value":"50","color":"yellow"},{"type":"max","color":"red"}]}326- ],327- "validations": [328- {"ranges":"B2:B6","criteria":"list","items":["Ann","Bo","Cy"],"help":"Pick who does it"},329- {"ranges":"D2:D6","criteria":"checkbox"},330- {"ranges":"C2:C6","criteria":"number","condition":"between","values":["0","80"],"reject":true}331- ]332-```333-334-- `ranges` are the rule's ranges, `A2:A9,C2:C9`.335-- A single-color rule has a `condition` (`empty`, `not_empty`, `contains`,336- `not_contains`, `starts_with`, `ends_with`, `exactly`, `date_is`,337- `date_before`, `date_after`, `gt`, `ge`, `lt`, `le`, `eq`, `ne`,338- `between`, `not_between`, `formula`), its `values` as typed, and its339- style: `text` and `fill` colors (`red`, `yellow`, `green`, `cyan`,340- `blue`, `magenta`) and `bold`, `italic`, `underline`, `strikethrough`.341-- A color scale has `scale`: two or three points, each a `type` (`min`,342- `max`, `num`, `percent`, `percentile`), a `value` where it takes one,343- and a `color`.344-- A validation rule has `criteria` (`list`, `range`, `checkbox`,345- `number`, `date`, `length`, `formula`) with its `items`, `source`346- (`Lists!A1:A20`), or `condition` (`between`, `not_between`, `eq`, `ne`,347- `gt`, `ge`, `lt`, `le`; none for any date) and `values`; `reject` refuses348- invalid entries, and `help` replaces the rule's own help text.349-350-A line of either list is also what Add conditional format rule and Add351-data validation rule take (in the palette, and as `run(...,352-answer=...)` in macros), which is how a macro records a rule added from353-the panel.@@ -0,0 +1,64 @@
1+---2+title: "Files"3+sidebar_position: 14+---5+6+# Files7+8+Spreadsheets save as `.012` files: JSON with one line per cell, every9+sheet in one file. Other formats come in through File > Import, File >10+Open or the command line (`012 sales.xlsx`), and go out through File >11+Download.12+13+14+15+## Import and download16+17+Imports run in the background with a progress bar; Esc cancels. Saving18+an imported sheet asks whether to save it as a `.012` file or download19+it back in its format.20+21+| Format | Import | Download |22+|---|---|---|23+| CSV, TSV | Delimiter (`,` `;` tab `\|`), UTF-8 BOM, UTF-16 and Windows-1252 detected; entries become numbers, dates, currency and percentages as if typed; formulas stay text | Values as shown, of the sheet shown, as Sheets' Download does; a selection of whole columns or rows downloads their data, not a million blank lines |24+| Excel `.xlsx` | Every sheet, with formulas, formats and most of what a sheet holds: see [Excel files](#excel-files) | The same |25+| SQLite | Pick a table or view, or type a query; a header row names the columns | The sheet shown or the selection as a table, first row as column names; a table of that name is replaced |26+| Parquet | Every column, with dates and timestamps; lists joined with commas | |27+| Lotus 1-2-3 `.wk1`, `.wks` | Numbers, labels with their alignment, formats, column widths, formulas translated (references, operators, `@SUM`, `@AVG`, `@IF`, `@ROUND`, `@PMT` and 60 more) or kept as values | |28+29+Formats other than XLSX import as one sheet named after the file (or the30+SQLite table).31+32+File > Import asks where the data goes, as Sheets' Import location does33+(a new, empty spreadsheet is simply replaced):34+35+| Location | Does |36+|---|---|37+| Insert new sheet(s) | Adds the file's sheets after the sheet shown. A name already taken gets a number (`Sales 2`) and the file's formulas follow it; named ranges come along unless their name is taken. One undo step, and the spreadsheet stays the file you're editing |38+| Replace current sheet | Puts the data in place of the sheet shown, keeping its name and position, so formulas and named ranges that read it read the new data. One undo step. Not offered for `.xlsx`, which holds several sheets |39+| Replace spreadsheet | Opens the file instead, as File > Open and the command line do, asking first when there are unsaved changes |40+41+Replacing the current sheet keeps the charts that fit the new data. A42+chart that drew a whole table is re-pointed to the table the file has at43+the same corner when it has as many columns (rows for a chart by row),44+so last month's chart draws this month's rows; a chart of part of a45+table, or of whole columns or rows, keeps its range. A chart whose table46+now has other columns, or whose range is empty, is removed. The context47+line lists the removed charts first, then which were re-pointed and48+which kept their range, and undo brings the removed ones back.49+50+**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](config.md#max-cells),52+two million by default, about 600 MB): whole rows, as many as fit, and53+the context line says how many rows were left out, e.g. `only the first54+166,666 rows fit in max-cells (2,000,000 cells); 12,000 rows left out`.55+Data past the grid's edges is left out the same way. WK1 files keep their56+own 8,192 by 256. See [limits.md](limits.md#imports) for speeds.57+58+## More59+60+| Page | For |61+|---|---|62+| [Excel files](@/files/excel.md) | What comes in from and goes out to `.xlsx` |63+| [Saving](@/files/saving.md) | Atomic saves, overwrite checks, unsaved work under `012 serve` |64+| [The .012 format](@/files/format.md) | The JSON format, its versions and fields |@@ -0,0 +1,125 @@
1+---2+title: "Excel files"3+sidebar_position: 24+---5+6+# Excel files7+8+**Read:** every sheet, opening on the one Excel showed; values, formulas9+(references between sheets too, shared formulas), workbook named ranges,10+number formats, bold, italic, underline, strikethrough, alignment,11+column widths, column and row styles, frozen panes, [filters](#filters-in-excel),12+notes (Excel's notes, its legacy comments; threaded comments aren't13+read), [conditional formatting and data validation](#rules-in-excel),14+[array formulas](#arrays-in-excel),15+dates in the 1904 system, and hidden sheets (unless it's the one Excel16+showed).17+18+**Written:** the same, with formulas in Excel's syntax and their results19+cached, column and row formats as column and row styles, frozen rows and20+columns as frozen panes, a filter as Excel's with the rows it hides21+hidden, notes as Excel's notes (which Sheets reads as notes), and22+formulas that spill as Excel 365's [dynamic array formulas](#arrays-in-excel).23+24+What changes on the way:25+26+| | |27+|---|---|28+| Formulas 012 can't read (unknown functions) | Keep their values |29+| Excel pivot tables | Come in as the values they showed; 012's go out as their results, not a pivot |30+| Excel's sheet protection | Comes in unprotected, with a note saying so: Excel's protection locks cells where 012's only warns. Protected ranges aren't written |31+| Array formulas (Excel 365's dynamic arrays and older `{=...}` ones) | Come in as formulas that spill again, without the values Excel kept in the cells they spill into |32+| Functions only Sheets has (`SORTN`, `FLATTEN`, `SPLIT`, `REGEXMATCH`, `REGEXEXTRACT`) | Go out as values, counted with the others below |33+| JEV functions, `#AND#`, formulas naming a sheet that doesn't exist | Go out as values (their `#REF!`, for a missing sheet: Excel would refuse the reference). The download's result counts the formulas saved as values, with an example |34+| A sheet name Excel can't take as is (spaces at its ends, or the same as another's but for them and case) | Written as one it can: without the spaces, with a number when two would clash (`Plan (2)`); formulas and named ranges naming it name that |35+| Files past the reader's limits (a zip bomb, 1 GB in one part, 2 GB in all, cells past XFD1048576) | Refused, with a message saying which |36+37+## Filters in Excel38+39+A sheet's filter goes out as Excel's AutoFilter over the same range, and40+an AutoFilter comes in as a filter:41+42+| 012 | Excel |43+|---|---|44+| Filter by values: the values unchecked | The list of values shown (Excel keeps those checked), with blanks when they're shown. Values are compared as displayed, ignoring case |45+| Is empty | Blanks only |46+| Is not empty | Custom filter: does not equal a space |47+| Text contains, does not contain, starts with, ends with, is exactly | Custom filter: equals or does not equal `*text*`, `text*`, `*text`, `text`, with Excel's wildcards in the text escaped (`~*`) |48+| Greater than, less than, is equal to, is not equal to (and or equal to) | Custom filter with that operator; numbers typed as `$1,200` or `12%` go out as the number |49+50+The rows the filter hides are written hidden, since Excel shows a file's51+rows as saved rather than filtering again. A column with both unchecked52+values and a condition goes out as the list of values both let through,53+and comes back as that list. What 012's filters can't do is left out,54+with a note saying how many criteria and which: two conditions in one55+column (`and`, `or`), wildcards other than at the ends of the text (`a?c`,56+`a*c`), top 10, dynamic filters (above average, this month), dates57+grouped by year or month, and filtering by color or icon. The filter58+itself still comes in over its range. Filters of Excel tables (as59+opposed to the sheet's AutoFilter) aren't read.60+61+## Arrays in Excel62+63+A formula whose array spills (see [formulas.md](formulas.md#arrays-and-spills))64+is written as Excel writes a dynamic array formula: an array formula over65+the cells it spills into (`<f t="array" ref="C1:C9">`) on a cell whose66+metadata marks it dynamic (`cm="1"`, defined in `xl/metadata.xml`), with67+the values in the cells below and right of it, so Excel spills it the68+same and older Excels show the values. Formulas calling array functions69+(`FILTER`, `SORT`, `UNIQUE`, `SEQUENCE`, `LET`, `LAMBDA` and the like) or70+holding an array literal are written that way even when they compute one71+value, so Excel doesn't put its implicit intersection (`@`) in front of72+them. Excel gets its own names for the functions newer than Excel 200773+(`_xlfn._xlws.FILTER`, `_xlfn.SEQUENCE`), `_xlpm.` before the names LET74+and LAMBDA bind, and the formula inside an `ARRAYFORMULA` around a whole75+formula, which a dynamic array formula computes over arrays anyway.76+What Excel can't hold goes out as the value it showed, counted in the77+download's note: functions only Sheets has (`SORTN`, `FLATTEN`, `SPLIT`,78+`REGEXMATCH`, and `REGEXEXTRACT`, whose Excel namesake returns the whole79+match where Sheets' returns the capture group), array literals holding80+references (Excel's hold only constants) and `ARRAYFORMULA` inside a81+formula.82+83+## Rules in Excel84+85+[Conditional formats](data.md#conditional-formatting) go out as Excel's86+conditional formatting, and [data validation](data.md#data-validation)87+as its data validation, and both come back:88+89+| Conditional format | Excel |90+|---|---|91+| Is empty, is not empty | `containsBlanks`, `notContainsBlanks` |92+| Text contains, does not contain, starts with, ends with | `containsText`, `notContainsText`, `beginsWith`, `endsWith` |93+| Text is exactly | `cellIs` equal to the text |94+| Date is today, tomorrow, yesterday | `timePeriod` |95+| Date is, is before, is after a date | an expression, `INT(A1)<DATE(2026,9,30)` |96+| Greater than ... is not between | `cellIs` with the operator; dates as Excel's serial numbers |97+| Custom formula is | `expression`, in Excel's syntax |98+| Color scale | `colorScale` with its points (`min`, `max`, `num`, `percent`, `percentile`) |99+| A rule's style | a differential style (dxf): bold, italic, underline, strikethrough, the text color and a solid fill |100+101+| Data validation | Excel |102+|---|---|103+| Dropdown, from a range | `list` of the items in quotes, or of the range (`Lists!$A$1:$A$20`) |104+| Checkbox | `list` of `TRUE,FALSE`, which comes back as a checkbox |105+| Number, date, text length | `decimal`, `date`, `textLength` with the operator; any date as a date greater than 0 |106+| Custom formula is | `custom` |107+| Reject the input, show a warning | error style `stop`, `warning`; help text as the prompt |108+109+Each single-color rule stops the ones after it (`stopIfTrue`) and its110+priority is its place, so Excel applies the first that matches, as 012111+does. Named colors go out in the colors of Sheets' palette, and Excel's112+colors (ARGB, theme and indexed) come in as the nearest named color by113+hue, grays as none; a color scale's gray or white point takes the nearest114+scale color. Validation in Excel 2010's extension (lists from other115+sheets) is read too; whole-number rules come in as number rules.116+117+Left out, with a note saying how many and which: conditional formats 012118+has no rule for (data bars, icon sets, top or bottom values, above or119+below average, duplicate or unique values, errors, date periods other120+than today, tomorrow and yesterday, and what Excel 2010's extension121+holds), rules whose style has only a number format or borders, time122+validation and lists from named ranges or formulas. Going out, a123+formula with no Excel equivalent (`#AND#`) leaves its rule out, and so124+do list items with commas or quotes, or longer than Excel's 255125+characters.@@ -0,0 +1,170 @@
1+---2+title: "The .012 format"3+sidebar_position: 44+---5+6+# The .012 format7+8+`.012` files are JSON with one line per cell, keyed by address, so diffs9+read naturally and files merge reasonably in version control. A cell10+without formatting is just what was typed; a formatted cell is a small11+object:12+13+```json14+{15+ "version": 2,16+ "widths": {"A": 14},17+ "cells": {18+ "A1": "Rent",19+ "B1": {"input":"1450","format":"currency","decimals":2},20+ "B2": {"input":"95","note":"Due on the 1st"},21+ "B3": "=SUM(B1:B2)"22+ }23+}24+```25+26+A formatted cell has its `input` and the formatting that isn't the27+default: `format` (as `number_format` in [macros](macros.md#cells) names28+it: `currency`, `percent`, `date`, ...), `decimals`, `pattern` for a29+custom format, `bold`, `italic`, `underline`, `strikethrough` and30+`align` (`left`, `center`, `right`).31+32+## Versions33+34+012 writes the lowest version that holds the workbook, so older builds35+open what they can, and refuses a version newer than it knows rather36+than lose sheets or pivots. Version 1 files (cells as plain strings)37+still load.38+39+| Version | Written when | Adds |40+|---|---|---|41+| 2 | One sheet, using none of the below | `cells`, `widths` |42+| 3 | A sheet has named ranges, frozen panes or a filter | `names` (each name's range, `#REF!` once its cells were deleted), `freeze` (`rows`, `cols`), `filter` (its `range`, and `columns` by letter, each with `hidden` values or a `condition` and its `value`) |43+| 4 | Several sheets, or a formula naming a sheet | `sheets`, a list; see [Several sheets](#several-sheets) |44+| 5 | A pivot table | a sheet's `pivot`; see [Pivot tables](#pivot-tables) |45+46+## Fields that need no version47+48+Builds that don't know these fields ignore them (and drop them if they49+save), so they raise no version:50+51+| Field | On | Holds |52+|---|---|---|53+| `note` | a cell | Its [note](data.md#notes) |54+| `own` | a cell | `true` when its formatting is its own, not its column's or row's (Automatic in a currency column) |55+| `lines` | a sheet | [Column and row formats](#column-and-row-formats) |56+| `name`, `hidden` | a sheet | Its name when renamed; `true` when hidden (a file whose sheets are all hidden opens with the first one shown) |57+| `charts` | a sheet | One chart per line: `type` (`column`, `bar`, `line`, `pie`, `area`, `scatter`), `data`, `at`, `width`, `height`, `byRow`, `header`, `labels`, `title`, and options left out at their defaults: `stack` (`stacked`, `percent`), `trend`, `min`, `max`, `log`, `gridlines` (only when off), `legend` (`right`, `none`). A build that charts but lacks a chart's type refuses the file |58+| `protected` | a sheet | [Protected ranges](data.md#protected-sheets-and-ranges): `{"range":"B2:C9","description":"Totals"}`, or `{"sheet":true}` |59+| `conditionalFormats`, `validations` | a sheet | [Rules](#conditional-formats-and-data-validation), one per line |60+| `arithmetic` | the workbook | `decimal` for [decimal arithmetic](formulas.md#decimal-arithmetic) |61+| `macros`, `macroOrigin` | the workbook | Macros as Starlark scripts, and the computer they were made or trusted on: see [macros.md](macros.md#in-the-file). Opening a file never runs them |62+63+## Column and row formats64+65+[Formats of whole columns and rows](data.md#rows-and-columns) are kept66+in a `lines` field after the widths, runs of lines with the same67+formatting together; `A:XFD` is the whole sheet's format:68+69+```json70+ "lines": {"A:XFD": {"italic":true}, "B:D": {"format":"currency","decimals":2}, "1:1": {"bold":true}},71+```72+73+## Several sheets74+75+In version 4 the sheets are a list, each with its name and the fields a76+version 3 file has at the top, and named ranges say their sheet. The77+workbook's settings (named ranges, decimal arithmetic, the sheet shown78+when saved as `active`) stay at the top:79+80+```json81+{82+ "version": 4,83+ "names": {"Rent": "Budget!B1"},84+ "active": 1,85+ "sheets": [86+ {87+ "name": "Budget",88+ "cells": {89+ "A1": "Rent",90+ "B1": "1450"91+ }92+ },93+ {94+ "name": "Q3 plan",95+ "hidden": true,96+ "cells": {97+ "A1": "=Budget!B1*3"98+ }99+ }100+ ]101+}102+```103+104+## Pivot tables105+106+A pivot's sheet has a `pivot` field, on one line after its cells,107+holding the definition. The results are never saved; they are computed108+again when the file opens, so the file stays small and can't disagree109+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.112+113+```json114+{115+ "name": "Pivot Table 1",116+ "cells": {},117+ "pivot": {"source":"Sales!A1:D200","rows":[{"column":"B"},{"column":"A","order":"desc","sortBy":1}],"columns":[{"column":"C"}],"values":[{"column":"D","summarize":"sum"},{"column":"D","summarize":"counta","showAs":"percent_of_total","name":"Share"}],"filters":[{"column":"A","hidden":["North"]}],"rowTotals":true,"columnTotals":false}118+}119+```120+121+- `source` is the data, with its sheet; `Sales!#REF!` once the range was122+ deleted. The sheet is by name, as in formulas, and follows renames.123+- `rows` and `columns` are fields by column letter on the source sheet,124+ with `order` `desc` for Z to A and `sortBy` the value, counting from 1,125+ whose totals order the groups.126+- `values` summarize a column: `sum`, `counta`, `count`, `countunique`,127+ `average`, `max`, `min`, or `rows` (every row, blank or not, which128+ frequency tables use); `showAs` is `percent_of_row`,129+ `percent_of_column` or `percent_of_total`; `name` replaces the header.130+- `filters` take a filter column's criteria: `hidden` values and a131+ `condition` with its `value`.132+- `rowTotals` and `columnTotals` are the grand total row and column.133+134+## Conditional formats and data validation135+136+A sheet's rules are two lists after its cells and charts, one rule per137+line:138+139+```json140+ "conditionalFormats": [141+ {"ranges":"A2:A6","condition":"formula","values":["=$D2"],"text":"green","strikethrough":true},142+ {"ranges":"C2:C6","scale":[{"type":"min","color":"green"},{"type":"percentile","value":"50","color":"yellow"},{"type":"max","color":"red"}]}143+ ],144+ "validations": [145+ {"ranges":"B2:B6","criteria":"list","items":["Ann","Bo","Cy"],"help":"Pick who does it"},146+ {"ranges":"D2:D6","criteria":"checkbox"},147+ {"ranges":"C2:C6","criteria":"number","condition":"between","values":["0","80"],"reject":true}148+ ]149+```150+151+- `ranges` are the rule's ranges, `A2:A9,C2:C9`.152+- A single-color rule has a `condition` (`empty`, `not_empty`, `contains`,153+ `not_contains`, `starts_with`, `ends_with`, `exactly`, `date_is`,154+ `date_before`, `date_after`, `gt`, `ge`, `lt`, `le`, `eq`, `ne`,155+ `between`, `not_between`, `formula`), its `values` as typed, and its156+ style: `text` and `fill` colors (`red`, `yellow`, `green`, `cyan`,157+ `blue`, `magenta`) and `bold`, `italic`, `underline`, `strikethrough`.158+- A color scale has `scale`: two or three points, each a `type` (`min`,159+ `max`, `num`, `percent`, `percentile`), a `value` where it takes one,160+ and a `color`.161+- A validation rule has `criteria` (`list`, `range`, `checkbox`,162+ `number`, `date`, `length`, `formula`) with its `items`, `source`163+ (`Lists!A1:A20`), or `condition` (`between`, `not_between`, `eq`, `ne`,164+ `gt`, `ge`, `lt`, `le`; none for any date) and `values`; `reject` refuses165+ invalid entries, and `help` replaces the rule's own help text.166+167+A line of either list is also what Add conditional format rule and Add168+data validation rule take (in the palette, and as `run(...,169+answer=...)` in macros), which is how a macro records a rule added from170+the panel.@@ -0,0 +1,19 @@
1+---2+title: "Saving"3+sidebar_position: 34+---5+6+# Saving7+8+Saves are atomic: 012 writes a temporary file and renames it. Saving over9+the open file when something else wrote it since it was opened or last10+saved (another program, or another session of [012 serve](ssh.md)) asks11+first: Enter overwrites, S saves under another name, Esc cancels. Save12+as (and `:w name`, `:wq name` with vim keys) onto another file that13+exists asks the same way: Enter replaces it, Esc cancels, and cancelling14+`:wq`'s question cancels its quit too.15+16+Under `012 serve`, a session that idles out or is ended by the server17+stopping keeps its unsaved changes in `.012-recovery/` in the served18+directory, and the next session opening that file offers them back: see19+[ssh.md](ssh.md#unsaved-work).@@ -1,231 +0,0 @@
1-# Entries and formulas2-3-## What you type4-5-Typing replaces the active cell, as in Google Sheets. 012 decides what the6-entry is the way Sheets does:7-8-| You type | You get |9-|---|---|10-| `Rent`, `Q3 total` | text |11-| `42`, `-3.5`, `1,234`, `1e3` | a number |12-| `$1,200`, `12%` | a number with currency or percent format |13-| `9/26/2026`, `2026-09-26`, `Sep 26, 2026`, `14:30`, `2pm` | a date or time (a serial number with a date or time format) |14-| `TRUE`, `false` | a boolean |15-| `=A1*2`, `+A1*2` | a formula |16-| `'42` | text, even though it looks like a number |17-18-A formula that doesn't parse isn't accepted: 012 stays in EDIT mode with the19-caret on the problem and says what's wrong on the context line, e.g.20-`Expected , or ) in SUM`.21-22-## References23-24-- Cells: `A1`, ranges: `A1:B3` (1-2-3's `A1..B3` also works), whole25- columns `A:C` and whole rows `2:5` (`$A:$A`, `$2:$2` absolute). Whole26- columns and rows stay whole when copied, filled or when lines are27- inserted and deleted; `A1:A1048576` reads back as `A:A`.28-- Absolute parts: `$A$1`, `A$1`, `$A1`. F4 while typing cycles the reference29- at the caret through them. Copying, filling, sorting and inserting or30- deleting rows and columns adjust the relative parts, as in Sheets;31- references to deleted cells become `#REF!`.32-- Named ranges: `=SUM(Sales)`. Define them from Data > Named ranges or Data >33- Define named range. Names are case-insensitive, follow the selection when34- rows move, and renaming one rewrites the formulas that use it.35-- Other sheets: `=Sheet2!A1`, `=SUM('Q3 plan'!B2:B9)`; names with spaces or36- punctuation, or that look like a cell, go in single quotes. While typing a37- formula, Ctrl+PgDn or clicking a tab points into another sheet and inserts38- the reference. Renaming a sheet rewrites the formulas that use it;39- deleting one leaves them as written, showing `#REF!` ("Unresolved sheet40- name") until a sheet of that name exists again, as in Sheets. Inserting41- and deleting rows moves references into that sheet from every sheet, and42- leaves references to other sheets alone. Named ranges belong to the whole43- file and may point into any sheet.44-- Each sheet is 16,384 columns (A to XFD) by 1,048,576 rows, Excel's size;45- a reference past them (`XFE1`, `A1048577`) reads as a name. Formulas cost46- what their ranges hold, not their size: `SUM(A:A)` over ten numbers reads47- ten cells, and `ROWS(A:A)` is still 1,048,576. See [limits.md](limits.md)48- for what that means in practice.49-50-## Building formulas51-52-- After an operator or `(`, arrow keys (or a click) pick a cell and insert53- its reference; Shift+arrows (or a drag) pick a range. Keep typing to go on.54-- While you type a name, suggestions drop down: named ranges first, then55- the other sheets, then functions with their arguments. Tab or Enter56- inserts `NAME(`, or a sheet with its `!`, quoted when it needs to be57- (`=Su` offers `Summary!`; `=Q3` or `='Q3` offers `'Q3 plan'!`). An arrow58- after it points into that sheet, as clicking its tab does. Hidden59- sheets aren't offered, though formulas still read them.60-- Inside a function's parentheses the context line shows its signature with61- the current argument marked, e.g. `SUMIF(range, criterion, [sum_range])`.62-- Alt+, and Alt+. trace precedents and dependents: the cells a formula reads63- and the formulas that read a cell. Press again to step through them.64- Cells on hidden sheets are skipped; when that leaves nothing, the65- context line names the hidden sheets ("Reads only Data, a hidden sheet;66- View > Hidden sheets shows it").67-- A formula left Automatic shows the format of what it reads: `=B5*2` of68- a currency cell shows currency, and so does `=SUM(B2:B9)`. A blank cell69- counts with its column's, row's or sheet's format, and changing those70- formats changes what the formulas show at once.71-72-## Operators73-74-`+ - * / ^` for arithmetic, `&` to join text, `= <> < > <= >=` to compare75-(giving TRUE or FALSE), `%` after a number divides by 100. Unary minus binds76-tighter than `^` as in Sheets and Excel, so `=-2^2` is 4. 1-2-3's `#AND#`,77-`#OR#` and `#NOT#` still work; `AND()`, `OR()` and `NOT()` are the Sheets way.78-79-## A range where one value is wanted80-81-A range used where a formula wants one value (with an operator, or as82-an argument that takes a number or text) reads as one of its cells, as83-in Sheets and Excel (implicit intersection):84-85-- a single column gives the cell in the formula's own row, so with the86- named range Rent = B2:B4, `=Rent*2` in F4 is B4*2 and `=B:B+1` in G5 is87- B5+1;88-- a single row gives the cell in the formula's own column: `=B6:D6+1` in89- C8 is C6+1;90-- a single cell is itself;91-- anything else is `#VALUE!`: a formula outside the range's rows (or92- columns), and a range of several rows and columns.93-94-Ranges on other sheets work the same, by the formula's row or column:95-`='Q3 plan'!C2:C9*10` in A3 reads `'Q3 plan'!C3`. A range that is a96-cell's whole formula, `=B2:B4` or `=Rent`, is an array and spills, as in97-Sheets (see [Arrays and spills](#arrays-and-spills)). Functions that98-take ranges (`SUM`, `COUNTIF`, `MATCH`, `VLOOKUP`, `SUMPRODUCT` and the99-like) read the whole range, and an expression given to them is computed100-over arrays: `SUM(B2:B4*2)` doubles each cell and adds them.101-102-## Arrays and spills103-104-An array is a block of values: a range read whole, an array literal105-such as `{1,2;3,4}` (`,` between values in a row, `;` between rows; its106-values may be ranges, so `{A1:A3,C1:C3}` puts two columns side by107-side), or what an array function computes. A formula whose result is an108-array shows its first value and spills the rest into the cells to its109-right and below, as in Sheets:110-111-- `=SEQUENCE(3, 2)` fills three rows of two; `=SORT(A2:A99)`,112- `=FILTER(A2:C99, C2:C99>100)` and `=UNIQUE(B:B)` spill as tall as113- their results, and grow or shrink as the data changes. Blank cells past114- a range's data aren't spilled, so `=SORT(A:A)` fills as many rows as115- column A holds.116-- Operators work value by value: `={1,2,3}*10` is 10, 20, 30. A row and a117- column combine into a block (`={1;2}+{10,20}` is two rows of two), and118- arrays of different sizes line up with `#N/A` past the smaller one.119-- `ARRAYFORMULA(...)` computes its formula over arrays: ranges read whole120- and functions of one value are applied to each value, so121- `=ARRAYFORMULA(IF(C2:C99>100, "big", "small"))` spills one word per row122- and `=ARRAYFORMULA(VLOOKUP(A2:A9, Prices, 2, FALSE))` looks each key up.123- The arguments of functions that take ranges are computed the same way,124- so `=SUM(LEN(A2:A9))` counts every character and125- `=SUMPRODUCT((B2:B99="north")*C2:C99)` sums a column by a condition.126-- Elsewhere an array reads as its first value, as Sheets does:127- `=LEN(SEQUENCE(3)*100)` is 3. `IF`, `IFERROR`, `IFNA`, `IFS`, `SWITCH`,128- `CHOOSE` and `INDEX` pass an array through, so129- `=IFERROR(FILTER(A2:A99, B2:B99="x"), "none")` spills or says none, and130- `=INDEX(A2:C9, 0, 2)` spills the second column.131-132-The context line says where a spilled cell's value comes from133-("Spilled from B2"), and on the formula's own cell where it spills134-("Spills into B2:C9"); spilled values are drawn in a color of their own,135-and the formula bar shows a spilled cell's formula dimmed. A spilled cell136-can't be typed into, cleared, pasted over or filled: the context line137-names the formula to edit instead. Selecting the formula with its spill138-and pressing Del clears it, formatting spilled cells keeps the139-formatting, and inserting or deleting rows and columns through a spill140-spills it again. Copying spilled cells pastes their values. Conditional141-formats color spilled cells by their values, and data validation marks142-spilled values it doesn't accept, but never stops an array from143-spilling: rules judge what's typed, and a spilled value isn't.144-145-When a cell in the way of an array isn't empty, the formula shows146-`#REF!` and says why, as Sheets does: "Array result was not expanded147-because it would overwrite data in C3". Clearing that cell lets the148-array spill. The same goes for an array that would pass the sheet's149-edge, or write more cells than `max-cells` allows.150-151-Formulas reading spilled cells recalculate when the array changes, on152-any sheet, and undo brings back what an array spilled with the formula.153-Files keep only the formula: its array is computed again when the file154-opens.155-156-## Names in a formula: LET and LAMBDA157-158-`=LET(total, SUM(B2:B99), count, COUNT(B2:B99), total/count)` names159-values for use in the rest of the formula. A name bound to a range reads160-as the range would where the name is used. Names that LET and LAMBDA161-bind are the formula's own: a named range of the same name isn't used162-inside them.163-164-`LAMBDA(x, y, x*y)` is a function of its names, called with values right165-after it, `=LAMBDA(x, x*2)(21)`, or through a name LET gave it,166-`=LET(double, LAMBDA(v, v*2), double(21))`. `MAP`, `REDUCE`, `SCAN`,167-`BYROW`, `BYCOL` and `MAKEARRAY` call one for each value, row or column:168-`=BYROW(B2:D9, LAMBDA(row, SUM(row)))` spills each row's total. A LAMBDA169-that isn't called is `#VALUE!`, and one called with the wrong number of170-values `#N/A`, as in Sheets.171-172-## Regular expressions173-174-`REGEXMATCH`, `REGEXEXTRACT` and `REGEXREPLACE` use Go's regular175-expressions, which are RE2, as Sheets' are: the same patterns mean the176-same thing. They take text only: a number is `#VALUE!`, as in Sheets177-(`A1&""` makes one text). `REGEXEXTRACT` returns the first178-capture group, or the whole match without one, and spills several179-groups across; `REGEXREPLACE` writes a group in the replacement as `$1`.180-`SPLIT` spills the pieces of a text across, reading those that look like181-numbers as numbers.182-183-## Values and errors184-185-Text in arithmetic is `#VALUE!` unless it reads as a number (`="3"*2` is 6);186-blanks count as 0 or empty text. Aggregates such as `SUM` over a range skip187-text. Errors use Sheets' codes:188-189-| Error | Meaning |190-|---|---|191-| `#DIV/0!` | division by zero |192-| `#VALUE!` | the wrong kind of value, e.g. text in arithmetic |193-| `#NAME?` | an unknown name |194-| `#REF!` | a reference to deleted cells, or a circular reference |195-| `#N/A` | not available, e.g. a lookup found nothing |196-| `#NUM!` | a number out of range, e.g. `SQRT(-1)` |197-198-Error cells are red with a curly underline, and the context line explains the199-active cell's error and where it came from, e.g.200-`#DIV/0! From B3: division by zero in B5/0`, or names the chain of a201-circular reference.202-203-## Decimal arithmetic204-205-Like Sheets and Excel, 012 computes in binary floating point, so206-`=0.1+0.2=0.3` is FALSE and `=INT($4.35*100)` is 434. File > Settings >207-Decimal arithmetic (or search the palette for "decimal") switches the whole208-file, every sheet of it, to decimal math for money. The status line then says209-`decimal`, the menu shows a check mark, and the setting is saved with the210-file and can be undone.211-212-213-214-| Computed in decimal | Stays binary |215-|---|---|216-| `+ - * /` and postfix `%`; `SUM`, `AVERAGE`, `PRODUCT`, `SUMIF`, `SUMIFS`, `SUMPRODUCT`; `ROUND`, `ROUNDUP`, `ROUNDDOWN`, `TRUNC` | `^`, `SQRT`, statistics, finance, dates and everything else |217-218-Values are still stored as doubles. Each decimal step reads its inputs as the219-shortest decimal that round-trips (what the cell shows), computes exactly220-(division to 34 significant digits) with221-[apd](https://github.com/cockroachdb/apd), and keeps the double nearest the222-result, so comparisons, lookups and formats see `0.3` rather than223-`0.30000000000000004`. Division still rounds: `=1/3*3` is 0.9999999999999999,224-as in any decimal system. Displayed numbers are already rounded to 15225-significant digits, so the setting matters for comparisons, `INT`/`MOD`-style226-truncation and residues such as `=1.1-1-0.1`. Older builds ignore the setting227-and compute in binary.228-229-## Functions230-231-See [functions.md](functions.md) for all of them, generated from the engine.@@ -0,0 +1,74 @@
1+---2+title: "Formulas"3+sidebar_position: 14+---5+6+# Formulas7+8+## What you type9+10+Typing replaces the active cell, as in Google Sheets. 012 decides what the11+entry is the way Sheets does:12+13+| You type | You get |14+|---|---|15+| `Rent`, `Q3 total` | text |16+| `42`, `-3.5`, `1,234`, `1e3` | a number |17+| `$1,200`, `12%` | a number with currency or percent format |18+| `9/26/2026`, `2026-09-26`, `Sep 26, 2026`, `14:30`, `2pm` | a date or time (a serial number with a date or time format) |19+| `TRUE`, `false` | a boolean |20+| `=A1*2`, `+A1*2` | a formula |21+| `'42` | text, even though it looks like a number |22+23+A formula that doesn't parse isn't accepted: 012 stays in EDIT mode with the24+caret on the problem and says what's wrong on the context line, e.g.25+`Expected , or ) in SUM`.26+27+## Operators28+29+`+ - * / ^` for arithmetic, `&` to join text, `= <> < > <= >=` to compare30+(giving TRUE or FALSE), `%` after a number divides by 100. Unary minus binds31+tighter than `^` as in Sheets and Excel, so `=-2^2` is 4. 1-2-3's `#AND#`,32+`#OR#` and `#NOT#` still work; `AND()`, `OR()` and `NOT()` are the Sheets way.33+34+## Values and errors35+36+Text in arithmetic is `#VALUE!` unless it reads as a number (`="3"*2` is 6);37+blanks count as 0 or empty text. Aggregates such as `SUM` over a range skip38+text. Errors use Sheets' codes:39+40+| Error | Meaning |41+|---|---|42+| `#DIV/0!` | division by zero |43+| `#VALUE!` | the wrong kind of value, e.g. text in arithmetic |44+| `#NAME?` | an unknown name |45+| `#REF!` | a reference to deleted cells, or a circular reference |46+| `#N/A` | not available, e.g. a lookup found nothing |47+| `#NUM!` | a number out of range, e.g. `SQRT(-1)` |48+49+Error cells are red with a curly underline, and the context line explains the50+active cell's error and where it came from, e.g.51+`#DIV/0! From B3: division by zero in B5/0`, or names the chain of a52+circular reference.53+54+## Regular expressions55+56+`REGEXMATCH`, `REGEXEXTRACT` and `REGEXREPLACE` use Go's regular57+expressions, which are RE2, as Sheets' are: the same patterns mean the58+same thing. They take text only: a number is `#VALUE!`, as in Sheets59+(`A1&""` makes one text). `REGEXEXTRACT` returns the first60+capture group, or the whole match without one, and spills several61+groups across; `REGEXREPLACE` writes a group in the replacement as `$1`.62+`SPLIT` spills the pieces of a text across, reading those that look like63+numbers as numbers.64+65+## More66+67+| Page | For |68+|---|---|69+| [References](@/formulas/references.md) | Cells, ranges, names, other sheets, and a range where one value is wanted |70+| [Building formulas](@/formulas/building.md) | Pointing, suggestions, argument hints, tracing |71+| [Arrays and spills](@/formulas/arrays.md) | FILTER, SORT, UNIQUE, SEQUENCE, ARRAYFORMULA, LET and LAMBDA |72+| [Decimal arithmetic](@/formulas/decimal.md) | Exact decimal math for money |73+| [JEV functions](@/formulas/jev.md) | Asking a hosted model from formulas |74+| [Functions](@/reference/functions.md) | Every function, generated from the engine |@@ -0,0 +1,74 @@
1+---2+title: "Arrays and spills"3+sidebar_position: 44+---5+6+# Arrays and spills7+8+An array is a block of values: a range read whole, an array literal9+such as `{1,2;3,4}` (`,` between values in a row, `;` between rows; its10+values may be ranges, so `{A1:A3,C1:C3}` puts two columns side by11+side), or what an array function computes. A formula whose result is an12+array shows its first value and spills the rest into the cells to its13+right and below, as in Sheets:14+15+- `=SEQUENCE(3, 2)` fills three rows of two; `=SORT(A2:A99)`,16+ `=FILTER(A2:C99, C2:C99>100)` and `=UNIQUE(B:B)` spill as tall as17+ their results, and grow or shrink as the data changes. Blank cells past18+ a range's data aren't spilled, so `=SORT(A:A)` fills as many rows as19+ column A holds.20+- Operators work value by value: `={1,2,3}*10` is 10, 20, 30. A row and a21+ column combine into a block (`={1;2}+{10,20}` is two rows of two), and22+ arrays of different sizes line up with `#N/A` past the smaller one.23+- `ARRAYFORMULA(...)` computes its formula over arrays: ranges read whole24+ and functions of one value are applied to each value, so25+ `=ARRAYFORMULA(IF(C2:C99>100, "big", "small"))` spills one word per row26+ and `=ARRAYFORMULA(VLOOKUP(A2:A9, Prices, 2, FALSE))` looks each key up.27+ The arguments of functions that take ranges are computed the same way,28+ so `=SUM(LEN(A2:A9))` counts every character and29+ `=SUMPRODUCT((B2:B99="north")*C2:C99)` sums a column by a condition.30+- Elsewhere an array reads as its first value, as Sheets does:31+ `=LEN(SEQUENCE(3)*100)` is 3. `IF`, `IFERROR`, `IFNA`, `IFS`, `SWITCH`,32+ `CHOOSE` and `INDEX` pass an array through, so33+ `=IFERROR(FILTER(A2:A99, B2:B99="x"), "none")` spills or says none, and34+ `=INDEX(A2:C9, 0, 2)` spills the second column.35+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.48+49+When a cell in the way of an array isn't empty, the formula shows50+`#REF!` and says why, as Sheets does: "Array result was not expanded51+because it would overwrite data in C3". Clearing that cell lets the52+array spill. The same goes for an array that would pass the sheet's53+edge, or write more cells than `max-cells` allows.54+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+60+## Names in a formula: LET and LAMBDA61+62+`=LET(total, SUM(B2:B99), count, COUNT(B2:B99), total/count)` names63+values for use in the rest of the formula. A name bound to a range reads64+as the range would where the name is used. Names that LET and LAMBDA65+bind are the formula's own: a named range of the same name isn't used66+inside them.67+68+`LAMBDA(x, y, x*y)` is a function of its names, called with values right69+after it, `=LAMBDA(x, x*2)(21)`, or through a name LET gave it,70+`=LET(double, LAMBDA(v, v*2), double(21))`. `MAP`, `REDUCE`, `SCAN`,71+`BYROW`, `BYCOL` and `MAKEARRAY` call one for each value, row or column:72+`=BYROW(B2:D9, LAMBDA(row, SUM(row)))` spills each row's total. A LAMBDA73+that isn't called is `#VALUE!`, and one called with the wrong number of74+values `#N/A`, as in Sheets.@@ -0,0 +1,26 @@
1+---2+title: "Building formulas"3+sidebar_position: 34+---5+6+# Building formulas7+8+- After an operator or `(`, arrow keys (or a click) pick a cell and insert9+ its reference; Shift+arrows (or a drag) pick a range. Keep typing to go on.10+- While you type a name, suggestions drop down: named ranges first, then11+ the other sheets, then functions with their arguments. Tab or Enter12+ inserts `NAME(`, or a sheet with its `!`, quoted when it needs to be13+ (`=Su` offers `Summary!`; `=Q3` or `='Q3` offers `'Q3 plan'!`). An arrow14+ after it points into that sheet, as clicking its tab does. Hidden15+ sheets aren't offered, though formulas still read them.16+- Inside a function's parentheses the context line shows its signature with17+ the current argument marked, e.g. `SUMIF(range, criterion, [sum_range])`.18+- Alt+, and Alt+. trace precedents and dependents: the cells a formula reads19+ and the formulas that read a cell. Press again to step through them.20+ Cells on hidden sheets are skipped; when that leaves nothing, the21+ context line names the hidden sheets ("Reads only Data, a hidden sheet;22+ View > Hidden sheets shows it").23+- A formula left Automatic shows the format of what it reads: `=B5*2` of24+ a currency cell shows currency, and so does `=SUM(B2:B9)`. A blank cell25+ counts with its column's, row's or sheet's format, and changing those26+ formats changes what the formulas show at once.@@ -0,0 +1,30 @@
1+---2+title: "Decimal arithmetic"3+sidebar_position: 54+---5+6+# Decimal arithmetic7+8+Like Sheets and Excel, 012 computes in binary floating point, so9+`=0.1+0.2=0.3` is FALSE and `=INT($4.35*100)` is 434. File > Settings >10+Decimal arithmetic (or search the palette for "decimal") switches the whole11+file, every sheet of it, to decimal math for money. The status line then says12+`decimal`, the menu shows a check mark, and the setting is saved with the13+file and can be undone.14+15+16+17+| Computed in decimal | Stays binary |18+|---|---|19+| `+ - * /` and postfix `%`; `SUM`, `AVERAGE`, `PRODUCT`, `SUMIF`, `SUMIFS`, `SUMPRODUCT`; `ROUND`, `ROUNDUP`, `ROUNDDOWN`, `TRUNC` | `^`, `SQRT`, statistics, finance, dates and everything else |20+21+Values are still stored as doubles. Each decimal step reads its inputs as the22+shortest decimal that round-trips (what the cell shows), computes exactly23+(division to 34 significant digits) with24+[apd](https://github.com/cockroachdb/apd), and keeps the double nearest the25+result, so comparisons, lookups and formats see `0.3` rather than26+`0.30000000000000004`. Division still rounds: `=1/3*3` is 0.9999999999999999,27+as in any decimal system. Displayed numbers are already rounded to 1528+significant digits, so the setting matters for comparisons, `INT`/`MOD`-style29+truncation and residues such as `=1.1-1-0.1`. Older builds ignore the setting30+and compute in binary.@@ -0,0 +1,55 @@
1+---2+title: "References"3+sidebar_position: 24+---5+6+# References7+8+- Cells: `A1`, ranges: `A1:B3` (1-2-3's `A1..B3` also works), whole9+ columns `A:C` and whole rows `2:5` (`$A:$A`, `$2:$2` absolute). Whole10+ columns and rows stay whole when copied, filled or when lines are11+ inserted and deleted; `A1:A1048576` reads back as `A:A`.12+- Absolute parts: `$A$1`, `A$1`, `$A1`. F4 while typing cycles the reference13+ at the caret through them. Copying, filling, sorting and inserting or14+ deleting rows and columns adjust the relative parts, as in Sheets;15+ references to deleted cells become `#REF!`.16+- Named ranges: `=SUM(Sales)`. Define them from Data > Named ranges or Data >17+ Define named range. Names are case-insensitive, follow the selection when18+ rows move, and renaming one rewrites the formulas that use it.19+- Other sheets: `=Sheet2!A1`, `=SUM('Q3 plan'!B2:B9)`; names with spaces or20+ punctuation, or that look like a cell, go in single quotes. While typing a21+ formula, Ctrl+PgDn or clicking a tab points into another sheet and inserts22+ the reference. Renaming a sheet rewrites the formulas that use it;23+ deleting one leaves them as written, showing `#REF!` ("Unresolved sheet24+ name") until a sheet of that name exists again, as in Sheets. Inserting25+ and deleting rows moves references into that sheet from every sheet, and26+ leaves references to other sheets alone. Named ranges belong to the whole27+ file and may point into any sheet.28+- Each sheet is 16,384 columns (A to XFD) by 1,048,576 rows, Excel's size;29+ a reference past them (`XFE1`, `A1048577`) reads as a name. Formulas cost30+ 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](limits.md)32+ for what that means in practice.33+34+## A range where one value is wanted35+36+A range used where a formula wants one value (with an operator, or as37+an argument that takes a number or text) reads as one of its cells, as38+in Sheets and Excel (implicit intersection):39+40+- a single column gives the cell in the formula's own row, so with the41+ named range Rent = B2:B4, `=Rent*2` in F4 is B4*2 and `=B:B+1` in G5 is42+ B5+1;43+- a single row gives the cell in the formula's own column: `=B6:D6+1` in44+ C8 is C6+1;45+- a single cell is itself;46+- anything else is `#VALUE!`: a formula outside the range's rows (or47+ columns), and a range of several rows and columns.48+49+Ranges on other sheets work the same, by the formula's row or column:50+`='Q3 plan'!C2:C9*10` in A3 reads `'Q3 plan'!C3`. A range that is a51+cell's whole formula, `=B2:B4` or `=Rent`, is an array and spills, as in52+Sheets (see [Arrays and spills](#arrays-and-spills)). Functions that53+take ranges (`SUM`, `COUNTIF`, `MATCH`, `VLOOKUP`, `SUMPRODUCT` and the54+like) read the whole range, and an expression given to them is computed55+over arrays: `SUM(B2:B4*2)` doubles each cell and adds them.@@ -0,0 +1,21 @@
1+---2+title: "Getting started"3+sidebar_position: 14+---5+6+# Getting started7+8+012 is a spreadsheet for the terminal: Google Sheets' behavior inside9+the grid, Lotus 1-2-3's look around it.10+11+12+13+1. [Install and run](@/getting-started/install.md) 012.14+2. Type into cells as in Sheets: text, numbers, `$1,200`, `12%`, dates,15+ and formulas starting with `=` ([formulas](@/formulas/README.md)).16+ Enter and Tab accept and move on.17+3. [The screen](@/getting-started/the-screen.md) says what's going on: the mode indicator,18+ the formula bar and the context line, which shows the keys that apply.19+4. F1 lists every shortcut, F10 or Alt+letter opens the menus, and20+ Ctrl+K searches every command. The [keys](@/reference/keys.md) page21+ has them all, and the vim keymap.@@ -0,0 +1,30 @@
1+---2+title: "Install and run"3+sidebar_position: 24+---5+6+# Install and run7+8+```sh9+go install github.com/FelineStateMachine/012/cmd/012@latest10+```11+12+012 needs Go 1.27 to build and is one pure-Go binary. From a clone,13+`make build` puts it in `bin/012`.14+15+```sh16+012 # a new sheet17+012 budget.012 # open a sheet, or create it when it doesn't exist18+012 sales.xlsx # import .xlsx, .csv, .tsv, .sqlite, .parquet or Lotus .wk119+012 serve ~/sheets # serve a directory over SSH, a 012 per session20+```21+22+A file on the command line opens as File > Open would: a `.012` sheet, or23+another format imported ([files](@/files/README.md)). `012 serve` is24+described in [Serving over SSH](@/terminal/ssh.md).25+26+`012 config` prints the settings in effect and `012 config edit` opens the27+settings file ([configuration](@/reference/config.md)). 012 draws in your28+terminal's own colors by default; [themes](@/terminal/themes.md) says29+how to pick a color scheme. The [terminal](@/terminal/README.md) page30+lists which terminal features 012 uses where they're available.@@ -0,0 +1,13 @@
1+---2+title: "The screen"3+sidebar_position: 34+---5+6+# The screen7+8+| Line | Shows |9+|---|---|10+| Menu bar (top) | The menus, and the mode indicator on the right (`REC` beside it while a macro records) |11+| Formula bar | The name box, then the cell's contents or the entry being typed |12+| Context line | Prompts, key hints, formula errors and explanations, and bars such as find and the chart editor |13+| Status line (bottom) | The sheet tabs (the one shown highlighted), the file name, whether it's modified, and Sum, Avg and Count for a selection |@@ -0,0 +1,13 @@
1+---2+title: "Reference"3+sidebar_position: 14+---5+6+# Reference7+8+| Page | Lists |9+|---|---|10+| [Keys and mouse](@/reference/keys.md) | Every shortcut, the vim keymap and the mouse |11+| [Functions](@/reference/functions.md) | Every function, generated from the engine |12+| [Configuration](@/reference/config.md) | The config file, `012 config` and every option, generated from the registry |13+| [Macro scripting API](@/reference/macro-api.md) | The functions and command ids macros can use |@@ -1,3 +1,8 @@
1+---2+title: "Keys and mouse"3+sidebar_position: 24+---5+16 # Keys and mouse27 38 Inside the grid, 012 behaves like Google Sheets: if you know a Sheets@@ -210,12 +215,3 @@ The pointer changes shape over cells, resize handles and the formula bar
210215 in terminals that support it (OSC 22). In Ghostty and xterm, Shift+click211216 normally starts the terminal's own text selection; 012 asks the terminal212217 to pass it through (XTSHIFTESCAPE) and restores that on exit.213-214-## The screen215-216-| Line | Shows |217-|---|---|218-| Menu bar (top) | The menus, and the mode indicator on the right (`REC` beside it while a macro records) |219-| Formula bar | The name box, then the cell's contents or the entry being typed |220-| Context line | Prompts, key hints, formula errors and explanations, and bars such as find and the chart editor |221-| Status line (bottom) | The sheet tabs (the one shown highlighted), the file name, whether it's modified, and Sum, Avg and Count for a selection |@@ -1,131 +1,12 @@
1-# Macros2-3-Macros repeat work: record what you do once, then run it again with a4-key. They work like Google Sheets' Extensions > Macros, under5-**Data > Macros**. A macro is a [Starlark](https://github.com/bazelbuild/starlark)6-script (a small, deterministic dialect of Python) saved in the `.012`7-file, as Sheets saves Apps Script with a spreadsheet. Recorded macros are8-scripts too, so you can read them, change them, or write your own.9-10-11-12-## Recording13-14-1. **Data > Macros > Record macro** (or search the palette for "record").15- `REC` appears beside the mode indicator, and the context line says16- what's being recorded.17-2. Do the work: type entries, move and select, run commands from the18- menus, keys or palette, paste, drag the fill handle or a column border.19-3. **Data > Macros > Stop and save recording**. Name the macro on the20- context line, then give it a shortcut digit, or leave it empty. Esc at21- either question goes back to recording; **Discard recording** stops22- without saving.23-24-Two ways to record, as in Sheets:25-26-| | Recorded as | Replays |27-|---|---|---|28-| **Record macro** (absolute references) | `select("B3")`: the cells you selected | On the same cells, wherever the active cell is |29-| **Record macro with relative references** | `move(0, 1)`, `extend(2, 0)`: steps from the active cell | From wherever the active cell is when you run it; formulas typed move with it, as in a copy, and Ctrl+arrows, Home, Ctrl+Home and Ctrl+End are recorded as `jump()`, since where they land depends on the data |30-31-What gets recorded:32-33-- entries as they're accepted (`enter("=SUM(B2:B8)")`), including34- Ctrl+Enter over a selection (`fill=True`);35-- the selection, only when something acts on it: pressing Down five times36- then typing records one `move(0, 5)` (or one `select()`), so a replay37- doesn't depend on the size of the window;38-- commands that change the sheet, by id (`run("format.bold")`), with the39- answer to the question they ask, if any40- (`run("column.width", answer="15")`, `run("sheet.delete", answer="enter")`);41-- pasted text (`paste_text(...)`), the fill handle (`fill(...)`), column42- borders dragged or fitted (`set_width(...)`) and tabs dragged43- (`move_sheet(...)`);44-- rules added in the conditional formatting and data validation panels,45- as the commands that add a rule from a line of the file46- (`run("format.conditional_add", answer='{"ranges":"B2:B9",...}')`), and47- items picked from a dropdown, as entries. Editing, removing or moving a48- rule in the panel is noted as a comment, as the dialogs below are.49-50-What doesn't: dialogs such as sorting by several columns, the filter51-picker, find and replace, the chart editor, dragging a chart, and undo.52-Anything like that which changes the workbook is noted in the script as a53-comment, `# Not recorded: sort A2:C9`, so the script never silently does54-less than you did. Files, menus, help and other macros are never part of a55-macro.56-57-## Running58-59-- **The shortcut**, Ctrl+Alt+Shift+digit, in READY. Terminals without the60- kitty keyboard protocol send it as Ctrl+Alt with the shifted symbol61- (`Ctrl+Alt+!` for 1 on a US layout), which works too.62-- **Data > Macros > Run macro**, a list of the saved macros.63-- **The palette** (Ctrl+K): type the macro's name.64-65-A run is one undo step: Ctrl+Z undoes everything the macro did. While it66-runs, the mode indicator says `CMD` (as 1-2-3 did), keys and the mouse67-wait, and **Esc stops it**; what it did so far stays, and Ctrl+Z undoes it.68-When it ends, the context line says `Ran Totals`, or the last line the69-script printed, or where it failed:1+---2+title: "Macro scripting API"3+sidebar_position: 54+---705 71-```72-Totals:3:5: get: not a cell or range: ZZ Ctrl+Z undoes what it did73-```74-75-(`Totals` is the macro's name, `3:5` the line and column in its script.)76-77-Every run has a limit of 10 million Starlark steps, so a script that loops78-forever stops within a second or so with79-`stopped after 10000000 steps: does it loop forever?`.80-81-## Managing82-83-**Data > Macros > Manage macros** lists the saved macros with their84-shortcuts:85-86-| Key | Action |87-|---|---|88-| Enter | Run the highlighted macro |89-| F2 | Rename it |90-| F3 | Change its shortcut: a digit, or empty for none |91-| F4 | Edit its script in your editor |92-| Ctrl+D | Delete it (Ctrl+Z brings it back) |93-| Type | Search by name |94-95-The first row, **+ Write a macro** (also **Data > Macros > Write a96-macro**), names a new macro and opens a script for it in your editor.97-98-Editing uses `$VISUAL`, then `$EDITOR`, then `vi`, with the screen handed99-over until the editor exits; the script is saved when it does. If it has100-a mistake, it's saved anyway and the context line says where101-(`Saved Totals, but it has a mistake at Totals:4:1: undefined: sett`), so102-you can fix it with F4. Sessions served over SSH don't start an editor:103-their macros can be recorded, run, renamed and deleted, not edited.104-105-Changes to macros (recording, renaming, deleting, editing) are undo steps106-and mark the file modified, like any edit.107-108-## Macros from other computers109-110-Opening a file never runs its macros. The file remembers which computer111-its macros were made or trusted on (a random id kept in 012's112-config directory, the one [config.md](config.md) describes). Running113-a macro from a file made elsewhere asks once, on the context line:114-115-```116-Trust this file's macros? Enter Run Esc Cancel117-```118-119-Enter trusts all of the file's macros for this session and, once the file120-is saved, on this computer. Sessions served over SSH have no id of their121-own, so they ask once per session.122-123-Scripts can't read or write files, reach the network, start programs, or124-read the clock or random numbers: all they can reach is the functions125-below. What a macro can do is what you could do with the keyboard in that126-workbook, which is why it asks before running someone else's.6+# Macro scripting API1277 128-## Writing scripts8+What a [macro](@/sheets/macros.md) script can do. Recorded macros use9+the same functions, so recording one is a quick way to see them.12910 13011 A script runs from top to bottom. It's Starlark: Python's syntax13112 (`for`, `if`, `def`, lists, dicts, string formatting, `while`) without@@ -155,7 +36,7 @@ References are strings in A1 notation: a cell (`"B3"`), a range
15536 as `int`, others as `float`, text as `str`, `True` and `False`, `None`15637 for a blank cell, and error values as their code (`"#DIV/0!"`).15738 158-### Cells39+## Cells15940 16041 | Function | Does |16142 |---|---|@@ -171,7 +52,7 @@ for a blank cell, and error values as their code (`"#DIV/0!"`).
17152 Formulas read inside a script are up to date: a value set a line earlier17253 is already in the formulas that use it.17354 174-### The selection55+## The selection17556 17657 | Function | Does |17758 |---|---|@@ -185,7 +66,7 @@ is already in the formulas that use it.
18566 | `paste_text(text)` | Paste tab-separated text from the active cell, as pasting from another program |18667 | `fill(to=None, rows=0, cols=0)` | Drag the fill handle: continue the selection's series or copy it over `to`, or `rows` down (up if negative), or `cols` right (left if negative) |18768 188-### Sheets69+## Sheets18970 19071 | Function | Does |19172 |---|---|@@ -196,7 +77,7 @@ is already in the formulas that use it.
19677 | `move_sheet(position)` | Move the sheet shown to a position, counting from 1 |19778 | `set_width(cols, width)` | Set column widths, 1 to 240: `set_width("B", 14)`, `set_width("B:D", 8)` |19879 199-### Commands80+## Commands20081 20182 `run(id, answer=None)` runs any command from the menus or palette by its20283 id, on the selection, as if picked. A command that asks something (a@@ -224,7 +105,7 @@ menus, help, undo or macros. The ids are those in the recorded scripts; a few:
224105 225106 Record a macro that uses a command to see its id.226107 227-### Output and errors108+## Output and errors228109 229110 `print(...)` shows its last line on the context line when the macro ends.230111 A failure stops the macro and shows where it happened; there's no@@ -0,0 +1,24 @@
1+---2+title: "Working with sheets"3+sidebar_position: 14+---5+6+# Working with sheets7+8+How to change what a sheet holds and how it looks, and the tools for9+working with its data.10+11+12+13+| Page | For |14+|---|---|15+| [Editing](@/sheets/editing.md) | Selecting, copy and paste, fill, undo, rows and columns, links |16+| [Formatting](@/sheets/formatting.md) | Number formats, text styles, column and row formats, widths |17+| [Sheets and tabs](@/sheets/sheets-and-tabs.md) | Several sheets: adding, moving, hiding |18+| [Freeze, sort and filter](@/sheets/sort-filter.md) | Keeping headers on screen, sorting, filtering by values or conditions |19+| [Find and replace](@/sheets/find-replace.md) | Searching one sheet, all of them or a range |20+| [Conditional formatting and data validation](@/sheets/rules.md) | Coloring cells by their values; dropdowns, checkboxes and entry rules |21+| [Pivot tables](@/sheets/pivots.md) | Summaries of a table on a sheet of their own, and frequency tables |22+| [Notes and protection](@/sheets/notes-protection.md) | Notes on cells; ranges that warn before an edit |23+| [Charts](@/sheets/charts.md) | Charts that float over the grid |24+| [Macros](@/sheets/macros.md) | Recording and running what you do |@@ -1,6 +1,9 @@
1-# Charts, links and the terminal1+---2+title: "Charts"3+sidebar_position: 104+---25 3-## Charts6+# Charts47 58 Insert > Chart charts the selection, or the table around the active cell,69 guessing the header row, the label column and a title. The chart floats over@@ -40,27 +43,3 @@ a real image, drawn in the terminal's own palette and placed with Unicode
4043 placeholders, so redraws never erase it. Elsewhere, and usually inside tmux,4144 the plot is drawn with block and braille characters. Axis labels, the4245 legend and the frame are text either way.43-44-## Links45-46-Cells that hold an `http`, `https` or `mailto` URL, and `=HYPERLINK(url,47-[label])`, are terminal hyperlinks (OSC 8): Cmd- or Ctrl-click opens them in48-terminals that support it.49-50-## Terminal features 012 uses51-52-| Feature | What for | Where it works |53-|---|---|---|54-| Kitty keyboard protocol | telling apart keys like Ctrl+I and Tab, Shift+Enter | kitty, Ghostty, WezTerm, foot, Alacritty, iTerm2 |55-| Mouse, all motion | hover highlights, drag, resize | most terminals |56-| Pointer shape (OSC 22) | resize and text cursors over the grid | Ghostty, kitty, foot, xterm |57-| Kitty graphics | chart images | kitty, Ghostty, WezTerm |58-| Hyperlinks (OSC 8) | links in cells | most modern terminals |59-| Clipboard (OSC 52) | copying ranges | most terminals; tmux needs `set-clipboard on` |60-| Progress (OSC 9;4) | JEV and import progress in the tab | Ghostty, Windows Terminal, iTerm2 |61-| Notifications (OSC 9) | when JEV or an import finishes in the background | iTerm2, Ghostty, WezTerm |62-| Curly, colored underlines | error cells | kitty, Ghostty, WezTerm, iTerm2, Alacritty |63-| Background color query and mode 2031 | light and dark themes, following system changes | most terminals; 2031 in Ghostty and kitty |64-| Synchronized output (2026) | flicker-free redraws | kitty, Ghostty, WezTerm, iTerm2, Alacritty |65-66-Everything degrades quietly where it isn't supported.@@ -0,0 +1,76 @@
1+---2+title: "Editing"3+sidebar_position: 24+---5+6+# Editing7+8+## Selection9+10+Shift+arrows, a mouse drag or Shift+click select a range; Ctrl+arrows jump11+to the edge of the data (add Shift to select as you go); Ctrl+A selects the12+data around the active cell, then everything; Ctrl+Space and Shift+Space13+select whole columns and rows. The active cell stays distinct from the rest14+of the selection, and the status line shows Sum, Avg and Count, dropping the15+ones that don't fit on a narrow terminal.16+17+18+19+## Copy, paste and fill20+21+Ctrl+C, Ctrl+X and Ctrl+V work as in Sheets: relative references shift,22+absolute ones don't, cut and paste moves cells and the formulas that point23+at them follow. Ctrl+Shift+V pastes values only, keeping the destination's24+formats.25+26+Formats travel with the cells. A pasted or moved cell shows what its27+source showed, whether the format was the cell's own or came from its28+row, its column or the whole sheet, and pasting plain cells into a29+currency column leaves them plain. Whole columns or rows (Ctrl+Space,30+Shift+Space) copied and pasted at the top of a column, or the start of a31+row, take their column or row formats along, so a pasted column is32+currency all the way down, not only where it had data; cut and pasted,33+they move them, leaving the source columns plain. Pasted anywhere else,34+they paste as a block of the cells that hold something. A block cut and35+pasted leaves the cells it came from plain, as Sheets does, even where36+a formatted column or row crosses them. Vim's `yy` and `dd` copy whole37+rows, so `p` and `P` bring their row formats along. Formulas reading38+a column whose format changes, blank cells included, show the new format39+at once (`=B5*2` shows currency when column B becomes currency). Copies also go to the40+system clipboard as tab-separated text (OSC 52, so it works over SSH), and41+pasting tab-separated or multi-line text from the terminal fills a block.42+43+Ctrl+D and Ctrl+R fill down and right. The fill handle, shown when you hover44+the corner of the selection, can be dragged to continue a series: 1, 2, 3;45+2, 4, 6; Jan, Feb; Mon, Tue; dates by day or month; Item 1, Item 2. Ctrl+Enter46+while typing enters the same entry into every selected cell.47+48+## Undo49+50+Ctrl+Z undoes and Ctrl+Y or Ctrl+Shift+Z redoes, 100 steps deep, across51+every sheet: undo shows the sheet a step changed. Every change is covered:52+typing, clearing, paste, fill, sort, insert and delete, formats, widths,53+names, charts, freeze, filters, pivot tables, and adding, deleting,54+renaming, moving and duplicating sheets. A multi-cell change is one step,55+and the context line says what was undone, e.g. `Undid: clear B3:B5`.56+57+## Rows and columns58+59+Insert and delete rows and columns from the Insert and Edit menus, the60+right-click menus, or Ctrl+Alt+= and Ctrl+Alt+- (rows, or columns when whole61+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.71+72+## Links73+74+Cells that hold an `http`, `https` or `mailto` URL, and `=HYPERLINK(url,75+[label])`, are terminal hyperlinks (OSC 8): Cmd- or Ctrl-click opens them in76+terminals that support it.@@ -0,0 +1,16 @@
1+---2+title: "Find and replace"3+sidebar_position: 64+---5+6+# Find and replace7+8+Ctrl+F opens a find bar on the context line: matches highlight as you type,9+the active cell follows the current one, and Enter and Shift+Enter step10+through them. Ctrl+H adds a replacement field; Enter replaces and moves on,11+Ctrl+Enter or Alt+A replaces all as one undo step. Chips toggle match case (Alt+C),12+whole cell (Alt+W), regular expressions (Alt+R, with `$1` in replacements),13+searching formulas (Alt+=). The scope chip says where to search, as Sheets'14+"Search" choice: this sheet, all sheets, or the range selected when the bar15+opened; Alt+S goes through them. Searching all sheets steps from sheet to16+sheet in tab order, and replacing all across sheets is still one undo step.@@ -0,0 +1,63 @@
1+---2+title: "Formatting"3+sidebar_position: 34+---5+6+# Formatting7+8+Formats change how values show, never what they are. Select cells,9+whole columns or rows, then pick a format from the Format menu, a key10+([keys](@/reference/keys.md#formatting)) or the palette. Entries such as11+`$1,200`, `12%` or `9/26/2026` take their format as you type them12+([formulas](@/formulas/README.md#what-you-type)).13+14+## Number formats15+16+Format > Number, as Sheets names them:17+18+| Format | Shows |19+|---|---|20+| Automatic | Numbers as typed, or as formulas infer from what they read |21+| Plain text | Entries exactly as typed, even numbers and formulas |22+| Number | Thousands separators: 1,000.12 |23+| Percent | 10.12% |24+| Scientific | 1.01E+03 |25+| Accounting | $ at the left, negatives in parentheses: $ (1,000.12) |26+| Financial | Negatives in parentheses: (1,000.12) |27+| Currency | $1,000.12 |28+| Currency rounded | $1,000 |29+| Date | 9/26/2026 |30+| Time | 3:59:00 PM |31+| Date time | 9/26/2026 15:59:00 |32+| Duration | Elapsed hours, minutes and seconds: 24:01:00 |33+34+Format > Increase and Decrease decimal places show one more or one less.35+A formula left Automatic shows the format of what it reads36+([building formulas](@/formulas/building.md)).37+38+## Text39+40+Bold, italic, underline and strikethrough, and alignment left, center41+or right. Numbers align right, text left, booleans and errors center, as42+in Sheets, until you align them yourself. Format > Clear formatting resets43+number formats and text styles and keeps the contents.44+45+## Whole columns and rows46+47+Formatting whole columns or rows (select them with Ctrl+Space or48+Shift+Space, or click their headers) keeps the format on the column or49+row, as Sheets does, rather than on each of their million cells: every50+cell of it shows the format unless it has its own, and a cell typed into51+later takes it. A cell's format comes from the cell, else its row, else52+its column, else the whole sheet's (Ctrl+A twice, then a format); the53+number format and the text style fall back separately. Copies carry what54+cells show ([editing](@/sheets/editing.md#copy-paste-and-fill)).55+56+## Column widths57+58+Drag a column header's right edge, or use Format > Column width (1 to59+240 characters); double-clicking the edge fits the contents, and Format >60+Reset column width goes back to the default.61+62+Conditional formats, which color cells by their values, are on63+[their own page](@/sheets/rules.md).@@ -0,0 +1,133 @@
1+---2+title: "Macros"3+sidebar_position: 114+---5+6+# Macros7+8+Macros repeat work: record what you do once, then run it again with a9+key. They work like Google Sheets' Extensions > Macros, under10+**Data > Macros**. A macro is a [Starlark](https://github.com/bazelbuild/starlark)11+script (a small, deterministic dialect of Python) saved in the `.012`12+file, as Sheets saves Apps Script with a spreadsheet. Recorded macros are13+scripts too, so you can read them, change them, or write your own.14+15+16+17+## Recording18+19+1. **Data > Macros > Record macro** (or search the palette for "record").20+ `REC` appears beside the mode indicator, and the context line says21+ what's being recorded.22+2. Do the work: type entries, move and select, run commands from the23+ menus, keys or palette, paste, drag the fill handle or a column border.24+3. **Data > Macros > Stop and save recording**. Name the macro on the25+ context line, then give it a shortcut digit, or leave it empty. Esc at26+ either question goes back to recording; **Discard recording** stops27+ without saving.28+29+Two ways to record, as in Sheets:30+31+| | Recorded as | Replays |32+|---|---|---|33+| **Record macro** (absolute references) | `select("B3")`: the cells you selected | On the same cells, wherever the active cell is |34+| **Record macro with relative references** | `move(0, 1)`, `extend(2, 0)`: steps from the active cell | From wherever the active cell is when you run it; formulas typed move with it, as in a copy, and Ctrl+arrows, Home, Ctrl+Home and Ctrl+End are recorded as `jump()`, since where they land depends on the data |35+36+What gets recorded:37+38+- entries as they're accepted (`enter("=SUM(B2:B8)")`), including39+ Ctrl+Enter over a selection (`fill=True`);40+- the selection, only when something acts on it: pressing Down five times41+ then typing records one `move(0, 5)` (or one `select()`), so a replay42+ doesn't depend on the size of the window;43+- commands that change the sheet, by id (`run("format.bold")`), with the44+ answer to the question they ask, if any45+ (`run("column.width", answer="15")`, `run("sheet.delete", answer="enter")`);46+- pasted text (`paste_text(...)`), the fill handle (`fill(...)`), column47+ borders dragged or fitted (`set_width(...)`) and tabs dragged48+ (`move_sheet(...)`);49+- rules added in the conditional formatting and data validation panels,50+ as the commands that add a rule from a line of the file51+ (`run("format.conditional_add", answer='{"ranges":"B2:B9",...}')`), and52+ items picked from a dropdown, as entries. Editing, removing or moving a53+ rule in the panel is noted as a comment, as the dialogs below are.54+55+What doesn't: dialogs such as sorting by several columns, the filter56+picker, find and replace, the chart editor, dragging a chart, and undo.57+Anything like that which changes the workbook is noted in the script as a58+comment, `# Not recorded: sort A2:C9`, so the script never silently does59+less than you did. Files, menus, help and other macros are never part of a60+macro.61+62+## Running63+64+- **The shortcut**, Ctrl+Alt+Shift+digit, in READY. Terminals without the65+ kitty keyboard protocol send it as Ctrl+Alt with the shifted symbol66+ (`Ctrl+Alt+!` for 1 on a US layout), which works too.67+- **Data > Macros > Run macro**, a list of the saved macros.68+- **The palette** (Ctrl+K): type the macro's name.69+70+A run is one undo step: Ctrl+Z undoes everything the macro did. While it71+runs, the mode indicator says `CMD` (as 1-2-3 did), keys and the mouse72+wait, and **Esc stops it**; what it did so far stays, and Ctrl+Z undoes it.73+When it ends, the context line says `Ran Totals`, or the last line the74+script printed, or where it failed:75+76+```77+Totals:3:5: get: not a cell or range: ZZ Ctrl+Z undoes what it did78+```79+80+(`Totals` is the macro's name, `3:5` the line and column in its script.)81+82+Every run has a limit of 10 million Starlark steps, so a script that loops83+forever stops within a second or so with84+`stopped after 10000000 steps: does it loop forever?`.85+86+## Managing87+88+**Data > Macros > Manage macros** lists the saved macros with their89+shortcuts:90+91+| Key | Action |92+|---|---|93+| Enter | Run the highlighted macro |94+| F2 | Rename it |95+| F3 | Change its shortcut: a digit, or empty for none |96+| F4 | Edit its script in your editor |97+| Ctrl+D | Delete it (Ctrl+Z brings it back) |98+| Type | Search by name |99+100+The first row, **+ Write a macro** (also **Data > Macros > Write a101+macro**), names a new macro and opens a script for it in your editor.102+103+Editing uses `$VISUAL`, then `$EDITOR`, then `vi`, with the screen handed104+over until the editor exits; the script is saved when it does. If it has105+a mistake, it's saved anyway and the context line says where106+(`Saved Totals, but it has a mistake at Totals:4:1: undefined: sett`), so107+you can fix it with F4. Sessions served over SSH don't start an editor:108+their macros can be recorded, run, renamed and deleted, not edited.109+110+Changes to macros (recording, renaming, deleting, editing) are undo steps111+and mark the file modified, like any edit.112+113+## Macros from other computers114+115+Opening a file never runs its macros. The file remembers which computer116+its macros were made or trusted on (a random id kept in 012's117+config directory, the one [config.md](config.md) describes). Running118+a macro from a file made elsewhere asks once, on the context line:119+120+```121+Trust this file's macros? Enter Run Esc Cancel122+```123+124+Enter trusts all of the file's macros for this session and, once the file125+is saved, on this computer. Sessions served over SSH have no id of their126+own, so they ask once per session.127+128+Scripts can't read or write files, reach the network, start programs, or129+read the clock or random numbers: all they can reach is the functions130+below. What a macro can do is what you could do with the keyboard in that131+workbook, which is why it asks before running someone else's.132+133+Macros are Starlark scripts you can read and write: the [scripting API](@/reference/macro-api.md) lists what they can do.@@ -0,0 +1,41 @@
1+---2+title: "Notes and protection"3+sidebar_position: 94+---5+6+# Notes and protection7+8+## Notes9+10+Insert > Note (Shift+F2, or Insert note on the right-click menu) adds a11+note to the active cell, as Sheets' notes: plain text typed on the context12+line, where Alt+Enter or Shift+Enter starts a new line (shown as ↵), and13+an empty note deletes it. A cell with a note shows ▝ in its top-right14+corner; while it is the active cell, the context line shows the note, and15+while the mouse is over it, a box beside it does. Delete notes (on the16+right-click menu) removes the notes of the selection.17+18+A note belongs to its cell: clearing the contents (Del) keeps it, it moves19+with the cell when rows or columns are inserted, deleted or sorted, and a20+copy or a cut takes it along, as in Sheets. Ctrl+Enter fills the entry21+into the selection and leaves each cell's note alone. Every change to a22+note is an undo step. Pivot table results can't take notes.23+24+## Protected sheets and ranges25+26+Data > Protect sheets and ranges works as Sheets' protection with "Show a27+warning when editing this range": it locks nothing, but an edit that28+touches a protected range, or anything on a protected sheet, asks first on29+the context line: Enter edits anyway, Esc cancels. The picker lists the30+sheet's protections; its first rows protect the selection or the whole31+sheet, with an optional description, Enter on a protection selects its32+range, and Ctrl+D removes it. Protect range and Remove protection on the33+right-click menu and in the palette do the same for the selection.34+35+What asks: typing into a cell, commands that change cells (clear, paste,36+fill, formats, sort, delete rows), pasting text from the terminal and the37+fill handle. Inserting rows or columns moves a protected range without38+asking, unless the whole sheet is protected. Macros that run don't ask, as39+Sheets' scripts don't. Protected ranges follow inserted and deleted rows40+and columns, save with the sheet, and adding or removing one is an undo41+step.@@ -0,0 +1,78 @@
1+---2+title: "Pivot tables"3+sidebar_position: 84+---5+6+# Pivot tables7+8+9+10+Data > Pivot table summarizes the selection, or the table around the11+active cell, on a new sheet named `Pivot Table 1`, as Sheets' Insert >12+Pivot table does. The data's first row names the fields. The pivot editor13+opens docked at the right of the grid, with the results updating behind it:14+15+- **Rows** group the data by a field's values, one row per value; with two16+ or more, the groups nest and each outer group gets a subtotal row.17+- **Columns** spread the groups across columns by a field's values; with18+ two or more, each outer group gets a subtotal column (`East Total`)19+ after its last column.20+- **Values** summarize a field for each group: SUM, COUNTA, COUNT,21+ COUNTUNIQUE, AVERAGE, MAX or MIN (Sheets' "Summarize by"), shown as they22+ are or as a share of their row, column or grand total ("Show as").23+ A new value is summed when its field holds numbers, and counted24+ otherwise. Its header is Sheets' `SUM of Units` until you rename it.25+- **Filters** leave out rows by a field's values or a condition, in the26+ same picker as a filter's column.27+- **Grand total row** and **Grand total column** add Sheets' totals, and28+ turn the subtotal rows and columns on and off with them.29+30+Up and Down pick a line. Space adds a field to the section it's on (pick31+it from the fields of the data, type to narrow the list), opens a filter's32+values, or flips a toggle; Space on the data range points at a new one on33+its sheet. On a row or column field, Left and Right order its groups A to34+Z, Z to A, or by a value's total, smallest or largest first, and35+Shift+Up and Shift+Down move it before or after the others, which changes36+how the groups nest. On a value, Left and Right change how it's37+summarized, S how it's shown, and R (or F2) renames it on the context38+line, the name heading its columns; an empty name goes back to Sheets'.39+A value keeps its name when its summary changes. Del removes a field. Every change is an40+undo step; Enter keeps them, Esc undoes them, and removes a pivot just41+created. Data > Edit pivot table opens the editor again.42+43+Groups follow Sheets: text ignoring case (`east` joins `East`), numbers44+and dates by value, with the source's format, so dates show as dates.45+Numbers and text never share a group: `1` and the text `'1` are two.46+Groups sort numbers first, then text, booleans and errors, and blank47+cells last; as in Sheets, the group of blank cells has a blank label,48+and its subtotal is just `Total`. Sorting an outer column field by a49+value's total orders whole outer groups, so their columns stay50+together. Rows blank across the whole range are51+left out, so a range can reach past the data. SUM, AVERAGE, MIN and MAX52+keep the column's number format: the column's own when it has one, else53+the format most of its numbers show in (ties go to the first), so one54+price typed as `$9,000` among `$2.50`s doesn't turn every total into55+whole dollars. A group's label shows in the format of its first cell. An56+error in a summed column shows as that error, as SUM would.57+58+The results are live: any change to the data, typed, pasted, filled or59+computed by a formula, recomputes the pivot, and rows or columns inserted60+inside the range widen it. Formulas can read the results (`='Pivot Table61+1'!B5`), and copying them pastes their values. The results themselves62+can't be edited, as in Sheets: typing, pasting, clearing, formatting,63+sorting or inserting rows or columns over them is refused with a note on64+the context line. A pivot shows `#REF!` in A1, explained on the context65+line, when its data's sheet is deleted or when its results would66+overwrite a cell typed next to them; undo or moving the cell brings it67+back. The sheet's columns widen to fit the results until you set a width68+yourself.69+70+## Frequency tables71+72+Data > Frequency table (column stats), or Alt+Shift+F (VisiData's73+Shift+F; plain Shift+F types an F), counts each value of the active74+column of the table around the active cell on a new sheet, `Frequency of75+<field>`: the value, how many rows have it and their percentage, most76+frequent first, with a Grand Total. Blank cells are counted too. It is a77+pivot table like any other, so it stays live, and the pivot editor78+changes it.@@ -0,0 +1,97 @@
1+---2+title: "Conditional formatting and data validation"3+sidebar_position: 74+---5+6+# Conditional formatting and data validation7+8+## Conditional formatting9+10+Format > Conditional formatting opens the sheet's rules in a panel at the11+right of the grid, as Sheets' sidebar: `+ Add rule` adds one over the12+selection, Enter edits the highlighted rule, Del removes it and13+Shift+Up/Down moves it. In a rule's form, Up/Down pick a line,14+Left/Right change a choice, Space flips a check, text lines are typed15+into, Enter saves and Esc goes back without saving. Every change is an16+undo step.17+18+19+20+A rule applies to ranges (`A2:A100`, or several: `A2:A9,C2:C9`) and is21+one of:22+23+- **Single color**: cells that meet a condition get a text color, a fill,24+ and bold, italic, underline or strikethrough. The conditions are25+ Sheets': is empty, is not empty, text contains, does not contain,26+ starts with, ends with, is exactly (ignoring case), date is, is before,27+ is after (a date, or today, tomorrow, yesterday), greater than, greater28+ than or equal to, less than, less than or equal to, is equal to, is not29+ equal to, is between, is not between, and custom formula is. A value30+ may be a formula (`=B2`), and a custom formula (`=$C2>100`) is written31+ for the first cell of the first range: its relative references move32+ with each cell, as a copied formula's would.33+- **Color scale**: every number in the ranges is shaded along two or three34+ colors, from its minimum to its maximum; each point is the minimum or35+ maximum value, a number, a percent of the way between them, or a36+ percentile, and a midpoint is optional.37+38+Rules are tried in order, and the first that applies to a cell formats39+it, so a rule higher in the list wins. The pointer and the selection draw40+over a rule's colors; its text styles show through them.41+42+Colors are named (red, yellow, green, cyan, blue, magenta) rather than43+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](themes.md))45+decides what they look like, and a fill's text is drawn in whichever of46+black or white reads on it. A text color that wouldn't read on a rule's47+fill takes the fill's ink instead. Color scales blend the scheme's colors48+(or the terminal's, as it reports them) and pick readable text for each49+shade.50+51+Rules only change how cells look, so they may go on a pivot table's52+results. They follow inserted and deleted rows and columns, and a rule53+whose cells are all deleted goes. Format > Clear conditional formats54+takes the selection out of every rule.55+56+## Data validation57+58+Data > Data validation opens the sheet's validation rules in the same59+panel; Insert > Dropdown opens it with a dropdown for the selection, and60+Insert > Checkbox makes the selection checkboxes at once. The criteria:61+62+| Criteria | Cells may hold | Shows |63+|---|---|---|64+| Dropdown | one of the items typed (`Yes, No, Maybe`), ignoring case | ▾ at the right of the cell |65+| Dropdown (from a range) | one of the values in a range, on any sheet (`Lists!A1:A20`) | ▾ |66+| Checkbox | TRUE or FALSE; a blank cell is unchecked | `[✓]` or `[ ]`, centered |67+| Number | a number between, not between, equal to, greater than ... values | |68+| Date | any date, or one on, after, before, between ... dates | |69+| Text length | text whose length is between, less than ... | |70+| Custom formula is | anything for which the formula (`=B2<=C2`) is TRUE | |71+72+Alt+Down, or a click on the ▾, opens a dropdown's list under the cell:73+type to search, Enter enters the item as if typed. Space, or a click on74+the box, checks or unchecks the selected checkboxes (all checked when75+the active one isn't, as in Sheets); Space anywhere else starts an entry76+as usual. When the active cell has no dropdown, Alt+Down opens the77+column's filter.78+79+Each rule either **rejects** an invalid entry or **shows a warning**.80+Rejected, the entry stays open with Sheets' message on the context line81+("Invalid entry in C2: Input must be a number between 0 and 80"), for82+fixing or Esc. Warned, it goes in, and the cell's text gets a dotted83+underline; on the active cell the context line says why ("Invalid:84+Input must be ..."). A rule's help text, when it has one, replaces its85+own words there. A formula is checked by the value it computes. Blanks86+are always valid.87+88+Each cell has one rule: a rule added over cells that had another takes89+them from it. Validation checks what's typed, picked from a dropdown and90+entered by macros; pastes, fills and imports aren't refused, and what91+they leave invalid is marked. Validation can't go on a pivot table's92+results, which take no entries. Data > Remove data validation takes the93+selection out of every rule.94+95+Adding a checkbox or a dropdown changes no cells, so on a protected range96+it doesn't ask first (on a protected sheet it does); checking a box or97+picking an item asks as typing does ([protection](#protected-sheets-and-ranges)).@@ -0,0 +1,24 @@
1+---2+title: "Sheets and tabs"3+sidebar_position: 44+---5+6+# Sheets and tabs7+8+A spreadsheet holds several sheets, shown as tabs at the left of the9+status line (as tmux lists its windows). Formulas read other sheets by10+name, `=Sheet2!A1` ([references](@/formulas/references.md)).11+12+| To | Do |13+|---|---|14+| Add a sheet | Shift+F11, Insert > Sheet, or click `+` after the tabs |15+| Go to the next or previous sheet | Ctrl+PgDn, Ctrl+PgUp (or Alt+Right, Alt+Left), or click a tab |16+| Go to a sheet by name | Alt+Shift+K |17+| Rename, duplicate, delete, hide, move left or right | Edit > Sheet, or right-click a tab; double-click a tab to rename it |18+| Move a sheet | Drag its tab onto another |19+| Show a hidden sheet | View > Hidden sheets |20+21+When the tabs don't all fit, `‹` and `›` step through them. A hidden22+sheet is still read by formulas; next, previous and the tabs skip it.23+While typing a formula, Ctrl+PgDn or clicking a tab points into that24+sheet, as in Sheets. Every change to sheets is an undo step.@@ -0,0 +1,31 @@
1+---2+title: "Freeze, sort and filter"3+sidebar_position: 54+---5+6+# Freeze, sort and filter7+8+## Freeze9+10+View > Freeze keeps rows or columns on screen while the rest scrolls, marked11+by thin divider lines. The mouse and keys treat the frozen area as Sheets12+does.13+14+## Sort15+16+Data > Sort sheet sorts by the active column A to Z or Z to A, keeping frozen17+rows on top. Data > Sort range sorts the selection, or the block of data18+around the active cell; its advanced option opens a bar to sort by several19+columns (Left/Right picks a column, Space flips the order, Alt+A adds a20+column, Alt+H toggles the header row). The order follows Sheets: numbers,21+then text ignoring case, then booleans, then errors, with blanks last.22+23+## Filter24+25+Data > Create a filter puts a filter on the selection or the table around26+the active cell; headers show `▾`. Alt+Down or a click on `▾` opens the27+column's filter: check values in a list (Space checks one, typing28+narrows the list), or pick a condition such as "greater than" or "text29+contains". Filtered-out rows are hidden, not30+deleted: row numbers show the gap, navigation skips them, formulas still31+count them, and the status line says how many rows are hidden.@@ -0,0 +1,33 @@
1+---2+title: "The terminal"3+sidebar_position: 14+---5+6+# The terminal7+8+012 uses what the terminal it runs in can do, and degrades quietly where9+it can't: charts as images or text, links, the clipboard, the mouse,10+colors that follow light and dark.11+12+| Page | For |13+|---|---|14+| [Themes](@/terminal/themes.md) | Your terminal's colors, a built-in scheme, or your own |15+| [Serving over SSH](@/terminal/ssh.md) | `012 serve`: a 012 per session, on one directory |16+17+## Terminal features 012 uses18+19+| Feature | What for | Where it works |20+|---|---|---|21+| Kitty keyboard protocol | telling apart keys like Ctrl+I and Tab, Shift+Enter | kitty, Ghostty, WezTerm, foot, Alacritty, iTerm2 |22+| Mouse, all motion | hover highlights, drag, resize | most terminals |23+| Pointer shape (OSC 22) | resize and text cursors over the grid | Ghostty, kitty, foot, xterm |24+| Kitty graphics | chart images | kitty, Ghostty, WezTerm |25+| Hyperlinks (OSC 8) | links in cells | most modern terminals |26+| Clipboard (OSC 52) | copying ranges | most terminals; tmux needs `set-clipboard on` |27+| Progress (OSC 9;4) | JEV and import progress in the tab | Ghostty, Windows Terminal, iTerm2 |28+| Notifications (OSC 9) | when JEV or an import finishes in the background | iTerm2, Ghostty, WezTerm |29+| Curly, colored underlines | error cells | kitty, Ghostty, WezTerm, iTerm2, Alacritty |30+| Background color query and mode 2031 | light and dark themes, following system changes | most terminals; 2031 in Ghostty and kitty |31+| Synchronized output (2026) | flicker-free redraws | kitty, Ghostty, WezTerm, iTerm2, Alacritty |32+33+Everything degrades quietly where it isn't supported.