Skip to content
Merged
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
Feat: Load Image (from Outputs) support in Vue Nodes (#6836)
## Summary

Expose the Auto-refresh and manual refresh controls.
Fixes an issue with the Dropdown where it was index dependent so wasn't
updating correctly as new items came in.

## Screenshots

<!-- Add screenshots or video recording to help explain your changes -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6836-Feat-Load-Image-from-Outputs-support-in-Vue-Nodes-2b46d73d365081f1b44fcf2054d653da)
by [Unito](https://www.unito.io)
  • Loading branch information
DrJKL authored and github-actions[bot] committed Nov 23, 2025
commit 3296660aff24f75bd409f137c27618f1440e06ab
6 changes: 4 additions & 2 deletions src/renderer/extensions/vueNodes/components/InputSlot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<SlotConnectionDot
ref="connectionDotRef"
:color="slotColor"
:class="cn('-translate-x-1/2', 'w-3', errorClassesDot)"
:class="cn('-translate-x-1/2 w-3', errorClassesDot)"
@pointerdown="onPointerDown"
/>

Expand Down Expand Up @@ -48,6 +48,7 @@ interface InputSlotProps {
connected?: boolean
compatible?: boolean
dotOnly?: boolean
socketless?: boolean
}

const props = defineProps<InputSlotProps>()
Expand Down Expand Up @@ -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'
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
}"
:node-id="nodeData?.id != null ? String(nodeData.id) : ''"
:index="widget.slotMetadata.index"
:socketless="widget.simplified.spec?.socketless"
dot-only
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<template>
<div class="flex flex-col gap-1">
<label v-if="widget.name" class="text-secondary text-sm">{{
widget.name
}}</label>
<Button
v-bind="filteredProps"
:aria-label="widget.name || widget.label"
size="small"
@click="handleClick"
/>
>
<template v-if="widget.name">
{{ widget.name }}
</template>
</Button>
</div>
</template>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ const outputItems = computed<DropdownItem[]>(() => {
})
})

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),
Expand Down Expand Up @@ -215,16 +215,14 @@ const layoutMode = ref<LayoutMode>(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 }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
)
)
</script>
Expand Down Expand Up @@ -78,13 +67,21 @@ const theButtonStyle = computed(() =>
>
<span class="min-w-0 flex-1 px-1 py-2 text-left truncate">
<span v-if="!selectedItems.length">
{{ props.placeholder }}
{{ placeholder }}
</span>
<span v-else>
{{ selectedItems.map((item) => item.label ?? item.name).join(', ') }}
</span>
</span>
<i class="icon-[lucide--chevron-down]" :class="chevronClass" />
<i
class="icon-[lucide--chevron-down]"
:class="
cn(
'mr-2 size-4 transition-transform duration-200 flex-shrink-0 text-component-node-foreground-secondary',
isOpen && 'rotate-180'
)
"
/>
</button>
<!-- Open File -->
<label
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,7 @@ export function useRemoteWidget<
* Add a refresh button to the node that, when clicked, will force the widget to refresh
*/
function addRefreshButton() {
node.addWidget('button', 'refresh', 'refresh', widget.refresh, {
canvasOnly: true
})
node.addWidget('button', 'refresh', 'refresh', widget.refresh)
}

/**
Expand All @@ -263,8 +261,7 @@ export function useRemoteWidget<
autoRefreshEnabled = value
},
{
serialize: false,
canvasOnly: true
serialize: false
}
)

Expand Down
1 change: 1 addition & 0 deletions src/schemas/nodeDefSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const zBaseInputOptions = z
defaultInput: z.boolean().optional(),
forceInput: z.boolean().optional(),
tooltip: z.string().optional(),
socketless: z.boolean().optional(),
hidden: z.boolean().optional(),
advanced: z.boolean().optional(),
widgetType: z.string().optional(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -610,8 +610,7 @@ describe('useRemoteWidget', () => {
false,
expect.any(Function),
{
serialize: false,
canvasOnly: true
serialize: false
}
)
})
Expand Down