mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-04 15:10:06 +00:00
refactor: simplify advanced widget resolution and tidy comments
This commit is contained in:
@@ -76,6 +76,14 @@ const advancedLabel = computed(() => {
|
||||
? t('rightSidePanel.advancedInputs')
|
||||
: undefined // SectionWidgets display node titles by default
|
||||
})
|
||||
|
||||
const showAdvancedSection = computed(
|
||||
() =>
|
||||
advancedWidgetsSectionDataList.value.length > 0 &&
|
||||
!isSearching.value &&
|
||||
!isMultipleNodesSelected.value &&
|
||||
!mustShowNodeTitle
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -113,14 +121,7 @@ const advancedLabel = computed(() => {
|
||||
class="border-b border-interface-stroke"
|
||||
/>
|
||||
</TransitionGroup>
|
||||
<template
|
||||
v-if="
|
||||
advancedWidgetsSectionDataList.length > 0 &&
|
||||
!isSearching &&
|
||||
!isMultipleNodesSelected &&
|
||||
!mustShowNodeTitle
|
||||
"
|
||||
>
|
||||
<template v-if="showAdvancedSection">
|
||||
<SectionWidgets
|
||||
v-for="{ widgets, node } in advancedWidgetsSectionDataList"
|
||||
:key="`advanced-${node.id}`"
|
||||
|
||||
@@ -110,7 +110,11 @@
|
||||
>
|
||||
<NodeSlots :node-data="nodeData" />
|
||||
|
||||
<NodeWidgets v-if="nodeData.widgets?.length" :node-data="nodeData" />
|
||||
<NodeWidgets
|
||||
v-if="nodeData.widgets?.length"
|
||||
:node-data="nodeData"
|
||||
:node="lgraphNode"
|
||||
/>
|
||||
|
||||
<div v-if="hasCustomContent" class="min-h-0 flex-1 flex">
|
||||
<NodeContent :node-data="nodeData" :media="nodeMedia" />
|
||||
@@ -499,18 +503,23 @@ const lgraphNode = computed(() => {
|
||||
|
||||
const advancedOverridesStore = useAdvancedWidgetOverridesStore()
|
||||
|
||||
/**
|
||||
* Whether to show the "Show Advanced Inputs" toggle button.
|
||||
*
|
||||
* - Subgraph nodes: button is shown when there are unpromoted interior widgets.
|
||||
* - Regular nodes: button is shown when the node has any effectively-advanced
|
||||
* widgets and advanced widgets are not forced visible by the global setting.
|
||||
*/
|
||||
const showAdvancedInputsButton = computed(() => {
|
||||
const node = lgraphNode.value
|
||||
if (!node) return false
|
||||
|
||||
// For subgraph nodes: check for unpromoted widgets
|
||||
if (node instanceof SubgraphNode) {
|
||||
const interiorNodes = node.subgraph.nodes
|
||||
const allInteriorWidgets = interiorNodes.flatMap((n) => n.widgets ?? [])
|
||||
return allInteriorWidgets.some((w) => !w.computedDisabled && !w.promoted)
|
||||
}
|
||||
|
||||
// For regular nodes: show button if there are effectively advanced widgets
|
||||
const alwaysShowAdvanced = settingStore.get(
|
||||
'Comfy.Node.AlwaysShowAdvancedWidgets'
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
>
|
||||
<div
|
||||
v-if="
|
||||
!widget.simplified.options?.hidden &&
|
||||
!widget.simplified.hidden &&
|
||||
(!widget.simplified.advanced || showAdvanced)
|
||||
"
|
||||
class="lg-node-widget group col-span-full grid grid-cols-subgrid items-stretch"
|
||||
@@ -71,17 +71,17 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { TooltipOptions } from 'primevue'
|
||||
import { computed, onErrorCaptured, provide, ref, toValue } from 'vue'
|
||||
import { computed, onErrorCaptured, ref, toValue } from 'vue'
|
||||
import type { Component } from 'vue'
|
||||
|
||||
import type {
|
||||
VueNodeData,
|
||||
WidgetSlotMetadata
|
||||
} from '@/composables/graph/useGraphNodeManager'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import { st } from '@/i18n'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
|
||||
import { useNodeTooltips } from '@/renderer/extensions/vueNodes/composables/useNodeTooltips'
|
||||
import { useNodeZIndex } from '@/renderer/extensions/vueNodes/composables/useNodeZIndex'
|
||||
@@ -93,38 +93,23 @@ import {
|
||||
shouldExpand,
|
||||
shouldRenderAsVue
|
||||
} from '@/renderer/extensions/vueNodes/widgets/registry/widgetRegistry'
|
||||
import { app } from '@/scripts/app'
|
||||
import { useAdvancedWidgetOverridesStore } from '@/stores/workspace/advancedWidgetOverridesStore'
|
||||
import type { SimplifiedWidget, WidgetValue } from '@/types/simplifiedWidget'
|
||||
import {
|
||||
getLocatorIdFromNodeData,
|
||||
getNodeByLocatorId
|
||||
} from '@/utils/graphTraversalUtil'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
import InputSlot from './InputSlot.vue'
|
||||
|
||||
interface NodeWidgetsProps {
|
||||
nodeData?: VueNodeData
|
||||
node?: LGraphNode | null
|
||||
}
|
||||
|
||||
const { nodeData } = defineProps<NodeWidgetsProps>()
|
||||
const { nodeData, node: lgraphNode = null } = defineProps<NodeWidgetsProps>()
|
||||
|
||||
const { shouldHandleNodePointerEvents, forwardEventToCanvas } =
|
||||
useCanvasInteractions()
|
||||
const { bringNodeToFront } = useNodeZIndex()
|
||||
|
||||
// Get the actual LGraphNode for providing to child components
|
||||
const lgraphNode = computed((): LGraphNode | null => {
|
||||
if (!nodeData) return null
|
||||
const locatorId = getLocatorIdFromNodeData(nodeData)
|
||||
const graph = app.rootGraph
|
||||
if (!graph) return null
|
||||
return getNodeByLocatorId(graph, locatorId) ?? null
|
||||
})
|
||||
|
||||
provide('node', lgraphNode)
|
||||
|
||||
const advancedOverridesStore = useAdvancedWidgetOverridesStore()
|
||||
|
||||
function handleWidgetPointerEvent(event: PointerEvent) {
|
||||
@@ -152,10 +137,11 @@ onErrorCaptured((error) => {
|
||||
|
||||
const nodeType = computed(() => nodeData?.type || '')
|
||||
const settingStore = useSettingStore()
|
||||
const alwaysShowAdvancedWidgets = computed(() =>
|
||||
settingStore.get('Comfy.Node.AlwaysShowAdvancedWidgets')
|
||||
)
|
||||
const showAdvanced = computed(
|
||||
() =>
|
||||
nodeData?.showAdvanced ||
|
||||
settingStore.get('Comfy.Node.AlwaysShowAdvancedWidgets')
|
||||
() => nodeData?.showAdvanced || alwaysShowAdvancedWidgets.value
|
||||
)
|
||||
const { getWidgetTooltip, createTooltipConfig } = useNodeTooltips(
|
||||
nodeType.value
|
||||
@@ -172,6 +158,12 @@ interface ProcessedWidget {
|
||||
slotMetadata?: WidgetSlotMetadata
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess Vue widgets for rendering and value updates.
|
||||
*
|
||||
* Widgets with linked input slots are forced disabled to avoid conflicting
|
||||
* input sources (linked slot vs. local widget value).
|
||||
*/
|
||||
const processedWidgets = computed((): ProcessedWidget[] => {
|
||||
if (!nodeData?.widgets) return []
|
||||
|
||||
@@ -187,15 +179,12 @@ const processedWidgets = computed((): ProcessedWidget[] => {
|
||||
|
||||
const { slotMetadata, options } = widget
|
||||
|
||||
// Core feature: Disable Vue widgets when their input slots are connected
|
||||
// This prevents conflicting input sources - when a slot is linked to another
|
||||
// node's output, the widget should be read-only to avoid data conflicts
|
||||
const widgetOptions = slotMetadata?.linked
|
||||
? { ...options, disabled: true }
|
||||
: options
|
||||
|
||||
const resolvedAdvanced = lgraphNode.value
|
||||
? advancedOverridesStore.getAdvancedState(lgraphNode.value, widget)
|
||||
const resolvedAdvanced = lgraphNode
|
||||
? advancedOverridesStore.getAdvancedState(lgraphNode, widget)
|
||||
: !!widget.options?.advanced
|
||||
|
||||
const simplified: SimplifiedWidget = {
|
||||
@@ -214,9 +203,7 @@ const processedWidgets = computed((): ProcessedWidget[] => {
|
||||
}
|
||||
|
||||
function updateHandler(value: WidgetValue) {
|
||||
// Update the widget value directly
|
||||
widget.value = value
|
||||
|
||||
widget.callback?.(value)
|
||||
}
|
||||
|
||||
@@ -235,12 +222,7 @@ const processedWidgets = computed((): ProcessedWidget[] => {
|
||||
})
|
||||
}
|
||||
|
||||
// When per-node showAdvanced is on, move advanced widgets to the end.
|
||||
// When global AlwaysShowAdvancedWidgets is on, keep original order.
|
||||
if (
|
||||
nodeData?.showAdvanced &&
|
||||
!settingStore.get('Comfy.Node.AlwaysShowAdvancedWidgets')
|
||||
) {
|
||||
if (nodeData?.showAdvanced && !alwaysShowAdvancedWidgets.value) {
|
||||
const normal = result.filter((w) => !w.simplified.advanced)
|
||||
const advanced = result.filter((w) => w.simplified.advanced)
|
||||
return [...normal, ...advanced]
|
||||
@@ -249,20 +231,20 @@ const processedWidgets = computed((): ProcessedWidget[] => {
|
||||
return result
|
||||
})
|
||||
|
||||
/**
|
||||
* Grid rows must follow the original `nodeData.widgets` order; the rendered list
|
||||
* may reorder widgets (e.g. grouping advanced widgets at the end).
|
||||
*/
|
||||
const gridTemplateRows = computed((): string => {
|
||||
if (!nodeData?.widgets) return ''
|
||||
const processedNames = new Set(toValue(processedWidgets).map((w) => w.name))
|
||||
|
||||
// Always use original nodeData.widgets order for grid template,
|
||||
// even when processedWidgets may be reordered for rendering
|
||||
return nodeData.widgets
|
||||
.filter((w) => {
|
||||
if (!processedNames.has(w.name)) return false
|
||||
if (w.options?.hidden) return false
|
||||
|
||||
// Check resolved advanced state (considering overrides)
|
||||
const resolved = lgraphNode.value
|
||||
? advancedOverridesStore.getAdvancedState(lgraphNode.value, w)
|
||||
const resolved = lgraphNode
|
||||
? advancedOverridesStore.getAdvancedState(lgraphNode, w)
|
||||
: !!w.options?.advanced
|
||||
|
||||
return !resolved || showAdvanced.value
|
||||
|
||||
@@ -7,7 +7,7 @@ import { cn } from '@/utils/tailwindUtil'
|
||||
const { widget } = defineProps<{
|
||||
widget: Pick<
|
||||
SimplifiedWidget<string | number | undefined>,
|
||||
'name' | 'label' | 'borderStyle' | 'advanced'
|
||||
'name' | 'label' | 'borderStyle'
|
||||
>
|
||||
}>()
|
||||
|
||||
@@ -18,10 +18,7 @@ const hideLayoutField = inject<boolean>('hideLayoutField', false)
|
||||
<div
|
||||
class="grid grid-cols-subgrid min-w-0 justify-between gap-1 text-node-component-slot-text"
|
||||
>
|
||||
<div
|
||||
v-if="!hideLayoutField"
|
||||
class="truncate content-center-safe flex items-center gap-2"
|
||||
>
|
||||
<div v-if="!hideLayoutField" class="truncate content-center-safe">
|
||||
<template v-if="widget.name">
|
||||
{{ widget.label || widget.name }}
|
||||
</template>
|
||||
|
||||
@@ -35,7 +35,7 @@ export const useAdvancedWidgetOverridesStore = defineStore(
|
||||
const workflowStore = useWorkflowStore()
|
||||
const canvasStore = useCanvasStore()
|
||||
|
||||
/** Map of override key → advanced boolean */
|
||||
/** Map of override key -> advanced boolean */
|
||||
const overrides = ref<Map<string, boolean>>(new Map())
|
||||
|
||||
function getOverrideKey(
|
||||
@@ -61,6 +61,21 @@ export const useAdvancedWidgetOverridesStore = defineStore(
|
||||
return workflowStore.nodeToNodeLocatorId(node)
|
||||
}
|
||||
|
||||
function setOverrideKey(key: string, advanced: boolean) {
|
||||
const next = new Map(overrides.value)
|
||||
next.set(key, advanced)
|
||||
overrides.value = next
|
||||
saveToWorkflow()
|
||||
}
|
||||
|
||||
function deleteOverrideKey(key: string) {
|
||||
if (!overrides.value.has(key)) return
|
||||
const next = new Map(overrides.value)
|
||||
next.delete(key)
|
||||
overrides.value = next
|
||||
saveToWorkflow()
|
||||
}
|
||||
|
||||
function loadFromWorkflow() {
|
||||
const graph = app.rootGraph
|
||||
if (!graph) {
|
||||
@@ -139,9 +154,7 @@ export const useAdvancedWidgetOverridesStore = defineStore(
|
||||
advanced: boolean
|
||||
) {
|
||||
const key = getOverrideKey(getNodeLocatorId(node), widgetName)
|
||||
overrides.value.set(key, advanced)
|
||||
overrides.value = new Map(overrides.value)
|
||||
saveToWorkflow()
|
||||
setOverrideKey(key, advanced)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,9 +162,7 @@ export const useAdvancedWidgetOverridesStore = defineStore(
|
||||
*/
|
||||
function clearOverride(node: LGraphNode, widgetName: string) {
|
||||
const key = getOverrideKey(getNodeLocatorId(node), widgetName)
|
||||
overrides.value.delete(key)
|
||||
overrides.value = new Map(overrides.value)
|
||||
saveToWorkflow()
|
||||
deleteOverrideKey(key)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,6 +184,7 @@ export const useAdvancedWidgetOverridesStore = defineStore(
|
||||
}
|
||||
|
||||
function clearAllOverrides() {
|
||||
if (overrides.value.size === 0) return
|
||||
overrides.value = new Map()
|
||||
saveToWorkflow()
|
||||
}
|
||||
@@ -185,36 +197,34 @@ export const useAdvancedWidgetOverridesStore = defineStore(
|
||||
if (!graph) return
|
||||
|
||||
const validKeys = new Set<string>()
|
||||
graph.nodes?.forEach((node) => {
|
||||
node.widgets?.forEach((widget) => {
|
||||
for (const node of graph.nodes ?? []) {
|
||||
for (const widget of node.widgets ?? []) {
|
||||
const key = getOverrideKey(
|
||||
getNodeLocatorId(node as LGraphNode),
|
||||
widget.name
|
||||
)
|
||||
validKeys.add(key)
|
||||
})
|
||||
})
|
||||
|
||||
let changed = false
|
||||
for (const key of overrides.value.keys()) {
|
||||
if (!validKeys.has(key)) {
|
||||
overrides.value.delete(key)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
overrides.value = new Map(overrides.value)
|
||||
saveToWorkflow()
|
||||
const next = new Map<string, boolean>()
|
||||
for (const [key, advanced] of overrides.value) {
|
||||
if (!validKeys.has(key)) continue
|
||||
next.set(key, advanced)
|
||||
}
|
||||
|
||||
if (next.size === overrides.value.size) return
|
||||
overrides.value = next
|
||||
saveToWorkflow()
|
||||
}
|
||||
|
||||
/** Keep overrides in sync with the currently active workflow. */
|
||||
watch(
|
||||
() => workflowStore.activeWorkflow,
|
||||
() => {
|
||||
loadFromWorkflow()
|
||||
},
|
||||
{ immediate: true }
|
||||
() => loadFromWorkflow(),
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user