mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-09 08:38:13 +00:00
Compare commits
1 Commits
codex/code
...
codex/code
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83be5e4efb |
@@ -260,7 +260,6 @@ useExtensionService().registerExtension({
|
||||
if (!isLoad3dNode(selectedNode)) return
|
||||
|
||||
ComfyApp.copyToClipspace(selectedNode)
|
||||
// @ts-expect-error clipspace_return_node is an extension property added at runtime
|
||||
ComfyApp.clipspace_return_node = selectedNode
|
||||
|
||||
const props = { node: selectedNode }
|
||||
|
||||
@@ -2494,6 +2494,7 @@ export class LGraph
|
||||
// Deprecated - old schema version, links are arrays
|
||||
if (Array.isArray(data.links)) {
|
||||
for (const linkData of data.links) {
|
||||
if (!linkData) continue
|
||||
const link = LLink.createFromArray(linkData)
|
||||
this._links.set(link.id, link)
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ export interface ISerialisedGraph extends BaseExportedGraph {
|
||||
last_node_id: SerializedNodeId
|
||||
last_link_id: number
|
||||
nodes: ISerialisedNode[]
|
||||
links: SerialisedLLinkArray[]
|
||||
links: (SerialisedLLinkArray | null)[]
|
||||
floatingLinks?: SerialisableLLink[]
|
||||
groups: ISerialisedGroup[]
|
||||
version: typeof LiteGraph.VERSION
|
||||
|
||||
@@ -21,7 +21,7 @@ const { isCreditsBadge } = usePriceBadge()
|
||||
const { t } = useI18n()
|
||||
|
||||
const creditsBadges = computed(() =>
|
||||
mapAllNodes(app.graph, (node) => {
|
||||
mapAllNodes(app.rootGraph, (node) => {
|
||||
if (node.isSubgraphNode()) return
|
||||
|
||||
const priceBadge = node.badges.find(isCreditsBadge)
|
||||
|
||||
3124
src/scripts/app.core.test.ts
Normal file
3124
src/scripts/app.core.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -244,7 +244,7 @@ export class ComfyApp {
|
||||
static clipspace_invalidate_handler: (() => void) | null = null
|
||||
static open_maskeditor: (() => void) | null = null
|
||||
static maskeditor_is_opended: (() => void) | null = null
|
||||
static clipspace_return_node = null
|
||||
static clipspace_return_node: LGraphNode | null = null
|
||||
|
||||
vueAppReady: boolean
|
||||
api: ComfyApi
|
||||
@@ -257,8 +257,8 @@ export class ComfyApp {
|
||||
|
||||
// TODO: Migrate internal usage to the
|
||||
/** @deprecated Use {@link rootGraph} instead */
|
||||
get graph() {
|
||||
return this.rootGraphInternal!
|
||||
get graph(): LGraph | undefined {
|
||||
return this.rootGraphInternal
|
||||
}
|
||||
|
||||
get rootGraph(): LGraph {
|
||||
@@ -1167,7 +1167,7 @@ export class ComfyApp {
|
||||
}
|
||||
|
||||
async loadGraphData(
|
||||
graphData?: ComfyWorkflowJSON,
|
||||
graphData?: ComfyWorkflowJSON | unknown[],
|
||||
clean: boolean = true,
|
||||
restore_view: boolean = true,
|
||||
workflow: string | null | ComfyWorkflow = null,
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('useSubgraphNavigationStore', () => {
|
||||
app.canvas.ds.offset = [0, 0]
|
||||
app.canvas.ds.state.scale = 1
|
||||
app.canvas.ds.state.offset = [0, 0]
|
||||
app.graph.getNodeById = vi.fn()
|
||||
app.rootGraph.getNodeById = vi.fn()
|
||||
})
|
||||
|
||||
it('should not clear navigation stack when workflow internal state changes', async () => {
|
||||
@@ -223,7 +223,7 @@ describe('useSubgraphNavigationStore', () => {
|
||||
|
||||
const unreachableSubgraph = createMockSubgraph('orphan-subgraph', app.graph)
|
||||
|
||||
app.graph.subgraphs.set(unreachableSubgraph.id, unreachableSubgraph)
|
||||
app.rootGraph.subgraphs.set(unreachableSubgraph.id, unreachableSubgraph)
|
||||
vi.mocked(findSubgraphPathById).mockReturnValue(null)
|
||||
|
||||
const mockWorkflow = fromPartial<ComfyWorkflow>({
|
||||
@@ -251,7 +251,7 @@ describe('useSubgraphNavigationStore', () => {
|
||||
const mockSubgraph = createMockSubgraph('subgraph-1', app.graph)
|
||||
|
||||
// Add the subgraph to the graph's subgraphs map
|
||||
app.graph.subgraphs.set('subgraph-1', mockSubgraph)
|
||||
app.rootGraph.subgraphs.set('subgraph-1', mockSubgraph)
|
||||
|
||||
// First set an active workflow
|
||||
const mockWorkflow = fromPartial<ComfyWorkflow>({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { fromPartial } from '@total-typescript/shoehorn'
|
||||
import { setActivePinia } from 'pinia'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { nextTick } from 'vue'
|
||||
@@ -89,7 +90,7 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
return rafCallbacks.length
|
||||
})
|
||||
mockCanvas.subgraph = undefined
|
||||
mockCanvas.graph = app.graph
|
||||
mockCanvas.graph = app.graph ?? null
|
||||
mockCanvas.ds.scale = 1
|
||||
mockCanvas.ds.offset = [0, 0]
|
||||
mockCanvas.ds.state.scale = 1
|
||||
@@ -136,6 +137,23 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
})
|
||||
|
||||
describe('saveViewport', () => {
|
||||
it('does not save when canvas is unavailable', () => {
|
||||
const store = useSubgraphNavigationStore()
|
||||
const canvas = app.canvas
|
||||
const appWithOptionalCanvas = app as {
|
||||
canvas: typeof app.canvas | undefined
|
||||
}
|
||||
appWithOptionalCanvas.canvas = undefined
|
||||
|
||||
try {
|
||||
store.saveViewport('root')
|
||||
|
||||
expect(store.viewportCache.has(':root')).toBe(false)
|
||||
} finally {
|
||||
appWithOptionalCanvas.canvas = canvas
|
||||
}
|
||||
})
|
||||
|
||||
it('saves viewport state for root graph', () => {
|
||||
const store = useSubgraphNavigationStore()
|
||||
mockCanvas.ds.state.scale = 2
|
||||
@@ -164,6 +182,42 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
})
|
||||
|
||||
describe('restoreViewport', () => {
|
||||
it('does nothing when canvas is unavailable', () => {
|
||||
const store = useSubgraphNavigationStore()
|
||||
const canvas = app.canvas
|
||||
const appWithOptionalCanvas = app as {
|
||||
canvas: typeof app.canvas | undefined
|
||||
}
|
||||
appWithOptionalCanvas.canvas = undefined
|
||||
|
||||
try {
|
||||
store.restoreViewport('root')
|
||||
|
||||
expect(mockSetDirty).not.toHaveBeenCalled()
|
||||
expect(rafCallbacks).toHaveLength(0)
|
||||
} finally {
|
||||
appWithOptionalCanvas.canvas = canvas
|
||||
}
|
||||
})
|
||||
|
||||
it('does not apply cached viewport when canvas disappears', () => {
|
||||
const store = useSubgraphNavigationStore()
|
||||
const canvas = app.canvas
|
||||
const appWithOptionalCanvas = app as {
|
||||
canvas: typeof app.canvas | undefined
|
||||
}
|
||||
store.viewportCache.set(':root', { scale: 2.5, offset: [150, 250] })
|
||||
appWithOptionalCanvas.canvas = undefined
|
||||
|
||||
try {
|
||||
store.restoreViewport('root')
|
||||
|
||||
expect(mockSetDirty).not.toHaveBeenCalled()
|
||||
} finally {
|
||||
appWithOptionalCanvas.canvas = canvas
|
||||
}
|
||||
})
|
||||
|
||||
it('restores cached viewport', () => {
|
||||
const store = useSubgraphNavigationStore()
|
||||
store.viewportCache.set(':root', { scale: 2.5, offset: [150, 250] })
|
||||
@@ -266,7 +320,7 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
expect(mockFitView).toHaveBeenCalledOnce()
|
||||
|
||||
// User navigated away before the inner RAF fired
|
||||
mockCanvas.subgraph = { id: 'different-graph' } as never
|
||||
mockCanvas.subgraph = fromPartial<Subgraph>({ id: 'different-graph' })
|
||||
rafCallbacks[1](performance.now())
|
||||
|
||||
expect(mockRequestSlotSyncAll).not.toHaveBeenCalled()
|
||||
@@ -283,7 +337,7 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
expect(rafCallbacks).toHaveLength(1)
|
||||
|
||||
// Simulate graph switching away before rAF fires
|
||||
mockCanvas.subgraph = { id: 'different-graph' } as never
|
||||
mockCanvas.subgraph = fromPartial<Subgraph>({ id: 'different-graph' })
|
||||
|
||||
rafCallbacks[0](performance.now())
|
||||
|
||||
@@ -296,12 +350,12 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
const store = useSubgraphNavigationStore()
|
||||
const workflowStore = useWorkflowStore()
|
||||
|
||||
const mockRootGraph = {
|
||||
const mockRootGraph = fromPartial<LGraph>({
|
||||
_nodes: [],
|
||||
nodes: [],
|
||||
subgraphs: new Map(),
|
||||
getNodeById: vi.fn()
|
||||
} as Partial<LGraph> as LGraph
|
||||
})
|
||||
const subgraph1 = {
|
||||
id: 'sub1',
|
||||
rootGraph: mockRootGraph,
|
||||
@@ -313,7 +367,7 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
mockCanvas.ds.state.offset = [100, 100]
|
||||
|
||||
// Enter subgraph
|
||||
workflowStore.activeSubgraph = subgraph1 as Partial<Subgraph> as Subgraph
|
||||
workflowStore.activeSubgraph = fromPartial<Subgraph>(subgraph1)
|
||||
await nextTick()
|
||||
|
||||
// Root viewport saved
|
||||
@@ -341,6 +395,23 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
expect(mockCanvas.ds.offset).toEqual([100, 100])
|
||||
})
|
||||
|
||||
it('does not save the outgoing viewport while a workflow switch is blocked', async () => {
|
||||
const store = useSubgraphNavigationStore()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const subgraph = fromPartial<Subgraph>({
|
||||
id: 'sub1',
|
||||
isRootGraph: false,
|
||||
rootGraph: app.rootGraph
|
||||
})
|
||||
|
||||
store.saveCurrentViewport()
|
||||
store.viewportCache.clear()
|
||||
workflowStore.activeSubgraph = subgraph
|
||||
await nextTick()
|
||||
|
||||
expect(store.viewportCache.has(':root')).toBe(false)
|
||||
})
|
||||
|
||||
it('preserves pre-existing cache entries across workflow switches', async () => {
|
||||
const store = useSubgraphNavigationStore()
|
||||
const workflowStore = useWorkflowStore()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { fromAny, fromPartial } from '@total-typescript/shoehorn'
|
||||
import { fromPartial } from '@total-typescript/shoehorn'
|
||||
import type { PartialDeep } from '@total-typescript/shoehorn'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { LGraph, LGraphNode, LLink } from '@/lib/litegraph/src/litegraph'
|
||||
@@ -8,6 +9,7 @@ import type {
|
||||
ISerialisedNode
|
||||
} from '@/lib/litegraph/src/types/serialisation'
|
||||
|
||||
import type { LinkId } from '@/types/linkId'
|
||||
import { toLinkId } from '@/types/linkId'
|
||||
import { toNodeId } from '@/types/nodeId'
|
||||
|
||||
@@ -162,6 +164,26 @@ describe('fixBadLinks', () => {
|
||||
expect(graph.nodes[1]?.inputs?.[0]?.link).toBe(1)
|
||||
})
|
||||
|
||||
it('reports a missing target input link during a dry run', () => {
|
||||
const graph = createGraph({
|
||||
nodes: [
|
||||
createNode({ id: 1, outputs: [createOutput([1])] }),
|
||||
createNode({ id: 2, inputs: [createInput(null)] })
|
||||
],
|
||||
links: [[1, 1, 0, 2, 0, '*']]
|
||||
})
|
||||
|
||||
const result = fixBadLinks(graph)
|
||||
|
||||
expect(result).toMatchObject({
|
||||
hasBadLinks: true,
|
||||
fixed: false,
|
||||
patched: 1,
|
||||
deleted: 0
|
||||
})
|
||||
expect(graph.nodes[1]?.inputs?.[0]?.link).toBeNull()
|
||||
})
|
||||
|
||||
it('removes the origin reference when the target input slot is missing', () => {
|
||||
const graph = createGraph({
|
||||
nodes: [
|
||||
@@ -206,6 +228,53 @@ describe('fixBadLinks', () => {
|
||||
expect(graph.links).toEqual([])
|
||||
})
|
||||
|
||||
it('keeps the later target link when two links target the same input slot', () => {
|
||||
const graph = createGraph({
|
||||
nodes: [
|
||||
createNode({ id: 1, outputs: [createOutput([1])] }),
|
||||
createNode({ id: 2, outputs: [createOutput([2])] }),
|
||||
createNode({ id: 3, inputs: [createInput(null)] })
|
||||
],
|
||||
links: [
|
||||
[1, 1, 0, 3, 0, '*'],
|
||||
[2, 2, 0, 3, 0, '*']
|
||||
]
|
||||
})
|
||||
|
||||
const result = fixBadLinks(graph, { fix: true })
|
||||
|
||||
expect(result).toMatchObject({
|
||||
hasBadLinks: false,
|
||||
fixed: true,
|
||||
deleted: 1
|
||||
})
|
||||
expect(graph.nodes[0]?.outputs?.[0]?.links).toEqual([])
|
||||
expect(graph.nodes[1]?.outputs?.[0]?.links).toEqual([2])
|
||||
expect(graph.nodes[2]?.inputs?.[0]?.link).toBe(2)
|
||||
expect(graph.links).toEqual([[2, 2, 0, 3, 0, '*']])
|
||||
})
|
||||
|
||||
it('reports stale origin references during a dry run', () => {
|
||||
const graph = createGraph({
|
||||
nodes: [
|
||||
createNode({ id: 1, outputs: [createOutput([1])] }),
|
||||
createNode({ id: 2, inputs: [createInput(2)] })
|
||||
],
|
||||
links: [[1, 1, 0, 2, 0, '*']]
|
||||
})
|
||||
|
||||
const result = fixBadLinks(graph)
|
||||
|
||||
expect(result).toMatchObject({
|
||||
hasBadLinks: true,
|
||||
fixed: false,
|
||||
patched: 1,
|
||||
deleted: 1
|
||||
})
|
||||
expect(graph.nodes[0]?.outputs?.[0]?.links).toEqual([1])
|
||||
expect(graph.links).toEqual([[1, 1, 0, 2, 0, '*']])
|
||||
})
|
||||
|
||||
it('cleans dangling references when a linked node is missing', () => {
|
||||
const graph = createGraph({
|
||||
nodes: [createNode({ id: 2, inputs: [createInput(1)] })],
|
||||
@@ -224,6 +293,24 @@ describe('fixBadLinks', () => {
|
||||
expect(graph.links).toEqual([])
|
||||
})
|
||||
|
||||
it('deletes missing-origin links when the target does not reference them', () => {
|
||||
const graph = createGraph({
|
||||
nodes: [createNode({ id: 2, inputs: [createInput(null)] })],
|
||||
links: [[1, 1, 0, 2, 0, '*']]
|
||||
})
|
||||
|
||||
const result = fixBadLinks(graph, { fix: true })
|
||||
|
||||
expect(result).toMatchObject({
|
||||
hasBadLinks: false,
|
||||
fixed: true,
|
||||
patched: 0,
|
||||
deleted: 1
|
||||
})
|
||||
expect(graph.nodes[0]?.inputs?.[0]?.link).toBeNull()
|
||||
expect(graph.links).toEqual([])
|
||||
})
|
||||
|
||||
it('cleans dangling origin references when the target node is missing', () => {
|
||||
const graph = createGraph({
|
||||
nodes: [createNode({ id: 1, outputs: [createOutput([1])] })],
|
||||
@@ -242,6 +329,24 @@ describe('fixBadLinks', () => {
|
||||
expect(graph.links).toEqual([])
|
||||
})
|
||||
|
||||
it('deletes missing-target links when the origin does not reference them', () => {
|
||||
const graph = createGraph({
|
||||
nodes: [createNode({ id: 1, outputs: [createOutput([])] })],
|
||||
links: [[1, 1, 0, 2, 0, '*']]
|
||||
})
|
||||
|
||||
const result = fixBadLinks(graph, { fix: true })
|
||||
|
||||
expect(result).toMatchObject({
|
||||
hasBadLinks: false,
|
||||
fixed: true,
|
||||
patched: 0,
|
||||
deleted: 1
|
||||
})
|
||||
expect(graph.nodes[0]?.outputs?.[0]?.links).toEqual([])
|
||||
expect(graph.links).toEqual([])
|
||||
})
|
||||
|
||||
it('deletes a stale link that neither endpoint references', () => {
|
||||
const graph = createGraph({
|
||||
nodes: [
|
||||
@@ -264,10 +369,10 @@ describe('fixBadLinks', () => {
|
||||
|
||||
it('deletes stale links from live graph link maps', () => {
|
||||
const linkId = toLinkId(1)
|
||||
const originNode = fromAny<LGraphNode, unknown>({
|
||||
const originNode = fromPartial<LGraphNode>({
|
||||
id: toNodeId(1),
|
||||
outputs: [{ links: [linkId] }]
|
||||
})
|
||||
} as PartialDeep<LGraphNode>)
|
||||
const link = fromPartial<LLink>({
|
||||
id: linkId,
|
||||
origin_id: originNode.id,
|
||||
@@ -276,13 +381,14 @@ describe('fixBadLinks', () => {
|
||||
target_slot: 0,
|
||||
type: '*'
|
||||
})
|
||||
const links = new Map([[linkId, link]])
|
||||
const graph = fromAny<LGraph, unknown>({
|
||||
const links = new Map([[linkId, link]]) as Map<LinkId, LLink> &
|
||||
Record<LinkId, LLink>
|
||||
const graph = fromPartial<LGraph>({
|
||||
links,
|
||||
getNodeById: vi.fn((nodeId) =>
|
||||
nodeId === originNode.id ? originNode : null
|
||||
)
|
||||
})
|
||||
} as PartialDeep<LGraph>)
|
||||
|
||||
const result = fixBadLinks(graph, { fix: true, silent: true })
|
||||
|
||||
@@ -317,4 +423,118 @@ describe('fixBadLinks', () => {
|
||||
expect(graph.nodes[0]?.outputs?.[0]?.links).toEqual([1])
|
||||
expect(logger.log).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('creates missing origin output slots in fix mode', () => {
|
||||
const graph = createGraph({
|
||||
nodes: [
|
||||
createNode({ id: 1 }),
|
||||
createNode({ id: 2, inputs: [createInput(1)] })
|
||||
],
|
||||
links: [[1, 1, 0, 2, 0, '*']]
|
||||
})
|
||||
|
||||
const result = fixBadLinks(graph, { fix: true })
|
||||
|
||||
expect(result).toMatchObject({
|
||||
hasBadLinks: false,
|
||||
fixed: true,
|
||||
patched: 1,
|
||||
deleted: 0
|
||||
})
|
||||
expect(graph.nodes[0]?.outputs?.[0]?.links).toEqual([1])
|
||||
})
|
||||
|
||||
it('deletes links whose serialized endpoints are both missing', () => {
|
||||
const graph = createGraph({
|
||||
nodes: [],
|
||||
links: [[1, 1, 0, 2, 0, '*']]
|
||||
})
|
||||
|
||||
const result = fixBadLinks(graph, { fix: true })
|
||||
|
||||
expect(result).toMatchObject({
|
||||
hasBadLinks: false,
|
||||
fixed: true,
|
||||
patched: 0,
|
||||
deleted: 1
|
||||
})
|
||||
expect(graph.links).toEqual([])
|
||||
})
|
||||
|
||||
it('ignores null serialized link entries', () => {
|
||||
const graph = {
|
||||
...createGraph({
|
||||
nodes: [createNode({ id: 1 })],
|
||||
links: []
|
||||
}),
|
||||
links: [null]
|
||||
}
|
||||
|
||||
const result = fixBadLinks(graph, { fix: true })
|
||||
|
||||
expect(result).toMatchObject({
|
||||
hasBadLinks: false,
|
||||
fixed: false,
|
||||
patched: 0,
|
||||
deleted: 0
|
||||
})
|
||||
expect(graph.links).toEqual([])
|
||||
})
|
||||
|
||||
it('deletes object-shaped serialized links', () => {
|
||||
const objectLink = {
|
||||
id: 1,
|
||||
origin_id: 1,
|
||||
origin_slot: 0,
|
||||
target_id: 2,
|
||||
target_slot: 0,
|
||||
type: '*'
|
||||
}
|
||||
const graph = fromPartial<ISerialisedGraph>({
|
||||
...createGraph({
|
||||
nodes: [],
|
||||
links: []
|
||||
})
|
||||
} as PartialDeep<ISerialisedGraph>)
|
||||
Reflect.set(graph, 'links', [objectLink])
|
||||
|
||||
const result = fixBadLinks(graph, { fix: true })
|
||||
|
||||
expect(result).toMatchObject({
|
||||
hasBadLinks: false,
|
||||
fixed: true,
|
||||
patched: 0,
|
||||
deleted: 1
|
||||
})
|
||||
expect(graph.links).toEqual([])
|
||||
})
|
||||
|
||||
it('treats invalid live graph endpoint ids as missing', () => {
|
||||
const linkId = toLinkId(1)
|
||||
const link = fromPartial<LLink>({
|
||||
id: linkId,
|
||||
origin_id: toNodeId(''),
|
||||
origin_slot: 0,
|
||||
target_id: toNodeId(''),
|
||||
target_slot: 0,
|
||||
type: '*'
|
||||
})
|
||||
const links = new Map([[linkId, link]]) as Map<LinkId, LLink> &
|
||||
Record<LinkId, LLink>
|
||||
const graph = fromPartial<LGraph>({
|
||||
links,
|
||||
getNodeById: vi.fn()
|
||||
} as PartialDeep<LGraph>)
|
||||
|
||||
const result = fixBadLinks(graph, { fix: true, silent: true })
|
||||
|
||||
expect(result).toMatchObject({
|
||||
hasBadLinks: false,
|
||||
fixed: true,
|
||||
patched: 0,
|
||||
deleted: 1
|
||||
})
|
||||
expect(graph.getNodeById).not.toHaveBeenCalled()
|
||||
expect(links.has(linkId)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -283,7 +283,9 @@ export function fixBadLinks(
|
||||
return hasAny
|
||||
}
|
||||
|
||||
const links: Array<SerialisedLLinkArray | LLink> = Array.isArray(graph.links)
|
||||
const links: Array<SerialisedLLinkArray | LLink | null> = Array.isArray(
|
||||
graph.links
|
||||
)
|
||||
? graph.links
|
||||
: Array.from((graph as LGraph).links.values())
|
||||
|
||||
|
||||
@@ -269,7 +269,9 @@ export function compressWidgetInputSlots(graph: ISerialisedGraph) {
|
||||
|
||||
for (const [inputIndex, input] of node.inputs?.entries() ?? []) {
|
||||
if (input.link) {
|
||||
const link = graph.links.find((link) => link[0] === input.link)
|
||||
const link = graph.links.find(
|
||||
(link) => link !== null && link[0] === input.link
|
||||
)
|
||||
if (link) {
|
||||
link[4] = inputIndex
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user