Model Check bar: "Don't show again" and a check-on-open switch
- The bar starts with "Model Check:" so it is clear it comes from the browser. - "Don't show again" turns the check on opening off and says where to turn it back on; Settings → Workflows has the same switch (on by default), and turning it on checks the open workflow right away. While off, the bar never shows. - The tour's Model Check step mentions the button. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
01dcdf37a9
commit
6c8d3e8eb4
+2
-1
@@ -456,7 +456,8 @@ covered by the `styles.css` manifest.
|
||||
Check's verdicts, no DOM; the feature was called Model Doctor, and code and CSS still
|
||||
say "doctor") and puts a found file into its node; `ui_doctor.js` is the
|
||||
Model Check page and `ui_doctor_banner.js` the bar over the canvas when an opened workflow
|
||||
misses models. `ui_node_assistant.js` owns the current-node panel (model actions, LoRA
|
||||
misses models (it also owns the check-on-open preference that Settings → Workflows and the
|
||||
bar's "Don't show again" set). `ui_node_assistant.js` owns the current-node panel (model actions, LoRA
|
||||
insertion, model cards and history), `ui_node_model_picker.js` owns native combo
|
||||
replacement, `ui_node_parameters.js` renders the panel's parameters section, and
|
||||
`node_parameter_sets.js` merges the node type's saved values (material files and recipe
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
- **Faster rescans (重复扫描更快)**: a scan no longer downloads every cover and Civitai model page again for models whose information is already on disk; only new or missing ones go to the network.
|
||||
- **Model types at the top (模型页顶部类型标签)**: the models page starts with a chip per model type (Checkpoint, LoRA, VAE…) and how many models it holds; a chip shows that whole type, subfolders included, so the grid no longer looks like it holds one folder's models. Picking a folder in the list narrows the grid to that folder, shown as a chip with ✕ that goes back to the whole type. The browser remembers the type you picked last.
|
||||
- **Model Check, formerly Model Doctor, redone (模型检查,原“模型医生”,重做)**: when a workflow opens with models this computer does not have, a bar over the canvas says how many, and how many are here under another name or folder; **Put in** swaps those in and **Show** opens the doctor. The doctor page lists what needs you first and says why in plain words (same file found elsewhere, same size only, not on this computer, no fingerprint saved in the workflow), with a button for each: put in, use it, pick one yourself, or look it up on Civitai; the models that are fine fold away. Opening the doctor no longer changes the workflow by itself, and a model put in by the doctor or its picker no longer stays marked red by ComfyUI. The "auto-detect on startup" switch (it did nothing) and the doctor's own deep hash scan are gone: the check hashes same-size files itself, and the page points to the scan page when unscanned models could be the missing ones.
|
||||
- **The Model Check bar can be turned off (模型检查横条可以关掉)**: the bar now starts with "Model Check:" so it is clear it comes from the browser, not ComfyUI, and has **Don't show again**, which stops checking workflows when they open and says where to turn it back on. The same switch is in Settings → Workflows ("Check for missing models when a workflow opens", on by default); with it off, run Model Check from the rail when you want it.
|
||||
- **Prompt boxes found by what they are (提示词框按节点定义识别)**: dropping or writing a prompt (material library, Prompt Studio, translator) now finds a node's prompt boxes by ComfyUI's own definition (multiline text inputs) instead of guessing from names, so a filename or "value" box no longer takes a prompt and third-party prompt nodes are recognised. Whether a box is positive or negative follows its wiring to the sampler, not the node's title: a CLIP Text Encode connected to the negative input gets the negative prompt even if it is still called "CLIP Text Encode". Prompt Studio's "take prompts from the selected node" also reads the roles right again (it could not follow links in current ComfyUI).
|
||||
- **See where a dragged prompt goes (拖提示词前先看清楚落在哪)**: while you drag a prompt from the material library, every prompt box on the canvas is outlined (green for positive, red for negative, grey when it is not wired to a sampler) and the box under the pointer lights up. The label next to the pointer says exactly what releasing does, e.g. "Release: into #6's positive prompt box; the negative prompt box on the same sampler (#7) too". Drop on a box and that box is filled; the matching negative (or positive) box of the same sampler gets the other half of the material. On a node with several prompt boxes, point at the one you mean instead of letting it guess. A short message confirms what was filled; the library's receipt undoes it. Dropping a prompt plan on empty canvas used to create an empty prompt node; it now creates a positive and, when the plan has one, a negative node with their text.
|
||||
- **Current node (当前节点, formerly Node Assistant)**: the panel no longer splits into "actions" and "parameter sets" tabs; it is one page with the model actions on top and **Parameters** below. Parameters lists every saved set of values for this kind of node in one list — from the material library and from recipes' parameter sets, the same values saved twice shown once — and each entry says exactly what it would change on the selected node ("steps: 20 → 30"). **Apply these N** changes only those; seeds and model files stay (change the model above), and the receipt undoes it. **Save these values** keeps the node's current values in the material library, so the next node of that kind can use them.
|
||||
|
||||
@@ -85,7 +85,8 @@ The main surfaces are:
|
||||
- Current node (Node Assistant)/Model Check: selected-node actions, saved parameters, and
|
||||
missing-model recovery. `model_check.js` decides each workflow model's state and
|
||||
applies fixes; `ui_doctor.js` (the page) and `ui_doctor_banner.js` (the bar shown
|
||||
after a workflow opens) only render it and call it on a press.
|
||||
after a workflow opens, unless its "Don't show again" or Settings → Workflows turned the
|
||||
check on opening off) only render it and call it on a press.
|
||||
`ui_node_assistant.js` owns the current-node panel and model history, `ui_node_model_picker.js`
|
||||
owns the native-widget model replacer, and `ui_node_parameters.js` with
|
||||
`node_parameter_sets.js` own the panel's one list of saved values for the node type;
|
||||
|
||||
+14
-2
@@ -22,7 +22,7 @@ export const i18n = {
|
||||
tourScanTitle: "🎯 扫描模型",
|
||||
tourScanBody: "第一次用先扫描一下:认出每个模型是什么(名字、封面、基础模型),找模型、查缺模型都靠它。一键只扫新加的模型,进度在页面里,关掉窗口也会在后台继续。鼠标放到模型卡片上,点准星按钮可以只扫那一个。",
|
||||
tourDoctorTitle: "🩺 模型检查(缺模型)",
|
||||
tourDoctorBody: "打开工作流时会自动检查,缺模型就在画布上方提示。改过名、换过文件夹的同一个文件(按 SHA256 指纹认)可以一键换上;其余的在这里自己挑,或看模型去哪下载。",
|
||||
tourDoctorBody: "打开工作流时会自动检查,缺模型就在画布上方提示。改过名、换过文件夹的同一个文件(按 SHA256 指纹认)可以一键换上;其余的在这里自己挑,或看模型去哪下载。不想自动弹,在横条上点“不再提醒”。",
|
||||
tourNoticeTitle: "(!) 更新说明与停靠",
|
||||
tourNoticeBody: "点 (!) 看最近改了什么,也能再开这个导览;旁边的 ◧ 在贴着 ComfyUI 侧边和独立浮动窗口之间切换。",
|
||||
tourSettingsTitle: "⚙️ 设置",
|
||||
@@ -716,6 +716,10 @@ export const i18n = {
|
||||
doctorBannerPutIn: '已换上 {count} 个模型。',
|
||||
doctorBannerShow: '查看',
|
||||
doctorBannerClose: '关闭提示',
|
||||
doctorBannerLabel: '模型检查:',
|
||||
doctorBannerMute: '不再提醒',
|
||||
doctorBannerMuteHint: '以后打开工作流不再自动检查;在 设置 → 工作流 里可以重新打开',
|
||||
doctorBannerMuted: '好的,以后打开工作流不再自动检查。想重新打开:设置 → 工作流 → 打开工作流时检查缺失模型。',
|
||||
assistantSelectNode: '请在画布中点击选中任意节点',
|
||||
assistantSelectedNode: '当前选中节点',
|
||||
assistantQuickActions: '快捷操作',
|
||||
@@ -963,6 +967,8 @@ export const i18n = {
|
||||
foldersSaveFailed: "没能保存:{error}",
|
||||
foldersLoadFailed: "读不到文件夹列表。",
|
||||
settingsWorkflows: "工作流",
|
||||
settingsCheckOnOpen: '打开工作流时检查缺失模型',
|
||||
settingsCheckOnOpenHelp: '打开或拖进工作流时,缺模型就在画布上方提示,本机有同一个文件的可以一键换上。关掉后,要检查就点左侧的模型检查。',
|
||||
settingsOpening: "打开方式与窗口",
|
||||
settingsEntryHelp: "在哪里打开这个浏览器。快捷键在 ComfyUI 设置 → Anomalous Model Browser 里设置。",
|
||||
settingsLayout: "窗口布局",
|
||||
@@ -1946,7 +1952,7 @@ export const i18n = {
|
||||
tourScanTitle: "🎯 Scan models",
|
||||
tourScanBody: "Scan once to start: it works out what each model is (name, cover, base model), which finding models and Model Check rely on. One button scans only the new ones, with progress on the page; it carries on in the background if you close the window. The crosshair button on a model card scans just that one.",
|
||||
tourDoctorTitle: "🩺 Model Check (missing models)",
|
||||
tourDoctorBody: "Each workflow you open is checked; missing models show a bar over the canvas. The same file under another name or folder (recognised by its SHA256) is put back with one press; for the rest, pick one here or see where to download it.",
|
||||
tourDoctorBody: "Each workflow you open is checked; missing models show a bar over the canvas. The same file under another name or folder (recognised by its SHA256) is put back with one press; for the rest, pick one here or see where to download it. Don't want the bar? Press \"Don't show again\" on it.",
|
||||
tourNoticeTitle: "(!) What's new & docking",
|
||||
tourNoticeBody: "Press (!) to see what changed and to start this tour again; the ◧ beside it switches between docking at ComfyUI's side and a floating window.",
|
||||
tourSettingsTitle: "⚙️ Settings",
|
||||
@@ -2640,6 +2646,10 @@ export const i18n = {
|
||||
doctorBannerPutIn: 'Put in {count} models.',
|
||||
doctorBannerShow: 'Show',
|
||||
doctorBannerClose: 'Close',
|
||||
doctorBannerLabel: 'Model Check: ',
|
||||
doctorBannerMute: "Don't show again",
|
||||
doctorBannerMuteHint: 'Stop checking workflows when they open; Settings → Workflows turns it back on',
|
||||
doctorBannerMuted: 'Workflows will no longer be checked when they open. To turn it back on: Settings → Workflows → Check for missing models when a workflow opens.',
|
||||
assistantSelectNode: 'select any node in the canvas',
|
||||
assistantSelectedNode: 'Selected node',
|
||||
assistantQuickActions: 'Quick actions',
|
||||
@@ -2887,6 +2897,8 @@ export const i18n = {
|
||||
foldersSaveFailed: "Could not save: {error}",
|
||||
foldersLoadFailed: "Could not read the folder list.",
|
||||
settingsWorkflows: "Workflows",
|
||||
settingsCheckOnOpen: 'Check for missing models when a workflow opens',
|
||||
settingsCheckOnOpenHelp: 'When you open or drop in a workflow, a bar over the canvas says which models are missing and puts in the ones this computer has under another name in one press. Turned off, run Model Check from the rail when you want it.',
|
||||
settingsOpening: "Opening and window",
|
||||
settingsEntryHelp: "Where you open this browser from. Shortcuts are set in ComfyUI's settings under Anomalous Model Browser.",
|
||||
settingsLayout: "Window layout",
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
* how many, how many are here under another name or folder (put in with one press), and
|
||||
* a way to Model Check's page for the rest. Each opened workflow is checked
|
||||
* (model_check.js, without reloading ComfyUI's model lists); the doctor page updates the
|
||||
* bar after its own checks. Closing it hides it for that workflow until the page reloads.
|
||||
* bar after its own checks. Closing it hides it for that workflow until the page reloads;
|
||||
* "Don't show again" turns the check on opening off (Settings → Workflows turns it back on),
|
||||
* and while it is off the bar never shows.
|
||||
*/
|
||||
|
||||
import { app } from "../../../scripts/app.js";
|
||||
@@ -11,6 +13,7 @@ import { translate as t } from './locales.js';
|
||||
import { checkWorkflowModels, fixWorkflowModels, isProblem } from './model_check.js';
|
||||
|
||||
const HIDE_AFTER_FIX_MS = 6000;
|
||||
const CHECK_ON_OPEN_KEY = 'anomalous_check_on_open';
|
||||
const closed = new Set();
|
||||
let bar = null;
|
||||
let checkRun = 0;
|
||||
@@ -35,6 +38,24 @@ function button(className, label, onClick) {
|
||||
return node;
|
||||
}
|
||||
|
||||
/** Whether opening a workflow checks its models (on unless turned off). */
|
||||
export function checkOnOpen() {
|
||||
try {
|
||||
return localStorage.getItem(CHECK_ON_OPEN_KEY) !== 'false';
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Turns the check on opening on (and checks the open workflow now) or off (and hides the bar). */
|
||||
export function setCheckOnOpen(owner, on) {
|
||||
try {
|
||||
localStorage.setItem(CHECK_ON_OPEN_KEY, on ? 'true' : 'false');
|
||||
} catch { /* stays as it was for this page */ }
|
||||
if (!on) hide();
|
||||
else checkOpenWorkflow(owner).catch(error => console.warn('[AMB] Doctor: checking the open workflow failed.', error));
|
||||
}
|
||||
|
||||
function hide() {
|
||||
clearTimeout(hideTimer);
|
||||
bar?.remove();
|
||||
@@ -46,7 +67,7 @@ export function updateDoctorBanner(owner, entries, putIn = 0) {
|
||||
const key = workflowKey();
|
||||
const problems = entries.filter(isProblem);
|
||||
const fixable = problems.filter(entry => entry.state === 'fixable');
|
||||
if (closed.has(key) || (!problems.length && !putIn)) {
|
||||
if (!checkOnOpen() || closed.has(key) || (!problems.length && !putIn)) {
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
@@ -57,7 +78,8 @@ export function updateDoctorBanner(owner, entries, putIn = 0) {
|
||||
if (putIn) lines.push(t('doctorBannerPutIn', { count: putIn }));
|
||||
if (problems.length) lines.push(t(putIn ? 'doctorBannerStill' : 'doctorBannerMissing', { count: problems.length }));
|
||||
if (fixable.length) lines.push(t('doctorBannerFixable', { count: fixable.length }));
|
||||
const copy = el('span', 'anomalous-doctor-banner-text', lines.join(' '));
|
||||
const copy = el('span', 'anomalous-doctor-banner-text');
|
||||
copy.append(el('strong', 'anomalous-doctor-banner-label', t('doctorBannerLabel')), lines.join(' '));
|
||||
const actions = el('span', 'anomalous-doctor-banner-actions');
|
||||
if (fixable.length) {
|
||||
actions.append(button('anomalous-doctor-banner-btn is-main', t('doctorPutInAll', { count: fixable.length }), async () => {
|
||||
@@ -71,16 +93,30 @@ export function updateDoctorBanner(owner, entries, putIn = 0) {
|
||||
owner.openDoctorPage();
|
||||
}));
|
||||
}
|
||||
const mute = button('anomalous-doctor-banner-mute', t('doctorBannerMute'), () => {
|
||||
setCheckOnOpen(owner, false);
|
||||
showMuted();
|
||||
});
|
||||
mute.title = t('doctorBannerMuteHint');
|
||||
const close = button('anomalous-doctor-banner-close', '✕', () => {
|
||||
closed.add(key);
|
||||
hide();
|
||||
});
|
||||
close.setAttribute('aria-label', t('doctorBannerClose'));
|
||||
bar.replaceChildren(el('span', 'anomalous-doctor-banner-icon', '🩺'), copy, actions, close);
|
||||
bar.replaceChildren(el('span', 'anomalous-doctor-banner-icon', '🩺'), copy, actions, mute, close);
|
||||
if (!bar.isConnected) document.body.append(bar);
|
||||
if (!problems.length) hideTimer = setTimeout(hide, HIDE_AFTER_FIX_MS);
|
||||
}
|
||||
|
||||
/** After "Don't show again": says where to turn it back on, then goes. */
|
||||
function showMuted() {
|
||||
bar = el('div', 'anomalous-doctor-banner');
|
||||
bar.setAttribute('role', 'status');
|
||||
bar.append(el('span', 'anomalous-doctor-banner-icon', '🩺'), el('span', 'anomalous-doctor-banner-text', t('doctorBannerMuted')));
|
||||
document.body.append(bar);
|
||||
hideTimer = setTimeout(hide, HIDE_AFTER_FIX_MS);
|
||||
}
|
||||
|
||||
async function checkOpenWorkflow(owner) {
|
||||
const run = ++checkRun;
|
||||
const entries = await checkWorkflowModels();
|
||||
@@ -94,7 +130,7 @@ export function watchWorkflowLoads(owner) {
|
||||
app.loadGraphData = async function (...args) {
|
||||
hide();
|
||||
const result = await load.apply(this, args);
|
||||
checkOpenWorkflow(owner).catch(error => console.warn('[AMB] Doctor: checking the opened workflow failed.', error));
|
||||
if (checkOnOpen()) checkOpenWorkflow(owner).catch(error => console.warn('[AMB] Doctor: checking the opened workflow failed.', error));
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { startSpotlightTour } from './ui_spotlight_tour.js';
|
||||
import { copyDiagnostics } from './feedback.js';
|
||||
import { renderFolderPage } from './ui_folder_manager.js';
|
||||
import { openFeedbackDialog } from './ui_feedback_dialog.js';
|
||||
import { checkOnOpen, setCheckOnOpen } from './ui_doctor_banner.js';
|
||||
|
||||
const t = (key, params) => translate(key, params);
|
||||
// The full written guide: the README, at its Chinese half for Chinese.
|
||||
@@ -186,8 +187,9 @@ function foldersGroup(owner) {
|
||||
})));
|
||||
}
|
||||
|
||||
function workflowGroup() {
|
||||
function workflowGroup(owner) {
|
||||
return group('settingsWorkflows',
|
||||
row('settingsCheckOnOpen', 'settingsCheckOnOpenHelp', toggleSwitch(checkOnOpen(), on => setCheckOnOpen(owner, on))),
|
||||
row('sidebarProvenance', 'sidebarProvenanceDesc', toggleSwitch(localStorage.getItem('anomalous_inject_hash') !== 'false', on => {
|
||||
// Read by hash_resolver.js when a workflow is saved.
|
||||
localStorage.setItem('anomalous_inject_hash', on ? 'true' : 'false');
|
||||
@@ -252,7 +254,7 @@ function render(owner) {
|
||||
appearanceGroup(owner, redraw),
|
||||
cardsGroup(owner, redraw),
|
||||
foldersGroup(owner),
|
||||
workflowGroup(),
|
||||
workflowGroup(owner),
|
||||
openingGroup(owner, redraw),
|
||||
helpGroup(owner),
|
||||
);
|
||||
|
||||
+1
-1
@@ -17,6 +17,6 @@
|
||||
@import url("./styles/16-activity.css?v=20261002-theme-1");
|
||||
@import url("./styles/17-model-types.css?v=20261002-theme-1");
|
||||
@import url("./styles/18-scan-page.css?v=20261003-folders-1");
|
||||
@import url("./styles/19-model-doctor.css?v=20261003-levels-2");
|
||||
@import url("./styles/19-model-doctor.css?v=20261005-mute-1");
|
||||
@import url("./styles/20-current-node.css?v=20261004-studio-1");
|
||||
@import url("./styles/21-prompt-studio.css?v=20261004-draftx-1");
|
||||
|
||||
@@ -139,6 +139,28 @@
|
||||
|
||||
.anomalous-doctor-banner-btn:hover { filter: brightness(1.15); }
|
||||
|
||||
.anomalous-doctor-banner-label {
|
||||
margin-right: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.anomalous-doctor-banner-mute {
|
||||
flex-shrink: 0;
|
||||
padding: 4px 6px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: var(--amb-text-muted);
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.anomalous-doctor-banner-mute:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: var(--amb-text-main);
|
||||
}
|
||||
|
||||
.anomalous-doctor-banner-close {
|
||||
flex-shrink: 0;
|
||||
padding: 4px 8px;
|
||||
|
||||
Reference in New Issue
Block a user