Files
wraith_executioner_year_2andCursor 34f88dba6a ✨ Any Split に Send to CPU を追加
分割出力を CPU へ転送する BOOLEAN ウィジェットを追加。旧名 Any List Split の二重登録を削除しノード検索の重複表示を解消。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-15 13:56:41 +09:00

188 lines
5.2 KiB
JavaScript

/** Any Split (misc) */
import { app } from "../../scripts/app.js"
import {
attachStabilizeHooks,
IoDirection,
LENGTH_OUTPUT_NAME,
countPrimitiveDataOutputs,
ensureLengthOutput,
followConnectionUntilType,
formatDataOutputName,
pinWildcardSlot,
scheduleStabilizeRetries,
slotLabelForLinkType,
syncNodeSizeToContent,
} from "./utils.js"
import { computeDesiredDataOutputCount } from "./logic/any-split-layout.js"
import { MISC_GRAPH_RESTORE_WINDOW_MS } from "./logic/restore-window.js"
import { getGraphLink } from "./utils/graph-links.js"
const NODE_CLASS = "Any Split (misc)"
const MIN_DATA_OUTPUTS = 1
const lengthTrailing = {
isMeta: (node) => node.outputs[node.outputs.length - 1]?.name === LENGTH_OUTPUT_NAME,
countData: countPrimitiveDataOutputs,
ensureMeta: ensureLengthOutput,
}
function getSplitInput(node) {
return node.inputs?.find((inp) => inp.name === "*")
}
function addDataOutputBeforeLength(node, num = 1, nodeType = "*") {
lengthTrailing.ensureMeta(node)
for (let n = 0; n < num; n++) {
if (!lengthTrailing.isMeta(node)) {
lengthTrailing.ensureMeta(node)
}
const indexOutput = node.outputs[node.outputs.length - 1]
const savedLengthLinkIds = [...(indexOutput?.links || [])]
node.outputs.pop()
const slotIndex = node.outputs.length + 1
node.addOutput(formatDataOutputName("any", slotIndex), nodeType || "*")
node.addOutput(LENGTH_OUTPUT_NAME, "INT")
const newLengthPos = node.outputs.length - 1
for (const linkId of savedLengthLinkIds) {
const link = getGraphLink(app.graph, linkId)
if (link && link.origin_id === node.id) {
link.origin_slot = newLengthPos
}
}
node.outputs[newLengthPos].type = "INT"
}
}
function renumberDataOutputs(node) {
const end = lengthTrailing.countData(node)
for (let i = 0; i < end; i++) {
node.outputs[i].name = formatDataOutputName("any", i + 1)
}
}
function ensureMinimumDataOutputs(node, minCount, nodeType) {
lengthTrailing.ensureMeta(node)
while (lengthTrailing.countData(node) < minCount) {
addDataOutputBeforeLength(node, 1, nodeType)
}
renumberDataOutputs(node)
}
function removeUnusedDataOutputsFromEnd(node, minCount) {
if (node.removed) {
return
}
lengthTrailing.ensureMeta(node)
const lengthPos = node.outputs.length - 1
for (let i = lengthPos - 1; i >= minCount; i--) {
if (!node.outputs[i]?.links?.length) {
node.removeOutput(i)
continue
}
break
}
renumberDataOutputs(node)
}
function normalizeLengthOutput(node) {
const lengthOut = node.outputs[node.outputs.length - 1]
if (!lengthOut) {
return
}
lengthOut.type = "INT"
lengthOut.name = LENGTH_OUTPUT_NAME
lengthOut.label = LENGTH_OUTPUT_NAME
}
function resolveElementType(node) {
const splitInput = getSplitInput(node)
const inputIndex = splitInput ? node.inputs.indexOf(splitInput) : 0
const connectedType = followConnectionUntilType(node, IoDirection.INPUT, inputIndex, true)
return connectedType?.type || "*"
}
function setupAnySplit(nodeType) {
nodeType.prototype.serialize_widgets = true
attachStabilizeHooks(nodeType, {
configureStabilize: "none",
afterBind(node) {
node._miscSplitRestoringUntil = 0
},
onConfigure(node) {
node._miscSplitRestoringUntil = Date.now() + MISC_GRAPH_RESTORE_WINDOW_MS
node.stabilize()
scheduleStabilizeRetries(node, [80, 200, 500])
},
onConnectionsChange(node, type, _slotIndex, _isConnected, _linkInfo, ioSlot) {
if (ioSlot && node.outputs?.includes(ioSlot)) {
const metaIndex = node.outputs.length - 1
if (node.outputs.indexOf(ioSlot) === metaIndex && ioSlot.name === LENGTH_OUTPUT_NAME) {
return false
}
}
if (type === IoDirection.OUTPUT && ioSlot?.name === LENGTH_OUTPUT_NAME) {
return false
}
},
})
nodeType.prototype.stabilize = function () {
if (this.removed) {
return
}
const splitInput = getSplitInput(this)
pinWildcardSlot(splitInput)
this.nodeType = resolveElementType(this)
lengthTrailing.ensureMeta(this)
const restoring = Date.now() < (this._miscSplitRestoringUntil || 0)
if (!restoring) {
removeUnusedDataOutputsFromEnd(this, MIN_DATA_OUTPUTS)
}
const dataOutputs = this.outputs.slice(0, lengthTrailing.countData(this))
const desired = computeDesiredDataOutputCount(dataOutputs)
ensureMinimumDataOutputs(this, desired, this.nodeType)
const dataCount = lengthTrailing.countData(this)
const primitivesLabel = slotLabelForLinkType(this.nodeType)
for (let i = 0; i < dataCount; i++) {
const output = this.outputs[i]
output.type = this.nodeType
output.label = primitivesLabel ?? output.name
}
normalizeLengthOutput(this)
syncNodeSizeToContent(this)
this.graph?.setDirtyCanvas?.(true, false)
}
}
app.registerExtension({
name: "comfyui-misc.AnySplit",
async beforeRegisterNodeDef(nodeType, nodeData) {
if (nodeData.name !== NODE_CLASS) {
return
}
nodeData.input = nodeData.input ?? {}
nodeData.input.required = {
"*": ["*"],
...(nodeData.input.required ?? {}),
}
nodeData.output = ["*", "INT"]
nodeData.output_name = ["any_01", "length"]
setupAnySplit(nodeType)
},
})