retake-demos 0.3.0 → 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/AGENTS.md +64 -0
- package/README.md +14 -11
- package/dist/captions.js +9 -10
- package/dist/cards.js +57 -14
- package/dist/cli.js +197 -20
- package/dist/describe.js +2 -1
- package/dist/dryrun.js +182 -2
- package/dist/manifest.js +67 -4
- package/dist/operator/tools.js +50 -9
- package/dist/presets.js +13 -13
- package/dist/record.js +440 -43
- package/dist/render.js +255 -23
- package/dist/tidy.js +103 -0
- package/dist/ui/chat.html +233 -54
- package/dist/ui/server.js +63 -10
- package/package.json +2 -3
- package/site/releases.json +45 -0
- package/skill/SKILL.md +148 -2
- package/demos/example.yaml +0 -65
- package/dist/ui/index.html +0 -724
- package/site/index.html +0 -260
- package/site/logos/avexjets.png +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_readme": "What's new, written for the person using Retake — not for whoever wrote it. Both the landing page and the Retake window read THIS file; there is no second copy. Rules for adding one: say what changed for them, name no files, no flags they will not type, no words like refactor or regression. If a line only makes sense to someone who has read the source, it does not belong here. Newest first. Add an entry on every release that changes what people see.",
|
|
3
|
+
"releases": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.4.0",
|
|
6
|
+
"date": "2026-08-25",
|
|
7
|
+
"headline": "Videos come out the size you asked for — and changing your mind is nearly free",
|
|
8
|
+
"notes": [
|
|
9
|
+
"Every video is exactly the size of the format you picked. Landscape is 1920×1080, square is square, vertical fits a phone. No more sizes drifting between takes.",
|
|
10
|
+
"Changing your mind costs seconds, not another recording. Size, shape, format, captions, speed, zoom, music, title cards and the cover image all re-make themselves from the recording you already have.",
|
|
11
|
+
"Moved a caption to the wrong moment? Nudge it. The caption, the still and the cover all follow, and you never record again.",
|
|
12
|
+
"Only the ending changed? Record only the ending. The earlier steps still run so your app gets to the right place, but they happen at full speed and off camera. On a seven-minute demo that is about three minutes instead of sixteen.",
|
|
13
|
+
"Your old recordings are kept. A re-record no longer throws away the take you had — you can look at the previous ones and go back to any of them. Only the recording itself is stored, so it stays small.",
|
|
14
|
+
"A failed recording can no longer cost you the one that worked.",
|
|
15
|
+
"New: clear out old files safely. It shows you what it would free before removing anything, and never touches a recording.",
|
|
16
|
+
"In the window: pick several demos at once — or a whole folder — and archive them together. The archive empties in one click.",
|
|
17
|
+
"Launch videos can open with a title card and close with your own sign-off, put music underneath, and add a spoken voiceover.",
|
|
18
|
+
"Dead air gets trimmed: while your app is thinking, the video moves through the wait instead of sitting in it.",
|
|
19
|
+
"You can sign in to a demo account without any AI ever seeing your password.",
|
|
20
|
+
"Renders are about twice as fast.",
|
|
21
|
+
"Your agent now checks far more before recording — that a click will land on something clickable, that a stub is really being used, that it is looking at the same screen the recording will — so fewer takes are wasted finding out mid-recording.",
|
|
22
|
+
"On a long demo your agent shows you a quick draft and asks before spending the full recording.",
|
|
23
|
+
"The window has been rebuilt: one place for everything, and it stops shouting at you."
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"version": "0.3.0",
|
|
28
|
+
"date": "2026-08-23",
|
|
29
|
+
"headline": "Demos behind a login",
|
|
30
|
+
"notes": [
|
|
31
|
+
"Demos of apps that need signing in. You sign in once in a real browser window; nothing types your password and nothing records it.",
|
|
32
|
+
"The video player shows your video in its own shape, with no black bars around it."
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"version": "0.2.0",
|
|
37
|
+
"date": "2026-08-23",
|
|
38
|
+
"headline": "The first public release",
|
|
39
|
+
"notes": [
|
|
40
|
+
"Describe a demo in one readable file, and get a video, still frames, and a written record of what happened.",
|
|
41
|
+
"When your app changes, fix the file and run it again — instead of recording the whole thing over."
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
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
|
|
@@ -151,7 +194,13 @@ Anything they would tell a videographer maps to a knob, in plain English:
|
|
|
151
194
|
- "hide the cursor" → `cursor: false`
|
|
152
195
|
- "no captions" / "shorter captions" → captions off, or rewrite them
|
|
153
196
|
- "slower" / "hold the ending" → `pauseAfter`, scene `holdMs`, end waits
|
|
154
|
-
- "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.
|
|
155
204
|
|
|
156
205
|
The default is calm and real: still camera (`camera: static` is the
|
|
157
206
|
manifest default — write `auto` only when asked), cursor shown, plain
|
|
@@ -212,6 +261,103 @@ Never ask for a credential in chat. Never write a literal one into a
|
|
|
212
261
|
manifest. Never invent one. Tell the person to use a demo account — the
|
|
213
262
|
output is a video they may publish.
|
|
214
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
|
+
|
|
215
361
|
## When a take fails
|
|
216
362
|
|
|
217
363
|
The camera stops at the first failed step, so a failed take is short and
|
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: static # still by default; `auto` 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 }
|