Repository navigation
Releases: lianghsun/open-sheet
Release list
v0.1.8 — six rounds of real workbooks
Twenty-three fixes, most of them found by building real workbooks against the
framework rather than by testing it as a library. Two were broken from the day
they shipped.
Broken since release
Leaving a note from the Inspector broke the workbook — every time. The marker
was written as //, which is a comment in JS but a bare text node in JSX
children, where a <Table> always sits. Every note ever left stopped the workbook
compiling, with an error reading <Stack> got bare text — which looks nothing
like "you left a note". The panel advertises the feature; it is the entry to the
collaboration path.
sheet-authoring/SKILL.md shipped overwritten since 0.1.3. A scripted edit
missed its target and wrote one of the skill's own reference files over it,
taking the file contract, the component surface, and the self-review checklist.
Nothing imports a skill and no build step reads one, so it failed in silence —
the only symptom was worse workbooks written by someone else's agent.
Both now have tests. The note test is named for what it missed:
leaves the file parseable — the test this feature shipped without.
Functions that existed but could not do the thing you wanted them for
IFERRORcould never catch anything. Arguments were evaluated eagerly and
any Excel error returned immediately, so it never reached its implementation.
IFevaluated both branches, failing on exactly the rows its guard existed for.
All three are lazy now, as Excel evaluates them.SUMPRODUCTdid not support arrays. Summing one plain range is justSUM;
array comparison is the entire reason to reach for it, so the evaluable subset
was precisely the part nobody uses.ABS,ROUNDand friends stopped at a range. They map now. Aggregates
deliberately do not:MINover a range is a minimum, not one per cell.rawtagged templates rejected expressions. The signature saidRef | Expr
and onlyRefwas implemented, so interpolatingr.cell('x')— the commonest
case — crashed the writer whiletscstayed happy.
Honesty
Anything the evaluator cannot compute now reports #NOT_EVALUATED rather than a
fabricated #VALUE!. That kept an invented Excel error out of the exported cache,
and stops iferror from swallowing a gap in this evaluator as though it were a
real spreadsheet condition.
Number formats honour Excel's sections, so accounting shows (84,500) and -
in the viewer as it does in Excel. The viewer and the workbook disagreeing about
the same cell is the failure a "what you see is what exports" tool can least
afford.
Errors that say what to do
Build failures name the workbook and file. Cycles name the construct — "costs" column "b" row 1 — rather than an internal coordinate the author never wrote.
A formula that fails to resolve names its block, column and row, instead of
leaving you to bisect a 450-line workbook by hand. r.cell() on a field with no
column explains both ways forward and names the tradeoff.
--help no longer runs a build and overwrites dist/.
What the framework cannot check for you
Three real workbooks produced three failures with one shape: every reference
correct, every figure sourced, nothing wrong in any cell — and the result wrong
anyway.
| Shape | Example |
|---|---|
Ordering decided in data |
a sorted array: right this month, stale next month |
| Periods of unequal length | two days of data beside thirty → +790% |
| Sources of unequal coverage | 30 days of cost ÷ 20 days of requests → wrong by 50% |
The framework guarantees referential integrity. It cannot guarantee that the
things being compared are comparable, and that class of error looks identical to
a correct result. sheet-authoring now names it, with the pattern that avoids
the third: normalise each figure by its own coverage before combining them.
Also
Design panel stops misreporting state; the viewer keeps your place across the
reload an edit triggers; comment markers name their block; elementwise mapping,
periods-as-columns, computed columns, and CSV's raw values are documented.
npx @open-sheet/cli init my-sheetsv0.1.2 — the viewer actually renders
Two patches after the first release, both fixing the same symptom: a blank viewer in every published install.
What was wrong
0.1.0 and 0.1.1 worked from a clone and were broken from npm. The main entry
reached Node-only code — the CLI, the dev server, and the optional
import('playwright') behind PDF export — and a workbook imports that entry and
is evaluated in the browser by the viewer. Under npm's flat node_modules
that handed the browser CommonJS it could not import, and Vite's dependency
optimizer could not analyse the dynamic import at all. The page died before
mounting.
What changed
Node-only exports moved to @open-sheet/core/node. The main entry is
browser-safe: authoring components, formula builders, references, compile,
evaluate, the style model, and the pure exporters. A workbook no longer pulls
exceljs or Vite into the browser.
0.1.1 separately fixed @vitejs/plugin-react being bundled into dist, which
made its Fast Refresh runtime resolve to a file that does not exist.
Why it shipped twice
Both faults existed only in the packaged shape, and both returned HTTP 200 for
every module. The pre-publish rehearsal checked status codes, which proved
nothing about whether anything rendered.
There is now a CI job that packs the packages, installs them into a clean
directory, drives a real browser, and asserts the grid painted formatted values
with an empty console. It is the only check that would have caught either bug.
Upgrading
rm -rf node_modules/.vite
npm install @open-sheet/core@latestv0.1.0 — first release
The spreadsheet framework built for agents. First release.
npx @open-sheet/cli init my-sheetsWhat it does
You author a workbook as JSX with named data columns, and export a .xlsx
containing live formulas — no cell address appears anywhere in the source.
Add a row to the data and every reference re-resolves.
col('grossProfit', { formula: (r) => sub(r.cell('revenue'), r.cell('cogs')) })exports as =B5-C5, and an assumption referenced by name exports as
=F6*(1-taxRate) against a real Excel defined name — legible to whoever opens
the file, not just to the machine.
In this release
- Compiler — JSX → measure → place → grid, with the framework owning every coordinate
- References and formulas — one expression AST with two backends:
serialize()writes the Excel formula,evaluate()computes what the viewer shows. Deliberately avoids GPL-licensed engines - Viewer — virtualized grid, formula bar showing the resolved formula, inspect mode with source write-back, design panel, themes and assets
- Agent skills —
/create-sheet,/sheet-authoring,/current-sheet,/apply-comments,/create-theme - MCP server —
open-sheet dev --mcp, sharing one implementation with the browser - Export — xlsx with live formulas, native charts, and data bars; plus csv, html, and pdf
Verified against a real spreadsheet application
CI recalculates the exported workbook in LibreOffice and diffs it against our own
evaluator, and a second test changes one assumption and asserts the whole column
moves with LibreOffice doing the arithmetic. Charts are round-tripped to confirm
their series stay bound to ranges rather than to values.
Packages
| Package | |
|---|---|
| @open-sheet/core | compiler, formula engine, viewer, export, CLI |
| @open-sheet/cli | npx @open-sheet/cli init |
| @open-sheet/mcp | MCP server |
Credits
Follows open-slide by @1weiho and
open-doc by
@simonliu-ai-product. This is the third medium.