Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/renderer/extensions/vueNodes/components/ImagePreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<!-- Main Image -->
<img
v-else
ref="currentImageEl"
:src="currentImageUrl"
:alt="imageAltText"
class="block size-full object-contain"
Expand Down Expand Up @@ -118,6 +119,7 @@ import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'

import { downloadFile } from '@/base/common/downloadUtil'
import { app } from '@/scripts/app'
import { useCommandStore } from '@/stores/commandStore'
import { useNodeOutputStore } from '@/stores/imagePreviewStore'

Expand All @@ -143,6 +145,8 @@ const actualDimensions = ref<string | null>(null)
const imageError = ref(false)
const isLoading = ref(false)

const currentImageEl = ref<HTMLImageElement>()

// Computed values
const currentImageUrl = computed(() => props.imageUrls[currentIndex.value])
const hasMultipleImages = computed(() => props.imageUrls.length > 1)
Expand Down Expand Up @@ -182,7 +186,18 @@ const handleImageError = () => {
actualDimensions.value = null
}

// In vueNodes mode, we need to set them manually before opening the mask editor.
const setupNodeForMaskEditor = () => {
if (!props.nodeId || !currentImageEl.value) return
const node = app.rootGraph?.getNodeById(props.nodeId)
if (!node) return
node.imageIndex = currentIndex.value
node.imgs = [currentImageEl.value]
app.canvas?.select(node)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary? How can the node be unselected if this is being called?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image Yes, this is necessary, please check the screenshot. In this case, even if no node is selected, the user may still see the icon to open the mask editor when hovering the mouse over the image. However, because no node is currently selected, the mask editor cannot be opened in this situation, since the mask editor requires accessing the selected node.

}

const handleEditMask = () => {
setupNodeForMaskEditor()
void commandStore.execute('Comfy.MaskEditor.OpenMaskEditor')
}

Expand Down
11 changes: 9 additions & 2 deletions src/scripts/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,15 @@ export class ComfyApp {
const paintedIndex = selectedIndex + 1
const combinedIndex = selectedIndex + 2

// for vueNodes mode
const images =
node.images ?? useNodeOutputStore().getNodeOutputs(node)?.images

ComfyApp.clipspace = {
widgets: widgets,
imgs: imgs,
original_imgs: orig_imgs,
images: node.images,
images: images,
selectedIndex: selectedIndex,
img_paste_mode: 'selected', // reset to default im_paste_mode state on copy action
paintedIndex: paintedIndex,
Expand All @@ -411,7 +415,8 @@ export class ComfyApp {
ComfyApp.clipspace.imgs[ComfyApp.clipspace.combinedIndex].src
}
if (ComfyApp.clipspace.imgs && node.imgs) {
if (node.images && ComfyApp.clipspace.images) {
// Update node.images even if it's initially undefined (vueNodes mode)
if (ComfyApp.clipspace.images) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[quality] high Priority

Issue: Logic condition removed may cause null reference errors
Context: Removed check for node.images existence before accessing ClipSpace.images could cause undefined/null references
Suggestion: Restore defensive check or add null safety: if (ComfyApp.clipspace.images && node.images)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to remove this confition as under VueNodes, node.images will be undefined and need to be updated

if (ComfyApp.clipspace['img_paste_mode'] == 'selected') {
node.images = [
ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']]
Expand Down Expand Up @@ -513,6 +518,8 @@ export class ComfyApp {
}

app.graph.setDirtyCanvas(true)

useNodeOutputStore().updateNodeImages(node)
}
}

Expand Down
21 changes: 21 additions & 0 deletions src/stores/imagePreviewStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,26 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
nodeOutputs.value = outputs
}

function updateNodeImages(node: LGraphNode) {
if (!node.images?.length) return

const nodeLocatorId = nodeIdToNodeLocatorId(node.id)

if (nodeLocatorId) {
const existingOutputs = app.nodeOutputs[nodeLocatorId]

if (existingOutputs) {
const updatedOutputs = {
...existingOutputs,
images: node.images
}

app.nodeOutputs[nodeLocatorId] = updatedOutputs
nodeOutputs.value[nodeLocatorId] = updatedOutputs
}
}
}

function resetAllOutputsAndPreviews() {
app.nodeOutputs = {}
nodeOutputs.value = {}
Expand All @@ -349,6 +369,7 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
setNodeOutputsByExecutionId,
setNodePreviewsByExecutionId,
setNodePreviewsByNodeId,
updateNodeImages,

// Cleanup
revokePreviewsByExecutionId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,8 @@ describe('ImagePreview', () => {
await navigationDots[1].trigger('click')
await nextTick()

// After clicking, component shows loading state (Skeleton), not img
// After clicking, component shows loading state (Skeleton)
expect(wrapper.find('skeleton-stub').exists()).toBe(true)
expect(wrapper.find('img').exists()).toBe(false)

// Simulate image load event to clear loading state
const component = wrapper.vm as any
Expand Down