[feat] Add tooltip support for Vue nodes (#5577)

## Summary

Added tooltip support for Vue node components using PrimeVue's v-tooltip
directive with proper data integration and container scoping.


https://github.com/user-attachments/assets/d1af31e6-ef6a-4df8-8de4-5098aa4490a1

## Changes

- **What**: Implemented tooltip functionality for Vue node headers,
input/output slots, and widgets using [PrimeVue
v-tooltip](https://primevue.org/tooltip/) directive
- **Dependencies**: Leverages existing PrimeVue tooltip system, no new
dependencies

## Review Focus

Container scoping implementation via provide/inject pattern for tooltip
positioning, proper TypeScript interfaces eliminating `as any` casts,
and integration with existing settings store for tooltip delays and
enable/disable functionality.

```mermaid
graph TD
    A[LGraphNode Container] --> B[provide tooltipContainer]
    B --> C[NodeHeader inject]
    B --> D[InputSlot inject]
    B --> E[OutputSlot inject]
    B --> F[NodeWidgets inject]

    G[useNodeTooltips composable] --> H[NodeDefStore lookup]
    G --> I[Settings integration]
    G --> J[i18n fallback]

    C --> G
    D --> G
    E --> G
    F --> G

    style A fill:#f9f9f9,stroke:#333,color:#000
    style G fill:#e8f4fd,stroke:#0066cc,color:#000
```

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Christian Byrne
2025-09-19 01:07:50 -07:00
committed by GitHub
parent a17c74fa0c
commit a975e50f1b
8 changed files with 380 additions and 54 deletions

View File

@@ -5,7 +5,7 @@
<div
v-else
class="lg-node-header flex items-center justify-between p-4 rounded-t-2xl cursor-move"
:data-testid="`node-header-${nodeInfo?.id || ''}`"
:data-testid="`node-header-${nodeData?.id || ''}`"
@dblclick="handleDoubleClick"
>
<!-- Collapse/Expand Button -->
@@ -23,7 +23,11 @@
</button>
<!-- Node Title -->
<div class="text-sm font-bold truncate flex-1" data-testid="node-title">
<div
v-tooltip.top="tooltipConfig"
class="text-sm font-bold truncate flex-1"
data-testid="node-title"
>
<EditableText
:model-value="displayTitle"
:is-editing="isEditing"
@@ -36,23 +40,22 @@
</template>
<script setup lang="ts">
import { computed, onErrorCaptured, ref, watch } from 'vue'
import { type Ref, computed, inject, onErrorCaptured, ref, watch } from 'vue'
import EditableText from '@/components/common/EditableText.vue'
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
import { useErrorHandling } from '@/composables/useErrorHandling'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { useNodeTooltips } from '@/renderer/extensions/vueNodes/composables/useNodeTooltips'
import type { LODLevel } from '@/renderer/extensions/vueNodes/lod/useLOD'
interface NodeHeaderProps {
node?: LGraphNode // For backwards compatibility
nodeData?: VueNodeData // New clean data structure
nodeData?: VueNodeData
readonly?: boolean
lodLevel?: LODLevel
collapsed?: boolean
}
const props = defineProps<NodeHeaderProps>()
const { nodeData, readonly, collapsed } = defineProps<NodeHeaderProps>()
const emit = defineEmits<{
collapse: []
@@ -72,9 +75,22 @@ onErrorCaptured((error) => {
// Editing state
const isEditing = ref(false)
const nodeInfo = computed(() => props.nodeData || props.node)
const tooltipContainer =
inject<Ref<HTMLElement | undefined>>('tooltipContainer')
const { getNodeDescription, createTooltipConfig } = useNodeTooltips(
nodeData?.type || '',
tooltipContainer
)
const resolveTitle = (info: LGraphNode | VueNodeData | undefined) => {
const tooltipConfig = computed(() => {
if (readonly || isEditing.value) {
return { value: '', disabled: true }
}
const description = getNodeDescription.value
return createTooltipConfig(description)
})
const resolveTitle = (info: VueNodeData | undefined) => {
const title = (info?.title ?? '').trim()
if (title.length > 0) return title
const type = (info?.type ?? '').trim()
@@ -82,13 +98,13 @@ const resolveTitle = (info: LGraphNode | VueNodeData | undefined) => {
}
// Local state for title to provide immediate feedback
const displayTitle = ref(resolveTitle(nodeInfo.value))
const displayTitle = ref(resolveTitle(nodeData))
// Watch for external changes to the node title or type
watch(
() => [nodeInfo.value?.title, nodeInfo.value?.type] as const,
() => [nodeData?.title, nodeData?.type] as const,
() => {
const next = resolveTitle(nodeInfo.value)
const next = resolveTitle(nodeData)
if (next !== displayTitle.value) {
displayTitle.value = next
}
@@ -101,7 +117,7 @@ const handleCollapse = () => {
}
const handleDoubleClick = () => {
if (!props.readonly) {
if (!readonly) {
isEditing.value = true
}
}