Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0411df4
feat(bolt-slides): replace the JSX starter with the slides v2 studio
geotrev Aug 24, 2026
1d41faf
fix(bolt-slides): treat presenter as a capability, not a query string
geotrev Aug 24, 2026
b670e57
fix(bolt-slides): keep store refreshes from clobbering in-flight edits
geotrev Aug 24, 2026
7f0d093
fix(bolt-slides): align deck-api throttle and schema notes with access()
geotrev Aug 24, 2026
88ac3ed
fix(bolt-slides): sanitize notes paste and drop canvas drag listeners
geotrev Aug 24, 2026
86fc0c5
docs(bolt-slides): match cloud-setup permissions to deck-api
geotrev Aug 24, 2026
6ba1d7f
fix(bolt-slides): keep structural slide writes in the refresh in-flig…
geotrev Aug 24, 2026
1012cf8
fix(bolt-slides): apply prettier to files failing format:check
geotrev Aug 24, 2026
063bbf4
fix(bolt-slides): close editor modals with Esc and per-card share status
geotrev Aug 24, 2026
dad970b
feat(bolt-slides): restyle skeleton chrome to Bolt DS tokens BOU-2539
geotrev Aug 24, 2026
2be547a
fix(bolt-slides): make size percent editable and toast PDF status
geotrev Aug 25, 2026
ff8a84c
fix(bolt-slides): stop size edits from nesting {s:} markers
geotrev Aug 25, 2026
ea687a7
fix(bolt-slides): unregister Freeform from the starter layout set
geotrev Aug 25, 2026
be90db5
fix(bolt-slides): trap editor dialogs and keep text styles in PDF export
geotrev Aug 25, 2026
cd5e817
fix(bolt-slides): keep PDF export in the iframe and only flatten clip…
geotrev Aug 25, 2026
020b389
fix(bolt-slides): yield to the UI between PDF slide rasters
geotrev Aug 25, 2026
c5f81ea
feat(bolt-slides): treat the published origin as the audience deck
geotrev Aug 25, 2026
6a3a001
fix(bolt-slides): hide disabled tooltips, raise the dock on focus, an…
geotrev Aug 25, 2026
ac00644
fix(bolt-slides): theme New slide thumbs from tokens, insert none bac…
geotrev Aug 26, 2026
700dad7
fix(bolt-slides): derive atmosphere and glow from the deck accent
geotrev Aug 26, 2026
a5e0585
fix(bolt-slides): show visual text size in pixels, keep em on the slide
geotrev Aug 26, 2026
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
3 changes: 3 additions & 0 deletions bolt-slides/.bolt/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"previewOwnerSecret": "DECK_OWNER_SECRET"
}
2 changes: 2 additions & 0 deletions bolt-slides/.bolt/prompt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
Import project modules with the `@/` path alias, which maps to `src/` (e.g. `@/components/Foo` == `src/components/Foo`), instead of deep relative paths like `../../components/Foo`.

This template uses Vite 8. `resolve.tsconfigPaths: true` is a valid built-in Vite option. Preserve it for the `@/` alias; do not claim it is invalid or add the `vite-tsconfig-paths` plugin.

Decks are rows in `deck` and `slides`. Follow `.bolt/skills/slides/SKILL.md`. Before authoring: apply `supabase/migrations/create_deck_tables.sql` (writing the file does not create the tables) and deploy `supabase/functions/deck-api` as-is — the editor needs it. Do not call `deck-api` to author. Do not rewrite the app.
580 changes: 272 additions & 308 deletions bolt-slides/.bolt/skills/slides/SKILL.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions bolt-slides/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ lerna-debug.log*
node_modules
dist
dist-ssr
.vite
*.local

.vscode/*
Expand All @@ -21,3 +22,5 @@ dist-ssr
*.sln
*.sw?
.env

public/og.png
80 changes: 53 additions & 27 deletions bolt-slides/README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,62 @@
# Bolt slides skill
# Bolt Slides

A Bolt skill that builds a premium, **responsive React presentation deck** — classic
paged slides you present one at a time, with a Slidev-style floating dock + thumbnail
rail, grid overview, click-builds, annotation, and presenter mode — but each slide is a
responsive web layout (no fixed canvas, no clipping) built from a rich component
library.
A Pitch-style slide studio for [Bolt](https://bolt.new): a deck **editor**, a
premium **presentation engine**, and a bundled **skill** so Bolt's AI can
prompt entire decks into existence — which you then refine by hand.

## The skill
Decks are structured data stored in **Postgres**. Prompted decks write
`deck` / `slides` rows. The visual editor reads and writes through the
`deck-api` edge function (share links, speaker notes), so both share one
store.

This repo **is** the running app. The authoring guide lives at
[`.bolt/skills/slides/SKILL.md`](./.bolt/skills/slides/SKILL.md); the app itself sits
at the repo root — a complete Vite + React deck: the paged engine + chrome
(`src/deck/`), fourteen slide layouts (`src/components/`: Cover, BigNumber,
Contrast, Chat, Globe, Bento, Split, StatGrid, Section, Quote, Pricing, Steps,
Agenda, Team) plus a dozen building blocks (Table, Comparison, Tabs, Accordion, Timeline,
CodeWindow, BrowserFrame, SpotlightCard, charts, CountUp, TiltCard, Marquee, …),
and the token-driven theme (`src/styles/`). The engine is left as-is; only the
`:root` token block and the slides in `src/App.tsx` are authored per deck.
## Quick start

## Add it in Bolt
```bash
npm install
npm run dev # editor at http://localhost:5173 — Present swaps this view
```

1. In Bolt's **Add skill from GitHub**, paste this repo's URL —
`https://github.com/inkko44/bolt-slides-skill`.
2. The `slides` skill auto-discovers at `.bolt/skills/slides.md`.
3. Tell Bolt to use the `slides` skill and build a deck about your topic/brand.
On first use in Bolt the agent must apply
`supabase/migrations/create_deck_tables.sql` and deploy
`supabase/functions/deck-api` (the instance is already provisioned; the
schema and function are not). After that, add slides in the editor or prompt
a deck with the `slides` skill.

## What's inside

- `/` — In the Bolt preview iframe this is the editor: thumbnail rail,
click-to-edit text, speaker notes. The bottom bar pages the deck and
holds Export PDF, Present, and Share (presenter console or editor;
optional password). Share is disabled in Bolt preview until the
project is published. Present replaces the editor in place (Esc or
the dock close control returns to editing). The published origin is
the audience deck (no notes, no editor). From local Vite, **P** opens
a presenter console in a second window. On the published audience
deck that control stays off; use a presenter-console share link. In
an embedded preview that control stays disabled; use Share after
publishing.
- `/present` — Presenter console and leftover presentation share links.

## Run it locally
## The skill

`.bolt/skills/slides/SKILL.md` covers bootstrap (apply the shipped
migration, deploy `deck-api` as-is) and authoring `deck` / `slides` rows.
Do not rewrite the app or call `deck-api` to author.

## Architecture

```bash
npm install
npm run dev
```
src/data/ types + zustand store (optimistic writes via deck-api)
src/layouts/ layout registry
src/components/ section components (locked)
src/deck/ presentation engine
src/edit/ editor
src/styles/tokens.css theme: edit :root values only
supabase/functions/deck-api edge function — browser / share backend
supabase/migrations/ schema (apply; writing the file is not enough)
```

## Theming

`npm run dev` opens the deck at `/`. Re-theme everything by editing one `:root` block
in `src/styles/tokens.css`.
Everything visual derives from the `:root` tokens in `src/styles/tokens.css`.
`--accent` must stay a solid color.
87 changes: 87 additions & 0 deletions bolt-slides/docs/cloud-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Deck backend (Bolt Cloud / Supabase)

The starter ships the schema and the `deck-api` edge function. A Supabase
project is already provisioned for every Bolt project; the agent still has
to **apply the migration** and **deploy the function** before the editor
can load slides. That is Step 0 in `.bolt/skills/slides/SKILL.md` — not
optional, not "only when publishing".

The agent authors `deck` / `slides` rows in Postgres. The browser never
queries those tables: it talks only to `deck-api`.

## Contract

The editor, presenter, and share links talk to:

```
$VITE_SUPABASE_URL/functions/v1/deck-api
```

The gateway still wants `Authorization` and `apikey`. Who is calling
decides which Bearer:

- **Browser (Bolt preview)** — anon key + host-injected `X-Deck-Owner`.
Bolt derives a per-project preview owner token, injects it into the
iframe, and (because `.bolt/config.json` sets `previewOwnerSecret`)
copies the same value to the `DECK_OWNER_SECRET` edge-function secret.
It is not in workspace `.env` and never a `VITE_` var.
- **Published `bolt.host` / cover capture** — anon key, no owner header.
That is the audience deck (`present`, notes stripped).
- **Share links** — anon key + `X-Share-Token` (and `X-Share-Grant` after
a password unlock) for the presenter console or the editor.

Custom headers `X-Share-Token`, `X-Share-Grant`, and `X-Deck-Owner` must be
allowed in CORS (the shipped function already does this). `GET /health` is
the deploy probe: `200` and no deck data, no owner credentials.

```
GET /health
GET /state
PUT /deck
POST /slides
PUT /slides/:id
POST /slides/:id/duplicate
DELETE /slides/:id
PUT /order
GET /export POST /import
GET|PUT|DELETE /shares[/:mode]
GET /share?token=…
POST /share/unlock
```

## Tables

`deck` (single row), `slides`, `shares`,
`share_grants`, `unlock_attempts`.

**RLS is on with no policies.** The browser must never query these tables
directly: `shares` holds password hashes and `slides` holds speaker notes.
The function uses the service role. Do not add `anon` CRUD policies.

## Permissions the function enforces

- `Authorization` or `apikey` equals `SUPABASE_SERVICE_ROLE_KEY` — **owner**
- `X-Deck-Owner` matching `DECK_OWNER_SECRET` — **owner**. Bolt injects the
preview owner token into the iframe and syncs it to this secret when
`previewOwnerSecret` is set in `.bolt/config.json`. Top-level published
`bolt.host` does not get the header.
- no share token, no service role, no owner proof — **`present`** (read,
notes stripped) once the secret is configured; **owner** if the secret
is unset (local Vite)
- `edit` share link — everything
- `presenter` — read, plus writing `notes` on a slide
- `present` — read, with `notes` stripped from the response
- export — any reader (notes stripped unless `edit`)
- import — owner or `edit`
- share management — owner only

A request carrying a share token is judged **by that token**, even from the
owner's own browser.

## Authoring

The skill writes `deck` and `slides` rows. Slide `props` use the same JSON
shape as `.bolt/skills/slides/SKILL.md`. After a browser write, `deck-api`
broadcasts on the `deck` channel so other open editors re-fetch (no table
SELECT; RLS stays locked). Direct table writes do not emit that ping — the
preview refetches on focus.
17 changes: 11 additions & 6 deletions bolt-slides/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Always replace the title + favicon emoji to match the deck topic -->
<title>Replace — your deck title</title>
<link
rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🎞️</text></svg>"
/>
<title>Slides</title>
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 178 227'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M103.267 198.762C87.1743 198.762 71.3565 192.914 62.2897 180.315L59.0909 195.151L0 226.551L6.37813 195.151L49.3961 0H102.089L86.8603 68.7856C99.1455 55.3229 110.567 50.3381 125.208 50.3381C156.823 50.3381 177.901 71.121 177.901 109.174C177.901 147.227 153.605 198.742 103.247 198.742L103.267 198.762ZM123.461 120.321C123.461 138.474 110.587 152.231 93.9056 152.231C84.5445 152.231 76.0469 148.718 70.493 142.576L78.6963 106.564C84.8389 100.421 91.8646 96.9083 100.068 96.9083C112.647 96.9083 123.48 106.269 123.48 120.321H123.461Z' fill='%231688FC'/%3E%3C/svg%3E" />
<meta name="theme-color" content="#05070a" />
<!-- OpenGraph — og.png is generated from slide 1 -->
<meta property="og:title" content="Slides" />
<meta property="og:description" content="A deck built with Bolt Slides." />
<meta property="og:type" content="website" />
<meta property="og:image" content="/og.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="/og.png" />
</head>
<body>
<div id="root"></div>
Expand Down
Loading