mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-07 06:00:03 +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:
87
src/utils/categoryUtil.test.ts
Normal file
87
src/utils/categoryUtil.test.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
generateCategoryId,
|
||||
getCategoryIcon,
|
||||
getProviderBorderStyle,
|
||||
getProviderIcon
|
||||
} from './categoryUtil'
|
||||
|
||||
describe('getCategoryIcon', () => {
|
||||
it('returns mapped icon for known category', () => {
|
||||
expect(getCategoryIcon('all')).toBe('icon-[lucide--list]')
|
||||
expect(getCategoryIcon('image')).toBe('icon-[lucide--image]')
|
||||
expect(getCategoryIcon('video')).toBe('icon-[lucide--film]')
|
||||
})
|
||||
|
||||
it('returns folder icon for unknown category', () => {
|
||||
expect(getCategoryIcon('unknown-category')).toBe('icon-[lucide--folder]')
|
||||
})
|
||||
|
||||
it('is case insensitive', () => {
|
||||
expect(getCategoryIcon('ALL')).toBe('icon-[lucide--list]')
|
||||
expect(getCategoryIcon('Image')).toBe('icon-[lucide--image]')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getProviderIcon', () => {
|
||||
it('returns icon class for simple provider name', () => {
|
||||
expect(getProviderIcon('BFL')).toBe('icon-[comfy--bfl]')
|
||||
expect(getProviderIcon('OpenAI')).toBe('icon-[comfy--openai]')
|
||||
})
|
||||
|
||||
it('converts spaces to hyphens', () => {
|
||||
expect(getProviderIcon('Stability AI')).toBe('icon-[comfy--stability-ai]')
|
||||
expect(getProviderIcon('Moonvalley Marey')).toBe(
|
||||
'icon-[comfy--moonvalley-marey]'
|
||||
)
|
||||
})
|
||||
|
||||
it('converts to lowercase', () => {
|
||||
expect(getProviderIcon('GEMINI')).toBe('icon-[comfy--gemini]')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getProviderBorderStyle', () => {
|
||||
it('returns solid color for single-color providers', () => {
|
||||
expect(getProviderBorderStyle('BFL')).toBe('#ffffff')
|
||||
expect(getProviderBorderStyle('OpenAI')).toBe('#B6B6B6')
|
||||
expect(getProviderBorderStyle('Bria')).toBe('#B6B6B6')
|
||||
})
|
||||
|
||||
it('returns gradient for dual-color providers', () => {
|
||||
expect(getProviderBorderStyle('Gemini')).toBe(
|
||||
'linear-gradient(90deg, #3186FF, #FABC12)'
|
||||
)
|
||||
expect(getProviderBorderStyle('Stability AI')).toBe(
|
||||
'linear-gradient(90deg, #9D39FF, #E80000)'
|
||||
)
|
||||
})
|
||||
|
||||
it('returns fallback color for unknown providers', () => {
|
||||
expect(getProviderBorderStyle('Unknown Provider')).toBe('#525252')
|
||||
})
|
||||
|
||||
it('handles provider names with spaces', () => {
|
||||
expect(getProviderBorderStyle('Stability AI')).toBe(
|
||||
'linear-gradient(90deg, #9D39FF, #E80000)'
|
||||
)
|
||||
expect(getProviderBorderStyle('Moonvalley Marey')).toBe('#DAD9C5')
|
||||
})
|
||||
})
|
||||
|
||||
describe('generateCategoryId', () => {
|
||||
it('generates category ID from group and title', () => {
|
||||
expect(generateCategoryId('Generation', 'Image')).toBe('generation-image')
|
||||
})
|
||||
|
||||
it('converts spaces to hyphens', () => {
|
||||
expect(generateCategoryId('API Nodes', 'Open Source')).toBe(
|
||||
'api-nodes-open-source'
|
||||
)
|
||||
})
|
||||
|
||||
it('converts to lowercase', () => {
|
||||
expect(generateCategoryId('GENERATION', 'VIDEO')).toBe('generation-video')
|
||||
})
|
||||
})
|
||||
@@ -51,6 +51,79 @@ export const getCategoryIcon = (categoryId: string): string => {
|
||||
return iconMap[categoryId.toLowerCase()] || 'icon-[lucide--folder]'
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider brand colors extracted from SVG icons.
|
||||
* Each entry can be a single color or [color1, color2] for gradient.
|
||||
*/
|
||||
const PROVIDER_COLORS: Record<string, string | [string, string]> = {
|
||||
bfl: '#ffffff',
|
||||
bria: '#B6B6B6',
|
||||
bytedance: ['#00C8D2', '#325AB4'],
|
||||
gemini: ['#3186FF', '#FABC12'],
|
||||
grok: '#B6B6B6',
|
||||
hitpaw: '#B6B6B6',
|
||||
ideogram: '#B6B6B6',
|
||||
kling: ['#0BF2F9', '#FFF959'],
|
||||
ltxv: '#B6B6B6',
|
||||
luma: ['#004EFF', '#00FFFF'],
|
||||
magnific: ['#EA5A3D', '#F1A64A'],
|
||||
meshy: ['#67B700', '#FA418C'],
|
||||
minimax: ['#E2167E', '#FE603C'],
|
||||
'moonvalley-marey': '#DAD9C5',
|
||||
openai: '#B6B6B6',
|
||||
pixverse: ['#B465E6', '#E8632A'],
|
||||
recraft: '#B6B6B6',
|
||||
rodin: '#F7F7F7',
|
||||
runway: '#B6B6B6',
|
||||
sora: ['#6BB6FE', '#ffffff'],
|
||||
'stability-ai': ['#9D39FF', '#E80000'],
|
||||
tencent: ['#004BE5', '#00B3FE'],
|
||||
topaz: '#B6B6B6',
|
||||
tripo: ['#F6D85A', '#B6B6B6'],
|
||||
veo: ['#4285F4', '#EB4335'],
|
||||
vidu: ['#047FFE', '#40EDD8'],
|
||||
wan: ['#6156EC', '#F4F3FD'],
|
||||
wavespeed: '#B6B6B6'
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the provider name from a node category path.
|
||||
* e.g. "api/image/BFL" -> "BFL"
|
||||
*/
|
||||
export function getProviderName(category: string): string {
|
||||
return category.split('/').at(-1) ?? ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the icon class for an API node provider (e.g., BFL, OpenAI, Stability AI)
|
||||
* @param providerName - The provider name from the node category
|
||||
* @returns The icon class string (e.g., 'icon-[comfy--bfl]')
|
||||
*/
|
||||
export function getProviderIcon(providerName: string): string {
|
||||
const iconKey = providerName.toLowerCase().replaceAll(/\s+/g, '-')
|
||||
return `icon-[comfy--${iconKey}]`
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the border color(s) for an API node provider badge.
|
||||
* @param providerName - The provider name from the node category
|
||||
* @returns CSS color string or gradient definition
|
||||
*/
|
||||
export function getProviderBorderStyle(providerName: string): string {
|
||||
const iconKey = providerName.toLowerCase().replaceAll(/\s+/g, '-')
|
||||
const colors = PROVIDER_COLORS[iconKey]
|
||||
|
||||
if (!colors) {
|
||||
return '#525252' // neutral-600 fallback
|
||||
}
|
||||
|
||||
if (Array.isArray(colors)) {
|
||||
return `linear-gradient(90deg, ${colors[0]}, ${colors[1]})`
|
||||
}
|
||||
|
||||
return colors
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique category ID from a category group and title
|
||||
*/
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import _ from 'es-toolkit/compat'
|
||||
|
||||
import type {
|
||||
ColorOption,
|
||||
LGraph,
|
||||
LGraphCanvas
|
||||
} from '@/lib/litegraph/src/litegraph'
|
||||
import type { ColorOption, LGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import type { ExecutedWsMessage } from '@/schemas/apiSchema'
|
||||
import {
|
||||
LGraphCanvas,
|
||||
LGraphGroup,
|
||||
LGraphNode,
|
||||
LiteGraph,
|
||||
@@ -303,6 +300,10 @@ function compressSubgraphWidgetInputSlots(
|
||||
}
|
||||
}
|
||||
|
||||
export function getLinkTypeColor(typeName: string): string {
|
||||
return LGraphCanvas.link_type_colors[typeName] ?? LiteGraph.LINK_COLOR
|
||||
}
|
||||
|
||||
export function isLoad3dNode(node: LGraphNode) {
|
||||
return (
|
||||
node &&
|
||||
|
||||
Reference in New Issue
Block a user