@a1knowhow/demo-maker 0.1.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/LICENSE +201 -0
- package/README.md +104 -0
- package/SECURITY.md +33 -0
- package/bin/demo-maker.mjs +14 -0
- package/docs/README.md +35 -0
- package/docs/actions.md +160 -0
- package/docs/cli.md +73 -0
- package/docs/getting-started.md +220 -0
- package/docs/imports-and-fragments.md +78 -0
- package/docs/library-api.md +64 -0
- package/docs/locators.md +119 -0
- package/docs/recording.md +59 -0
- package/docs/repairing-scenarios.md +73 -0
- package/docs/scenario-schema.md +80 -0
- package/docs/url-capture.md +75 -0
- package/docs/variables.md +48 -0
- package/docs/video-and-pacing.md +109 -0
- package/examples/demo1-introduction.yaml +166 -0
- package/examples/demo2-github-actions.yaml +109 -0
- package/examples/files/demo1-introduction-run.log +50 -0
- package/examples/fragments/open-repository.yaml +15 -0
- package/examples/smoke.yaml +18 -0
- package/llms.txt +32 -0
- package/package.json +66 -0
- package/src/cli.ts +226 -0
- package/src/env.ts +112 -0
- package/src/export-chapters.ts +99 -0
- package/src/ffmpeg.ts +168 -0
- package/src/index.ts +54 -0
- package/src/load.ts +143 -0
- package/src/logger.ts +126 -0
- package/src/main.ts +68 -0
- package/src/parse-errors.ts +99 -0
- package/src/parse.ts +622 -0
- package/src/playwright-selector-to-locator.ts +328 -0
- package/src/record-cli.ts +179 -0
- package/src/recorder.ts +442 -0
- package/src/recording-to-yaml.ts +435 -0
- package/src/runner.ts +1582 -0
- package/src/selector-to-locator.ts +69 -0
- package/src/templates.ts +48 -0
- package/src/types.ts +319 -0
- package/src/url-capture.ts +105 -0
- package/src/variables.ts +64 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# URL capture
|
|
2
|
+
|
|
3
|
+
Purpose: Store path segments from the browser URL as `{{ variables }}` during a run.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
When the runner navigates (and after some steps), it can match the current page URL against regex rules. Each rule’s **first capturing group** is written to a named variable for later steps.
|
|
8
|
+
|
|
9
|
+
Example: after opening `/projects/abc123/settings`, capture `project_id=abc123` and use `{{ project_id }}` in the next URL or fill.
|
|
10
|
+
|
|
11
|
+
No rules by default — nothing is captured until you configure them.
|
|
12
|
+
|
|
13
|
+
## Scenario YAML
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
name: my-demo
|
|
17
|
+
base_url: https://app.example/
|
|
18
|
+
url_capture:
|
|
19
|
+
- variable: workspace_id
|
|
20
|
+
pattern: "/workspace/([^/]+)(?:/|$)"
|
|
21
|
+
- variable: document_id
|
|
22
|
+
pattern: "/workspace/[^/]+/doc/([^/]+)"
|
|
23
|
+
steps:
|
|
24
|
+
- action: navigate
|
|
25
|
+
url: /workspace/… # after load, workspace_id is set if the URL matches
|
|
26
|
+
- action: navigate
|
|
27
|
+
url: "/workspace/{{ workspace_id }}/doc/{{ document_id }}/edit"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`pattern` is a **RegExp source string** (not slash-wrapped YAML regex). Entry-file only (imports do not contribute `url_capture`).
|
|
31
|
+
|
|
32
|
+
## CLI
|
|
33
|
+
|
|
34
|
+
Repeatable flag:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx demo-maker run demo.yaml \
|
|
38
|
+
--capture-from-url 'workspace_id=/workspace/([^/]+)(?:/|$)' \
|
|
39
|
+
--capture-from-url 'document_id=/workspace/[^/]+/doc/([^/]+)'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or a JSON file:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
[
|
|
46
|
+
{ "variable": "workspace_id", "pattern": "/workspace/([^/]+)(?:/|$)" },
|
|
47
|
+
{ "variable": "document_id", "pattern": "/workspace/[^/]+/doc/([^/]+)" }
|
|
48
|
+
]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx demo-maker run demo.yaml --url-capture-file=./url-capture.json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
CLI rules are applied **after** scenario `url_capture` (same variable name overwrites when both match later).
|
|
56
|
+
|
|
57
|
+
## Library
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { runScenario, createRegexUrlCapture } from "@a1knowhow/demo-maker";
|
|
61
|
+
|
|
62
|
+
await runScenario(scenario, context, {
|
|
63
|
+
urlCaptureHooks: [
|
|
64
|
+
createRegexUrlCapture([
|
|
65
|
+
{ variable: "org_id", pattern: String.raw`/orgs/([^/]+)` },
|
|
66
|
+
]),
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Related
|
|
72
|
+
|
|
73
|
+
- [Variables](./variables.md)
|
|
74
|
+
- [CLI](./cli.md)
|
|
75
|
+
- [Library API](./library-api.md)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Variables and templates
|
|
2
|
+
|
|
3
|
+
Purpose: Runtime substitution in scenario strings.
|
|
4
|
+
|
|
5
|
+
## `{{ placeholders }}`
|
|
6
|
+
|
|
7
|
+
In `value:`, `url:`, string locator fields, and captions, use:
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
value: "{{ auth.email }}"
|
|
11
|
+
url: "{{ BASE_URL }}/settings"
|
|
12
|
+
locator: { text: "{{ DEMO_WORKSPACE1 }}" }
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Sources (later overrides earlier where applicable):
|
|
16
|
+
|
|
17
|
+
- `--env-file` / `.env`-style file (all keys)
|
|
18
|
+
- Shell `process.env` only for: auth env names, `BASE_URL`, and flat `{{ NAME }}` keys referenced in the scenario
|
|
19
|
+
- Captured variables during the run (see [URL capture](./url-capture.md))
|
|
20
|
+
- Built-in `auth.email` / `auth.password` from env names in `auth:` (or defaults `E2E_TEST_USER_EMAIL` / `E2E_TEST_USER_PASSWORD`)
|
|
21
|
+
- Nested keys: `{{ auth.email }}`
|
|
22
|
+
|
|
23
|
+
Prefer putting secrets and demo-specific values in `--env-file` rather than relying on the ambient shell environment.
|
|
24
|
+
|
|
25
|
+
`BASE_URL` from the env file overrides scenario `base_url` unless `--base-url` is passed (CLI wins).
|
|
26
|
+
|
|
27
|
+
**Regex locators are not interpolated** — see [Locators](./locators.md).
|
|
28
|
+
|
|
29
|
+
## Template functions
|
|
30
|
+
|
|
31
|
+
`[fnName:"arg"]` in supported string fields. Currently used in `video.filename`:
|
|
32
|
+
|
|
33
|
+
| Syntax | Description |
|
|
34
|
+
|--------|-------------|
|
|
35
|
+
| `[date:"FORMAT"]` | Current local date/time |
|
|
36
|
+
|
|
37
|
+
Tokens: `YYYY`, `MM`, `DD`, `HH`, `mm`.
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
video:
|
|
41
|
+
enabled: true
|
|
42
|
+
dir: output/videos
|
|
43
|
+
filename: demo-[date:"YYYY-MM-DD-HHmm"].mp4
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Produces e.g. `demo-2026-04-01-1432.mp4`.
|
|
47
|
+
|
|
48
|
+
Implemented in `src/templates.ts`.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Video and pacing
|
|
2
|
+
|
|
3
|
+
Purpose: Recording quality, overlays, ffmpeg post-processing, and timing.
|
|
4
|
+
|
|
5
|
+
Requires **ffmpeg** on `PATH` for `.mp4`, trim/splice (`start_step`/`end_step`, `cut_video`), and background audio.
|
|
6
|
+
|
|
7
|
+
## Video config
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
video:
|
|
11
|
+
enabled: true
|
|
12
|
+
dir: "output/videos" # relative to process cwd
|
|
13
|
+
filename: "demo-[date:\"YYYY-MM-DD-HHmm\"].mp4"
|
|
14
|
+
size: { width: 1920, height: 1080 } # final output
|
|
15
|
+
viewport: { width: 1440, height: 810 } # browser render (optional)
|
|
16
|
+
start_step: 11 # optional trim (1-based); needs ffmpeg
|
|
17
|
+
end_step: 40
|
|
18
|
+
caption_parallel: true # optional
|
|
19
|
+
playback_speed: 1.25 # optional; .mp4 only
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
| Field | Notes |
|
|
23
|
+
|-------|--------|
|
|
24
|
+
| `enabled` | Must be `true` to record |
|
|
25
|
+
| `dir` | Created if missing; relative to **cwd**, not the YAML path |
|
|
26
|
+
| `filename` | Optional; `.mp4` triggers H.264 re-encode (CRF 18). Supports `[date:"…"]` |
|
|
27
|
+
| `size` | Final resolution (YouTube-oriented default 1920×1080) |
|
|
28
|
+
| `viewport` | Browser size; if smaller than `size`, ffmpeg upscales (lanczos). Use on laptops so headed Chrome fits the screen |
|
|
29
|
+
| `start_step` / `end_step` | Trim saved video after full run |
|
|
30
|
+
| `caption_parallel` | If true, caption overlay does not block the next step |
|
|
31
|
+
| `playback_speed` | Speed final MP4 (`setpts`); chapters remapped; BGM stays normal tempo |
|
|
32
|
+
|
|
33
|
+
Playwright records VP8 WebM internally; `.mp4` re-encodes to H.264 (`libx264`, `yuv420p`, `faststart`).
|
|
34
|
+
|
|
35
|
+
## Captions
|
|
36
|
+
|
|
37
|
+
Any step may set `caption:`. With video enabled, a bottom overlay is shown after the step. Default: wait ~2.5s before the next step. With `caption_parallel: true`, the caption fades on a timer while the next steps run.
|
|
38
|
+
|
|
39
|
+
Optional `caption_style: chapter` — full-width bottom bar, larger type.
|
|
40
|
+
|
|
41
|
+
Prefer few captions (scene intent), not every click. Supports `{{ ENV }}`.
|
|
42
|
+
|
|
43
|
+
## Scene polish actions
|
|
44
|
+
|
|
45
|
+
| Action | Role |
|
|
46
|
+
|--------|------|
|
|
47
|
+
| `title_card` | Full-screen chapter title (`text`, `hold_ms`, `fade_ms`) |
|
|
48
|
+
| `transition` | Fade to black (`fade_ms`; optional `hold_ms` for outro) |
|
|
49
|
+
| `cut_video` | Exclude nested duration from MP4; optional `title_card` placeholder |
|
|
50
|
+
| `video_exclude: true` | Exclude a single step |
|
|
51
|
+
|
|
52
|
+
## Background audio
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
audio:
|
|
56
|
+
background: path/to/music.mp3
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Mixed into the final MP4 via ffmpeg (when producing `.mp4`).
|
|
60
|
+
|
|
61
|
+
## Timing and chapters
|
|
62
|
+
|
|
63
|
+
On success with video, the runner writes `<basename>.timing.json` beside the video. Chapter timestamps remap after splice/speed.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
demo-maker export-chapters output/videos/demo.timing.json
|
|
67
|
+
demo-maker export-chapters output/videos
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Use `action: log` with `── Scene N: Title` and/or `caption:` for chapter labels. Excluded (`cut_video`) steps are omitted from chapter export.
|
|
71
|
+
|
|
72
|
+
## Pacing fields
|
|
73
|
+
|
|
74
|
+
| Field | Role |
|
|
75
|
+
|-------|------|
|
|
76
|
+
| `step_delay_ms` | After each step |
|
|
77
|
+
| `slow_mo_ms` | Playwright slowMo |
|
|
78
|
+
| `typing_delay_ms` | Between keystrokes (`type`); per-step override OK |
|
|
79
|
+
| `post_click_delay_ms` | After click settle |
|
|
80
|
+
| `navigate_hold_ms` | After navigate settle |
|
|
81
|
+
| `pre_action_delay_ms` | Before navigate/click |
|
|
82
|
+
|
|
83
|
+
`--fast` disables pacing for smoke checks.
|
|
84
|
+
|
|
85
|
+
Hold on a frame for video:
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
- action: wait
|
|
89
|
+
timeout_ms: 4000
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Recommended YouTube settings (laptop)
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
video:
|
|
96
|
+
enabled: true
|
|
97
|
+
dir: output/videos
|
|
98
|
+
filename: demo-[date:"YYYY-MM-DD-HHmm"].mp4
|
|
99
|
+
size: { width: 1920, height: 1080 }
|
|
100
|
+
viewport: { width: 1440, height: 810 }
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
On a large external display, omit `viewport` for native 1080p rendering.
|
|
104
|
+
|
|
105
|
+
## Related
|
|
106
|
+
|
|
107
|
+
- [Actions](./actions.md) (`title_card`, `cut_video`, `wait`)
|
|
108
|
+
- [Variables](./variables.md) (filename templates)
|
|
109
|
+
- [Getting started](./getting-started.md)
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
name: demo1-introduction
|
|
2
|
+
title: "demo-maker — Getting started walkthrough"
|
|
3
|
+
description: |
|
|
4
|
+
Public intro demo: open this repo on GitHub, walk getting-started.md
|
|
5
|
+
(install → YAML → env → video → record), then actions.md and locators.md
|
|
6
|
+
(including the accessibility tip). No auth required.
|
|
7
|
+
base_url: https://github.com
|
|
8
|
+
video:
|
|
9
|
+
enabled: true
|
|
10
|
+
dir: output/videos
|
|
11
|
+
filename: demo1-introduction-[date:"YYYY-MM-DD-HHmm"].mp4
|
|
12
|
+
size: { width: 1920, height: 1080 }
|
|
13
|
+
viewport: { width: 1440, height: 810 }
|
|
14
|
+
caption_parallel: true
|
|
15
|
+
playback_speed: 1.1
|
|
16
|
+
# Optional BGM (bring your own royalty-free track; path relative to cwd):
|
|
17
|
+
# audio:
|
|
18
|
+
# background: path/to/your-track.mp3
|
|
19
|
+
step_delay_ms: 250
|
|
20
|
+
slow_mo_ms: 50
|
|
21
|
+
typing_delay_ms: 40
|
|
22
|
+
post_click_delay_ms: 250
|
|
23
|
+
navigate_hold_ms: 1000
|
|
24
|
+
pre_action_delay_ms: 200
|
|
25
|
+
action_timeout_ms: 30000
|
|
26
|
+
|
|
27
|
+
steps:
|
|
28
|
+
- action: title_card
|
|
29
|
+
text: "From install to your first MP4"
|
|
30
|
+
hold_ms: 1400
|
|
31
|
+
|
|
32
|
+
- action: log
|
|
33
|
+
message: "── Scene 1: Repository home → Getting started"
|
|
34
|
+
|
|
35
|
+
- import: fragments/open-repository.yaml
|
|
36
|
+
|
|
37
|
+
- action: navigate
|
|
38
|
+
url: /a1knowhow/demo-maker/blob/main/docs/getting-started.md
|
|
39
|
+
caption: "Getting started"
|
|
40
|
+
|
|
41
|
+
- action: expect_visible
|
|
42
|
+
locator:
|
|
43
|
+
role: heading
|
|
44
|
+
name: Getting started
|
|
45
|
+
|
|
46
|
+
- action: log
|
|
47
|
+
message: "── Scene 2: Install"
|
|
48
|
+
|
|
49
|
+
- action: click
|
|
50
|
+
locator:
|
|
51
|
+
role: link
|
|
52
|
+
name: "Permalink: Step 1 — Install prerequisites"
|
|
53
|
+
caption: "Install"
|
|
54
|
+
|
|
55
|
+
- action: wait
|
|
56
|
+
timeout_ms: 900
|
|
57
|
+
|
|
58
|
+
- action: log
|
|
59
|
+
message: "── Scene 3: Create your YAML"
|
|
60
|
+
|
|
61
|
+
- action: click
|
|
62
|
+
locator:
|
|
63
|
+
role: link
|
|
64
|
+
name: "Permalink: Step 3 — Point at your app"
|
|
65
|
+
|
|
66
|
+
- action: click
|
|
67
|
+
locator:
|
|
68
|
+
text: "name: my-app-smoke"
|
|
69
|
+
caption: "Create your YAML file"
|
|
70
|
+
|
|
71
|
+
- action: wait
|
|
72
|
+
timeout_ms: 1000
|
|
73
|
+
|
|
74
|
+
- action: log
|
|
75
|
+
message: "── Scene 4: Env vars when needed"
|
|
76
|
+
|
|
77
|
+
- action: click
|
|
78
|
+
locator:
|
|
79
|
+
role: link
|
|
80
|
+
name: "Permalink: Step 4 — Env file and auth (when needed)"
|
|
81
|
+
caption: "Env vars when you need auth"
|
|
82
|
+
|
|
83
|
+
- action: wait
|
|
84
|
+
timeout_ms: 900
|
|
85
|
+
|
|
86
|
+
- action: log
|
|
87
|
+
message: "── Scene 5: Record video"
|
|
88
|
+
|
|
89
|
+
- action: click
|
|
90
|
+
locator:
|
|
91
|
+
role: link
|
|
92
|
+
name: "Permalink: Step 5 — Record a video (MP4)"
|
|
93
|
+
caption: "Enable video for MP4 output"
|
|
94
|
+
|
|
95
|
+
- action: wait
|
|
96
|
+
timeout_ms: 500
|
|
97
|
+
|
|
98
|
+
- action: log
|
|
99
|
+
message: "── Scene 6: Generate draft YAML"
|
|
100
|
+
|
|
101
|
+
- action: click
|
|
102
|
+
locator:
|
|
103
|
+
role: link
|
|
104
|
+
name: "Permalink: Step 6 — Record interactions instead of writing YAML by hand"
|
|
105
|
+
caption: "Generate draft YAML with demo-maker record"
|
|
106
|
+
|
|
107
|
+
- action: wait
|
|
108
|
+
timeout_ms: 1000
|
|
109
|
+
|
|
110
|
+
- action: log
|
|
111
|
+
message: "── Scene 7: Actions reference"
|
|
112
|
+
|
|
113
|
+
- action: click
|
|
114
|
+
locator:
|
|
115
|
+
role: link
|
|
116
|
+
name: "Permalink: Next reading"
|
|
117
|
+
|
|
118
|
+
- action: click
|
|
119
|
+
locator:
|
|
120
|
+
selector: '.markdown-body a[href$="actions.md"]'
|
|
121
|
+
settle_until:
|
|
122
|
+
locator:
|
|
123
|
+
role: heading
|
|
124
|
+
name: Actions
|
|
125
|
+
caption: "Browse every action"
|
|
126
|
+
|
|
127
|
+
- action: wait
|
|
128
|
+
timeout_ms: 800
|
|
129
|
+
caption: "navigate, click, type, expect_visible, …"
|
|
130
|
+
|
|
131
|
+
- action: log
|
|
132
|
+
message: "── Scene 8: Locators"
|
|
133
|
+
|
|
134
|
+
- action: click
|
|
135
|
+
locator:
|
|
136
|
+
role: link
|
|
137
|
+
name: "Permalink: Related"
|
|
138
|
+
|
|
139
|
+
- action: click
|
|
140
|
+
locator:
|
|
141
|
+
selector: '.markdown-body a[href$="locators.md"]'
|
|
142
|
+
settle_until:
|
|
143
|
+
locator:
|
|
144
|
+
role: heading
|
|
145
|
+
name: Locators
|
|
146
|
+
caption: "How to target UI elements"
|
|
147
|
+
|
|
148
|
+
- action: wait
|
|
149
|
+
timeout_ms: 800
|
|
150
|
+
|
|
151
|
+
- action: click
|
|
152
|
+
locator:
|
|
153
|
+
role: link
|
|
154
|
+
name: "Permalink: Accessibility tip (for app authors)"
|
|
155
|
+
caption: "Unique aria-labels keep demos stable"
|
|
156
|
+
|
|
157
|
+
- action: wait
|
|
158
|
+
timeout_ms: 1400
|
|
159
|
+
|
|
160
|
+
- action: transition
|
|
161
|
+
fade_ms: 400
|
|
162
|
+
hold_ms: 300
|
|
163
|
+
|
|
164
|
+
- action: title_card
|
|
165
|
+
text: "Try https://github.com/a1knowhow/demo-maker"
|
|
166
|
+
hold_ms: 1800
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: demo2-github-actions
|
|
2
|
+
title: "demo-maker — GitHub Actions walkthrough"
|
|
3
|
+
description: |
|
|
4
|
+
Richer public example: open this repo on GitHub, open Actions, open the latest
|
|
5
|
+
successful run, and open the test job (green check-marks on each step).
|
|
6
|
+
No auth required for this walkthrough. Expanding step logs needs a signed-in
|
|
7
|
+
GitHub session (optional --storage-state); anonymously GitHub disables clicks
|
|
8
|
+
on log steps (pointer-events:none).
|
|
9
|
+
base_url: https://github.com
|
|
10
|
+
video:
|
|
11
|
+
enabled: true
|
|
12
|
+
dir: output/videos
|
|
13
|
+
filename: demo2-github-actions-[date:"YYYY-MM-DD-HHmm"].mp4
|
|
14
|
+
size: { width: 1920, height: 1080 }
|
|
15
|
+
viewport: { width: 1440, height: 810 }
|
|
16
|
+
caption_parallel: true
|
|
17
|
+
playback_speed: 1.25
|
|
18
|
+
# Optional BGM (bring your own royalty-free track; path relative to cwd):
|
|
19
|
+
# audio:
|
|
20
|
+
# background: path/to/your-track.mp3
|
|
21
|
+
step_delay_ms: 400
|
|
22
|
+
slow_mo_ms: 80
|
|
23
|
+
typing_delay_ms: 40
|
|
24
|
+
post_click_delay_ms: 350
|
|
25
|
+
navigate_hold_ms: 1800
|
|
26
|
+
pre_action_delay_ms: 350
|
|
27
|
+
action_timeout_ms: 30000
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- action: title_card
|
|
31
|
+
text: "Demo videos as code"
|
|
32
|
+
hold_ms: 1600
|
|
33
|
+
|
|
34
|
+
- action: log
|
|
35
|
+
message: "── Scene 1: Repository home → Actions"
|
|
36
|
+
|
|
37
|
+
- import: fragments/open-repository.yaml
|
|
38
|
+
|
|
39
|
+
- action: click
|
|
40
|
+
locator:
|
|
41
|
+
role: link
|
|
42
|
+
name: Actions
|
|
43
|
+
settle_until:
|
|
44
|
+
locator:
|
|
45
|
+
role: combobox
|
|
46
|
+
name: Filter workflow runs
|
|
47
|
+
caption: "Open GitHub Actions"
|
|
48
|
+
|
|
49
|
+
- action: wait
|
|
50
|
+
timeout_ms: 1200
|
|
51
|
+
caption: "CI runs for this repo"
|
|
52
|
+
|
|
53
|
+
- action: log
|
|
54
|
+
message: "── Scene 2: Open the latest successful run"
|
|
55
|
+
|
|
56
|
+
- action: title_card
|
|
57
|
+
text: "Inspect a green CI run"
|
|
58
|
+
hold_ms: 1400
|
|
59
|
+
|
|
60
|
+
- action: click
|
|
61
|
+
locator:
|
|
62
|
+
role: link
|
|
63
|
+
name: /completed successfully:/i
|
|
64
|
+
settle_until:
|
|
65
|
+
locator:
|
|
66
|
+
role: link
|
|
67
|
+
name: test
|
|
68
|
+
caption: "Open the first successful workflow run"
|
|
69
|
+
|
|
70
|
+
- action: wait
|
|
71
|
+
timeout_ms: 1000
|
|
72
|
+
|
|
73
|
+
- action: log
|
|
74
|
+
message: "── Scene 3: Open the test job"
|
|
75
|
+
|
|
76
|
+
- action: click
|
|
77
|
+
locator:
|
|
78
|
+
role: link
|
|
79
|
+
name: test
|
|
80
|
+
settle_until:
|
|
81
|
+
text: Run npm test
|
|
82
|
+
caption: "Open the test job"
|
|
83
|
+
|
|
84
|
+
- action: expect_visible
|
|
85
|
+
locator:
|
|
86
|
+
text: Run npm test
|
|
87
|
+
|
|
88
|
+
- action: expect_visible
|
|
89
|
+
locator:
|
|
90
|
+
text: Run npm run typecheck
|
|
91
|
+
|
|
92
|
+
- action: wait
|
|
93
|
+
timeout_ms: 2800
|
|
94
|
+
caption: "Green checks on every step — re-run forever"
|
|
95
|
+
|
|
96
|
+
# Expanding a step log requires a signed-in GitHub session (anonymous UI uses
|
|
97
|
+
# pointer-events:none on <details>). With storage_state, you can add:
|
|
98
|
+
# - action: click
|
|
99
|
+
# locator:
|
|
100
|
+
# selector: 'summary.CheckStep-header:has-text("Run npm test")'
|
|
101
|
+
# caption: "Expand the npm test step"
|
|
102
|
+
|
|
103
|
+
- action: transition
|
|
104
|
+
fade_ms: 500
|
|
105
|
+
hold_ms: 400
|
|
106
|
+
|
|
107
|
+
- action: title_card
|
|
108
|
+
text: "Try https://github.com/a1knowhow/demo-maker"
|
|
109
|
+
hold_ms: 2200
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
Base URL: https://github.com
|
|
2
|
+
Running scenario: demo1-introduction (35/35 steps)
|
|
3
|
+
10:35:58 - Step 1/35: title_card - From install to your first MP4
|
|
4
|
+
10:36:00 - Step 2/35: log - ── Scene 1: Repository home → Getting started
|
|
5
|
+
[log] ── Scene 1: Repository home → Getting started
|
|
6
|
+
10:36:01 - Step 3/35: navigate - Open the demo-maker repository
|
|
7
|
+
10:36:04 - Step 4/35: expect_visible - link "demo-maker"
|
|
8
|
+
10:36:04 - Step 5/35: wait - 800ms
|
|
9
|
+
10:36:05 - Step 6/35: navigate - Getting started
|
|
10
|
+
10:36:08 - Step 7/35: expect_visible - heading "Getting started"
|
|
11
|
+
10:36:08 - Step 8/35: log - ── Scene 2: Install
|
|
12
|
+
[log] ── Scene 2: Install
|
|
13
|
+
10:36:08 - Step 9/35: click - Install
|
|
14
|
+
10:36:10 - Step 10/35: wait - 900ms
|
|
15
|
+
10:36:11 - Step 11/35: log - ── Scene 3: Create your YAML
|
|
16
|
+
[log] ── Scene 3: Create your YAML
|
|
17
|
+
10:36:12 - Step 12/35: click - link "Permalink: Step 3 — Point at your app"
|
|
18
|
+
10:36:13 - Step 13/35: click - Create your YAML file
|
|
19
|
+
10:36:14 - Step 14/35: wait - 1000ms
|
|
20
|
+
10:36:16 - Step 15/35: log - ── Scene 4: Env vars when needed
|
|
21
|
+
[log] ── Scene 4: Env vars when needed
|
|
22
|
+
10:36:16 - Step 16/35: click - Env vars when you need auth
|
|
23
|
+
10:36:18 - Step 17/35: wait - 900ms
|
|
24
|
+
10:36:19 - Step 18/35: log - ── Scene 5: Record video
|
|
25
|
+
[log] ── Scene 5: Record video
|
|
26
|
+
10:36:19 - Step 19/35: click - Enable video for MP4 output
|
|
27
|
+
10:36:21 - Step 20/35: wait - 500ms
|
|
28
|
+
10:36:21 - Step 21/35: log - ── Scene 6: Generate draft YAML
|
|
29
|
+
[log] ── Scene 6: Generate draft YAML
|
|
30
|
+
10:36:22 - Step 22/35: click - Generate draft YAML with demo-maker record
|
|
31
|
+
10:36:23 - Step 23/35: wait - 1000ms
|
|
32
|
+
10:36:24 - Step 24/35: log - ── Scene 7: Actions reference
|
|
33
|
+
[log] ── Scene 7: Actions reference
|
|
34
|
+
10:36:25 - Step 25/35: click - link "Permalink: Next reading"
|
|
35
|
+
10:36:26 - Step 26/35: click - Browse every action
|
|
36
|
+
10:36:28 - Step 27/35: wait - navigate, click, type, expect_visible, …
|
|
37
|
+
10:36:29 - Step 28/35: log - ── Scene 8: Locators
|
|
38
|
+
[log] ── Scene 8: Locators
|
|
39
|
+
10:36:30 - Step 29/35: click - link "Permalink: Related"
|
|
40
|
+
10:36:31 - Step 30/35: click - How to target UI elements
|
|
41
|
+
10:36:33 - Step 31/35: wait - 800ms
|
|
42
|
+
10:36:34 - Step 32/35: click - Unique aria-labels keep demos stable
|
|
43
|
+
10:36:35 - Step 33/35: wait - 1400ms
|
|
44
|
+
10:36:37 - Step 34/35: transition - fade 400ms
|
|
45
|
+
10:36:38 - Step 35/35: title_card - Try https://github.com/a1knowhow/demo-maker
|
|
46
|
+
Re-encoded to H.264 MP4 (CRF 18). Playback speed 1.1×. Upscaled 1440x810 → 1920x1080.
|
|
47
|
+
Timing saved: demo-maker/output/videos/demo1-introduction-2026-09-12-0735.timing.json
|
|
48
|
+
Scenario completed successfully.
|
|
49
|
+
Video saved: demo-maker/output/videos/demo1-introduction-2026-09-12-0735.mp4
|
|
50
|
+
Timing saved: demo-maker/output/videos/demo1-introduction-2026-09-12-0735.timing.json
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: open-repository
|
|
2
|
+
description: Fragment — open the demo-maker repository home page
|
|
3
|
+
base_url: https://github.com
|
|
4
|
+
steps:
|
|
5
|
+
- action: navigate
|
|
6
|
+
url: /a1knowhow/demo-maker
|
|
7
|
+
caption: "Open the demo-maker repository"
|
|
8
|
+
|
|
9
|
+
- action: expect_visible
|
|
10
|
+
locator:
|
|
11
|
+
role: link
|
|
12
|
+
name: demo-maker
|
|
13
|
+
|
|
14
|
+
- action: wait
|
|
15
|
+
timeout_ms: 800
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: smoke
|
|
2
|
+
description: Minimal public example — opens example.com (no auth)
|
|
3
|
+
base_url: https://example.com
|
|
4
|
+
video:
|
|
5
|
+
enabled: true
|
|
6
|
+
dir: output/videos
|
|
7
|
+
filename: smoke-[date:"YYYY-MM-DD-HHmm"].mp4
|
|
8
|
+
size: { width: 1920, height: 1080 }
|
|
9
|
+
viewport: { width: 1440, height: 810 } # optional; fit laptop screen when headed
|
|
10
|
+
steps:
|
|
11
|
+
- action: navigate
|
|
12
|
+
url: "/"
|
|
13
|
+
caption: "Example domain"
|
|
14
|
+
- action: expect_visible
|
|
15
|
+
locator:
|
|
16
|
+
text: Example Domain
|
|
17
|
+
- action: wait
|
|
18
|
+
timeout_ms: 800
|
package/llms.txt
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# demo-maker
|
|
2
|
+
|
|
3
|
+
> Demo videos as code — YAML + Playwright → polished, consistent product feature videos. Configure once, re-run forever.
|
|
4
|
+
|
|
5
|
+
Open-source CLI (`@a1knowhow/demo-maker`). Requires Node.js 20+, Playwright Chromium, and ffmpeg. Apache-2.0.
|
|
6
|
+
|
|
7
|
+
Prefer markdown docs linked below over scraping HTML. Schema lives in source TypeScript.
|
|
8
|
+
|
|
9
|
+
## Docs
|
|
10
|
+
|
|
11
|
+
- [README](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/README.md): Overview, install, quick start
|
|
12
|
+
- [Getting started](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/getting-started.md): First scenario and MP4
|
|
13
|
+
- [CLI](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/cli.md): Commands and flags
|
|
14
|
+
- [Scenario schema](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/scenario-schema.md): YAML structure
|
|
15
|
+
- [Actions](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/actions.md): Step actions
|
|
16
|
+
- [Locators](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/locators.md): Targeting UI
|
|
17
|
+
- [Video & pacing](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/video-and-pacing.md): Recording quality
|
|
18
|
+
- [Recording](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/recording.md): Live record to YAML
|
|
19
|
+
- [Repair](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/repairing-scenarios.md): Fix after UI changes
|
|
20
|
+
|
|
21
|
+
## Optional
|
|
22
|
+
|
|
23
|
+
- [Docs index](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/README.md)
|
|
24
|
+
- [Imports & fragments](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/imports-and-fragments.md)
|
|
25
|
+
- [Variables](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/variables.md)
|
|
26
|
+
- [URL capture](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/url-capture.md)
|
|
27
|
+
- [Library API](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/docs/library-api.md)
|
|
28
|
+
- [Types](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/src/types.ts): Typed scenario schema
|
|
29
|
+
- [Example scenario](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/examples/smoke.yaml)
|
|
30
|
+
- [Getting started walkthrough](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/examples/demo1-introduction.yaml)
|
|
31
|
+
- [GitHub Actions walkthrough](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/examples/demo2-github-actions.yaml)
|
|
32
|
+
- [Security policy](https://raw.githubusercontent.com/a1knowhow/demo-maker/main/SECURITY.md)
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@a1knowhow/demo-maker",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Demo videos as code — configure once, re-run forever. YAML + Playwright → polished, consistent product feature videos.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"demo-maker": "./bin/demo-maker.mjs"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"bin",
|
|
15
|
+
"src",
|
|
16
|
+
"examples",
|
|
17
|
+
"docs",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"README.md",
|
|
20
|
+
"SECURITY.md",
|
|
21
|
+
"llms.txt"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"automation",
|
|
25
|
+
"cli",
|
|
26
|
+
"demo",
|
|
27
|
+
"ffmpeg",
|
|
28
|
+
"playwright",
|
|
29
|
+
"product-demo",
|
|
30
|
+
"screen-recording",
|
|
31
|
+
"video",
|
|
32
|
+
"yaml"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/a1knowhow/demo-maker.git"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/a1knowhow/demo-maker#readme",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/a1knowhow/demo-maker/issues"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"demo-maker": "tsx src/main.ts",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:watch": "vitest",
|
|
52
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
53
|
+
"pack:check": "npm pack --dry-run",
|
|
54
|
+
"prepublishOnly": "npm test && npm run typecheck"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"playwright": "^1.62.1",
|
|
58
|
+
"tsx": "^4.23.13",
|
|
59
|
+
"yaml": "^2.9.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^22.13.10",
|
|
63
|
+
"typescript": "^5.9.3",
|
|
64
|
+
"vitest": "^3.0.9"
|
|
65
|
+
}
|
|
66
|
+
}
|