diff --git a/.gitignore b/.gitignore index 8cf662f..77570df 100644 --- a/.gitignore +++ b/.gitignore @@ -65,9 +65,6 @@ docs/architecture/* !docs/decisions/ docs/decisions/* !docs/decisions/*.md -!docs/audits/ -docs/audits/* -!docs/audits/*.md !docs/guides/ docs/guides/* !docs/guides/*.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 3d69ace..325e057 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -24,7 +24,6 @@ and Current node lists the saved values. See the material contract below. | Workflow Recipes, packages, galleries, Parameter Notebooks, or prompt roles | [`docs/architecture/recipes.md`](docs/architecture/recipes.md) | | Saved prompts and node values (material files), image parameter details, keeping an output image | [`docs/architecture/material-library.md`](docs/architecture/material-library.md) | | Model Check, provenance hashes, missing-model recovery, or model scanning | [`docs/architecture/model-resolution.md`](docs/architecture/model-resolution.md) | -| Browser audits, E2E functional bug reports, or verification sign-offs | [`docs/audits/README.md`](docs/audits/README.md) | | Why a current product boundary exists | [`docs/decisions/README.md`](docs/decisions/README.md) | | Recurring implementation mistakes and post-mortems | [`docs/architecture/lessons.md`](docs/architecture/lessons.md) | diff --git a/CHANGELOG.md b/CHANGELOG.md index 893d4c8..60db959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,9 +103,6 @@ ### Fixes - **Ctrl+Z after a scan (扫描后的 Ctrl+Z)**: after any scan, the first Ctrl+Z used to seem to do nothing: the workflow's saved fingerprints had changed, which ComfyUI counted as a step. A change of fingerprints alone no longer takes a step. -- **Picking a model keeps your place (换模型时列表不再跳回顶部)**: in Current node's model picker, choosing a model further down redrew the whole list and scrolled back to the top (the model was chosen all the same). Only the chosen card's outline changes now. -- **A combo is saved once, not once per tag (搭配不再一下存几十次)**: opening a combo's bilingual tags saved it once for nothing, then once more for every tag's translation as it came back, so a long prompt wrote the file (and the activity log) dozens of times in a second. The translations are now saved together, half a second after the last one. -- **Activity shows where a long prompt changed (操作记录能看到长提示词改了哪里)**: values are kept to 400 characters, cut from the start, so an edit late in a long prompt showed the same text before and after. A long before/after pair is now cut around its first difference. ## v1.58.0 Beta (New Layout, Keep & Reuse, Prompt Studio, Model Check, Voices) — 2026-10-09 @@ -207,6 +204,9 @@ - **Gallery pictures dragged to the canvas open their workflow again (图库拖到画布又能直接打开工作流)**: since the gallery shows small thumbnails, Chrome handed ComfyUI the thumbnail, which has no workflow in it, so only a Load Image node appeared. The original is dragged now: on an empty spot it opens the workflow, on a Load Image node it becomes that node's picture (full size, not the thumbnail). - **The picture details' thumbnail rail keeps its place (查看参数左边的缩略图不再重新翻一遍)**: picking a picture near the end loads the next gallery page; the rail used to be rebuilt and scroll down again from the top, now the new pictures are added below. - **Back and the tabs work on a scrolled page (页面往下滚后,返回和顶部标签点得动)**: on a combo's page and on a Workflow Recipe's details, once the content was scrolled under the header, clicks on **‹ All combos**, **← Back to recipes** and the recipe's tabs went to the model tiles or the cover hidden behind them (Chrome still counted those as being there). Both pages now give the click to what you see. +- **Picking a model keeps your place (换模型时列表不再跳回顶部)**: in Current node's model picker, choosing a model further down redrew the whole list and scrolled back to the top (the model was chosen all the same). Only the chosen card's outline changes now. +- **A combo is saved once, not once per tag (搭配不再一下存几十次)**: opening a combo's bilingual tags saved it once for nothing, then once more for every tag's translation as it came back, so a long prompt wrote the file (and the activity log) dozens of times in a second. The translations are now saved together, half a second after the last one. +- **Activity shows where a long prompt changed (操作记录能看到长提示词改了哪里)**: values are kept to 400 characters, cut from the start, so an edit late in a long prompt showed the same text before and after. A long before/after pair is now cut around its first difference. ## v1.57.3 Beta (Hotfix: Offline Scan, Prompt Workshop) — 2026-09-29 diff --git a/GEMINI.md b/GEMINI.md index 5ab303f..603c129 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -8,4 +8,4 @@ 如果当前工具没有加载本文件,任务发起者应明确要求 Gemini 阅读 `GEMINI.md` 和 `AGENTS.md`。文件的存在不代表工具已实际读取,更不代表检查已自动执行。 -前端设计任务另读 [前端设计与协作方案](docs/plans/frontend-design-brief.md);具体功能计划按该文链接进入,不使用历史视觉建议作为当前任务单。 +前端设计任务按 AGENTS.md 第 4 节和 [前端架构](docs/architecture/frontend.md) 进行,不使用历史视觉建议作为当前任务单。 diff --git a/docs/audits/AUDIT_AND_BUG_REPORT.md b/docs/audits/AUDIT_AND_BUG_REPORT.md deleted file mode 100644 index 22cdff4..0000000 --- a/docs/audits/AUDIT_AND_BUG_REPORT.md +++ /dev/null @@ -1,200 +0,0 @@ -# Anomalous Model Browser 全功能实测审计与问题复原报告 -**Audit & Functional Verification Report** - -- **测试对象**:`Anomalous Model Browser` (异构模型浏览器 ComfyUI 插件) -- **运行环境**:ComfyUI Portable (Windows x64) / 本地端口 `http://127.0.0.1:8188` -- **当前 Git 分支**:`feature/recipe-model-binding` -- **浏览器会话录屏存档**:`anomalous_deep_test_1789816916045.webp` - ---- - -## 一、 测试操作过程完整复原 (Step-by-Step Reproduction) - -本次实测通过浏览器子智能体(Browser Subagent)与本地真实运行的 ComfyUI 实例进行端到端(E2E)交互操作,测试全流程操作复原如下: - -### 1. 插件唤起与主界面交互 -1. **浏览器进入 ComfyUI 画布**: - - 页面初始加载默认工作流节点(包括 `Load Checkpoint`、`CLIP Text Encode`、`KSampler`、`Anima VAE` 等)。 -2. **唤起插件主界面**: - - 点击 WebUI 界面悬浮触发球及左侧快捷立方体图标,`#anomalous-modal` 顺畅展开,居中层叠在画布上。 - - 验证右上角 `×` 关闭按钮与侧边栏 `☰` 折叠按钮,动画过渡自然,能顺畅收起并安全回到主画布。 - -### 2. 模型医生(Model Doctor)全局体检与修复测试 -1. **打开体检中心**: - - 点击侧边栏听诊器图标(🩺),切换至 Model Doctor 面板。 -2. **体检扫描表现**: - - 面板显示当前画布的节点模型状态:“健康 2 个 / 缺失 1 个”。 - - 正确检测到当前画布缺失模型:`Anima\qwen_image_vae.safetensors`。 -3. **交互操作测试**: - - 点击右上角“刷新缓存”按钮,控制台正确触发 `app.refreshComboInNodes()` 和 `/anomalous/all_hashes` 刷新。 - - 查看缺失模型右侧操作按钮:包含了“深度哈希扫描”、“手动替换”、“查看哈希”以及“Civitai 搜索”链接。 - -### 3. 素材库(Material Library)与分类检索测试 -1. **打开素材库**: - - 点击工具箱或侧边栏素材库图标,成功打开 Material Library 视图。 -2. **分类与搜索切换**: - - 点击顶部筛选胶囊:“全部”、“工作流”、“提示词”等分类,卡片即时响应过滤。 - - 在搜索框输入关键词 `Kirazuri`,检索到对应的完整工作流素材卡片 `Kirazuri (Anima)_v4.0`。 -3. **卡片拖拽交互测试**: - - 测试按住卡片尝试拖拽至画布空白处或节点上,排查拖拽反馈(发现重大逻辑断点,详见下文第二节)。 - -### 4. 模型扫描向导(Scan Wizard)与设置中心测试 -1. **打开扫描向导**: - - 点击侧边栏雷达扫描图标,弹出 Scan Wizard 原生 Dialog 弹窗。 - - 查看各项扫描策略配置:包括完整哈希、Civitai 元数据提取等。 - - 测试取消弹窗:按 `Escape` 键或点击外部背景,发现原生 Dialog 没有绑定外部点击关闭逻辑,必须向下滚动找到底部的“取消”按钮才能退出遮罩层。 -2. **全局设置中心(Settings Hub)**: - - 打开齿轮图标设置面板,测试在“紧凑模式 (Compact)”与“标准模式 (Standard)”之间来回切换,网格与卡片间距自适应良好。 - ---- - -## 二、 发现的核心缺陷与不合理之处深度剖析 (Critical Issues & Bugs) - -通过“浏览器实际操作 + 源码 AST/逻辑级走查”,发现以下 **4 个影响核心体验或违反技术方案** 的问题: - ---- - -### 🚨 缺陷 1(核心技术方案违背):本地缺失模型被误判为“⛔ 身份冲突”,导致修复链路完全中断 - -#### 1. 问题定位 -- **文件**:`api/model_resolution.py`(第 178-181 行) -- **函数**:`_resolve_from_candidates` - -#### 2. 代码问题根源 -在比对带有来源信息(Provenance,同时包含 `hash` 与 `size`)的模型时: -```python - hash_matches = [candidate for candidate in candidates if target_hash in _candidate_hashes(candidate)] - if hash_matches: - return {"found": False, "identity_conflict": True} - return {"found": False, "identity_conflict": True} # <--- 无论 hash_matches 是否为空,均返回 identity_conflict: True! -``` -- 当用户导入一个网上下载的带有哈希的工作流,而本地**压根没有这个模型**时,`candidates` 或 `size_matches` 是空的。 -- 代码执行到末尾,**无条件返回了 `{"found": False, "identity_conflict": True}`**! - -#### 3. 产生的不合理现象 -1. **误导用户**:在前端 `ui_doctor.js` 和 `ui_recipe_model_matching.js` 中,该模型会被标上鲜红的 `⛔ 身份冲突 (Identity Conflict)`,给用户一种“本地文件损坏或文件哈希冲突被系统拒绝”的假象。 -2. **阻断修复方案**:根据 `docs/architecture/model-resolution.md` 规范,只有当本地存在尺寸相同但哈希不同(或哈希相同但尺寸不同)的文件时,才算作真正的 `identity_conflict`。如果本地根本没有该模型,应该返回普通的 `{"found": False}`,以便触发正常的“未找到模型”提示、Civitai 自动检索建议或下载引导。 - -#### 4. 修复建议 -修改 `api/model_resolution.py`,只有在发现真正的冲突(如尺寸符合但哈希不匹配、或哈希符合但尺寸矛盾)时返回 `identity_conflict: True`;无任何匹配时应返回普通未找到: -```python - hash_matches = [candidate for candidate in candidates if target_hash in _candidate_hashes(candidate)] - if hash_matches: - return {"found": False, "identity_conflict": True} - return {"found": False} -``` - ---- - -### 🚨 缺陷 2(功能承诺未兑现 & 事件拦截冲突):素材库卡片“拖入画布载入工作流”无效且拖拽逻辑存在优先级漏洞 - -#### 1. 问题定位 -- **文件 A**:`web/modules/ui_material_cards.js`(第 90-96 行) -- **文件 B**:`web/modules/material_drag.js`(第 55-84 行) - -#### 2. 代码问题根源 -1. **提示与实现脱节(虚假功能)**: - - 卡片上的 Tooltip 明确写着:`"按住可拖拽至画布节点注入参数,或拖至空白处载入工作流"`(`t('materialCardDragHint')`)。 - - 但是在 `ui_material_cards.js` 中绑定拖拽时: - ```javascript - if (material.node_types?.length) { - bindMaterialDrag(card, owner, { - payload: () => ({ ...material, node_types: [...material.node_types], dragHint: t('materialDragParameters') || '拖拽素材参数至目标节点' }), - accepts: (node, source) => source.node_types.includes(node.type), - drop: (node, source, graph) => applyLibraryMaterial(owner, source, node, graph), - // 致命缺失:根本没有传入 dropOnCanvas 回调! - }); - } - ``` - **根本没有传入 `dropOnCanvas` 回调**!导致用户拖到空白画布释放时什么都没发生。 - - 此外,如果一个素材是**整图工作流素材**(`material.node_types` 为空),因 `if (material.node_types?.length)` 为假,导致**整张卡片完全没有被绑定拖拽能力**(`draggable = false`),用户根本拖不动! - -2. **`material_drag.js` 中的命中优先级缺陷(Precedence Inversion)**: - 在 `material_drag.js` 的拖拽结束处理中: - ```javascript - if (dropOnCanvas && overCanvas) { - try { await dropOnCanvas(event, data, graph); } - return; - } - if (!node || !accepts?.(node, data)) return; - try { await drop(node, data, graph); } - ``` - - 只要定义了 `dropOnCanvas`,一旦鼠标落在画布区域(`overCanvas == true`),代码会**无条件先执行 `dropOnCanvas`**(载入整图工作流覆盖画布)! - - 即便鼠标此时正精准悬停在某个兼容的节点上,也不会触发节点的参数注入! - -#### 3. 修复建议 -1. 在 `material_drag.js` 中调整执行优先级:**先判断是否命中了兼容节点(Target Node),若命中了则优先执行节点注入;仅当未命中任何节点且落在画布空白区域时,才触发 `dropOnCanvas` 载入工作流**。 -2. 在 `ui_material_cards.js` 中,只要素材包含工作流(`material.has_workflow` 或 `material.data?.workflow`),就为其绑定 `dropOnCanvas: async (e, data, graph) => openMaterialWorkflow(owner, material.filename)`,并在 `material.node_types` 为空时也允许拖拽。 - ---- - -### ⚠️ 缺陷 3(交互体验问题):扫描向导(Scan Wizard)缺少遮罩点击关闭 & 纵向溢出 - -#### 1. 问题表现 -- 扫描向导使用原生的 HTML `` 挂载并使用 `showModal()`。 -- 当弹窗内容较多(包含多项模型目录开关与说明)时,若屏幕纵向高度较小,底部“开始扫描”与“取消”按钮会被推到视口外部。 -- 用户尝试点击周围半透明深色遮罩(Backdrop)时,无法关闭弹窗;由于未在 `dialog` 上绑定 `click` 检测 `event.target === dialog` 的关闭逻辑,用户必须手动向下滚动寻找底部取消按钮。 - -#### 2. 优化建议 -- 在 `ui_scan_wizard.js` 中,为 Dialog 添加点击遮罩关闭逻辑: - ```javascript - dialog.addEventListener('click', (e) => { - const rect = dialog.getBoundingClientRect(); - const isInDialog = (rect.top <= e.clientY && e.clientY <= rect.top + rect.height - && rect.left <= e.clientX && e.clientX <= rect.left + rect.width); - if (!isInDialog) dialog.close(); - }); - ``` -- 限制 `max-height: 85vh` 并对内部表单容器开启 `overflow-y: auto`,确保底部的操作按钮始终固定可见(Sticky Bottom)。 - ---- - -### ⚠️ 缺陷 4(同步与竞态隐患):模型单次精准扫描后前端下拉列表刷新时机 - -#### 1. 问题表现 -- 在针对单个模型触发精准扫描(`triggerDirectModelScan`)时,后端会计算 SHA-256 并生成 `.info` 文件。 -- 前端在通知扫描成功后,虽然调用了 `window.anomalous_reload_hashes()`,但在 ComfyUI 原生节点的下拉菜单选项(`widget.options.values`)重新拉取存在微弱的异步延迟,此时若立即在 Model Doctor 中点击“自动修复”,偶尔会触发 `optionsCacheStale`,需等待二次刷新才能消除红框。 - -#### 2. 优化建议 -- 在单模型扫描写入完成后,增加 `await fetch('/anomalous/clear_cache', { method: 'POST' });` 并等待 `await app.refreshComboInNodes()` 完成后再触发 Doctor 的状态比对。 - ---- - -## 三、 总结与排查结论 - -1. **整体功能架构**: - 插件整体架构非常先进且完整,Model Doctor、Material Library、Recipe 工作流、Scan Wizard 的多面板协调运作良好,UI 设计美观、动效细腻。 -2. **核心问题收敛**: - - **核心 Bug 1**(`api/model_resolution.py` 误报 `identity_conflict`)是必须修复的高危逻辑漏洞,它直接导致工作流模型恢复方案失效; - - **核心 Bug 2**(素材库拖拽无响应及画布/节点优先级颠倒)直接影响了用户对于“素材一拖即用”的核心预期。 - ---- - -## 四、 修复复查与验收记录 (Verification Sign-off) - -- **验收时间**:2026-09-19 20:30 -- **修复 Commit**:`04b2eeff8f` (*fix(materials, doctor, scan): polymorphic material drag, third-party prompt nodes, model resolution, and scan wizard improvements*) -- **复查录屏文件**:`anomalous_fix_verify_1789820782702.webp` - -### 1. 自动化回归测试结果 -- **Python 后端单元测试**:运行 `unittest discover -s tests -p "test_*.py"`,**99 个测试全数通过 (OK)**。 - - 特别验证 `test_dynamic_hash_mismatch_is_rejected` 与空候选测试,确认无哈希匹配时不再误报 `identity_conflict`。 -- **前端契约与模块测试**:运行全部 32 个前端 `.mjs` 测试(涵盖 `studio_contracts.mjs`、`sidebar_feature_modules.mjs`、`material_feature_modules.mjs` 等),**32 项全部通过 (PASSED)**。 - -### 2. 浏览器端到端实测验证 -1. **模型修复与身份冲突 (Model Doctor)**: - - 模型比对逻辑恢复正常,本地缺失模型返回 `found: False`,恢复正常下载与引导逻辑; - - 唤起模型医生点击 `🔄 刷新缓存` 顺畅运行,控制台 0 报错。 -2. **素材库多态拖拽 (Polymorphic Material Drag)**: - - 全图工作流素材:卡片 Tooltip 提示更新为“按住拖至空白画布载入完整工作流”,拖拽至空白画布成功加载完整图谱; - - 提示词素材:支持拖拽至任意文本/提示词节点注入文本,或拖至空白画布自动创建对应 `CLIPTextEncode` 节点; - - 节点参数素材:拖拽至目标节点优先注入参数,拖至空白画布新建对应节点;修复了过去由于优先级颠倒导致的画布与节点拦截冲突。 -3. **扫描向导 (Scan Wizard)**: - - 弹窗底部操作按钮已设置固定粘性定位(Sticky Bottom),在任何屏幕高度下均不会被挤出视口; - - 点击外部暗色遮罩 Backdrop 立即平滑关闭向导; - - 按下键盘 `Escape` 键立即响应并关闭向导。 -4. **控制台与稳定性**: - - 全流程操作无任何未捕获的 JavaScript 异常或 HTTP 异常状态码。 - -**验收结论**:本次修复完整、精准,所有指出的严重缺陷及交互体验问题均已彻底解决并闭环。 - diff --git a/docs/audits/README.md b/docs/audits/README.md deleted file mode 100644 index 631c2ea..0000000 --- a/docs/audits/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# 审计与测试报告索引 (Audits & Verification Reports) - -本目录专门用于归档与整理 Anomalous Model Browser 插件在各开发阶段的端到端浏览器实测、质量审计、缺陷复原以及验收记录。 - ---- - -## 报告目录归档 - -| 日期 | 报告文件 | 测试类型 | 涉及模块 | 验收状态 | -| :--- | :--- | :--- | :--- | :--- | -| **2026-09-19** | [AUDIT_AND_BUG_REPORT.md](AUDIT_AND_BUG_REPORT.md) | E2E 浏览器全功能实测 & 缺陷深度排查 | Model Doctor / 素材库多态拖拽 / 扫描向导 / 模型恢复 | ✅ 已全数修复并验收闭环 | - ---- - -## 维护规范 -1. 新增功能测试、跨版本兼容性体检或重大 Bug 排查报告时,请统一存放在本目录 (`docs/audits/`)。 -2. 保持插件根目录整洁,避免在根目录直接堆放临时或专项测试文档。 diff --git a/docs/plans/code-structure-refactor.md b/docs/plans/code-structure-refactor.md deleted file mode 100644 index 8011c92..0000000 --- a/docs/plans/code-structure-refactor.md +++ /dev/null @@ -1,248 +0,0 @@ -# 代码结构拆分计划(交给 GPT-5.6-sol) - -日期:2026-09-17。代码基线:`29b14d2c7`。**状态:阶段 0–7 已完成;自动化验收通过,真实 ComfyUI 宿主视觉/交互验收仍需在运行中的宿主完成。** - -用户当前优先级是整理代码结构,暂停其他 UI 美化与功能扩展。本文保留原实施任务单,并在末尾记录实际完成结果;当前架构以专题文档和代码为准。 - -## 1. 要达到的结果 - -将大文件按实际职责拆开,做到修改一个页面或一项行为时,不必理解整个浏览器。保留现有界面、操作、接口和用户数据。每完成一个可验证的小阶段就本地提交,继续下一阶段,不必逐项询问用户。 - -不是把大文件切成 `part1/part2`,也不是迁移到新框架。不得为拆分引入打包系统、事件总线、通用服务容器或新依赖。 - -阅读顺序:[AGENTS.md](../../AGENTS.md) → [架构入口](../../ARCHITECTURE.md) → 当前阶段对应的专题。不要每轮通读所有文档。本文的文件名是拟定归属,先搜索是否已有等价模块;存在时优先复用。 - -## 2. 当前规模与真正的拆分点 - -以下是含空行的物理行数,仅用于定位;执行时按函数和调用方搜索,不按固定行号切割。 - -| 文件 | 行数 | 主要问题 | -| --- | ---: | --- | -| `web/styles.css` | 14,620 | 多个页面、主题和响应式规则混在一起,层叠顺序容易被破坏 | -| `web/modules/ui_recipe_detail.js` | 3,348 | 总览、模型匹配、参数编辑、版本、图库集中在一个模块 | -| `web/modules/ui_sidebar.js` | 2,963 | `createDOM()` 约 2,250 行,包含扫描向导、设置、工具箱、导航和窗口交互 | -| `web/modules/ui_doctor.js` | 2,186 | 模型诊断、节点助手、替换图库、参数方案交叉放置 | -| `web/modules/ui_detail.js` | 1,938 | 模型详情、编辑弹窗、高级选择器合并 | -| `api/recipes.py` | 1,514 | 工作流规范化、身份信息、图片、文件存储、历史和 HTTP 路由混合 | -| `web/modules/ui_gallery_detail.js` | 1,453 | 图片工作台生命周期、媒体交互、参数检查与保存操作集中 | -| `web/main.js` / `api/models.py` | 各 1,413 | 前者注册、设置、实例、浮动入口耦合;后者目录、媒体、修改、匹配路由耦合 | -| `web/modules/ui_recipes.js` | 1,297 | 列表筛选、卡片、保存/编辑对话框与保存流程混合 | -| `api/materials.py` | 1,294 | 素材构建、提示词规则、资源存储、查询、路由混合 | -| `web/modules/ui_materials.js` | 1,186 | 目录、详情、节点选择监听、应用操作混合 | -| `web/modules/ui_notebooks.js` | 1,042 | 工作台返回、笔记目录、编辑器和节点创建混合 | - -`locales.js` 为 2,373 行,主体是字典,不优先拆。已有提示词工坊、来源中控等模块虽然仍较大,已有明确边界,放到后续复核,避免第一轮同时动所有系统。 - -**已确认的耦合:** - -- `main.js` 通过 `AnomalousBrowser.prototype` 接入各 UI 模块。首轮保留方法名、参数和 `this` 绑定,只调整导入归属;不要同时更换整个实例模式。 -- `ui_sidebar.js` 的扫描状态、语言刷新、按钮和导航大量依赖 `createDOM()` 闭包。移动函数前必须列出它读取、修改的外部变量。 -- `api/materials.py` 从 `recipes.py` 导入了 12 个名称;`parameters.py` 也导入其中的参数签名、读取与图库辅助函数。后端不能把每份文件单独移动后再补循环导入。 -- `ui_gallery_detail.js` 有模块级 `wb` 和元数据缓存。它们不能在多个新文件各复制一份。 -- `api/__init__.py` 目前用星号导入路由。最后接线时应改成明确的模块引用,防止移动后隐式导出丢失或名称覆盖。 - -## 3. 拆分时保持的边界 - -依赖方向采用现有结构:**宿主接线 → 页面协调与视图 → 领域逻辑/存储与已有公共工具**。纯逻辑不能反向导入页面,子视图不能导入 `main.js` 获取实例。 - -- 每项状态只保留一份。已有跨页返回状态先留在当前 owner;局部 DOM、请求和清理归所属视图。迁移状态和移动代码分开提交,避免难以区分故障来源。 -- 为实际功能传入必要容器、数据和回调。若拆分需要几十个闭包参数,说明边界选错,应把相关状态和行为一起归入该功能模块。 -- 模块接入时直接修改所有内部调用方。只有确有外部调用或兼容需求的入口才保留薄转接,不建立永久的重复实现。 -- 同一阶段保持路由 URL、请求/响应结构、文件路径、字段、存储锁、撤销行为、节点连接与参数写入语义不变。不迁移用户数据。 -- 复用 `ui_lifecycle.js`、`ui_dom.js`、`recipe_actions.js`、`recipe_parser.js`、`recipe_identity.js`、`node_material_actions.js` 等现有能力,不再复制一套。 -- 普通逻辑模块通常以 200–600 行为参考。确有多个职责才继续拆,不能靠压缩代码、删注释或空壳包装达标。语言表和样式表不套这个阈值。 -- 文件改放子目录时重新计算相对导入,特别是 ComfyUI 的 `scripts/app.js` 和图片 URL。导入无副作用的模块不应注册第二个扩展、启动扫描或立即创建页面。 - -## 4. 分阶段执行 - -每个阶段允许拆成几个提交;一个提交完成一个可验证的职责迁移。不要一次把以下所有文件建好再尝试运行。 - -### 阶段 0:建立本轮基线 - -1. 查看状态和最新差异,记录起始提交;保留他人的改动。 -2. 为即将移动的职责记录:入口/调用方、读写状态、全局监听、请求、DOM/CSS 依赖、关闭路径。只写本阶段,不建立全仓超大清单。 -3. 先运行本阶段已有的相关测试,记录已有失败。需要补测试时优先覆盖真实行为,而不是源码中是否存在某个字符串。 -4. 记录相关页面的当前视觉和交互。后续比较必须使用同一主题、窗口尺寸和数据;不拿不同时期截图作基线。 - -### 阶段 1:先从侧栏移走独立功能 - -目标文件:`ui_sidebar.js`。第一刀选扫描向导,不先重写导航。 - -| 拟建模块 | 迁移范围与接口 | -| --- | --- | -| `ui_scan_wizard.js` | `createWizardModal()` 及其专属构建/扫描控制;接收全局或指定模型范围与完成回调,返回打开、关闭与语言刷新所需接口 | -| `ui_folder_manager.js` | `openFolderManager()` 与该对话框专属请求、DOM、关闭逻辑;原来的目录浏览 `renderSidebar/loadFolders` 暂时保留 | -| `ui_help.js` | `showHelp()` 及内容构建,仅在打开时创建所需内容 | - -扫描进度继续使用 `scan_progress.js`,不能新增另一份“是否正在扫描”。若该状态也被全局按钮使用,明确由扫描功能拥有,按钮只读取/订阅实际状态。关闭向导是否取消扫描必须保持当前语义,不能把 UI 关闭误当成后台任务取消。 - -验收:扫描向导打开/关闭、单模型与全局入口、失败反馈、文件夹管理取消与保存刷新、帮助关闭。涉及扫描或写文件的验证优先使用隔离数据与桩服务,不自动扫描用户整个模型库。 - -### 阶段 2:收拢外壳和入口 - -在阶段 1 稳定后,从 `createDOM()` 提取: - -- `ui_settings_hub.js`:设置中心与模型显示设置。接口覆盖 open/close/refreshLanguage,保留原来的设置来源、键名和生效时机。 -- `ui_toolbox.js`:工具箱内容、开关和底部按钮装配;复用 `tool_registry.js`、`sidebar_actions.js`。按**当前代码**保持固定栏与工具过滤规则,不顺便实施旧计划中的另一种交互。 -- `ui_browser_navigation.js`:主面板切换与返回协调。先收拢现有 `hideAllPanels/closeWorkspace` 相关调用,不引入新的路由系统;笔记模块最终不再独占其他工作台的公共关闭逻辑。 -- 窗口拖动、缩放、停靠先保留在外壳;仍形成独立大段时再提取 `browser_window.js`,复用生命周期工具,不改变尺寸偏好。 - -`createDOM()` 最终只创建骨架、装配功能、连接明确回调。初始化完所有被引用的控件后再绑定/刷新语言,避免迁移后出现 TDZ 或读取尚未初始化的按钮。 - -随后整理 `main.js`:浏览器类与方法接线放到 `browser.js`,浮动入口放到 `browser_entry.js`,设置描述与语言/主题应用放到 `interface_settings.js`。扩展注册、实例创建顺序和宿主钩子仍由 `main.js` 统一协调。设置模块通过参数/回调工作,不能反向导入 main;保留被其他模块调用的现有全局入口,先核实调用者再删除。 - -验收:首次打开、关闭重开、各主页面切换、配方→模型详情→配方、语言切换、设置保存、工具箱提示、停靠、窗口拖动与调整尺寸。确认只有一个浏览器实例和一份全局监听。 - -### 阶段 3:拆配方详情,再拆目录 - -`ui_recipe_detail.js` 留下 `showRecipeDetail()`、活动页签、一次详情会话的状态与生命周期协调。按下面顺序逐块移动: - -1. `ui_recipe_versions.js`:`renderVersions/renderDiffPanel`。继续用 `recipe_diff.js`;恢复版本后的刷新由明确回调交回详情协调器。 -2. `ui_recipe_gallery.js`:`renderRecipeGallery/showGalleryComparison` 及专属资源处理;图片详情打开通过回调或直接依赖现有图片工作台入口,不能绕素材目录模块中转。 -3. `recipe_model_matching.js` + `ui_recipe_models.js`:前者负责匹配操作与结果,后者负责组成卡片、来源编辑和本地匹配操作界面;复用身份规则,不能从名称/预览图推断同一模型。 -4. `recipe_parameter_data.js` + `ui_recipe_parameters.js`:先移出 `parameterNodeOrder/topologicalSortNodes/parseEditorValue/paramsWithPromptRole` 等无 DOM 逻辑,再移参数方案 UI。较大的原始节点检查与编辑器可分别放 `ui_recipe_node_inspector.js`、`ui_recipe_parameter_editor.js`。 -5. `ui_recipe_overview.js`:总览与元数据编辑,参数正文读取继续遵循真实工作流,不改用列表摘要。 - -不要把所有小函数扔进 `recipe_detail_utils.js`。格式化工具仅在确有多个调用者时共享;请求成功后的刷新由协调器处理,不让子页互相直接调用重绘。 - -接着将 `ui_recipes.js` 的 `showRecipeSaveDialog/showRecipeEditDialog` 放到 `ui_recipe_dialogs.js`,卡片渲染放 `ui_recipe_cards.js`;列表查询、筛选、分页与页面状态留在目录模块。`handleSaveRecipe` 继续协调实际捕获和保存,不能在新对话框中复制该流程。 - -验收:配方筛选与详情、长提示词、原始参数、参数方案保存再读回、模型详情往返、历史比较/恢复、图库打开/关闭。配方包导入导出保持当前关闭状态,移除的入口不恢复。 - -### 阶段 4:拆其余混合页面 - -逐行执行下表,每行独立提交与验证,禁止把全部页面同时改成半成品。 - -| 当前文件 | 拟定归属 | 重点验证 | -| --- | --- | --- | -| `ui_doctor.js` | 保留诊断面板和全局扫描;节点助手到 `ui_node_assistant.js`;`_openGalleryReplacer` 到 `ui_node_model_picker.js`;参数方案卡片与预览到 `ui_node_presets.js` | 哈希依据不变;替换正确节点;取消无写入;助手历史可用 | -| `ui_detail.js` | 保留 `showDetail`;`showEditModal` 到 `ui_model_editor.js`;`_openAdvancedModelSelector` 到 `ui_model_selector.js` | 详情往返、编辑保存、选择器取消、原节点与路径不串 | -| `ui_gallery_detail.js` | 保留工作台打开/关闭与唯一 `wb`;舞台缩放/胶片栏到 `ui_image_stage.js`;参数、提示词、模型与节点检查到 `ui_image_inspector.js` | 快速换图无旧结果覆盖;关闭释放媒体与请求;选中块保存准确 | -| `ui_materials.js` | 保留目录与分页;详情到 `ui_material_detail.js`;卡片到 `ui_material_cards.js`;应用和选择监听尽量归入已有 `ui_material_application.js` | 素材拖放仍整体替换;节点身份复核;详情读正文;翻页与筛选不丢 | -| `ui_notebooks.js` | 保留笔记目录与保存协调;编辑器到 `ui_notebook_editor.js`;画布创建到 `notebook_canvas.js`;公共返回已归阶段 2 | 保存队列和重开不丢内容;正负提示词隔离;节点创建与连接不变 | - -图片元数据缓存可以留在协调模块,缓存上限和失效规则保持不变。需要跨模块使用时暴露窄访问接口,不复制缓存,不把 `wb` 作为可随意修改的全局对象导出。 - -### 阶段 5:样式独立拆分,不同时重新设计 - -保留 `web/styles.css` 作为外部入口。先核对 `main.js` 的 CSS 加载逻辑,再把样式拆到 `web/styles/` 下的基础控件、外壳、模型、图库、配方、素材、提示词、工具与主题等文件。 - -**首先保证原始层叠顺序。** 不能按关键词把分散的同名选择器直接合并搬走。先记录规则顺序与主题/媒体条件;需要时按连续职责块分两步迁移,稳定后再清理重复声明。混合多个领域的选择器先归公共部分,未经确认不拆成几个不同位置的规则。 - -低风险方案是入口顶部有序 `@import`。若使用多 link,也必须只有一份确定顺序的清单,并保留防重复加载。二选一,不能双重加载。不要引入 CSS cascade layers,因为它会改变已有优先级。 - -迁移时检查相对 `url(...)` 资源路径、`@keyframes`、主题限定、媒体查询和插件作用域;子样式的缓存刷新也要验证,不能只刷新入口版本。检查网络中是否缺文件、串行请求造成明显闪烁。没有测试依据,不顺便做按页面动态加载。 - -验收:同条件截图/关键计算样式对比,普通主题与深海血族、普通窗口与停靠、浮层层级、文件夹隐藏/恢复、提示定位、拖放与键盘焦点。此阶段不换配色、间距、动画或 DOM 结构。 - -### 阶段 6:先拆后端共享领域,再移动路由 - -先读 [后端专题](../architecture/backend.md)、[配方专题](../architecture/recipes.md)、[素材专题](../architecture/material-library.md)。 - -1. 从 `recipes.py` 提取 `workflow_schema.py`:工作流校验、指纹、参数签名、易变字段规则等纯计算。确属配方专有的规范化放 `recipe_schema.py`;不要把文件路径和 HTTP request 带入纯逻辑。 -2. 提取 `recipe_store.py`:目录、读取、写入与历史操作;提取 `recipe_images.py`:来源图片验证、读取与封面处理。路径授权仍调用现有安全工具,I/O 仍放到原来的工作线程边界。 -3. 同步改 `materials.py`、`parameters.py` 和 `recipe_packages.py` 的调用。后者目前是 `from . import recipes as recipe_store`,这个别名指向旧路由模块,不是拟建的存储模块。逐项检查其 `recipe_store.xxx` 访问,不能只搜索导入行或直接把别名换成新文件。 -4. 路由模块依赖领域与存储模块,领域模块不得反向导入路由。锁和缓存只保留在实际拥有者中,同一类写入不能因拆文件变成两把独立锁。 -5. `api/recipes.py` 最终保留 HTTP 参数解析、错误/状态码映射、调用和响应。不要为了“薄路由”把整个旧文件原封不动搬进另一个 1,500 行的 service。 - -随后拆 `api/models.py`:目录查询/列表、身份解析、元数据修改、媒体与封面分别归入 `model_catalog.py`、`model_resolution.py`、`model_metadata.py`、`model_media.py`。先按现有端点边界移动,公共路径与类别规则继续复用 `utils.py` 和根目录的 `model_policies.py/model_identity.py`。保留跨目录回退和受保护类别规则。 - -最后拆 `api/materials.py`:`material_schema.py` 负责构建与提示词规范化,`material_store.py` 负责记录及资源生命周期,路由模块负责请求响应和有界查询。若存储模块仍大,再将图片资产操作独立成 `material_assets.py`,不预先造空模块。 - -`api/__init__.py` 改成明确的模块引用和注册,例如 `from . import models`、`models.api_get_models`;比较拆分前后的 **HTTP method + path** 清单,避免少注册、多注册或重复注册。用户设置和导入导出 gate 保持不变。 - -验收全部使用临时目录与隔离配置:保存/读取往返、非法路径拒绝、失败保留旧文件、历史恢复、素材分页过滤、修改封面/元数据、禁用导入导出的 503。不能通过删除或覆盖用户真实数据测试。 - -### 阶段 7:收尾与剩余热点复核 - -- 再统计大文件,按职责复核 `ui_prompt_workbench.js`、`ui_prompt_source_deck.js`、`ui_model_sources.js`、`material_inspector.js` 和 `api/utils.py`。有独立职责才继续拆,并记录边界;不能仅因超过阈值机械拆分。 -- 复核导入环、无人调用的旧导出、遗留重复实现和 `EXTRACTED` 占位。不要用任意 optional chaining 或空 catch 掩盖断掉的接线。 -- 更新实际发生变化的架构专题与入口所有者列表。此时删除过时说明,不能把本文的拟定方案提前复制成已实现架构。 -- 本计划逐阶段补充提交号、验证和剩余事项;发生中断时,下一轮从这里继续,不重新扫描整个仓库。 - -## 5. 验证与效率要求 - -每阶段先做 JS 语法/模块链接或 Python 编译/导入检查,再跑相关行为测试。涉及界面和宿主行为时还需真实 ComfyUI 验证,语法通过不能代替点击路径。移动模块后更新测试入口,尽量加载实际模块,不把待测行为本身 mock 成成功。 - -当前存在的定向检查(从插件根目录运行)。下表每个 JS 文件分别用 `node --experimental-vm-modules <文件路径>` 执行;此处参数表示替换成表内实际路径,不是一次执行整表: - -| 影响范围 | 现有检查 | -| --- | --- | -| 外壳/快捷栏 | `tests/shortcut_layout.mjs`、`tests/shortcut_organizer.mjs`、`tests/update_guide.mjs` | -| 素材与节点应用 | `tests/material_workspace_flow.mjs`、`tests/studio_contracts.mjs` | -| 笔记 | `tests/notebook_render_test.mjs`、`tests/notebook_save_roundtrip.mjs` | -| 提示词工坊/读取 | `tests/prompt_ui_lifecycle.mjs`、`tests/prompt_material_source.mjs`、`tests/prompt_library_sync.mjs` | -| 模型来源 | `tests/model_sources_hub.mjs` | -| 导出 gate | `tests/export_availability.mjs`;Python 用便携环境执行 `python -B tests/test_export_availability.py` | - -表中测试存在不等于本轮已经通过。配方详情、模型编辑、扫描向导、后端存储等未被这些测试充分覆盖的路径,需要有针对性地补行为验证,不能拿无关测试通过数充当覆盖。 - -新增测试先核对 `.gitignore`,必须明确哪些随仓库交付。每次只跑受影响的集合;阶段完成后不重复跑同一组,除非又改了代码或发现新问题。 - -完成标准:原调用方都接入新归属;原文件没有第二份实现;关键行为验证通过;入口和返回仍可用;无新增持续轮询、全量数据加载或监听泄漏。行数下降本身不是验收标准。 - -## 6. 交给 GPT-5.6-sol 的启动指令 - -> 请按 docs/plans/code-structure-refactor.md 整理 Anomalous_Model_Browser 的代码结构。先读 AGENTS.md 和相关架构专题,确认最新差异,从阶段 0、阶段 1 开始,然后按依赖顺序继续。保留现有外观、功能、数据和接口,不顺手美化,不恢复已关闭或移除的功能。 -> -> 每次完成一个职责迁移,检查调用方、状态归属、闭包依赖和关闭路径,运行相关验证后本地提交,再继续。常规实现选择自行处理;遇到已有故障先区分基线与回归,不能隐藏或伪造成功。不要把所有文件一次拆完才验证。 -> -> 每阶段在计划中记录实际模块归属、提交号、验证结果与未完成项。需要中断时留下可接续状态;不推送远端。最终说明职责如何变清楚、哪些行为已验证,以及还有哪些真实限制。 - -## 执行记录 - -- 阶段 0(完成):以 `c39701773` 为本轮起点,开工时工作区干净。基线通过 - `shortcut_layout.mjs`、`shortcut_organizer.mjs`、`update_guide.mjs`;未建立真实 - ComfyUI 点击截图基线,因此后续不能把 DOM 模拟检查称为宿主视觉验收。 -- 阶段 1(完成,`2a53e3edf`):扫描向导归 `ui_scan_wizard.js`,文件夹管理归 - `ui_folder_manager.js`,帮助弹窗归 `ui_help.js`。单模型卡片与全局扫描入口共用 - `openScanWizard()`;新增行为检查覆盖帮助关闭、文件夹取消/保存刷新、单模型扫描请求 - 与向导关闭不取消后台扫描。 -- 阶段 2(代码拆分完成,`1e42033ac`、`67d5be6277`、`a984ee4a31`):设置中心与模型卡显示设置归 - `ui_settings_hub.js`,工具目录、固定快捷栏和工具分派归 `ui_toolbox.js`,公共面板 - 隐藏/配方详情清理归 `ui_browser_navigation.js`。`ui_sidebar.js` 已从约 2,963 行降至 - 约 780 行,`createDOM()` 现在主要保留外壳、顶部导航、窗口交互和子模块装配。 - 浏览器类和方法装配归 `browser.js`,浮动/顶栏/菜单入口及唯一实例归 - `browser_entry.js`,语言和主题设置归 `interface_settings.js`;`main.js` 现只协调 - 扩展注册与宿主钩子。笔记、配方和素材共用的工作区返回协调也已迁入 - `ui_browser_navigation.js`。新增模块边界测试覆盖注册描述、入口设置、语言、主题与 - 工作区恢复;真实 ComfyUI 拖动、停靠与窗口缩放仍待宿主验收。 -- 阶段 3(代码拆分完成,`d8691d881`、`413b876dfe`、`56774a2d99`、`881570e887`、`e5f3301971`):配方版本历史/差异/恢复归 - `ui_recipe_versions.js`,结果图库与图片工作台跳转归 `ui_recipe_gallery.js`,跨子页 - 共用的 DOM、长值展示和复制控件归 `ui_recipe_detail_dom.js`。删除了无人调用的旧图库 - 对比实现;图库不再借道素材目录模块打开图片工作台。目录筛选归 - `ui_recipe_catalog.js`,卡片与拖拽归 `ui_recipe_cards.js`,保存/编辑对话框归 - `ui_recipe_dialogs.js`,封面媒体归 `ui_recipe_media.js`;模型预览、匹配与显式替换归 - `ui_recipe_model_matching.js`,总览归 `ui_recipe_overview.js`,提示词角色、参数编辑、原始节点与 - 参数预设归 `ui_recipe_parameters.js`,内联持久化归 `ui_recipe_metadata.js`。协调器 - `ui_recipe_detail.js` 从约 2,983 行降至约 760 行,`ui_recipes.js` 从约 1,297 行降至约 - 690 行。新增检查覆盖版本恢复、图库交接、目录卡片隔离、模型替换持久化、参数拓扑/解析、 - 总览和提示词提取;真实 ComfyUI 视觉与交互仍待宿主验收。 -- 阶段 4(完成,`e6217bb557`、`12875f9053`、`3e3e26d2b2`、`2951d610c5`、`7afa7518bc`): - 模型诊断保留诊断、全局扫描和哈希协调,助手、原生控件选择器、参数方案分别归 - `ui_node_assistant.js`、`ui_node_model_picker.js`、`ui_node_presets.js`。模型详情保留展示协调, - 编辑器与高级选择器归 `ui_model_editor.js`、`ui_model_selector.js`。图片工作台仍唯一持有 `wb` - 和元数据缓存,舞台/缩放/胶片条归 `ui_image_stage.js`,检查器标签归 `ui_image_inspector.js`。 - 素材目录、卡片、详情与节点应用/监听分别归 `ui_materials.js`、`ui_material_cards.js`、 - `ui_material_detail.js`、`ui_material_application.js`。笔记目录与保存留在 `ui_notebooks.js`, - 编辑器归 `ui_notebook_editor.js`,画布节点创建归 `notebook_canvas.js`。新增边界与行为检查覆盖 - 以上接线、监听器清理、控件路径、素材应用和笔记保存队列;真实 ComfyUI 视觉仍待宿主验收。 -- 阶段 5(完成,`e8b50ecaf5`):`web/styles.css` 保留为唯一外部入口,并按原始字节顺序有序导入 - `web/styles/00-foundation-models.css` 至 `10-model-sources.css`。所有子样式带同一缓存版本; - `css_bundle_order.mjs` 校验导入唯一/有序,且子文件拼接 SHA-256 与拆分前完全一致 - (`d014a9d1826e2c48a46ac72389fe4e5bdd0b2ba85618f653d13852cd93812bc1`)。 - 未引入 cascade layer、动态加载或视觉改版;真实主题、停靠和浮层对比仍需宿主验收。 -- 阶段 6(完成,`6ad975c1cd`、`9cebdbd994`、`a0c6849878`):配方的工作流规则、配方规范化、 - 图片和存储分别归 `workflow_schema.py`、`recipe_schema.py`、`recipe_images.py`、`recipe_store.py`; - 模型目录、身份解析、元数据写入和媒体归 `model_catalog.py`、`model_resolution.py`、 - `model_metadata.py`、`model_media.py`;素材规范化、资产和单一锁/缓存存储归 - `material_schema.py`、`material_assets.py`、`material_store.py`。路由入口改为显式模块注册, - `test_route_manifest.py` 固定 68 个 method/path 对。兼容门面仅保留历史内部测试/调用点,不复制状态。 -- 阶段 7(完成,`a63a615514`):复核后保留 `ui_prompt_workbench.js` 与 - `ui_prompt_source_deck.js` 的单工厂闭包边界,避免拆出大量可变状态参数;`ui_model_sources.js` - 仍是模型来源这一项功能的查询/编辑/持久化协调器;`material_inspector.js` 仍聚焦共享元数据与 - 节点参数检查。发现 `api/utils.py` 确有五类独立职责,已拆为 `path_utils.py`、 - `media_routes.py`、`translation_routes.py`、`gallery_routes.py`、`folder_types.py`,原文件仅作兼容导出。 - Python/JavaScript 相对导入图均无环,未发现 `EXTRACTED`/待提取占位或旧模型/配方/素材星号路由导入。 - 最终通过全部 29 个 `.mjs` 检查、89 个 Python `unittest`、全量 `api/*.py` 编译和路由清单检查。 - 本轮未连接运行中的 ComfyUI,因此不声称已完成真实拖放、停靠、主题、媒体释放和点击路径视觉验收。 diff --git a/docs/plans/customizable-tool-shortcuts.md b/docs/plans/customizable-tool-shortcuts.md deleted file mode 100644 index a222066..0000000 --- a/docs/plans/customizable-tool-shortcuts.md +++ /dev/null @@ -1,133 +0,0 @@ -# 工具箱与快捷栏自定义:交给 Gemini 的实施计划 - -状态:已放弃。当前是固定快捷栏(见 `ui_toolbox.js`);按本计划写的 `shortcut_layout.js`、`ui_shortcut_organizer.js` 从未接入,已删除。只作历史参考,不要照此实施。 -基线:`77ae7ce88`;开工时以实际 Git 差异和现有代码为准,不回退后续工作。 - -## 目标与范围 - -把左下角做成用户可整理的快捷栏:工具箱的工具能拖到栏中,栏中的可移动工具能拖回工具箱,也能调整顺序。 -同时完成短文字、详细提示的出现时间与位置调整。保留当前紧凑布局、工具箱外框尺寸和现有工具行为。 -先阅读 `AGENTS.md`、`docs/architecture/frontend.md`、`docs/architecture/update-guide.md`,仅加载本次相关内容。 -本任务有拖动状态与持久化,按下面阶段实施;不借机重写整个侧栏、换 UI 框架或引入拖拽依赖。 - -## 一、交互设计定稿要求 - -实施前用简短文字或草图说明:正常、拖出、拖回、排序、容量已满、取消六种状态。 -按本计划做出完整方案后直接实施;常规视觉细节由 Gemini 决定,不增加多轮审批。若与现有代码冲突,优先保持业务语义。 - -### 1. 工具箱是完整目录,底栏是快捷入口 - -- 工具箱始终保留所有可用工具。拖到快捷栏代表“固定”,工具箱卡片仍在,并标注“已固定”。 -- 从快捷栏拖回工具箱代表“从快捷栏移除”,不删除工具、不关闭正在执行的任务、不更改数据。 -- 第一版固定两个锚点:最左的工具箱、最右的设置。它们不能被拖走,防止用户丢失整理和恢复入口。 -- 中间最多 4 个可自定义入口,默认依旧是扫描、模型医生、节点助手、素材库,总共最多 6 个按钮。 -- 工具箱中现有的导入导出中心、提示词工坊、翻译助手、模型来源也能固定到中间位置。 -- 工具箱卡片顺序固定,快捷栏顺序由用户决定。第一版不做工具箱内部自由排序、分组、分页或自由浮动窗口。 -- 拖出只接受快捷栏区域;丢到画布、图库、模型卡片或窗口外一律取消,不能生成节点或触发文件操作。 -- 容量已满时不自动挤走其他工具,提示“快捷栏已满,请先将一个工具移回工具箱”;菜单固定与拖动规则一致。 -- 重复固定不产生副本;已固定工具拖到快捷栏只调整已有位置。 -- 提供“恢复默认快捷栏”,只重置本布局,不调用重置插件全部设置的功能。 - -### 2. 工具箱内的可发现性 - -- 在工具箱空白背景区域设置低干扰、可读的提示:“拖到下方,固定常用工具;拖回这里,收起快捷入口”。 -- 使用独立的提示/放置区域,不把文字覆盖在卡片上;空间不足时改为底部短提示,不能撑大工具箱。 -- 保持工具箱外框尺寸,工具增多只让内部列表滚动;底部提示/放置区保持可见,不遮挡卡片和关闭按钮。 -- 工具卡片悬停或键盘聚焦时显露小拖动把手和“更多”操作入口;不要用常驻的大段教程挤占工具区域。 -- 拖动快捷入口进入工具箱时,放置区域高亮并显示“松开,从快捷栏移除”;工具箱按钮本身也可作为拖回目标。 -- 工具箱关闭时,拖到工具箱按钮上约 350 毫秒后自动展开;计时离开即取消。只在确实拖动快捷入口时启用。 -- 没有完成放置时,不提前改动排列或保存设置。预览插入位置使用占位符/指示线。 - -### 3. 前一轮文字、时间与布局调整(本任务必做) - -| 元素 | 第一版参数与行为 | -| --- | --- | -| 按钮内短文字 | 鼠标停留约 100 毫秒后,以约 120 毫秒的向上淡入替换图标;保持按钮尺寸与点击区域不变 | -| 完整说明框 | 从进入按钮开始累计约 600 毫秒后出现;不是短文字结束后再多等 600 毫秒 | -| 离开按钮 | 取消未触发的等待,详细提示立即隐藏;图标在约 100 毫秒内恢复,不保留离场延迟 | -| 说明框位置 | 默认在按钮上方,间隔约 8 像素;左右限制在实际侧栏边界内,并兼顾视口 | -| 排版 | 名称稍突出;正文常规字重、颜色稍淡、自然换行;不把整段解释都加粗 | -| 键盘 | focus-visible 立即显示短文字和清晰焦点,详细说明遵循约 600 毫秒等待;失焦立即清理 | -| 无悬停设备 | 短文字直接可见;提供点击“更多”的整理方式,不依赖悬停或长按才能使用 | -| 减少动态效果 | 保留识别文字,但取消位移动画,遵守已有 prefers-reduced-motion 规则 | - -这些时间是初始设计值,真实浏览器验证时允许小幅调整,最终只在一处定义,不能散落在多个模块。 -从一个按钮快速移到另一个时,重新计时,不闪现上一个按钮的说明。 -拖动开始立即隐藏所有按钮提示,拖动期间不显示短文字过渡和说明框;结束后等待新的悬停/聚焦再触发。 -提示框不能拦截点击、遮挡拖放目标;不要再使用超宽且正文加粗的提示框。 -当前 tooltip 使用伪元素:若要分开标题/正文或准确限制边界,替换这一组按钮的提示渲染为一个共享提示节点,并移除旧提示属性/规则,避免出现两套提示。 -不要改动插件所有其他 tooltip。新增可固定工具也必须有短标签、完整名称、说明与中英文翻译。 - -### 4. 拖动和普通点击必须分清 - -- 鼠标按下不立即拖动,移动超过约 6 CSS 像素才进入拖动态;工具箱优先从把手起拖,卡片正文点击继续打开工具。 -- 快捷栏支持按住按钮拖动。超过阈值后的 pointerup/click 不能打开工具或触发扫描,即使最后取消也不能误点击。 -- 执行原有动作只允许来自明确的普通点击或键盘激活;拖动预览不能调用工具 action。 -- 拖动时保留来源占位、显示小型拖动预览和有效放置提示,不扩大按钮、推动整个侧栏或影响画布缩放。 -- 拖动过程暂缓工具箱的“点击外部关闭”。与现有 document mousedown 关闭监听统一协调,不能边拖边把目标关闭。 -- Esc、pointercancel、失焦、关闭插件、语言切换或重建相关视图均取消拖动并恢复原布局;清理指针捕获、全局监听、计时器和预览。 -- 自动展开的工具箱取消后恢复此前开关状态;原本打开的工具箱保持打开。放置后可保留打开,方便继续整理。 -- 在插件缩放和停靠模式下统一使用视口坐标与 getBoundingClientRect,不能混用缩放前尺寸计算落点。 - -### 5. 不会拖动也能完成整理 - -工具箱的“更多”操作提供“固定到快捷栏/从快捷栏移除”;快捷入口提供“左移/右移/移回工具箱”。 -不要仅提供右键菜单,也不要依赖鼠标。使用真实 button、可读名称、可见焦点;操作后把焦点留在合理的位置。 -菜单和拖动走同一个布局修改函数,包含相同的容量、去重、固定入口约束;成功放置可用 aria-live 简短播报。 - -## 二、工程结构 - -### 现有责任与风险 - -- `web/modules/ui_sidebar.js`:目前拥有工具箱卡片、侧栏按钮、扫描状态、打开/关闭和语言刷新;它已经很大,新完整拖动实现不要继续塞入 createDOM。 -- `web/modules/sidebar_actions.js`:目前只认识六个硬编码 DOM ID;自定义后必须让新增快捷工具也能获取标签和用途。 -- `web/modules/ui_lifecycle.js`:复用视图资源清理机制;不要新建永久全局拖动监听。 -- `web/styles.css`:找到现有 sidebar-actions、toolbox、tooltip 规则并修改,移除本次取代的规则;禁止追加覆盖包。 -- `web/modules/ui_update_guide.js`:保持上方手动入口,不因自定义布局或新引导版本恢复自动弹窗。 - -### 建议职责拆分(可按仓库命名习惯调整,不为拆分增加空壳) - -1. 工具定义:一个稳定 ID 对应一份名称键、短标签键、说明键、图标和可固定属性;目录和快捷栏共用。动作回调在侧栏装配时显式传入,纯数据模块不读取画布。 -2. 布局状态:单独的小模块负责默认值、去重/过滤、增删排序、容量与存储;只保存可移动 ID 的顺序,固定两端由视图添加。 -3. 整理视图:独立模块负责拖动、放置预览、菜单与取消清理。只在有效放置/菜单确认时调用布局修改接口。 -4. 现有 sidebar_actions 模块负责短标签和说明提示,可改为接收工具定义;禁止同时保留两套工具元信息和两套点击动作。 - -不要直接搬移或克隆工具 DOM 来实现固定:这会带走旧关闭逻辑、造成重复 ID 或丢失状态。 -每个工具在两处调用同一个动作。保留现有隐藏其他面板、初始化、扫描中状态和错误反馈;不要通过隐藏按钮的 `.click()` 间接调用。 -特别核对 `setScanButtonState`、`anomalous-scan-btn` 及语言刷新的所有调用方:扫描运行中移动入口,不能丢失进度、重复启动、产生失效引用。 -DOM ID 保证唯一;两个可见入口用稳定 toolId 关联。当前未固定的工具也不能因为入口不在底栏而失去业务状态。 - -### 持久化与兼容 - -- 使用一个带 schemaVersion 的独立 localStorage 键,例如 `anomalous_shortcut_layout_v1`,内容仅为 `{ schemaVersion: 1, pinned: [...] }`。 -- 首次使用采用原来的四个中间入口;显式的空数组是合法选择,不得又补回默认值。 -- 读取时拒绝错误结构/未知版本;去掉未知工具 ID、重复项和固定锚点,限制容量;删除工具或升级后仍能打开界面。 -- 不保存 DOM、翻译文本、回调代码或文件路径;不修改后端配置、工作流或素材数据。 -- 写入失败保留当前会话布局,准确提示刷新后可能丢失;禁止伪报“已保存”,也不能让插件打不开。 -- 第一版只在当前浏览器配置中持久化,不实现账户同步、多设备同步或布局导入导出。 - -## 三、不能改变的产品边界 - -- 导入导出快捷入口只打开现有已验证的 AMB0/AMB1 中心,仍是同一个中心;不能恢复配方、素材库、提示词工坊的独立文件导入导出。 -- 保留素材提示词自动同步、来源标注和不可在工坊删除来源卡片的规则。 -- 不修改图片/工作流哈希、模型识别、模型扫描内容、节点写入语义或用户文件。 -- 不新增全局自动教程;功能上线后再更新引导内容配置和双语文案,仍由用户点击感叹号查看。 - -## 四、实施顺序与验收 - -1. **设计与状态**:说明六种交互状态,提取工具定义和布局纯逻辑,验证默认、空数组、去重、容量、损坏数据及存储失败。此阶段保留旧界面可运行。 -2. **无拖动整理**:先让目录与快捷栏共用动作,通过菜单完成固定、移除、排序、恢复默认和刷新恢复;核对扫描状态、语言刷新、唯一 DOM ID。验证通过即本地提交。 -3. **拖动与提示**:接入同一状态接口,实现阈值、放置、取消和文字/说明时间布局;验证后再提交,不另造第二套状态。 -4. **交付**:仅更新改变的架构契约与用户可见更新说明。汇报实际验证、未测环境及本地提交,不推送 GitHub。 - -自动测试加载真实布局模块与交互模块,只模拟 DOM/存储边界。拖动至少覆盖成功放置、移出目标取消、Esc/父视图关闭清理、拖后不触发点击。 -现有相关测试是 `node --experimental-vm-modules tests/update_guide.mjs`;扫描动作和已验证导入导出入口发生接线变化时追加相应回归,不默认跑全部后端测试。 -新增测试确保被 Git 跟踪。语法/模块链接检查不能代替下面的浏览器操作: - -- 工具箱 → 快捷栏、快捷栏 → 工具箱、栏内排序、重复固定、满栏拒绝、取消拖动;默认工具都能打开且只执行一次。 -- 刷新、恢复默认、所有可移动项移除、扫描中移动入口、中英文切换;工具箱与设置始终可达。 -- 快速掠过不闪说明,停留出现分层文案,边缘按钮提示不越出侧栏;拖动无提示遮挡。 -- 普通窗口、窄侧栏、插件缩放、侧边停靠;工具箱尺寸稳定、内部可滚动、拖放坐标准确。 -- 键盘菜单能完成同样操作;点击感叹号才显示更新引导。至少验证常用主题,未测环境如实说明。 - -按 AGENTS.md 及时保存阶段快照;若额度将尽或任务中断,保存有明确“验证待完成”说明的恢复快照,而不是无提交地把全部工作留到最后。 diff --git a/docs/plans/frontend-design-brief.md b/docs/plans/frontend-design-brief.md deleted file mode 100644 index b5aee03..0000000 --- a/docs/plans/frontend-design-brief.md +++ /dev/null @@ -1,88 +0,0 @@ -# 前端设计与 Gemini 协作方案 - -更新:2026-09-16。取代 9 月 9–11 日的前端视觉、布局建议,后续直接更新本文,不再按日期追加同类建议文件。 - -这是设计任务说明;工程规范以根目录 [AGENTS.md](../../AGENTS.md) 为准。具体任务若已有更新的专门方案,以专门方案为准。 - -## 1. 当前起点 - -本次核对基于 `3f71ddfcb` 的代码与计划,没有重新进行浏览器视觉验收。 - -- 配方已经有独立容器和工具栏,笔记有自己的标题,不再把“拆开创作工作台”列为待办。 -- 提示词工坊已经独立。继续使用现有工坊,不恢复旧嵌入式组合面板,也不拿早期的简单组合设想限制现在已有的能力。 -- 文件夹与配方覆盖层的定位问题已有修复,不重复下发修复任务。 -- 工具箱已有入口;快捷栏自定义是**待实施计划**,见 [工具箱与快捷栏方案](customizable-tool-shortcuts.md)。不要把计划当作已经实现的功能。 -- 当前配方包导入按钮处于禁用状态。设计可以解释不可用原因,不能为了演示效果直接启用、伪造成功或隐藏这个限制。 - -以上用于避免重复劳动,不代表所有关联功能已经验收通过。旧审查报告中的问题需要复现后判断,不能整份搬成当前待办,也不能直接宣布全部解决。 - -## 2. 怎样发挥 Gemini 的优势 - -让 Gemini 负责视觉层次、排版、密度、图标、过渡和响应式细节,并完成对应前端实现。不给每个像素下指令,也不要求每个小调整都等待批准。 - -任务边界和操作语义先明确:用户从哪里进入、想完成什么、写入什么、如何取消、完成后去哪里。Codex 可以负责梳理这些约束与检查数据、节点和生命周期;Gemini 做实现时也必须覆盖它们,不能把操作闭环全部留给下一位补。 - -**每轮交付一条完整操作流程。** “把工具箱做漂亮”范围模糊;“让用户从工具箱固定一个工具,再移除或取消,且任何时候都能恢复默认”才是可完成的任务。 - -## 3. 统一的设计方向 - -目标是清楚、紧凑、有质感的 ComfyUI 创作工具。图片和用户内容是视觉重点,装饰只辅助识别。 - -| 区域 | 设计职责 | -| --- | --- | -| 顶部导航 | 模型、图库、工作流配方;进入和返回的位置稳定 | -| 模型文件夹 | 只负责模型目录,不承担全局导航 | -| 左下工具区 | 默认保留素材库;工具箱是完整目录,快捷栏是常用入口。自定义行为遵循专门方案 | -| 列表工具栏 | 搜索、筛选、结果数、主操作;窄窗口可换行,控件不能互相挤没 | -| 内容卡片 | 图片、名称、类型和一项主操作。技术细节按需要展开,次操作可集中到“更多” | -| 详情与编辑 | 返回路径明确;区分查看、编辑、应用,明确应用到哪个节点或画布 | - -不要为了统一外观强行让所有卡片显示同样字段。模型用于选择资产,配方用于复用工作流,素材用于复用内容,信息重点各不相同。 - -外观由 Gemini 在现有主题内自行发挥:统一按钮层级、间距、圆角与文字密度;选中、警告、禁用和键盘焦点要清楚。普通按钮不加持续发光或跳动。两套主题共享布局与操作规则,不能各自长出一套交互。 - -不把新的全站导航重构与本轮工具箱改动捆绑实施。发现其他页面需要调整时,写一句后续项即可。 - -## 4. 防止只考虑一个局部 - -动手前用几行列出本次操作链,并填下面这张表。只检查与本次变更有关的部分,不做每轮全仓审查。 - -| 必须想清楚的事 | 交付时需要回答 | -| --- | --- | -| 入口与出口 | 从哪个页面进入?关闭、返回、再次打开后分别是什么状态? | -| 正常与边界状态 | 加载、空内容、筛选无结果、失败、禁用、取消是否可理解?没有此状态的说明不适用 | -| 关联影响 | 改了共享按钮或浮层,哪些相邻页面、主题、提示框会受影响? | -| 操作对象 | 当前改的是快捷入口、素材数据、节点参数还是画布?文案是否说清楚? | -| 尺寸与输入 | 普通窗口、侧边停靠、长中文/英文、鼠标与键盘是否能完成同一任务? | -| 离开后的行为 | 悬浮预览、拖动、视频和请求会不会在关闭后继续活动或写错对象? | - -例如“更多”菜单变漂亮,仍必须考虑靠右时是否出界、被父容器裁切、滚动时是否漂移,以及 Esc 关闭的是菜单还是整个浏览器。选择一个代表页面打磨公共样式后,再核对受影响页面,不要求一次重做它们。 - -## 5. 性能怎么兼顾 - -- 默认采用静态布局与局部短过渡。大面积模糊、连续光效、鼠标移动驱动全页动画必须有实际价值,否则不用。 -- 大列表继续按现有分页、缩略图和懒加载策略工作;不能为视觉预览一次性加载全部原图、详情和视频。 -- 工具提示随内容和交互更新,不为一小段文字增加持续轮询。隐藏页面的事件、预览和请求按照已有生命周期释放。 -- 验证本次增加的成本:是否多了持续任务、重复请求或重复监听,滚动和拖动是否出现明显卡顿。需要声称性能提升时提供同一条件下的测量;没有测量就不报百分比,也不猜显存变化。 - -## 6. 推荐工作顺序 - -**第一轮:完成工具箱与快捷栏专门方案。** 这是明确的当前任务单;视觉与交互一起完成,包含正常放置、拖回、取消、重复固定、容量已满和恢复默认,不顺手改其他页面导航。 - -**第二轮:统一受影响的公共控件。** 以工具箱的按钮、提示、选中和禁用状态为起点,检查它们在模型、配方、素材库中的实际使用。修改原规则,清掉本次替代的旧声明。 - -**第三轮:逐页优化信息密度。** 先选一个真实使用频繁的页面,完整处理列表、详情、返回和失败反馈,再决定下一页。无需为了执行本文件同时美化全插件。 - -每轮完成后更新状态,删除已完成待办。验证发现的新问题直接修复;超出本轮范围的只记录具体触发条件,避免积累泛泛的“以后再优化”。 - -## 7. 可直接发给 Gemini 的任务模板 - -> 先阅读 GEMINI.md、AGENTS.md,以及 docs/plans/frontend-design-brief.md。本轮只完成【具体操作流程】,具体规则以【对应方案】为准。 -> -> 先简述入口、正常操作、取消/失败、返回路径,以及会受影响的相邻界面,然后直接实施。颜色、排版和细节由你在现有主题内决定,不必逐项询问。 -> -> 交付完整交互,不只做正常状态的截图。保留真实数据和操作语义,覆盖相关的窄窗口、主题、键盘与关闭路径。发现接口或业务阻塞时明确说明,不能用演示数据、假成功或静默隐藏功能替代。 -> -> 完成后用简短清单说明:改变了什么、实际验证了哪些路径、哪些未验证、剩余的具体问题和本地提交。验证范围与改动相称,不默认跑全部测试;未完成项不得写成已完成。 - -这样用户主要判断“是否顺手、是否好看”,无需每次替实现者补齐所有边界情况。 diff --git a/docs/plans/gallery-ai-assistant.md b/docs/plans/gallery-ai-assistant.md deleted file mode 100644 index 8e6c77c..0000000 --- a/docs/plans/gallery-ai-assistant.md +++ /dev/null @@ -1,34 +0,0 @@ -# 后续构想:让 AI 从图库中帮助创作 - -记录日期:2026-09-18。 -**状态:仅保存想法,现阶段不开发。本文只描述想实现什么,不规定代码、接口或技术方案。** - -## 核心想法 - -图库不仅用于看图,也积累了生成图片时使用的提示词、模型、参数和工作流。未来允许用户连接自己选择的 AI 服务,从这些创作记录中总结经验,推荐值得尝试的工作流组合。 - -希望它能回答:“根据我喜欢的结果、已经拥有的模型和现在想画的内容,下一次可以怎样搭配?” - -## 希望提供的体验 - -- **分析选中的作品**:选择一张或一组图片,查看它们使用了哪些模型、提示词和参数,有哪些共同点与差异。 -- **推荐工作流组合**:结合创作目标与现有模型,推荐可尝试的节点、模型、提示词和参数组合,并解释推荐理由、参考了哪些作品、需要补充哪些条件。 -- **总结个人偏好**:结合收藏、喜欢/不喜欢及主动评价,帮助用户发现自己偏好的风格和搭配,而不是把生成次数最多的组合当成最好。 -- **比较与改进**:对比满意与不满意的结果,提出值得尝试的调整方向;通过后续试图和反馈,逐步判断建议是否适合用户。 -- **整理可复用经验**:把用户认可的总结整理成提示词笔记、素材或工作流配方,方便再次使用。 -- **探索更多玩法**:以后可以考虑用自然语言寻找历史作品、归纳某个模型的常用搭配、为新题材寻找可参考的旧工作流。逐项评估,不要求一次做完。 - -## 对结果的期望 - -推荐应当具体、可解释,区分“图库记录中的事实”和“AI 提出的尝试建议”。缺少必要模型或节点时提前说明,用户确认后再决定是否保存或应用。 - -仅凭参数不能保证找出“最佳工作流”,也不能把相关性当作某个参数造成画面改善的证明。有图片分析能力时可结合实际画面;没有时应明确仅依据参数与用户评价。 - -## 保留的边界 - -- 图库中并非每张图片都带有完整参数或工作流;缺失信息要明确标注,不让 AI 编造来源和配置。 -- 用户主动选择分析范围,并能在发送前了解将交给外部 AI 的图片、提示词和工作流内容;不默认上传整个图库。 -- 分析与应用分开:不自动改写现有工作流、不覆盖记录、不自动下载模型或启动生成。 -- 用户能控制分析规模与费用;现有看图和素材功能保持独立,不连接 AI 也能正常使用。 - -这是一项长期探索方向,当前优先完成现有功能收尾。真正启动时再讨论最小可用范围。 diff --git a/docs/plans/material-node-capture.md b/docs/plans/material-node-capture.md deleted file mode 100644 index 7a2ddd3..0000000 --- a/docs/plans/material-node-capture.md +++ /dev/null @@ -1,29 +0,0 @@ -# 素材库:保存画布选中节点 - -记录日期:2026-09-18。 -**状态:后续需求,仅记录,本轮不开发。** - -## 想做什么 - -在 ComfyUI 画布中选中一个或多个节点,通过一个方便的入口直接保存到素材库,方便以后复用,不必先生成图片或建立工作流配方。 - -- 单选:将这个节点及其当前参数保存为一份素材。 -- 多选:将全部选中节点作为一个整体,保存为一份素材,不拆成多个独立条目。 -- 保存选中节点之间的内部连线;与未选中节点相连的部分不自动扩展收录,不能留下指向不存在节点的无效连线。 -- 保存不改变当前画布、节点参数或现有连接。 - -## 建议的最小交互 - -- 优先考虑节点或画布右键菜单中的“保存到素材库”,单选和多选共用入口;具体入口接入方式开发时核对 ComfyUI 能力。 -- 轻量确认框显示“已选 N 个节点”,填写名称即可保存,标签可选;不强制提供封面。 -- 未选中节点时不允许保存;成功提示只在实际保存完成后出现,并提供查看新素材的入口。失败保留输入,便于重试。 -- 点击保存时固定节点选择与参数快照,后续切换选择不改变本次保存对象。 - -## 开发时注意 - -- 先核对现有节点素材、参数素材的保存与应用能力,复用素材库的存储、列表、详情和校验流程,不另建一套收藏系统。 -- 当前图片/配方来源的保存接口不能直接假定支持画布快照;需要改协议时,明确新增来源的校验与兼容规则,不伪造图片来源。 -- 单节点素材应能继续参与已有节点参数应用;多节点素材保留整体结构。首次开发需明确复用入口,保存成功后必须能在库中查看并通过相应流程使用。 -- 先做好单选、多选、内部连线、外部连接边界、无选中节点和保存失败的验证;不借此扩展复杂节点组编辑器或自动收集依赖。 - -正式开发前遵循 [AGENTS.md](../../AGENTS.md),阅读 [素材库架构](../architecture/material-library.md),按最新实现细化方案。本文件不表示功能已完成。 diff --git a/docs/plans/model-source-links.md b/docs/plans/model-source-links.md deleted file mode 100644 index e2c110a..0000000 --- a/docs/plans/model-source-links.md +++ /dev/null @@ -1,157 +0,0 @@ -# 模型来源中控:链接管理范围与后续方案 - -更新:2026-09-18。实施基线:`1b2013fe69`;接手时先核对最新代码。 -**状态:2026-09-18 已完成“交给 5.6Sol”范围的代码与自动化验收;真实 ComfyUI 后端需重启后再做最终页面复核。** 本文是唯一交接计划,后续原地更新。 - -## 交给 5.6Sol:本轮只做目录范围对齐与基础组件折叠 - -### 目标与已确认原因 - -来源中控仍只管理链接。补齐本地模型列表,让用户通过现有文件夹管理决定纳入哪些目录,同时将基础组件默认收起,降低整理负担。 - -已确认的代码问题: - -- 中控 `fetchAllLibraryModels()` 调用 `/anomalous/all_scan_models`,后端 `_collect_all_scan_models()` 只读分类模式的 `get_active_folder_types()`,没有遵循当前物理目录模式。 -- 本次本地配置中,物理目录模式的 `clip` 已开启,但分类配置的 `text_encoders` 关闭。这能解释本地库遗漏 CLIP;不能因此认定所有缺项都只有这一种原因。 -- 此接口只枚举 `.safetensors/.ckpt/.pt/.bin/.sft`,遗漏 `.gguf/.pth`,且扩展名比较区分大小写。(已修:统一用 `is_model_file()`。) -- 上轮提交只补了工作流收集、基础组件标签与对应类别的元数据读取,没有修复本地目录范围。 - -### 一、目录范围与文件格式 - -1. **本地库遵循文件夹管理当前模式。** 分类模式按分类勾选;物理目录模式按物理目录勾选。隐藏目录不遍历、不读取元数据;不自动开启 CLIP 等目录,不改用户配置。 -2. 复用 `api/folder_types.py` 的模式、启用目录规则。若需提取目录枚举,放在该职责内,返回真实的 `type/path_idx/base_dir`;不能只拿物理目录名当模型类别。物理模式下同一类别可对应多个根目录,必须逐根判断,不能把启用其中一个解释为全部启用。 -3. 对同一实际根目录进行规范化去重,保留可被后续接口解析的类别与路径索引。不要按 basename 合并模型;不同目录里的同名文件是独立条目。 -4. 补齐此列表的 GGUF/PTH,并统一大小写判断。仅表示“可列出并记录链接”,不代表这些格式已支持元数据解析、身份恢复或云端识别。不要直接扩大全局 `MODEL_EXTENSIONS` 后让所有读写路径被动接受新格式。 -5. `/all_scan_models` 同时被 `ui_model_selector.js` 使用。先检查两处调用;保持返回结构、分页与 `limit=0` 语义,验证选择器没有因此丢项或出现重复。确需区分调用用途时再加最小兼容参数,不先造专用数据库或另一套扫描系统。 -6. **当前工作流仍列出实际引用的模型**,不被本地库隐藏目录设置抹掉。文件夹设置控制库盘点范围,不能让分享工作流时遗漏依赖。 - -### 二、基础组件默认折叠 - -- 工作流和本地库都保留两个显示组:主要模型直接显示;基础组件放进默认收起的 **“基础组件(高级) · N”**。使用可键盘操作的折叠控件,不再加一层弹窗。 -- 本轮基础组件固定为现有策略已覆盖的 `clip/text_encoders/clip_vision/vae/vae_approx`。Checkpoint、扩散模型、LoRA、ControlNet 留在主要列表。其他已启用类别维持原有展示,避免顺手建设全站“核心/非核心”分类系统。 -- 展开状态由当前中控会话持有,筛选、编辑、保存后的刷新应保留;重新打开默认收起。折叠时不要生成所有组件行的 DOM。 -- 无来源的基础组件显示 **“未填写(可选)”**,不警告、不计入主要模型“来源待补充”数量;已有来源照常查看、修改和保存。 -- “全部/已填写来源/来源待补充”筛选作用于主要模型;组件组独立于这三个来源状态筛选,展开后显示全部组件。搜索同时作用于两组,折叠栏数量随搜索更新;文案明确“基础组件来源可选,不计入待补”。 -- 工作流里确定缺少本地文件的组件,折叠栏仍显示 **“缺少本地文件 × 项”**;该警告按当前范围全部组件计算,不被搜索或来源筛选隐藏。动态下拉选项未知不等于文件缺失,保留上轮判断保护。 -- 底部统计明确标为“主要模型来源:已填写 X / Y”,使用当前搜索与筛选后的主要列表;组件数量单列在折叠栏,不把未填可选来源算成未完成任务。 -- 无主要模型但存在组件时,保留组件入口;不能显示“未检测到任何模型”。目录未纳入范围时提供简短提示“本地库范围跟随设置 → 文件夹管理”,不增加重复配置界面。 -- 所有新增文案同步中英文。使用现有样式变量和中性标签,不加发光、动效或强提醒。 - -### 三、保存、性能与职责边界 - -- 折叠只影响显示,不能删除已有来源或改变底层模型集合。保存到工作流、生成 Note 保留当前整份工作流语义;复制摘要应明确当前显示范围,不能因折叠悄悄改成导出所有内容。 -- 本地保存继续使用准确的 `type/path_idx/subfolder/filename`。未定位实际文件的工作流组件只允许记录工作流链接,不得猜测路径写入 `.civitai.info`。 -- 保留人工链接、原文件名、封面和已有 info。打开、展开、筛选均不触发云端扫描、哈希计算或附属文件写入。 -- 列表枚举继续放在已有工作线程路径;折叠、搜索只处理已加载数据,不反复全盘读取。加载成功但为空、加载失败、尚未加载要能区分;打开时若指定 library 范围也须正确加载。 -- `ui_model_sources.js` 已超过规模阈值:优先把目录结果整形、分组/筛选等有明确职责的逻辑提取到来源中控专用模块,弹窗保留生命周期与协调。只在本轮需要处提取,不做全仓拆分,也不把业务塞入通用 utils。 - -### 修改落点 - -| 位置 | 本轮责任 | -| --- | --- | -| `api/folder_types.py`、`api/model_catalog.py` | 当前模式对应的目录范围、路径去重、列表格式支持 | -| `web/modules/ui_model_sources.js` 及必要的来源专用模块 | 组件折叠、分组筛选、统计、空态与加载状态 | -| `web/modules/model_policies.js` | 复用现有类型判断,避免改动重命名或身份恢复策略 | -| `web/modules/locales.js`、`web/styles/10-model-sources.css` | 中英文文案、局部折叠布局 | -| `tests/model_sources_hub.mjs`、`tests/model_source_components.mjs` | 更新为新交互,同时保留准确定位与保存保护测试 | -| 对应 Python 测试 | 使用临时目录验证目录模式、过滤与去重,不能拿真实模型做写入夹具 | - -### 验收清单 - -1. 物理模式开启 `clip`、分类配置关闭 `text_encoders` 时,库仍显示所选物理目录里的 CLIP;反向关闭物理目录时不枚举它。分类模式按自身配置工作。 -2. 多根目录只启用其中一个、路径别名指向同一实际根、不同根有同名文件,均得到正确范围与去重结果;隐藏根目录零遍历、零元数据读取。 -3. 无来源模型仍出现;GGUF/PTH 及大写扩展名可列出。现有格式、分页、选择器调用继续可用。 -4. 基础组件初始折叠,展开后可查看/编辑;切换筛选或保存不丢展开状态与输入内容。主要待补计数不含组件;搜索、仅有组件、全空、加载失败均有明确结果。 -5. 折叠时仍能看到组件缺文件告警;“无来源”和“文件缺失”独立。搜索无匹配不能抹掉当前工作流的缺文件告警。 -6. 折叠与筛选不清空已保存链接。已定位文件准确保存;未知目标不写本地;关闭窗口后迟到请求不重新渲染。 -7. 跑上述两组 JS 测试和对应 Python 目录枚举测试。实际 ComfyUI 验证两个范围、展开/收起、筛选搜索、窄窗口和原有选择器;写入/缺失场景用隔离夹具,不清空真实链接来制造测试条件。 -8. Python 改动需重启 ComfyUI 才生效;只有前端刷新不能证明新接口已加载。不要强停有任务的实例;如暂不能验证新后端,明确报告缺口。 -9. 完成后审查差异、更新本文状态与最窄的架构专题、本地提交,不推送。架构中的目录范围说明也要同步区分分类与物理模式。 - -### 本轮实施记录(2026-09-18) - -- 已增加文件夹管理范围的统一根目录枚举,来源库按当前分类/物理模式取根,保留 `type/path_idx`、真实路径去重,并只在来源库列表补充大小写无关的 GGUF/PTH。 -- 已提取来源中控专用的数据整形与分组模块;工作流和本地库的基础组件默认折叠,主要模型筛选、组件搜索计数、缺文件提示和当前可见范围复制各自保持独立语义。 -- 自动化验证:来源中控两组 JS 专项测试、95 项 Python 测试、CSS/本地化契约通过;临时目录覆盖物理/分类范围、隐藏根零遍历与零元数据、路径别名、同名文件和 GGUF/PTH 大小写。 -- 已在运行中的 ComfyUI 页面验证默认折叠、键盘展开、来源筛选不隐藏组件、搜索计数、主要模型统计与本地库范围提示;并在独立 Python 进程按真实物理配置确认 7 个启用根,其中 `text_encoders/path_idx=1` 正确对应已开启的 `clip` 根。运行实例未强停;Python 路由仍是重启前模块,目录范围新逻辑须在安全重启后复核。当前浏览器控制器不支持临时窄视口覆盖,窄窗口实机检查仍待补。 - -### 本轮不做 - -多来源切换、HF 抓图/info、平台多选、一键补全、扫描结果共享、全站格式支持和目录设置重做均不做。下文“链接可信度”中的历史识别/保存问题另列后续,不因本次计划一并扩张;若阻断上述验收,才做最小必要修复并说明。 - -## 已完成:工作流基础组件链接展示(2026-09-18) - -- 复用模型类别判断,显示文本编码器、VAE、预览 VAE、视觉编码器标签;纳入无扩展名的组件选项及 PTH/GGUF 文件引用。 -- 来源待补充使用普通提示,与本地缺失警告独立;保留手动填写和保存到工作流的能力。 -- 基础组件只按对应类别、明确相对路径读取本地元数据,不搜索其他类别或进行云端扫描。不能定位实际文件时不提供本地附属文件写入;无扩展名选项、后端尚不支持定位的文件格式仍可保存工作流链接。 -- 本地库目录范围、封面、文件名保持现状。大于 16 项的组件分批读取,覆盖后端请求上限。 -- 验证:来源中控与组件专项测试通过;真实 ComfyUI 页面确认 CLIP/VAE 标签和已有链接正常。真实库当前没有待补来源条目,缺失、手动填写和保存分支使用隔离夹具验证,未改写用户链接或画布。 - -## 当前方向:先把链接管好 - -中控现阶段定位为模型链接管理页,负责查看、填写、修改、打开与保存来源链接。保留已有工作流/本地库范围与各自保存含义;不因为缩小范围顺手删除现有功能。 - -下次实施优先解决与链接可信度直接相关的问题: - -- 无来源的模型必须能够被看到并补链接。“没有来源链接”不能成为排除模型的条件。 -- 区分“未填写来源”和“本地缺少模型文件”,不把两者都显示成缺失模型。 -- 自动识别失败返回的搜索页只能作为“去搜索”入口,不能保存为已识别来源、显示识别成功或计入已补全数量。当前 `autoDetectModelSource()` 的搜索回退及调用方提示需要一起检查。 -- 保存成功以实际写入结果为准;明确“保存到本地”和“写入当前工作流”的区别。无法确定本地文件身份时,不凭同名文件猜测写入位置。 -- 保留手工录入的合法网页链接,不把用户填写的地址称为“已验证官方来源”。打开链接不隐式抓图、覆盖介绍或启动扫描。 - -用户报告“缺失来源的模型没有显示”,当前静态代码没有直接按空链接排除列表项。本次已确认目录模式不一致与格式遗漏,见上面的实施范围;其他来源识别与保存问题仍需分别复现,不将它们统称为已修复。 - -## 暂缓一:模型详情的多来源入口 - -双链接可行,但应理解为“同一模型有多个来源”,不是切换到另一份模型数据。以下是后续建议,尚未实现。 - -- 只有一个来源时保持现有入口;两个来源时,详情的来源区域并列显示 `Civitai`、`Hugging Face` 两个文字按钮。无需为了两个链接引入整个页面的切换标签。 -- 如果确实需要默认入口,可提供“首选来源”单选;切换只影响默认打开哪个链接,不改变封面、介绍、模型文件、哈希或扫描设置。 -- 第三个及更多来源再考虑菜单,不预先建设无限多平台管理器。无链接的平台不显示空按钮。 -- 当前 `civitai_url` 是独立字段,`source_url` 是通用自定义来源,不能假定后者必然是 HF。初版可优先复用现有字段;确需额外首选来源字段时,单独定义兼容规则。 -- 多个链接并存不证明文件一致。确认匹配仍依赖现有模型身份规则;不能根据名称近似自动合并模型。 - -不建议首版让“切换来源”同时切换整套封面和 info:这会引入两套详情状态、缓存和失败回退,超出当前链接管理范围。 - -## 暂缓二:扫描来源的单选/多选 - -放在现有扫描向导或相关设置里,不在中控再做另一套扫描控制台。 - -明确区分三件事: - -| 选项 | 含义 | -| --- | --- | -| 模型范围 | 扫描哪些本地模型,沿用现有选择能力 | -| 来源平台 | 去哪些已支持的平台获取信息,可单选或多选 | -| 首选链接 | 默认打开哪个已保存来源,与扫描平台互不联动 | - -推荐平台选择采用复选框:只勾一个就是单来源,勾多个就是多来源,无需再增加“单选模式/多选模式”开关。只展示真正实现并可用的提供方,不能提前让 HF 扫描成为可点击的空功能。 - -多平台补全规则另行实施时必须一起确定:优先复用本地信息、只补缺项、保留人工内容、一个平台失败不丢其他结果、明确取消语义。已有链接不等于已有封面;已有 info 文件也不等于每个字段完整。 - -## 暂缓三:中控与扫描共享结果 - -两者共用现有模型附属信息读写能力,不建立中控专用数据库,也不让两个界面分别维护一份权威副本。 - -- 向导保存结果后,中控在打开时或收到完成通知后重新读取;不用持续轮询所有模型。 -- 中控保存的手工链接,向导可读取并保护。链接只说明用户记录的来源,不代表扫描器已经支持解析该平台。 -- 如增加“补全缺失来源”,仅复用已有扫描任务,限定当前范围并显示真实成功/未找到/失败结果,不自动扫描整个模型库。 - -## 暂缓四:HF 信息补全 - -- 工作流基础组件展示已完成,见上文;不据此扩大本地库目录范围或重做全站模型类型系统。 -- 基础组件保留原文件名,不强制提供封面;没有封面时使用类型图标。 -- 填写 HF 链接时保留现有封面、介绍和 Civitai 信息,不将自定义链接写进 Civitai 身份字段。 -- 将来如实现 HF 信息读取,先只读明确仓库的模型卡与有明确声明的缩略图;缺失时保留本地信息。不做自动抓任意 README 图片、全网找封面或无限平台适配。 -- 图片和介绍的来源与下载链接可以不同,但应可辨别,且不能把仓库级说明误当成已验证的具体文件信息。 - -参考:[HF 模型卡官方说明](https://huggingface.co/docs/hub/model-cards)。这只是后续技术参考,不表示本项目已支持自动抓取。 - -## 后续实施顺序 - -1. 先完成本文“交给 5.6Sol”范围:目录模式对齐、列表格式补齐、基础组件折叠与统计。 -2. 再单独核查历史识别结果与保存反馈,完成链接管理的基本用途。 -3. 确有需要时再做模型详情双来源入口;此时仍不联动封面或扫描。 -4. 最后评估扫描结果共享、平台多选及 HF 元数据读取,逐项实施,不一次打包上线。 - -实施时遵循 [AGENTS.md](../../AGENTS.md),核对最新代码和相关测试;本文不替代架构契约,也不授权自动启用所有暂缓功能。