From 2a8b76b1f4c2c35976fc3e20d1a3665bc5811608 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Wed, 17 May 2023 13:40:17 -0500 Subject: [PATCH] More subgraph clone fixes --- litegraph | 2 +- src/lib/ComfyGraph.ts | 26 +++++++++++----- src/lib/stores/layoutState.ts | 57 ++++++++++++++++++++++++++++++----- 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/litegraph b/litegraph index 06db1ba..97895e1 160000 --- a/litegraph +++ b/litegraph @@ -1 +1 @@ -Subproject commit 06db1ba07907bc1f2701610c7006f4867728c991 +Subproject commit 97895e1103c44cb801a9c2d6eee6fd07f617f617 diff --git a/src/lib/ComfyGraph.ts b/src/lib/ComfyGraph.ts index 19c9e61..f95931e 100644 --- a/src/lib/ComfyGraph.ts +++ b/src/lib/ComfyGraph.ts @@ -47,15 +47,27 @@ export default class ComfyGraph extends LGraph { * then options.subgraphsh will have the list of subgraphs down the chain. */ override onNodeAdded(node: LGraphNode, options: LGraphAddNodeOptions) { - // Don't add nodes in subgraphs + // Don't add nodes in subgraphs until this callback reaches the root + // graph if (node.getRootGraph() == null || this._is_subgraph) return; + this.doAddNode(node, options); + + // console.debug("Added", node); + this.eventBus.emit("nodeAdded", node); + } + + /* + * Add widget UI/groups for newly added nodes. + */ + private doAddNode(node: LGraphNode, options: LGraphAddNodeOptions) { layoutState.nodeAdded(node, options) // All nodes whether they come from base litegraph or ComfyBox should // have tags added to them. Can't override serialization for existing - // node types to add `tags` as anew field so putting it in properties is better. + // node types to add `tags` as a new field so putting it in properties + // is better. if (node.properties.tags == null) node.properties.tags = [] @@ -127,16 +139,14 @@ export default class ComfyGraph extends LGraph { } } - // Handle subgraphs being attached + // Handle nodes in subgraphs being attached to this graph indirectly + // ************** RECURSION ALERT ! ************** if (node.is(Subgraph)) { - console.error("ISSUBGRAPH") for (const child of node.subgraph.iterateNodesInOrder()) { - this.onNodeAdded(child, options) + this.doAddNode(child, options) } } - - // console.debug("Added", node); - this.eventBus.emit("nodeAdded", node); + // ************** RECURSION ALERT ! ************** } override onNodeRemoved(node: LGraphNode, options: LGraphRemoveNodeOptions) { diff --git a/src/lib/stores/layoutState.ts b/src/lib/stores/layoutState.ts index 1fc131a..35b58bb 100644 --- a/src/lib/stores/layoutState.ts +++ b/src/lib/stores/layoutState.ts @@ -1,12 +1,15 @@ import { get, writable } from 'svelte/store'; import type { Writable } from 'svelte/store'; import type ComfyApp from "$lib/components/ComfyApp" -import { type LGraphNode, type IWidget, type LGraph, NodeMode, type LGraphRemoveNodeOptions, type LGraphAddNodeOptions, type UUID, type NodeID, LiteGraph } from "@litegraph-ts/core" +import { type LGraphNode, type IWidget, type LGraph, NodeMode, type LGraphRemoveNodeOptions, type LGraphAddNodeOptions, type UUID, type NodeID, LiteGraph, type GraphIDMapping } from "@litegraph-ts/core" import { SHADOW_PLACEHOLDER_ITEM_ID } from 'svelte-dnd-action'; -import type { ComfyWidgetNode } from '$lib/nodes'; import type { ComfyNodeID } from '$lib/api'; import { v4 as uuidv4 } from "uuid"; -import IComfyInputSlot from '$lib/IComfyInputSlot'; +import type { ComfyWidgetNode } from '$lib/nodes/widgets'; + +function isComfyWidgetNode(node: LGraphNode): node is ComfyWidgetNode { + return "svelteComponentType" in node +} type DragItemEntry = { /* @@ -836,24 +839,62 @@ function removeEntry(state: LayoutState, id: DragItemID) { } function nodeAdded(node: LGraphNode, options: LGraphAddNodeOptions) { + // Only concern ourselves with widget nodes + if (!isComfyWidgetNode(node)) + return; + const state = get(store) if (state.isConfiguring) return; + let attrs: Partial = {} + if (options.addedBy === "moveIntoSubgraph" || options.addedBy === "moveOutOfSubgraph") { // All we need to do is update the nodeID linked to this node. - const item = state.allItemsByNode[options.prevNodeId] - delete state.allItemsByNode[options.prevNodeId] + const item = state.allItemsByNode[options.prevNodeID] + delete state.allItemsByNode[options.prevNodeID] state.allItemsByNode[node.id] = item return; } + else if ((options.addedBy === "cloneSelection" || options.addedBy === "paste") && options.prevNodeID != null) { + console.warn("WASCLONED", options.addedBy, options.prevNodeID, Object.keys(state.allItemsByNode), options.prevNode, options.subgraphs) + // Grab layout state information and clone it too. + + let prevWidget = state.allItemsByNode[node.id] + if (prevWidget == null) { + // If a subgraph was cloned, try looking for the original widget node corresponding to the new widget node being added. + // `node` is the new ComfyWidgetNode instance to copy attrs to. + // `options.cloneData` should contain the results of Subgraph.clone(), called "subgraphNewIDMapping". + // `options.cloneData` is attached to the onNodeAdded options if a node is added to a graph after being + // selection-cloned or pasted, as they both call clone() internally. + const cloneData = options.cloneData.forNode[options.prevNodeID] + if (cloneData && cloneData.subgraphNewIDMapping != null) { + // At this point we know options.prevNodeID points to a subgraph. + const mapping = cloneData.subgraphNewIDMapping as GraphIDMapping + + // This mapping is two-way, so oldID -> newID *and* newID -> oldID are supported. + // Take the cloned node's ID and look up what the original node's ID was. + const nodeIDInLayoutState = mapping.nodeIDs[node.id]; + + if (nodeIDInLayoutState) { + // Gottem. + prevWidget = state.allItemsByNode[nodeIDInLayoutState] + console.warn("FOUND CLONED SUBGRAPH NODE", node.id, "=>", nodeIDInLayoutState, prevWidget) + } + } + } + + if (prevWidget) { + console.warn("FOUND", prevWidget.dragItem.attrs) + // XXX: Will this work for most properties? + attrs = structuredClone(prevWidget.dragItem.attrs) + } + } const parent = findDefaultContainerForInsertion(); console.debug("[layoutState] nodeAdded", node.id) - if ("svelteComponentType" in node) { - addWidget(parent, node as ComfyWidgetNode); - } + addWidget(parent, node, attrs); } function nodeRemoved(node: LGraphNode, options: LGraphRemoveNodeOptions) {