@t3lnet/sceneforge 1.0.3 → 1.0.5
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 +20 -444
- package/cli/cli.js +80 -0
- package/cli/commands/add-audio-to-steps.js +328 -0
- package/cli/commands/concat-final-videos.js +480 -0
- package/cli/commands/doctor.js +102 -0
- package/cli/commands/generate-voiceover.js +304 -0
- package/cli/commands/pipeline.js +314 -0
- package/cli/commands/record-demo.js +305 -0
- package/cli/commands/setup.js +218 -0
- package/cli/commands/split-video.js +236 -0
- package/cli/utils/args.js +15 -0
- package/cli/utils/media.js +81 -0
- package/cli/utils/paths.js +93 -0
- package/cli/utils/sanitize.js +19 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -1,336 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @t3lnet/sceneforge
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
SceneForge is a small toolkit for running YAML-driven browser demos and generating script/audio metadata. This package is the **runtime library** for programmatic playback and voice/script generation.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
If you want the recorder extension or full CLI pipeline, see the source repo: https://github.com/jhandel/sceneforge
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- Replay YAML with Playwright to produce a video plus script metadata.
|
|
9
|
-
- Split the recording into per-step clips.
|
|
10
|
-
- Generate voiceover audio with ElevenLabs.
|
|
11
|
-
- Add audio to each clip and concatenate into a final demo video.
|
|
12
|
-
|
|
13
|
-
## Architecture
|
|
14
|
-
|
|
15
|
-
### Package Map
|
|
16
|
-
|
|
17
|
-
```mermaid
|
|
18
|
-
flowchart LR
|
|
19
|
-
Extension["@t3lnet/sceneforge-extension"] -->|"records actions"| YAML["YAML demo definition"]
|
|
20
|
-
Shared["@t3lnet/sceneforge-shared"] --> Extension
|
|
21
|
-
Shared --> Playwright["@t3lnet/sceneforge-playwright"]
|
|
22
|
-
Shared --> CLI["@t3lnet/sceneforge-cli"]
|
|
23
|
-
Generation["@t3lnet/sceneforge-generation"] --> CLI
|
|
24
|
-
Shared --> Library["@t3lnet/sceneforge"]
|
|
25
|
-
Generation --> Library
|
|
26
|
-
Playwright --> Library
|
|
27
|
-
Playwright -->|"video + scripts"| Output["output/"]
|
|
28
|
-
CLI --> Output
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### End-to-End Pipeline
|
|
32
|
-
|
|
33
|
-
```mermaid
|
|
34
|
-
flowchart LR
|
|
35
|
-
A["Record in Extension"] --> B["YAML definition"]
|
|
36
|
-
B --> C["sceneforge record (Playwright)"]
|
|
37
|
-
C --> D["output/videos/<demo>.webm"]
|
|
38
|
-
C --> E["output/scripts/<demo>.json/.srt/.md/.voice.json"]
|
|
39
|
-
D --> F["sceneforge split"]
|
|
40
|
-
F --> G["output/videos/<demo>/step_XX_*.mp4"]
|
|
41
|
-
E --> H["sceneforge voiceover (ElevenLabs)"]
|
|
42
|
-
H --> I["output/audio/<demo>/manifest.json + audio files"]
|
|
43
|
-
G --> J["sceneforge add-audio"]
|
|
44
|
-
I --> J
|
|
45
|
-
J --> K["output/videos/<demo>/step_XX_*_with_audio.mp4"]
|
|
46
|
-
K --> L["sceneforge concat"]
|
|
47
|
-
L --> M["output/final/<demo>.mp4"]
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Requirements
|
|
51
|
-
|
|
52
|
-
- Bun (workspace + builds)
|
|
53
|
-
- Node.js 18+ (CLI runtime)
|
|
54
|
-
- FFmpeg (split/add-audio/concat)
|
|
55
|
-
- ElevenLabs API key (voiceover generation)
|
|
56
|
-
|
|
57
|
-
## Install (Library)
|
|
7
|
+
## Install
|
|
58
8
|
|
|
59
9
|
```bash
|
|
60
10
|
npm i -D @t3lnet/sceneforge @playwright/test
|
|
61
11
|
```
|
|
62
12
|
|
|
63
|
-
##
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
cd sceneforge
|
|
67
|
-
bun install
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Voiceover Environment
|
|
71
|
-
|
|
72
|
-
Create a `.env` file in `sceneforge/` (copy from `.env.example`) with your ElevenLabs credentials:
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
cp .env.example .env
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
You can also point the CLI at a specific env file with `--env-file`.
|
|
79
|
-
|
|
80
|
-
## Recording Options
|
|
81
|
-
|
|
82
|
-
### Chrome Extension (authoring YAML)
|
|
83
|
-
|
|
84
|
-
1. Build and load the extension:
|
|
85
|
-
```bash
|
|
86
|
-
bun run build
|
|
87
|
-
bun run chrome # Opens chrome://extensions
|
|
88
|
-
```
|
|
89
|
-
- Enable "Developer mode" (toggle in top-right)
|
|
90
|
-
- Click "Load unpacked"
|
|
91
|
-
- Select the `dist` folder
|
|
92
|
-
2. Navigate to your app and click the extension icon.
|
|
93
|
-
3. Click **Record** to capture interactions.
|
|
94
|
-
4. Edit steps and export as YAML.
|
|
95
|
-
|
|
96
|
-
### Playwright (recording a demo run)
|
|
97
|
-
|
|
98
|
-
The CLI `record` command replays YAML and records video + scripts:
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
bunx @t3lnet/sceneforge-cli record \
|
|
102
|
-
--definition examples/create-dxf-quote.yaml \
|
|
103
|
-
--base-url http://localhost:5173
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Common flags:
|
|
107
|
-
- `--start-path /app/quotes` to open a route before running steps
|
|
108
|
-
- `--storage-state path/to/user.json` to reuse auth
|
|
109
|
-
- `--asset-root path/to/files` for upload resolution
|
|
110
|
-
- `--output-dir output` or `--root /path/to/repo`
|
|
111
|
-
- `--locale en-US` or `DEMO_LOCALE=en-US` to control request locale (default: `en-US`)
|
|
112
|
-
|
|
113
|
-
## YAML Format
|
|
114
|
-
|
|
115
|
-
```yaml
|
|
116
|
-
version: 1
|
|
117
|
-
name: demo-name
|
|
118
|
-
title: "Demo Title"
|
|
119
|
-
description: |
|
|
120
|
-
Optional description
|
|
121
|
-
|
|
122
|
-
# Optional media configuration for final video
|
|
123
|
-
media:
|
|
124
|
-
intro:
|
|
125
|
-
file: "assets/intro.mp4"
|
|
126
|
-
fade: true
|
|
127
|
-
fadeDuration: 0.5
|
|
128
|
-
outro:
|
|
129
|
-
file: "assets/outro.mp4"
|
|
130
|
-
fade: true
|
|
131
|
-
backgroundMusic:
|
|
132
|
-
file: "assets/background-music.mp3"
|
|
133
|
-
volume: 0.15
|
|
134
|
-
loop: true
|
|
135
|
-
fadeIn: 1.5
|
|
136
|
-
fadeOut: 2.0
|
|
137
|
-
startAt:
|
|
138
|
-
type: "afterIntro"
|
|
139
|
-
endAt:
|
|
140
|
-
type: "beforeOutro"
|
|
141
|
-
|
|
142
|
-
steps:
|
|
143
|
-
- id: step-id
|
|
144
|
-
script: "Voiceover text for this step"
|
|
145
|
-
actions:
|
|
146
|
-
- action: click
|
|
147
|
-
target:
|
|
148
|
-
type: selector
|
|
149
|
-
selector: "button:has-text('Save')"
|
|
150
|
-
highlight: true
|
|
151
|
-
- action: wait
|
|
152
|
-
waitFor:
|
|
153
|
-
type: text
|
|
154
|
-
value: "Success"
|
|
155
|
-
timeout: 15000
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Supported `waitFor.type` values:
|
|
159
|
-
- `text`
|
|
160
|
-
- `selector`
|
|
161
|
-
- `navigation`
|
|
162
|
-
- `idle`
|
|
163
|
-
- `selectorHidden`
|
|
164
|
-
- `textHidden`
|
|
165
|
-
|
|
166
|
-
`version` defaults to `1` if omitted, but including it is recommended for forward compatibility.
|
|
167
|
-
|
|
168
|
-
## Secrets in YAML
|
|
169
|
-
|
|
170
|
-
Use `${SECRET:VAR_NAME}` (or `${ENV:VAR_NAME}`) placeholders to avoid committing credentials:
|
|
171
|
-
|
|
172
|
-
```yaml
|
|
173
|
-
actions:
|
|
174
|
-
- action: type
|
|
175
|
-
target:
|
|
176
|
-
type: selector
|
|
177
|
-
selector: "input[name=\"email\"]"
|
|
178
|
-
text: "${SECRET:NANOQUOTE_USER_EMAIL}"
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
The CLI will load `.env` or `.local/.env` automatically (or use `--env-file`) when running `record`, `setup`, or `pipeline`. Missing secrets will error during parsing.
|
|
182
|
-
|
|
183
|
-
## Supported Actions
|
|
184
|
-
|
|
185
|
-
| Action | Parameters | Description |
|
|
186
|
-
|--------|-----------|-------------|
|
|
187
|
-
| `navigate` | `path` | Go to URL (supports `{baseURL}` template) |
|
|
188
|
-
| `click` | `target`, `highlight?` | Click element |
|
|
189
|
-
| `type` | `target`, `text` | Type into input field |
|
|
190
|
-
| `upload` | `file`, `target?` | Upload file (auto-finds file input if no target) |
|
|
191
|
-
| `wait` | `duration` OR `waitFor` | Wait for time or condition |
|
|
192
|
-
| `hover` | `target` | Hover over element |
|
|
193
|
-
| `scroll` | `duration` | Scroll page |
|
|
194
|
-
| `scrollTo` | `target` | Scroll element into view |
|
|
195
|
-
| `drag` | `target`, `drag{deltaX,deltaY,steps}` | Drag element by offset |
|
|
13
|
+
## Programmatic Usage
|
|
196
14
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
The CLI packages the post-processing pipeline for voiceover, video splits, and final concatenation.
|
|
200
|
-
|
|
201
|
-
### Setup/Login (Storage State)
|
|
202
|
-
|
|
203
|
-
Run a setup YAML to log in once and save Playwright storage state for later sessions:
|
|
204
|
-
|
|
205
|
-
```bash
|
|
206
|
-
bunx @t3lnet/sceneforge-cli setup \
|
|
207
|
-
--definition examples/setup-login.yaml \
|
|
208
|
-
--base-url http://localhost:5173 \
|
|
209
|
-
--start-path /app \
|
|
210
|
-
--headed \
|
|
211
|
-
--storage-state output/storage/login.json
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
Then reuse the cached session during recording or pipeline runs:
|
|
215
|
-
|
|
216
|
-
```bash
|
|
217
|
-
bunx @t3lnet/sceneforge-cli record \
|
|
218
|
-
--definition examples/create-dxf-quote.yaml \
|
|
219
|
-
--base-url http://localhost:5173 \
|
|
220
|
-
--storage-state output/storage/login.json
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
```bash
|
|
224
|
-
# Record a demo with Playwright and generate script JSON
|
|
225
|
-
bunx @t3lnet/sceneforge-cli record \
|
|
226
|
-
--definition examples/create-dxf-quote.yaml \
|
|
227
|
-
--base-url http://localhost:5173
|
|
228
|
-
|
|
229
|
-
# Run the full pipeline in one command
|
|
230
|
-
bunx @t3lnet/sceneforge-cli pipeline \
|
|
231
|
-
--definition examples/create-dxf-quote.yaml \
|
|
232
|
-
--base-url http://localhost:5173 \
|
|
233
|
-
--clean
|
|
234
|
-
|
|
235
|
-
# Preview pipeline steps and skip existing artifacts
|
|
236
|
-
bunx @t3lnet/sceneforge-cli pipeline \
|
|
237
|
-
--definition examples/create-dxf-quote.yaml \
|
|
238
|
-
--resume \
|
|
239
|
-
--progress \
|
|
240
|
-
--dry-run
|
|
241
|
-
|
|
242
|
-
# Split, voiceover, add-audio, concat
|
|
243
|
-
# (The sample YAML uses name: "new-demo", so downstream commands use that demo name.)
|
|
244
|
-
bunx @t3lnet/sceneforge-cli split --demo new-demo
|
|
245
|
-
bunx @t3lnet/sceneforge-cli voiceover --demo new-demo
|
|
246
|
-
bunx @t3lnet/sceneforge-cli add-audio --demo new-demo
|
|
247
|
-
bunx @t3lnet/sceneforge-cli concat --demo new-demo
|
|
248
|
-
|
|
249
|
-
# Concat with intro/outro and background music (CLI overrides)
|
|
250
|
-
bunx @t3lnet/sceneforge-cli concat --demo new-demo \
|
|
251
|
-
--intro assets/intro.mp4 \
|
|
252
|
-
--outro assets/outro.mp4 \
|
|
253
|
-
--music assets/background.mp3 \
|
|
254
|
-
--music-volume 0.15 \
|
|
255
|
-
--music-loop
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### Media Options (Intro/Outro/Background Music)
|
|
259
|
-
|
|
260
|
-
You can add intro/outro videos and background music to the final demo either via YAML configuration or CLI flags:
|
|
261
|
-
|
|
262
|
-
**YAML Configuration (recommended for project defaults):**
|
|
263
|
-
```yaml
|
|
264
|
-
media:
|
|
265
|
-
intro:
|
|
266
|
-
file: "assets/intro.mp4" # Prepended to demo
|
|
267
|
-
fade: true # Enable fade transition
|
|
268
|
-
fadeDuration: 0.5 # Fade duration in seconds
|
|
269
|
-
outro:
|
|
270
|
-
file: "assets/outro.mp4" # Appended to demo
|
|
271
|
-
backgroundMusic:
|
|
272
|
-
file: "assets/music.mp3"
|
|
273
|
-
volume: 0.15 # 0.0 to 1.0 (15% is typical for background)
|
|
274
|
-
loop: true # Repeat if shorter than video
|
|
275
|
-
fadeIn: 1.5 # Fade in duration
|
|
276
|
-
fadeOut: 2.0 # Fade out duration
|
|
277
|
-
startAt:
|
|
278
|
-
type: "afterIntro" # Options: beginning, afterIntro, step, time
|
|
279
|
-
endAt:
|
|
280
|
-
type: "beforeOutro" # Options: end, beforeOutro, step, time
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
**CLI Flags (override YAML config):**
|
|
284
|
-
- `--intro <path>` - Intro video to prepend
|
|
285
|
-
- `--outro <path>` - Outro video to append
|
|
286
|
-
- `--music <path>` - Background music file
|
|
287
|
-
- `--music-volume <0-1>` - Music volume (default: 0.15)
|
|
288
|
-
- `--music-loop` - Loop music if shorter than video
|
|
289
|
-
- `--music-fade-in <s>` - Fade in duration (default: 1)
|
|
290
|
-
- `--music-fade-out <s>` - Fade out duration (default: 2)
|
|
291
|
-
|
|
292
|
-
Notes:
|
|
293
|
-
- `split` reads `output/scripts/<demo>.json` and `output/videos/<demo>.webm`.
|
|
294
|
-
- `voiceover` uses `ELEVENLABS_API_KEY` and `ELEVENLABS_VOICE_ID`.
|
|
295
|
-
- `add-audio` pads or extends clips to align audio with video.
|
|
296
|
-
- `concat` re-encodes to avoid audio dropouts at clip boundaries.
|
|
297
|
-
- `pipeline --resume` skips steps with existing artifacts; `--clean` overrides resume.
|
|
298
|
-
- `setup` saves Playwright storage state to reuse login sessions.
|
|
299
|
-
|
|
300
|
-
By default, the CLI writes to `output/` in the project root (or `e2e/output` if it already exists). You can override with `--root` and `--output-dir`.
|
|
301
|
-
|
|
302
|
-
## Output Layout
|
|
303
|
-
|
|
304
|
-
```
|
|
305
|
-
output/
|
|
306
|
-
├── scripts/
|
|
307
|
-
│ ├── <demo>.json
|
|
308
|
-
│ ├── <demo>.srt
|
|
309
|
-
│ ├── <demo>.md
|
|
310
|
-
│ └── <demo>.voice.json
|
|
311
|
-
├── videos/
|
|
312
|
-
│ ├── <demo>.webm
|
|
313
|
-
│ └── <demo>/
|
|
314
|
-
│ ├── step_01_<stepId>.mp4
|
|
315
|
-
│ ├── step_01_<stepId>_with_audio.mp4
|
|
316
|
-
│ └── steps-manifest.json
|
|
317
|
-
├── audio/
|
|
318
|
-
│ └── <demo>/
|
|
319
|
-
│ ├── manifest.json
|
|
320
|
-
│ └── segment_*.mp3
|
|
321
|
-
└── final/
|
|
322
|
-
└── <demo>.mp4
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
## Programmatic Playback
|
|
326
|
-
|
|
327
|
-
Install the single-package API:
|
|
328
|
-
|
|
329
|
-
```bash
|
|
330
|
-
npm i -D @t3lnet/sceneforge @playwright/test
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
```typescript
|
|
15
|
+
```ts
|
|
334
16
|
import { chromium } from "@playwright/test";
|
|
335
17
|
import { runDemoFromFile } from "@t3lnet/sceneforge";
|
|
336
18
|
|
|
@@ -345,136 +27,30 @@ await runDemoFromFile("./examples/create-dxf-quote.yaml", {
|
|
|
345
27
|
});
|
|
346
28
|
```
|
|
347
29
|
|
|
348
|
-
##
|
|
349
|
-
|
|
350
|
-
```
|
|
351
|
-
sceneforge/
|
|
352
|
-
├── packages/
|
|
353
|
-
│ ├── shared/ # Shared types and utilities
|
|
354
|
-
│ │ └── src/
|
|
355
|
-
│ │ ├── types.ts # TypeScript interfaces
|
|
356
|
-
│ │ ├── yaml-parser.ts # YAML parsing/serialization
|
|
357
|
-
│ │ ├── target-resolver.ts # Selector resolution
|
|
358
|
-
│ │ └── action-helpers.ts # Action factory functions
|
|
359
|
-
│ │
|
|
360
|
-
│ ├── playwright/ # Playwright demo runner
|
|
361
|
-
│ │ └── src/
|
|
362
|
-
│ │ ├── demo-runner.ts # Main runner
|
|
363
|
-
│ │ └── cursor-overlay.ts # Visual cursor effects
|
|
364
|
-
│ │
|
|
365
|
-
│ ├── generation/ # Script + audio generation utilities
|
|
366
|
-
│ │ └── src/
|
|
367
|
-
│ │ ├── script-generator.ts
|
|
368
|
-
│ │ └── voice-synthesis.ts
|
|
369
|
-
│ │
|
|
370
|
-
│ ├── sceneforge/ # Public runner + generation API (npm)
|
|
371
|
-
│ │ └── src/
|
|
372
|
-
│ │
|
|
373
|
-
│ ├── cli/ # CLI for generation pipeline
|
|
374
|
-
│ │ └── src/
|
|
375
|
-
│ │ ├── cli.js
|
|
376
|
-
│ │ └── commands/
|
|
377
|
-
│ │
|
|
378
|
-
│ └── extension/ # Chrome extension
|
|
379
|
-
│ ├── manifest.json
|
|
380
|
-
│ └── src/
|
|
381
|
-
│ ├── background/ # Service worker
|
|
382
|
-
│ ├── content/ # Content scripts
|
|
383
|
-
│ ├── sidepanel/ # React UI
|
|
384
|
-
│ └── shared/ # Re-exports from @t3lnet/sceneforge-shared
|
|
385
|
-
│
|
|
386
|
-
├── examples/ # Example demo definitions
|
|
387
|
-
│ ├── create-dxf-quote.yaml
|
|
388
|
-
│ └── setup-login.yaml
|
|
389
|
-
│
|
|
390
|
-
├── package.json # Workspace root
|
|
391
|
-
└── tsconfig.json
|
|
392
|
-
```
|
|
30
|
+
## CLI
|
|
393
31
|
|
|
394
|
-
|
|
32
|
+
The CLI is included in this package. After install you can run `sceneforge` via `npx` or your package manager:
|
|
395
33
|
|
|
396
34
|
```bash
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
# Build all packages
|
|
401
|
-
bun run build
|
|
402
|
-
|
|
403
|
-
# Build just the extension
|
|
404
|
-
bun run build:extension
|
|
405
|
-
|
|
406
|
-
# Type check
|
|
407
|
-
bun run typecheck
|
|
408
|
-
|
|
409
|
-
# Open Chrome extensions page
|
|
410
|
-
bun run chrome
|
|
35
|
+
npx sceneforge record --definition examples/create-dxf-quote.yaml --base-url http://localhost:5173
|
|
36
|
+
npx sceneforge pipeline --definition examples/create-dxf-quote.yaml --base-url http://localhost:5173 --clean
|
|
411
37
|
```
|
|
412
38
|
|
|
413
|
-
##
|
|
39
|
+
## Extension (optional)
|
|
40
|
+
|
|
41
|
+
The Chrome extension for recording demos is part of the source repo under `packages/extension`. Build it with:
|
|
414
42
|
|
|
415
43
|
```bash
|
|
416
|
-
|
|
417
|
-
# example:
|
|
418
|
-
./scripts/release.sh 0.2.0 next
|
|
44
|
+
bun run build:extension
|
|
419
45
|
```
|
|
420
46
|
|
|
421
|
-
|
|
422
|
-
- Requires an npm automation token with publish access to @t3lnet.
|
|
423
|
-
- Builds are manual (no CI/CD publish).
|
|
424
|
-
|
|
425
|
-
## Chrome Extension Features
|
|
426
|
-
|
|
427
|
-
### Recording Mode
|
|
428
|
-
- Click **Record** to capture clicks and form inputs
|
|
429
|
-
- Actions are grouped into steps with editable voiceover scripts
|
|
430
|
-
- Click **Stop** when done
|
|
431
|
-
- Use **Pause** / **Resume** or press `Ctrl+Shift+P` to toggle recording
|
|
432
|
-
|
|
433
|
-
### Element Picker
|
|
434
|
-
- Click **Pick Element** to enter visual selection mode
|
|
435
|
-
- Hover over elements to see selector preview
|
|
436
|
-
- Click to add as a click action
|
|
437
|
-
- Press **Esc** to cancel
|
|
438
|
-
|
|
439
|
-
### Selector Strategies (Configurable)
|
|
440
|
-
Choose which strategies are enabled in the sidepanel:
|
|
441
|
-
1. `data-testid` - Most stable
|
|
442
|
-
2. `aria-label` - Accessible and stable
|
|
443
|
-
3. `role + text` - e.g., `button:has-text("Save")`
|
|
444
|
-
4. `placeholder` - For input fields
|
|
445
|
-
5. `name` - Form controls with name attributes
|
|
446
|
-
6. `id` - Stable IDs only
|
|
447
|
-
7. `css-class` - Semantic class names
|
|
448
|
-
8. `role + class` - Role-scoped class selectors
|
|
449
|
-
9. `title` - Title attribute selectors
|
|
450
|
-
10. `text` - Generic text fallback
|
|
451
|
-
11. `css-path` - CSS path fallback
|
|
452
|
-
|
|
453
|
-
### Suggested Waits
|
|
454
|
-
- The extension detects DOM changes after interactions and suggests waits.
|
|
455
|
-
- Use **Add** to insert a wait action into the current step.
|
|
456
|
-
|
|
457
|
-
### YAML Preview & Export
|
|
458
|
-
- **YAML Preview** tab shows live output
|
|
459
|
-
- **Copy** to clipboard or **Download** as file
|
|
460
|
-
- **Edit YAML** mode for direct editing with validation
|
|
461
|
-
|
|
462
|
-
### Playback
|
|
463
|
-
- **Play Step** to test individual steps
|
|
464
|
-
- **Play All** to run the complete demo
|
|
465
|
-
- Requires content script to be active on page
|
|
466
|
-
|
|
467
|
-
### Diagnostics
|
|
468
|
-
- `sceneforge doctor` checks for ffmpeg/ffprobe and ElevenLabs env setup.
|
|
47
|
+
Then load the `dist/` folder in `chrome://extensions`.
|
|
469
48
|
|
|
470
|
-
##
|
|
49
|
+
## Notes
|
|
471
50
|
|
|
472
|
-
-
|
|
473
|
-
-
|
|
474
|
-
- `Uploads fail`: use `--asset-root` or provide absolute file paths.
|
|
475
|
-
- `Selectors miss portal content`: prefer portal-scoped selectors (the recorder detects Radix/Headless UI portals).
|
|
476
|
-
- `Audio cuts between steps`: re-run `add-audio` (pads silence) and `concat` (re-encodes).
|
|
51
|
+
- Voiceover generation uses ElevenLabs and requires `ELEVENLABS_API_KEY` + `ELEVENLABS_VOICE_ID`.
|
|
52
|
+
- Video pipeline steps require FFmpeg installed locally.
|
|
477
53
|
|
|
478
|
-
##
|
|
54
|
+
## Repository
|
|
479
55
|
|
|
480
|
-
|
|
56
|
+
https://github.com/jhandel/sceneforge
|
package/cli/cli.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runSplitVideoCommand } from "./commands/split-video.js";
|
|
3
|
+
import { runGenerateVoiceoverCommand } from "./commands/generate-voiceover.js";
|
|
4
|
+
import { runAddAudioCommand } from "./commands/add-audio-to-steps.js";
|
|
5
|
+
import { runConcatCommand } from "./commands/concat-final-videos.js";
|
|
6
|
+
import { runRecordDemoCommand } from "./commands/record-demo.js";
|
|
7
|
+
import { runPipelineCommand } from "./commands/pipeline.js";
|
|
8
|
+
import { runDoctorCommand } from "./commands/doctor.js";
|
|
9
|
+
import { runSetupCommand } from "./commands/setup.js";
|
|
10
|
+
|
|
11
|
+
function printHelp() {
|
|
12
|
+
console.log(`
|
|
13
|
+
SceneForge CLI
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
sceneforge <command> [options]
|
|
17
|
+
|
|
18
|
+
Commands:
|
|
19
|
+
record Run a demo definition with Playwright and generate scripts
|
|
20
|
+
setup Run a setup definition and save storage state
|
|
21
|
+
pipeline Run the full pipeline (record → split → voiceover → add-audio → concat)
|
|
22
|
+
split Split recorded demo videos into per-step clips
|
|
23
|
+
voiceover Generate voiceover audio with ElevenLabs
|
|
24
|
+
add-audio Add audio tracks to per-step clips
|
|
25
|
+
concat Concatenate clips into final demo videos
|
|
26
|
+
doctor Run diagnostics for ffmpeg/ffprobe/env
|
|
27
|
+
|
|
28
|
+
Run "sceneforge <command> --help" for command-specific options.
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const [command, ...rest] = process.argv.slice(2);
|
|
33
|
+
|
|
34
|
+
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
35
|
+
printHelp();
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const normalized = command.toLowerCase();
|
|
40
|
+
|
|
41
|
+
switch (normalized) {
|
|
42
|
+
case "record":
|
|
43
|
+
case "run":
|
|
44
|
+
case "generate":
|
|
45
|
+
await runRecordDemoCommand(rest);
|
|
46
|
+
break;
|
|
47
|
+
case "setup":
|
|
48
|
+
case "login":
|
|
49
|
+
await runSetupCommand(rest);
|
|
50
|
+
break;
|
|
51
|
+
case "pipeline":
|
|
52
|
+
case "run-pipeline":
|
|
53
|
+
await runPipelineCommand(rest);
|
|
54
|
+
break;
|
|
55
|
+
case "split":
|
|
56
|
+
case "split-video":
|
|
57
|
+
case "split-video-by-steps":
|
|
58
|
+
await runSplitVideoCommand(rest);
|
|
59
|
+
break;
|
|
60
|
+
case "voiceover":
|
|
61
|
+
case "generate-voiceover":
|
|
62
|
+
await runGenerateVoiceoverCommand(rest);
|
|
63
|
+
break;
|
|
64
|
+
case "add-audio":
|
|
65
|
+
case "add-audio-to-steps":
|
|
66
|
+
await runAddAudioCommand(rest);
|
|
67
|
+
break;
|
|
68
|
+
case "concat":
|
|
69
|
+
case "concat-final-videos":
|
|
70
|
+
await runConcatCommand(rest);
|
|
71
|
+
break;
|
|
72
|
+
case "doctor":
|
|
73
|
+
case "diagnostics":
|
|
74
|
+
await runDoctorCommand(rest);
|
|
75
|
+
break;
|
|
76
|
+
default:
|
|
77
|
+
console.error(`[error] Unknown command: ${command}`);
|
|
78
|
+
printHelp();
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|