From 52b38f7f44510589baa2c7ea0a8cca8c05e897df Mon Sep 17 00:00:00 2001 From: rgthree Date: Wed, 6 Dec 2023 01:51:12 -0500 Subject: [PATCH] A hacky workaround to compensate for upstream comfyui changes. May help #69, in a round-a-bout hacky way. --- ts/display_any.ts | 15 +++- ts/rgthree.ts | 185 ++++++++++++++++++++++++++---------------- ts/seed.ts | 20 ++++- ts/typings/comfy.d.ts | 4 +- ts/utils.ts | 58 +++++++++++-- web/display_any.js | 10 ++- web/rgthree.js | 114 +++++++++++++++----------- web/seed.js | 18 +++- web/utils.js | 38 ++++++++- 9 files changed, 326 insertions(+), 136 deletions(-) diff --git a/ts/display_any.ts b/ts/display_any.ts index 8f2473f..c19c015 100644 --- a/ts/display_any.ts +++ b/ts/display_any.ts @@ -10,6 +10,7 @@ import type { } from "./typings/litegraph.js"; import type { ComfyApp, ComfyObjectInfo } from "./typings/comfy.js"; import { addConnectionLayoutSupport, replaceNode } from "./utils.js"; +import { rgthree } from "./rgthree.js"; declare const LiteGraph: typeof TLiteGraph; declare const LGraphNode: typeof TLGraphNode; @@ -38,12 +39,18 @@ app.registerExtension({ ).widget; (this as any).showValueWidget.inputEl!.readOnly = true; (this as any).showValueWidget.serializeValue = async ( - node: SerializedLGraphNode, + node: TLGraphNode, index: number, ) => { - // Since we need a round trip to get the value, the serizalized value means nothing, and - // saving it to the metadata would just be confusing. So, we clear it here. - node.widgets_values![index] = ""; + const n = rgthree.getNodeFromInitialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff(node); + if (n) { + // Since we need a round trip to get the value, the serizalized value means nothing, and + // saving it to the metadata would just be confusing. So, we clear it here. + n.widgets_values![index] = ""; + } else { + console.warn('No serialized node found in workflow. May be attributed to ' + + 'https://github.com/comfyanonymous/ComfyUI/issues/2193'); + } return ""; }; }; diff --git a/ts/rgthree.ts b/ts/rgthree.ts index b289254..1eb5528 100644 --- a/ts/rgthree.ts +++ b/ts/rgthree.ts @@ -2,9 +2,10 @@ import type { LGraphNode, SerializedLGraphNode, serializedLGraph } from "litegra // @ts-ignore import { app } from "../../scripts/app.js"; // @ts-ignore -import {rgthreeConfig} from "./rgthree_config.js"; +import { rgthreeConfig } from "./rgthree_config.js"; import { fixBadLinks } from "./link_fixer.js"; import { wait } from "./shared_utils.js"; +import { waitForGraph } from "./utils.js"; export enum LogLevel { IMPORTANT = 1, @@ -77,7 +78,7 @@ class LogSession { export type RgthreeUiMessage = { id: string; message: string; - type?: 'warn'|null; + type?: "warn" | null; timeout?: number; // closeable?: boolean; // TODO actions?: Array<{ @@ -85,7 +86,7 @@ export type RgthreeUiMessage = { href?: string; callback?: (event: MouseEvent) => void; }>; -} +}; /** * A global class as 'rgthree'; exposed on wiindow. Lots can go in here. @@ -96,61 +97,63 @@ class Rgthree extends EventTarget { altKey = false; metaKey = false; shiftKey = false; - readonly downKeys: {[key:string]: boolean} = {}; + readonly downKeys: { [key: string]: boolean } = {}; logger = new LogSession("[rgthree]"); monitorBadLinksAlerted = false; - monitorLinkTimeout: number|null = null; + monitorLinkTimeout: number | null = null; processingQueue = false; + initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff: any = null; + async clearAllMessages() { - let container = document.querySelector('.rgthree-top-messages-container'); - container && (container.innerHTML = ''); + let container = document.querySelector(".rgthree-top-messages-container"); + container && (container.innerHTML = ""); } async showMessage(data: RgthreeUiMessage) { - let container = document.querySelector('.rgthree-top-messages-container'); + let container = document.querySelector(".rgthree-top-messages-container"); if (!container) { - container = document.createElement('div'); - container.classList.add('rgthree-top-messages-container'); + container = document.createElement("div"); + container.classList.add("rgthree-top-messages-container"); document.body.appendChild(container); } // Hide if we exist. await this.hideMessage(data.id); - const messageContainer = document.createElement('div'); - messageContainer.setAttribute('type', data.type || 'info'); + const messageContainer = document.createElement("div"); + messageContainer.setAttribute("type", data.type || "info"); - const message = document.createElement('span'); + const message = document.createElement("span"); message.innerText = data.message; messageContainer.appendChild(message); for (let a = 0; a < (data.actions || []).length; a++) { const action = data.actions![a]!; if (a > 0) { - const sep = document.createElement('span'); - sep.innerHTML = ' | '; + const sep = document.createElement("span"); + sep.innerHTML = " | "; messageContainer.appendChild(sep); } - const actionEl = document.createElement('a'); + const actionEl = document.createElement("a"); actionEl.innerText = action.label; if (action.href) { - actionEl.target = '_blank'; + actionEl.target = "_blank"; actionEl.href = action.href; } if (action.callback) { actionEl.onclick = (e) => { return action.callback!(e); - } + }; } messageContainer.appendChild(actionEl); } - const messageAnimContainer = document.createElement('div'); - messageAnimContainer.setAttribute('msg-id', data.id); + const messageAnimContainer = document.createElement("div"); + messageAnimContainer.setAttribute("msg-id", data.id); messageAnimContainer.appendChild(messageContainer); container.appendChild(messageAnimContainer); @@ -158,19 +161,18 @@ class Rgthree extends EventTarget { await wait(64); messageAnimContainer.style.marginTop = `-${messageAnimContainer.offsetHeight}px`; await wait(64); - messageAnimContainer.classList.add('-show'); + messageAnimContainer.classList.add("-show"); if (data.timeout) { await wait(data.timeout); this.hideMessage(data.id); } - } async hideMessage(id: string) { const msg = document.querySelector(`.rgthree-top-messages-container > [msg-id="${id}"]`); - if (msg?.classList.contains('-show')) { - msg.classList.remove('-show'); + if (msg?.classList.contains("-show")) { + msg.classList.remove("-show"); await wait(750); } msg && msg.remove(); @@ -183,7 +185,7 @@ class Rgthree extends EventTarget { this.metaKey = !!e.metaKey; this.shiftKey = !!e.shiftKey; this.downKeys[e.key.toLocaleUpperCase()] = true; - this.downKeys['^' + e.key.toLocaleUpperCase()] = true; + this.downKeys["^" + e.key.toLocaleUpperCase()] = true; } /** Handle keyup. Pulled out because sometimes a node will get a keyup before rgthree. */ @@ -193,20 +195,42 @@ class Rgthree extends EventTarget { this.metaKey = !!e.metaKey; this.shiftKey = !!e.shiftKey; this.downKeys[e.key.toLocaleUpperCase()] = false; - this.downKeys['^' + e.key.toLocaleUpperCase()] = false; + this.downKeys["^" + e.key.toLocaleUpperCase()] = false; } areAllKeysDown(keys: string[], caseSensitive = false) { return keys.every((k) => { if (caseSensitive) { - return rgthree.downKeys['^' + k.trim()]; + return rgthree.downKeys["^" + k.trim()]; } return rgthree.downKeys[k.trim().toUpperCase()]; }); } + // This sucks.. but to mitigate changes from https://github.com/rgthree/rgthree-comfy/issues/69 + // and https://github.com/comfyanonymous/ComfyUI/issues/2193 we can try to store the workflow + // node so out nodes can find the seralized node. What a way to work around... + getNodeFromInitialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff(node: LGraphNode) : SerializedLGraphNode|null { + return this.initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff?.nodes?.find( + (n: SerializedLGraphNode) => n.id === node.id, + ) ?? null; + } + + + private async initializeGraphHooks() { + const graph = await waitForGraph(); + const onSerialize = (graph as any).onSerialize; + (graph as any).onSerialize = (data: any) => { + // This sucks.. but to mitigate changes from https://github.com/rgthree/rgthree-comfy/issues/69 + // and https://github.com/comfyanonymous/ComfyUI/issues/2193 we can try to store the workflow + // node so out nodes can find the seralized node. What a way to work around... + this.initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff = data; + onSerialize?.apply(graph, data); + } + } + constructor() { - super() + super(); window.addEventListener("keydown", (e) => { this.handleKeydown(e); }); @@ -215,104 +239,126 @@ class Rgthree extends EventTarget { this.handleKeyup(e); }); + this.initializeGraphHooks(); + // Override the loadGraphData so we can check for bad links and ask the user to fix them. const that = this; const queuePrompt = app.queuePrompt as Function; - app.queuePrompt = async function() { - that.dispatchEvent(new CustomEvent('queue')); + app.queuePrompt = async function () { + that.dispatchEvent(new CustomEvent("queue")); that.processingQueue = true; try { - await queuePrompt.apply(app, [...arguments]) + await queuePrompt.apply(app, [...arguments]); } finally { that.processingQueue = false; - that.dispatchEvent(new CustomEvent('queue-end')); + that.dispatchEvent(new CustomEvent("queue-end")); } - } + }; const graphToPrompt = app.graphToPrompt as Function; - app.graphToPrompt = async function() { - that.dispatchEvent(new CustomEvent('graph-to-prompt')); + app.graphToPrompt = async function () { + that.dispatchEvent(new CustomEvent("graph-to-prompt")); let promise = graphToPrompt.apply(app, [...arguments]); await promise; - that.dispatchEvent(new CustomEvent('graph-to-prompt-end')); + that.dispatchEvent(new CustomEvent("graph-to-prompt-end")); return promise; - } + }; const clean = app.clean; - app.clean = function() { + app.clean = function () { that.clearAllMessages(); clean && clean.call(app, ...arguments); }; const loadGraphData = app.loadGraphData; - app.loadGraphData = function(graph: serializedLGraph) { + app.loadGraphData = function (graph: serializedLGraph) { if (that.monitorLinkTimeout) { clearTimeout(that.monitorLinkTimeout); that.monitorLinkTimeout = null; } that.clearAllMessages(); // Try to make a copy to use, because ComfyUI's loadGraphData will modify it. - let graphCopy: serializedLGraph|null; + let graphCopy: serializedLGraph | null; try { graphCopy = JSON.parse(JSON.stringify(graph)); - } catch(e) { + } catch (e) { graphCopy = null; } setTimeout(() => { - const wasLoadingAborted = document.querySelector('.comfy-modal-content')?.textContent?.includes('Loading aborted due'); - const graphToUse = wasLoadingAborted ? (graphCopy || graph) : app.graph + const wasLoadingAborted = document + .querySelector(".comfy-modal-content") + ?.textContent?.includes("Loading aborted due"); + const graphToUse = wasLoadingAborted ? graphCopy || graph : app.graph; const fixBadLinksResult = fixBadLinks(graphToUse); if (fixBadLinksResult.hasBadLinks) { - that.log(LogLevel.WARN, `The workflow you've loaded has corrupt linking data. Open ${new URL(location.href).origin}/extensions/rgthree-comfy/html/links.html to try to fix.`); - if (rgthreeConfig['show_alerts_for_corrupt_workflows']) { + that.log( + LogLevel.WARN, + `The workflow you've loaded has corrupt linking data. Open ${ + new URL(location.href).origin + }/extensions/rgthree-comfy/html/links.html to try to fix.`, + ); + if (rgthreeConfig["show_alerts_for_corrupt_workflows"]) { that.showMessage({ - id: 'bad-links', - type: 'warn', - message: 'The workflow you\'ve loaded has corrupt linking data that may be able to be fixed.', + id: "bad-links", + type: "warn", + message: + "The workflow you've loaded has corrupt linking data that may be able to be fixed.", actions: [ { - label: 'Open fixer', - href: '/extensions/rgthree-comfy/html/links.html', + label: "Open fixer", + href: "/extensions/rgthree-comfy/html/links.html", }, { - label: 'Fix in place', - href: '/extensions/rgthree-comfy/html/links.html', + label: "Fix in place", + href: "/extensions/rgthree-comfy/html/links.html", callback: (event) => { event.stopPropagation(); event.preventDefault(); - if (confirm('This will attempt to fix in place. Please make sure to have a saved copy of your workflow.')) { + if ( + confirm( + "This will attempt to fix in place. Please make sure to have a saved copy of your workflow.", + ) + ) { try { const fixBadLinksResult = fixBadLinks(graphToUse, true); if (!fixBadLinksResult.hasBadLinks) { - that.hideMessage('bad-links'); - alert('Success! It\'s possible some valid links may have been affected. Please check and verify your workflow.'); + that.hideMessage("bad-links"); + alert( + "Success! It's possible some valid links may have been affected. Please check and verify your workflow.", + ); wasLoadingAborted && app.loadGraphData(fixBadLinksResult.graph); - if (rgthreeConfig['monitor_for_corrupt_links'] || rgthreeConfig['monitor_bad_links']) { + if ( + rgthreeConfig["monitor_for_corrupt_links"] || + rgthreeConfig["monitor_bad_links"] + ) { that.monitorLinkTimeout = setTimeout(() => { that.monitorBadLinks(); }, 5000); } } - } catch(e) { + } catch (e) { console.error(e); - alert('Unsuccessful at fixing corrupt data. :('); - that.hideMessage('bad-links'); + alert("Unsuccessful at fixing corrupt data. :("); + that.hideMessage("bad-links"); } } - } + }, }, - ] + ], }); } - } else if (rgthreeConfig['monitor_for_corrupt_links'] || rgthreeConfig['monitor_bad_links']) { + } else if ( + rgthreeConfig["monitor_for_corrupt_links"] || + rgthreeConfig["monitor_bad_links"] + ) { that.monitorLinkTimeout = setTimeout(() => { that.monitorBadLinks(); }, 5000); } }, 100); loadGraphData && loadGraphData.call(app, ...arguments); - } + }; wait(100).then(() => { this.injectRgthreeCss(); @@ -321,9 +367,9 @@ class Rgthree extends EventTarget { private injectRgthreeCss() { let link = document.createElement("link"); - link.rel = 'stylesheet'; - link.type = 'text/css'; - link.href = 'extensions/rgthree-comfy/rgthree.css'; + link.rel = "stylesheet"; + link.type = "text/css"; + link.href = "extensions/rgthree-comfy/rgthree.css"; document.head.appendChild(link); } @@ -343,7 +389,11 @@ class Rgthree extends EventTarget { const badLinksFound = fixBadLinks(app.graph); if (badLinksFound.hasBadLinks && !this.monitorBadLinksAlerted) { this.monitorBadLinksAlerted = true; - alert(`Problematic links just found in live data. Can you save your workflow and file a bug with the last few steps you took to trigger this at https://github.com/rgthree/rgthree-comfy/issues. Thank you!`) + alert( + `Problematic links just found in live data. Can you save your workflow and file a bug with ` + + `the last few steps you took to trigger this at ` + + `https://github.com/rgthree/rgthree-comfy/issues. Thank you!`, + ); } else if (!badLinksFound.hasBadLinks) { // Clear the alert once fixed so we can alert again. this.monitorBadLinksAlerted = false; @@ -357,4 +407,3 @@ class Rgthree extends EventTarget { export const rgthree = new Rgthree(); // @ts-ignore. Expose it on window because, why not. window.rgthree = rgthree; - diff --git a/ts/seed.ts b/ts/seed.ts index 2bf0dea..c7cc3f6 100644 --- a/ts/seed.ts +++ b/ts/seed.ts @@ -120,7 +120,7 @@ class SeedControl { * for serialization, so it's saved in the image metadata. When re-opening the window, the * seed value will be pre-filled, instead of `-1`. */ - this.seedWidget.serializeValue = async (node: SerializedLGraphNode, index: number) => { + this.seedWidget.serializeValue = async (node: TLGraphNode, index: number) => { const inputSeed = this.seedWidget.value; // Only actually swap and set the value when we're currently queuing. Some other nodes, like // cg-use-everywhere, serializes the graph even when not queing, and we don't want to swap @@ -153,7 +153,13 @@ class SeedControl { this.serializedCtx.seedUsed = this.seedWidget.value; } - node.widgets_values![index] = this.serializedCtx.seedUsed; + const n = rgthree.getNodeFromInitialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff(node); + if (n) { + n.widgets_values![index] = this.serializedCtx.seedUsed; + } else { + console.warn('No serialized node found in workflow. May be attributed to ' + + 'https://github.com/comfyanonymous/ComfyUI/issues/2193'); + } this.seedWidget.value = this.serializedCtx.seedUsed; this.lastSeed = this.serializedCtx.seedUsed!; // Enabled the 'Last seed' Button @@ -215,8 +221,14 @@ class SeedControl { this.lastSeedValue!.inputEl!.readOnly = true; this.lastSeedValue!.inputEl!.style.fontSize = "0.75rem"; this.lastSeedValue!.inputEl!.style.textAlign = "center"; - this.lastSeedValue!.serializeValue = async (node: SerializedLGraphNode, index: number) => { - node.widgets_values![index] = ""; + this.lastSeedValue!.serializeValue = async (node: TLGraphNode, index: number) => { + const n = rgthree.getNodeFromInitialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff(node); + if (n) { + n.widgets_values![index] = ""; + } else { + console.warn('No serialized node found in workflow. May be attributed to ' + + 'https://github.com/comfyanonymous/ComfyUI/issues/2193'); + } return ""; }; this.node.computeSize(); diff --git a/ts/typings/comfy.d.ts b/ts/typings/comfy.d.ts index 04c29ad..795dcfc 100644 --- a/ts/typings/comfy.d.ts +++ b/ts/typings/comfy.d.ts @@ -4,7 +4,9 @@ import { ComfyApp } from "../../../../web/scripts/app"; export { ComfyApp } from "../../../../web/scripts/app"; export interface ComfyWidget extends IWidget { - serializeValue(nodeType: SerializedLGraphNode, index: number): Promise; + // https://github.com/comfyanonymous/ComfyUI/issues/2193 Changes from SerializedLGraphNode to + // LGraphNode... + serializeValue(nodeType: TLGraphNode, index: number): Promise; afterQueued(): void; inputEl?: HTMLTextAreaElement; width: number; diff --git a/ts/utils.ts b/ts/utils.ts index e5de477..4bdc4cb 100644 --- a/ts/utils.ts +++ b/ts/utils.ts @@ -4,7 +4,7 @@ import type { LGraphCanvas as TLGraphCanvas, ContextMenuItem, LLink, - LGraph, + LGraph as TLGraph, IContextMenuOptions, ContextMenu, LGraphNode as TLGraphNode, @@ -18,7 +18,7 @@ import type { Constructor } from "./typings/index.js"; import { app } from "../../scripts/app.js"; // @ts-ignore import { api } from "../../scripts/api.js"; -import { wait } from "./shared_utils.js"; +import { Resolver, getResolver, wait } from "./shared_utils.js"; import { RgthreeHelpDialog } from "./dialog.js"; declare const LGraphNode: typeof TLGraphNode; @@ -81,6 +81,48 @@ export function addMenuItem(node: Constructor, _app: ComfyApp, conf } } +/** + * Waits for the canvas to be available on app using a single promise. + */ +let canvasResolver: Resolver|null = null; +export function waitForCanvas() { + if (canvasResolver === null) { + canvasResolver = getResolver(); + function _waitForCanvas() { + if (!canvasResolver!.completed) { + if (app?.canvas) { + canvasResolver!.resolve(app.canvas); + } else { + requestAnimationFrame(_waitForCanvas); + } + } + } + _waitForCanvas(); + } + return canvasResolver.promise; +} + +/** + * Waits for the graph to be available on app using a single promise. + */ +let graphResolver: Resolver|null = null; +export function waitForGraph() { + if (graphResolver === null) { + graphResolver = getResolver(); + function _wait() { + if (!graphResolver!.completed) { + if (app?.graph) { + graphResolver!.resolve(app.graph); + } else { + requestAnimationFrame(_wait); + } + } + } + _wait(); + } + return graphResolver.promise; +} + export function addMenuItemOnExtraMenuOptions(node: TLGraphNode, config: MenuConfig, menuOptions: ContextMenuItem[], after = 'Shape') { let idx = menuOptions @@ -468,7 +510,7 @@ export function getConnectedNodes( return []; } } - let graph = app.graph as LGraph; + let graph = app.graph as TLGraph; for (const linkId of linkIds) { const link: LLink = (linkId != null && graph.links[linkId]) as LLink; if (!link) { @@ -545,7 +587,7 @@ function getTypeFromSlot( dir: IoDirection, skipSelf = false, ): ConnectionType | null { - let graph = app.graph as LGraph; + let graph = app.graph as TLGraph; let type = slot?.type; if (!skipSelf && type != null && type != "*") { return { type: type as string, label: slot?.label || slot?.name }; @@ -619,7 +661,7 @@ export async function replaceNode( }[] = []; for (const [index, output] of existingNode.outputs.entries()) { for (const linkId of output.links || []) { - const link: LLink = (app.graph as LGraph).links[linkId]!; + const link: LLink = (app.graph as TLGraph).links[linkId]!; if (!link) continue; const targetNode = app.graph.getNodeById(link.target_id); links.push({ node: newNode, slot: output.name, targetNode, targetSlot: link.target_slot }); @@ -628,7 +670,7 @@ export async function replaceNode( for (const [index, input] of existingNode.inputs.entries()) { const linkId = input.link; if (linkId) { - const link: LLink = (app.graph as LGraph).links[linkId]!; + const link: LLink = (app.graph as TLGraph).links[linkId]!; const originNode = app.graph.getNodeById(link.origin_id); links.push({ node: originNode, @@ -688,7 +730,7 @@ export function getSlotLinks(inputOrOutput?: INodeInputSlot | INodeOutputSlot | if ((inputOrOutput as INodeOutputSlot).links?.length) { const output = inputOrOutput as INodeOutputSlot; for (const linkId of output.links || []) { - const link: LLink = (app.graph as LGraph).links[linkId]!; + const link: LLink = (app.graph as TLGraph).links[linkId]!; if (link) { links.push({ id: linkId, link: link }); } @@ -696,7 +738,7 @@ export function getSlotLinks(inputOrOutput?: INodeInputSlot | INodeOutputSlot | } if ((inputOrOutput as INodeInputSlot).link) { const input = inputOrOutput as INodeInputSlot; - const link: LLink = (app.graph as LGraph).links[input.link!]!; + const link: LLink = (app.graph as TLGraph).links[input.link!]!; if (link) { links.push({ id: input.link!, link: link }); } diff --git a/web/display_any.js b/web/display_any.js index 697578b..6afb6da 100644 --- a/web/display_any.js +++ b/web/display_any.js @@ -1,6 +1,7 @@ import { app } from "../../scripts/app.js"; import { ComfyWidgets } from "../../scripts/widgets.js"; import { addConnectionLayoutSupport } from "./utils.js"; +import { rgthree } from "./rgthree.js"; let hasShownAlertForUpdatingInt = false; app.registerExtension({ name: "rgthree.DisplayAny", @@ -13,7 +14,14 @@ app.registerExtension({ this.showValueWidget = ComfyWidgets["STRING"](this, "output", ["STRING", { multiline: true }], app).widget; this.showValueWidget.inputEl.readOnly = true; this.showValueWidget.serializeValue = async (node, index) => { - node.widgets_values[index] = ""; + const n = rgthree.getNodeFromInitialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff(node); + if (n) { + n.widgets_values[index] = ""; + } + else { + console.warn('No serialized node found in workflow. May be attributed to ' + + 'https://github.com/comfyanonymous/ComfyUI/issues/2193'); + } return ""; }; }; diff --git a/web/rgthree.js b/web/rgthree.js index 9a13ede..1ac229d 100644 --- a/web/rgthree.js +++ b/web/rgthree.js @@ -2,6 +2,7 @@ import { app } from "../../scripts/app.js"; import { rgthreeConfig } from "./rgthree_config.js"; import { fixBadLinks } from "./link_fixer.js"; import { wait } from "./shared_utils.js"; +import { waitForGraph } from "./utils.js"; export var LogLevel; (function (LogLevel) { LogLevel[LogLevel["IMPORTANT"] = 1] = "IMPORTANT"; @@ -60,33 +61,33 @@ class LogSession { } class Rgthree extends EventTarget { async clearAllMessages() { - let container = document.querySelector('.rgthree-top-messages-container'); - container && (container.innerHTML = ''); + let container = document.querySelector(".rgthree-top-messages-container"); + container && (container.innerHTML = ""); } async showMessage(data) { - let container = document.querySelector('.rgthree-top-messages-container'); + let container = document.querySelector(".rgthree-top-messages-container"); if (!container) { - container = document.createElement('div'); - container.classList.add('rgthree-top-messages-container'); + container = document.createElement("div"); + container.classList.add("rgthree-top-messages-container"); document.body.appendChild(container); } await this.hideMessage(data.id); - const messageContainer = document.createElement('div'); - messageContainer.setAttribute('type', data.type || 'info'); - const message = document.createElement('span'); + const messageContainer = document.createElement("div"); + messageContainer.setAttribute("type", data.type || "info"); + const message = document.createElement("span"); message.innerText = data.message; messageContainer.appendChild(message); for (let a = 0; a < (data.actions || []).length; a++) { const action = data.actions[a]; if (a > 0) { - const sep = document.createElement('span'); - sep.innerHTML = ' | '; + const sep = document.createElement("span"); + sep.innerHTML = " | "; messageContainer.appendChild(sep); } - const actionEl = document.createElement('a'); + const actionEl = document.createElement("a"); actionEl.innerText = action.label; if (action.href) { - actionEl.target = '_blank'; + actionEl.target = "_blank"; actionEl.href = action.href; } if (action.callback) { @@ -96,14 +97,14 @@ class Rgthree extends EventTarget { } messageContainer.appendChild(actionEl); } - const messageAnimContainer = document.createElement('div'); - messageAnimContainer.setAttribute('msg-id', data.id); + const messageAnimContainer = document.createElement("div"); + messageAnimContainer.setAttribute("msg-id", data.id); messageAnimContainer.appendChild(messageContainer); container.appendChild(messageAnimContainer); await wait(64); messageAnimContainer.style.marginTop = `-${messageAnimContainer.offsetHeight}px`; await wait(64); - messageAnimContainer.classList.add('-show'); + messageAnimContainer.classList.add("-show"); if (data.timeout) { await wait(data.timeout); this.hideMessage(data.id); @@ -111,8 +112,8 @@ class Rgthree extends EventTarget { } async hideMessage(id) { const msg = document.querySelector(`.rgthree-top-messages-container > [msg-id="${id}"]`); - if (msg === null || msg === void 0 ? void 0 : msg.classList.contains('-show')) { - msg.classList.remove('-show'); + if (msg === null || msg === void 0 ? void 0 : msg.classList.contains("-show")) { + msg.classList.remove("-show"); await wait(750); } msg && msg.remove(); @@ -123,7 +124,7 @@ class Rgthree extends EventTarget { this.metaKey = !!e.metaKey; this.shiftKey = !!e.shiftKey; this.downKeys[e.key.toLocaleUpperCase()] = true; - this.downKeys['^' + e.key.toLocaleUpperCase()] = true; + this.downKeys["^" + e.key.toLocaleUpperCase()] = true; } handleKeyup(e) { this.ctrlKey = !!e.ctrlKey; @@ -131,16 +132,28 @@ class Rgthree extends EventTarget { this.metaKey = !!e.metaKey; this.shiftKey = !!e.shiftKey; this.downKeys[e.key.toLocaleUpperCase()] = false; - this.downKeys['^' + e.key.toLocaleUpperCase()] = false; + this.downKeys["^" + e.key.toLocaleUpperCase()] = false; } areAllKeysDown(keys, caseSensitive = false) { return keys.every((k) => { if (caseSensitive) { - return rgthree.downKeys['^' + k.trim()]; + return rgthree.downKeys["^" + k.trim()]; } return rgthree.downKeys[k.trim().toUpperCase()]; }); } + getNodeFromInitialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff(node) { + var _a, _b, _c; + return (_c = (_b = (_a = this.initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff) === null || _a === void 0 ? void 0 : _a.nodes) === null || _b === void 0 ? void 0 : _b.find((n) => n.id === node.id)) !== null && _c !== void 0 ? _c : null; + } + async initializeGraphHooks() { + const graph = await waitForGraph(); + const onSerialize = graph.onSerialize; + graph.onSerialize = (data) => { + this.initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff = data; + onSerialize === null || onSerialize === void 0 ? void 0 : onSerialize.apply(graph, data); + }; + } constructor() { super(); this.ctrlKey = false; @@ -152,31 +165,33 @@ class Rgthree extends EventTarget { this.monitorBadLinksAlerted = false; this.monitorLinkTimeout = null; this.processingQueue = false; + this.initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff = null; window.addEventListener("keydown", (e) => { this.handleKeydown(e); }); window.addEventListener("keyup", (e) => { this.handleKeyup(e); }); + this.initializeGraphHooks(); const that = this; const queuePrompt = app.queuePrompt; app.queuePrompt = async function () { - that.dispatchEvent(new CustomEvent('queue')); + that.dispatchEvent(new CustomEvent("queue")); that.processingQueue = true; try { await queuePrompt.apply(app, [...arguments]); } finally { that.processingQueue = false; - that.dispatchEvent(new CustomEvent('queue-end')); + that.dispatchEvent(new CustomEvent("queue-end")); } }; const graphToPrompt = app.graphToPrompt; app.graphToPrompt = async function () { - that.dispatchEvent(new CustomEvent('graph-to-prompt')); + that.dispatchEvent(new CustomEvent("graph-to-prompt")); let promise = graphToPrompt.apply(app, [...arguments]); await promise; - that.dispatchEvent(new CustomEvent('graph-to-prompt-end')); + that.dispatchEvent(new CustomEvent("graph-to-prompt-end")); return promise; }; const clean = app.clean; @@ -200,35 +215,37 @@ class Rgthree extends EventTarget { } setTimeout(() => { var _a, _b; - const wasLoadingAborted = (_b = (_a = document.querySelector('.comfy-modal-content')) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.includes('Loading aborted due'); - const graphToUse = wasLoadingAborted ? (graphCopy || graph) : app.graph; + const wasLoadingAborted = (_b = (_a = document + .querySelector(".comfy-modal-content")) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.includes("Loading aborted due"); + const graphToUse = wasLoadingAborted ? graphCopy || graph : app.graph; const fixBadLinksResult = fixBadLinks(graphToUse); if (fixBadLinksResult.hasBadLinks) { that.log(LogLevel.WARN, `The workflow you've loaded has corrupt linking data. Open ${new URL(location.href).origin}/extensions/rgthree-comfy/html/links.html to try to fix.`); - if (rgthreeConfig['show_alerts_for_corrupt_workflows']) { + if (rgthreeConfig["show_alerts_for_corrupt_workflows"]) { that.showMessage({ - id: 'bad-links', - type: 'warn', - message: 'The workflow you\'ve loaded has corrupt linking data that may be able to be fixed.', + id: "bad-links", + type: "warn", + message: "The workflow you've loaded has corrupt linking data that may be able to be fixed.", actions: [ { - label: 'Open fixer', - href: '/extensions/rgthree-comfy/html/links.html', + label: "Open fixer", + href: "/extensions/rgthree-comfy/html/links.html", }, { - label: 'Fix in place', - href: '/extensions/rgthree-comfy/html/links.html', + label: "Fix in place", + href: "/extensions/rgthree-comfy/html/links.html", callback: (event) => { event.stopPropagation(); event.preventDefault(); - if (confirm('This will attempt to fix in place. Please make sure to have a saved copy of your workflow.')) { + if (confirm("This will attempt to fix in place. Please make sure to have a saved copy of your workflow.")) { try { const fixBadLinksResult = fixBadLinks(graphToUse, true); if (!fixBadLinksResult.hasBadLinks) { - that.hideMessage('bad-links'); - alert('Success! It\'s possible some valid links may have been affected. Please check and verify your workflow.'); + that.hideMessage("bad-links"); + alert("Success! It's possible some valid links may have been affected. Please check and verify your workflow."); wasLoadingAborted && app.loadGraphData(fixBadLinksResult.graph); - if (rgthreeConfig['monitor_for_corrupt_links'] || rgthreeConfig['monitor_bad_links']) { + if (rgthreeConfig["monitor_for_corrupt_links"] || + rgthreeConfig["monitor_bad_links"]) { that.monitorLinkTimeout = setTimeout(() => { that.monitorBadLinks(); }, 5000); @@ -237,17 +254,18 @@ class Rgthree extends EventTarget { } catch (e) { console.error(e); - alert('Unsuccessful at fixing corrupt data. :('); - that.hideMessage('bad-links'); + alert("Unsuccessful at fixing corrupt data. :("); + that.hideMessage("bad-links"); } } - } + }, }, - ] + ], }); } } - else if (rgthreeConfig['monitor_for_corrupt_links'] || rgthreeConfig['monitor_bad_links']) { + else if (rgthreeConfig["monitor_for_corrupt_links"] || + rgthreeConfig["monitor_bad_links"]) { that.monitorLinkTimeout = setTimeout(() => { that.monitorBadLinks(); }, 5000); @@ -261,9 +279,9 @@ class Rgthree extends EventTarget { } injectRgthreeCss() { let link = document.createElement("link"); - link.rel = 'stylesheet'; - link.type = 'text/css'; - link.href = 'extensions/rgthree-comfy/rgthree.css'; + link.rel = "stylesheet"; + link.type = "text/css"; + link.href = "extensions/rgthree-comfy/rgthree.css"; document.head.appendChild(link); } setLogLevel(level) { @@ -279,7 +297,9 @@ class Rgthree extends EventTarget { const badLinksFound = fixBadLinks(app.graph); if (badLinksFound.hasBadLinks && !this.monitorBadLinksAlerted) { this.monitorBadLinksAlerted = true; - alert(`Problematic links just found in live data. Can you save your workflow and file a bug with the last few steps you took to trigger this at https://github.com/rgthree/rgthree-comfy/issues. Thank you!`); + alert(`Problematic links just found in live data. Can you save your workflow and file a bug with ` + + `the last few steps you took to trigger this at ` + + `https://github.com/rgthree/rgthree-comfy/issues. Thank you!`); } else if (!badLinksFound.hasBadLinks) { this.monitorBadLinksAlerted = false; diff --git a/web/seed.js b/web/seed.js index b744dfa..56e9cdb 100644 --- a/web/seed.js +++ b/web/seed.js @@ -78,7 +78,14 @@ class SeedControl { else { this.serializedCtx.seedUsed = this.seedWidget.value; } - node.widgets_values[index] = this.serializedCtx.seedUsed; + const n = rgthree.getNodeFromInitialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff(node); + if (n) { + n.widgets_values[index] = this.serializedCtx.seedUsed; + } + else { + console.warn('No serialized node found in workflow. May be attributed to ' + + 'https://github.com/comfyanonymous/ComfyUI/issues/2193'); + } this.seedWidget.value = this.serializedCtx.seedUsed; this.lastSeed = this.serializedCtx.seedUsed; if (SPECIAL_SEEDS.includes(this.serializedCtx.inputSeed)) { @@ -125,7 +132,14 @@ class SeedControl { this.lastSeedValue.inputEl.style.fontSize = "0.75rem"; this.lastSeedValue.inputEl.style.textAlign = "center"; this.lastSeedValue.serializeValue = async (node, index) => { - node.widgets_values[index] = ""; + const n = rgthree.getNodeFromInitialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff(node); + if (n) { + n.widgets_values[index] = ""; + } + else { + console.warn('No serialized node found in workflow. May be attributed to ' + + 'https://github.com/comfyanonymous/ComfyUI/issues/2193'); + } return ""; }; this.node.computeSize(); diff --git a/web/utils.js b/web/utils.js index 5e0730e..ec41b26 100644 --- a/web/utils.js +++ b/web/utils.js @@ -1,6 +1,6 @@ import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js"; -import { wait } from "./shared_utils.js"; +import { getResolver, wait } from "./shared_utils.js"; import { RgthreeHelpDialog } from "./dialog.js"; const oldApiGetNodeDefs = api.getNodeDefs; api.getNodeDefs = async function () { @@ -34,6 +34,42 @@ export function addMenuItem(node, _app, config, after = 'Shape') { addMenuItemOnExtraMenuOptions(this, config, menuOptions, after); }; } +let canvasResolver = null; +export function waitForCanvas() { + if (canvasResolver === null) { + canvasResolver = getResolver(); + function _waitForCanvas() { + if (!canvasResolver.completed) { + if (app === null || app === void 0 ? void 0 : app.canvas) { + canvasResolver.resolve(app.canvas); + } + else { + requestAnimationFrame(_waitForCanvas); + } + } + } + _waitForCanvas(); + } + return canvasResolver.promise; +} +let graphResolver = null; +export function waitForGraph() { + if (graphResolver === null) { + graphResolver = getResolver(); + function _wait() { + if (!graphResolver.completed) { + if (app === null || app === void 0 ? void 0 : app.graph) { + graphResolver.resolve(app.graph); + } + else { + requestAnimationFrame(_wait); + } + } + } + _wait(); + } + return graphResolver.promise; +} export function addMenuItemOnExtraMenuOptions(node, config, menuOptions, after = 'Shape') { let idx = menuOptions .slice()