From 77d49a67ddd56305e153e9f3be4bef18b5a026d1 Mon Sep 17 00:00:00 2001 From: Austin Mroz Date: Mon, 9 Mar 2026 09:11:50 -0700 Subject: [PATCH 1/3] Always use interior nodeId --- src/components/builder/AppBuilder.vue | 39 +++++++++++++++---- .../extensions/linearMode/LinearControls.vue | 6 ++- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/components/builder/AppBuilder.vue b/src/components/builder/AppBuilder.vue index 9a2c5ed340d..fd582d29776 100644 --- a/src/components/builder/AppBuilder.vue +++ b/src/components/builder/AppBuilder.vue @@ -8,6 +8,7 @@ import DraggableList from '@/components/common/DraggableList.vue' import IoItem from '@/components/builder/IoItem.vue' import PropertiesAccordionItem from '@/components/rightSidePanel/layout/PropertiesAccordionItem.vue' import WidgetItem from '@/components/rightSidePanel/parameters/WidgetItem.vue' +import { isPromotedWidgetView } from '@/core/graph/subgraph/promotedWidgetTypes' import { LiteGraph } from '@/lib/litegraph/src/litegraph' import type { LGraphNode, NodeId } from '@/lib/litegraph/src/LGraphNode' import type { LGraphCanvas } from '@/lib/litegraph/src/LGraphCanvas' @@ -52,10 +53,8 @@ workflowStore.activeWorkflow?.changeTracker?.reset() const arrangeInputs = computed(() => appModeStore.selectedInputs .map(([nodeId, widgetName]) => { - const node = resolveNode(nodeId) - if (!node) return null - const widget = node.widgets?.find((w) => w.name === widgetName) - return { nodeId, widgetName, node, widget } + const [node, widget] = resolveNodeWidget(nodeId, widgetName) + return node ? { nodeId, widgetName, node, widget } : null }) .filter((item): item is NonNullable => item !== null) ) @@ -105,10 +104,34 @@ function getHovered( if (widget || node.constructor.nodeData?.output_node) return [node, widget] } +function resolveNodeWidget( + nodeId: NodeId, + widgetName?: string +): [LGraphNode, IBaseWidget] | [LGraphNode] | [] { + const node = app.graph.getNodeById(nodeId) + if (!widgetName) return node ? [node] : [] + if (node) { + const widget = node.widgets?.find((w) => w.name === widgetName) + return widget ? [node, widget] : [] + } + + for (const node of app.graph.nodes) { + if (!node.isSubgraphNode()) continue + const widget = node.widgets?.find( + (w) => + isPromotedWidgetView(w) && + w.sourceWidgetName === widgetName && + w.sourceNodeId === nodeId + ) + if (widget) return [node, widget] + } + + return [] +} function getBounding(nodeId: NodeId, widgetName?: string) { if (settingStore.get('Comfy.VueNodes.Enabled')) return undefined - const node = app.rootGraph.getNodeById(nodeId) + const [node, widget] = resolveNodeWidget(nodeId, widgetName) if (!node) return const titleOffset = @@ -121,7 +144,6 @@ function getBounding(nodeId: NodeId, widgetName?: string) { left: `${node.pos[0]}px`, top: `${node.pos[1] - titleOffset}px` } - const widget = node.widgets?.find((w) => w.name === widgetName) if (!widget) return const margin = widget instanceof DOMWidgetImpl ? widget.margin : undefined @@ -162,10 +184,11 @@ function handleClick(e: MouseEvent) { } if (!isSelectInputsMode.value) return + const widgetId = isPromotedWidgetView(widget) ? widget.sourceNodeId : node.id const index = appModeStore.selectedInputs.findIndex( - ([nodeId, widgetName]) => node.id == nodeId && widget.name === widgetName + ([nodeId, widgetName]) => widgetId == nodeId && widget.name === widgetName ) - if (index === -1) appModeStore.selectedInputs.push([node.id, widget.name]) + if (index === -1) appModeStore.selectedInputs.push([widgetId, widget.name]) else appModeStore.selectedInputs.splice(index, 1) } diff --git a/src/renderer/extensions/linearMode/LinearControls.vue b/src/renderer/extensions/linearMode/LinearControls.vue index 9dafd9a8f88..74bb0b0054f 100644 --- a/src/renderer/extensions/linearMode/LinearControls.vue +++ b/src/renderer/extensions/linearMode/LinearControls.vue @@ -107,8 +107,10 @@ function getDropIndicator(node: LGraphNode) { function nodeToNodeData(node: LGraphNode) { const dropIndicator = getDropIndicator(node) const nodeData = extractVueNodeData(node) - remove(nodeData.widgets ?? [], (w) => w.slotMetadata?.linked ?? false) - for (const widget of nodeData.widgets ?? []) widget.slotMetadata = undefined + for (const widget of nodeData.widgets ?? []) { + widget.slotMetadata = undefined + widget.nodeId = String(node.id) + } return { ...nodeData, From 19c4709cd939b1a18985530044438e2c01380841 Mon Sep 17 00:00:00 2001 From: Austin Mroz Date: Mon, 9 Mar 2026 09:42:51 -0700 Subject: [PATCH 2/3] Use resolveNodeWidget for builder IOItem display --- src/components/builder/AppBuilder.vue | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/builder/AppBuilder.vue b/src/components/builder/AppBuilder.vue index fd582d29776..040670c1c1a 100644 --- a/src/components/builder/AppBuilder.vue +++ b/src/components/builder/AppBuilder.vue @@ -28,7 +28,6 @@ import { DOMWidgetImpl } from '@/scripts/domWidget' import { promptRenameWidget } from '@/utils/widgetUtil' import { useAppMode } from '@/composables/useAppMode' import { nodeTypeValidForApp, useAppModeStore } from '@/stores/appModeStore' -import { resolveNode } from '@/utils/litegraphUtil' import { cn } from '@/utils/tailwindUtil' import { HideLayoutFieldKey } from '@/types/widgetTypes' @@ -61,8 +60,7 @@ const arrangeInputs = computed(() => const inputsWithState = computed(() => appModeStore.selectedInputs.map(([nodeId, widgetName]) => { - const node = resolveNode(nodeId) - const widget = node?.widgets?.find((w) => w.name === widgetName) + const [node, widget] = resolveNodeWidget(nodeId, widgetName) if (!node || !widget) { return { nodeId, From 9f9c154e6d229ed223b0de6cccb4ad67d934d522 Mon Sep 17 00:00:00 2001 From: Austin Mroz Date: Mon, 9 Mar 2026 11:17:06 -0700 Subject: [PATCH 3/3] Resolve storeName for promotedWidgets --- src/components/builder/AppBuilder.vue | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/builder/AppBuilder.vue b/src/components/builder/AppBuilder.vue index 040670c1c1a..472b38e0224 100644 --- a/src/components/builder/AppBuilder.vue +++ b/src/components/builder/AppBuilder.vue @@ -182,11 +182,14 @@ function handleClick(e: MouseEvent) { } if (!isSelectInputsMode.value) return - const widgetId = isPromotedWidgetView(widget) ? widget.sourceNodeId : node.id + const storeId = isPromotedWidgetView(widget) ? widget.sourceNodeId : node.id + const storeName = isPromotedWidgetView(widget) + ? widget.sourceWidgetName + : widget.name const index = appModeStore.selectedInputs.findIndex( - ([nodeId, widgetName]) => widgetId == nodeId && widget.name === widgetName + ([nodeId, widgetName]) => storeId == nodeId && storeName === widgetName ) - if (index === -1) appModeStore.selectedInputs.push([widgetId, widget.name]) + if (index === -1) appModeStore.selectedInputs.push([storeId, storeName]) else appModeStore.selectedInputs.splice(index, 1) }