paged.sheet — spreadsheets
The spreadsheet plugin — a Rust/WASM calc engine with spill, structured tables, charts, and XLSX round-trip, lowered to native print-grade tables and editable in-frame.
In short paged.sheet is a full spreadsheet engine in Rust/WASM that lives
inside a print layout — a dependency-graph calc engine with dynamic-array spill,
structured tables, a chart engine, and preservation-safe XLSX round-trip. It
lowers to native <Table> content and is editable in-frame.
What it does
- Calc engine (dependency graph, dirty recalc, Excel-compatible numerics) with 263 registered functions and dynamic-array spill.
- Structured tables, a chart engine (plotters → a custom drawing backend → frozen chart geometry), and number formatting with locales.
- XLSX import/export with a byte-faithful preservation invariant.
- Page-surface lowering to native tables, multi-frame pagination, and an in-frame grid with a modal edit session; consumes governed datasets from paged.data.
- Spreadsheet editing in the grid: range selection, the grid keys, find, formula-carrying copy and paste, the fill handle, a formula bar that completes function names from the engine, and host undo outside the edit session.
- In development: cell styles, merged cells, row and column sizes, freeze panes, defined names, CSV import, and the workbook as typed objects for scripts and bindings (the object model).
How it's built
A Cargo workspace (sheet-core, sheet-parser, sheet-calc, sheet-fn,
sheet-xlsx, sheet-lower, sheet-grid, sheet-chart, sheet-js) plus a TS
bundle; a coverage gate requires every function to have a test.
Capability state
● shipped & verified · ○ shipped, re-test pending · ◍ shipped, untested · ◐ partial · ✗ failing · · planned
Plugin · sheet 47 features · 100% green
| Feature | API | Sheet |
|---|---|---|
paged.sheet serves the HOST Swatches and Character/Paragraph panels (ADR 023 phase D)sheet.binding-providers | ● | ◐ |
Exact-decimal numeric mode (D-6 spike) — seam proven, deferred to v2 opt-insheet.calc.decimal | ◐ | |
Calculation engine — dependency graph, dirty recalc, f64 numeric core (D-6), coercion, error valuessheet.calc.engine | ● | |
Iterative (circular) calculation — OFF by default (D-7), converges to a fixed point when enabledsheet.calc.iterative | ● | |
Row/col insert-delete wasm door — structural re-emission, refused over preserved cell addressessheet.calc.rewrite.structural-door | ◐ | |
Dynamic-array spill (anchor + spill range,sheet.calc.spill | ● | |
Chart authoring — create a chart from the editor (page-side lowering)sheet.chart.authoring | ● | |
Charts — plotters-backed custom DrawingBackend → frozen ChartGeometry IR → grid view + paged.draw page loweringsheet.chart.engine | ● | |
Graph types — stacked column/bar + radar, axis titles, transpose rows/columns, c:grouping importsheet.chart.graphs | ● | |
Data-provider CONSUMER — source a sheet from a governed dataset (host.dataProviders)sheet.data.consumer | ● | |
Multi-cell copy/paste — formulas re-addressed on paste, tabular payload, TSV interop, tilingsheet.edit.clipboard | ● | |
Fill handle and fill down/right — number, date, weekday/month and numbered-text series; formulas re-addressedsheet.edit.fill | ● | |
Session edit ops — stable range sort (values lane), find over values/formulas, replace with skip reportingsheet.edit.ops | ● | |
Host Cmd-Z reaches sheet edits outside the modal session (ADR-012 Tier 2)sheet.edit.undo | ● | |
Function library — registered functions by family, registry-driven dispatchsheet.fn.library | ● | |
Cell formatting — number format, font, fill, borders, alignment, wrap (setStyle) persisted to styles.xmlsheet.format.cell-style | ◐ | |
Number-format engine + en/de locale (D-8)sheet.format.engine | ● | |
Western-European Latin locale tier (fr/es/it) — display formatting data rowssheet.format.locale.latin-tier | ● | |
Find in sheet — a find field in the grid panel; next/previous select and reveal the match in-framesheet.grid.find | ● | |
Frozen panes — parse sheetViews/pane + render frozen rows/columns in-gridsheet.grid.freeze.render | ● | |
In-frame grid — sceneLayer render + K-1 modal select/edit (sheets-mode lite)sheet.grid.inframe | ◐ | |
Grid keys — arrows, Tab/Enter, F2, Delete, Esc, PageUp/Down and Cmd shortcuts in-frame and in the panelsheet.grid.keyboard | ● | |
Grid panel edit and commit contractsheet.grid.panel-edit-contract | ● | |
Range selection — drag, shift-click and shift-arrows in the grid panel and in-framesheet.grid.selection | ● | |
CSV / TSV import as a one-sheet workbook, typed by localesheet.import.csv | ● | |
Freeze panes — set / clear (sheetView pane) from the panelsheet.layout.freeze | ● | |
Merge / unmerge cells (model, xlsx mergeCells, page spans)sheet.layout.merge | ● | |
Column width / row height — set and reset, persisted, used by the page geometrysheet.layout.sizes | ◐ | |
Cell fill / text colours are MINTED swatches — a raw hex in a colorRef paints nothingsheet.lower.cell-colour-swatches | ◐ | |
Page-surface lowering — native <Table> + IR-v2 style map + measured column widthssheet.lower.page | ● | |
Live multi-frame pagination across the host frame chainsheet.lower.paginate | ◐ | |
Defined names — resolve on load, define / redefine / delete, place a name or tablesheet.names.define | ● | |
Object model — workbook, sheets, cells, ranges, names, tables and charts addressable through host.objectssheet.objects | ● | |
Formula dialect parser — literals, A1 refs, operator precedence, functions, union/intersection, array literals, defined names, canonical print fixpointsheet.parser.dialect | ● | |
paged.sheet bundle (media.paged.sheet) — manifest + activate + 3 panels + 7 commands + importer/exporter + engine bootsheet.plugin.bundle | ● | |
Formula bar bound to the selected cell, with function autocompletion from the enginesheet.plugin.formula-bar | ● | |
Function browser + workbook inventory + the styleFromCell command (workbook panel)sheet.plugin.function-browser | ● | |
Workbook persistence — host.blob (OPFS) + the metadata binding envelopesheet.plugin.persistence | ◐ | |
Workbook visual styles — xlsx parse -> deduped IR-v2 style table -> both surfaces (one resolver, cross-surface parity)sheet.style.mapping | ◐ | |
Excel structured tables / ListObjects — model + structured references + tableN.xml partsheet.table.structured | ● | |
get_range_styled — the workbook's REAL per-cell visual styles, as a door of its ownsheet.wasm.range-styled-door | ◐ | |
Workbook structure — new blank workbook, add / rename / delete sheetsheet.workbook.sheets | ◐ | |
Cell comments — preserve opaquely + display (grid indicator + panel list), preserve-firstsheet.xlsx.comments | ● | |
Conditional formatting — xlsx part fidelity + lowering to style overridessheet.xlsx.condfmt | ◐ | |
XLSX data validation — preserve byte-faithfully + read-only inventory (NEVER enforced)sheet.xlsx.data-validation | ● | |
Pivot tables — preserve-only, NEVER interpreted (publishing-first non-goal)sheet.xlsx.pivot | ● | |
XLSX I/O on the fidelity ladder — preservation invariant + import/export through the platformsheet.xlsx.roundtrip | ● |
Recent activity
- 2026-10-18README: moved to paged-media/paged (modules/sheet) [skip ci]cdea034
- 2026-10-18Merge pull request #12 from paged-media/fix/placement-active-spread89ae789
- 2026-10-18bundle: new content goes to the spread in view, not to a far-off selection11a6de2
- 2026-10-17Merge pull request #10 from paged-media/fix/gesture-reflow-header-bands0217708
- 2026-10-17Merge origin/main into fix/gesture-reflow-header-bands55b39c7
- 2026-10-17Merge pull request #11 from paged-media/fix/zip-93d5bd4a
- 2026-10-17test: every saved entry carries the pinned zip headers, over the whole corpus06b30c9
- 2026-10-17xlsx: zip 2.4.2 -> 9.0.4; a save writes the same bytes as before6662429
paged.image — raster
The raster-image plugin — a Rust/WASM pipeline of GPU kernels with PSD round-trip, dual processing engines, color management, and in-editor ingest into the engine's image scene layer.
paged.data — database publishing
The database-publishing plugin — a binding DSL, a sync engine, record-flow pagination, and batch automation over DuckDB-WASM, publishing datasets other plugins can consume.