1+ import type { LGraph , Subgraph } from '@comfyorg/litegraph'
12import { defineStore } from 'pinia'
23import { computed , ref } from 'vue'
34
@@ -22,7 +23,7 @@ import type {
2223import { api } from '@/scripts/api'
2324
2425import { useCanvasStore } from './graphStore'
25- import { ComfyWorkflow } from './workflowStore'
26+ import { ComfyWorkflow , useWorkflowStore } from './workflowStore'
2627
2728export interface QueuedPrompt {
2829 /**
@@ -37,6 +38,7 @@ export interface QueuedPrompt {
3738}
3839
3940export const useExecutionStore = defineStore ( 'execution' , ( ) => {
41+ const workflowStore = useWorkflowStore ( )
4042 const canvasStore = useCanvasStore ( )
4143
4244 const clientId = ref < string | null > ( null )
@@ -61,6 +63,59 @@ export const useExecutionStore = defineStore('execution', () => {
6163 )
6264 } )
6365
66+ const subgraphNodeIdToSubgraph = ( id : string , graph : LGraph | Subgraph ) => {
67+ const node = graph . getNodeById ( id )
68+ if ( node ?. isSubgraphNode ( ) ) return node . subgraph
69+ }
70+
71+ /**
72+ * Recursively get the subgraph objects for the given subgraph instance IDs
73+ * @param currentGraph The current graph
74+ * @param subgraphNodeIds The instance IDs
75+ * @param subgraphs The subgraphs
76+ * @returns The subgraphs that correspond to each of the instance IDs.
77+ */
78+ const getSubgraphsFromInstanceIds = (
79+ currentGraph : LGraph | Subgraph ,
80+ subgraphNodeIds : string [ ] ,
81+ subgraphs : Subgraph [ ] = [ ]
82+ ) : Subgraph [ ] => {
83+ // Last segment is the node portion; nothing to do.
84+ if ( subgraphNodeIds . length === 1 ) return subgraphs
85+
86+ const currentPart = subgraphNodeIds . shift ( )
87+ if ( currentPart === undefined ) return subgraphs
88+
89+ const subgraph = subgraphNodeIdToSubgraph ( currentPart , currentGraph )
90+ if ( ! subgraph ) throw new Error ( `Subgraph not found: ${ currentPart } ` )
91+
92+ subgraphs . push ( subgraph )
93+ return getSubgraphsFromInstanceIds ( subgraph , subgraphNodeIds , subgraphs )
94+ }
95+
96+ const executionIdToCurrentId = ( id : string ) => {
97+ const subgraph = workflowStore . activeSubgraph
98+
99+ // Short-circuit: ID belongs to the parent workflow / no active subgraph
100+ if ( ! id . includes ( ':' ) ) {
101+ return ! subgraph ? id : undefined
102+ } else if ( ! subgraph ) {
103+ return
104+ }
105+
106+ // Parse the hierarchical ID (e.g., "123:456:789")
107+ const subgraphNodeIds = id . split ( ':' )
108+
109+ // If the last subgraph is the active subgraph, return the node ID
110+ const subgraphs = getSubgraphsFromInstanceIds (
111+ subgraph . rootGraph ,
112+ subgraphNodeIds
113+ )
114+ if ( subgraphs . at ( - 1 ) === subgraph ) {
115+ return subgraphNodeIds . at ( - 1 )
116+ }
117+ }
118+
64119 // This is the progress of the currently executing node, if any
65120 const _executingNodeProgress = ref < ProgressWsMessage | null > ( null )
66121 const executingNodeProgress = computed ( ( ) =>
@@ -167,12 +222,16 @@ export const useExecutionStore = defineStore('execution', () => {
167222 // Seems sometimes nodes that are cached fire executing but not executed
168223 activePrompt . value . nodes [ executingNodeId . value ] = true
169224 }
170- executingNodeId . value = e . detail
171- if ( executingNodeId . value === null ) {
172- if ( activePromptId . value ) {
173- delete queuedPrompts . value [ activePromptId . value ]
225+ if ( typeof e . detail === 'string' ) {
226+ executingNodeId . value = executionIdToCurrentId ( e . detail ) ?? null
227+ } else {
228+ executingNodeId . value = e . detail
229+ if ( executingNodeId . value === null ) {
230+ if ( activePromptId . value ) {
231+ delete queuedPrompts . value [ activePromptId . value ]
232+ }
233+ activePromptId . value = null
174234 }
175- activePromptId . value = null
176235 }
177236 }
178237
@@ -193,19 +252,31 @@ export const useExecutionStore = defineStore('execution', () => {
193252 lastExecutionError . value = e . detail
194253 }
195254
255+ function getNodeIdIfExecuting ( nodeId : string | number ) {
256+ const nodeIdStr = String ( nodeId )
257+ return nodeIdStr . includes ( ':' )
258+ ? workflowStore . executionIdToCurrentId ( nodeIdStr )
259+ : nodeIdStr
260+ }
261+
196262 function handleProgressText ( e : CustomEvent < ProgressTextWsMessage > ) {
197263 const { nodeId, text } = e . detail
198264 if ( ! text || ! nodeId ) return
199265
200- const node = canvasStore . getCanvas ( ) . graph ?. getNodeById ( nodeId )
266+ // Handle hierarchical node IDs for subgraphs
267+ const currentId = getNodeIdIfExecuting ( nodeId )
268+ const node = canvasStore . getCanvas ( ) . graph ?. getNodeById ( currentId )
201269 if ( ! node ) return
202270
203271 useNodeProgressText ( ) . showTextPreview ( node , text )
204272 }
205273
206274 function handleDisplayComponent ( e : CustomEvent < DisplayComponentWsMessage > ) {
207- const { node_id, component, props = { } } = e . detail
208- const node = canvasStore . getCanvas ( ) . graph ?. getNodeById ( node_id )
275+ const { node_id : nodeId , component, props = { } } = e . detail
276+
277+ // Handle hierarchical node IDs for subgraphs
278+ const currentId = getNodeIdIfExecuting ( nodeId )
279+ const node = canvasStore . getCanvas ( ) . graph ?. getNodeById ( currentId )
209280 if ( ! node ) return
210281
211282 if ( component === 'ChatHistoryWidget' ) {
0 commit comments