Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
41b21a5
feat(bolt-slides): replace template with slides v2 prototype
geotrev Aug 19, 2026
89ff9fc
feat(bolt-slides): re-apply toolchain modernization from #121
geotrev Aug 19, 2026
da4099b
refactor(bolt-slides): use the @/ alias for internal imports
geotrev Aug 19, 2026
ecba49b
feat(bolt-slides): render published decks from a build-time snapshot
geotrev Aug 19, 2026
df32a88
docs(bolt-slides): correct publishing guidance and restore .bolt/prompt
geotrev Aug 19, 2026
34df6c8
fix(bolt-slides): build with plain vite build, as the deploy does
geotrev Aug 19, 2026
746f1b7
test(bolt-slides): cover the deck build and the editor
geotrev Aug 19, 2026
e570c69
style(bolt-slides): format with the repo's prettier config
geotrev Aug 19, 2026
e06e86a
fix(bolt-slides): show an imported deck without a manual reload
geotrev Aug 19, 2026
d0e3861
docs(bolt-slides): stop the agent conflating the draft with the database
geotrev Aug 19, 2026
e963818
feat(bolt-slides): seed a three-slide deck instead of an empty one
geotrev Aug 19, 2026
ba5e632
fix(bolt-slides): stop the dev server overwriting an imported deck
geotrev Aug 19, 2026
7186f6c
feat(bolt-slides): apply an authored draft without waiting for the im…
geotrev Aug 20, 2026
57952dc
fix(bolt-slides): stop an import breaking the editor it lands on
geotrev Aug 20, 2026
c7d04f2
fix(bolt-slides): let the browser keep the text being typed into a slide
geotrev Aug 20, 2026
c5d78db
fix(bolt-slides): present in the frame the deck was given
geotrev Aug 20, 2026
5461042
feat(bolt-slides): share the published deck, not the dev server
geotrev Aug 20, 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
6 changes: 6 additions & 0 deletions bolt-slides/.bolt/prompt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
This project is a slide studio, not a blank app. A deck is DATA: JSON rows rendered by a layout registry. Author a deck by writing `deck.draft.json` at the project root, then running `node scripts/deck.mjs import deck.draft.json`. Always run the import: it is what confirms the deck parsed and reports how many slides landed, so never report slides as added before it has. `data/deck.json` is the generated live database: reach it only through that CLI, never by hand. Never write JSX slides, and never rebuild the editor, the presentation engine, or the layouts. Follow the `slides` skill in `.bolt/skills/slides/`.

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.

After publishing the project, record where it landed with `node scripts/deck.mjs published <url>`, and again whenever that URL changes. Sharing a deck means sharing the published site: until the deck knows that address the editor's Share button stays disabled, since a link built from the dev server's own address opens for nobody else.

The deck API in `server/` is Vite dev-server middleware, so it does not exist in a published build. `vite.config.ts` bakes the deck into the bundle at build time and the client falls back to that snapshot, which is what makes a published deck presentable. Preserve both; do not "fix" the missing API by deleting the fallback.
612 changes: 313 additions & 299 deletions bolt-slides/.bolt/skills/slides/SKILL.md

Large diffs are not rendered by default.

29 changes: 8 additions & 21 deletions bolt-slides/.gitignore
Original file line number Diff line number Diff line change
@@ -1,23 +1,10 @@
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

.vscode/*
!.vscode/extensions.json
.idea
node_modules/
dist/
.vite/
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.env

# generated at runtime — the deck lives here (seed via data/deck.seed.json)
data/deck.json
data/deck.db
public/og.png
171 changes: 145 additions & 26 deletions bolt-slides/README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,155 @@
# Bolt slides skill
# Bolt Slides — starter

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
Everything persists to **one portable JSON file** (`data/deck.json`) with no database and no dependencies — the development stand-in for a hosted backend.

## Quick start

```bash
npm install
npm run dev # editor at http://localhost:5173 · present at /present
```

The first run creates an empty deck from `data/deck.seed.json` — add slides in
the editor, or prompt one into existence with the `slides` skill.

## What's inside

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.
| Route | What it is |
|---|---|
| `/` | Editor — thumbnail rail (always open; drag to reorder, right-click to duplicate/delete/insert), click any text on the slide to edit it, inspector for layout props · background (color/gradient/image) · animation · transition, comments, per-slide status, speaker notes |
| `/present` | Presentation — floating dock, side panel (S) and grid overview (G), click-builds, presenter view with notes + timer (P), annotation mode (D — pen, highlighter, laser, shapes, eraser, undo/redo), fullscreen (F) |

## Add it in Bolt
Present and the presenter console open in a window of their own when the app has
a tab of its own, so the deck can go up on a projector while the editor stays
put. Framed — a Bolt preview, a docs page — they take over the frame instead: a
preview URL is only resolvable by the tab connected to the project, so a second
tab would open to nothing. The dock's last button goes back to the editor.

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.
- **Share** (top bar) hands out links to the *published* deck: the presentation,
and the presenter console for whoever is speaking. It stays disabled until the
deck knows where it was published, because the address this editor runs on is
reachable by nobody else — a preview URL belongs to the one tab connected to
the project, and localhost belongs to one machine. Record it after publishing
(`node scripts/deck.mjs published <url>`, which the agent does for you) or type
it into the dialog, and change it there if the project moves.

## Run it locally
A published deck is a static build with no server behind it, so a link cannot
be made private, password protected, or writable — and the speaker notes in it
are public to anyone holding it. The token-and-password layer the server still
implements (`server/share.mjs`: 96-bit tokens, scrypt password hashes, unlock
throttling, per-mode permissions) only enforces anything while a server is
running, which is what [docs/cloud-setup.md](docs/cloud-setup.md) is for.

- **Export PDF** (top bar) renders every slide at 1280×720 and downloads a PDF.
- **OG image** (top bar) renders slide 1 to `public/og.png`, wired to the
OpenGraph tags in `index.html` — share a deployed deck and slide 1 is the
preview card.
- **People are lightweight profiles** (name + color, no passwords) — you're
asked who you are the first time you comment, never before.

## Decks as data

A slide is a row: `layout` + JSON `props` + background/animation/transition +
status/notes. 31 premium layouts (cover, section, statement, manifesto, big
number, quote, agenda, steps, pillars, timeline, contrast, comparison, table, tabs,
accordion, q&a, pricing, team, logos, poster, story, speaker, persona, bento, stat grid, figures, chart, insight, chat, code) render those rows through `src/layouts/` — so the AI can author decks
and the editor can edit them without either touching React code.

```bash
npm install
npm run dev
node scripts/deck.mjs export deck.draft.json # deck → JSON
node scripts/deck.mjs import deck.draft.json # JSON → deck (data/deck.json)
node scripts/deck.mjs apply # import the draft if it changed
node scripts/deck.mjs status # slide list: layout · status · owner
node scripts/deck.mjs reset # re-seed from data/deck.seed.json
```

`npm run dev` opens the deck at `/`. Re-theme everything by editing one `:root` block
in `src/styles/tokens.css`.
Two files, one name: `deck.draft.json` is the portable deck you hand to the CLI,
`data/deck.json` is the live database it writes. The second is generated and
gitignored — edit the deck in the app or re-import, never by hand.

Importing while the editor is open is fine: the dev server watches
`data/deck.json` and tells the app to re-fetch, so an import from the skill (or
from your own terminal) shows up without a page reload. Saves the editor makes
itself are excluded, so a re-fetch never lands on top of what you are typing.

A `deck.draft.json` left unimported is applied on its own — by `predev` if it is
there before the server boots, by the watcher if it appears after. The deck
records which draft content it already reflects, so a draft applies once and a
restart never re-applies it. Editing in the app does not change the draft, so
your work is what survives; a draft with new content in it is what replaces the
deck.

Round trips are lossless: `export` carries slide ids and `import` keeps them, so
exporting a deck, editing the JSON and importing it back leaves the slides —
and the comments attached to them — intact. A slide written without an `id`
takes over whatever sat in its position, so importing a deck authored from
scratch does not silently re-issue every id: an editor open on the deck keeps
working, and comments stay where they were. A comment whose slide is gone
altogether goes with it.

## The skill

`.bolt/skills/slides/SKILL.md` teaches Bolt's AI to author decks as JSON —
including which animation, transition, background and status to set per slide
— and to import them with the CLI. Ask Bolt for "a 12-slide seed pitch for …"
and refine what lands in the editor.

## Publishing

Publish and the deck presents — read-only.

The API lives in the Vite dev server, so a published build has no backend to
talk to. `npm run build` therefore bakes the current deck into the bundle as
`deck-snapshot.json`, and the app falls back to it when no API answers. A
published link gets every layout, animation, transition, present mode and
presenter view. It cannot write: no editing, no comments, and `/` redirects
to `/present`.

Two things worth knowing:

- **The snapshot is taken at build time** — re-publish to push later edits.
- **Speaker notes are baked in and public.** Anyone with the link can open
presenter view and read them. Per-slide status, comments, profiles and share
links are stripped from the snapshot and never leave your machine.
- **Tell the deck where it landed** — `node scripts/deck.mjs published <url>`,
or the Share dialog. That URL is the base for every link the editor hands out,
and it is the only base that works: the published site is the one address a
visitor can open.

Editing a published deck — plus comments, per-audience links and passwords, which
all need a server to enforce — means moving storage to a real backend:
**[docs/cloud-setup.md](docs/cloud-setup.md)** has the route contract, the
table layout, the permission rules, the owner question (the bare published URL
is a credential — decide that deliberately) and a publish checklist.

`npm run preview` deliberately serves the build *without* the API, so it shows
you what visitors get.

## Architecture

```
data/deck.json ← the single source of truth (gitignored; seed JSON is committed)
server/db.mjs ← JSON file persistence + cross-process reload
server/api.mjs ← REST API, mounted on the Vite dev server (vite.config.ts)
server/snapshot.mjs ← bakes the deck into the build so published decks render
server/draft.mjs ← applies an unimported deck.draft.json (predev + watcher)
scripts/deck.mjs ← import/export/apply/reset/status/published CLI
src/data/ ← types + zustand store (optimistic writes)
src/layouts/ ← the layout registry: props schema + renderer per layout
src/components/ ← the premium section components (locked design system)
src/deck/ ← the presentation engine (dock, rail, builds, presenter)
src/slide/SlideView.tsx ← one slide row → pixels (backgrounds, animation modes)
src/edit/ ← the editor (rail, canvas, inspector, comments, notes)
src/export/ ← PDF + OG rendering (off-screen iframes)
src/styles/tokens.css ← the theme: edit :root values only; accent is ONE solid color
```

## Theming

Everything visual derives from the `:root` tokens in `src/styles/tokens.css`
— change the values (never the names) to re-brand the deck AND the editor
chrome in one place. `--accent` must stay a solid color.
49 changes: 49 additions & 0 deletions bolt-slides/data/deck.seed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"title": "Bolt Slides",
"transition": "fade",
"font": "inter",
"slides": [
{
"layout": "cover",
"props": {
"kicker": "Bolt Slides",
"title": "Your deck ==starts here==.",
"subtitle": "Ask Bolt for a presentation and these three slides are replaced by yours.",
"foot": "Editing at / · presenting at /present"
},
"animation": "cascade",
"notes": "Placeholder deck. Ask for a real one and this is overwritten."
},
{
"layout": "steps",
"props": {
"kicker": "How it works",
"title": "A deck is data, not code.",
"items": [
{
"title": "Describe the deck",
"body": "Topic, audience, brand. Real numbers beat placeholders."
},
{
"title": "Bolt writes and imports it",
"body": "Slides are JSON rows rendered by 31 layouts — no JSX per slide."
},
{
"title": "You take it from there",
"body": "Edit any text in place, reorder, comment, present, export a PDF."
}
]
},
"animation": "cascade"
},
{
"layout": "statement",
"props": {
"kicker": "Try it",
"title": "\"Make me a ==Series A deck== for a robotics company.\"",
"body": "Or open /present to see how this one behaves first."
},
"animation": "fade"
}
]
}
Loading