From 84f77e76754c013a93b6e2491a0ebfe5a99aa224 Mon Sep 17 00:00:00 2001 From: AustinMroz Date: Thu, 12 Mar 2026 09:14:11 -0700 Subject: [PATCH] Support search filtering to dynamic input types (#9388) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, MatchType and Autogrow inputs would not be considered would filtering searchbox entires. For example, "Batch Images" would not show as a suggestion would dragging a noodle from a "Load Image" node. This is resolved by adding a step during nodeDef registration to precalculate a list of all input types. This may have performance implications. - Search filtering should be more performant - Initial node registration will be slower - There's additional memory cost to store this information on every node. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9388-Support-search-filtering-to-dynamic-input-types-3196d73d365081d9939eff5e167a7e83) by [Unito](https://www.unito.io) --- src/core/graph/widgets/dynamicTypes.ts | 35 ++++++++++++++++++++++++ src/core/graph/widgets/dynamicWidgets.ts | 13 +++++---- src/schemas/nodeDefSchema.ts | 9 ++++++ src/services/nodeSearchService.ts | 5 +--- src/stores/nodeDefStore.ts | 5 ++++ 5 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 src/core/graph/widgets/dynamicTypes.ts diff --git a/src/core/graph/widgets/dynamicTypes.ts b/src/core/graph/widgets/dynamicTypes.ts new file mode 100644 index 0000000000..92a7850b96 --- /dev/null +++ b/src/core/graph/widgets/dynamicTypes.ts @@ -0,0 +1,35 @@ +import { transformInputSpecV1ToV2 } from '@/schemas/nodeDef/migration' +import { zAutogrowOptions, zMatchTypeOptions } from '@/schemas/nodeDefSchema' +import type { InputSpec } from '@/schemas/nodeDefSchema' +import type { InputSpec as InputSpecV2 } from '@/schemas/nodeDef/nodeDefSchemaV2' + +const dynamicTypeResolvers: Record< + string, + (inputSpec: InputSpecV2) => string[] +> = { + COMFY_AUTOGROW_V3: resolveAutogrowType, + COMFY_MATCHTYPE_V3: (input) => + zMatchTypeOptions + .safeParse(input) + .data?.template?.allowed_types?.split(',') ?? [] +} + +export function resolveInputType(input: InputSpecV2): string[] { + return input.type in dynamicTypeResolvers + ? dynamicTypeResolvers[input.type](input) + : input.type.split(',') +} + +function resolveAutogrowType(rawSpec: InputSpecV2): string[] { + const { input } = zAutogrowOptions.safeParse(rawSpec).data?.template ?? {} + + const inputTypes: (Record | undefined)[] = [ + input?.required, + input?.optional + ] + return inputTypes.flatMap((inputType) => + Object.entries(inputType ?? {}).flatMap(([name, v]) => + resolveInputType(transformInputSpecV1ToV2(v, { name })) + ) + ) +} diff --git a/src/core/graph/widgets/dynamicWidgets.ts b/src/core/graph/widgets/dynamicWidgets.ts index 21913dedd0..6d90c2f56c 100644 --- a/src/core/graph/widgets/dynamicWidgets.ts +++ b/src/core/graph/widgets/dynamicWidgets.ts @@ -16,7 +16,8 @@ import type { ComboInputSpec, InputSpec } from '@/schemas/nodeDefSchema' import type { InputSpec as InputSpecV2 } from '@/schemas/nodeDef/nodeDefSchemaV2' import { zAutogrowOptions, - zDynamicComboInputSpec + zDynamicComboInputSpec, + zMatchTypeOptions } from '@/schemas/nodeDefSchema' import { useLitegraphService } from '@/services/litegraphService' import { app } from '@/scripts/app' @@ -215,6 +216,7 @@ export function applyDynamicInputs( dynamicInputs[inputSpec.type](node, inputSpec) return true } + function spliceInputs( node: LGraphNode, startIndex: number, @@ -329,11 +331,10 @@ function withComfyMatchType(node: LGraphNode): asserts node is MatchTypeNode { function applyMatchType(node: LGraphNode, inputSpec: InputSpecV2) { const { addNodeInput } = useLitegraphService() const name = inputSpec.name - const { allowed_types, template_id } = ( - inputSpec as InputSpecV2 & { - template: { allowed_types: string; template_id: string } - } - ).template + const matchTypeSpec = zMatchTypeOptions.safeParse(inputSpec).data + if (!matchTypeSpec) return + + const { allowed_types, template_id } = matchTypeSpec.template const typedSpec = { ...inputSpec, type: allowed_types } addNodeInput(node, typedSpec) withComfyMatchType(node) diff --git a/src/schemas/nodeDefSchema.ts b/src/schemas/nodeDefSchema.ts index 2fdfeeada9..6120641af2 100644 --- a/src/schemas/nodeDefSchema.ts +++ b/src/schemas/nodeDefSchema.ts @@ -341,6 +341,15 @@ export const zDynamicComboInputSpec = z.tuple([ }) ]) +export const zMatchTypeOptions = z.object({ + ...zBaseInputOptions.shape, + type: z.literal('COMFY_MATCHTYPE_V3'), + template: z.object({ + allowed_types: z.string(), + template_id: z.string() + }) +}) + // `/object_info` export type ComfyInputsSpec = z.infer export type ComfyOutputTypesSpec = z.infer diff --git a/src/services/nodeSearchService.ts b/src/services/nodeSearchService.ts index 75dccf2687..f9ca61e5bc 100644 --- a/src/services/nodeSearchService.ts +++ b/src/services/nodeSearchService.ts @@ -34,10 +34,7 @@ export class NodeSearchService { id: 'input', name: 'Input Type', invokeSequence: 'i', - getItemOptions: (node) => - Object.values(node.inputs ?? []).flatMap((input) => - input.type.split(',') - ), + getItemOptions: (node) => node.inputTypes, fuseOptions }) diff --git a/src/stores/nodeDefStore.ts b/src/stores/nodeDefStore.ts index e43267f50c..2c0703948a 100644 --- a/src/stores/nodeDefStore.ts +++ b/src/stores/nodeDefStore.ts @@ -6,6 +6,7 @@ import { computed, ref, watchEffect } from 'vue' import { t } from '@/i18n' import { isPromotedWidgetView } from '@/core/graph/subgraph/promotedWidgetTypes' import { resolvePromotedWidgetSource } from '@/core/graph/subgraph/resolvePromotedWidgetSource' +import { resolveInputType } from '@/core/graph/widgets/dynamicTypes' import { LiteGraph } from '@/lib/litegraph/src/litegraph' import type { LGraphNode } from '@/lib/litegraph/src/litegraph' import { transformNodeDefV1ToV2 } from '@/schemas/nodeDef/migration' @@ -98,6 +99,7 @@ export class ComfyNodeDefImpl // ComfyNodeDefImpl fields readonly nodeSource: NodeSource + readonly inputTypes: string[] /** * @internal @@ -179,6 +181,9 @@ export class ComfyNodeDefImpl // Initialize node source this.nodeSource = getNodeSource(obj.python_module, this.essentials_category) + this.inputTypes = _.uniq( + Object.values(this.inputs).flatMap(resolveInputType) + ) } get nodePath(): string {