A hacky workaround to compensate for upstream comfyui changes. May help #69, in a round-a-bout hacky way.
This commit is contained in:
+11
-4
@@ -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 "";
|
||||
};
|
||||
};
|
||||
|
||||
+117
-68
@@ -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;
|
||||
|
||||
|
||||
+16
-4
@@ -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();
|
||||
|
||||
Vendored
+3
-1
@@ -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<TValue>;
|
||||
// https://github.com/comfyanonymous/ComfyUI/issues/2193 Changes from SerializedLGraphNode to
|
||||
// LGraphNode...
|
||||
serializeValue(nodeType: TLGraphNode, index: number): Promise<TValue>;
|
||||
afterQueued(): void;
|
||||
inputEl?: HTMLTextAreaElement;
|
||||
width: number;
|
||||
|
||||
+50
-8
@@ -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<TLGraphNode>, _app: ComfyApp, conf
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the canvas to be available on app using a single promise.
|
||||
*/
|
||||
let canvasResolver: Resolver<TLGraphCanvas>|null = null;
|
||||
export function waitForCanvas() {
|
||||
if (canvasResolver === null) {
|
||||
canvasResolver = getResolver<TLGraphCanvas>();
|
||||
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<TLGraph>|null = null;
|
||||
export function waitForGraph() {
|
||||
if (graphResolver === null) {
|
||||
graphResolver = getResolver<TLGraph>();
|
||||
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 });
|
||||
}
|
||||
|
||||
+9
-1
@@ -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 "";
|
||||
};
|
||||
};
|
||||
|
||||
+67
-47
@@ -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;
|
||||
|
||||
+16
-2
@@ -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();
|
||||
|
||||
+37
-1
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user