570 lines
19 KiB
JavaScript
570 lines
19 KiB
JavaScript
import { addWidgets, convertToInput, getDefaultOptions } from "./widgets.js";
|
|
import { string_widget } from "./constants.js";
|
|
|
|
export function createColor(type, bg = false) {
|
|
// Generate a consistent color based on the type string
|
|
const hash = type.split("").reduce((acc, char) => {
|
|
return char.charCodeAt(0) + ((acc << 5) - acc);
|
|
}, 0);
|
|
|
|
// Convert hash to RGB values
|
|
let r = (hash & 0xff0000) >> 16;
|
|
let g = (hash & 0x00ff00) >> 8;
|
|
let b = hash & 0x0000ff;
|
|
|
|
// For background colors, make them lighter
|
|
// For background colors, make them darker by reducing RGB values
|
|
|
|
if (bg) {
|
|
const darkenFactor = 0.7; // Adjust this value to control darkness
|
|
r = Math.floor(r * darkenFactor);
|
|
g = Math.floor(g * darkenFactor);
|
|
b = Math.floor(b * darkenFactor);
|
|
return `#${r.toString(16).padStart(2, "0")}${g
|
|
.toString(16)
|
|
.padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
|
} else {
|
|
return `#${r.toString(16).padStart(2, "0")}${g
|
|
.toString(16)
|
|
.padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
|
}
|
|
}
|
|
|
|
export function cleanInputs(root_obj, start_index = 2, reset_value = true) {
|
|
for (let i = 0; i < root_obj.outputs.length; i++) {
|
|
const output = root_obj.outputs[i];
|
|
if (output.links && output.links.length) {
|
|
// Make a copy of the links array because it will be modified during disconnection
|
|
const links = output.links.slice();
|
|
for (const linkId of links) {
|
|
root_obj.graph.removeLink(linkId);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Same for inputs
|
|
for (let i = 0; i < root_obj.inputs.length; i++) {
|
|
const input = root_obj.inputs[i];
|
|
if (input.link) {
|
|
root_obj.graph.removeLink(input.link);
|
|
}
|
|
}
|
|
|
|
root_obj.widgets = root_obj.widgets.splice(0, start_index);
|
|
if (reset_value) {
|
|
root_obj.widgets_values = [];
|
|
const max_node_output = root_obj.outputs.length;
|
|
for (let i = 0; i < max_node_output; i++) root_obj.removeOutput(0);
|
|
}
|
|
|
|
const max_node_input = root_obj.inputs.length;
|
|
for (let i = 0; i < max_node_input; i++) root_obj.removeInput(0);
|
|
}
|
|
|
|
export function clearInputs(root_obj, reset_value = true, splice = 2) {
|
|
//nodeData.input.required = {};
|
|
|
|
if (!root_obj.widgets_values) root_obj.widgets_values = [];
|
|
|
|
// Déconnecter tous les liens d'abord pour les entrées
|
|
for (let i = 0; i < root_obj.outputs.length; i++) {
|
|
const output = root_obj.outputs[i];
|
|
if (output.links && output.links.length) {
|
|
const links = output.links.slice();
|
|
for (const linkId of links) {
|
|
root_obj.graph.removeLink(linkId);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parcourir les entrées en sens inverse pour éviter les problèmes d'indice
|
|
for (let i = root_obj.inputs.length - 1; i >= 0; i--) {
|
|
const input = root_obj.inputs[i];
|
|
if ((input.name === "default" || input.name === "input") && input.link) {
|
|
root_obj.graph.removeLink(input.link);
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < root_obj.inputs.length; i++) {
|
|
if (
|
|
root_obj.inputs[i].name == "default" ||
|
|
root_obj.inputs[i].name == "input"
|
|
) {
|
|
root_obj.removeInput(i);
|
|
}
|
|
}
|
|
|
|
root_obj.widgets = root_obj.widgets.splice(0, splice);
|
|
if (reset_value) {
|
|
for (let key in root_obj.widgets_values) {
|
|
if (key != "Name" && key != "type") {
|
|
delete root_obj.widgets_values[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
const max_node_input = root_obj.outputs.length;
|
|
for (let i = root_obj.outputs.length - 1; i >= 0; i--)
|
|
root_obj.removeOutput(i);
|
|
/*
|
|
if (root_obj.graph) {
|
|
root_obj.graph.setDirtyCanvas(true);
|
|
root_obj.graph.change();
|
|
}*/
|
|
}
|
|
|
|
export function addOutputs(root_obj, workflow_name) {
|
|
const outputs = app.lipsync_studio[workflow_name].outputs;
|
|
for (let [key, value] of Object.entries(outputs)) {
|
|
let name = "";
|
|
let type = "";
|
|
if (value.inputs.Name) {
|
|
name = value.inputs.Name.value;
|
|
type = value.inputs.type.value;
|
|
} else {
|
|
name = value.inputs[0];
|
|
type = value.inputs[1];
|
|
}
|
|
|
|
const isoutput = root_obj.outputs.find((i) => i.name == name);
|
|
if (!isoutput) {
|
|
root_obj.addOutput(name, type);
|
|
}
|
|
}
|
|
// remove out not in outputs
|
|
const all_outputs = Object.entries(outputs).map(([key, value]) => value);
|
|
for (let i = 0; i < root_obj.outputs.length; i++) {
|
|
if (!all_outputs.find((out) => out.inputs[0] == root_obj.outputs[i].name)) {
|
|
root_obj.removeOutput(i);
|
|
}
|
|
}
|
|
organizeOutputs(root_obj, workflow_name);
|
|
}
|
|
|
|
export function addInputs(node, inputs, widgets_values) {
|
|
// Phase 1: Préparer les données triées et simplifiées
|
|
const mapped_input = Object.entries(inputs)
|
|
.sort(([, a], [, b]) => a.position - b.position)
|
|
.map(([, input], index) => {
|
|
let val = "";
|
|
let type = input.inputs[1];
|
|
if (input.inputs[2]?.values?.length > 0) {
|
|
type = "COMBO";
|
|
val = input.inputs[1];
|
|
} else {
|
|
val = input.inputs.length > 2 ? input.inputs[2] : undefined;
|
|
}
|
|
return {
|
|
name: input.inputs[0],
|
|
type: type,
|
|
value: val,
|
|
orderIndex: index, // Ajouter un index d'ordre basé sur la position
|
|
options: type == "COMBO" ? input.inputs[2] : undefined,
|
|
};
|
|
});
|
|
|
|
// Séparer les inputs pour un traitement différencié
|
|
const widget_inputs = mapped_input.filter(
|
|
(input) => input.value !== undefined && input.value !== null
|
|
);
|
|
// replace value in widget_inputs with the value in widgets_values
|
|
if (widgets_values) {
|
|
for (let i = 0; i < widget_inputs.length; i++) {
|
|
const widget_input = widget_inputs[i];
|
|
const widget_value = widgets_values[2 + i];
|
|
//find name in mapped_input and replace value
|
|
if (widget_value)
|
|
for (let j = 0; j < mapped_input.length; j++)
|
|
if (mapped_input[j].name == widget_input.name) {
|
|
mapped_input[j].value = widget_value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const pure_inputs = mapped_input.filter(
|
|
(input) => input.value === undefined || input.value === null
|
|
);
|
|
|
|
// S'assurer que le nœud a un objet local_input_defs
|
|
if (!node.local_input_defs) {
|
|
node.local_input_defs = {
|
|
required: {},
|
|
optional: {},
|
|
};
|
|
}
|
|
|
|
// Phase 2: Ajouter les nouveaux widgets/inputs avec leur index d'ordre
|
|
for (const input of mapped_input) {
|
|
const { name, type, value, orderIndex, options } = input;
|
|
let isWidget = node.widgets.find((w) => w.name === name);
|
|
let isinput = node.inputs.filter((i) => i.name === name);
|
|
|
|
const seen = {};
|
|
const indexesToRemove = [];
|
|
node.inputs.forEach((input, idx) => {
|
|
if (seen[input.name] !== undefined) {
|
|
indexesToRemove.push(idx);
|
|
} else {
|
|
seen[input.name] = idx;
|
|
}
|
|
});
|
|
if (node.graph._nodes){
|
|
for (let i = indexesToRemove.length - 1; i >= 0; i--) {
|
|
node.removeInput(indexesToRemove[i]);
|
|
}
|
|
}
|
|
isinput = node.inputs.find((i) => i.name === name);
|
|
|
|
if (!isWidget && (!isinput || isinput.widget)) {
|
|
if (value !== undefined) {
|
|
// Ajouter un widget avec sa valeur et son index d'ordre
|
|
let widget_param = { value, type, options };
|
|
if (string_widget.includes(type))
|
|
widget_param = { value, type: "STRING" };
|
|
addWidgets(node, name, widget_param, app);
|
|
isWidget = node.widgets.find((w) => w.name === name);
|
|
if (isWidget) {
|
|
if (orderIndex !== undefined) isWidget.orderIndex = orderIndex; // Stocker l'index d'ordre// Stocker l'index d'ordre
|
|
}
|
|
|
|
if (isinput && !isinput.widget) {
|
|
convertToInput(node, isWidget);
|
|
// Préserver l'index d'ordre sur l'input converti
|
|
const newInput = node.inputs.find((i) => i.name === name);
|
|
if (newInput && orderIndex !== undefined)
|
|
newInput.orderIndex = orderIndex;
|
|
newInput.pos = isWidget.pos;
|
|
}
|
|
} else {
|
|
// C'est un input pur
|
|
if (!isinput) {
|
|
node.addInput(name, type);
|
|
const newInput = node.inputs[node.inputs.length - 1];
|
|
newInput.orderIndex = orderIndex; // Stocker l'index d'ordre
|
|
}
|
|
}
|
|
} else {
|
|
// Mettre à jour l'index d'ordre des éléments existants
|
|
if (isWidget) isWidget.orderIndex = orderIndex;
|
|
if (isinput) isinput.orderIndex = orderIndex;
|
|
}
|
|
}
|
|
|
|
// Phase 3: Mettre à jour les types si nécessaires
|
|
for (const input of mapped_input) {
|
|
const { name, type, value } = input;
|
|
let isWidget = node.widgets.find((w) => w.name === name);
|
|
let isinput = node.inputs.find((i) => i.name === name);
|
|
|
|
if (isWidget || isinput) {
|
|
const localType = node.local_input_defs?.required[name] || null;
|
|
const typeToUse = string_widget.includes(type) ? "STRING" : type;
|
|
// Vérifier si le type est différent et le mettre à jour
|
|
if (localType && localType[0] !== typeToUse) {
|
|
if (isinput) {
|
|
if (isinput.link) {
|
|
node.graph.removeLink(isinput.link);
|
|
}
|
|
isinput.type = typeToUse;
|
|
}
|
|
|
|
if (isWidget) {
|
|
isWidget.type = typeToUse;
|
|
const options = getDefaultOptions(typeToUse, value);
|
|
node.local_input_defs.required[name] = [typeToUse, options];
|
|
isWidget.options = options;
|
|
isWidget.value = value !== undefined ? value : isWidget.value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Phase 4: Réorganiser les widgets selon leur index d'ordre
|
|
// Séparer les widgets système (les 2 premiers) des widgets à trier
|
|
const systemWidgets = node.widgets.slice(0, 2);
|
|
const sortableWidgets = node.widgets.slice(2);
|
|
|
|
// Trier les widgets par index d'ordre
|
|
sortableWidgets.sort((a, b) => {
|
|
if (a.orderIndex === undefined) return 1;
|
|
if (b.orderIndex === undefined) return -1;
|
|
return a.orderIndex - b.orderIndex;
|
|
});
|
|
|
|
// Reconstruire le tableau des widgets
|
|
node.widgets = [...systemWidgets, ...sortableWidgets];
|
|
|
|
// Maintenant ajuster les positions visuelles des widgets
|
|
for (let i = 2; i < node.widgets.length; i++) {
|
|
node.widgets[i].y =
|
|
node.widgets[i - 1].y +
|
|
(node.widgets[i - 1].computedHeight || LiteGraph.NODE_WIDGET_HEIGHT);
|
|
node.widgets[i].last_y = node.widgets[i].y;
|
|
}
|
|
|
|
// Réorganiser les inputs purs
|
|
for (let i = 0; i < pure_inputs.length; i++) {
|
|
const targetInput = pure_inputs[i];
|
|
const actualIndex = node.inputs.findIndex(
|
|
(input) => input.name === targetInput.name
|
|
);
|
|
|
|
if (actualIndex !== -1) {
|
|
// const actualIndex = node.inputs.indexOf(pureNodeInputs[inputIndex]);
|
|
const expected_position = i;
|
|
|
|
if (actualIndex !== expected_position) {
|
|
// Déconnecter temporairement le lien s'il existe
|
|
let link = null;
|
|
if (node.inputs[actualIndex].link) {
|
|
link = node.graph.links[node.inputs[actualIndex].link];
|
|
node.graph.removeLink(node.inputs[actualIndex].link);
|
|
}
|
|
|
|
// Sauvegarder les propriétés importantes
|
|
const inputToMove = node.inputs[actualIndex];
|
|
const inputName = inputToMove.name;
|
|
const inputType = inputToMove.type;
|
|
const inputWidget = inputToMove.widget;
|
|
const inputPos = inputToMove.pos;
|
|
|
|
// Supprimer l'input
|
|
node.removeInput(actualIndex);
|
|
|
|
// Ajouter à la position attendue
|
|
node.addInput(
|
|
inputName,
|
|
inputType,
|
|
{ pos: inputPos, widget: inputWidget },
|
|
expected_position
|
|
);
|
|
|
|
// Reconnecter le lien si nécessaire
|
|
if (link && link.origin_id !== null) {
|
|
const newIndex = node.inputs.findIndex(
|
|
(input) => input.name === targetInput.name
|
|
);
|
|
link.target_slot = newIndex;
|
|
node.graph.links[link.id] = link;
|
|
node.inputs[newIndex].link = link.id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Phase 6: S'assurer que les inputs avec widget sont toujours à la fin de la liste
|
|
const inputsWithWidgets = [];
|
|
const inputsWithoutWidgets = [];
|
|
|
|
// 1. Trier les inputs en deux groupes
|
|
for (let i = 0; i < node.inputs.length; i++) {
|
|
if (node.inputs[i].widget) {
|
|
inputsWithWidgets.push(i);
|
|
} else {
|
|
inputsWithoutWidgets.push(i);
|
|
}
|
|
}
|
|
|
|
// 2. Si des inputs avec widgets ne sont pas déjà à la fin, les réorganiser
|
|
if (
|
|
inputsWithWidgets.length > 0 &&
|
|
inputsWithWidgets[0] < inputsWithoutWidgets[inputsWithoutWidgets.length - 1]
|
|
) {
|
|
// Déplacer tous les inputs avec widgets à la fin
|
|
for (let i = 0; i < inputsWithWidgets.length; i++) {
|
|
const currentIndex = inputsWithWidgets[i] - i; // Ajuster l'index car la liste change à chaque suppression
|
|
const currentWidget = node.inputs[currentIndex];
|
|
// Sauvegarder les informations de l'input et son lien
|
|
let link = null;
|
|
if (currentWidget.link) {
|
|
let link = null;
|
|
if (currentWidget.link) {
|
|
link = node.graph.links[currentWidget.link];
|
|
node.graph.removeLink(currentWidget.link);
|
|
}
|
|
}
|
|
|
|
// Sauvegarder les propriétés importantes
|
|
const inputToMove = currentWidget;
|
|
const inputName = inputToMove.name;
|
|
const inputType = inputToMove.type;
|
|
const inputWidget = inputToMove.widget;
|
|
const inputPos = inputToMove.pos;
|
|
|
|
node.removeInput(currentIndex);
|
|
node.addInput(inputName, inputType, {
|
|
pos: inputPos,
|
|
widget: inputWidget,
|
|
});
|
|
|
|
// Reconnecter le lien si nécessaire
|
|
if (link && link.origin_id !== null) {
|
|
const newIndex = node.inputs.findIndex(
|
|
(input) => input.name === inputName
|
|
);
|
|
node.graph.links[link.id].target_slot = newIndex;
|
|
node.inputs[newIndex].link = link.id;
|
|
}
|
|
/*if (link && link.origin_id !== null) {
|
|
const newIndex = node.inputs.findIndex(input => input.name === inputName);
|
|
link.target_slot = newIndex;
|
|
node.graph.links[link.id] = link;
|
|
node.inputs[newIndex].link = link.id;
|
|
}*/
|
|
}
|
|
|
|
// Rafraîchir le canvas
|
|
/*
|
|
if (node.graph) {
|
|
node.graph.setDirtyCanvas(true);
|
|
}*/
|
|
}
|
|
|
|
// Rafraîchir le canvas si nécessaire
|
|
if (node.graph) {
|
|
node.graph.setDirtyCanvas(false, true);
|
|
//node.graph.afterChange();
|
|
//node.graph.connectionChange(node);
|
|
}
|
|
}
|
|
|
|
function organizeOutputs(node, workflow_name) {
|
|
const outputs = app.lipsync_studio[workflow_name].outputs;
|
|
|
|
// Extraire et trier les outputs selon leur position
|
|
let sortedOutputs;
|
|
sortedOutputs = Object.entries(outputs)
|
|
.sort(([, a], [, b]) => a.position - b.position)
|
|
.map(([, output]) => {
|
|
let name = "";
|
|
let type = "";
|
|
if (output.inputs.length === undefined) {
|
|
name = output.inputs.Name.value;
|
|
type = output.inputs.type.value;
|
|
} else {
|
|
name = output.inputs[0];
|
|
type = output.inputs[1];
|
|
}
|
|
return { name, type };
|
|
});
|
|
// Réorganiser les outputs selon l'ordre déterminé
|
|
for (let i = 0; i < sortedOutputs.length; i++) {
|
|
const targetOutput = sortedOutputs[i];
|
|
const actualIndex = node.outputs.findIndex(
|
|
(output) => output.name === targetOutput.name
|
|
);
|
|
|
|
if (actualIndex !== -1 && actualIndex !== i) {
|
|
// Sauvegarder les liens existants
|
|
let links = [];
|
|
if (
|
|
node.outputs[actualIndex].links &&
|
|
node.outputs[actualIndex].links.length > 0
|
|
) {
|
|
for (const linkId of node.outputs[actualIndex].links) {
|
|
const linkInfo = node.graph.links[linkId];
|
|
if (linkInfo) {
|
|
links.push({
|
|
id: linkId,
|
|
origin_id: linkInfo.origin_id,
|
|
origin_slot: linkInfo.origin_slot,
|
|
target_id: linkInfo.target_id,
|
|
target_slot: linkInfo.target_slot,
|
|
});
|
|
// Déconnecter temporairement
|
|
node.graph.removeLink(linkId);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sauvegarder les propriétés importantes
|
|
const outputToMove = node.outputs[actualIndex];
|
|
const outputName = outputToMove.name;
|
|
const outputType = outputToMove.type;
|
|
|
|
// Supprimer l'output
|
|
node.removeOutput(actualIndex);
|
|
|
|
// Ajouter à la position attendue
|
|
node.addOutput(outputName, outputType, i);
|
|
const newOutput = node.outputs.findIndex(
|
|
(output) => output.name === outputName
|
|
);
|
|
|
|
// Reconnecter les liens sauvegardés
|
|
for (const link of links) {
|
|
const targetNode = node.graph.getNodeById(link.target_id);
|
|
node.connect(newOutput, targetNode, link.target_slot);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Rafraîchir le canvas
|
|
if (node.graph) {
|
|
node.graph.setDirtyCanvas(true);
|
|
}
|
|
}
|
|
|
|
export function removeInputs(node, inputs, widgets_values) {
|
|
// Ensemble des noms d'entrées valides à partir de inputs
|
|
const validInputNames = new Set();
|
|
for (let [key, value] of Object.entries(inputs)) {
|
|
if (value.inputs && value.inputs[0]) {
|
|
validInputNames.add(value.inputs[0]);
|
|
}
|
|
}
|
|
|
|
// Noms à préserver quoi qu'il arrive
|
|
const preserveNames = new Set(["workflows", "workflow"]);
|
|
|
|
// Collecter les noms d'entrées actuelles
|
|
const currentInputNames = new Set();
|
|
for (let input of node.inputs)
|
|
if (!preserveNames.has(input.name)) currentInputNames.add(input.name);
|
|
|
|
// Collecter les noms de widgets actuels (à partir de l'index 2)
|
|
const currentWidgetNames = new Set();
|
|
for (let i = 2; i < node.widgets.length; i++)
|
|
if (!preserveNames.has(node.widgets[i].name))
|
|
currentWidgetNames.add(node.widgets[i].name);
|
|
|
|
// Identifier les éléments à supprimer (ceux qui sont dans current mais pas dans valid)
|
|
const inputsToRemove = [...currentInputNames].filter(
|
|
(name) => !validInputNames.has(name)
|
|
);
|
|
const widgetsToRemove = [...currentWidgetNames].filter(
|
|
(name) => !validInputNames.has(name)
|
|
);
|
|
|
|
// Supprimer les entrées identifiées (en parcourant en sens inverse)
|
|
if (inputsToRemove.length > 0)
|
|
for (let i = node.inputs.length - 1; i >= 0; i--)
|
|
if (inputsToRemove.includes(node.inputs[i].name)) {
|
|
// Déconnecter le lien s'il existe
|
|
if (node.inputs[i].link != null)
|
|
node.graph.removeLink(node.inputs[i].link);
|
|
node.removeInput(i);
|
|
}
|
|
|
|
// Supprimer les widgets identifiés (en parcourant en sens inverse)
|
|
if (widgetsToRemove.length > 0)
|
|
for (let i = node.widgets.length - 1; i >= 2; i--)
|
|
if (widgetsToRemove.includes(node.widgets[i].name)) {
|
|
widgetName = node.widgets[i].name;
|
|
node.widgets.splice(i, 1);
|
|
widgets_values.splice(i, 1);
|
|
if (
|
|
node.local_input_defs &&
|
|
node.local_input_defs.required[widgetName]
|
|
) {
|
|
delete node.local_input_defs.required[widgetName];
|
|
}
|
|
// change y and last_y of widgets
|
|
for (let j = i; j < node.widgets.length; j++) {
|
|
node.widgets[j].y -= node.widgets[j].computedHeight;
|
|
node.widgets[j].last_y -= node.widgets[j].computedHeight;
|
|
}
|
|
}
|
|
}
|