Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
5 changes: 4 additions & 1 deletion src/core/graph/subgraph/proxyWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,10 @@ 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?.type === 'button' && widget.disabled) 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
}
}