Skip to content

feat(bolt-slides): replace the slides starter with slides v2 BOU-2538 - #126

Open
geotrev wants to merge 18 commits into
mainfrom
geotrev/bou-2538-add-slides-v2-to-starters-repo
Open

feat(bolt-slides): replace the slides starter with slides v2 BOU-2538#126
geotrev wants to merge 18 commits into
mainfrom
geotrev/bou-2538-add-slides-v2-to-starters-repo

Conversation

@geotrev

@geotrev geotrev commented Aug 22, 2026

Copy link
Copy Markdown

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-slides with a Pitch-style studio. Decks live in repo-root deck.json. The agent writes that file (and src/styles/tokens.css when theming). The studio runs in the Bolt preview iframe or local Vite. Present and Presenter open new tabs on the current origin. The published origin is the audience deck (notes stripped). Collaborate by sharing the Bolt project.

Paired Bolt pin: stackblitz/bolt#7075. Closes BOU-2538. Part of BOU-2539.

Implementation

  • React 19 / Vite 8 / TS 6 / @/ kept. Skeleton chrome uses a Bolt DS token snapshot (BOU-2539) without depending on @blitz/design-system.
  • Three surfaces:
    • Studio (/ in the Bolt iframe or local Vite): thumbnail rail, grid, notes, Download as PDF/JSON, Present, Presenter. Reorder, duplicate, and delete live here. No inspector or add-slide UI.
    • Audience present (published /, and /?present=1 from the studio): speaker notes stripped on the published origin. ?presenter=1 on a published top-level visit does not leak notes.
    • Presenter console (/?presenter=1 or /present): on-screen now, up next, notes (read-only), timer, note text size. Dock P opens this in a new tab.
  • Studio vs audience is iframe/DEV, not an injected host identity. Postgres, deck-api, share tokens, owner-proof HMAC, and in-slides sharing are not in this PR.
  • Skill authors deck.json. Layout props contracts live in src/data/layoutProps.ts. Homonym keys (points, items) follow that layout's type.
  • Dev persist is GET/POST /__deck plus a BroadcastChannel handshake so new presenter tabs hydrate from disk and live studio peers.

Test plan

  • New slides project on Bolt staging pinned to this SHA (bolt#7075)
  • Studio loads in the Bolt preview iframe (thumbnail rail, notes, Present, Download as)
  • Present opens /?present=1 in a new tab; the studio stays put
  • Dock P opens /?presenter=1 in a new tab (works unpublished)
  • Published / is the audience deck; ?presenter=1 there does not show notes
  • Prompted decks write deck.json (and tokens.css when theming)
  • Reorder / duplicate / delete persist; notes sync to an already-open presenter tab
  • Download as PDF captures fully painted slides; JSON round-trips

@bolt-new-by-stackblitz

Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@geotrev
geotrev force-pushed the geotrev/bou-2538-add-slides-v2-to-starters-repo branch from b44b0a3 to 0411df4 Compare August 24, 2026 13:24
@geotrev

geotrev commented Aug 24, 2026

Copy link
Copy Markdown
Author

Review follow-ups (five commits on top of 0411df4)

These landed after a seam-first review of the v2 squash. They are grouped so later reviewers can treat each commit as a batch instead of re-deriving intent from the full studio diff.

Pinned in Bolt as 86fc0c50c9fb63619c99ba020d16b50adcf75999 (geotrev/bou-2538-pin-slides-v2).

1. Presenter is a capability, not a query string — 1d41faf

Merge blockers. ?presenter=1 used to open speaker chrome whenever it was in the URL.

  • isPresenter is now allowPresenter && the presenter query. Present-share (mode === 'present') stays audience-only even with ?presenter=1.
  • P from in-place Present opens a popup that reads #slide so it does not BroadcastChannel the live deck back to slide 1.
  • Esc / close in that popup closes the window. It does not tear down Present into the editor (onExit is omitted on the popup route).
  • Audience tabs subscribe to deck-sync only. Slide/click payloads are validated as integers.
  • Speaker chrome keys (S/G/F/D/H/O) are ignored in the presenter window.

2. Store write / refresh integrity — b670e57

  • refresh() waits for debounce timers and in-flight PUTs, and drops a /state snapshot if a write started after the GET. Focus/realtime refetch should no longer clobber notes or patchSlide.
  • shareInfo() failure on a ?k= / session token tab fail-closes to share-required. It no longer defaults mode to 'edit'.
  • updateDeck debounce PUTs the latest { title, transition, font, accent } instead of the values from the first keystroke.
  • Notes-only patchSlide is debounced; structural ops still bump a write generation so a concurrent refresh cannot win.

3. deck-api throttle and schema notes vs access()7f0d093

  • Unlock rate-limit key prefers cf-connecting-ip / x-real-ip, else the last X-Forwarded-For hop (first hop is client-controlled).
  • 500 bodies are { error: 'internal' }; exception text stays in logs.
  • Migration comment matches access(): once DECK_OWNER_SECRET is set, absence of a share token is not owner.

4. Editor hygiene — 88ac3ed

  • Notes paste is plain text (same idea as EditableText). code() HTML-escapes; highlight data-color already required a hex value.
  • Freeform canvas drag listeners abort on pointercancel and unmount so a mid-drag slide change cannot keep writing.

5. Docs — 86fc0c5

docs/cloud-setup.md matches the API: export is any reader (notes stripped unless edit); import is owner/edit; share minting is owner-only. canEdit is documented as advisory — the API is the gate.


Leftover edges (not bugs we treated as merge blockers)

Worth clicking if you are regression-testing this pin:

  • Same-tab /?presenter=1 with no opener: Esc/window.close() is a no-op. Exit by dropping ?presenter from the URL. This is the popup path; a blocked window.open stays on in-place Present.
  • Opening P still publishes clicks: 0 on first deck-sync. The slide should be correct; mid-build animations on the live window can snap back to the start of that slide.
  • In-place Present and the P popup are both channel leaders. Last write wins if they diverge.
  • Stale sessionStorage share token + failed /share lookup now shows the share gate instead of falling through to editor chrome. Clear origin storage to recover.
  • addSlide / duplicateSlide / deleteSlide / importDeck bump write generation but do not themselves flush a parked refresh. A collaborator change can wait until the next focus/visibility tick.
  • Formatted notes paste from Docs/Notion is intentionally plain text.

Out of scope here

Layout restyle (BOU-2539), a full test suite as a merge gate, live agent-SQL without a focus/visibility refetch, CORS *, fail-open when the owner secret is unset, unused Globe.tsx.

@geotrev
geotrev marked this pull request as draft August 25, 2026 13:29
Author decks in deck.json with a studio, present tab, presenter console, and Bolt DS chrome. BOU-2538 / BOU-2539.
@geotrev
geotrev force-pushed the geotrev/bou-2538-add-slides-v2-to-starters-repo branch from 6104652 to f23c164 Compare September 3, 2026 14:43
Give the skill a typed per-layout contract instead of restating keys, so chart and insight no longer share an explained `points` shape.
…are UI

The studio no longer mounts those surfaces; the leftover rules were unused.
… setters

Studio no longer has inspector or add-slide UI. Keep SlideStatus and animation unions for deck.json; drop the unused picker arrays and dead APIs.
Add-slide and the side inspector are gone. Keep defaults for kind-switch and example copy; authoring contracts stay in layoutProps.ts.
…senter icons

One e() helper, one highlight splitter, and one icon set instead of copies in each file.
Presenter notes were stale because a new tab booted from Vite's cached
deck.json import while persist skipped HMR. GET /__deck then a
request-deck handshake so in-memory studio notes win if disk is still
behind the debounce.
Insight points, logos items, and other homonym keys crash Present when
copied from the wrong layout. Guard .map/.split, name the collisions
in the skill, and drop the accordion Postgres leftover.
Strip leftover narrative comments and match the EditorApp blank-line
rhythm so the rest of the starter is as readable as the first pass.
Keep photo overlays readable without an agent pass, capture fully painted slides in PDF export, and let wide bento columns use the slide width.
@geotrev
geotrev marked this pull request as ready for review September 8, 2026 01:28
@geotrev
geotrev requested review from Idered and removed request for Idered September 8, 2026 13:52

@alexpate alexpate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job! Nothing blocking. One thing that came out of review was just a reminder:

templates.json:5 (in stackblitz/bolt) - the new SHA is the head of an unmerged starters feature branch, and all ten starters from that repo now point at it. Re-pin to main after the starters PR lands

Keep the layout viewport at 1280×720 so clamp() and .container match the studio, then raster onto 2560×1440 pages.
Stamp authored fields with data-deck-path and write inspector DOM edits through setProp so copy survives Present and reload without relying on the agent.
Keep setProp off while the inspector contenteditable is focused so the caret does not jump, then write deck.json on focusout.
Tables, comparison text, logos, code, and chart figures were preview-only strings, so Select edits never flushed. Stamp those fields (including pipe slices) so blur persist covers the rest of authored copy.
…r edits

The contentEditable path was never mounted, and CountUp ticks were written back as visual edits. Split remaining chrome and persist into studio/ and copy/.
12-col spans left agent tiles cramped on the left; equal N-up (max 6) fills the row, and old 8+4 mosaics scale onto the new grid.
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.

2 participants