Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { computed, provide, ref, toRef, watch } from 'vue'
import { useWidgetValue } from '@/composables/graph/useWidgetValue'
import { useTransformCompatOverlayProps } from '@/composables/useTransformCompatOverlayProps'
import { t } from '@/i18n'
import { useMediaAssets } from '@/platform/assets/composables/media/useMediaAssets'
import { useToastStore } from '@/platform/updates/common/toastStore'
import FormDropdown from '@/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdown.vue'
import { AssetKindKey } from '@/renderer/extensions/vueNodes/widgets/components/form/dropdown/types'
Expand All @@ -19,9 +20,9 @@ import { useAssetWidgetData } from '@/renderer/extensions/vueNodes/widgets/compo
import type { ResultItemType } from '@/schemas/apiSchema'
import { api } from '@/scripts/api'
import { useAssetsStore } from '@/stores/assetsStore'
import { useQueueStore } from '@/stores/queueStore'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import type { AssetKind } from '@/types/widgetTypes'
import { getMediaTypeFromFilename } from '@/utils/formatUtil'
import {
PANEL_EXCLUDED_PROPS,
filterWidgetProps
Expand Down Expand Up @@ -55,7 +56,8 @@ const { localValue, onChange } = useWidgetValue({
})

const toastStore = useToastStore()
const queueStore = useQueueStore()

const outputMediaAssets = useMediaAssets('output')

const transformCompatProps = useTransformCompatOverlayProps()

Expand Down Expand Up @@ -119,37 +121,42 @@ const inputItems = computed<DropdownItem[]>(() => {
}))
})
const outputItems = computed<DropdownItem[]>(() => {
if (!['image', 'video'].includes(props.assetKind ?? '')) return []

const outputs = new Set<string>()

// Extract output images/videos from queue history
queueStore.historyTasks.forEach((task) => {
task.flatOutputs.forEach((output) => {
const isTargetType =
(props.assetKind === 'image' && output.mediaType === 'images') ||
(props.assetKind === 'video' && output.mediaType === 'video')

if (output.type === 'output' && isTargetType) {
const path = output.subfolder
? `${output.subfolder}/${output.filename}`
: output.filename
// Add [output] annotation so the preview component knows the type
const annotatedPath = `${path} [output]`
outputs.add(annotatedPath)
}
})
if (!['image', 'video', 'audio'].includes(props.assetKind ?? '')) return []

// Filter assets by media type using getMediaTypeFromFilename
const outputFiles = outputMediaAssets.media.value.filter((asset) => {
const mediaType = getMediaTypeFromFilename(asset.name)
return toAssertType(mediaType) === props.assetKind
})

return Array.from(outputs).map((output, index) => ({
id: `output-${index}`,
mediaSrc: getMediaUrl(output.replace(' [output]', ''), 'output'),
name: output,
label: getDisplayLabel(output),
metadata: ''
}))
return outputFiles.map((asset, index) => {
// Add [output] annotation so the preview component knows the type
const annotatedPath = `${asset.name} [output]`
return {
id: `output-${index}`,
mediaSrc: asset.preview_url || getMediaUrl(asset.name, 'output'),
name: annotatedPath,
label: getDisplayLabel(annotatedPath),
metadata: ''
}
})
})
Comment on lines +124 to 143
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

outputItems guard prevents model outputs from ever showing

In outputItems, this guard:

if (!['image', 'video', 'audio'].includes(props.assetKind ?? '')) return []

short-circuits for assetKind === 'model', even though toAssertType explicitly maps a '3D' media type to 'model'. That means 3D/model outputs will never appear in the dropdown, despite having logic to support them.

A minimal fix is to include 'model' in the allowed list so those outputs can be surfaced:

-const outputItems = computed<DropdownItem[]>(() => {
-  if (!['image', 'video', 'audio'].includes(props.assetKind ?? '')) return []
+const outputItems = computed<DropdownItem[]>(() => {
+  if (!['image', 'video', 'audio', 'model'].includes(props.assetKind ?? '')) {
+    return []
+  }
@@
-  const outputFiles = outputMediaAssets.media.value.filter((asset) => {
-    const mediaType = getMediaTypeFromFilename(asset.name)
-    return toAssertType(mediaType) === props.assetKind
-  })
+  const assetKind = props.assetKind
+
+  const outputFiles = outputMediaAssets.media.value.filter((asset) => {
+    const mediaType = getMediaTypeFromFilename(asset.name)
+    return assetKind != null && toAssertType(mediaType) === assetKind
+  })

That keeps the filtering behavior but actually allows model/3D outputs to show up. If assetKind: 'unknown' should also see outputs, you may want to relax the guard further.

Also applies to: 145-158, 340-346


I'll help you verify the review comment about the WidgetSelectDropdown.vue file. Let me examine the code and understand the context.


---

🏁 Script executed:

```shell
#!/bin/bash
# Examine the target file
cat src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue | head -200

Length of output: 5887


🏁 Script executed:

#!/bin/bash
# Search for toAssertType function definition
rg -n "toAssertType" --type=ts --type=js --type=vue -A 5 -B 2

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for getMediaTypeFromFilename to understand media type mapping
rg -n "getMediaTypeFromFilename" --type=ts --type=js --type=vue -A 5 -B 2

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Look for assetKind prop definition
rg -n "assetKind" src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue -B 3 -A 3

Length of output: 1553



---

🏁 Script executed:

```shell
#!/bin/bash
# Find AssetKind type definition
rg -n "type AssetKind" -A 5 -B 2

Length of output: 311


🏁 Script executed:

#!/bin/bash
# Get full context around getMediaUrl function
sed -n '338,347p' src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue

Length of output: 240


🏁 Script executed:

#!/bin/bash
# Get the complete outputItems and toAssertType functions
sed -n '123,158p' src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue

Length of output: 1047


Fix guards to allow model outputs to display

The outputItems computed property currently returns empty for any assetKind not in ['image', 'video', 'audio']. However, toAssertType explicitly maps the '3D' media type to 'model' (line 145), and AssetKind type definition includes 'model' as valid, yet the guard at line 124 prevents model outputs from ever being processed.

Additionally, getMediaUrl (line 344) has a similar guard that excludes both 'audio' and 'model' from media URL generation.

Required fixes:

  1. Line 124: Add 'model' to the allowed asset kinds in outputItems
  2. Line 344: Update getMediaUrl guard to include 'audio' and 'model' (or make it comprehensive based on intended support)

The suggested diff in the original review for line 124 is appropriate. Apply similar logic to the getMediaUrl guard to ensure model assets can display media previews.

🤖 Prompt for AI Agents
In src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue
around lines 124-143 and line ~344, the outputItems computed property and
getMediaUrl guard currently exclude 'model' (and getMediaUrl also excludes
'audio'), preventing model outputs from being processed/displayed; update the
guard at ~124 to include 'model' in the allowed asset kinds (e.g.,
['image','video','audio','model']) and adjust the getMediaUrl guard at ~344 to
include 'audio' and 'model' (or broaden the guard to allow all AssetKind values
your UI supports) so model assets are passed through to preview URL generation
and rendering.


function toAssertType(
mediaType: ReturnType<typeof getMediaTypeFromFilename>
): AssetKind {
switch (mediaType) {
case 'image':
case 'video':
case 'audio':
return mediaType
case '3D':
return 'model'
default:
return 'unknown'
}
}

const allItems = computed<DropdownItem[]>(() => {
if (props.isAssetMode && assetData) {
return assetData.dropdownItems.value
Expand Down Expand Up @@ -337,6 +344,12 @@ function getMediaUrl(
if (!['image', 'video'].includes(props.assetKind ?? '')) return ''
return `/api/view?filename=${encodeURIComponent(filename)}&type=${type}`
}

function handleIsOpenUpdate(isOpen: boolean) {
if (isOpen && !outputMediaAssets.loading) {
outputMediaAssets.refresh()
}
}
</script>

<template>
Expand All @@ -355,6 +368,7 @@ function getMediaUrl(
class="w-full"
@update:selected="updateSelectedItems"
@update:files="handleFilesUpdate"
@update:is-open="handleIsOpenUpdate"
/>
</WidgetLayoutField>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const props = withDefaults(defineProps<Props>(), {
searcher: defaultSearcher
})

const isOpen = defineModel<boolean>('isOpen', { default: false })
const selected = defineModel<Set<SelectedKey>>('selected', {
default: new Set()
})
Expand All @@ -75,7 +76,6 @@ const isQuerying = ref(false)
const toastStore = useToastStore()
const popoverRef = ref<InstanceType<typeof Popover>>()
const triggerRef = useTemplateRef('triggerRef')
const isOpen = ref(false)

const maxSelectable = computed(() => {
if (props.multiple === true) return Infinity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@ const searchQuery = defineModel<string>('searchQuery')
"
>
<div class="pointer-events-none absolute inset-x-3 top-0 z-10 h-5" />
<div
v-if="items.length === 0"
class="absolute inset-0 flex items-center justify-center"
>
<i
title="No items"
class="icon-[lucide--circle-off] size-30 text-zinc-500/20"
/>
</div>
<template v-if="items.length === 0">
Copy link

Choose a reason for hiding this comment

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

[quality] low Priority

Issue: Template structure change from div to template is good but missing semantic improvement
Context: Using template wrapper is more performant than div for conditional rendering
Suggestion: Consider adding proper ARIA attributes or role for better accessibility

<div class="absolute inset-0 flex items-center justify-center">
<i
title="No items"
class="icon-[lucide--circle-off] size-30 text-zinc-500/20"
/>
</div>
Comment on lines +71 to +76
Copy link
Contributor

Choose a reason for hiding this comment

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

note: We also have this

(not blocking this PR).

<div class="min-h-50" />
</template>
<!-- Item -->
<FormDropdownMenuItem
v-for="(item, index) in items"
Expand Down