From 19b673cbf5deb7aac7a9cb43f494dbeaef8f24ed Mon Sep 17 00:00:00 2001 From: Comfy Org PR Bot Date: Mon, 24 Nov 2025 06:01:43 +0900 Subject: [PATCH] [backport cloud/1.32] Feat: Load Image (from Outputs) support in Vue Nodes (#6872) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport of #6836 to `cloud/1.32` Automatically created by backport workflow. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6872-backport-cloud-1-32-Feat-Load-Image-from-Outputs-support-in-Vue-Nodes-2b46d73d365081e3a39bc0ba9e19accf) by [Unito](https://www.unito.io) Co-authored-by: Alexander Brown --- .../vueNodes/components/InputSlot.vue | 6 ++-- .../vueNodes/components/NodeWidgets.vue | 1 + .../widgets/components/WidgetButton.test.ts | 14 ++------- .../widgets/components/WidgetButton.vue | 9 +++--- .../components/WidgetSelectDropdown.vue | 20 ++++++------ .../form/dropdown/FormDropdownInput.vue | 31 +++++++++---------- .../widgets/composables/useRemoteWidget.ts | 7 ++--- src/schemas/nodeDefSchema.ts | 1 + .../composables/useRemoteWidget.test.ts | 3 +- 9 files changed, 39 insertions(+), 53 deletions(-) diff --git a/src/renderer/extensions/vueNodes/components/InputSlot.vue b/src/renderer/extensions/vueNodes/components/InputSlot.vue index c5d534638..238a60027 100644 --- a/src/renderer/extensions/vueNodes/components/InputSlot.vue +++ b/src/renderer/extensions/vueNodes/components/InputSlot.vue @@ -5,7 +5,7 @@ @@ -48,6 +48,7 @@ interface InputSlotProps { connected?: boolean compatible?: boolean dotOnly?: boolean + socketless?: boolean } const props = defineProps() @@ -121,7 +122,8 @@ const slotWrapperClass = computed(() => 'lg-slot--connected': props.connected, 'lg-slot--compatible': props.compatible, 'opacity-40': shouldDim.value - } + }, + props.socketless && 'pointer-events-none invisible' ) ) diff --git a/src/renderer/extensions/vueNodes/components/NodeWidgets.vue b/src/renderer/extensions/vueNodes/components/NodeWidgets.vue index 10a06ff15..15a5d07d1 100644 --- a/src/renderer/extensions/vueNodes/components/NodeWidgets.vue +++ b/src/renderer/extensions/vueNodes/components/NodeWidgets.vue @@ -40,6 +40,7 @@ }" :node-id="nodeData?.id != null ? String(nodeData.id) : ''" :index="widget.slotMetadata.index" + :socketless="widget.simplified.spec?.socketless" dot-only /> diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetButton.test.ts b/src/renderer/extensions/vueNodes/widgets/components/WidgetButton.test.ts index 2ca7a4340..d5fd7c154 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetButton.test.ts +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetButton.test.ts @@ -82,21 +82,11 @@ describe('WidgetButton Interactions', () => { expect(button.exists()).toBe(true) }) - it('renders widget label when name is provided', () => { + it('renders widget text when name is provided', () => { const widget = createMockWidget() const wrapper = mountComponent(widget) - const label = wrapper.find('label') - expect(label.exists()).toBe(true) - expect(label.text()).toBe('test_button') - }) - - it('does not render label when widget name is empty', () => { - const widget = createMockWidget({}, undefined, '') - const wrapper = mountComponent(widget) - - const label = wrapper.find('label') - expect(label.exists()).toBe(false) + expect(wrapper.text()).toBe('test_button') }) it('sets button size to small', () => { diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetButton.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetButton.vue index d2a160282..96a24ced1 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetButton.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetButton.vue @@ -1,14 +1,15 @@ diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue index 97cac26e2..e891b9bbd 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue @@ -136,8 +136,8 @@ const outputItems = computed(() => { }) }) - return Array.from(outputs).map((output, index) => ({ - id: `output-${index}`, + return Array.from(outputs).map((output) => ({ + id: `output-${output}`, mediaSrc: getMediaUrl(output.replace(' [output]', ''), 'output'), name: output, label: getDisplayLabel(output), @@ -215,16 +215,14 @@ const layoutMode = ref(props.defaultLayoutMode ?? 'grid') watch( modelValue, (currentValue) => { - if (currentValue !== undefined) { - const item = dropdownItems.value.find( - (item) => item.name === currentValue - ) - if (item) { - selectedSet.value.clear() - selectedSet.value.add(item.id) - } - } else { + if (currentValue === undefined) { selectedSet.value.clear() + return + } + const item = dropdownItems.value.find((item) => item.name === currentValue) + if (item) { + selectedSet.value.clear() + selectedSet.value.add(item.id) } }, { immediate: true } diff --git a/src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownInput.vue b/src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownInput.vue index 8d32d58ba..025a79586 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownInput.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownInput.vue @@ -32,24 +32,13 @@ const selectedItems = computed(() => { return props.items.filter((item) => props.selected.has(item.id)) }) -const chevronClass = computed(() => - cn( - 'mr-2 size-4 transition-transform duration-200 flex-shrink-0 text-component-node-foreground-secondary', - { - 'rotate-180': props.isOpen - } - ) -) - const theButtonStyle = computed(() => cn( 'border-0 bg-component-node-widget-background outline-none text-text-secondary', - { - 'hover:bg-component-node-widget-background-hovered cursor-pointer': - !props.disabled, - 'cursor-not-allowed': props.disabled, - 'text-text-primary': selectedItems.value.length > 0 - } + props.disabled + ? 'cursor-not-allowed' + : 'hover:bg-component-node-widget-background-hovered cursor-pointer', + selectedItems.value.length > 0 && 'text-text-primary' ) ) @@ -78,13 +67,21 @@ const theButtonStyle = computed(() => > - {{ props.placeholder }} + {{ placeholder }} {{ selectedItems.map((item) => item.label ?? item.name).join(', ') }} - +