Merge pull request #74 from space-nuko/fix-graph-input-output-tags
Support for subgraph actions/events + switch node
This commit is contained in:
Submodule litegraph updated: d335948703...ffc140dd03
@@ -3,7 +3,7 @@
|
|||||||
"comfyUIPort": 8188,
|
"comfyUIPort": 8188,
|
||||||
"alwaysStripUserState": false,
|
"alwaysStripUserState": false,
|
||||||
"promptForWorkflowName": false,
|
"promptForWorkflowName": false,
|
||||||
"confirmWhenUnloadingUnsavedChanges": true,
|
"confirmWhenUnloadingUnsavedChanges": false,
|
||||||
"builtInTemplates": ["ControlNet", "LoRA x5", "Model Loader", "Positive_Negative", "Seed Randomizer"],
|
"builtInTemplates": ["ControlNet", "LoRA x5", "Model Loader", "Positive_Negative", "Seed Randomizer"],
|
||||||
"cacheBuiltInResources": true
|
"cacheBuiltInResources": true
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1020,7 +1020,7 @@ export default class ComfyApp {
|
|||||||
try {
|
try {
|
||||||
const response = await this.api.queuePrompt(request);
|
const response = await this.api.queuePrompt(request);
|
||||||
if (response.error != null) {
|
if (response.error != null) {
|
||||||
error = response.error;
|
error = response;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
queueState.afterQueued(workflow.id, response.promptID, num, p.output, extraData)
|
queueState.afterQueued(workflow.id, response.promptID, num, p.output, extraData)
|
||||||
@@ -1030,7 +1030,7 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
const mes: string = error;
|
const mes: any = error;
|
||||||
notify(`Error queuing prompt: \n${mes} `, { type: "error" })
|
notify(`Error queuing prompt: \n${mes} `, { type: "error" })
|
||||||
console.error(graphToGraphVis(workflow.graph))
|
console.error(graphToGraphVis(workflow.graph))
|
||||||
console.error(promptToGraphVis(p))
|
console.error(promptToGraphVis(p))
|
||||||
|
|||||||
@@ -103,6 +103,16 @@ export default class ComfyGraphNode extends LGraphNode {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Traverses this node backwards in the graph in order to determine the type
|
||||||
|
* for slot type inheritance. This is used if there isn't a valid upstream
|
||||||
|
* link but the output type can be inferred otherwise (for example from
|
||||||
|
* properties or other connected inputs)
|
||||||
|
*/
|
||||||
|
getUpstreamLinkForInheritedType(): LLink | null {
|
||||||
|
return this.getUpstreamLink();
|
||||||
|
}
|
||||||
|
|
||||||
get layoutState(): WritableLayoutStateStore | null {
|
get layoutState(): WritableLayoutStateStore | null {
|
||||||
return layoutStates.getLayoutByNode(this);
|
return layoutStates.getLayoutByNode(this);
|
||||||
}
|
}
|
||||||
@@ -151,7 +161,7 @@ export default class ComfyGraphNode extends LGraphNode {
|
|||||||
|
|
||||||
while (currentNode) {
|
while (currentNode) {
|
||||||
updateNodes.unshift(currentNode);
|
updateNodes.unshift(currentNode);
|
||||||
const link = currentNode.getUpstreamLink();
|
const link = currentNode.getUpstreamLinkForInheritedType();
|
||||||
if (link !== null) {
|
if (link !== null) {
|
||||||
const node = this.graph.getNodeById(link.origin_id) as ComfyGraphNode;
|
const node = this.graph.getNodeById(link.origin_id) as ComfyGraphNode;
|
||||||
if (node.canInheritSlotTypes) {
|
if (node.canInheritSlotTypes) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { BuiltInSlotType, LiteGraph, NodeMode, type INodeInputSlot, type SlotLayout, type INodeOutputSlot, LLink, LConnectionKind, type ITextWidget, type SerializedLGraphNode, type IComboWidget } from "@litegraph-ts/core";
|
import { BuiltInSlotType, LiteGraph, NodeMode, type INodeInputSlot, type SlotLayout, type INodeOutputSlot, LLink, LConnectionKind, type ITextWidget, type SerializedLGraphNode, type IComboWidget } from "@litegraph-ts/core";
|
||||||
import ComfyGraphNode, { type ComfyGraphNodeProperties } from "./ComfyGraphNode";
|
import ComfyGraphNode, { type ComfyGraphNodeProperties } from "./ComfyGraphNode";
|
||||||
import { Watch } from "@litegraph-ts/nodes-basic";
|
import { Watch } from "@litegraph-ts/nodes-basic";
|
||||||
|
import { nextLetter } from "$lib/utils";
|
||||||
|
|
||||||
export type PickFirstMode = "anyActiveLink" | "truthy" | "dataNonNull"
|
export type PickFirstMode = "anyActiveLink" | "truthy" | "dataNonNull"
|
||||||
|
|
||||||
@@ -8,17 +9,6 @@ export interface ComfyPickFirstNodeProperties extends ComfyGraphNodeProperties {
|
|||||||
mode: PickFirstMode
|
mode: PickFirstMode
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextLetter(s: string): string {
|
|
||||||
return s.replace(/([a-zA-Z])[^a-zA-Z]*$/, function(a) {
|
|
||||||
var c = a.charCodeAt(0);
|
|
||||||
switch (c) {
|
|
||||||
case 90: return 'A';
|
|
||||||
case 122: return 'a';
|
|
||||||
default: return String.fromCharCode(++c);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class ComfyPickFirstNode extends ComfyGraphNode {
|
export default class ComfyPickFirstNode extends ComfyGraphNode {
|
||||||
override properties: ComfyPickFirstNodeProperties = {
|
override properties: ComfyPickFirstNodeProperties = {
|
||||||
tags: [],
|
tags: [],
|
||||||
|
|||||||
147
src/lib/nodes/ComfySwitch.ts
Normal file
147
src/lib/nodes/ComfySwitch.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import { nextLetter } from "$lib/utils";
|
||||||
|
import { LConnectionKind, LLink, LiteGraph, type INodeInputSlot, type INodeOutputSlot, type SlotLayout } from "@litegraph-ts/core";
|
||||||
|
import ComfyGraphNode from "./ComfyGraphNode";
|
||||||
|
|
||||||
|
export default class ComfySwitch extends ComfyGraphNode {
|
||||||
|
static slotLayout: SlotLayout = {
|
||||||
|
inputs: [
|
||||||
|
{ name: "A_value", type: "*" },
|
||||||
|
{ name: "A_cond", type: "boolean" },
|
||||||
|
],
|
||||||
|
outputs: [
|
||||||
|
{ name: "out", type: "*" }
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
override canInheritSlotTypes = true;
|
||||||
|
|
||||||
|
private _selected: number | null = null;
|
||||||
|
|
||||||
|
constructor(title?: string) {
|
||||||
|
super(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
override getUpstreamLinkForInheritedType(): LLink | null {
|
||||||
|
for (let index = 0; index < this.inputs.length / 2; index++) {
|
||||||
|
const link = this.getInputLink(index * 2);
|
||||||
|
if (link != null)
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getUpstreamLink(): LLink | null {
|
||||||
|
const selected = this.getSelected();
|
||||||
|
if (selected == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return this.getInputLink(selected * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelected(): number | null {
|
||||||
|
for (let i = 0; i < this.inputs.length / 2; i++) {
|
||||||
|
if (this.getInputData(i * 2 + 1) == true)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
override onDrawBackground(ctx: CanvasRenderingContext2D) {
|
||||||
|
if (this.flags.collapsed || this._selected == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx.fillStyle = "#AFB";
|
||||||
|
var y = this._selected * 2 * LiteGraph.NODE_SLOT_HEIGHT + 6;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(30 + 50, y);
|
||||||
|
ctx.lineTo(30 + 50, y + LiteGraph.NODE_SLOT_HEIGHT);
|
||||||
|
ctx.lineTo(30 + 34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5);
|
||||||
|
ctx.fill();
|
||||||
|
};
|
||||||
|
|
||||||
|
override onExecute() {
|
||||||
|
this._selected = this.getSelected();
|
||||||
|
var sel = this._selected
|
||||||
|
|
||||||
|
if (sel == null || sel.constructor !== Number) {
|
||||||
|
this.setOutputData(0, null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var v = this.getInputData(sel * 2);
|
||||||
|
if (v !== undefined) {
|
||||||
|
this.setOutputData(0, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasActiveSlots(pairIndex: number): boolean {
|
||||||
|
const slotValue = this.inputs[pairIndex * 2]
|
||||||
|
const slotCond = this.inputs[pairIndex * 2 + 1];
|
||||||
|
return slotValue && slotCond && (slotValue.link != null || slotCond.link != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
override onConnectionsChange(
|
||||||
|
type: LConnectionKind,
|
||||||
|
slotIndex: number,
|
||||||
|
isConnected: boolean,
|
||||||
|
link: LLink,
|
||||||
|
ioSlot: (INodeInputSlot | INodeOutputSlot)
|
||||||
|
) {
|
||||||
|
super.onConnectionsChange(type, slotIndex, isConnected, link, ioSlot);
|
||||||
|
|
||||||
|
if (type !== LConnectionKind.INPUT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const lastPairIdx = Math.floor((this.inputs.length / 2) - 1);
|
||||||
|
|
||||||
|
let newlyConnected = false;
|
||||||
|
if (isConnected) {
|
||||||
|
newlyConnected = this.hasActiveSlots(lastPairIdx)
|
||||||
|
}
|
||||||
|
let newlyDisconnected = false;
|
||||||
|
if (!isConnected) {
|
||||||
|
newlyDisconnected = !this.hasActiveSlots(lastPairIdx) && !this.hasActiveSlots(lastPairIdx - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error("CONNCHANGE", lastPairIdx, this.hasActiveSlots(lastPairIdx), isConnected, slotIndex, this.inputs.length, newlyConnected, newlyDisconnected);
|
||||||
|
|
||||||
|
if (newlyConnected) {
|
||||||
|
if (link != null) {
|
||||||
|
// Add new inputs
|
||||||
|
const lastInputName = this.inputs[this.inputs.length - 1].name
|
||||||
|
const inputName = nextLetter(lastInputName.split("_")[0]);
|
||||||
|
this.addInput(`${inputName}_value`, this.inputs[0].type)
|
||||||
|
this.addInput(`${inputName}_cond`, "boolean")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (newlyDisconnected) {
|
||||||
|
// Remove empty inputs
|
||||||
|
for (let i = this.inputs.length / 2; i > 0; i -= 1) {
|
||||||
|
if (i <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!this.hasActiveSlots(i - 1)) {
|
||||||
|
this.removeInput(i * 2)
|
||||||
|
this.removeInput(i * 2)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = "A"
|
||||||
|
for (let i = 0; i < this.inputs.length; i += 2) {
|
||||||
|
this.inputs[i].name = `${name}_value`;
|
||||||
|
this.inputs[i + 1].name = `${name}_cond`
|
||||||
|
name = nextLetter(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LiteGraph.registerNodeType({
|
||||||
|
class: ComfySwitch,
|
||||||
|
title: "Comfy.Switch",
|
||||||
|
desc: "Selects an output if its condition is true, if none match returns null",
|
||||||
|
type: "utils/switch"
|
||||||
|
})
|
||||||
@@ -2,6 +2,7 @@ export { default as ComfyReroute } from "./ComfyReroute"
|
|||||||
export { default as ComfyPickFirstNode } from "./ComfyPickFirstNode"
|
export { default as ComfyPickFirstNode } from "./ComfyPickFirstNode"
|
||||||
export { default as ComfyValueControl } from "./ComfyValueControl"
|
export { default as ComfyValueControl } from "./ComfyValueControl"
|
||||||
export { default as ComfySelector } from "./ComfySelector"
|
export { default as ComfySelector } from "./ComfySelector"
|
||||||
|
export { default as ComfySwitch } from "./ComfySwitch"
|
||||||
export { default as ComfyTriggerNewEventNode } from "./ComfyTriggerNewEventNode"
|
export { default as ComfyTriggerNewEventNode } from "./ComfyTriggerNewEventNode"
|
||||||
export { default as ComfyConfigureQueuePromptButton } from "./ComfyConfigureQueuePromptButton"
|
export { default as ComfyConfigureQueuePromptButton } from "./ComfyConfigureQueuePromptButton"
|
||||||
export { default as ComfyPickImageNode } from "./ComfyPickImageNode"
|
export { default as ComfyPickImageNode } from "./ComfyPickImageNode"
|
||||||
|
|||||||
@@ -610,3 +610,15 @@ export async function readFileToText(file: File): Promise<string> {
|
|||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function nextLetter(s: string): string {
|
||||||
|
return s.replace(/([a-zA-Z])[^a-zA-Z]*$/, function(a) {
|
||||||
|
var c = a.charCodeAt(0);
|
||||||
|
switch (c) {
|
||||||
|
case 90: return 'A';
|
||||||
|
case 122: return 'a';
|
||||||
|
default: return String.fromCharCode(++c);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user