From 7e4c756258cd5cc5546d48df8a95d1325d138315 Mon Sep 17 00:00:00 2001 From: Rizumu Ayaka Date: Fri, 3 Oct 2025 04:06:08 +0800 Subject: [PATCH] feat: inputs/outputs filter to widget dropdown (#5894) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related https://github.com/Comfy-Org/ComfyUI_frontend/issues/5827 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5894-feat-inputs-outputs-filter-to-widget-dropdown-2806d73d365081498d92d0576b7da6a8) by [Unito](https://www.unito.io) --------- Co-authored-by: bymyself --- .../components/WidgetSelectDropdown.vue | 78 +++++++++++++++---- .../components/form/dropdown/FormDropdown.vue | 2 +- .../composables/useWidgetRenderer.test.ts | 8 +- 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue index 1332c563d..83d6854eb 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue @@ -7,6 +7,7 @@ import { t } from '@/i18n' import { useToastStore } from '@/platform/updates/common/toastStore' import type { ResultItemType } from '@/schemas/apiSchema' import { api } from '@/scripts/api' +import { useQueueStore } from '@/stores/queueStore' import type { SimplifiedWidget } from '@/types/simplifiedWidget' import type { AssetKind } from '@/types/widgetTypes' import { @@ -42,6 +43,7 @@ const { localValue, onChange } = useWidgetValue({ }) const toastStore = useToastStore() +const queueStore = useQueueStore() const transformCompatProps = useTransformCompatOverlayProps() @@ -50,8 +52,15 @@ const combinedProps = computed(() => ({ ...transformCompatProps.value })) +const filterSelected = ref('all') +const filterOptions = ref([ + { id: 'all', name: 'All' }, + { id: 'inputs', name: 'Inputs' }, + { id: 'outputs', name: 'Outputs' } +]) + const selectedSet = ref>(new Set()) -const dropdownItems = computed(() => { +const inputItems = computed(() => { const values = props.widget.options?.values || [] if (!Array.isArray(values)) { @@ -59,12 +68,57 @@ const dropdownItems = computed(() => { } return values.map((value: string, index: number) => ({ - id: index, - imageSrc: getMediaUrl(value), + id: `input-${index}`, + imageSrc: getMediaUrl(value, 'input'), name: value, metadata: '' })) }) +const outputItems = computed(() => { + if (!['image', 'video'].includes(props.assetKind ?? '')) return [] + + const outputs = new Set() + + // 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) + } + }) + }) + + return Array.from(outputs).map((output, index) => ({ + id: `output-${index}`, + imageSrc: getMediaUrl(output.replace(' [output]', ''), 'output'), + name: output, + metadata: '' + })) +}) + +const allItems = computed(() => { + return [...inputItems.value, ...outputItems.value] +}) +const dropdownItems = computed(() => { + switch (filterSelected.value) { + case 'inputs': + return inputItems.value + case 'outputs': + return outputItems.value + case 'all': + default: + return allItems.value + } +}) const mediaPlaceholder = computed(() => { const options = props.widget.options @@ -197,19 +251,13 @@ async function handleFilesUpdate(files: File[]) { } } -function getMediaUrl(filename: string): string { - if (props.assetKind !== 'image') return '' - // TODO: This needs to be adapted based on actual ComfyUI API structure - return `/api/view?filename=${encodeURIComponent(filename)}&type=input` +function getMediaUrl( + filename: string, + type: 'input' | 'output' = 'input' +): string { + if (!['image', 'video'].includes(props.assetKind ?? '')) return '' + return `/api/view?filename=${encodeURIComponent(filename)}&type=${type}` } - -// TODO handle filter logic -const filterSelected = ref('all') -const filterOptions = ref([ - { id: 'all', name: 'All' }, - { id: 'image', name: 'Inputs' }, - { id: 'video', name: 'Outputs' } -])