Fix: Vue Node Align/Distribute (#6712)

## Summary

Fixes the issue of the nodes not moving when in Vue mode (but changing
if switching back to litegraph)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6712-Fix-Vue-Node-Align-Distribute-2ad6d73d365081339aa6f61e18832bc4)
by [Unito](https://www.unito.io)
This commit is contained in:
Alexander Brown
2025-11-15 18:20:43 -08:00
committed by GitHub
parent 8dd5a9900b
commit c43a4990a9
4 changed files with 77 additions and 28 deletions

View File

@@ -49,6 +49,7 @@ import type {
ISlotType,
LinkNetwork,
LinkSegment,
NewNodePosition,
NullableProperties,
Point,
Positionable,
@@ -1011,7 +1012,8 @@ export class LGraphCanvas
direction: Direction,
align_to?: LGraphNode
): void {
alignNodes(Object.values(nodes), direction, align_to)
const newPositions = alignNodes(Object.values(nodes), direction, align_to)
LGraphCanvas.active_canvas.repositionNodesVueMode(newPositions)
LGraphCanvas.active_canvas.setDirty(true, true)
}
@@ -1031,11 +1033,12 @@ export class LGraphCanvas
})
function inner_clicked(value: string) {
alignNodes(
const newPositions = alignNodes(
Object.values(LGraphCanvas.active_canvas.selected_nodes),
value.toLowerCase() as Direction,
node
)
LGraphCanvas.active_canvas.repositionNodesVueMode(newPositions)
LGraphCanvas.active_canvas.setDirty(true, true)
}
}
@@ -1055,10 +1058,11 @@ export class LGraphCanvas
})
function inner_clicked(value: string) {
alignNodes(
const newPositions = alignNodes(
Object.values(LGraphCanvas.active_canvas.selected_nodes),
value.toLowerCase() as Direction
)
LGraphCanvas.active_canvas.repositionNodesVueMode(newPositions)
LGraphCanvas.active_canvas.setDirty(true, true)
}
}
@@ -1079,10 +1083,11 @@ export class LGraphCanvas
function inner_clicked(value: string) {
const canvas = LGraphCanvas.active_canvas
distributeNodes(
const newPositions = distributeNodes(
Object.values(canvas.selected_nodes),
value === 'Horizontally'
)
canvas.repositionNodesVueMode(newPositions)
canvas.setDirty(true, true)
}
}
@@ -8557,10 +8562,7 @@ export class LGraphCanvas
) {
const mutations = this.initLayoutMutations()
const nodesInMovingGroups = this.collectNodesInGroups(allItems)
const nodesToMove: Array<{
node: LGraphNode
newPos: { x: number; y: number }
}> = []
const nodesToMove: NewNodePosition[] = []
// First, collect all the moves we need to make
for (const item of allItems) {
@@ -8586,4 +8588,9 @@ export class LGraphCanvas
// Now apply all the node moves at once
this.applyNodePositionUpdates(nodesToMove, mutations)
}
repositionNodesVueMode(nodesToReposition: NewNodePosition[]) {
const mutations = this.initLayoutMutations()
this.applyNodePositionUpdates(nodesToReposition, mutations)
}
}

View File

@@ -254,7 +254,13 @@ type KeysOfType<T, Match> = Exclude<
/** The names of all (optional) methods and functions in T */
export type MethodNames<T> = KeysOfType<T, ((...args: any) => any) | undefined>
export interface NewNodePosition {
node: LGraphNode
newPos: {
x: number
y: number
}
}
export interface IBoundaryNodes {
top: LGraphNode
right: LGraphNode

View File

@@ -1,5 +1,5 @@
import type { LGraphNode } from '../LGraphNode'
import type { Direction, IBoundaryNodes } from '../interfaces'
import type { Direction, IBoundaryNodes, NewNodePosition } from '../interfaces'
/**
* Finds the nodes that are farthest in all four directions, representing the boundary of the nodes.
@@ -43,9 +43,9 @@ export function getBoundaryNodes(nodes: LGraphNode[]): IBoundaryNodes | null {
export function distributeNodes(
nodes: LGraphNode[],
horizontal?: boolean
): void {
): NewNodePosition[] {
const nodeCount = nodes?.length
if (!(nodeCount > 1)) return
if (!(nodeCount > 1)) return []
const index = horizontal ? 0 : 1
@@ -68,6 +68,16 @@ export function distributeNodes(
node.pos[index] = startAt + gap * i
startAt += node.size[index]
}
const newPositions = sorted.map(
(node): NewNodePosition => ({
node,
newPos: {
x: node.pos[0],
y: node.pos[1]
}
})
)
return newPositions
}
/**
@@ -80,32 +90,56 @@ export function alignNodes(
nodes: LGraphNode[],
direction: Direction,
align_to?: LGraphNode
): void {
if (!nodes) return
): NewNodePosition[] {
if (!nodes) return []
const boundary =
align_to === undefined
? getBoundaryNodes(nodes)
: { top: align_to, right: align_to, bottom: align_to, left: align_to }
if (boundary === null) return
if (boundary === null) return []
for (const node of nodes) {
const nodePositions = nodes.map((node): NewNodePosition => {
switch (direction) {
case 'right':
node.pos[0] =
boundary.right.pos[0] + boundary.right.size[0] - node.size[0]
break
return {
node,
newPos: {
x: boundary.right.pos[0] + boundary.right.size[0] - node.size[0],
y: node.pos[1]
}
}
case 'left':
node.pos[0] = boundary.left.pos[0]
break
return {
node,
newPos: {
x: boundary.left.pos[0],
y: node.pos[1]
}
}
case 'top':
node.pos[1] = boundary.top.pos[1]
break
return {
node,
newPos: {
x: node.pos[0],
y: boundary.top.pos[1]
}
}
case 'bottom':
node.pos[1] =
boundary.bottom.pos[1] + boundary.bottom.size[1] - node.size[1]
break
return {
node,
newPos: {
x: node.pos[0],
y: boundary.bottom.pos[1] + boundary.bottom.size[1] - node.size[1]
}
}
}
})
for (const { node, newPos } of nodePositions) {
node.pos[0] = newPos.x
node.pos[1] = newPos.y
}
return nodePositions
}