Node source/id badge (#781)

* Add basic node badge

* Node source badge

* Prevent manager badge rendering

* Update litegraph (Badge support)

* Add playwright tests

* Separate nodes

* nit

* Checkout devtools repo for browser test expectation CI

* Fix failing unittests

* Rename setting

* Hide all badges in playwright tests

* Handle group node

* Update test expectations [skip ci]

* Fix unittest

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Chenlei Hu
2024-09-12 09:36:06 +09:00
committed by GitHub
parent f2a30ec197
commit 80ca1808f0
29 changed files with 301 additions and 29 deletions

View File

@@ -24,6 +24,11 @@ jobs:
repository: "Comfy-Org/ComfyUI_frontend" repository: "Comfy-Org/ComfyUI_frontend"
path: "ComfyUI_frontend" path: "ComfyUI_frontend"
ref: ${{ github.head_ref }} ref: ${{ github.head_ref }}
- name: Checkout ComfyUI_devtools
uses: actions/checkout@v4
with:
repository: "Comfy-Org/ComfyUI_devtools"
path: "ComfyUI/custom_nodes/ComfyUI_devtools"
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: lts/* node-version: lts/*

View File

@@ -3,7 +3,7 @@ import { test as base } from '@playwright/test'
import dotenv from 'dotenv' import dotenv from 'dotenv'
dotenv.config() dotenv.config()
import * as fs from 'fs' import * as fs from 'fs'
import * as path from 'path' import { NodeBadgeMode } from '../src/types/nodeSource'
interface Position { interface Position {
x: number x: number
@@ -202,6 +202,13 @@ export class ComfyPage {
// Reset view to force re-rendering of canvas. So that info fields like fps // Reset view to force re-rendering of canvas. So that info fields like fps
// become hidden. // become hidden.
await this.resetView() await this.resetView()
// Hide all badges by default.
await this.setSetting('Comfy.NodeBadge.NodeIdBadgeMode', NodeBadgeMode.None)
await this.setSetting(
'Comfy.NodeBadge.NodeSourceBadgeMode',
NodeBadgeMode.None
)
} }
public assetPath(fileName: string) { public assetPath(fileName: string) {

View File

@@ -5,10 +5,10 @@
{ {
"id": 14, "id": 14,
"type": "PreviewImage", "type": "PreviewImage",
"pos": [ "pos": {
858, "0": 300,
-41 "1": 60
], },
"size": { "size": {
"0": 213.8594970703125, "0": 213.8594970703125,
"1": 50.65289306640625 "1": 50.65289306640625
@@ -23,6 +23,7 @@
"link": 15 "link": 15
} }
], ],
"outputs": [],
"properties": { "properties": {
"Node name for S&R": "PreviewImage" "Node name for S&R": "PreviewImage"
} }
@@ -30,10 +31,10 @@
{ {
"id": 17, "id": 17,
"type": "DevToolsErrorRaiseNode", "type": "DevToolsErrorRaiseNode",
"pos": [ "pos": {
477, "0": 20,
-40 "1": 60
], },
"size": { "size": {
"0": 210, "0": 210,
"1": 26 "1": 26
@@ -41,6 +42,7 @@
"flags": {}, "flags": {},
"order": 0, "order": 0,
"mode": 0, "mode": 0,
"inputs": [],
"outputs": [ "outputs": [
{ {
"name": "IMAGE", "name": "IMAGE",
@@ -71,10 +73,10 @@
"config": {}, "config": {},
"extra": { "extra": {
"ds": { "ds": {
"scale": 1.2100000000000006, "scale": 1,
"offset": [ "offset": [
-266.1038310281165, 117.20766722169206,
337.94335447664554 472.69035116826046
] ]
} }
}, },

View File

@@ -0,0 +1,79 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from './ComfyPage'
import type { ComfyApp } from '../src/scripts/app'
import { NodeBadgeMode } from '../src/types/nodeSource'
test.describe('Node Badge', () => {
test('Can add badge', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
const LGraphBadge = window['LGraphBadge']
const app = window['app'] as ComfyApp
const graph = app.graph
// @ts-expect-error - accessing private property
const nodes = graph._nodes
for (const node of nodes) {
node.badges = [new LGraphBadge({ text: 'Test Badge' })]
}
graph.setDirtyCanvas(true, true)
})
await expect(comfyPage.canvas).toHaveScreenshot('node-badge.png')
})
test('Can add multiple badges', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
const LGraphBadge = window['LGraphBadge']
const app = window['app'] as ComfyApp
const graph = app.graph
// @ts-expect-error - accessing private property
const nodes = graph._nodes
for (const node of nodes) {
node.badges = [
new LGraphBadge({ text: 'Test Badge 1' }),
new LGraphBadge({ text: 'Test Badge 2' })
]
}
graph.setDirtyCanvas(true, true)
})
await expect(comfyPage.canvas).toHaveScreenshot('node-badge-multiple.png')
})
test('Can add badge left-side', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
const LGraphBadge = window['LGraphBadge']
const app = window['app'] as ComfyApp
const graph = app.graph
// @ts-expect-error - accessing private property
const nodes = graph._nodes
for (const node of nodes) {
node.badges = [new LGraphBadge({ text: 'Test Badge' })]
// @ts-expect-error - Enum value
node.badgePosition = 'top-left'
}
graph.setDirtyCanvas(true, true)
})
await expect(comfyPage.canvas).toHaveScreenshot('node-badge-left.png')
})
})
test.describe('Node source badge', () => {
Object.values(NodeBadgeMode).forEach(async (mode) => {
test(`Shows node badges (${mode})`, async ({ comfyPage }) => {
// Execution error workflow has both custom node and core node.
await comfyPage.loadWorkflow('execution_error')
await comfyPage.setSetting('Comfy.NodeBadge.NodeSourceBadgeMode', mode)
await comfyPage.setSetting('Comfy.NodeBadge.NodeIdBadgeMode', mode)
await comfyPage.nextFrame()
await comfyPage.resetView()
await expect(comfyPage.canvas).toHaveScreenshot(`node-badge-${mode}.png`)
})
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

8
package-lock.json generated
View File

@@ -9,7 +9,7 @@
"version": "1.2.48", "version": "1.2.48",
"dependencies": { "dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.2.1", "@atlaskit/pragmatic-drag-and-drop": "^1.2.1",
"@comfyorg/litegraph": "^0.7.65", "@comfyorg/litegraph": "^0.7.67",
"@primevue/themes": "^4.0.5", "@primevue/themes": "^4.0.5",
"@vitejs/plugin-vue": "^5.0.5", "@vitejs/plugin-vue": "^5.0.5",
"@vueuse/core": "^11.0.0", "@vueuse/core": "^11.0.0",
@@ -1909,9 +1909,9 @@
"dev": true "dev": true
}, },
"node_modules/@comfyorg/litegraph": { "node_modules/@comfyorg/litegraph": {
"version": "0.7.65", "version": "0.7.67",
"resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.7.65.tgz", "resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.7.67.tgz",
"integrity": "sha512-Yau14XPptHRmk/2My46mfkQBrZhZgtib75mghgwPelt2oRrk+O4OrhtbPQRzYoll7LmJAA7cHA9YI+zZbJ6IaA==", "integrity": "sha512-X8eRpBmSGTahJteNFDG9P0IsHXOk4QDU3p3iWPhk0rGfTnl4RZ8YcJ8MVo7zRgF3qxxX/Tcw4RpelhnjBJe4Gg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@cspotcode/source-map-support": { "node_modules/@cspotcode/source-map-support": {

View File

@@ -62,7 +62,7 @@
}, },
"dependencies": { "dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.2.1", "@atlaskit/pragmatic-drag-and-drop": "^1.2.1",
"@comfyorg/litegraph": "^0.7.65", "@comfyorg/litegraph": "^0.7.67",
"@primevue/themes": "^4.0.5", "@primevue/themes": "^4.0.5",
"@vitejs/plugin-vue": "^5.0.5", "@vitejs/plugin-vue": "^5.0.5",
"@vueuse/core": "^11.0.0", "@vueuse/core": "^11.0.0",

View File

@@ -32,7 +32,8 @@ import {
LGraphGroup, LGraphGroup,
DragAndScale, DragAndScale,
LGraphCanvas, LGraphCanvas,
ContextMenu ContextMenu,
LGraphBadge
} from '@comfyorg/litegraph' } from '@comfyorg/litegraph'
import type { RenderedTreeExplorerNode } from '@/types/treeExplorerTypes' import type { RenderedTreeExplorerNode } from '@/types/treeExplorerTypes'
import { useNodeBookmarkStore } from '@/stores/nodeBookmarkStore' import { useNodeBookmarkStore } from '@/stores/nodeBookmarkStore'
@@ -98,6 +99,7 @@ onMounted(async () => {
window['DragAndScale'] = DragAndScale window['DragAndScale'] = DragAndScale
window['LGraphCanvas'] = LGraphCanvas window['LGraphCanvas'] = LGraphCanvas
window['ContextMenu'] = ContextMenu window['ContextMenu'] = ContextMenu
window['LGraphBadge'] = LGraphBadge
comfyApp.vueAppReady = true comfyApp.vueAppReady = true

View File

@@ -5,7 +5,7 @@ import { LGraphCanvas, LiteGraph } from '@comfyorg/litegraph'
// Manage color palettes // Manage color palettes
const colorPalettes: ColorPalettes = { export const colorPalettes: ColorPalettes = {
dark: { dark: {
id: 'dark', id: 'dark',
name: 'Dark (Default)', name: 'Dark (Default)',
@@ -52,7 +52,10 @@ const colorPalettes: ColorPalettes = {
LINK_COLOR: '#9A9', LINK_COLOR: '#9A9',
EVENT_LINK_COLOR: '#A86', EVENT_LINK_COLOR: '#A86',
CONNECTING_LINK_COLOR: '#AFA' CONNECTING_LINK_COLOR: '#AFA',
BADGE_FG_COLOR: '#FFF',
BADGE_BG_COLOR: '#0F1F0F'
}, },
comfy_base: { comfy_base: {
'fg-color': '#fff', 'fg-color': '#fff',
@@ -114,7 +117,10 @@ const colorPalettes: ColorPalettes = {
LINK_COLOR: '#4CAF50', LINK_COLOR: '#4CAF50',
EVENT_LINK_COLOR: '#FF9800', EVENT_LINK_COLOR: '#FF9800',
CONNECTING_LINK_COLOR: '#2196F3' CONNECTING_LINK_COLOR: '#2196F3',
BADGE_FG_COLOR: '#000',
BADGE_BG_COLOR: '#FFF'
}, },
comfy_base: { comfy_base: {
'fg-color': '#222', 'fg-color': '#222',

View File

@@ -17,9 +17,11 @@ const ext = {
const filter = document.createElement('input') const filter = document.createElement('input')
filter.classList.add('comfy-context-menu-filter') filter.classList.add('comfy-context-menu-filter')
filter.placeholder = 'Filter list' filter.placeholder = 'Filter list'
// @ts-expect-error
ctx.root.prepend(filter) ctx.root.prepend(filter)
const items = Array.from( const items = Array.from(
// @ts-expect-error
ctx.root.querySelectorAll('.litemenu-entry') ctx.root.querySelectorAll('.litemenu-entry')
) as HTMLElement[] ) as HTMLElement[]
let displayedItems = [...items] let displayedItems = [...items]
@@ -61,14 +63,18 @@ const ext = {
} }
const positionList = () => { const positionList = () => {
// @ts-expect-error
const rect = ctx.root.getBoundingClientRect() const rect = ctx.root.getBoundingClientRect()
// If the top is off-screen then shift the element with scaling applied // If the top is off-screen then shift the element with scaling applied
if (rect.top < 0) { if (rect.top < 0) {
const scale = const scale =
1 - 1 -
// @ts-expect-error
ctx.root.getBoundingClientRect().height / ctx.root.clientHeight ctx.root.getBoundingClientRect().height / ctx.root.clientHeight
// @ts-expect-error
const shift = (ctx.root.clientHeight * scale) / 2 const shift = (ctx.root.clientHeight * scale) / 2
// @ts-expect-error
ctx.root.style.top = -shift + 'px' ctx.root.style.top = -shift + 'px'
} }
} }
@@ -139,6 +145,7 @@ const ext = {
let top = options.event.clientY - 10 let top = options.event.clientY - 10
const bodyRect = document.body.getBoundingClientRect() const bodyRect = document.body.getBoundingClientRect()
// @ts-expect-error
const rootRect = ctx.root.getBoundingClientRect() const rootRect = ctx.root.getBoundingClientRect()
if ( if (
bodyRect.height && bodyRect.height &&
@@ -147,6 +154,7 @@ const ext = {
top = Math.max(0, bodyRect.height - rootRect.height - 10) top = Math.max(0, bodyRect.height - rootRect.height - 10)
} }
// @ts-expect-error
ctx.root.style.top = top + 'px' ctx.root.style.top = top + 'px'
positionList() positionList()
} }

View File

@@ -21,3 +21,4 @@ import './uploadImage'
import './webcamCapture' import './webcamCapture'
import './widgetInputs' import './widgetInputs'
import './uploadAudio' import './uploadAudio'
import './nodeBadge'

View File

@@ -0,0 +1,117 @@
import { app, type ComfyApp } from '@/scripts/app'
import type { ComfyExtension } from '@/types/comfy'
import type { ComfyLGraphNode } from '@/types/comfyLGraphNode'
import { LGraphBadge } from '@comfyorg/litegraph'
import { useSettingStore } from '@/stores/settingStore'
import { computed, ComputedRef, watch } from 'vue'
import {
getNodeSource as getNodeSourceFromPythonModule,
NodeBadgeMode
} from '@/types/nodeSource'
import _ from 'lodash'
import { colorPalettes } from './colorPalette'
import { BadgePosition } from '@comfyorg/litegraph'
import type { Palette } from '@/types/colorPalette'
function getNodeSource(node: ComfyLGraphNode) {
const pythonModule = (node.constructor as typeof ComfyLGraphNode).nodeData
?.python_module
return pythonModule ? getNodeSourceFromPythonModule(pythonModule) : null
}
function isCoreNode(node: ComfyLGraphNode) {
return getNodeSource(node)?.type === 'core'
}
function getNodeIdBadge(node: ComfyLGraphNode, nodeIdBadgeMode: NodeBadgeMode) {
return nodeIdBadgeMode === NodeBadgeMode.None ||
(isCoreNode(node) && nodeIdBadgeMode === NodeBadgeMode.HideBuiltIn)
? ''
: `#${node.id}`
}
function getNodeSourceBadge(
node: ComfyLGraphNode,
nodeSourceBadgeMode: NodeBadgeMode
) {
const nodeSource = getNodeSource(node)
return nodeSourceBadgeMode === NodeBadgeMode.None ||
(isCoreNode(node) && nodeSourceBadgeMode === NodeBadgeMode.HideBuiltIn)
? ''
: nodeSource?.badgeText ?? ''
}
class NodeBadgeExtension implements ComfyExtension {
name = 'Comfy.NodeBadge'
constructor(
public nodeIdBadgeMode: ComputedRef<NodeBadgeMode> | null = null,
public nodeSourceBadgeMode: ComputedRef<NodeBadgeMode> | null = null,
public colorPalette: ComputedRef<Palette> | null = null,
public defaultColorPalette: Palette | null = null
) {}
init(app: ComfyApp) {
if (!app.vueAppReady) {
return
}
const settingStore = useSettingStore()
this.nodeSourceBadgeMode = computed(
() =>
settingStore.get('Comfy.NodeBadge.NodeSourceBadgeMode') as NodeBadgeMode
)
this.nodeIdBadgeMode = computed(
() => settingStore.get('Comfy.NodeBadge.NodeIdBadgeMode') as NodeBadgeMode
)
this.colorPalette = computed(
() => colorPalettes[settingStore.get('Comfy.ColorPalette')]
)
this.defaultColorPalette = colorPalettes['dark']
watch(this.nodeSourceBadgeMode, () => {
app.graph.setDirtyCanvas(true, true)
})
watch(this.nodeIdBadgeMode, () => {
app.graph.setDirtyCanvas(true, true)
})
}
nodeCreated(node: ComfyLGraphNode, app: ComfyApp) {
if (!app.vueAppReady) {
return
}
node.badgePosition = BadgePosition.TopRight
// @ts-expect-error Disable ComfyUI-Manager's badge drawing by setting badge_enabled to true. Remove this when ComfyUI-Manager's badge drawing is removed.
node.badge_enabled = true
const badge = computed(
() =>
new LGraphBadge({
text: _.truncate(
[
getNodeIdBadge(node, this.nodeIdBadgeMode.value),
getNodeSourceBadge(node, this.nodeSourceBadgeMode.value)
]
.filter((s) => s.length > 0)
.join(' '),
{
length: 25
}
),
fgColor:
this.colorPalette.value.colors.litegraph_base?.BADGE_FG_COLOR ||
this.defaultColorPalette.colors.litegraph_base.BADGE_FG_COLOR,
bgColor:
this.colorPalette.value.colors.litegraph_base?.BADGE_BG_COLOR ||
this.defaultColorPalette.colors.litegraph_base.BADGE_BG_COLOR
})
)
node.badges.push(() => badge.value)
}
}
app.registerExtension(new NodeBadgeExtension())

View File

@@ -51,6 +51,7 @@ import { useToastStore } from '@/stores/toastStore'
import { ModelStore, useModelStore } from '@/stores/modelStore' import { ModelStore, useModelStore } from '@/stores/modelStore'
import type { ToastMessageOptions } from 'primevue/toast' import type { ToastMessageOptions } from 'primevue/toast'
import { useWorkspaceStore } from '@/stores/workspaceStateStore' import { useWorkspaceStore } from '@/stores/workspaceStateStore'
import { ComfyLGraphNode } from '@/types/comfyLGraphNode'
export const ANIM_PREVIEW_WIDGET = '$$comfy_animation_preview' export const ANIM_PREVIEW_WIDGET = '$$comfy_animation_preview'
@@ -2007,12 +2008,12 @@ export class ComfyApp {
async registerNodeDef(nodeId: string, nodeData: ComfyNodeDef) { async registerNodeDef(nodeId: string, nodeData: ComfyNodeDef) {
const self = this const self = this
const node = class ComfyNode extends LGraphNode { const node: new () => ComfyLGraphNode = class ComfyNode extends LGraphNode {
static comfyClass? = nodeData.name static comfyClass? = nodeData.name
// TODO: change to "title?" once litegraph.d.ts has been updated // TODO: change to "title?" once litegraph.d.ts has been updated
static title = nodeData.display_name || nodeData.name static title = nodeData.display_name || nodeData.name
static nodeData? = nodeData static nodeData? = nodeData
static category?: string static category: string = nodeData.category
constructor(title?: string) { constructor(title?: string) {
super(title) super(title)
@@ -2083,7 +2084,6 @@ export class ComfyApp {
app.#invokeExtensionsAsync('nodeCreated', this) app.#invokeExtensionsAsync('nodeCreated', this)
} }
} }
// @ts-expect-error
node.prototype.comfyClass = nodeData.name node.prototype.comfyClass = nodeData.name
this.#addNodeContextMenuHandler(node) this.#addNodeContextMenuHandler(node)
@@ -2092,7 +2092,6 @@ export class ComfyApp {
await this.#invokeExtensionsAsync('beforeRegisterNodeDef', node, nodeData) await this.#invokeExtensionsAsync('beforeRegisterNodeDef', node, nodeData)
LiteGraph.registerNodeType(nodeId, node) LiteGraph.registerNodeType(nodeId, node)
node.category = nodeData.category
} }
async registerNodesFromDefs(defs: Record<string, ComfyNodeDef>) { async registerNodesFromDefs(defs: Record<string, ComfyNodeDef>) {

View File

@@ -10,6 +10,7 @@
import { app } from '@/scripts/app' import { app } from '@/scripts/app'
import { ComfySettingsDialog } from '@/scripts/ui/settings' import { ComfySettingsDialog } from '@/scripts/ui/settings'
import { Settings } from '@/types/apiTypes' import { Settings } from '@/types/apiTypes'
import { NodeBadgeMode } from '@/types/nodeSource'
import { import {
LinkReleaseTriggerAction, LinkReleaseTriggerAction,
LinkReleaseTriggerMode LinkReleaseTriggerMode
@@ -330,6 +331,22 @@ export const useSettingStore = defineStore('setting', {
options: ['en', 'zh'], options: ['en', 'zh'],
defaultValue: navigator.language.split('-')[0] || 'en' defaultValue: navigator.language.split('-')[0] || 'en'
}) })
app.ui.settings.addSetting({
id: 'Comfy.NodeBadge.NodeSourceBadgeMode',
name: 'Node source badge mode',
type: 'combo',
options: Object.values(NodeBadgeMode),
defaultValue: NodeBadgeMode.HideBuiltIn
})
app.ui.settings.addSetting({
id: 'Comfy.NodeBadge.NodeIdBadgeMode',
name: 'Node ID badge mode',
type: 'combo',
options: [NodeBadgeMode.None, NodeBadgeMode.ShowAll],
defaultValue: NodeBadgeMode.ShowAll
})
}, },
set<K extends keyof Settings>(key: K, value: Settings[K]) { set<K extends keyof Settings>(key: K, value: Settings[K]) {

View File

@@ -3,6 +3,7 @@ import { zComfyWorkflow, zNodeId } from './comfyWorkflow'
import { fromZodError } from 'zod-validation-error' import { fromZodError } from 'zod-validation-error'
import { colorPalettesSchema } from './colorPalette' import { colorPalettesSchema } from './colorPalette'
import { LinkReleaseTriggerAction } from './searchBoxTypes' import { LinkReleaseTriggerAction } from './searchBoxTypes'
import { NodeBadgeMode } from './nodeSource'
const zNodeType = z.string() const zNodeType = z.string()
const zQueueIndex = z.number() const zQueueIndex = z.number()
@@ -424,6 +425,10 @@ const zLinkReleaseTriggerAction = z.enum(
Object.values(LinkReleaseTriggerAction) as [string, ...string[]] Object.values(LinkReleaseTriggerAction) as [string, ...string[]]
) )
const zNodeBadgeMode = z.enum(
Object.values(NodeBadgeMode) as [string, ...string[]]
)
const zSettings = z.record(z.any()).and( const zSettings = z.record(z.any()).and(
z z
.object({ .object({
@@ -484,7 +489,9 @@ const zSettings = z.record(z.any()).and(
'Comfy.Workflow.ModelDownload.AllowedSources': z.array(z.string()), 'Comfy.Workflow.ModelDownload.AllowedSources': z.array(z.string()),
'Comfy.Workflow.ModelDownload.AllowedSuffixes': z.array(z.string()), 'Comfy.Workflow.ModelDownload.AllowedSuffixes': z.array(z.string()),
'Comfy.Node.DoubleClickTitleToEdit': z.boolean(), 'Comfy.Node.DoubleClickTitleToEdit': z.boolean(),
'Comfy.Window.UnloadConfirmation': z.boolean() 'Comfy.Window.UnloadConfirmation': z.boolean(),
'Comfy.NodeBadge.NodeSourceBadgeMode': zNodeBadgeMode,
'Comfy.NodeBadge.NodeIdBadgeMode': zNodeBadgeMode
}) })
.optional() .optional()
) )

View File

@@ -52,7 +52,9 @@ const litegraphBaseSchema = z
WIDGET_SECONDARY_TEXT_COLOR: z.string(), WIDGET_SECONDARY_TEXT_COLOR: z.string(),
LINK_COLOR: z.string(), LINK_COLOR: z.string(),
EVENT_LINK_COLOR: z.string(), EVENT_LINK_COLOR: z.string(),
CONNECTING_LINK_COLOR: z.string() CONNECTING_LINK_COLOR: z.string(),
BADGE_FG_COLOR: z.string().optional(),
BADGE_BG_COLOR: z.string().optional()
}) })
.passthrough() .passthrough()

View File

@@ -0,0 +1,11 @@
import type { LGraphNode } from '@comfyorg/litegraph'
import type { ComfyNodeDef } from './apiTypes'
export declare class ComfyLGraphNode extends LGraphNode {
static comfyClass: string
static title: string
static nodeData?: ComfyNodeDef
static category: string
constructor(title?: string)
}

View File

@@ -3,6 +3,7 @@ export type NodeSource = {
type: NodeSourceType type: NodeSourceType
className: string className: string
displayText: string displayText: string
badgeText: string
} }
export const getNodeSource = (python_module: string): NodeSource => { export const getNodeSource = (python_module: string): NodeSource => {
@@ -11,15 +12,23 @@ export const getNodeSource = (python_module: string): NodeSource => {
return { return {
type: 'core', type: 'core',
className: 'comfy-core', className: 'comfy-core',
displayText: 'Comfy Core' displayText: 'Comfy Core',
badgeText: '🦊'
} }
} else if (modules[0] === 'custom_nodes') { } else if (modules[0] === 'custom_nodes') {
return { return {
type: 'custom_nodes', type: 'custom_nodes',
className: 'comfy-custom-nodes', className: 'comfy-custom-nodes',
displayText: modules[1] displayText: modules[1],
badgeText: modules[1]
} }
} else { } else {
throw new Error(`Unknown node source: ${python_module}`) throw new Error(`Unknown node source: ${python_module}`)
} }
} }
export enum NodeBadgeMode {
None = 'None',
ShowAll = 'Show all',
HideBuiltIn = 'Hide built-in'
}