|
import { app } from "../../scripts/app.js"; |
|
import { mapLinksToNodes, isOutputInternal, isInputInternal, nodeDefs } from "./nodeMenu.js"; |
|
|
|
|
|
export const nestedNodeType = "NestedNode"; |
|
export const nestedNodeTitle = "Nested Node"; |
|
|
|
const HIDDEN_CONVERTED_TYPE = "hidden-converted-widget"; |
|
const INHERITED_CONVERTED_TYPE = "inherited-converted-widget"; |
|
|
|
export function serializeWorkflow(workflow) { |
|
let nodes = []; |
|
for (const id in workflow) { |
|
const node = workflow[id]; |
|
nodes.push(LiteGraph.cloneObject(node.serialize())); |
|
} |
|
return nodes; |
|
} |
|
|
|
export function arrToIdMap(serialiedWorkflow) { |
|
const result = {}; |
|
for (const node of serialiedWorkflow) { |
|
result[node.id] = node; |
|
} |
|
return result; |
|
} |
|
|
|
export function cleanLinks(serializedWorkflow) { |
|
|
|
const linksMapping = mapLinksToNodes(serializedWorkflow); |
|
const result = structuredClone(serializedWorkflow); |
|
for (const node of result) { |
|
for (const input of node.inputs ?? []) { |
|
const entry = linksMapping[input.link]; |
|
const isLinkWithinWorkflow = entry && entry.srcId && entry.dstId; |
|
if (!isLinkWithinWorkflow) { |
|
|
|
input.link = null; |
|
} |
|
} |
|
for (const output of node.outputs ?? []) { |
|
for (const link of output.links ?? []) { |
|
const entry = linksMapping[link]; |
|
const isLinkWithinWorkflow = entry && entry.srcId && entry.dstId; |
|
if (!isLinkWithinWorkflow) { |
|
|
|
output.links.splice(output.links.indexOf(link), 1); |
|
} |
|
} |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
function averagePos(nodes) { |
|
let x = 0; |
|
let y = 0; |
|
let count = 0; |
|
for (const i in nodes) { |
|
const node = nodes[i]; |
|
x += node.pos[0]; |
|
y += node.pos[1]; |
|
count++; |
|
} |
|
x /= count; |
|
y /= count; |
|
return [x, y]; |
|
} |
|
|
|
export function getRerouteName(rerouteNode) { |
|
const input = rerouteNode.inputs[0]; |
|
const output = rerouteNode.outputs[0]; |
|
return input.label || output.label || output.type; |
|
} |
|
|
|
export class NestedNode { |
|
|
|
get nestedNodes() { |
|
return this.properties.nestedData.nestedNodes; |
|
} |
|
|
|
nestedNodeSetup() { |
|
console.log("[NestedNodeBuilder] Nested node setup") |
|
this.addWidgetListeners(); |
|
this.nestedNodeIdMapping = arrToIdMap(this.nestedNodes); |
|
this.linksMapping = mapLinksToNodes(this.nestedNodes); |
|
|
|
this.inheritRerouteNodeInputs(); |
|
this.inheritRerouteNodeOutputs(); |
|
this.inheritConvertedWidgets(); |
|
this.inheritPrimitiveWidgets(); |
|
this.renameInputs(); |
|
this.resizeNestedNode(); |
|
this.inheritWidgetValues(); |
|
|
|
|
|
const origOnConfigure = this.onConfigure; |
|
this.onConfigure = function () { |
|
const widgets = []; |
|
for (const input of this.inputs ?? []) { |
|
if (input.isInherited || (input.isReroute && input.widget)) { |
|
widgets.push(input.widget); |
|
input.widget = undefined; |
|
} else { |
|
widgets.push(null); |
|
} |
|
} |
|
const r = origOnConfigure ? origOnConfigure.apply(this, arguments) : undefined; |
|
for (let i = 0; i < (this.inputs ?? []).length; i++) { |
|
if (widgets[i]) { |
|
this.inputs[i].widget = widgets[i]; |
|
} |
|
} |
|
return r; |
|
}; |
|
} |
|
|
|
getNumDefinedInputs() { |
|
let num = 0; |
|
for (const input of this.inputs ?? []) { |
|
if (input.widget) { |
|
break; |
|
} |
|
num++; |
|
} |
|
return num; |
|
} |
|
|
|
onAdded() { |
|
if (!this.isSetup) { |
|
this.nestedNodeSetup(); |
|
this.isSetup = true; |
|
} |
|
} |
|
|
|
|
|
nestWorkflow(workflow) { |
|
console.log("[NestedNodeBuilder] Nesting workflow") |
|
|
|
this.properties.nestedData = { nestedNodes: serializeWorkflow(workflow) }; |
|
this.linksMapping = mapLinksToNodes(this.nestedNodes); |
|
this.placeNestedNode(workflow); |
|
|
|
|
|
|
|
this.inheritLinks(); |
|
this.inheritWidgetValues(); |
|
this.removeNestedNodes(workflow); |
|
|
|
} |
|
|
|
|
|
removeNestedNodes(workflow) { |
|
for (const id in workflow) { |
|
const node = workflow[id]; |
|
app.graph.remove(node); |
|
} |
|
} |
|
|
|
|
|
placeNestedNode(workflow) { |
|
this.pos = averagePos(workflow) |
|
} |
|
|
|
|
|
resizeNestedNode() { |
|
this.size = this.computeSize(); |
|
this.size[0] *= 1.5; |
|
} |
|
|
|
renameInputs() { |
|
|
|
|
|
|
|
for (const input of this.inputs ?? []) { |
|
input.name = input.name.replace(/_\d+$/, ''); |
|
} |
|
|
|
for (const widget of this.widgets ?? []) { |
|
widget.name = widget.name.replace(/_\d+$/, ''); |
|
} |
|
} |
|
|
|
inheritPrimitiveWidgets() { |
|
const serialized = this.nestedNodes; |
|
const linksMapping = this.linksMapping; |
|
let widgetIdx = 0; |
|
for (const i in serialized) { |
|
const node = serialized[i]; |
|
|
|
|
|
const tempNode = LiteGraph.createNode(node.type); |
|
if (tempNode !== null && tempNode.type == "PrimitiveNode" && node.outputs[0].links) { |
|
const tempGraph = new LiteGraph.LGraph(); |
|
tempGraph.add(tempNode); |
|
const linkId = node.outputs[0].links[0]; |
|
const entry = linksMapping[linkId]; |
|
const dst = this.nestedNodeIdMapping[entry.dstId]; |
|
const dstNode = LiteGraph.createNode(dst.type); |
|
tempGraph.add(dstNode); |
|
dstNode.configure(dst); |
|
tempNode.configure(node); |
|
|
|
const widget = tempNode.widgets[0]; |
|
delete widget.callback |
|
widget.name = tempNode.title |
|
this.widgets.splice(widgetIdx - 1, 0, widget); |
|
widgetIdx++; |
|
} else { |
|
widgetIdx += (node.widgets_values ?? []).length; |
|
} |
|
} |
|
} |
|
|
|
|
|
inheritWidgetValues() { |
|
const serialized = this.nestedNodes; |
|
this.widgets_values = []; |
|
let widgetIdx = 0; |
|
for (const i in serialized) { |
|
const node = serialized[i]; |
|
|
|
|
|
const tempNode = LiteGraph.createNode(node.type); |
|
for (const j in node.widgets_values) { |
|
|
|
const thisWidget = this.widgets?.[widgetIdx]; |
|
const tempWidget = tempNode?.widgets?.[j]; |
|
|
|
if (!thisWidget || (!tempWidget && tempNode.type !== "PrimitiveNode")) { |
|
continue; |
|
} |
|
|
|
const thisWidgetName = thisWidget?.name.replace(/_\d+$/, ''); |
|
const primitveMatch = node.type === "PrimitiveNode" && thisWidget?.name === node.title; |
|
if (thisWidgetName !== tempWidget?.name && !primitveMatch) { |
|
continue; |
|
} |
|
const widget_value = node.widgets_values[j]; |
|
this.widgets_values.push(widget_value); |
|
this.widgets[widgetIdx].value = widget_value; |
|
widgetIdx++; |
|
} |
|
} |
|
} |
|
|
|
inheritConvertedWidgets() { |
|
const serialized = this.nestedNodes; |
|
const widgetToCount = {}; |
|
const linksMapping = this.linksMapping; |
|
if (!this.widgets || this.widgets.length == 0) { |
|
return; |
|
} |
|
for (const nodeIdx in serialized) { |
|
const node = serialized[nodeIdx]; |
|
for (const inputIdx in node.inputs ?? []) { |
|
const input = node.inputs[inputIdx]; |
|
if (input.widget) { |
|
const count = widgetToCount[input.widget.name]; |
|
const suffix = count ? '_' + count : ''; |
|
const nestedWidgetName = input.widget.name + suffix |
|
for (let widgetIdx = 0; widgetIdx < this.widgets.length; widgetIdx++) { |
|
const widget = this.widgets[widgetIdx]; |
|
const widgetName = widget.name; |
|
|
|
|
|
|
|
if (widget.type === INHERITED_CONVERTED_TYPE || widget.type === HIDDEN_CONVERTED_TYPE || widget.type === CONVERTED_TYPE) { |
|
continue; |
|
} |
|
if (widgetName === nestedWidgetName) { |
|
|
|
const config = getConfig(nodeDefs[node.type], widget); |
|
convertToInput(this, widget, config); |
|
widgetToCount[input.widget.name] = (widgetToCount[input.widget.name] ?? 1) + 1; |
|
|
|
|
|
|
|
if (isInputInternal(node, inputIdx, linksMapping)) { |
|
this.inputs.pop(); |
|
|
|
widget.type = HIDDEN_CONVERTED_TYPE; |
|
} else { |
|
widget.type = INHERITED_CONVERTED_TYPE; |
|
this.inputs.at(-1).isInherited = true; |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
updateSerializedWorkflow() { |
|
|
|
const serialized = this.nestedNodes; |
|
let widgetIdx = 0; |
|
for (const i in serialized) { |
|
const node = serialized[i]; |
|
const tempNode = LiteGraph.createNode(node.type); |
|
for (const j in node.widgets_values) { |
|
|
|
const thisWidget = this.widgets?.[widgetIdx]; |
|
if (!thisWidget) continue; |
|
const tempWidget = tempNode?.widgets?.[j]; |
|
|
|
if (node.type !== "PrimitiveNode") { |
|
|
|
if (!tempWidget) continue; |
|
|
|
const thisWidgetName = thisWidget?.name.replace(/_\d+$/, ''); |
|
if (thisWidgetName !== tempWidget?.name) continue; |
|
node.widgets_values[j] = thisWidget.value; |
|
widgetIdx++; |
|
} else { |
|
|
|
const thisWidgetName = thisWidget?.name.replace(/_\d+$/, ''); |
|
if (thisWidgetName !== node.title) continue; |
|
|
|
node.widgets_values[j] = thisWidget.value; |
|
widgetIdx++; |
|
|
|
break; |
|
} |
|
|
|
} |
|
} |
|
} |
|
|
|
|
|
addWidgetListeners() { |
|
for (const widget of this.widgets ?? []) { |
|
if (widget.inputEl) { |
|
widget.inputEl.addEventListener("change", (e) => { |
|
this.onWidgetChanged(widget.name, widget.value, widget.value, widget); |
|
}); |
|
} |
|
} |
|
} |
|
|
|
|
|
onPropertyChanged(name, value) { |
|
if (name === "serializedWorkflow") { |
|
this.inheritWidgetValues(); |
|
} |
|
} |
|
|
|
onWidgetChanged(name, value, old_value, widget) { |
|
this.updateSerializedWorkflow(); |
|
} |
|
|
|
beforeQueuePrompt() { |
|
this.updateSerializedWorkflow() |
|
} |
|
|
|
insertInput(name, type, index) { |
|
|
|
|
|
|
|
|
|
this.addInput(name, type); |
|
const input = this.inputs.pop(); |
|
this.inputs.splice(index, 0, input); |
|
return input; |
|
} |
|
|
|
insertOutput(name, type, index) { |
|
|
|
|
|
|
|
this.addOutput(name, type); |
|
const output = this.outputs.pop(); |
|
this.outputs.splice(index, 0, output); |
|
return output; |
|
} |
|
|
|
|
|
inheritRerouteNodeInputs() { |
|
|
|
|
|
|
|
let inputIdx = 0; |
|
const serialized = this.nestedNodes; |
|
const linksMapping = this.linksMapping; |
|
for (const node of serialized) { |
|
if (node.type === "Reroute" && !this.inputs?.[inputIdx]?.isReroute && !isInputInternal(node, 0, linksMapping)) { |
|
|
|
const rerouteType = node.outputs[0].type; |
|
const inputName = getRerouteName(node); |
|
const newInput = this.insertInput(inputName, rerouteType, inputIdx); |
|
newInput.isReroute = true; |
|
newInput.widget = node?.inputs?.[0]?.widget; |
|
} |
|
for (let i = 0; i < (node.inputs ?? []).length; i++) { |
|
const isConvertedWidget = !!node.inputs[i].widget; |
|
if (!isInputInternal(node, i, linksMapping) && !isConvertedWidget) inputIdx++; |
|
} |
|
} |
|
} |
|
|
|
inheritRerouteNodeOutputs() { |
|
|
|
|
|
let outputIdx = 0; |
|
const serialized = this.nestedNodes; |
|
const linksMapping = this.linksMapping; |
|
for (const node of serialized) { |
|
if (node.type === "Reroute" && !this.outputs?.[outputIdx]?.isReroute && !isOutputInternal(node, 0, linksMapping)) { |
|
const rerouteType = node.outputs[0].type; |
|
const outputName = getRerouteName(node); |
|
const newOutput = this.insertOutput(outputName, rerouteType, outputIdx); |
|
newOutput.isReroute = true; |
|
} |
|
for (let i = 0; i < (node.outputs ?? []).length; i++) { |
|
if (!isOutputInternal(node, i, linksMapping)) outputIdx++; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
inheritLinks() { |
|
const linksMapping = this.linksMapping; |
|
for (const linkId in linksMapping) { |
|
const entry = linksMapping[linkId]; |
|
if (entry.srcId && entry.dstId) { |
|
continue; |
|
} |
|
const link = app.graph.links[linkId]; |
|
if (entry.dstId) { |
|
|
|
const src = app.graph.getNodeById(link.origin_id); |
|
const dstSlot = this.getNestedInputSlot(entry.dstId, entry.dstSlot); |
|
src.connect(link.origin_slot, this, dstSlot); |
|
} |
|
else if (entry.srcId) { |
|
|
|
const dst = app.graph.getNodeById(link.target_id); |
|
const srcSlot = this.getNestedOutputSlot(entry.srcId, entry.srcSlot); |
|
this.connect(srcSlot, dst, link.target_slot); |
|
} |
|
} |
|
} |
|
|
|
getNestedInputSlot(internalNodeId, internalSlotId) { |
|
|
|
const serialized = this.nestedNodes; |
|
const linksMapping = this.linksMapping; |
|
let slotIdx = 0; |
|
|
|
let convertedSlotIdx = 0; |
|
for (const i in serialized) { |
|
const node = serialized[i]; |
|
const nodeInputs = node.inputs ?? []; |
|
for (let inputIdx = 0; inputIdx < nodeInputs.length; inputIdx++) { |
|
const isConvertedWidget = !!nodeInputs[inputIdx].widget; |
|
const isCorrectSlot = node.id === internalNodeId && inputIdx === internalSlotId; |
|
if (isConvertedWidget) { |
|
if (isCorrectSlot) { |
|
return this.getNumDefinedInputs() + convertedSlotIdx; |
|
} |
|
if (!isInputInternal(node, inputIdx, linksMapping)) { |
|
convertedSlotIdx++; |
|
} |
|
continue; |
|
} |
|
if (isCorrectSlot) { |
|
return slotIdx; |
|
} |
|
if (!isInputInternal(node, inputIdx, linksMapping)) { |
|
slotIdx++; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
getNestedOutputSlot(internalNodeId, internalSlotId) { |
|
|
|
const serialized = this.nestedNodes; |
|
let slotIdx = 0; |
|
|
|
const linksMapping = this.linksMapping; |
|
for (const i in serialized) { |
|
const node = serialized[i]; |
|
if (node.id === internalNodeId) { |
|
let numInternalOutputs = 0; |
|
|
|
for (let j = 0; j < internalSlotId; j++) { |
|
if (isOutputInternal(node, j, linksMapping)) { |
|
numInternalOutputs++; |
|
} |
|
} |
|
return slotIdx + internalSlotId - numInternalOutputs; |
|
} |
|
let numNonInternalOutputs = 0; |
|
for (const j in node.outputs) { |
|
if (!isOutputInternal(node, j, linksMapping)) { |
|
numNonInternalOutputs++; |
|
} |
|
} |
|
slotIdx += numNonInternalOutputs; |
|
} |
|
return null; |
|
} |
|
|
|
unnest() { |
|
const serializedWorkflow = this.nestedNodes; |
|
this.linksMapping = mapLinksToNodes(serializedWorkflow); |
|
const linksMapping = this.linksMapping; |
|
|
|
const nestedNodes = []; |
|
const internalOutputList = []; |
|
const internalInputList = []; |
|
const avgPos = averagePos(serializedWorkflow); |
|
const serializedToNodeMapping = {}; |
|
for (const idx in serializedWorkflow) { |
|
const serializedNode = serializedWorkflow[idx]; |
|
let node = LiteGraph.createNode(serializedNode.type); |
|
let rerouteInputLink = null; |
|
let rerouteOutputLinks = null; |
|
if (node) { |
|
|
|
node.graph = app.graph; |
|
|
|
if (node.type === "Reroute") { |
|
rerouteInputLink = serializedNode.inputs[0].link; |
|
if (serializedNode.outputs[0].links) { |
|
rerouteOutputLinks = serializedNode.outputs[0].links.slice(); |
|
} |
|
serializedNode.inputs[0].link = null; |
|
serializedNode.outputs[0].links = []; |
|
} |
|
} else { |
|
|
|
node = new LiteGraph.LGraphNode(); |
|
node.last_serialization = serializedNode; |
|
node.has_errors = true; |
|
} |
|
|
|
node.configure(serializedNode); |
|
|
|
if (node.type === "Reroute") { |
|
serializedNode.inputs[0].link = rerouteInputLink; |
|
if (rerouteOutputLinks) { |
|
serializedNode.outputs[0].links = rerouteOutputLinks; |
|
} |
|
} |
|
|
|
const dx = serializedNode.pos[0] - avgPos[0]; |
|
const dy = serializedNode.pos[1] - avgPos[1]; |
|
node.pos = [this.pos[0] + dx, this.pos[1] + dy]; |
|
|
|
const isInputsInternal = []; |
|
for (let i = 0; i < (serializedNode.inputs ?? []).length; i++) { |
|
isInputsInternal.push(isInputInternal(serializedNode, i, linksMapping)); |
|
} |
|
internalInputList.push(isInputsInternal); |
|
|
|
const isOutputsInternal = []; |
|
for (const i in serializedNode.outputs) { |
|
const output = serializedNode.outputs[i]; |
|
let isInternal = true; |
|
if (!output.links || output.links.length === 0) { |
|
isInternal = false; |
|
} |
|
for (const link of output.links ?? []) { |
|
const entry = linksMapping[link]; |
|
if (!(entry.srcId && entry.dstId)) { |
|
isInternal = false; |
|
break; |
|
} |
|
} |
|
isOutputsInternal.push(isInternal); |
|
} |
|
internalOutputList.push(isOutputsInternal); |
|
|
|
|
|
for (const i in node.inputs) { |
|
node.inputs[i].link = null; |
|
} |
|
for (const i in node.outputs) { |
|
node.outputs[i].links = []; |
|
} |
|
|
|
app.graph.add(node); |
|
nestedNodes.push(node); |
|
serializedToNodeMapping[serializedNode.id] = node; |
|
} |
|
|
|
|
|
|
|
for (const link in linksMapping) { |
|
const entry = linksMapping[link]; |
|
if (entry && entry.srcId && entry.dstId) { |
|
const src = serializedToNodeMapping[entry.srcId]; |
|
const dst = serializedToNodeMapping[entry.dstId]; |
|
src.connect(entry.srcSlot, dst, entry.dstSlot); |
|
} |
|
} |
|
|
|
|
|
let nestedInputSlot = 0; |
|
let nestedOutputSlot = 0; |
|
let nestedConvertedWidgetSlot = this.getNumDefinedInputs(); |
|
|
|
|
|
for (const i in nestedNodes) { |
|
const node = nestedNodes[i]; |
|
for (let inputSlot = 0; inputSlot < (node.inputs ?? []).length; inputSlot++) { |
|
|
|
if (nestedInputSlot >= (this.inputs ?? []).length) { |
|
break; |
|
} |
|
|
|
if (internalInputList[i][inputSlot]) { |
|
continue; |
|
} |
|
|
|
|
|
let isRerouteMatching = false; |
|
if (node.type === "Reroute") { |
|
const rerouteType = node.__outputType; |
|
isRerouteMatching = rerouteType === this.inputs[nestedInputSlot].type; |
|
isRerouteMatching = isRerouteMatching || rerouteType === undefined; |
|
} |
|
const dstName = node.type === "Reroute" ? getRerouteName(node) : node.title; |
|
let matchingTypes = node.inputs[inputSlot].type === this.inputs[nestedInputSlot].type; |
|
matchingTypes ||= isRerouteMatching; |
|
if (!matchingTypes) { |
|
continue; |
|
} |
|
const link = this.getInputLink(nestedInputSlot); |
|
if (link) { |
|
const originNode = app.graph.getNodeById(link.origin_id); |
|
const srcName = originNode.type === "Reroute" ? getRerouteName(originNode) : originNode.title; |
|
originNode.connect(link.origin_slot, node, inputSlot); |
|
} |
|
nestedInputSlot++; |
|
} |
|
|
|
for (let inputSlot = 0; inputSlot < (node.inputs ?? []).length; inputSlot++) { |
|
|
|
if (nestedConvertedWidgetSlot >= (this.inputs ?? []).length) { |
|
break; |
|
} |
|
if (node.inputs[inputSlot].type !== this.inputs[nestedConvertedWidgetSlot].type) { |
|
continue; |
|
} |
|
|
|
if (internalInputList[i][inputSlot]) { |
|
continue; |
|
} |
|
const link = this.getInputLink(nestedConvertedWidgetSlot); |
|
if (link) { |
|
const originNode = app.graph.getNodeById(link.origin_id); |
|
originNode.connect(link.origin_slot, node, inputSlot); |
|
} |
|
nestedConvertedWidgetSlot++; |
|
} |
|
|
|
for (let outputSlot = 0; outputSlot < (node.outputs ?? []).length; outputSlot++) { |
|
|
|
if (nestedOutputSlot >= (this.outputs ?? []).length) { |
|
break; |
|
} |
|
|
|
|
|
const isWildcardMatching = node.outputs[outputSlot].type === "*" || this.outputs[nestedOutputSlot].type === "*"; |
|
if (!isWildcardMatching && node.outputs[outputSlot].type !== this.outputs[nestedOutputSlot].type) { |
|
continue; |
|
} |
|
|
|
if (internalOutputList[i][outputSlot]) { |
|
continue; |
|
} |
|
|
|
const links = this.getOutputInfo(nestedOutputSlot).links; |
|
const toConnect = []; |
|
for (const linkId of links ?? []) { |
|
const link = app.graph.links[linkId]; |
|
if (link) { |
|
const targetNode = app.graph.getNodeById(link.target_id); |
|
toConnect.push({ node: targetNode, slot: link.target_slot }); |
|
} |
|
} |
|
for (const { node: targetNode, slot: targetSlot } of toConnect) { |
|
node.connect(outputSlot, targetNode, targetSlot); |
|
} |
|
nestedOutputSlot++; |
|
} |
|
} |
|
|
|
|
|
app.graph.remove(graph.getNodeById(this.id)); |
|
|
|
|
|
for (const node of nestedNodes) { |
|
app.canvas.selectNode(node, true); |
|
} |
|
|
|
return nestedNodes; |
|
} |
|
|
|
getConnectedInputNodes() { |
|
const result = []; |
|
for (let inputSlot = 0; inputSlot < (this.inputs ?? []).length; inputSlot++) { |
|
const link = this.getInputLink(inputSlot); |
|
if (link) { |
|
const originNode = app.graph.getNodeById(link.origin_id); |
|
const data = { |
|
node: originNode, |
|
srcSlot: link.origin_slot, |
|
dstSlot: inputSlot, |
|
}; |
|
result.push(data); |
|
} |
|
} |
|
return result; |
|
} |
|
} |
|
|
|
const CONVERTED_TYPE = "converted-widget"; |
|
const VALID_TYPES = ["STRING", "combo", "number"]; |
|
|
|
export function isConvertableWidget(widget, config) { |
|
return VALID_TYPES.includes(widget.type) || VALID_TYPES.includes(config[0]); |
|
} |
|
|
|
function hideWidget(node, widget, suffix = "") { |
|
widget.origType = widget.type; |
|
widget.origComputeSize = widget.computeSize; |
|
widget.origSerializeValue = widget.serializeValue; |
|
widget.computeSize = () => [0, -4]; |
|
widget.type = CONVERTED_TYPE + suffix; |
|
widget.serializeValue = () => { |
|
|
|
const { link } = node.inputs.find((i) => i.widget?.name === widget.name); |
|
if (link == null) { |
|
return undefined; |
|
} |
|
return widget.origSerializeValue ? widget.origSerializeValue() : widget.value; |
|
}; |
|
|
|
|
|
if (widget.linkedWidgets) { |
|
for (const w of widget.linkedWidgets) { |
|
hideWidget(node, w, ":" + widget.name); |
|
} |
|
} |
|
} |
|
|
|
function convertToInput(node, widget, config) { |
|
hideWidget(node, widget); |
|
const { linkType } = getWidgetType(config); |
|
|
|
|
|
const sz = node.size; |
|
node.addInput(widget.name, linkType, { |
|
widget: { name: widget.name, config }, |
|
}); |
|
|
|
|
|
node.setSize([Math.max(sz[0], node.size[0]), Math.max(sz[1], node.size[1])]); |
|
} |
|
|
|
function getWidgetType(config) { |
|
|
|
let type = config[0]; |
|
let linkType = type; |
|
if (type instanceof Array) { |
|
type = "COMBO"; |
|
linkType = linkType.join(","); |
|
} |
|
return { type, linkType }; |
|
} |
|
|
|
function getConfig(nodeData, widget) { |
|
const originalName = widget.name.replace(/_\d+$/, ''); |
|
return nodeData?.input?.required[originalName] || nodeData?.input?.optional?.[originalName] || [widget.type, widget.options || {}]; |
|
} |