feat: add settings option to always show advanced widgets on all nodes (#8244)

Solved issue:
Currently, the display status of advanced widgets can only be set
individually for each node, but users would like to have a global switch
to always display all advanced widgets.

I also adjusted some related code to solve the issue of code
duplication.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8244-feat-add-settings-option-to-always-show-advanced-widgets-on-all-nodes-2f06d73d365081358023efa3e1ff3094)
by [Unito](https://www.unito.io)
This commit is contained in:
Rizumu Ayaka
2026-01-24 09:47:51 +07:00
committed by GitHub
parent 3e9a390c25
commit 1b1356951e
9 changed files with 125 additions and 48 deletions

View File

@@ -8,7 +8,7 @@ import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
import FormSearchInput from '@/renderer/extensions/vueNodes/widgets/components/form/FormSearchInput.vue' import FormSearchInput from '@/renderer/extensions/vueNodes/widgets/components/form/FormSearchInput.vue'
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore' import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
import { searchWidgetsAndNodes } from '../shared' import { computedSectionDataList, searchWidgetsAndNodes } from '../shared'
import type { NodeWidgetsListList } from '../shared' import type { NodeWidgetsListList } from '../shared'
import SectionWidgets from './SectionWidgets.vue' import SectionWidgets from './SectionWidgets.vue'
@@ -24,18 +24,7 @@ const nodes = computed((): LGraphNode[] => {
const rightSidePanelStore = useRightSidePanelStore() const rightSidePanelStore = useRightSidePanelStore()
const { searchQuery } = storeToRefs(rightSidePanelStore) const { searchQuery } = storeToRefs(rightSidePanelStore)
const widgetsSectionDataList = computed((): NodeWidgetsListList => { const { widgetsSectionDataList } = computedSectionDataList(nodes)
return nodes.value.map((node) => {
const { widgets = [] } = node
const shownWidgets = widgets
.filter((w) => !(w.options?.canvasOnly || w.options?.hidden))
.map((widget) => ({ node, widget }))
return {
widgets: shownWidgets,
node
}
})
})
const searchedWidgetsSectionDataList = shallowRef<NodeWidgetsListList>( const searchedWidgetsSectionDataList = shallowRef<NodeWidgetsListList>(
widgetsSectionDataList.value widgetsSectionDataList.value

View File

@@ -7,7 +7,7 @@ import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import FormSearchInput from '@/renderer/extensions/vueNodes/widgets/components/form/FormSearchInput.vue' import FormSearchInput from '@/renderer/extensions/vueNodes/widgets/components/form/FormSearchInput.vue'
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore' import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
import { searchWidgetsAndNodes } from '../shared' import { computedSectionDataList, searchWidgetsAndNodes } from '../shared'
import type { NodeWidgetsListList } from '../shared' import type { NodeWidgetsListList } from '../shared'
import SectionWidgets from './SectionWidgets.vue' import SectionWidgets from './SectionWidgets.vue'
@@ -21,21 +21,14 @@ const { t } = useI18n()
const rightSidePanelStore = useRightSidePanelStore() const rightSidePanelStore = useRightSidePanelStore()
const { searchQuery } = storeToRefs(rightSidePanelStore) const { searchQuery } = storeToRefs(rightSidePanelStore)
const widgetsSectionDataList = computed((): NodeWidgetsListList => { const { widgetsSectionDataList, includesAdvanced } = computedSectionDataList(
return nodes.map((node) => { () => nodes
const { widgets = [] } = node )
const shownWidgets = widgets
.filter(
(w) =>
!(w.options?.canvasOnly || w.options?.hidden || w.options?.advanced)
)
.map((widget) => ({ node, widget }))
return { widgets: shownWidgets, node }
})
})
const advancedWidgetsSectionDataList = computed((): NodeWidgetsListList => { const advancedWidgetsSectionDataList = computed((): NodeWidgetsListList => {
if (includesAdvanced.value) {
return []
}
return nodes return nodes
.map((node) => { .map((node) => {
const { widgets = [] } = node const { widgets = [] } = node

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import InputNumber from 'primevue/inputnumber' import InputNumber from 'primevue/inputnumber'
import Select from 'primevue/select' import Select from 'primevue/select'
import { computed, ref } from 'vue' import { computed } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import Button from '@/components/ui/button/Button.vue' import Button from '@/components/ui/button/Button.vue'
@@ -23,7 +23,11 @@ const settingStore = useSettingStore()
const dialogService = useDialogService() const dialogService = useDialogService()
// NODES settings // NODES settings
const showAdvancedParameters = ref(false) // Placeholder for future implementation const showAdvancedParameters = computed({
get: () => settingStore.get('Comfy.Node.AlwaysShowAdvancedWidgets'),
set: (value) =>
settingStore.set('Comfy.Node.AlwaysShowAdvancedWidgets', value)
})
const showToolbox = computed({ const showToolbox = computed({
get: () => settingStore.get('Comfy.Canvas.SelectionToolbox'), get: () => settingStore.get('Comfy.Canvas.SelectionToolbox'),

View File

@@ -6,6 +6,7 @@ import type { LGraphGroup } from '@/lib/litegraph/src/LGraphGroup'
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets' import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
import { isLGraphGroup, isLGraphNode } from '@/utils/litegraphUtil' import { isLGraphGroup, isLGraphNode } from '@/utils/litegraphUtil'
import { useSettingStore } from '@/platform/settings/settingStore'
export const GetNodeParentGroupKey: InjectionKey< export const GetNodeParentGroupKey: InjectionKey<
(node: LGraphNode) => LGraphGroup | null (node: LGraphNode) => LGraphGroup | null
@@ -203,3 +204,33 @@ function repeatItems<T>(items: T[]): T[] {
} }
return result return result
} }
export function computedSectionDataList(nodes: MaybeRefOrGetter<LGraphNode[]>) {
const settingStore = useSettingStore()
const includesAdvanced = computed(() =>
settingStore.get('Comfy.Node.AlwaysShowAdvancedWidgets')
)
const widgetsSectionDataList = computed((): NodeWidgetsListList => {
return toValue(nodes).map((node) => {
const { widgets = [] } = node
const shownWidgets = widgets
.filter(
(w) =>
!(
w.options?.canvasOnly ||
w.options?.hidden ||
(w.options?.advanced && !includesAdvanced.value)
)
)
.map((widget) => ({ node, widget }))
return { widgets: shownWidgets, node }
})
})
return {
widgetsSectionDataList,
includesAdvanced
}
}

View File

@@ -2683,7 +2683,8 @@
"noneSearchDesc": "No items match your search", "noneSearchDesc": "No items match your search",
"nodesNoneDesc": "NO NODES", "nodesNoneDesc": "NO NODES",
"fallbackGroupTitle": "Group", "fallbackGroupTitle": "Group",
"fallbackNodeTitle": "Node" "fallbackNodeTitle": "Node",
"hideAdvancedInputsButton": "Hide advanced inputs"
}, },
"help": { "help": {
"recentReleases": "Recent releases", "recentReleases": "Recent releases",

View File

@@ -1174,5 +1174,15 @@ export const CORE_SETTINGS: SettingParams[] = [
'Replaces the floating job queue panel with an equivalent job queue embedded in the Assets side panel. You can disable this to return to the floating panel layout.', 'Replaces the floating job queue panel with an equivalent job queue embedded in the Assets side panel. You can disable this to return to the floating panel layout.',
defaultValue: true, defaultValue: true,
experimental: true experimental: true
},
{
id: 'Comfy.Node.AlwaysShowAdvancedWidgets',
category: ['LiteGraph', 'Node Widget', 'AlwaysShowAdvancedWidgets'],
name: 'Always show advanced widgets on all nodes',
tooltip:
'When enabled, advanced widgets are always visible on all nodes without needing to expand them individually.',
type: 'boolean',
defaultValue: false,
versionAdded: '1.39.0'
} }
] ]

View File

@@ -130,10 +130,16 @@
'transition-all cursor-pointer hover:bg-accent-background duration-150 active:scale-95' 'transition-all cursor-pointer hover:bg-accent-background duration-150 active:scale-95'
) )
" "
@click.stop="handleShowAdvancedInputs" @click.stop="showAdvancedState = !showAdvancedState"
> >
<i class="icon-[lucide--settings-2] size-4" /> <template v-if="showAdvancedState">
<span>{{ t('rightSidePanel.showAdvancedInputsButton') }}</span> <i class="icon-[lucide--chevron-up] size-4" />
<span>{{ t('rightSidePanel.hideAdvancedInputsButton') }}</span>
</template>
<template v-else>
<i class="icon-[lucide--settings-2] size-4" />
<span>{{ t('rightSidePanel.showAdvancedInputsButton') }} </span>
</template>
</button> </button>
</div> </div>
</div> </div>
@@ -152,7 +158,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { computed, nextTick, onErrorCaptured, onMounted, ref, watch } from 'vue' import {
computed,
customRef,
nextTick,
onErrorCaptured,
onMounted,
ref,
watch
} from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager' import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
@@ -212,6 +226,8 @@ const { nodeData, error = null } = defineProps<LGraphNodeProps>()
const { t } = useI18n() const { t } = useI18n()
const settingStore = useSettingStore()
const { handleNodeCollapse, handleNodeTitleUpdate, handleNodeRightClick } = const { handleNodeCollapse, handleNodeTitleUpdate, handleNodeRightClick } =
useNodeEventHandlers() useNodeEventHandlers()
const { bringNodeToFront } = useNodeZIndex() const { bringNodeToFront } = useNodeZIndex()
@@ -248,7 +264,7 @@ const bypassed = computed(
const muted = computed((): boolean => nodeData.mode === LGraphEventMode.NEVER) const muted = computed((): boolean => nodeData.mode === LGraphEventMode.NEVER)
const nodeOpacity = computed(() => { const nodeOpacity = computed(() => {
const globalOpacity = useSettingStore().get('Comfy.Node.Opacity') ?? 1 const globalOpacity = settingStore.get('Comfy.Node.Opacity') ?? 1
// For muted/bypassed nodes, apply the 0.5 multiplier on top of global opacity // For muted/bypassed nodes, apply the 0.5 multiplier on top of global opacity
if (bypassed.value || muted.value) { if (bypassed.value || muted.value) {
@@ -493,20 +509,45 @@ const showAdvancedInputsButton = computed(() => {
// For regular nodes: show button if there are advanced widgets and they're currently hidden // For regular nodes: show button if there are advanced widgets and they're currently hidden
const hasAdvancedWidgets = nodeData.widgets?.some((w) => w.options?.advanced) const hasAdvancedWidgets = nodeData.widgets?.some((w) => w.options?.advanced)
return hasAdvancedWidgets && !node.showAdvanced const alwaysShowAdvanced = settingStore.get(
'Comfy.Node.AlwaysShowAdvancedWidgets'
)
return hasAdvancedWidgets && !alwaysShowAdvanced
}) })
function handleShowAdvancedInputs() { const showAdvancedState = customRef((track, trigger) => {
const node = lgraphNode.value let internalState = false
if (!node) return
if (node instanceof SubgraphNode) { const node = lgraphNode.value
const rightSidePanelStore = useRightSidePanelStore() if (node && !(node instanceof SubgraphNode)) {
rightSidePanelStore.focusSection('advanced-inputs') internalState = !!node.showAdvanced
} else {
node.showAdvanced = true
} }
}
return {
get() {
track()
return internalState
},
set(value: boolean) {
const node = lgraphNode.value
if (!node) return
if (node instanceof SubgraphNode) {
// Do not modify internalState for subgraph nodes
const rightSidePanelStore = useRightSidePanelStore()
if (value) {
rightSidePanelStore.focusSection('advanced-inputs')
} else {
rightSidePanelStore.closePanel()
}
} else {
node.showAdvanced = value
internalState = value
}
trigger()
}
}
})
const nodeMedia = computed(() => { const nodeMedia = computed(() => {
const newOutputs = nodeOutputs.nodeOutputs[nodeOutputLocatorId.value] const newOutputs = nodeOutputs.nodeOutputs[nodeOutputLocatorId.value]

View File

@@ -27,7 +27,7 @@
<div <div
v-if=" v-if="
!widget.simplified.options?.hidden && !widget.simplified.options?.hidden &&
(!widget.simplified.options?.advanced || nodeData?.showAdvanced) (!widget.simplified.options?.advanced || showAdvanced)
" "
class="lg-node-widget group col-span-full grid grid-cols-subgrid items-stretch" class="lg-node-widget group col-span-full grid grid-cols-subgrid items-stretch"
> >
@@ -78,6 +78,7 @@ import type {
VueNodeData, VueNodeData,
WidgetSlotMetadata WidgetSlotMetadata
} from '@/composables/graph/useGraphNodeManager' } from '@/composables/graph/useGraphNodeManager'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useErrorHandling } from '@/composables/useErrorHandling' import { useErrorHandling } from '@/composables/useErrorHandling'
import { st } from '@/i18n' import { st } from '@/i18n'
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions' import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
@@ -130,6 +131,12 @@ onErrorCaptured((error) => {
}) })
const nodeType = computed(() => nodeData?.type || '') const nodeType = computed(() => nodeData?.type || '')
const settingStore = useSettingStore()
const showAdvanced = computed(
() =>
nodeData?.showAdvanced ||
settingStore.get('Comfy.Node.AlwaysShowAdvancedWidgets')
)
const { getWidgetTooltip, createTooltipConfig } = useNodeTooltips( const { getWidgetTooltip, createTooltipConfig } = useNodeTooltips(
nodeType.value nodeType.value
) )
@@ -213,7 +220,7 @@ const gridTemplateRows = computed((): string => {
(w) => (w) =>
processedNames.has(w.name) && processedNames.has(w.name) &&
!w.options?.hidden && !w.options?.hidden &&
(!w.options?.advanced || nodeData?.showAdvanced) (!w.options?.advanced || showAdvanced.value)
) )
.map((w) => .map((w) =>
shouldExpand(w.type) || w.hasLayoutSize ? 'auto' : 'min-content' shouldExpand(w.type) || w.hasLayoutSize ? 'auto' : 'min-content'

View File

@@ -430,7 +430,8 @@ const zSettings = z.object({
'LiteGraph.Node.DefaultPadding': z.boolean(), 'LiteGraph.Node.DefaultPadding': z.boolean(),
'LiteGraph.Pointer.TrackpadGestures': z.boolean(), 'LiteGraph.Pointer.TrackpadGestures': z.boolean(),
'Comfy.VersionCompatibility.DisableWarnings': z.boolean(), 'Comfy.VersionCompatibility.DisableWarnings': z.boolean(),
'Comfy.RightSidePanel.IsOpen': z.boolean() 'Comfy.RightSidePanel.IsOpen': z.boolean(),
'Comfy.Node.AlwaysShowAdvancedWidgets': z.boolean()
}) })
export type EmbeddingsResponse = z.infer<typeof zEmbeddingsResponse> export type EmbeddingsResponse = z.infer<typeof zEmbeddingsResponse>