Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
3dd3e26
[backport cloud/1.33] feat: open template via URL in linear mode (#6968)
comfy-pr-bot Nov 27, 2025
31d8422
[backport cloud/1.33] fix: don't use registry when only checking for …
christian-byrne Nov 27, 2025
334404a
[backport cloud/1.33] fix: remove LOD from vue nodes (#6983)
christian-byrne Nov 27, 2025
0cd0218
[backport cloud/1.33] fix: Vue Node <-> Litegraph node height offset …
christian-byrne Nov 27, 2025
896867b
[backport cloud/1.33] fix: add filter for combo widgets (#7003)
comfy-pr-bot Nov 27, 2025
550ca0c
[backport cloud/1.33] Remove app.graph usage from widgetInput code (#…
comfy-pr-bot Nov 28, 2025
637c199
[backport cloud/1.33] [fix] Re-encode cloud-subscription video to VP9…
comfy-pr-bot Nov 28, 2025
22aea29
[backport cloud/1.33] [feat] Show "Finished in" duration for complete…
comfy-pr-bot Nov 28, 2025
72a2581
[backport cloud/1.33] feat(api-nodes-pricing): add prices for ByteDan…
comfy-pr-bot Nov 29, 2025
7b589b5
[backport cloud/1.33] mark vue nodes menu toggle with beta tag (#7052)
comfy-pr-bot Nov 30, 2025
3856e0d
[backport cloud/1.33] fix: loader node widget value shows placeholder…
comfy-pr-bot Nov 30, 2025
ffa55cb
[backport cloud/1.33] Simplify Vue node resize to bottom-right corner…
christian-byrne Dec 1, 2025
3920413
[backport cloud/1.33] [fix] Prevent drag activation during Vue node r…
comfy-pr-bot Dec 1, 2025
c8a1df3
[backport cloud/1.33] feat(api-nodes-pricing): add prices for Kling O…
comfy-pr-bot Dec 1, 2025
b663244
[backport cloud/1.33] fix: normalize path separators in comfyAPIPlugi…
comfy-pr-bot Dec 3, 2025
6c34085
[backport cloud/1.33] cloud: increase feature flag polling interval t…
comfy-pr-bot Dec 3, 2025
d314172
[feat] Add remote config support for model upload and asset update fe…
luke-mino-altherr Dec 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[backport cloud/1.33] fix: don't use registry when only checking for …
…presence of missing nodes (#6972)

## Summary
Backport of #6965 onto cloud/1.33 (clean cherry-pick of 83f0449).

## Testing
- pnpm typecheck

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6972-backport-cloud-1-33-fix-don-t-use-registry-when-only-checking-for-presence-of-missing--2b86d73d36508150af46da84e754df3a)
by [Unito](https://www.unito.io)
  • Loading branch information
christian-byrne authored Nov 27, 2025
commit 31d842217b0b539b15501bca306d1e2f2a6609a8
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,22 @@ import { useI18n } from 'vue-i18n'

import { isCloud } from '@/platform/distribution/types'
import { useTelemetry } from '@/platform/telemetry'
import { app } from '@/scripts/app'
import { useCommandStore } from '@/stores/commandStore'
import { useNodeDefStore } from '@/stores/nodeDefStore'
import { useQueueSettingsStore } from '@/stores/queueStore'
import { useWorkspaceStore } from '@/stores/workspaceStore'
import { useMissingNodes } from '@/workbench/extensions/manager/composables/nodePack/useMissingNodes'
import { graphHasMissingNodes } from '@/workbench/extensions/manager/utils/graphHasMissingNodes'

import BatchCountEdit from '../BatchCountEdit.vue'

const workspaceStore = useWorkspaceStore()
const { mode: queueMode, batchCount } = storeToRefs(useQueueSettingsStore())

const { hasMissingNodes } = useMissingNodes()
const nodeDefStore = useNodeDefStore()
const hasMissingNodes = computed(() =>
graphHasMissingNodes(app.graph, nodeDefStore.nodeDefsByName)
)

const { t } = useI18n()
const queueModeMenuItemLookup = computed(() => {
Expand Down
9 changes: 7 additions & 2 deletions src/components/breadcrumb/SubgraphBreadcrumbItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,13 @@ import {
ComfyWorkflow,
useWorkflowStore
} from '@/platform/workflow/management/stores/workflowStore'
import { app } from '@/scripts/app'
import { useDialogService } from '@/services/dialogService'
import { useCommandStore } from '@/stores/commandStore'
import { useNodeDefStore } from '@/stores/nodeDefStore'
import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore'
import { appendJsonExt } from '@/utils/formatUtil'
import { useMissingNodes } from '@/workbench/extensions/manager/composables/nodePack/useMissingNodes'
import { graphHasMissingNodes } from '@/workbench/extensions/manager/utils/graphHasMissingNodes'

interface Props {
item: MenuItem
Expand All @@ -79,7 +81,10 @@ const props = withDefaults(defineProps<Props>(), {
isActive: false
})

const { hasMissingNodes } = useMissingNodes()
const nodeDefStore = useNodeDefStore()
const hasMissingNodes = computed(() =>
graphHasMissingNodes(app.graph, nodeDefStore.nodeDefsByName)
)

const { t } = useI18n()
const menu = ref<InstanceType<typeof Menu> & MenuState>()
Expand Down
37 changes: 37 additions & 0 deletions src/workbench/extensions/manager/utils/graphHasMissingNodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { unref } from 'vue'
import type { MaybeRef } from 'vue'

import type {
LGraph,
LGraphNode,
Subgraph
} from '@/lib/litegraph/src/litegraph'
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
import { collectAllNodes } from '@/utils/graphTraversalUtil'

export type NodeDefLookup = Record<string, ComfyNodeDefImpl | undefined>

const isNodeMissingDefinition = (
node: LGraphNode,
nodeDefsByName: NodeDefLookup
) => {
const nodeName = node?.type
if (!nodeName) return false
return !nodeDefsByName[nodeName]
}

export const collectMissingNodes = (
graph: LGraph | Subgraph | null | undefined,
nodeDefsByName: MaybeRef<NodeDefLookup>
): LGraphNode[] => {
if (!graph) return []
const lookup = unref(nodeDefsByName)
return collectAllNodes(graph, (node) => isNodeMissingDefinition(node, lookup))
}

export const graphHasMissingNodes = (
graph: LGraph | Subgraph | null | undefined,
nodeDefsByName: MaybeRef<NodeDefLookup>
) => {
return collectMissingNodes(graph, nodeDefsByName).length > 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { describe, expect, it } from 'vitest'

import type {
LGraph,
LGraphNode,
Subgraph
} from '@/lib/litegraph/src/litegraph'
import {
collectMissingNodes,
graphHasMissingNodes
} from '@/workbench/extensions/manager/utils/graphHasMissingNodes'
import type { NodeDefLookup } from '@/workbench/extensions/manager/utils/graphHasMissingNodes'
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'

type NodeDefs = NodeDefLookup

let nodeIdCounter = 0
const mockNodeDef = {} as ComfyNodeDefImpl

const createGraph = (nodes: LGraphNode[] = []): LGraph => {
return { nodes } as Partial<LGraph> as LGraph
}

const createSubgraph = (nodes: LGraphNode[]): Subgraph => {
return { nodes } as Partial<Subgraph> as Subgraph
}

const createNode = (
type?: string,
subgraphNodes?: LGraphNode[]
): LGraphNode => {
return {
id: nodeIdCounter++,
type,
isSubgraphNode: subgraphNodes ? () => true : undefined,
subgraph: subgraphNodes ? createSubgraph(subgraphNodes) : undefined
} as unknown as LGraphNode
}

describe('graphHasMissingNodes', () => {
it('returns false when graph is null', () => {
expect(graphHasMissingNodes(null, {})).toBe(false)
})

it('returns false when graph is undefined', () => {
expect(graphHasMissingNodes(undefined, {})).toBe(false)
})

it('returns false when graph has no nodes', () => {
expect(graphHasMissingNodes(createGraph(), {})).toBe(false)
})

it('returns false when every node has a definition', () => {
const graph = createGraph([createNode('FooNode'), createNode('BarNode')])
const nodeDefs: NodeDefs = {
FooNode: mockNodeDef,
BarNode: mockNodeDef
}

expect(graphHasMissingNodes(graph, nodeDefs)).toBe(false)
})

it('returns true when at least one node is missing', () => {
const graph = createGraph([
createNode('FooNode'),
createNode('MissingNode')
])
const nodeDefs: NodeDefs = {
FooNode: mockNodeDef
}

expect(graphHasMissingNodes(graph, nodeDefs)).toBe(true)
})

it('checks nodes nested in subgraphs', () => {
const graph = createGraph([
createNode('ContainerNode', [createNode('InnerMissing')])
])
const nodeDefs: NodeDefs = {
ContainerNode: mockNodeDef
}

const missingNodes = collectMissingNodes(graph, nodeDefs)
expect(missingNodes).toHaveLength(1)
expect(missingNodes[0]?.type).toBe('InnerMissing')
})

it('ignores nodes without a type', () => {
const graph = createGraph([
createNode(undefined),
createNode(null as unknown as string)
])

expect(graphHasMissingNodes(graph, {})).toBe(false)
})

it('traverses deeply nested subgraphs', () => {
const deepGraph = createGraph([
createNode('Layer1', [
createNode('Layer2', [
createNode('Layer3', [createNode('MissingDeep')])
])
])
])
const nodeDefs: NodeDefs = {
Layer1: mockNodeDef,
Layer2: mockNodeDef,
Layer3: mockNodeDef
}

const missingNodes = collectMissingNodes(deepGraph, nodeDefs)
expect(missingNodes).toHaveLength(1)
expect(missingNodes[0]?.type).toBe('MissingDeep')
})
})