mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-14 09:27:41 +00:00
## 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>
105 lines
2.6 KiB
TypeScript
105 lines
2.6 KiB
TypeScript
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}`
|
|
)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
)
|