diff --git a/src/composables/graph/useSubgraphOperations.ts b/src/composables/graph/useSubgraphOperations.ts index b42cc2ec4..45a665d4b 100644 --- a/src/composables/graph/useSubgraphOperations.ts +++ b/src/composables/graph/useSubgraphOperations.ts @@ -37,6 +37,21 @@ export function useSubgraphOperations() { workflowStore.activeWorkflow?.changeTracker?.checkState() } + const doUnpack = ( + subgraphNodes: SubgraphNode[], + skipMissingNodes: boolean + ) => { + const canvas = canvasStore.getCanvas() + const graph = canvas.subgraph ?? canvas.graph + if (!graph) return + + for (const subgraphNode of subgraphNodes) { + nodeOutputStore.revokeSubgraphPreviews(subgraphNode) + graph.unpackSubgraph(subgraphNode, { skipMissingNodes }) + } + workflowStore.activeWorkflow?.changeTracker?.checkState() + } + const unpackSubgraph = () => { const canvas = canvasStore.getCanvas() const graph = canvas.subgraph ?? canvas.graph @@ -53,17 +68,7 @@ export function useSubgraphOperations() { if (subgraphNodes.length === 0) { return } - - subgraphNodes.forEach((subgraphNode) => { - // Revoke any image previews for the subgraph - nodeOutputStore.revokeSubgraphPreviews(subgraphNode) - - // Unpack the subgraph - graph.unpackSubgraph(subgraphNode) - }) - - // Trigger change tracking - workflowStore.activeWorkflow?.changeTracker?.checkState() + doUnpack(subgraphNodes, true) } const addSubgraphToLibrary = async () => { diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts index 1cbfec909..b582a0b64 100644 --- a/src/composables/useCoreCommands.ts +++ b/src/composables/useCoreCommands.ts @@ -1,6 +1,7 @@ import { useCurrentUser } from '@/composables/auth/useCurrentUser' import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions' import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems' +import { useSubgraphOperations } from '@/composables/graph/useSubgraphOperations' import { useExternalLink } from '@/composables/useExternalLink' import { useModelSelectorDialog } from '@/composables/useModelSelectorDialog' import { @@ -14,7 +15,6 @@ import { LGraphGroup, LGraphNode, LiteGraph, - SubgraphNode } from '@/lib/litegraph/src/litegraph' import type { Point } from '@/lib/litegraph/src/litegraph' import { useAssetBrowserDialog } from '@/platform/assets/composables/useAssetBrowserDialog' @@ -41,7 +41,6 @@ import { useLitegraphService } from '@/services/litegraphService' import type { ComfyCommand } from '@/stores/commandStore' import { useExecutionStore } from '@/stores/executionStore' import { useHelpCenterStore } from '@/stores/helpCenterStore' -import { useNodeOutputStore } from '@/stores/imagePreviewStore' import { useQueueSettingsStore, useQueueStore } from '@/stores/queueStore' import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore' import { useSubgraphStore } from '@/stores/subgraphStore' @@ -1010,14 +1009,8 @@ export function useCoreCommands(): ComfyCommand[] { label: 'Unpack the selected Subgraph', versionAdded: '1.26.3', function: () => { - const canvas = canvasStore.getCanvas() - const graph = canvas.subgraph ?? canvas.graph - if (!graph) throw new TypeError('Canvas has no graph or subgraph set.') - - const subgraphNode = app.canvas.selectedItems.values().next().value - if (!(subgraphNode instanceof SubgraphNode)) return - useNodeOutputStore().revokeSubgraphPreviews(subgraphNode) - graph.unpackSubgraph(subgraphNode) + const { unpackSubgraph } = useSubgraphOperations() + unpackSubgraph() } }, { diff --git a/src/lib/litegraph/src/LGraph.ts b/src/lib/litegraph/src/LGraph.ts index d5d6c1820..478a2a784 100644 --- a/src/lib/litegraph/src/LGraph.ts +++ b/src/lib/litegraph/src/LGraph.ts @@ -1726,9 +1726,15 @@ export class LGraph return { subgraph, node: subgraphNode as SubgraphNode } } - unpackSubgraph(subgraphNode: SubgraphNode) { + unpackSubgraph( + subgraphNode: SubgraphNode, + options?: { skipMissingNodes?: boolean } + ) { if (!(subgraphNode instanceof SubgraphNode)) throw new Error('Can only unpack Subgraph Nodes') + + const skipMissingNodes = options?.skipMissingNodes ?? false + this.beforeChange() //NOTE: Create bounds can not be called on positionables directly as the subgraph is not being displayed and boundingRect is not initialized. //NOTE: NODE_TITLE_HEIGHT is explicitly excluded here @@ -1750,9 +1756,21 @@ export class LGraph const movedNodes = multiClone(subgraphNode.subgraph.nodes) const nodeIdMap = new Map() for (const n_info of movedNodes) { - const node = LiteGraph.createNode(String(n_info.type), n_info.title) + let node = LiteGraph.createNode(String(n_info.type), n_info.title) if (!node) { - throw new Error('Node not found') + if (skipMissingNodes) { + console.warn( + `Cannot unpack node of type "${n_info.type}" - node type not found. Creating placeholder node.` + ) + node = new LGraphNode(n_info.title || n_info.type || 'Missing Node') + node.last_serialization = n_info + node.has_errors = true + node.type = String(n_info.type) + } else { + throw new Error( + `Cannot unpack: node type "${n_info.type}" is not registered` + ) + } } nodeIdMap.set(n_info.id, ++this.last_node_id) diff --git a/src/lib/litegraph/src/subgraph/subgraphUtils.ts b/src/lib/litegraph/src/subgraph/subgraphUtils.ts index 70ffc7a5d..38f74efaa 100644 --- a/src/lib/litegraph/src/subgraph/subgraphUtils.ts +++ b/src/lib/litegraph/src/subgraph/subgraphUtils.ts @@ -221,11 +221,13 @@ export function multiClone(nodes: Iterable): ISerialisedNode[] { const newNode = LiteGraph.createNode(node.type) if (!newNode) { console.warn('Failed to create node', node.type) + const serializedData = structuredClone(node.serialize()) + clonedNodes.push(serializedData) continue } // Must be cloned; litegraph "serialize" is mostly shallow clone - const data = LiteGraph.cloneObject(node.serialize()) + const data = structuredClone(node.serialize()) newNode.configure(data) clonedNodes.push(newNode.serialize()) diff --git a/src/services/litegraphService.ts b/src/services/litegraphService.ts index 7cc451c03..8ad39f84a 100644 --- a/src/services/litegraphService.ts +++ b/src/services/litegraphService.ts @@ -2,6 +2,7 @@ import _ from 'es-toolkit/compat' import { downloadFile } from '@/base/common/downloadUtil' import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems' +import { useSubgraphOperations } from '@/composables/graph/useSubgraphOperations' import { useNodeAnimatedImage } from '@/composables/node/useNodeAnimatedImage' import { useNodeCanvasImagePreview } from '@/composables/node/useNodeCanvasImagePreview' import { useNodeImage, useNodeVideo } from '@/composables/node/useNodeImage' @@ -661,8 +662,8 @@ export const useLitegraphService = () => { { content: 'Unpack Subgraph', callback: () => { - useNodeOutputStore().revokeSubgraphPreviews(this) - this.graph.unpackSubgraph(this) + const { unpackSubgraph } = useSubgraphOperations() + unpackSubgraph() } } )