From 71a3abf5180743923748bad16aed86129749bcf2 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Thu, 4 May 2023 11:42:21 -0500 Subject: [PATCH] Auto add new widgets for backend nodes --- litegraph | 2 +- src/lib/ComfyGraph.ts | 92 +++++++++++++++++++++++ src/lib/GraphSync.ts | 11 ++- src/lib/IComfyInputSlot.ts | 6 +- src/lib/api.ts | 4 +- src/lib/components/ComfyApp.svelte | 4 +- src/lib/components/ComfyApp.ts | 80 +++----------------- src/lib/components/WidgetContainer.svelte | 4 +- src/lib/nodes/ComfyWidgetNodes.ts | 5 -- src/lib/stores/layoutState.ts | 31 ++++++-- src/lib/stores/uiState.ts | 2 + src/lib/widgets.ts | 9 ++- src/lib/widgets/ComboWidget.svelte | 1 + 13 files changed, 150 insertions(+), 101 deletions(-) create mode 100644 src/lib/ComfyGraph.ts diff --git a/litegraph b/litegraph index ac3621c..f09cba0 160000 --- a/litegraph +++ b/litegraph @@ -1 +1 @@ -Subproject commit ac3621c4bdb9b5dd7a9f0a4b0b07599613dee42a +Subproject commit f09cba0804836d506c98235fb680ec7024e3e69a diff --git a/src/lib/ComfyGraph.ts b/src/lib/ComfyGraph.ts new file mode 100644 index 0000000..1c7d6eb --- /dev/null +++ b/src/lib/ComfyGraph.ts @@ -0,0 +1,92 @@ +import { LConnectionKind, LGraph, LGraphNode, type INodeSlot, type SlotIndex, LiteGraph, getStaticProperty, type LGraphAddNodeOptions } from "@litegraph-ts/core"; +import GraphSync from "./GraphSync"; +import EventEmitter from "events"; +import type TypedEmitter from "typed-emitter"; +import layoutState from "./stores/layoutState"; +import uiState from "./stores/uiState"; +import { get } from "svelte/store"; +import type ComfyGraphNode from "./nodes/ComfyGraphNode"; +import type IComfyInputSlot from "./IComfyInputSlot"; + +type ComfyGraphEvents = { + configured: (graph: LGraph) => void + nodeAdded: (node: LGraphNode) => void + nodeRemoved: (node: LGraphNode) => void + nodeConnectionChanged: (kind: LConnectionKind, node: LGraphNode, slot: SlotIndex, targetNode: LGraphNode, targetSlot: SlotIndex) => void + cleared: () => void + beforeChange: (graph: LGraph, param: any) => void + afterChange: (graph: LGraph, param: any) => void +} + +export default class ComfyGraph extends LGraph { + graphSync: GraphSync; + eventBus: TypedEmitter = new EventEmitter() as TypedEmitter; + + constructor() { + super(); + this.graphSync = new GraphSync(this) + } + + override onConfigure() { + console.debug("Configured"); + this.eventBus.emit("configured", this); + } + + override onBeforeChange(graph: LGraph, info: any) { + console.debug("BeforeChange", info); + this.eventBus.emit("beforeChange", graph, info); + } + + override onAfterChange(graph: LGraph, info: any) { + console.debug("AfterChange", info); + this.eventBus.emit("afterChange", graph, info); + } + + override onNodeAdded(node: LGraphNode, options: LGraphAddNodeOptions) { + layoutState.nodeAdded(node) + this.graphSync.onNodeAdded(node); + + if ("comfyClass" in node // Is this a comfy node + && !("svelteComponentType" in node) // ...and not also a ComfyWidgetNode + && !options.addedByDeserialize // ...and we're not trying to deserialize an existing workflow + && get(uiState).autoAddUI) { + console.debug("[ComfyGraph] AutoAdd UI") + const comfyNode = node as ComfyGraphNode; + const widgetNodesAdded = [] + for (let index = 0; index < comfyNode.inputs.length; index++) { + const input = comfyNode.inputs[index]; + if ("config" in input) { + const comfyInput = input as IComfyInputSlot; + if (comfyInput.defaultWidgetNode) { + const widgetNode = LiteGraph.createNode(comfyInput.defaultWidgetNode) + const inputPos = comfyNode.getConnectionPos(true, index); + this.add(widgetNode) + widgetNode.connect(0, comfyNode, index); + widgetNode.collapse(); + widgetNode.pos = [inputPos[0] - 140, inputPos[1] + LiteGraph.NODE_SLOT_HEIGHT / 2]; + widgetNodesAdded.push(widgetNode) + } + } + } + const dragItems = widgetNodesAdded.map(wn => get(layoutState).allItemsByNode[wn.id]?.dragItem).filter(di => di) + console.debug("[ComfyGraph] Group new widgets", dragItems) + layoutState.groupItems(dragItems, comfyNode.comfyClass) + } + + console.debug("Added", node); + this.eventBus.emit("nodeAdded", node); + } + + override onNodeRemoved(node: LGraphNode) { + layoutState.nodeRemoved(node); + this.graphSync.onNodeRemoved(node); + + console.debug("Removed", node); + this.eventBus.emit("nodeRemoved", node); + } + + override onNodeConnectionChange(kind: LConnectionKind, node: LGraphNode, slot: SlotIndex, targetNode: LGraphNode, targetSlot: SlotIndex) { + console.debug("ConnectionChange", node); + this.eventBus.emit("nodeConnectionChanged", kind, node, slot, targetNode, targetSlot); + } +} diff --git a/src/lib/GraphSync.ts b/src/lib/GraphSync.ts index 4b18344..9614594 100644 --- a/src/lib/GraphSync.ts +++ b/src/lib/GraphSync.ts @@ -2,6 +2,7 @@ import type { LGraph, LGraphNode } from "@litegraph-ts/core"; import type ComfyApp from "./components/ComfyApp"; import type { Unsubscriber, Writable } from "svelte/store"; import type { ComfyWidgetNode } from "./nodes"; +import type ComfyGraph from "./ComfyGraph"; type WidgetSubStore = { store: WidgetUIStateStore, @@ -28,13 +29,11 @@ export default class GraphSync { // nodeId -> widgetSubStore private stores: Record = {} - constructor(app: ComfyApp) { - this.graph = app.lGraph; - app.eventBus.on("nodeAdded", this.onNodeAdded.bind(this)); - app.eventBus.on("nodeRemoved", this.onNodeRemoved.bind(this)); + constructor(graph: ComfyGraph) { + this.graph = graph; } - private onNodeAdded(node: LGraphNode) { + onNodeAdded(node: LGraphNode) { // TODO assumes only a single graph's widget state. if ("svelteComponentType" in node) { @@ -44,7 +43,7 @@ export default class GraphSync { this.graph.setDirtyCanvas(true, true); } - private onNodeRemoved(node: LGraphNode) { + onNodeRemoved(node: LGraphNode) { if ("svelteComponentType" in node) { this.removeStore(node as ComfyWidgetNode); } diff --git a/src/lib/IComfyInputSlot.ts b/src/lib/IComfyInputSlot.ts index 0765207..23dc382 100644 --- a/src/lib/IComfyInputSlot.ts +++ b/src/lib/IComfyInputSlot.ts @@ -1,4 +1,5 @@ import type { INodeInputSlot } from "@litegraph-ts/core"; +import type { ComfyWidgetNode } from "./nodes"; // TODO generalize export type ComfyInputConfig = { @@ -12,6 +13,7 @@ export type ComfyInputConfig = { } export default interface IComfyInputSlot extends INodeInputSlot { - serialize: boolean - config: ComfyInputConfig // stores range min/max/step, etc. + serialize: boolean, + defaultWidgetNode?: new (name?: string) => ComfyWidgetNode, + config: ComfyInputConfig, // stores range min/max/step, etc. } diff --git a/src/lib/api.ts b/src/lib/api.ts index b7fd412..159d455 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -231,7 +231,7 @@ export default class ComfyAPI extends EventTarget { }; } catch (error) { console.error(error); - return { Running: [], Pending: [] }; + return { Running: [], Pending: [], error }; } } @@ -245,7 +245,7 @@ export default class ComfyAPI extends EventTarget { return { History: Object.values(await res.json()) }; } catch (error) { console.error(error); - return { History: [] }; + return { History: [], error }; } } diff --git a/src/lib/components/ComfyApp.svelte b/src/lib/components/ComfyApp.svelte index 8391532..3eb8191 100644 --- a/src/lib/components/ComfyApp.svelte +++ b/src/lib/components/ComfyApp.svelte @@ -107,9 +107,6 @@ }) } - app.eventBus.on("nodeAdded", layoutState.nodeAdded); - app.eventBus.on("nodeRemoved", layoutState.nodeRemoved); - app.api.addEventListener("status", (ev: CustomEvent) => { queueState.statusUpdated(ev.detail as ComfyAPIStatus); }); @@ -176,6 +173,7 @@ +