Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/core/graph/subgraph/proxyWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,11 @@ function resolveLinkedWidget(
const { graph, nodeId, widgetName } = overlay
const n = getNodeByExecutionId(graph, nodeId)
if (!n) return [undefined, undefined]
return [n, n.widgets?.find((w: IBaseWidget) => w.name === widgetName)]
const widget = n.widgets?.find((w: IBaseWidget) => w.name === widgetName)
//Slightly hacky. Force recursive resolution of nested widgets
if (widget instanceof disconnectedWidget.constructor && isProxyWidget(widget))
widget.computedHeight = 20
return [n, widget]
}

function newProxyFromOverlay(subgraphNode: SubgraphNode, overlay: Overlay) {
Expand Down
133 changes: 46 additions & 87 deletions src/lib/litegraph/src/LGraphCanvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ import {
RenderShape,
TitleMode
} from './types/globalEnums'
import type { ClipboardItems, SubgraphIO } from './types/serialisation'
import type {
ClipboardItems,
ISerialisedNode,
SubgraphIO
} from './types/serialisation'
import type { NeverNever, PickNevers } from './types/utility'
import type { IBaseWidget } from './types/widgets'
import { alignNodes, distributeNodes, getBoundaryNodes } from './utils/arrange'
Expand Down Expand Up @@ -1772,47 +1776,24 @@ export class LGraphCanvas
menu: ContextMenu,
node: LGraphNode
): void {
const { graph } = node
if (!graph) throw new NullGraphError()
graph.beforeChange()

const newSelected = new Set<LGraphNode>()

const fApplyMultiNode = function (
node: LGraphNode,
newNodes: Set<LGraphNode>
): void {
if (node.clonable === false) return

const newnode = node.clone()
if (!newnode) return

newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]
if (!node.graph) throw new NullGraphError()

node.graph.add(newnode)
newNodes.add(newnode)
}

const canvas = LGraphCanvas.active_canvas
if (
!canvas.selected_nodes ||
Object.keys(canvas.selected_nodes).length <= 1
) {
fApplyMultiNode(node, newSelected)
} else {
for (const i in canvas.selected_nodes) {
fApplyMultiNode(canvas.selected_nodes[i], newSelected)
}
}
const nodes = canvas.selectedItems.size ? canvas.selectedItems : [node]

if (newSelected.size) {
canvas.selectNodes([...newSelected])
// Find top-left-most boundary
let offsetX = Infinity
let offsetY = Infinity
for (const item of nodes) {
if (item.pos == null)
throw new TypeError(
'Invalid node encountered on clone. `pos` was null.'
)
if (item.pos[0] < offsetX) offsetX = item.pos[0]
if (item.pos[1] < offsetY) offsetY = item.pos[1]
}

graph.afterChange()

canvas.setDirty(true, true)
canvas._deserializeItems(canvas._serializeItems(nodes), {
position: [offsetX + 5, offsetY + 5]
})
}

/**
Expand Down Expand Up @@ -2384,42 +2365,22 @@ export class LGraphCanvas
node &&
this.allow_interaction
) {
let newType = node.type

if (node instanceof SubgraphNode) {
const cloned = node.subgraph.clone().asSerialisable()

const subgraph = graph.createSubgraph(cloned)
subgraph.configure(cloned)
newType = subgraph.id
}

const node_data = node.clone()?.serialize()
if (node_data?.type != null) {
// Ensure the cloned node is configured against the correct type (especially for SubgraphNodes)
node_data.type = newType
const cloned = LiteGraph.createNode(newType)
if (cloned) {
cloned.configure(node_data)
cloned.pos[0] += 5
cloned.pos[1] += 5
const items = this._deserializeItems(this._serializeItems([node]), {
position: node.pos
})
const cloned = items?.created[0] as LGraphNode | undefined
if (!cloned) return

if (this.allow_dragnodes) {
pointer.onDragStart = (pointer) => {
graph.add(cloned, false)
this.#startDraggingItems(cloned, pointer)
}
pointer.onDragEnd = (e) => this.#processDraggedItems(e)
} else {
// TODO: Check if before/after change are necessary here.
graph.beforeChange()
graph.add(cloned, false)
graph.afterChange()
}
cloned.pos[0] += 5
cloned.pos[1] += 5

return
if (this.allow_dragnodes) {
pointer.onDragStart = (pointer) => {
this.#startDraggingItems(cloned, pointer)
}
pointer.onDragEnd = (e) => this.#processDraggedItems(e)
}
return
}

// Node clicked
Expand Down Expand Up @@ -3963,17 +3924,26 @@ export class LGraphCanvas
const { created, nodes, links, reroutes } = results

// const failedNodes: ISerialisedNode[] = []
const subgraphIdMap: Record<string, string> = {}
// SubgraphV2: Remove always-clone behaviour
//Update subgraph ids
for (const subgraphInfo of parsed.subgraphs)
subgraphInfo.id = subgraphIdMap[subgraphInfo.id] = createUuidv4()
const allNodeInfo: ISerialisedNode[] = [
parsed.nodes ? [parsed.nodes] : [],
parsed.subgraphs ? parsed.subgraphs.map((s) => s.nodes ?? []) : []
].flat(2)
for (const nodeInfo of allNodeInfo)
if (nodeInfo.type in subgraphIdMap)
nodeInfo.type = subgraphIdMap[nodeInfo.type]

// Subgraphs
for (const info of parsed.subgraphs) {
// SubgraphV2: Remove always-clone behaviour
const originalId = info.id
info.id = createUuidv4()

const subgraph = graph.createSubgraph(info)
subgraph.configure(info)
results.subgraphs.set(originalId, subgraph)
results.subgraphs.set(info.id, subgraph)
}
for (const info of parsed.subgraphs)
results.subgraphs.get(info.id)?.configure(info)

// Groups
for (const info of parsed.groups) {
Expand All @@ -3985,17 +3955,6 @@ export class LGraphCanvas
created.push(group)
}

// Update subgraph ids with nesting
function updateSubgraphIds(nodes: { type: string }[]) {
for (const info of nodes) {
const subgraph = results.subgraphs.get(info.type)
if (!subgraph) continue
info.type = subgraph.id
updateSubgraphIds(subgraph.nodes)
}
}
updateSubgraphIds(parsed.nodes)

// Nodes
for (const info of parsed.nodes) {
const node = info.type == null ? null : LiteGraph.createNode(info.type)
Expand Down
13 changes: 13 additions & 0 deletions src/lib/litegraph/src/subgraph/SubgraphNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -618,4 +618,17 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
// Call parent serialize method
return super.serialize()
}
override clone() {
const clone = super.clone()
// force reasign so domWidgets reset ownership
// eslint-disable-next-line no-self-assign
this.properties.proxyWidgets = this.properties.proxyWidgets

//TODO: Consider deep cloning subgraphs here.
//It's the safest place to prevent creation of linked subgraphs
//But the frequency of clone().serialize() calls is likely to result in
//pollution of rootGraph.subgraphs

return clone
}
}