demo-dev 0.0.1-alpha.0 → 0.0.2-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -161,6 +161,29 @@ var ZOOM_INJECT_SCRIPT = `
|
|
|
161
161
|
};
|
|
162
162
|
})();
|
|
163
163
|
`;
|
|
164
|
+
var SCENE_TRANSITION_SCRIPT = `
|
|
165
|
+
(() => {
|
|
166
|
+
if (document.getElementById('__scene-fade')) return;
|
|
167
|
+
const overlay = document.createElement('div');
|
|
168
|
+
overlay.id = '__scene-fade';
|
|
169
|
+
Object.assign(overlay.style, {
|
|
170
|
+
position: 'fixed', top: '0', left: '0', width: '100vw', height: '100vh',
|
|
171
|
+
zIndex: '999997', pointerEvents: 'none',
|
|
172
|
+
background: 'white', opacity: '0',
|
|
173
|
+
transition: 'opacity 0.4s ease-in-out',
|
|
174
|
+
});
|
|
175
|
+
document.body.appendChild(overlay);
|
|
176
|
+
window.__fadeOut = () => { overlay.style.opacity = '1'; };
|
|
177
|
+
window.__fadeIn = () => { overlay.style.opacity = '0'; };
|
|
178
|
+
})();
|
|
179
|
+
`;
|
|
180
|
+
var fadeTransition = async (page) => {
|
|
181
|
+
await page.evaluate(SCENE_TRANSITION_SCRIPT).catch(() => void 0);
|
|
182
|
+
await page.evaluate(() => window.__fadeOut?.()).catch(() => void 0);
|
|
183
|
+
await page.waitForTimeout(450);
|
|
184
|
+
await page.evaluate(() => window.__fadeIn?.()).catch(() => void 0);
|
|
185
|
+
await page.waitForTimeout(450);
|
|
186
|
+
};
|
|
164
187
|
var zoomToPoint = async (page, x, y, scale = 1.5) => {
|
|
165
188
|
await page.evaluate(ZOOM_INJECT_SCRIPT).catch(() => void 0);
|
|
166
189
|
await page.evaluate(
|
|
@@ -195,13 +218,13 @@ var CURSOR_OVERLAY_SCRIPT = `
|
|
|
195
218
|
cursor.id = '__ghost-cursor';
|
|
196
219
|
Object.assign(cursor.style, {
|
|
197
220
|
position: 'fixed', zIndex: '999999', pointerEvents: 'none',
|
|
198
|
-
left: '0px', top: '0px', width: '
|
|
199
|
-
transition: 'left 0.
|
|
200
|
-
filter: 'drop-shadow(0 2px 3px rgba(0,0,0,0.4))',
|
|
221
|
+
left: '0px', top: '0px', width: '20px', height: '28px',
|
|
222
|
+
transition: 'left 0.03s linear, top 0.03s linear',
|
|
201
223
|
});
|
|
202
|
-
/*
|
|
203
|
-
cursor.innerHTML = '<svg width="
|
|
204
|
-
'<
|
|
224
|
+
/* Precise macOS default cursor \u2014 traced from Apple's actual cursor spec */
|
|
225
|
+
cursor.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="28" viewBox="0 0 20 28">' +
|
|
226
|
+
'<defs><filter id="cs" x="-20%" y="-20%" width="140%" height="140%"><feDropShadow dx="0.5" dy="1.5" stdDeviation="0.8" flood-opacity="0.45"/></filter></defs>' +
|
|
227
|
+
'<path filter="url(#cs)" d="M2.2 0L2.2 21.4L6.8 16.4L10.6 24.8L13.2 23.6L9.4 15.6L16 15.6Z" fill="white" stroke="black" stroke-width="1.1" stroke-linejoin="round"/>' +
|
|
205
228
|
'</svg>';
|
|
206
229
|
document.body.appendChild(cursor);
|
|
207
230
|
|
|
@@ -263,6 +286,7 @@ var runActionWithCursor = async (page, action, baseUrl, cursor) => {
|
|
|
263
286
|
await page.evaluate(CURSOR_OVERLAY_SCRIPT).catch(() => void 0);
|
|
264
287
|
await page.evaluate(CURSOR_TRACKER_SCRIPT).catch(() => void 0);
|
|
265
288
|
await page.evaluate(ZOOM_INJECT_SCRIPT).catch(() => void 0);
|
|
289
|
+
await page.evaluate(SCENE_TRANSITION_SCRIPT).catch(() => void 0);
|
|
266
290
|
await page.waitForTimeout(humanDelay(400));
|
|
267
291
|
break;
|
|
268
292
|
}
|
|
@@ -497,7 +521,7 @@ var capturePlanContinuous = async (plan, options) => {
|
|
|
497
521
|
endMs: sceneEnd,
|
|
498
522
|
url: page.url()
|
|
499
523
|
});
|
|
500
|
-
await page
|
|
524
|
+
await fadeTransition(page);
|
|
501
525
|
}
|
|
502
526
|
await drainCursorSamples(page, cursorLog, cursorTrackingOffset);
|
|
503
527
|
const totalDurationMs = elapsed();
|
package/package.json
CHANGED
package/skills/demo-dev/SKILL.md
CHANGED
|
@@ -1,153 +1,109 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: demo-dev
|
|
3
|
-
description: Generate
|
|
3
|
+
description: Generate product demo videos for any web app. Give a URL and a prompt, get a narrated Screen Studio-style video. Supports authenticated SaaS, prompt-driven or diff-driven planning, and AI narration.
|
|
4
4
|
allowed-tools: Bash Read Edit Write
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# demo-dev
|
|
8
8
|
|
|
9
|
-
Use this skill when the user wants
|
|
9
|
+
Use this skill when the user wants to generate a demo video of a web app.
|
|
10
10
|
|
|
11
11
|
## What this skill does
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
13
|
+
- Opens a web app in a headless browser
|
|
14
|
+
- AI plans the demo from a natural language prompt (or git diff)
|
|
15
|
+
- Ghost-cursor navigates with human-like mouse movement
|
|
16
|
+
- Records continuously with Playwright screencast + CSS zoom
|
|
17
|
+
- Generates narration per scene with ElevenLabs / OpenAI / local TTS
|
|
18
|
+
- Composes a polished mp4 with FFmpeg (speed ramps, browser frame, audio)
|
|
18
19
|
|
|
19
|
-
##
|
|
20
|
+
## Quick start
|
|
20
21
|
|
|
21
|
-
### 1.
|
|
22
|
+
### 1. Check environment
|
|
22
23
|
|
|
23
24
|
```bash
|
|
24
|
-
demo-dev config
|
|
25
|
-
demo-dev config --field baseUrl
|
|
26
25
|
demo-dev doctor
|
|
27
26
|
```
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
Requires: Node.js >= 20, FFmpeg, Chromium (`npx playwright install chromium`).
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
### 2. If auth is required, bootstrap login
|
|
30
|
+
### 2. If auth is required
|
|
34
31
|
|
|
35
32
|
```bash
|
|
36
|
-
demo-dev auth
|
|
33
|
+
demo-dev auth \
|
|
34
|
+
--base-url https://app.example.com \
|
|
37
35
|
--email you@example.com \
|
|
38
36
|
--password 'your-password'
|
|
39
37
|
```
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
### 3. Run the full pipeline
|
|
39
|
+
### 3. Generate a demo
|
|
44
40
|
|
|
45
41
|
```bash
|
|
46
|
-
demo-dev
|
|
42
|
+
demo-dev demo \
|
|
43
|
+
--base-url https://app.example.com \
|
|
44
|
+
--prompt "Show the dashboard, create a new project, invite a teammate" \
|
|
45
|
+
--frame
|
|
47
46
|
```
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
This single command will: explore the site → AI plan scenes → record → narrate → compose → mp4.
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
demo-dev pr-demo --base-url http://localhost:3000 --base-ref origin/main
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### 4. Re-render only when capture and manifest already exist
|
|
50
|
+
### Diff-driven mode (from a PR branch)
|
|
56
51
|
|
|
57
52
|
```bash
|
|
58
|
-
demo-dev
|
|
53
|
+
demo-dev demo --base-url http://localhost:3000
|
|
59
54
|
```
|
|
60
55
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Use a manual plan when:
|
|
64
|
-
- the feature is behind auth or deep navigation
|
|
65
|
-
- the planner cannot infer the correct route from diff
|
|
66
|
-
- the user wants a specific product moment, such as AI editing, onboarding, settings, inbox, or dashboards
|
|
67
|
-
- the user wants a more launch-style product film rather than raw replay
|
|
68
|
-
|
|
69
|
-
Place plans in an artifacts folder such as:
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
artifacts/manual-plan.json
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
Then run capture / manifest / render around that plan.
|
|
76
|
-
|
|
77
|
-
See [references/recipes.md](references/recipes.md).
|
|
78
|
-
|
|
79
|
-
## Recommended agent behavior
|
|
56
|
+
No `--prompt` = auto-detect changes from git diff.
|
|
80
57
|
|
|
81
|
-
|
|
82
|
-
2. For the same route, keep one screen object and move the camera instead of cutting to a new screen.
|
|
83
|
-
3. Prefer stable states over loading transitions.
|
|
84
|
-
4. If auth is required, verify storage state early.
|
|
85
|
-
5. If planner output is weak, create a tighter manual plan instead of forcing bad automation.
|
|
86
|
-
6. Keep copy concise and product-first.
|
|
58
|
+
## Key flags
|
|
87
59
|
|
|
88
|
-
|
|
60
|
+
| Flag | Description |
|
|
61
|
+
|------|-------------|
|
|
62
|
+
| `--prompt "..."` | Describe the demo in natural language |
|
|
63
|
+
| `--frame` | Add Screen Studio-style browser frame with gradient background |
|
|
64
|
+
| `--quality draft\|standard\|high` | Video quality preset |
|
|
65
|
+
| `--base-url` | URL of the app |
|
|
66
|
+
| `--output-dir` | Where to write output (default: artifacts) |
|
|
89
67
|
|
|
90
|
-
|
|
68
|
+
## All commands
|
|
91
69
|
|
|
92
70
|
```bash
|
|
93
|
-
demo-dev
|
|
94
|
-
demo-dev
|
|
95
|
-
demo-dev
|
|
96
|
-
demo-dev
|
|
97
|
-
demo-dev
|
|
98
|
-
demo-dev
|
|
99
|
-
demo-dev
|
|
100
|
-
demo-dev
|
|
101
|
-
demo-dev
|
|
102
|
-
demo-dev
|
|
103
|
-
demo-dev
|
|
71
|
+
demo-dev demo # Full pipeline
|
|
72
|
+
demo-dev auth # Login and save session
|
|
73
|
+
demo-dev capture # Record only
|
|
74
|
+
demo-dev voice # Generate narration only
|
|
75
|
+
demo-dev render # Record + narrate + compose
|
|
76
|
+
demo-dev plan # Generate plan from git diff
|
|
77
|
+
demo-dev probe # Plan + probe pages
|
|
78
|
+
demo-dev init # Create config file
|
|
79
|
+
demo-dev doctor # Check environment
|
|
80
|
+
demo-dev config # Show config
|
|
81
|
+
demo-dev providers # List AI/TTS providers
|
|
82
|
+
demo-dev comment # Post as PR comment
|
|
104
83
|
```
|
|
105
84
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
npm run config
|
|
110
|
-
npm run providers
|
|
111
|
-
npm run plan
|
|
112
|
-
npm run probe
|
|
113
|
-
npm run auth:bootstrap
|
|
114
|
-
npm run capture
|
|
115
|
-
npm run voice
|
|
116
|
-
npm run manifest
|
|
117
|
-
npm run render -- --manifest artifacts/render-manifest.json --out artifacts/pr-demo.mp4
|
|
118
|
-
npm run comment -- --output-dir artifacts --pr-number 123
|
|
119
|
-
npm run pr-demo
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Outputs to check
|
|
123
|
-
|
|
124
|
-
- `artifacts/demo-context.json`
|
|
125
|
-
- `artifacts/demo-plan.initial.json`
|
|
126
|
-
- `artifacts/page-probes.json`
|
|
127
|
-
- `artifacts/demo-plan.json`
|
|
128
|
-
- `artifacts/captures.json`
|
|
129
|
-
- `artifacts/render-manifest.json`
|
|
130
|
-
- `artifacts/pr-demo.mp4`
|
|
131
|
-
|
|
132
|
-
## Troubleshooting
|
|
85
|
+
## When to use prompt vs diff mode
|
|
133
86
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
87
|
+
| Mode | When to use |
|
|
88
|
+
|------|-------------|
|
|
89
|
+
| `--prompt "..."` | Feature demos, product tours, any app you can reach via URL |
|
|
90
|
+
| No prompt (diff) | PR walkthroughs, auto-detected from code changes |
|
|
138
91
|
|
|
139
|
-
##
|
|
140
|
-
|
|
141
|
-
Other users can install this repo as a pi package or point pi at this repo's `skills/` directory.
|
|
92
|
+
## Recommended agent behavior
|
|
142
93
|
|
|
143
|
-
|
|
94
|
+
1. Prefer prompt-driven mode — it works with any web app, no git repo needed.
|
|
95
|
+
2. Use `--frame` for polished output.
|
|
96
|
+
3. If auth is required, run `demo-dev auth` first.
|
|
97
|
+
4. If AI-generated selectors fail, the system auto-retries with fuzzy matching.
|
|
98
|
+
5. Keep narration conversational and product-focused.
|
|
99
|
+
6. For best voice quality, set `DEMO_TTS_PROVIDER=elevenlabs`.
|
|
144
100
|
|
|
145
|
-
|
|
146
|
-
pi install /absolute/path/to/demo.dev
|
|
147
|
-
```
|
|
101
|
+
## Outputs
|
|
148
102
|
|
|
149
|
-
|
|
103
|
+
- `artifacts/pr-demo.mp4` — the final video
|
|
104
|
+
- `artifacts/demo-plan.json` — AI-generated scene plan
|
|
105
|
+
- `artifacts/visual-plan.json` — zoom keyframes + speed ramps
|
|
106
|
+
- `artifacts/voice-script.json` — narration text + audio paths
|
|
107
|
+
- `artifacts/continuous-capture.json` — recording metadata
|
|
150
108
|
|
|
151
|
-
|
|
152
|
-
pi install git:github.com/your-org/demo.dev
|
|
153
|
-
```
|
|
109
|
+
See [references/configuration.md](references/configuration.md) and [references/recipes.md](references/recipes.md).
|
|
@@ -1,45 +1,19 @@
|
|
|
1
|
-
# demo
|
|
1
|
+
# demo-dev configuration
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Config file (optional)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Config is **optional for prompt-driven mode**. Just pass `--base-url` and `--prompt` directly.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
demo-dev init
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
That writes a starter config plus the workflow template.
|
|
12
|
-
|
|
13
|
-
## Minimal config
|
|
14
|
-
|
|
15
|
-
Create `demo.dev.config.json` in the target repo:
|
|
7
|
+
For project-level defaults, create `demo.dev.config.json`:
|
|
16
8
|
|
|
17
9
|
```json
|
|
18
10
|
{
|
|
19
11
|
"projectName": "My App",
|
|
20
|
-
"baseUrl": "http://localhost:3000",
|
|
21
|
-
"readyUrl": "http://localhost:3000",
|
|
22
|
-
"devCommand": "npm run dev",
|
|
23
|
-
"baseRef": "origin/main",
|
|
24
|
-
"outputDir": "artifacts",
|
|
25
|
-
"preferredRoutes": ["/", "/dashboard"],
|
|
26
|
-
"featureHints": ["home", "dashboard"]
|
|
27
|
-
}
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Auth-enabled config
|
|
31
|
-
|
|
32
|
-
```json
|
|
33
|
-
{
|
|
34
|
-
"projectName": "My SaaS",
|
|
35
12
|
"baseUrl": "https://app.example.com",
|
|
36
|
-
"readyUrl": "https://app.example.com",
|
|
37
13
|
"baseRef": "origin/main",
|
|
38
14
|
"outputDir": "artifacts",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"preferredRoutes": ["/dashboard", "/settings"],
|
|
42
|
-
"featureHints": ["dashboard", "settings"],
|
|
15
|
+
"preferredRoutes": ["/", "/dashboard", "/settings"],
|
|
16
|
+
"featureHints": ["dashboard", "onboarding", "settings"],
|
|
43
17
|
"authRequiredRoutes": ["/dashboard", "/settings"],
|
|
44
18
|
"auth": {
|
|
45
19
|
"loginPath": "/login",
|
|
@@ -51,52 +25,70 @@ Create `demo.dev.config.json` in the target repo:
|
|
|
51
25
|
}
|
|
52
26
|
```
|
|
53
27
|
|
|
54
|
-
|
|
28
|
+
Or bootstrap with `demo-dev init`.
|
|
55
29
|
|
|
56
|
-
|
|
30
|
+
## Config fields
|
|
57
31
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
32
|
+
| Field | Description |
|
|
33
|
+
|-------|-------------|
|
|
34
|
+
| `projectName` | Display name for the project |
|
|
35
|
+
| `baseUrl` | Default URL of the web app |
|
|
36
|
+
| `baseRef` | Git base ref for diff-based planning (default: origin/main) |
|
|
37
|
+
| `outputDir` | Where to write artifacts (default: artifacts) |
|
|
38
|
+
| `preferredRoutes` | Routes the AI planner should explore and prioritize |
|
|
39
|
+
| `featureHints` | Feature names to help the AI planner |
|
|
40
|
+
| `authRequiredRoutes` | Routes that need login |
|
|
41
|
+
| `auth.*` | Login flow configuration |
|
|
65
42
|
|
|
66
|
-
##
|
|
43
|
+
## Environment variables
|
|
67
44
|
|
|
68
|
-
|
|
69
|
-
It does not fall back to generic provider keys such as `OPENAI_API_KEY` or `ELEVENLABS_API_KEY`.
|
|
45
|
+
### AI providers
|
|
70
46
|
|
|
71
|
-
|
|
47
|
+
```bash
|
|
48
|
+
DEMO_AI_PROVIDER=auto # auto, claude, cursor, codex, openai
|
|
49
|
+
DEMO_OPENAI_API_KEY=sk-... # Required for openai provider
|
|
50
|
+
DEMO_OPENAI_BASE_URL=... # Optional
|
|
51
|
+
DEMO_OPENAI_MODEL=... # Optional model override
|
|
52
|
+
```
|
|
72
53
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
- `DEMO_AI_MODEL`
|
|
78
|
-
- `DEMO_TTS_PROVIDER`
|
|
79
|
-
- `DEMO_TTS_MODEL`
|
|
80
|
-
- `DEMO_TTS_VOICE`
|
|
81
|
-
- `DEMO_ELEVENLABS_API_KEY`
|
|
82
|
-
- `DEMO_ELEVENLABS_VOICE_ID`
|
|
54
|
+
### TTS providers
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
DEMO_TTS_PROVIDER=auto # auto, elevenlabs, openai, local
|
|
83
58
|
|
|
84
|
-
|
|
59
|
+
# ElevenLabs (best quality)
|
|
60
|
+
DEMO_ELEVENLABS_API_KEY=sk_...
|
|
61
|
+
DEMO_ELEVENLABS_VOICE_ID=...
|
|
62
|
+
|
|
63
|
+
# OpenAI TTS
|
|
64
|
+
DEMO_OPENAI_API_KEY=sk-...
|
|
65
|
+
|
|
66
|
+
# Local (macOS only, free)
|
|
67
|
+
DEMO_LOCAL_TTS_VOICE=Samantha
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Auth
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
DEMO_STORAGE_STATE=path/to/storage-state.json
|
|
74
|
+
DEMO_LOGIN_EMAIL=you@example.com
|
|
75
|
+
DEMO_LOGIN_PASSWORD=your-password
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Background music
|
|
85
79
|
|
|
86
80
|
```bash
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
DEMO_TTS_PROVIDER=openai
|
|
81
|
+
DEMO_BGM_PATH=./assets/music/bed.mp3
|
|
82
|
+
DEMO_BGM_VOLUME=0.16
|
|
90
83
|
```
|
|
91
84
|
|
|
92
|
-
##
|
|
85
|
+
## Precedence
|
|
86
|
+
|
|
87
|
+
CLI flags > Environment variables > Config file > Defaults
|
|
93
88
|
|
|
94
|
-
|
|
95
|
-
2. Adjust `baseUrl`, `devCommand`, and `auth`.
|
|
96
|
-
3. Validate the repo:
|
|
89
|
+
## Verify setup
|
|
97
90
|
|
|
98
91
|
```bash
|
|
99
92
|
demo-dev doctor
|
|
100
93
|
demo-dev config
|
|
101
|
-
demo-dev pr-demo
|
|
102
94
|
```
|
|
@@ -1,83 +1,100 @@
|
|
|
1
|
-
# demo
|
|
1
|
+
# demo-dev recipes
|
|
2
2
|
|
|
3
|
-
## Recipe:
|
|
3
|
+
## Recipe: prompt-driven demo (recommended)
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
demo-dev demo \
|
|
7
|
+
--base-url https://app.example.com \
|
|
8
|
+
--prompt "Show the onboarding flow, create a workspace, invite a teammate" \
|
|
9
|
+
--frame
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Recipe: prompt-driven with premium voice
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
DEMO_ELEVENLABS_API_KEY=sk_... \
|
|
16
|
+
DEMO_ELEVENLABS_VOICE_ID=... \
|
|
17
|
+
DEMO_TTS_PROVIDER=elevenlabs \
|
|
18
|
+
demo-dev demo \
|
|
19
|
+
--base-url https://app.example.com \
|
|
20
|
+
--prompt "Show the dashboard and settings page" \
|
|
21
|
+
--frame
|
|
10
22
|
```
|
|
11
23
|
|
|
12
24
|
## Recipe: authenticated SaaS
|
|
13
25
|
|
|
14
26
|
```bash
|
|
15
|
-
demo-dev auth
|
|
27
|
+
demo-dev auth \
|
|
28
|
+
--base-url https://app.example.com \
|
|
16
29
|
--email you@example.com \
|
|
17
30
|
--password 'your-password'
|
|
18
31
|
|
|
19
|
-
demo-dev
|
|
32
|
+
demo-dev demo \
|
|
33
|
+
--base-url https://app.example.com \
|
|
34
|
+
--prompt "Show the inbox and open a conversation" \
|
|
35
|
+
--frame
|
|
20
36
|
```
|
|
21
37
|
|
|
22
|
-
## Recipe:
|
|
38
|
+
## Recipe: diff-driven PR demo
|
|
23
39
|
|
|
24
40
|
```bash
|
|
25
|
-
|
|
26
|
-
DEMO_AI_PROVIDER=openai \
|
|
27
|
-
DEMO_TTS_PROVIDER=openai \
|
|
28
|
-
demo-dev pr-demo
|
|
41
|
+
demo-dev demo --base-url http://localhost:3000
|
|
29
42
|
```
|
|
30
43
|
|
|
31
|
-
|
|
44
|
+
No `--prompt` means it reads the git diff and auto-plans scenes.
|
|
32
45
|
|
|
33
|
-
## Recipe:
|
|
46
|
+
## Recipe: high quality render
|
|
34
47
|
|
|
35
48
|
```bash
|
|
36
|
-
demo-dev
|
|
37
|
-
|
|
49
|
+
demo-dev demo \
|
|
50
|
+
--base-url https://app.example.com \
|
|
51
|
+
--prompt "..." \
|
|
52
|
+
--frame \
|
|
53
|
+
--quality high
|
|
38
54
|
```
|
|
39
55
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
## Recipe: re-render without recapturing
|
|
56
|
+
## Recipe: fast draft for iteration
|
|
43
57
|
|
|
44
58
|
```bash
|
|
45
|
-
demo-dev
|
|
59
|
+
demo-dev demo \
|
|
60
|
+
--base-url https://app.example.com \
|
|
61
|
+
--prompt "..." \
|
|
62
|
+
--quality draft
|
|
46
63
|
```
|
|
47
64
|
|
|
48
|
-
## Recipe:
|
|
65
|
+
## Recipe: with background music
|
|
49
66
|
|
|
50
67
|
```bash
|
|
51
68
|
DEMO_BGM_PATH=./assets/music/bed.mp3 \
|
|
52
69
|
DEMO_BGM_VOLUME=0.14 \
|
|
53
|
-
|
|
54
|
-
|
|
70
|
+
demo-dev demo \
|
|
71
|
+
--base-url https://app.example.com \
|
|
72
|
+
--prompt "..." \
|
|
73
|
+
--frame
|
|
55
74
|
```
|
|
56
75
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
## Recipe: manual feature film
|
|
60
|
-
|
|
61
|
-
Use a manual plan when you need a specific flow like:
|
|
62
|
-
- AI editing
|
|
63
|
-
- inbox triage
|
|
64
|
-
- onboarding
|
|
65
|
-
- dashboard walkthrough
|
|
76
|
+
## Recipe: OpenAI for planning and TTS
|
|
66
77
|
|
|
67
|
-
|
|
78
|
+
```bash
|
|
79
|
+
DEMO_OPENAI_API_KEY=sk-... \
|
|
80
|
+
DEMO_AI_PROVIDER=openai \
|
|
81
|
+
DEMO_TTS_PROVIDER=openai \
|
|
82
|
+
demo-dev demo \
|
|
83
|
+
--base-url https://app.example.com \
|
|
84
|
+
--prompt "..." \
|
|
85
|
+
--frame
|
|
86
|
+
```
|
|
68
87
|
|
|
69
|
-
|
|
70
|
-
2. Run capture against that plan
|
|
71
|
-
3. Build manifest
|
|
72
|
-
4. Render mp4
|
|
88
|
+
## Recipe: inspect plan before recording
|
|
73
89
|
|
|
74
|
-
|
|
90
|
+
```bash
|
|
91
|
+
demo-dev plan
|
|
92
|
+
cat artifacts/demo-plan.json
|
|
93
|
+
```
|
|
75
94
|
|
|
76
|
-
|
|
95
|
+
## Recipe: PR automation in CI
|
|
77
96
|
|
|
78
97
|
```bash
|
|
79
|
-
demo-dev
|
|
98
|
+
demo-dev demo --base-url http://localhost:3000
|
|
80
99
|
demo-dev comment --output-dir artifacts
|
|
81
100
|
```
|
|
82
|
-
|
|
83
|
-
If the app needs a server, set `devCommand` and `readyUrl` in config or GitHub Variables.
|
|
@@ -257,6 +257,32 @@ const ZOOM_INJECT_SCRIPT = `
|
|
|
257
257
|
})();
|
|
258
258
|
`;
|
|
259
259
|
|
|
260
|
+
const SCENE_TRANSITION_SCRIPT = `
|
|
261
|
+
(() => {
|
|
262
|
+
if (document.getElementById('__scene-fade')) return;
|
|
263
|
+
const overlay = document.createElement('div');
|
|
264
|
+
overlay.id = '__scene-fade';
|
|
265
|
+
Object.assign(overlay.style, {
|
|
266
|
+
position: 'fixed', top: '0', left: '0', width: '100vw', height: '100vh',
|
|
267
|
+
zIndex: '999997', pointerEvents: 'none',
|
|
268
|
+
background: 'white', opacity: '0',
|
|
269
|
+
transition: 'opacity 0.4s ease-in-out',
|
|
270
|
+
});
|
|
271
|
+
document.body.appendChild(overlay);
|
|
272
|
+
window.__fadeOut = () => { overlay.style.opacity = '1'; };
|
|
273
|
+
window.__fadeIn = () => { overlay.style.opacity = '0'; };
|
|
274
|
+
})();
|
|
275
|
+
`;
|
|
276
|
+
|
|
277
|
+
/** Fade to white and back — used between scenes. */
|
|
278
|
+
const fadeTransition = async (page: Page) => {
|
|
279
|
+
await page.evaluate(SCENE_TRANSITION_SCRIPT).catch(() => undefined);
|
|
280
|
+
await page.evaluate(() => (window as any).__fadeOut?.()).catch(() => undefined);
|
|
281
|
+
await page.waitForTimeout(450);
|
|
282
|
+
await page.evaluate(() => (window as any).__fadeIn?.()).catch(() => undefined);
|
|
283
|
+
await page.waitForTimeout(450);
|
|
284
|
+
};
|
|
285
|
+
|
|
260
286
|
/** Smoothly zoom into a point on the page (captured by screencast at 60fps). */
|
|
261
287
|
const zoomToPoint = async (page: Page, x: number, y: number, scale = 1.5) => {
|
|
262
288
|
await page.evaluate(ZOOM_INJECT_SCRIPT).catch(() => undefined);
|
|
@@ -307,13 +333,13 @@ const CURSOR_OVERLAY_SCRIPT = `
|
|
|
307
333
|
cursor.id = '__ghost-cursor';
|
|
308
334
|
Object.assign(cursor.style, {
|
|
309
335
|
position: 'fixed', zIndex: '999999', pointerEvents: 'none',
|
|
310
|
-
left: '0px', top: '0px', width: '
|
|
311
|
-
transition: 'left 0.
|
|
312
|
-
filter: 'drop-shadow(0 2px 3px rgba(0,0,0,0.4))',
|
|
336
|
+
left: '0px', top: '0px', width: '20px', height: '28px',
|
|
337
|
+
transition: 'left 0.03s linear, top 0.03s linear',
|
|
313
338
|
});
|
|
314
|
-
/*
|
|
315
|
-
cursor.innerHTML = '<svg width="
|
|
316
|
-
'<
|
|
339
|
+
/* Precise macOS default cursor — traced from Apple's actual cursor spec */
|
|
340
|
+
cursor.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="28" viewBox="0 0 20 28">' +
|
|
341
|
+
'<defs><filter id="cs" x="-20%" y="-20%" width="140%" height="140%"><feDropShadow dx="0.5" dy="1.5" stdDeviation="0.8" flood-opacity="0.45"/></filter></defs>' +
|
|
342
|
+
'<path filter="url(#cs)" d="M2.2 0L2.2 21.4L6.8 16.4L10.6 24.8L13.2 23.6L9.4 15.6L16 15.6Z" fill="white" stroke="black" stroke-width="1.1" stroke-linejoin="round"/>' +
|
|
317
343
|
'</svg>';
|
|
318
344
|
document.body.appendChild(cursor);
|
|
319
345
|
|
|
@@ -391,6 +417,7 @@ const runActionWithCursor = async (
|
|
|
391
417
|
await page.evaluate(CURSOR_OVERLAY_SCRIPT).catch(() => undefined);
|
|
392
418
|
await page.evaluate(CURSOR_TRACKER_SCRIPT).catch(() => undefined);
|
|
393
419
|
await page.evaluate(ZOOM_INJECT_SCRIPT).catch(() => undefined);
|
|
420
|
+
await page.evaluate(SCENE_TRANSITION_SCRIPT).catch(() => undefined);
|
|
394
421
|
await page.waitForTimeout(humanDelay(400));
|
|
395
422
|
break;
|
|
396
423
|
}
|
|
@@ -711,8 +738,8 @@ export const capturePlanContinuous = async (
|
|
|
711
738
|
url: page.url(),
|
|
712
739
|
});
|
|
713
740
|
|
|
714
|
-
//
|
|
715
|
-
await page
|
|
741
|
+
// Smooth fade transition between scenes
|
|
742
|
+
await fadeTransition(page);
|
|
716
743
|
}
|
|
717
744
|
|
|
718
745
|
// Final drain of cursor samples
|
package/src/planner/llm.ts
CHANGED
|
@@ -6,13 +6,13 @@ import { demoPlanSchema } from "./schema.js";
|
|
|
6
6
|
|
|
7
7
|
const buildPlannerPrompt = (context: DiffContext, projectConfig?: ProjectConfig) => {
|
|
8
8
|
return [
|
|
9
|
-
"You are a product demo director
|
|
9
|
+
"You are a product demo director creating a polished product walkthrough video.",
|
|
10
10
|
"Goal: turn a git diff into a concise, recordable, voice-friendly demo plan.",
|
|
11
11
|
"Requirements:",
|
|
12
12
|
"1. Pick only 2-4 scenes that best represent visible user-facing changes.",
|
|
13
13
|
"2. Keep actions stable and executable. Prefer navigate / click / fill / waitForText / waitForUrl / scroll.",
|
|
14
14
|
"3. If the diff does not reveal specific elements, do not invent complex actions. Fall back to page-level presentation.",
|
|
15
|
-
"4. narration should sound like product
|
|
15
|
+
"4. narration should sound conversational and human — like giving a product tour to a friend, not reading a test report.",
|
|
16
16
|
"5. Output strict JSON only, with no markdown explanation.",
|
|
17
17
|
"6. All URLs must be in-app relative paths such as /dashboard.",
|
|
18
18
|
"7. Keep captions short enough for on-screen usage.",
|