diff --git a/src/components/searchbox/NodeSearchItem.vue b/src/components/searchbox/NodeSearchItem.vue
index da9a0cd83..a9859eca4 100644
--- a/src/components/searchbox/NodeSearchItem.vue
+++ b/src/components/searchbox/NodeSearchItem.vue
@@ -21,16 +21,17 @@
-
+
+
{
static comfyClass: string
static override title: string
static override category: string
- static nodeData: ComfyNodeDefV1 & ComfyNodeDefV2
+ static override nodeData: ComfyNodeDefV1 & ComfyNodeDefV2
_initialMinSize = { width: 1, height: 1 }
@@ -394,7 +394,7 @@ export const useLitegraphService = () => {
static comfyClass: string
static override title: string
static override category: string
- static nodeData: ComfyNodeDefV1 & ComfyNodeDefV2
+ static override nodeData: ComfyNodeDefV1 & ComfyNodeDefV2
_initialMinSize = { width: 1, height: 1 }
@@ -496,6 +496,13 @@ export const useLitegraphService = () => {
// because `registerNodeType` will overwrite the assignments.
node.category = nodeDef.category
node.title = nodeDef.display_name || nodeDef.name
+
+ // Set skip_list for dev-only nodes based on current DevMode setting
+ // This ensures nodes registered after initial load respect the current setting
+ if (nodeDef.dev_only) {
+ const settingStore = useSettingStore()
+ node.skip_list = !settingStore.get('Comfy.DevMode')
+ }
}
/**
diff --git a/src/stores/nodeDefStore.ts b/src/stores/nodeDefStore.ts
index 217b784ca..7bfa17c2c 100644
--- a/src/stores/nodeDefStore.ts
+++ b/src/stores/nodeDefStore.ts
@@ -1,9 +1,10 @@
import axios from 'axios'
import _ from 'es-toolkit/compat'
import { defineStore } from 'pinia'
-import { computed, ref } from 'vue'
+import { computed, ref, watchEffect } from 'vue'
import { isProxyWidget } from '@/core/graph/subgraph/proxyWidget'
+import { LiteGraph } from '@/lib/litegraph/src/litegraph'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { transformNodeDefV1ToV2 } from '@/schemas/nodeDef/migration'
import type {
@@ -17,6 +18,7 @@ import type {
ComfyOutputTypesSpec as ComfyOutputSpecV1,
PriceBadge
} from '@/schemas/nodeDefSchema'
+import { useSettingStore } from '@/platform/settings/settingStore'
import { NodeSearchService } from '@/services/nodeSearchService'
import { useSubgraphStore } from '@/stores/subgraphStore'
import { NodeSourceType, getNodeSource } from '@/types/nodeSource'
@@ -41,6 +43,7 @@ export class ComfyNodeDefImpl
readonly help: string
readonly deprecated: boolean
readonly experimental: boolean
+ readonly dev_only: boolean
readonly output_node: boolean
readonly api_node: boolean
/**
@@ -133,6 +136,7 @@ export class ComfyNodeDefImpl
this.deprecated = obj.deprecated ?? obj.category === ''
this.experimental =
obj.experimental ?? obj.category.startsWith('_for_testing')
+ this.dev_only = obj.dev_only ?? false
this.output_node = obj.output_node
this.api_node = !!obj.api_node
this.input = obj.input ?? {}
@@ -174,6 +178,7 @@ export class ComfyNodeDefImpl
get nodeLifeCycleBadgeText(): string {
if (this.deprecated) return '[DEPR]'
if (this.experimental) return '[BETA]'
+ if (this.dev_only) return '[DEV]'
return ''
}
}
@@ -299,12 +304,27 @@ export interface NodeDefFilter {
}
export const useNodeDefStore = defineStore('nodeDef', () => {
+ const settingStore = useSettingStore()
+
const nodeDefsByName = ref>({})
const nodeDefsByDisplayName = ref>({})
const showDeprecated = ref(false)
const showExperimental = ref(false)
+ const showDevOnly = computed(() => settingStore.get('Comfy.DevMode'))
const nodeDefFilters = ref([])
+ // Update skip_list on all registered node types when dev mode changes
+ // This ensures LiteGraph's getNodeTypesCategories/getNodeTypesInCategory
+ // correctly filter dev-only nodes from the right-click context menu
+ watchEffect(() => {
+ const devModeEnabled = showDevOnly.value
+ for (const nodeType of Object.values(LiteGraph.registered_node_types)) {
+ if (nodeType.nodeData?.dev_only) {
+ nodeType.skip_list = !devModeEnabled
+ }
+ }
+ })
+
const nodeDefs = computed(() => {
const subgraphStore = useSubgraphStore()
// Blueprints first for discoverability in the node library sidebar
@@ -422,6 +442,14 @@ export const useNodeDefStore = defineStore('nodeDef', () => {
predicate: (nodeDef) => showExperimental.value || !nodeDef.experimental
})
+ // Dev-only nodes filter
+ registerNodeDefFilter({
+ id: 'core.dev_only',
+ name: 'Hide Dev-Only Nodes',
+ description: 'Hides nodes marked as dev-only unless dev mode is enabled',
+ predicate: (nodeDef) => showDevOnly.value || !nodeDef.dev_only
+ })
+
// Subgraph nodes filter
// Filter out litegraph typed subgraphs, saved blueprints are added in separately
registerNodeDefFilter({
@@ -446,6 +474,7 @@ export const useNodeDefStore = defineStore('nodeDef', () => {
nodeDefsByDisplayName,
showDeprecated,
showExperimental,
+ showDevOnly,
nodeDefFilters,
nodeDefs,
diff --git a/src/utils/nodeFilterUtil.test.ts b/src/utils/nodeFilterUtil.test.ts
index 871f66109..0e6149c0d 100644
--- a/src/utils/nodeFilterUtil.test.ts
+++ b/src/utils/nodeFilterUtil.test.ts
@@ -11,7 +11,7 @@ describe('nodeFilterUtil', () => {
): LGraphNode => {
// Create a custom class with the nodeData static property
class MockNode extends LGraphNode {
- static nodeData = isOutputNode ? { output_node: true } : {}
+ static override nodeData = isOutputNode ? { output_node: true } : {}
}
const node = new MockNode('')
@@ -71,11 +71,11 @@ describe('nodeFilterUtil', () => {
})
it('should handle nodes with undefined output_node', () => {
- class MockNodeWithOtherData extends LGraphNode {
- static nodeData = { someOtherProperty: true }
+ class MockNodeWithEmptyData extends LGraphNode {
+ static override nodeData = {}
}
- const node = new MockNodeWithOtherData('')
+ const node = new MockNodeWithEmptyData('')
node.id = 1
const result = filterOutputNodes([node])