mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 19:21:54 +00:00
refactor: move node.imgs sync from component to store
Move syncNodeImgs logic from ImagePreview.vue to imagePreviewStore.ts for proper separation of concerns. Store handles backwards compatibility sync when setting node outputs.
This commit is contained in:
@@ -125,7 +125,6 @@ import { computed, ref, watch } from 'vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import { downloadFile } from '@/base/common/downloadUtil'
|
import { downloadFile } from '@/base/common/downloadUtil'
|
||||||
import { app } from '@/scripts/app'
|
|
||||||
import { useCommandStore } from '@/stores/commandStore'
|
import { useCommandStore } from '@/stores/commandStore'
|
||||||
import { useNodeOutputStore } from '@/stores/imagePreviewStore'
|
import { useNodeOutputStore } from '@/stores/imagePreviewStore'
|
||||||
|
|
||||||
@@ -188,17 +187,6 @@ watch(
|
|||||||
{ deep: true, immediate: true }
|
{ deep: true, immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
|
||||||
* Sync node.imgs for backwards compatibility with legacy systems (e.g., Copy Image).
|
|
||||||
*/
|
|
||||||
const syncNodeImgs = () => {
|
|
||||||
if (!props.nodeId || !currentImageEl.value) return
|
|
||||||
const node = app.rootGraph?.getNodeById(props.nodeId)
|
|
||||||
if (!node) return
|
|
||||||
node.imgs = [currentImageEl.value]
|
|
||||||
node.imageIndex = currentIndex.value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event handlers
|
// Event handlers
|
||||||
const handleImageLoad = (event: Event) => {
|
const handleImageLoad = (event: Event) => {
|
||||||
if (!event.target || !(event.target instanceof HTMLImageElement)) return
|
if (!event.target || !(event.target instanceof HTMLImageElement)) return
|
||||||
@@ -209,7 +197,6 @@ const handleImageLoad = (event: Event) => {
|
|||||||
if (img.naturalWidth && img.naturalHeight) {
|
if (img.naturalWidth && img.naturalHeight) {
|
||||||
actualDimensions.value = `${img.naturalWidth} x ${img.naturalHeight}`
|
actualDimensions.value = `${img.naturalWidth} x ${img.naturalHeight}`
|
||||||
}
|
}
|
||||||
syncNodeImgs()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleImageError = () => {
|
const handleImageError = () => {
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ interface SetOutputOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useNodeOutputStore = defineStore('nodeOutput', () => {
|
export const useNodeOutputStore = defineStore('nodeOutput', () => {
|
||||||
const { nodeIdToNodeLocatorId, nodeToNodeLocatorId } = useWorkflowStore()
|
const { nodeIdToNodeLocatorId, nodeToNodeLocatorId, nodeLocatorIdToNodeId } =
|
||||||
|
useWorkflowStore()
|
||||||
const { executionIdToNodeLocatorId } = useExecutionStore()
|
const { executionIdToNodeLocatorId } = useExecutionStore()
|
||||||
const scheduledRevoke: Record<NodeLocatorId, { stop: () => void }> = {}
|
const scheduledRevoke: Record<NodeLocatorId, { stop: () => void }> = {}
|
||||||
const latestOutput = ref<string[]>([])
|
const latestOutput = ref<string[]>([])
|
||||||
@@ -156,6 +157,26 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
|
|||||||
}) ?? []
|
}) ?? []
|
||||||
app.nodeOutputs[nodeLocatorId] = outputs
|
app.nodeOutputs[nodeLocatorId] = outputs
|
||||||
nodeOutputs.value[nodeLocatorId] = outputs
|
nodeOutputs.value[nodeLocatorId] = outputs
|
||||||
|
|
||||||
|
syncNodeImgs(nodeLocatorId, latestOutput.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync node.imgs for backwards compatibility with legacy systems (e.g., Copy Image).
|
||||||
|
*/
|
||||||
|
function syncNodeImgs(nodeLocatorId: NodeLocatorId, imageUrls: string[]) {
|
||||||
|
if (!imageUrls.length) return
|
||||||
|
const nodeId = nodeLocatorIdToNodeId(nodeLocatorId)
|
||||||
|
if (nodeId === null) return
|
||||||
|
const node = app.canvas?.graph?.getNodeById(nodeId)
|
||||||
|
if (!node) return
|
||||||
|
|
||||||
|
const img = new Image()
|
||||||
|
img.onload = () => {
|
||||||
|
node.imgs = [img]
|
||||||
|
node.imageIndex = 0
|
||||||
|
}
|
||||||
|
img.src = imageUrls[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNodeOutputs(
|
function setNodeOutputs(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createTestingPinia } from '@pinia/testing'
|
import { createTestingPinia } from '@pinia/testing'
|
||||||
import type { VueWrapper } from '@vue/test-utils'
|
import type { VueWrapper } from '@vue/test-utils'
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
|
|
||||||
@@ -13,22 +13,6 @@ vi.mock('@/base/common/downloadUtil', () => ({
|
|||||||
downloadFile: vi.fn()
|
downloadFile: vi.fn()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Mock app for node.imgs sync tests
|
|
||||||
const mockNode: {
|
|
||||||
imgs?: HTMLImageElement[] | undefined
|
|
||||||
imageIndex?: number | null
|
|
||||||
} = {}
|
|
||||||
vi.mock('@/scripts/app', () => ({
|
|
||||||
app: {
|
|
||||||
rootGraph: {
|
|
||||||
getNodeById: vi.fn(() => mockNode)
|
|
||||||
},
|
|
||||||
canvas: {
|
|
||||||
select: vi.fn()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
const i18n = createI18n({
|
const i18n = createI18n({
|
||||||
legacy: false,
|
legacy: false,
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
@@ -60,11 +44,6 @@ describe('ImagePreview', () => {
|
|||||||
}
|
}
|
||||||
const wrapperRegistry = new Set<VueWrapper>()
|
const wrapperRegistry = new Set<VueWrapper>()
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
delete mockNode.imgs
|
|
||||||
delete mockNode.imageIndex
|
|
||||||
})
|
|
||||||
|
|
||||||
const mountImagePreview = (props = {}) => {
|
const mountImagePreview = (props = {}) => {
|
||||||
const wrapper = mount(ImagePreview, {
|
const wrapper = mount(ImagePreview, {
|
||||||
props: { ...defaultProps, ...props },
|
props: { ...defaultProps, ...props },
|
||||||
@@ -329,21 +308,4 @@ describe('ImagePreview', () => {
|
|||||||
expect(imgElement.exists()).toBe(true)
|
expect(imgElement.exists()).toBe(true)
|
||||||
expect(imgElement.attributes('alt')).toBe('Node output 2')
|
expect(imgElement.attributes('alt')).toBe('Node output 2')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('syncs node.imgs on image load for legacy compatibility', async () => {
|
|
||||||
const wrapper = mountImagePreview({
|
|
||||||
imageUrls: [defaultProps.imageUrls[0]],
|
|
||||||
nodeId: 'test-node-123'
|
|
||||||
})
|
|
||||||
|
|
||||||
const img = wrapper.find('img')
|
|
||||||
expect(img.exists()).toBe(true)
|
|
||||||
|
|
||||||
await img.trigger('load')
|
|
||||||
await nextTick()
|
|
||||||
|
|
||||||
expect(mockNode.imgs).toHaveLength(1)
|
|
||||||
expect(mockNode.imgs?.[0]).toBe(img.element)
|
|
||||||
expect(mockNode.imageIndex).toBe(0)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user