Files
olivv-cs-ComfyUI-FunPack/app/boot.js
T
digital-garbage f37f526268 app: Generate wired to a real run -- queued, progress, cancel, result
ComfyUI's own queue and its own socket, not a second one alongside them. A
FunPack queue would be a second thing tracking what is running, and the one the
user believes is whichever spoke last -- so this speaks to /prompt, /interrupt
and /ws as any ComfyUI client does and holds only a view of the server's state.
The graph is asked for, never assembled here, for the same reason.

The client id is remembered: ComfyUI addresses progress to the client that
queued the run, so a reload with a fresh id hears nothing about the generation
already in flight -- the run continues, the GPU time is spent, and the app says
Ready.

Faults found on the way, each in code that already had passing tests:

* `[hidden]` was inert across the whole kit. The UA rule is element-level, so
  `.cx-btn { display: inline-flex }` beats it -- Cancel was marked hidden, sat
  there, and nothing reported anything. jsdom cannot see it: the attribute was
  always correct. One reset rule, and a browser test over four element kinds.
* A panel passed no height to its body and a split pane passed none to its
  panes, so the viewer asked for 100% of `auto` and collapsed to a single line
  inside a panel that was the right size around it.
* subscribe() delivered the first state outside the guard every later delivery
  gets, so one bad listener took down whoever was wiring the UI up -- at
  startup, before anything existed to report it.
* A whole tests directory was never run: the test script globbed
  composer/tests only. `node --test` discovers them itself now, so there is no
  list to forget.
* `empty` was not in the content rule's key list, so three elements were never
  asked to prove their empty-state text comes back as text rather than markup.

The dev server now mounts the REAL routes -- register() already took a route
table -- instead of reimplementing them over http.server. It answered POST
/api/pipeline with 501, so the edit-and-queue path could not be looked at, which
is the one thing the dev server exists for.
2026-08-30 03:36:40 +03:00

90 lines
3.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, viewUrl } from "./shell/run.js";
import { clientId, connect } from "./shell/client.js";
import { check } from "./shell/pipeline.js";
const root = document.querySelector("#app");
async function start() {
const run = createRun({ clientId: clientId(), connect });
const page = build(root, {
async onGenerate() {
// Asked for, not assembled: core builds the graph from the pipeline's
// slots, and a refusal comes back as a reason rather than as a failure
// several stages later.
let plan;
try {
plan = await check({});
} catch (err) {
page.transport.status.setText(`The pipeline could not be read: ${err.message}`);
return;
}
const stopping = [...plan.refused, ...plan.incomplete];
if (!plan.queueable || !plan.prompt) {
page.transport.status.setText(stopping[0] || "This pipeline is not ready to run.");
return;
}
try {
await run.start(plan.prompt);
} catch { /* the run's own state carries the reason, and draw() shows it */ }
},
onCancel: () => run.cancel(),
});
// One subscription draws everything the run affects. Two would be two places
// that can disagree about what is happening.
run.subscribe((state) => {
page.transport.draw(state);
const last = state.images[state.images.length - 1];
if (last) page.viewer.setSource(viewUrl(last), kindOf(last));
});
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),
};
}
/** Video and image results arrive the same way and cannot be shown the same way. */
function kindOf(image) {
return /\.(mp4|webm|mov|mkv)$/i.test(image.filename || "") ? "video" : "image";
}
start();