subscribe() delivers the CURRENT state at once, and boot.js registered it right after wire() had deliberately disabled Generate for the length of the reattach lookup. That first delivery drew an idle run, and drawing an idle run hands the button back -- so on every single page load Generate looked available while the page still had no idea whether it already had a run. The queue stayed safe, because the generator waits on the same lookup, but a live-looking button that answers a click with nothing is its own fault. Fixed twice over, because either alone would rot. The subscription moved into wire() with the rest of the wiring, and the button being held is now a state the transport keeps rather than something a caller does once: draw() runs on every change, and each of those was free to undo it. The fake run had no subscribe() at all -- which is exactly why the suite could not see this. That is the second time a fake was tidier than the real object and hid the fault it stood in for, so it now delivers state on subscribe like the real one, and announces changes. boot.js is down to assembling six things and mounting the modules.
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, runningFor, 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, runningFor, 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();
|