mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 19:21:54 +00:00
Fix uploaded image not forcing re-render (#3115)
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -459,7 +459,14 @@ export class ComfyPage {
|
|||||||
await this.nextFrame()
|
await this.nextFrame()
|
||||||
}
|
}
|
||||||
|
|
||||||
async dragAndDropFile(fileName: string) {
|
async dragAndDropFile(
|
||||||
|
fileName: string,
|
||||||
|
options: {
|
||||||
|
dropPosition?: Position
|
||||||
|
} = {}
|
||||||
|
) {
|
||||||
|
const { dropPosition = { x: 100, y: 100 } } = options
|
||||||
|
|
||||||
const filePath = this.assetPath(fileName)
|
const filePath = this.assetPath(fileName)
|
||||||
|
|
||||||
// Read the file content
|
// Read the file content
|
||||||
@@ -477,32 +484,56 @@ export class ComfyPage {
|
|||||||
const fileType = getFileType(fileName)
|
const fileType = getFileType(fileName)
|
||||||
|
|
||||||
await this.page.evaluate(
|
await this.page.evaluate(
|
||||||
async ({ buffer, fileName, fileType }) => {
|
async ({ buffer, fileName, fileType, dropPosition }) => {
|
||||||
const file = new File([new Uint8Array(buffer)], fileName, {
|
const file = new File([new Uint8Array(buffer)], fileName, {
|
||||||
type: fileType
|
type: fileType
|
||||||
})
|
})
|
||||||
const dataTransfer = new DataTransfer()
|
const dataTransfer = new DataTransfer()
|
||||||
dataTransfer.items.add(file)
|
dataTransfer.items.add(file)
|
||||||
|
|
||||||
const dropEvent = new DragEvent('drop', {
|
const targetElement = document.elementFromPoint(
|
||||||
|
dropPosition.x,
|
||||||
|
dropPosition.y
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!targetElement) {
|
||||||
|
console.error('No element found at drop position:', dropPosition)
|
||||||
|
return { success: false, error: 'No element at position' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventOptions = {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
cancelable: true,
|
cancelable: true,
|
||||||
dataTransfer
|
dataTransfer,
|
||||||
})
|
clientX: dropPosition.x,
|
||||||
|
clientY: dropPosition.y
|
||||||
|
}
|
||||||
|
|
||||||
|
const dragOverEvent = new DragEvent('dragover', eventOptions)
|
||||||
|
const dropEvent = new DragEvent('drop', eventOptions)
|
||||||
|
|
||||||
Object.defineProperty(dropEvent, 'preventDefault', {
|
Object.defineProperty(dropEvent, 'preventDefault', {
|
||||||
value: () => {},
|
value: () => {},
|
||||||
writable: false
|
writable: false
|
||||||
})
|
})
|
||||||
|
|
||||||
Object.defineProperty(dropEvent, 'stopPropagation', {
|
Object.defineProperty(dropEvent, 'stopPropagation', {
|
||||||
value: () => {},
|
value: () => {},
|
||||||
writable: false
|
writable: false
|
||||||
})
|
})
|
||||||
|
|
||||||
document.dispatchEvent(dropEvent)
|
targetElement.dispatchEvent(dragOverEvent)
|
||||||
|
targetElement.dispatchEvent(dropEvent)
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
targetInfo: {
|
||||||
|
tagName: targetElement.tagName,
|
||||||
|
id: targetElement.id,
|
||||||
|
classList: Array.from(targetElement.classList)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ buffer: [...new Uint8Array(buffer)], fileName, fileType }
|
{ buffer: [...new Uint8Array(buffer)], fileName, fileType, dropPosition }
|
||||||
)
|
)
|
||||||
|
|
||||||
await this.nextFrame()
|
await this.nextFrame()
|
||||||
|
|||||||
@@ -115,8 +115,20 @@ export class NodeWidgetReference {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
async getValue() {
|
||||||
|
return await this.node.comfyPage.page.evaluate(
|
||||||
|
([id, index]) => {
|
||||||
|
const node = window['app'].graph.getNodeById(id)
|
||||||
|
if (!node) throw new Error(`Node ${id} not found.`)
|
||||||
|
const widget = node.widgets[index]
|
||||||
|
if (!widget) throw new Error(`Widget ${index} not found.`)
|
||||||
|
return widget.value
|
||||||
|
},
|
||||||
|
[this.node.id, this.index] as const
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
export class NodeReference {
|
export class NodeReference {
|
||||||
constructor(
|
constructor(
|
||||||
readonly id: NodeId,
|
readonly id: NodeId,
|
||||||
|
|||||||
@@ -128,11 +128,62 @@ test.describe('Dynamic widget manipulation', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Load image widget', () => {
|
test.describe('Image widget', () => {
|
||||||
test('Can load image', async ({ comfyPage }) => {
|
test('Can load image', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('widgets/load_image_widget')
|
await comfyPage.loadWorkflow('widgets/load_image_widget')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('load_image_widget.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('load_image_widget.png')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Can drag and drop image', async ({ comfyPage }) => {
|
||||||
|
await comfyPage.loadWorkflow('widgets/load_image_widget')
|
||||||
|
|
||||||
|
// Get position of the load image node
|
||||||
|
const nodes = await comfyPage.getNodeRefsByType('LoadImage')
|
||||||
|
const loadImageNode = nodes[0]
|
||||||
|
const { x, y } = await loadImageNode.getPosition()
|
||||||
|
|
||||||
|
// Drag and drop image file onto the load image node
|
||||||
|
await comfyPage.dragAndDropFile('image32x32.webp', {
|
||||||
|
dropPosition: { x, y }
|
||||||
|
})
|
||||||
|
|
||||||
|
// Expect the image preview to change automatically
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'image_preview_drag_and_dropped.png'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Expect the filename combo value to be updated
|
||||||
|
const fileComboWidget = await loadImageNode.getWidget(0)
|
||||||
|
const filename = await fileComboWidget.getValue()
|
||||||
|
expect(filename).toBe('image32x32.webp')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Can change image by changing the filename combo value', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await comfyPage.loadWorkflow('widgets/load_image_widget')
|
||||||
|
const nodes = await comfyPage.getNodeRefsByType('LoadImage')
|
||||||
|
const loadImageNode = nodes[0]
|
||||||
|
|
||||||
|
// Click the combo widget used to select the image filename
|
||||||
|
const fileComboWidget = await loadImageNode.getWidget(0)
|
||||||
|
await fileComboWidget.click()
|
||||||
|
|
||||||
|
// Select a new image filename value from the combo context menu
|
||||||
|
const comboEntry = comfyPage.page.getByRole('menuitem', {
|
||||||
|
name: 'image32x32.webp'
|
||||||
|
})
|
||||||
|
await comboEntry.click({ noWaitAfter: true })
|
||||||
|
|
||||||
|
// Expect the image preview to change automatically
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'image_preview_changed_by_combo_value.png'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Expect the filename combo value to be updated
|
||||||
|
const filename = await fileComboWidget.getValue()
|
||||||
|
expect(filename).toBe('image32x32.webp')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Load audio widget', () => {
|
test.describe('Load audio widget', () => {
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@@ -93,6 +93,7 @@ export const useImageUploadWidget = () => {
|
|||||||
// Add our own callback to the combo widget to render an image when it changes
|
// Add our own callback to the combo widget to render an image when it changes
|
||||||
fileComboWidget.callback = function () {
|
fileComboWidget.callback = function () {
|
||||||
nodeOutputStore.setNodeOutputs(node, fileComboWidget.value)
|
nodeOutputStore.setNodeOutputs(node, fileComboWidget.value)
|
||||||
|
node.graph?.setDirtyCanvas(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// On load if we have a value then render the image
|
// On load if we have a value then render the image
|
||||||
|
|||||||
Reference in New Issue
Block a user