Files
DemonGatanjieuandClaude Opus 5.5 355f95af48 AI workbench: a new look for the task list, the task header and a waiting task
The task column (now ui_ai_task_list.js) leads with a New task button, icon
buttons for Connect an AI and the gates, which AI came last, quick filters
by state with counts, and state icons; the current task has an accent bar.
A task's header uses icon buttons (Materials, Undo all, show the stage,
rename and remove as pencil and bin). A waiting task shows a picture, three
numbered steps joined by a line (what to do, the sentence with a large
Copy button, a faint preview of steps) and a connect card that opens the
guide in place; the empty page matches. One set of line icons for the AI
pages (ui_ai_icons.js). Illustrations made with ChatGPT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:37:57 +08:00

73 lines
3.5 KiB
JavaScript

/**
* The materials picker's pictures (ui_ai_toolbox.js): a badge for each node source (a custom node pack gets its
* initials on a colour of its own, the same every time), and the illustrations in web/assets
* (made with ChatGPT for this window): the empty selection, and the stand-in cover of a combo,
* workflow or model that has none. Static SVG text only; nothing from a user or a file goes in.
*/
import { icon } from './ui_ai_icons.js';
function hue(text) {
let hash = 0;
for (const char of String(text)) hash = (hash * 31 + char.codePointAt(0)) >>> 0;
return hash % 360;
}
/** Up to two letters for a pack: its words without "ComfyUI" and "comfy" ("ComfyUI-Easy-Use" → "EU"). */
export function initials(label) {
const words = String(label || '').replace(/comfy[_-]?ui|comfy/gi, ' ').split(/[\s_\-.]+/).filter(Boolean);
const letters = (words.length > 1 ? words.slice(0, 2).map(word => word[0]) : [...(words[0] || '?').slice(0, 2)]).join('');
return letters.toUpperCase();
}
/** The badge of a node source: ComfyUI's own, the paid API nodes, or a pack's initials on its colour. */
export function sourceBadge(source, size = 26) {
const badge = document.createElement('span');
badge.className = `anomalous-tb-badge is-${source.kind}`;
badge.style.setProperty('--tb-size', `${size}px`);
if (source.kind === 'builtin') {
badge.innerHTML = `<svg viewBox="0 0 24 24" width="${Math.round(size * 0.62)}" height="${Math.round(size * 0.62)}" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M16.5 7.5A6 6 0 1 0 16.5 16.5"/><circle cx="17" cy="12" r="1.3" fill="currentColor" stroke="none"/></svg>`;
} else if (source.kind === 'api') {
badge.append(icon('coin', Math.round(size * 0.62)));
} else {
badge.style.setProperty('--tb-hue', String(hue(source.key)));
badge.textContent = initials(source.label || source.key);
}
badge.setAttribute('aria-hidden', 'true');
return badge;
}
const ART = {
empty: new URL('../assets/toolbox_empty.webp', import.meta.url).href,
combos: new URL('../assets/toolbox_combo.webp', import.meta.url).href,
workflows: new URL('../assets/toolbox_workflow.webp', import.meta.url).href,
models: new URL('../assets/toolbox_model.webp', import.meta.url).href,
};
function art(src, className) {
const img = document.createElement('img');
img.className = className;
img.src = src;
img.alt = '';
img.decoding = 'async';
img.setAttribute('aria-hidden', 'true');
return img;
}
/** The picture of an empty selection: a toolbox with cards rising out of it. */
export const emptyPicture = () => art(ART.empty, 'anomalous-tb-empty-art');
/** The stand-in cover of a combo, workflow or model without one. */
export const standInCover = kind => art(ART[kind], 'anomalous-tb-stand-in');
/** The colour ComfyUI draws a socket type with: the open canvas's own when it says, else ComfyUI's defaults. */
const SOCKET_COLORS = {
MODEL: '#B39DDB', CLIP: '#FFD500', VAE: '#FF6E6E', CONDITIONING: '#FFA931', LATENT: '#FF9CF9', IMAGE: '#64B5F6',
MASK: '#81C784', CONTROL_NET: '#00D78D', CLIP_VISION: '#A8DADC', CLIP_VISION_OUTPUT: '#ad7452', STYLE_MODEL: '#C2FFAE',
UPSCALE_MODEL: '#87CEFA', NOISE: '#B0B0B0', SAMPLER: '#ECB4B4', SIGMAS: '#CDFFCD', GUIDER: '#66FFFF', AUDIO: '#7FD3D4', VIDEO: '#C8A2C8',
};
export function socketColor(type, canvas = globalThis.app?.canvas) {
const own = canvas?.default_connection_color_byType?.[type];
return own || SOCKET_COLORS[type] || '#9AA4B2';
}