@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.
Files changed (224) hide show
  1. package/README.md +241 -0
  2. package/SKILL.md +119 -0
  3. package/dist/cli.d.ts +38 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +386 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/core/components.d.ts +179 -0
  8. package/dist/core/components.d.ts.map +1 -0
  9. package/dist/core/components.js +40 -0
  10. package/dist/core/components.js.map +1 -0
  11. package/dist/core/fake-runtime.d.ts +138 -0
  12. package/dist/core/fake-runtime.d.ts.map +1 -0
  13. package/dist/core/fake-runtime.js +210 -0
  14. package/dist/core/fake-runtime.js.map +1 -0
  15. package/dist/core/font-resolver.d.ts +28 -0
  16. package/dist/core/font-resolver.d.ts.map +1 -0
  17. package/dist/core/font-resolver.js +30 -0
  18. package/dist/core/font-resolver.js.map +1 -0
  19. package/dist/core/geometry.d.ts +71 -0
  20. package/dist/core/geometry.d.ts.map +1 -0
  21. package/dist/core/geometry.js +44 -0
  22. package/dist/core/geometry.js.map +1 -0
  23. package/dist/core/index.d.ts +19 -0
  24. package/dist/core/index.d.ts.map +1 -0
  25. package/dist/core/index.js +20 -0
  26. package/dist/core/index.js.map +1 -0
  27. package/dist/core/manifest.d.ts +123 -0
  28. package/dist/core/manifest.d.ts.map +1 -0
  29. package/dist/core/manifest.js +43 -0
  30. package/dist/core/manifest.js.map +1 -0
  31. package/dist/core/op-translator-pptx.d.ts +150 -0
  32. package/dist/core/op-translator-pptx.d.ts.map +1 -0
  33. package/dist/core/op-translator-pptx.js +245 -0
  34. package/dist/core/op-translator-pptx.js.map +1 -0
  35. package/dist/core/pptx-runtime.d.ts +103 -0
  36. package/dist/core/pptx-runtime.d.ts.map +1 -0
  37. package/dist/core/pptx-runtime.js +405 -0
  38. package/dist/core/pptx-runtime.js.map +1 -0
  39. package/dist/core/reconciler.d.ts +113 -0
  40. package/dist/core/reconciler.d.ts.map +1 -0
  41. package/dist/core/reconciler.js +453 -0
  42. package/dist/core/reconciler.js.map +1 -0
  43. package/dist/core/runtime.d.ts +161 -0
  44. package/dist/core/runtime.d.ts.map +1 -0
  45. package/dist/core/runtime.js +11 -0
  46. package/dist/core/runtime.js.map +1 -0
  47. package/dist/core/template.d.ts +32 -0
  48. package/dist/core/template.d.ts.map +1 -0
  49. package/dist/core/template.js +3 -0
  50. package/dist/core/template.js.map +1 -0
  51. package/dist/dev/auto-examples.d.ts +6 -0
  52. package/dist/dev/auto-examples.d.ts.map +1 -0
  53. package/dist/dev/auto-examples.js +79 -0
  54. package/dist/dev/auto-examples.js.map +1 -0
  55. package/dist/dev/bin/slides-dev.d.ts +3 -0
  56. package/dist/dev/bin/slides-dev.d.ts.map +1 -0
  57. package/dist/dev/bin/slides-dev.js +87 -0
  58. package/dist/dev/bin/slides-dev.js.map +1 -0
  59. package/dist/dev/bin/slides-dev.mjs +24 -0
  60. package/dist/dev/compose-deck.d.ts +18 -0
  61. package/dist/dev/compose-deck.d.ts.map +1 -0
  62. package/dist/dev/compose-deck.js +19 -0
  63. package/dist/dev/compose-deck.js.map +1 -0
  64. package/dist/dev/deck-viewer.d.ts +19 -0
  65. package/dist/dev/deck-viewer.d.ts.map +1 -0
  66. package/dist/dev/deck-viewer.js +237 -0
  67. package/dist/dev/deck-viewer.js.map +1 -0
  68. package/dist/dev/dev-server/client/entry.d.ts +2 -0
  69. package/dist/dev/dev-server/client/entry.d.ts.map +1 -0
  70. package/dist/dev/dev-server/client/entry.js +12 -0
  71. package/dist/dev/dev-server/client/entry.js.map +1 -0
  72. package/dist/dev/dev-server/output.d.ts +8 -0
  73. package/dist/dev/dev-server/output.d.ts.map +1 -0
  74. package/dist/dev/dev-server/output.js +32 -0
  75. package/dist/dev/dev-server/output.js.map +1 -0
  76. package/dist/dev/dev-server/server-only-stub.d.ts +7 -0
  77. package/dist/dev/dev-server/server-only-stub.d.ts.map +1 -0
  78. package/dist/dev/dev-server/server-only-stub.js +12 -0
  79. package/dist/dev/dev-server/server-only-stub.js.map +1 -0
  80. package/dist/dev/dev-server/start.d.ts +14 -0
  81. package/dist/dev/dev-server/start.d.ts.map +1 -0
  82. package/dist/dev/dev-server/start.js +135 -0
  83. package/dist/dev/dev-server/start.js.map +1 -0
  84. package/dist/dev/index.d.ts +5 -0
  85. package/dist/dev/index.d.ts.map +1 -0
  86. package/dist/dev/index.js +5 -0
  87. package/dist/dev/index.js.map +1 -0
  88. package/dist/dev/lib/cn.d.ts +3 -0
  89. package/dist/dev/lib/cn.d.ts.map +1 -0
  90. package/dist/dev/lib/cn.js +3 -0
  91. package/dist/dev/lib/cn.js.map +1 -0
  92. package/dist/dev/slide-canvas.d.ts +12 -0
  93. package/dist/dev/slide-canvas.d.ts.map +1 -0
  94. package/dist/dev/slide-canvas.js +123 -0
  95. package/dist/dev/slide-canvas.js.map +1 -0
  96. package/dist/dev/styles.css +37 -0
  97. package/dist/dev/ui/icon-button.d.ts +12 -0
  98. package/dist/dev/ui/icon-button.d.ts.map +1 -0
  99. package/dist/dev/ui/icon-button.js +6 -0
  100. package/dist/dev/ui/icon-button.js.map +1 -0
  101. package/dist/dev/ui/kbd.d.ts +6 -0
  102. package/dist/dev/ui/kbd.d.ts.map +1 -0
  103. package/dist/dev/ui/kbd.js +4 -0
  104. package/dist/dev/ui/kbd.js.map +1 -0
  105. package/dist/dev/ui/text-button.d.ts +10 -0
  106. package/dist/dev/ui/text-button.d.ts.map +1 -0
  107. package/dist/dev/ui/text-button.js +6 -0
  108. package/dist/dev/ui/text-button.js.map +1 -0
  109. package/dist/dev/url-state.d.ts +7 -0
  110. package/dist/dev/url-state.d.ts.map +1 -0
  111. package/dist/dev/url-state.js +13 -0
  112. package/dist/dev/url-state.js.map +1 -0
  113. package/dist/dev/use-keyboard-nav.d.ts +17 -0
  114. package/dist/dev/use-keyboard-nav.d.ts.map +1 -0
  115. package/dist/dev/use-keyboard-nav.js +53 -0
  116. package/dist/dev/use-keyboard-nav.js.map +1 -0
  117. package/dist/index.d.ts +17 -0
  118. package/dist/index.d.ts.map +1 -0
  119. package/dist/index.js +17 -0
  120. package/dist/index.js.map +1 -0
  121. package/dist/mcp/errors.d.ts +57 -0
  122. package/dist/mcp/errors.d.ts.map +1 -0
  123. package/dist/mcp/errors.js +44 -0
  124. package/dist/mcp/errors.js.map +1 -0
  125. package/dist/mcp/index.d.ts +29 -0
  126. package/dist/mcp/index.d.ts.map +1 -0
  127. package/dist/mcp/index.js +29 -0
  128. package/dist/mcp/index.js.map +1 -0
  129. package/dist/mcp/naming.d.ts +37 -0
  130. package/dist/mcp/naming.d.ts.map +1 -0
  131. package/dist/mcp/naming.js +43 -0
  132. package/dist/mcp/naming.js.map +1 -0
  133. package/dist/mcp/render.d.ts +45 -0
  134. package/dist/mcp/render.d.ts.map +1 -0
  135. package/dist/mcp/render.js +77 -0
  136. package/dist/mcp/render.js.map +1 -0
  137. package/dist/mcp/schema.d.ts +54 -0
  138. package/dist/mcp/schema.d.ts.map +1 -0
  139. package/dist/mcp/schema.js +55 -0
  140. package/dist/mcp/schema.js.map +1 -0
  141. package/dist/mcp/server.d.ts +63 -0
  142. package/dist/mcp/server.d.ts.map +1 -0
  143. package/dist/mcp/server.js +196 -0
  144. package/dist/mcp/server.js.map +1 -0
  145. package/dist/scaffold/index.d.ts +39 -0
  146. package/dist/scaffold/index.d.ts.map +1 -0
  147. package/dist/scaffold/index.js +84 -0
  148. package/dist/scaffold/index.js.map +1 -0
  149. package/dist/scaffold/template-base/README.md +134 -0
  150. package/dist/scaffold/template-base/_gitignore +4 -0
  151. package/dist/scaffold/template-base/package.json +35 -0
  152. package/dist/scaffold/template-base/src/components/Cover.tsx +30 -0
  153. package/dist/scaffold/template-base/src/index.ts +27 -0
  154. package/dist/scaffold/template-base/src/preview.tsx +9 -0
  155. package/dist/scaffold/template-base/tsconfig.build.json +10 -0
  156. package/dist/scaffold/template-base/tsconfig.json +18 -0
  157. package/package.json +164 -0
  158. package/src/__tests__/fixtures/test-template/index.tsx +77 -0
  159. package/src/__tests__/pptx-mcp.test.ts +85 -0
  160. package/src/__tests__/pptx-smoke.test.ts +45 -0
  161. package/src/__tests__/preview.test.ts +28 -0
  162. package/src/cli.ts +426 -0
  163. package/src/core/__snapshots__/reconciler.test.ts.snap +320 -0
  164. package/src/core/components.test.ts +57 -0
  165. package/src/core/components.ts +196 -0
  166. package/src/core/fake-runtime.test.ts +174 -0
  167. package/src/core/fake-runtime.ts +302 -0
  168. package/src/core/font-resolver.ts +46 -0
  169. package/src/core/geometry.test.ts +58 -0
  170. package/src/core/geometry.ts +91 -0
  171. package/src/core/index.ts +69 -0
  172. package/src/core/manifest.test.ts +33 -0
  173. package/src/core/manifest.ts +150 -0
  174. package/src/core/op-translator-pptx.test.ts +204 -0
  175. package/src/core/op-translator-pptx.ts +365 -0
  176. package/src/core/pptx-runtime.test.ts +137 -0
  177. package/src/core/pptx-runtime.ts +504 -0
  178. package/src/core/reconciler.test.ts +644 -0
  179. package/src/core/reconciler.ts +603 -0
  180. package/src/core/runtime.ts +150 -0
  181. package/src/core/template.test.ts +136 -0
  182. package/src/core/template.ts +37 -0
  183. package/src/dev/auto-examples.ts +89 -0
  184. package/src/dev/bin/slides-dev.mjs +24 -0
  185. package/src/dev/bin/slides-dev.ts +101 -0
  186. package/src/dev/compose-deck.test.ts +68 -0
  187. package/src/dev/compose-deck.ts +40 -0
  188. package/src/dev/deck-viewer.tsx +677 -0
  189. package/src/dev/dev-server/client/entry.tsx +15 -0
  190. package/src/dev/dev-server/client/index.html +24 -0
  191. package/src/dev/dev-server/output.ts +37 -0
  192. package/src/dev/dev-server/server-only-stub.ts +12 -0
  193. package/src/dev/dev-server/start.ts +155 -0
  194. package/src/dev/index.ts +4 -0
  195. package/src/dev/lib/cn.ts +3 -0
  196. package/src/dev/slide-canvas.test.tsx +66 -0
  197. package/src/dev/slide-canvas.tsx +170 -0
  198. package/src/dev/styles.css +37 -0
  199. package/src/dev/ui/icon-button.tsx +31 -0
  200. package/src/dev/ui/kbd.tsx +20 -0
  201. package/src/dev/ui/text-button.tsx +31 -0
  202. package/src/dev/url-state.test.ts +22 -0
  203. package/src/dev/url-state.ts +17 -0
  204. package/src/dev/use-keyboard-nav.ts +64 -0
  205. package/src/index.ts +17 -0
  206. package/src/mcp/errors.test.ts +51 -0
  207. package/src/mcp/errors.ts +76 -0
  208. package/src/mcp/index.ts +45 -0
  209. package/src/mcp/naming.test.ts +39 -0
  210. package/src/mcp/naming.ts +49 -0
  211. package/src/mcp/render.ts +110 -0
  212. package/src/mcp/schema.test.ts +86 -0
  213. package/src/mcp/schema.ts +93 -0
  214. package/src/mcp/server.test.ts +309 -0
  215. package/src/mcp/server.ts +276 -0
  216. package/src/scaffold/index.ts +102 -0
  217. package/src/scaffold/template-base/README.md +134 -0
  218. package/src/scaffold/template-base/_gitignore +4 -0
  219. package/src/scaffold/template-base/package.json +35 -0
  220. package/src/scaffold/template-base/src/components/Cover.tsx +30 -0
  221. package/src/scaffold/template-base/src/index.ts +27 -0
  222. package/src/scaffold/template-base/src/preview.tsx +9 -0
  223. package/src/scaffold/template-base/tsconfig.build.json +10 -0
  224. 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
+ [![ci](https://github.com/sanity-labs/slides/actions/workflows/ci.yml/badge.svg)](https://github.com/sanity-labs/slides/actions/workflows/ci.yml)
6
+ [![npm](https://img.shields.io/npm/v/@sanity-labs/slides)](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"}