mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-19 22:09:37 +00:00
## Changes
### Essential nodes
- Include blueprint nodes in essentials tab via `BLUEPRINT_PREFIX_MAP`
matching, removing dependency on backend `essentials_category`
- Sort essentials folders by `ESSENTIALS_CATEGORIES` order
- Disambiguate duplicate blueprint labels with provider suffix (e.g. two
"Text to image" → "Text to image (Flux 1)")
- Resolve blueprint icons by prefix instead of full node name
- Add 15 new SVG icons for blueprint categories, remove 2 old
subgraph-blueprint-specific SVGs
- Remove unnecessary parenthetical suffixes from unique display names
("Load style (LoRA)" → "Load style", "Text generation (LLM)" → "Text
generation")
essential nodes with blueprints icon
<img width="507" height="550" alt="image"
src="https://github.com/user-attachments/assets/967cd4b6-ea4d-44a2-9d6d-e66c152370c7"
/>
### All nodes panel
- section bottom change from `pb-6` to `pb-2`
<img width="525" height="215" alt="image"
src="https://github.com/user-attachments/assets/252cf655-3138-42f9-a9ef-9d771d3281e4"
/>
### Favorite to Bookmark
- change `Favorite node` to `Bookmark node`
- change `Unfavorite node` to `Unbookmark node`
- change `No favorites yet` to `No bookmarks yet`
<img width="495" height="380" alt="image"
src="https://github.com/user-attachments/assets/7ba6f631-15ae-4406-874b-737551ca441c"
/>
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
167 lines
5.6 KiB
TypeScript
167 lines
5.6 KiB
TypeScript
import type { EssentialsCategory } from '@/constants/essentialsNodes'
|
|
import { t } from '@/i18n'
|
|
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
|
|
|
|
const BLUEPRINT_PREFIX = 'SubgraphBlueprint.'
|
|
|
|
/**
|
|
* Static mapping of node names to their Essentials tab display name i18n keys.
|
|
*/
|
|
const EXACT_NAME_MAP: Record<string, string> = {
|
|
// Basics
|
|
LoadImage: 'essentials.loadImage',
|
|
SaveImage: 'essentials.saveImage',
|
|
LoadVideo: 'essentials.loadVideo',
|
|
SaveVideo: 'essentials.saveVideo',
|
|
Load3D: 'essentials.load3DModel',
|
|
SaveGLB: 'essentials.save3DModel',
|
|
PrimitiveStringMultiline: 'essentials.text',
|
|
|
|
// Image Tools
|
|
BatchImagesNode: 'essentials.batchImage',
|
|
ImageCrop: 'essentials.cropImage',
|
|
ImageScale: 'essentials.resizeImage',
|
|
ImageRotate: 'essentials.rotate',
|
|
ImageInvert: 'essentials.invert',
|
|
Canny: 'essentials.canny',
|
|
RecraftRemoveBackgroundNode: 'essentials.removeBackground',
|
|
ImageCompare: 'essentials.imageCompare',
|
|
|
|
// Video Tools
|
|
'Video Slice': 'essentials.extractFrame',
|
|
|
|
// Image Generation
|
|
LoraLoader: 'essentials.loadStyleLora',
|
|
|
|
// Video Generation
|
|
KlingLipSyncAudioToVideoNode: 'essentials.lipsync',
|
|
KlingLipSyncTextToVideoNode: 'essentials.lipsync',
|
|
|
|
// Text Generation
|
|
OpenAIChatNode: 'essentials.textGenerationLLM',
|
|
|
|
// 3D
|
|
TencentTextToModelNode: 'essentials.textTo3DModel',
|
|
TencentImageToModelNode: 'essentials.imageTo3DModel',
|
|
MeshyTextToModelNode: 'essentials.textTo3DModel',
|
|
MeshyImageToModelNode: 'essentials.imageTo3DModel',
|
|
TripoTextToModelNode: 'essentials.textTo3DModel',
|
|
TripoImageToModelNode: 'essentials.imageTo3DModel',
|
|
|
|
// Audio
|
|
StabilityTextToAudio: 'essentials.musicGeneration',
|
|
LoadAudio: 'essentials.loadAudio',
|
|
SaveAudio: 'essentials.saveAudio'
|
|
}
|
|
|
|
/**
|
|
* Blueprint prefix patterns mapped to display name i18n keys.
|
|
* Entries are matched by checking if the blueprint filename
|
|
* (after removing the SubgraphBlueprint. prefix) starts with the key.
|
|
* Ordered longest-first so more specific prefixes match before shorter ones.
|
|
*/
|
|
const BLUEPRINT_PREFIX_MAP: [
|
|
prefix: string,
|
|
displayNameKey: string,
|
|
category: EssentialsCategory
|
|
][] = [
|
|
// Image Generation
|
|
['image_inpainting_', 'essentials.inpaintImage', 'image generation'],
|
|
['image_outpainting_', 'essentials.outpaintImage', 'image generation'],
|
|
['image_edit', 'essentials.imageToImage', 'image generation'],
|
|
['text_to_image', 'essentials.textToImage', 'image generation'],
|
|
['pose_to_image', 'essentials.poseToImage', 'image generation'],
|
|
['canny_to_image', 'essentials.cannyToImage', 'image generation'],
|
|
['depth_to_image', 'essentials.depthToImage', 'image generation'],
|
|
|
|
// Video Generation
|
|
['text_to_video', 'essentials.textToVideo', 'video generation'],
|
|
['image_to_video', 'essentials.imageToVideo', 'video generation'],
|
|
['pose_to_video', 'essentials.poseToVideo', 'video generation'],
|
|
['canny_to_video', 'essentials.cannyToVideo', 'video generation'],
|
|
['depth_to_video', 'essentials.depthToVideo', 'video generation']
|
|
]
|
|
|
|
function resolveBlueprintDisplayName(
|
|
blueprintName: string
|
|
): string | undefined {
|
|
for (const [prefix, displayNameKey] of BLUEPRINT_PREFIX_MAP) {
|
|
if (blueprintName.startsWith(prefix)) {
|
|
return t(displayNameKey)
|
|
}
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
/**
|
|
* Resolves the icon class for a blueprint node based on its prefix.
|
|
* E.g. `SubgraphBlueprint.canny_to_image_flux` → `"icon-[comfy--canny-to-image]"`
|
|
*/
|
|
export function resolveBlueprintIcon(nodeName: string): string | undefined {
|
|
if (!nodeName.startsWith(BLUEPRINT_PREFIX)) return undefined
|
|
const blueprintName = nodeName.slice(BLUEPRINT_PREFIX.length)
|
|
for (const [prefix] of BLUEPRINT_PREFIX_MAP) {
|
|
if (blueprintName.startsWith(prefix)) {
|
|
const iconName = prefix.replace(/_$/, '').replaceAll('_', '-')
|
|
return `icon-[comfy--${iconName}]`
|
|
}
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
/**
|
|
* Extracts the provider/model suffix from a blueprint name for disambiguation.
|
|
* E.g. `SubgraphBlueprint.text_to_image_flux_1` → `"Flux 1"`
|
|
*/
|
|
export function resolveBlueprintSuffix(nodeName: string): string | undefined {
|
|
if (!nodeName.startsWith(BLUEPRINT_PREFIX)) return undefined
|
|
const blueprintName = nodeName.slice(BLUEPRINT_PREFIX.length)
|
|
for (const [prefix] of BLUEPRINT_PREFIX_MAP) {
|
|
if (blueprintName.startsWith(prefix)) {
|
|
const raw = blueprintName.slice(prefix.length).replace(/^_/, '')
|
|
if (!raw) return undefined
|
|
return raw
|
|
.split('_')
|
|
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
.join(' ')
|
|
}
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
/**
|
|
* Returns the essentials category for a blueprint node based on its name,
|
|
* or `undefined` if the blueprint doesn't belong in the essentials tab.
|
|
*/
|
|
export function resolveBlueprintEssentialsCategory(
|
|
nodeName: string
|
|
): EssentialsCategory | undefined {
|
|
if (!nodeName.startsWith(BLUEPRINT_PREFIX)) return undefined
|
|
const blueprintName = nodeName.slice(BLUEPRINT_PREFIX.length)
|
|
for (const [prefix, , category] of BLUEPRINT_PREFIX_MAP) {
|
|
if (blueprintName.startsWith(prefix)) {
|
|
return category
|
|
}
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
/**
|
|
* Resolves the Essentials tab display name for a given node definition.
|
|
* Returns `undefined` if the node has no Essentials display name mapping.
|
|
*/
|
|
export function resolveEssentialsDisplayName(
|
|
nodeDef: Pick<ComfyNodeDefImpl, 'name'> | undefined
|
|
): string | undefined {
|
|
if (!nodeDef) return undefined
|
|
const { name } = nodeDef
|
|
|
|
if (name.startsWith(BLUEPRINT_PREFIX)) {
|
|
const blueprintName = name.slice(BLUEPRINT_PREFIX.length)
|
|
return resolveBlueprintDisplayName(blueprintName)
|
|
}
|
|
|
|
const key = EXACT_NAME_MAP[name]
|
|
return key ? t(key) : undefined
|
|
}
|