/queue answers with queue_running AND queue_pending, and only the first was looked at. A job sits in the second from the moment /prompt returns until the worker picks it up -- always briefly, and for as long as it takes whenever something is running ahead of it, which on a shared box is the ordinary case. A reload in that window found nothing, handed Generate back, and let the same job be queued twice: the first copy then ran unnamed, its messages failing the id check, burning GPU time while the UI narrated the second. Every fixture in client.test.js was cleaner than the real payload -- none of them carried a queue_pending key at all, though ComfyUI always sends both. That is the third time a stand-in tidier than the real thing hid what it stood in for, so the fixtures now carry both halves, including the malformed entries a real answer can contain. Also renamed, because the function no longer answers the question its name asked: queuedFor returns which half it found the job in, and a job waiting its turn is adopted as QUEUED. Saying "working" over a run that has not started is the same small lie as saying "cancelled" over one that has not stopped.
61 lines
2.2 KiB
JavaScript
61 lines
2.2 KiB
JavaScript
// Entry point.
|
|
//
|
|
// The whole of what the app does at startup: build the regions, ask the server
|
|
// what exists, and mount whatever announced itself. Nothing here names a module.
|
|
|
|
import { build } from "./shell/layout.js";
|
|
import { fetchManifest } from "./shell/manifest.js";
|
|
import { mountAll } from "./shell/panels.js";
|
|
import { all as allValues } from "./shell/values.js";
|
|
import { composer } from "./composer/composer.js";
|
|
import { createRun } from "./shell/run.js";
|
|
import { clientId, connect, queuedFor, finishedFor } from "./shell/client.js";
|
|
import { wire } from "./shell/session.js";
|
|
import { check } from "./shell/pipeline.js";
|
|
|
|
const root = document.querySelector("#app");
|
|
|
|
async function start() {
|
|
const id = clientId();
|
|
const run = createRun({ clientId: id, connect });
|
|
|
|
const page = build(root, {
|
|
onGenerate: () => session.generate(),
|
|
onCancel: () => run.cancel(),
|
|
});
|
|
const session = wire({ run, page, check, id, queuedFor, finishedFor });
|
|
|
|
let manifest;
|
|
try {
|
|
manifest = await fetchManifest();
|
|
} catch (err) {
|
|
// The one failure the user must see: with no manifest there is no app, so
|
|
// silence here would be an empty window with no explanation.
|
|
root.replaceChildren(composer.emptyState.default({
|
|
icon: "▲",
|
|
title: "Could not reach FunPack",
|
|
hint: `${err.message}. Is ComfyUI running?`,
|
|
}).node);
|
|
return;
|
|
}
|
|
|
|
const { mounted, hidden } = await mountAll(manifest);
|
|
const failed = manifest.failed || [];
|
|
|
|
console.info(`[FunPack] ${mounted.length} module(s) mounted`,
|
|
hidden.length ? `· ${hidden.length} hidden` : "",
|
|
failed.length ? `· ${failed.length} failed to load` : "");
|
|
for (const { id, why } of hidden) console.warn(`[FunPack] ${id} is hidden: ${why}`);
|
|
// These never reached the manifest, so no panel could be missing "in a way
|
|
// the user notices" -- which is exactly why they have to be said. A module
|
|
// that failed to import looks identical to one nobody installed.
|
|
for (const { where, why } of failed) console.warn(`[FunPack] ${where} did not load: ${why}`);
|
|
|
|
window.FunPack = {
|
|
manifest, values: allValues, failed, hidden, run,
|
|
mounted: mounted.map((m) => m.id),
|
|
};
|
|
}
|
|
|
|
start();
|