## File: README.md

<a href="https://vercel.com/open-source-program">
  
</a>

# open-slide

[](https://github.com/1weiho/open-slide/stargazers)
[](https://github.com/1weiho/open-slide/network/members)
[](https://opensource.org/licenses/MIT)

**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.

```bash
npx @open-slide/cli init my-slide
```

## Why 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](https://svgl.app/) 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

```bash
npx @open-slide/cli init my-slide
cd my-slide
pnpm dev
```

The 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](CLAUDE.md) for the hard rules.

## Repo layout

This repo is a pnpm + Turbo monorepo.

| Path | Description |
| --- | --- |
| [packages/core](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](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](apps/demo) | Example workspace that consumes `@open-slide/core` via `workspace:*`. Used for local development of the framework. |

## Development

```bash
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 biome
```

## Support

If open-slide has been useful to you, consider supporting development:

[](https://ko-fi.com/D1D11YPUP1)

## License

MIT © [Yiwei Ho](https://github.com/1weiho)

---

## 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-level `assets/` folder and are
  imported through the `@assets/*` Vite alias.

```text
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.mp4
```

```tsx
import 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](https://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
npm run build
npm run preview     # serves dist/ for sanity-checking
```

`dist/` 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 in
[`open-slide.config.ts`](/docs/reference/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](/docs/core-feature/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`](/docs/skills/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`](/docs/skills/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 / `PgDn` to advance.
- `←` / `↑` / `PgUp` to go back.
- `Home` / `End` to jump to first / last.
- `Esc` to 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):

```tsx
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-slide` reads 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

```text
themes/
├── corporate.md
├── corporate.demo.tsx
├── editorial.md
├── editorial.demo.tsx
├── retro-print.md
└── retro-print.demo.tsx
```

The markdown is free-form. A useful template:

```md title="themes/corporate.md"
---
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):

```tsx title="themes/corporate.demo.tsx"
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:

```text
/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`](/docs/skills/create-theme) to extract its design tokens
into a reusable theme file.

---

## File: apps/web/content/docs/cli/build.mdx

---
title: open-slide build
---

```npm
open-slide build
# or
npm run build
```

Builds every deck under `slides/` into a self-contained static site at
`dist/`. 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

```text
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
  `build` toggles — see [Export](/docs/core-feature/export).
- If the build won't sit at the domain root (GitHub Pages project sites, a
  reverse-proxy folder), set `base` so asset URLs and navigation resolve
  against the deployed path — see [Config](/docs/reference/config).

---

## File: apps/web/content/docs/cli/dev.mdx

---
title: open-slide dev
---

```npm
open-slide dev
# or, via the generated script
npm run dev
```

Boots 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`:

```ts title="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`](/docs/reference/config#remote-ide--proxied-dev-server)
in the config.