From 0fea54c542a7a6d817174b3900f80d25499b4bda Mon Sep 17 00:00:00 2001 From: Alexander Brown Date: Wed, 24 Sep 2025 14:35:14 -0700 Subject: [PATCH] Typing: Slots in VueNodeData (#5759) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Replace the unknown type with the interface in Litegraph. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5759-Typing-Slots-in-VueNodeData-2786d73d36508194b286fad172b13c51) by [Unito](https://www.unito.io) --- src/composables/graph/useGraphNodeManager.ts | 6 ++-- .../vueNodes/components/LGraphNodePreview.vue | 20 +++++++++---- .../vueNodes/components/NodeSlots.spec.ts | 29 ++++++++++++++----- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/composables/graph/useGraphNodeManager.ts b/src/composables/graph/useGraphNodeManager.ts index ea3f61a14..31cc63a81 100644 --- a/src/composables/graph/useGraphNodeManager.ts +++ b/src/composables/graph/useGraphNodeManager.ts @@ -5,6 +5,8 @@ import { reactive } from 'vue' import { useChainCallback } from '@/composables/functional/useChainCallback' +import type { INodeOutputSlot } from '@/lib/litegraph/src/interfaces' +import type { INodeInputSlot } from '@/lib/litegraph/src/interfaces' import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations' import { LayoutSource } from '@/renderer/core/layout/types' import type { WidgetValue } from '@/types/simplifiedWidget' @@ -28,8 +30,8 @@ export interface VueNodeData { executing: boolean subgraphId?: string | null widgets?: SafeWidgetData[] - inputs?: unknown[] - outputs?: unknown[] + inputs?: INodeInputSlot[] + outputs?: INodeOutputSlot[] hasErrors?: boolean flags?: { collapsed?: boolean diff --git a/src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue b/src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue index 9a3229a33..a6513f039 100644 --- a/src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue +++ b/src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue @@ -38,6 +38,11 @@ import { computed } from 'vue' import type { VueNodeData } from '@/composables/graph/useGraphNodeManager' +import type { + INodeInputSlot, + INodeOutputSlot +} from '@/lib/litegraph/src/interfaces' +import { RenderShape } from '@/lib/litegraph/src/litegraph' import NodeContent from '@/renderer/extensions/vueNodes/components/NodeContent.vue' import NodeHeader from '@/renderer/extensions/vueNodes/components/NodeHeader.vue' import NodeSlots from '@/renderer/extensions/vueNodes/components/NodeSlots.vue' @@ -74,26 +79,29 @@ const nodeData = computed(() => { } })) - const inputs = Object.entries(nodeDef.inputs || {}) + const inputs: INodeInputSlot[] = Object.entries(nodeDef.inputs || {}) .filter(([_, input]) => !widgetStore.inputIsWidget(input)) .map(([name, input]) => ({ name, type: input.type, - shape: input.isOptional ? 'HollowCircle' : undefined, - boundingRect: [0, 0, 0, 0] + shape: input.isOptional ? RenderShape.HollowCircle : undefined, + boundingRect: [0, 0, 0, 0], + link: null })) - const outputs = (nodeDef.outputs || []).map((output) => { + const outputs: INodeOutputSlot[] = (nodeDef.outputs || []).map((output) => { if (typeof output === 'string') { return { name: output, type: output, - boundingRect: [0, 0, 0, 0] + boundingRect: [0, 0, 0, 0], + links: [] } } return { ...output, - boundingRect: [0, 0, 0, 0] + boundingRect: [0, 0, 0, 0], + links: [] } }) diff --git a/src/renderer/extensions/vueNodes/components/NodeSlots.spec.ts b/src/renderer/extensions/vueNodes/components/NodeSlots.spec.ts index f58e115ad..ed8bb8ed2 100644 --- a/src/renderer/extensions/vueNodes/components/NodeSlots.spec.ts +++ b/src/renderer/extensions/vueNodes/components/NodeSlots.spec.ts @@ -5,6 +5,8 @@ import { type PropType, defineComponent } from 'vue' import { createI18n } from 'vue-i18n' import type { VueNodeData } from '@/composables/graph/useGraphNodeManager' +import type { INodeOutputSlot } from '@/lib/litegraph/src/interfaces' +import type { INodeInputSlot } from '@/lib/litegraph/src/interfaces' import enMessages from '@/locales/en/main.json' with { type: 'json' } import NodeSlots from './NodeSlots.vue' @@ -94,15 +96,17 @@ describe('NodeSlots.vue', () => { const inputObjNoWidget = { name: 'objNoWidget', type: 'number', - boundingRect: [0, 0, 0, 0] + boundingRect: new Float32Array([0, 0, 0, 0]), + link: null } const inputObjWithWidget = { name: 'objWithWidget', type: 'number', - boundingRect: [0, 0, 0, 0], - widget: { name: 'objWithWidget' } + boundingRect: new Float32Array([0, 0, 0, 0]), + widget: { name: 'objWithWidget' }, + link: null } - const inputs = [inputObjNoWidget, inputObjWithWidget, 'stringInput'] + const inputs: INodeInputSlot[] = [inputObjNoWidget, inputObjWithWidget] const wrapper = mountSlots(makeNodeData({ inputs })) @@ -143,8 +147,19 @@ describe('NodeSlots.vue', () => { }) it('maps outputs and passes correct indexes', () => { - const outputObj = { name: 'outA', type: 'any', boundingRect: [0, 0, 0, 0] } - const outputs = [outputObj, 'outB'] + const outputObj = { + name: 'outA', + type: 'any', + boundingRect: new Float32Array([0, 0, 0, 0]), + links: [] + } + const outputObjB = { + name: 'outB', + type: 'any', + boundingRect: new Float32Array([0, 0, 0, 0]), + links: [] + } + const outputs: INodeOutputSlot[] = [outputObj, outputObjB] const wrapper = mountSlots(makeNodeData({ outputs })) const outputEls = wrapper @@ -174,7 +189,7 @@ describe('NodeSlots.vue', () => { it('passes readonly to child slots', () => { const wrapper = mountSlots( - makeNodeData({ inputs: ['a'], outputs: ['b'] }), + makeNodeData({ inputs: [], outputs: [] }), /* readonly */ true ) const all = [