Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ jobs:
- name: Splice conformance gate
run: node scripts/shell-gate.mjs slides/dist-single/Bento_Slides.bento.html

- name: Shipped-file acceptance (image export)
# Drives the built file's desktop and 320px menus through file://.
# CI=1 makes a missing Chrome a failure rather than a skip.
env:
CI: '1'
run: node scripts/test-slide-image-export-acceptance.ts

# spaces: a second app proving the kernel seam — same pipeline, same gate.
- name: Install (spaces)
working-directory: spaces
Expand Down Expand Up @@ -379,6 +386,19 @@ jobs:
--outfile="$RUNNER_TEMP/test-sanitize.mjs"
node "$RUNNER_TEMP/test-sanitize.mjs"

- name: Slide image export contracts
# Selection, names, allocation limits and STORE ZIP bytes. Independent
# readers verify the archive rather than our writer verifying itself.
run: |
slides/node_modules/.bin/esbuild scripts/test-slide-image-export.ts --bundle --platform=node --format=esm \
--outfile="$RUNNER_TEMP/test-slide-image-export.mjs"
node "$RUNNER_TEMP/test-slide-image-export.mjs"

- name: Slide image export in browser
# Proves file:// rasterization and zero export fetches with pixels plus
# a CDP request log.
run: node scripts/test-slide-image-export-browser.ts

- name: Export-secrets rig
# "Copy document JSON" shipped the room's owner private key while the
# tooltip recommended pasting it into an AI chat, and "Save as
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ pre-1.0.

## [Unreleased]

- **Export slides as images.** Save ▾ → "Export slides as images…" writes the
current slide as a PNG or JPEG, or every main slide as one ZIP of numbered
images (`slide-01.png`, …), at 1× or 2×. "All" skips hidden and state slides;
"current" exports either. The offline export refuses linked resources rather
than fetching them. Images and ZIPs from a password-protected deck are not
encrypted; the original `.bento.html` is unchanged. Measured in Chrome only.

## [1.0.19] — 2026-09-04

- **Bento Slides works on a phone.** Eight changes land together, because
Expand Down
16 changes: 16 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@ names provisional.
(64KB, model.ts) tiers the output: full → images dropped for tinted boxes →
title card. Guards: `scripts/test-preview.ts` + shell-gate's
preview-carrying-shell invariant. Full rationale in docs/DECISIONS.md.
- `src/image-export.ts` + `src/image-export-zip.ts` + `src/image-export-errors.ts`
+ `src/editor/image-export-dialog.ts` — **export slides as PNG/JPEG, or all
main slides as one STORE ZIP**. It renders the model through the shared
renderer into a `data:` SVG and one reused canvas; a `blob:` SVG taints
`file://`. Resource preflight and the detached-DOM audit must stay fail-closed
and fetch nothing: inspect both `href` and `xlink:href`, tokenize CSS escapes
and `image-set()`, and accept only inventoried embedded payloads. Re-collect a
slide immediately before rendering to close the edit/preflight race. Limits
are product policy measured only in Chrome. The dialog owns DOM/a11y only.
Run
`slides/node_modules/.bin/esbuild scripts/test-slide-image-export.ts --bundle
--platform=node --format=esm --outfile=$TMPDIR/t.mjs && node $TMPDIR/t.mjs`
plus the browser rig after touching it; after `build:single`, run the shipped
acceptance rig. The one-off Chrome allocation characterization is recorded in
`image-export.ts`, not retained as a regression mode. These tests do not
exercise native OS save-panel UX or tray/WebExtension writeback.
- `src/autosave.ts` (v0.9.8) — auto-save + local version history, IndexedDB
(`bento-autosave`, two stores: `recovery` single-latest-per-docId, `versions`
capped timeline). Editor debounces (2.5s) on `doc` events: writes a recovery
Expand Down
Loading