mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-25 08:49:36 +00:00
## Summary Fixes reroute node styling in Vue Nodes 2.0 by hiding slot labels when slot names are intentionally empty. | Before | After | | --------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | <img width="1437" height="473" alt="image" src="https://github.com/user-attachments/assets/603f52e0-7b75-4822-8c91-0a8374cc0cb6" /> | <img width="1350" height="493" alt="image" src="https://github.com/user-attachments/assets/38168955-4d35-4c61-a685-a54efb44cd5d" /> | ## Problem Reroute nodes displayed unwanted fallback labels ("Input 0", "Output 0") instead of appearing as minimal connection-only nodes. This happened because: - Reroute nodes intentionally use empty string (`""`) for slot names - Slot components used `||` operator for fallback labels, treating `''` as falsy ## Solution - Add `hasNoLabel` computed property to detect when all label sources (`label`, `localized_name`, `name`) are empty/falsy - Derive `dotOnly` from either the existing prop OR `hasNoLabel` being true - When `dotOnly` is true: label text is hidden, padding removed (`lg-slot--dot-only` class), only connection dot visible Combined with existing `NO_TITLE` support from #7589, reroutes now display as minimal nodes with just connection dots—matching classic reroute appearance. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Bug Fixes** * Enhanced input and output slot label handling to automatically conceal labels when unavailable * Improved fallback display names for slots with more reliable naming logic <!-- end of auto-generated comment: release notes by coderabbit.ai --> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8574-fix-vue-nodes-hide-slot-labels-for-reroute-nodes-with-empty-names-2fc6d73d365081c38031e260402283d3) by [Unito](https://www.unito.io)
124 lines
3.7 KiB
Vue
124 lines
3.7 KiB
Vue
<template>
|
||
<div v-if="renderError" class="node-error p-1 text-xs text-red-500">⚠️</div>
|
||
<div v-else v-tooltip.right="tooltipConfig" :class="slotWrapperClass">
|
||
<div class="relative h-full flex items-center min-w-0">
|
||
<!-- Slot Name -->
|
||
<span
|
||
v-if="!props.dotOnly && !hasNoLabel"
|
||
class="truncate text-node-component-slot-text"
|
||
>
|
||
{{ slotData.localized_name || (slotData.name ?? `Output ${index}`) }}
|
||
</span>
|
||
</div>
|
||
<!-- Connection Dot -->
|
||
<SlotConnectionDot
|
||
ref="connectionDotRef"
|
||
class="w-3 translate-x-1/2"
|
||
:slot-data
|
||
@pointerdown="onPointerDown"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, onErrorCaptured, ref, watchEffect } from 'vue'
|
||
import type { ComponentPublicInstance } from 'vue'
|
||
|
||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||
import type { INodeSlot } from '@/lib/litegraph/src/litegraph'
|
||
import { useSlotLinkDragUIState } from '@/renderer/core/canvas/links/slotLinkDragUIState'
|
||
import { getSlotKey } from '@/renderer/core/layout/slots/slotIdentifier'
|
||
import { useNodeTooltips } from '@/renderer/extensions/vueNodes/composables/useNodeTooltips'
|
||
import { useSlotElementTracking } from '@/renderer/extensions/vueNodes/composables/useSlotElementTracking'
|
||
import { useSlotLinkInteraction } from '@/renderer/extensions/vueNodes/composables/useSlotLinkInteraction'
|
||
import { cn } from '@/utils/tailwindUtil'
|
||
|
||
import SlotConnectionDot from './SlotConnectionDot.vue'
|
||
|
||
interface OutputSlotProps {
|
||
nodeType?: string
|
||
nodeId?: string
|
||
slotData: INodeSlot
|
||
index: number
|
||
connected?: boolean
|
||
compatible?: boolean
|
||
dotOnly?: boolean
|
||
}
|
||
|
||
const props = defineProps<OutputSlotProps>()
|
||
|
||
const hasNoLabel = computed(
|
||
() => !props.slotData.localized_name && props.slotData.name === ''
|
||
)
|
||
const dotOnly = computed(() => props.dotOnly || hasNoLabel.value)
|
||
|
||
// Error boundary implementation
|
||
const renderError = ref<string | null>(null)
|
||
|
||
const { toastErrorHandler } = useErrorHandling()
|
||
|
||
const { getOutputSlotTooltip, createTooltipConfig } = useNodeTooltips(
|
||
props.nodeType || ''
|
||
)
|
||
|
||
const tooltipConfig = computed(() => {
|
||
const slotName = props.slotData.name || ''
|
||
const tooltipText = getOutputSlotTooltip(props.index)
|
||
const fallbackText = tooltipText || `Output: ${slotName}`
|
||
return createTooltipConfig(fallbackText)
|
||
})
|
||
|
||
onErrorCaptured((error) => {
|
||
renderError.value = error.message
|
||
toastErrorHandler(error)
|
||
return false
|
||
})
|
||
|
||
const { state: dragState } = useSlotLinkDragUIState()
|
||
const slotKey = computed(() =>
|
||
getSlotKey(props.nodeId ?? '', props.index, false)
|
||
)
|
||
const shouldDim = computed(() => {
|
||
if (!dragState.active) return false
|
||
return !dragState.compatible.get(slotKey.value)
|
||
})
|
||
|
||
const slotWrapperClass = computed(() =>
|
||
cn(
|
||
'lg-slot lg-slot--output flex items-center justify-end group rounded-l-lg h-6',
|
||
'cursor-crosshair',
|
||
dotOnly.value ? 'lg-slot--dot-only justify-center' : 'pl-6',
|
||
{
|
||
'lg-slot--connected': props.connected,
|
||
'lg-slot--compatible': props.compatible,
|
||
'opacity-40': shouldDim.value
|
||
}
|
||
)
|
||
)
|
||
|
||
const connectionDotRef = ref<ComponentPublicInstance<{
|
||
slotElRef: HTMLElement | undefined
|
||
}> | null>(null)
|
||
const slotElRef = ref<HTMLElement | null>(null)
|
||
|
||
// Watch for when the child component's ref becomes available
|
||
// Vue automatically unwraps the Ref when exposing it
|
||
watchEffect(() => {
|
||
const el = connectionDotRef.value?.slotElRef
|
||
slotElRef.value = el || null
|
||
})
|
||
|
||
useSlotElementTracking({
|
||
nodeId: props.nodeId ?? '',
|
||
index: props.index,
|
||
type: 'output',
|
||
element: slotElRef
|
||
})
|
||
|
||
const { onPointerDown } = useSlotLinkInteraction({
|
||
nodeId: props.nodeId ?? '',
|
||
index: props.index,
|
||
type: 'output'
|
||
})
|
||
</script>
|