mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
Support node deprecated/experimental flag (#563)
* Add deprecated field * Hide deprecated nodes * Add experimental node show/hide * Add setting tooltips * nit * nit * nit
This commit is contained in:
@@ -16,7 +16,7 @@ import SideToolbar from '@/components/sidebar/SideToolbar.vue'
|
||||
import LiteGraphCanvasSplitterOverlay from '@/components/LiteGraphCanvasSplitterOverlay.vue'
|
||||
import NodeSearchboxPopover from '@/components/searchbox/NodeSearchBoxPopover.vue'
|
||||
import NodeTooltip from '@/components/graph/NodeTooltip.vue'
|
||||
import { ref, computed, onUnmounted, watch, onMounted } from 'vue'
|
||||
import { ref, computed, onUnmounted, watch, onMounted, watchEffect } from 'vue'
|
||||
import { app as comfyApp } from '@/scripts/app'
|
||||
import { useSettingStore } from '@/stores/settingStore'
|
||||
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
const emit = defineEmits(['ready'])
|
||||
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
||||
const settingStore = useSettingStore()
|
||||
const nodeDefStore = useNodeDefStore()
|
||||
const workspaceStore = useWorkspaceStore()
|
||||
|
||||
const betaMenuEnabled = computed(
|
||||
@@ -63,6 +64,16 @@ watch(
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watchEffect(() => {
|
||||
nodeDefStore.showDeprecated = settingStore.get('Comfy.Node.ShowDeprecated')
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
nodeDefStore.showExperimental = settingStore.get(
|
||||
'Comfy.Node.ShowExperimental'
|
||||
)
|
||||
})
|
||||
|
||||
let dropTargetCleanup = () => {}
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -98,7 +109,7 @@ onMounted(async () => {
|
||||
const comfyNodeName = event.source.element.getAttribute(
|
||||
'data-comfy-node-name'
|
||||
)
|
||||
const nodeDef = useNodeDefStore().nodeDefsByName[comfyNodeName]
|
||||
const nodeDef = nodeDefStore.nodeDefsByName[comfyNodeName]
|
||||
comfyApp.addNodeOnGraph(nodeDef, { pos })
|
||||
}
|
||||
})
|
||||
|
||||
@@ -112,6 +112,7 @@ const settingStore = useSettingStore()
|
||||
const sidebarLocation = computed<'left' | 'right'>(() =>
|
||||
settingStore.get('Comfy.Sidebar.Location')
|
||||
)
|
||||
|
||||
const nodePreviewStyle = ref<Record<string, string>>({
|
||||
position: 'absolute',
|
||||
top: '0px',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NodeSearchService } from '@/services/nodeSearchService'
|
||||
import { ComfyNodeDef } from '@/types/apiTypes'
|
||||
import { defineStore } from 'pinia'
|
||||
import { Type, Transform, plainToClass } from 'class-transformer'
|
||||
import { Type, Transform, plainToClass, Expose } from 'class-transformer'
|
||||
import { ComfyWidgetConstructor } from '@/scripts/widgets'
|
||||
import { TreeNode } from 'primevue/treenode'
|
||||
import { buildTree } from '@/utils/treeUtil'
|
||||
@@ -166,6 +166,23 @@ export class ComfyNodeDefImpl {
|
||||
python_module: string
|
||||
description: string
|
||||
|
||||
@Transform(({ value, obj }) => value ?? obj.category === '', {
|
||||
toClassOnly: true
|
||||
})
|
||||
@Type(() => Boolean)
|
||||
@Expose()
|
||||
deprecated: boolean
|
||||
|
||||
@Transform(
|
||||
({ value, obj }) => value ?? obj.category.startsWith('_for_testing'),
|
||||
{
|
||||
toClassOnly: true
|
||||
}
|
||||
)
|
||||
@Type(() => Boolean)
|
||||
@Expose()
|
||||
experimental: boolean
|
||||
|
||||
@Type(() => ComfyInputsSpec)
|
||||
input: ComfyInputsSpec
|
||||
|
||||
@@ -229,22 +246,34 @@ export const SYSTEM_NODE_DEFS: Record<string, ComfyNodeDef> = {
|
||||
interface State {
|
||||
nodeDefsByName: Record<string, ComfyNodeDefImpl>
|
||||
widgets: Record<string, ComfyWidgetConstructor>
|
||||
showDeprecated: boolean
|
||||
showExperimental: boolean
|
||||
}
|
||||
|
||||
export const useNodeDefStore = defineStore('nodeDef', {
|
||||
state: (): State => ({
|
||||
nodeDefsByName: {},
|
||||
widgets: {}
|
||||
widgets: {},
|
||||
showDeprecated: false,
|
||||
showExperimental: false
|
||||
}),
|
||||
getters: {
|
||||
nodeDefs(state) {
|
||||
return Object.values(state.nodeDefsByName)
|
||||
},
|
||||
nodeSearchService(state) {
|
||||
return new NodeSearchService(Object.values(state.nodeDefsByName))
|
||||
// Node defs that are not deprecated
|
||||
visibleNodeDefs(state) {
|
||||
return this.nodeDefs.filter(
|
||||
(nodeDef: ComfyNodeDefImpl) =>
|
||||
(state.showDeprecated || !nodeDef.deprecated) &&
|
||||
(state.showExperimental || !nodeDef.experimental)
|
||||
)
|
||||
},
|
||||
nodeSearchService() {
|
||||
return new NodeSearchService(this.visibleNodeDefs)
|
||||
},
|
||||
nodeTree(): TreeNode {
|
||||
return buildTree(this.nodeDefs, (nodeDef: ComfyNodeDefImpl) => [
|
||||
return buildTree(this.visibleNodeDefs, (nodeDef: ComfyNodeDefImpl) => [
|
||||
...nodeDef.category.split('/'),
|
||||
nodeDef.display_name
|
||||
])
|
||||
|
||||
@@ -147,6 +147,24 @@ export const useSettingStore = defineStore('setting', {
|
||||
type: 'boolean',
|
||||
defaultValue: true
|
||||
})
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: 'Comfy.Node.ShowDeprecated',
|
||||
name: 'Show deprecated nodes in search',
|
||||
tooltip:
|
||||
'Deprecated nodes are hidden by default in the UI, but remain functional in existing workflows that use them.',
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
})
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: 'Comfy.Node.ShowExperimental',
|
||||
name: 'Show experimental nodes in search',
|
||||
tooltip:
|
||||
'Experimental nodes are marked as such in the UI and may be subject to significant changes or removal in future versions. Use with caution in production workflows',
|
||||
type: 'boolean',
|
||||
defaultValue: true
|
||||
})
|
||||
},
|
||||
|
||||
set<K extends keyof Settings>(key: K, value: Settings[K]) {
|
||||
|
||||
@@ -335,7 +335,9 @@ const zComfyNodeDef = z.object({
|
||||
description: z.string(),
|
||||
category: z.string(),
|
||||
output_node: z.boolean(),
|
||||
python_module: z.string()
|
||||
python_module: z.string(),
|
||||
deprecated: z.boolean().optional(),
|
||||
experimental: z.boolean().optional()
|
||||
})
|
||||
|
||||
// `/object_info`
|
||||
@@ -419,6 +421,8 @@ const zSettings = z.record(z.any()).and(
|
||||
'Comfy.NodeSearchBoxImpl': z.enum(['default', 'simple']),
|
||||
'Comfy.NodeSearchBoxImpl.ShowCategory': z.boolean(),
|
||||
'Comfy.NodeSuggestions.number': z.number(),
|
||||
'Comfy.Node.ShowDeprecated': z.boolean(),
|
||||
'Comfy.Node.ShowExperimental': z.boolean(),
|
||||
'Comfy.PreviewFormat': z.string(),
|
||||
'Comfy.PromptFilename': z.boolean(),
|
||||
'Comfy.Sidebar.Location': z.enum(['left', 'right']),
|
||||
|
||||
@@ -16,7 +16,9 @@ const EXAMPLE_NODE_DEF: ComfyNodeDef = {
|
||||
description: '',
|
||||
python_module: 'nodes',
|
||||
category: 'loaders',
|
||||
output_node: false
|
||||
output_node: false,
|
||||
experimental: false,
|
||||
deprecated: false
|
||||
}
|
||||
|
||||
describe('validateNodeDef', () => {
|
||||
|
||||
@@ -194,6 +194,52 @@ describe('ComfyNodeDefImpl', () => {
|
||||
is_list: false
|
||||
}
|
||||
])
|
||||
expect(result.deprecated).toBe(false)
|
||||
})
|
||||
|
||||
it('should transform a deprecated basic node definition', () => {
|
||||
const plainObject = {
|
||||
name: 'TestNode',
|
||||
display_name: 'Test Node',
|
||||
category: 'Testing',
|
||||
python_module: 'test_module',
|
||||
description: 'A test node',
|
||||
input: {
|
||||
required: {
|
||||
intInput: ['INT', { min: 0, max: 100, default: 50 }]
|
||||
}
|
||||
},
|
||||
output: ['INT'],
|
||||
output_is_list: [false],
|
||||
output_name: ['intOutput'],
|
||||
deprecated: true
|
||||
}
|
||||
|
||||
const result = plainToClass(ComfyNodeDefImpl, plainObject)
|
||||
expect(result.deprecated).toBe(true)
|
||||
})
|
||||
|
||||
// Legacy way of marking a node as deprecated
|
||||
it('should mark deprecated with empty category', () => {
|
||||
const plainObject = {
|
||||
name: 'TestNode',
|
||||
display_name: 'Test Node',
|
||||
// Empty category should be treated as deprecated
|
||||
category: '',
|
||||
python_module: 'test_module',
|
||||
description: 'A test node',
|
||||
input: {
|
||||
required: {
|
||||
intInput: ['INT', { min: 0, max: 100, default: 50 }]
|
||||
}
|
||||
},
|
||||
output: ['INT'],
|
||||
output_is_list: [false],
|
||||
output_name: ['intOutput']
|
||||
}
|
||||
|
||||
const result = plainToClass(ComfyNodeDefImpl, plainObject)
|
||||
expect(result.deprecated).toBe(true)
|
||||
})
|
||||
|
||||
it('should handle multiple outputs including COMBO type', () => {
|
||||
|
||||
Reference in New Issue
Block a user