File: README.md
open-slide
The slide framework built for agents. Describe your deck in natural language β your coding agent writes the React. open-slide handles the canvas, scaling, navigation, hot reload, and present mode so the agent can focus on content.
Every slide renders into a fixed 1920 Γ 1080 canvas. Pages are arbitrary React components, not a constrained DSL.
npx @open-slide/cli init my-slideWhy open-slide
Slides are visual code. Agents are great at writing code. open-slide is the missing runtime that turns "make slides about X" into a polished, presentable deck β without you ever leaving the chat.
Highlights
π€ Agent-native authoring
Works with any coding agent (Claude Code, Codex, Cursor, β¦). The scaffolder ships with built-in skills:
/create-slideβ drafts a deck end-to-end. Asks four scoping questions (topic & aesthetic, page count, text density, motion vs. static), picks an id, plans the structure, and writes the pages./slide-authoringβ the technical reference for the 1920 Γ 1080 canvas, type scale, palette, and layout rules. The agent reads this before writing.
From a one-line prompt to a polished deck, no boilerplate.
π― In-browser inspector
Click any element in the dev server and attach a comment β "make this red", "change to 'Open Slide Rocks'", "shrink the headline". Comments are persisted as @slide-comment markers in source. Run /apply-comments and the agent applies every pending edit, then clears the markers.
The loop: present β click to comment β /apply-comments β repeat.
πΌοΈ Assets manager + svgl logo search
Manage images, videos, and fonts per deck through a built-in assets panel. Search and drop in any brand logo via the integrated svgl catalogue β no more hunting for SVGs.
π¬ Professional present mode
Fullscreen playback with keyboard navigation, plus a presenter mode with current/next slide preview, speaker notes, and a timer. Built for the stage, not just the browser tab.
π¦ Export to static HTML & PDF
One command exports your deck as a self-contained static HTML site or a print-ready PDF. Share without a server.
π Slide manager
Organise decks into folders with custom emoji and drag-and-drop to reorder. Useful once you've built more than three decks and need to find anything.
π Deploy-friendly
Outputs a plain static build β one-click deploy to Vercel, Cloudflare Pages, Zeabur, Netlify, or any static host. No server, no runtime, no lock-in.
Get started
npx @open-slide/cli init my-slide
cd my-slide
pnpm devThe scaffolded workspace ships with agent skills preconfigured for Claude Code. From there you drive the deck through your agent β or edit slides//index.tsx directly. See CLAUDE.md for the hard rules.
Repo layout
This repo is a pnpm + Turbo monorepo.
| Path | Description |
|---|---|
| packages/core | @open-slide/core β runtime (home page, slide viewer, present mode, inspector), Vite plugin, and the open-slide dev/build/preview CLI. |
| packages/cli | @open-slide/cli β npx @open-slide/cli init scaffolder. Generates a minimal workspace where Vite/React/tsconfig stay hidden inside core. |
| apps/demo | Example workspace that consumes @open-slide/core via workspace:*. Used for local development of the framework. |
Development
pnpm install
pnpm dev # runs the demo against the local @open-slide/core
pnpm build # builds all packages
pnpm check # type-checks all packages
pnpm lint # lints via biomeSupport
If open-slide has been useful to you, consider supporting development:
License
MIT Β© Yiwei Ho
File: apps/web/content/docs/core-feature/assets-manager.mdx
title: Assets manager
Assets come in two scopes:
- Slide-local β one-off files only used by a single deck. They live next to
the deck that uses them and are imported with a relative path. - Global β files reused across decks or themes (company logos, presenter
avatars, recurring icons). They live in a top-levelassets/folder and are
imported through the@assets/*Vite alias.
project-root/
βββ assets/ # global β shared across decks
β βββ acme.svg
β βββ avatar.png
βββ slides/
βββ q2-launch/
βββ index.tsx
βββ assets/ # slide-local β only this deck
βββ cover.png
βββ chart.svg
βββ intro.mp4import cover from './assets/cover.png'; // slide-local
import chart from './assets/chart.svg'; // slide-local
import logo from '@assets/acme.svg'; // global
const Cover = () => (
<>
</>
);Both paths run through Vite's normal asset pipeline β files get hashed and
emitted on build like any other import.
In-browser asset manager
The dev server has an Assets panel with a Slide / Global scope toggle
at the top:
- Slide scope reads and writes
slides//assets/. - Global scope reads and writes the project root
assets/folder.
Drag files in to upload them. Rename and replace from the same pane the
inspector uses to swap an element's src β the Replace picker carries the
same scope toggle, and the rewritten import uses ./assets/... or@assets/... to match the source.
The home sidebar also exposes the global manager directly: click Assets
under Themes to open the locked-global view at /assets.
svgl logo search
Need a brand logo? The Assets panel has an svgl search powered by
svgl.app. Search, pick, drop β the SVG lands in whichever
scope is active (slide-local or global) and you can import it immediately.
File: apps/web/content/docs/core-feature/export.mdx
title: Export
open-slide build produces a plain static site under dist/ β everything
renders on the client, so no server is needed.
Static HTML
npm run build
npm run preview # serves dist/ for sanity-checkingdist/ is portable. Drop it on Vercel, Cloudflare Pages, Netlify, Zeabur,
GitHub Pages β anything that hosts files.
From the toolbar
The Export menu in the slide toolbar exports the active deck directly:
- HTML β a static HTML snapshot of the deck. Decks that reference
assets download as a zip (HTML file + assets folder). - PDF β one 1920 Γ 1080 landscape PDF page per deck page. Not supported
in Safari. - PPTX (images) β each page rendered as an image on a PowerPoint slide.
Native editable PPTX export is not available yet.
What gets shipped
The build pipeline only includes:
- The decks under
slides/. - The assets each deck imports.
- The runtime β viewer, scaler, present mode (configurable; see below).
You can trim the surface area for a public share by toggling fields inopen-slide.config.ts:
import type { OpenSlideConfig } from '@open-slide/core';
const config: OpenSlideConfig = {
build: {
showSlideBrowser: false, // hide the deck index
showSlideUi: false, // hide on-canvas UI (toolbar etc.)
allowHtmlDownload: false, // hide the export menu (HTML / PDF / PPTX)
},
};
export default config;File: apps/web/content/docs/core-feature/inspector.mdx
title: Inspector
The inspector is a dev-only overlay built into the open-slide runtime. Toggle
it from the toolbar (or press i), then click any element on the slide.
Two surfaces
The inspector exposes two ways to edit a slide:
Visual editor
Click an element. A property panel opens with editable inputs:
- Text β replace inline.
- Typography β font family, weight, size, letter-spacing.
- Colour β text colour, background colour.
- Image src β swap to any file in the deck's or the global assets
folder; see the assets manager for
how the two scopes work.
All edits buffer in memory until you press Save in the SaveBar. One save
lands as a single HMR write to disk.
Comments
Sometimes the change is easier to describe than to perform. Click an element,
pick Comment, type a note:
use the accent colour on this title
The inspector pins the note as an @slide-comment marker in the source. The
marker travels with the element and is invisible at runtime. Run/apply-comments in your agent to turn the notes into edits β see/apply-comments.
Selection is visible to your agent
Whenever you click, the inspector publishes the picked element to a file
your agent reads via /current-slide β so
"make this bigger" resolves to the exact JSX node with no "which one?"
round-trip.
File: apps/web/content/docs/core-feature/present-mode.mdx
title: Present mode
Fullscreen playback
From the dev server, hit Present (or press f). The current deck goes
fullscreen, scaled uniformly to fit the screen:
- Arrow / space /
PgDnto advance. β/β/PgUpto go back.Home/Endto jump to first / last.Escto exit.
Presenter view
Press p (or the presenter button in the present control bar) to open the
presenter view in a second window:
- Current slide at the size you'll show it.
- Next slide preview.
- Speaker notes for the current page.
- Timer β elapsed time since you entered present mode.
Keep it on the laptop screen while the deck mirrors to the projector.
Speaker notes
Add notes alongside the page array β an array of strings, index-aligned with
the pages (use undefined to skip a page):
export const notes = [
'Open with a smile. Mention the analyst quote from yesterday.',
undefined,
'Pause here for questions. The chart is a hand-off.',
];File: apps/web/content/docs/core-feature/themes.mdx
title: Themes
A theme is a bundle of two paired files under themes/:
themes/.mdβ agent-facing recipe: palette, typography, layout,
fixed components, motion.create-slidereads this before authoring.themes/.demo.tsxβ a runnable mini-slide that demonstrates the
theme. The dev UI's Themes panel imports it and renders it as the
theme's live preview card.
Both files share the same stem so the runtime can pair them automatically.
If you author more than two decks you'll want them to look related β a theme
captures the recipe in one place any deck can reference, and the demo makes
it browsable in the dev UI before you pick one.
File layout
themes/
βββ corporate.md
βββ corporate.demo.tsx
βββ editorial.md
βββ editorial.demo.tsx
βββ retro-print.md
βββ retro-print.demo.tsxThe markdown is free-form. A useful template:
---
name: Corporate
mode: dark
---
# Corporate
## Palette
- ink: #0a0a0c
- accent: #4a52b5
- mint: #1f8458
## Typography
- display: 'SΓΆhne', system-ui
- mono: 'JetBrains Mono'
## Voice
- Tight, declarative, never coy.
- One concept per slide.The demo is a normal slide module β same shape as slides//index.tsx,
just placed under themes/ so the runtime treats it as preview-only (it
does not appear in the slides list):
import type { Page } from '@open-slide/core';
const Cover: Page = () => (/* β¦inline Title / Footer using theme tokensβ¦ */);
const Content: Page = () => (/* β¦ */);
export default [Cover, Content];Keep the demo's inline Title/Footer/Eyebrow components verbatim in sync
with the snippets in the markdown β what authors see in the Themes panel
should match what create-slide will paste into a real slide.
Using a theme
When you call /create-slide, mention the theme by name:
/create-slide for "Q3 board update β use the corporate theme"The agent loads themes/corporate.md, applies the palette and type stack,
and writes pages that match.
Extracting a theme
Already have a deck whose look you like? Use/create-theme to extract its design tokens
into a reusable theme file.
File: apps/web/content/docs/cli/build.mdx
title: open-slide build
open-slide build
# or
npm run buildBuilds every deck under slides/ into a self-contained static site atdist/. The output has no server requirement β drop it on any static host.
Flags
| Flag | Default | Description |
|---|---|---|
--out-dir <dir> |
dist |
Write the static build to a custom folder. |
Output
dist/
βββ index.html
βββ assets/
β βββ ...hashed files...
βββ ...Configuration
Two open-slide.config.ts fields matter at build time:
- For a public share, hide the slide browser and on-canvas UI with the
buildtoggles β see Export. - If the build won't sit at the domain root (GitHub Pages project sites, a
reverse-proxy folder), setbaseso asset URLs and navigation resolve
against the deployed path β see Config.
File: apps/web/content/docs/cli/dev.mdx
title: open-slide dev
open-slide dev
# or, via the generated script
npm run devBoots a Vite-powered dev server on http://localhost:5173 (or the next free
port). Hot module reload is wired up for both the runtime and your slide
files β saving an index.tsx updates the canvas without losing slide state.
What you get
- Slide browser β list of every deck under
slides/. - Canvas β the active page, scaled to fit the viewport.
- Inspector β click any element to edit it visually, drop a
@slide-comment, or both. - Assets panel β drag, rename, replace, plus svgl logo search.
- Present mode β fullscreen playback with presenter view.
Flags
| Flag | Default | Description |
|---|---|---|
-p, --port |
from config | Override the listening port. |
--host [host] |
localhost only | Expose the dev server on the network. Optional host pin. |
--open |
off | Open the browser on start. |
--no-skills-check |
off | Skip the built-in skills drift check. |
The skills check also obeys the OPEN_SLIDE_SKIP_SKILLS_CHECK=1
environment variable β useful in CI or container builds where stdin
isn't a TTY.
Configuration
Override the port (and a few other knobs) in open-slide.config.ts:
import type { OpenSlideConfig } from '@open-slide/core';
const config: OpenSlideConfig = {
port: 5174,
slidesDir: 'slides',
};
export default config;Accessing the dev server through a proxied domain (Coder, Codespaces, a
reverse proxy) and hitting Blocked request. This host is not allowed.?
Set allowedHosts
in the config.