agent-screenplay 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +27 -7
  2. package/assets/editor/index.html +143 -23
  3. package/dist/cli.js +3 -2
  4. package/dist/cli.js.map +1 -1
  5. package/dist/core.d.ts +2 -0
  6. package/dist/core.js +3 -15
  7. package/dist/core.js.map +1 -1
  8. package/dist/direct/plan.d.ts +2 -0
  9. package/dist/direct/plan.js +8 -0
  10. package/dist/direct/plan.js.map +1 -1
  11. package/dist/drivers/browser.d.ts +14 -0
  12. package/dist/drivers/browser.js +39 -1
  13. package/dist/drivers/browser.js.map +1 -1
  14. package/dist/drivers/web.d.ts +17 -0
  15. package/dist/drivers/web.js +80 -8
  16. package/dist/drivers/web.js.map +1 -1
  17. package/dist/editor/server.d.ts +28 -2
  18. package/dist/editor/server.js +39 -7
  19. package/dist/editor/server.js.map +1 -1
  20. package/dist/login.d.ts +9 -1
  21. package/dist/login.js +62 -29
  22. package/dist/login.js.map +1 -1
  23. package/dist/mcp/server.js +13 -3
  24. package/dist/mcp/server.js.map +1 -1
  25. package/dist/render/presets.d.ts +20 -3
  26. package/dist/render/presets.js +30 -3
  27. package/dist/render/presets.js.map +1 -1
  28. package/dist/render/render.d.ts +2 -0
  29. package/dist/render/render.js +5 -0
  30. package/dist/render/render.js.map +1 -1
  31. package/dist/render/style.d.ts +18 -5
  32. package/dist/render/style.js +124 -15
  33. package/dist/render/style.js.map +1 -1
  34. package/dist/scenario/config.d.ts +1 -0
  35. package/dist/scenario/fingerprint.js +2 -2
  36. package/dist/scenario/fingerprint.js.map +1 -1
  37. package/dist/scenario/schema.d.ts +43 -1
  38. package/dist/scenario/schema.js +25 -6
  39. package/dist/scenario/schema.js.map +1 -1
  40. package/dist/scenario/target.js +2 -1
  41. package/dist/scenario/target.js.map +1 -1
  42. package/dist/version.d.ts +1 -1
  43. package/dist/version.js +1 -1
  44. package/dist/voice/audio.d.ts +6 -0
  45. package/dist/voice/audio.js +36 -0
  46. package/dist/voice/audio.js.map +1 -0
  47. package/dist/voice/cartesia.d.ts +5 -0
  48. package/dist/voice/cartesia.js +17 -6
  49. package/dist/voice/cartesia.js.map +1 -1
  50. package/dist/voice/elevenlabs.d.ts +8 -0
  51. package/dist/voice/elevenlabs.js +29 -0
  52. package/dist/voice/elevenlabs.js.map +1 -0
  53. package/dist/voice/narration.d.ts +16 -0
  54. package/dist/voice/narration.js +83 -0
  55. package/dist/voice/narration.js.map +1 -0
  56. package/package.json +7 -3
  57. package/schema/scenario.schema.json +78 -6
  58. package/skills/screenplay/SKILL.md +21 -13
package/README.md CHANGED
@@ -16,7 +16,18 @@ npx agent-screenplay init
16
16
 
17
17
  The npm package is `agent-screenplay`; installed globally (`npm i -g agent-screenplay`), its command is `screenplay`.
18
18
 
19
- `init` downloads Chromium (once), adds the Screenplay MCP server to the agents it finds (Claude Code, Codex, Cursor), and installs the guide that teaches them to make good videos. Then ask your agent for a recording.
19
+ `init` downloads Chromium (once), adds the Screenplay MCP server to the agents it finds (Claude Code, Codex, Cursor), and installs the guide that teaches them to make good videos. Restart your agent after it.
20
+
21
+ ## Use it
22
+
23
+ In **Claude Code**, type `/screenplay` and say what you want:
24
+
25
+ ```
26
+ /screenplay Make a 30-second walkthrough of https://askli.co
27
+ /screenplay Film how to invite a teammate in our app, with a French voice-over
28
+ ```
29
+
30
+ In **Codex, Cursor** or any agent with the MCP server, just ask: *"Make a screen recording of …"*. The agent explores the site, asks whether you want a voice-over, writes the scenario, films it, checks the storyboard and gives you the MP4. To change the look afterwards: `npx agent-screenplay style <scenario.json>`.
20
31
 
21
32
  Without MCP, any agent that can run commands can use the CLI directly:
22
33
 
@@ -34,7 +45,7 @@ Requirements: Node 20+. ffmpeg and the drawing library ship with the package.
34
45
  ```json
35
46
  {
36
47
  "url": "https://app.example.com/inbox",
37
- "style": { "background": "ocean" },
48
+ "style": { "background": "dawn" },
38
49
  "steps": [
39
50
  { "title": "Human handoff, in one click", "subtitle": "New in 2.4" },
40
51
  { "click": "text=Camille Laurent", "say": "When the AI agent gets stuck, the conversation lands in your inbox." },
@@ -51,7 +62,7 @@ Requirements: Node 20+. ffmpeg and the drawing library ship with the package.
51
62
  - **Targets** name elements the way people see them: `text=…`, `role=button[name='…']`, `label=…`, `placeholder=…`, `testid=…`, `css=…` (web only), `at=x,y` (desktop apps without accessible elements only).
52
63
  - **Actions**: `goto`, `click`, `type`, `press`, `hover`, `scroll`, `select`, `wait`, `pause`.
53
64
  - **Direction**: `title` and `end` cards, `callout` (a bubble pointing at an element), `highlight` (spotlight), `zoomTo`, and on any step `say` (caption and voice), `zoom`, `hold`.
54
- - **Style**: background preset (`sunset`, `ocean`, `lavender`, `forest`, `midnight`, `mono`, `paper`), your own gradient, a color or an image; padding, corner radius, shadow, cursor style, maximum zoom, browser frame, accent color. See [Style](#style).
65
+ - **Style**: background preset (soft light gradients: `dawn`, `iris`, `citrus`, `apricot`, `coral`, `blaze`, `cloud`, `glacier`, `tide`, `meadow`), your own gradient, a color or an image; padding, corner radius, shadow, cursor style, maximum zoom, browser frame, accent color. See [Style](#style).
55
66
  - **Output**: 1920×1080 at 30 fps by default; any size up to 4K, 24 to 60 fps, optional GIF.
56
67
 
57
68
  The full format is the JSON schema in [`schema/scenario.schema.json`](schema/scenario.schema.json) (`npx agent-screenplay schema`), and the agent guide in [`skills/screenplay/SKILL.md`](skills/screenplay/SKILL.md). Examples are in [`examples/`](examples).
@@ -60,7 +71,7 @@ The full format is the JSON schema in [`schema/scenario.schema.json`](schema/sce
60
71
 
61
72
  ## Style
62
73
 
63
- Set the look by eye: `npx agent-screenplay style demo.json` opens an editor in your browser, on a frame of the last recording. Pick a gradient (or make your own), a color or an image (drop it on the page), set the margin, corners and shadow, then save to **this video** or to **all videos**, and render without filming again.
74
+ Set the look by eye: `npx agent-screenplay style demo.json` opens an editor in your browser, on a frame of the last recording. Pick a gradient (or make your own), a color or an image (drop it on the page), set the padding, corners and shadow, then save to **this video** or to **all videos**, and render without filming again.
64
75
 
65
76
  ![The style editor](docs/style-editor.png)
66
77
 
@@ -77,11 +88,14 @@ Set the look by eye: `npx agent-screenplay style demo.json` opens an editor in y
77
88
  }
78
89
  ```
79
90
 
80
- `background` is a preset name, a color (`"#0f172a"`), an image path (relative to the file that sets it), or `{ "gradient": [2 to 4 colors], "angle": degrees }`.
91
+ Title and end cards (`title` and `end` steps) are **off by default**: turn them on with `"cards": true`, or with the switch in the editor.
92
+
93
+ `background` is a preset name (`dawn` by default; earlier presets such as `aurora` or `ocean` still work), a color (`"#0f172a"`), an image path (relative to the file that sets it), or `{ "gradient": [2 to 4 colors], "angle": degrees }`.
81
94
 
82
95
  ## Signing in
83
96
 
84
- - `npx agent-screenplay login https://app.example.com` opens a browser; sign in, press Enter, and the session is saved to `.screenplay/auth.json`. Use it with `"auth": { "storageState": ".screenplay/auth.json" }`. Keep that file out of git.
97
+ - `npx agent-screenplay login https://app.example.com` opens a browser window: sign in, then **close the window**. The session is saved to `.screenplay/auth.json` (while you go, and when the window closes; Enter in the terminal works too). Use it with `"auth": { "storageState": ".screenplay/auth.json" }`. Keep that file out of git.
98
+ - **Bot checks** (Cloudflare's "Just a moment…"): Screenplay's browser presents itself as an ordinary Chrome, so the checks that need no person pass by themselves. When a site asks a person to prove they are human, `check` says so: run `login` on that site, pass the check in the window, close it, and set `auth.storageState`. Some sites ask again on every visit; those cannot be filmed automatically.
85
99
  - Agents without a screen can sign in with steps: `{ "type": "${EMAIL}", "into": "label=Email" }`, `{ "type": "${PASSWORD}", "into": "label=Password", "secret": true }`. `${VAR}` is read from the environment; `secret` masks the text in captions and logs.
86
100
 
87
101
  ## Voice-over
@@ -92,7 +106,13 @@ Off by default. Turn it on per scenario:
92
106
  "voice": { "enabled": true, "language": "fr" }
93
107
  ```
94
108
 
95
- Each step's `say` is spoken with [Cartesia](https://cartesia.ai) (set `CARTESIA_API_KEY`), and the video waits for each sentence to finish, so the voice sets the pace. Narration is cached: re-rendering with unchanged text costs nothing. `voice.id` picks another voice, `voice.speed` its pace. Without voice, `say` still shows as a caption.
109
+ Each step's `say` is spoken, and the video waits for each sentence to finish, so the voice sets the pace. Three ways to get the voice:
110
+
111
+ - **[Cartesia](https://cartesia.ai)**: set `CARTESIA_API_KEY`.
112
+ - **[ElevenLabs](https://elevenlabs.io)**: set `ELEVENLABS_API_KEY`. When both keys are set, Cartesia is used unless `"provider": "elevenlabs"`.
113
+ - **Your own audio files**, with no key: give a step `"audio": "voice/02.mp3"` (MP3, WAV, M4A…), for instance lines you generated in ElevenLabs' or Cartesia's web app. The agent can write the narration for you, one line per step, ready to paste there. Adding audio needs no new filming: `render`.
114
+
115
+ Narration is cached: re-rendering with unchanged text costs nothing. `voice.id` picks another voice, `voice.speed` its pace. Without voice, `say` still shows as a caption.
96
116
 
97
117
  ## Desktop apps
98
118
 
@@ -31,6 +31,7 @@ button:focus-visible, input:focus-visible { outline: 2px solid rgba(255,255,255,
31
31
  .frame { position: relative; border-radius: 12px; overflow: hidden; box-shadow: 0 0 0 1px var(--line), 0 30px 80px rgba(0,0,0,.5); }
32
32
  .comp { position: absolute; left: 0; top: 0; transform-origin: 0 0; overflow: hidden; }
33
33
  .bg { position: absolute; inset: 0; background-size: cover; background-position: center; transition: opacity 320ms var(--ease-out); }
34
+ .grain { position: absolute; inset: 0; opacity: 0; background-size: 128px 128px; transition: opacity 320ms var(--ease-out); pointer-events: none; }
34
35
  .glow { position: absolute; inset: 0; background: radial-gradient(circle 1536px at 20% 10%, rgba(255,255,255,.18), rgba(255,255,255,0)); transition: opacity 320ms var(--ease-out); }
35
36
  .win { position: absolute; overflow: hidden; background: #fff;
36
37
  transition: left 280ms var(--ease-out), top 280ms var(--ease-out), width 280ms var(--ease-out), height 280ms var(--ease-out), border-radius 280ms var(--ease-out), box-shadow 280ms var(--ease-out); }
@@ -45,12 +46,27 @@ button:focus-visible, input:focus-visible { outline: 2px solid rgba(255,255,255,
45
46
  .video { position: absolute; inset: 0; width: 100%; height: 100%; background: #000; opacity: 0; transform: scale(.985); pointer-events: none; transition: opacity 240ms var(--ease-out), transform 240ms var(--ease-out); }
46
47
  .showVideo .video { opacity: 1; transform: none; pointer-events: auto; }
47
48
 
48
- .under { display: flex; justify-content: center; gap: 12px; align-items: center; padding: 0 24px 20px; font-size: 12px; color: var(--muted); min-height: 52px; }
49
+ .under { display: flex; justify-content: center; padding: 0 24px 20px; }
50
+ /* The caption describes the preview: right under the frame, and only on the Preview tab. */
51
+ .frameWrap { position: relative; }
52
+ .hint { position: absolute; left: 0; right: 0; top: calc(100% + 12px); text-align: center; font-size: 12px; color: var(--muted); transition: opacity 180ms ease, transform 180ms var(--ease-out); }
53
+ .videoMode .hint { opacity: 0; transform: translateY(-2px); }
49
54
 
50
55
  /* Sidebar */
51
56
  .side { background: var(--panel); border-left: 1px solid var(--line); display: grid; grid-template-rows: 1fr auto; min-height: 0; }
52
57
  .scroll { overflow-y: auto; padding: 20px 20px 8px; }
53
- .section { padding: 4px 0 20px; animation: rise 420ms var(--ease-out) both; }
58
+ .section { padding: 18px 0 20px; border-top: 1px solid var(--line); animation: rise 420ms var(--ease-out) both; }
59
+ .head + .section { border-top: 0; padding-top: 4px; }
60
+ .head { display: flex; align-items: center; justify-content: space-between; margin: -4px 0 12px; font-size: 13px; font-weight: 600; }
61
+ .reset { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 9px; border-radius: 7px; font-size: 12px; font-weight: 500; color: var(--muted); transition: color 160ms ease, background 160ms ease, opacity 160ms ease, transform 160ms var(--ease-out); }
62
+ .reset:hover:not(:disabled) { color: var(--text); background: var(--raised); }
63
+ .reset:active:not(:disabled) { transform: scale(.96); }
64
+ .reset:disabled { opacity: .35; cursor: default; }
65
+ .reset.spun svg { animation: unwind 420ms var(--ease-out); }
66
+ @keyframes unwind { from { transform: rotate(0); } to { transform: rotate(-360deg); } }
67
+ .custom { margin-top: 12px; padding: 12px; border-radius: var(--r); background: var(--bg); border: 1px solid var(--line); }
68
+ .custom .stops { margin-top: 0; }
69
+ .custom .stop { background: var(--panel); }
54
70
  .section:nth-child(2) { animation-delay: 50ms; } .section:nth-child(3) { animation-delay: 100ms; }
55
71
  @keyframes rise { from { opacity: 0; transform: translateY(6px); } }
56
72
  .label { font-size: 12px; font-weight: 500; color: var(--muted); margin: 0 0 10px; display: flex; justify-content: space-between; align-items: baseline; }
@@ -103,6 +119,8 @@ button:focus-visible, input:focus-visible { outline: 2px solid rgba(255,255,255,
103
119
  .range input::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; margin-top: -6px; border-radius: 50%; background: #fff; box-shadow: 0 1px 4px rgba(0,0,0,.5); transition: transform 140ms var(--ease-out); }
104
120
  .range input:active::-webkit-slider-thumb { transform: scale(1.15); }
105
121
  .range input::-moz-range-thumb { width: 16px; height: 16px; border: 0; border-radius: 50%; background: #fff; }
122
+ .note { margin: 8px 0 0; font-size: 12px; line-height: 1.45; color: var(--faint); display: none; }
123
+ .note.on { display: block; animation: paneIn 200ms var(--ease-out); }
106
124
  .row { display: flex; align-items: center; justify-content: space-between; font-size: 13px; }
107
125
  .dial { width: 18px; height: 18px; border-radius: 50%; border: 1px solid var(--line-2); position: relative; }
108
126
  .dial::after { content: ""; position: absolute; left: 50%; top: 2px; width: 1.5px; height: 7px; margin-left: -.75px; border-radius: 1px; background: #fff; transform-origin: 50% 6px; transform: rotate(var(--a)); }
@@ -125,8 +143,15 @@ button:focus-visible, input:focus-visible { outline: 2px solid rgba(255,255,255,
125
143
  .btn.swapping .txt { opacity: 0; filter: blur(3px); }
126
144
  .dirty { width: 6px; height: 6px; border-radius: 50%; background: #0a0a0b; opacity: 0; transform: scale(.5); transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out); }
127
145
  .isDirty .dirty { opacity: .55; transform: none; }
128
- .spin { width: 13px; height: 13px; border-radius: 50%; border: 2px solid currentColor; border-right-color: transparent; animation: spin 600ms linear infinite; }
129
- @keyframes spin { to { transform: rotate(360deg); } }
146
+ /* macOS activity indicator: eight bars, the brightest leading, turning in eight steps. */
147
+ .mspin { position: relative; flex: none; width: 14px; height: 14px; animation: mspin 800ms steps(8) infinite; }
148
+ .mspin i { position: absolute; left: 6.25px; top: 0; width: 1.5px; height: 4.2px; border-radius: 1px; background: currentColor; transform-origin: .75px 7px; }
149
+ @keyframes mspin { to { transform: rotate(360deg); } }
150
+ /* While rendering, the button fills up with the progress and is not dimmed. */
151
+ .btn.busy:disabled { opacity: 1; cursor: progress; }
152
+ .btn .fill { position: absolute; inset: 0; border-radius: inherit; background: rgba(255,255,255,.09); transform-origin: left; transform: scaleX(0); transition: transform 240ms linear, opacity 200ms ease; pointer-events: none; }
153
+ .btn .txt { position: relative; display: inline-flex; align-items: center; gap: 8px; }
154
+ .pct { font-variant-numeric: tabular-nums; }
130
155
  kbd { font: 11px ui-monospace, Menlo, monospace; color: var(--faint); }
131
156
 
132
157
  .toast { position: fixed; left: calc((100% - 320px) / 2); top: 14px; transform: translate(-50%, -8px); opacity: 0; padding: 9px 14px; border-radius: 10px; background: var(--raised); box-shadow: 0 0 0 1px var(--line-2), 0 10px 30px rgba(0,0,0,.5); font-size: 13px; pointer-events: none; transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out); max-width: 60vw; }
@@ -149,23 +174,33 @@ kbd { font: 11px ui-monospace, Menlo, monospace; color: var(--faint); }
149
174
  <div class="main">
150
175
  <div class="top"><b>Screenplay</b><span class="sep">/</span><span id="name"></span></div>
151
176
  <div class="stage"><div class="fit" id="fit">
177
+ <div class="frameWrap">
152
178
  <div class="frame" id="frame">
153
179
  <div class="comp" id="comp">
154
180
  <div class="bg" id="bgA"></div><div class="bg" id="bgB" style="opacity:0"></div>
155
181
  <div class="glow" id="glow"></div>
182
+ <div class="grain" id="grain"></div>
156
183
  <div class="win" id="win"></div>
157
184
  </div>
158
185
  <video class="video" id="video" controls playsinline></video>
159
186
  </div>
187
+ <span class="hint" id="hint"></span>
188
+ </div>
160
189
  </div></div>
161
190
  <div class="under">
162
191
  <div class="tabs"><button id="tabPreview" aria-pressed="true">Preview</button><button id="tabVideo" disabled>Video</button></div>
163
- <span id="hint"></span>
164
192
  </div>
165
193
  </div>
166
194
 
167
195
  <aside class="side">
168
196
  <div class="scroll">
197
+ <div class="head">
198
+ <span>Style</span>
199
+ <button class="reset" id="reset" title="Back to Screenplay's defaults">
200
+ <svg width="13" height="13" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 7a4.5 4.5 0 1 0 1.3-3.2"/><path d="M2.5 1.8v2.4h2.4"/></svg>
201
+ Reset
202
+ </button>
203
+ </div>
169
204
  <div class="section">
170
205
  <p class="label">Background</p>
171
206
  <div class="seg" id="bgKind"><span class="pill"></span>
@@ -173,6 +208,7 @@ kbd { font: 11px ui-monospace, Menlo, monospace; color: var(--faint); }
173
208
  </div>
174
209
  <div class="pane" data-pane="gradient">
175
210
  <div class="swatches" id="presets"></div>
211
+ <div class="custom">
176
212
  <div class="stops">
177
213
  <label class="stop"><i id="s1i"></i><span id="s1t"></span><input type="color" id="s1"></label>
178
214
  <button class="swap" id="swap" title="Swap colors" aria-label="Swap colors">
@@ -180,10 +216,11 @@ kbd { font: 11px ui-monospace, Menlo, monospace; color: var(--faint); }
180
216
  </button>
181
217
  <label class="stop"><i id="s2i"></i><span id="s2t"></span><input type="color" id="s2"></label>
182
218
  </div>
183
- <div style="margin-top:16px">
184
- <p class="label"><span>Direction</span><span style="display:flex;gap:8px;align-items:center"><span class="dial" id="dial"></span><output id="angleV"></output></span></p>
219
+ <div style="margin-top:14px">
220
+ <p class="label"><span>Gradient angle</span><span style="display:flex;gap:8px;align-items:center"><span class="dial" id="dial"></span><output id="angleV"></output></span></p>
185
221
  <div class="range"><input type="range" id="angle" min="0" max="360" step="5"></div>
186
222
  </div>
223
+ </div>
187
224
  </div>
188
225
  <div class="pane" data-pane="color">
189
226
  <div class="swatches" id="colors"></div>
@@ -200,14 +237,16 @@ kbd { font: 11px ui-monospace, Menlo, monospace; color: var(--faint); }
200
237
  </div>
201
238
 
202
239
  <div class="section">
203
- <p class="label"><span>Margin</span><output id="padV"></output></p>
240
+ <p class="label"><span>Padding</span><output id="padV"></output></p>
204
241
  <div class="range"><input type="range" id="pad" min="0" max="240" step="2"></div>
242
+ <p class="note" id="shapeNote"></p>
205
243
  <p class="label" style="margin-top:18px"><span>Corners</span><output id="radV"></output></p>
206
244
  <div class="range"><input type="range" id="rad" min="0" max="48" step="1"></div>
207
245
  </div>
208
246
 
209
247
  <div class="section">
210
248
  <div class="row"><span>Shadow</span><button class="switch" id="shadow" role="switch" aria-label="Shadow"></button></div>
249
+ <div class="row" style="margin-top:16px"><span>Intro &amp; outro cards</span><button class="switch" id="cards" role="switch" aria-label="Intro and outro cards"></button></div>
211
250
  </div>
212
251
  </div>
213
252
 
@@ -218,7 +257,7 @@ kbd { font: 11px ui-monospace, Menlo, monospace; color: var(--faint); }
218
257
  </div>
219
258
  <div class="dest" id="dest"></div>
220
259
  <div class="actions">
221
- <button class="btn ghost" id="renderBtn"><span class="txt">Render video</span></button>
260
+ <button class="btn ghost" id="renderBtn"><span class="fill" id="renderFill"></span><span class="txt">Render video</span></button>
222
261
  <button class="btn primary" id="saveBtn"><span class="txt">Save</span><span class="dirty"></span></button>
223
262
  </div>
224
263
  </div>
@@ -245,7 +284,7 @@ async function load() {
245
284
  S = await (await api("/api/state")).json();
246
285
  $("name").textContent = S.name;
247
286
  const eff = { ...S.defaults, ...S.projectStyle, ...S.scenarioStyle };
248
- style = { background: eff.background, padding: eff.padding, radius: eff.radius, shadow: eff.shadow };
287
+ style = { background: eff.background, padding: eff.padding, radius: eff.radius, shadow: eff.shadow, cards: eff.cards };
249
288
  if (kindOf(style.background) === "image") imageRef = { path: style.background, from: S.scenarioStyle.background !== undefined ? "scenario" : "project" };
250
289
  saved = JSON.stringify(style);
251
290
  // A video that already has its own look keeps it; otherwise the look is for the whole project.
@@ -274,16 +313,42 @@ function layout() {
274
313
 
275
314
  function fitStage() {
276
315
  const L = layout(), box = $("fit").getBoundingClientRect();
277
- const sc = Math.min(box.width / L.OW, box.height / L.OH);
316
+ const sc = Math.min(box.width / L.OW, (box.height - 32) / L.OH); // room for the caption under the frame
278
317
  $("frame").style.width = L.OW * sc + "px"; $("frame").style.height = L.OH * sc + "px";
279
318
  $("comp").style.width = L.OW + "px"; $("comp").style.height = L.OH + "px";
280
319
  $("comp").style.transform = `scale(${sc})`;
281
320
  }
282
321
 
322
+ /* Same drawing as the renderer (src/render/presets.ts, paintMesh): color fields over a base. */
323
+ const rgba = (hex, a) => { const n = parseInt(hex.slice(1), 16); return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${Math.max(0, Math.min(1, a)).toFixed(3)})`; };
324
+ function meshCss(p, w, h) {
325
+ const side = Math.max(w, h);
326
+ const layers = p.blobs.map((b) => `radial-gradient(circle ${b.r * side}px at ${b.x * w}px ${b.y * h}px, ${rgba(b.color, b.alpha)} 0%, ${rgba(b.color, b.alpha * 0.6)} 40%, ${rgba(b.color, 0)} 100%)`);
327
+ return [...layers.reverse(), p.base].join(", ");
328
+ }
329
+ function presetCss(p, w, h, name) {
330
+ // Glow presets are drawn by the renderer on the server: the preview is the video's own image.
331
+ if (p.kind === "glow") return `center / cover no-repeat url("/api/preset?t=${TOKEN}&name=${encodeURIComponent(name)}&w=${Math.round(w)}&h=${Math.round(h)}")`;
332
+ return p.kind === "mesh" ? meshCss(p, w, h) : `linear-gradient(${p.angle}deg, ${p.stops.join(", ")})`;
333
+ }
334
+ /* The grain tile: the renderer's seeded noise (mulberry32, seed 7), 128×128. */
335
+ const grainUrl = (() => {
336
+ const N = 128, c = document.createElement("canvas"); c.width = c.height = N;
337
+ const ctx = c.getContext("2d"), img = ctx.createImageData(N, N);
338
+ let a = 7;
339
+ for (let i = 0; i < N * N; i++) {
340
+ a = (a + 0x6d2b79f5) >>> 0; let t = a;
341
+ t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
342
+ const v = ((t ^ (t >>> 14)) >>> 0) % 256;
343
+ img.data[i * 4] = img.data[i * 4 + 1] = img.data[i * 4 + 2] = v; img.data[i * 4 + 3] = 255;
344
+ }
345
+ ctx.putImageData(img, 0, 0);
346
+ return c.toDataURL();
347
+ })();
283
348
  function bgCss(bg) {
284
349
  if (typeof bg === "object") return `linear-gradient(${bg.angle ?? 135}deg, ${bg.gradient.join(", ")})`;
285
350
  const p = S.presets[bg];
286
- if (p) return `linear-gradient(${p.angle}deg, ${p.stops.join(", ")})`;
351
+ if (p) return presetCss(p, S.output.width, S.output.height, bg);
287
352
  if (/^#/.test(bg)) return bg;
288
353
  if (imageRef) return `url("/api/image?t=${TOKEN}&from=${imageRef.from}&path=${encodeURIComponent(imageRef.path)}")`;
289
354
  return "#111";
@@ -300,7 +365,14 @@ function draw(initial) {
300
365
  if (initial) { $(back).style.transition = "none"; requestAnimationFrame(() => $(back).style.transition = ""); }
301
366
  $(back).style.opacity = 1; $(front).style.opacity = 0; front = back; lastBg = css;
302
367
  }
303
- $("glow").style.opacity = kindOf(style.background) === "gradient" ? 1 : 0;
368
+ const preset = typeof style.background === "string" ? S.presets[style.background] : null;
369
+ // The light from the top left belongs to linear gradients only.
370
+ $("glow").style.opacity = kindOf(style.background) === "gradient" && !(preset && preset.kind !== "linear") ? 1 : 0;
371
+ $("grain").style.backgroundImage = `url(${grainUrl})`;
372
+ // Same tile size as the renderer: 128 px, doubled at 4K.
373
+ const tile = 128 * Math.max(1, Math.round(S.output.height / 1080));
374
+ $("grain").style.backgroundSize = `${tile}px ${tile}px`;
375
+ $("grain").style.opacity = preset && preset.kind === "mesh" ? preset.grain : 0;
304
376
  Object.assign(win.style, { left: L.x + "px", top: L.y + "px", width: L.w + "px", height: L.h + "px", borderRadius: style.radius * L.k + "px",
305
377
  boxShadow: style.shadow ? `0 ${24 * L.k}px ${60 * L.k}px rgba(0,0,0,.35), 0 ${2 * L.k}px ${8 * L.k}px rgba(0,0,0,.18)` : "none" });
306
378
  if (!win.dataset.built) {
@@ -332,7 +404,7 @@ function setKind(k, initial) {
332
404
  if (initial) return;
333
405
  const cur = style.background, curKind = kindOf(cur);
334
406
  if (k === curKind) return;
335
- if (k === "gradient") style.background = lastGradient || "ocean";
407
+ if (k === "gradient") style.background = lastGradient || S.defaults.background;
336
408
  if (k === "color") style.background = lastColor || "#0f172a";
337
409
  if (k === "image") { if (imageRef) style.background = imageRef.path; else { sync(); return; } }
338
410
  change();
@@ -341,10 +413,12 @@ let lastGradient = null, lastColor = null;
341
413
 
342
414
  function buildSwatches() {
343
415
  const pre = $("presets");
344
- for (const [name, p] of Object.entries(S.presets)) {
416
+ for (const name of S.offered) {
417
+ const p = S.presets[name];
345
418
  const b = document.createElement("button");
346
419
  b.className = "sw"; b.dataset.v = name; b.title = name; b.setAttribute("aria-label", name);
347
- b.style.background = `linear-gradient(${p.angle}deg, ${p.stops.join(", ")})`;
420
+ // A swatch is the same mesh, drawn on a square.
421
+ b.style.background = presetCss(p, 120, 120, name);
348
422
  b.onclick = () => { style.background = name; change(); };
349
423
  pre.appendChild(b);
350
424
  }
@@ -364,8 +438,8 @@ function buildSwatches() {
364
438
  function currentStops() {
365
439
  const bg = style.background;
366
440
  if (typeof bg === "object") return { stops: bg.gradient, angle: bg.angle ?? 135 };
367
- const p = S.presets[bg] || S.presets.ocean;
368
- return { stops: [p.stops[0], p.stops[p.stops.length - 1]], angle: p.angle };
441
+ const p = S.presets[bg] || S.presets[S.defaults.background];
442
+ return { stops: p.colors, angle: p.kind === "linear" ? p.angle : p.kind === "glow" ? 90 + p.angle : 135 };
369
443
  }
370
444
  function customGradient() {
371
445
  if (typeof style.background === "object") return style.background;
@@ -386,6 +460,16 @@ $("hex").onchange = (e) => { const v = e.target.value.trim(); if (/^#([0-9a-f]{3
386
460
  $("pad").oninput = (e) => { live(); style.padding = +e.target.value; change(); };
387
461
  $("rad").oninput = (e) => { live(); style.radius = +e.target.value; change(); };
388
462
  $("shadow").onclick = () => { style.shadow = !style.shadow; change(); };
463
+ // Cards are not in the frame preview: the rendered video shows them.
464
+ $("cards").onclick = () => { style.cards = !style.cards; change(); };
465
+ $("reset").onclick = () => {
466
+ style = { ...S.defaults };
467
+ setKind(kindOf(style.background), true);
468
+ change();
469
+ const b = $("reset"); b.classList.remove("spun"); void b.offsetWidth; b.classList.add("spun");
470
+ setTimeout(() => b.classList.remove("spun"), 400);
471
+ toast("Back to the defaults · Save to keep them");
472
+ };
389
473
  $("bgKind").querySelectorAll("button").forEach((b) => (b.onclick = () => setKind(b.dataset.k)));
390
474
  $("target").querySelectorAll("button").forEach((b) => (b.onclick = () => { target = b.dataset.k; setSeg($("target"), target); sync(); }));
391
475
 
@@ -449,6 +533,16 @@ function sync() {
449
533
  $(id + "V").textContent = v + unit;
450
534
  }
451
535
  $("shadow").setAttribute("aria-checked", String(style.shadow));
536
+ $("cards").setAttribute("aria-checked", String(style.cards));
537
+ $("reset").disabled = JSON.stringify(style) === JSON.stringify({ ...S.defaults });
538
+ // The recording keeps its shape: when it is not the video's, the background shows on two sides even with no padding.
539
+ const cap = S.capture || { width: 1440, height: 900 }, r = cap.width / cap.height, R = S.output.width / S.output.height;
540
+ const off = Math.abs(r - R) > 0.01;
541
+ $("shapeNote").classList.toggle("on", off && style.padding < 24);
542
+ const shape = Math.abs(R - 16 / 9) < 0.01 ? "16:9" : `${S.output.width}×${S.output.height}`;
543
+ const fit = S.defaultViewport;
544
+ $("shapeNote").textContent = !off ? "" : `The ${S.url ? "recording" : "window"} is ${cap.width}×${cap.height}, not ${shape} like the video, so the background shows on the ${r < R ? "sides" : "top and bottom"}.` +
545
+ (S.url ? ` Record with a ${shape} viewport (such as ${fit.width}×${fit.height}, the default) to fill the frame.` : "");
452
546
  const file = target === "project" ? S.configFile : S.scenarioFile;
453
547
  $("dest").textContent = (target === "project" ? (S.configExists ? "Saves to " : "Creates ") : "Saves to ") + file.split(/[\\/]/).pop();
454
548
  $("dest").title = file;
@@ -496,11 +590,34 @@ $("renderBtn").onclick = async () => {
496
590
  if (JSON.stringify(style) !== saved && !(await save())) return;
497
591
  if (JSON.stringify(style) !== saved) return toast("Save your latest changes first", true);
498
592
  btn.disabled = true;
499
- await swapText(btn, '<span class="spin"></span>Rendering…');
500
- const r = await api("/api/render", { method: "POST" });
501
- const j = await r.json();
593
+ btn.classList.add("busy");
594
+ const fill = $("renderFill");
595
+ fill.style.opacity = 1; fill.style.transform = "scaleX(0)";
596
+ const spinner = '<span class="mspin">' + [0, 1, 2, 3, 4, 5, 6, 7].map((n) => `<i style="transform:rotate(${n * 45}deg);opacity:${(1 - ((8 - n) % 8) * 0.11).toFixed(2)}"></i>`).join("") + "</span>";
597
+ await swapText(btn, `${spinner}Rendering <span class="pct" id="pct">0%</span>`);
598
+ // The server reports the editing progress; the button fills up with it.
599
+ const poll = setInterval(async () => {
600
+ try {
601
+ const p = await (await api("/api/progress")).json();
602
+ if (!p.rendering) return;
603
+ fill.style.transform = `scaleX(${p.progress})`;
604
+ if ($("pct")) $("pct").textContent = Math.round(p.progress * 100) + "%";
605
+ } catch {}
606
+ }, 250);
607
+ let r, j;
608
+ try {
609
+ r = await api("/api/render", { method: "POST" });
610
+ j = await r.json();
611
+ } finally {
612
+ clearInterval(poll);
613
+ }
614
+ fill.style.transform = "scaleX(1)";
615
+ await new Promise((res) => setTimeout(res, 250));
616
+ fill.style.opacity = 0;
502
617
  btn.disabled = false;
618
+ btn.classList.remove("busy");
503
619
  await swapText(btn, "Render video");
620
+ setTimeout(() => (fill.style.transform = "scaleX(0)"), 220);
504
621
  if (!r.ok) return toast(j.error || "Render failed", true);
505
622
  $("video").src = `/api/video?t=${TOKEN}&v=${j.at}`;
506
623
  $("tabVideo").disabled = false;
@@ -511,6 +628,7 @@ $("renderBtn").onclick = async () => {
511
628
 
512
629
  function showVideo(on) {
513
630
  $("frame").classList.toggle("showVideo", on);
631
+ document.body.classList.toggle("videoMode", on);
514
632
  $("tabPreview").setAttribute("aria-pressed", String(!on));
515
633
  $("tabVideo").setAttribute("aria-pressed", String(on));
516
634
  if (!on) $("video").pause();
@@ -518,8 +636,10 @@ function showVideo(on) {
518
636
  }
519
637
  $("tabPreview").onclick = () => showVideo(false);
520
638
  $("tabVideo").onclick = () => showVideo(true);
521
- document.addEventListener("input", () => showVideo(false), true);
522
- document.addEventListener("click", (e) => { if (e.target.closest(".side") && !e.target.closest(".foot")) showVideo(false); }, true);
639
+ // Changing a setting shows the preview again; using the video's own controls (seek, volume) does not.
640
+ const editsStyle = (e) => e.target instanceof Element && e.target.closest(".side") && !e.target.closest(".foot");
641
+ document.addEventListener("input", (e) => { if (editsStyle(e)) showVideo(false); }, true);
642
+ document.addEventListener("click", (e) => { if (editsStyle(e)) showVideo(false); }, true);
523
643
 
524
644
  let toastT;
525
645
  function toast(msg, err) {
package/dist/cli.js CHANGED
@@ -36,6 +36,7 @@ program
36
36
  log(` storyboard: ${r.storyboard}`);
37
37
  if (r.gif)
38
38
  log(` gif: ${r.gif}`);
39
+ log(`\nAdjust the look (background, padding, cards…): npx agent-screenplay style ${file}`);
39
40
  }
40
41
  }
41
42
  catch (e) {
@@ -64,7 +65,7 @@ program
64
65
  program
65
66
  .command("style")
66
67
  .argument("<scenario>", "scenario JSON file")
67
- .description("Open the style editor in your browser: background, margin, corners and shadow, on a frame of the recording.")
68
+ .description("Open the style editor in your browser: background, padding, corners and shadow, on a frame of the recording.")
68
69
  .option("--port <port>", "port to listen on (default: any free port)")
69
70
  .option("--no-open", "print the link instead of opening the browser")
70
71
  .action(async (file, o) => {
@@ -130,7 +131,7 @@ program
130
131
  .command("login")
131
132
  .argument("<url>", "page to sign in on")
132
133
  .option("-o, --out <file>", "where to save the session", ".screenplay/auth.json")
133
- .description("Open a browser to sign in once; the session is saved for scenarios (auth.storageState).")
134
+ .description("Open a browser to sign in once, or to pass a bot check (Cloudflare) by hand; close the window when done. The session is saved for scenarios (auth.storageState).")
134
135
  .action(async (url, o) => {
135
136
  try {
136
137
  const file = await login(url, o.out, log);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/core.d.ts CHANGED
@@ -5,6 +5,8 @@ export interface RunOptions {
5
5
  noVoice?: boolean;
6
6
  gif?: boolean;
7
7
  log?: (msg: string) => void;
8
+ /** Editing progress, 0 to 1, a few times a second. */
9
+ onProgress?: (fraction: number) => void;
8
10
  }
9
11
  export interface RecordResult extends RenderResult {
10
12
  take: string;
package/dist/core.js CHANGED
@@ -6,21 +6,9 @@ import { readingTime } from "./drivers/pace.js";
6
6
  import { readTake, writeTake } from "./take.js";
7
7
  import { captureInputs } from "./scenario/fingerprint.js";
8
8
  import { renderTake } from "./render/render.js";
9
- import { synthesizeAll } from "./voice/cartesia.js";
9
+ import { narrate } from "./voice/narration.js";
10
10
  async function narration(loaded, opts) {
11
- const voice = loaded.scenario.voice;
12
- if (!voice?.enabled || opts.noVoice)
13
- return new Map();
14
- const texts = new Map();
15
- loaded.scenario.steps.forEach((s, i) => {
16
- const say = s.say;
17
- if (say)
18
- texts.set(i, say);
19
- });
20
- if (texts.size === 0)
21
- return new Map();
22
- opts.log?.(`Generating narration for ${texts.size} step(s)`);
23
- return synthesizeAll(texts, voice, path.join(loaded.workDir, "cache", "voice"));
11
+ return narrate(loaded, { noVoice: opts.noVoice, log: opts.log });
24
12
  }
25
13
  /** How long each step must last so its narration or caption can be heard or read. */
26
14
  function minDurations(loaded, audio) {
@@ -55,7 +43,7 @@ export async function render(file, opts = {}) {
55
43
  const loaded = loadScenario(file);
56
44
  const take = readTake(loaded.takeDir);
57
45
  const audio = await narration(loaded, opts);
58
- return renderTake(loaded, take, { audio, gif: opts.gif, log: opts.log });
46
+ return renderTake(loaded, take, { audio, gif: opts.gif, log: opts.log, onProgress: opts.onProgress });
59
47
  }
60
48
  export async function check(file, opts = {}) {
61
49
  const loaded = loadScenario(file);
package/dist/core.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
@@ -85,6 +85,8 @@ export interface PlanOptions {
85
85
  autoZoom: boolean;
86
86
  maxZoom: number;
87
87
  captions: boolean;
88
+ /** Show the title and end cards (style.cards). When false they are left out of the video. */
89
+ cards?: boolean;
88
90
  }
89
91
  export declare const OVERLAY_DELAY = 0.55;
90
92
  export declare function planTake(take: Take, scenario: Scenario, opts: PlanOptions): Plan;
@@ -48,6 +48,14 @@ export function planTake(take, scenario, opts) {
48
48
  const voice = [];
49
49
  for (const st of steps) {
50
50
  const audio = opts.audio.get(st.index);
51
+ if (st.card && opts.cards === false) {
52
+ // Cards are off: the step takes no time in the video. A video that started with one
53
+ // still drops the still lead-in recorded before it.
54
+ if (pieces.length === 0)
55
+ sp = st.start;
56
+ emitCapture(st.start);
57
+ continue;
58
+ }
51
59
  if (st.card) {
52
60
  // An opening card starts the video: drop the still lead-in recorded before it.
53
61
  if (pieces.length === 0)