feat(bolt-slides): replace the slides starter with slides v2 BOU-2538 - #126
feat(bolt-slides): replace the slides starter with slides v2 BOU-2538#126geotrev wants to merge 43 commits into
Conversation
|
|
Decks live in Postgres. The editor uses deck-api; the agent writes deck and slides rows. Owner Present is in-place. Share waits for a published origin. BOU-2538.
b44b0a3 to
0411df4
Compare
Present-share ?presenter=1 no longer opens the speaker console. P popups read the hash and close on Esc instead of becoming the editor or yanking the live deck to slide 1.
Refresh now waits for debounce timers and in-flight PUTs, and drops a stale /state snapshot if a write started after the GET. Share lookup failure no longer defaults a ?k= tab to editor; deck patches coalesce.
Unlock rate-limit keys on a platform client IP (or the last X-Forwarded-For hop), not the first. 500s no longer echo exception text. The migration comment no longer teaches "no token = owner".
Notes now paste as plain text (matching EditableText) and wrap code with escaped text. Freeform canvas drags abort on unmount and pointercancel so a mid-drag slide change cannot keep writing.
Export is any reader (notes stripped unless edit); share minting is owner-only. canEdit is documented as advisory — the API is the gate.
Review follow-ups (five commits on top of
|
…ht set add/duplicate/delete/import bumped writeGen but never tracked the POST, so a focus or realtime refresh could apply a pre-write snapshot after the local set.
Port dark-theme text, border, background, and shadow onto editor and present controls without adding the design-system package, so slide content stays on deck tokens.
Keep the canvas files and CSS for a later rework, but drop them from the registry, add-slide picker, and skill catalog so new decks cannot choose that layout.
Share and New slide share one focus trap so paging keys cannot fire while a modal is open, and the picker stays at max height. PDF/OG rasterization inlines iframe styles so accent, weight, and size survive the snapshot.
…ped text Inlining every computed style and cloning into the parent produced empty black pages. Snapshot the iframe mount as before, and turn background-clip:text into a solid color so accent and figures still paint.
html-to-image and jsPDF have to run on the window thread. Painting after each progress update lets the busy toast show instead of freezing through the whole deck.
…d lock New slide thumbs
1px −/+ was a no-op near layout size because setSize treated |em-1| < 5% as a reset. Step and arrows now write the size, and typed values debounce 250ms so "14" is not "1".
Split clone, draw, and PNG encode and insert macrotask yields so a long export can ack Bolt's preview heartbeat between phases.
The studio rail, Present, and Download as share a repo-root JSON file; published / stays the audience deck.
…board menu Speaker notes stay read-only in the presenter console. The download control follows the WAI-ARIA menu-button pattern.
…s a popover Agents often write big-number or type instead of catalog keys; alias those so slides render. Studio notes now open as a non-modal dialog that focuses the editor and restores the trigger.
SVG snapshots drop live webfonts, so inline @font-face as data URIs. The notes trigger no longer recaptures focus after click.
Hide the presenter control in the Bolt preview until a live publish exists, and never window.open a WebContainer origin.
Presenter no longer waits for a live bolt.host; P always opens same-origin.
…stay alive pixelRatio 2 plus PNG inflate/deflate on the main thread could miss Bolt's 2s heartbeat ack. Match the page size and let jsPDF stash DCTDecode bytes.
Transparent background none let studio chrome show through while thumbs letterboxed on --bg. Default to color var(--bg), paint that surface for none/missing, and tell the skill to always set background.
Same typescript-eslint / react-hooks / react-refresh kit as bolt-vite-react-ts, plus id-length warnings to surface single-letter debt. Fix three lint errors with no behavior change.
…o hot path Clearer names in Deck, Presenter, store, and studio shell (slideIndex, buildMax, state selectors) with no behavior change. Broadcast sync still uses the slide wire field.
Move keyboard, hash, broadcast, fullscreen, and idle effects into deckHooks. Deck and Presenter render SlideView from SlideData instead of sharing Children element trees.
Move stroke geometry/persistence out of Annotator and notes markup out of the WYSIWYG editor. Keep freeform unregistered with a registry guard, and tighten FieldSpec typing.
Drop the Round 1 id-length probe so lint matches the Vite React starters. Split mixed Fast Refresh exports, list real hook deps, and un-export internals nothing imported.
Gate notes, export, Presenter, and rail mutate by view permission so the published origin stays audience-only.
Inset the editor canvas so the rail does not crush the slide, lock thumbs to a fixed presentation stage, and morph the framed slide into present.
Drop the dock border, use DS tertiary fills for bar/hover, and slow the sidebar so it moves with the canvas inset.
Drive sidebar and Present size with layout/layoutId instead of competing width springs, and host annotate (like notes) in a shared dock popover so it stays above the bar and follows drag.
The agent never reaches these modules, and canvas is no longer a catalog layout.
Stable squash (2026-08-24):
0411df4b77467c6381efe1f59f85d13a0c47bbd1. Pair with boltf1e9b32a9d(#7075), which pins this SHA.Problem
The bolt-slides starter was JSX the agent rewrote on disk. Sharing and presenting also minted WebContainer URLs, which fail as visitor links and leak the preview origin.
Solution
Replace
bolt-slideswith a Pitch-style studio: decks are Postgres rows. The editor talks todeck-api; the agent writesdeck/slidesrows directly. Owner Present is in-place. The published origin is the audience deck (notes stripped). Share mints presenter-console and editor links on that origin. Host identity and published origin are injected by Bolt — stackblitz/bolt#7075.Implementation Details
@/kept. Skeleton chrome uses a Bolt DS token snapshot (BOU-2539) without depending on@blitz/design-system./), audience present (published/, notes stripped; leftover/present?k=still works), presenter console (share or/present?presenter=1). Audience present has no presenter control.X-Deck-OwnermatchingDECK_OWNER_SECRET→ no token ispresentonce the secret is set (local Vite with the secret unset stays owner). Opt-in via.bolt/config.jsonpreviewOwnerSecret. Owner proof is not stored inlocalStorage.BroadcastChannelis same-origin).deck-apicurls). Comments/profiles removed.