@mineo71/showtime 0.2.0 → 0.3.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/CHANGELOG.md +23 -0
- package/README.md +82 -10
- package/docs/script.schema.json +217 -0
- package/package.json +5 -2
- package/skill/SKILL.md +34 -10
- package/src/cli/format.ts +10 -0
- package/src/cli/main.ts +85 -32
- package/src/lint.ts +101 -0
- package/src/logo.ts +20 -0
- package/src/paths.ts +6 -0
- package/src/pipeline.ts +12 -3
- package/src/render/remotion/Intro.tsx +2 -0
- package/src/render/remotion/Logo.tsx +6 -0
- package/src/render/remotion/Outro.tsx +38 -0
- package/src/render/remotion/Overlays.tsx +4 -3
- package/src/render/remotion/ShowtimeVideo.tsx +10 -3
- package/src/runner/browser.ts +47 -6
- package/src/runner/check.ts +10 -1
- package/src/runner/hooks.ts +111 -0
- package/src/runner/record.ts +52 -34
- package/src/runner/steps.ts +3 -1
- package/src/script/config.ts +58 -0
- package/src/script/hash.ts +7 -1
- package/src/script/load.ts +12 -2
- package/src/script/schema.ts +62 -1
- package/src/setup.ts +13 -4
- package/src/sheet.ts +47 -0
- package/src/timeline/build.ts +46 -6
- package/src/timeline/frames.ts +12 -2
- package/src/timeline/layout.ts +21 -0
- package/src/timeline/types.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 — 2026-10-13
|
|
4
|
+
|
|
5
|
+
- `showtime frames` also renders a contact sheet (`frames/sheet.png`) of every step.
|
|
6
|
+
- `showtime lint`: long captions, pauses that should be waits, zoom/spotlight that cannot apply,
|
|
7
|
+
and, with a take, captions too brief to read and overall length.
|
|
8
|
+
- `--json` on every command.
|
|
9
|
+
- `hooks.before` / `hooks.after`: shell commands or HTTP calls around every `check` and `record`,
|
|
10
|
+
so state can be reset between rehearsal and take.
|
|
11
|
+
- `app.mock` (canned responses for matching requests), `app.clock` (the page clock starts at a fixed date),
|
|
12
|
+
`app.dialogs` (accept or dismiss native dialogs; accepted by default).
|
|
13
|
+
- Safe re-takes: a failed `record` leaves the previous take untouched (`take.tmp` keeps the failure).
|
|
14
|
+
- Settling ignores event streams; relative `goto` URLs resolve against `app.url`.
|
|
15
|
+
- Captions never overlap, and move to the top when the action is at the bottom of the screen.
|
|
16
|
+
- `meta.outro` closing card and `meta.logo` on the intro and outro cards.
|
|
17
|
+
- `showtime.config.json` brand file with default `style` and `meta` for every script below it.
|
|
18
|
+
- Takes recorded with 0.2 stay valid.
|
|
19
|
+
|
|
20
|
+
## 0.2.1 — 2026-10-13
|
|
21
|
+
|
|
22
|
+
- Releases are automated: `npm version patch|minor|major` dates the changelog, tags and pushes;
|
|
23
|
+
the `release` workflow publishes to npm through trusted publishing (with provenance) and
|
|
24
|
+
creates the GitHub Release.
|
|
25
|
+
|
|
3
26
|
## 0.2.0 — 2026-10-13
|
|
4
27
|
|
|
5
28
|
- Published to npm as `@mineo71/showtime`.
|
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ No timeline editor. No re-take when a caption changes.
|
|
|
26
26
|
<div align="center">
|
|
27
27
|
<a href="docs/media/demo.mp4"><img src="docs/media/hero.webp" width="860" alt="A demo of a fictional invoicing app, filmed and edited by showTime: the camera zooms on a chart, a cursor fills in a new invoice, a spotlight lands on the total, and the sent invoice appears in the table" /></a>
|
|
28
28
|
<br />
|
|
29
|
-
<sub>Made by <a href="examples/fennel/demo.json">
|
|
29
|
+
<sub>Made by <a href="examples/fennel/demo.json">a 39-line script</a>, a <a href="examples/fennel/showtime.config.json">brand file</a> and one command, with no manual editing. <a href="docs/media/demo.mp4">▶ Full-quality MP4</a></sub>
|
|
30
30
|
</div>
|
|
31
31
|
|
|
32
32
|
---
|
|
@@ -121,7 +121,19 @@ again.
|
|
|
121
121
|
<td valign="top">
|
|
122
122
|
<img src="docs/media/intro.jpg" alt="Intro card with title and subtitle" /><br />
|
|
123
123
|
<b>🎬 Intro card and window chrome</b><br />
|
|
124
|
-
<sub>Title card, a browser frame with your URL, a gradient backdrop, rounded corners and a shadow. Each of them is a <code>style</code> field.</sub>
|
|
124
|
+
<sub>Title card with your logo, a browser frame with your URL, a gradient backdrop, rounded corners and a shadow. Each of them is a <code>style</code> or <code>meta</code> field.</sub>
|
|
125
|
+
</td>
|
|
126
|
+
</tr>
|
|
127
|
+
<tr>
|
|
128
|
+
<td valign="top">
|
|
129
|
+
<img src="docs/media/outro.jpg" alt="Outro card with logo, title and URL" /><br />
|
|
130
|
+
<b>📣 Ends on your call to action</b><br />
|
|
131
|
+
<sub>An outro card with your logo, a headline and the URL to visit. Put it in a brand file once and every demo ends the same way.</sub>
|
|
132
|
+
</td>
|
|
133
|
+
<td valign="top">
|
|
134
|
+
<img src="docs/media/sheet.jpg" alt="Contact sheet of every step" /><br />
|
|
135
|
+
<b>🧪 Reviews its own work</b><br />
|
|
136
|
+
<sub><code>showtime frames</code> renders a contact sheet of every step, and <code>showtime lint</code> flags captions that are too long or too brief to read. The agent fixes them before you look.</sub>
|
|
125
137
|
</td>
|
|
126
138
|
</tr>
|
|
127
139
|
</table>
|
|
@@ -132,8 +144,16 @@ And under the hood:
|
|
|
132
144
|
When a step fails it prints an ARIA snapshot of the page, so Claude can fix its own selectors.
|
|
133
145
|
- 🔁 **Stale-take detection.** The take stores a hash of the actions. Edit a caption and `render`
|
|
134
146
|
reuses the take. Edit a click and `render` tells you to `record` again.
|
|
135
|
-
-
|
|
136
|
-
|
|
147
|
+
- 🧹 **Clean state for every take.** `hooks` run a reset command or call an endpoint before and
|
|
148
|
+
after each rehearsal and take, so a flow that creates data works twice in a row.
|
|
149
|
+
- 🎭 **Deterministic pages.** `app.mock` answers flaky or slow APIs with canned JSON, `app.clock`
|
|
150
|
+
pins the date, and `app.dialogs` answers `confirm()` the way the demo needs.
|
|
151
|
+
- 🎨 **One brand file.** `showtime.config.json` holds your colours, logo and outro; every script
|
|
152
|
+
below it inherits them.
|
|
153
|
+
- 🛟 **Safe re-takes.** A take that fails halfway leaves the last good one untouched.
|
|
154
|
+
- 🤖 **JSON everywhere.** Every command takes `--json`, so agents read results instead of parsing
|
|
155
|
+
prose. Captions also move to the top on their own when the action happens at the bottom of the
|
|
156
|
+
screen.
|
|
137
157
|
- 📐 **Typed scripts.** Point `"$schema"` at [`docs/script.schema.json`](docs/script.schema.json)
|
|
138
158
|
for autocomplete and validation in your editor.
|
|
139
159
|
- 🖥️ **Crisp at 1080p.** The app is captured at 2× device scale, so zoomed shots stay sharp.
|
|
@@ -169,7 +189,8 @@ The agent takes it from there:
|
|
|
169
189
|
3. **Rehearses** with `showtime check` until every step passes, fixing selectors from the ARIA
|
|
170
190
|
snapshot.
|
|
171
191
|
4. **Records and renders** with `showtime record` and `showtime render`.
|
|
172
|
-
5. **Reviews** the
|
|
192
|
+
5. **Reviews** the contact sheet from `showtime frames` and the warnings from `showtime lint`,
|
|
193
|
+
tightens captions and pacing, and re-renders.
|
|
173
194
|
|
|
174
195
|
The skill ([`skill/SKILL.md`](skill/SKILL.md)) is a plain-Markdown
|
|
175
196
|
[Agent Skill](https://agentskills.io). Any agent that can run shell commands can follow it, and so
|
|
@@ -224,8 +245,9 @@ into and waited on, and everything cinematic is computed afterwards.
|
|
|
224
245
|
}
|
|
225
246
|
```
|
|
226
247
|
|
|
227
|
-
`meta.title` adds an intro card
|
|
228
|
-
|
|
248
|
+
`meta.title` adds an intro card, `meta.outro` a closing card and `meta.logo` puts your logo on
|
|
249
|
+
both. `setup` steps run before the camera starts (cookie banners, onboarding). Scenes group steps
|
|
250
|
+
for readability and for naming the stills.
|
|
229
251
|
|
|
230
252
|
<details>
|
|
231
253
|
<summary><b>Actions</b> (<code>do</code>)</summary>
|
|
@@ -291,6 +313,52 @@ Targets are Playwright selectors: `role=button[name='Save']`, `text=Save`, `#id`
|
|
|
291
313
|
| `colorScheme` | none | `light` or `dark`; also picks the browser-bar theme |
|
|
292
314
|
| `locale`, `timezoneId` | none | passed to the browser context |
|
|
293
315
|
| `mask` | `[]` | CSS selectors to blur (plain CSS, not `text=`) |
|
|
316
|
+
| `mock` | `[]` | `[{ url, method?, status?, json? \| body?, headers?, delayMs? }]`: answer matching requests (`url` is a glob like `**/api/stats*`; the first match wins) |
|
|
317
|
+
| `clock` | none | start the page clock at this ISO date-time; it then runs normally |
|
|
318
|
+
| `dialogs` | `accept` | answer `alert`/`confirm`/`prompt` with `accept` or `dismiss` |
|
|
319
|
+
|
|
320
|
+
</details>
|
|
321
|
+
|
|
322
|
+
<details>
|
|
323
|
+
<summary><b>Cards</b> (<code>meta</code>)</summary>
|
|
324
|
+
|
|
325
|
+
| field | |
|
|
326
|
+
|------------|-|
|
|
327
|
+
| `title`, `subtitle` | intro card |
|
|
328
|
+
| `logo` | SVG, PNG, JPEG or WebP (≤ 2 MB), relative to the file that names it; shown on the intro and outro |
|
|
329
|
+
| `outro` | `{ title, subtitle?, url? }`: closing card after the video |
|
|
330
|
+
|
|
331
|
+
</details>
|
|
332
|
+
|
|
333
|
+
<details>
|
|
334
|
+
<summary><b>Hooks</b> (<code>hooks</code>)</summary>
|
|
335
|
+
|
|
336
|
+
```json
|
|
337
|
+
"hooks": {
|
|
338
|
+
"before": [{ "run": "npm run db:reset" }],
|
|
339
|
+
"after": [{ "request": { "url": "http://localhost:3000/api/test/reset", "method": "POST" } }]
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
`before` hooks run at the start of every `check` and `record` (so `make` resets between the
|
|
344
|
+
rehearsal and the take); `after` hooks run when they end, even after a failure, and a failing
|
|
345
|
+
`after` hook is a warning that never costs you the take. `run` is a shell
|
|
346
|
+
command in the script's folder (120 s timeout); `request` is an HTTP call that must answer 2xx.
|
|
347
|
+
|
|
348
|
+
</details>
|
|
349
|
+
|
|
350
|
+
<details>
|
|
351
|
+
<summary><b>Brand file</b> (<code>showtime.config.json</code>)</summary>
|
|
352
|
+
|
|
353
|
+
Put it next to your scripts or in any folder above them; the nearest one applies. Its `style` and
|
|
354
|
+
`meta` are defaults, and a script's own fields win key by key.
|
|
355
|
+
|
|
356
|
+
```json
|
|
357
|
+
{
|
|
358
|
+
"style": { "background": "linear-gradient(135deg, #0f172a, #312e81)", "accent": "#6366f1", "address": "app.acme.com" },
|
|
359
|
+
"meta": { "logo": "brand/logo.svg", "outro": { "title": "Try Acme free", "url": "acme.com" } }
|
|
360
|
+
}
|
|
361
|
+
```
|
|
294
362
|
|
|
295
363
|
</details>
|
|
296
364
|
|
|
@@ -303,10 +371,12 @@ Targets are Playwright selectors: `role=button[name='Save']`, `text=Save`, `#id`
|
|
|
303
371
|
| `showtime check <script> [--json] [--headed]` | rehearse; a screenshot per step; the first failure with an ARIA snapshot |
|
|
304
372
|
| `showtime record <script> [--headed] [--keep-frames]` | record a take into `.showtime/<name>/take` |
|
|
305
373
|
| `showtime render <script> [-o out.mp4]` | render from the last take, without re-recording |
|
|
306
|
-
| `showtime frames <script>` | QA stills
|
|
374
|
+
| `showtime frames <script>` | QA stills (intro, one per step, outro) and a contact sheet of them |
|
|
375
|
+
| `showtime lint <script>` | warnings about captions, pauses and length; timing checks need a take |
|
|
307
376
|
| `showtime schema` | print the script's JSON Schema |
|
|
308
377
|
|
|
309
|
-
|
|
378
|
+
Every command takes `--json` and then prints exactly one JSON document. The video lands next to
|
|
379
|
+
the script (`demo/pitch.json` → `demo/pitch.mp4`). Working files live in
|
|
310
380
|
`.showtime/<name>/`, which is worth adding to your `.gitignore`.
|
|
311
381
|
|
|
312
382
|
## Tips for a great demo
|
|
@@ -317,7 +387,9 @@ The video lands next to the script (`demo/pitch.json` → `demo/pitch.mp4`). Wor
|
|
|
317
387
|
`spotlight` on the one number that sells it.
|
|
318
388
|
- **Never `pause` for loading.** Use `wait` with a selector and auto-speed compresses the spinner.
|
|
319
389
|
- **Start clean.** Use `setup` for cookie banners, `app.storageState` to skip the login, `app.mask`
|
|
320
|
-
for anything personal, and
|
|
390
|
+
for anything personal, and `hooks` to reset the data between `check` and `record`.
|
|
391
|
+
- **Pin the world.** Mock slow or random APIs with `app.mock` and pin the date with `app.clock`,
|
|
392
|
+
so every take looks the same.
|
|
321
393
|
|
|
322
394
|
## FAQ
|
|
323
395
|
|
package/docs/script.schema.json
CHANGED
|
@@ -18,6 +18,31 @@
|
|
|
18
18
|
},
|
|
19
19
|
"subtitle": {
|
|
20
20
|
"type": "string"
|
|
21
|
+
},
|
|
22
|
+
"logo": {
|
|
23
|
+
"description": "SVG/PNG/JPEG/WebP shown on the intro and outro cards",
|
|
24
|
+
"type": "string",
|
|
25
|
+
"minLength": 1
|
|
26
|
+
},
|
|
27
|
+
"outro": {
|
|
28
|
+
"description": "Closing card after the video",
|
|
29
|
+
"type": "object",
|
|
30
|
+
"properties": {
|
|
31
|
+
"title": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"minLength": 1
|
|
34
|
+
},
|
|
35
|
+
"subtitle": {
|
|
36
|
+
"type": "string"
|
|
37
|
+
},
|
|
38
|
+
"url": {
|
|
39
|
+
"type": "string"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"required": [
|
|
43
|
+
"title"
|
|
44
|
+
],
|
|
45
|
+
"additionalProperties": false
|
|
21
46
|
}
|
|
22
47
|
},
|
|
23
48
|
"additionalProperties": false
|
|
@@ -83,6 +108,71 @@
|
|
|
83
108
|
"type": "string",
|
|
84
109
|
"minLength": 1
|
|
85
110
|
}
|
|
111
|
+
},
|
|
112
|
+
"mock": {
|
|
113
|
+
"default": [],
|
|
114
|
+
"description": "Answer matching network requests with canned responses; the first match wins",
|
|
115
|
+
"type": "array",
|
|
116
|
+
"items": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"properties": {
|
|
119
|
+
"url": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"minLength": 1,
|
|
122
|
+
"description": "Playwright URL glob of the requests to answer, e.g. **/api/reports*"
|
|
123
|
+
},
|
|
124
|
+
"method": {
|
|
125
|
+
"description": "Only answer this HTTP method; others go to the network",
|
|
126
|
+
"type": "string",
|
|
127
|
+
"minLength": 1
|
|
128
|
+
},
|
|
129
|
+
"status": {
|
|
130
|
+
"default": 200,
|
|
131
|
+
"type": "integer",
|
|
132
|
+
"minimum": 100,
|
|
133
|
+
"maximum": 599
|
|
134
|
+
},
|
|
135
|
+
"json": {
|
|
136
|
+
"description": "Response body, sent as application/json"
|
|
137
|
+
},
|
|
138
|
+
"body": {
|
|
139
|
+
"description": "Raw response body",
|
|
140
|
+
"type": "string"
|
|
141
|
+
},
|
|
142
|
+
"headers": {
|
|
143
|
+
"type": "object",
|
|
144
|
+
"propertyNames": {
|
|
145
|
+
"type": "string"
|
|
146
|
+
},
|
|
147
|
+
"additionalProperties": {
|
|
148
|
+
"type": "string"
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"delayMs": {
|
|
152
|
+
"description": "Wait this long before answering",
|
|
153
|
+
"type": "integer",
|
|
154
|
+
"minimum": 0,
|
|
155
|
+
"maximum": 30000
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"required": [
|
|
159
|
+
"url"
|
|
160
|
+
],
|
|
161
|
+
"additionalProperties": false
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"clock": {
|
|
165
|
+
"description": "Start the page clock at this ISO date-time; it then runs normally",
|
|
166
|
+
"type": "string"
|
|
167
|
+
},
|
|
168
|
+
"dialogs": {
|
|
169
|
+
"default": "accept",
|
|
170
|
+
"description": "How to answer alert/confirm/prompt dialogs",
|
|
171
|
+
"type": "string",
|
|
172
|
+
"enum": [
|
|
173
|
+
"accept",
|
|
174
|
+
"dismiss"
|
|
175
|
+
]
|
|
86
176
|
}
|
|
87
177
|
},
|
|
88
178
|
"required": [
|
|
@@ -90,6 +180,133 @@
|
|
|
90
180
|
],
|
|
91
181
|
"additionalProperties": false
|
|
92
182
|
},
|
|
183
|
+
"hooks": {
|
|
184
|
+
"default": {},
|
|
185
|
+
"type": "object",
|
|
186
|
+
"properties": {
|
|
187
|
+
"before": {
|
|
188
|
+
"default": [],
|
|
189
|
+
"description": "Run before every check and record, e.g. to reset demo data",
|
|
190
|
+
"type": "array",
|
|
191
|
+
"items": {
|
|
192
|
+
"anyOf": [
|
|
193
|
+
{
|
|
194
|
+
"type": "object",
|
|
195
|
+
"properties": {
|
|
196
|
+
"run": {
|
|
197
|
+
"type": "string",
|
|
198
|
+
"minLength": 1,
|
|
199
|
+
"description": "Shell command, run in the script folder"
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"required": [
|
|
203
|
+
"run"
|
|
204
|
+
],
|
|
205
|
+
"additionalProperties": false
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"type": "object",
|
|
209
|
+
"properties": {
|
|
210
|
+
"request": {
|
|
211
|
+
"type": "object",
|
|
212
|
+
"properties": {
|
|
213
|
+
"url": {
|
|
214
|
+
"type": "string",
|
|
215
|
+
"minLength": 1
|
|
216
|
+
},
|
|
217
|
+
"method": {
|
|
218
|
+
"default": "POST",
|
|
219
|
+
"type": "string",
|
|
220
|
+
"minLength": 1
|
|
221
|
+
},
|
|
222
|
+
"json": {},
|
|
223
|
+
"headers": {
|
|
224
|
+
"type": "object",
|
|
225
|
+
"propertyNames": {
|
|
226
|
+
"type": "string"
|
|
227
|
+
},
|
|
228
|
+
"additionalProperties": {
|
|
229
|
+
"type": "string"
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
"required": [
|
|
234
|
+
"url"
|
|
235
|
+
],
|
|
236
|
+
"additionalProperties": false
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
"required": [
|
|
240
|
+
"request"
|
|
241
|
+
],
|
|
242
|
+
"additionalProperties": false
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
"after": {
|
|
248
|
+
"default": [],
|
|
249
|
+
"description": "Run after every check and record, even when it failed",
|
|
250
|
+
"type": "array",
|
|
251
|
+
"items": {
|
|
252
|
+
"anyOf": [
|
|
253
|
+
{
|
|
254
|
+
"type": "object",
|
|
255
|
+
"properties": {
|
|
256
|
+
"run": {
|
|
257
|
+
"type": "string",
|
|
258
|
+
"minLength": 1,
|
|
259
|
+
"description": "Shell command, run in the script folder"
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
"required": [
|
|
263
|
+
"run"
|
|
264
|
+
],
|
|
265
|
+
"additionalProperties": false
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"type": "object",
|
|
269
|
+
"properties": {
|
|
270
|
+
"request": {
|
|
271
|
+
"type": "object",
|
|
272
|
+
"properties": {
|
|
273
|
+
"url": {
|
|
274
|
+
"type": "string",
|
|
275
|
+
"minLength": 1
|
|
276
|
+
},
|
|
277
|
+
"method": {
|
|
278
|
+
"default": "POST",
|
|
279
|
+
"type": "string",
|
|
280
|
+
"minLength": 1
|
|
281
|
+
},
|
|
282
|
+
"json": {},
|
|
283
|
+
"headers": {
|
|
284
|
+
"type": "object",
|
|
285
|
+
"propertyNames": {
|
|
286
|
+
"type": "string"
|
|
287
|
+
},
|
|
288
|
+
"additionalProperties": {
|
|
289
|
+
"type": "string"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
"required": [
|
|
294
|
+
"url"
|
|
295
|
+
],
|
|
296
|
+
"additionalProperties": false
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
"required": [
|
|
300
|
+
"request"
|
|
301
|
+
],
|
|
302
|
+
"additionalProperties": false
|
|
303
|
+
}
|
|
304
|
+
]
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
"additionalProperties": false
|
|
309
|
+
},
|
|
93
310
|
"setup": {
|
|
94
311
|
"default": [],
|
|
95
312
|
"type": "array",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mineo71/showtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Product demos, directed by AI: Claude writes a JSON script, showTime records your web app and renders a polished MP4",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"demo",
|
|
@@ -50,7 +50,10 @@
|
|
|
50
50
|
"example": "node scripts/serve.mjs examples 4800",
|
|
51
51
|
"media": "tsx scripts/media.ts",
|
|
52
52
|
"schema:write": "node bin/showtime.mjs schema > docs/script.schema.json",
|
|
53
|
-
"install-skill": "node bin/showtime.mjs setup --no-browsers"
|
|
53
|
+
"install-skill": "node bin/showtime.mjs setup --no-browsers",
|
|
54
|
+
"preversion": "tsx scripts/changelog.ts check && npm run typecheck && npm test",
|
|
55
|
+
"version": "tsx scripts/changelog.ts release && git add CHANGELOG.md",
|
|
56
|
+
"postversion": "git push --follow-tags"
|
|
54
57
|
},
|
|
55
58
|
"dependencies": {
|
|
56
59
|
"@remotion/bundler": "4.0.535",
|
package/skill/SKILL.md
CHANGED
|
@@ -33,14 +33,19 @@ showtime setup # downloads the browsers it records with
|
|
|
33
33
|
4. **Rehearse:** `showtime check demo/pitch.json`. On failure read the error, the ARIA snapshot it
|
|
34
34
|
prints and `.showtime/<name>/check/failure.png`; fix the selector or add a `wait`, repeat until
|
|
35
35
|
green.
|
|
36
|
-
5. **
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
8. **
|
|
36
|
+
5. **Lint:** `showtime lint demo/pitch.json` and fix every warning (long captions, pauses that
|
|
37
|
+
should be waits, zoom or spotlight on steps without a target).
|
|
38
|
+
6. **Record:** `showtime record demo/pitch.json` (runs in real time).
|
|
39
|
+
7. **Render:** `showtime render demo/pitch.json` → `demo/pitch.mp4`.
|
|
40
|
+
8. **QA:** `showtime frames demo/pitch.json`, then Read the contact sheet it prints first
|
|
41
|
+
(`sheet.png`: every step in one image); open single stills only to look closer. Check that zoom
|
|
42
|
+
lands on the right control, captions are legible, and pacing feels right. Run `showtime lint`
|
|
43
|
+
again: with a take it also flags captions on screen too briefly to read.
|
|
44
|
+
9. **Iterate:** edits to `show`, `style` or `meta` only need `render`. Edits to actions (`do`,
|
|
41
45
|
targets, text, `hold`, `app`) need `record` again — `render` refuses a stale take.
|
|
42
46
|
|
|
43
|
-
`showtime make <script>` runs check → record → render in one go.
|
|
47
|
+
`showtime make <script>` runs check → record → render in one go. Every command takes `--json`
|
|
48
|
+
and then prints exactly one JSON document on stdout.
|
|
44
49
|
|
|
45
50
|
## Long-running commands
|
|
46
51
|
|
|
@@ -48,14 +53,22 @@ showtime setup # downloads the browsers it records with
|
|
|
48
53
|
Remotion. They routinely take several minutes. In Claude Code run them with `timeout: 600000`, or
|
|
49
54
|
with `run_in_background: true` and wait for the completion notification. In Codex and other
|
|
50
55
|
agents, give the command a timeout of at least 10 minutes.
|
|
51
|
-
|
|
56
|
+
A `record` that fails or is stopped halfway leaves the previous take in place.
|
|
52
57
|
|
|
53
58
|
## Side effects
|
|
54
59
|
|
|
55
60
|
`check` performs every action for real (it is a full run, just fast), and `record` repeats them.
|
|
56
61
|
Flows that create data ("New project", sign-up, checkout) can collide on the second run
|
|
57
|
-
("name already exists").
|
|
58
|
-
|
|
62
|
+
("name already exists"). Add `hooks` that reset the data: `before` hooks run at the start of every
|
|
63
|
+
`check` and `record`, `after` hooks when they end.
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
"hooks": { "before": [{ "run": "npm run db:reset" }] }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
A hook is `{ "run": "<shell command, run in the script folder>" }` or
|
|
70
|
+
`{ "request": { "url": "…", "method": "POST", "json": {…} } }`. If the project has no reset command,
|
|
71
|
+
ask the user for one rather than guessing.
|
|
59
72
|
|
|
60
73
|
## Example
|
|
61
74
|
|
|
@@ -97,7 +110,14 @@ the change in `setup`; after a green `check`, reset the data before `record`.
|
|
|
97
110
|
(address-bar text), `padding`, `radius`, `shadow`, `font`, `accent`, `autoZoom`, `autoSpeed`, `maxZoom`.
|
|
98
111
|
- **`app`:** `url`, `viewport`, `deviceScaleFactor`, `storageState` (Playwright storage-state JSON,
|
|
99
112
|
path relative to the script), `colorScheme`, `locale`, `timezoneId`, `mask` (CSS selectors to blur —
|
|
100
|
-
plain CSS only, not `text=` / `role=`)
|
|
113
|
+
plain CSS only, not `text=` / `role=`), `mock` (`[{ url: "**/api/x*", json | body, status?,
|
|
114
|
+
method?, delayMs? }]`), `clock` (ISO date-time the page clock starts at; first matching mock wins), `dialogs` (`accept` | `dismiss`).
|
|
115
|
+
Relative `goto` URLs resolve against `app.url`.
|
|
116
|
+
- **`meta`:** `title`, `subtitle` (intro card), `logo` (SVG/PNG/JPEG/WebP), `outro`
|
|
117
|
+
(`{ title, subtitle?, url? }`, closing card).
|
|
118
|
+
- **`hooks`:** `before` / `after` lists of `{ run }` or `{ request }`.
|
|
119
|
+
- **Brand file:** `showtime.config.json` in the script's folder or above holds default `style` and
|
|
120
|
+
`meta` (logo, outro) for every script; the script's own fields win.
|
|
101
121
|
- Output lands next to the script; working files in `.showtime/<script-name>/`.
|
|
102
122
|
|
|
103
123
|
## Direction tips
|
|
@@ -109,4 +129,8 @@ the change in `setup`; after a green `check`, reset the data before `record`.
|
|
|
109
129
|
- Never `pause` to wait for loading — use `wait {for}`; auto-speed compresses the loader.
|
|
110
130
|
- Use `setup` for cookie banners and onboarding, `app.storageState` to start logged in, `app.mask`
|
|
111
131
|
to blur personal data.
|
|
132
|
+
- Mock APIs that are slow, random or need real accounts (`app.mock`), and pin the date
|
|
133
|
+
(`app.clock`) so every take shows the same numbers and timestamps.
|
|
134
|
+
- End pitch demos with `meta.outro`: the product name and the URL to visit. Put brand colours, logo
|
|
135
|
+
and outro in `showtime.config.json` when a project has several demos.
|
|
112
136
|
- Keep the default 1440×900 viewport at `deviceScaleFactor` 2 for crisp 16:9 zooms.
|
package/src/cli/format.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import type { Issue } from '../lint';
|
|
2
3
|
import type { CheckReport } from '../runner/check';
|
|
3
4
|
|
|
4
5
|
const ARIA_LINES = 80;
|
|
@@ -17,3 +18,12 @@ export function formatCheck(report: CheckReport): string {
|
|
|
17
18
|
...aria.map((line) => ` ${line}`),
|
|
18
19
|
].join('\n');
|
|
19
20
|
}
|
|
21
|
+
|
|
22
|
+
export function formatLint(issues: Issue[], timing: boolean): string {
|
|
23
|
+
const lines = issues.map((i) => `${i.level === 'warn' ? '⚠' : 'ℹ'} ${i.where} ${i.message} [${i.rule}]`);
|
|
24
|
+
const warns = issues.filter((i) => i.level === 'warn').length;
|
|
25
|
+
const notes = issues.length - warns;
|
|
26
|
+
lines.push(issues.length ? `${warns} warning${warns === 1 ? '' : 's'}, ${notes} note${notes === 1 ? '' : 's'}` : '✓ no issues');
|
|
27
|
+
if (!timing) lines.push('(record a take to also check timing)');
|
|
28
|
+
return lines.join('\n');
|
|
29
|
+
}
|