mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-26 17:54:14 +00:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user