diff --git a/src/components/graph/NodeTooltip.vue b/src/components/graph/NodeTooltip.vue index 643c60db3d..a3e514a5ba 100644 --- a/src/components/graph/NodeTooltip.vue +++ b/src/components/graph/NodeTooltip.vue @@ -69,7 +69,7 @@ const onIdle = () => { ) if (inputSlot !== -1) { const inputName = node.inputs[inputSlot].name - return showTooltip(nodeDef.input.getInput(inputName)?.tooltip) + return showTooltip(nodeDef.inputs.getInput(inputName)?.tooltip) } const outputSlot = canvas.isOverNodeOutput( @@ -79,14 +79,14 @@ const onIdle = () => { [0, 0] ) if (outputSlot !== -1) { - return showTooltip(nodeDef.output.all?.[outputSlot]?.tooltip) + return showTooltip(nodeDef.outputs.all?.[outputSlot]?.tooltip) } const widget = comfyApp.canvas.getWidgetAtCursor() // Dont show for DOM widgets, these use native browser tooltips as we dont get proper mouse events on these if (widget && !widget.element) { return showTooltip( - widget.tooltip ?? nodeDef.input.getInput(widget.name)?.tooltip + widget.tooltip ?? nodeDef.inputs.getInput(widget.name)?.tooltip ) } } diff --git a/src/components/node/NodePreview.vue b/src/components/node/NodePreview.vue index 46b4c63b07..059643a1e2 100644 --- a/src/components/node/NodePreview.vue +++ b/src/components/node/NodePreview.vue @@ -105,8 +105,8 @@ const litegraphColors = colors ?? defaultColorPalette.colors.litegraph_base const widgetStore = useWidgetStore() const nodeDef = props.nodeDef -const allInputDefs = nodeDef.input.all -const allOutputDefs = nodeDef.output.all +const allInputDefs = nodeDef.inputs.all +const allOutputDefs = nodeDef.outputs.all const slotInputDefs = allInputDefs.filter( (input) => !widgetStore.inputIsWidget(input) ) diff --git a/src/scripts/app.ts b/src/scripts/app.ts index b8bcd63778..8132ef466d 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -1600,7 +1600,7 @@ export class ComfyApp { app.canvas.getWidgetLinkType = function (widget, node) { const nodeDefStore = useNodeDefStore() const nodeDef = nodeDefStore.nodeDefsByName[node.type] - const input = nodeDef.input.getInput(widget.name) + const input = nodeDef.inputs.getInput(widget.name) return input?.type } diff --git a/src/services/nodeSearchService.ts b/src/services/nodeSearchService.ts index a1c60ff205..805850b46d 100644 --- a/src/services/nodeSearchService.ts +++ b/src/services/nodeSearchService.ts @@ -209,7 +209,7 @@ export class NodeSearchService { /* name */ 'Input Type', /* invokeSequence */ 'i', /* longInvokeSequence */ 'input', - (node) => node.input.all.map((input) => input.type), + (node) => node.inputs.all.map((input) => input.type), data, filterSearchOptions ) @@ -219,7 +219,7 @@ export class NodeSearchService { /* name */ 'Output Type', /* invokeSequence */ 'o', /* longInvokeSequence */ 'output', - (node) => node.output.all.map((output) => output.type), + (node) => node.outputs.all.map((output) => output.type), data, filterSearchOptions ) diff --git a/src/stores/nodeDefStore.ts b/src/stores/nodeDefStore.ts index dac62d4fbf..89ca88c068 100644 --- a/src/stores/nodeDefStore.ts +++ b/src/stores/nodeDefStore.ts @@ -5,10 +5,10 @@ import { import { type ComfyNodeDef, type ComfyInputsSpec as ComfyInputsSpecSchema, + type ComfyOutputTypesSpec as ComfyOutputTypesSpecSchema, type InputSpec } from '@/types/apiTypes' import { defineStore } from 'pinia' -import { ComfyWidgetConstructor } from '@/scripts/widgets' import { TreeNode } from 'primevue/treenode' import { buildTree } from '@/utils/treeUtil' import { computed, ref } from 'vue' @@ -149,21 +149,45 @@ export class ComfyOutputsSpec { } } -/** - * Note: This class does not implement the ComfyNodeDef interface, as we are - * using a custom output spec for output definitions. - */ -export class ComfyNodeDefImpl { - name: string - display_name: string +export class ComfyNodeDefImpl implements ComfyNodeDef { + // ComfyNodeDef fields + readonly name: string + readonly display_name: string + /** + * Category is not marked as readonly as the bookmark system + * needs to write to it to assign a node to a custom folder. + */ category: string - python_module: string - description: string - deprecated: boolean - experimental: boolean - input: ComfyInputsSpec - output: ComfyOutputsSpec - nodeSource: NodeSource + readonly python_module: string + readonly description: string + readonly deprecated: boolean + readonly experimental: boolean + readonly output_node: boolean + /** + * @deprecated Use `inputs` instead + */ + readonly input: ComfyInputsSpecSchema + /** + * @deprecated Use `outputs` instead + */ + readonly output: ComfyOutputTypesSpecSchema + /** + * @deprecated Use `outputs[n].is_list` instead + */ + readonly output_is_list?: boolean[] + /** + * @deprecated Use `outputs[n].name` instead + */ + readonly output_name?: string[] + /** + * @deprecated Use `outputs[n].tooltip` instead + */ + readonly output_tooltips?: string[] + + // ComfyNodeDefImpl fields + readonly inputs: ComfyInputsSpec + readonly outputs: ComfyOutputsSpec + readonly nodeSource: NodeSource constructor(obj: ComfyNodeDef) { this.name = obj.name @@ -174,8 +198,15 @@ export class ComfyNodeDefImpl { this.deprecated = obj.deprecated ?? obj.category === '' this.experimental = obj.experimental ?? obj.category.startsWith('_for_testing') - this.input = new ComfyInputsSpec(obj.input ?? {}) - this.output = ComfyNodeDefImpl.transformOutputSpec(obj) + this.output_node = obj.output_node + this.input = obj.input ?? {} + this.output = obj.output ?? [] + this.output_is_list = obj.output_is_list + this.output_name = obj.output_name + this.output_tooltips = obj.output_tooltips + + this.inputs = new ComfyInputsSpec(obj.input ?? {}) + this.outputs = ComfyNodeDefImpl.transformOutputSpec(obj) this.nodeSource = getNodeSource(obj.python_module) } @@ -284,7 +315,6 @@ export function createDummyFolderNodeDef(folderPath: string): ComfyNodeDefImpl { export const useNodeDefStore = defineStore('nodeDef', () => { const nodeDefsByName = ref>({}) const nodeDefsByDisplayName = ref>({}) - const widgets = ref>({}) const showDeprecated = ref(false) const showExperimental = ref(false) diff --git a/tests-ui/tests/fast/nodeDef.test.ts b/tests-ui/tests/fast/nodeDef.test.ts index 09bdd0f4c5..0b7f55d75b 100644 --- a/tests-ui/tests/fast/nodeDef.test.ts +++ b/tests-ui/tests/fast/nodeDef.test.ts @@ -171,8 +171,8 @@ describe('ComfyNodeDefImpl', () => { expect(result.category).toBe('Testing') expect(result.python_module).toBe('test_module') expect(result.description).toBe('A test node') - expect(result.input).toBeInstanceOf(ComfyInputsSpec) - expect(result.output.all).toEqual([ + expect(result.inputs).toBeInstanceOf(ComfyInputsSpec) + expect(result.outputs.all).toEqual([ { index: 0, name: 'intOutput', @@ -243,7 +243,7 @@ describe('ComfyNodeDefImpl', () => { const result = new ComfyNodeDefImpl(plainObject) - expect(result.output.all).toEqual([ + expect(result.outputs.all).toEqual([ { index: 0, name: 'stringOutput', @@ -281,7 +281,7 @@ describe('ComfyNodeDefImpl', () => { const result = new ComfyNodeDefImpl(plainObject) - expect(result.output.all).toEqual([ + expect(result.outputs.all).toEqual([ { index: 0, name: 'INT', @@ -317,7 +317,7 @@ describe('ComfyNodeDefImpl', () => { } const result = new ComfyNodeDefImpl(plainObject) - expect(result.output.all).toEqual([ + expect(result.outputs.all).toEqual([ { index: 0, name: 'output', @@ -354,7 +354,7 @@ describe('ComfyNodeDefImpl', () => { const result = new ComfyNodeDefImpl(plainObject) - expect(result.output.all).toEqual([]) + expect(result.outputs.all).toEqual([]) }) it('should handle undefined fields', () => { @@ -367,8 +367,8 @@ describe('ComfyNodeDefImpl', () => { } const result = new ComfyNodeDefImpl(plainObject) - expect(result.output.all).toEqual([]) - expect(result.input.all).toEqual([]) + expect(result.outputs.all).toEqual([]) + expect(result.inputs.all).toEqual([]) }) it('should handle complex input specifications', () => { @@ -395,8 +395,8 @@ describe('ComfyNodeDefImpl', () => { const result = new ComfyNodeDefImpl(plainObject) - expect(result.input).toBeInstanceOf(ComfyInputsSpec) - expect(result.input.required).toBeDefined() - expect(result.input.optional).toBeDefined() + expect(result.inputs).toBeInstanceOf(ComfyInputsSpec) + expect(result.inputs.required).toBeDefined() + expect(result.inputs.optional).toBeDefined() }) })