Don't manipulate inputs for base-any-input nodes if we're currently copying many to clipboard. Fixes #76
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
// @ts-ignore
|
||||
import {app} from "../../scripts/app.js";
|
||||
import { RgthreeBaseNode } from "./base_node.js";
|
||||
import type {Vector2, LLink, INodeInputSlot, INodeOutputSlot, LGraphNode as TLGraphNode, LiteGraph as TLiteGraph, IWidget} from './typings/litegraph.js';
|
||||
import {rgthree} from "./rgthree.js"
|
||||
import type {Vector2, LLink, INodeInputSlot, INodeOutputSlot, LGraphNode as TLGraphNode, LiteGraph as TLiteGraph, IWidget, SerializedLGraphNode} from './typings/litegraph.js';
|
||||
import { PassThroughFollowing, addConnectionLayoutSupport, addMenuItem, filterOutPassthroughNodes, getConnectedInputNodes, getConnectedInputNodesAndFilterPassThroughs, getConnectedOutputNodes, getConnectedOutputNodesAndFilterPassThroughs} from "./utils.js";
|
||||
|
||||
declare const LiteGraph: typeof TLiteGraph;
|
||||
@@ -46,11 +47,17 @@ export class BaseAnyInputConnectedNode extends RgthreeBaseNode {
|
||||
|
||||
override clone() {
|
||||
const cloned = super.clone();
|
||||
while (cloned.inputs.length > 1) {
|
||||
cloned.removeInput(cloned.inputs.length - 1);
|
||||
}
|
||||
if (cloned.inputs[0]) {
|
||||
cloned.inputs[0].label = '';
|
||||
// Copying to clipboard (and also, creating node templates) work by cloning nodes and, for some
|
||||
// reason, it manually manipulates the cloned data. So, we want to keep the present input slots
|
||||
// so if it's pasted/templatized the data is correct. Otherwise, clear the inputs and so the new
|
||||
// node is ready to go, fresh.
|
||||
if (!rgthree.canvasCurrentlyCopyingToClipboardWithMultipleNodes) {
|
||||
while (cloned.inputs.length > 1) {
|
||||
cloned.removeInput(cloned.inputs.length - 1);
|
||||
}
|
||||
if (cloned.inputs[0]) {
|
||||
cloned.inputs[0].label = '';
|
||||
}
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
+255
-176
@@ -1,7 +1,14 @@
|
||||
import { type LGraphCanvas as TLGraphCanvas, type LGraphNode, type SerializedLGraphNode, type serializedLGraph } from "litegraph.js";
|
||||
import {
|
||||
type LGraphCanvas as TLGraphCanvas,
|
||||
type LGraphNode,
|
||||
type SerializedLGraphNode,
|
||||
type serializedLGraph,
|
||||
} from "litegraph.js";
|
||||
// @ts-ignore
|
||||
import { app } from "../../scripts/app.js";
|
||||
// @ts-ignore
|
||||
import { api } from "../../scripts/api.js";
|
||||
// @ts-ignore
|
||||
import { rgthreeConfig } from "./rgthree_config.js";
|
||||
import { fixBadLinks } from "./link_fixer.js";
|
||||
import { wait } from "./shared_utils.js";
|
||||
@@ -94,9 +101,12 @@ export type RgthreeUiMessage = {
|
||||
* A global class as 'rgthree'; exposed on wiindow. Lots can go in here.
|
||||
*/
|
||||
class Rgthree extends EventTarget {
|
||||
|
||||
/** The users' config. */
|
||||
readonly config = rgthreeConfig;
|
||||
|
||||
/** Exposes the ComfyUI api instance on rgthree. */
|
||||
readonly api = api;
|
||||
|
||||
/** Are any functional keys pressed in this given moment? */
|
||||
ctrlKey = false;
|
||||
altKey = false;
|
||||
@@ -111,13 +121,227 @@ class Rgthree extends EventTarget {
|
||||
|
||||
processingQueue = false;
|
||||
|
||||
// Comfy/LiteGraph states so nodes and tell what the hell is going on.
|
||||
canvasCurrentlyCopyingToClipboard = false;
|
||||
canvasCurrentlyCopyingToClipboardWithMultipleNodes = false;
|
||||
initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff: any = null;
|
||||
|
||||
async clearAllMessages() {
|
||||
let container = document.querySelector(".rgthree-top-messages-container");
|
||||
container && (container.innerHTML = "");
|
||||
constructor() {
|
||||
super();
|
||||
window.addEventListener("keydown", (e) => {
|
||||
this.handleKeydown(e);
|
||||
});
|
||||
|
||||
window.addEventListener("keyup", (e) => {
|
||||
this.handleKeyup(e);
|
||||
});
|
||||
|
||||
this.initializeGraphAndCanvasHooks();
|
||||
this.initializeComfyUIHooks();
|
||||
|
||||
wait(100).then(() => {
|
||||
this.injectRgthreeCss();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a bunch of hooks into LiteGraph itself so we can either keep state or context on
|
||||
* what's happening so nodes can respond appropriately. This is usually to fix broken assumptions
|
||||
* in the unowned code [🤮], but sometimes to add features or enhancements too [⭐].
|
||||
*/
|
||||
private async initializeGraphAndCanvasHooks() {
|
||||
const rgthree = this;
|
||||
const [canvas, graph] = await Promise.all([waitForCanvas(), waitForGraph()]);
|
||||
|
||||
// [🤮] 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 our nodes can find the seralized node. Works with method
|
||||
// `getNodeFromInitialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff` to find a node
|
||||
// while serializing. What a way to work around...
|
||||
const onSerialize = (graph as any).onSerialize;
|
||||
(graph as any).onSerialize = (data: any) => {
|
||||
this.initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff = data;
|
||||
onSerialize?.apply(graph, data);
|
||||
};
|
||||
|
||||
// [🤮] Copying to clipboard clones nodes and then manipulats the linking data manually which
|
||||
// does not allow a node to handle connections. This harms nodes that manually handle inputs,
|
||||
// like our any-input nodes that may start with one input, and manually add new ones when one is
|
||||
// attached.
|
||||
const copyToClipboard = LGraphCanvas.prototype.copyToClipboard;
|
||||
LGraphCanvas.prototype.copyToClipboard = function (nodes: LGraphNode[]) {
|
||||
rgthree.canvasCurrentlyCopyingToClipboard = true;
|
||||
rgthree.canvasCurrentlyCopyingToClipboardWithMultipleNodes =
|
||||
Object.values(nodes || this.selected_nodes || []).length > 1;
|
||||
copyToClipboard.apply(canvas, [...arguments] as any);
|
||||
rgthree.canvasCurrentlyCopyingToClipboard = false;
|
||||
rgthree.canvasCurrentlyCopyingToClipboardWithMultipleNodes = false;
|
||||
};
|
||||
|
||||
// [⭐] Make it so when we add a group, we get to name it immediately.
|
||||
const onGroupAdd = LGraphCanvas.onGroupAdd;
|
||||
LGraphCanvas.onGroupAdd = function (...args: any[]) {
|
||||
onGroupAdd.apply(canvas, [...args] as any);
|
||||
LGraphCanvas.onShowPropertyEditor(
|
||||
{},
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
graph._groups[graph._groups.length - 1],
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a bunch of hooks into ComfyUI and/or LiteGraph itself so we can either keep state or
|
||||
* context on what's happening so nodes can respond appropriately. This is usually to fix broken
|
||||
* assumptions in the unowned code [🤮], but sometimes to add features or enhancements too [⭐].
|
||||
*/
|
||||
private initializeComfyUIHooks() {
|
||||
const rgthree = this;
|
||||
|
||||
// Keep state for when the app is queuing the prompt. For instance, this is used for seed to
|
||||
// understand if we're serializing because we're queueing (and return the random seed to use) or
|
||||
// for saving the workflow (and keep -1, etc.).
|
||||
const queuePrompt = app.queuePrompt as Function;
|
||||
app.queuePrompt = async function () {
|
||||
rgthree.dispatchEvent(new CustomEvent("queue"));
|
||||
rgthree.processingQueue = true;
|
||||
try {
|
||||
await queuePrompt.apply(app, [...arguments]);
|
||||
} finally {
|
||||
rgthree.processingQueue = false;
|
||||
rgthree.dispatchEvent(new CustomEvent("queue-end"));
|
||||
}
|
||||
};
|
||||
|
||||
// Keep state for when the app is serizalizing the graph to prompt.
|
||||
const graphToPrompt = app.graphToPrompt as Function;
|
||||
app.graphToPrompt = async function () {
|
||||
rgthree.dispatchEvent(new CustomEvent("graph-to-prompt"));
|
||||
let promise = graphToPrompt.apply(app, [...arguments]);
|
||||
await promise;
|
||||
rgthree.dispatchEvent(new CustomEvent("graph-to-prompt-end"));
|
||||
return promise;
|
||||
};
|
||||
|
||||
// Hook into a clean call; allow us to clear and rgthree messages.
|
||||
const clean = app.clean;
|
||||
app.clean = function () {
|
||||
rgthree.clearAllMessages();
|
||||
clean && clean.call(app, ...arguments);
|
||||
};
|
||||
|
||||
// Hook into a data load, like from an image or JSON drop-in. This is (currently) used to
|
||||
// monitor for bad linking data.
|
||||
const loadGraphData = app.loadGraphData;
|
||||
app.loadGraphData = function (graph: serializedLGraph) {
|
||||
if (rgthree.monitorLinkTimeout) {
|
||||
clearTimeout(rgthree.monitorLinkTimeout);
|
||||
rgthree.monitorLinkTimeout = null;
|
||||
}
|
||||
rgthree.clearAllMessages();
|
||||
// Try to make a copy to use, because ComfyUI's loadGraphData will modify it.
|
||||
let graphCopy: serializedLGraph | null;
|
||||
try {
|
||||
graphCopy = JSON.parse(JSON.stringify(graph));
|
||||
} 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 fixBadLinksResult = fixBadLinks(graphToUse);
|
||||
if (fixBadLinksResult.hasBadLinks) {
|
||||
rgthree.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"]) {
|
||||
rgthree.showMessage({
|
||||
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: "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.",
|
||||
)
|
||||
) {
|
||||
try {
|
||||
const fixBadLinksResult = fixBadLinks(graphToUse, true);
|
||||
if (!fixBadLinksResult.hasBadLinks) {
|
||||
rgthree.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"]
|
||||
) {
|
||||
rgthree.monitorLinkTimeout = setTimeout(() => {
|
||||
rgthree.monitorBadLinks();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert("Unsuccessful at fixing corrupt data. :(");
|
||||
rgthree.hideMessage("bad-links");
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
} else if (
|
||||
rgthreeConfig["monitor_for_corrupt_links"] ||
|
||||
rgthreeConfig["monitor_bad_links"]
|
||||
) {
|
||||
rgthree.monitorLinkTimeout = setTimeout(() => {
|
||||
rgthree.monitorBadLinks();
|
||||
}, 5000);
|
||||
}
|
||||
}, 100);
|
||||
loadGraphData && loadGraphData.call(app, ...arguments);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* [🤮] Finds a node in the currently serializing workflow from the hook setup above. This is to
|
||||
* mitigate breakages from https://github.com/comfyanonymous/ComfyUI/issues/2193 we can try to
|
||||
* store the workflow node so our nodes can find the seralized node.
|
||||
*/
|
||||
getNodeFromInitialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff(
|
||||
node: LGraphNode,
|
||||
): SerializedLGraphNode | null {
|
||||
return (
|
||||
this.initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff?.nodes?.find(
|
||||
(n: SerializedLGraphNode) => n.id === node.id,
|
||||
) ?? null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a message in the UI.
|
||||
*/
|
||||
async showMessage(data: RgthreeUiMessage) {
|
||||
let container = document.querySelector(".rgthree-top-messages-container");
|
||||
if (!container) {
|
||||
@@ -174,6 +398,9 @@ class Rgthree extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides a message in the UI.
|
||||
*/
|
||||
async hideMessage(id: string) {
|
||||
const msg = document.querySelector(`.rgthree-top-messages-container > [msg-id="${id}"]`);
|
||||
if (msg?.classList.contains("-show")) {
|
||||
@@ -183,7 +410,17 @@ class Rgthree extends EventTarget {
|
||||
msg && msg.remove();
|
||||
}
|
||||
|
||||
/** Handle keydown. Pulled out because sometimes a node will get a keydown before rgthree. */
|
||||
/**
|
||||
* Clears all messages in the UI.
|
||||
*/
|
||||
async clearAllMessages() {
|
||||
let container = document.querySelector(".rgthree-top-messages-container");
|
||||
container && (container.innerHTML = "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle keydown. Pulled out because sometimes a node will get a keydown before rgthree.
|
||||
*/
|
||||
handleKeydown(e: KeyboardEvent) {
|
||||
this.ctrlKey = !!e.ctrlKey;
|
||||
this.altKey = !!e.altKey;
|
||||
@@ -193,7 +430,9 @@ class Rgthree extends EventTarget {
|
||||
this.downKeys["^" + e.key.toLocaleUpperCase()] = true;
|
||||
}
|
||||
|
||||
/** Handle keyup. Pulled out because sometimes a node will get a keyup before rgthree. */
|
||||
/**
|
||||
* Handle keyup. Pulled out because sometimes a node will get a keyup before rgthree.
|
||||
*/
|
||||
handleKeyup(e: KeyboardEvent) {
|
||||
this.ctrlKey = !!e.ctrlKey;
|
||||
this.altKey = !!e.altKey;
|
||||
@@ -203,6 +442,9 @@ class Rgthree extends EventTarget {
|
||||
this.downKeys["^" + e.key.toLocaleUpperCase()] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all keys passed in are down.
|
||||
*/
|
||||
areAllKeysDown(keys: string[], caseSensitive = false) {
|
||||
return keys.every((k) => {
|
||||
if (caseSensitive) {
|
||||
@@ -212,172 +454,9 @@ class Rgthree extends EventTarget {
|
||||
});
|
||||
}
|
||||
|
||||
// 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 initializeGraphAndCanvasHooks() {
|
||||
const [canvas, graph] = await Promise.all([waitForCanvas(), 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);
|
||||
};
|
||||
|
||||
// Make it so when we add a group, we get to name it immediately.
|
||||
const onGroupAdd = LGraphCanvas.onGroupAdd;
|
||||
LGraphCanvas.onGroupAdd = function(...args: any[]) {
|
||||
onGroupAdd.apply(canvas, [...args] as any);
|
||||
LGraphCanvas.onShowPropertyEditor({}, null, null, null, graph._groups[graph._groups.length - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
window.addEventListener("keydown", (e) => {
|
||||
this.handleKeydown(e);
|
||||
});
|
||||
|
||||
window.addEventListener("keyup", (e) => {
|
||||
this.handleKeyup(e);
|
||||
});
|
||||
|
||||
this.initializeGraphAndCanvasHooks();
|
||||
|
||||
// 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"));
|
||||
that.processingQueue = true;
|
||||
try {
|
||||
await queuePrompt.apply(app, [...arguments]);
|
||||
} finally {
|
||||
that.processingQueue = false;
|
||||
that.dispatchEvent(new CustomEvent("queue-end"));
|
||||
}
|
||||
};
|
||||
|
||||
const graphToPrompt = app.graphToPrompt as Function;
|
||||
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"));
|
||||
return promise;
|
||||
};
|
||||
|
||||
const clean = app.clean;
|
||||
app.clean = function () {
|
||||
that.clearAllMessages();
|
||||
clean && clean.call(app, ...arguments);
|
||||
};
|
||||
|
||||
const loadGraphData = app.loadGraphData;
|
||||
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;
|
||||
try {
|
||||
graphCopy = JSON.parse(JSON.stringify(graph));
|
||||
} 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 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.showMessage({
|
||||
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: "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.",
|
||||
)
|
||||
) {
|
||||
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.",
|
||||
);
|
||||
wasLoadingAborted && app.loadGraphData(fixBadLinksResult.graph);
|
||||
if (
|
||||
rgthreeConfig["monitor_for_corrupt_links"] ||
|
||||
rgthreeConfig["monitor_bad_links"]
|
||||
) {
|
||||
that.monitorLinkTimeout = setTimeout(() => {
|
||||
that.monitorBadLinks();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert("Unsuccessful at fixing corrupt data. :(");
|
||||
that.hideMessage("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();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the rgthree.css file into the app.
|
||||
*/
|
||||
private injectRgthreeCss() {
|
||||
let link = document.createElement("link");
|
||||
link.rel = "stylesheet";
|
||||
@@ -403,9 +482,9 @@ class Rgthree extends EventTarget {
|
||||
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!`,
|
||||
`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.
|
||||
|
||||
Vendored
+2
-1
@@ -1462,7 +1462,8 @@ export declare class LGraphCanvas {
|
||||
/** process a key event */
|
||||
processKey(e: KeyboardEvent): boolean | undefined;
|
||||
|
||||
copyToClipboard(): void;
|
||||
// @rgthree - added param
|
||||
copyToClipboard(nodes: LGraphNode[]|{[key:number]:LGraphNode}): void;
|
||||
pasteFromClipboard(): void;
|
||||
processDrop(e: DragEvent): void;
|
||||
checkDropItem(e: DragEvent): void;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { RgthreeBaseNode } from "./base_node.js";
|
||||
import { rgthree } from "./rgthree.js";
|
||||
import { PassThroughFollowing, addConnectionLayoutSupport, addMenuItem, getConnectedInputNodes, getConnectedInputNodesAndFilterPassThroughs, getConnectedOutputNodes, getConnectedOutputNodesAndFilterPassThroughs } from "./utils.js";
|
||||
export class BaseAnyInputConnectedNode extends RgthreeBaseNode {
|
||||
constructor(title = BaseAnyInputConnectedNode.title) {
|
||||
@@ -24,11 +25,13 @@ export class BaseAnyInputConnectedNode extends RgthreeBaseNode {
|
||||
}
|
||||
clone() {
|
||||
const cloned = super.clone();
|
||||
while (cloned.inputs.length > 1) {
|
||||
cloned.removeInput(cloned.inputs.length - 1);
|
||||
}
|
||||
if (cloned.inputs[0]) {
|
||||
cloned.inputs[0].label = '';
|
||||
if (!rgthree.canvasCurrentlyCopyingToClipboardWithMultipleNodes) {
|
||||
while (cloned.inputs.length > 1) {
|
||||
cloned.removeInput(cloned.inputs.length - 1);
|
||||
}
|
||||
if (cloned.inputs[0]) {
|
||||
cloned.inputs[0].label = '';
|
||||
}
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
+164
-147
@@ -1,4 +1,5 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
import { rgthreeConfig } from "./rgthree_config.js";
|
||||
import { fixBadLinks } from "./link_fixer.js";
|
||||
import { wait } from "./shared_utils.js";
|
||||
@@ -60,9 +61,162 @@ class LogSession {
|
||||
}
|
||||
}
|
||||
class Rgthree extends EventTarget {
|
||||
async clearAllMessages() {
|
||||
let container = document.querySelector(".rgthree-top-messages-container");
|
||||
container && (container.innerHTML = "");
|
||||
constructor() {
|
||||
super();
|
||||
this.config = rgthreeConfig;
|
||||
this.api = api;
|
||||
this.ctrlKey = false;
|
||||
this.altKey = false;
|
||||
this.metaKey = false;
|
||||
this.shiftKey = false;
|
||||
this.downKeys = {};
|
||||
this.logger = new LogSession("[rgthree]");
|
||||
this.monitorBadLinksAlerted = false;
|
||||
this.monitorLinkTimeout = null;
|
||||
this.processingQueue = false;
|
||||
this.canvasCurrentlyCopyingToClipboard = false;
|
||||
this.canvasCurrentlyCopyingToClipboardWithMultipleNodes = false;
|
||||
this.initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff = null;
|
||||
window.addEventListener("keydown", (e) => {
|
||||
this.handleKeydown(e);
|
||||
});
|
||||
window.addEventListener("keyup", (e) => {
|
||||
this.handleKeyup(e);
|
||||
});
|
||||
this.initializeGraphAndCanvasHooks();
|
||||
this.initializeComfyUIHooks();
|
||||
wait(100).then(() => {
|
||||
this.injectRgthreeCss();
|
||||
});
|
||||
}
|
||||
async initializeGraphAndCanvasHooks() {
|
||||
const rgthree = this;
|
||||
const [canvas, graph] = await Promise.all([waitForCanvas(), waitForGraph()]);
|
||||
const onSerialize = graph.onSerialize;
|
||||
graph.onSerialize = (data) => {
|
||||
this.initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff = data;
|
||||
onSerialize === null || onSerialize === void 0 ? void 0 : onSerialize.apply(graph, data);
|
||||
};
|
||||
const copyToClipboard = LGraphCanvas.prototype.copyToClipboard;
|
||||
LGraphCanvas.prototype.copyToClipboard = function (nodes) {
|
||||
rgthree.canvasCurrentlyCopyingToClipboard = true;
|
||||
rgthree.canvasCurrentlyCopyingToClipboardWithMultipleNodes =
|
||||
Object.values(nodes || this.selected_nodes || []).length > 1;
|
||||
copyToClipboard.apply(canvas, [...arguments]);
|
||||
rgthree.canvasCurrentlyCopyingToClipboard = false;
|
||||
rgthree.canvasCurrentlyCopyingToClipboardWithMultipleNodes = false;
|
||||
};
|
||||
const onGroupAdd = LGraphCanvas.onGroupAdd;
|
||||
LGraphCanvas.onGroupAdd = function (...args) {
|
||||
onGroupAdd.apply(canvas, [...args]);
|
||||
LGraphCanvas.onShowPropertyEditor({}, null, null, null, graph._groups[graph._groups.length - 1]);
|
||||
};
|
||||
}
|
||||
initializeComfyUIHooks() {
|
||||
const rgthree = this;
|
||||
const queuePrompt = app.queuePrompt;
|
||||
app.queuePrompt = async function () {
|
||||
rgthree.dispatchEvent(new CustomEvent("queue"));
|
||||
rgthree.processingQueue = true;
|
||||
try {
|
||||
await queuePrompt.apply(app, [...arguments]);
|
||||
}
|
||||
finally {
|
||||
rgthree.processingQueue = false;
|
||||
rgthree.dispatchEvent(new CustomEvent("queue-end"));
|
||||
}
|
||||
};
|
||||
const graphToPrompt = app.graphToPrompt;
|
||||
app.graphToPrompt = async function () {
|
||||
rgthree.dispatchEvent(new CustomEvent("graph-to-prompt"));
|
||||
let promise = graphToPrompt.apply(app, [...arguments]);
|
||||
await promise;
|
||||
rgthree.dispatchEvent(new CustomEvent("graph-to-prompt-end"));
|
||||
return promise;
|
||||
};
|
||||
const clean = app.clean;
|
||||
app.clean = function () {
|
||||
rgthree.clearAllMessages();
|
||||
clean && clean.call(app, ...arguments);
|
||||
};
|
||||
const loadGraphData = app.loadGraphData;
|
||||
app.loadGraphData = function (graph) {
|
||||
if (rgthree.monitorLinkTimeout) {
|
||||
clearTimeout(rgthree.monitorLinkTimeout);
|
||||
rgthree.monitorLinkTimeout = null;
|
||||
}
|
||||
rgthree.clearAllMessages();
|
||||
let graphCopy;
|
||||
try {
|
||||
graphCopy = JSON.parse(JSON.stringify(graph));
|
||||
}
|
||||
catch (e) {
|
||||
graphCopy = null;
|
||||
}
|
||||
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 fixBadLinksResult = fixBadLinks(graphToUse);
|
||||
if (fixBadLinksResult.hasBadLinks) {
|
||||
rgthree.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"]) {
|
||||
rgthree.showMessage({
|
||||
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: "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.")) {
|
||||
try {
|
||||
const fixBadLinksResult = fixBadLinks(graphToUse, true);
|
||||
if (!fixBadLinksResult.hasBadLinks) {
|
||||
rgthree.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"]) {
|
||||
rgthree.monitorLinkTimeout = setTimeout(() => {
|
||||
rgthree.monitorBadLinks();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e);
|
||||
alert("Unsuccessful at fixing corrupt data. :(");
|
||||
rgthree.hideMessage("bad-links");
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (rgthreeConfig["monitor_for_corrupt_links"] ||
|
||||
rgthreeConfig["monitor_bad_links"]) {
|
||||
rgthree.monitorLinkTimeout = setTimeout(() => {
|
||||
rgthree.monitorBadLinks();
|
||||
}, 5000);
|
||||
}
|
||||
}, 100);
|
||||
loadGraphData && loadGraphData.call(app, ...arguments);
|
||||
};
|
||||
}
|
||||
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 showMessage(data) {
|
||||
let container = document.querySelector(".rgthree-top-messages-container");
|
||||
@@ -118,6 +272,10 @@ class Rgthree extends EventTarget {
|
||||
}
|
||||
msg && msg.remove();
|
||||
}
|
||||
async clearAllMessages() {
|
||||
let container = document.querySelector(".rgthree-top-messages-container");
|
||||
container && (container.innerHTML = "");
|
||||
}
|
||||
handleKeydown(e) {
|
||||
this.ctrlKey = !!e.ctrlKey;
|
||||
this.altKey = !!e.altKey;
|
||||
@@ -142,147 +300,6 @@ class Rgthree extends EventTarget {
|
||||
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 initializeGraphAndCanvasHooks() {
|
||||
const [canvas, graph] = await Promise.all([waitForCanvas(), waitForGraph()]);
|
||||
const onSerialize = graph.onSerialize;
|
||||
graph.onSerialize = (data) => {
|
||||
this.initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff = data;
|
||||
onSerialize === null || onSerialize === void 0 ? void 0 : onSerialize.apply(graph, data);
|
||||
};
|
||||
const onGroupAdd = LGraphCanvas.onGroupAdd;
|
||||
LGraphCanvas.onGroupAdd = function (...args) {
|
||||
onGroupAdd.apply(canvas, [...args]);
|
||||
LGraphCanvas.onShowPropertyEditor({}, null, null, null, graph._groups[graph._groups.length - 1]);
|
||||
};
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
this.config = rgthreeConfig;
|
||||
this.ctrlKey = false;
|
||||
this.altKey = false;
|
||||
this.metaKey = false;
|
||||
this.shiftKey = false;
|
||||
this.downKeys = {};
|
||||
this.logger = new LogSession("[rgthree]");
|
||||
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.initializeGraphAndCanvasHooks();
|
||||
const that = this;
|
||||
const queuePrompt = app.queuePrompt;
|
||||
app.queuePrompt = async function () {
|
||||
that.dispatchEvent(new CustomEvent("queue"));
|
||||
that.processingQueue = true;
|
||||
try {
|
||||
await queuePrompt.apply(app, [...arguments]);
|
||||
}
|
||||
finally {
|
||||
that.processingQueue = false;
|
||||
that.dispatchEvent(new CustomEvent("queue-end"));
|
||||
}
|
||||
};
|
||||
const graphToPrompt = app.graphToPrompt;
|
||||
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"));
|
||||
return promise;
|
||||
};
|
||||
const clean = app.clean;
|
||||
app.clean = function () {
|
||||
that.clearAllMessages();
|
||||
clean && clean.call(app, ...arguments);
|
||||
};
|
||||
const loadGraphData = app.loadGraphData;
|
||||
app.loadGraphData = function (graph) {
|
||||
if (that.monitorLinkTimeout) {
|
||||
clearTimeout(that.monitorLinkTimeout);
|
||||
that.monitorLinkTimeout = null;
|
||||
}
|
||||
that.clearAllMessages();
|
||||
let graphCopy;
|
||||
try {
|
||||
graphCopy = JSON.parse(JSON.stringify(graph));
|
||||
}
|
||||
catch (e) {
|
||||
graphCopy = null;
|
||||
}
|
||||
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 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.showMessage({
|
||||
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: "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.")) {
|
||||
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.");
|
||||
wasLoadingAborted && app.loadGraphData(fixBadLinksResult.graph);
|
||||
if (rgthreeConfig["monitor_for_corrupt_links"] ||
|
||||
rgthreeConfig["monitor_bad_links"]) {
|
||||
that.monitorLinkTimeout = setTimeout(() => {
|
||||
that.monitorBadLinks();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e);
|
||||
alert("Unsuccessful at fixing corrupt data. :(");
|
||||
that.hideMessage("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();
|
||||
});
|
||||
}
|
||||
injectRgthreeCss() {
|
||||
let link = document.createElement("link");
|
||||
link.rel = "stylesheet";
|
||||
@@ -303,9 +320,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;
|
||||
|
||||
Reference in New Issue
Block a user