Compare commits

...

6 Commits

Author SHA1 Message Date
Comfy Org PR Bot
0b952a8b21 1.39.13 (#8916)
Patch version increment to 1.39.13

**Base branch:** `core/1.39`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8916-1-39-13-3096d73d3650815599b5f1890498a9e8)
by [Unito](https://www.unito.io)

Co-authored-by: comfy-pr-bot <172744619+comfy-pr-bot@users.noreply.github.com>
2026-02-16 22:01:49 -08:00
Comfy Org PR Bot
9f760b543b [backport core/1.39] fix: SaveImage node not updating outputs during batch runs (vue-nodes) (#8893)
Backport of #8862 to `core/1.39`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8893-backport-core-1-39-fix-SaveImage-node-not-updating-outputs-during-batch-runs-vue-node-3086d73d36508181a887e26121d3e590)
by [Unito](https://www.unito.io)

Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Terry Jia <terryjia88@gmail.com>
2026-02-15 13:38:57 +00:00
Comfy Org PR Bot
805649e2c9 [backport core/1.39] fix: hide output images for ImageCropV2 node (#8883)
Backport of #8873 to `core/1.39`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8883-backport-core-1-39-fix-hide-output-images-for-ImageCropV2-node-3076d73d3650810da523e02735cedab9)
by [Unito](https://www.unito.io)

Co-authored-by: Terry Jia <terryjia88@gmail.com>
Co-authored-by: pythongosssss <125205205+pythongosssss@users.noreply.github.com>
2026-02-15 05:01:49 -08:00
Comfy Org PR Bot
fc627106e1 [backport core/1.39] feat: add hideOutputImages flag for nodes with custom preview (#8891)
Backport of #8857 to `core/1.39`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8891-backport-core-1-39-feat-add-hideOutputImages-flag-for-nodes-with-custom-preview-3086d73d365081e292d1d363d019eb38)
by [Unito](https://www.unito.io)

Co-authored-by: Terry Jia <terryjia88@gmail.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-02-15 04:50:50 -08:00
Comfy Org PR Bot
3b88f55158 [backport core/1.39] fix: clear draft on workflow close to prevent stale state on reopen (#8870)
Backport of #8854 to `core/1.39`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8870-backport-core-1-39-fix-clear-draft-on-workflow-close-to-prevent-stale-state-on-reopen-3076d73d365081448562eccd00a790b0)
by [Unito](https://www.unito.io)

Co-authored-by: Christian Byrne <cbyrne@comfy.org>
2026-02-14 02:59:05 -08:00
Comfy Org PR Bot
8cfc714e9f [backport core/1.39] Add z-index to popover component (#8824)
Backport of #8823 to `core/1.39`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8824-backport-core-1-39-Add-z-index-to-popover-component-3056d73d3650818da234dfc0095e612c)
by [Unito](https://www.unito.io)

Co-authored-by: AustinMroz <austin@comfy.org>
2026-02-11 21:51:44 -08:00
10 changed files with 67 additions and 7 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@comfyorg/comfyui-frontend",
"version": "1.39.12",
"version": "1.39.13",
"private": true,
"description": "Official front-end implementation of ComfyUI",
"homepage": "https://comfy.org",

View File

@@ -36,7 +36,7 @@ defineProps<{
:side-offset="5"
:collision-padding="10"
v-bind="$attrs"
class="rounded-lg p-2 bg-base-background shadow-sm border border-border-subtle will-change-[transform,opacity] data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade"
class="z-1700 rounded-lg p-2 bg-base-background shadow-sm border border-border-subtle will-change-[transform,opacity] data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade"
>
<slot>
<div class="flex flex-col p-1">

View File

@@ -6,6 +6,7 @@ useExtensionService().registerExtension({
async nodeCreated(node) {
if (node.constructor.comfyClass !== 'ImageCropV2') return
node.hideOutputImages = true
const [oldWidth, oldHeight] = node.size
node.setSize([Math.max(oldWidth, 300), Math.max(oldHeight, 450)])
}

View File

@@ -215,6 +215,8 @@ export const useWorkflowService = () => {
}
}
workflowDraftStore.removeDraft(workflow.path)
// If this is the last workflow, create a new default temporary workflow
if (workflowStore.openWorkflows.length === 1) {
await loadDefaultWorkflow()

View File

@@ -12,6 +12,7 @@ import {
useWorkflowBookmarkStore,
useWorkflowStore
} from '@/platform/workflow/management/stores/workflowStore'
import { useWorkflowDraftStore } from '@/platform/workflow/persistence/stores/workflowDraftStore'
import { api } from '@/scripts/api'
import { app as comfyApp } from '@/scripts/app'
import { defaultGraph, defaultGraphJSON } from '@/scripts/defaultGraph'
@@ -911,4 +912,41 @@ describe('useWorkflowStore', () => {
expect(mostRecent).toBeNull()
})
})
describe('closeWorkflow draft cleanup', () => {
it('should remove draft for persisted workflows on close', async () => {
const draftStore = useWorkflowDraftStore()
await syncRemoteWorkflows(['a.json'])
const workflow = store.getWorkflowByPath('workflows/a.json')!
draftStore.saveDraft('workflows/a.json', {
data: '{"dirty":true}',
updatedAt: Date.now(),
name: 'a.json',
isTemporary: false
})
expect(draftStore.getDraft('workflows/a.json')).toBeDefined()
await store.closeWorkflow(workflow)
expect(draftStore.getDraft('workflows/a.json')).toBeUndefined()
})
it('should remove draft for temporary workflows on close', async () => {
const draftStore = useWorkflowDraftStore()
const workflow = store.createTemporary('temp.json')
draftStore.saveDraft(workflow.path, {
data: '{"dirty":true}',
updatedAt: Date.now(),
name: 'temp.json',
isTemporary: true
})
expect(draftStore.getDraft(workflow.path)).toBeDefined()
await store.closeWorkflow(workflow)
expect(draftStore.getDraft(workflow.path)).toBeUndefined()
})
})
})

View File

@@ -320,11 +320,9 @@ export const useWorkflowStore = defineStore('workflow', () => {
openWorkflowPaths.value = openWorkflowPaths.value.filter(
(path) => path !== workflow.path
)
useWorkflowDraftStore().removeDraft(workflow.path)
if (workflow.isTemporary) {
// Clear thumbnail when temporary workflow is closed
clearThumbnail(workflow.key)
// Clear draft when unsaved workflow tab is closed
useWorkflowDraftStore().removeDraft(workflow.path)
delete workflowLookup.value[workflow.path]
} else {
workflow.unload()

View File

@@ -567,7 +567,8 @@ const nodeMedia = computed(() => {
const newOutputs = nodeOutputs.nodeOutputs[nodeOutputLocatorId.value]
const node = lgraphNode.value
if (!node || !newOutputs?.images?.length) return undefined
if (!node || !newOutputs?.images?.length || node.hideOutputImages)
return undefined
const urls = nodeOutputs.getNodeImageUrls(node)
if (!urls?.length) return undefined

View File

@@ -85,6 +85,24 @@ describe('imagePreviewStore setNodeOutputsByExecutionId with merge', () => {
)
expect(store.nodeOutputs[executionId]?.images).toHaveLength(2)
})
it('should create a new object reference on merge so Vue detects the change', () => {
const store = useNodeOutputStore()
const executionId = '1'
const initialOutput = createMockOutputs([{ filename: 'a.png' }])
store.setNodeOutputsByExecutionId(executionId, initialOutput)
const refBefore = store.nodeOutputs[executionId]
const newOutput = createMockOutputs([{ filename: 'b.png' }])
store.setNodeOutputsByExecutionId(executionId, newOutput, { merge: true })
const refAfter = store.nodeOutputs[executionId]
expect(refAfter).not.toBe(refBefore)
expect(refAfter?.images).toHaveLength(2)
})
})
describe('imagePreviewStore getPreviewParam', () => {

View File

@@ -152,7 +152,7 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
existingOutput[k] = newValue
}
}
nodeOutputs.value[nodeLocatorId] = existingOutput
nodeOutputs.value[nodeLocatorId] = { ...existingOutput }
return
}
}

View File

@@ -177,6 +177,8 @@ declare module '@/lib/litegraph/src/litegraph' {
isLoading?: boolean
/** The content type of the node's preview media */
previewMediaType?: 'image' | 'video' | 'audio' | 'model'
/** If true, output images are stored but not rendered below the node */
hideOutputImages?: boolean
preview: string[]
/** Index of the currently selected image on a multi-image node such as Preview Image */