Skip to content

Commit a8f6bea

Browse files
AustinMrozDrJKLcoderabbitai[bot]
authored
Color links as common type (#7211)
Previously the color of a link would simply use the type of the target slot and fallback to the type of the origin slot. When a connection is made to a node that accepts the any type ('*'), the link has the green color of an unknown type. Instead, when a connection is made, the type of a link is now calculated as the greatest common type of the source and destination. This means that connections to reroutes are correctly colored. | Before | After | | ------ | ----- | | <img width="360" alt="before" src="https://github.com/user-attachments/assets/a5544730-e69a-4c85-af33-b303bb30ae71" />| <img width="360" alt="after" src="https://github.com/user-attachments/assets/7d7b59fd-1b79-440b-a97d-a1657313c484" />| The code for calculating common types already exists, it has simply been moved into litegraph and given a more descriptive name. Resolves #7196 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7211-Color-links-as-common-type-2c16d73d365081188460f6b5973db962) by [Unito](https://www.unito.io) --------- Co-authored-by: Alexander Brown <drjkl@comfy.org> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent 1d18583 commit a8f6bea

File tree

4 files changed

+39
-38
lines changed

4 files changed

+39
-38
lines changed

src/core/graph/widgets/dynamicWidgets.ts

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { without } from 'es-toolkit'
2-
31
import { useChainCallback } from '@/composables/functional/useChainCallback'
42
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
53
import type {
@@ -10,6 +8,7 @@ import type {
108
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
119
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
1210
import type { LLink } from '@/lib/litegraph/src/LLink'
11+
import { commonType } from '@/lib/litegraph/src/utils/type'
1312
import { transformInputSpecV1ToV2 } from '@/schemas/nodeDef/migration'
1413
import type { ComboInputSpec, InputSpec } from '@/schemas/nodeDefSchema'
1514
import type { InputSpec as InputSpecV2 } from '@/schemas/nodeDef/nodeDefSchemaV2'
@@ -20,7 +19,6 @@ import {
2019
import { useLitegraphService } from '@/services/litegraphService'
2120
import { app } from '@/scripts/app'
2221
import type { ComfyApp } from '@/scripts/app'
23-
import { isStrings } from '@/utils/typeGuardUtil'
2422

2523
const INLINE_INPUTS = false
2624

@@ -244,30 +242,6 @@ function changeOutputType(
244242
}
245243
}
246244

247-
function combineTypes(...types: ISlotType[]): ISlotType | undefined {
248-
if (!isStrings(types)) return undefined
249-
250-
const withoutWildcards = without(types, '*')
251-
if (withoutWildcards.length === 0) return '*'
252-
253-
const typeLists: string[][] = withoutWildcards.map((type) => type.split(','))
254-
255-
const combinedTypes = intersection(...typeLists)
256-
if (combinedTypes.length === 0) return undefined
257-
258-
return combinedTypes.join(',')
259-
}
260-
261-
function intersection(...sets: string[][]): string[] {
262-
const itemCounts: Record<string, number> = {}
263-
for (const set of sets)
264-
for (const item of new Set(set))
265-
itemCounts[item] = (itemCounts[item] ?? 0) + 1
266-
return Object.entries(itemCounts)
267-
.filter(([, count]) => count == sets.length)
268-
.map(([key]) => key)
269-
}
270-
271245
function withComfyMatchType(node: LGraphNode): asserts node is MatchTypeNode {
272246
if (node.comfyMatchType) return
273247
node.comfyMatchType = {}
@@ -290,8 +264,6 @@ function withComfyMatchType(node: LGraphNode): asserts node is MatchTypeNode {
290264
if (!matchGroup) return
291265
if (iscon && linf) {
292266
const { output, subgraphInput } = linf.resolve(this.graph)
293-
//TODO: fix this bug globally. A link type (and therefore color)
294-
//should be the combinedType of origin and target type
295267
const connectingType = (output ?? subgraphInput)?.type
296268
if (connectingType) linf.type = connectingType
297269
}
@@ -316,14 +288,14 @@ function withComfyMatchType(node: LGraphNode): asserts node is MatchTypeNode {
316288
...connectedTypes.slice(0, idx),
317289
...connectedTypes.slice(idx + 1)
318290
]
319-
const combinedType = combineTypes(
291+
const combinedType = commonType(
320292
...otherConnected,
321293
matchGroup[input.name]
322294
)
323295
if (!combinedType) throw new Error('invalid connection')
324296
input.type = combinedType
325297
})
326-
const outputType = combineTypes(...connectedTypes)
298+
const outputType = commonType(...connectedTypes)
327299
if (!outputType) throw new Error('invalid connection')
328300
this.outputs.forEach((output, idx) => {
329301
if (!(outputGroups?.[idx] == matchKey)) return

src/lib/litegraph/src/LGraphNode.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMuta
1010
import { LayoutSource } from '@/renderer/core/layout/types'
1111
import { adjustColor } from '@/utils/colorUtil'
1212
import type { ColorAdjustOptions } from '@/utils/colorUtil'
13+
import { commonType, toClass } from '@/lib/litegraph/src/utils/type'
1314

1415
import { SUBGRAPH_OUTPUT_ID } from '@/lib/litegraph/src/constants'
1516
import type { DragAndScale } from './DragAndScale'
@@ -84,7 +85,6 @@ import { findFreeSlotOfType } from './utils/collections'
8485
import { warnDeprecated } from './utils/feedback'
8586
import { distributeSpace } from './utils/spaceDistribution'
8687
import { truncateText } from './utils/textUtils'
87-
import { toClass } from './utils/type'
8888
import { BaseWidget } from './widgets/BaseWidget'
8989
import { toConcreteWidget } from './widgets/widgetMap'
9090
import type { WidgetTypeMap } from './widgets/widgetMap'
@@ -2832,9 +2832,12 @@ export class LGraphNode
28322832
inputNode.disconnectInput(inputIndex, true)
28332833
}
28342834

2835+
const maybeCommonType =
2836+
input.type && output.type && commonType(input.type, output.type)
2837+
28352838
const link = new LLink(
28362839
++graph.state.lastLinkId,
2837-
input.type || output.type,
2840+
maybeCommonType || input.type || output.type,
28382841
this.id,
28392842
outputIndex,
28402843
inputNode.id,

src/lib/litegraph/src/utils/type.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import type { IColorable } from '@/lib/litegraph/src/interfaces'
1+
import { without } from 'es-toolkit'
2+
3+
import type { IColorable, ISlotType } from '@/lib/litegraph/src/interfaces'
24

35
/**
46
* Converts a plain object to a class instance if it is not already an instance of the class.
@@ -26,3 +28,31 @@ export function isColorable(obj: unknown): obj is IColorable {
2628
'getColorOption' in obj
2729
)
2830
}
31+
32+
export function commonType(...types: ISlotType[]): ISlotType | undefined {
33+
if (!isStrings(types)) return undefined
34+
35+
const withoutWildcards = without(types, '*')
36+
if (withoutWildcards.length === 0) return '*'
37+
38+
const typeLists: string[][] = withoutWildcards.map((type) => type.split(','))
39+
40+
const combinedTypes = intersection(...typeLists)
41+
if (combinedTypes.length === 0) return undefined
42+
43+
return combinedTypes.join(',')
44+
}
45+
46+
function intersection(...sets: string[][]): string[] {
47+
const itemCounts: Record<string, number> = {}
48+
for (const set of sets)
49+
for (const item of new Set(set))
50+
itemCounts[item] = (itemCounts[item] ?? 0) + 1
51+
return Object.entries(itemCounts)
52+
.filter(([, count]) => count === sets.length)
53+
.map(([key]) => key)
54+
}
55+
56+
function isStrings(types: unknown[]): types is string[] {
57+
return types.every((t) => typeof t === 'string')
58+
}

src/utils/typeGuardUtil.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,3 @@ export const isResultItemType = (
6060
): value is ResultItemType => {
6161
return value === 'input' || value === 'output' || value === 'temp'
6262
}
63-
64-
export function isStrings(types: unknown[]): types is string[] {
65-
return types.every((t) => typeof t === 'string')
66-
}

0 commit comments

Comments
 (0)