V2 Node Search (+ hidden Node Library changes) (#8987)

## Summary

Redesigned node search with categories

## Changes

- **What**: Adds a v2 search component, leaving the existing
implementation untouched
- It also brings onboard the incomplete node library & preview changes,
disabled and behind a hidden setting
- **Breaking**: Changes the 'default' value of the node search setting
to v2, adding v1 (legacy) as an option

## Screenshots (if applicable)




https://github.com/user-attachments/assets/2ab797df-58f0-48e8-8b20-2a1809e3735f

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8987-V2-Node-Search-hidden-Node-Library-changes-30c6d73d36508160902bcb92553f147c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Yourz <crazilou@vip.qq.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
This commit is contained in:
pythongosssss
2026-02-20 09:10:03 +00:00
committed by GitHub
parent 8f5cdead73
commit 6902e38e6a
183 changed files with 7972 additions and 127 deletions

View File

@@ -3,16 +3,20 @@ import { buildNodeDefTree } from '@/stores/nodeDefStore'
import type {
NodeGroupingStrategy,
NodeOrganizationOptions,
NodeSortStrategy
NodeSection,
NodeSortStrategy,
TabId
} from '@/types/nodeOrganizationTypes'
import { NodeSourceType } from '@/types/nodeSource'
import type { TreeNode } from '@/types/treeExplorerTypes'
import { sortedTree } from '@/utils/treeUtil'
import { upperCase } from 'es-toolkit/string'
const DEFAULT_ICON = 'pi pi-sort'
export const DEFAULT_GROUPING_ID = 'category' as const
export const DEFAULT_SORTING_ID = 'original' as const
export const DEFAULT_TAB_ID = 'all' as const
class NodeOrganizationService {
private readonly groupingStrategies: NodeGroupingStrategy[] = [
@@ -112,6 +116,98 @@ class NodeOrganizationService {
return this.sortingStrategies.find((strategy) => strategy.id === id)
}
organizeNodesByTab(
nodes: ComfyNodeDefImpl[],
tabId: TabId = DEFAULT_TAB_ID
): NodeSection[] {
const categoryPathExtractor = (nodeDef: ComfyNodeDefImpl) => {
const category = nodeDef.category || ''
const categoryParts = category ? category.split('/') : []
return [...categoryParts, nodeDef.name]
}
switch (tabId) {
case 'essentials': {
const essentialNodes = nodes.filter(
(nodeDef) => nodeDef.essentials_category !== undefined
)
const essentialsPathExtractor = (nodeDef: ComfyNodeDefImpl) => {
const folder = nodeDef.essentials_category || ''
return folder ? [folder, nodeDef.name] : [nodeDef.name]
}
const tree = buildNodeDefTree(essentialNodes, {
pathExtractor: essentialsPathExtractor
})
const folderOrder = [
'basics',
'text generation',
'image generation',
'video generation',
'image tools',
'video tools',
'audio',
'3D'
]
if (tree.children) {
const len = folderOrder.length
const originalIndex = new Map(
tree.children.map((child, i) => [child, i])
)
tree.children.sort((a, b) => {
const ai = folderOrder.indexOf(a.label ?? '')
const bi = folderOrder.indexOf(b.label ?? '')
const orderA = ai === -1 ? len + originalIndex.get(a)! : ai
const orderB = bi === -1 ? len + originalIndex.get(b)! : bi
return orderA - orderB
})
}
return [{ tree }]
}
case 'custom': {
const customNodes = nodes.filter(
(nodeDef) => nodeDef.nodeSource.type === NodeSourceType.CustomNodes
)
const groupedByMainCategory = new Map<string, ComfyNodeDefImpl[]>()
for (const node of customNodes) {
const mainCategory = node.main_category ?? 'custom_extensions'
if (!groupedByMainCategory.has(mainCategory)) {
groupedByMainCategory.set(mainCategory, [])
}
groupedByMainCategory.get(mainCategory)!.push(node)
}
return Array.from(groupedByMainCategory.entries()).map(
([mainCategory, categoryNodes]) => ({
title: upperCase(mainCategory),
tree: buildNodeDefTree(categoryNodes, {
pathExtractor: categoryPathExtractor
})
})
)
}
case 'all':
default: {
const groupedByMainCategory = new Map<string, ComfyNodeDefImpl[]>()
for (const node of nodes) {
const mainCategory = node.main_category ?? 'basics'
if (!groupedByMainCategory.has(mainCategory)) {
groupedByMainCategory.set(mainCategory, [])
}
groupedByMainCategory.get(mainCategory)!.push(node)
}
return Array.from(groupedByMainCategory.entries()).map(
([mainCategory, categoryNodes]) => ({
title: upperCase(mainCategory),
tree: buildNodeDefTree(categoryNodes, {
pathExtractor: categoryPathExtractor
})
})
)
}
}
}
organizeNodes(
nodes: ComfyNodeDefImpl[],
options: NodeOrganizationOptions = {}
@@ -131,9 +227,9 @@ class NodeOrganizationService {
}
const sortedNodes =
sortingStrategy.id !== 'original'
? [...nodes].sort(sortingStrategy.compare)
: nodes
sortingStrategy.id === 'original'
? nodes
: [...nodes].sort(sortingStrategy.compare)
const tree = buildNodeDefTree(sortedNodes, {
pathExtractor: groupingStrategy.getNodePath