Skip to content

Commit 19b673c

Browse files
comfy-pr-botDrJKL
andauthored
[backport cloud/1.32] Feat: Load Image (from Outputs) support in Vue Nodes (#6872)
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 <drjkl@comfy.org>
1 parent 50c353e commit 19b673c

File tree

9 files changed

+39
-53
lines changed

9 files changed

+39
-53
lines changed

src/renderer/extensions/vueNodes/components/InputSlot.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<SlotConnectionDot
66
ref="connectionDotRef"
77
:color="slotColor"
8-
:class="cn('-translate-x-1/2', 'w-3', errorClassesDot)"
8+
:class="cn('-translate-x-1/2 w-3', errorClassesDot)"
99
@pointerdown="onPointerDown"
1010
/>
1111

@@ -48,6 +48,7 @@ interface InputSlotProps {
4848
connected?: boolean
4949
compatible?: boolean
5050
dotOnly?: boolean
51+
socketless?: boolean
5152
}
5253
5354
const props = defineProps<InputSlotProps>()
@@ -121,7 +122,8 @@ const slotWrapperClass = computed(() =>
121122
'lg-slot--connected': props.connected,
122123
'lg-slot--compatible': props.compatible,
123124
'opacity-40': shouldDim.value
124-
}
125+
},
126+
props.socketless && 'pointer-events-none invisible'
125127
)
126128
)
127129

src/renderer/extensions/vueNodes/components/NodeWidgets.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
}"
4141
:node-id="nodeData?.id != null ? String(nodeData.id) : ''"
4242
:index="widget.slotMetadata.index"
43+
:socketless="widget.simplified.spec?.socketless"
4344
dot-only
4445
/>
4546
</div>

src/renderer/extensions/vueNodes/widgets/components/WidgetButton.test.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,11 @@ describe('WidgetButton Interactions', () => {
8282
expect(button.exists()).toBe(true)
8383
})
8484

85-
it('renders widget label when name is provided', () => {
85+
it('renders widget text when name is provided', () => {
8686
const widget = createMockWidget()
8787
const wrapper = mountComponent(widget)
8888

89-
const label = wrapper.find('label')
90-
expect(label.exists()).toBe(true)
91-
expect(label.text()).toBe('test_button')
92-
})
93-
94-
it('does not render label when widget name is empty', () => {
95-
const widget = createMockWidget({}, undefined, '')
96-
const wrapper = mountComponent(widget)
97-
98-
const label = wrapper.find('label')
99-
expect(label.exists()).toBe(false)
89+
expect(wrapper.text()).toBe('test_button')
10090
})
10191

10292
it('sets button size to small', () => {

src/renderer/extensions/vueNodes/widgets/components/WidgetButton.vue

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
<template>
22
<div class="flex flex-col gap-1">
3-
<label v-if="widget.name" class="text-secondary text-sm">{{
4-
widget.name
5-
}}</label>
63
<Button
74
v-bind="filteredProps"
85
:aria-label="widget.name || widget.label"
96
size="small"
107
@click="handleClick"
11-
/>
8+
>
9+
<template v-if="widget.name">
10+
{{ widget.name }}
11+
</template>
12+
</Button>
1213
</div>
1314
</template>
1415

src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ const outputItems = computed<DropdownItem[]>(() => {
136136
})
137137
})
138138
139-
return Array.from(outputs).map((output, index) => ({
140-
id: `output-${index}`,
139+
return Array.from(outputs).map((output) => ({
140+
id: `output-${output}`,
141141
mediaSrc: getMediaUrl(output.replace(' [output]', ''), 'output'),
142142
name: output,
143143
label: getDisplayLabel(output),
@@ -215,16 +215,14 @@ const layoutMode = ref<LayoutMode>(props.defaultLayoutMode ?? 'grid')
215215
watch(
216216
modelValue,
217217
(currentValue) => {
218-
if (currentValue !== undefined) {
219-
const item = dropdownItems.value.find(
220-
(item) => item.name === currentValue
221-
)
222-
if (item) {
223-
selectedSet.value.clear()
224-
selectedSet.value.add(item.id)
225-
}
226-
} else {
218+
if (currentValue === undefined) {
219+
selectedSet.value.clear()
220+
return
221+
}
222+
const item = dropdownItems.value.find((item) => item.name === currentValue)
223+
if (item) {
227224
selectedSet.value.clear()
225+
selectedSet.value.add(item.id)
228226
}
229227
},
230228
{ immediate: true }

src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownInput.vue

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,13 @@ const selectedItems = computed(() => {
3232
return props.items.filter((item) => props.selected.has(item.id))
3333
})
3434
35-
const chevronClass = computed(() =>
36-
cn(
37-
'mr-2 size-4 transition-transform duration-200 flex-shrink-0 text-component-node-foreground-secondary',
38-
{
39-
'rotate-180': props.isOpen
40-
}
41-
)
42-
)
43-
4435
const theButtonStyle = computed(() =>
4536
cn(
4637
'border-0 bg-component-node-widget-background outline-none text-text-secondary',
47-
{
48-
'hover:bg-component-node-widget-background-hovered cursor-pointer':
49-
!props.disabled,
50-
'cursor-not-allowed': props.disabled,
51-
'text-text-primary': selectedItems.value.length > 0
52-
}
38+
props.disabled
39+
? 'cursor-not-allowed'
40+
: 'hover:bg-component-node-widget-background-hovered cursor-pointer',
41+
selectedItems.value.length > 0 && 'text-text-primary'
5342
)
5443
)
5544
</script>
@@ -78,13 +67,21 @@ const theButtonStyle = computed(() =>
7867
>
7968
<span class="min-w-0 flex-1 px-1 py-2 text-left truncate">
8069
<span v-if="!selectedItems.length">
81-
{{ props.placeholder }}
70+
{{ placeholder }}
8271
</span>
8372
<span v-else>
8473
{{ selectedItems.map((item) => item.label ?? item.name).join(', ') }}
8574
</span>
8675
</span>
87-
<i class="icon-[lucide--chevron-down]" :class="chevronClass" />
76+
<i
77+
class="icon-[lucide--chevron-down]"
78+
:class="
79+
cn(
80+
'mr-2 size-4 transition-transform duration-200 flex-shrink-0 text-component-node-foreground-secondary',
81+
isOpen && 'rotate-180'
82+
)
83+
"
84+
/>
8885
</button>
8986
<!-- Open File -->
9087
<label

src/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,7 @@ export function useRemoteWidget<
236236
* Add a refresh button to the node that, when clicked, will force the widget to refresh
237237
*/
238238
function addRefreshButton() {
239-
node.addWidget('button', 'refresh', 'refresh', widget.refresh, {
240-
canvasOnly: true
241-
})
239+
node.addWidget('button', 'refresh', 'refresh', widget.refresh)
242240
}
243241

244242
/**
@@ -263,8 +261,7 @@ export function useRemoteWidget<
263261
autoRefreshEnabled = value
264262
},
265263
{
266-
serialize: false,
267-
canvasOnly: true
264+
serialize: false
268265
}
269266
)
270267

src/schemas/nodeDefSchema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export const zBaseInputOptions = z
2929
defaultInput: z.boolean().optional(),
3030
forceInput: z.boolean().optional(),
3131
tooltip: z.string().optional(),
32+
socketless: z.boolean().optional(),
3233
hidden: z.boolean().optional(),
3334
advanced: z.boolean().optional(),
3435
widgetType: z.string().optional(),

tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,8 +610,7 @@ describe('useRemoteWidget', () => {
610610
false,
611611
expect.any(Function),
612612
{
613-
serialize: false,
614-
canvasOnly: true
613+
serialize: false
615614
}
616615
)
617616
})

0 commit comments

Comments
 (0)