Feat: Alt+Drag to clone - Vue Nodes (#6789)

## Summary

Replicate the alt+drag to clone behavior present in litegraph.

## Changes

- **What**: Simplify the interaction/drag handling, now with less state!
- **What**: Alt+Click+Drag a node to clone it

## Screenshots (if applicable)



https://github.com/user-attachments/assets/469e33c2-de0c-4e64-a344-1e9d9339d528



<!-- Add screenshots or video recording to help explain your changes -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6789-WIP-Alt-Drag-to-clone-Vue-Nodes-2b16d73d36508102a871ffe97ed2831f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Alexander Brown
2025-11-21 14:16:03 -08:00
committed by GitHub
parent a8d6f7baff
commit 9da82f47ef
22 changed files with 574 additions and 1568 deletions

View File

@@ -1771,18 +1771,19 @@ export class LGraphCanvas
}
static onMenuNodeClone(
// @ts-expect-error - unused parameter
value: IContextMenuValue,
// @ts-expect-error - unused parameter
options: IContextMenuOptions,
// @ts-expect-error - unused parameter
e: MouseEvent,
// @ts-expect-error - unused parameter
menu: ContextMenu,
_value: IContextMenuValue,
_options: IContextMenuOptions,
_e: MouseEvent,
_menu: ContextMenu,
node: LGraphNode
): void {
const canvas = LGraphCanvas.active_canvas
const nodes = canvas.selectedItems.size ? canvas.selectedItems : [node]
const nodes = canvas.selectedItems.size ? [...canvas.selectedItems] : [node]
if (nodes.length) LGraphCanvas.cloneNodes(nodes)
}
static cloneNodes(nodes: Positionable[]) {
const canvas = LGraphCanvas.active_canvas
// Find top-left-most boundary
let offsetX = Infinity
@@ -1792,11 +1793,11 @@ export class LGraphCanvas
throw new TypeError(
'Invalid node encountered on clone. `pos` was null.'
)
if (item.pos[0] < offsetX) offsetX = item.pos[0]
if (item.pos[1] < offsetY) offsetY = item.pos[1]
offsetX = Math.min(offsetX, item.pos[0])
offsetY = Math.min(offsetY, item.pos[1])
}
canvas._deserializeItems(canvas._serializeItems(nodes), {
return canvas._deserializeItems(canvas._serializeItems(nodes), {
position: [offsetX + 5, offsetY + 5]
})
}