agent-screenplay 0.1.0 → 0.1.1
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/README.md +4 -4
- package/assets/editor/index.html +136 -22
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/core.d.ts +2 -0
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/drivers/web.d.ts +12 -0
- package/dist/drivers/web.js +12 -2
- package/dist/drivers/web.js.map +1 -1
- package/dist/editor/server.d.ts +16 -1
- package/dist/editor/server.js +15 -6
- package/dist/editor/server.js.map +1 -1
- package/dist/render/render.d.ts +2 -0
- package/dist/render/render.js +2 -0
- package/dist/render/render.js.map +1 -1
- package/dist/render/style.d.ts +9 -5
- package/dist/render/style.js +48 -15
- package/dist/render/style.js.map +1 -1
- package/dist/scenario/schema.d.ts +3 -1
- package/dist/scenario/schema.js +5 -3
- package/dist/scenario/schema.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -3
- package/schema/scenario.schema.json +3 -3
- package/skills/screenplay/SKILL.md +4 -5
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ Requirements: Node 20+. ffmpeg and the drawing library ship with the package.
|
|
|
34
34
|
```json
|
|
35
35
|
{
|
|
36
36
|
"url": "https://app.example.com/inbox",
|
|
37
|
-
"style": { "background": "
|
|
37
|
+
"style": { "background": "aurora" },
|
|
38
38
|
"steps": [
|
|
39
39
|
{ "title": "Human handoff, in one click", "subtitle": "New in 2.4" },
|
|
40
40
|
{ "click": "text=Camille Laurent", "say": "When the AI agent gets stuck, the conversation lands in your inbox." },
|
|
@@ -51,7 +51,7 @@ Requirements: Node 20+. ffmpeg and the drawing library ship with the package.
|
|
|
51
51
|
- **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
52
|
- **Actions**: `goto`, `click`, `type`, `press`, `hover`, `scroll`, `select`, `wait`, `pause`.
|
|
53
53
|
- **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 (`
|
|
54
|
+
- **Style**: background preset (mesh gradients: `aurora`, `sky`, `dusk`, `ember`, `graphite`, `lagoon`, `lilac`, `peach`, `linen`), your own gradient, a color or an image; padding, corner radius, shadow, cursor style, maximum zoom, browser frame, accent color. See [Style](#style).
|
|
55
55
|
- **Output**: 1920×1080 at 30 fps by default; any size up to 4K, 24 to 60 fps, optional GIF.
|
|
56
56
|
|
|
57
57
|
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 +60,7 @@ The full format is the JSON schema in [`schema/scenario.schema.json`](schema/sce
|
|
|
60
60
|
|
|
61
61
|
## Style
|
|
62
62
|
|
|
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
|
|
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 padding, corners and shadow, then save to **this video** or to **all videos**, and render without filming again.
|
|
64
64
|
|
|
65
65
|

|
|
66
66
|
|
|
@@ -77,7 +77,7 @@ Set the look by eye: `npx agent-screenplay style demo.json` opens an editor in y
|
|
|
77
77
|
}
|
|
78
78
|
```
|
|
79
79
|
|
|
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 }`.
|
|
80
|
+
`background` is a preset name (`aurora` by default; the first presets, `ocean`, `sunset` and so on, still work), a color (`"#0f172a"`), an image path (relative to the file that sets it), or `{ "gradient": [2 to 4 colors], "angle": degrees }`.
|
|
81
81
|
|
|
82
82
|
## Signing in
|
|
83
83
|
|
package/assets/editor/index.html
CHANGED
|
@@ -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;
|
|
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:
|
|
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
|
-
|
|
129
|
-
|
|
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:
|
|
184
|
-
<p class="label"><span>
|
|
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,8 +237,9 @@ 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>
|
|
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>
|
|
@@ -218,7 +256,7 @@ kbd { font: 11px ui-monospace, Menlo, monospace; color: var(--faint); }
|
|
|
218
256
|
</div>
|
|
219
257
|
<div class="dest" id="dest"></div>
|
|
220
258
|
<div class="actions">
|
|
221
|
-
<button class="btn ghost" id="renderBtn"><span class="txt">Render video</span></button>
|
|
259
|
+
<button class="btn ghost" id="renderBtn"><span class="fill" id="renderFill"></span><span class="txt">Render video</span></button>
|
|
222
260
|
<button class="btn primary" id="saveBtn"><span class="txt">Save</span><span class="dirty"></span></button>
|
|
223
261
|
</div>
|
|
224
262
|
</div>
|
|
@@ -274,16 +312,40 @@ function layout() {
|
|
|
274
312
|
|
|
275
313
|
function fitStage() {
|
|
276
314
|
const L = layout(), box = $("fit").getBoundingClientRect();
|
|
277
|
-
const sc = Math.min(box.width / L.OW, box.height / L.OH);
|
|
315
|
+
const sc = Math.min(box.width / L.OW, (box.height - 32) / L.OH); // room for the caption under the frame
|
|
278
316
|
$("frame").style.width = L.OW * sc + "px"; $("frame").style.height = L.OH * sc + "px";
|
|
279
317
|
$("comp").style.width = L.OW + "px"; $("comp").style.height = L.OH + "px";
|
|
280
318
|
$("comp").style.transform = `scale(${sc})`;
|
|
281
319
|
}
|
|
282
320
|
|
|
321
|
+
/* Same drawing as the renderer (src/render/presets.ts, paintMesh): color fields over a base. */
|
|
322
|
+
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)})`; };
|
|
323
|
+
function meshCss(p, w, h) {
|
|
324
|
+
const side = Math.max(w, h);
|
|
325
|
+
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%)`);
|
|
326
|
+
return [...layers.reverse(), p.base].join(", ");
|
|
327
|
+
}
|
|
328
|
+
function presetCss(p, w, h) {
|
|
329
|
+
return p.kind === "mesh" ? meshCss(p, w, h) : `linear-gradient(${p.angle}deg, ${p.stops.join(", ")})`;
|
|
330
|
+
}
|
|
331
|
+
/* The grain tile: the renderer's seeded noise (mulberry32, seed 7), 128×128. */
|
|
332
|
+
const grainUrl = (() => {
|
|
333
|
+
const N = 128, c = document.createElement("canvas"); c.width = c.height = N;
|
|
334
|
+
const ctx = c.getContext("2d"), img = ctx.createImageData(N, N);
|
|
335
|
+
let a = 7;
|
|
336
|
+
for (let i = 0; i < N * N; i++) {
|
|
337
|
+
a = (a + 0x6d2b79f5) >>> 0; let t = a;
|
|
338
|
+
t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
339
|
+
const v = ((t ^ (t >>> 14)) >>> 0) % 256;
|
|
340
|
+
img.data[i * 4] = img.data[i * 4 + 1] = img.data[i * 4 + 2] = v; img.data[i * 4 + 3] = 255;
|
|
341
|
+
}
|
|
342
|
+
ctx.putImageData(img, 0, 0);
|
|
343
|
+
return c.toDataURL();
|
|
344
|
+
})();
|
|
283
345
|
function bgCss(bg) {
|
|
284
346
|
if (typeof bg === "object") return `linear-gradient(${bg.angle ?? 135}deg, ${bg.gradient.join(", ")})`;
|
|
285
347
|
const p = S.presets[bg];
|
|
286
|
-
if (p) return
|
|
348
|
+
if (p) return presetCss(p, S.output.width, S.output.height);
|
|
287
349
|
if (/^#/.test(bg)) return bg;
|
|
288
350
|
if (imageRef) return `url("/api/image?t=${TOKEN}&from=${imageRef.from}&path=${encodeURIComponent(imageRef.path)}")`;
|
|
289
351
|
return "#111";
|
|
@@ -300,7 +362,14 @@ function draw(initial) {
|
|
|
300
362
|
if (initial) { $(back).style.transition = "none"; requestAnimationFrame(() => $(back).style.transition = ""); }
|
|
301
363
|
$(back).style.opacity = 1; $(front).style.opacity = 0; front = back; lastBg = css;
|
|
302
364
|
}
|
|
303
|
-
|
|
365
|
+
const preset = typeof style.background === "string" ? S.presets[style.background] : null;
|
|
366
|
+
// The light from the top left belongs to linear gradients; mesh presets have their grain.
|
|
367
|
+
$("glow").style.opacity = kindOf(style.background) === "gradient" && !(preset && preset.kind === "mesh") ? 1 : 0;
|
|
368
|
+
$("grain").style.backgroundImage = `url(${grainUrl})`;
|
|
369
|
+
// Same tile size as the renderer: 128 px, doubled at 4K.
|
|
370
|
+
const tile = 128 * Math.max(1, Math.round(S.output.height / 1080));
|
|
371
|
+
$("grain").style.backgroundSize = `${tile}px ${tile}px`;
|
|
372
|
+
$("grain").style.opacity = preset && preset.kind === "mesh" ? preset.grain : 0;
|
|
304
373
|
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
374
|
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
375
|
if (!win.dataset.built) {
|
|
@@ -332,7 +401,7 @@ function setKind(k, initial) {
|
|
|
332
401
|
if (initial) return;
|
|
333
402
|
const cur = style.background, curKind = kindOf(cur);
|
|
334
403
|
if (k === curKind) return;
|
|
335
|
-
if (k === "gradient") style.background = lastGradient ||
|
|
404
|
+
if (k === "gradient") style.background = lastGradient || S.defaults.background;
|
|
336
405
|
if (k === "color") style.background = lastColor || "#0f172a";
|
|
337
406
|
if (k === "image") { if (imageRef) style.background = imageRef.path; else { sync(); return; } }
|
|
338
407
|
change();
|
|
@@ -341,10 +410,12 @@ let lastGradient = null, lastColor = null;
|
|
|
341
410
|
|
|
342
411
|
function buildSwatches() {
|
|
343
412
|
const pre = $("presets");
|
|
344
|
-
for (const
|
|
413
|
+
for (const name of S.offered) {
|
|
414
|
+
const p = S.presets[name];
|
|
345
415
|
const b = document.createElement("button");
|
|
346
416
|
b.className = "sw"; b.dataset.v = name; b.title = name; b.setAttribute("aria-label", name);
|
|
347
|
-
|
|
417
|
+
// A swatch is the same mesh, drawn on a square.
|
|
418
|
+
b.style.background = presetCss(p, 60, 60);
|
|
348
419
|
b.onclick = () => { style.background = name; change(); };
|
|
349
420
|
pre.appendChild(b);
|
|
350
421
|
}
|
|
@@ -364,8 +435,8 @@ function buildSwatches() {
|
|
|
364
435
|
function currentStops() {
|
|
365
436
|
const bg = style.background;
|
|
366
437
|
if (typeof bg === "object") return { stops: bg.gradient, angle: bg.angle ?? 135 };
|
|
367
|
-
const p = S.presets[bg] || S.presets.
|
|
368
|
-
return { stops:
|
|
438
|
+
const p = S.presets[bg] || S.presets[S.defaults.background];
|
|
439
|
+
return { stops: p.colors, angle: p.kind === "linear" ? p.angle : 135 };
|
|
369
440
|
}
|
|
370
441
|
function customGradient() {
|
|
371
442
|
if (typeof style.background === "object") return style.background;
|
|
@@ -386,6 +457,14 @@ $("hex").onchange = (e) => { const v = e.target.value.trim(); if (/^#([0-9a-f]{3
|
|
|
386
457
|
$("pad").oninput = (e) => { live(); style.padding = +e.target.value; change(); };
|
|
387
458
|
$("rad").oninput = (e) => { live(); style.radius = +e.target.value; change(); };
|
|
388
459
|
$("shadow").onclick = () => { style.shadow = !style.shadow; change(); };
|
|
460
|
+
$("reset").onclick = () => {
|
|
461
|
+
style = { ...S.defaults };
|
|
462
|
+
setKind(kindOf(style.background), true);
|
|
463
|
+
change();
|
|
464
|
+
const b = $("reset"); b.classList.remove("spun"); void b.offsetWidth; b.classList.add("spun");
|
|
465
|
+
setTimeout(() => b.classList.remove("spun"), 400);
|
|
466
|
+
toast("Back to the defaults · Save to keep them");
|
|
467
|
+
};
|
|
389
468
|
$("bgKind").querySelectorAll("button").forEach((b) => (b.onclick = () => setKind(b.dataset.k)));
|
|
390
469
|
$("target").querySelectorAll("button").forEach((b) => (b.onclick = () => { target = b.dataset.k; setSeg($("target"), target); sync(); }));
|
|
391
470
|
|
|
@@ -449,6 +528,15 @@ function sync() {
|
|
|
449
528
|
$(id + "V").textContent = v + unit;
|
|
450
529
|
}
|
|
451
530
|
$("shadow").setAttribute("aria-checked", String(style.shadow));
|
|
531
|
+
$("reset").disabled = JSON.stringify(style) === JSON.stringify({ ...S.defaults });
|
|
532
|
+
// The recording keeps its shape: when it is not the video's, the background shows on two sides even with no padding.
|
|
533
|
+
const cap = S.capture || { width: 1440, height: 900 }, r = cap.width / cap.height, R = S.output.width / S.output.height;
|
|
534
|
+
const off = Math.abs(r - R) > 0.01;
|
|
535
|
+
$("shapeNote").classList.toggle("on", off && style.padding < 24);
|
|
536
|
+
const shape = Math.abs(R - 16 / 9) < 0.01 ? "16:9" : `${S.output.width}×${S.output.height}`;
|
|
537
|
+
const fit = S.defaultViewport;
|
|
538
|
+
$("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"}.` +
|
|
539
|
+
(S.url ? ` Record with a ${shape} viewport (such as ${fit.width}×${fit.height}, the default) to fill the frame.` : "");
|
|
452
540
|
const file = target === "project" ? S.configFile : S.scenarioFile;
|
|
453
541
|
$("dest").textContent = (target === "project" ? (S.configExists ? "Saves to " : "Creates ") : "Saves to ") + file.split(/[\\/]/).pop();
|
|
454
542
|
$("dest").title = file;
|
|
@@ -496,11 +584,34 @@ $("renderBtn").onclick = async () => {
|
|
|
496
584
|
if (JSON.stringify(style) !== saved && !(await save())) return;
|
|
497
585
|
if (JSON.stringify(style) !== saved) return toast("Save your latest changes first", true);
|
|
498
586
|
btn.disabled = true;
|
|
499
|
-
|
|
500
|
-
const
|
|
501
|
-
|
|
587
|
+
btn.classList.add("busy");
|
|
588
|
+
const fill = $("renderFill");
|
|
589
|
+
fill.style.opacity = 1; fill.style.transform = "scaleX(0)";
|
|
590
|
+
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>";
|
|
591
|
+
await swapText(btn, `${spinner}Rendering <span class="pct" id="pct">0%</span>`);
|
|
592
|
+
// The server reports the editing progress; the button fills up with it.
|
|
593
|
+
const poll = setInterval(async () => {
|
|
594
|
+
try {
|
|
595
|
+
const p = await (await api("/api/progress")).json();
|
|
596
|
+
if (!p.rendering) return;
|
|
597
|
+
fill.style.transform = `scaleX(${p.progress})`;
|
|
598
|
+
if ($("pct")) $("pct").textContent = Math.round(p.progress * 100) + "%";
|
|
599
|
+
} catch {}
|
|
600
|
+
}, 250);
|
|
601
|
+
let r, j;
|
|
602
|
+
try {
|
|
603
|
+
r = await api("/api/render", { method: "POST" });
|
|
604
|
+
j = await r.json();
|
|
605
|
+
} finally {
|
|
606
|
+
clearInterval(poll);
|
|
607
|
+
}
|
|
608
|
+
fill.style.transform = "scaleX(1)";
|
|
609
|
+
await new Promise((res) => setTimeout(res, 250));
|
|
610
|
+
fill.style.opacity = 0;
|
|
502
611
|
btn.disabled = false;
|
|
612
|
+
btn.classList.remove("busy");
|
|
503
613
|
await swapText(btn, "Render video");
|
|
614
|
+
setTimeout(() => (fill.style.transform = "scaleX(0)"), 220);
|
|
504
615
|
if (!r.ok) return toast(j.error || "Render failed", true);
|
|
505
616
|
$("video").src = `/api/video?t=${TOKEN}&v=${j.at}`;
|
|
506
617
|
$("tabVideo").disabled = false;
|
|
@@ -511,6 +622,7 @@ $("renderBtn").onclick = async () => {
|
|
|
511
622
|
|
|
512
623
|
function showVideo(on) {
|
|
513
624
|
$("frame").classList.toggle("showVideo", on);
|
|
625
|
+
document.body.classList.toggle("videoMode", on);
|
|
514
626
|
$("tabPreview").setAttribute("aria-pressed", String(!on));
|
|
515
627
|
$("tabVideo").setAttribute("aria-pressed", String(on));
|
|
516
628
|
if (!on) $("video").pause();
|
|
@@ -518,8 +630,10 @@ function showVideo(on) {
|
|
|
518
630
|
}
|
|
519
631
|
$("tabPreview").onclick = () => showVideo(false);
|
|
520
632
|
$("tabVideo").onclick = () => showVideo(true);
|
|
521
|
-
|
|
522
|
-
|
|
633
|
+
// Changing a setting shows the preview again; using the video's own controls (seek, volume) does not.
|
|
634
|
+
const editsStyle = (e) => e.target instanceof Element && e.target.closest(".side") && !e.target.closest(".foot");
|
|
635
|
+
document.addEventListener("input", (e) => { if (editsStyle(e)) showVideo(false); }, true);
|
|
636
|
+
document.addEventListener("click", (e) => { if (editsStyle(e)) showVideo(false); }, true);
|
|
523
637
|
|
|
524
638
|
let toastT;
|
|
525
639
|
function toast(msg, err) {
|
package/dist/cli.js
CHANGED
|
@@ -64,7 +64,7 @@ program
|
|
|
64
64
|
program
|
|
65
65
|
.command("style")
|
|
66
66
|
.argument("<scenario>", "scenario JSON file")
|
|
67
|
-
.description("Open the style editor in your browser: background,
|
|
67
|
+
.description("Open the style editor in your browser: background, padding, corners and shadow, on a frame of the recording.")
|
|
68
68
|
.option("--port <port>", "port to listen on (default: any free port)")
|
|
69
69
|
.option("--no-open", "print the link instead of opening the browser")
|
|
70
70
|
.action(async (file, o) => {
|
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
|
@@ -55,7 +55,7 @@ export async function render(file, opts = {}) {
|
|
|
55
55
|
const loaded = loadScenario(file);
|
|
56
56
|
const take = readTake(loaded.takeDir);
|
|
57
57
|
const audio = await narration(loaded, opts);
|
|
58
|
-
return renderTake(loaded, take, { audio, gif: opts.gif, log: opts.log });
|
|
58
|
+
return renderTake(loaded, take, { audio, gif: opts.gif, log: opts.log, onProgress: opts.onProgress });
|
|
59
59
|
}
|
|
60
60
|
export async function check(file, opts = {}) {
|
|
61
61
|
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":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAuB,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,MAAM,EAAoB,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAa,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAqB,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,aAAa,EAAa,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|
package/dist/drivers/web.d.ts
CHANGED
|
@@ -22,6 +22,18 @@ export interface CheckReport {
|
|
|
22
22
|
screenshot?: string;
|
|
23
23
|
url?: string;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* The browser size when the scenario sets none: the video's shape with a longer side of 1440
|
|
27
|
+
* (1440x810 for 16:9, 810x1440 for a vertical video), so with no padding the page fills the
|
|
28
|
+
* whole frame.
|
|
29
|
+
*/
|
|
30
|
+
export declare function defaultViewport(output?: {
|
|
31
|
+
width?: number;
|
|
32
|
+
height?: number;
|
|
33
|
+
}): {
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
};
|
|
25
37
|
export declare class StepError extends Error {
|
|
26
38
|
index: number;
|
|
27
39
|
step: string;
|
package/dist/drivers/web.js
CHANGED
|
@@ -4,7 +4,17 @@ import { describeStep, stepKind } from "../scenario/schema.js";
|
|
|
4
4
|
import { parseTarget } from "../scenario/target.js";
|
|
5
5
|
import { launchBrowser } from "./browser.js";
|
|
6
6
|
import { cardDuration, travelDuration, typingDelays } from "./pace.js";
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* The browser size when the scenario sets none: the video's shape with a longer side of 1440
|
|
9
|
+
* (1440x810 for 16:9, 810x1440 for a vertical video), so with no padding the page fills the
|
|
10
|
+
* whole frame.
|
|
11
|
+
*/
|
|
12
|
+
export function defaultViewport(output) {
|
|
13
|
+
const ow = output?.width ?? 1920;
|
|
14
|
+
const oh = output?.height ?? 1080;
|
|
15
|
+
const even = (n) => Math.round(n / 2) * 2;
|
|
16
|
+
return ow >= oh ? { width: 1440, height: even((1440 * oh) / ow) } : { width: even((1440 * ow) / oh), height: 1440 };
|
|
17
|
+
}
|
|
8
18
|
const ACTION_TIMEOUT_MS = 15_000;
|
|
9
19
|
export class StepError extends Error {
|
|
10
20
|
index;
|
|
@@ -21,7 +31,7 @@ export async function runWeb(loaded, opts) {
|
|
|
21
31
|
const { scenario } = loaded;
|
|
22
32
|
const record = opts.mode === "record";
|
|
23
33
|
const log = opts.log ?? (() => { });
|
|
24
|
-
const viewport = scenario.viewport ??
|
|
34
|
+
const viewport = scenario.viewport ?? defaultViewport(scenario.output);
|
|
25
35
|
const scale = record ? (scenario.scale ?? 2) : 1;
|
|
26
36
|
const baseUrl = scenario.url;
|
|
27
37
|
const browser = await launchBrowser();
|