datasette-paper embed provider + spreadsheet-faithful block preview#3
Draft
asg017 wants to merge 3 commits into
Draft
datasette-paper embed provider + spreadsheet-faithful block preview#3asg017 wants to merge 3 commits into
asg017 wants to merge 3 commits into
Conversation
…ard)
Contribute a `paper_embed_provider` so a sheets workbook can be referenced
and embedded inside a paper document, mirroring datasette-places /
datasette-mendez.
Backend (`datasette_sheets/paper.py`, imported in `__init__.py`): describes
the `sheets-workbook` provider — its `ref_prefixes`, `/`-menu source, and
the vite paper-embed bundle URLs — so paper lazy-loads the bundle on demand.
Frontend (`src/pages/paper-embed/main.ts` + `style.css`, new vite entry):
the default-exported provider claims `/-/sheets/workbook/{db}/{id}` refs,
resolves the inline pill (workbook name + table icon) and mounts a read-only
block preview (sheet tabs + first sheet's top-left cells) fetched from the
sheets API with the viewer's cookie. Cross-database picker `search()`
enumerates `/.json` then each db's workbooks API. Leak discipline: 403 ->
denied, missing -> not_found, no label/data leaked.
Workbook pages are database-scoped, so the stored ref normalizes the db
behind the fixed prefix (`/-/sheets/workbook/{db}/{id}`) to keep paper's
prefix-based lazy-load working; `matchUrl` maps the real page URL onto it.
vite.config.ts: `preserveEntrySignatures: "strict"` so Rollup keeps the
entry's `export default` — paper `import()`s the bundle and reads the default
export to register the provider; without it the export is tree-shaken away
and every sheets ref renders as "not found".
pyproject.toml: `datasette-sheets[paper]` extra pins datasette-paper>=0.0.2a3
via a [tool.uv.sources] path to the built wheel.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013ikxoxihTC15haHeknRcgg
The datasette-paper block preview rendered a sheets workbook as a plain sans-serif HTML table with raw values and top underline-tabs — it read as a generic table, not a spreadsheet. Bring it to visual parity with the live grid (docs/screenshots/editor.png). Styling (paper-embed/style.css): mirror the grid's `--sheet-*` palette — "Courier New" monospace cells, the #f0f3f5 blue-grey header fill on the A/B/C column-letter row + row-number gutter, right-aligned accent (#276890) numerals, and bottom folder-style sheet tabs with the per-sheet colour dot. Content fidelity (currency masks + bold): the /data endpoint only returned raw computed values, so the preview couldn't show $4,200 or bold header / total rows. data.py grows an opt-in `?include_format=1` that adds a `formats` grid parallel to `rows` (each cell's parsed format_json, None where unformatted; arrays mode only, default response unchanged). The embed requests it and renders every cell through the shared formatValue / createDefaultFormat — the same single source of truth the live grid uses — so currency / bold / dates / booleans match cell-for-cell. The formatter import adds no bundle bloat (formatter.ts pulls only type-only deps). tests/test_workbooks.py: cover the include_format contract (formats grid present + parallel only when requested, None for unformatted cells). scripts/embed-preview-shot.mjs: dev-only harness (modelled on scripts/screenshots.mjs) that boots the seeded throwaway datasette and screenshots the embed web component standalone, for visual diffing against editor.png while iterating. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
embed-preview-shot.mjs now clips to the block card (#embed-card) instead of the full padded page, so docs/screenshots/paper-embed.png is a tight, README/PR-ready asset mirroring docs/screenshots/editor.png. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a
paper_embed_providerso a sheets workbook can be referenced and embedded inside a datasette-paper document — an inline pill (workbook name + table icon) and a read-only block preview — and makes that block preview render like the real spreadsheet rather than a generic HTML table.Block preview — matches the live grid
The preview now mirrors
docs/screenshots/editor.pngcell-for-cell: monospace cells,A/B/Ccolumn-letter headers + row-number gutter on the grid's blue-grey header fill, right-aligned navy numerals, currency masks, bold header/total rows, and bottom folder-tabs with the per-sheet colour dot.The real grid (target):
The embed block preview (this PR):
How it works
datasette_sheets/paper.py, registered in__init__.py): describes thesheets-workbookprovider —ref_prefixes,/-menu source, vite bundle URLs — so paper lazy-loads the bundle on demand. Frontend entryfrontend/src/pages/paper-embed/main.tsclaims/-/sheets/workbook/{db}/{id}refs, resolves the pill, and mounts the block preview, fetching the sheets API with the viewer's cookie (leak discipline: 403 → denied, missing → not_found, no label/data leaked).--sheet-*palette (paper-embed/style.css)./dataendpoint grew an opt-in?include_format=1that returns aformatsgrid parallel torows(each cell's parsedformat_json,Nonewhere unformatted; arrays mode only — default response unchanged). The embed renders every cell through the sharedformatValue/createDefaultFormat— the same single source of truth the live grid uses — so currency / bold / dates / booleans match exactly. No bundle bloat (formatter.tspulls only type-only deps; the embed bundle stays ~5 kB).Known scope limits
Testing
tests/test_workbooks.pycovers theinclude_formatcontract (formats grid present + parallel only when requested,Nonefor unformatted cells). Full Python suite + frontend gate (test/check/lint/format) green.scripts/embed-preview-shot.mjs— dev-only harness (modelled onscripts/screenshots.mjs) that boots the seeded throwaway datasette and screenshots the embed web component standalone, for visual diffing againsteditor.png. It generated the screenshot above.🤖 Generated with Claude Code