cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

Document the file formats table, charts and telemetry, and the split screen tests

Dami committed 44a8bd2
architecture.md gains sections on internal/fileio, internal/chart and
internal/telemetry; extending.md's registries name the fileio and chart
tables and drops their gaps; limits.md says which importers stop at the
row limit and what charts cost; testing.md and UX.md point at the new
screen list file.
commit
44a8bd27038d20a07449f6b93aa1a3f9051a33cf
parent
534700c830f52c2d310c9ebede99c78a60058ddc
author
Dami <dami.etoile@gmail.com>
5 files changed, +69 -12
Mdocs/UX.md2+-
Mdocs/architecture.md47++++++++++++++++++++++++++++++
Mdocs/extending.md8+----
Mdocs/limits.md20++++++++++---
Mdocs/testing.md4++-
M docs/UX.md +1 -1
M docs/architecture.md +47 -0
@@ -9,6 +9,8 @@ internal/sheet the engine: cells, parsing, evaluation, recalculation, undo, fi
99 internal/fileio import and export: CSV, TSV, XLSX, SQLite, Parquet, Lotus .wk11010 internal/chart chart layout, text rendering and kitty image encoding1111 internal/jev JEV configuration, answer cache and TypeSafe client12+internal/telemetry opt-in JSON log and OTLP export of spans, events and frame stats13+internal/stress synthetic worst-case sheets for the -tags stress benchmarks1214 internal/ui the Bubble Tea model: modes, menus, overlays, rendering1315 e2e/ end-to-end tests through libghostty (separate module, cgo)1416 oracle/ differential tests against excelize (separate module)
@@ -61,6 +63,51 @@ user's palette applies.
6163 6264 See [UX.md](UX.md) for the rules every change follows.6365 66+## Files67+68+`internal/fileio` moves data between sheets and other formats. Each format69+is one row of the table in `formats.go` (its name, extensions, the words70+the UI shows for it, whether it holds several sheets or tables, and its71+importer and exporter) plus a file of its own; `Import`, `Export`,72+recognizing a file by its extension, the import picker and the Download73+menu all come from the table. Importers build a new workbook through the74+engine's public API with a shared `builder`, which keeps text as text,75+falls back to a formula's cached value when it can't be translated, and76+counts what didn't fit. They stream: CSV and TSV are read record by77+record, Parquet a batch of rows at a time, SQLite a row at a time, and78+Parquet files and SQLite tables stop at the sheet's last row, taking the79+number of rows left out from the file. XLSX goes through excelize, which80+holds the worksheet in memory while its rows are read, so it is the one81+format read twice; its files are small next to the others' and it's82+bounded by the sheet's size. Exporters write a `Snapshot` taken on the UI83+goroutine, in the background, through one atomic write-then-rename; CSV84+and TSV are written a row at a time. One package suits formats that share85+this much (the builder, number formats, serial dates and Excel formula86+translation); a format that grew its own dependencies would move to a87+subpackage behind the same table row.88+89+## Charts90+91+`internal/chart` draws a chart in two layers that share one layout: text92+for any terminal (block elements for bars, braille for lines, half blocks93+for pies) and, on terminals with kitty graphics, an image of the plot area94+with the axes and legend still terminal text. Each chart type is a layout95+in the `types` table, returning a plan that draws it both ways; the type96+constants and their names belong to `internal/sheet`, since charts are97+saved with sheets. Work is bounded by the chart's size, not its data: only98+the categories that fit are drawn, pie slices are found by binary search,99+and a pie image supersamples only pixels on a slice edge or the rim.100+101+## Telemetry102+103+`internal/telemetry` is off unless asked for. When on, spans, events and104+per-second frame summaries go to a JSON log, an OTLP/HTTP collector, or105+both (see [observability.md](observability.md)). The OTLP exporter uses106+only the standard library: bounded queues per signal, drained by one107+goroutine in batches; when a queue is full new items are dropped and108+counted, so the UI never waits on the network. Operation durations are109+histograms keyed by span name, which are fixed in the code.110+64111 ## Testing65112 66113 See [testing.md](testing.md).
M docs/extending.md +2 -6
@@ -16,8 +16,8 @@ shows it is derived from the table rather than listing it again.
1616 |---|---|---|1717 | `sheet.FuncDef` | a function: name, signature, description, arity, eval | parsing, autocomplete, argument hints, help, [functions.md](functions.md) |1818 | `ui.command` | an action: id, title, description, run, enabled, checked | key bindings, menu bar, context menus, palette, shortcuts help |19-| file formats | a format: name, extensions, importer, exporter | import picker, File > Download, command line, detection |20-| chart types | a type: name, text renderer, image renderer | chart editor, Insert > Chart |19+| `fileio` formats (`formats.go`) | a format: name, extensions, labels, traits, importer, exporter | `Import`, `Export`, detection, import picker, File > Download, command line |20+| `chart.types`, with `sheet.ChartTypes` | a type: name and order (sheet, saved in files), a layout drawing text and image (chart) | `chart.Draw`, `chart.Image`, chart editor, Insert > Chart |2121 | theme roles | a role: dark and light styles on the 16 ANSI colors | every style in the UI |2222 2323 Adding a function, command, format or chart type means adding an entry
@@ -111,10 +111,6 @@ a golden screen reviewed in the gallery for anything visible (see
111111 112112 Where the code doesn't follow the patterns yet:113113 114-- File formats are a closed `Kind` list with switches in `Import` and115- `Export` (pattern 1).116-- Chart types are switches on `sheet.ChartType` in `internal/chart`117- (pattern 1).118114 - `sheet.Remote` is a package global; it belongs to the workbook (pattern 4).119115 - Cells are a bare `map[Addr]*Cell` used directly across the engine120116 (pattern 6).
M docs/limits.md +16 -4
@@ -89,7 +89,14 @@ terminal (`BenchmarkFrame`).
8989 | 20 charts (text) | 0.82 ms | 2.9 ms | 7.3 ms |9090 9191 About half of a frame at 400 x 120 is Bubble Tea's parse and diff of the92-view string. Key presses add little: an arrow key through to its frame92+view string.93+94+A chart drawn as text costs 4 to 50 us at 24 x 10 to 120 x 40 cells,95+whatever its data: only the categories that fit are drawn (a pie of 819296+slices, 0.44 ms, is the worst). An image for kitty graphics is redrawn97+when its data, size or theme changes: 25 to 260 us at 24 x 10 cells and98+1 to 1.5 ms at 120 x 40, a pie 8.6 ms (`internal/chart`,99+`BenchmarkDraw`, `BenchmarkImage`). Key presses add little: an arrow key through to its frame93100 is 0.21 ms at 80 x 24 and 1.06 ms at 200 x 60; Page Down 1.26 ms.94101 95102 With the whole of an 8192 x 256 sheet selected, the status line's Sum,
@@ -113,8 +120,12 @@ Real, openly licensed datasets fetched by `scripts/stress-data.sh`
113120 | Parquet alltypes_tiny_pages | 7300 x 13 | 40 ms | 2.4 M cells/s | 276 B |114121 115122 Past the limits, imports keep the first 8192 rows and 256 columns and116-say how much they left out; reading the rest of the file still costs117-(the airport codes file is read to its end).123+say how much they left out. Parquet files and SQLite tables stop reading124+at the last row and take the count of the rest from the file; CSV, TSV125+and SQLite queries are read to their end to count it, without keeping126+it (the airport codes file is read to its end). Every importer but XLSX127+streams, so memory follows the sheet, not the file; excelize holds an128+XLSX worksheet in memory while its rows are read.118129 119130 ## Undo120131
@@ -251,7 +262,8 @@ make stress-e2e # key press to screen through libghostty
251262 Benchmarks are built only with `-tags stress`, so `go test ./...` stays252263 fast. They live in `internal/sheet/stress_test.go` (engine),253264 `internal/ui/stress_test.go` (View, frames, keystrokes, JEV, telemetry254-overhead), `internal/fileio/stress_test.go` (imports and exports) and265+overhead), `internal/fileio/stress_test.go` (imports and exports),266+`internal/chart/stress_test.go` (charts as text and images) and255267 `e2e/stress_test.go`; the synthetic sheets are built by256268 `internal/stress`. Results accumulate in `.deps/stress/results` and, with257269 the observability stack up, in ClickHouse.
M docs/testing.md +3 -1