Files

167 lines
7.3 KiB
JavaScript

// Copyright 2025 FeiMao-326
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { app } from "../../scripts/app.js";
app.registerExtension({
name: "FeiMao-326.DynamicInputs",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
// --- 1. General API Node (Dynamic Image Inputs) ---
if (nodeData.name === "FeiMao_326_GeneralAPINode") {
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info, slot) {
if (onConnectionsChange) {
onConnectionsChange.apply(this, arguments);
}
if (type !== 1) return;
const imageInputRegex = /^image_(\d+)$/;
const imageInputs = this.inputs.filter(inp => imageInputRegex.test(inp.name));
let maxConnectedIndex = 0;
for (const inp of imageInputs) {
if (inp.link !== null) {
const idx = parseInt(inp.name.match(imageInputRegex)[1]);
if (idx > maxConnectedIndex) maxConnectedIndex = idx;
}
}
const targetCount = Math.max(3, maxConnectedIndex + 1);
const existingIndices = imageInputs.map(inp => parseInt(inp.name.match(imageInputRegex)[1])).sort((a, b) => a - b);
const maxExistingIndex = existingIndices.length > 0 ? existingIndices[existingIndices.length - 1] : 0;
if (maxExistingIndex < targetCount) {
for (let i = maxExistingIndex + 1; i <= targetCount; i++) {
const newName = `image_${i}`;
if (!this.inputs.find(inp => inp.name === newName)) {
this.addInput(newName, "IMAGE");
}
}
this.onResize?.(this.size);
app.canvas.setDirty?.(true);
}
if (maxExistingIndex > targetCount) {
for (let i = maxExistingIndex; i > targetCount; i--) {
const nameToRemove = `image_${i}`;
const inputIndex = this.inputs.findIndex(inp => inp.name === nameToRemove);
if (inputIndex !== -1) {
this.removeInput(inputIndex);
}
}
this.onResize?.(this.size);
app.canvas.setDirty?.(true);
}
};
}
// --- 2. JSON Parser (Dynamic Outputs) ---
if (nodeData.name === "FeiMao_326_JsonParser") {
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info, slot) {
if (onConnectionsChange) {
onConnectionsChange.apply(this, arguments);
}
if (type !== 2) return;
const outputRegex = /^output_(\d+)$/;
const outputs = this.outputs.filter(out => outputRegex.test(out.name));
let maxConnectedIndex = 0;
for (const out of outputs) {
if (out.links && out.links.length > 0) {
const idx = parseInt(out.name.match(outputRegex)[1]);
if (idx > maxConnectedIndex) maxConnectedIndex = idx;
}
}
const targetCount = Math.max(2, maxConnectedIndex + 1);
const MAX_ALLOWED = 50;
const finalTargetCount = Math.min(targetCount, MAX_ALLOWED);
const existingIndices = outputs.map(out => parseInt(out.name.match(outputRegex)[1])).sort((a, b) => a - b);
const maxExistingIndex = existingIndices.length > 0 ? existingIndices[existingIndices.length - 1] : 0;
if (maxExistingIndex < finalTargetCount) {
for (let i = maxExistingIndex + 1; i <= finalTargetCount; i++) {
const newName = `output_${i}`;
if (!this.outputs.find(out => out.name === newName)) {
this.addOutput(newName, "STRING");
}
}
this.onResize?.(this.size);
app.canvas.setDirty?.(true);
}
if (maxExistingIndex > finalTargetCount) {
for (let i = maxExistingIndex; i > finalTargetCount; i--) {
const nameToRemove = `output_${i}`;
const outputIndex = this.outputs.findIndex(out => out.name === nameToRemove);
if (outputIndex !== -1) {
this.removeOutput(outputIndex);
}
}
this.onResize?.(this.size);
}
};
}
// --- 3. Text Template (Variable-based Dynamic Inputs) ---
if (nodeData.name === "FeiMao_326_TextTemplate") {
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function () {
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
const templateWidget = this.widgets.find(w => w.name === "template");
const updateInputs = () => {
const text = templateWidget.value || "";
// Match all {var_name} (Support Unicode/Chinese)
const matches = text.match(/\{([^{}\s]+)\}/g) || [];
const vars = [...new Set(matches.map(m => m.slice(1, -1)))];
// Add new inputs
vars.forEach(v => {
if (!this.inputs || !this.inputs.find(i => i.name === v)) {
this.addInput(v, "*"); // Universal type
}
});
// Remove outdated inputs
if (this.inputs) {
for (let i = this.inputs.length - 1; i >= 0; i--) {
const inputName = this.inputs[i].name;
if (!vars.includes(inputName)) {
this.removeInput(i);
}
}
}
this.onResize?.(this.size);
app.canvas.setDirty?.(true);
};
// Watch for widget changes
templateWidget.callback = updateInputs;
// Initial sync
setTimeout(updateInputs, 100);
return r;
};
}
},
});