mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-14 01:20:03 +00:00
fix: simplify ensureCorrectLayoutScale and fix link sync during Vue node drag (#9680)
## Summary Fix node layout drift from repeated `ensureCorrectLayoutScale` scaling, simplify it to a pure one-time normalizer, and fix links not following Vue nodes during drag. ## Changes - **What**: - `ensureCorrectLayoutScale` simplified to a one-time normalizer: unprojects legacy Vue-scaled coordinates back to canonical LiteGraph coordinates, marks the graph as corrected, and does nothing else. No longer touches the layout store, syncs reroutes, or changes canvas scale. - Removed no-op calls from `useVueNodeLifecycle.ts` (a renderer version string was passed where an `LGraph` was expected). - `layoutStore.finalizeOperation` now calls `notifyChange` synchronously instead of via `setTimeout`. This ensures `useLayoutSync`'s `onChange` callback pushes positions to LiteGraph `node.pos` and calls `canvas.setDirty()` within the same RAF frame as a drag update, fixing links not following Vue nodes during drag. - **Tests**: Added tests for `ensureCorrectLayoutScale` (idempotency, round-trip, unknown-renderer no-op) and `graphRenderTransform` (project/unproject round-trips, anchor caching). ## Review Focus - The `setTimeout(() => this.notifyChange(change), 0)` → `this.notifyChange(change)` change in `layoutStore.ts` is the key fix for the drag-link-sync bug. The listener (`useLayoutSync`) only writes to LiteGraph, not back to the layout store, so synchronous notification is safe. - `ensureCorrectLayoutScale` no longer has any side effects beyond normalizing coordinates and setting `workflowRendererVersion` metadata. --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: Christian Byrne <cbyrne@comfy.org> Co-authored-by: jaeone94 <89377375+jaeone94@users.noreply.github.com> Co-authored-by: AustinMroz <austin@comfy.org> Co-authored-by: Hunter <huntcsg@users.noreply.github.com> Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
104
browser_tests/tests/rendererToggleStability.spec.ts
Normal file
104
browser_tests/tests/rendererToggleStability.spec.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import {
|
||||
comfyExpect as expect,
|
||||
comfyPageFixture as test
|
||||
} from '../fixtures/ComfyPage'
|
||||
import type { ComfyPage } from '../fixtures/ComfyPage'
|
||||
import type { Position } from '../fixtures/types'
|
||||
|
||||
type NodeSnapshot = { id: number } & Position
|
||||
|
||||
async function getAllNodePositions(
|
||||
comfyPage: ComfyPage
|
||||
): Promise<NodeSnapshot[]> {
|
||||
return comfyPage.page.evaluate(() =>
|
||||
window.app!.graph.nodes.map((n) => ({
|
||||
id: n.id as number,
|
||||
x: n.pos[0],
|
||||
y: n.pos[1]
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
async function getNodePosition(
|
||||
comfyPage: ComfyPage,
|
||||
nodeId: number
|
||||
): Promise<Position | undefined> {
|
||||
return comfyPage.page.evaluate((targetNodeId) => {
|
||||
const node = window.app!.graph.nodes.find((n) => n.id === targetNodeId)
|
||||
if (!node) return
|
||||
|
||||
return {
|
||||
x: node.pos[0],
|
||||
y: node.pos[1]
|
||||
}
|
||||
}, nodeId)
|
||||
}
|
||||
|
||||
async function expectNodePositionStable(
|
||||
comfyPage: ComfyPage,
|
||||
initial: NodeSnapshot,
|
||||
mode: string
|
||||
) {
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
const current = await getNodePosition(comfyPage, initial.id)
|
||||
return current?.x ?? Number.NaN
|
||||
},
|
||||
{ message: `node ${initial.id} x drifted in ${mode} mode` }
|
||||
)
|
||||
.toBeCloseTo(initial.x, 1)
|
||||
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
const current = await getNodePosition(comfyPage, initial.id)
|
||||
return current?.y ?? Number.NaN
|
||||
},
|
||||
{ message: `node ${initial.id} y drifted in ${mode} mode` }
|
||||
)
|
||||
.toBeCloseTo(initial.y, 1)
|
||||
}
|
||||
|
||||
async function setVueMode(comfyPage: ComfyPage, enabled: boolean) {
|
||||
await comfyPage.settings.setSetting('Comfy.VueNodes.Enabled', enabled)
|
||||
if (enabled) {
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
}
|
||||
await comfyPage.nextFrame()
|
||||
}
|
||||
|
||||
test.describe(
|
||||
'Renderer toggle stability',
|
||||
{ tag: ['@node', '@canvas'] },
|
||||
() => {
|
||||
test('node positions do not drift when toggling between Vue and LiteGraph renderers', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const TOGGLE_COUNT = 5
|
||||
|
||||
const initialPositions = await getAllNodePositions(comfyPage)
|
||||
expect(initialPositions.length).toBeGreaterThan(0)
|
||||
|
||||
for (let i = 0; i < TOGGLE_COUNT; i++) {
|
||||
await setVueMode(comfyPage, true)
|
||||
for (const initial of initialPositions) {
|
||||
await expectNodePositionStable(
|
||||
comfyPage,
|
||||
initial,
|
||||
`Vue toggle ${i + 1}`
|
||||
)
|
||||
}
|
||||
|
||||
await setVueMode(comfyPage, false)
|
||||
for (const initial of initialPositions) {
|
||||
await expectNodePositionStable(
|
||||
comfyPage,
|
||||
initial,
|
||||
`LiteGraph toggle ${i + 1}`
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user