mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-23 08:14:06 +00:00
refactor(groupNode): improve type safety and reduce casts
Amp-Thread-ID: https://ampcode.com/threads/T-019baa1c-6aa2-769b-a5f9-a705b5ef2b2b Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -4,10 +4,11 @@ import type { GroupNodeWorkflowData } from '@/lib/litegraph/src/LGraph'
|
||||
import type {
|
||||
GroupNodeInputConfig,
|
||||
GroupNodeInputsSpec,
|
||||
GroupNodeInternalLink,
|
||||
GroupNodeOutputType,
|
||||
PartialLinkInfo
|
||||
} from './groupNodeTypes'
|
||||
import { LLink, type SerialisedLLinkArray } from '@/lib/litegraph/src/LLink'
|
||||
import { LLink } from '@/lib/litegraph/src/LLink'
|
||||
import type { NodeId } from '@/lib/litegraph/src/LGraphNode'
|
||||
import type { IContextMenuValue } from '@/lib/litegraph/src/interfaces'
|
||||
import {
|
||||
@@ -39,7 +40,7 @@ import { app } from '../../scripts/app'
|
||||
import { ManageGroupDialog } from './groupNodeManage'
|
||||
import { mergeIfValid } from './widgetInputs'
|
||||
|
||||
type GroupNodeLink = SerialisedLLinkArray
|
||||
type GroupNodeLink = GroupNodeInternalLink
|
||||
type LinksFromMap = Record<number, Record<number, GroupNodeLink[]>>
|
||||
type LinksToMap = Record<number, Record<number, GroupNodeLink>>
|
||||
type ExternalFromMap = Record<number, Record<number, string | number>>
|
||||
@@ -1005,7 +1006,7 @@ export class GroupNodeHandler {
|
||||
return inputNode
|
||||
}
|
||||
|
||||
innerNode.getInputLink = ((slot: number): PartialLinkInfo | null => {
|
||||
innerNode.getInputLink = (slot: number): PartialLinkInfo | null => {
|
||||
const nodeIdx = innerNode.index ?? 0
|
||||
const externalSlot = this.groupData.oldToNewInputMap[nodeIdx]?.[slot]
|
||||
if (externalSlot != null) {
|
||||
@@ -1025,14 +1026,15 @@ export class GroupNodeHandler {
|
||||
const innerLink = this.groupData.linksTo[nodeIdx]?.[slot]
|
||||
if (!innerLink) return null
|
||||
const linkSrcIdx = innerLink[0]
|
||||
if (linkSrcIdx == null) return null
|
||||
const linkSrcSlot = innerLink[1]
|
||||
if (linkSrcIdx == null || linkSrcSlot == null) return null
|
||||
return {
|
||||
origin_id: innerNodes[Number(linkSrcIdx)].id,
|
||||
origin_slot: innerLink[1],
|
||||
origin_slot: linkSrcSlot,
|
||||
target_id: innerNode.id,
|
||||
target_slot: +slot
|
||||
}
|
||||
}) as typeof innerNode.getInputLink
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1042,7 +1044,7 @@ export class GroupNodeHandler {
|
||||
if (!output || !this.innerNodes) return null
|
||||
const nodeIdx = output.node.index ?? 0
|
||||
let innerNode: LGraphNode | null = this.innerNodes[nodeIdx]
|
||||
let l
|
||||
let l = innerNode?.getInputLink(0)
|
||||
while (innerNode?.type === 'Reroute') {
|
||||
l = innerNode.getInputLink(0)
|
||||
innerNode = innerNode.getInputNode(0)
|
||||
@@ -1053,7 +1055,7 @@ export class GroupNodeHandler {
|
||||
}
|
||||
|
||||
if (
|
||||
l &&
|
||||
l instanceof LLink &&
|
||||
GroupNodeHandler.isGroupNode(innerNode) &&
|
||||
innerNode.updateLink
|
||||
) {
|
||||
@@ -1098,10 +1100,9 @@ export class GroupNodeHandler {
|
||||
|
||||
const subgraphInstanceIdPath = [...subgraphNodePath, this.node.id]
|
||||
|
||||
// Assertion: Deprecated, does not matter.
|
||||
const subgraphNode = (this.node.graph?.getNodeById(
|
||||
subgraphNodePath.at(-1)
|
||||
) ?? undefined) as SubgraphNode | undefined
|
||||
// Get the parent subgraph node if we're inside a subgraph
|
||||
const parentNode = this.node.graph?.getNodeById(subgraphNodePath.at(-1))
|
||||
const subgraphNode = parentNode?.isSubgraphNode() ? parentNode : undefined
|
||||
|
||||
for (const node of this.innerNodes ?? []) {
|
||||
node.graph ??= this.node.graph
|
||||
@@ -1437,13 +1438,9 @@ export class GroupNodeHandler {
|
||||
|
||||
type EventDetail = { display_node?: string; node?: string } | string
|
||||
const handleEvent = (
|
||||
type: string,
|
||||
type: 'executing' | 'executed',
|
||||
getId: (detail: EventDetail) => string | undefined,
|
||||
getEvent: (
|
||||
detail: EventDetail,
|
||||
id: string,
|
||||
node: LGraphNode
|
||||
) => EventDetail
|
||||
getEvent: (detail: EventDetail, id: string, node: LGraphNode) => unknown
|
||||
) => {
|
||||
const handler = ({ detail }: CustomEvent<EventDetail>) => {
|
||||
const id = getId(detail)
|
||||
@@ -1457,16 +1454,14 @@ export class GroupNodeHandler {
|
||||
;(
|
||||
this.node as LGraphNode & { runningInternalNodeId?: number }
|
||||
).runningInternalNodeId = innerNodeIndex
|
||||
// Cast needed: dispatching synthetic events for inner nodes with transformed payloads
|
||||
api.dispatchCustomEvent(
|
||||
type as 'executing',
|
||||
type,
|
||||
getEvent(detail, `${this.node.id}`, this.node) as string
|
||||
)
|
||||
}
|
||||
}
|
||||
api.addEventListener(
|
||||
type as 'executing' | 'executed',
|
||||
handler as EventListener
|
||||
)
|
||||
api.addEventListener(type, handler as EventListener)
|
||||
return handler
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
import type { SerialisedLLinkArray } from '@/lib/litegraph/src/LLink'
|
||||
import type { ILinkRouting } from '@/lib/litegraph/src/interfaces'
|
||||
import type { ISlotType } from '@/lib/litegraph/src/interfaces'
|
||||
import type { ISerialisedNode } from '@/lib/litegraph/src/types/serialisation'
|
||||
|
||||
/**
|
||||
* Group node internal link format.
|
||||
* This differs from standard SerialisedLLinkArray - indices represent node/slot positions within the group.
|
||||
* Format: [sourceNodeIndex, sourceSlot, targetNodeIndex, targetSlot, ...optionalData]
|
||||
* The type (ISlotType) may be at index 5 if present.
|
||||
*/
|
||||
export type GroupNodeInternalLink = [
|
||||
sourceNodeIndex: number | null,
|
||||
sourceSlot: number | null,
|
||||
targetNodeIndex: number | null,
|
||||
targetSlot: number | null,
|
||||
...rest: (number | string | ISlotType | null | undefined)[]
|
||||
]
|
||||
|
||||
/** Serialized node data within a group node workflow, with group-specific index */
|
||||
export interface GroupNodeSerializedNode extends Partial<ISerialisedNode> {
|
||||
/** Position of this node within the group */
|
||||
@@ -9,7 +24,7 @@ export interface GroupNodeSerializedNode extends Partial<ISerialisedNode> {
|
||||
|
||||
export interface GroupNodeWorkflowData {
|
||||
external: (number | string)[][]
|
||||
links: SerialisedLLinkArray[]
|
||||
links: GroupNodeInternalLink[]
|
||||
nodes: GroupNodeSerializedNode[]
|
||||
config?: Record<number, unknown>
|
||||
}
|
||||
@@ -37,11 +52,6 @@ export type GroupNodeOutputType = string | (string | number)[]
|
||||
|
||||
/**
|
||||
* Partial link info used internally by group node getInputLink override.
|
||||
* Contains only the properties needed for group node execution context.
|
||||
* Extends ILinkRouting to be compatible with the base getInputLink return type.
|
||||
*/
|
||||
export interface PartialLinkInfo {
|
||||
origin_id: string | number
|
||||
origin_slot: number | string
|
||||
target_id: string | number
|
||||
target_slot: number
|
||||
}
|
||||
export interface PartialLinkInfo extends ILinkRouting {}
|
||||
|
||||
@@ -34,6 +34,7 @@ import type {
|
||||
IColorable,
|
||||
IContextMenuValue,
|
||||
IFoundSlot,
|
||||
ILinkRouting,
|
||||
INodeFlags,
|
||||
INodeInputSlot,
|
||||
INodeOutputSlot,
|
||||
@@ -1153,10 +1154,11 @@ export class LGraphNode
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the link info in the connection of an input slot
|
||||
* @returns object or null
|
||||
* Returns the link info in the connection of an input slot.
|
||||
* Group nodes may override this to return ILinkRouting for internal routing.
|
||||
* @returns LLink, ILinkRouting, or null
|
||||
*/
|
||||
getInputLink(slot: number): LLink | null {
|
||||
getInputLink(slot: number): LLink | ILinkRouting | null {
|
||||
if (!this.inputs) return null
|
||||
|
||||
if (slot < this.inputs.length) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import type { LGraphNode, NodeId } from './LGraphNode'
|
||||
import type { Reroute, RerouteId } from './Reroute'
|
||||
import type {
|
||||
CanvasColour,
|
||||
ILinkRouting,
|
||||
INodeInputSlot,
|
||||
INodeOutputSlot,
|
||||
ISlotType,
|
||||
@@ -89,7 +90,9 @@ type BasicReadonlyNetwork = Pick<
|
||||
>
|
||||
|
||||
// this is the class in charge of storing link information
|
||||
export class LLink implements LinkSegment, Serialisable<SerialisableLLink> {
|
||||
export class LLink
|
||||
implements LinkSegment, Serialisable<SerialisableLLink>, ILinkRouting
|
||||
{
|
||||
static _drawDebug = false
|
||||
|
||||
/** Link ID */
|
||||
|
||||
@@ -184,6 +184,21 @@ export interface ItemLocator {
|
||||
): SubgraphInputNode | SubgraphOutputNode | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimal link routing information used for input resolution.
|
||||
* Both LLink and partial link info objects satisfy this interface.
|
||||
*/
|
||||
export interface ILinkRouting {
|
||||
/** Output node ID */
|
||||
readonly origin_id: NodeId
|
||||
/** Output slot index */
|
||||
readonly origin_slot: number
|
||||
/** Input node ID */
|
||||
readonly target_id: NodeId
|
||||
/** Input slot index */
|
||||
readonly target_slot: number
|
||||
}
|
||||
|
||||
/** Contains a cached 2D canvas path and a centre point, with an optional forward angle. */
|
||||
export interface LinkSegment {
|
||||
/** Link / reroute ID */
|
||||
|
||||
@@ -9,7 +9,11 @@ import type {
|
||||
CallbackReturn,
|
||||
ISlotType
|
||||
} from '@/lib/litegraph/src/interfaces'
|
||||
import { LGraphEventMode, LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import {
|
||||
LGraphEventMode,
|
||||
LiteGraph,
|
||||
LLink
|
||||
} from '@/lib/litegraph/src/litegraph'
|
||||
|
||||
import type { Subgraph } from './Subgraph'
|
||||
import type { SubgraphNode } from './SubgraphNode'
|
||||
@@ -289,7 +293,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
|
||||
|
||||
if (node.isVirtualNode) {
|
||||
const virtualLink = this.node.getInputLink(slot)
|
||||
if (virtualLink) {
|
||||
if (virtualLink instanceof LLink) {
|
||||
const { inputNode } = virtualLink.resolve(this.graph)
|
||||
if (!inputNode)
|
||||
throw new InvalidLinkError(
|
||||
|
||||
Reference in New Issue
Block a user