| name | screenshot |
| description | 通用 HTML / 本地 URL 截图工具:读 JSON 配置,逐项打开 → setup → 等待 → 截图 → 输出 PNG 到 tmp/screenshots/<task-id>/。只负责出图——不上传任何平台、不写 spec、不实现业务交互;调用方通过 `setup_js` 提供 raw JS hook 控制业务逻辑。 |
| when_to_use | 用户说「截图 / 生成原型截图 / HTML 截图 / 把 Mermaid 渲染成图」时;
需要把本地 HTML 原型或 Mermaid 图转成 PNG 归档、嵌入文档时。
也被 prd-writer / 发布器 / 报告类 skill 作为出图步骤调用。
边界:要的是可交互 demo 而非静态图 → html-demo。
|
| user-invocable | true |
| effort | low |
| allowed-tools | ["Bash","Read","Write"] |
Screenshot Skill — 通用 HTML 截图
环境依赖:Node.js 18+ · puppeteer-core(首次运行自动装到 tmp/screenshot-deps/)· Edge / Chrome / Chromium(系统已有任一即可,跨平台自动探测)
1. 定位
只做一件事:把 HTML 或本地 URL 渲染成 PNG。
做:
- 启动 headless 浏览器
- 设置 viewport
- 打开 source(本地 HTML / file:// URL)
- 按配置依次执行
setup_js(可选 raw JS hook)→ wait_selector / wait_ms → 截图(全页或 selector)
- 自适应宽度(
fit_to_selector 自动扩 viewport,源自原 flowchart-screenshot 能力)
- 输出 PNG 到
tmp/screenshots/<task-id>/
不做:
- 不上传任何外部文档系统(属项目配备的发布 skill,如
feishu-publish)
- 不生成 PRD/spec / 不写需求文档(属
prd-writer / requirement-analysis)
- 不长期保留图片——任务结束
tmp/ 整体清理;调用方需要长期保留则显式移到 assets/
- 不实现业务交互 DSL——不接受
actions: [openWizard, goToStep:2] 这类业务步骤抽象。复杂交互让调用方在 setup_js 里写 raw JS(可访问 page 和 sleep)
定位澄清(与 Claude in Chrome 互补,勿混):本 skill = HTML / URL → 静态 PNG 归档(headless 出图,供入库 / 发布引用);交互式页面的实时调试核对(边改边看视觉 / 动效 / 交互)走 Claude in Chrome 扩展 + 本地 http server 流程(见 html-demo / prd-writer/html-prototype.md)。两者互补:前者沉淀静态图,后者驱动实时调试,不互相替代。
2. 输入输出契约
2.1 配置文件(JSON)
示例中 <iteration-dir> 占位符 = 调用方的子需求目录(定义见 prd-writer/html-prototype.md
「落盘路径」);不经 prd-writer 直接调用时,source 填实际 HTML 路径即可。
{
"source": "<iteration-dir>/prototypes/index.html",
"task_id": "T-20260429-001",
"viewport": { "width": 1440, "height": 900, "deviceScaleFactor": 2 },
"captures": [
{
"name": "01-entry",
"wait_ms": 800
},
{
"name": "02-step1",
"setup_js": "await page.evaluate(() => openWizard()); await sleep(500);"
},
{
"name": "03-step2-upload",
"setup_js": "await page.evaluate(() => goToStep(1)); await sleep(400);"
2.2 字段说明
| 字段 | 类型 | 必填 | 说明 |
|---|
source | string | △ | 默认页面:HTML 文件路径(相对 cwd 或绝对)/ file:// URL / http(s):// URL。与 captures[].source 二选一——两者皆无时该 capture 报错 既无顶层 config.source,也无 cap.source |
task_id | string | 否 | 输出子目录名;省略时用 YYYYMMDD-HHmmss-<8位hash> |
viewport | object | 否 | 默认 {width:1440,height:900,deviceScaleFactor:2} |
captures[] | list | 是 | 每项一张截图。字段名就是 captures——写成 items 之类不报错,静默产出 0 张图 |
captures[].name | string | 是 | 输出文件名(不含 .png) |
captures[].source | string | △ | 覆盖顶层 source(与上一次不同则重新 goto)。每个 Mermaid 块各渲一个 HTML 时用它,此时顶层 source 可省略 |
captures[].selector | string | 否 | CSS 选择器;省略则全页 (fullPage: true) |
captures[].setup_js | string | 否 | raw JS(async body),打开页面后、截图前执行;可访问 page 和 sleep(ms) |
captures[].wait_selector | string | 否 | 截图前等待该 selector 出现(page.waitForSelector) |
captures[].wait_timeout | number | 否 | wait_selector 超时(默认 30000ms) |
captures[].wait_ms | number | 否 | 截图前固定等待(默认 0) |
captures[].fit_to_selector | string | 否 | 测量该 selector bounding box 后,扩大 viewport 重新渲染(自适应宽度) |
△ = 条件必填:source 与 captures[].source 至少给一个。
2.3 输出
tmp/screenshots/<task_id>/
├── 01-entry.png
├── 02-step1.png
├── ...
└── _manifest.json # 截图清单 + 输出路径列表 + 时间戳
返回给调用方的结构(manifest):
{
"task_id": "T-20260429-001",
"started_at": "2026-04-29T16:00:00",
"ended_at": "2026-04-29T16:00:08",
"source": "<iteration-dir>/prototypes/index.html",
"captures": [
{ "name": "01-entry", "path": "tmp/screenshots/T-20260429-001/01-entry.png", "status": "success" },
{ "name": "02-step1", "path": "tmp/screenshots/T-20260429-001/02-step1.png", "status": "success" }
],
"errors": []
}
任务日志摘要写入 .claude/workframe-state/logs/screenshot/<task_id>.json(schema: workframe.task-log.v1)。
3. 调用方式
3.1 直接命令行
本 skill 随 core plugin 分发,脚本在插件目录内(项目 .claude/skills/ 下没有副本);插件根从 plugin-root.txt 取:
node "$(cat .claude/workframe-state/plugin-root.txt)/skills/screenshot/scripts/screenshot.js" --config <path/to/config.json>
3.2 从 prd-writer / 项目配备的发布 skill 调用
调用方负责:
- 准备
index.html(prd-writer 已生成到 <iteration-dir>/prototypes/index.html)或 Mermaid HTML
- 写 config JSON(含
setup_js 业务交互逻辑)
- 按 §3.1 的命令执行
screenshot.js(插件根从 plugin-root.txt 取)
- 读 manifest 获取 PNG 路径
- 后续动作(移到 assets / 上传外部文档系统)由调用方完成
- 任务结束清理
tmp/screenshots/<task_id>/(清理责任在调用方;SessionEnd hook 存在但不清理截图,调用方必须自己清。详见 §9 临时产物管理)
4. 自适应宽度(Mermaid / 大流程图)
fit_to_selector 解决 SVG / 大表格被 viewport 截断的问题:
- 用初始 viewport 渲染
- 测量目标元素 bounding box
- 若
box.width + 80 > viewport.width 或 box.height + 80 > viewport.height,扩大 viewport 重新渲染一次
- 截图(按
selector 截 element 而非全页)
源自原 flowchart-screenshot.js 的能力。
4A. Mermaid → PNG 标准流程(调用方:prd-writer / 项目配备的发布 skill 等)
把 Markdown 中的 Mermaid 流程图渲染成 PNG 的标准化路径,渲染样式与参数从此统一:
- 复制
scripts/mermaid-render-template.html,替换两个占位符 → 写入 tmp/mermaid-render-<slug>.html:
{{TITLE}}:图标题(显示在 PNG 顶部)
{{MERMAID_SOURCE}}:Mermaid 源码(注入 JS 模板字符串,源码含反引号或 ${ 时需转义;节点文本含括号时用双引号包裹节点文本)
- 写 config。照抄下面这块改名字即可,参数已定死,不要凭印象重编:
{
"task_id": "mermaid-<slug>",
"viewport": { "width": 1500, "height": 900, "deviceScaleFactor": 2 },
"captures": [
{
"name": "流程图-<slug>-导出流程",
"source": "tmp/mermaid-render-导出流程.html",
"selector": "#out svg",
"fit_to_selector": "#out svg",
"wait_selector": "#out svg",
"wait_timeout": 45000,
"wait_ms": 1500
}
]
}
多张图时:每个 Mermaid 块各渲一个 HTML,在 capture 里各自写 source(如上),顶层
source 可省略;只有一张图时也可把 source 提到顶层、capture 里不写。
⚠️ 顶层数组字段名是 captures,不是 items/shots/list。
screenshot.js 读的是 config.captures || []——写错字段名不会报错,
它安安静静地产出 0 张图、返回一个 captures: [] 的 manifest,
你要到「PNG 怎么没生成」时才回头查。实测踩过一次。
- 执行
screenshot.js → PNG 移入目标 assets/(文件名由调用方约定,如 流程图-{req_slug}-{图名}.png)→ 清理 tmp 渲染件与 tmp/screenshots/<task_id>/
- 失败降级:报告原因一次,提示用户手工截图(同 §7)
模板依赖 mermaid CDN(jsdelivr);离线环境直接走降级。
5. 业务交互的 setup_js 示例
"setup_js": "await page.evaluate(() => openWizard()); await sleep(500);"
"setup_js": "await page.evaluate(() => { aiParsed = true; if (typeof fillAIData === 'function') fillAIData(); goToStep(2); }); await sleep(500);"
"setup_js": "await page.waitForSelector('.tab-loaded'); await page.click('#tab-2'); await sleep(300);"
setup_js 是 raw JS 字符串,调用方完全控制。skill 内部用 new AsyncFunction('page', 'sleep', body) 执行,可访问 page(puppeteer Page 对象)和 sleep(ms) 工具函数。
6. 浏览器探测顺序(跨平台)
| 平台 | 候选路径 |
|---|
| win32 | C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe → C:/Program Files/Google/Chrome/Application/chrome.exe |
| darwin | /Applications/Microsoft Edge.app/.../Microsoft Edge → /Applications/Google Chrome.app/.../Google Chrome |
| linux | /usr/bin/microsoft-edge → /usr/bin/google-chrome → /usr/bin/chromium |
任一存在即用其执行 puppeteer-core,不下载 Chromium。
7. 失败降级
skill 失败时(环境问题 / 浏览器找不到 / 选择器不匹配 / setup_js 抛错):
- 写
errors[] 到 manifest
- 已成功的 PNG 保留在 tmp/,不回滚
- 整体退出码非 0,调用方决定降级(prd-writer / html-demo 走 html-prototype.md §S5.3 的"降级预案"——告知用户手动截图 + 不向外部文档系统插入空图)
8. 与其他能力的关系
| 能力 | 关系 |
|---|
prd-writer S5(HTML 原型截图) | 作为调用方;提供 prototypes/index.html + config,screenshot 返回 PNG manifest |
html-demo(仿真 demo 关键状态截图) | 作为调用方;setup_js 触发状态切换 / 模拟开关后截图,PNG 归档 assets/ 供 PRD 嵌图 |
项目配备的发布 skill(如 feishu-publish,Mermaid → 图片) | 作为调用方;本地 Markdown 中的 Mermaid 块由发布 skill 渲染成临时 HTML,调 screenshot 转 PNG,再上传 |
skill: document-norms §6 | 资源位置约束:截图 PNG 默认进 tmp/,调用方决定是否长期化 |
| 报告类 skill | 同样可调用 |
9. 临时产物管理(与 tmp/ 规则一致)
职责边界:
- 本 skill 只生成产物,不主动清理
tmp/screenshots/<task_id>/
- 清理责任完全在调用方:
- 调用方在确认输出已成功消费(移到
assets/ / 上传飞书 / 嵌入报告等)后,立即清理 tmp/screenshots/<task_id>/
- 调用方失败 / 中断时也应清理(除非显式保留现场)
WORKFRAME_KEEP_TMP=1 仅是调用方约定:调用方应当读取该环境变量,为 1 时跳过清理(保留 PNG 便于排障),其他值或未设时执行清理;本 skill 不读取该变量,只负责生成
反例:
10. 执行清单