web-dev-album · 给项目拍照(网页应用成长相册)
把 web 类项目每个里程碑的页面用系统 Chrome 无头截图,存进项目自己的目录下的 album/ 文件夹,维护一条带版本说明的时间线相册。也支持给动画/游戏项目录一段 webm 实机动画。
纯本地、零远程依赖:截图只落在 <项目目录>/album/ 里,不连任何服务器,整套 skill 可直接分享给任何人。
- 依赖系统已装的 Chrome / Chromium / Edge(自动探测:系统常见位置 →
PATH→ 便携/第三方目录;都不行用ALBUM_CHROME点名),零 npm 依赖。 - 需要 Node 16+。Node 太老会在解析阶段直接抛
SyntaxError,见 FAQ。 - 支持 Windows / macOS / Linux(含以 root 运行的服务器 / 容器)。
什么时候用(触发词)
- 用户做网页 / H5 / 小程序 / Canvas 游戏,说了这些话之一:「存个快照」「记一版」「截图」「相册」「看看项目演变」「存视频」「录一段」「存个动画」。
- 你刚做完一次能肉眼看出来的改动(改版式、加功能、换配色、加页面),按下面「核心规则」应主动存一版,不用等用户说。
核心规则
- 只截桌面视图(1280×860),不截手机视图。
- 一次里程碑按子页面 / 子项目数量截多张,每张
--shots "标签=URL"带标签,不能只截主页一张。 - 相册存进项目目录的
album/文件夹:默认<项目目录>/album/,可用--album-dir <名>或环境变量ALBUM_DIR改名。不依赖任何远程服务器,纯本地文件。 - 相册记过程,不是存最新一张:每次可视觉验证的改动都存一版;每次写入 =
album/snapshots.json追加一个 seq,历史永不覆盖、永不删。 - 更新卡片缩略图必须走
--card:它会在覆盖前把旧卡片图自动归档进相册时间线。禁止裸用 Chrome 截图覆盖album/card.png(那样历史就没了)。 - 视频(--video)由用户激活,仅限有 canvas 动画 / 游戏的项目;视频体积大,不要主动录、不要每个里程碑都录。
存储结构
<项目目录>/
└── album/ # 默认名,可用 --album-dir 覆盖
├── index.html # 相册看板(每项目自带 · 双击即看 · 存图时自动重建)
├── snapshots.json # 快照清单(数据源,时间线)
├── shot-01-1.png # 第 1 里程碑 · 第 1 张
├── shot-01-2.png # 第 1 里程碑 · 第 2 张
├── card.png # 卡片缩略图(最新版,覆盖式,由 --card 维护)
├── card-20260912-1030.png # 旧卡片图(--card 覆盖前自动归档)
└── video-02.webm # 录屏里程碑(动画项目,可选)
snapshots.json 结构(file 写相对 album/ 的纯文件名):
{
"projectId": "my-project",
"projectName": "我的项目",
"snapshots": [
{
"seq": 1,
"version": "v2 线上运行版",
"caption": "一句话:这版长啥样",
"changes": "改了什么",
"ts": "2026-09-03T00:54:30.119Z",
"images": [{ "label": "门户", "file": "shot-01-1.png" }]
},
{
"seq": 2,
"version": "v3 实机录屏",
"caption": "第1关降落实机录屏",
"changes": "首次存视频里程碑",
"ts": "2026-09-11T03:30:02.346Z",
"video": { "label": "第1关·长安降落实机", "file": "video-02.webm" },
"images": []
}
]
}
每个项目自包含:清单与所有图片都在自己目录的 album/ 下,拷走项目目录即带走全部相册。若想在别的看板里展示这些相册,直接读各项目 album/snapshots.json 即可(结构稳定)。
相册看板(每项目自带 album/index.html)
每个项目的 album/ 里都带一个 index.html 相册看板:把该项目所有里程碑排成时间线,缩略图点开可放大(图片 / 视频走灯箱)。
- 自动生成:每次
capture.js写盘(里程碑 / 视频 / 卡片图归档)后自动重建album/index.html,无需手动,做到「存即所见」。 - 双击即看、可分享:数据内联在 HTML 里、图片用相对路径(
./shot-…png),所以 file:// 双击打开即可,不需起服务、不依赖网络;把整个album/文件夹(index.html+ 图片 +snapshots.json)拷给别人,换台机器照样能看。 - 手动重建(改了
snapshots.json、或从别处拷来相册想补个看板时):
node "scripts/gen-board.js" --project "<项目目录>"
# → 生成 <项目目录>/album/index.html;album/ 尚不存在则生成一个空看板
看板内容全部取自 album/snapshots.json,所以清单在,看板就能随时重生成。
命令速查
脚本零 npm 依赖,用系统 Chrome(无需 ssh/scp、无需远程服务器)。流程:截图 → 临时目录 → 全部成功才落 album/ → 更新 snapshots.json → 删临时目录。任一步失败都不会污染 album/(重复运行接着往后排 seq)。
命令中的 scripts/… 是相对于本 skill 目录的路径,请在 web-dev-album 技能目录下执行(或换成你机器上的绝对/相对路径)。
多张里程碑(推荐,子项目多的项目必用)
node "scripts/capture.js" \
--project "<项目目录绝对路径>" [--id <标识>] [--name "<项目名>"] \
--version "<版本标签>" --caption "<一句话:这版长啥样>" --changes "<改了什么>" \
--shots "标签1=URL1" --shots "标签2=URL2" --shots "标签3=URL3"
--project传项目根目录路径;不传则默认当前工作目录。若给的是 id 而不是目录,会尝试从PROJECTS_JSON环境变量指向的清单里解析本地路径(默认不设该变量,分享者请直接传目录)。- 单张:把
--shots换成--url "<URL>"即可。 --id/--name写进snapshots.json,便于以后归集;省略时默认用项目目录名。
更新卡片缩略图(旧图自动归档——换图必走这个)
node "scripts/capture.js" \
--project "<项目目录>" [--id <标识>] [--name "<项目名>"] --card --url "<页面地址>"
截 1280×720 → 与现有 album/card.png 比 md5 → 不同则旧图归档进相册(追加「卡片图留档」里程碑)→ 覆盖 → 重建清单;md5 相同则跳过。
录动画片段(用户说「存视频」才用,仅 canvas 动画 / 游戏)
node "scripts/capture.js" \
--video --project "<项目目录>" [--name "<项目名>"] \
--url "<游戏页面地址或本地 file:// 路径>" \
--click "开始降落" --duration 6 \
--label "第1关·长安降落实机" --version "v… 实机录屏" --caption "…" --changes "…"
- 原理:CDP 驱动无头 Chrome,
canvas.captureStream+MediaRecorder直接录 webm(vp9),不依赖 ffmpeg。 - 参数:
--duration(秒,默认 6,上限 20)、--fps(默认 30)、--click "<按钮文字>"(录制前先点开始按钮,轮询最多 8s)、--warmup(点击后等多少 ms 再录,默认 1500)、--label(视频标题)。 - 录的是页面里第一个
<canvas>;纯 DOM/CSS 动画录不到,这类项目改用图片快照。
环境变量(按需覆盖)
| 变量 | 作用 | 默认 |
|---|---|---|
ALBUM_CHROME |
手动指定 Chrome / Chromium 可执行文件绝对路径 | 自动探测:系统路径 → PATH → 便携目录 |
ALBUM_VTB |
页面等待时长(ms) | 4000 |
ALBUM_TIMEOUT |
Chrome 超时(ms) | 45000 |
ALBUM_DIR |
album 文件夹名 | album |
PROJECTS_JSON |
让 --project <id> 可解析本地目录 |
不设(不硬编码路径) |
常见问题(FAQ)
Chrome / Chromium 找不到?
脚本按顺序找:① 平台常见安装位置(Windows 的 Program Files / LocalAppData、macOS 的 /Applications、Linux 的 /usr/bin、/snap/bin);② PATH 里的 chrome / google-chrome / chromium / msedge;③ 便携安装目录(~/.cache/puppeteer、~/.cache/ms-playwright、~/.chromium-browser-snapshots —— chrome-for-testing / puppeteer「解压即用」的那类布局)。
都找不到就点名指定:
ALBUM_CHROME="/path/to/chrome" node "scripts/capture.js" ...
报 SyntaxError: Unexpected reserved word?先看 Node 版本。
跑 node -v。本 skill 需要 Node 16+。
Linux 发行版自带的 nodejs 包往往很旧(Ubuntu 22.04 自带 v12),会在解析阶段就失败。报错行号会指向某个 await,但真正原因是 Node 太老,不是脚本 bug —— 语法错误发生在任何代码执行之前,所以脚本里也没法提前给提示。
换新版 Node 即可,三选一:
# ① nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash && nvm install 22
# ② 官方 LTS 安装包:https://nodejs.org/zh-cn/download
# ③ 国内镜像直下(最快)
curl -LO https://registry.npmmirror.com/-/binary/node/latest-v22.x/node-v22.13.1-linux-x64.tar.xz
tar xf node-v22.13.1-linux-x64.tar.xz && export PATH="$PWD/node-v22.13.1-linux-x64/bin:$PATH"
在 Linux 服务器 / 容器里跑要注意什么?
- 以 root 运行:Chrome 必须带 --no-sandbox,脚本(截图与 CDP 两条路)已内置。自己手敲 chrome --headless 调试时记得加,否则会报 Running as root without --no-sandbox is not supported 并且没有任何截图输出。
- 不需要 curl:脚本连 Chrome 调试端口走 Node 内置 http,不依赖外部命令(很多最小化 Linux 镜像没装 curl)。
- 服务器通常没浏览器:下便携版 Chrome 解压即用,脚本会自动从 ~/.cache/puppeteer 等目录找到它,或直接 ALBUM_CHROME=<绝对路径>。
截图是空白 / 黑屏 / 截到旧内容?
① 页面太慢 —— 试 ALBUM_VTB=9000 ALBUM_TIMEOUT=90000(WebGL / three.js 首帧着色器编译慢是典型);
② 素材是异步换入的(先占位再加载大图),截图前素材还没就绪,要留足等待;
③ 只截首屏视口(非整页)——这是设计如此,对应「首屏长啥样」。
http 地址截不到?先查代理,不是沙箱。
本机若设了 HTTP_PROXY,Chrome 继承后会把 127.0.0.1 与局域网地址的请求也丢给代理 ⇒ ERR_CONNECTION_REFUSED 或空图。脚本已内置 --no-proxy-server 并清掉本进程代理变量;手动调试同理,curl 验证用 curl --noproxy '*'。
--project 报「解析失败」?
它只接受两种:① 已存在的目录路径;② 在 PROJECTS_JSON 指向的清单里能查到 id / name 的项目。该变量默认不设,所以分享给别人时请直接传目录路径。
本地 HTML 该传 file:// 还是 http?
自包含单文件 → 直接传裸路径或 file://(脚本自动转换);
依赖运行时 fetch / ES module / importmap 的页面 → 必须用 http(本地起服务后传 http://127.0.0.1:PORT/...),file:// 下这些会被 CORS 拦死。
录视频录不到 / 报 nocanvas?
--video 只录页面里第一个 <canvas>,仅适用于 canvas 动画 / 游戏。纯 DOM/CSS 动画录不到(会明确提示「页面里没有 canvas」)——这类项目改用图片快照。
录制数据由页面以 base64 直接交回 Node 落盘;万一没拿到,会打印提示并回退到「等 Chrome 下载目录」的老路。
卡片图为什么要走 --card?
--card 会在覆盖 card.png 前把旧图归档进时间线。裸用 Chrome 截图覆盖 album/card.png 会丢历史,永远走 --card。
截图失败会污染相册吗?
不会。流程是「临时目录截图 → 全部成功才落 album/」,任一张失败整体丢弃、不写 snapshots.json,重跑接着往后排 seq。偶发失败会自动重试一次,失败信息里会给出排查方向。
⚠️ 别自己在 Bash 里手敲 chrome --headless。
Git Bash 下该进程会被立刻 SIGTERM 杀掉,既无截图也无报错。capture.js 用 Node spawnSync 起 Chrome,是唯一可靠调用方式;临时调试也请写一小段 Node 脚本来 spawn。
⚠️ Windows 上写 JSON 注意 BOM。
用 PowerShell Set-Content -Encoding utf8 或记事本保存的 spec.json 可能带 UTF-8 BOM,会让 JSON.parse 失败(脚本已做容错,但建议存成「UTF-8 无 BOM」)。
文件说明(脚本分工)
| 文件 | 职责 |
|---|---|
scripts/capture.js |
主入口。三种用法:多张里程碑截图 / --card 卡片图归档 / --video 录屏。写 album/ 并自动刷新看板。 |
scripts/board.js |
看板 HTML 生成器(被 capture.js 调用)。数据内联、图片走相对路径,解决 file:// 的 CORS 限制。 |
scripts/gen-board.js |
看板生成的手动 CLI 入口(改了 snapshots.json、想补/重建看板时用)。 |
scripts/capture-cdp.mjs |
进阶:零依赖 CDP 截图器,支持注入 localStorage / 页内切视图。普通截图不需要它,见下。 |
scripts/package.json |
声明 ESM(type: module)与最低 Node 版本(engines)。必须与脚本同目录,否则 .js 会被当 CommonJS 解析、顶层 await 报错。 |
进阶专题
references/cdp-injection.md—— 需要「注入身份 / 切 SPA 视图 / 截交互态(弹层、切换后)」时看这里:capture-cdp.mjs用法、SPA 深链、截交互态的副本注入手法。references/private-setup.md—— 作者自用的环境约定(项目清单PROJECTS_JSON、部署到看板的albums/<id>/相册)。分享给别人时可以不带这个文件。
从零到可用:新项目怎么拍
- 确定项目目录:这是相册的归属地。直接传
--project "<绝对路径>";不传则用当前目录。 - 问清这一版要截几个页面(或直接按子页面 / 子项目数量决定),用
--shots "标签=URL"一次传完。 - 跑
capture.js(命令见上)。脚本会在项目目录下建album/并写入snapshots.json。 - 收尾验证:打开
<项目目录>/album/index.html看相册看板(应与本次截图一致),并确认album/里有图片与snapshots.json。 - 把该项目「固定截几张、分别是什么地址」记到你自己的一份约定表,下次直接复用(skill 本身不内置你的私有项目清单,分享给他人时各自维护)。
用户只说「截个图 / 存个快照」时的默认动作
- 不反问太多:先确定项目目录(当前目录或用户刚在做的项目);不清楚就问一次「这个项目有几个页面要截」。
- 至少截全主要页面,不偷懒只截主页。
- 版本标签 / 一句话特点 / 改动点由你根据本次改动自动总结,写进
album/snapshots.json。 - 若用户说的是「存视频 / 录一段 / 存个动画」→ 走
--video(仅 canvas 动画 / 游戏项目);反之不要录。