mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
refactor: migrate node tooltips from PrimeVue to BaseTooltip
Replace PrimeVue v-tooltip directive with Reka UI BaseTooltip component in NodeHeader, InputSlot, OutputSlot, and NodeWidgets. Remove PrimeVue-specific tooltip config (createTooltipConfig, hideTooltipsGlobally, tooltipsTemporarilyDisabled, global event listeners) from useNodeTooltips composable, keeping only text-resolution functions and tooltipsEnabled. Add tooltipDelay computed from LiteGraph.Node.TooltipDelay setting.
This commit is contained in:
@@ -1,63 +1,71 @@
|
||||
<template>
|
||||
<div v-if="renderError" class="node-error p-1 text-xs text-red-500">⚠️</div>
|
||||
<div
|
||||
<BaseTooltip
|
||||
v-else
|
||||
v-tooltip.left="tooltipConfig"
|
||||
:class="
|
||||
cn(
|
||||
'lg-slot lg-slot--input group m-0 flex items-center rounded-r-lg',
|
||||
'cursor-crosshair',
|
||||
dotOnly ? 'lg-slot--dot-only' : 'pr-6',
|
||||
{
|
||||
'lg-slot--connected': props.connected,
|
||||
'lg-slot--compatible': props.compatible,
|
||||
'opacity-40': shouldDim
|
||||
},
|
||||
props.socketless && 'pointer-events-none invisible'
|
||||
)
|
||||
"
|
||||
:text="inputTooltipText"
|
||||
side="left"
|
||||
size="large"
|
||||
:delay-duration="tooltipDelay"
|
||||
:disabled="!tooltipsEnabled"
|
||||
>
|
||||
<!-- Connection Dot -->
|
||||
<SlotConnectionDot
|
||||
ref="connectionDotRef"
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'w-3 -translate-x-1/2',
|
||||
hasError &&
|
||||
'before:pointer-events-none before:absolute before:size-4 before:rounded-full before:ring-2 before:ring-error before:ring-offset-0'
|
||||
'lg-slot lg-slot--input group m-0 flex items-center rounded-r-lg',
|
||||
'cursor-crosshair',
|
||||
dotOnly ? 'lg-slot--dot-only' : 'pr-6',
|
||||
{
|
||||
'lg-slot--connected': props.connected,
|
||||
'lg-slot--compatible': props.compatible,
|
||||
'opacity-40': shouldDim
|
||||
},
|
||||
props.socketless && 'pointer-events-none invisible'
|
||||
)
|
||||
"
|
||||
:slot-data
|
||||
@click="onClick"
|
||||
@dblclick="onDoubleClick"
|
||||
@pointerdown="onPointerDown"
|
||||
/>
|
||||
|
||||
<!-- Slot Name -->
|
||||
<div class="flex h-full min-w-0 items-center">
|
||||
<span
|
||||
v-if="!props.dotOnly && !hasNoLabel"
|
||||
>
|
||||
<!-- Connection Dot -->
|
||||
<SlotConnectionDot
|
||||
ref="connectionDotRef"
|
||||
:class="
|
||||
cn(
|
||||
'truncate text-node-component-slot-text',
|
||||
hasError && 'font-medium text-error'
|
||||
'w-3 -translate-x-1/2',
|
||||
hasError &&
|
||||
'before:pointer-events-none before:absolute before:size-4 before:rounded-full before:ring-2 before:ring-error before:ring-offset-0'
|
||||
)
|
||||
"
|
||||
>
|
||||
{{
|
||||
slotData.label ||
|
||||
slotData.localized_name ||
|
||||
(slotData.name ?? `Input ${index}`)
|
||||
}}
|
||||
</span>
|
||||
:slot-data
|
||||
@click="onClick"
|
||||
@dblclick="onDoubleClick"
|
||||
@pointerdown="onPointerDown"
|
||||
/>
|
||||
|
||||
<!-- Slot Name -->
|
||||
<div class="flex h-full min-w-0 items-center">
|
||||
<span
|
||||
v-if="!props.dotOnly && !hasNoLabel"
|
||||
:class="
|
||||
cn(
|
||||
'truncate text-node-component-slot-text',
|
||||
hasError && 'font-medium text-error'
|
||||
)
|
||||
"
|
||||
>
|
||||
{{
|
||||
slotData.label ||
|
||||
slotData.localized_name ||
|
||||
(slotData.name ?? `Input ${index}`)
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseTooltip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onErrorCaptured, ref, watchEffect } from 'vue'
|
||||
import type { ComponentPublicInstance } from 'vue'
|
||||
|
||||
import BaseTooltip from '@/components/ui/tooltip/BaseTooltip.vue'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import type { INodeSlot } from '@/lib/litegraph/src/litegraph'
|
||||
import { useSlotLinkDragUIState } from '@/renderer/core/canvas/links/slotLinkDragUIState'
|
||||
@@ -94,15 +102,13 @@ const dotOnly = computed(() => props.dotOnly || hasNoLabel.value)
|
||||
const renderError = ref<string | null>(null)
|
||||
const { toastErrorHandler } = useErrorHandling()
|
||||
|
||||
const { getInputSlotTooltip, createTooltipConfig } = useNodeTooltips(
|
||||
const { getInputSlotTooltip, tooltipsEnabled, tooltipDelay } = useNodeTooltips(
|
||||
props.nodeType || ''
|
||||
)
|
||||
|
||||
const tooltipConfig = computed(() => {
|
||||
const inputTooltipText = computed(() => {
|
||||
const slotName = props.slotData.localized_name || props.slotData.name || ''
|
||||
const tooltipText = getInputSlotTooltip(slotName)
|
||||
const fallbackText = tooltipText || `Input: ${slotName}`
|
||||
return createTooltipConfig(fallbackText)
|
||||
return getInputSlotTooltip(slotName) || `Input: ${slotName}`
|
||||
})
|
||||
|
||||
onErrorCaptured((error) => {
|
||||
|
||||
@@ -192,6 +192,7 @@ describe('LGraphNode', () => {
|
||||
global: {
|
||||
plugins: [pinia, i18n],
|
||||
stubs: {
|
||||
BaseTooltip: { template: '<div><slot /></div>' },
|
||||
NodeSlots: true,
|
||||
NodeWidgets: true,
|
||||
NodeContent: true,
|
||||
|
||||
@@ -96,11 +96,9 @@ const createMountConfig = () => {
|
||||
global: {
|
||||
plugins: [PrimeVue, i18n, pinia],
|
||||
components: { InputText },
|
||||
directives: {
|
||||
tooltip: {
|
||||
mounted: vi.fn(),
|
||||
updated: vi.fn(),
|
||||
unmounted: vi.fn()
|
||||
stubs: {
|
||||
BaseTooltip: {
|
||||
template: '<div><slot /></div>'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,56 +188,13 @@ describe('NodeHeader.vue', () => {
|
||||
})
|
||||
|
||||
describe('Tooltips', () => {
|
||||
it('applies tooltip directive to node title with correct configuration', () => {
|
||||
it('renders node title inside a tooltip wrapper', () => {
|
||||
const wrapper = mountHeader({
|
||||
nodeData: makeNodeData({ type: 'KSampler' })
|
||||
})
|
||||
|
||||
const titleElement = wrapper.find('[data-testid="node-title"]')
|
||||
expect(titleElement.exists()).toBe(true)
|
||||
|
||||
// Check that v-tooltip directive was applied
|
||||
const directive = wrapper.vm.$el.querySelector(
|
||||
'[data-testid="node-title"]'
|
||||
)
|
||||
expect(directive).toBeTruthy()
|
||||
})
|
||||
|
||||
it('disables tooltip when editing is active', async () => {
|
||||
const wrapper = mountHeader({
|
||||
nodeData: makeNodeData({ type: 'KSampler' })
|
||||
})
|
||||
|
||||
// Enter edit mode
|
||||
await wrapper.get('[data-testid="node-header-1"]').trigger('dblclick')
|
||||
|
||||
// Tooltip should be disabled during editing
|
||||
const titleElement = wrapper.find('[data-testid="node-title"]')
|
||||
expect(titleElement.exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('creates tooltip configuration when component mounts', () => {
|
||||
const wrapper = mountHeader({
|
||||
nodeData: makeNodeData({ type: 'KSampler' })
|
||||
})
|
||||
|
||||
// Verify tooltip directive is applied to the title element
|
||||
const titleElement = wrapper.find('[data-testid="node-title"]')
|
||||
expect(titleElement.exists()).toBe(true)
|
||||
|
||||
// The tooltip composable should be initialized
|
||||
expect(wrapper.vm).toBeDefined()
|
||||
})
|
||||
|
||||
it('uses tooltip container from provide/inject', () => {
|
||||
const wrapper = mountHeader({
|
||||
nodeData: makeNodeData({ type: 'KSampler' })
|
||||
})
|
||||
|
||||
expect(wrapper.exists()).toBe(true)
|
||||
// Container should be provided through inject
|
||||
const titleElement = wrapper.find('[data-testid="node-title"]')
|
||||
expect(titleElement.exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -38,21 +38,28 @@
|
||||
</Button>
|
||||
</div>
|
||||
<!-- Node Title -->
|
||||
<div
|
||||
v-tooltip.top="tooltipConfig"
|
||||
class="flex min-w-0 flex-1 items-center gap-2"
|
||||
data-testid="node-title"
|
||||
<BaseTooltip
|
||||
:text="isEditing ? '' : getNodeDescription"
|
||||
side="top"
|
||||
size="large"
|
||||
:delay-duration="tooltipDelay"
|
||||
:disabled="!tooltipsEnabled"
|
||||
>
|
||||
<div class="flex-1 truncate">
|
||||
<EditableText
|
||||
:model-value="displayTitle"
|
||||
:is-editing="isEditing"
|
||||
:input-attrs="{ 'data-testid': 'node-title-input' }"
|
||||
@edit="handleTitleEdit"
|
||||
@cancel="handleTitleCancel"
|
||||
/>
|
||||
<div
|
||||
class="flex min-w-0 flex-1 items-center gap-2"
|
||||
data-testid="node-title"
|
||||
>
|
||||
<div class="flex-1 truncate">
|
||||
<EditableText
|
||||
:model-value="displayTitle"
|
||||
:is-editing="isEditing"
|
||||
:input-attrs="{ 'data-testid': 'node-title-input' }"
|
||||
@edit="handleTitleEdit"
|
||||
@cancel="handleTitleCancel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseTooltip>
|
||||
</div>
|
||||
|
||||
<template v-for="badge in priceBadges ?? []" :key="badge.required">
|
||||
@@ -89,6 +96,7 @@ import { computed, onErrorCaptured, ref, watch } from 'vue'
|
||||
|
||||
import EditableText from '@/components/common/EditableText.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import BaseTooltip from '@/components/ui/tooltip/BaseTooltip.vue'
|
||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import { st } from '@/i18n'
|
||||
@@ -126,18 +134,10 @@ onErrorCaptured((error) => {
|
||||
// Editing state
|
||||
const isEditing = ref(false)
|
||||
|
||||
const { getNodeDescription, createTooltipConfig } = useNodeTooltips(
|
||||
const { getNodeDescription, tooltipsEnabled, tooltipDelay } = useNodeTooltips(
|
||||
nodeData?.type || ''
|
||||
)
|
||||
|
||||
const tooltipConfig = computed(() => {
|
||||
if (isEditing.value) {
|
||||
return { value: '', disabled: true }
|
||||
}
|
||||
const description = getNodeDescription.value
|
||||
return createTooltipConfig(description)
|
||||
})
|
||||
|
||||
const resolveTitle = (info: VueNodeData | undefined) => {
|
||||
const untitledLabel = st('g.untitled', 'Untitled')
|
||||
return resolveNodeDisplayName(info ?? null, {
|
||||
|
||||
@@ -55,22 +55,29 @@
|
||||
:name="widget.name"
|
||||
:enable="canSelectInputs && !widget.simplified.options?.disabled"
|
||||
>
|
||||
<component
|
||||
:is="widget.vueComponent"
|
||||
v-model="widget.value"
|
||||
v-tooltip.left="widget.tooltipConfig"
|
||||
:widget="widget.simplified"
|
||||
:node-id="nodeData?.id != null ? String(nodeData.id) : ''"
|
||||
:node-type="nodeType"
|
||||
:class="
|
||||
cn(
|
||||
'col-span-2',
|
||||
widget.hasError && 'font-bold text-node-stroke-error'
|
||||
)
|
||||
"
|
||||
@update:model-value="widget.updateHandler"
|
||||
@contextmenu="widget.handleContextMenu"
|
||||
/>
|
||||
<BaseTooltip
|
||||
:text="widget.tooltipText"
|
||||
side="left"
|
||||
size="large"
|
||||
:delay-duration="tooltipDelay"
|
||||
:disabled="!tooltipsEnabled"
|
||||
>
|
||||
<component
|
||||
:is="widget.vueComponent"
|
||||
v-model="widget.value"
|
||||
:widget="widget.simplified"
|
||||
:node-id="nodeData?.id != null ? String(nodeData.id) : ''"
|
||||
:node-type="nodeType"
|
||||
:class="
|
||||
cn(
|
||||
'col-span-2',
|
||||
widget.hasError && 'font-bold text-node-stroke-error'
|
||||
)
|
||||
"
|
||||
@update:model-value="widget.updateHandler"
|
||||
@contextmenu="widget.handleContextMenu"
|
||||
/>
|
||||
</BaseTooltip>
|
||||
</AppInput>
|
||||
</div>
|
||||
</template>
|
||||
@@ -78,10 +85,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { TooltipOptions } from 'primevue'
|
||||
import { computed, onErrorCaptured, ref, toValue } from 'vue'
|
||||
import type { Component } from 'vue'
|
||||
|
||||
import BaseTooltip from '@/components/ui/tooltip/BaseTooltip.vue'
|
||||
import type {
|
||||
SafeWidgetData,
|
||||
VueNodeData,
|
||||
@@ -182,7 +189,7 @@ const showAdvanced = computed(
|
||||
nodeData?.showAdvanced ||
|
||||
settingStore.get('Comfy.Node.AlwaysShowAdvancedWidgets')
|
||||
)
|
||||
const { getWidgetTooltip, createTooltipConfig } = useNodeTooltips(
|
||||
const { getWidgetTooltip, tooltipsEnabled, tooltipDelay } = useNodeTooltips(
|
||||
nodeType.value
|
||||
)
|
||||
const widgetValueStore = useWidgetValueStore()
|
||||
@@ -217,7 +224,7 @@ interface ProcessedWidget {
|
||||
name: string
|
||||
renderKey: string
|
||||
simplified: SimplifiedWidget
|
||||
tooltipConfig: TooltipOptions
|
||||
tooltipText: string
|
||||
type: string
|
||||
updateHandler: (value: WidgetValue) => void
|
||||
value: WidgetValue
|
||||
@@ -426,7 +433,6 @@ const processedWidgets = computed((): ProcessedWidget[] => {
|
||||
)
|
||||
|
||||
const tooltipText = getWidgetTooltip(widget)
|
||||
const tooltipConfig = createTooltipConfig(tooltipText)
|
||||
const handleContextMenu = (e: PointerEvent) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
@@ -454,7 +460,7 @@ const processedWidgets = computed((): ProcessedWidget[] => {
|
||||
simplified,
|
||||
value,
|
||||
updateHandler,
|
||||
tooltipConfig,
|
||||
tooltipText,
|
||||
slotMetadata
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,27 +1,36 @@
|
||||
<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 flex h-full min-w-0 items-center">
|
||||
<!-- Slot Name -->
|
||||
<span
|
||||
v-if="!props.dotOnly && !hasNoLabel"
|
||||
class="truncate text-node-component-slot-text"
|
||||
>
|
||||
{{
|
||||
slotData.label ||
|
||||
slotData.localized_name ||
|
||||
(slotData.name ?? `Output ${index}`)
|
||||
}}
|
||||
</span>
|
||||
<BaseTooltip
|
||||
v-else
|
||||
:text="outputTooltipText"
|
||||
side="right"
|
||||
size="large"
|
||||
:delay-duration="tooltipDelay"
|
||||
:disabled="!tooltipsEnabled"
|
||||
>
|
||||
<div :class="slotWrapperClass">
|
||||
<div class="relative flex h-full min-w-0 items-center">
|
||||
<!-- Slot Name -->
|
||||
<span
|
||||
v-if="!props.dotOnly && !hasNoLabel"
|
||||
class="truncate text-node-component-slot-text"
|
||||
>
|
||||
{{
|
||||
slotData.label ||
|
||||
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>
|
||||
<!-- Connection Dot -->
|
||||
<SlotConnectionDot
|
||||
ref="connectionDotRef"
|
||||
class="w-3 translate-x-1/2"
|
||||
:slot-data
|
||||
@pointerdown="onPointerDown"
|
||||
/>
|
||||
</div>
|
||||
</BaseTooltip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -29,6 +38,7 @@ import { computed, onErrorCaptured, ref, watchEffect } from 'vue'
|
||||
import type { ComponentPublicInstance } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import BaseTooltip from '@/components/ui/tooltip/BaseTooltip.vue'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import type { INodeSlot } from '@/lib/litegraph/src/litegraph'
|
||||
import { RenderShape } from '@/lib/litegraph/src/types/globalEnums'
|
||||
@@ -65,11 +75,11 @@ const renderError = ref<string | null>(null)
|
||||
|
||||
const { toastErrorHandler } = useErrorHandling()
|
||||
|
||||
const { getOutputSlotTooltip, createTooltipConfig } = useNodeTooltips(
|
||||
const { getOutputSlotTooltip, tooltipsEnabled, tooltipDelay } = useNodeTooltips(
|
||||
props.nodeType || ''
|
||||
)
|
||||
|
||||
const tooltipConfig = computed(() => {
|
||||
const outputTooltipText = computed(() => {
|
||||
const slotName = props.slotData.name || ''
|
||||
const tooltipText = getOutputSlotTooltip(props.index)
|
||||
const fallbackText = tooltipText || `Output: ${slotName}`
|
||||
@@ -77,7 +87,7 @@ const tooltipConfig = computed(() => {
|
||||
props.slotData.shape === RenderShape.GRID
|
||||
? ` ${t('vueNodesSlot.iterative')}`
|
||||
: ''
|
||||
return createTooltipConfig(fallbackText + iterativeSuffix)
|
||||
return fallbackText + iterativeSuffix
|
||||
})
|
||||
|
||||
onErrorCaptured((error) => {
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import type {
|
||||
TooltipOptions,
|
||||
TooltipPassThroughMethodOptions
|
||||
} from 'primevue/tooltip'
|
||||
import { computed, ref, unref } from 'vue'
|
||||
import { computed, unref } from 'vue'
|
||||
import type { MaybeRef } from 'vue'
|
||||
|
||||
import type { SafeWidgetData } from '@/composables/graph/useGraphNodeManager'
|
||||
@@ -10,77 +6,6 @@ import { st } from '@/i18n'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useNodeDefStore } from '@/stores/nodeDefStore'
|
||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
// PrimeVue adds this internal property to elements with tooltips
|
||||
interface PrimeVueTooltipElement extends Element {
|
||||
$_ptooltipId?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide all visible tooltips by dispatching mouseleave events
|
||||
*
|
||||
*
|
||||
* IMPORTANT: this escape is needed for many reason due to primevue's directive tooltip system.
|
||||
* We cannot use PT to conditionally render the tooltips because the entire PT object only run
|
||||
* once during the initialization of the directive not every mount/unmount.
|
||||
* Once the directive is constructed its no longer reactive in the traditional sense.
|
||||
* We have to use something non destructive like mouseevents to dismiss the tooltip.
|
||||
*
|
||||
* TODO: use a better tooltip component like RekaUI for vue nodes specifically.
|
||||
*/
|
||||
|
||||
const tooltipsTemporarilyDisabled = ref(false)
|
||||
|
||||
const hideTooltipsGlobally = () => {
|
||||
// Get all visible tooltip elements
|
||||
const tooltips = document.querySelectorAll('.p-tooltip')
|
||||
|
||||
// Early return if no tooltips are visible
|
||||
if (tooltips.length === 0) return
|
||||
|
||||
tooltips.forEach((tooltipEl) => {
|
||||
const tooltipId = tooltipEl.id
|
||||
if (!tooltipId) return
|
||||
|
||||
// Find the target element that owns this tooltip
|
||||
const targetElements = document.querySelectorAll('[data-pd-tooltip="true"]')
|
||||
for (const targetEl of targetElements) {
|
||||
if ((targetEl as PrimeVueTooltipElement).$_ptooltipId === tooltipId) {
|
||||
;(targetEl as HTMLElement).dispatchEvent(
|
||||
new MouseEvent('mouseleave', { bubbles: true })
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Disable tooltips temporarily after hiding (for drag operations)
|
||||
tooltipsTemporarilyDisabled.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-enable tooltips after pointer interaction ends
|
||||
*/
|
||||
const handlePointerUp = () => {
|
||||
tooltipsTemporarilyDisabled.value = false
|
||||
}
|
||||
|
||||
// Global tooltip hiding system
|
||||
const globalTooltipState = { listenersSetup: false }
|
||||
|
||||
function setupGlobalTooltipHiding() {
|
||||
if (globalTooltipState.listenersSetup) return
|
||||
|
||||
document.addEventListener('pointerdown', hideTooltipsGlobally)
|
||||
document.addEventListener('pointerup', handlePointerUp)
|
||||
window.addEventListener('wheel', hideTooltipsGlobally, {
|
||||
capture: true, //Need this to bypass the event layer from Litegraph
|
||||
passive: true
|
||||
})
|
||||
|
||||
globalTooltipState.listenersSetup = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable for managing Vue node tooltips
|
||||
@@ -90,15 +15,14 @@ export function useNodeTooltips(nodeType: MaybeRef<string>) {
|
||||
const nodeDefStore = useNodeDefStore()
|
||||
const settingsStore = useSettingStore()
|
||||
|
||||
// Setup global pointerdown listener once
|
||||
setupGlobalTooltipHiding()
|
||||
|
||||
// Check if tooltips are globally enabled
|
||||
const tooltipsEnabled = computed(() =>
|
||||
settingsStore.get('Comfy.EnableTooltips')
|
||||
)
|
||||
|
||||
// Get node definition for tooltip data
|
||||
const tooltipDelay = computed(
|
||||
() => settingsStore.get('LiteGraph.Node.TooltipDelay') as number
|
||||
)
|
||||
|
||||
const nodeDef = computed(() => nodeDefStore.nodeDefsByName[unref(nodeType)])
|
||||
|
||||
/**
|
||||
@@ -114,7 +38,7 @@ export function useNodeTooltips(nodeType: MaybeRef<string>) {
|
||||
/**
|
||||
* Get tooltip text for input slots
|
||||
*/
|
||||
const getInputSlotTooltip = (slotName: string) => {
|
||||
function getInputSlotTooltip(slotName: string) {
|
||||
if (!tooltipsEnabled.value || !nodeDef.value) return ''
|
||||
|
||||
const key = `nodeDefs.${normalizeI18nKey(unref(nodeType))}.inputs.${normalizeI18nKey(slotName)}.tooltip`
|
||||
@@ -125,7 +49,7 @@ export function useNodeTooltips(nodeType: MaybeRef<string>) {
|
||||
/**
|
||||
* Get tooltip text for output slots
|
||||
*/
|
||||
const getOutputSlotTooltip = (slotIndex: number) => {
|
||||
function getOutputSlotTooltip(slotIndex: number) {
|
||||
if (!tooltipsEnabled.value || !nodeDef.value) return ''
|
||||
|
||||
const key = `nodeDefs.${normalizeI18nKey(unref(nodeType))}.outputs.${slotIndex}.tooltip`
|
||||
@@ -136,7 +60,7 @@ export function useNodeTooltips(nodeType: MaybeRef<string>) {
|
||||
/**
|
||||
* Get tooltip text for widgets
|
||||
*/
|
||||
const getWidgetTooltip = (widget: SafeWidgetData) => {
|
||||
function getWidgetTooltip(widget: SafeWidgetData) {
|
||||
if (!tooltipsEnabled.value || !nodeDef.value) return ''
|
||||
|
||||
// First try widget-specific tooltip
|
||||
@@ -149,46 +73,12 @@ export function useNodeTooltips(nodeType: MaybeRef<string>) {
|
||||
return st(key, inputTooltip)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create tooltip configuration object for v-tooltip directive
|
||||
* Components wrap this in computed() for reactivity
|
||||
*/
|
||||
const createTooltipConfig = (text: string): TooltipOptions => {
|
||||
const tooltipDelay = settingsStore.get('LiteGraph.Node.TooltipDelay')
|
||||
const tooltipText = text || ''
|
||||
|
||||
return {
|
||||
value: tooltipText,
|
||||
showDelay: tooltipDelay as number,
|
||||
hideDelay: 0, // Immediate hiding
|
||||
disabled:
|
||||
!tooltipsEnabled.value ||
|
||||
!tooltipText ||
|
||||
tooltipsTemporarilyDisabled.value, // this reactive value works but only on next mount,
|
||||
// so if the tooltip is already visible changing this will not hide it
|
||||
pt: {
|
||||
text: {
|
||||
class:
|
||||
'border-node-component-tooltip-border bg-node-component-tooltip-surface border rounded-md px-4 py-2 text-node-component-tooltip text-sm font-normal leading-tight max-w-75 shadow-none'
|
||||
},
|
||||
arrow: ({ context }: TooltipPassThroughMethodOptions) => ({
|
||||
class: cn(
|
||||
context.top && 'border-t-node-component-tooltip-border',
|
||||
context.bottom && 'border-b-node-component-tooltip-border',
|
||||
context.left && 'border-l-node-component-tooltip-border',
|
||||
context.right && 'border-r-node-component-tooltip-border'
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tooltipsEnabled,
|
||||
tooltipDelay,
|
||||
getNodeDescription,
|
||||
getInputSlotTooltip,
|
||||
getOutputSlotTooltip,
|
||||
getWidgetTooltip,
|
||||
createTooltipConfig
|
||||
getWidgetTooltip
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user