mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-09 08:38:13 +00:00
refactor: extract anchorRerouteChain and collapse chain-splice loops
Deduplicate the connect-path floating-chain cleanup (LGraphNode, SubgraphInput, SubgraphOutput) into anchorRerouteChain, add unregisterAllRerouteChains mirroring the link-side helper, splice createReroute's live/floating loops into one pass over the actual link objects (also removing the ambiguous cross-id-space lookup for a link segment), inline the single-caller Reroute.update into setReroute, and drop test pinia hooks shadowed by the file-level one.
This commit is contained in:
@@ -399,8 +399,6 @@ describe('Graph Clearing and Callbacks', () => {
|
||||
})
|
||||
|
||||
test('clear() removes graph-scoped preview and widget-value state', () => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
|
||||
const graph = new LGraph()
|
||||
const graphId = 'graph-clear-cleanup' as UUID
|
||||
graph.id = graphId
|
||||
@@ -529,10 +527,6 @@ describe('node:before-removed event', () => {
|
||||
})
|
||||
|
||||
describe('Subgraph Definition Garbage Collection', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
})
|
||||
|
||||
function createSubgraphWithNodes(rootGraph: LGraph, nodeCount: number) {
|
||||
const subgraph = rootGraph.createSubgraph(createTestSubgraphData())
|
||||
|
||||
@@ -891,8 +885,6 @@ describe('ensureGlobalIdUniqueness', () => {
|
||||
})
|
||||
|
||||
describe('_removeDuplicateLinks', () => {
|
||||
beforeEach(() => setActivePinia(createTestingPinia({ stubActions: false })))
|
||||
|
||||
class TestNode extends LGraphNode {
|
||||
constructor(title?: string) {
|
||||
super(title ?? 'TestNode')
|
||||
@@ -1207,7 +1199,6 @@ describe('deduplicateSubgraphNodeIds (via configure)', () => {
|
||||
const SHARED_NODE_IDS = [3, 8, 37]
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
LiteGraph.registerNodeType('dummy', DummyNode)
|
||||
})
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import { MapProxyHandler } from './MapProxyHandler'
|
||||
import {
|
||||
registerRerouteChain,
|
||||
Reroute,
|
||||
unregisterAllRerouteChains,
|
||||
unregisterRerouteChain
|
||||
} from './Reroute'
|
||||
import type { RerouteId } from './Reroute'
|
||||
@@ -415,9 +416,7 @@ export class LGraph
|
||||
// Subgraphs and unconfigured (zero-uuid) graphs share their store
|
||||
// bucket with other graphs, so unregister each link individually.
|
||||
unregisterAllLinkTopologies(this)
|
||||
for (const reroute of this.reroutes.values()) {
|
||||
unregisterRerouteChain(reroute)
|
||||
}
|
||||
unregisterAllRerouteChains(this)
|
||||
}
|
||||
|
||||
this.id = zeroUuid
|
||||
@@ -1142,9 +1141,7 @@ export class LGraph
|
||||
if (!hasRemainingReferences) {
|
||||
forEachNode(node.subgraph, fireNodeRemovalLifecycle)
|
||||
unregisterAllLinkTopologies(node.subgraph)
|
||||
for (const reroute of node.subgraph.reroutes.values()) {
|
||||
unregisterRerouteChain(reroute)
|
||||
}
|
||||
unregisterAllRerouteChains(node.subgraph)
|
||||
this.rootGraph.subgraphs.delete(node.subgraph.id)
|
||||
}
|
||||
}
|
||||
@@ -1587,9 +1584,10 @@ export class LGraph
|
||||
}
|
||||
|
||||
const reroute = this.reroutes.get(rerouteId) ?? new Reroute(rerouteId, this)
|
||||
const typedParentId =
|
||||
reroute.parentId =
|
||||
parentId === undefined ? undefined : toRerouteId(parentId)
|
||||
reroute.update(typedParentId, pos, floating)
|
||||
if (pos) reroute.pos = pos
|
||||
reroute.floating = floating
|
||||
this._addReroute(reroute)
|
||||
return reroute
|
||||
}
|
||||
@@ -1608,10 +1606,15 @@ export class LGraph
|
||||
}
|
||||
const rerouteId = toRerouteId(Number(this.state.lastRerouteId) + 1)
|
||||
this.state.lastRerouteId = rerouteId
|
||||
const linkIds =
|
||||
before instanceof Reroute ? [...before.linkIds] : [before.id]
|
||||
const floatingLinkIds =
|
||||
before instanceof Reroute ? [...before.floatingLinkIds] : [before.id]
|
||||
const chainLinks =
|
||||
before instanceof Reroute
|
||||
? [
|
||||
...[...before.linkIds].map((id) => this._links.get(id)),
|
||||
...[...before.floatingLinkIds].map((id) =>
|
||||
this.floatingLinks.get(id)
|
||||
)
|
||||
]
|
||||
: [before]
|
||||
const reroute = new Reroute(rerouteId, this, pos, before.parentId)
|
||||
this._addReroute(reroute)
|
||||
|
||||
@@ -1621,22 +1624,11 @@ export class LGraph
|
||||
rerouteId,
|
||||
{ x: pos[0], y: pos[1] },
|
||||
before.parentId,
|
||||
linkIds
|
||||
before instanceof Reroute ? [...before.linkIds] : [before.id]
|
||||
)
|
||||
|
||||
for (const linkId of linkIds) {
|
||||
const link = this._links.get(linkId)
|
||||
if (!link) continue
|
||||
if (link.parentId === before.parentId) link.parentId = rerouteId
|
||||
|
||||
const reroutes = LLink.getReroutes(this, link)
|
||||
for (const x of reroutes.filter((x) => x.parentId === before.parentId)) {
|
||||
x.parentId = rerouteId
|
||||
}
|
||||
}
|
||||
|
||||
for (const linkId of floatingLinkIds) {
|
||||
const link = this.floatingLinks.get(linkId)
|
||||
// Splice the new reroute into every chain that contained `before`
|
||||
for (const link of chainLinks) {
|
||||
if (!link) continue
|
||||
if (link.parentId === before.parentId) link.parentId = rerouteId
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import { LGraphButton } from './LGraphButton'
|
||||
import type { LGraphButtonOptions } from './LGraphButton'
|
||||
import { LGraphCanvas } from './LGraphCanvas'
|
||||
import { LLink } from './LLink'
|
||||
import { anchorRerouteChain } from './Reroute'
|
||||
import type { Reroute, RerouteId } from './Reroute'
|
||||
import { getNodeInputOnPos, getNodeOutputOnPos } from './canvas/measureSlots'
|
||||
import type { IDrawBoundingOptions } from './draw'
|
||||
@@ -2987,23 +2988,7 @@ export class LGraphNode
|
||||
})
|
||||
}
|
||||
|
||||
// Reroutes
|
||||
const reroutes = LLink.getReroutes(graph, link)
|
||||
for (const reroute of reroutes) {
|
||||
if (reroute.floating) reroute.floating = undefined
|
||||
reroute._dragging = undefined
|
||||
}
|
||||
|
||||
// If this is the terminus of a floating link, remove it
|
||||
const lastReroute = reroutes.at(-1)
|
||||
if (lastReroute) {
|
||||
for (const linkId of lastReroute.floatingLinkIds) {
|
||||
const link = graph.floatingLinks.get(linkId)
|
||||
if (link?.parentId === lastReroute.id) {
|
||||
graph.removeFloatingLink(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
anchorRerouteChain(graph, link)
|
||||
graph.incrementVersion()
|
||||
|
||||
// link has been created now, so its updated
|
||||
|
||||
@@ -242,23 +242,6 @@ export class Reroute
|
||||
if (pos) this.pos = pos
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a new parentId to the reroute, and optionally a new position.
|
||||
* Primarily used for deserialisation.
|
||||
* @param parentId The ID of the reroute prior to this reroute, or
|
||||
* `undefined` if it is the first reroute connected to a nodes output
|
||||
* @param pos The position of this reroute
|
||||
*/
|
||||
update(
|
||||
parentId: RerouteId | undefined,
|
||||
pos?: Point,
|
||||
floating?: FloatingRerouteSlot
|
||||
): void {
|
||||
this.parentId = parentId
|
||||
if (pos) this.pos = pos
|
||||
this.floating = floating
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an ordered array of all reroutes from the node output.
|
||||
* @param visited Internal. A set of reroutes that this function
|
||||
@@ -806,6 +789,31 @@ function getDirection(fromPos: Point, toPos: Point) {
|
||||
return Math.atan2(toPos[1] - fromPos[1], toPos[0] - fromPos[0])
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a link's reroute chain as no longer floating: clears each reroute's
|
||||
* floating marker and drag state, and removes any floating link that
|
||||
* terminates at the chain's last reroute. Call when a real link connects
|
||||
* through the chain.
|
||||
* @param network The network containing the chain
|
||||
* @param link The link whose chain was just connected
|
||||
*/
|
||||
export function anchorRerouteChain(network: LinkNetwork, link: LLink): void {
|
||||
const reroutes = LLink.getReroutes(network, link)
|
||||
for (const reroute of reroutes) {
|
||||
reroute.floating = undefined
|
||||
reroute._dragging = undefined
|
||||
}
|
||||
|
||||
const lastReroute = reroutes.at(-1)
|
||||
if (!lastReroute) return
|
||||
for (const linkId of lastReroute.floatingLinkIds) {
|
||||
const floatingLink = network.floatingLinks.get(linkId)
|
||||
if (floatingLink?.parentId === lastReroute.id) {
|
||||
network.removeFloatingLink(floatingLink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a reroute's chain state into {@link useRerouteStore} and adopts
|
||||
* the store's reactive proxy as {@link Reroute._chain}, so the store and the
|
||||
@@ -833,3 +841,15 @@ export function unregisterRerouteChain(reroute: Reroute): void {
|
||||
useRerouteStore().deleteReroute(reroute._graphId, reroute._chain)
|
||||
reroute._graphId = undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters every reroute a graph owns. Used when a graph's reroutes
|
||||
* leave the store without a whole-bucket wipe: subgraph-definition removal,
|
||||
* and clearing a graph that shares its bucket with other graphs.
|
||||
* @param graph The graph whose reroutes should be unregistered
|
||||
*/
|
||||
export function unregisterAllRerouteChains(
|
||||
graph: Pick<LGraph, 'reroutes'>
|
||||
): void {
|
||||
for (const reroute of graph.reroutes.values()) unregisterRerouteChain(reroute)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||
import { LLink } from '@/lib/litegraph/src/LLink'
|
||||
import { toLinkId } from '@/types/linkId'
|
||||
import { anchorRerouteChain } from '@/lib/litegraph/src/Reroute'
|
||||
import type { RerouteId } from '@/lib/litegraph/src/Reroute'
|
||||
import { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'
|
||||
import type { SubgraphInputEventMap } from '@/lib/litegraph/src/infrastructure/SubgraphInputEventMap'
|
||||
@@ -120,23 +121,7 @@ export class SubgraphInput extends SubgraphSlot {
|
||||
this.linkIds.push(link.id)
|
||||
slot.link = link.id
|
||||
|
||||
// Reroutes
|
||||
const reroutes = LLink.getReroutes(subgraph, link)
|
||||
for (const reroute of reroutes) {
|
||||
reroute.floating = undefined
|
||||
reroute._dragging = undefined
|
||||
}
|
||||
|
||||
// If this is the terminus of a floating link, remove it
|
||||
const lastReroute = reroutes.at(-1)
|
||||
if (lastReroute) {
|
||||
for (const linkId of lastReroute.floatingLinkIds) {
|
||||
const link = subgraph.floatingLinks.get(linkId)
|
||||
if (link?.parentId === lastReroute.id) {
|
||||
subgraph.removeFloatingLink(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
anchorRerouteChain(subgraph, link)
|
||||
subgraph.incrementVersion()
|
||||
|
||||
subgraph.trigger('node:slot-links:changed', {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { pull } from 'es-toolkit/compat'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||
import { LLink } from '@/lib/litegraph/src/LLink'
|
||||
import { toLinkId } from '@/types/linkId'
|
||||
import { anchorRerouteChain } from '@/lib/litegraph/src/Reroute'
|
||||
import type { RerouteId } from '@/lib/litegraph/src/Reroute'
|
||||
import type {
|
||||
INodeInputSlot,
|
||||
@@ -85,23 +86,7 @@ export class SubgraphOutput extends SubgraphSlot {
|
||||
slot.links ??= []
|
||||
slot.links.push(link.id)
|
||||
|
||||
// Reroutes
|
||||
const reroutes = LLink.getReroutes(subgraph, link)
|
||||
for (const reroute of reroutes) {
|
||||
reroute.floating = undefined
|
||||
reroute._dragging = undefined
|
||||
}
|
||||
|
||||
// If this is the terminus of a floating link, remove it
|
||||
const lastReroute = reroutes.at(-1)
|
||||
if (lastReroute) {
|
||||
for (const linkId of lastReroute.floatingLinkIds) {
|
||||
const link = subgraph.floatingLinks.get(linkId)
|
||||
if (link?.parentId === lastReroute.id) {
|
||||
subgraph.removeFloatingLink(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
anchorRerouteChain(subgraph, link)
|
||||
subgraph.incrementVersion()
|
||||
|
||||
node.onConnectionsChange?.(
|
||||
|
||||
@@ -84,7 +84,7 @@ export const useLinkStore = defineStore('link', () => {
|
||||
const existing = targets.get(key)
|
||||
if (existing && toRaw(existing) !== toRaw(topology)) return undefined
|
||||
targets.set(key, topology)
|
||||
return targets.get(key)
|
||||
return reactive(topology)
|
||||
}
|
||||
|
||||
/** Removes a link's placement; only the registered topology may vacate it. */
|
||||
|
||||
Reference in New Issue
Block a user