The pattern behind issue #22 came back once already (removed in v1.57.2,
back with the icon rail in v1.58.0). tools/check_structure.mjs now fails
on any :has() in the web stylesheets whose argument matches [style...],
pointing at the line; AGENTS.md, docs/guides/testing.md and lessons.md
say why and what to do instead (a class on an ancestor).
Prepared in a cloud session (checkpoint cbfc782), committed now that the
issue #22 fix is on this branch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10 KiB
踩过的坑(Lessons)
反复出现的坑和严重故障。每条写清:现象、根因、怎么发现的、以后怎么做。
2026-09-26 · v1.57.2:一条 :has() 样式让拖动画布掉帧(严重)
现象:B 站用户反馈装了插件后拖动工作流 10% low 只有二十几帧;和 ComfyUI Prompt Assistant 一起装时,大工作流只剩个位数帧甚至卡死。插件界面关着也一样。
根因:web/styles/01-shell-tools.css(b6acd09,2026-09-19,进了 v1.57 / v1.57.1)
body:has(#anomalous-toolbox-modal[style*="display: flex"]) #anomalous-sidebar-tooltip-bubble, …
:has() 里放属性选择器(这里是 [style*=…]),浏览器在页面上任何元素的该属性变化时,都要重新判断 body:has(...)。ComfyUI 平移、缩放画布时每一帧都在改每个 DOM 控件(文本框)的 style,于是每帧都要付出和页面元素数量成正比的样式计算。Prompt Assistant 给每个文本框加一组按钮,页面元素多十几倍,开销跟着放大。两个插件本身没有冲突。
实测(真 ComfyUI 前端 1.53.6 + Chromium,300 节点工作流平移 120 帧,样式重算合计):
| 情况 | 修复前 | 修复后 |
|---|---|---|
| 不装插件 | 60 ms | — |
| 只装 AMB | 690 ms | 63 ms |
| AMB + Prompt Assistant | 2300 ms(约 19 ms/帧,超过 60 帧的一整帧) | 86 ms |
| 只装 Prompt Assistant | 80 ms | — |
逐条删规则二分:只删掉这 7 条 :has() 就回到基线,其中罪魁是带 [style*=…] 的那条;button:has(.x) 等其他 :has 测不出开销。
修复:删掉这条规则。它是多余的:工具箱和设置窗口打开前都会调用 hideTooltipImmediately(),showSharedTooltip 在窗口开着时也会拒绝显示。
以后怎么做
- 不要用
:has()或[style*="display: …"]去判断界面开关状态。要表示“开着”,由 JS 加减一个 class(作用在自己的容器上),或者直接在 JS 里处理。 - 全局生效的昂贵选择器(
body:has、html:has、*、未限定作用域的button、textarea)会作用在 ComfyUI 的每一帧上。新增前先问:它会不会在画布拖动时被反复判断? - 性能问题要实测,别只看代码猜:
- 装一个真的 ComfyUI(
--cpu即可),用 Playwright 在空白处拖动画布; - 用 CDP 的
Performance.getMetrics比较RecalcStyleDuration; - 再在页面里删掉可疑规则做二分(遍历
document.styleSheets,对插件自己的样式表deleteRule)。 - 没有显卡时总帧时间不准,但样式计算的倍数可信。
- 装一个真的 ComfyUI(
2026-09-26 · 修复只在本机 main,发布版没带上
现象:素材库“选中节点时卡片错位”(应用按钮重复、图标重复成 ⚡ ⚡、标签日期挤在一起)的修复 01fc925、9e761d0 早在 9 月 24 日就做了,但只进了 feature/audio-engines(通过“Merge main … into feature”)。GitHub 上的 main 并没有,发布版 v1.57.1 的用户一直带着这个问题。
根因:修复提交在本机 main 上,只把 main 合进了 feature,本机 main 从未推送。
以后怎么做
- 发布或回答“修了没有”之前,用
git branch -r --contains <提交>确认修复在远端的哪个分支,别只看本机。 - 本机 main 有未推送提交时,先推送或说明,再做合并。
- 这次已把两个提交 cherry-pick 进远端 main(e1ac84f、4e8603b),随 v1.57.2 发布。
2026-09-26 · 发布热修复的检查项
- 改了 CSS 或 JS,要同步改缓存参数:
web/styles.css里对应的@import …?v=、browser_entry.js的styles.css?v=、main.js引用browser_entry.js的?v=。不然用户刷新后浏览器可能还用旧文件。feature 分支已去掉部分?v=,合并时以各分支现状为准。 CHANGELOG.md是 CRLF、LF 混合换行。用脚本编辑时按原字节读写(newline=''),否则整份文件都会显示成改动。- 云端会话能推分支,推不了标签,GitHub 工具也不能创建 Release。发布要在网页上完成:Draft a new release → 填新标签 → Target 选
main并核对提交号 → 粘贴说明 → Publish。插件的版本面板只列正式 Release,用 ComfyUI Manager 更新的用户则直接拉 main 最新提交。
2026-09-27 · 拆文件漏了 import,保存工作流在发布版里整个坏掉
现象:v1.57 到 v1.57.2,工作流页“保存当前工作流”只要工作流里有模型加载器就提示“保存工作流配方失败”。B 站用户反馈“就没成功过”。素材库走的是另一条路,所以能存。
根因:refactor(recipes): separate schema images and storage(6ad975c)把函数搬进 recipe_images.py,但它调用的 _resolve_exact_model_reference 和 hashlib 留在了旧文件里。同一轮拆分还让 media_routes.py 少了 time、tempfile:卡片缩略图出错后被 except Exception 吞掉,静默退回原图。另外 model_metadata.py 少了 _resolve_paths_to_model_info_sync。Python 只在那一行真正执行时才报 NameError,导入和其他测试都通过。唯一的保存测试关掉了模型预览快照,正好绕开了出错的那一行。
以后怎么做
- 拆模块后跑
tests/test_backend_names.py:后端文件读到却从没绑定的名字,它都会列出来。 - 行为测试要走默认路径。
recipe_payload默认关着快照,新测试test_saving_with_preview_snapshots_captures_the_model_cover专门走默认的“开着快照”。 - 真实按钮至少点一次:在隔离的 ComfyUI 里点“保存当前工作流”,一眼就能看出问题。
2026-10-12 · 页面往下滚后,点“返回”点到了下面的模型(第二次)
现象:从画布节点存的搭配(节点搭配),页面往下滚一点后,点“‹ 全部搭配”打开的是被滚到标题下面的模型封面。普通搭配和搭配列表没事。
根因:和 2026-10-09 修过的是同一个 Chrome 行为:滚动框里有 overflow: hidden 的元素(这里是模型封面的圆角框)或 content-visibility: auto 时,滚出可见区域的部分在点击判断里仍算“在那里”,盖住了上面的标题按钮。上次只给当时有问题的两个滚动框加了 clip-path: inset(0),之后新做的节点搭配页面又放进了带圆角封面的行。
怎么发现的:在隔离的 ComfyUI 里把节点存成搭配,把编辑区分 12 步滚到底,每一步用 document.elementFromPoint 取标题里每个按钮中心点的元素,看是不是按钮自己;滚到 140px 起就变成了封面 IMG。
以后怎么做
- 标题、标签栏下面的滚动框一律加
clip-path: inset(0)(它对点击判断也生效)。overflow本身裁不住这种情况。 - 新页面或新内容放进滚动框后,用上面的办法扫一遍:每个滚动框分步滚到底,检查每个可见按钮都能拿到自己的点击。
2026-10-08 · ComfyUI-Manager 审核 Anomalous_TTS:三类安全口子
现象:Anomalous_TTS 申请收录进 ComfyUI-Manager(Comfy-Org/ComfyUI-Manager#3342),维护者两轮审核退回,先后指出:
- 下载来的东西被当代码执行:中文多音字模型从个人 ModelScope 账号的
master下载,不核对指纹,解压后执行包里的config.py;英文词典读 pickle。而网页请求还能改“去哪个文件夹找这些文件”,等于网页能决定运行什么代码。 - 网页请求决定删改哪里:
POST /anomalous_tts/storage从请求里取新的存放位置,跨盘移动后对旧位置rmtree。只检查“是不是本机请求”不够:本机网页也不该决定在哪里建、删文件夹。 - 按请求里的路径读任意文件:导入预览、导入检查会返回请求点名的任何文件内容。
根因:这些接口最初是为了方便(在界面里选文件夹、试听本机音频、一键挪角色),没有把“网页请求是不可信的输入”当成前提;“只接受本机请求”被当成了足够的保护。
怎么改的(接口版本 13):配置文件当数据解析,词典从文本建,不读写 pickle,测试拦住 pickle.load / exec_module / eval(;下载锁定版本并先核对 SHA-256 再解压;存放位置、角色文件夹、底模来源只从用户手写的设置文件读;导入只收上传的文件,没有接口再接受本机路径。
以后怎么做
- 两个仓库的规范都写了安全底线(本仓库 AGENTS.md 第 5 节“安全底线”,Anomalous_TTS 的 ARCHITECTURE.md“不能破的规矩”)。新接口按它写,审查时逐条对照。
- 本仓库的现状(2026-10-09 统计):55 个会写文件的接口,只有 MCP、模型导入、整理三处检查了本机请求;删除模型、改模型信息、开始下载、导入备份等在
--listen时局域网里的其他设备也能调用。没有执行下载内容、pickle、eval 的代码。2026-10-12 的 v1.58.1 一次补齐:api/local_guard.py一个中间件把关/anomalous/下所有会改东西的请求,只接受本机(见 backend.md“Requests from other devices”)。
2026-10-13 · v1.58.0–v1.58.2:同一种 :has() 又掉帧(第二次)
现象:#22(Lindemellis):216 个节点的工作流,开着 Prompt Assistant 时一次画布重绘 465–770 ms,拖动只有 2–3 帧。只删掉一条规则就回到 53 ms。
根因:图标栏(46facb9,2026-09-29,随 v1.58.0 发布)在 web/styles/15-shell-rail.css 里又写了
#anomalous-container.anomalous-has-rail:has(> .anomalous-nb-modal[style*="display: flex"]) #anomalous-sidebar-wrapper
和上面 v1.57.2 那条是同一种写法::has() 里按 style 属性匹配。v1.57.2 修好三天后就写回来了,因为经验只写在这份文档里,没有东西拦着;当时的性能实测也只跑了 main 和音频分支,没跑图标栏所在的分支。
修复:工作区开没开由 JS 在容器上加减 anomalous-workspace-open,CSS 只看这个 class(#22 的修复)。
以后怎么做
node tools/check_structure.mjs现在会拒绝:has()里出现[style…],报出文件和行号。- 同一个坑第二次出现,就把它做成检查,不再只补一段经验。