retake-demos 0.2.2 → 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/site/index.html CHANGED
@@ -3,10 +3,26 @@
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>Retake — rerun the demo, don't re-record it</title>
7
- <meta name="description" content="Retake turns a plain-English walkthrough into a repeatable video of your real app. Your agent writes the flow; Retake checks it, records it, and keeps it rerunnable." />
6
+ <title>Retake — demos that re-record themselves</title>
7
+ <!-- Social card. The domain is stamped by scripts/site-domain.mjs; until
8
+ then these point at the placeholder and a shared link shows no card. -->
9
+ <meta property="og:type" content="website" />
10
+ <meta property="og:site_name" content="Retake" />
11
+ <meta property="og:title" content="Retake — demos that re-record themselves" />
12
+ <meta property="og:description" content="A demo is a small file your coding agent writes. When the UI changes, change one line and run it again." />
13
+ <meta property="og:url" content="https://retake.example.com/" />
14
+ <meta property="og:image" content="https://retake.example.com/og.png" />
15
+ <meta property="og:image:width" content="1200" />
16
+ <meta property="og:image:height" content="630" />
17
+ <meta name="twitter:card" content="summary_large_image" />
18
+ <meta name="twitter:title" content="Retake — demos that re-record themselves" />
19
+ <meta name="twitter:description" content="A demo is a small file your coding agent writes. When the UI changes, change one line and run it again." />
20
+ <meta name="twitter:image" content="https://retake.example.com/og.png" />
21
+ <link rel="canonical" href="https://retake.example.com/" />
22
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgcm9sZT0iaW1nIiBhcmlhLWxhYmVsPSJSZXRha2UiPiA8cmVjdCB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgcng9IjExMiIgZmlsbD0iIzFhMWMxZSIvPiA8ZyBmaWxsPSIjZWNlZWU5Ij4gPHJlY3QgeD0iMTMyIiB5PSIxMTIiIHdpZHRoPSI5NiIgaGVpZ2h0PSI0NiIgcng9IjIzIiBvcGFjaXR5PSIwLjU1Ii8+IDxyZWN0IHg9IjEzMiIgeT0iMTgwIiB3aWR0aD0iMTkyIiBoZWlnaHQ9IjQ2IiByeD0iMjMiIG9wYWNpdHk9IjAuOCIvPiA8cmVjdCB4PSIxMzIiIHk9IjI0OCIgd2lkdGg9IjI4OCIgaGVpZ2h0PSI0NiIgcng9IjIzIiBmaWxsPSIjZThiMDRiIi8+IDxyZWN0IHg9IjEzMiIgeT0iMzE2IiB3aWR0aD0iMTkyIiBoZWlnaHQ9IjQ2IiByeD0iMjMiIG9wYWNpdHk9IjAuOCIvPiA8cmVjdCB4PSIxMzIiIHk9IjM4NCIgd2lkdGg9Ijk2IiBoZWlnaHQ9IjQ2IiByeD0iMjMiIG9wYWNpdHk9IjAuNTUiLz4gPC9nPiA8L3N2Zz4=" />
23
+ <meta name="description" content="A demo is a small file your coding agent writes. When the UI changes, run the file again — nobody re-records anything by hand." />
8
24
  <style>
9
- :root { --paper:#edefe8; --card:#f8f9f5; --ink:#191d19; --muted:#767e74; --rule:#dbdfd3; --rule2:#c9cec0; --accent:#1a1c1e; --amber:#e8b04b; }
25
+ :root { --paper:#edefe8; --card:#f8f9f5; --ink:#191d19; --muted:#666e64; --rule:#dbdfd3; --rule2:#c9cec0; --accent:#1a1c1e; --amber:#e8b04b; }
10
26
  * { box-sizing:border-box; }
11
27
  body { margin:0; background:var(--paper); color:var(--ink); font:16px/1.7 -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Inter, Arial, sans-serif; -webkit-font-smoothing:antialiased; }
12
28
  .top { display:flex; align-items:center; gap:10px; padding:18px 26px; max-width:880px; margin:0 auto; }
@@ -18,18 +34,21 @@
18
34
  .top a { color:var(--muted); text-decoration:none; font-size:14px; margin-left:18px; }
19
35
  .top a:hover { color:var(--ink); }
20
36
  main { max-width:880px; margin:0 auto; padding:30px 26px 90px; }
21
- .hero { padding:40px 0 30px; }
37
+ .hero { padding:52px 0 34px; }
38
+ .flow { margin:26px 0 0; font:13px ui-monospace, Menlo, monospace; color:var(--muted); letter-spacing:.02em; }
39
+ .flow span { color:var(--rule2); padding:0 2px; }
40
+ .terms { margin:10px 0 0; font-size:12.5px; color:var(--muted); }
22
41
  .kicker { font:12px ui-monospace, Menlo, monospace; letter-spacing:.09em; text-transform:uppercase; color:var(--muted); margin:0 0 14px; }
23
42
  h1 { font-size:clamp(34px, 6vw, 54px); line-height:1.08; letter-spacing:-.025em; margin:0 0 18px; }
24
43
  h1 em { font-style:normal; color:var(--muted); }
25
44
  .lede { font-size:19px; color:var(--muted); max-width:620px; margin:0 0 28px; }
26
45
  .actions { display:flex; gap:10px; flex-wrap:wrap; }
27
- .btn { display:inline-block; text-decoration:none; border-radius:12px; padding:12px 22px; font-size:15px; }
46
+ .btn { display:inline-block; text-decoration:none; border-radius:14px; padding:13px 24px; font-size:15px; letter-spacing:-.01em; }
28
47
  .btn.dark { background:var(--accent); color:#eceee9; }
29
48
  .btn.dark:hover { opacity:.9; }
30
49
  .btn.light { border:1px solid var(--rule2); color:var(--ink); background:#fff; }
31
50
  .btn.light:hover { border-color:var(--ink); }
32
- .say { background:var(--card); border:1px solid var(--rule); border-radius:14px; padding:16px 20px; margin:46px 0 10px; font-size:17px; }
51
+ .say { background:var(--card); border:1px solid var(--rule); border-radius:14px; padding:18px 22px; margin:46px 0 10px; font-size:16.5px; box-shadow:0 1px 0 rgba(24,28,24,.03); }
33
52
  .say .who { font:11px ui-monospace, Menlo, monospace; letter-spacing:.07em; text-transform:uppercase; color:var(--muted); display:block; margin-bottom:6px; }
34
53
  .say i { font-style:normal; }
35
54
  .arrow { text-align:center; color:var(--muted); margin:8px 0; }
@@ -38,10 +57,10 @@
38
57
  .shot video { width:100%; height:auto; display:block; }
39
58
  .shot figcaption { background:var(--card); padding:12px 18px 14px; font-size:14px; color:var(--muted); }
40
59
  .shot figcaption b { color:var(--ink); font-weight:600; }
41
- section { margin-top:72px; }
60
+ section { margin-top:84px; }
42
61
  h2 { font-size:24px; letter-spacing:-.015em; margin:0 0 8px; }
43
62
  .sub { color:var(--muted); margin:0 0 26px; max-width:560px; }
44
- .grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(240px, 1fr)); gap:14px; }
63
+ .grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(300px, 1fr)); gap:14px; }
45
64
  .cell { background:var(--card); border:1px solid var(--rule); border-radius:14px; padding:18px 20px; }
46
65
  .cell b { display:block; margin-bottom:6px; font-size:15px; }
47
66
  .cell p { margin:0; font-size:14px; color:var(--muted); line-height:1.6; }
@@ -53,6 +72,42 @@
53
72
  .step p { margin:2px 0 0; color:var(--muted); font-size:14.5px; }
54
73
  pre { background:var(--card); border:1px solid var(--rule); border-radius:12px; padding:16px 20px; overflow-x:auto; font:14px/1.8 ui-monospace, Menlo, monospace; }
55
74
  pre .c { color:var(--muted); }
75
+ .diff { font:13.5px/1.9 ui-monospace, Menlo, monospace; overflow-x:auto; }
76
+ .diff div { white-space:pre; }
77
+ .diff .del { color:#9a4238; } .diff .del::before { content:"− "; opacity:.55; }
78
+ .diff .add { color:#33613f; } .diff .add::before { content:"+ "; opacity:.55; }
79
+ .diff .ctx { color:var(--muted); font-size:12px; margin-bottom:4px; }
80
+ .copyline { display:flex; gap:10px; align-items:stretch; margin-top:0; }
81
+ .copyline .say { flex:1; margin:0; }
82
+ .copybtn { flex:none; border:1px solid var(--rule2); background:#fff; border-radius:14px; padding:0 20px; font:14px/1 inherit; cursor:pointer; transition:border-color .15s; }
83
+ .copybtn:hover { border-color:var(--ink); }
84
+ .vocab { background:var(--card); border:1px solid var(--rule); border-left:3px solid var(--amber); border-radius:14px; padding:18px 22px; font-size:15px; color:var(--muted); line-height:1.8; }
85
+ .vocab b { color:var(--ink); }
86
+ .cases { display:grid; grid-template-columns:repeat(auto-fit, minmax(250px, 1fr)); gap:14px; }
87
+ .case { background:var(--card); border:1px solid var(--rule); border-radius:16px; padding:22px 24px; display:flex; flex-direction:column; box-shadow:0 10px 26px rgba(24,28,24,.06); }
88
+ .wordmark { font-size:24px; font-weight:700; letter-spacing:-.02em; margin-bottom:12px; display:flex; align-items:center; gap:10px; }
89
+ .caselogo { height:28px; width:28px; border-radius:7px; object-fit:contain; }
90
+ .caselogo.wide { height:26px; width:auto; max-width:170px; border-radius:0; }
91
+ .wordmark .serif { font-family:Georgia, "Times New Roman", serif; font-weight:600; letter-spacing:0; }
92
+ .wordmark.tbmark { font-weight:900; letter-spacing:-.03em; gap:1px; }
93
+ .wordmark .avex { color:var(--amber); margin-right:2px; }
94
+ .case .quote { margin:0 0 14px; font-size:14.5px; line-height:1.65; flex:1; }
95
+ .case .meta { margin:0; font-size:12.5px; color:var(--muted); }
96
+ .case .watch { margin:12px 0 0; padding-top:10px; border-top:1px solid var(--rule); font-size:13px; display:flex; gap:8px; align-items:baseline; }
97
+ .case .watch .k { font:10.5px ui-monospace, Menlo, monospace; letter-spacing:.08em; color:var(--muted); flex:none; }
98
+ .case .watch a { color:var(--ink); }
99
+ .case .meta a { color:inherit; }
100
+ @media (max-width: 560px) {
101
+ /* The diff is the page's best proof, so on a phone it wraps rather than
102
+ clipping — it stays the real selector line, not a shortened fake, and
103
+ nothing is hidden off the right edge. */
104
+ .diff { font-size:11.5px; line-height:1.8; }
105
+ .diff div { white-space:pre-wrap; word-break:break-word; }
106
+ /* Keep the − / + markers: colour alone is not a signal everyone gets. */
107
+ .diff .del, .diff .add { padding-left:18px; text-indent:-18px; }
108
+ .copyline { flex-direction:column; }
109
+ .copybtn { padding:12px 20px; }
110
+ }
56
111
  .foot { margin-top:80px; padding-top:22px; border-top:1px solid var(--rule); color:var(--muted); font-size:13.5px; display:flex; gap:18px; flex-wrap:wrap; }
57
112
  .foot a { color:inherit; }
58
113
  </style>
@@ -66,24 +121,62 @@
66
121
  </div>
67
122
  <main>
68
123
  <div class="hero">
69
- <p class="kicker">Product demos, as code</p>
70
- <h1>Rerun the demo.<br><em>Don't re-record it.</em></h1>
71
- <p class="lede">Retake turns a plain-English walkthrough into a repeatable video of your real app. Your coding agent writes the flow. Retake checks it, drives the browser, records in 1080p, and lets you rerun the same demo when the UI changes.</p>
124
+ <p class="kicker">your product changed. your demo is outdated.</p>
125
+ <h1>Demos that<br><em>re-record themselves.</em></h1>
126
+ <p class="lede">A demo is a small file your coding agent writes. When the UI changes, change one line and run it again — the client walkthrough on Tuesday and the launch video on Friday, from the same current take.</p>
72
127
  <div class="actions">
73
- <a class="btn dark" href="#start">Paste the prompt</a>
74
- <a class="btn light" href="#proof">Watch a demo</a>
128
+ <a class="btn dark" href="#start">Copy the setup prompt</a>
129
+ <a class="btn light" href="#proof">Watch the 14-second demo</a>
75
130
  </div>
131
+ <p class="flow">one prompt <span>→</span> a YAML demo <span>→</span> dry-run checked <span>→</span> recorded <span>→</span> rerun on the next release</p>
132
+ <p class="terms">MIT licensed · runs on your machine · no account, nothing uploaded</p>
76
133
  </div>
77
134
 
78
- <div class="say"><span class="who">You, to the agent you already use</span><i>“Record a demo of my app at localhost:3000 showing the sign-up flow.”</i></div>
79
- <p class="arrow">↓</p>
135
+ <div class="say"><span class="who">The release renamed a button. This is the whole fix</span>
136
+ <div class="diff" style="border:0;padding:6px 0 0;background:transparent">
137
+ <div class="ctx">demos/checkout.yaml</div>
138
+ <div class="del">{ action: click, selector: "button:has-text('Buy now')" }</div>
139
+ <div class="add">{ action: click, selector: "button:has-text('Complete purchase')" }</div>
140
+ </div>
141
+ </div>
142
+ <p class="arrow" style="font-size:13.5px">one line changed · <code>retake run</code> · a new take of the same demo ↓</p>
80
143
  <div class="videos" id="proof" style="scroll-margin-top:20px"></div>
81
144
 
145
+ <section>
146
+ <h2>Built against three real workflows</h2>
147
+ <p class="sub" style="max-width:640px">Retake was built inside these, not for a landing page. All three are mine — there are no customer quotes here yet, and this page is not going to pretend otherwise.</p>
148
+ <div class="cases">
149
+ <div class="case">
150
+ <div class="wordmark"><span class="serif">capture<span style="color:#2F5D3F">.</span></span></div>
151
+ <p class="quote">A five-video launch set, drafted, corrected and re-cut in one afternoon. Re-doing a take stopped being a decision worth having.</p>
152
+ <p class="meta">Thought-sorting app · <a href="https://capture-playground.vercel.app">capture-playground.vercel.app</a></p>
153
+ <p class="watch"><span class="k">WATCH</span><span>Three of those videos are live on <a href="https://capture-playground.vercel.app">Capture's own page</a>, which credits Retake by name.</span></p>
154
+ </div>
155
+ <div class="case">
156
+ <div class="wordmark"><img src="logos/avexjets.png" alt="AvexJets" class="caselogo wide"></div>
157
+ <p class="quote">One repeatable flow across three portals, replacing a twenty-minute live walkthrough. The proof log catches a broken step before a client ever sees it.</p>
158
+ <p class="meta">Private aviation charter · <a href="https://www.avex-jets.com">avex-jets.com</a></p>
159
+ <p class="watch"><span class="k">SITE</span><span>The walkthrough itself is client-facing; <a href="https://www.avex-jets.com">the product it records is public</a>.</span></p>
160
+ </div>
161
+ <div class="case">
162
+ <div class="wordmark tbmark"><span style="color:#193b92">Tech</span><span style="color:#2C7A7B">Bash</span></div>
163
+ <p class="quote">An agent operates the real Scratch editor, dragging the actual blocks. When a lesson changes, the same file produces the next walkthrough.</p>
164
+ <p class="meta">Coding school for young learners · <a href="https://bash.techtutor.academy">bash.techtutor.academy</a></p>
165
+ <p class="watch"><span class="k">WATCH</span><span><a href="media/scratch-mini-project.mp4">The take, 19 seconds</a> — four blocks dragged into one script, and the cat runs it.</span></p>
166
+ </div>
167
+ </div>
168
+ </section>
169
+
82
170
  <section id="start">
83
- <h2>Paste this to your agent</h2>
84
- <p class="sub">Ask from the coding agent you already use. It installs Retake, registers the tools, and produces the first take. Or do the two commands yourself and just ask for the demo.</p>
85
- <div class="say" style="margin-top:0"><span class="who">In Claude Code, Codex, Hermes, or another coding agent</span><i>“Install retake-demos from npm (npm i -g retake-demos, then retake install in a new folder), then record a demo of my app at localhost:3000 showing the checkout flow.”</i></div>
86
- <p class="sub" style="margin:14px 0 0">After setup, each demo starts as a sentence and ends as files you can inspect, rerender, or rerun.</p>
171
+ <h2>Give it to your agent</h2>
172
+ <p class="sub">The link is the instruction. The README tells your agent how to install, wire itself up, and open the window for you. You say what to show.</p>
173
+ <div class="copyline">
174
+ <div class="say"><span class="who">In Claude Code, Codex, Cursor — the agent you already use</span><i id="pasteline">“github.com/glebbogachev00/retake — set this up, then record a demo of my app at localhost:3000 showing the checkout flow.”</i></div>
175
+ <button class="copybtn" data-copy="pasteline">Copy</button>
176
+ </div>
177
+ <p class="sub" style="margin:22px 0 10px">Doing it yourself is two commands:</p>
178
+ <pre>npm install -g retake-demos <span class="c"># Node 20+; ffmpeg bundled, Chromium downloads once</span>
179
+ mkdir demos && cd demos && retake install</pre>
87
180
  </section>
88
181
 
89
182
  <section id="how">
@@ -97,46 +190,44 @@
97
190
  </div>
98
191
  </section>
99
192
 
100
- <section>
101
- <h2>What people record</h2>
102
- <p class="sub">One engine, different audiences. Because the demo is a file, each version is a rerun, not a fresh recording session.</p>
103
- <div class="grid">
104
- <div class="cell"><b>Client walkthroughs</b><p>Show the flow they asked about without booking a call. When the question changes, adjust the file and send a fresh take.</p></div>
105
- <div class="cell"><b>Launches and social</b><p>Render the same interaction as landscape, square, or vertical. Ship the feature, then post the 20 seconds that show it.</p></div>
106
- <div class="cell"><b>Docs and onboarding</b><p>README GIFs, first-run walkthroughs, and help-page clips can be regenerated when the UI changes.</p></div>
107
- <div class="cell"><b>Regression proof</b><p>The dry run walks every step in seconds. A demo that fails when your app breaks is both a visible repro and a useful check.</p></div>
108
- </div>
109
- </section>
110
-
111
193
  <section>
112
194
  <h2>What's in the box</h2>
113
195
  <p class="sub">Everything required to produce the same clean take again.</p>
114
196
  <div class="grid">
115
- <div class="cell"><b>Scenes with captions</b><p>Captions are timed from the actual run and placed outside the product frame.</p></div>
197
+ <div class="cell"><b>Scenes with captions</b><p>Captions timed from the actual run, placed under the app — never over it. Every scene also yields a crisp still.</p></div>
116
198
  <div class="cell"><b>Render-time camera</b><p>Zoom and framing applied after recording. Change your mind in seconds, no browser involved.</p></div>
117
- <div class="cell"><b>Presets that post well</b><p>1080p landscape, square, vertical, docs GIF. Pick the publishing format, not the browser window.</p></div>
118
- <div class="cell"><b>A still for every scene</b><p>Each scene yields a crisp PNG. A video usually ships with a picture next to it.</p></div>
119
- <div class="cell"><b>Secrets stay secret</b><p>Credentials live in .env, redacted from logs, warned about if they'd appear on camera.</p></div>
120
- <div class="cell"><b>Stubs and seeds</b><p>Stub the backend and reset state so demos stay repeatable.</p></div>
199
+ <div class="cell"><b>Presets that post well</b><p>Landscape, a true square, a true 9:16, docs GIF — the same take, re-rendered. Stubs and seeds keep it repeatable.</p></div>
200
+ <div class="cell"><b>Behind the login, off camera</b><p>The agent asks for a demo account by name; you type it into the window and it stays in one local file. The sign-in runs before recording and is trimmed off. 2FA included.</p></div>
201
+ <div class="cell"><b>Plain, or produced</b><p><code>mode: demo</code> keeps everything but your product out of the frame. <code>mode: launch</code> adds a title card, cuts the waiting, and lets you pick the poster. Demo is the default; your agent never upgrades one on its own.</p></div>
121
202
  </div>
122
203
  </section>
123
204
 
205
+ <section>
206
+ <h2>Not a recorder, not a test runner</h2>
207
+ <p class="sub" style="max-width:640px">A screen recording is pixels you can never edit; a browser test is a pass/fail nobody can watch. A Retake demo is both halves: a readable file that proves itself like a test and renders like a film — and reruns when the app changes. And it refuses to become a video editor: if a take needs editing, the file was wrong. Fix the file, run it again.</p>
208
+ </section>
124
209
 
210
+ <section>
211
+ <h2>Record your first rerunnable demo</h2>
212
+ <p class="sub" style="max-width:620px">The demo outlives the video. When the next release moves a button, the fix is a line in a file — and landscape, square, vertical and the GIF all re-render from the same take.</p>
213
+ <div class="copyline">
214
+ <div class="say"><span class="who">Start with the sentence</span><i id="pasteline2">“github.com/glebbogachev00/retake — set this up, then record a demo of my app at localhost:3000 showing the checkout flow.”</i></div>
215
+ <button class="copybtn" data-copy="pasteline2">Copy</button>
216
+ </div>
217
+ </section>
125
218
 
126
219
  <div class="foot">
127
220
  <span>MIT licensed.</span>
128
221
  <a href="https://github.com/glebbogachev00/retake">Source</a>
129
222
  <span>Built on Playwright, testreel, and ffmpeg.</span>
130
- <a href="https://github.com/glebbogachev00/retake/issues/new?template=gremlin.md">Report a bug</a>
223
+ <a href="https://github.com/glebbogachev00/retake/issues/new?template=bug.md">Report a bug</a>
131
224
  </div>
132
225
  </main>
133
226
  <script>
134
227
  // The proof section fills itself from whatever clean renders exist in
135
228
  // site/media/. Committed pages carry no product footage; media is local.
136
229
  const CANDIDATES = [
137
- { file: 'todo-basics', cap: '<b>Written by an agent, performed by a browser.</b> Real typing, real clicks, captions timed from the actual run — 22 seconds, every step passed.' },
138
- { file: 'store-checkout', cap: '<b>The login is handled properly.</b> Credentials from .env, marked secret, signed in before the camera rolls — then a checkout, end to end.' },
139
- { file: 'distill-messy', cap: '<b>A conversation, recorded end to end.</b> Drafted, dry-run, and performed by an agent against a live model — 33 seconds.' },
230
+ { file: 'capture-two-places', cap: '<b>Capture — the launch pitch.</b> One messy sentence lands as an action and a thread. Drafted, dry-run, recorded, titled and posterised from one YAML file. 14 seconds.' },
140
231
  ];
141
232
  (async () => {
142
233
  const host = document.getElementById('proof');
@@ -151,8 +242,19 @@ const CANDIDATES = [
151
242
  f.innerHTML = '<video src="' + url + '" poster="' + url.replace('.mp4', '.jpg') + '" controls muted playsinline preload="metadata"></video><figcaption>' + c.cap + '</figcaption>';
152
243
  host.appendChild(f);
153
244
  }
245
+ if (host.children.length) {
246
+ const more = document.createElement('p');
247
+ more.className = 'sub'; more.style.cssText = 'margin:14px 0 0;font-size:13.5px';
248
+ const base = location.pathname.startsWith('/landing') ? '/landing/media/' : 'media/';
249
+ more.innerHTML = 'Two more launch cuts: <a style="color:inherit" href="' + base + 'scratch-mini-project.mp4">a first Scratch project for TechBash</a> · <a style="color:inherit" href="' + base + 'mark-done.mp4">Capture closing a loop</a>.';
250
+ host.after(more);
251
+ }
154
252
  if (!host.children.length) host.innerHTML = '<div class="say" style="margin-top:0"><span class="who">Proof pending</span>Record a demo and drop its render into <code>site/media/</code> — this section fills itself.</div>';
155
253
  })();
254
+ for (const b of document.querySelectorAll('.copybtn')) b.onclick = async () => {
255
+ const t = document.getElementById(b.dataset.copy).textContent.replace(/^“|”$/g, '');
256
+ try { await navigator.clipboard.writeText(t); b.textContent = 'Copied'; setTimeout(() => (b.textContent = 'Copy'), 1600); } catch {}
257
+ };
156
258
  </script>
157
259
  </body>
158
260
  </html>
Binary file
package/skill/SKILL.md CHANGED
@@ -77,23 +77,141 @@ zooms or caption tone until they have seen one. Two moves instead:
77
77
  → landscape, still camera, a hold on the payoff. Skip the question if
78
78
  the project's style note already answers it.
79
79
  2. **The first take is the menu.** Record the calm default as a preview,
80
- let them watch it, then offer two or three concrete alternatives as
81
- sentences ("Square for a post? Captions off? Zoom on the result?").
82
- Whatever they choose, save it with `style` so it never comes up again.
80
+ let them watch it, then one line — no list, no questions before it:
81
+ "Want it different? A zoom on the result, cursor off, square for a post —
82
+ say so and I'll re-render." Whatever they choose, save it with `style`
83
+ so it never comes up again in this project. Never ask about zoom or
84
+ cursor before they have seen a take.
85
+
86
+ **Which kind of take is this? Get it from the destination, never from the
87
+ subject.** Write it into the manifest as `mode:`, and default to `demo`.
88
+
89
+ - **`mode: demo`** — it proves how the thing works. A client walkthrough IS
90
+ a demo. So is a PR review, a bug repro, a docs clip, a lesson, "show the
91
+ team", "send it to the customer", "so they can see the flow". Nothing in
92
+ the frame but the product doing the thing. No title card, no music, no
93
+ callouts. Most takes are this.
94
+ - **`mode: launch`** — it presents the product to strangers, in public: a
95
+ launch, a post, an ad, a landing page, Product Hunt, "for X", a trailer.
96
+ Only then are a title card, music, emphasis and a branded ending on the
97
+ table, and each still has to earn its second.
98
+
99
+ The tell is the audience, not the polish: *someone who already wants to
100
+ know how it works* → demo. *Someone who has to be given a reason to care*
101
+ → launch. If the person has not said, ask the one destination question and
102
+ take `demo` if they shrug. **Never upgrade a demo to a launch on your own**
103
+ — `validate` warns when a demo carries launch furniture, and that warning
104
+ means you guessed.
105
+
106
+ A launch cut ends with THEIR product, never with Retake. Retake branding
107
+ belongs only in a video that presents Retake.
108
+
109
+ **Two kinds of take, one file — and the destination decides.** The default
110
+ is the plain cut: still camera, cursor, captions, nothing else. That is
111
+ correct for PRs, clients, docs, lessons — most takes. When the person's
112
+ answer to "where is this going?" is a launch, a post, or anything public
113
+ and promotional, offer the produced cut in ONE sentence: "Want the launch
114
+ treatment — title card, a callout on the payoff, the waits compressed,
115
+ the captions read aloud, square or vertical for the feed, and music if
116
+ you give me a track?" Never apply it unasked, and
117
+ never ask about its parts one by one.
118
+
119
+ The produced cut, when they say yes (all render-time except typing):
120
+ - `intro: { title, subtitle }` and usually `outro:` (the product name or URL)
121
+ - one `callout` on the payoff moment, not more
122
+ - the destination's shape: `post-square` or `post-vertical`
123
+ - `compressIdle: true` — the app's waits shown as ~1.5s each; the person's
124
+ pacing `wait`s are never touched
125
+ - **voiceover: leave it off.** It needs `script:` (one continuous script,
126
+ synthesized as one performance) AND `approved: true`, which only a person
127
+ who has HEARD it can set. Reading the captions line by line resets prosody
128
+ at every scene — that is a defect in the shape, not the voice. A silent cut
129
+ beats an unapproved synthetic voice. Offer narration only if they ask for
130
+ it, and let them listen before it ships.
131
+ - `typing: brisk` — fast keys, the pauses carry the meaning (re-record)
132
+ - `music: <file>` only if they hand you a file or point at one on disk.
133
+ Retake bundles no tracks (licensing) — say: "give me an mp3 you have the
134
+ rights to; CC0 tracks live at freepd.com or pixabay.com/music". Skip
135
+ music without complaint if they shrug.
83
136
 
84
137
  Anything they would tell a videographer maps to a knob, in plain English:
85
138
 
139
+ - "add a title card" / "give it a cover" → `intro: { title, subtitle }`
140
+ (and `outro:`) — render-time, and it also writes two poster candidates:
141
+ cover.png (the card) and cover-titled.png (a real frame with the title on
142
+ it, which is what stops a scroll)
143
+ - "the thumbnail is wrong" → they pick the poster themselves in the window
144
+ (any frame, any still, a cover, or their own image). Point them at it
145
+ rather than re-rendering; `outputs.thumbnail: { scene }` only sets the
146
+ starting guess.
147
+ - "point at it" / "highlight the button" → a `callout` step with a label —
148
+ at most one per scene, and only where the caption alone is not enough
149
+
86
150
  - "no zooms" / "keep the camera still" → `camera: static` on scenes
87
151
  - "hide the cursor" → `cursor: false`
88
152
  - "no captions" / "shorter captions" → captions off, or rewrite them
89
153
  - "slower" / "hold the ending" → `pauseAfter`, scene `holdMs`, end waits
90
154
  - "square" / "vertical" / "for X" → preset `post-square` / `post-vertical`
91
155
 
92
- The default is already calm: still camera, cursor shown, plain captions.
156
+ The default is calm and real: still camera (`camera: static` is the
157
+ manifest default — write `auto` only when asked), cursor shown, plain
158
+ captions.
93
159
  The first time the person expresses taste, save it with the `style` tool
94
160
  (or write `demos/style.md`) — every later draft in the project reads it, so
95
161
  they never have to say it twice. Their stated taste always beats defaults.
96
162
 
163
+ ## The person may never have seen Retake — walk them
164
+
165
+ Assume a first-timer: your agent installed Retake for them and they know
166
+ nothing about a window, a workspace or a `.env`. So, every time:
167
+
168
+ - **Say where to look, with the link.** At the start of a demo: "I'll record
169
+ this with Retake. You can watch at http://localhost:4310 (run `retake ui`
170
+ if it isn't open) — the video, stills and a step-by-step log land there."
171
+ Never assume the window is open or that they know the address.
172
+ - **One thing at a time, in their words.** When you need them to act (type a
173
+ demo account, log in by hand, start their app), say exactly what to do,
174
+ where, and how you'll know it's done: "…press Save to .env, then tell me
175
+ it's done." Then wait for them to say so. Do not go quiet — a tool that
176
+ waits on them is your cue to speak, not to sit.
177
+ - **Close the loop.** When the take is done: the one-line result, where the
178
+ file is, and the one thing they can change without a re-record (speed,
179
+ captions, format). Offer, don't pile on.
180
+
181
+ ## Logins — most real demos start behind one
182
+
183
+ You will never see a password, and you never need to. The whole method:
184
+
185
+ 1. **Notice it early.** `read_project` reports sign-in fields; `scout` shows a
186
+ password input; or the person says so. Do not draft a manifest that
187
+ pretends the app is open.
188
+ 2. **Call `secrets` with the NAMES** you will use — `APP_USER`, `APP_PASSWORD`,
189
+ and `APP_TOTP_SECRET` if the site asks for an authenticator code — and one
190
+ sentence of why. It returns immediately with the words to say: a form is
191
+ open in the window at a link, or a one-line terminal command. **Relay
192
+ those words in full, link included**, then stop and wait for "done". Call
193
+ `secrets` again; it answers "set" and you carry on. The values went
194
+ straight into the workspace `.env` — you never saw them.
195
+ 3. **Write the sign-in under `auth.setup`**, referencing `${APP_USER}` and
196
+ `${APP_PASSWORD}` with `secret: true`, and set `auth.storageState` to
197
+ `.auth/<name>.json`. The login runs before the camera and is trimmed off;
198
+ the session is saved and later takes skip the login entirely. A failed
199
+ sign-in is never saved as a session, so a wrong password stops the take
200
+ instead of recording a logged-out one.
201
+ 4. **Authenticator codes:** `text: "${TOTP:APP_TOTP_SECRET}"` — Retake
202
+ computes the six-digit code at the moment it fills the field.
203
+ 5. **SMS codes, SSO, captchas, anything only a human can do:** do not try.
204
+ Tell the person: "run `retake signin demos/<name>.yaml` and log in once by
205
+ hand — Retake keeps the session." Then run as normal. The manifest still
206
+ needs `auth.storageState`; `auth.setup` can be empty.
207
+ 6. **Firebase-style apps** keep the session in IndexedDB, which a saved
208
+ session cannot restore: put the login under plain `setup` instead, so it
209
+ runs every take (still before the camera, still trimmed).
210
+
211
+ Never ask for a credential in chat. Never write a literal one into a
212
+ manifest. Never invent one. Tell the person to use a demo account — the
213
+ output is a video they may publish.
214
+
97
215
  ## When a take fails
98
216
 
99
217
  The camera stops at the first failed step, so a failed take is short and
@@ -136,8 +254,8 @@ up to the end of that scene.
136
254
 
137
255
  - Real interactions read as real: type actual text, scroll to what you use,
138
256
  hold on the result. No zoom tricks unless asked.
139
- - Secrets never go in a manifest. `${ENV_VAR}` + `secret: true`, and tell
140
- the person which variable to set.
257
+ - Secrets never go in a manifest and never into chat: the `secrets` tool
258
+ gets them into `.env` by name; you reference `${NAME}` with `secret: true`.
141
259
  - Never invent selectors — only what scout/read_project returned.
142
260
  - If the backend is down, prefer a `stub:` block over giving up.
143
261
  - Starting the person's app needs their explicit say-so, every time.