Compare commits
4 Commits
v1.45.8
...
fix/node-n
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2601ee307d | [automated] Update test expectations | ||
|
|
acc68e478a |
fix: use processSelect to trigger onSelectionChange after node placement
canvas.select() only sets selectionChanged flag but never calls onSelectionChange or setDirty, so Vue reactive state and the canvas render were never updated. processSelect() completes all three steps in the correct order. |
||
|
|
a11a2841ed |
fix: defer node selection to nextTick to outlast processMouseUp deselectAll
The click-to-place flow fires pointerup on the canvas after endDrag places the node. processMouseDown had already recorded the position as empty canvas and set pointer.onClick to processSelect(null), which calls deselectAll() in processMouseUp - overwriting any selection we set synchronously in addNodeOnGraph. Deferring the select/deselectAll to the next microtask (nextTick) ensures it runs after processMouseUp completes, so the newly placed node ends up selected regardless of how the canvas event cycle resolved. |
||
|
|
f71fb2e9dd |
fix: select node after placing it on graph from node library
After placement via click or drag from the node library sidebar, the node was not entered into canvas.selected_nodes. The ghost path already handled this via startGhostPlacement, but the direct-add path had no equivalent selection call. Call canvas.deselectAll() + canvas.select(node) after graph.add() for all non-ghost placements so the behavior is consistent regardless of how a node is added. |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 96 KiB |
|
|
@@ -2,13 +2,34 @@ import { createTestingPinia } from '@pinia/testing'
|
|||
import { setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const mockProcessSelect = vi.hoisted(() => vi.fn())
|
||||
const mockGraphAdd = vi.hoisted(() => vi.fn())
|
||||
|
||||
vi.mock('@/scripts/app', () => ({
|
||||
app: { canvas: undefined },
|
||||
app: { canvas: undefined, graph: null },
|
||||
ComfyApp: class {}
|
||||
}))
|
||||
|
||||
vi.mock('@/renderer/core/canvas/canvasStore', () => ({
|
||||
useCanvasStore: vi.fn(() => ({
|
||||
canvas: { processSelect: mockProcessSelect }
|
||||
}))
|
||||
}))
|
||||
|
||||
vi.mock('@/platform/workflow/management/stores/workflowStore', () => ({
|
||||
useWorkflowStore: vi.fn(() => ({ activeSubgraph: null }))
|
||||
}))
|
||||
|
||||
vi.mock('@/stores/subgraphStore', () => ({
|
||||
useSubgraphStore: vi.fn(() => ({ typePrefix: 'SubgraphBlueprint.' }))
|
||||
}))
|
||||
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import type { ComfyNodeDef as ComfyNodeDefV1 } from '@/schemas/nodeDefSchema'
|
||||
import { app } from '@/scripts/app'
|
||||
import { useLitegraphService } from '@/services/litegraphService'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
describe('useLitegraphService().getCanvasCenter', () => {
|
||||
beforeEach(() => {
|
||||
|
|
@@ -41,3 +62,50 @@ describe('useLitegraphService().getCanvasCenter', () => {
|
|||
expect(center).toEqual([110, 70])
|
||||
})
|
||||
})
|
||||
|
||||
describe('useLitegraphService().addNodeOnGraph', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
mockProcessSelect.mockReset()
|
||||
mockGraphAdd.mockReset()
|
||||
Reflect.set(app, 'canvas', undefined)
|
||||
Reflect.set(app, 'graph', { add: mockGraphAdd })
|
||||
})
|
||||
|
||||
it('selects the node after placing it on the graph', async () => {
|
||||
const fakeNode = { id: 1, flags: {} }
|
||||
vi.spyOn(LiteGraph, 'createNode').mockReturnValue(
|
||||
fakeNode as unknown as LGraphNode
|
||||
)
|
||||
const nodeDef = {
|
||||
name: 'TestNode',
|
||||
display_name: 'Test Node'
|
||||
} as unknown as ComfyNodeDefV1
|
||||
|
||||
useLitegraphService().addNodeOnGraph(nodeDef, { pos: [0, 0] })
|
||||
await nextTick()
|
||||
|
||||
expect(mockProcessSelect).toHaveBeenCalledOnce()
|
||||
expect(mockProcessSelect).toHaveBeenCalledWith(fakeNode, undefined)
|
||||
})
|
||||
|
||||
it('does not select the node when placing in ghost mode', async () => {
|
||||
const fakeNode = { id: 1, flags: {} }
|
||||
vi.spyOn(LiteGraph, 'createNode').mockReturnValue(
|
||||
fakeNode as unknown as LGraphNode
|
||||
)
|
||||
const nodeDef = {
|
||||
name: 'TestNode',
|
||||
display_name: 'Test Node'
|
||||
} as unknown as ComfyNodeDefV1
|
||||
|
||||
useLitegraphService().addNodeOnGraph(
|
||||
nodeDef,
|
||||
{ pos: [0, 0] },
|
||||
{ ghost: true }
|
||||
)
|
||||
await nextTick()
|
||||
|
||||
expect(mockProcessSelect).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@@ -62,6 +62,8 @@ import { useSubgraphStore } from '@/stores/subgraphStore'
|
|||
import { useFavoritedWidgetsStore } from '@/stores/workspace/favoritedWidgetsStore'
|
||||
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
|
||||
import { useWidgetStore } from '@/stores/widgetStore'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
||||
import {
|
||||
isAnimatedOutput,
|
||||
|
|
@@ -944,6 +946,14 @@ export const useLitegraphService = () => {
|
|||
if (!graph || !node) return null
|
||||
|
||||
graph.add(node, addOptions)
|
||||
if (!addOptions?.ghost) {
|
||||
const canvas = canvasStore.canvas
|
||||
if (canvas) {
|
||||
void nextTick(() => {
|
||||
canvas.processSelect(node, undefined)
|
||||
})
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
|
|
|
|||