jimku233/workflow-visualizer

业务流程を Mermaid ソースコード + 自己完結型インタラクティブ HTML フローチャートに変換します(ゼロ依存、完全オフライン、CDN不要)。口頭説明や文書内のプロセス、SOP、生産ライン、振り返りタイムラインをクリック可能な図にします。

workflow-visualizer とは?

このスキルは、ユーザーが口述または文書で説明したビジネスプロセスを、インタラクティブなフローチャートとして可視化するための強力なツールです。主な特徴は、`node:` 組み込みモジュールのみを使用した完全なゼロ依存、CDNを一切参照しない完全オフライン動作、そして単一のHTMLファイルやSVGファイルとして出力可能な自己完結性です。出力されるHTMLは、ノードのクリックによる詳細表示、キーワード検索とフィルタリング、ダーク/ライトテーマの切り替え、SVG/PNGエクスポートなどの高度なインタラクティブ機能を備えています。また、Mermaidコードも同時に生成されるため、Feishu、Yuque、GitHubなどのドキュメントプラットフォームへの貼り付けも容易です。このスキルは、特に開発ツールやAIエージェントのワークフロー可視化に最適で、オフライン環境やセキュリティ制限のあるネットワークでも確実に動作します。

対応✓Claude Code✓Codex CLI✓Cursor
npx skills add jimku233/workflow-visualizer

Installed? Explore more 生産性&コラボレーション skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

workflow-visualizer:业务流程 → 可交互流程图

渲染脚本:scripts/flow.mjs(相对于本 skill 目录) 绝对路径:scripts\flow.mjs 完整字段与语法手册:references/spec.md(写规格前先读它)

一、核心约束(决定了为什么这么做)

  1. 零依赖:只用 node: 内置模块。不需要 npm install,不用装 mermaid。
  2. 纯离线:布局与渲染是本脚本自己实现的 SVG,不引用任何 CDN。断网、内网、无外网都能出图(本机 web_fetch/docs.deepseek.com 都不通,任何依赖 CDN 的方案都会失败)。
  3. 产物自包含:导出的 .svg 内含 <style>,单独发给别人也能正确显示;.html 单文件,双击即开。

二、三步用法

第 1 步:提取流程(这步不能省) 从用户的真实材料里提取,不要凭空编。来源优先级:

  • 工作区里的方案/复盘文档(*.md,通常已有"工作流/SOP/排期"章节)
  • 用户口述(这时要把不确定的地方标成 status: hold 并在 note 里写明待确认)
  • 会话历史或日志

提取时固定抓这五样:环节(谁做什么)、时间窗、先后依赖、判断分支、风险/需人工把关的点。

第 2 步:写规格

  • 图不复杂(纯线性 + 一两个分支)→ 直接写 Mermaid 存成 .mmd,最省事。
  • 要带时间/负责人/状态/说明/分组 → 写 JSON(信息密度高得多,面板才有内容可展示)。
  • 先跑 sample 拿到一份成品规格当模板最快:
node "scripts\flow.mjs" sample --out ".\out\我的流程.json"

第 3 步:校验 + 渲染

$sk = "scripts\flow.mjs"
node $sk check  ".\out\我的流程.json"     # 结构检查:孤立节点/不可达/无终点/环
node $sk render ".\out\我的流程.json" --all --out ".\out\我的流程.html"

产物默认落在输入文件同目录。

一次 render 出两个版式(同一个 HTML 里,按屏幕自动选,底部可手动切):

版式布局适配
竖版 portrait单列纵向(wrap: 0)手机:竖向滚动,宽度撑满
横版 landscape长流程自动分多栏(wrap: -1,目标宽高比 1.6)电脑:一屏放下

常用参数:--out <file.html>、--all(额外输出 <名>-竖版.svg、<名>-横版.svg、.mmd)、--wrap N(横版每栏固定 N 行)、--debug(打印两版的栏数/行数与各栏成员)。

验证两版(推荐):

node $sk preview ".\out\我的流程.html"   # 生成「【电脑用】对照预览.html」:手机/电脑两个 iframe 并排

在调试浏览器里打开它,一眼就能看出两版是否都对。要精确到真实设备尺寸,用 CDP 设备模拟:

# 手机 + 电脑两张实测图,并打印 canvas/aside/footer 的实际高度与自动选中的版式
node $sk\..\tools\devshot.mjs "http://127.0.0.1:8765/我的流程.html" 手机.png 电脑.png

# 想在页面里跑一段 JS 验证交互(例如点节点、输入搜索、开抽屉):
node $sk\..\tools\probe.mjs <url> 探针.js --w 390 --h 844 --mobile --touch

这两个工具都会新建自己的临时标签页,用完关掉,不会碰用户正在浏览的标签页(早期版本直接抓第一个 page target,把用户正在看的页面当成了被测页面,教训记在下面)。但要注意:新标签页会出现在用户的浏览器窗口里并抢占焦点——如果用户正在用那个调试浏览器,先问一句再用,否则会互相打断。

三、出图后必须自检(不要只看"命令成功了")

命令成功 ≠ 图能看。至少做一次视觉确认,用 browser-control:

# 1) 生成一个 ASCII 文件名的副本,避开 file:// 的中文编码坑
Copy-Item ".\out\我的流程.html" ".\out\preview.html" -Force
# 2) 用系统 URI 生成器拿到正确的 file:// 地址(手写百分号编码极易出错)
$u = [System.Uri]::new((Resolve-Path ".\out\preview.html").Path).AbsoluteUri
node browser-control\webctl.mjs shot $u "out\shot.png" 5000

然后看图确认:文字没被裁、分支没交叉成一团、分组带没盖住节点。 点击交互也可以脚本验证(webctl.mjs evalfile,注意必须是 (function(){...})() 表达式,裸 return 会报 Illegal return statement)。

四、给人看:电脑 / 手机两种打开方式

电脑:直接双击那个 .html(单文件、离线可用);要贴进文档就拖 .svg。

手机(HTML 在电脑上,手机浏览器没有"打开本地文件"这回事)——用局域网访问,不用传文件:

node "scripts\serve.mjs" ".\out" 8765

脚本会把可用的手机地址直接打出来(VPN/虚拟网卡/169.254 会自动标「忽略」),形如: 手机 → http://<你的局域网IP>:8765/ ✔ 用这个(WLAN) 手机连同一个 Wi-Fi,浏览器打开这个地址,点文件名即可。要点:

  • 只把产物目录暴露出去,别指向工作区根。
  • Windows 首次会弹防火墙询问,勾选「专用网络」允许入站;如果没弹又打不开,就是被防火墙拦了。
  • 目录页是文件列表,手机上可点进任意 .html / .svg / .png。
  • 看完可以让我停掉这个服务(它是后台任务),或者直接关掉那个终端窗口。

不在同一个网络 / 要发给别人:直接发那个 HTML 文件本身——它自包含、离线可开,微信/QQ 传文件都行;只想要图就点页面里的「导出 PNG」发图片,或在浏览器里 Ctrl+P 存 PDF。

手机上的操作:单指拖拽平移、双指捏合缩放、轻点节点看详情、底部「适配」回到整幅;竖版/横版按钮在页脚左侧(手机上只显示图标)。

区域能力
顶栏品牌点 + 标题、搜索框、「全部流程图」按钮(右上)、状态图例胶囊(点击筛选)
右栏未选中时是流程概览;搜索时先列命中清单(见下);选中节点后是详情卡 +「返回概览」
底栏竖版/横版切换、概览/详情(手机)、缩放、适配、导出 SVG/PNG、明暗主题、Mermaid 源码

搜索的三档命中(重要,别把它当 bug)

搜索同时查 标题 / id / 时间 / 负责人 / 说明 / 备注,但画布上只看得到标题,所以命中分两类:

  • 可见命中(标题/id/时间/负责人)→ 节点描蓝边,计数直接算一个
  • 只在说明/备注里的命中 → 节点描橙色虚线边,计数写成 2 个匹配(1 个只在说明/备注里),右侧同时列出命中清单(点一下定位过去)

命中色用专用变量 --hit(深色 #4a9dff、米色主题 #2b7fe0),不能借用 --accent —— 米色主题的强调色是赤陶色 #bf6b3a,借用它命中就变成"红边",跟"蓝边"的说明对不上(踩过)。

例:在示例流程里搜 杯 会命中 2 个 —— 开第二个出杯位(标题)和 午后补货(说明里的"杯具");搜 f 会命中 id 为 f 的节点,以及任何说明/备注里含字母 f 的节点。同一个节点只会计一次,这不是重复计数。

明暗主题:总设置 / 单张图 / 锁定(后改的优先)

三个键都存在浏览器的 localStorage(都存 JSON {mode, ts},靠时间戳比较,谁后改谁生效):

键谁写含义
flowviz-theme首页「显示设置」总设置:跟随系统 / 始终深色 / 始终浅色
flowviz-theme:<文件名>流程图页面的明暗键单张图的单独设置(取值 dark/light)
flowviz-lock:<文件名>流程图页面的「锁定颜色」锁定后冻结该图颜色({locked:true, mode})

生效规则(页面 <head> 的引导脚本与页面 JS 完全一致,避免闪色):

  1. 被锁定 → 用锁定那一刻冻结的颜色,总设置与单张设置都影响不到它
  2. 否则比较总设置与单张设置的时间戳,新的一方生效
  3. 都没有 → 跟随系统(prefers-color-scheme),系统切换时实时跟随

所以:先改单张、再动总设置 → 全部跟着总设置变;先动总设置、再改单张 → 只有那一张变。锁定后必须先解锁才能改颜色(锁定态点明暗键会被拦下并弹提示)。

实测矩阵(8 步全过、0 异常):清空 → 总=浅 → 图=浅;点单张明暗 → 图=深(写入单张键);再改总=浅 → 图=浅(总设置更新,覆盖单张);再点单张 → 只有这张变深;锁定 → 改总设置后仍保持被锁的深色;锁定态点明暗键 → 被拦下并提示"先解锁";解锁 → 回到总设置的浅色;首页卡片显示「🔒 已锁定 浅色」徽标。

其他要点:

  • 同一个键反复 setItem 是覆盖,不会越积越多(实测点 12 次明暗键后 Object.keys(localStorage) 仍只有 1 项、合计 18 字节),不需要"删掉上一次"
  • 偏好存在设备浏览器里,服务重启不受影响;换 IP/端口(换了 origin)、清浏览器数据、换浏览器才会重置
  • 浅色是米色千禧年风格,不是纯白:底 #f2ebdd、面板 #fdfaf3、正文 #33302a、强调色赤陶 #bf6b3a、图内底纹 #e6dcc8(深色主题保持原来那套)

顶部按钮

🏠 首页 回到文件列表页(href="./")|全部流程图 打开右侧抽屉|搜索框|状态图例|明/暗 切这张图|锁定颜色 冻结这张图的颜色。 抽屉里当前页渲染成非链接(点了不会有反应,避免"我以为坏了"),列表固定按文件名排序,所以 fetch 刷新清单时不会重排。

右上角按钮弹出右侧抽屉,列出同目录下由本 skill 生成的所有流程图(固定按文件名排序,当前页标「当前」且渲染成不可点的非链接),点一条即可切换。识别方式是页面里的 data-flow-viz="1" 标记,所以别的 HTML 不会被收进来。

  • 清单文件是目录里的 流程索引.json,每次 render 自动重建(也可 node flow.mjs index <目录> 单独重建)
  • 用 HTTP 打开时抽屉会实时 fetch 清单刷新;直接双击本地文件时用页面内嵌的快照兜底(每个页面都嵌了一份,所以本地也能切换)

搜索的交互("命中不明显"的三个补丁)

  1. 自动展开结果面板:搜出命中且当前没选中节点时就 setPanel(true) —— 手机端结果清单默认藏在收起的底部抽屉里,不展开等于没提示(这是"搜了等于没提示"的根因)
  2. 上一个 / 下一个:结果区标题栏有 ↑ / ↓ 和 当前/总数 计数;搜索框里 Enter = 下一个、Shift+Enter = 上一个;Esc 清空
  3. 自动聚焦(可关):结果区有「自动聚焦命中」开关。开启时——
    • 命中的包围盒能装下、且所需缩放 ≥ 当前缩放 × 0.6 → 框住全部命中(多个命中同屏可见)
    • 否则(命中离太远)→ 不缩小,保持当前缩放居中到"当前命中",靠 ↑/↓ 逐个跳 这样永远不会为了"把两个远距离命中都装下"而把文字缩没
  4. 搜索期间给 svg 加 .searching:非命中淡出到 14%(连线 10%、分组带 35%),命中描边加粗到 3px
  5. 程序化移动(聚焦/跳转)给 #vp 加 .vp-anim(0.28s 过渡),手动拖拽不受影响

重名流程图:先分清「内容相同」还是「只是同名」

同一标题下出现多个文件时(典型来源:删掉又恢复文件,系统生成了 xxx - 副本.html),首页顶部会弹出一个提示框,按内容分两种处理:

情况判据给的动作
内容相同(同一份流程的两次渲染)流程语义指纹相同「只保留最新的一份,其余移入回收文件夹」
内容不同(同名但是不同版本)指纹不同不删,只提供「改名…」(默认建议 原名(旧版 日期).html)
  • 指纹 = 流程语义,只取 标题/副标题/方向/分组/节点/边,忽略 selfFile、flows、slug 等页面级字段。所以"同一份流程在不同时间渲染出的两个文件"指纹相同,而"只改了页面元数据"不会被误判成不同内容。
  • 删除一律是"移入回收文件夹":同目录下建 .trash-YYYY-MM-DD/,把 HTML 连同它的 SVG / MMD(按归一化名字认亲)一起 rename 进去,可随时取回。
  • 处理动作走后端端点 POST /__flow/act({op:'trash', remove:[...]} / {op:'rename', file, to}):
    • 只允许 127.0.0.1 调用(手机、局域网一律 403)——否则等于把"删你文件"的能力挂到 Wi-Fi 上
    • 文件名过白名单:不含路径分隔符、长度 ≤200、必须以 .html 结尾;重名冲突返回 409
    • 手机 / 本地 file:// 模式下不显示按钮,只显示提示(那两种场景没有可写的后端)
  • 卡片上仍会保留文件名(标题重名时),方便人工辨认

两种打开方式(都要能进首页)

方式怎么进首页首页从哪来
本地双击(file://)流程图右上角 🏠 → ./index.htmlrender 写出的静态 index.html(浏览器自带的目录列表只有文件名,所以必须有这份)
局域网 / HTTP🏠 → ./serve.mjs 动态生成
  • render 每次都重建 index.html,它与 HTTP 首页共用同一段渲染代码(serve.mjs 导出的 buildHome();serve.mjs 只在直接运行时才起服务,被 import 时只提供函数)
  • 静态版与 HTTP 版的差别:面包屑显示 📁 目录名(本地文件夹)、徽标写「流程图页面」、页脚多一段"本地文件模式:手机要访问得先起服务"
  • 🏠 按钮是协议感知的:location.protocol==='file:' 时把 href 改成 ./index.html
  • index.html 自己不会出现在卡片列表里(早期版本会,第一张卡叫 "index");既不在清单里、又解析不出 flow-stats 的 HTML 会被归到「其它文件」

首页(电脑/手机同一页)

  • 面包屑:🏠 流程图 / 子目录(当前),每级可点
  • 磁盘路径块:直接显示 .\out,带「复制路径」按钮——要发原文件、找源文件时不用再猜
  • 检索 + 排序 + 汇总:按标题/文件名过滤;排序可切「最近打开优先 / 按名称 / 按修改时间」;汇总显示 3 张 · 共 36 环节 / 47 连接 · 需关注 4 处(2 张图)
  • 最近打开:点卡片时记进 flowviz-recent(最多 8 条),默认置顶并加「最近打开」徽标
  • 卡片还会显示「🔒 已锁定 X」「单独设为 X」徽标(读本机 localStorage)
  • 文案按设备分:同一份 HTML 里写两套提示,用 @media (hover:none),(pointer:coarse) 切换(比按宽度判断更准,横屏手机也对)——
    • 电脑(.only-pc):🖱️ 点卡片打开。图里可以滚轮缩放、按住拖拽平移;右上角「显示设置」切换深色/浅色
    • 手机(.only-touch):📱 点卡片打开:图上单指拖动、双指缩放、点环节看详情…
    • 卡片徽标「手机上点这个」加 .touchonly,电脑上不显示;对照页徽标改成中性的「并排对照页 · 给电脑看的」(两边都看得懂)
    • 流程图页面的页脚/侧栏提示同理:电脑看 滚轮缩放 · 拖拽平移 · Esc,手机看 单指拖动 · 双指缩放
  • 同名流程图(例如 xxx - 副本.html 和 xxx.html)光看标题分不清 → 这类卡片在标题下再显示一行文件名
  • 缩略图按归一化名字匹配:去掉空格、去掉 -横版/-竖版 再比,所以 示例流程 - 副本.html 能配上 示例流程-横版 - 副本.svg(后缀位置不同也能配上)
  • 目录页响应必须带 cache-control: no-store:它是动态生成的,浏览器一旦缓存就看不到新版(踩过一次:用户一直看到旧版纯列表,误以为"首页很简陋、没有缩略图")

手机上的操作

单指拖拽平移、双指捏合缩放、轻点节点看详情、底部「适配」回到整幅。 手机上概览/详情是覆盖式底部抽屉(position:fixed):点节点滑出、点空白收起、底栏「概览」按钮手动开。它是覆盖层而不是 flex 兄弟节点——所以开合面板时画布高度不变(实测 607→607),不会触发"画布变矮 → 重新适配 → 图被裁掉"的连锁反应。

两个和"缩放后看着不对"有关的实现细节:

  • 点阵底纹在 #vp 内部、范围是画布的 3 倍且以内容为中心(x=-W, y=-H, w=3W, h=3H)。这样底纹跟着缩放平移走,视野里不会露出底纹矩形的硬边(早期版本底纹只有画布那么大,放大到 150% 再平移就会看到一条边)。
  • HTML 版不画 SVG 标题块,只有导出的 .svg 才画(页面顶栏已经有标题了)。否则放大后标题会压住节点。
  • 窄屏上切横版一律整幅适配:判据是 画布宽/内容宽 > fit×1.8 且内容宽高比 ≤ 1.2 才用"宽度撑满"。横版是 1.58 的扁图,在 390px 屏上如果宽度撑满会放大到 150%(只看得见一角),现在稳定 100%。

五、画业务流程图的经验规则

  1. 节点粒度:一个节点 = 一个可交付的动作("写口播稿"是节点,"认真思考"不是)。单图 8–16 个节点最舒服,超过 20 个就拆成主图 + 子流程。
  2. 状态要用起来:done/doing/todo/risk/hold 不是装饰。真实流程里"等人确认/等素材"这类阻塞要标 hold,"必须人工把关"标 risk——这正是流程图比文档强的地方。
  3. 判断节点只放真分支:{...} 用于会走不同路径的决策。只是"检查一下然后继续"的,用 milestone。
  4. 时间窗尽量来自材料,不要自己编。材料里没有就留空,别猜。
  5. 版权/合规/红线的节点要显式画出来并写进 note,否则图看着很顺、实际会踩雷。
  6. 长链别用横向 LR:13 步的链做成 LR 是 1980×489 的 4:1 长条,在 1100px 宽的画布里 fit 出来只有 0.55 倍,字全糊;而分栏(横版)能把同一张图压到约 1.6:1,一屏放下还不缩字。经验:链式流程要横向 → 用分栏,不用 LR。
  7. 两版怎么选:手机看竖版(一列往下滚,符合竖屏直觉);电脑看横版(分栏,一屏全局)。autoVariant() 的阈值是 宽<760px 或 宽高比<1.08 → 竖版。
  8. 分栏的读向仍是向下:不做蛇形(上下交替),因为反向列会让箭头方向混乱。跨栏连接线走列间空隙(行内只有一个节点时)或底部走廊(行内多节点时),不会横穿节点。
  9. 分组跨栏时会自动切成多段并标「(续)」,避免一个矩形把不相干的节点圈进去。

六、已验证记录(2026-10-04)

  • 输入格式:JSON / Mermaid 子集 / .md 里的 ```mermaid 代码块,三条路径均实测通过。
  • Mermaid 往返:解析 → 重导出 .mmd,判断节点 {}、虚线 -.->、边标签 -->|有|、subgraph 分组均正确保留。
  • 两版布局(13 节点 / 13 连线 / 3 分组的示例流程):竖版 533×1936(单列 12 行);横版 1119×710(自动分 3 栏 × 4 行,宽高比 1.58,一屏放下)。7 节点的示例流程:竖版 533×890、横版 806×459(自动 2 栏)。
  • 自动选版(CDP 设备模拟实测):390×844 → show=portrait;1440×820 → show=landscape。
  • 手机版布局(390×844 实测高度):canvas 364 / 概览面板 253 / 页脚 81,概览内容完整渲染(panelLen 1533)。
  • 触屏手势(CDP 合成 TouchEvent 实测):单指拖拽平移 ✓、双指捏合缩放 ✓、轻点节点选中并弹出「← 返回概览 | s | T 日 12:00 开工 | 起点 | 已完成」✓。鼠标路径同样三项通过。
  • 交互:点击节点 → 详情卡 + 上下游高亮 + 其余变暗;图例筛选写入 data-hide-status;搜索命中计数;+/0 键缩放与适配回到 100%;明/暗主题切换;两版同时保持选中/搜索/筛选状态。
  • 加载期异常检查(Runtime.exceptionThrown):两个样例页面均 0 异常。
  • 美化版:卡片式节点(状态色竖条 + 柔和投影)、类型徽标、时间药丸、圆角正交旁路、胶囊边标签、分组带标签胶囊、右栏流程概览(计数 + 状态筛选 + 需关注列表)、点阵底纹、图标工具栏。

踩过的坑(都已在代码里修掉,记下来省下次的时间)

  1. Mermaid 判断节点不生效:touch() 写的是 shape 字段,normalize() 读的是 kind —— 字段名对不上,菱形全画成矩形。
  2. 标签引号被吃掉:标注"真题改编" 变成 标注"真题改编,因为无条件剥了首尾引号;改成只在首尾同引号时剥。
  3. 缩放百分比与实际不符:SVG viewBox 自己会做一次 meet 缩放,原来的 transform 是叠在它上面的,导致「100%」实际显示 26%。改成在元素像素空间统一算 view{K,TX,TY} 再反解。
  4. PowerShell Set-Content 加了 UTF-8 BOM,破坏 #!/usr/bin/env node 直接 SyntaxError。改这个脚本别用 shell 写文件。
  5. b.members 未定义:分组带对象漏传 members,带标签的图直接崩。
  6. fit 整幅让长流程图缩到 26%:改成"整幅与宽度撑满相差不到 1.8 倍就整幅,否则宽度撑满 + 顶部对齐"。
  7. 菱形压字:判断节点按内接矩形条件重算尺寸后,是语法还是语篇问题? 才能一行放下。
  8. 两版并存时 id 冲突:renderSvg 里的 <g id="vp"> 没加版本后缀,客户端 getElementById('vp-landscape') 拿到 null → paint() 抛异常,整个初始化中断,概览面板空白(现象是 aside 只剩 25px)。两版的所有 id(defs/filter/pattern/marker/vp)都必须带 -${vid}。
  9. wrap 参数语义写反:它表示"每栏行数",不换行应是 R = L 而不是 R = 1(R=1 会变成每层一栏的横向长条,竖版一度被排成 3476×101)。
  10. CDP 异常回放会骗人:Runtime.enable 后会把上一个文档的异常一起送来。判断"页面是否真的报错"前,先 Page.navigate 到 about:blank 再进目标页,否则会把旧页面的错算到新页面头上。
  11. 轻点选中的验证要先适配:手势操作后目标节点可能已被移出可视区,elementFromPoint 自然命中不到——这是测试脚本的问题,不是页面的问题。
  12. CDP 验证抢了用户的标签页:webctl shot / 直接抓第一个 page target,都会导航用户正在看的那个标签页;用户同时在用浏览器时,双方互相打断(实测:我导航过去、用户一搜就把它导航回来,location 每隔几十秒变成实习网站)。改成 tools/cdp.mjs 的 withOwnTab()——自己 Target.createTarget 建临时标签页、轮询等它出现在 /json/list、用完 Target.closeTarget 关掉。即便如此新标签仍会抢焦点,用户在用浏览器时要先问。
  13. 搜索"多出来"的匹配不是 bug:搜 口 命中 2 个,是因为搜索同时查说明/备注("口径校准")和 id(节点 id j)。修法不是改数字,而是把不可见的命中标出来(橙色虚线圈 + 分档计数 + 命中清单)。
  14. 后台标签页不能截图:Target.createTarget({background:true}) 不抢用户焦点,但后台标签页不产生帧,Page.captureScreenshot 会永远挂住(实测 120s 超时)。要截图就得用前台标签页(会抢焦点);后台标签页只适合纯几何测量(getBoundingClientRect / getComputedStyle 都正常)。另外后台标签页的 CSS transition 不推进——测几何前先注入 *{transition:none!important},否则会量到动画起点(我把抽屉的 rect.x=398 误判成"没打开",实际是节流)。
  15. 给"电脑用"的产物也会被挂上"手机上点这个":文件列表的徽标只看扩展名。现在 【/_ 开头的文件单独标「电脑用,手机上别点」并置灰——判断条件要跟着展示文案一起改。
  16. 验证产物不要放在被服务的目录里:shot-*.png、探针脚本会出现在手机的文件列表里。统一放进一个不被服务的目录(例如 out/_shots/),别放进被 serve.mjs 服务的产物目录。
  17. 抽屉列表在手指底下重排 = 点错条目:页面内嵌快照原来是"当前页排最前",而 fetch('流程索引.json') 回来是按 mtime 排序 —— 打开抽屉后约 100ms 列表会跳一次,用户点到的已经不是想点的那条,表现为"点了没反应 / 回不去"。修法两条:清单改成固定按文件名排序(快照与清单顺序永远一致),当前页渲染成非链接。教训:任何"打开后异步刷新"的列表都必须保证顺序稳定。
  18. 改了 serve.mjs 要重启服务:页面模板在进程内存里,不重启就一直是旧版(我为此白查了一轮"首页没有 h1")。
  19. 目录页漏了 cache-control 会让用户永远看到旧首页:用户两次说"电脑版首页是不是太简陋了/没有缩略图",实际是我早已改成卡片+缩略图,但浏览器一直用缓存的旧响应。动态生成的 HTML 必须 no-store。
  20. Start-Process 分离启动的服务活不过命令结束:沙箱在命令结束时连带清理进程树,服务会静默消失(表现为下一次请求"无法连接到远程服务器")。要用 harness 后台任务,或让用户自己在终端里跑。
  21. 别拿目录里同名/近名的 JSON 当输入:目录里有个 甲流程.json,名字像甲流程,内容其实是乙流程的规格;我照它 render,直接把已有的 甲流程.html 覆盖成了别的内容。渲染前先核对 JSON 里的 title/节点数,并且优先用与产物同名的输入。
  22. 中文注释里带直引号会截断双引号 JS 字符串(" // 说的是"纯文件名"列表" → 提前结束)。同一个坑踩了三次:中文注释里要么不用引号,要么用「」。
  23. file:// 下没有"目录首页":href="./" 在本地会落到浏览器的纯文件名列表,用户看到的就是"只有文件名"。动态生成的目录页必须另写一份静态 index.html,并让首页按钮按协议切换目标。
  24. 没有调试浏览器时的截图办法:沙箱里 Edge 起不来(mojo 命名管道被拒),但 msedge --headless=new --screenshot=... --window-size=W,H <url> 一次性渲染写文件是可行的(需一次不受限权限)。能拍 file:// 本地页、也能按尺寸模拟手机/电脑,是本轮定位"手机首屏看不到卡片""汇总算出 0"的关键。
  25. 模板字符串里写正则要双反斜杠:\d/\s 在模板字符串里会退化成 d/s,生成到页面上的正则就废了(汇总一度显示"共 0 环节")。写在页面脚本里的正则统一用 \\d、\\s。
  26. 文案不能跨设备硬写:电脑首页上写「手机上点这个」、手机流程图里教人「滚轮缩放/双击/Esc」,都是"设备错配"。做法是同一处写两套(.only-pc / .only-touch)+ @media (hover:none),(pointer:coarse) 切换 —— 用输入方式判断比用屏宽判断准(横屏手机、窄窗口电脑都不会错)。
  27. 同目录出现同名流程图时卡片会长得一模一样:xxx - 副本.html 与 xxx.html 标题相同,用户分不清。标题重名时补一行文件名;缩略图也要按"去掉空格与 -横版/-竖版"的归一化名字匹配。
  28. 模板字符串里的 \n 会变成真换行:写在 homeScript() 这类模板里的页面 JS,'...\n\n' + x 到了页面上就是字符串字面量里夹真换行 → 页面脚本语法错误(node --check 检查 serve.mjs 是查不出来的)。要用 \\n。验证办法:把 buildHome() 生成的两个 <script> 抽出来分别 node --check(我加了这个回归步骤)。
  29. 判断"内容是否相同"要按语义,不要按文件哈希:整页哈希必然不同(渲染时间戳不同),误判成 "内容不同 → 不改名但也不处理"。改为按流程语义指纹。

関連スキル