- Durable Job Inbox: every async submit is journaled to the shared sqlite store and survives ComfyUI restarts; the Fal Job Inbox node lists pending/collected jobs and outputs the newest pending request_id/endpoint for one-wire recovery via Result by Request ID. - Provenance: Fal Save Media from URL writes a <file>.fal.json sidecar and embeds a fal_provenance PNG text chunk (preserving existing text chunks and ICC profiles), returning the request_id as a second output; new Fal Provenance from File node reads saved files back into endpoint_id + request_id for free re-materialization. - First web extension (web/ + /fal_api server routes): per-node cost pills rendered above every priced fal node (live estimates on free-typed endpoint fields), a fal sidebar tab with session spend, account balance and a job list with copy/cancel, and in-canvas endpoint search with price labels. All features degrade silently on older frontends; routes are exception-guarded and cached. Review fixes: PNG provenance embed preserves source text chunks and ICC profile; jobs route limit clamped. 15 new tests (91 total).
55 lines
1.5 KiB
JavaScript
55 lines
1.5 KiB
JavaScript
// fal platform extension: cost badges, session/balance/jobs sidebar,
|
|
// and endpoint autocomplete for the ComfyUI canvas.
|
|
|
|
import { app } from "../../scripts/app.js";
|
|
import { loadPricingMap, setupNodeBadges } from "./fal_badges.js";
|
|
import { registerSidebar } from "./fal_sidebar.js";
|
|
import { installAutocomplete } from "./fal_autocomplete.js";
|
|
|
|
// Start loading the pricing map immediately: node definitions register before
|
|
// setup() runs, and the badge drawer looks the map up lazily at draw time.
|
|
const pricingReady = loadPricingMap();
|
|
|
|
function injectStylesheet() {
|
|
try {
|
|
const link = document.createElement("link");
|
|
link.rel = "stylesheet";
|
|
link.href = new URL("./fal_platform.css", import.meta.url).href;
|
|
document.head.appendChild(link);
|
|
} catch (error) {
|
|
console.debug("[fal] stylesheet injection failed", error);
|
|
}
|
|
}
|
|
|
|
app.registerExtension({
|
|
name: "fal.platform",
|
|
|
|
beforeRegisterNodeDef(nodeType, nodeData) {
|
|
try {
|
|
setupNodeBadges(nodeType, nodeData);
|
|
} catch (error) {
|
|
console.debug("[fal] badge setup failed", error);
|
|
}
|
|
},
|
|
|
|
async setup() {
|
|
injectStylesheet();
|
|
try {
|
|
registerSidebar(app);
|
|
} catch (error) {
|
|
console.debug("[fal] sidebar registration failed", error);
|
|
}
|
|
try {
|
|
installAutocomplete();
|
|
} catch (error) {
|
|
console.debug("[fal] autocomplete install failed", error);
|
|
}
|
|
try {
|
|
await pricingReady;
|
|
app.graph?.setDirtyCanvas?.(true, true);
|
|
} catch (error) {
|
|
console.debug("[fal] pricing map warmup failed", error);
|
|
}
|
|
},
|
|
});
|