fix: display Blueprint badge instead of UUID for global subgraph blueprints (#9048)

## Problem

The node search box badge displays long UUID strings (e.g.,
`comfyui-ltx-video-0fbc55c6-...`) for global/core blueprints, while
user-created subgraphs correctly show "Blueprint" as the badge.


![image](https://github.com/user-attachments/assets/placeholder-before.png)

## Root Cause

In `loadGlobalBlueprint`, global blueprints set `python_module:
v.info.node_pack` which contains UUID-like strings. The
`getNodeSource()` function processes `python_module` to determine badge
text, but UUID strings don't match any known pattern, resulting in ugly
badge text.

## Solution

- Change global blueprints to use `python_module: 'blueprint'` so they
display "Blueprint" badge like user blueprints
- Add `isGlobal` boolean flag to `ComfyNodeDef` schema to distinguish
global from user blueprints
- Update `isGlobalBlueprint()` to check the new `isGlobal` flag instead
of `python_module !== 'blueprint'`

## Changes

| File | Change |
|------|--------|
| `src/schemas/nodeDefSchema.ts` | Add optional `isGlobal?: boolean`
field |
| `src/stores/nodeDefStore.ts` | Add `isGlobal` field to
`ComfyNodeDefImpl` class |
| `src/stores/subgraphStore.ts` | Use `python_module: 'blueprint'` +
`isGlobal: true` for global blueprints; update `isGlobalBlueprint()`
check |

## Testing

- [x] Existing unit tests pass
- [x] TypeScript compiles without errors
- [x] Lint passes

Fixes COM-15168

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9048-fix-display-Blueprint-badge-instead-of-UUID-for-global-subgraph-blueprints-30e6d73d3650813cac27e02f8f2088df)
by [Unito](https://www.unito.io)
This commit is contained in:
Christian Byrne
2026-02-20 22:34:48 -08:00
committed by GitHub
parent dac58ad811
commit 1bcebd8293
4 changed files with 57 additions and 4 deletions

View File

@@ -298,7 +298,9 @@ export const zComfyNodeDef = z.object({
*/
price_badge: zPriceBadge.optional(),
/** Category for the Essentials tab. If set, the node appears in Essentials. */
essentials_category: z.string().optional()
essentials_category: z.string().optional(),
/** Whether the blueprint is a global/installed blueprint (not user-created). */
isGlobal: z.boolean().optional()
})
export const zAutogrowOptions = z.object({

View File

@@ -89,6 +89,8 @@ export class ComfyNodeDefImpl
readonly search_aliases?: string[]
/** Category for the Essentials tab. If set, the node appears in Essentials. */
readonly essentials_category?: string
/** Whether the blueprint is a global/installed blueprint (not user-created). */
readonly isGlobal?: boolean
// V2 fields
readonly inputs: Record<string, InputSpecV2>
@@ -165,6 +167,7 @@ export class ComfyNodeDefImpl
obj.name,
obj.essentials_category
)
this.isGlobal = obj.isGlobal
// Initialize V2 fields
const defV2 = transformNodeDefV1ToV2(obj)

View File

@@ -177,6 +177,54 @@ describe('useSubgraphStore', () => {
expect(store.isGlobalBlueprint('nonexistent')).toBe(false)
})
describe('blueprint badge display', () => {
it('should set isGlobal flag on global blueprints', async () => {
await mockFetch(
{},
{
global_bp: {
name: 'Global Blueprint',
info: { node_pack: 'some-uuid-string' },
data: JSON.stringify(mockGraph)
}
}
)
const nodeDef = useNodeDefStore().nodeDefs.find(
(d) => d.name === 'SubgraphBlueprint.global_bp'
)
expect(nodeDef).toBeDefined()
expect(nodeDef?.isGlobal).toBe(true)
})
it('should not set isGlobal flag on user blueprints', async () => {
await mockFetch({ 'user-blueprint.json': mockGraph })
const nodeDef = useNodeDefStore().nodeDefs.find(
(d) => d.name === 'SubgraphBlueprint.user-blueprint'
)
expect(nodeDef).toBeDefined()
expect(nodeDef?.isGlobal).toBeUndefined()
})
it('should use blueprint python_module for global blueprints to show Blueprint badge', async () => {
await mockFetch(
{},
{
global_bp: {
name: 'Global Blueprint',
info: { node_pack: 'comfyui-ltx-video-0fbc55c6-long-uuid' },
data: JSON.stringify(mockGraph)
}
}
)
const nodeDef = useNodeDefStore().nodeDefs.find(
(d) => d.name === 'SubgraphBlueprint.global_bp'
)
expect(nodeDef).toBeDefined()
expect(nodeDef?.python_module).toBe('blueprint')
expect(nodeDef?.nodeSource.displayText).toBe('Blueprint')
})
})
describe('search_aliases support', () => {
it('should include search_aliases from workflow extra', async () => {
const mockGraphWithAliases = {

View File

@@ -214,10 +214,10 @@ export const useSubgraphStore = defineStore('subgraph', () => {
registerNodeDef(
loaded,
{
python_module: v.info.node_pack,
display_name: v.name,
category,
search_aliases: v.info.search_aliases
search_aliases: v.info.search_aliases,
isGlobal: true
},
k
)
@@ -410,7 +410,7 @@ export const useSubgraphStore = defineStore('subgraph', () => {
function isGlobalBlueprint(name: string): boolean {
const nodeDef = subgraphDefCache.value.get(name)
return nodeDef !== undefined && nodeDef.python_module !== 'blueprint'
return nodeDef !== undefined && nodeDef.isGlobal === true
}
return {