retake-demos 0.2.2 → 0.4.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/.env.example +10 -0
- package/AGENTS.md +277 -0
- package/README.md +63 -22
- package/dist/captions.js +55 -0
- package/dist/cards.js +177 -0
- package/dist/cli.js +300 -15
- package/dist/describe.js +30 -10
- package/dist/dryrun.js +185 -2
- package/dist/env.js +76 -0
- package/dist/manifest.js +178 -10
- package/dist/operator/run.js +4 -0
- package/dist/operator/tools.js +111 -13
- package/dist/pace.js +99 -0
- package/dist/presets.js +7 -7
- package/dist/record.js +475 -46
- package/dist/render.js +427 -64
- package/dist/tidy.js +103 -0
- package/dist/ui/chat.html +301 -43
- package/dist/ui/guide.html +2 -1
- package/dist/ui/server.js +172 -36
- package/dist/voice.js +36 -0
- package/llms.txt +11 -0
- package/package.json +5 -3
- package/site/releases.json +45 -0
- package/skill/SKILL.md +272 -8
- package/demos/example.yaml +0 -65
- package/dist/ui/index.html +0 -724
- package/site/index.html +0 -158
package/skill/SKILL.md
CHANGED
|
@@ -62,10 +62,53 @@ say so in one line and stop.
|
|
|
62
62
|
names actually on screen? Anything in shot that shouldn't be (dev
|
|
63
63
|
badges, leftover data, cut-off text)? Wrong story or failed step →
|
|
64
64
|
`edit`, run again. More than four rounds → ask the person.
|
|
65
|
-
8.
|
|
65
|
+
8. **Before the expensive take, if it is expensive, show them the draft.**
|
|
66
|
+
`run` prints an estimate before capture. If it said this is a long take
|
|
67
|
+
(over ~2.5 minutes), do not go straight to full quality: `ask` them,
|
|
68
|
+
naming the demo and pointing at the draft in the Retake window, and say
|
|
69
|
+
what the full take will cost. Something like *"Draft is ready — 40s, 6
|
|
70
|
+
scenes, in the window. Full quality is about 7 minutes of recording.
|
|
71
|
+
Worth spending, or change something first?"*
|
|
72
|
+
- **Their taste is the thing you cannot check.** Every mechanical
|
|
73
|
+
problem is already caught by `dry` and the take's own checks. What a
|
|
74
|
+
person sees that no checker will is whether the story lands, whether
|
|
75
|
+
the payoff reads, whether it looks right for their product. Getting
|
|
76
|
+
that verdict on a cheap draft is the whole point.
|
|
77
|
+
- **Short take?** Skip the gate. Asking someone to approve a 30-second
|
|
78
|
+
recording costs them more than just recording it.
|
|
79
|
+
- **Nobody there to ask?** Never block and never guess. Leave the draft,
|
|
80
|
+
`done` with what you have and what it would cost, and let them start
|
|
81
|
+
the expensive one when they are back.
|
|
82
|
+
9. When they say go: `run` with `preview=false` once (full quality), then
|
|
66
83
|
`done` with one sentence. Outputs: `demo.mp4`, `stills/` (one PNG per
|
|
67
84
|
scene), `proof-log.md`.
|
|
68
85
|
|
|
86
|
+
## What a change costs — read this before re-recording anything
|
|
87
|
+
|
|
88
|
+
The one mistake that wastes real time is reaching for a new recording when a
|
|
89
|
+
re-render would do. Almost nothing needs the camera twice.
|
|
90
|
+
|
|
91
|
+
| You want to change | Do this | Cost |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| size, shape, format (landscape → square → vertical) | `render` with another preset | seconds |
|
|
94
|
+
| a caption's words, the speed, a zoom, cards, music | edit the manifest, `render` | seconds |
|
|
95
|
+
| **where** a caption or still sits in time | `nudge: <ms>` on the scene, then `render` | seconds |
|
|
96
|
+
| which frame is the poster | click a frame in the window, or `--scene` | seconds |
|
|
97
|
+
| the ENDING of a demo | `run --from <scene>` | the tail only |
|
|
98
|
+
| one beat, iterating | `run --until <scene>` | the head only |
|
|
99
|
+
| the steps themselves — what the demo DOES | full `run` | the whole thing |
|
|
100
|
+
|
|
101
|
+
Only the last row needs the camera for the whole demo. If you are about to
|
|
102
|
+
re-record and your reason is not in that last row, you are paying for
|
|
103
|
+
something you already have.
|
|
104
|
+
|
|
105
|
+
`nudge` and `--from` are the two most recently added and the two most often
|
|
106
|
+
missed. `nudge: -1500` moves a scene's marker 1.5s earlier in the finished
|
|
107
|
+
video — caption, still and thumbnail follow it. `--from <scene>` runs the
|
|
108
|
+
earlier steps at full speed off camera so the app reaches the right state,
|
|
109
|
+
then starts recording there; a seven-minute demo whose last third changed
|
|
110
|
+
costs ninety seconds instead of seven minutes.
|
|
111
|
+
|
|
69
112
|
## Style — how the person wants their videos to look
|
|
70
113
|
|
|
71
114
|
Do not ask about knobs before the first take — nobody has opinions about
|
|
@@ -77,23 +120,244 @@ zooms or caption tone until they have seen one. Two moves instead:
|
|
|
77
120
|
→ landscape, still camera, a hold on the payoff. Skip the question if
|
|
78
121
|
the project's style note already answers it.
|
|
79
122
|
2. **The first take is the menu.** Record the calm default as a preview,
|
|
80
|
-
let them watch it, then
|
|
81
|
-
|
|
82
|
-
Whatever they choose, save it with `style`
|
|
123
|
+
let them watch it, then one line — no list, no questions before it:
|
|
124
|
+
"Want it different? A zoom on the result, cursor off, square for a post —
|
|
125
|
+
say so and I'll re-render." Whatever they choose, save it with `style`
|
|
126
|
+
so it never comes up again in this project. Never ask about zoom or
|
|
127
|
+
cursor before they have seen a take.
|
|
128
|
+
|
|
129
|
+
**Which kind of take is this? Get it from the destination, never from the
|
|
130
|
+
subject.** Write it into the manifest as `mode:`, and default to `demo`.
|
|
131
|
+
|
|
132
|
+
- **`mode: demo`** — it proves how the thing works. A client walkthrough IS
|
|
133
|
+
a demo. So is a PR review, a bug repro, a docs clip, a lesson, "show the
|
|
134
|
+
team", "send it to the customer", "so they can see the flow". Nothing in
|
|
135
|
+
the frame but the product doing the thing. No title card, no music, no
|
|
136
|
+
callouts. Most takes are this.
|
|
137
|
+
- **`mode: launch`** — it presents the product to strangers, in public: a
|
|
138
|
+
launch, a post, an ad, a landing page, Product Hunt, "for X", a trailer.
|
|
139
|
+
Only then are a title card, music, emphasis and a branded ending on the
|
|
140
|
+
table, and each still has to earn its second.
|
|
141
|
+
|
|
142
|
+
The tell is the audience, not the polish: *someone who already wants to
|
|
143
|
+
know how it works* → demo. *Someone who has to be given a reason to care*
|
|
144
|
+
→ launch. If the person has not said, ask the one destination question and
|
|
145
|
+
take `demo` if they shrug. **Never upgrade a demo to a launch on your own**
|
|
146
|
+
— `validate` warns when a demo carries launch furniture, and that warning
|
|
147
|
+
means you guessed.
|
|
148
|
+
|
|
149
|
+
A launch cut ends with THEIR product, never with Retake. Retake branding
|
|
150
|
+
belongs only in a video that presents Retake.
|
|
151
|
+
|
|
152
|
+
**Two kinds of take, one file — and the destination decides.** The default
|
|
153
|
+
is the plain cut: still camera, cursor, captions, nothing else. That is
|
|
154
|
+
correct for PRs, clients, docs, lessons — most takes. When the person's
|
|
155
|
+
answer to "where is this going?" is a launch, a post, or anything public
|
|
156
|
+
and promotional, offer the produced cut in ONE sentence: "Want the launch
|
|
157
|
+
treatment — title card, a callout on the payoff, the waits compressed,
|
|
158
|
+
the captions read aloud, square or vertical for the feed, and music if
|
|
159
|
+
you give me a track?" Never apply it unasked, and
|
|
160
|
+
never ask about its parts one by one.
|
|
161
|
+
|
|
162
|
+
The produced cut, when they say yes (all render-time except typing):
|
|
163
|
+
- `intro: { title, subtitle }` and usually `outro:` (the product name or URL)
|
|
164
|
+
- one `callout` on the payoff moment, not more
|
|
165
|
+
- the destination's shape: `post-square` or `post-vertical`
|
|
166
|
+
- `compressIdle: true` — the app's waits shown as ~1.5s each; the person's
|
|
167
|
+
pacing `wait`s are never touched
|
|
168
|
+
- **voiceover: leave it off.** It needs `script:` (one continuous script,
|
|
169
|
+
synthesized as one performance) AND `approved: true`, which only a person
|
|
170
|
+
who has HEARD it can set. Reading the captions line by line resets prosody
|
|
171
|
+
at every scene — that is a defect in the shape, not the voice. A silent cut
|
|
172
|
+
beats an unapproved synthetic voice. Offer narration only if they ask for
|
|
173
|
+
it, and let them listen before it ships.
|
|
174
|
+
- `typing: brisk` — fast keys, the pauses carry the meaning (re-record)
|
|
175
|
+
- `music: <file>` only if they hand you a file or point at one on disk.
|
|
176
|
+
Retake bundles no tracks (licensing) — say: "give me an mp3 you have the
|
|
177
|
+
rights to; CC0 tracks live at freepd.com or pixabay.com/music". Skip
|
|
178
|
+
music without complaint if they shrug.
|
|
83
179
|
|
|
84
180
|
Anything they would tell a videographer maps to a knob, in plain English:
|
|
85
181
|
|
|
182
|
+
- "add a title card" / "give it a cover" → `intro: { title, subtitle }`
|
|
183
|
+
(and `outro:`) — render-time, and it also writes two poster candidates:
|
|
184
|
+
cover.png (the card) and cover-titled.png (a real frame with the title on
|
|
185
|
+
it, which is what stops a scroll)
|
|
186
|
+
- "the thumbnail is wrong" → they pick the poster themselves in the window
|
|
187
|
+
(any frame, any still, a cover, or their own image). Point them at it
|
|
188
|
+
rather than re-rendering; `outputs.thumbnail: { scene }` only sets the
|
|
189
|
+
starting guess.
|
|
190
|
+
- "point at it" / "highlight the button" → a `callout` step with a label —
|
|
191
|
+
at most one per scene, and only where the caption alone is not enough
|
|
192
|
+
|
|
86
193
|
- "no zooms" / "keep the camera still" → `camera: static` on scenes
|
|
87
194
|
- "hide the cursor" → `cursor: false`
|
|
88
195
|
- "no captions" / "shorter captions" → captions off, or rewrite them
|
|
89
196
|
- "slower" / "hold the ending" → `pauseAfter`, scene `holdMs`, end waits
|
|
90
|
-
- "square" / "vertical" / "for X" → preset `post-square` / `post-vertical
|
|
197
|
+
- "square" / "vertical" / "for X" → preset `post-square` / `post-vertical`,
|
|
198
|
+
and this is a RE-RENDER of the take you already have, never a re-record:
|
|
199
|
+
`retake render outputs/<name> --preset post-square`. Size is not recorded.
|
|
200
|
+
- **Never set `viewport`.** The preset owns the size — one preset, one
|
|
201
|
+
output shape, every time. A manifest that sets its own viewport comes out
|
|
202
|
+
a different shape from every other demo of the same app, and players
|
|
203
|
+
letterbox it. `validate` warns.
|
|
91
204
|
|
|
92
|
-
The default is
|
|
205
|
+
The default is calm and real: still camera (`camera: static` is the
|
|
206
|
+
manifest default — write `auto` only when asked), cursor shown, plain
|
|
207
|
+
captions.
|
|
93
208
|
The first time the person expresses taste, save it with the `style` tool
|
|
94
209
|
(or write `demos/style.md`) — every later draft in the project reads it, so
|
|
95
210
|
they never have to say it twice. Their stated taste always beats defaults.
|
|
96
211
|
|
|
212
|
+
## The person may never have seen Retake — walk them
|
|
213
|
+
|
|
214
|
+
Assume a first-timer: your agent installed Retake for them and they know
|
|
215
|
+
nothing about a window, a workspace or a `.env`. So, every time:
|
|
216
|
+
|
|
217
|
+
- **Say where to look, with the link.** At the start of a demo: "I'll record
|
|
218
|
+
this with Retake. You can watch at http://localhost:4310 (run `retake ui`
|
|
219
|
+
if it isn't open) — the video, stills and a step-by-step log land there."
|
|
220
|
+
Never assume the window is open or that they know the address.
|
|
221
|
+
- **One thing at a time, in their words.** When you need them to act (type a
|
|
222
|
+
demo account, log in by hand, start their app), say exactly what to do,
|
|
223
|
+
where, and how you'll know it's done: "…press Save to .env, then tell me
|
|
224
|
+
it's done." Then wait for them to say so. Do not go quiet — a tool that
|
|
225
|
+
waits on them is your cue to speak, not to sit.
|
|
226
|
+
- **Close the loop.** When the take is done: the one-line result, where the
|
|
227
|
+
file is, and the one thing they can change without a re-record (speed,
|
|
228
|
+
captions, format). Offer, don't pile on.
|
|
229
|
+
|
|
230
|
+
## Logins — most real demos start behind one
|
|
231
|
+
|
|
232
|
+
You will never see a password, and you never need to. The whole method:
|
|
233
|
+
|
|
234
|
+
1. **Notice it early.** `read_project` reports sign-in fields; `scout` shows a
|
|
235
|
+
password input; or the person says so. Do not draft a manifest that
|
|
236
|
+
pretends the app is open.
|
|
237
|
+
2. **Call `secrets` with the NAMES** you will use — `APP_USER`, `APP_PASSWORD`,
|
|
238
|
+
and `APP_TOTP_SECRET` if the site asks for an authenticator code — and one
|
|
239
|
+
sentence of why. It returns immediately with the words to say: a form is
|
|
240
|
+
open in the window at a link, or a one-line terminal command. **Relay
|
|
241
|
+
those words in full, link included**, then stop and wait for "done". Call
|
|
242
|
+
`secrets` again; it answers "set" and you carry on. The values went
|
|
243
|
+
straight into the workspace `.env` — you never saw them.
|
|
244
|
+
3. **Write the sign-in under `auth.setup`**, referencing `${APP_USER}` and
|
|
245
|
+
`${APP_PASSWORD}` with `secret: true`, and set `auth.storageState` to
|
|
246
|
+
`.auth/<name>.json`. The login runs before the camera and is trimmed off;
|
|
247
|
+
the session is saved and later takes skip the login entirely. A failed
|
|
248
|
+
sign-in is never saved as a session, so a wrong password stops the take
|
|
249
|
+
instead of recording a logged-out one.
|
|
250
|
+
4. **Authenticator codes:** `text: "${TOTP:APP_TOTP_SECRET}"` — Retake
|
|
251
|
+
computes the six-digit code at the moment it fills the field.
|
|
252
|
+
5. **SMS codes, SSO, captchas, anything only a human can do:** do not try.
|
|
253
|
+
Tell the person: "run `retake signin demos/<name>.yaml` and log in once by
|
|
254
|
+
hand — Retake keeps the session." Then run as normal. The manifest still
|
|
255
|
+
needs `auth.storageState`; `auth.setup` can be empty.
|
|
256
|
+
6. **Firebase-style apps** keep the session in IndexedDB, which a saved
|
|
257
|
+
session cannot restore: put the login under plain `setup` instead, so it
|
|
258
|
+
runs every take (still before the camera, still trimmed).
|
|
259
|
+
|
|
260
|
+
Never ask for a credential in chat. Never write a literal one into a
|
|
261
|
+
manifest. Never invent one. Tell the person to use a demo account — the
|
|
262
|
+
output is a video they may publish.
|
|
263
|
+
|
|
264
|
+
## Hard-won rules from real sessions
|
|
265
|
+
|
|
266
|
+
- **Scout again after ANY server restart** — not just the first time. A dev
|
|
267
|
+
server that restarts can come back serving a different build or mode on
|
|
268
|
+
the same port, and the take fails 20 seconds in with a selector timeout
|
|
269
|
+
that looks like your manifest's fault. Five takes were lost to this once.
|
|
270
|
+
If the run aborts saying "the app at <url> is not what this manifest
|
|
271
|
+
expects", believe it: look at not-the-app.png, fix the server, re-run.
|
|
272
|
+
- **A failed step leaves failed-step.png next to the take** — a full-page
|
|
273
|
+
picture of the exact moment. Read it BEFORE re-running or editing
|
|
274
|
+
anything; it turns a five-round guess loop into one round.
|
|
275
|
+
- **`waitFor` can match a STALE element.** After an action, the previous
|
|
276
|
+
capture's Undo button (or any leftover) may still satisfy your selector,
|
|
277
|
+
and the click lands on the wrong thing. Wait for something unique to the
|
|
278
|
+
NEW state (a count, a heading, a row with the new text), never for a
|
|
279
|
+
control that also existed before the action.
|
|
280
|
+
- **What `compressIdle` compresses:** the app's own delays (`waitFor`,
|
|
281
|
+
`navigate`) and long typing. It NEVER touches your `wait` steps — those
|
|
282
|
+
are your pacing. Do not re-render hoping a `wait` gets shorter; shorten
|
|
283
|
+
the wait.
|
|
284
|
+
- **Streaming content:** `waitFor` on a container resolves when the shell
|
|
285
|
+
exists, not when the text has streamed in. Wait on the signal the app
|
|
286
|
+
itself uses — a button flipping from busy to ready
|
|
287
|
+
(`button:has-text("Send")`), a count, a class change.
|
|
288
|
+
|
|
289
|
+
## Waits that do not lie
|
|
290
|
+
|
|
291
|
+
A bare `waitFor` resolves the instant the element exists. Three times that
|
|
292
|
+
is the wrong moment, and each has cost a take:
|
|
293
|
+
|
|
294
|
+
- the element is the **previous** action's success banner, still on screen →
|
|
295
|
+
`{ action: waitFor, selector: ".landed", gone: true }`
|
|
296
|
+
- the element is an empty shell that **streams** its text in →
|
|
297
|
+
`minChars: 40`
|
|
298
|
+
- the element is there but still **changing** → `stableMs: 500`
|
|
299
|
+
|
|
300
|
+
Prefer these to a flat `wait`. A timer that happens to work today is the
|
|
301
|
+
thing that breaks when the app gets slower.
|
|
302
|
+
|
|
303
|
+
## Rules the manifest enforces, so you do not have to remember them
|
|
304
|
+
|
|
305
|
+
`validate` and `dry` refuse these outright rather than letting a take discover
|
|
306
|
+
them:
|
|
307
|
+
|
|
308
|
+
- **Scene labels are unique.** `thumbnail`, `--until`, the stills and the proof
|
|
309
|
+
log all address a scene by its label.
|
|
310
|
+
- **The action has to fit the element.** `type` on a `<select>`, `select` on a
|
|
311
|
+
`<div>`, `upload` on something that is not a file input, `click` on a button
|
|
312
|
+
that is disabled at that moment — `dry` names the fix.
|
|
313
|
+
- **A navigate that changes only the `#fragment` is flagged.** It does not
|
|
314
|
+
reload the document, so an SPA keeps its state and a stub armed just before
|
|
315
|
+
it never fires. Add a throwaway query param, or click the app's own link.
|
|
316
|
+
- **A stub that never answers a request fails `check`.** Armed but unmatched
|
|
317
|
+
means that screen showed the app's real data.
|
|
318
|
+
- **Seeds may not leave anything running** — no `&`, `nohup`, `pm2`. Starting
|
|
319
|
+
the app is `start_app`'s job, which the person authorises.
|
|
320
|
+
|
|
321
|
+
## Cheap iteration — a demo should not cost five takes
|
|
322
|
+
|
|
323
|
+
A take is real time (capture is ~2.3× the finished video) and the field
|
|
324
|
+
average is three to five takes per demo. Most of that is avoidable:
|
|
325
|
+
|
|
326
|
+
1. **Draft everything.** `--preset draft` has the same layout and timing at
|
|
327
|
+
a quarter of the pixels. Judge the story there; switch to a post preset
|
|
328
|
+
once, at the end.
|
|
329
|
+
2. **`--until <scene>` for the head, `--from <scene>` for the tail.** These
|
|
330
|
+
are the two halves of the same idea and `--from` is the one people miss:
|
|
331
|
+
when the ending changed, everything before it still runs (the app has to
|
|
332
|
+
reach that state) but at full speed and off camera. Re-recording a 60s
|
|
333
|
+
demo to change its last 8 seconds is paying for 50 seconds you already
|
|
334
|
+
have.
|
|
335
|
+
3. **Get a person's eyes on a cheap draft before buying an expensive take.**
|
|
336
|
+
A draft costs a quarter of the pixels and answers the only question a
|
|
337
|
+
checker cannot: is this the right video? Spending seven minutes of
|
|
338
|
+
capture to find out the story was wrong is the most expensive mistake
|
|
339
|
+
available in this tool.
|
|
340
|
+
4. **A caption in the wrong place is a nudge, not a take.** `nudge: -1500` on
|
|
341
|
+
a scene moves its marker 1.5s earlier in the finished video; the caption,
|
|
342
|
+
the still and the thumbnail all follow. Re-render and look — it is seconds.
|
|
343
|
+
5. **Never re-record for a size or a format.** `render outputs/<name>
|
|
344
|
+
--preset post-vertical` re-frames the take you have. Captions, camera,
|
|
345
|
+
speed, cards and posters are all render-time too.
|
|
346
|
+
6. **`dry` before every `run`** — including after the app restarts. It is
|
|
347
|
+
~10s and it catches the wrong-build-on-the-port failure that otherwise
|
|
348
|
+
costs a take and a confusing video.
|
|
349
|
+
7. **Fix everything `dry` and the proof log report in ONE pass.** Reading
|
|
350
|
+
the whole failure list before editing turns three rounds into one.
|
|
351
|
+
|
|
352
|
+
## Judging the cut
|
|
353
|
+
|
|
354
|
+
`check: pass` proves the file is sound, not that the demo is good. Before
|
|
355
|
+
`done`, look: `retake contact outputs/<name>` writes one timestamped grid
|
|
356
|
+
of the whole video (launch cuts get it automatically). Read it for the
|
|
357
|
+
things checks cannot see — a caption that is true when its scene starts and
|
|
358
|
+
false by the end, a payoff nobody can find, four seconds where nothing
|
|
359
|
+
changes. `look` at the payoff scene is the other half.
|
|
360
|
+
|
|
97
361
|
## When a take fails
|
|
98
362
|
|
|
99
363
|
The camera stops at the first failed step, so a failed take is short and
|
|
@@ -136,8 +400,8 @@ up to the end of that scene.
|
|
|
136
400
|
|
|
137
401
|
- Real interactions read as real: type actual text, scroll to what you use,
|
|
138
402
|
hold on the result. No zoom tricks unless asked.
|
|
139
|
-
- Secrets never go in a manifest
|
|
140
|
-
|
|
403
|
+
- Secrets never go in a manifest and never into chat: the `secrets` tool
|
|
404
|
+
gets them into `.env` by name; you reference `${NAME}` with `secret: true`.
|
|
141
405
|
- Never invent selectors — only what scout/read_project returned.
|
|
142
406
|
- If the backend is down, prefer a `stub:` block over giving up.
|
|
143
407
|
- Starting the person's app needs their explicit say-so, every time.
|
package/demos/example.yaml
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# A template. Copy it, point it at your app, replace the selectors.
|
|
2
|
-
#
|
|
3
|
-
# retake validate demos/example.yaml schema + warnings, instant
|
|
4
|
-
# retake dry demos/example.yaml every selector and wait, no camera
|
|
5
|
-
# retake run demos/example.yaml record + render → outputs/example/
|
|
6
|
-
#
|
|
7
|
-
# Or don't write it at all: once `retake install` has run, ask your coding
|
|
8
|
-
# agent for "a demo of my app at localhost:3000 showing …" and it drafts,
|
|
9
|
-
# dry-runs, records and checks this file for you. `retake ui` is the window
|
|
10
|
-
# to watch it in.
|
|
11
|
-
|
|
12
|
-
name: example
|
|
13
|
-
title: "Example — the shape of a demo"
|
|
14
|
-
url: http://localhost:3000
|
|
15
|
-
|
|
16
|
-
preset: post-landscape # 1080p @ 30fps · see `retake presets`
|
|
17
|
-
viewport: { width: 1440, height: 1080 } # match your app's natural shape
|
|
18
|
-
scale: 1.8 # page drawn at 1.8× so text stays crisp at 1080p
|
|
19
|
-
reducedMotion: true # calms infinite spinners and entrance animations
|
|
20
|
-
camera: auto # each scene eases toward the last thing touched
|
|
21
|
-
|
|
22
|
-
# Put the app in a known state before the camera rolls. Pick what fits:
|
|
23
|
-
# - { kind: file, path: /tmp/app/state.json, from: seeds/empty.json }
|
|
24
|
-
# - { kind: evaluate, script: "localStorage.clear()" }
|
|
25
|
-
# - { kind: command, run: "curl -X POST http://localhost:3000/api/test/reset" }
|
|
26
|
-
seed: []
|
|
27
|
-
|
|
28
|
-
# Runs before recording starts and is trimmed off the front of the video.
|
|
29
|
-
# Sign-ins belong here — see the "Signing in" section of the README.
|
|
30
|
-
setup: []
|
|
31
|
-
|
|
32
|
-
steps:
|
|
33
|
-
- action: scene
|
|
34
|
-
label: start
|
|
35
|
-
caption: "The problem, before anything happens."
|
|
36
|
-
camera: static
|
|
37
|
-
- action: wait
|
|
38
|
-
ms: 1800
|
|
39
|
-
|
|
40
|
-
- action: click
|
|
41
|
-
selector: "button:has-text('Get started')"
|
|
42
|
-
pauseAfter: 900
|
|
43
|
-
|
|
44
|
-
- action: type
|
|
45
|
-
selector: "input[name=title]"
|
|
46
|
-
text: "A realistic thing a person would type"
|
|
47
|
-
delay: 40
|
|
48
|
-
pauseAfter: 600
|
|
49
|
-
|
|
50
|
-
- action: click
|
|
51
|
-
selector: "button[type=submit]"
|
|
52
|
-
- action: waitFor # wait for the RESULT, never a fixed sleep
|
|
53
|
-
selector: ".result"
|
|
54
|
-
timeout: 30000
|
|
55
|
-
|
|
56
|
-
- action: scene
|
|
57
|
-
label: result
|
|
58
|
-
caption: "And the result, which is the whole point."
|
|
59
|
-
- action: wait
|
|
60
|
-
ms: 2600
|
|
61
|
-
|
|
62
|
-
outputs:
|
|
63
|
-
mp4: true
|
|
64
|
-
gif: false # opt-in; gifski is used when installed
|
|
65
|
-
thumbnail: { scene: result }
|