Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a4c5d5c72 | ||
|
|
b9fd30ad72 |
+1
-1
@@ -363,7 +363,7 @@ covered by the `styles.css` manifest.
|
||||
task adds `links` {from, next}, begin_task gives an AI the task it continues and what was
|
||||
decided there (`continues`). `ui_ai_task_links.js` shows them under the task's title and picks
|
||||
one to link. `tests/test_ai_task_links.py`. `ui_ai_recent.js` the home page's list, `ui_ai_results.js` what a
|
||||
step made and where it leads. `ui_ai_outputs.js` is what the whole task made: a line over the
|
||||
step made and where it leads, `ui_ai_shared.js` what the page's parts share. `ui_ai_outputs.js` is what the whole task made: a line over the
|
||||
timeline counting it, and the side column's Results view (`ai_task_view.taskOutputs`: the
|
||||
pictures by the step that brought them, the workflow, the combos, the newest picture's prompts,
|
||||
the models used and download requests — `ai_tasks.results_of` names the downloads — and the sounds),
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# 📈 Anomalous Model Browser Changelog
|
||||
|
||||
## v1.62.1 (Fix: Anomalous sometimes did not start) — 2026-11-05
|
||||
|
||||
- 修好一个偶发的启动故障:有时打开 ComfyUI,AMB 整个没加载,悬浮按钮和窗口都没有,刷新一下又好了。
|
||||
- Fixed a startup failure that came now and then: ComfyUI opened with no Anomalous at all, no button and no window, until a reload.
|
||||
|
||||
## v1.62.0 (AI Workbench, AI Builds Workflows on the Canvas, Repaint an Area, Materials Side Panel) — 2026-10-29
|
||||
|
||||
- 音频库搬进了“角色语音”页:页面顶上的标签现在是 **角色 | 配音 | 音频库**,左边栏少了一项。加了 AI 工作台以后左边栏有 10 项,数字键只有 1–9,音频库就没键可按了;现在 9 打开角色语音,并回到你上次停的那个标签。
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
[](https://github.com/ltdrdata/ComfyUI-Manager)
|
||||
[](LICENSE)
|
||||
[](CHANGELOG.md)
|
||||
[](CHANGELOG.md)
|
||||
[](https://www.bilibili.com/video/BV1boHx61Enc/)
|
||||
[](https://www.bilibili.com/video/BV1bkpZ6uEZQ/)
|
||||
[](https://www.reddit.com/r/comfyui/s/n38ihf3DLf)
|
||||
|
||||
@@ -123,3 +123,13 @@ body:has(#anomalous-toolbox-modal[style*="display: flex"]) #anomalous-sidebar-to
|
||||
**以后怎么做**
|
||||
- `node tools/check_structure.mjs` 现在会拒绝 `:has()` 里出现 `[style…]`,报出文件和行号。
|
||||
- 同一个坑第二次出现,就把它做成检查,不再只补一段经验。
|
||||
|
||||
## 2026-11-05 · 偶尔打开 ComfyUI,AMB 整个不见了
|
||||
|
||||
**现象**:没有悬浮按钮,没有窗口,控制台里是几十条 `Cannot access 'shared' before initialization`;刷新一下又好了。大约十几次加载碰到一次。
|
||||
|
||||
**原因**:ComfyUI 把 `web/` 下每个 .js 都当成扩展,同时各自导入一遍,谁先跑完全看网络。AMB 的模块有一个很大的循环导入,`ui_ai_workbench.js` 加载时就要写 `ui_ai_results.js` 里的 `const shared`。从 `main.js` 进来顺序是对的;碰巧先从 `ui_ai_results.js` 或 `ui_ai_step_body.js` 进来,`shared` 还没初始化,这个模块就报错,而且错误会被缓存,整个 AMB 跟着起不来。
|
||||
|
||||
**做法**:`shared` 搬进不导入任何东西的 `ui_ai_shared.js`。`tests/module_cycles.mjs` 查“加载时就读同一个循环里的 const”,这种写法以后过不了测试。
|
||||
|
||||
**以后**:会在加载时用到的值,放在循环外面的模块里。别只从 `main.js` 想加载顺序。要逐个模块验证,在浏览器里给每个模块开一个 iframe 单独 import。
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// ComfyUI imports every .js under web/ on its own, all at once, so any module of an import cycle can happen
|
||||
// to run first. A module that uses, as it loads, a `const` of a module in its own cycle then fails now and
|
||||
// then, and AMB with it: no button, no window (docs/architecture/lessons.md). Such a value goes in a module
|
||||
// outside the cycle.
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync, readdirSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const folder = join(dirname(fileURLToPath(import.meta.url)), '..', 'web', 'modules');
|
||||
const IMPORT = /^import\s+\{([^}]*)\}\s+from\s+'\.\/([\w.]+\.js)'/gm;
|
||||
|
||||
const modules = new Map(readdirSync(folder).filter(name => name.endsWith('.js')).map((name) => {
|
||||
const text = readFileSync(join(folder, name), 'utf8').replace(/\r\n/g, '\n');
|
||||
const imports = [...text.matchAll(IMPORT)].map(([, names, from]) => ({ from, names: names.split(',').map(item => item.trim().split(/\s+as\s+/)).filter(([first]) => first) }));
|
||||
return [name, { text, imports, to: new Set(imports.map(item => item.from)) }];
|
||||
}));
|
||||
|
||||
/** The modules in cycles, each with the set of those in the same cycle (Tarjan, on the imports). */
|
||||
function cycles() {
|
||||
const index = new Map();
|
||||
const low = new Map();
|
||||
const stack = [];
|
||||
const groups = new Map();
|
||||
const visit = (name) => {
|
||||
index.set(name, index.size);
|
||||
low.set(name, index.get(name));
|
||||
stack.push(name);
|
||||
for (const next of modules.get(name)?.to || []) {
|
||||
if (!modules.has(next)) continue;
|
||||
if (!index.has(next)) {
|
||||
visit(next);
|
||||
low.set(name, Math.min(low.get(name), low.get(next)));
|
||||
} else if (stack.includes(next)) low.set(name, Math.min(low.get(name), index.get(next)));
|
||||
}
|
||||
if (low.get(name) !== index.get(name)) return;
|
||||
const group = new Set();
|
||||
let member;
|
||||
do {
|
||||
member = stack.pop();
|
||||
group.add(member);
|
||||
} while (member !== name);
|
||||
for (const item of group) groups.set(item, group);
|
||||
};
|
||||
for (const name of modules.keys()) if (!index.has(name)) visit(name);
|
||||
return groups;
|
||||
}
|
||||
|
||||
/** Is `local` used by a line at the top of `text`, that is one run as the module loads? */
|
||||
function usedAsItLoads(text, local) {
|
||||
return text.split('\n').some(line => /^[\w(]/.test(line)
|
||||
&& !/^(import|function|async function|export (async )?function|export default function|class)\b/.test(line)
|
||||
&& new RegExp(`\\b${local}\\b`).test(line.split('=>')[0]));
|
||||
}
|
||||
|
||||
const groups = cycles();
|
||||
const found = [];
|
||||
for (const [name, { text, imports }] of modules) {
|
||||
for (const { from, names } of imports) {
|
||||
if (!groups.get(name)?.has(from) || from === name) continue;
|
||||
const source = modules.get(from).text;
|
||||
for (const [exported, local = exported] of names) {
|
||||
const isValue = new RegExp(`^export (const|let) ${exported}\\b`, 'm').test(source);
|
||||
if (isValue && usedAsItLoads(text, local)) found.push(`${name} uses ${exported} of ${from} as it loads`);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.deepEqual(found, []);
|
||||
console.log('Module cycles: nothing reads a value of its own cycle as it loads.');
|
||||
@@ -22,8 +22,7 @@ import { canvasChanges } from './ui_canvas_changes.js';
|
||||
import { estimateBox, runProgress } from './ui_ai_run_live.js';
|
||||
import { runMapBox } from './ui_ai_run_map.js';
|
||||
import { iconButton } from './ui_ai_icons.js';
|
||||
|
||||
export const shared = { owner: null, taskId: null, entries: new Map(), player: null, openMap: null }; // openMap: ui_ai_workbench.js
|
||||
import { shared } from './ui_ai_shared.js';
|
||||
|
||||
/** A run step's progress with its stop button (while the task shown is the run's). */
|
||||
const liveRun = progress => runProgress(progress, shared.taskId ? { task: shared.taskId, step: progress.step } : null);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* What the parts of the AI page share: AMB's window, the task shown, its canvas entries, the player and
|
||||
* the way to a run's map. It imports nothing, so it stands before any of them runs: ui_ai_workbench.js
|
||||
* writes here as it loads, and ComfyUI may run any of AMB's modules first (tests/module_cycles.mjs).
|
||||
*/
|
||||
|
||||
export const shared = { owner: null, taskId: null, entries: new Map(), player: null, openMap: null };
|
||||
@@ -44,7 +44,8 @@ import { iconButton } from './ui_ai_icons.js';
|
||||
import { listToggle, renderTaskList, stateIcon } from './ui_ai_task_list.js';
|
||||
import { flowMap } from './ui_ai_flow.js';
|
||||
import { drawRecentAiTasks } from './ui_ai_recent.js';
|
||||
import { locateNode, openModel, resultLinks, shared, updateRunProgress } from './ui_ai_results.js';
|
||||
import { locateNode, openModel, resultLinks, updateRunProgress } from './ui_ai_results.js';
|
||||
import { shared } from './ui_ai_shared.js';
|
||||
import { drawOutputsLine, newOutputs } from './ui_ai_outputs.js';
|
||||
import { compareBar, setMarker } from './ui_ai_images.js';
|
||||
import { openMarker } from './ui_ai_marker.js';
|
||||
|
||||
Reference in New Issue
Block a user