feat: add KSampler live previews to assets sidebar jobs (#8723)

## Summary
Show live KSampler previews on active job cards/list items in the Assets
sidebar, while preserving existing fallback behavior.

## Changes
- **What**:
- Added a prompt-scoped job preview store (`jobPreviewStore`) gated by
`Comfy.Execution.PreviewMethod`.
- Wired `b_preview_with_metadata` handling to map previews by
`promptId`.
- Extended queue job view model with `livePreviewUrl` and consumed it in
both sidebar list and grid active job UIs.
  - Cleared prompt previews on execution reset.
- Added ref-counted shared blob URL lifecycle utility (`objectUrlUtil`)
and updated preview stores to retain/release shared URLs so each preview
event creates one object URL.
- Added/updated unit coverage in `useJobList.test.ts` for preview
enable/disable mapping.

## Review Focus
- Object URL lifecycle correctness across node previews and job previews
(retain/release behavior).
- Preview gating behavior when `Comfy.Execution.PreviewMethod` is
`none`.
- Active job UI fallback behavior (`livePreviewUrl` -> `iconImageUrl`).

## Screenshots (if applicable)
<img width="808" height="614" alt="image"
src="https://github.com/user-attachments/assets/37c66eb2-8c28-4eb4-bb86-5679cb77d740"
/>
<img width="775" height="345" alt="image"
src="https://github.com/user-attachments/assets/aa420642-b0d4-4ae6-b94a-e7934b5df9d6"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8723-feat-add-KSampler-live-previews-to-assets-sidebar-jobs-3006d73d365081aeb81dd8279bf99f94)
by [Unito](https://www.unito.io)
This commit is contained in:
Benjamin Lu
2026-02-09 10:49:27 -08:00
committed by GitHub
parent 815be49112
commit 9209badd37
7 changed files with 199 additions and 5 deletions

View File

@@ -61,6 +61,7 @@ import { useExecutionStore } from '@/stores/executionStore'
import { useExtensionStore } from '@/stores/extensionStore'
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
import { useNodeOutputStore } from '@/stores/imagePreviewStore'
import { useJobPreviewStore } from '@/stores/jobPreviewStore'
import { KeyComboImpl } from '@/platform/keybindings/keyCombo'
import { useKeybindingStore } from '@/platform/keybindings/keybindingStore'
import { useModelStore } from '@/stores/modelStore'
@@ -86,6 +87,10 @@ import {
fixLinkInputSlots,
isImageNode
} from '@/utils/litegraphUtil'
import {
createSharedObjectUrl,
releaseSharedObjectUrl
} from '@/utils/objectUrlUtil'
import {
findLegacyRerouteNodes,
noNativeReroutes
@@ -701,12 +706,13 @@ export class ComfyApp {
api.addEventListener('b_preview_with_metadata', ({ detail }) => {
// Enhanced preview with explicit node context
const { blob, displayNodeId } = detail
const { blob, displayNodeId, promptId } = detail
const { setNodePreviewsByExecutionId, revokePreviewsByExecutionId } =
useNodeOutputStore()
const blobUrl = createSharedObjectUrl(blob)
useJobPreviewStore().setPreviewUrl(promptId, blobUrl)
// Ensure clean up if `executing` event is missed.
revokePreviewsByExecutionId(displayNodeId)
const blobUrl = URL.createObjectURL(blob)
// Preview cleanup is handled in progress_state event to support multiple concurrent previews
const nodeParents = displayNodeId.split(':')
for (let i = 1; i <= nodeParents.length; i++) {
@@ -714,6 +720,7 @@ export class ComfyApp {
blobUrl
])
}
releaseSharedObjectUrl(blobUrl)
})
api.init()