Drag vuenodes input (#6514)

This pull request introduces several improvements to Vue reactivity and
user experience in the graph node and widget system. The main focus is
on ensuring that changes to node and widget data reliably trigger
updates in Vue components, improving drag-and-drop support for nodes,
and enhancing widget value handling for better compatibility and
reactivity.

**Vue Reactivity Improvements:**

* In `useGraphNodeManager.ts`, node data updates now create a completely
new object and add a timestamp (`_updateTs`) to force Vue's reactivity
system to detect changes. Additionally, node data is re-set on the next
tick to guarantee component updates.
[[1]](diffhunk://#diff-f980db6f42cef913c3fe92669783b255d617e40b9ccef9a1ab9cc8e326ff1790L272-R280)
[[2]](diffhunk://#diff-f980db6f42cef913c3fe92669783b255d617e40b9ccef9a1ab9cc8e326ff1790R326-R335)
* Widget value composables (`useWidgetValue` and related helpers) now
accept either a direct value or a getter function for `modelValue`, and
always normalize it to a getter. Watches are updated to use this getter
for more reliable reactivity.
[[1]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L13-R14)
[[2]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911R49-R57)
[[3]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L82-R91)
[[4]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L100-R104)
[[5]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L117-R121)
[[6]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L140-R144)
[[7]](diffhunk://#diff-0c43cefa9fb524ae86541c7ca851e97a22b3fd01f95795c83273c977be77468fL47-R47)
* In `useImageUploadWidget.ts`, widget value updates now use a new
array/object to ensure Vue detects the change, especially for batch
uploads.

**Drag-and-Drop Support for Nodes:**

* The `LGraphNode.vue` component adds drag-and-drop event handlers
(`dragover`, `dragleave`, `drop`) and visual feedback (`isDraggingOver`
state and highlight ring) for improved user experience when dragging
files onto nodes. Node callbacks (`onDragOver`, `onDragDrop`) are used
for custom validation and handling.
[[1]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2L26-R27)
[[2]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R47-R49)
[[3]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R482-R521)

**Widget and Audio Upload Handling:**

* In `uploadAudio.ts`, after uploading an audio file, the widget's
callback is manually triggered to ensure Vue nodes update. There is also
a commented-out call to mark the canvas as dirty for potential future
refresh logic.
[[1]](diffhunk://#diff-796b36f2cafb906a5e95b5750ca5ddc1bf57a304d4a022e0bdaee04b4ee5bbc4R61-R65)
[[2]](diffhunk://#diff-796b36f2cafb906a5e95b5750ca5ddc1bf57a304d4a022e0bdaee04b4ee5bbc4R190-R191)

These changes collectively improve the reliability and responsiveness of
UI updates in the graph node system, especially in scenarios involving
external updates, drag-and-drop interactions, and batch widget value
changes.



https://github.com/user-attachments/assets/8e3194c9-196c-4e13-ad0b-a32177f2d062



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6514-Drag-vuenodes-input-29e6d73d3650817da1b7ef96b61b752d)
by [Unito](https://www.unito.io)
This commit is contained in:
Johnpaul Chiwetelu
2025-11-05 09:11:56 +01:00
committed by GitHub
parent 693fbbd3e4
commit fac86e35bf
6 changed files with 85 additions and 26 deletions

View File

@@ -23,7 +23,8 @@
bypassed,
'before:rounded-2xl before:pointer-events-none before:absolute before:inset-0':
muted,
'will-change-transform': isDragging
'will-change-transform': isDragging,
'ring-4 ring-primary-500 bg-primary-500/10': isDraggingOver
},
shouldHandleNodePointerEvents
@@ -43,6 +44,9 @@
v-bind="pointerHandlers"
@wheel="handleWheel"
@contextmenu="handleContextMenu"
@dragover.prevent="handleDragOver"
@dragleave="handleDragLeave"
@drop="handleDrop"
>
<div class="flex flex-col justify-center items-center relative">
<template v-if="isCollapsed">
@@ -132,7 +136,7 @@
</template>
<script setup lang="ts">
import { whenever } from '@vueuse/core'
import { useMouseInElement, whenever } from '@vueuse/core'
import { storeToRefs } from 'pinia'
import { computed, inject, onErrorCaptured, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
@@ -481,4 +485,43 @@ const nodeMedia = computed(() => {
})
const nodeContainerRef = ref<HTMLDivElement>()
// Track mouse position relative to node container for drag and drop
const { isOutside } = useMouseInElement(nodeContainerRef)
// Drag and drop support
const isDraggingOver = ref(false)
const handleDragOver = (event: DragEvent) => {
const node = lgraphNode.value
if (!node || !node.onDragOver) {
isDraggingOver.value = false
return
}
// Call the litegraph node's onDragOver callback to check if files are valid
const canDrop = node.onDragOver(event)
isDraggingOver.value = canDrop
}
const handleDragLeave = () => {
if (isOutside.value) {
isDraggingOver.value = false
}
}
const handleDrop = async (event: DragEvent) => {
event.preventDefault()
event.stopPropagation()
isDraggingOver.value = false
const node = lgraphNode.value
if (!node || !node.onDragDrop) {
return
}
// Forward the drop event to the litegraph node's onDragDrop callback
await node.onDragDrop(event)
}
</script>

View File

@@ -44,7 +44,7 @@ const emit = defineEmits<{
const { localValue, onChange } = useWidgetValue({
widget: props.widget,
modelValue: props.modelValue,
modelValue: () => props.modelValue,
defaultValue: props.widget.options?.values?.[0] || '',
emit
})

View File

@@ -78,9 +78,13 @@ export const useImageUploadWidget = () => {
folder,
onUploadComplete: (output) => {
output.forEach((path) => addToComboValues(fileComboWidget, path))
// Create a NEW array to ensure Vue reactivity detects the change
const newValue = allow_batch ? [...output] : output[0]
// @ts-expect-error litegraph combo value type does not support arrays yet
fileComboWidget.value = output
fileComboWidget.callback?.(output)
fileComboWidget.value = newValue
fileComboWidget.callback?.(newValue)
}
})