Show sampling previews on Vue nodes (#5579)

* refactor: simplify preview state provider

- Remove unnecessary event listeners and manual syncing
- Use computed() to directly reference app.nodePreviewImages
- Eliminate data duplication and any types
- Rely on Vue's reactivity for automatic updates
- Follow established patterns from execution state provider

* feat: optimize Vue node preview image display with reactive store

- Move preview display logic from inline ternaries to computed properties
- Add useNodePreviewState composable for preview state management
- Implement reactive store approach using Pinia storeToRefs
- Use VueUse useTimeoutFn for modern timeout management instead of window.setTimeout
- Add v-memo optimization for preview image template rendering
- Maintain proper sync between app.nodePreviewImages and reactive store state

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: update props usage for Vue 3.5 destructured props syntax

* [refactor] improve code style and architecture based on review feedback

- Replace inject pattern with direct store access in useNodePreviewState
- Use optional chaining for more concise conditional checks
- Use modern Array.at(-1) for accessing last element
- Remove provide/inject for nodePreviewImages in favor of direct store refs
- Update preview image styling: remove rounded borders, use flexible height
- Simplify scheduleRevoke function with optional chaining

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>

* [cleanup] remove unused NodePreviewImagesKey injection key

Addresses knip unused export warning after switching from provide/inject
to direct store access pattern.

* [test] add mock for useNodePreviewState in LGraphNode test

Fixes test failure after adding preview functionality to LGraphNode component.

* [fix] update workflowStore import path after rebase

Updates import to new location: @/platform/workflow/management/stores/workflowStore

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
This commit is contained in:
Christian Byrne
2025-09-16 17:34:04 -07:00
committed by GitHub
parent 15cffe9d9e
commit 0483630f82
4 changed files with 117 additions and 4 deletions

View File

@@ -114,6 +114,18 @@
:lod-level="lodLevel"
:image-urls="nodeImageUrls"
/>
<!-- Live preview image -->
<div
v-if="shouldShowPreviewImg"
v-memo="[latestPreviewUrl]"
class="px-4"
>
<img
:src="latestPreviewUrl"
alt="preview"
class="w-full max-h-64 object-contain"
/>
</div>
</div>
</template>
</div>
@@ -138,6 +150,7 @@ import { SelectedNodeIdsKey } from '@/renderer/core/canvas/injectionKeys'
import { useNodeExecutionState } from '@/renderer/extensions/vueNodes/execution/useNodeExecutionState'
import { useNodeLayout } from '@/renderer/extensions/vueNodes/layout/useNodeLayout'
import { LODLevel, useLOD } from '@/renderer/extensions/vueNodes/lod/useLOD'
import { useNodePreviewState } from '@/renderer/extensions/vueNodes/preview/useNodePreviewState'
import { ExecutedWsMessage } from '@/schemas/apiSchema'
import { app } from '@/scripts/app'
import { useExecutionStore } from '@/stores/executionStore'
@@ -312,6 +325,14 @@ const separatorClasses =
'bg-sand-100 dark-theme:bg-charcoal-600 h-px mx-0 w-full'
const progressClasses = 'h-2 bg-primary-500 transition-all duration-300'
const { latestPreviewUrl, shouldShowPreviewImg } = useNodePreviewState(
nodeData.id,
{
isMinimalLOD,
isCollapsed
}
)
// Common condition computations to avoid repetition
const shouldShowWidgets = computed(
() => shouldRenderWidgets.value && nodeData.widgets?.length

View File

@@ -0,0 +1,51 @@
import { storeToRefs } from 'pinia'
import { type Ref, computed } from 'vue'
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
import { useNodeOutputStore } from '@/stores/imagePreviewStore'
export const useNodePreviewState = (
nodeId: string,
options?: {
isMinimalLOD?: Ref<boolean>
isCollapsed?: Ref<boolean>
}
) => {
const workflowStore = useWorkflowStore()
const { nodePreviewImages } = storeToRefs(useNodeOutputStore())
const locatorId = computed(() => workflowStore.nodeIdToNodeLocatorId(nodeId))
const previewUrls = computed(() => {
const key = locatorId.value
if (!key) return undefined
const urls = nodePreviewImages.value[key]
return urls?.length ? urls : undefined
})
const hasPreview = computed(() => !!previewUrls.value?.length)
const latestPreviewUrl = computed(() => {
const urls = previewUrls.value
return urls?.length ? urls.at(-1) : ''
})
const shouldShowPreviewImg = computed(() => {
if (!options?.isMinimalLOD || !options?.isCollapsed) {
return hasPreview.value
}
return (
!options.isMinimalLOD.value &&
!options.isCollapsed.value &&
hasPreview.value
)
})
return {
locatorId,
previewUrls,
hasPreview,
latestPreviewUrl,
shouldShowPreviewImg
}
}