An Obsidian plugin that lets you read Apple .numbers spreadsheets without
leaving your vault. Read-only: it never writes to, moves or modifies a .numbers
file.
It is not a spreadsheet editor — Sheet Plus and Univer already cover that. The gap this plugin fills is reading Apple's proprietary format, which no other plugin interprets.
Click a .numbers file in your vault and it opens in its own view. Toggle between
Preview — the thumbnail Apple already stores in the file, first sheet only — and
Tables, which shows every sheet and table. Each table has buttons to copy its
contents as Markdown or CSV.
Clipboard: those two buttons are the only clipboard use. They write the table you
clicked to the system clipboard via navigator.clipboard.writeText. The plugin never
reads the clipboard, and never writes to it unless you click one of the buttons.
Tried in cascade; the view tells you which one it ended up using.
.z, .s or .w).npm install
npm run dev # watch build into test-vault/.obsidian/plugins/numbers-viewer/
npm test # node --test test/*.test.ts
Open test-vault/ as a vault in Obsidian to test with hot reload.
.github/workflows/release.yml builds, attests and publishes on a tag push. GitHub
Actions is currently disabled on this account, so releases are cut locally instead:
git tag 0.1.2 && git push origin 0.1.2
scripts/release.sh # build + checks only
scripts/release.sh --publish # create/update the GitHub release
The script mirrors the workflow except for build provenance attestations, which need the Actions OIDC token and have no local equivalent. The workflow stays in place and takes over unchanged once Actions is available again.
The test fixture test/fixtures/sample.numbers is synthetic — generated by
scripts/make-fixture.py with numbers-parser, so no personal spreadsheet ships in
this repository. Regenerate it with:
pip install 'numbers-parser'
python scripts/make-fixture.py
Styles in that script must be registered through doc.add_style(); a bare Style()
only persists bg_color.
One consequence of shipping a synthetic fixture: the SheetJS fallback engine has no
automated coverage. SheetJS cannot read a file written by numbers-parser, and the
only files it can read are real Numbers.app documents. Test that engine by hand
against a real .numbers file when you change src/numbers/via-sheetjs.ts.
Measured against 9 real .numbers files, with every cell diffed against
numbers-parser as an oracle: 3,651 styled cells match exactly, and of 1,303
numeric cells the only differences are two deliberate deviations, both documented
below.
.numbers file saved as a package (a folder) instead of a single file.
Obsidian sees a folder and never even routes the click to this plugin, so there is
no error to show — none of our code runs. The only remedy is to re-save from
Numbers as a single file.2.653, which is what Numbers displays;
numbers-parser truncates to 2652. This affects 35 cells in the corpus and is
intentional.#.# stub), producing 36000, € where Numbers shows
36.000,00 €.merge_region_map — the same order of preference as the
reference implementation.CalculationEngine.iwa, but the AST decoder is not ported. The computed
value is correct.. for thousands,
, for decimals. The document locale is not read yet, so other locales will see
their numbers grouped the Spanish way.[[wiki]] links inside a cell work: pattern recognition over the extracted
text, independent of everything above."NUMBERS missing tile 24").src/numbers/iwa/descset.generated.ts embeds the full IWA schema (1,129 messages)
extracted from numbers-parser. It must be regenerated when Apple ships a Numbers
release that changes the format — see scripts/extract-iwa-schema.py and
scripts/embed-iwa-schema.mjs.
MIT. See LICENSE.