@remixmate/cli 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.
Files changed (122) hide show
  1. package/README.md +100 -83
  2. package/README.zh-CN.md +183 -0
  3. package/dist/handlers/gen-digital-human.d.ts +12 -0
  4. package/dist/handlers/gen-digital-human.js +157 -0
  5. package/dist/handlers/gen-image.js +5 -41
  6. package/dist/handlers/gen-video.d.ts +18 -0
  7. package/dist/handlers/gen-video.js +153 -0
  8. package/dist/handlers/gen-voice.js +2 -12
  9. package/dist/handlers/index.js +4 -2
  10. package/dist/handlers/shared.d.ts +26 -0
  11. package/dist/handlers/shared.js +66 -0
  12. package/dist/http.d.ts +4 -2
  13. package/dist/http.js +5 -3
  14. package/dist/manifest.json +27 -24
  15. package/dist/registry.d.ts +5 -16
  16. package/dist/registry.js +11 -17
  17. package/dist/skill-schema.d.ts +47 -0
  18. package/dist/skill-schema.js +49 -0
  19. package/package.json +5 -4
  20. package/skills/export-jianying/scripts/gen_jianying_draft.py +4 -4
  21. package/skills/gen-digital-human/SKILL.md +23 -46
  22. package/skills/gen-digital-human/skill.json +3 -2
  23. package/skills/gen-digital-human/version.json +1 -1
  24. package/skills/gen-image/SKILL.md +2 -2
  25. package/skills/gen-image/skill.json +2 -1
  26. package/skills/gen-image/version.json +1 -1
  27. package/skills/gen-script/SKILL.md +13 -13
  28. package/skills/gen-script/scripts/gen_script.py +11 -12
  29. package/skills/gen-video/SKILL.md +22 -33
  30. package/skills/gen-video/skill.json +3 -2
  31. package/skills/gen-video/version.json +1 -1
  32. package/skills/gen-voice/SKILL.md +1 -1
  33. package/skills/gen-voice/version.json +1 -1
  34. package/skills/prepare-video-assets/SKILL.md +2 -2
  35. package/skills/prepare-video-assets/skill.json +1 -1
  36. package/skills/render-video/scripts/render_video.py +8 -8
  37. package/skills/{template-bind → template-registry}/README.md +4 -4
  38. package/skills/{template-bind → template-registry}/SKILL.md +14 -12
  39. package/skills/template-registry/scripts/list_templates.py +129 -0
  40. package/skills/{template-bind → template-registry}/scripts/match_template.py +4 -3
  41. package/skills/{template-bind → template-registry}/scripts/registry_loader.py +1 -1
  42. package/skills/{template-bind → template-registry}/scripts/template_paths.py +7 -7
  43. package/skills/{template-bind → template-registry}/skill.json +6 -5
  44. package/skills/template-registry/version.json +7 -0
  45. package/skills/{template-bind → template-registry}/video_dsl/README.md +2 -2
  46. package/skills/{template-bind → template-registry}/video_dsl/runtime/__init__.py +1 -1
  47. package/skills/video-parser/SKILL.md +47 -160
  48. package/skills/video-parser/skill.json +3 -4
  49. package/skills/video-parser/version.json +1 -1
  50. package/skills/web-capture/SKILL.md +407 -116
  51. package/skills/web-capture/scripts/_media_screenshot/__init__.py +21 -0
  52. package/skills/web-capture/scripts/_media_screenshot/bootstrap.py +34 -0
  53. package/skills/web-capture/scripts/_media_screenshot/browser.py +86 -0
  54. package/skills/web-capture/scripts/_media_screenshot/cli_args.py +111 -0
  55. package/skills/web-capture/scripts/_media_screenshot/js/arrow.js +86 -0
  56. package/skills/web-capture/scripts/_media_screenshot/js/caption.js +27 -0
  57. package/skills/web-capture/scripts/_media_screenshot/js/clear_overlay.js +5 -0
  58. package/skills/web-capture/scripts/_media_screenshot/js/cursor_bootstrap.js +26 -0
  59. package/skills/web-capture/scripts/_media_screenshot/js/cursor_hide.js +6 -0
  60. package/skills/web-capture/scripts/_media_screenshot/js/cursor_move.js +12 -0
  61. package/skills/web-capture/scripts/_media_screenshot/js/cursor_ripple.js +25 -0
  62. package/skills/web-capture/scripts/_media_screenshot/js/element_zoom.js +72 -0
  63. package/skills/web-capture/scripts/_media_screenshot/js/element_zoom_restore.js +21 -0
  64. package/skills/web-capture/scripts/_media_screenshot/js/fade_out_overlays.js +10 -0
  65. package/skills/web-capture/scripts/_media_screenshot/js/focus.js +38 -0
  66. package/skills/web-capture/scripts/_media_screenshot/js/highlight.js +97 -0
  67. package/skills/web-capture/scripts/_media_screenshot/js/overlay_bootstrap.js +19 -0
  68. package/skills/web-capture/scripts/_media_screenshot/js/page_zoom_in.js +54 -0
  69. package/skills/web-capture/scripts/_media_screenshot/js/page_zoom_out.js +11 -0
  70. package/skills/web-capture/scripts/_media_screenshot/js/page_zoom_restore.js +13 -0
  71. package/skills/web-capture/scripts/_media_screenshot/js/redact.js +33 -0
  72. package/skills/web-capture/scripts/_media_screenshot/js/scroll.js +26 -0
  73. package/skills/web-capture/scripts/_media_screenshot/js/scroll_through.js +18 -0
  74. package/skills/web-capture/scripts/_media_screenshot/js/sequence.js +76 -0
  75. package/skills/web-capture/scripts/_media_screenshot/js/settled_check.js +15 -0
  76. package/skills/web-capture/scripts/_media_screenshot/js/title_card.js +71 -0
  77. package/skills/web-capture/scripts/_media_screenshot/js_loader.py +14 -0
  78. package/skills/web-capture/scripts/_media_screenshot/overlay.py +17 -0
  79. package/skills/web-capture/scripts/_media_screenshot/recording.py +100 -0
  80. package/skills/web-capture/scripts/_media_screenshot/scenes/__init__.py +39 -0
  81. package/skills/web-capture/scripts/_media_screenshot/scenes/arrow.py +50 -0
  82. package/skills/web-capture/scripts/_media_screenshot/scenes/caption.py +20 -0
  83. package/skills/web-capture/scripts/_media_screenshot/scenes/click.py +35 -0
  84. package/skills/web-capture/scripts/_media_screenshot/scenes/code_lines.py +54 -0
  85. package/skills/web-capture/scripts/_media_screenshot/scenes/focus.py +24 -0
  86. package/skills/web-capture/scripts/_media_screenshot/scenes/highlight.py +54 -0
  87. package/skills/web-capture/scripts/_media_screenshot/scenes/hover.py +28 -0
  88. package/skills/web-capture/scripts/_media_screenshot/scenes/redact.py +32 -0
  89. package/skills/web-capture/scripts/_media_screenshot/scenes/scroll.py +32 -0
  90. package/skills/web-capture/scripts/_media_screenshot/scenes/sequence.py +43 -0
  91. package/skills/web-capture/scripts/_media_screenshot/scenes/title_card.py +20 -0
  92. package/skills/web-capture/scripts/_media_screenshot/scenes/type.py +42 -0
  93. package/skills/web-capture/scripts/_media_screenshot/scenes/wait.py +9 -0
  94. package/skills/web-capture/scripts/_media_screenshot/scenes/zoom.py +72 -0
  95. package/skills/web-capture/scripts/_media_screenshot/screenshot.py +166 -0
  96. package/skills/web-capture/scripts/_media_screenshot/storyboard.py +88 -0
  97. package/skills/web-capture/scripts/_media_screenshot/template.py +119 -0
  98. package/skills/web-capture/scripts/_media_screenshot/trim.py +82 -0
  99. package/skills/web-capture/scripts/record.py +186 -0
  100. package/skills/web-capture/scripts/screenshot.py +94 -0
  101. package/skills/web-capture/skill.json +14 -14
  102. package/skills/web-capture/templates/github-code-tour.json +59 -0
  103. package/skills/web-capture/templates/github-readme-tour.json +77 -0
  104. package/skills/web-capture/templates/github-repo-intro.json +74 -0
  105. package/skills/web-capture/version.json +2 -2
  106. package/dist/handlers/template-bind.d.ts +0 -15
  107. package/dist/handlers/template-bind.js +0 -149
  108. package/skills/gen-digital-human/scripts/gen_digital_human_video.py +0 -574
  109. package/skills/gen-video/scripts/gen_video.py +0 -591
  110. package/skills/template-bind/version.json +0 -7
  111. package/skills/web-capture/scripts/web_capture.py +0 -461
  112. /package/skills/{template-bind → template-registry}/scripts/check_contracts.py +0 -0
  113. /package/skills/{template-bind → template-registry}/scripts/render_job_client.py +0 -0
  114. /package/skills/{template-bind → template-registry}/scripts/sync_registry.py +0 -0
  115. /package/skills/{template-bind → template-registry}/video_dsl/runtime/dsl_validator.py +0 -0
  116. /package/skills/{template-bind → template-registry}/video_dsl/runtime/prompt_enhancer.py +0 -0
  117. /package/skills/{template-bind → template-registry}/video_dsl/runtime/template_binder.py +0 -0
  118. /package/skills/{template-bind → template-registry}/video_dsl/runtime/timeline_compiler.py +0 -0
  119. /package/skills/{template-bind → template-registry}/video_dsl/schema/render-plan-v1alpha1.json +0 -0
  120. /package/skills/{template-bind → template-registry}/video_dsl/schema/template-binding-v1alpha1.json +0 -0
  121. /package/skills/{template-bind → template-registry}/video_dsl/schema/template-definition-v1alpha1.json +0 -0
  122. /package/skills/{template-bind → template-registry}/video_dsl/schema/video-dsl-v1alpha1.json +0 -0
@@ -1,183 +1,474 @@
1
1
  ---
2
2
  name: web-capture
3
3
  description: |
4
- Web page capture skill. Drives a headless browser (Playwright) to turn a URL into reusable visual assets — viewport / full-page screenshots, or a smooth top-to-bottom scrolling video (MP4). These assets are the upstream material for the `screen-walkthrough` template.
5
-
6
- Use this skill as soon as the user mentions any of these intents:
7
- - Screenshot a web page / website / a specific URL, full-page screenshot
8
- - Record a page scrolling top-to-bottom into a video, "scroll video" of a site
9
- - Produce screen-recording / walkthrough footage of a GitHub repo, landing page, or product page
10
-
11
- Even when the user does not say "Playwright" or "browser", use this skill whenever they want to turn a web address into a screenshot or a scrolling demo video.
4
+ 网页截屏 / 录屏 / 分镜视频技能,基于 Playwright Python,对任意 URL 落盘到本地。
5
+ 支持:整页 / 元素 / 区域截屏、固定时长 / 条件触发录屏、自动滚动录屏、分镜视频(高亮 / 聚焦 / 放大 / 滚动 / 虚拟光标点击 / 字幕 / 标题卡 / 箭头 / 编号序列 / 打码 / 代码行高亮)。
6
+
7
+ 当用户提到以下任何需求时,立即使用本 skill:
8
+ - 网页截图、网页截屏、整页截图、长截图、full page screenshot
9
+ - 截某个元素 / 区域、局部截屏、focus 某个区域
10
+ - 录屏、网页录制、录制视频、录一段操作、生成 webm
11
+ - 滚动录屏、页面从头划到尾的视频
12
+ - 多场景视频、分镜视频、storyboard、按时间线编排(红框高亮 / 局部放大 / 滚动介绍)
13
+ - 演示点击 / 输入 / 悬停(虚拟鼠标 + 涟漪 / 打字机 / 触发 tooltip)
14
+ - 加箭头、加字幕条、加标题卡 / 片头片尾、按顺序编号高亮多个区域
15
+ - 截图前隐藏元素 / 涂盖打码(cookie banner、敏感信息)
16
+ - 给静态截图加注释(红框 / 箭头 / 标签)
17
+ - 高亮某文件的 L5-L20 代码行
18
+ - 等待元素出现/消失后再截屏或停止录屏
19
+ - 移动端 / 设备模拟(iPhone / Pixel 等)
20
+ - 带 cookie / 登录态、storageState 截屏 / 录屏
21
+
22
+ 即使用户没明确提「playwright」,只要他们想对一个 URL 截屏/录屏/做分镜视频并拿到本地文件,也要使用本 skill。
12
23
  triggers:
13
- - Screenshot a web page / website / a specific URL, full-page screenshot
14
- - Record a page scrolling top-to-bottom into a video, "scroll video" of a site
15
- - Produce screen-recording / walkthrough footage of a GitHub repo, landing page, or product page
24
+ - 网页截图、网页截屏、整页截图、长截图、full page screenshot
25
+ - 截某个元素 / 区域、局部截屏、focus 某个区域
26
+ - 录屏、网页录制、录制视频、录一段操作、生成 webm
27
+ - 滚动录屏、页面从头划到尾的视频
28
+ - 多场景视频、分镜视频、storyboard、按时间线编排
29
+ - 演示点击 / 输入 / 悬停(虚拟鼠标 + 涟漪 / 打字机 / 触发 tooltip)
30
+ - 加箭头、加字幕条、加标题卡 / 片头片尾、按顺序编号高亮多个区域
31
+ - 截图前隐藏元素 / 涂盖打码、给静态截图加注释(红框 / 箭头 / 标签)
32
+ - 移动端 / 设备模拟、带 cookie / 登录态截屏 / 录屏
16
33
  ---
17
34
 
18
- # Web Page Capture Skill
35
+ # 网页截屏 / 录屏 Skill
19
36
 
20
- Drives a **headless browser via Playwright (Python)** to capture a web page as reusable visual assets:
37
+ 两个入口脚本,纯 Python 实现:
21
38
 
22
- - **Screenshots**viewport, full scrollable page, or a single element.
23
- - **Scroll video** opens the page and smoothly scrolls top-to-bottom, capturing frames and assembling a deterministic MP4 with ffmpeg.
24
- - **Record video** — Playwright's native context recording (webm), transcoded to MP4.
39
+ - **`scripts/screenshot.py`**截图。整页 / 视口 / 元素 / 区域。
40
+ - **`scripts/record.py`**录屏(webm)。固定时长 / 条件停止 / 自动滚动 / 分镜(storyboard)/ **模板(templates/)**。
25
41
 
26
- The MP4 / PNG output is designed to feed the **`screen-walkthrough`** template's screen stage.
42
+ 底层共享 **`scripts/_media_screenshot/`** 包,封装 Playwright Python API + 所有浏览器侧 JS 注入(overlay / zoom / scroll 等)。
27
43
 
28
- ## Dependencies
44
+ **模板目录** `templates/`:参数化的 storyboard,常见场景一行命令出片。详见下文 "分镜视频 / 模板"。
29
45
 
30
- Aligned with method 1 of the design the runtime provides the browser, the skill does not bundle it.
46
+ > 路径约定:读取系统注入的 `Base directory for this skill: <path>` 作为 `<SkillDir>`,命令里的 `<SkillDir>` 替换为该路径,切勿硬编码绝对路径。`python` macOS 上可能需写成 `python3`。
31
47
 
32
- - **Python 3** with **Playwright**: `pip install playwright` then `playwright install chromium` (one-time; downloads the browser binary).
33
- - **ffmpeg**: must be installed locally (`brew install ffmpeg`) — used to assemble the scroll-video frames and transcode native recordings to MP4. Same dependency the `video-parser` skill relies on.
48
+ ## 前置依赖
34
49
 
35
- The script checks for both at startup and prints an actionable install hint if either is missing (`__progress__` phase `web-capture:preflight`).
50
+ - **Python 3.9+**
51
+ - **`playwright` pip 包 + chromium 内核**:首次运行**自动 bootstrap**——脚本检测到缺包会执行 `pip install playwright` + `playwright install chromium`。
52
+ - 也可提前手动装:
53
+ ```bash
54
+ pip install playwright
55
+ playwright install chromium
56
+ ```
57
+ - **ffmpeg**(可选):仅 storyboard 模式用来精确裁剪开头加载段。无 ffmpeg 时会保留原片并提示。
36
58
 
37
- ## Multi-user isolation & security
59
+ ## 截屏 `screenshot.py`
38
60
 
39
- ab-agent spawns one fresh `python3` process per tool call (`spawn('python3', ...)`), and this script launches its own Playwright browser instance per run and closes it on exit. So **concurrent users are isolated at the process + browser-instance level** — no cookie/session bleed between users. The skill hardens a few defaults on top of that:
61
+ ### 最小用法
40
62
 
41
- - **Unique output dir**: when `--output-dir` is omitted, output goes to a unique system temp dir (`tempfile.mkdtemp`), never the script's cwd. Concurrent calls can't collide or pollute `skills/`.
42
- - **Temp cleanup**: per-frame images (`_frames/`) and native-recording webm (`_rec/`) are removed in a `finally` block, so failures don't leak temp data.
43
- - **SSRF guard**: the user-supplied `url` is validated before any browser launch — only `http`/`https`, and the resolved IP must not be private / loopback / link-local / reserved / cloud-metadata (`169.254.169.254`). Rejection exits with code 5.
44
- - **Sandbox on by default**: Chromium keeps its sandbox; `--no-sandbox` is only added when `WEB_CAPTURE_NO_SANDBOX=1`.
63
+ ```bash
64
+ python3 <SkillDir>/scripts/screenshot.py \
65
+ --url "https://example.com" \
66
+ --output "example.png"
67
+ ```
45
68
 
46
- Not handled in-skill (orchestration concern): **concurrency limits**. Each browser is ~150–300MB; many simultaneous `scroll-video` calls can exhaust host memory. Gate concurrency at the ab-agent layer or via host resources.
69
+ ### 整页截图
47
70
 
48
- ### Environment variables
71
+ ```bash
72
+ python3 <SkillDir>/scripts/screenshot.py \
73
+ --url "https://example.com" \
74
+ --output "example_full.png" \
75
+ --full-page
76
+ ```
49
77
 
50
- | Env var | Description | Default |
51
- |---------|-------------|---------|
52
- | `WEB_CAPTURE_BROWSER` | Default browser engine (`--browser` overrides). | `chromium` |
53
- | `WEB_CAPTURE_OUTPUT_BASE` | Base dir for the auto-created temp output dir. | system tempdir |
54
- | `WEB_CAPTURE_NO_SANDBOX` | Set `1` to add `--no-sandbox` (only for containers that can't use user namespaces). | unset (sandbox on) |
55
- | `WEB_CAPTURE_ALLOW_PRIVATE_HOSTS` | Set `1` to disable the private/internal-IP SSRF block (single-tenant / trusted intranet only). | unset (block on) |
56
- | `PLAYWRIGHT_BROWSERS_PATH` | Playwright browser binary location (provided by the runtime). | Playwright default |
78
+ ### 元素截屏(局部 focus)
57
79
 
58
- ## Operations
80
+ CSS 选择器截**元素本身**(自动 `scrollIntoViewIfNeeded`):
59
81
 
60
- 1. **Script path**: read the system-injected `Base directory for this skill: <path>` as `<SkillDir>` and always use `<SkillDir>/scripts/web_capture.py`. Never hard-code paths.
61
- 2. **Pick the mode**: `screenshot` (default), `scroll-video`, or `record-video`.
62
- 3. **Run the capture** and wait for it to finish.
63
- 4. **Inspect the result**: a `capture.json` manifest plus the produced `*.png` / `*.mp4` under `--output-dir`.
82
+ ```bash
83
+ python3 <SkillDir>/scripts/screenshot.py \
84
+ --url "https://example.com" \
85
+ --output "card.png" \
86
+ --selector ".card.featured" \
87
+ --wait-for-selector ".card.featured"
88
+ ```
89
+
90
+ 如不希望自动滚动:加 `--no-scroll-into-view`。
64
91
 
65
- ### Viewport screenshot
92
+ ### 区域截屏(像素矩形 clip)
66
93
 
67
94
  ```bash
68
- python3 <SkillDir>/scripts/web_capture.py \
69
- --url "https://github.com/microsoft/playwright" \
70
- --mode screenshot
95
+ # 全页像素坐标
96
+ python3 <SkillDir>/scripts/screenshot.py \
97
+ --url "https://example.com" \
98
+ --output "region.png" \
99
+ --clip "100,200,600,400" # x, y, w, h
100
+
101
+ # 以元素为基点偏移裁剪(先 scrollIntoView 该元素,再用其 boundingBox 起点 + 偏移裁剪)
102
+ python3 <SkillDir>/scripts/screenshot.py \
103
+ --url "https://example.com" \
104
+ --output "region_in_card.png" \
105
+ --selector ".card" \
106
+ --clip "0,0,300,180"
71
107
  ```
72
108
 
73
- ### Full-page screenshot (whole scrollable page)
109
+ ### 移动端 / 设备模拟
74
110
 
75
111
  ```bash
76
- python3 <SkillDir>/scripts/web_capture.py \
77
- --url "https://example.com" \
78
- --mode screenshot \
112
+ python3 <SkillDir>/scripts/screenshot.py \
113
+ --url "https://m.example.com" \
114
+ --output "mobile.png" \
115
+ --device "iPhone 15 Pro" \
79
116
  --full-page
80
117
  ```
81
118
 
82
- ### Element-only screenshot
119
+ 自定义 viewport:
120
+
121
+ ```bash
122
+ python3 <SkillDir>/scripts/screenshot.py \
123
+ --url "https://example.com" \
124
+ --output "wide.png" \
125
+ --viewport "1440,900"
126
+ ```
127
+
128
+ ### 等待动态内容
129
+
130
+ ```bash
131
+ # 等待选择器出现
132
+ python3 <SkillDir>/scripts/screenshot.py \
133
+ --url "https://example.com" \
134
+ --output "ready.png" \
135
+ --wait-for-selector "main .loaded" \
136
+ --timeout 30000
137
+
138
+ # 固定延时(适合纯前端动画)
139
+ python3 <SkillDir>/scripts/screenshot.py \
140
+ --url "https://example.com" \
141
+ --output "delayed.png" \
142
+ --wait-for-timeout 2000
143
+ ```
144
+
145
+ ### 隐藏 / 涂盖元素
83
146
 
84
147
  ```bash
85
- python3 <SkillDir>/scripts/web_capture.py \
148
+ # 隐藏 cookie banner、登录浮层等干扰元素(可重复)
149
+ python3 <SkillDir>/scripts/screenshot.py \
86
150
  --url "https://example.com" \
87
- --mode screenshot \
88
- --selector "#readme"
151
+ --output "clean.png" \
152
+ --hide-selector ".cookie-banner" \
153
+ --hide-selector "#login-modal"
154
+
155
+ # 涂盖敏感信息(Playwright 原生 mask,默认粉色,可自定义颜色)
156
+ python3 <SkillDir>/scripts/screenshot.py \
157
+ --url "https://app.example.com/dashboard" \
158
+ --output "redacted.png" \
159
+ --mask-selector ".user-email" \
160
+ --mask-selector ".api-token" \
161
+ --mask-color "#222"
162
+ ```
163
+
164
+ ### 静态截图加注释(红框 / 箭头 / 字幕 / 编号 / 打码)
165
+
166
+ `--annotate` 接受一份 JSON(路径或字符串),里面用 storyboard 的场景类型描述要画的注解。所有场景在截屏前一次性画好。
167
+
168
+ ```bash
169
+ python3 <SkillDir>/scripts/screenshot.py \
170
+ --url "https://github.com/user/repo" \
171
+ --output "annotated.png" \
172
+ --annotate annotate.json
173
+ ```
174
+
175
+ `annotate.json` 示例:
176
+
177
+ ```json
178
+ {
179
+ "annotations": [
180
+ { "type": "highlight", "selector": "strong[itemprop=\"name\"] a",
181
+ "color": "#ff3b30", "label": "看这里" },
182
+ { "type": "arrow",
183
+ "from": { "selector": "#repo-stars-counter-star" },
184
+ "to": { "selector": "strong[itemprop=\"name\"] a", "side": "right" },
185
+ "color": "#ff9500", "label": "⭐" },
186
+ { "type": "caption", "text": "项目主页", "position": "bottom" },
187
+ { "type": "redact", "selectors": [".user-email"], "mode": "blur" }
188
+ ],
189
+ "settleMs": 900
190
+ }
89
191
  ```
90
192
 
91
- ### Scrolling video (top-to-bottom)
193
+ > 顶层也可以直接是数组:`[{"type": "highlight", ...}]`。`settleMs` 是画完后等动画稳定再截屏的时长,默认 900ms。
194
+
195
+ ### 带登录态截屏
196
+
197
+ 两种方式,二选一:
198
+
199
+ **A. 直接传 storageState(推荐)**
92
200
 
93
201
  ```bash
94
- python3 <SkillDir>/scripts/web_capture.py \
95
- --url "https://github.com/microsoft/playwright" \
96
- --mode scroll-video \
97
- --width 1280 --height 720 \
98
- --fps 30 \
99
- --scroll-duration 8
202
+ python3 <SkillDir>/scripts/screenshot.py \
203
+ --url "https://app.example.com/dashboard" \
204
+ --output "dashboard.png" \
205
+ --storage-state "./auth.json"
100
206
  ```
101
207
 
102
- Produces a deterministic `scroll.mp4` fps, resolution and scroll speed are all controllable, which keeps the output aligned with the `screen-walkthrough` timeline.
208
+ > `auth.json` 可由 `npx playwright codegen --save-storage=auth.json <url>` 在登录后导出。
103
209
 
104
- ### Native recording (webm → mp4)
210
+ **B. 仅传 cookies JSON**
105
211
 
106
212
  ```bash
107
- python3 <SkillDir>/scripts/web_capture.py \
213
+ python3 <SkillDir>/scripts/screenshot.py \
214
+ --url "https://app.example.com/dashboard" \
215
+ --output "dashboard.png" \
216
+ --cookies "./cookies.json"
217
+ ```
218
+
219
+ cookies 顶层必须是数组,每项遵循 Playwright cookie 格式:
220
+
221
+ ```json
222
+ [
223
+ {"name": "session", "value": "abc", "domain": ".example.com", "path": "/", "httpOnly": true, "secure": true}
224
+ ]
225
+ ```
226
+
227
+ ### screenshot.py 参数
228
+
229
+ | 参数 | 说明 | 默认 |
230
+ |------|------|------|
231
+ | `-u` / `--url` | 目标 URL(必填) | — |
232
+ | `-o` / `--output` | 本地输出路径 | `screenshot.png` |
233
+ | `-b` / `--browser` | `chromium` / `firefox` / `webkit` | `chromium` |
234
+ | `--device` | 设备模拟名 | 无 |
235
+ | `--viewport` | `"宽,高"` | 无 |
236
+ | `--full-page` | 整页截屏 | 关 |
237
+ | `--selector` | 截元素本身 | 无 |
238
+ | `--clip` | `"x,y,w,h"` 区域截屏;和 selector 同设时基于元素 boundingBox 偏移 | 无 |
239
+ | `--no-scroll-into-view` | selector 模式下关闭自动滚动 | 关 |
240
+ | `--wait-for-selector` | 操作前等待选择器出现 | 无 |
241
+ | `--wait-for-timeout` | 操作前固定等待(ms) | 无 |
242
+ | `--color-scheme` | `light` / `dark` / `no-preference` | 无 |
243
+ | `--user-agent` | 覆盖 UA | 无 |
244
+ | `--timeout` | Playwright 全局超时(ms) | 无 |
245
+ | `--ignore-https-errors` | 忽略证书错误 | 关 |
246
+ | `--storage-state` | storageState JSON 文件路径 | 无 |
247
+ | `--cookies` | cookies JSON 字符串或文件 | 无 |
248
+ | `--hide-selector` | 截屏前 display:none 该选择器,可重复 | 无 |
249
+ | `--mask-selector` | Playwright 原生 mask 涂盖该选择器,可重复 | 无 |
250
+ | `--mask-color` | mask 填充色 | Playwright 粉 |
251
+ | `--annotate` | 注释 JSON 文件或字符串(数组或 `{annotations, settleMs}`) | 无 |
252
+
253
+ ## 录屏 `record.py`
254
+
255
+ > 输出为 **`.webm`**(Playwright 录屏的原生格式)。如需 mp4,请用 ffmpeg 后处理。
256
+
257
+ ### 固定时长录屏
258
+
259
+ ```bash
260
+ python3 <SkillDir>/scripts/record.py \
108
261
  --url "https://example.com" \
109
- --mode record-video \
110
- --scroll-duration 6
262
+ --output "demo.webm" \
263
+ --viewport "1280,720" \
264
+ --duration 8000
111
265
  ```
112
266
 
113
- Simpler than frame-capture but the frame rate is not precisely controllable; prefer `scroll-video` when timing matters.
267
+ ### 等条件结束(推荐配合 max-duration 防卡死)
268
+
269
+ ```bash
270
+ # 等到 "加载完成" 元素出现就停
271
+ python3 <SkillDir>/scripts/record.py \
272
+ --url "https://example.com" \
273
+ --output "loaded.webm" \
274
+ --stop-when-selector "div.loaded" \
275
+ --max-duration 30000
276
+
277
+ # 等到 loading 转圈消失就停
278
+ python3 <SkillDir>/scripts/record.py \
279
+ --url "https://example.com" \
280
+ --output "spinner_done.webm" \
281
+ --stop-when-hidden ".spinner" \
282
+ --max-duration 30000
283
+ ```
114
284
 
115
- ### JSON pipeline mode
285
+ ### 组合:先满足谁就停
116
286
 
117
287
  ```bash
118
- python3 <SkillDir>/scripts/web_capture.py \
288
+ python3 <SkillDir>/scripts/record.py \
119
289
  --url "https://example.com" \
120
- --mode screenshot \
121
- --json-output
290
+ --output "race.webm" \
291
+ --duration 15000 \
292
+ --stop-when-selector ".done" \
293
+ --max-duration 20000
122
294
  ```
123
295
 
124
- Prints only the final `capture.json` payload to stdout — no progress lines — suitable for piping into downstream skills.
296
+ ### 录屏前等页面就绪
125
297
 
126
- See every option: `python3 <SkillDir>/scripts/web_capture.py --help`.
298
+ ```bash
299
+ python3 <SkillDir>/scripts/record.py \
300
+ --url "https://app.example.com" \
301
+ --output "after_login.webm" \
302
+ --storage-state "./auth.json" \
303
+ --wait-for-selector "#dashboard" \
304
+ --duration 6000
305
+ ```
306
+
307
+ ### 自动滚动录屏(从头划到尾)
127
308
 
128
- ## Common CLI flags
309
+ ```bash
310
+ python3 <SkillDir>/scripts/record.py \
311
+ --url "https://example.com" \
312
+ --output "scroll.webm" \
313
+ --viewport "1440,900" \
314
+ --scroll-through \
315
+ --scroll-step 50 \
316
+ --scroll-interval 60 \
317
+ --scroll-pause-top 1000 \
318
+ --scroll-pause-bottom 1500
319
+ ```
129
320
 
130
- | Flag | Description | Default |
131
- |------|-------------|---------|
132
- | `--url` | Target page URL (http/https). | required |
133
- | `--mode` | `screenshot` / `scroll-video` / `record-video`. | `screenshot` |
134
- | `-o` / `--output-dir` | Output directory. | unique system temp dir |
135
- | `--width` | Viewport width (px). | `1280` |
136
- | `--height` | Viewport height (px). | `720` |
137
- | `--device-scale-factor` | Device pixel ratio (crisp / retina). | `2` |
138
- | `--full-page` | Screenshot mode: capture the whole scrollable page. | off |
139
- | `--selector` | Screenshot mode: capture only this CSS element. | none |
140
- | `--fps` | Scroll / record video frame rate. | `30` |
141
- | `--scroll-duration` | Seconds for the full top-to-bottom scroll. | `8` |
142
- | `--browser` | `chromium` / `firefox` / `webkit`. | `chromium` |
143
- | `--wait-until` | `load` / `domcontentloaded` / `networkidle`. | `networkidle` |
144
- | `--wait-ms` | Extra wait after load before capture (ms). | `1500` |
145
- | `--json-output` | Pipeline mode — JSON-only stdout. | off |
321
+ ### 模板视频(快捷出片)
146
322
 
147
- ## Output layout
323
+ `templates/` 下提供参数化的 storyboard 模板,常见结构一行命令出片:
148
324
 
325
+ ```bash
326
+ # 列出可用模板
327
+ python3 <SkillDir>/scripts/record.py --list-templates
328
+
329
+ # 用 github-repo-intro 模板录任意 GitHub 仓库
330
+ python3 <SkillDir>/scripts/record.py \
331
+ --template github-repo-intro \
332
+ --param repo_url=https://github.com/user/repo \
333
+ --output repo.webm
149
334
  ```
150
- <output-dir>/
151
- ├── capture.json # asset manifest (mode, url, viewport, produced files)
152
- ├── screenshot.png # screenshot mode
153
- └── scroll.mp4 # scroll-video / record-video mode
335
+
336
+ 可重复 `--param key=value` 覆盖模板里的默认值(标签文字、颜色、缩放倍数等)。模板内 `_meta.params` 声明可接收的参数。
337
+
338
+ **当前模板**:
339
+
340
+ | 名字 | 说明 | 必填参数 |
341
+ |------|------|---------|
342
+ | `github-repo-intro` | GitHub 仓库三连:项目名红框 → star 页面 zoom → README 滚动 | `repo_url` |
343
+ | `github-readme-tour` | 标题卡 → 项目名 → 光标 hover star → README 滚动 → 结尾呼吁 | `repo_url` |
344
+ | `github-code-tour` | 跳转到文件 → 高亮行范围 → 字幕讲解 | `file_url` / `start_line` / `end_line` |
345
+
346
+ **新增模板**:在 `<SkillDir>/templates/` 下放一个 JSON,按 storyboard 格式写场景,里面用 `{{var_name}}` 占位。可选加 `_meta.params` 声明参数(标 `required: true` / `default: ...`)。占位单独成串时会自动转 int/float/bool(如 `"scale": "{{zoom_scale}}"` 渲染后是数字)。
347
+
348
+ ### 自定义分镜(storyboard)
349
+
350
+ 把一系列「场景」组合成一段视频,场景间用 overlay 淡入淡出过渡(**不会黑屏**)。
351
+
352
+ ```bash
353
+ python3 <SkillDir>/scripts/record.py \
354
+ --url "https://github.com/user/repo" \
355
+ --output "story.webm" \
356
+ --viewport "1440,900" \
357
+ --storyboard story.json
154
358
  ```
155
359
 
156
- ### `capture.json` shape
360
+ `story.json` 示例:
157
361
 
158
362
  ```json
159
363
  {
160
- "source": { "url": "https://example.com", "finalUrl": "https://example.com/" },
161
- "mode": "scroll-video",
162
- "viewport": { "width": 1280, "height": 720, "deviceScaleFactor": 2 },
163
- "outputs": [
164
- { "type": "video", "localPath": "<output-dir>/scroll.mp4", "fps": 30, "durationSec": 8.0, "frames": 240 }
364
+ "url": "https://github.com/user/repo",
365
+ "viewport": [1440, 900],
366
+ "waitForReadySelectors": ["article.markdown-body"],
367
+ "settleMs": 1200,
368
+ "transition": { "fadeOutMs": 320, "gapMs": 180 },
369
+ "scenes": [
370
+ {
371
+ "type": "highlight",
372
+ "selector": "strong[itemprop=\"name\"] a",
373
+ "color": "#ff3b30",
374
+ "lineWidth": 5,
375
+ "padding": 10,
376
+ "label": "项目名称",
377
+ "duration": 3000
378
+ },
379
+ {
380
+ "type": "zoom",
381
+ "target": "page",
382
+ "selector": "#repo-stars-counter-star",
383
+ "scale": 2.4,
384
+ "rampInMs": 900,
385
+ "holdMs": 2200,
386
+ "rampOutMs": 700,
387
+ "label": "⭐ Star 数量"
388
+ },
389
+ {
390
+ "type": "scroll",
391
+ "fromSelector": "article.markdown-body",
392
+ "step": 50,
393
+ "interval": 70,
394
+ "duration": 10000,
395
+ "holdAtEnd": 800
396
+ }
165
397
  ]
166
398
  }
167
399
  ```
168
400
 
169
- For `screenshot` mode each entry is `{ "type": "image", "localPath": "...", "fullPage": false }`.
170
-
171
- ## Notes on the deprecated `microsoft/playwright-cli`
172
-
173
- The original `microsoft/playwright-cli` repository was **archived (read-only) in January 2021** and is deprecated; its functionality was folded into the main `playwright` package. This skill therefore uses the maintained **Playwright Python** library directly. Pure screenshots are also available via `npx playwright screenshot`, but scrolling-video capture requires script-driven scrolling + frame assembly, which is what this skill provides.
174
-
175
- ## Error handling
176
-
177
- - **Playwright not installed**: the script detects it and prints `pip install playwright && playwright install chromium`.
178
- - **Browser binary missing** (`Executable doesn't exist`): run `playwright install chromium` (or the chosen browser).
179
- - **ffmpeg not installed** (video modes): the script prompts `brew install ffmpeg`.
180
- - **URL rejected (exit 5)**: non-http(s) scheme, or the host resolves to a private/internal/metadata address. Use a public URL, or set `WEB_CAPTURE_ALLOW_PRIVATE_HOSTS=1` for a trusted intranet target.
181
- - **Navigation timeout**: the page took too long; try `--wait-until load` or a smaller `--wait-ms`.
182
- - **Blank / partial capture**: increase `--wait-ms`, or some sites block headless browsers.
183
- - **Network**: verify the URL is reachable from the runtime host.
401
+ **场景类型**:
402
+
403
+ *视觉强调*
404
+ - `highlight` — 元素红框 + 标签,`padding` / `lineWidth` / `color` / `duration`
405
+ - `focus` 周围暗化(spotlight)+ 元素描边 + 标签
406
+ - `zoom` — 放大动画
407
+ - `target: "element"` (默认) — 缩放目标元素本身(弹性曲线)
408
+ - `target: "page"` — 像浏览器 zoom 一样整个页面以目标元素中心放大
409
+ - `scale` / `rampInMs` / `rampOutMs` / `holdMs` / `dim` / `label` / `color`
410
+ - `scroll` 滚动到目标。`fromSelector` 起点、`toSelector` 终点、`step` / `interval` / `duration` / `holdAtEnd`
411
+ - `wait` 单纯停顿 `duration`
412
+
413
+ *交互模拟(虚拟光标)*
414
+ - `click` — 光标飞向元素 → 涟漪 → 真实触发 `el.click()`。`moveMs` / `preHoldMs` / `postHoldMs` / `rippleMs` / `color` / `realClick`(默认 true,可设 false 只动画不点)
415
+ - `type` 光标飞向输入框 focus 逐字打字。`text` / `delay`(ms/字) / `clearFirst` / `moveCursor`
416
+ - `hover` — 光标移到元素并触发原生 hover,常用于悬出 tooltip / hover card。`moveMs` / `holdMs`
417
+
418
+ *注释表达*
419
+ - `arrow` — 从一个锚点画 SVG 弧线箭头指向另一个锚点。锚点 `from` / `to` 形如 `{selector, side}`(side: center/left/right/top/bottom)或 `{x, y}`。`color` / `lineWidth` / `label` / `duration`
420
+ - `sequence` — 多个 selector 顺序出 ①②③ 圆形 badge + 红框 + 标签。`items: [{selector, label}, ...]` / `stepMs` 控制每步间隔 / `color` / `holdMs`
421
+ - `caption` — 顶部/底部滑入字幕条。`text` / `position: top|bottom` / `background` / `color` / `fontSize` / `duration`
422
+ - `title-card` — 全屏开场/结尾大标题卡。`title` / `subtitle`(可选) / `background` / `accent` / `duration`
423
+ - `redact` — 局部打码:blur / pixelate / 实色。`selectors: [...]` 或 `selector` / `mode: "blur" | "pixelate" | "solid"` / `color`(实色时) / `duration`
424
+
425
+ *GitHub 专属*
426
+ - `code-lines` — 高亮连续代码行范围(默认匹配新版 React blob `[data-line-number="{n}"]`;老版 blob 表格用 `linePattern: "#LC{n}"`)。`startLine` / `endLine` / `linePattern` / `color` / `label` / `duration`
427
+
428
+ **自动裁剪**:storyboard 模式录制时会等待页面真正稳定(networkidle + skeleton 消失),并在事后用 ffmpeg 帧精度裁掉开头加载段,最终视频长度 ≈ 各场景总时长。
429
+
430
+ ### record.py 参数
431
+
432
+ | 参数 | 说明 | 默认 |
433
+ |------|------|------|
434
+ | `-u` / `--url` | 目标 URL(必填) | — |
435
+ | `-o` / `--output` | 本地输出 `.webm` 路径 | `recording.webm` |
436
+ | `-b` / `--browser` | `chromium` / `firefox` / `webkit`(建议 chromium) | `chromium` |
437
+ | `--device` | 设备模拟 | 无 |
438
+ | `--viewport` | `"宽,高"` | 无 |
439
+ | `--duration` | 固定录制时长(ms) | 无 |
440
+ | `--stop-when-selector` | 选择器出现就停 | 无 |
441
+ | `--stop-when-hidden` | 选择器消失就停 | 无 |
442
+ | `--max-duration` | 条件模式安全上限(ms) | 60000 |
443
+ | `--scroll-through` | 自动从顶部滚到底部 | 关 |
444
+ | `--scroll-step` | 每步像素 | 60 |
445
+ | `--scroll-interval` | 每步间隔(ms) | 50 |
446
+ | `--scroll-pause-top` | 顶部停留(ms) | 800 |
447
+ | `--scroll-pause-bottom` | 底部停留(ms) | 1200 |
448
+ | `--storyboard` | 分镜 JSON 文件路径 | 无 |
449
+ | `--template` | 模板名(templates/<name>.json);与 `--storyboard` 互斥 | 无 |
450
+ | `--param` | 模板参数 `key=value`,可重复 | 无 |
451
+ | `--list-templates` | 列出可用模板并退出 | — |
452
+ | `--wait-for-selector` | 开始录前等待出现 | 无 |
453
+ | `--wait-for-timeout` | 开始录前固定等待(ms) | 无 |
454
+ | `--color-scheme` | prefers-color-scheme | 无 |
455
+ | `--user-agent` | 覆盖 UA | 无 |
456
+ | `--timeout` | Playwright 全局超时(ms) | 无 |
457
+ | `--ignore-https-errors` | 忽略证书错误 | 关 |
458
+ | `--storage-state` | storageState JSON 文件 | 无 |
459
+ | `--cookies` | cookies JSON 字符串或文件 | 无 |
460
+
461
+ 至少需提供 `--duration` / `--stop-when-selector` / `--stop-when-hidden` / `--scroll-through` / `--storyboard` / `--template` 之一;组合使用时**先满足谁就停**,同时受 `--max-duration` 兜底。模板 / storyboard JSON 可自带 `url`,此时可省略 `--url`。
462
+
463
+ ## 错误处理
464
+
465
+ - **首次跑慢**:脚本自动 `pip install playwright` + `playwright install chromium`,耐心等一次即可。
466
+ - **`Cannot find module 'playwright'` / `ModuleNotFoundError: playwright`**:bootstrap 没装上,手动跑 `pip install playwright && playwright install chromium`。
467
+ - **`Executable doesn't exist`**:浏览器内核没装,跑 `playwright install chromium`(脚本通常会自动触发)。
468
+ - **`Timeout ... exceeded`**:拉大 `--timeout`、`--wait-for-timeout`,或换更稳的 `--wait-for-selector`。
469
+ - **截屏空白 / 没渲染完**:加 `--wait-for-selector` 等业务关键元素;纯动画类用 `--wait-for-timeout`。
470
+ - **`selector "X" has no bounding box`**:元素存在但不可见 / 尺寸为 0;先用 `--wait-for-selector` 等可见。
471
+ - **登录态失效**:重新导出 `storageState` 或更新 `cookies` JSON;`domain` / `path` 必须匹配。
472
+ - **HTTPS 证书报错**:测试环境可加 `--ignore-https-errors`。
473
+ - **录屏文件没产生**:检查 `--output` 后缀是否 `.webm`;查看 stderr 的 `stop reason`。
474
+ - **storyboard 开头还有加载残影**:装 ffmpeg;或加大 `settleMs` / `waitForReadySelectors`。
@@ -0,0 +1,21 @@
1
+ """media-screenshot internal package.
2
+
3
+ Public surface used by the entry-point scripts. Anything else under this
4
+ package is implementation detail.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ from . import cli_args, scenes, template, trim
9
+ from .recording import do_record
10
+ from .screenshot import do_screenshot
11
+ from .storyboard import do_storyboard
12
+
13
+ __all__ = [
14
+ "cli_args",
15
+ "do_record",
16
+ "do_screenshot",
17
+ "do_storyboard",
18
+ "scenes",
19
+ "template",
20
+ "trim",
21
+ ]
@@ -0,0 +1,34 @@
1
+ """Auto-install playwright + chromium on first run."""
2
+ from __future__ import annotations
3
+
4
+ import subprocess
5
+ import sys
6
+
7
+
8
+ def _pip_install(pkg: str) -> None:
9
+ print(f"[bootstrap] pip install {pkg} ...", file=sys.stderr)
10
+ subprocess.check_call(
11
+ [sys.executable, "-m", "pip", "install", "--quiet", pkg]
12
+ )
13
+
14
+
15
+ def _ensure_playwright_pkg() -> None:
16
+ try:
17
+ import playwright.sync_api # noqa: F401
18
+ return
19
+ except ImportError:
20
+ pass
21
+ _pip_install("playwright")
22
+
23
+
24
+ def ensure_chromium() -> None:
25
+ """Install the chromium browser via `playwright install`."""
26
+ print("[bootstrap] playwright install chromium ...", file=sys.stderr)
27
+ subprocess.check_call(
28
+ [sys.executable, "-m", "playwright", "install", "chromium"]
29
+ )
30
+
31
+
32
+ def ensure_runtime() -> None:
33
+ """Make sure the playwright pip package is importable."""
34
+ _ensure_playwright_pkg()