Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions .changeset/quiet-slides-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@open-slide/core': minor
'@open-slide/shared': minor
'@open-slide/react': minor
'@open-slide/svelte': minor
'@open-slide/cli': minor
---

Add native Svelte support while automatically keeping existing core installations on React.
2 changes: 1 addition & 1 deletion .changeset/thick-donuts-yell.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
'@open-slide/core': minor
'@open-slide/react': minor
---

Add a ⌘K command menu for searching decks, jumping to pages, and running slide actions.
2 changes: 1 addition & 1 deletion .changeset/witty-donkeys-test.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
'@open-slide/core': patch
'@open-slide/react': patch
---

Add a Playwright e2e suite covering the viewer, present mode, presenter sync, inspector editing, dev API, and static builds.
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ dist
.vite
*.tsbuildinfo
.turbo
packages/cli/template/.agents/skills
packages/core/e2e/.scratch
packages/cli/templates/react/.agents/skills
packages/cli/templates/svelte/.agents/skills
packages/react/e2e/.scratch
playwright-report
test-results
18 changes: 11 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ pnpm + Turbo monorepo.

| Path | Package | Role |
| --- | --- | --- |
| `packages/core` | `@open-slide/core` | Runtime (viewer, present mode, inspector), Vite plugin, `open-slide` dev/build CLI. |
| `packages/core` | `@open-slide/core` | Compatibility package that automatically routes existing installations to the React runtime. |
| `packages/shared` | `@open-slide/shared` | Framework-neutral config, types, slide discovery, virtual modules, and shared utilities. |
| `packages/react` | `@open-slide/react` | React viewer, presenter, inspector, Vite integration, and runtime CLI. |
| `packages/svelte` | `@open-slide/svelte` | Svelte viewer, presenter, inspector, Vite integration, and runtime CLI. |
| `packages/cli` | `@open-slide/cli` | `npx @open-slide/cli init` scaffolder + project template. |
| `apps/demo` | private | Local consumer of `@open-slide/core` via `workspace:*`. Dogfood target — run `pnpm dev` here to exercise the framework. |
| `apps/demo` | private | React dogfood target consuming `@open-slide/react` via `workspace:*`. |
| `apps/svelte-demo` | private | Svelte dogfood target consuming `@open-slide/svelte` via `workspace:*`. |
| `apps/web` | private | Marketing site (Next.js). |

Shared config: `biome.json`, `turbo.json`, `pnpm-workspace.yaml`, `tsconfig` per package.
Expand All @@ -28,20 +32,20 @@ pnpm check:fix # auto-fix what biome can
pnpm test # vitest
```

Filter to one package: `pnpm core <script>` / `pnpm cli <script>`.
Filter to one package: `pnpm shared <script>` / `pnpm core <script>` / `pnpm react <script>` / `pnpm svelte <script>` / `pnpm cli <script>`.

## Hard rules

- **Biome must pass before commit.** Run `pnpm check` (or `pnpm check:fix`). CI and the user's review both expect a clean tree.
- **If `packages/core` or `packages/cli` changes, add a changeset.** Run `pnpm changeset`, pick the right package(s) and bump (`patch` for fixes/polish, `minor` for new public API, `major` for breaking). Apps (`demo`, `web`) and root tooling do **not** need one.
- **If a published package changes, add a changeset.** Run `pnpm changeset`, pick the right package(s) and bump (`patch` for fixes/polish, `minor` for new public API, `major` for breaking). Apps and root tooling do **not** need one.
- **Changeset descriptions: short and direct.** One line, present-tense, what changed from a user's perspective. Match the tone of `.changeset/*.md` already in the repo. No paragraphs, no rationale, no "this PR…".
- Good: `Replace spinner with a hairline + sliding bar for slide and presenter loading states.`
- Bad: `This change introduces a new loading indicator because the previous spinner felt heavy and we wanted something more subtle for presentation contexts…`
- Don't bump versions or edit `CHANGELOG.md` by hand — `changeset version` owns that.
- Don't add dependencies casually. The `core` runtime ships to users; every dep inflates install size.
- `packages/core/src/app/components/ui` is shadcn-generated and biome-ignored — leave it alone unless regenerating.
- Don't add dependencies casually. The framework runtimes ship to users; every dep inflates install size.
- `packages/react/src/app/components/ui` is shadcn-generated and biome-ignored — leave it alone unless regenerating.
- **Default to writing no comments.** Only add one when the WHY is non-obvious — a hidden constraint, a subtle invariant, a workaround for a specific bug, behavior that would surprise a reader. Don't explain WHAT the code does (well-named identifiers handle that), don't reference tasks/PRs/callers ("added for X", "used by Y"), don't write section-divider banners (`// ── Section ──`) or module-header descriptions, and don't leave commented-out code. If removing a comment wouldn't confuse a future reader, don't write it.

## Releasing (reference)

`pnpm release` builds `core` + `cli` and runs `changeset publish`. Triggered by the maintainer, not by agents.
`pnpm release` builds all published packages and runs `changeset publish`. Triggered by the maintainer, not by agents.
25 changes: 16 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Contributing to open-slide

Thanks for your interest in improving open-slide! This guide covers the workflow for contributing to the framework itself — the `@open-slide/core` runtime, the `@open-slide/cli` scaffolder, and the supporting apps.
Thanks for your interest in improving open-slide! This guide covers the shared framework contracts, compatibility package, React and Svelte runtimes, CLI scaffolder, and supporting apps.

If you're authoring slides inside a scaffolded project, you don't need this file — drive your deck through your coding agent or edit `slides/<id>/index.tsx` directly.

Expand All @@ -19,9 +19,13 @@ pnpm + Turbo monorepo.

| Path | Package | Role |
| --- | --- | --- |
| [`packages/core`](packages/core) | `@open-slide/core` | Runtime (viewer, present mode, inspector), Vite plugin, `open-slide` dev/build CLI. |
| [`packages/core`](packages/core) | `@open-slide/core` | Compatibility package that automatically routes existing installations to the React runtime. |
| [`packages/shared`](packages/shared) | `@open-slide/shared` | Framework-neutral config, types, slide discovery, virtual modules, and shared utilities. |
| [`packages/react`](packages/react) | `@open-slide/react` | React viewer, presenter, inspector, Vite integration, and runtime CLI. |
| [`packages/svelte`](packages/svelte) | `@open-slide/svelte` | Svelte viewer, presenter, inspector, Vite integration, and runtime CLI. |
| [`packages/cli`](packages/cli) | `@open-slide/cli` | `npx @open-slide/cli init` scaffolder + project template. |
| [`apps/demo`](apps/demo) | private | Local consumer of `@open-slide/core` via `workspace:*`. The dogfood target for the framework. |
| [`apps/demo`](apps/demo) | private | React dogfood target consuming `@open-slide/react` via `workspace:*`. |
| [`apps/svelte-demo`](apps/svelte-demo) | private | Svelte dogfood target consuming `@open-slide/svelte` via `workspace:*`. |
| [`apps/web`](apps/web) | private | Marketing site (Next.js). |

## Prerequisites
Expand All @@ -38,13 +42,13 @@ cd open-slide
pnpm install
```

Then run the demo against the local `@open-slide/core`:
Then run both demos against the local runtime packages:

```bash
pnpm dev
```

`apps/demo` is the fastest way to exercise framework changes — edit `packages/core`, the demo hot-reloads.
Use `apps/demo` for React and `pnpm dev:svelte-demo` for Svelte. Both consume the workspace packages directly.

## Useful scripts

Expand All @@ -61,6 +65,9 @@ Filter to one package:

```bash
pnpm core <script> # e.g. pnpm core build
pnpm shared <script>
pnpm react <script>
pnpm svelte <script>
pnpm cli <script>
```

Expand All @@ -75,7 +82,7 @@ pnpm cli <script>
pnpm test
```
`pnpm check:fix` will auto-fix most formatting and lint issues.
4. **Add a changeset if you touched `packages/core` or `packages/cli`:**
4. **Add a changeset if you touched a published package:**
```bash
pnpm changeset
```
Expand All @@ -99,9 +106,9 @@ pnpm cli <script>
## Style & conventions

- **Biome must pass.** Formatting, lint, and import organisation are all enforced by `pnpm check`.
- **No casual dependencies.** The `core` runtime ships to users — every dep inflates install size. Prefer a small piece of inline code over a new package.
- **No casual dependencies.** The framework runtimes ship to users — every dep inflates install size. Prefer a small piece of inline code over a new package.
- **Default to writing no comments.** Only add one when the *why* is non-obvious — a hidden constraint, a subtle invariant, a workaround for a specific bug. Don't explain *what* the code does; well-named identifiers handle that.
- **Leave `packages/core/src/app/components/ui` alone.** It's shadcn-generated and biome-ignored unless you're regenerating it.
- **Leave `packages/react/src/app/components/ui` alone.** It's shadcn-generated and biome-ignored unless you're regenerating it.

## Testing

Expand All @@ -110,7 +117,7 @@ pnpm cli <script>

## Releases

Releases are cut by the maintainer via `pnpm release`, which builds `@open-slide/core` + `@open-slide/cli` and runs `changeset publish`. Contributors don't need to publish anything — just land the changeset alongside your code.
Releases are cut by the maintainer via `pnpm release`, which builds every published package and runs `changeset publish`. Contributors don't need to publish anything — just land the changeset alongside your code.

## Questions

Expand Down
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
[![GitHub forks](https://img.shields.io/github/forks/1weiho/open-slide?style=for-the-badge)](https://github.com/1weiho/open-slide/network/members)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](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.
**The slide framework built for agents.** Describe your deck in natural language — your coding agent writes React or Svelte. 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.
Every slide renders into a fixed **1920 × 1080** canvas. Pages are arbitrary framework components, not a constrained DSL.

```bash
npx @open-slide/cli init my-slide
# or: npx @open-slide/cli init my-slide --framework svelte
```

## Why open-slide
Expand Down Expand Up @@ -69,26 +70,31 @@ cd my-slide
pnpm dev
```

The scaffolded workspace ships with agent skills preconfigured for Claude Code. From there you drive the deck through your agentor edit `slides/<id>/index.tsx` directly. See [CLAUDE.md](CLAUDE.md) for the hard rules.
The React scaffold ships with agent skills preconfigured for Claude Code. From there you drive the deck through your agent, or edit `slides/<id>/index.tsx` directly. Svelte workspaces use an `index.ts` deck manifest and one `.svelte` component per page.

## 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. |
| [packages/core](packages/core) | `@open-slide/core` — compatibility package that automatically routes existing installations to the React runtime. |
| [packages/shared](packages/shared) | `@open-slide/shared` — framework-neutral config, types, slide discovery, virtual modules, filesystem helpers, and shared runtime utilities. |
| [packages/react](packages/react) | `@open-slide/react` — the existing React viewer, presenter, inspector, Vite integration, and runtime CLI. |
| [packages/svelte](packages/svelte) | `@open-slide/svelte` — native Svelte viewer, presenter, inspector, Vite integration, and runtime CLI. |
| [packages/cli](packages/cli) | `@open-slide/cli` — framework-selecting workspace scaffolder. |
| [apps/demo](apps/demo) | React dogfood workspace that consumes `@open-slide/react` via `workspace:*`. |
| [apps/svelte-demo](apps/svelte-demo) | Svelte dogfood workspace that validates the native runtime in the Turbo graph. |

## 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
pnpm dev # runs both framework demos
pnpm dev:svelte-demo # runs only the Svelte demo
pnpm build # builds all packages and apps
pnpm typecheck # type-checks the workspace graph
pnpm check # formats and lints with Biome
```

## Star history
Expand Down
6 changes: 3 additions & 3 deletions apps/demo/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# open-slide workspace

Slides as React components. Each slide lives under `slides/<id>/index.tsx` and default-exports an array of page components. The `@open-slide/core` runtime handles layout, scaling, navigation, thumbnails, and fullscreen play mode — you just write the pages.
Slides as React components. Each slide lives under `slides/<id>/index.tsx` and default-exports an array of page components. The `@open-slide/react` runtime handles layout, scaling, navigation, thumbnails, and fullscreen play mode — you just write the pages.

## Getting started

Expand All @@ -23,7 +23,7 @@ Then open the dev server and create a new slide at `slides/<your-slide>/index.ts

```tsx
// slides/my-slide/index.tsx
import type { Page, SlideMeta } from '@open-slide/core';
import type { Page, SlideMeta } from '@open-slide/react';

const Cover: Page = () => (
<div style={{ width: '100%', height: '100%' }}>Hello</div>
Expand Down Expand Up @@ -52,7 +52,7 @@ This workspace ships with Claude Code skills preconfigured under `.claude/skills
Optional `open-slide.config.ts` at the workspace root:

```ts
import type { OpenSlideConfig } from '@open-slide/core';
import type { OpenSlideConfig } from '@open-slide/react';

const openSlideConfig: OpenSlideConfig = {
port: 5173,
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/open-slide.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { OpenSlideConfig } from '@open-slide/core';
import type { OpenSlideConfig } from '@open-slide/react';

const openSlideConfig: OpenSlideConfig = {};

Expand Down
2 changes: 1 addition & 1 deletion apps/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"preview": "open-slide preview"
},
"dependencies": {
"@open-slide/core": "workspace:*",
"@open-slide/react": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/slides/build-on-reveal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type DesignSystem, type Page, type SlideMeta, Step, Steps } from '@open-slide/core';
import { type DesignSystem, type Page, type SlideMeta, Step, Steps } from '@open-slide/react';
import type { CSSProperties } from 'react';

export const design: DesignSystem = {
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/slides/claude-code-intro/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DesignSystem, Page, SlideMeta } from '@open-slide/core';
import type { DesignSystem, Page, SlideMeta } from '@open-slide/react';

export const design: DesignSystem = {
palette: {
Expand Down
7 changes: 6 additions & 1 deletion apps/demo/slides/harness-engineering/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { type DesignSystem, type Page, type SlideMeta, useSlidePageNumber } from '@open-slide/core';
import {
type DesignSystem,
type Page,
type SlideMeta,
useSlidePageNumber,
} from '@open-slide/react';

export const design: DesignSystem = {
palette: { bg: '#05070a', text: '#e6edf3', accent: '#39ff88' },
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/slides/image-placeholder-demo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ImagePlaceholder, type Page, type SlideMeta } from '@open-slide/core';
import { ImagePlaceholder, type Page, type SlideMeta } from '@open-slide/react';

const fill = {
width: '100%',
Expand Down
7 changes: 6 additions & 1 deletion apps/demo/slides/llm-fundamentals/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { type DesignSystem, type Page, type SlideMeta, useSlidePageNumber } from '@open-slide/core';
import {
type DesignSystem,
type Page,
type SlideMeta,
useSlidePageNumber,
} from '@open-slide/react';

export const design: DesignSystem = {
palette: { bg: '#f7f5f0', text: '#1a1814', accent: '#6d4cff' },
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/slides/material-design-2014/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DesignSystem, Page, SlideMeta } from '@open-slide/core';
import type { DesignSystem, Page, SlideMeta } from '@open-slide/react';

export const design: DesignSystem = {
palette: { bg: '#fafafa', text: '#212121', accent: '#3f51b5' },
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/slides/morph-demo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
type Page,
type SlideMeta,
type SlideTransition,
} from '@open-slide/core';
} from '@open-slide/react';
import type { CSSProperties, ReactNode } from 'react';

export const design: DesignSystem = {
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/slides/morph-messages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
type SlideMeta,
type SlideTransition,
useIsActivePage,
} from '@open-slide/core';
} from '@open-slide/react';
import { type CSSProperties, type ReactNode, useState } from 'react';

export const design: DesignSystem = {
Expand Down
7 changes: 6 additions & 1 deletion apps/demo/slides/nextjs-ppr-cache/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { type DesignSystem, type Page, type SlideMeta, useSlidePageNumber } from '@open-slide/core';
import {
type DesignSystem,
type Page,
type SlideMeta,
useSlidePageNumber,
} from '@open-slide/react';
import type { ReactNode } from 'react';
import nextMark from './assets/next-js.svg';
import vercelMark from './assets/vercel.svg';
Expand Down
11 changes: 8 additions & 3 deletions apps/demo/slides/open-slide-anatomy/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { type DesignSystem, type Page, type SlideMeta, useSlidePageNumber } from '@open-slide/core';
import {
type DesignSystem,
type Page,
type SlideMeta,
useSlidePageNumber,
} from '@open-slide/react';
import type { ReactNode } from 'react';

export const design: DesignSystem = {
Expand Down Expand Up @@ -412,7 +417,7 @@ const FileContract: Page = () => {
<>
<Token c={violet}>import type</Token> {'{'} <Token c={warm}>DesignSystem</Token>,{' '}
<Token c={warm}>Page</Token>, <Token c={warm}>SlideMeta</Token> {'}'}{' '}
<Token c={violet}>from</Token> <Token c={mint}>'@open-slide/core'</Token>
<Token c={violet}>from</Token> <Token c={mint}>'@open-slide/react'</Token>
{';'}
</>
),
Expand Down Expand Up @@ -1609,7 +1614,7 @@ const Cli: Page = () => {
{ cmd: 'open-slide preview', desc: 'serve dist/ for local check', tint: warm },
{
cmd: 'npx @open-slide/cli init',
desc: 'scaffold a new project from packages/cli/template/',
desc: 'scaffold a new project from packages/cli/templates/react/',
tint: violet,
},
];
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/slides/open-slide-launch/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DesignSystem, Page, SlideMeta } from '@open-slide/core';
import type { DesignSystem, Page, SlideMeta } from '@open-slide/react';
import type { CSSProperties } from 'react';
import openSlide from './assets/open-slide.png';

Expand Down
2 changes: 1 addition & 1 deletion apps/demo/slides/open-slide-on-replit/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DesignSystem, Page, SlideMeta, SlideTransition } from '@open-slide/core';
import type { DesignSystem, Page, SlideMeta, SlideTransition } from '@open-slide/react';
import createSlideSkill from './assets/create-slide-skill.webp';
import initCommand from './assets/init-command.webp';
import openslideHome from './assets/openslide-home.webp';
Expand Down
7 changes: 6 additions & 1 deletion apps/demo/slides/raycast-api/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { type DesignSystem, type Page, type SlideMeta, useSlidePageNumber } from '@open-slide/core';
import {
type DesignSystem,
type Page,
type SlideMeta,
useSlidePageNumber,
} from '@open-slide/react';
import raycastIcon from './assets/raycast.svg';

export const design: DesignSystem = {
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/slides/slide-transitions-maximal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DesignSystem, Page, SlideMeta, SlideTransition } from '@open-slide/core';
import type { DesignSystem, Page, SlideMeta, SlideTransition } from '@open-slide/react';
import type { CSSProperties } from 'react';

export const design: DesignSystem = {
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/slides/slide-transitions/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DesignSystem, Page, SlideMeta, SlideTransition } from '@open-slide/core';
import type { DesignSystem, Page, SlideMeta, SlideTransition } from '@open-slide/react';
import type { CSSProperties } from 'react';

export const design: DesignSystem = {
Expand Down
Loading