mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-22 23:39:45 +00:00
fix Vue node widgets should be in disabled state if their slots are connected with a link (#5834)
## Summary Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/5692 by making widget link connection status trigger on change so Vue widgets with connected links could properly switch to the `disabled` state when they are implicitly converted to inputs. ## Changes - **What**: Added `node:slot-links:changed` event tracking and reactive slot data synchronization for Vue widgets ```mermaid graph TD A[Widget Link Change] --> B[NodeInputSlot.link setter] B --> C{Is Widget Input?} C -->|Yes| D[Trigger slot-links:changed] C -->|No| E[End] D --> F[Graph Event Handler] F --> G[syncNodeSlotData] G --> H[Update Vue Reactive Data] H --> I[Widget Re-render] style A fill:#f9f9f9,stroke:#333,color:#000 style I fill:#f9f9f9,stroke:#333,color:#000 ``` ## Review Focus Widget reactivity performance with frequent link changes and event handler memory management in graph operations. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5834-fix-Vue-node-widgets-should-be-in-disabled-state-if-their-slots-are-connected-with-a-link-27c6d73d365081f6a6c3c1ddc3905c5e) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -57,6 +57,12 @@ import {
|
||||
splitPositionables
|
||||
} from './subgraph/subgraphUtils'
|
||||
import { Alignment, LGraphEventMode } from './types/globalEnums'
|
||||
import type {
|
||||
LGraphTriggerAction,
|
||||
LGraphTriggerEvent,
|
||||
LGraphTriggerHandler,
|
||||
LGraphTriggerParam
|
||||
} from './types/graphTriggers'
|
||||
import type {
|
||||
ExportedSubgraph,
|
||||
ExposedWidget,
|
||||
@@ -68,6 +74,11 @@ import type {
|
||||
} from './types/serialisation'
|
||||
import { getAllNestedItems } from './utils/collections'
|
||||
|
||||
export type {
|
||||
LGraphTriggerAction,
|
||||
LGraphTriggerParam
|
||||
} from './types/graphTriggers'
|
||||
|
||||
export interface LGraphState {
|
||||
lastGroupId: number
|
||||
lastNodeId: number
|
||||
@@ -257,7 +268,7 @@ export class LGraph
|
||||
onExecuteStep?(): void
|
||||
onNodeAdded?(node: LGraphNode): void
|
||||
onNodeRemoved?(node: LGraphNode): void
|
||||
onTrigger?(action: string, param: unknown): void
|
||||
onTrigger?: LGraphTriggerHandler
|
||||
onBeforeChange?(graph: LGraph, info?: LGraphNode): void
|
||||
onAfterChange?(graph: LGraph, info?: LGraphNode | null): void
|
||||
onConnectionChange?(node: LGraphNode): void
|
||||
@@ -1183,8 +1194,23 @@ export class LGraph
|
||||
}
|
||||
|
||||
// ********** GLOBALS *****************
|
||||
trigger<A extends LGraphTriggerAction>(
|
||||
action: A,
|
||||
param: LGraphTriggerParam<A>
|
||||
): void
|
||||
trigger(action: string, param: unknown): void
|
||||
trigger(action: string, param: unknown) {
|
||||
this.onTrigger?.(action, param)
|
||||
// Convert to discriminated union format for typed handlers
|
||||
const validEventTypes = new Set([
|
||||
'node:slot-links:changed',
|
||||
'node:slot-errors:changed',
|
||||
'node:property:changed'
|
||||
])
|
||||
|
||||
if (validEventTypes.has(action) && param && typeof param === 'object') {
|
||||
this.onTrigger?.({ type: action, ...param } as LGraphTriggerEvent)
|
||||
}
|
||||
// Don't handle unknown events - just ignore them
|
||||
}
|
||||
|
||||
/** @todo Clean up - never implemented. */
|
||||
|
||||
@@ -2851,7 +2851,17 @@ export class LGraphNode
|
||||
output.links ??= []
|
||||
output.links.push(link.id)
|
||||
// connect in input
|
||||
inputNode.inputs[inputIndex].link = link.id
|
||||
const targetInput = inputNode.inputs[inputIndex]
|
||||
targetInput.link = link.id
|
||||
if (targetInput.widget) {
|
||||
graph.trigger('node:slot-links:changed', {
|
||||
nodeId: inputNode.id,
|
||||
slotType: NodeSlotType.INPUT,
|
||||
slotIndex: inputIndex,
|
||||
connected: true,
|
||||
linkId: link.id
|
||||
})
|
||||
}
|
||||
|
||||
// Reroutes
|
||||
const reroutes = LLink.getReroutes(graph, link)
|
||||
@@ -3008,6 +3018,15 @@ export class LGraphNode
|
||||
const input = target.inputs[link_info.target_slot]
|
||||
// remove there
|
||||
input.link = null
|
||||
if (input.widget) {
|
||||
graph.trigger('node:slot-links:changed', {
|
||||
nodeId: target.id,
|
||||
slotType: NodeSlotType.INPUT,
|
||||
slotIndex: link_info.target_slot,
|
||||
connected: false,
|
||||
linkId: link_info.id
|
||||
})
|
||||
}
|
||||
|
||||
// remove the link from the links pool
|
||||
link_info.disconnect(graph, 'input')
|
||||
@@ -3044,6 +3063,15 @@ export class LGraphNode
|
||||
const input = target.inputs[link_info.target_slot]
|
||||
// remove other side link
|
||||
input.link = null
|
||||
if (input.widget) {
|
||||
graph.trigger('node:slot-links:changed', {
|
||||
nodeId: target.id,
|
||||
slotType: NodeSlotType.INPUT,
|
||||
slotIndex: link_info.target_slot,
|
||||
connected: false,
|
||||
linkId: link_info.id
|
||||
})
|
||||
}
|
||||
|
||||
// link_info hasn't been modified so its ok
|
||||
target.onConnectionsChange?.(
|
||||
@@ -3113,6 +3141,15 @@ export class LGraphNode
|
||||
const link_id = this.inputs[slot].link
|
||||
if (link_id != null) {
|
||||
this.inputs[slot].link = null
|
||||
if (input.widget) {
|
||||
graph.trigger('node:slot-links:changed', {
|
||||
nodeId: this.id,
|
||||
slotType: NodeSlotType.INPUT,
|
||||
slotIndex: slot,
|
||||
connected: false,
|
||||
linkId: link_id
|
||||
})
|
||||
}
|
||||
|
||||
// remove other side
|
||||
const link_info = graph._links.get(link_id)
|
||||
|
||||
@@ -102,7 +102,12 @@ export type {
|
||||
Positionable,
|
||||
Size
|
||||
} from './interfaces'
|
||||
export { LGraph } from './LGraph'
|
||||
export {
|
||||
LGraph,
|
||||
type LGraphTriggerAction,
|
||||
type LGraphTriggerParam
|
||||
} from './LGraph'
|
||||
export type { LGraphTriggerEvent } from './types/graphTriggers'
|
||||
export { BadgePosition, LGraphBadge } from './LGraphBadge'
|
||||
export { LGraphCanvas } from './LGraphCanvas'
|
||||
export { LGraphGroup } from './LGraphGroup'
|
||||
|
||||
38
src/lib/litegraph/src/types/graphTriggers.ts
Normal file
38
src/lib/litegraph/src/types/graphTriggers.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { NodeId } from '../LGraphNode'
|
||||
import type { NodeSlotType } from './globalEnums'
|
||||
|
||||
interface NodePropertyChangedEvent {
|
||||
type: 'node:property:changed'
|
||||
nodeId: NodeId
|
||||
property: string
|
||||
oldValue: unknown
|
||||
newValue: unknown
|
||||
}
|
||||
|
||||
interface NodeSlotErrorsChangedEvent {
|
||||
type: 'node:slot-errors:changed'
|
||||
nodeId: NodeId
|
||||
}
|
||||
|
||||
interface NodeSlotLinksChangedEvent {
|
||||
type: 'node:slot-links:changed'
|
||||
nodeId: NodeId
|
||||
slotType: NodeSlotType
|
||||
slotIndex: number
|
||||
connected: boolean
|
||||
linkId: number
|
||||
}
|
||||
|
||||
export type LGraphTriggerEvent =
|
||||
| NodePropertyChangedEvent
|
||||
| NodeSlotErrorsChangedEvent
|
||||
| NodeSlotLinksChangedEvent
|
||||
|
||||
export type LGraphTriggerAction = LGraphTriggerEvent['type']
|
||||
|
||||
export type LGraphTriggerParam<A extends LGraphTriggerAction> = Extract<
|
||||
LGraphTriggerEvent,
|
||||
{ type: A }
|
||||
>
|
||||
|
||||
export type LGraphTriggerHandler = (event: LGraphTriggerEvent) => void
|
||||
Reference in New Issue
Block a user