From 6f05bbcfcb487ea73caa0c4d5c1e9de7e2f3331f Mon Sep 17 00:00:00 2001 From: Rizumu Ayaka Date: Fri, 23 Jan 2026 20:02:13 +0800 Subject: [PATCH] fix: default image input for the template is displayed as empty on dropdown selection --- .../components/WidgetSelectDropdown.test.ts | 83 +++++++++++++++++++ .../components/WidgetSelectDropdown.vue | 35 +++++++- 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.test.ts b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.test.ts index 730ceb0bf..42e725485 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.test.ts +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.test.ts @@ -171,4 +171,87 @@ describe('WidgetSelectDropdown custom label mapping', () => { expect(Array.isArray(outputItems)).toBe(true) }) }) + + describe('missing value handling for template-loaded nodes', () => { + it('creates a fallback item in "all" filter when modelValue is not in available items', () => { + const widget = createMockWidget('template_image.png', { + values: ['img_001.png', 'photo_abc.jpg'] + }) + const wrapper = mountComponent(widget, 'template_image.png') + + const inputItems = wrapper.vm.inputItems + expect(inputItems).toHaveLength(2) + expect( + inputItems.some((item) => item.name === 'template_image.png') + ).toBe(false) + + // The missing value should be accessible via dropdownItems when filter is 'all' (default) + const dropdownItems = ( + wrapper.vm as unknown as { dropdownItems: DropdownItem[] } + ).dropdownItems + expect( + dropdownItems.some((item) => item.name === 'template_image.png') + ).toBe(true) + expect(dropdownItems[0].name).toBe('template_image.png') + expect(dropdownItems[0].id).toBe('missing-template_image.png') + }) + + it('does not include fallback item when filter is "inputs" or "outputs"', async () => { + const widget = createMockWidget('template_image.png', { + values: ['img_001.png', 'photo_abc.jpg'] + }) + const wrapper = mountComponent(widget, 'template_image.png') + + // Set filter to 'inputs' + await wrapper.setProps({ filterSelected: 'inputs' } as never) + await wrapper.vm.$nextTick() + + const vmWithFilter = wrapper.vm as unknown as { + filterSelected: string + dropdownItems: DropdownItem[] + } + + // Manually update filterSelected since it's a model + vmWithFilter.filterSelected = 'inputs' + await wrapper.vm.$nextTick() + + // inputItems should not contain the missing value + expect(wrapper.vm.inputItems).toHaveLength(2) + expect( + wrapper.vm.inputItems.every( + (item) => !String(item.id).startsWith('missing-') + ) + ).toBe(true) + }) + + it('does not create a fallback item when modelValue exists in available items', () => { + const widget = createMockWidget('img_001.png', { + values: ['img_001.png', 'photo_abc.jpg'] + }) + const wrapper = mountComponent(widget, 'img_001.png') + + const dropdownItems = ( + wrapper.vm as unknown as { dropdownItems: DropdownItem[] } + ).dropdownItems + expect(dropdownItems).toHaveLength(2) + expect( + dropdownItems.every((item) => !String(item.id).startsWith('missing-')) + ).toBe(true) + }) + + it('does not create a fallback item when modelValue is undefined', () => { + const widget = createMockWidget(undefined as unknown as string, { + values: ['img_001.png', 'photo_abc.jpg'] + }) + const wrapper = mountComponent(widget, undefined) + + const dropdownItems = ( + wrapper.vm as unknown as { dropdownItems: DropdownItem[] } + ).dropdownItems + expect(dropdownItems).toHaveLength(2) + expect( + dropdownItems.every((item) => !String(item.id).startsWith('missing-')) + ).toBe(true) + }) + }) }) diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue index 84cb49218..95237f5b7 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue @@ -146,11 +146,42 @@ const outputItems = computed(() => { })) }) +/** + * Creates a fallback item for the current modelValue when it doesn't exist + * in the available items list. This handles cases like template-loaded nodes + * where the saved value may not exist in the current server environment. + */ +const missingValueItem = computed(() => { + const currentValue = modelValue.value + if (!currentValue) return null + + const existsInInputs = inputItems.value.some( + (item) => item.name === currentValue + ) + const existsInOutputs = outputItems.value.some( + (item) => item.name === currentValue + ) + + if (existsInInputs || existsInOutputs) return null + + return { + id: `missing-${currentValue}`, + mediaSrc: getMediaUrl(currentValue, 'input'), + name: currentValue, + label: getDisplayLabel(currentValue), + metadata: '' + } +}) + const allItems = computed(() => { if (props.isAssetMode && assetData) { return assetData.dropdownItems.value } - return [...inputItems.value, ...outputItems.value] + const items = [...inputItems.value, ...outputItems.value] + if (missingValueItem.value) { + items.unshift(missingValueItem.value) + } + return items }) const dropdownItems = computed(() => { @@ -165,7 +196,7 @@ const dropdownItems = computed(() => { return outputItems.value case 'all': default: - return [...inputItems.value, ...outputItems.value] + return allItems.value } })