slides: the embed element - #466
Merged
Merged
Conversation
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.
…ies on save #447 landed this morning: a save now drops every doc.assets entry nothing refers to, and referencedAssetKeys enumerates the forms that existed then. This branch adds two more — an embed's `view` and `doc` are both interned by "Capture view…" as asset refs — and on the merged tree a save deleted them. Measured before the fix: {"assetsAfterSave":[],"EMBED_VIEW_LOST":true} and after: {"assetsAfterSave":["view-1","src-1"],"EMBED_VIEW_LOST":false} Silent, and neither rig could see it: #447's pins the eight forms it knew, this branch's never saves. It is the failure the prune's own comment names — a new field that references an asset must be added there — arriving one day after the comment was written. The rig case is added so the next one cannot. Also merges main (7e8c0d1 -> 401cade), clean. (cherry picked from commit b3636ce)
…pe; the naming settles; the panel says what a live frame costs Three things on top of #424's head, for the contributor to cherry-pick: 1. An embed's `doc` is another deck's JSON, and a deck's envelope carries its collaboration secrets (`collab`, including the saved sync state) and its identity (`docId`). The shape gate checked shape only, so both would pass, survive every save by additivity and travel with every export — while the export-secrets rig reads the top-level block alone. `embedDoc` now returns an object source without them, copying rather than mutating the caller's. Pinned in test-embed.ts (the gate keeps content, drops both keys, no private half survives anywhere, caller untouched; 64 checks, 2 red with the strip removed) and in test-export-secrets.ts by shape (67). 2. "Beta build" (thirteen places) becomes the element's own name, the render.ts comment points at the rig that exists (scripts/test-embed.ts), and build-modelkeys no longer cites a docs section that never existed. 3. The embed panel gains one hint: a clicked frame keeps the arrow keys while presenting until you click outside it, and everyone who presents the deck loads the page from its site. In all eight catalogs. Also carried from `pr424-asset-prune`, as a proper commit this time: #447 prunes assets nothing refers to and did not know the embed element's view/doc form, so on the merged tree a save deleted them (measured). Plus the changelog entry the feature was missing, and main merged in.
…lks embedded documents An embed's doc is another deck's JSON; that deck's envelope (collab, docId) must never ride inside this one. The shape gate stripped it on the way in for pasted content; a file already authored with it kept the inner block in every read-only and invite copy, because the export strip only saw the top level. Both now apply the same rule — slides/src/ envelope.ts, node-importable — the gate through stripEnvelope, and stripCollabSecrets through stripEmbeddedEnvelopes (slides and layouts), before its own early return so a copy that drops its whole collab block still loses the embedded ones. test-export-secrets runs both rather than grepping: a deck with an embedded ownerPriv, writerPriv, invite.priv, room key, room id and docId exports none of the six with the content intact; security's mutation (behaviour killed, text kept) now turns it red (72/74). 74 checks.
The packs sat at 89.4% with the embed strings landed in the core catalogs only; the floor is 90%. Machine-drafted by the packs' convention; tk's header records its twelve as awaiting the named translator.
Build size
Updated: |
nyblnet
added a commit
that referenced
this pull request
Sep 13, 2026
…joins the features, once, credited
nyblnet
added a commit
that referenced
this pull request
Sep 13, 2026
…temporary merge of the fix branch is now redundant
nyblnet
added a commit
that referenced
this pull request
Sep 13, 2026
#466 landed an isWebUrl beside the embed element (scheme only); this branch had a stricter one (scheme, length, no quote or angle bracket). One definition now, the stricter, where the embed's was; every caller (embed url gate, live-frame gate, element link, text anchor, markdown) asks it. The embed rig passes against it unchanged (64/64).
nyblnet
added a commit
that referenced
this pull request
Sep 14, 2026
…hortcut list (#465) * slides: clickable web links, * and indented bullets, and a complete shortcut list Three things from the issue and discussion scan, each small enough for this release. Links (#421; discussions #373, #374). An element's `link` may be an http(s) URL as well as a slide id (Presenting → Web link), and [caption](https://…) in a text box becomes an <a href>. One scheme test, isWebUrl in model.ts, is asked by every surface: the shape gate, the text sanitizer (an <a> keeps its href only when it passes and is unwrapped to text otherwise), both markdown converters, and the show before it opens anything — always a NEW tab with noopener,noreferrer, never a navigation of the deck, target/rel decided at click time and never stored. In the editor a link click edits text. The offline switch is honoured with a toast. Measured in Chrome: text and element links open via window.open with those flags and the deck stays; a javascript: anchor is unwrapped; slide links still jump; the editor never navigates; with offline mode on nothing opens. test-sanitize gains the anchor checks (143 in the browser); scripts/test-slides-links.ts (27) pins isWebUrl, the converters, and by shape the four callers. Bullets (#255, #368). "* " makes a bullet like "- ", and two or more leading spaces make an indented sub-bullet — while typing (autoformat, undoable with ⌘Z like before) and when pasting markdown (indent kept as NBSPs). Shortcuts (#269). The ? overlay names B, G, [ ], ⌘B/I/U, the zoom keys, and the arrow keys' two jobs. Sixteen strings in all eight catalogs and all 22 packs (four of them pre-existing gaps from the code-snippet work); changelog entries; DECISIONS entry for the link decisions. * slides: a middle-click on a link goes through the same door as a click auxclick is not click: the browser's default for it on an anchor is 'open in a new tab', straight past the offline gate and the noreferrer flag. Intercepted on the show's slide host; button 1 opens through openWeb like a click, anything else is swallowed. Pinned by shape. * slides: sanitizeHtml walks an element's children before unwrapping it The walk visited a snapshot of a node's children; an unknown element's children were lifted into place after the snapshot and not visited on that pass. Walk first, then lift, so lifted children are held to the same rule as their siblings. One case in test-sanitize: nested markup is walked like top-level markup, rendered and exercised in the browser. * slides: show anchors carry rel at mount; a refused anchor's contents are walked before it is unwrapped rel='noopener noreferrer' is set on the show's anchors when a section is built — never stored, never in render.ts — so the browser's own routes to an anchor (context menu, drag), which do not pass through the click handler, send no referrer on a hosted deck either. The refused-anchor unwrap now walks its contents first, like the general unwrap. The nested-markup sanitizer case gains the anchor shape. * slides: one isWebUrl — the stricter test, at the embed element's site #466 landed an isWebUrl beside the embed element (scheme only); this branch had a stricter one (scheme, length, no quote or angle bracket). One definition now, the stricter, where the embed's was; every caller (embed url gate, live-frame gate, element link, text anchor, markdown) asks it. The embed rig passes against it unchanged (64/64). * ci: re-run on this head (no run was recorded for 511dc29)
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.
Carries #424 — Johan Høgåsen-Hallesby's embed element, as his own commit (cherry-picked; authorship intact) — plus the four follow-ups that were waiting for it on
pr424-embed-conditions, on top of today'smain:Verified at this head:
test-embedbundled as CI runs it — 64/64 (browser half included);test-export-secrets— 74/74, negative-controlled; typecheck,build-i18n --check,build-modelkeys --check,test-slides-assets28/28,test-ci-registered314/314, language coverage 90.9%.Merge-tree measurement against
refs/pull/424/head: NOT clean — a three-way merge from #424's fork point conflicts on the files where the follow-ups edited lines his commit added (untrusted.ts'sembedDoc, the "Beta build" comments, the panel copy, the catalogs). That is by construction: a follow-up that changes a contributor's line can never leave the original PR a no-op merge. So #424 should be closed as landed here once this merges — his commit is onmainunder his name, which is the credit that lasts.