@@ -13,7 +13,18 @@ import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
13
13
import { useNodeDefStore } from '@/stores/nodeDefStore'
14
14
import type { WidgetValue } from '@/types/simplifiedWidget'
15
15
16
- import type { LGraph , LGraphNode } from '../../lib/litegraph/src/litegraph'
16
+ import type {
17
+ LGraph ,
18
+ LGraphNode ,
19
+ LGraphTriggerAction ,
20
+ LGraphTriggerParam
21
+ } from '../../lib/litegraph/src/litegraph'
22
+ import { NodeSlotType } from '../../lib/litegraph/src/types/globalEnums'
23
+
24
+ export interface WidgetSlotMetadata {
25
+ index : number
26
+ linked : boolean
27
+ }
17
28
18
29
export interface SafeWidgetData {
19
30
name : string
@@ -23,6 +34,7 @@ export interface SafeWidgetData {
23
34
options ?: Record < string , unknown >
24
35
callback ?: ( ( value : unknown ) => void ) | undefined
25
36
spec ?: InputSpec
37
+ slotMetadata ?: WidgetSlotMetadata
26
38
}
27
39
28
40
export interface VueNodeData {
@@ -66,6 +78,22 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
66
78
// Non-reactive storage for original LiteGraph nodes
67
79
const nodeRefs = new Map < string , LGraphNode > ( )
68
80
81
+ const refreshNodeSlots = ( nodeId : string ) => {
82
+ const nodeRef = nodeRefs . get ( nodeId )
83
+ const currentData = vueNodeData . get ( nodeId )
84
+
85
+ if ( ! nodeRef || ! currentData ) return
86
+
87
+ const refreshedData = extractVueNodeData ( nodeRef )
88
+
89
+ vueNodeData . set ( nodeId , {
90
+ ...currentData ,
91
+ widgets : refreshedData . widgets ,
92
+ inputs : refreshedData . inputs ,
93
+ outputs : refreshedData . outputs
94
+ } )
95
+ }
96
+
69
97
// Extract safe data from LiteGraph node for Vue consumption
70
98
const extractVueNodeData = ( node : LGraphNode ) : VueNodeData => {
71
99
// Determine subgraph ID - null for root graph, string for subgraphs
@@ -74,6 +102,16 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
74
102
? String ( node . graph . id )
75
103
: null
76
104
// Extract safe widget data
105
+ const slotMetadata = new Map < string , WidgetSlotMetadata > ( )
106
+
107
+ node . inputs ?. forEach ( ( input , index ) => {
108
+ if ( ! input ?. widget ?. name ) return
109
+ slotMetadata . set ( input . widget . name , {
110
+ index,
111
+ linked : input . link != null
112
+ } )
113
+ } )
114
+
77
115
const safeWidgets = node . widgets ?. map ( ( widget ) => {
78
116
try {
79
117
// TODO: Use widget.getReactiveData() once TypeScript types are updated
@@ -90,6 +128,7 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
90
128
value = widget . options . values [ 0 ]
91
129
}
92
130
const spec = nodeDefStore . getInputSpecForWidget ( node , widget . name )
131
+ const slotInfo = slotMetadata . get ( widget . name )
93
132
94
133
return {
95
134
name : widget . name ,
@@ -98,7 +137,8 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
98
137
label : widget . label ,
99
138
options : widget . options ? { ...widget . options } : undefined ,
100
139
callback : widget . callback ,
101
- spec
140
+ spec,
141
+ slotMetadata : slotInfo
102
142
}
103
143
} catch ( error ) {
104
144
return {
@@ -405,37 +445,27 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
405
445
handleNodeRemoved ( node , originalOnNodeRemoved )
406
446
}
407
447
408
- // Listen for property change events from instrumented nodes
409
- graph . onTrigger = ( action : string , param : unknown ) => {
410
- if (
411
- action === 'node:property:changed' &&
412
- param &&
413
- typeof param === 'object'
414
- ) {
415
- const event = param as {
416
- nodeId : string | number
417
- property : string
418
- oldValue : unknown
419
- newValue : unknown
420
- }
421
-
422
- const nodeId = String ( event . nodeId )
448
+ const triggerHandlers : {
449
+ [ K in LGraphTriggerAction ] : ( event : LGraphTriggerParam < K > ) => void
450
+ } = {
451
+ 'node:property:changed' : ( propertyEvent ) => {
452
+ const nodeId = String ( propertyEvent . nodeId )
423
453
const currentData = vueNodeData . get ( nodeId )
424
454
425
455
if ( currentData ) {
426
- switch ( event . property ) {
456
+ switch ( propertyEvent . property ) {
427
457
case 'title' :
428
458
vueNodeData . set ( nodeId , {
429
459
...currentData ,
430
- title : String ( event . newValue )
460
+ title : String ( propertyEvent . newValue )
431
461
} )
432
462
break
433
463
case 'flags.collapsed' :
434
464
vueNodeData . set ( nodeId , {
435
465
...currentData ,
436
466
flags : {
437
467
...currentData . flags ,
438
- collapsed : Boolean ( event . newValue )
468
+ collapsed : Boolean ( propertyEvent . newValue )
439
469
}
440
470
} )
441
471
break
@@ -444,63 +474,59 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
444
474
...currentData ,
445
475
flags : {
446
476
...currentData . flags ,
447
- pinned : Boolean ( event . newValue )
477
+ pinned : Boolean ( propertyEvent . newValue )
448
478
}
449
479
} )
450
480
break
451
481
case 'mode' :
452
482
vueNodeData . set ( nodeId , {
453
483
...currentData ,
454
- mode : typeof event . newValue === 'number' ? event . newValue : 0
484
+ mode :
485
+ typeof propertyEvent . newValue === 'number'
486
+ ? propertyEvent . newValue
487
+ : 0
455
488
} )
456
489
break
457
490
case 'color' :
458
491
vueNodeData . set ( nodeId , {
459
492
...currentData ,
460
493
color :
461
- typeof event . newValue === 'string'
462
- ? event . newValue
494
+ typeof propertyEvent . newValue === 'string'
495
+ ? propertyEvent . newValue
463
496
: undefined
464
497
} )
465
498
break
466
499
case 'bgcolor' :
467
500
vueNodeData . set ( nodeId , {
468
501
...currentData ,
469
502
bgcolor :
470
- typeof event . newValue === 'string'
471
- ? event . newValue
503
+ typeof propertyEvent . newValue === 'string'
504
+ ? propertyEvent . newValue
472
505
: undefined
473
506
} )
474
507
}
475
508
}
476
- } else if (
477
- action === 'node:slot-errors:changed' &&
478
- param &&
479
- typeof param === 'object'
480
- ) {
481
- const event = param as { nodeId : string | number }
482
- const nodeId = String ( event . nodeId )
483
- const litegraphNode = nodeRefs . get ( nodeId )
484
- const currentData = vueNodeData . get ( nodeId )
485
-
486
- if ( litegraphNode && currentData ) {
487
- // Re-extract slot data with updated hasErrors properties
488
- vueNodeData . set ( nodeId , {
489
- ...currentData ,
490
- inputs : litegraphNode . inputs
491
- ? [ ...litegraphNode . inputs ]
492
- : undefined ,
493
- outputs : litegraphNode . outputs
494
- ? [ ...litegraphNode . outputs ]
495
- : undefined
496
- } )
509
+ } ,
510
+ 'node:slot-errors:changed' : ( slotErrorsEvent ) => {
511
+ refreshNodeSlots ( String ( slotErrorsEvent . nodeId ) )
512
+ } ,
513
+ 'node:slot-links:changed' : ( slotLinksEvent ) => {
514
+ if ( slotLinksEvent . slotType === NodeSlotType . INPUT ) {
515
+ refreshNodeSlots ( String ( slotLinksEvent . nodeId ) )
497
516
}
498
517
}
518
+ }
499
519
500
- // Call original trigger handler if it exists
501
- if ( originalOnTrigger ) {
502
- originalOnTrigger ( action , param )
520
+ const isTriggerAction = ( value : string ) : value is LGraphTriggerAction =>
521
+ Object . prototype . hasOwnProperty . call ( triggerHandlers , value )
522
+
523
+ graph . onTrigger = ( action : string , event : unknown ) => {
524
+ if ( isTriggerAction ( action ) ) {
525
+ const handler = triggerHandlers [ action ] as ( payload : unknown ) => void
526
+ handler ( event )
503
527
}
528
+
529
+ originalOnTrigger ?.( action , event )
504
530
}
505
531
506
532
// Initialize state
0 commit comments