Skip to content

Spreadsheet 2.0.0: edit cells, formulas, rows and columns; save in place - #70

Merged
nwparker merged 12 commits into
mainfrom
nwparker/spreadsheet-v2
Nov 3, 2026
Merged

nwparker merged 12 commits into
mainfrom
nwparker/spreadsheet-v2

Conversation

@nwparker

@nwparker nwparker commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

ELI5

Spreadsheet 2.0.0 makes spreadsheets editable in Orca (stablyai/orca#20362). You can type values and formulas, insert and delete rows and columns, clear, paste, and undo and redo, then save with Orca's normal save. Saving changes only the cells you edited, so formatting, charts, comments and macros survive.

Safe to merge before the Orca release. The catalog lists 2.0.0 (needs Orca 1.4.248) ahead of 1.1.0 (1.4.245). Orca installs the newest release its version can run, so people on older Orca keep the 1.1.0 viewer.

Before and after, for the user

1.1.0 (Spreadsheet Viewer) 2.0.0 (Spreadsheet)
Cells View only Type over a cell, F2 or double-click to edit, or use the formula bar; Delete clears; paste fills cells
Formulas Shows the results saved in the file Recalculates as you type (arithmetic, references, SUM, IF, COUNTIF, ROUND and other common functions); anything else keeps its saved result, and Excel recalculates on open
Rows and columns — Right-click or Shift+F10: insert above/below/left/right, delete. Formulas, merges, conditional formats, validation, links and named ranges follow
Undo — Cmd/Ctrl+Z, Cmd/Ctrl+Shift+Z (Ctrl+Y)
Saving — .xlsx, .xlsm and .ods save in place through Orca. .xls and .xlsb open read-only with a notice explaining why
Outside changes Reloads If an agent writes the file while you have unsaved edits, both are kept: your edits win on the cells you changed

The mechanism

  • Orca's document session. The editor uses orca.document.session({ decode, encode, merge }), like Excalidraw 2.1.0. Orca owns the buffer, dirty state, saving, flush-on-close and outside-write conflicts.
  • The document is "the file + a list of operations". Each edit is an operation (set cells; insert or delete rows or columns). It updates the grid at once and returns its inverse, which is what undo applies. Saving replays the list onto the file's own XML:
    • .xlsx/.xlsm: only edited sheets are rewritten, cell by cell, keeping each cell's style. New text is stored inline, so the shared string table is untouched. Every other part (styles, drawings, comments, the macro project) is copied with its exact compressed bytes. The stale calculation chain is dropped, since Excel repairs files that keep one, and the workbook asks Excel to recalculate on open.
    • .ods: only content.xml changes, and in it only the edited cells. Repeated rows and cells are split as needed.
  • Row and column changes move references the way Excel does (ranges grow and shrink; a deleted target becomes #REF!), in every formula, merge, column width, conditional format, validation rule, hyperlink, page break and defined name. A sheet with something the writer cannot move (drawings, comments, tables, pivot tables, or a chart reading it) refuses the change and says why in the status bar. Its cells stay editable.
  • Merge replays this tab's unaccepted operations onto the outside file. Encoded bytes are remembered, so an accepted edit is never parsed again or replayed twice.
  • The editor stays a single self-contained page ("frame" stays any), so browser clients can edit too. One small MIT zip library is added to the bundle, for synchronous zip reads and writes (the session's encode and merge can't wait). It is listed in THIRD_PARTY_NOTICES.txt.

Why this approach

Re-serializing the whole workbook through a spreadsheet library would be simpler. But a community-edition writer drops styles, charts, comments and conditional formatting, and that loses data silently. Patching the original XML keeps everything we don't understand byte for byte. It also makes .xlsm safe to edit, since the macro project is never touched. Where a change can't be applied safely (row shifts under drawings, binary .xls/.xlsb), the editor refuses it and explains why. It never writes a degraded file.

Testing

  • npm test in build/: 88 tests. These include writer round-trips against hand-written workbooks with styles, shared strings and formulas, merges, conditional formats, validation, a defined name, a calculation chain, a drawing and a macro project. They check that untouched parts keep their bytes. Session tests run against Orca's real session code through mockEditorHost (conflict merge, no double replay of accepted edits). Headless browser tests type, use the menu, undo/redo, paste, save and re-read the saved file, in both themes.
  • orca plugin check: ok. orca plugin test: the tests with no npm imports pass. It refuses bare imports, so the writer tests run through npm test (gap below).
  • Live, on an Orca build from current main (background, isolated profile), with orca plugin shot --editor --act --in-place: typed values and a formula, inserted a row from the menu, then pressed Cmd+S. The file on disk had the edits, the shifted formulas (SUM(B2:B6), Budget!B7 on another sheet), its styles and drawing, and no calculation chain. Shots in light and dark are below.
  • node scripts/release-entry.mjs and node scripts/check-catalog.mjs: ok.

Risk

Medium: this writes users' spreadsheets. The writer only rewrites edited sheets or content.xml, and refuses what it can't move. Known costs: saving after an edit to a 50,000-row sheet takes about 1.5 s, because that sheet's XML is rewritten. Sheets can't be added or renamed yet. Back out by reverting to 1.1.0.

In Orca: a typed value recalculated the totals, and a formula is being typed into B5

In Orca, dark theme: the cell menu with insert and delete rows and columns

In Orca, dark theme: a row inserted above Power; totals and formulas moved with it, then saved

… xlsm and ods in place

Moves Spreadsheet onto Orca's document session. Edits are an operation log
replayed onto the file's own XML, so a save rewrites only edited cells and
keeps styles, charts, pictures, comments and macros byte for byte. Formulas
recalculate for common functions, references follow inserted and deleted
rows and columns, undo and redo are local, and outside writes merge with
unsaved edits. .xls and .xlsb open read-only with a notice.
- Merge only disjoint cell edits; set aside anything an outside row or column
  change could misplace, with reapply or discard.
- Operations hold their own cell copies, so a replayed log never shifts a
  formula twice.
- Refuse row and column changes on sheets with linked parts (pivot tables,
  comments, tables...) or read by pivot caches, charts, table formulas or other
  sheets' sparklines and rules; refuse pushing cells past the sheet's end.
- Save formulas it cannot calculate, array formulas included, without a guessed
  result; keep dynamic-array metadata; write ; separators as commas.
- Refuse edits to Excel table header and totals cells.
- Move internal hyperlink targets with their rows.
- Reapply puts back cell edits only, each through the checks a typed edit
  gets; row and column changes are never reapplied, and they and any refused
  cell are listed for the person.
- Held edits survive later outside writes; the banner says they are unsaved
  and that closing the tab discards them.
- A merge is refused when a formula this tab typed reads cells that changed.
- A row insert no longer drops the saved result of formulas it cannot
  calculate; only deleting data does.
…ange

Results such as ROW, MATCH or COUNTBLANK depend on position, so no saved
result is kept across a row or column change: formulas the editor calculates
are recalculated, the rest are saved without a result for the next app to
compute. A merge is also refused when an edited cell became a table header.
@nwparker
nwparker merged commit 671ef38 into main Nov 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant