Skip to content

slides: add the embed element to the bento/embed shape - #424

Open
johanhal wants to merge 1 commit into
nyblnet:mainfrom
betamobility:upstream-pr/embed-consumer
Open

slides: add the embed element to the bento/embed shape#424
johanhal wants to merge 1 commit into
nyblnet:mainfrom
betamobility:upstream-pr/embed-consumer

Conversation

@johanhal

@johanhal johanhal commented Sep 8, 2026

Copy link
Copy Markdown

slides: add the embed element to the bento/embed shape

docs/DECISIONS.md (2026-08-19) settled bento/embed as one shape for every app: a static view always present, the doc source always present, a sandboxed live iframe opt-in. type/ has the consumer side; slides/ does not. This adds it to slides, written to that shape so lifting it into the kernel later is a move, not a redesign.

  • model.ts: EmbedElement { type:'embed', app, view, doc?, url?, live?, w, h } in the SlideElement union; modelkeys.generated.ts regenerated (build-modelkeys.mjs learns the interface name).
  • render.ts: one case 'embed'. view (inline <svg> or an asset: key) paints through sanitizeSvg, the svg element's path. The live frame exists only on a live surface (liveMedia, which present mode already passes), so the editor canvas paints the view and a repaint never re-navigates the author's URL. When live is true, app is web, url is http(s), remoteSrcBlocked(url) is false and navigator.onLine is true, a sandbox="allow-scripts allow-forms" iframe with referrerPolicy="no-referrer" is layered over the view; its error handler removes it. Both kinds of offline (the privacy switch and network absence) therefore show the view, and test-offline.ts stays green because the only network decision goes through kernel/src/net.ts.
  • validate.ts: embed-missing-view (error), embed-remote-view and embed-live-no-url (warnings).
  • untrusted.ts, clipboard.ts: paste and CRDT shape gates; asset views travel and remap on key collision.
  • panels.ts: an Embed section with url, live, and a "Capture view" picker that interns an SVG, or a raster wrapped as <svg><image href="data:…">, as the view. A sandboxed cross-origin frame cannot be screenshotted, which is the point of the sandbox, so the view is supplied, not captured.
  • Eleven UI strings in every catalog.

Why now: today an unknown element type loads, survives a round trip and renders as an empty, correctly positioned box, with no finding from validate() and a silent drop on paste (render.ts renderElement has no default; validate.ts looks the type up in MODEL_KEYS.element and finds nothing). A deck authored in a build that has this element degrades silently in this one. With the element known, it paints its view everywhere.

Verified: tsc -b, build-modelkeys.mjs --check, build-i18n.mjs --check and coverage, test-offline.ts, test-sanitize.ts (two added cases), test-validate.ts, test-clipboard.ts, test-sync.ts, test-ci-registered.ts, npm run build:single, shell-gate.mjs, and scripts/test-embed.ts (59 checks: node half plus headless Chrome with a request log proving the frame is requested only when allowed).

An `embed` element { type:'embed', app, view, doc?, url?, live? } per the
2026-08-19 decision: the static `view` (svg markup or an asset) always
paints through the same sanitiser the svg element uses; `doc` is the
source; a sandboxed live iframe (no allow-same-origin, no top navigation,
no referrer) is opt-in and exists only when `app` is `web`, `url` is
http(s), remoteSrcBlocked(url) is false and navigator.onLine is true, and
drops back to the view on error. validate() reports a missing view as an
error and a remote view or live-without-url as warnings; untrusted.ts
gates paste and CRDT shape; the editor gets an Embed panel with a
"Capture view" picker. Strings in every catalog. Rig: scripts/test-embed.ts.

Motivation: today an unknown element type loads, survives a round trip
and renders as an empty positioned box with no finding, so a deck from a
build that has this element degrades silently in this one.
johanhal added a commit to betamobility/slides that referenced this pull request Sep 8, 2026
…without R2 (#2)

README gains a Beta "Releasing" section (dated versions and why, gh
set-default, Pages direct upload, the .html 308) and links the two upstream
PRs (nyblnet#423, nyblnet#424). AGENTS.md's upstream-merge conflict list adds
slides/package.json and CHANGELOG.md. wrangler.toml drops the R2 binding per
plan U2: binding it means a paid subscription on the account, which is the
maintainer's decision, and the inline fallback is upstream-supported.


Claude-Session: https://claude.ai/code/session_01SQN76RTkurkAAxMMvnwfbt

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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