mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-26 01:09:46 +00:00
Restore hiding of linked inputs in app mode (#9671)
As a temporary fix for widgets being incorrectly hidden, #9669 allowed all disabled widgets to be displayed. This PR provides a more robust implementation to derive whether the widget, as would be displayed from the root graph, is disabled. Potential regression: - Drag drop handlers are applied on node, not widgets. A subgraph containing a "Load Image" node, does not allow dragging and dropping an image onto the subgraphNode in order to load it. Because app mode widgets would display from the original owning node prior to this PR, these drag/drop handlers would apply. Placing "Load Image" nodes. I believe this change makes behavior more consistent, but it warrants consideration. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9671-Restore-hiding-of-linked-inputs-in-app-mode-31e6d73d365081688e37fbb931f3af68) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -28,6 +28,7 @@ import { DOMWidgetImpl } from '@/scripts/domWidget'
|
|||||||
import { promptRenameWidget } from '@/utils/widgetUtil'
|
import { promptRenameWidget } from '@/utils/widgetUtil'
|
||||||
import { useAppMode } from '@/composables/useAppMode'
|
import { useAppMode } from '@/composables/useAppMode'
|
||||||
import { nodeTypeValidForApp, useAppModeStore } from '@/stores/appModeStore'
|
import { nodeTypeValidForApp, useAppModeStore } from '@/stores/appModeStore'
|
||||||
|
import { resolveNodeWidget } from '@/utils/litegraphUtil'
|
||||||
import { cn } from '@/utils/tailwindUtil'
|
import { cn } from '@/utils/tailwindUtil'
|
||||||
import { HideLayoutFieldKey } from '@/types/widgetTypes'
|
import { HideLayoutFieldKey } from '@/types/widgetTypes'
|
||||||
|
|
||||||
@@ -102,30 +103,6 @@ function getHovered(
|
|||||||
|
|
||||||
if (widget || node.constructor.nodeData?.output_node) return [node, widget]
|
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) {
|
function getBounding(nodeId: NodeId, widgetName?: string) {
|
||||||
if (settingStore.get('Comfy.VueNodes.Enabled')) return undefined
|
if (settingStore.get('Comfy.VueNodes.Enabled')) return undefined
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import ScrubableNumberInput from '@/components/common/ScrubableNumberInput.vue'
|
|||||||
import Popover from '@/components/ui/Popover.vue'
|
import Popover from '@/components/ui/Popover.vue'
|
||||||
import Button from '@/components/ui/button/Button.vue'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import { extractVueNodeData } from '@/composables/graph/useGraphNodeManager'
|
import { extractVueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||||
|
import { isPromotedWidgetView } from '@/core/graph/subgraph/promotedWidgetTypes'
|
||||||
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||||
import { LGraphEventMode } from '@/lib/litegraph/src/types/globalEnums'
|
import { LGraphEventMode } from '@/lib/litegraph/src/types/globalEnums'
|
||||||
import { useBillingContext } from '@/composables/billing/useBillingContext'
|
import { useBillingContext } from '@/composables/billing/useBillingContext'
|
||||||
@@ -29,7 +30,7 @@ import { useQueueSettingsStore } from '@/stores/queueStore'
|
|||||||
import { cn } from '@/utils/tailwindUtil'
|
import { cn } from '@/utils/tailwindUtil'
|
||||||
import { useAppMode } from '@/composables/useAppMode'
|
import { useAppMode } from '@/composables/useAppMode'
|
||||||
import { useAppModeStore } from '@/stores/appModeStore'
|
import { useAppModeStore } from '@/stores/appModeStore'
|
||||||
import { resolveNode } from '@/utils/litegraphUtil'
|
import { resolveNodeWidget } from '@/utils/litegraphUtil'
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const commandStore = useCommandStore()
|
const commandStore = useCommandStore()
|
||||||
const executionErrorStore = useExecutionErrorStore()
|
const executionErrorStore = useExecutionErrorStore()
|
||||||
@@ -63,21 +64,41 @@ useEventListener(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const mappedSelections = computed(() => {
|
const mappedSelections = computed(() => {
|
||||||
let unprocessedInputs = [...appModeStore.selectedInputs]
|
let unprocessedInputs = appModeStore.selectedInputs.flatMap(
|
||||||
//FIXME strict typing here
|
([nodeId, widgetName]) => {
|
||||||
|
const [node, widget] = resolveNodeWidget(nodeId, widgetName)
|
||||||
|
return widget ? ([[node, widget]] as const) : []
|
||||||
|
}
|
||||||
|
)
|
||||||
const processedInputs: ReturnType<typeof nodeToNodeData>[] = []
|
const processedInputs: ReturnType<typeof nodeToNodeData>[] = []
|
||||||
while (unprocessedInputs.length) {
|
while (unprocessedInputs.length) {
|
||||||
const nodeId = unprocessedInputs[0][0]
|
const [node] = unprocessedInputs[0]
|
||||||
const inputGroup = takeWhile(
|
const inputGroup = takeWhile(unprocessedInputs, ([n]) => n === node).map(
|
||||||
unprocessedInputs,
|
([, widget]) => widget
|
||||||
([id]) => id === nodeId
|
)
|
||||||
).map(([, widgetName]) => widgetName)
|
|
||||||
unprocessedInputs = unprocessedInputs.slice(inputGroup.length)
|
unprocessedInputs = unprocessedInputs.slice(inputGroup.length)
|
||||||
const node = resolveNode(nodeId)
|
//FIXME: hide widget if owning node bypassed
|
||||||
if (node?.mode !== LGraphEventMode.ALWAYS) continue
|
if (node?.mode !== LGraphEventMode.ALWAYS) continue
|
||||||
|
|
||||||
const nodeData = nodeToNodeData(node)
|
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)
|
processedInputs.push(nodeData)
|
||||||
}
|
}
|
||||||
return processedInputs
|
return processedInputs
|
||||||
@@ -107,10 +128,6 @@ function getDropIndicator(node: LGraphNode) {
|
|||||||
function nodeToNodeData(node: LGraphNode) {
|
function nodeToNodeData(node: LGraphNode) {
|
||||||
const dropIndicator = getDropIndicator(node)
|
const dropIndicator = getDropIndicator(node)
|
||||||
const nodeData = extractVueNodeData(node)
|
const nodeData = extractVueNodeData(node)
|
||||||
for (const widget of nodeData.widgets ?? []) {
|
|
||||||
widget.slotMetadata = undefined
|
|
||||||
widget.nodeId = String(node.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...nodeData,
|
...nodeData,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import _ from 'es-toolkit/compat'
|
import _ from 'es-toolkit/compat'
|
||||||
|
|
||||||
|
import { isPromotedWidgetView } from '@/core/graph/subgraph/promotedWidgetTypes'
|
||||||
import type { ColorOption, LGraph } from '@/lib/litegraph/src/litegraph'
|
import type { ColorOption, LGraph } from '@/lib/litegraph/src/litegraph'
|
||||||
import type { ExecutedWsMessage } from '@/schemas/apiSchema'
|
import type { ExecutedWsMessage } from '@/schemas/apiSchema'
|
||||||
import {
|
import {
|
||||||
@@ -319,6 +320,31 @@ export function resolveNode(
|
|||||||
}
|
}
|
||||||
return undefined
|
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) {
|
export function isLoad3dNode(node: LGraphNode) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user