Skip to content

Commit b52a450

Browse files
committed
fix: restore legacy slot links
Amp-Thread-ID: https://ampcode.com/threads/T-01a02889-c911-708b-bf1a-11dfb0904fff
1 parent 9586adb commit b52a450

3 files changed

Lines changed: 127 additions & 19 deletions

File tree

src/lib/litegraph/src/node/NodeInputSlot.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
2-
import type { LinkId } from '@/lib/litegraph/src/LLink'
2+
import type { LLink,LinkId } from '@/lib/litegraph/src/LLink'
33
import { LabelPosition } from '@/lib/litegraph/src/draw'
44
import type {
55
INodeInputSlot,
@@ -9,7 +9,8 @@ import type {
99
} from '@/lib/litegraph/src/interfaces'
1010
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
1111
import { NodeSlot } from '@/lib/litegraph/src/node/NodeSlot'
12-
import { inputHasLink, inputLinkId } from '@/lib/litegraph/src/node/slotLinks'
12+
import { inputHasLink, inputLink } from '@/lib/litegraph/src/node/slotLinks'
13+
import { restoreLegacyLink } from '@/lib/litegraph/src/node/restoreLegacyLink'
1314
import { warnDeprecated } from '@/lib/litegraph/src/utils/feedback'
1415
import type { IDrawOptions } from '@/lib/litegraph/src/node/NodeSlot'
1516
import type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'
@@ -19,25 +20,35 @@ import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
1920

2021
export class NodeInputSlot extends NodeSlot implements INodeInputSlot {
2122
alwaysVisible?: boolean
23+
private legacyLink?: LLink
2224

2325
/**
2426
* @deprecated Reads return the store-derived link id. Assigning null
25-
* disconnects through the store; assigning an id is ignored. First-party
26-
* code uses the slotLinks helpers and node topology methods.
27+
* disconnects through the store; assigning that id again restores the
28+
* observed topology when it is still valid.
2729
*/
2830
get link(): LinkId | null {
2931
warnDeprecated(
3032
'input.link is deprecated. Read connectivity via node.isInputConnected(slot) / node.getInputLink(slot); mutate via node.connect() / node.disconnectInput().'
3133
)
32-
return linkIdOf(this)
34+
const link = linkOf(this)
35+
if (link) this.legacyLink = link
36+
return link?.id ?? null
3337
}
3438

3539
set link(value: LinkId | null) {
3640
warnDeprecated(
3741
'Assignment to input.link is deprecated; null disconnects through the link store. Mutate via node.connect() / node.disconnectInput().'
3842
)
3943
const slot = indexOf(this)
40-
if (value === null && slot !== -1) this._node.disconnectInput(slot)
44+
if (slot === -1) return
45+
if (value === null) {
46+
const link = linkOf(this)
47+
if (link) this.legacyLink = link
48+
this._node.disconnectInput(slot)
49+
} else if (this.legacyLink?.id === value) {
50+
restoreLegacyLink(this.legacyLink, this._node, slot, 'input')
51+
}
4152
}
4253

4354
get isWidgetInputSlot(): boolean {
@@ -124,7 +135,10 @@ function indexOf(slot: NodeInputSlot): number {
124135
}
125136

126137
function linkIdOf(slot: NodeInputSlot): LinkId | null {
138+
return linkOf(slot)?.id ?? null
139+
}
140+
141+
function linkOf(slot: NodeInputSlot): LLink | undefined {
127142
const { graph } = slot.node
128-
if (!graph) return null
129-
return inputLinkId(graph, slot.node.id, indexOf(slot)) ?? null
143+
return graph ? inputLink(graph, slot.node.id, indexOf(slot)) : undefined
130144
}

src/lib/litegraph/src/node/NodeOutputSlot.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
2-
import type { LinkId } from '@/lib/litegraph/src/LLink'
2+
import type { LLink,LinkId } from '@/lib/litegraph/src/LLink'
33
import { LabelPosition } from '@/lib/litegraph/src/draw'
44
import type {
55
INodeInputSlot,
@@ -11,11 +11,8 @@ import { LiteGraph } from '@/lib/litegraph/src/litegraph'
1111
import { createArrayMutationView } from '@/lib/litegraph/src/infrastructure/createMutationView'
1212
import { NodeSlot } from '@/lib/litegraph/src/node/NodeSlot'
1313
import type { IDrawOptions } from '@/lib/litegraph/src/node/NodeSlot'
14-
import {
15-
outputHasLinks,
16-
outputLinkIds,
17-
outputLinks
18-
} from '@/lib/litegraph/src/node/slotLinks'
14+
import { restoreLegacyLink } from '@/lib/litegraph/src/node/restoreLegacyLink'
15+
import { outputHasLinks, outputLinks } from '@/lib/litegraph/src/node/slotLinks'
1916
import type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'
2017
import type { SubgraphOutput } from '@/lib/litegraph/src/subgraph/SubgraphOutput'
2118
import { isSubgraphOutput } from '@/lib/litegraph/src/subgraph/subgraphUtils'
@@ -25,13 +22,14 @@ export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
2522
_data?: unknown
2623
slot_index?: number
2724
private readonly legacyLinkIds!: LinkId[]
25+
private readonly legacyLinks!: Map<LinkId, LLink>
2826
private readonly legacyLinksView!: LinkId[]
2927
private legacyLinksPresent!: boolean
3028

3129
/**
3230
* @deprecated Reads return a stable store-derived view. Removing ids from the
33-
* view disconnects them; additions are discarded. First-party code uses the
34-
* slotLinks helpers and node topology methods.
31+
* view disconnects them; adding a previously observed id restores its
32+
* topology when it is still valid.
3533
*/
3634
get links(): LinkId[] | null {
3735
warnDeprecated(
@@ -51,7 +49,9 @@ export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
5149
}
5250

5351
private synchronizeLegacyLinks(): void {
54-
const ids = linkIdsOf(this)
52+
const links = linksOf(this)
53+
for (const link of links) this.legacyLinks.set(link.id, link)
54+
const ids = links.map((link) => link.id)
5555
this.legacyLinkIds.splice(0, this.legacyLinkIds.length, ...ids)
5656
if (ids.length) this.legacyLinksPresent = true
5757
}
@@ -73,10 +73,17 @@ export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
7373
}
7474

7575
const desired = new Set(this.legacyLinkIds)
76-
for (const link of outputLinks(graph, this._node.id, slot)) {
76+
const current = outputLinks(graph, this._node.id, slot)
77+
for (const link of current) this.legacyLinks.set(link.id, link)
78+
for (const link of current) {
7779
if (desired.has(link.id)) continue
7880
graph.getNodeById(link.target_id)?.disconnectInput(link.target_slot)
7981
}
82+
for (const id of desired) {
83+
if (graph.getLink(id)) continue
84+
const link = this.legacyLinks.get(id)
85+
if (link) restoreLegacyLink(link, this._node, slot, 'output')
86+
}
8087
this.synchronizeLegacyLinks()
8188
}
8289

@@ -103,6 +110,7 @@ export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
103110
const legacyLinkIds: LinkId[] = []
104111
Object.defineProperties(this, {
105112
legacyLinkIds: { value: legacyLinkIds },
113+
legacyLinks: { value: new Map<LinkId, LLink>() },
106114
legacyLinksView: {
107115
value: createArrayMutationView(
108116
legacyLinkIds,
@@ -175,6 +183,10 @@ function indexOf(slot: NodeOutputSlot): number {
175183
}
176184

177185
function linkIdsOf(slot: NodeOutputSlot): LinkId[] {
186+
return linksOf(slot).map((link) => link.id)
187+
}
188+
189+
function linksOf(slot: NodeOutputSlot): LLink[] {
178190
const { graph } = slot.node
179-
return graph ? outputLinkIds(graph, slot.node.id, indexOf(slot)) : []
191+
return graph ? outputLinks(graph, slot.node.id, indexOf(slot)) : []
180192
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
2+
import type { LLink } from '@/lib/litegraph/src/LLink'
3+
import { registerLinkTopology } from '@/lib/litegraph/src/LLink'
4+
import { inputHasLink } from '@/lib/litegraph/src/node/slotLinks'
5+
import { anchorRerouteChain } from '@/lib/litegraph/src/Reroute'
6+
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
7+
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
8+
9+
export function restoreLegacyLink(
10+
link: LLink,
11+
node: LGraphNode,
12+
slot: number,
13+
side: 'input' | 'output'
14+
): boolean {
15+
const { graph } = node
16+
if (!graph || graph.getLink(link.id)) return false
17+
if (
18+
side === 'input'
19+
? !link.hasTarget(node.id, slot)
20+
: !link.hasOrigin(node.id, slot)
21+
)
22+
return false
23+
24+
const outputNode = graph.getNodeById(link.origin_id)
25+
const inputNode = graph.getNodeById(link.target_id)
26+
const output = outputNode?.outputs[link.origin_slot]
27+
const input = inputNode?.inputs[link.target_slot]
28+
if (!outputNode || !inputNode || !output || !input) return false
29+
if (inputHasLink(graph, inputNode.id, link.target_slot)) return false
30+
if (!LiteGraph.isValidConnection(output.type, input.type)) return false
31+
if (
32+
inputNode.onConnectInput?.(
33+
link.target_slot,
34+
output.type,
35+
output,
36+
outputNode,
37+
link.origin_slot
38+
) === false ||
39+
outputNode.onConnectOutput?.(
40+
link.origin_slot,
41+
input.type,
42+
input,
43+
inputNode,
44+
link.target_slot
45+
) === false
46+
)
47+
return false
48+
49+
if (link.parentId !== undefined && !graph.reroutes.has(link.parentId)) {
50+
link.parentId = undefined
51+
}
52+
if (!registerLinkTopology(graph, link)) return false
53+
54+
anchorRerouteChain(graph, link)
55+
graph.incrementVersion()
56+
outputNode.onConnectionsChange?.(
57+
NodeSlotType.OUTPUT,
58+
link.origin_slot,
59+
true,
60+
link,
61+
output
62+
)
63+
if (graph.getLink(link.id) !== link) {
64+
graph.afterChange()
65+
return false
66+
}
67+
inputNode.onConnectionsChange?.(
68+
NodeSlotType.INPUT,
69+
link.target_slot,
70+
true,
71+
link,
72+
input
73+
)
74+
if (graph.getLink(link.id) !== link) {
75+
graph.afterChange()
76+
return false
77+
}
78+
79+
outputNode.setDirtyCanvas(false, true)
80+
graph.afterChange()
81+
return true
82+
}

0 commit comments

Comments
 (0)