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>
126 lines
10 KiB
Markdown
126 lines
10 KiB
Markdown
# 踩过的坑(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)
|
||
|
||
```css
|
||
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` 里又写了
|
||
|
||
```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…]`,报出文件和行号。
|
||
- 同一个坑第二次出现,就把它做成检查,不再只补一段经验。
|