mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-04 23:20:07 +00:00
fix(vueNodes): sync node size changes from extensions to Vue components
This commit is contained in:
@@ -9,7 +9,6 @@ import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
|
||||
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
|
||||
import { useLayoutSync } from '@/renderer/core/layout/sync/useLayoutSync'
|
||||
import { removeNodeTitleHeight } from '@/renderer/core/layout/utils/nodeSizeUtil'
|
||||
import { ensureCorrectLayoutScale } from '@/renderer/extensions/vueNodes/layout/ensureCorrectLayoutScale'
|
||||
import { app as comfyApp } from '@/scripts/app'
|
||||
|
||||
@@ -33,10 +32,7 @@ function useVueNodeLifecycleIndividual() {
|
||||
const nodes = activeGraph._nodes.map((node: LGraphNode) => ({
|
||||
id: node.id.toString(),
|
||||
pos: [node.pos[0], node.pos[1]] as [number, number],
|
||||
size: [node.size[0], removeNodeTitleHeight(node.size[1])] as [
|
||||
number,
|
||||
number
|
||||
]
|
||||
size: [node.size[0], node.size[1]] as [number, number]
|
||||
}))
|
||||
layoutStore.initializeFromLiteGraph(nodes)
|
||||
|
||||
|
||||
@@ -755,8 +755,10 @@ export class LGraph
|
||||
let max_size = 100
|
||||
let y = margin + LiteGraph.NODE_TITLE_HEIGHT
|
||||
for (const node of column) {
|
||||
node.pos[0] = layout == LiteGraph.VERTICAL_LAYOUT ? y : x
|
||||
node.pos[1] = layout == LiteGraph.VERTICAL_LAYOUT ? x : y
|
||||
node.setPos(
|
||||
layout == LiteGraph.VERTICAL_LAYOUT ? y : x,
|
||||
layout == LiteGraph.VERTICAL_LAYOUT ? x : y
|
||||
)
|
||||
const max_size_index = layout == LiteGraph.VERTICAL_LAYOUT ? 1 : 0
|
||||
if (node.size[max_size_index] > max_size) {
|
||||
max_size = node.size[max_size_index]
|
||||
@@ -1681,7 +1683,10 @@ export class LGraph
|
||||
)
|
||||
|
||||
//Correct for title height. It's included in bounding box, but not _posSize
|
||||
subgraphNode.pos[1] += LiteGraph.NODE_TITLE_HEIGHT / 2
|
||||
subgraphNode.setPos(
|
||||
subgraphNode.pos[0],
|
||||
subgraphNode.pos[1] + LiteGraph.NODE_TITLE_HEIGHT / 2
|
||||
)
|
||||
|
||||
// Add the subgraph node to the graph
|
||||
this.add(subgraphNode)
|
||||
@@ -1848,8 +1853,7 @@ export class LGraph
|
||||
|
||||
this.add(node, true)
|
||||
node.configure(n_info)
|
||||
node.pos[0] += offsetX
|
||||
node.pos[1] += offsetY
|
||||
node.setPos(node.pos[0] + offsetX, node.pos[1] + offsetY)
|
||||
for (const input of node.inputs) {
|
||||
input.link = null
|
||||
}
|
||||
|
||||
@@ -4,10 +4,8 @@ import { PREFIX, SEPARATOR } from '@/constants/groupNodeConstants'
|
||||
import { LitegraphLinkAdapter } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'
|
||||
import type { LinkRenderContext } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'
|
||||
import { getSlotPosition } from '@/renderer/core/canvas/litegraph/slotCalculations'
|
||||
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
|
||||
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
|
||||
import { LayoutSource } from '@/renderer/core/layout/types'
|
||||
import { removeNodeTitleHeight } from '@/renderer/core/layout/utils/nodeSizeUtil'
|
||||
import { forEachNode } from '@/utils/graphTraversalUtil'
|
||||
|
||||
import { CanvasPointer } from './CanvasPointer'
|
||||
@@ -2354,8 +2352,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
const cloned = items?.created[0] as LGraphNode | undefined
|
||||
if (!cloned) return
|
||||
|
||||
cloned.pos[0] += 5
|
||||
cloned.pos[1] += 5
|
||||
cloned.setPos(cloned.pos[0] + 5, cloned.pos[1] + 5)
|
||||
|
||||
if (this.allow_dragnodes) {
|
||||
pointer.onDragStart = (pointer) => {
|
||||
@@ -4032,29 +4029,29 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
|
||||
// Adjust positions
|
||||
for (const item of created) {
|
||||
item.pos[0] += position[0] - offsetX
|
||||
item.pos[1] += position[1] - offsetY
|
||||
const newX = item.pos[0] + position[0] - offsetX
|
||||
const newY = item.pos[1] + position[1] - offsetY
|
||||
if (item instanceof LGraphNode) {
|
||||
item.setPos(newX, newY)
|
||||
} else {
|
||||
item.pos[0] = newX
|
||||
item.pos[1] = newY
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Report failures, i.e. `failedNodes`
|
||||
|
||||
const newPositions = created
|
||||
.filter((item): item is LGraphNode => item instanceof LGraphNode)
|
||||
.map((node) => {
|
||||
const fullHeight = node.size?.[1] ?? 200
|
||||
const layoutHeight = LiteGraph.vueNodesMode
|
||||
? removeNodeTitleHeight(fullHeight)
|
||||
: fullHeight
|
||||
return {
|
||||
nodeId: String(node.id),
|
||||
bounds: {
|
||||
x: node.pos[0],
|
||||
y: node.pos[1],
|
||||
width: node.size?.[0] ?? 100,
|
||||
height: layoutHeight
|
||||
}
|
||||
.map((node) => ({
|
||||
nodeId: String(node.id),
|
||||
bounds: {
|
||||
x: node.pos[0],
|
||||
y: node.pos[1],
|
||||
width: node.size?.[0] ?? 100,
|
||||
height: node.size?.[1] ?? 200
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
if (newPositions.length) layoutStore.setSource(LayoutSource.Canvas)
|
||||
layoutStore.batchUpdateNodeBounds(newPositions)
|
||||
@@ -6244,7 +6241,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
options
|
||||
)
|
||||
) {
|
||||
node.pos[0] -= node.size[0] * 0.5
|
||||
node.setPos(node.pos[0] - node.size[0] * 0.5, node.pos[1])
|
||||
}
|
||||
})
|
||||
break
|
||||
@@ -8532,27 +8529,14 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* Apply batched node position updates
|
||||
*/
|
||||
private applyNodePositionUpdates(
|
||||
nodesToMove: Array<{ node: LGraphNode; newPos: { x: number; y: number } }>,
|
||||
mutations: ReturnType<typeof useLayoutMutations>
|
||||
nodesToMove: Array<{ node: LGraphNode; newPos: { x: number; y: number } }>
|
||||
): void {
|
||||
for (const { node, newPos } of nodesToMove) {
|
||||
// Update LiteGraph position first so next drag uses correct base position
|
||||
node.pos[0] = newPos.x
|
||||
node.pos[1] = newPos.y
|
||||
// Then update layout store which will update Vue nodes
|
||||
mutations.moveNode(node.id, newPos)
|
||||
// setPos automatically syncs to layout store
|
||||
node.setPos(newPos.x, newPos.y)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize layout mutations with Canvas source
|
||||
*/
|
||||
private initLayoutMutations(): ReturnType<typeof useLayoutMutations> {
|
||||
const mutations = useLayoutMutations()
|
||||
mutations.setSource(LayoutSource.Canvas)
|
||||
return mutations
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all nodes that are children of groups in the selection
|
||||
*/
|
||||
@@ -8600,7 +8584,6 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
deltaX: number,
|
||||
deltaY: number
|
||||
) {
|
||||
const mutations = this.initLayoutMutations()
|
||||
const nodesInMovingGroups = this.collectNodesInGroups(allItems)
|
||||
const nodesToMove: NewNodePosition[] = []
|
||||
|
||||
@@ -8626,11 +8609,10 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
|
||||
// Now apply all the node moves at once
|
||||
this.applyNodePositionUpdates(nodesToMove, mutations)
|
||||
this.applyNodePositionUpdates(nodesToMove)
|
||||
}
|
||||
|
||||
repositionNodesVueMode(nodesToReposition: NewNodePosition[]) {
|
||||
const mutations = this.initLayoutMutations()
|
||||
this.applyNodePositionUpdates(nodesToReposition, mutations)
|
||||
this.applyNodePositionUpdates(nodesToReposition)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,6 +472,17 @@ export class LGraphNode
|
||||
|
||||
this._pos[0] = value[0]
|
||||
this._pos[1] = value[1]
|
||||
|
||||
const mutations = useLayoutMutations()
|
||||
mutations.setSource(LayoutSource.Canvas)
|
||||
mutations.moveNode(String(this.id), { x: value[0], y: value[1] })
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the node position to an absolute location.
|
||||
*/
|
||||
setPos(x: number, y: number): void {
|
||||
this.pos = [x, y]
|
||||
}
|
||||
|
||||
public get size() {
|
||||
@@ -483,6 +494,13 @@ export class LGraphNode
|
||||
|
||||
this._size[0] = value[0]
|
||||
this._size[1] = value[1]
|
||||
|
||||
const mutations = useLayoutMutations()
|
||||
mutations.setSource(LayoutSource.Canvas)
|
||||
mutations.resizeNode(String(this.id), {
|
||||
width: value[0],
|
||||
height: value[1]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1993,8 +2011,7 @@ export class LGraphNode
|
||||
return
|
||||
}
|
||||
|
||||
this.pos[0] += deltaX
|
||||
this.pos[1] += deltaY
|
||||
this.pos = [this._pos[0] + deltaX, this._pos[1] + deltaY]
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -138,8 +138,7 @@ export function alignNodes(
|
||||
})
|
||||
|
||||
for (const { node, newPos } of nodePositions) {
|
||||
node.pos[0] = newPos.x
|
||||
node.pos[1] = newPos.y
|
||||
node.setPos(newPos.x, newPos.y)
|
||||
}
|
||||
return nodePositions
|
||||
}
|
||||
|
||||
@@ -164,6 +164,7 @@ import {
|
||||
nextTick,
|
||||
onErrorCaptured,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
ref,
|
||||
watch
|
||||
} from 'vue'
|
||||
@@ -186,6 +187,7 @@ import { useTelemetry } from '@/platform/telemetry'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
|
||||
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
|
||||
import { LayoutSource } from '@/renderer/core/layout/types'
|
||||
import SlotConnectionDot from '@/renderer/extensions/vueNodes/components/SlotConnectionDot.vue'
|
||||
import { useNodeEventHandlers } from '@/renderer/extensions/vueNodes/composables/useNodeEventHandlers'
|
||||
import { useNodePointerInteractions } from '@/renderer/extensions/vueNodes/composables/useNodePointerInteractions'
|
||||
@@ -322,15 +324,8 @@ const handleContextMenu = (event: MouseEvent) => {
|
||||
showNodeOptions(event)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initSizeStyles()
|
||||
})
|
||||
|
||||
/**
|
||||
* Set initial DOM size from layout store, but respect intrinsic content minimum.
|
||||
* Important: nodes can mount in a collapsed state, and the collapse watcher won't
|
||||
* run initially. Match the collapsed runtime behavior by writing to the correct
|
||||
* CSS variables on mount.
|
||||
* Set initial DOM size from layout store.
|
||||
*/
|
||||
function initSizeStyles() {
|
||||
const el = nodeContainerRef.value
|
||||
@@ -343,6 +338,44 @@ function initSizeStyles() {
|
||||
el.style.setProperty(`--node-height${suffix}`, `${height}px`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle external size changes (e.g., from extensions calling node.setSize()).
|
||||
* Updates CSS variables when layoutStore changes from Canvas/External source.
|
||||
*/
|
||||
function handleLayoutChange(change: {
|
||||
source: LayoutSource
|
||||
nodeIds: string[]
|
||||
}) {
|
||||
// Only handle Canvas or External source (extensions calling setSize)
|
||||
if (
|
||||
change.source !== LayoutSource.Canvas &&
|
||||
change.source !== LayoutSource.External
|
||||
)
|
||||
return
|
||||
|
||||
if (!change.nodeIds.includes(nodeData.id)) return
|
||||
if (layoutStore.isResizingVueNodes.value) return
|
||||
if (isCollapsed.value) return
|
||||
|
||||
const el = nodeContainerRef.value
|
||||
if (!el) return
|
||||
|
||||
const newSize = size.value
|
||||
el.style.setProperty('--node-width', `${newSize.width}px`)
|
||||
el.style.setProperty('--node-height', `${newSize.height}px`)
|
||||
}
|
||||
|
||||
let unsubscribeLayoutChange: (() => void) | null = null
|
||||
|
||||
onMounted(() => {
|
||||
initSizeStyles()
|
||||
unsubscribeLayoutChange = layoutStore.onChange(handleLayoutChange)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
unsubscribeLayoutChange?.()
|
||||
})
|
||||
|
||||
const baseResizeHandleClasses =
|
||||
'absolute h-3 w-3 opacity-0 pointer-events-auto focus-visible:outline focus-visible:outline-2 focus-visible:outline-white/40'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user