diff --git a/pyproject.toml b/pyproject.toml index 47fbe13..68973c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "rgthree-comfy" description = "Making ComfyUI more comfortable." -version = "1.0.2508182334" +version = "1.0.2508192121" license = { file = "LICENSE" } dependencies = [] diff --git a/src_web/comfyui/base_power_prompt.ts b/src_web/comfyui/base_power_prompt.ts index 4f9b46e..02a161b 100644 --- a/src_web/comfyui/base_power_prompt.ts +++ b/src_web/comfyui/base_power_prompt.ts @@ -78,7 +78,7 @@ export class PowerPrompt { } return ( this.configuring || - rgthree.loadingApiJson || + !!rgthree.loadingApiJson || (canConnect && !this.node.inputs[inputIndex]!.disabled) ); }; @@ -103,7 +103,7 @@ export class PowerPrompt { } return ( this.configuring || - rgthree.loadingApiJson || + !!rgthree.loadingApiJson || (canConnect && !this.node.outputs[outputIndex]!.disabled) ); }; diff --git a/src_web/comfyui/power_lora_loader.ts b/src_web/comfyui/power_lora_loader.ts index 76585d0..02b2ff8 100644 --- a/src_web/comfyui/power_lora_loader.ts +++ b/src_web/comfyui/power_lora_loader.ts @@ -9,7 +9,7 @@ import type { ICustomWidget, CanvasPointerEvent, } from "@comfyorg/frontend"; -import type {ComfyNodeDef} from "typings/comfy.js"; +import type {ComfyApiFormat, ComfyNodeDef} from "typings/comfy.js"; import type {RgthreeModelInfo} from "typings/rgthree.js"; import {app} from "scripts/app.js"; @@ -74,6 +74,39 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode { // Prefetch loras list. rgthreeApi.getLoras(); + + // [🤮] If ComfyUI is loading from API JSON it doesn't pass us the actual information at all + // (like, in a `configure` call) and tries to set the widget data on its own. Unfortunately, + // since Power Lora Loader has dynamic widgets, this fails on ComfyUI's side. We can do so after + // the fact but, unfortuntely, we need to do it after a timeout since we don't have any + // information at this point to be able to tell what data we need (like, even the node id, let + // alone the actual data). + if (rgthree.loadingApiJson) { + const fullApiJson = rgthree.loadingApiJson; + setTimeout(() => { + this.configureFromApiJson(fullApiJson); + }, 16); + } + } + + private configureFromApiJson(fullApiJson: ComfyApiFormat) { + if (this.id == null) { + const [n, v] = this.logger.errorParts("Cannot load from API JSON without node id."); + console[n]?.(...v); + return; + } + const nodeData = + fullApiJson[this.id] || fullApiJson[String(this.id)] || fullApiJson[Number(this.id)]; + if (nodeData == null) { + const [n, v] = this.logger.errorParts(`No node found in API JSON for node id ${this.id}.`); + console[n]?.(...v); + return; + } + this.configure({ + widgets_values: Object.values(nodeData.inputs).filter( + (input) => typeof (input as any)?.["lora"] === "string", + ), + }); } /** @@ -81,10 +114,16 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode { * added in `onNodeCreated`, letting `super.configure` and do nothing, then create our lora * widgets and, finally, add back in our default widgets. */ - override configure(info: ISerialisedNode): void { + override configure( + info: ISerialisedNode | {widgets_values: ISerialisedNode["widgets_values"]}, + ): void { while (this.widgets?.length) this.removeWidget(0); this.widgetButtonSpacer = null; - super.configure(info); + // Since we may be calling into configure manually for just widgets_values setting (like, from + // API JSON) we want to only call the parent class's configure with a real ISerialisedNode data. + if ((info as ISerialisedNode).id != null) { + super.configure(info as ISerialisedNode); + } (this as any)._tempWidth = this.size[0]; (this as any)._tempHeight = this.size[1]; diff --git a/src_web/comfyui/rgthree.ts b/src_web/comfyui/rgthree.ts index 6cc8bc1..a32f59f 100644 --- a/src_web/comfyui/rgthree.ts +++ b/src_web/comfyui/rgthree.ts @@ -198,6 +198,10 @@ class LogSession { return this.logParts(LogLevel.WARN, message, ...args); } + errorParts(message?: string, ...args: any[]) { + return this.logParts(LogLevel.ERROR, message, ...args); + } + newSession(name?: string) { return new LogSession(`${this.name}${name}`); } @@ -237,7 +241,11 @@ class Rgthree extends EventTarget { monitorLinkTimeout: number | null = null; processingQueue = false; - loadingApiJson = false; + /** + * The API Json currently being loaded, or null. Can be used as a falsy boolean to determine if + * `app.loadApiJson` is currently executing. + */ + loadingApiJson: ComfyApiFormat | null = null; replacingReroute: NodeId | null = null; processingMouseDown = false; processingMouseUp = false; @@ -698,12 +706,12 @@ class Rgthree extends EventTarget { // Keep state for when the app is in the middle of loading from an api JSON file. const loadApiJson = app.loadApiJson; - app.loadApiJson = async function () { - rgthree.loadingApiJson = true; + app.loadApiJson = async function(apiData: any, fileName: string) { + rgthree.loadingApiJson = apiData as ComfyApiFormat; try { loadApiJson.apply(app, [...arguments] as any); } finally { - rgthree.loadingApiJson = false; + rgthree.loadingApiJson = null; } }; diff --git a/web/comfyui/base_power_prompt.js b/web/comfyui/base_power_prompt.js index 450b9b1..c682f23 100644 --- a/web/comfyui/base_power_prompt.js +++ b/web/comfyui/base_power_prompt.js @@ -38,7 +38,7 @@ export class PowerPrompt { ]); } return (this.configuring || - rgthree.loadingApiJson || + !!rgthree.loadingApiJson || (canConnect && !this.node.inputs[inputIndex].disabled)); }; const oldOnConnectOutput = this.node.onConnectOutput; @@ -54,7 +54,7 @@ export class PowerPrompt { ]); } return (this.configuring || - rgthree.loadingApiJson || + !!rgthree.loadingApiJson || (canConnect && !this.node.outputs[outputIndex].disabled)); }; const onPropertyChanged = this.node.onPropertyChanged; diff --git a/web/comfyui/power_lora_loader.js b/web/comfyui/power_lora_loader.js index 3fc033c..9380aa7 100644 --- a/web/comfyui/power_lora_loader.js +++ b/web/comfyui/power_lora_loader.js @@ -24,13 +24,38 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode { this.widgetButtonSpacer = null; this.properties[PROP_LABEL_SHOW_STRENGTHS] = PROP_VALUE_SHOW_STRENGTHS_SINGLE; rgthreeApi.getLoras(); + if (rgthree.loadingApiJson) { + const fullApiJson = rgthree.loadingApiJson; + setTimeout(() => { + this.configureFromApiJson(fullApiJson); + }, 16); + } + } + configureFromApiJson(fullApiJson) { + var _b, _c; + if (this.id == null) { + const [n, v] = this.logger.errorParts("Cannot load from API JSON without node id."); + (_b = console[n]) === null || _b === void 0 ? void 0 : _b.call(console, ...v); + return; + } + const nodeData = fullApiJson[this.id] || fullApiJson[String(this.id)] || fullApiJson[Number(this.id)]; + if (nodeData == null) { + const [n, v] = this.logger.errorParts(`No node found in API JSON for node id ${this.id}.`); + (_c = console[n]) === null || _c === void 0 ? void 0 : _c.call(console, ...v); + return; + } + this.configure({ + widgets_values: Object.values(nodeData.inputs).filter((input) => typeof (input === null || input === void 0 ? void 0 : input["lora"]) === "string"), + }); } configure(info) { var _b; while ((_b = this.widgets) === null || _b === void 0 ? void 0 : _b.length) this.removeWidget(0); this.widgetButtonSpacer = null; - super.configure(info); + if (info.id != null) { + super.configure(info); + } this._tempWidth = this.size[0]; this._tempHeight = this.size[1]; for (const widgetValue of info.widgets_values || []) { diff --git a/web/comfyui/rgthree.js b/web/comfyui/rgthree.js index 5009b56..5e28bfb 100644 --- a/web/comfyui/rgthree.js +++ b/web/comfyui/rgthree.js @@ -115,6 +115,9 @@ class LogSession { warnParts(message, ...args) { return this.logParts(LogLevel.WARN, message, ...args); } + errorParts(message, ...args) { + return this.logParts(LogLevel.ERROR, message, ...args); + } newSession(name) { return new LogSession(`${this.name}${name}`); } @@ -132,7 +135,7 @@ class Rgthree extends EventTarget { this.monitorBadLinksAlerted = false; this.monitorLinkTimeout = null; this.processingQueue = false; - this.loadingApiJson = false; + this.loadingApiJson = null; this.replacingReroute = null; this.processingMouseDown = false; this.processingMouseUp = false; @@ -470,13 +473,13 @@ class Rgthree extends EventTarget { } }; const loadApiJson = app.loadApiJson; - app.loadApiJson = async function () { - rgthree.loadingApiJson = true; + app.loadApiJson = async function (apiData, fileName) { + rgthree.loadingApiJson = apiData; try { loadApiJson.apply(app, [...arguments]); } finally { - rgthree.loadingApiJson = false; + rgthree.loadingApiJson = null; } }; const graphToPrompt = app.graphToPrompt;