mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-14 09:27:41 +00:00
Feat: Vue Node Slot Improvements (#6359)
## Summary Several fixes and improvements to the slot behavior on Vue nodes. ## Changes - **What**: Restore the pseudo-slots, if there are slots being hidden by collapse - **What**: Connections while collapsed - **What**: Display the links in a more reasonable location - **What**: Fixes styling of linked widgets - **What**: [~Fix reconnecting logic to prioritize newly disconnected and now empty slots~](https://github.com/Comfy-Org/ComfyUI_frontend/pull/6370) ## Review Focus <!-- Critical design decisions or edge cases that need attention --> <!-- If this PR fixes an issue, uncomment and update the line below --> <!-- Fixes #ISSUE_NUMBER --> ## Screenshots (if applicable) https://github.com/user-attachments/assets/913cfb8f-acdd-4f3d-b619-c280cc11cce5 <!-- Add screenshots or video recording to help explain your changes --> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6359-WIP-Collapsed-nodes-multislots-29b6d73d3650817289d5f0a8efdade84) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||
import type {
|
||||
INodeInputSlot,
|
||||
IWidgetLocator
|
||||
} from '@/lib/litegraph/src/interfaces'
|
||||
import type { LinkId } from '@/renderer/core/layout/types'
|
||||
import {
|
||||
linkedWidgetedInputs,
|
||||
nonWidgetedInputs
|
||||
} from '@/renderer/extensions/vueNodes/utils/nodeDataUtils'
|
||||
import { describe, it } from 'vitest'
|
||||
|
||||
function makeFakeInputSlot(
|
||||
name: string,
|
||||
withWidget = false,
|
||||
link: LinkId | null = null
|
||||
): INodeInputSlot {
|
||||
const widget: IWidgetLocator | undefined = withWidget ? { name } : undefined
|
||||
return {
|
||||
name,
|
||||
widget,
|
||||
link,
|
||||
boundingRect: [0, 0, 0, 0],
|
||||
type: 'FAKE'
|
||||
}
|
||||
}
|
||||
|
||||
function makeFakeNodeData(inputs: INodeInputSlot[]): VueNodeData {
|
||||
const nodeData: Partial<VueNodeData> = { inputs }
|
||||
return nodeData as VueNodeData
|
||||
}
|
||||
|
||||
describe('nodeDataUtils', () => {
|
||||
describe('nonWidgetedInputs', () => {
|
||||
it('should handle an empty inputs list', () => {
|
||||
const inputs: INodeInputSlot[] = []
|
||||
const nodeData = makeFakeNodeData(inputs)
|
||||
|
||||
const actual = nonWidgetedInputs(nodeData)
|
||||
|
||||
expect(actual.length).toBe(0)
|
||||
})
|
||||
|
||||
it('should handle a list of only widgeted inputs', () => {
|
||||
const inputs: INodeInputSlot[] = [
|
||||
makeFakeInputSlot('first', true),
|
||||
makeFakeInputSlot('second', true)
|
||||
]
|
||||
const nodeData = makeFakeNodeData(inputs)
|
||||
|
||||
const actual = nonWidgetedInputs(nodeData)
|
||||
|
||||
expect(actual.length).toBe(0)
|
||||
})
|
||||
|
||||
it('should handle a list of only slot inputs', () => {
|
||||
const inputs: INodeInputSlot[] = [
|
||||
makeFakeInputSlot('first'),
|
||||
makeFakeInputSlot('second')
|
||||
]
|
||||
const nodeData = makeFakeNodeData(inputs)
|
||||
|
||||
const actual = nonWidgetedInputs(nodeData)
|
||||
|
||||
expect(actual.length).toBe(2)
|
||||
})
|
||||
|
||||
it('should handle a list of mixed inputs', () => {
|
||||
const inputs: INodeInputSlot[] = [
|
||||
makeFakeInputSlot('first'),
|
||||
makeFakeInputSlot('second'),
|
||||
makeFakeInputSlot('third', true),
|
||||
makeFakeInputSlot('fourth', true)
|
||||
]
|
||||
const nodeData = makeFakeNodeData(inputs)
|
||||
|
||||
const actual = nonWidgetedInputs(nodeData)
|
||||
|
||||
expect(actual.length).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('linkedWidgetedInputs', () => {
|
||||
it('should return input slots that are bound to widgets and are linked: none present', () => {
|
||||
const inputs: INodeInputSlot[] = [
|
||||
makeFakeInputSlot('first'),
|
||||
makeFakeInputSlot('second'),
|
||||
makeFakeInputSlot('third', true),
|
||||
makeFakeInputSlot('fourth', true)
|
||||
]
|
||||
const nodeData = makeFakeNodeData(inputs)
|
||||
|
||||
const actual = linkedWidgetedInputs(nodeData)
|
||||
|
||||
expect(actual.length).toBe(0)
|
||||
})
|
||||
|
||||
it('should return input slots that are bound to widgets and are linked: one present', () => {
|
||||
const inputs: INodeInputSlot[] = [
|
||||
makeFakeInputSlot('first'),
|
||||
makeFakeInputSlot('second'),
|
||||
makeFakeInputSlot('third', true),
|
||||
makeFakeInputSlot('fourth', true, 1)
|
||||
]
|
||||
const nodeData = makeFakeNodeData(inputs)
|
||||
|
||||
const actual = linkedWidgetedInputs(nodeData)
|
||||
|
||||
expect(actual.length).toBe(1)
|
||||
})
|
||||
|
||||
it('should return input slots that are bound to widgets and are linked: multiple present', () => {
|
||||
const inputs: INodeInputSlot[] = [
|
||||
makeFakeInputSlot('first'),
|
||||
makeFakeInputSlot('second'),
|
||||
makeFakeInputSlot('third', true),
|
||||
makeFakeInputSlot('fourth', true, 1),
|
||||
makeFakeInputSlot('fifth', true, 2)
|
||||
]
|
||||
const nodeData = makeFakeNodeData(inputs)
|
||||
|
||||
const actual = linkedWidgetedInputs(nodeData)
|
||||
|
||||
expect(actual.length).toBe(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
36
src/renderer/extensions/vueNodes/utils/nodeDataUtils.ts
Normal file
36
src/renderer/extensions/vueNodes/utils/nodeDataUtils.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||
import type { INodeInputSlot, INodeSlot } from '@/lib/litegraph/src/interfaces'
|
||||
import { isSlotObject } from '@/utils/typeGuardUtil'
|
||||
|
||||
function coerceINodeSlot(input: INodeInputSlot): INodeSlot {
|
||||
return isSlotObject(input)
|
||||
? input
|
||||
: {
|
||||
name: typeof input === 'string' ? input : '',
|
||||
type: 'any',
|
||||
boundingRect: [0, 0, 0, 0]
|
||||
}
|
||||
}
|
||||
|
||||
function inputHasWidget(input: INodeInputSlot) {
|
||||
return isSlotObject(input) && 'widget' in input && input.widget
|
||||
}
|
||||
export function nonWidgetedInputs(
|
||||
nodeData: VueNodeData | undefined
|
||||
): INodeSlot[] {
|
||||
if (!nodeData?.inputs) return []
|
||||
|
||||
return nodeData.inputs
|
||||
.filter((input) => !inputHasWidget(input))
|
||||
.map(coerceINodeSlot)
|
||||
}
|
||||
|
||||
export function linkedWidgetedInputs(
|
||||
nodeData: VueNodeData | undefined
|
||||
): INodeSlot[] {
|
||||
if (!nodeData?.inputs) return []
|
||||
|
||||
return nodeData.inputs
|
||||
.filter((input) => inputHasWidget(input) && !!input.link)
|
||||
.map(coerceINodeSlot)
|
||||
}
|
||||
Reference in New Issue
Block a user