Files
DemonGatanjieuandClaude Opus 5.5 a2b25ab1d9 Group the examples by plan and add Anima two-pass versions
Anima loads through other nodes than a checkpoint, so none of the examples
could open on it. Two passes now comes in three versions: SDXL / SD 1.5,
Anima on ComfyUI's own nodes, and the recommended Anima held by Tile, the
way's tested values, kept in step by a test. The own-nodes version redraws
at 0.35: at 0.5 the shapes began to move with nothing holding them.

The window shows plans, versions and notes. A version that lacks a pack
or a model says what and has no Open button; an AI only gets those that
can run here. The layers of cards, examples, ways and skills are written
down as AD-025.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 01:30:25 +08:00

105 lines
4.7 KiB
JavaScript

/**
* The example workflows on the Workflows page (api/example_workflows.py): one card that opens a window of
* them by plan, each version with its note and a button that opens it in a tab of its own, on a model and a
* picture that are here. A version that lacks a pack or a model says what, a pack with the way into the
* Manager, and has no button. The card comes first, or it is lost under the user's own, and goes while the list is filtered.
*/
import { app } from "../../../scripts/app.js";
import { resolveLocale, translate as t } from './locales.js';
import { anomalousAlert } from './ui_dialog.js';
import { aiWindow } from './ui_ai_connect.js';
import { icon } from './ui_ai_icons.js';
import { openManager } from './ui_pack_usage.js';
import { appendText } from './ui_recipe_detail_dom.js';
const words = value => value?.[resolveLocale()] || value?.en || value?.zh || '';
async function openExample(owner, example, closeWindow) {
const response = await fetch(`/anomalous/example_workflows?id=${encodeURIComponent(example.id)}`);
const payload = await response.json().catch(() => ({}));
if (!response.ok || !payload.data?.workflow) throw new Error(payload.error || response.statusText);
const tab = `${words(example.title)} ${words(example.version)}`.replace(/\s*\/\s*/g, ' '); // a tab's name is a path to ComfyUI
await app.loadGraphData(payload.data.workflow, true, true, tab);
closeWindow();
owner?.close?.();
}
/** What `example` lacks: a pack is a button into the Manager, a model a word. */
function lacksLine(owner, example) {
const line = document.createElement('div');
line.className = 'anomalous-ai-block-lacks';
for (const lack of example.lacks) {
if (lack.model) {
appendText(line, 'span', t(`recipeExampleLacks_${lack.model}`), 'anomalous-ai-block-lack');
continue;
}
const pack = appendText(line, 'button', t('aiBlocksNeedsPack', { name: lack.pack }), 'anomalous-ai-block-lack is-pack');
pack.type = 'button';
pack.title = t('aiBlocksFindPack');
pack.onclick = () => void openManager(owner, { name: lack.pack });
}
return line;
}
function exampleRow(owner, example, closeWindow) {
const row = document.createElement('div');
row.className = `anomalous-example${example.lacks.length ? ' is-missing' : ''}`;
const text = appendText(row, 'div', '', 'anomalous-example-words');
const head = appendText(text, 'div', '', 'anomalous-example-title');
appendText(head, 'span', words(example.version));
if (example.recommended) appendText(head, 'span', t('recipeExampleRecommended'), 'anomalous-example-badge');
appendText(text, 'div', words(example.about), 'anomalous-example-about');
if (example.lacks.length) {
text.append(lacksLine(owner, example));
return row;
}
const open = appendText(row, 'button', t('recipeOpenCanvas'), 'anomalous-example-open');
open.type = 'button';
open.onclick = async () => {
open.disabled = true;
try {
await openExample(owner, example, closeWindow);
} catch (error) {
open.disabled = false;
await anomalousAlert(t('recipeOpenError'));
}
};
return row;
}
async function showExamples(owner) {
const response = await fetch('/anomalous/example_workflows');
const payload = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(response.statusText);
const body = document.createElement('div');
body.className = 'anomalous-examples';
appendText(body, 'p', t('recipeExamplesHint'), 'anomalous-examples-hint');
let closeWindow = () => {};
let plan = '';
for (const example of payload.examples || []) {
if (words(example.title) !== plan) {
plan = words(example.title);
appendText(body, 'div', plan, 'anomalous-examples-plan');
}
body.append(exampleRow(owner, example, () => closeWindow()));
}
closeWindow = aiWindow(t('recipeExamplesTitle'), body);
}
/** The card; `owner`: the browser window. */
export function exampleCard(owner) {
const card = document.createElement('article');
card.className = 'anomalous-wf-card is-examples';
const cover = document.createElement('div');
cover.className = 'anomalous-wf-cover';
cover.append(icon('blocks', 34));
const info = document.createElement('div');
info.className = 'anomalous-wf-info';
appendText(info, 'h3', t('recipeExamplesTitle'), 'anomalous-wf-title');
appendText(info, 'div', t('recipeExamplesCard'), 'anomalous-wf-meta');
card.append(cover, info);
card.onclick = () => showExamples(owner).catch(() => anomalousAlert(t('recipeLoadError')));
return card;
}