diff --git a/src/components/builder/AppBuilder.vue b/src/components/builder/AppBuilder.vue index ef3adf946d..bd2610d22b 100644 --- a/src/components/builder/AppBuilder.vue +++ b/src/components/builder/AppBuilder.vue @@ -28,6 +28,7 @@ import { DOMWidgetImpl } from '@/scripts/domWidget' import { promptRenameWidget } from '@/utils/widgetUtil' import { useAppMode } from '@/composables/useAppMode' import { nodeTypeValidForApp, useAppModeStore } from '@/stores/appModeStore' +import { resolveNodeWidget } from '@/utils/litegraphUtil' import { cn } from '@/utils/tailwindUtil' import { HideLayoutFieldKey } from '@/types/widgetTypes' @@ -102,30 +103,6 @@ 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 diff --git a/src/renderer/extensions/linearMode/LinearControls.vue b/src/renderer/extensions/linearMode/LinearControls.vue index 74bb0b0054..1b235ff51b 100644 --- a/src/renderer/extensions/linearMode/LinearControls.vue +++ b/src/renderer/extensions/linearMode/LinearControls.vue @@ -10,6 +10,7 @@ import ScrubableNumberInput from '@/components/common/ScrubableNumberInput.vue' import Popover from '@/components/ui/Popover.vue' import Button from '@/components/ui/button/Button.vue' import { extractVueNodeData } from '@/composables/graph/useGraphNodeManager' +import { isPromotedWidgetView } from '@/core/graph/subgraph/promotedWidgetTypes' import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import { LGraphEventMode } from '@/lib/litegraph/src/types/globalEnums' import { useBillingContext } from '@/composables/billing/useBillingContext' @@ -29,7 +30,7 @@ import { useQueueSettingsStore } from '@/stores/queueStore' import { cn } from '@/utils/tailwindUtil' import { useAppMode } from '@/composables/useAppMode' import { useAppModeStore } from '@/stores/appModeStore' -import { resolveNode } from '@/utils/litegraphUtil' +import { resolveNodeWidget } from '@/utils/litegraphUtil' const { t } = useI18n() const commandStore = useCommandStore() const executionErrorStore = useExecutionErrorStore() @@ -63,21 +64,41 @@ useEventListener( ) const mappedSelections = computed(() => { - let unprocessedInputs = [...appModeStore.selectedInputs] - //FIXME strict typing here + let unprocessedInputs = appModeStore.selectedInputs.flatMap( + ([nodeId, widgetName]) => { + const [node, widget] = resolveNodeWidget(nodeId, widgetName) + return widget ? ([[node, widget]] as const) : [] + } + ) const processedInputs: ReturnType[] = [] while (unprocessedInputs.length) { - const nodeId = unprocessedInputs[0][0] - const inputGroup = takeWhile( - unprocessedInputs, - ([id]) => id === nodeId - ).map(([, widgetName]) => widgetName) + const [node] = unprocessedInputs[0] + const inputGroup = takeWhile(unprocessedInputs, ([n]) => n === node).map( + ([, widget]) => widget + ) unprocessedInputs = unprocessedInputs.slice(inputGroup.length) - const node = resolveNode(nodeId) + //FIXME: hide widget if owning node bypassed if (node?.mode !== LGraphEventMode.ALWAYS) continue const nodeData = nodeToNodeData(node) - remove(nodeData.widgets ?? [], (w) => !inputGroup.includes(w.name)) + remove(nodeData.widgets ?? [], (vueWidget) => { + if (vueWidget.slotMetadata?.linked) return true + + if (!node.isSubgraphNode()) + return !inputGroup.some((w) => w.name === vueWidget.name) + + const storeNodeId = vueWidget.storeNodeId?.split(':')?.[1] ?? '' + return !inputGroup.some( + (subWidget) => + isPromotedWidgetView(subWidget) && + subWidget.sourceNodeId == storeNodeId && + subWidget.sourceWidgetName === vueWidget.storeName + ) + }) + for (const widget of nodeData.widgets ?? []) { + widget.slotMetadata = undefined + widget.nodeId = String(node.id) + } processedInputs.push(nodeData) } return processedInputs @@ -107,10 +128,6 @@ function getDropIndicator(node: LGraphNode) { function nodeToNodeData(node: LGraphNode) { const dropIndicator = getDropIndicator(node) const nodeData = extractVueNodeData(node) - for (const widget of nodeData.widgets ?? []) { - widget.slotMetadata = undefined - widget.nodeId = String(node.id) - } return { ...nodeData, diff --git a/src/utils/litegraphUtil.ts b/src/utils/litegraphUtil.ts index 2a05397383..65629a8d3e 100644 --- a/src/utils/litegraphUtil.ts +++ b/src/utils/litegraphUtil.ts @@ -1,5 +1,6 @@ import _ from 'es-toolkit/compat' +import { isPromotedWidgetView } from '@/core/graph/subgraph/promotedWidgetTypes' import type { ColorOption, LGraph } from '@/lib/litegraph/src/litegraph' import type { ExecutedWsMessage } from '@/schemas/apiSchema' import { @@ -319,6 +320,31 @@ export function resolveNode( } return undefined } +export function resolveNodeWidget( + nodeId: NodeId, + widgetName?: string, + graph: LGraph = app.rootGraph +): [LGraphNode, IBaseWidget] | [LGraphNode] | [] { + const node = 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 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 [] +} export function isLoad3dNode(node: LGraphNode) { return (