@sanity-labs/slides 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +241 -0
- package/SKILL.md +119 -0
- package/dist/cli.d.ts +38 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +386 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/components.d.ts +179 -0
- package/dist/core/components.d.ts.map +1 -0
- package/dist/core/components.js +40 -0
- package/dist/core/components.js.map +1 -0
- package/dist/core/fake-runtime.d.ts +138 -0
- package/dist/core/fake-runtime.d.ts.map +1 -0
- package/dist/core/fake-runtime.js +210 -0
- package/dist/core/fake-runtime.js.map +1 -0
- package/dist/core/font-resolver.d.ts +28 -0
- package/dist/core/font-resolver.d.ts.map +1 -0
- package/dist/core/font-resolver.js +30 -0
- package/dist/core/font-resolver.js.map +1 -0
- package/dist/core/geometry.d.ts +71 -0
- package/dist/core/geometry.d.ts.map +1 -0
- package/dist/core/geometry.js +44 -0
- package/dist/core/geometry.js.map +1 -0
- package/dist/core/index.d.ts +19 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +20 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/manifest.d.ts +123 -0
- package/dist/core/manifest.d.ts.map +1 -0
- package/dist/core/manifest.js +43 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/core/op-translator-pptx.d.ts +150 -0
- package/dist/core/op-translator-pptx.d.ts.map +1 -0
- package/dist/core/op-translator-pptx.js +245 -0
- package/dist/core/op-translator-pptx.js.map +1 -0
- package/dist/core/pptx-runtime.d.ts +103 -0
- package/dist/core/pptx-runtime.d.ts.map +1 -0
- package/dist/core/pptx-runtime.js +405 -0
- package/dist/core/pptx-runtime.js.map +1 -0
- package/dist/core/reconciler.d.ts +113 -0
- package/dist/core/reconciler.d.ts.map +1 -0
- package/dist/core/reconciler.js +453 -0
- package/dist/core/reconciler.js.map +1 -0
- package/dist/core/runtime.d.ts +161 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +11 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/template.d.ts +32 -0
- package/dist/core/template.d.ts.map +1 -0
- package/dist/core/template.js +3 -0
- package/dist/core/template.js.map +1 -0
- package/dist/dev/auto-examples.d.ts +6 -0
- package/dist/dev/auto-examples.d.ts.map +1 -0
- package/dist/dev/auto-examples.js +79 -0
- package/dist/dev/auto-examples.js.map +1 -0
- package/dist/dev/bin/slides-dev.d.ts +3 -0
- package/dist/dev/bin/slides-dev.d.ts.map +1 -0
- package/dist/dev/bin/slides-dev.js +87 -0
- package/dist/dev/bin/slides-dev.js.map +1 -0
- package/dist/dev/bin/slides-dev.mjs +24 -0
- package/dist/dev/compose-deck.d.ts +18 -0
- package/dist/dev/compose-deck.d.ts.map +1 -0
- package/dist/dev/compose-deck.js +19 -0
- package/dist/dev/compose-deck.js.map +1 -0
- package/dist/dev/deck-viewer.d.ts +19 -0
- package/dist/dev/deck-viewer.d.ts.map +1 -0
- package/dist/dev/deck-viewer.js +237 -0
- package/dist/dev/deck-viewer.js.map +1 -0
- package/dist/dev/dev-server/client/entry.d.ts +2 -0
- package/dist/dev/dev-server/client/entry.d.ts.map +1 -0
- package/dist/dev/dev-server/client/entry.js +12 -0
- package/dist/dev/dev-server/client/entry.js.map +1 -0
- package/dist/dev/dev-server/output.d.ts +8 -0
- package/dist/dev/dev-server/output.d.ts.map +1 -0
- package/dist/dev/dev-server/output.js +32 -0
- package/dist/dev/dev-server/output.js.map +1 -0
- package/dist/dev/dev-server/server-only-stub.d.ts +7 -0
- package/dist/dev/dev-server/server-only-stub.d.ts.map +1 -0
- package/dist/dev/dev-server/server-only-stub.js +12 -0
- package/dist/dev/dev-server/server-only-stub.js.map +1 -0
- package/dist/dev/dev-server/start.d.ts +14 -0
- package/dist/dev/dev-server/start.d.ts.map +1 -0
- package/dist/dev/dev-server/start.js +135 -0
- package/dist/dev/dev-server/start.js.map +1 -0
- package/dist/dev/index.d.ts +5 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +5 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/lib/cn.d.ts +3 -0
- package/dist/dev/lib/cn.d.ts.map +1 -0
- package/dist/dev/lib/cn.js +3 -0
- package/dist/dev/lib/cn.js.map +1 -0
- package/dist/dev/slide-canvas.d.ts +12 -0
- package/dist/dev/slide-canvas.d.ts.map +1 -0
- package/dist/dev/slide-canvas.js +123 -0
- package/dist/dev/slide-canvas.js.map +1 -0
- package/dist/dev/styles.css +37 -0
- package/dist/dev/ui/icon-button.d.ts +12 -0
- package/dist/dev/ui/icon-button.d.ts.map +1 -0
- package/dist/dev/ui/icon-button.js +6 -0
- package/dist/dev/ui/icon-button.js.map +1 -0
- package/dist/dev/ui/kbd.d.ts +6 -0
- package/dist/dev/ui/kbd.d.ts.map +1 -0
- package/dist/dev/ui/kbd.js +4 -0
- package/dist/dev/ui/kbd.js.map +1 -0
- package/dist/dev/ui/text-button.d.ts +10 -0
- package/dist/dev/ui/text-button.d.ts.map +1 -0
- package/dist/dev/ui/text-button.js +6 -0
- package/dist/dev/ui/text-button.js.map +1 -0
- package/dist/dev/url-state.d.ts +7 -0
- package/dist/dev/url-state.d.ts.map +1 -0
- package/dist/dev/url-state.js +13 -0
- package/dist/dev/url-state.js.map +1 -0
- package/dist/dev/use-keyboard-nav.d.ts +17 -0
- package/dist/dev/use-keyboard-nav.d.ts.map +1 -0
- package/dist/dev/use-keyboard-nav.js +53 -0
- package/dist/dev/use-keyboard-nav.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/errors.d.ts +57 -0
- package/dist/mcp/errors.d.ts.map +1 -0
- package/dist/mcp/errors.js +44 -0
- package/dist/mcp/errors.js.map +1 -0
- package/dist/mcp/index.d.ts +29 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +29 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/naming.d.ts +37 -0
- package/dist/mcp/naming.d.ts.map +1 -0
- package/dist/mcp/naming.js +43 -0
- package/dist/mcp/naming.js.map +1 -0
- package/dist/mcp/render.d.ts +45 -0
- package/dist/mcp/render.d.ts.map +1 -0
- package/dist/mcp/render.js +77 -0
- package/dist/mcp/render.js.map +1 -0
- package/dist/mcp/schema.d.ts +54 -0
- package/dist/mcp/schema.d.ts.map +1 -0
- package/dist/mcp/schema.js +55 -0
- package/dist/mcp/schema.js.map +1 -0
- package/dist/mcp/server.d.ts +63 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +196 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/scaffold/index.d.ts +39 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +84 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/template-base/README.md +134 -0
- package/dist/scaffold/template-base/_gitignore +4 -0
- package/dist/scaffold/template-base/package.json +35 -0
- package/dist/scaffold/template-base/src/components/Cover.tsx +30 -0
- package/dist/scaffold/template-base/src/index.ts +27 -0
- package/dist/scaffold/template-base/src/preview.tsx +9 -0
- package/dist/scaffold/template-base/tsconfig.build.json +10 -0
- package/dist/scaffold/template-base/tsconfig.json +18 -0
- package/package.json +164 -0
- package/src/__tests__/fixtures/test-template/index.tsx +77 -0
- package/src/__tests__/pptx-mcp.test.ts +85 -0
- package/src/__tests__/pptx-smoke.test.ts +45 -0
- package/src/__tests__/preview.test.ts +28 -0
- package/src/cli.ts +426 -0
- package/src/core/__snapshots__/reconciler.test.ts.snap +320 -0
- package/src/core/components.test.ts +57 -0
- package/src/core/components.ts +196 -0
- package/src/core/fake-runtime.test.ts +174 -0
- package/src/core/fake-runtime.ts +302 -0
- package/src/core/font-resolver.ts +46 -0
- package/src/core/geometry.test.ts +58 -0
- package/src/core/geometry.ts +91 -0
- package/src/core/index.ts +69 -0
- package/src/core/manifest.test.ts +33 -0
- package/src/core/manifest.ts +150 -0
- package/src/core/op-translator-pptx.test.ts +204 -0
- package/src/core/op-translator-pptx.ts +365 -0
- package/src/core/pptx-runtime.test.ts +137 -0
- package/src/core/pptx-runtime.ts +504 -0
- package/src/core/reconciler.test.ts +644 -0
- package/src/core/reconciler.ts +603 -0
- package/src/core/runtime.ts +150 -0
- package/src/core/template.test.ts +136 -0
- package/src/core/template.ts +37 -0
- package/src/dev/auto-examples.ts +89 -0
- package/src/dev/bin/slides-dev.mjs +24 -0
- package/src/dev/bin/slides-dev.ts +101 -0
- package/src/dev/compose-deck.test.ts +68 -0
- package/src/dev/compose-deck.ts +40 -0
- package/src/dev/deck-viewer.tsx +677 -0
- package/src/dev/dev-server/client/entry.tsx +15 -0
- package/src/dev/dev-server/client/index.html +24 -0
- package/src/dev/dev-server/output.ts +37 -0
- package/src/dev/dev-server/server-only-stub.ts +12 -0
- package/src/dev/dev-server/start.ts +155 -0
- package/src/dev/index.ts +4 -0
- package/src/dev/lib/cn.ts +3 -0
- package/src/dev/slide-canvas.test.tsx +66 -0
- package/src/dev/slide-canvas.tsx +170 -0
- package/src/dev/styles.css +37 -0
- package/src/dev/ui/icon-button.tsx +31 -0
- package/src/dev/ui/kbd.tsx +20 -0
- package/src/dev/ui/text-button.tsx +31 -0
- package/src/dev/url-state.test.ts +22 -0
- package/src/dev/url-state.ts +17 -0
- package/src/dev/use-keyboard-nav.ts +64 -0
- package/src/index.ts +17 -0
- package/src/mcp/errors.test.ts +51 -0
- package/src/mcp/errors.ts +76 -0
- package/src/mcp/index.ts +45 -0
- package/src/mcp/naming.test.ts +39 -0
- package/src/mcp/naming.ts +49 -0
- package/src/mcp/render.ts +110 -0
- package/src/mcp/schema.test.ts +86 -0
- package/src/mcp/schema.ts +93 -0
- package/src/mcp/server.test.ts +309 -0
- package/src/mcp/server.ts +276 -0
- package/src/scaffold/index.ts +102 -0
- package/src/scaffold/template-base/README.md +134 -0
- package/src/scaffold/template-base/_gitignore +4 -0
- package/src/scaffold/template-base/package.json +35 -0
- package/src/scaffold/template-base/src/components/Cover.tsx +30 -0
- package/src/scaffold/template-base/src/index.ts +27 -0
- package/src/scaffold/template-base/src/preview.tsx +9 -0
- package/src/scaffold/template-base/tsconfig.build.json +10 -0
- package/src/scaffold/template-base/tsconfig.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# @sanity-labs/slides
|
|
2
|
+
|
|
3
|
+
**Brand-locked PowerPoint generation for LLMs. Write your slide template in React; Claude writes the deck.**
|
|
4
|
+
|
|
5
|
+
[](https://github.com/sanity-labs/slides/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/@sanity-labs/slides)
|
|
7
|
+
|
|
8
|
+
A template is a React project. Each slide type is a component + a Zod schema. The bundled MCP server reads your template and exposes it to Claude (or any MCP client) as auto-derived tools. The LLM can pick slide types and fill props, but it cannot pick fonts, colors, or layout — those are locked in the template.
|
|
9
|
+
|
|
10
|
+
One package on npm. Four subpath exports. One CLI bin.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Quickstart
|
|
15
|
+
|
|
16
|
+
### 1. Scaffold a template
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx @sanity-labs/slides scaffold my-template
|
|
20
|
+
cd my-template
|
|
21
|
+
pnpm install
|
|
22
|
+
pnpm dev # opens the hot-reloading viewer at http://localhost:5173
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
You get a working template with one starter slide (`Cover`) and a Vite-backed dev viewer. Edit anything under `src/` and the page updates.
|
|
26
|
+
|
|
27
|
+
### 2. Build it
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm build
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
That emits `dist/index.js` — the file the MCP server will import.
|
|
34
|
+
|
|
35
|
+
### 3. Wire it into Claude Desktop
|
|
36
|
+
|
|
37
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (or the equivalent on your OS):
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"mcpServers": {
|
|
42
|
+
"slides": {
|
|
43
|
+
"command": "npx",
|
|
44
|
+
"args": [
|
|
45
|
+
"-y",
|
|
46
|
+
"@sanity-labs/slides",
|
|
47
|
+
"serve",
|
|
48
|
+
"--template",
|
|
49
|
+
"/absolute/path/to/my-template/dist/index.js",
|
|
50
|
+
"--output",
|
|
51
|
+
"/Users/you/Desktop"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Restart Claude.
|
|
59
|
+
|
|
60
|
+
### 4. Ask
|
|
61
|
+
|
|
62
|
+
> "Make me a 5-slide Q4 review deck with my template."
|
|
63
|
+
|
|
64
|
+
Claude calls `slides_list` to learn what slide types exist, fills props from the conversation, calls `slides_create`, and hands you back the absolute path to the `.pptx`.
|
|
65
|
+
|
|
66
|
+
Total setup time once you have a template: about two minutes.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## What you ship when you build a template
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
my-template/
|
|
74
|
+
├── package.json
|
|
75
|
+
├── src/
|
|
76
|
+
│ ├── index.ts # the Template value — tokens, fonts, components map
|
|
77
|
+
│ ├── preview.tsx # canonical slide order shown in the viewer
|
|
78
|
+
│ └── components/
|
|
79
|
+
│ └── Cover.tsx # one component = one slide type = one MCP tool
|
|
80
|
+
└── dist/ # `pnpm build` output — what the MCP server imports
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
A slide type is a React component with a Zod schema next to it:
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
// src/components/Quote.tsx
|
|
87
|
+
import { Slide, Box, Text } from '@sanity-labs/slides';
|
|
88
|
+
import { z } from 'zod';
|
|
89
|
+
|
|
90
|
+
export const QuoteSchema = z
|
|
91
|
+
.object({
|
|
92
|
+
quote: z.string().min(1).describe('The pull quote. Plain text.'),
|
|
93
|
+
attribution: z.string().optional().describe('Optional speaker.'),
|
|
94
|
+
})
|
|
95
|
+
.strict();
|
|
96
|
+
|
|
97
|
+
export const Quote = ({ quote, attribution }: z.infer<typeof QuoteSchema>) => (
|
|
98
|
+
<Slide>
|
|
99
|
+
<Box rect={{ x: 0, y: 0, w: 960, h: 540 }} fill={{ kind: 'solid', color: '#ff5500' }} />
|
|
100
|
+
<Box rect={{ x: 60, y: 200, w: 840, h: 160 }}>
|
|
101
|
+
<Text textStyle={{ fontFamily: 'display', fontSize: 48, foregroundColor: '#0b0b0b' }}>
|
|
102
|
+
“{quote}”
|
|
103
|
+
</Text>
|
|
104
|
+
</Box>
|
|
105
|
+
{attribution ? (
|
|
106
|
+
<Box rect={{ x: 60, y: 380, w: 840, h: 32 }}>
|
|
107
|
+
<Text textStyle={{ fontFamily: 'mono', fontSize: 14, foregroundColor: '#0b0b0b' }}>
|
|
108
|
+
— {attribution}
|
|
109
|
+
</Text>
|
|
110
|
+
</Box>
|
|
111
|
+
) : null}
|
|
112
|
+
</Slide>
|
|
113
|
+
);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Register it in `src/index.ts`:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
components: {
|
|
120
|
+
Quote: defineTemplateComponent({
|
|
121
|
+
component: Quote,
|
|
122
|
+
schema: QuoteSchema,
|
|
123
|
+
description: 'Pull-quote slide. Use sparingly between sections.',
|
|
124
|
+
}),
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`pnpm build` and Claude will see a new `slides_add_quote` tool on its next request.
|
|
129
|
+
|
|
130
|
+
The Zod schema field `.describe(...)` text becomes the LLM-facing documentation. The `description` on the template entry tells Claude when to pick the type.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## What Claude sees
|
|
135
|
+
|
|
136
|
+
After you wire the MCP server in, Claude has three tools, auto-derived from your template:
|
|
137
|
+
|
|
138
|
+
| Tool | Purpose |
|
|
139
|
+
| ------------------- | ------------------------------------------------------------------------- |
|
|
140
|
+
| `slides_list` | Lists every slide type with its description. |
|
|
141
|
+
| `slides_add_<type>` | Validates a single slide's props against the Zod schema. |
|
|
142
|
+
| `slides_create` | Takes `{ title, slides }`, writes the `.pptx`, returns the absolute path. |
|
|
143
|
+
|
|
144
|
+
The package also ships an LLM skill describing the calling convention. Print it with:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npx @sanity-labs/slides skill
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Paste it into a Claude project's knowledge sources to teach the model the workflow up front.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Subpath layout
|
|
155
|
+
|
|
156
|
+
One npm package, four import surfaces:
|
|
157
|
+
|
|
158
|
+
| Subpath | Contents |
|
|
159
|
+
| ------------------------------ | ----------------------------------------------------------------------- |
|
|
160
|
+
| `@sanity-labs/slides` | Renderer + `Template` type + `Slide`/`Box`/`Text` primitives + runtime. |
|
|
161
|
+
| `@sanity-labs/slides/mcp` | `createSlideServer`, `renderSlides` — programmatic MCP API. |
|
|
162
|
+
| `@sanity-labs/slides/dev` | Dev-viewer building blocks (`composeDeck`, `DeckViewer`). |
|
|
163
|
+
| `@sanity-labs/slides/scaffold` | `scaffoldTemplate({ target, name })` — programmatic scaffold API. |
|
|
164
|
+
| `@sanity-labs/slides/skill` | The bundled `SKILL.md` Markdown file. |
|
|
165
|
+
|
|
166
|
+
Vite, tailwind, react-dom, lucide-react and friends are listed as **optional peer dependencies**, only required when you import `/dev`. Users wiring the MCP into Claude pay for none of that install weight.
|
|
167
|
+
|
|
168
|
+
### Bins
|
|
169
|
+
|
|
170
|
+
| Bin | Purpose |
|
|
171
|
+
| ------------ | ---------------------------------------------------------------------------------------------- |
|
|
172
|
+
| `slidesctl` | The CLI the MCP server runs on. Subcommands: `serve`, `generate`, `list`, `scaffold`, `skill`. |
|
|
173
|
+
| `slides-dev` | The Vite-backed dev viewer used by template authors' `pnpm dev`. |
|
|
174
|
+
|
|
175
|
+
`npx @sanity-labs/slides …` resolves to the `slidesctl` bin (matches the package's short name).
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Reference template
|
|
180
|
+
|
|
181
|
+
The Sanity-branded reference template lives in its own repo at [`sanity-labs/slides-template`](https://github.com/sanity-labs/slides-template). It's the most thorough authoring example — eight slide types, brand chrome helpers, embedded raster assets, and SVG textures — and it dogfoods this package by consuming `@sanity-labs/slides` exactly the way an external user would.
|
|
182
|
+
|
|
183
|
+
To use it:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
git clone git@github.com:sanity-labs/slides-template.git
|
|
187
|
+
cd slides-template
|
|
188
|
+
pnpm install
|
|
189
|
+
pnpm build
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Then point Claude at it the same way as any other template:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
"args": ["-y", "@sanity-labs/slides", "serve", "--template", "/abs/path/to/slides-template/dist/index.js"]
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Repo layout
|
|
201
|
+
|
|
202
|
+
```text
|
|
203
|
+
src/
|
|
204
|
+
├── index.ts # root: renderer + Template + primitives
|
|
205
|
+
├── cli.ts # `slidesctl` bin
|
|
206
|
+
├── core/ # renderer + PPTX runtime
|
|
207
|
+
├── mcp/ # MCP server framework
|
|
208
|
+
├── dev/ # browser dev viewer (incl. slides-dev bin)
|
|
209
|
+
├── scaffold/ # scaffold logic + template-base/
|
|
210
|
+
└── __tests__/ # framework smoke tests + synthetic fixture template
|
|
211
|
+
|
|
212
|
+
scripts/copy-static-assets.mjs copies non-TS assets (CSS, template-base, shims) into dist/
|
|
213
|
+
docs/ architecture, testing strategy
|
|
214
|
+
.changeset/ changesets config + queued release notes
|
|
215
|
+
.github/workflows/ CI + release pipeline
|
|
216
|
+
SKILL.md LLM skill — served by `slidesctl skill`
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Contributing
|
|
222
|
+
|
|
223
|
+
See [`docs/architecture.md`](./docs/architecture.md) for the layer pyramid.
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
pnpm install
|
|
227
|
+
pnpm verify # typecheck + lint + format + build + test + knip + verify-bins
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Releases use [Changesets](https://github.com/changesets/changesets):
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
pnpm changeset # describe what changed (single package — just pick patch/minor/major)
|
|
234
|
+
git commit -am "feat: …"
|
|
235
|
+
# A "Version Packages" PR opens automatically on push to main.
|
|
236
|
+
# Merge that PR and the release workflow publishes @sanity-labs/slides to npm.
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
MIT
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-pptx-slides
|
|
3
|
+
description: How to drive a `react-pptx` template MCP server. Use this skill whenever the user asks for a slide deck or `.pptx` file and an MCP server exposing the `slides_list`, `slides_add_*`, and `slides_create` tools is available. The skill teaches the calling convention, the validation contract, and the recovery patterns for the three error codes the server returns.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: react-pptx
|
|
7
|
+
version: '1.0.0'
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# react-pptx slide-deck MCP
|
|
11
|
+
|
|
12
|
+
`react-pptx` template servers expose a brand's slide vocabulary as MCP tools. Every template has its own server binary (`<template>-slides`) that publishes the same three tools, derived from the template's component schemas at startup.
|
|
13
|
+
|
|
14
|
+
## When to apply
|
|
15
|
+
|
|
16
|
+
Use this skill when the user asks for a slide deck and any of the following are true:
|
|
17
|
+
|
|
18
|
+
- The conversation context contains a `react-pptx`-derived MCP server (tool names beginning with `slides_`).
|
|
19
|
+
- The user references "the template", "our brand template", or a specific template name they've installed.
|
|
20
|
+
- The user asks for a `.pptx` / PowerPoint file.
|
|
21
|
+
|
|
22
|
+
Do **not** invent slide content with `Box`/`Text` primitives directly. The template's components are the only legal vocabulary; anything else is off-brand and will be rejected.
|
|
23
|
+
|
|
24
|
+
## Tool surface
|
|
25
|
+
|
|
26
|
+
| Tool | Direction | Use it for |
|
|
27
|
+
| ------------------- | ----------------- | ----------------------------------------------------------------------------------------------- |
|
|
28
|
+
| `slides_list` | discovery | Learn what slide types this template supports and what each is for. |
|
|
29
|
+
| `slides_add_<type>` | per-slide preview | Validate a single slide's props against the schema. Returns a `SlideSpec`. Optional but useful. |
|
|
30
|
+
| `slides_create` | final write | Take an array of `SlideSpec`s, render the deck, write the `.pptx`, return the absolute path. |
|
|
31
|
+
|
|
32
|
+
## The standard flow
|
|
33
|
+
|
|
34
|
+
1. **Discover.** Call `slides_list` once. It returns `{ template, slides: [{ name, toolName, description, inputJsonSchema }] }`. Read every `description` and `inputJsonSchema` so you know which component to pick for each beat in the user's outline.
|
|
35
|
+
2. **Plan.** Map the user's request to a sequence of slide types. The first slide is usually the template's `Cover`; the last is usually a closing slide (`Closing`, `ThankYou`, or similar — read the descriptions). In between, choose from the available types based on what the user is communicating.
|
|
36
|
+
3. **Validate (optional).** If you're unsure about a slide's props, call `slides_add_<type>` with them. The server returns either a clean `SlideSpec` or a validation error with field-level paths. Use this when you've drafted a complex slide (grids, lists) and want a sanity check before paying the cost of a full render.
|
|
37
|
+
4. **Create.** Call `slides_create` with `{ title, slides: [...] }`. On success it returns `{ filePath, slideCount }`. Surface the absolute `filePath` to the user.
|
|
38
|
+
|
|
39
|
+
## Response shape from `slides_create`
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{ "filePath": "/Users/you/Desktop/Q4-Review.pptx", "slideCount": 6 }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
On error, the server returns a structured error with one of three codes:
|
|
46
|
+
|
|
47
|
+
| Code | Meaning | Recovery |
|
|
48
|
+
| ------------------------------------ | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `unknown_component` | You used a `component` name that isn't in the template. | Re-read `slides_list` (the server lists known types in the error message). Pick a real one. |
|
|
50
|
+
| `validation_error` | Zod rejected the props on a slide. The error includes `path` per issue. | Fix only the failing fields and resend the full `slides_create` call. Don't re-emit slides that weren't flagged. |
|
|
51
|
+
| `reconciler_error` / `runtime_error` | Something went wrong below the schema layer. | Report the message to the user verbatim. Suggest they file an issue against the template; this is rarely an LLM-side problem. |
|
|
52
|
+
|
|
53
|
+
## Conventions
|
|
54
|
+
|
|
55
|
+
- **Prefer one `slides_create` call, not many.** The tool atomically writes one file from the full slide list. Calling it per slide produces N files, not one deck.
|
|
56
|
+
- **Don't ask the user for fonts, colors, or sizes.** They're inexpressible through the tool surface for a reason. The template owns them.
|
|
57
|
+
- **Read schema descriptions before guessing.** Every Zod field carries a `.describe(...)` that tells you what content goes there. The default in `inputJsonSchema.properties.<field>.description` is authoritative.
|
|
58
|
+
- **Default to the template's `preview` order when unsure of structure.** A common shape is: `Cover` → 1–3 body slides (`OneColumn`, `TitleAndBody`, `TitleAndGrid`) → optional `SectionDivider`s → 1–2 more body slides → `Closing`. Templates that diverge from this carry it in their descriptions.
|
|
59
|
+
- **Respect array minimums.** `cells` in a grid, `items` in an agenda, `bullets` in a detail row — the schema specifies the minimum count. Padding is not allowed; pick a different component if the user has fewer items than the schema demands.
|
|
60
|
+
- **Surface the file path.** After `slides_create` succeeds, tell the user the path verbatim — don't paraphrase or shorten.
|
|
61
|
+
|
|
62
|
+
## Worked example
|
|
63
|
+
|
|
64
|
+
User: _"Make me a five-slide Q4 review. Cover, two metric grids, one quote from the CEO, and a thank-you."_
|
|
65
|
+
|
|
66
|
+
1. Call `slides_list`. Suppose the template returns `Cover`, `TitleAndGrid`, `Quote`, `Closing`.
|
|
67
|
+
2. Compose:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"title": "Q4 Review",
|
|
72
|
+
"slides": [
|
|
73
|
+
{ "component": "Cover", "props": { "title": "Q4 Review", "subtitle": "Year in numbers" } },
|
|
74
|
+
{
|
|
75
|
+
"component": "TitleAndGrid",
|
|
76
|
+
"props": {
|
|
77
|
+
"title": "Revenue lines",
|
|
78
|
+
"cols": 3,
|
|
79
|
+
"rows": 1,
|
|
80
|
+
"cells": [
|
|
81
|
+
{ "eyebrow": "ARR", "body": "$54M, +18% YoY" },
|
|
82
|
+
{ "eyebrow": "NDR", "body": "121%" },
|
|
83
|
+
{ "eyebrow": "Logos", "body": "+47 enterprise" }
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"component": "TitleAndGrid",
|
|
89
|
+
"props": {
|
|
90
|
+
"title": "Org",
|
|
91
|
+
"cols": 2,
|
|
92
|
+
"rows": 1,
|
|
93
|
+
"cells": [
|
|
94
|
+
{ "eyebrow": "Headcount", "body": "180" },
|
|
95
|
+
{ "eyebrow": "Hiring", "body": "12 open roles" }
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"component": "Quote",
|
|
101
|
+
"props": {
|
|
102
|
+
"quote": "Structure powers intelligence.",
|
|
103
|
+
"attribution": "Lars Bakker, CEO"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
{ "component": "Closing", "props": {} }
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
3. Call `slides_create` with that payload. Report the returned `filePath` to the user.
|
|
112
|
+
|
|
113
|
+
## Anti-patterns
|
|
114
|
+
|
|
115
|
+
- ❌ Asking the user "what color should the title be?" — the template decides.
|
|
116
|
+
- ❌ Calling `slides_create` multiple times to "build the deck up" — it isn't incremental, one call writes the final file.
|
|
117
|
+
- ❌ Inventing component names — only what `slides_list` returned will be accepted.
|
|
118
|
+
- ❌ Stuffing prose into a `Cover.title` — keep titles short, put paragraphs in `OneColumn` or `TitleAndBody`.
|
|
119
|
+
- ❌ Hiding the file path from the user once you have it. Always print the absolute path verbatim.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `slidesctl` — the CLI bin shipped with `@sanity-labs/slides`.
|
|
4
|
+
*
|
|
5
|
+
* Subcommands:
|
|
6
|
+
*
|
|
7
|
+
* serve --template <path-or-specifier> [--output <dir>] [--name <id>]
|
|
8
|
+
* Start an MCP server over stdio. Exposes `slides_list`,
|
|
9
|
+
* `slides_add_<type>`, and `slides_create` derived from the
|
|
10
|
+
* loaded template.
|
|
11
|
+
*
|
|
12
|
+
* generate --template <path-or-specifier> [--output <dir>]
|
|
13
|
+
* Read `{ title, slides: [...] }` JSON from stdin and write
|
|
14
|
+
* the resulting `.pptx`. Prints the absolute path on stdout.
|
|
15
|
+
*
|
|
16
|
+
* list --template <path-or-specifier>
|
|
17
|
+
* Print every slide type the template exposes, with
|
|
18
|
+
* descriptions. Useful for humans inspecting a template
|
|
19
|
+
* without an MCP client.
|
|
20
|
+
*
|
|
21
|
+
* scaffold <dir> [--name <slug>]
|
|
22
|
+
* Scaffold a new template into <dir>. Replaces the old
|
|
23
|
+
* `npm create react-pptx-template` flow.
|
|
24
|
+
*
|
|
25
|
+
* skill [--path]
|
|
26
|
+
* Print the bundled `SKILL.md` to stdout (or its absolute
|
|
27
|
+
* path with `--path`). Paste into a Claude project to teach
|
|
28
|
+
* the model how to drive any react-pptx MCP server.
|
|
29
|
+
*
|
|
30
|
+
* The `--template` flag accepts:
|
|
31
|
+
* - A bare package specifier resolved from the current directory
|
|
32
|
+
* (e.g. `@acme/slide-template`).
|
|
33
|
+
* - A path to a built JS file (`./dist/index.js`).
|
|
34
|
+
* - A path to a directory containing `package.json` (the bin reads
|
|
35
|
+
* `main` / `exports['.']`).
|
|
36
|
+
*/
|
|
37
|
+
export declare const main: (argv: readonly string[]) => Promise<void>;
|
|
38
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AA0UH,eAAO,MAAM,IAAI,GAAU,MAAM,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,IAAI,CA6ChE,CAAC"}
|