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

126 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 踩过的坑(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…]`,报出文件和行号。
- 同一个坑第二次出现,就把它做成检查,不再只补一段经验。