Files
DemonGatanjieu-Anomalous_Mo…/docs/architecture/lessons.md
T
DemonGatanjieuandClaude Opus 5.5 b2389de45b Structure check rejects :has() that matches the style attribute
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>
2026-10-15 12:53:51 +08:00

10 KiB
Raw Blame History

踩过的坑(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)。
    • 没有显卡时总帧时间不准,但样式计算的倍数可信。

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),维护者两轮审核退回,先后指出:

  1. 下载来的东西被当代码执行:中文多音字模型从个人 ModelScope 账号的 master 下载,不核对指纹,解压后执行包里的 config.py;英文词典读 pickle。而网页请求还能改“去哪个文件夹找这些文件”,等于网页能决定运行什么代码。
  2. 网页请求决定删改哪里:POST /anomalous_tts/storage 从请求里取新的存放位置,跨盘移动后对旧位置 rmtree。只检查“是不是本机请求”不够:本机网页也不该决定在哪里建、删文件夹。
  3. 按请求里的路径读任意文件:导入预览、导入检查会返回请求点名的任何文件内容。

根因:这些接口最初是为了方便(在界面里选文件夹、试听本机音频、一键挪角色),没有把“网页请求是不可信的输入”当成前提;“只接受本机请求”被当成了足够的保护。

怎么改的(接口版本 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…],报出文件和行号。
  • 同一个坑第二次出现,就把它做成检查,不再只补一段经验。