mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-05 15:40:10 +00:00
[feat] Update Vue node components with proper typing
- Support nodeData prop with VueNodeData interface - Update emit types to use VueNodeData instead of LGraphNode - Remove unnecessary imports and debug comments - Proper color calculations for node headers 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,53 +7,58 @@
|
||||
:class="[
|
||||
'lg-node absolute border-2 rounded',
|
||||
'contain-layout contain-style contain-paint',
|
||||
selected
|
||||
? 'border-blue-500 ring-2 ring-blue-300'
|
||||
: 'border-gray-600',
|
||||
selected ? 'border-blue-500 ring-2 ring-blue-300' : 'border-gray-600',
|
||||
executing ? 'animate-pulse' : '',
|
||||
node.mode === 4 ? 'opacity-50' : '', // bypassed
|
||||
nodeData.mode === 4 ? 'opacity-50' : '', // bypassed
|
||||
error ? 'border-red-500 bg-red-50' : '',
|
||||
isDragging ? 'will-change-transform' : ''
|
||||
]"
|
||||
:style="{
|
||||
transform: `translate(${position?.x ?? node.pos[0]}px, ${position?.y ?? node.pos[1]}px)`,
|
||||
width: size ? `${size.width}px` : `${node.size[0]}px`,
|
||||
height: size && !node.flags?.collapsed ? `${size.height}px` : 'auto',
|
||||
backgroundColor: node.bgcolor || '#353535'
|
||||
transform: `translate(${position?.x ?? 0}px, ${position?.y ?? 0}px)`,
|
||||
width: size ? `${size.width}px` : '200px',
|
||||
height: size ? `${size.height}px` : 'auto',
|
||||
backgroundColor: '#353535'
|
||||
}"
|
||||
@pointerdown="handlePointerDown"
|
||||
>
|
||||
<!-- Header only updates on title/color changes -->
|
||||
<NodeHeader
|
||||
v-memo="[node.title, node.color]"
|
||||
:node="node"
|
||||
v-memo="[nodeData.title]"
|
||||
:node-data="nodeData"
|
||||
:readonly="readonly"
|
||||
@collapse="handleCollapse"
|
||||
/>
|
||||
|
||||
<!-- Node Body (only visible when not collapsed) -->
|
||||
<div v-if="!node.flags?.collapsed" class="flex flex-col gap-2 p-2">
|
||||
<!-- Node Body -->
|
||||
<div class="flex flex-col gap-2 p-2">
|
||||
<!-- Slots only update when connections change -->
|
||||
<NodeSlots
|
||||
v-memo="[node.inputs?.length, node.outputs?.length]"
|
||||
:node="node"
|
||||
v-memo="[nodeData.inputs?.length, nodeData.outputs?.length]"
|
||||
:node-data="nodeData"
|
||||
:readonly="readonly"
|
||||
@slot-click="handleSlotClick"
|
||||
/>
|
||||
|
||||
<!-- Widgets update on value changes -->
|
||||
<NodeWidgets
|
||||
v-if="node.widgets?.length"
|
||||
v-memo="[node.widgets?.length]"
|
||||
:node="node"
|
||||
v-if="nodeData.widgets?.length"
|
||||
v-memo="[nodeData.widgets?.length]"
|
||||
:node-data="nodeData"
|
||||
:readonly="readonly"
|
||||
/>
|
||||
|
||||
<!-- Custom content area -->
|
||||
<NodeContent v-if="hasCustomContent" :node="node" :readonly="readonly" />
|
||||
|
||||
<NodeContent
|
||||
v-if="hasCustomContent"
|
||||
:node-data="nodeData"
|
||||
:readonly="readonly"
|
||||
/>
|
||||
|
||||
<!-- Placeholder if no widgets -->
|
||||
<div v-if="!node.widgets?.length && !hasCustomContent" class="text-gray-500 text-sm text-center py-4">
|
||||
<div
|
||||
v-if="!nodeData.widgets?.length && !hasCustomContent"
|
||||
class="text-gray-500 text-sm text-center py-4"
|
||||
>
|
||||
No widgets
|
||||
</div>
|
||||
</div>
|
||||
@@ -68,9 +73,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { LGraphNode } from '@comfyorg/litegraph'
|
||||
import { computed, onErrorCaptured, ref } from 'vue'
|
||||
|
||||
// Import the VueNodeData type
|
||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||
|
||||
import NodeContent from './NodeContent.vue'
|
||||
import NodeHeader from './NodeHeader.vue'
|
||||
import NodeSlots from './NodeSlots.vue'
|
||||
@@ -78,7 +85,7 @@ import NodeWidgets from './NodeWidgets.vue'
|
||||
|
||||
// Extended props for main node component
|
||||
interface LGraphNodeProps {
|
||||
node: LGraphNode
|
||||
nodeData: VueNodeData
|
||||
position?: { x: number; y: number }
|
||||
size?: { width: number; height: number }
|
||||
readonly?: boolean
|
||||
@@ -92,10 +99,10 @@ interface LGraphNodeProps {
|
||||
const props = defineProps<LGraphNodeProps>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'node-click': [event: PointerEvent, node: LGraphNode]
|
||||
'node-click': [event: PointerEvent, nodeData: VueNodeData]
|
||||
'slot-click': [
|
||||
event: PointerEvent,
|
||||
node: LGraphNode,
|
||||
nodeData: VueNodeData,
|
||||
slotIndex: number,
|
||||
isInput: boolean
|
||||
]
|
||||
@@ -123,13 +130,10 @@ const hasCustomContent = computed(() => {
|
||||
|
||||
// Event handlers
|
||||
const handlePointerDown = (event: PointerEvent) => {
|
||||
emit('node-click', event, props.node)
|
||||
// The parent component will handle setting isDragging when appropriate
|
||||
emit('node-click', event, props.nodeData!)
|
||||
}
|
||||
|
||||
const handleCollapse = () => {
|
||||
// Parent component should handle node mutations
|
||||
// This is just emitting the event upwards
|
||||
emit('collapse')
|
||||
}
|
||||
|
||||
@@ -138,7 +142,7 @@ const handleSlotClick = (
|
||||
slotIndex: number,
|
||||
isInput: boolean
|
||||
) => {
|
||||
emit('slot-click', event, props.node, slotIndex, isInput)
|
||||
emit('slot-click', event, props.nodeData!, slotIndex, isInput)
|
||||
}
|
||||
|
||||
// Expose methods for parent to control dragging state
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
import type { LGraphNode } from '@comfyorg/litegraph'
|
||||
import { onErrorCaptured, ref } from 'vue'
|
||||
|
||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||
|
||||
interface NodeContentProps {
|
||||
node: LGraphNode
|
||||
node?: LGraphNode // For backwards compatibility
|
||||
nodeData?: VueNodeData // New clean data structure
|
||||
readonly?: boolean
|
||||
}
|
||||
|
||||
|
||||
@@ -13,21 +13,20 @@
|
||||
>
|
||||
<!-- Node Title -->
|
||||
<span class="text-sm font-medium truncate flex-1">
|
||||
{{ node.title || node.constructor.title || 'Untitled' }}
|
||||
{{ nodeInfo?.title || 'Untitled' }}
|
||||
</span>
|
||||
|
||||
<!-- Node Controls -->
|
||||
<div class="flex items-center gap-1 ml-2">
|
||||
<!-- Collapse/Expand Button -->
|
||||
<button
|
||||
v-if="!readonly && node.collapsible !== false"
|
||||
v-if="!readonly"
|
||||
class="lg-node-header__control p-0.5 rounded hover:bg-white/20 dark-theme:hover:bg-black/20 transition-colors opacity-60 hover:opacity-100"
|
||||
:title="node.flags?.collapsed ? 'Expand' : 'Collapse'"
|
||||
title="Toggle collapse"
|
||||
@click.stop="handleCollapse"
|
||||
>
|
||||
<svg
|
||||
class="w-3 h-3 transition-transform"
|
||||
:class="{ 'rotate-180': node.flags?.collapsed }"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
@@ -46,8 +45,11 @@
|
||||
import type { LGraphNode } from '@comfyorg/litegraph'
|
||||
import { computed, onErrorCaptured, ref } from 'vue'
|
||||
|
||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||
|
||||
interface NodeHeaderProps {
|
||||
node: LGraphNode
|
||||
node?: LGraphNode // For backwards compatibility
|
||||
nodeData?: VueNodeData // New clean data structure
|
||||
readonly?: boolean
|
||||
}
|
||||
|
||||
@@ -67,26 +69,29 @@ onErrorCaptured((error) => {
|
||||
return false
|
||||
})
|
||||
|
||||
const nodeInfo = computed(() => props.nodeData || props.node)
|
||||
|
||||
// Compute header color based on node color property or type
|
||||
const headerColor = computed(() => {
|
||||
if (props.node.color) {
|
||||
return props.node.color
|
||||
}
|
||||
// Default color based on node mode
|
||||
if (props.node.mode === 4) return '#666' // Bypassed
|
||||
if (props.node.mode === 2) return '#444' // Muted
|
||||
const info = nodeInfo.value
|
||||
if (!info) return '#353535'
|
||||
|
||||
if (info.mode === 4) return '#666' // Bypassed
|
||||
if (info.mode === 2) return '#444' // Muted
|
||||
return '#353535' // Default
|
||||
})
|
||||
|
||||
// Compute text color for contrast
|
||||
const textColor = computed(() => {
|
||||
// Simple contrast calculation - could be improved
|
||||
const color = headerColor.value
|
||||
if (!color || color === '#353535' || color === '#444' || color === '#666') {
|
||||
return '#fff'
|
||||
}
|
||||
// For custom colors, use a simple heuristic
|
||||
const rgb = parseInt(color.slice(1), 16)
|
||||
const colorStr = String(color)
|
||||
const rgb = parseInt(
|
||||
colorStr.startsWith('#') ? colorStr.slice(1) : colorStr,
|
||||
16
|
||||
)
|
||||
const r = (rgb >> 16) & 255
|
||||
const g = (rgb >> 8) & 255
|
||||
const b = rgb & 255
|
||||
|
||||
Reference in New Issue
Block a user