refactor: simplify advanced widget resolution and tidy comments

This commit is contained in:
Rizumu Ayaka
2026-01-27 21:09:09 +08:00
parent 016d8f6126
commit 633da6bf60
5 changed files with 81 additions and 82 deletions

View File

@@ -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}`"

View File

@@ -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'
)

View File

@@ -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

View File

@@ -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>

View File

@@ -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 {