Files
ComfyUI_frontend/src/renderer/extensions/vueNodes/components/OutputSlot.vue
Alexander Brown 37fab21daf Cleanup: YAGNI readonly props, private swap on ComfyApp, Canvas resize events simplification, v-memos on individual instances (#5869)
## Summary

Assorted cleanup opportunities found while working through the Vue node
rendering logic cleanup.

## Review Focus

Am I wrong that the readonly logic was never actually executing because
it was defined as False in GraphCanvas when making each LGraphNode?

Is there an edge case or some other reason that the ResizeObserver
wouldn't work as a single signal to resize the canvas?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5869-Cleanup-YAGNI-readonly-props-private-swap-on-ComfyApp-Canvas-resize-events-simplificat-27e6d73d3650811ba1dcf29e8d43091e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-02 10:35:10 -07:00

125 lines
3.4 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div v-if="renderError" class="node-error p-1 text-red-500 text-xs"></div>
<div v-else v-tooltip.right="tooltipConfig" :class="slotWrapperClass">
<div class="relative">
<!-- Slot Name -->
<span
v-if="!dotOnly"
class="whitespace-nowrap text-sm font-normal dark-theme:text-slate-200 text-stone-200 lod-toggle"
>
{{ slotData.localized_name || slotData.name || `Output ${index}` }}
</span>
<LODFallback />
</div>
<!-- Connection Dot -->
<SlotConnectionDot
ref="connectionDotRef"
:color="slotColor"
class="translate-x-1/2"
@pointerdown="onPointerDown"
/>
</div>
</template>
<script setup lang="ts">
import {
type ComponentPublicInstance,
type Ref,
computed,
inject,
onErrorCaptured,
ref,
watchEffect
} from 'vue'
import { useErrorHandling } from '@/composables/useErrorHandling'
import { getSlotColor } from '@/constants/slotColors'
import type { INodeSlot } from '@/lib/litegraph/src/litegraph'
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 LODFallback from './LODFallback.vue'
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>()
// Error boundary implementation
const renderError = ref<string | null>(null)
const { toastErrorHandler } = useErrorHandling()
const tooltipContainer =
inject<Ref<HTMLElement | undefined>>('tooltipContainer')
const { getOutputSlotTooltip, createTooltipConfig } = useNodeTooltips(
props.nodeType || '',
tooltipContainer
)
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
})
// Get slot color based on type
const slotColor = computed(() => getSlotColor(props.slotData.type))
const slotWrapperClass = computed(() =>
cn(
'lg-slot lg-slot--output flex items-center justify-end group rounded-l-lg h-6',
'cursor-crosshair',
props.dotOnly
? 'lg-slot--dot-only justify-center'
: 'pl-6 hover:bg-black/5 hover:dark:bg-white/5',
{
'lg-slot--connected': props.connected,
'lg-slot--compatible': props.compatible
}
)
)
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>