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:
Alexander Brown
2025-10-29 20:32:05 -07:00
committed by GitHub
parent 5e9a9923e4
commit e606ff34ec
17 changed files with 263 additions and 72 deletions

View File

@@ -1,6 +1,5 @@
import { useEventListener } from '@vueuse/core'
import { tryOnScopeDispose, useEventListener } from '@vueuse/core'
import type { Fn } from '@vueuse/core'
import { onBeforeUnmount } from 'vue'
import { useSharedCanvasPositionConversion } from '@/composables/element/useCanvasPositionConversion'
import type { LGraph } from '@/lib/litegraph/src/LGraph'
@@ -555,6 +554,8 @@ export function useSlotLinkInteraction({
if (event.button !== 0) return
if (!nodeId) return
if (pointerSession.isActive()) return
event.preventDefault()
event.stopPropagation()
const canvas = app.canvas
const graph = canvas?.graph
@@ -613,7 +614,7 @@ export function useSlotLinkInteraction({
if (shouldBatchDisconnectOutputLinks && resolvedNode) {
resolvedNode.disconnectOutput(index)
app.canvas?.setDirty(true, true)
canvas.setDirty(true, true)
event.preventDefault()
event.stopPropagation()
return
@@ -634,20 +635,18 @@ export function useSlotLinkInteraction({
const shouldMoveExistingInput =
isInputSlot && !shouldBreakExistingInputLink && hasExistingInputLink
if (activeAdapter) {
if (isOutputSlot) {
activeAdapter.beginFromOutput(localNodeId, index, {
moveExisting: shouldMoveExistingOutput
})
} else {
activeAdapter.beginFromInput(localNodeId, index, {
moveExisting: shouldMoveExistingInput
})
}
if (isOutputSlot) {
activeAdapter.beginFromOutput(localNodeId, index, {
moveExisting: shouldMoveExistingOutput
})
} else {
activeAdapter.beginFromInput(localNodeId, index, {
moveExisting: shouldMoveExistingInput
})
}
if (shouldMoveExistingInput && existingInputLink) {
existingInputLink._dragging = true
}
if (shouldMoveExistingInput && existingInputLink) {
existingInputLink._dragging = true
}
syncRenderLinkOrigins()
@@ -678,21 +677,19 @@ export function useSlotLinkInteraction({
toCanvasPointerEvent(event)
updatePointerState(event)
if (activeAdapter) {
activeAdapter.linkConnector.state.snapLinksPos = [
state.pointer.canvas.x,
state.pointer.canvas.y
]
}
activeAdapter.linkConnector.state.snapLinksPos = [
state.pointer.canvas.x,
state.pointer.canvas.y
]
pointerSession.register(
useEventListener(window, 'pointermove', handlePointerMove, {
useEventListener('pointermove', handlePointerMove, {
capture: true
}),
useEventListener(window, 'pointerup', handlePointerUp, {
useEventListener('pointerup', handlePointerUp, {
capture: true
}),
useEventListener(window, 'pointercancel', handlePointerCancel, {
useEventListener('pointercancel', handlePointerCancel, {
capture: true
})
)
@@ -710,12 +707,10 @@ export function useSlotLinkInteraction({
: activeAdapter.isOutputValidDrop(slotLayout.nodeId, idx)
setCompatibleForKey(key, ok)
}
app.canvas?.setDirty(true, true)
event.preventDefault()
event.stopPropagation()
canvas.setDirty(true, true)
}
onBeforeUnmount(() => {
tryOnScopeDispose(() => {
if (pointerSession.isActive()) {
cleanupInteraction()
}