Fix orders and Convert From Windows and Linux Path
This commit is contained in:
+63
-3
@@ -1,3 +1,63 @@
|
||||
export const colors = ["#222222", "#5940bb", "#FFFFFF", "#7cbb1a", "#29699c", "#777788", "#268bd2", "#2ab7ca", "#d33682", "#dc322f", "#facfad","#77ff77", "#5940bb"]
|
||||
export const bg_colors = ["#000000", "#392978", "#89888d", "#496c12", "#19466a", "#4b4b56", "#165481", "#176974", "#851f50", "#911e1c", "#9f826b", "#499f49", "#392978"]
|
||||
export const node_type_list = ["none", "IMAGE", "MASK", "STRING", "INT", "FLOAT", "LATENT", "CLIP", "CONDITIONING", "MODEL", "VAE", "BOOLEAN", "SWITCH"]
|
||||
export const colors = [
|
||||
"#222222",
|
||||
"#5940bb",
|
||||
"#AFFFFF",
|
||||
"#7cbb1a",
|
||||
"#29699c",
|
||||
"#777788",
|
||||
"#268bd2",
|
||||
"#2ab7ca",
|
||||
"#d33682",
|
||||
"#dc322f",
|
||||
"#facfad",
|
||||
"#77ff77",
|
||||
"#6940bb",
|
||||
"#165481",
|
||||
"#176974",
|
||||
"#851f50",
|
||||
"#911e1c",
|
||||
"#9f826b",
|
||||
"#499f49",
|
||||
"#392978",
|
||||
];
|
||||
export const bg_colors = [
|
||||
"#000000",
|
||||
"#392978",
|
||||
"#59888d",
|
||||
"#496c12",
|
||||
"#19466a",
|
||||
"#4b4b56",
|
||||
"#165481",
|
||||
"#176974",
|
||||
"#851f50",
|
||||
"#911e1c",
|
||||
"#9f826b",
|
||||
"#499f49",
|
||||
"#192978",
|
||||
"#065481",
|
||||
"#076974",
|
||||
"#551f50",
|
||||
"#611e1c",
|
||||
"#7f826b",
|
||||
"#199f49",
|
||||
"#092978",
|
||||
];
|
||||
export const node_type_list = [
|
||||
"none",
|
||||
"IMAGE",
|
||||
"MASK",
|
||||
"STRING",
|
||||
"INT",
|
||||
"FLOAT",
|
||||
"LATENT",
|
||||
"CLIP",
|
||||
"CONDITIONING",
|
||||
"MODEL",
|
||||
"VAE",
|
||||
"BOOLEAN",
|
||||
"SWITCH",
|
||||
"AUDIO"
|
||||
];
|
||||
export const string_widget = [
|
||||
"STRING"
|
||||
];
|
||||
|
||||
+439
-404
@@ -1,454 +1,489 @@
|
||||
import { addWidgets, convertToInput, getDefaultOptions } from "./widgets.js";
|
||||
import { string_widget } from "./constants.js";
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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) {
|
||||
//nodeData.input.required = {};
|
||||
export function clearInputs(root_obj, reset_value = true) {
|
||||
//nodeData.input.required = {};
|
||||
|
||||
if (!root_obj.widgets_values) root_obj.widgets_values = [];
|
||||
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);
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
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,2)
|
||||
if(reset_value){
|
||||
for (let key in root_obj.widgets_values) {
|
||||
if (key != "Name" && key != "type"){
|
||||
delete root_obj.widgets_values[key];
|
||||
}
|
||||
}
|
||||
root_obj.widgets = root_obj.widgets.splice(0, 2);
|
||||
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);
|
||||
/*
|
||||
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)){
|
||||
const isoutput = root_obj.outputs.find(i => i.name == value.inputs.Name.value);
|
||||
if (!isoutput){
|
||||
root_obj.addOutput(value.inputs.Name.value, value.inputs.type.value);
|
||||
}
|
||||
export function addOutputs(root_obj, workflow_name) {
|
||||
const outputs = app.lipsync_studio[workflow_name].outputs;
|
||||
for (let [key, value] of Object.entries(outputs)) {
|
||||
const isoutput = root_obj.outputs.find(
|
||||
(i) => i.name == value.inputs.Name.value
|
||||
);
|
||||
if (!isoutput) {
|
||||
root_obj.addOutput(value.inputs.Name.value, value.inputs.type.value);
|
||||
}
|
||||
organizeOutputs(root_obj, workflow_name);
|
||||
}
|
||||
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) => {
|
||||
return {
|
||||
name: input.inputs[0],
|
||||
type: input.inputs[1],
|
||||
value: input.inputs.length > 2 ? input.inputs[2] : undefined,
|
||||
orderIndex: index // Ajouter un index d'ordre basé sur la position
|
||||
};
|
||||
});
|
||||
|
||||
// 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 } = input;
|
||||
let isWidget = node.widgets.find(w => w.name === name);
|
||||
let 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
|
||||
const widget_param = { value, type };
|
||||
addWidgets(node, name, widget_param, app);
|
||||
isWidget = node.widgets.find(w => w.name === name);
|
||||
isWidget.orderIndex = orderIndex; // 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) 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;
|
||||
|
||||
// Vérifier si le type est différent et le mettre à jour
|
||||
if (localType && localType[0] !== type) {
|
||||
if (isinput) {
|
||||
if (isinput.link) {
|
||||
node.graph.removeLink(isinput.link);
|
||||
}
|
||||
isinput.type = type;
|
||||
}
|
||||
|
||||
if (isWidget) {
|
||||
isWidget.type = type;
|
||||
const options = getDefaultOptions(type, value);
|
||||
node.local_input_defs.required[name] = [type, 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;
|
||||
// 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) => {
|
||||
return {
|
||||
name: input.inputs[0],
|
||||
type: input.inputs[1],
|
||||
value: input.inputs.length > 2 ? input.inputs[2] : undefined,
|
||||
orderIndex: index, // Ajouter un index d'ordre basé sur la position
|
||||
};
|
||||
});
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 } = input;
|
||||
let isWidget = node.widgets.find((w) => w.name === name);
|
||||
let 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 };
|
||||
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 (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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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
|
||||
// Rafraîchir le canvas
|
||||
if (node.graph) {
|
||||
node.graph.setDirtyCanvas(false, true);
|
||||
//node.graph.afterChange();
|
||||
//node.graph.connectionChange(node);
|
||||
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
|
||||
const sortedOutputs = Object.entries(outputs)
|
||||
.sort(([, a], [, b]) => a.position - b.position)
|
||||
.map(([, output]) => ({
|
||||
name: output.inputs.Name.value,
|
||||
type: output.inputs.type.value
|
||||
}));
|
||||
|
||||
// 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);
|
||||
}
|
||||
const outputs = app.lipsync_studio[workflow_name].outputs;
|
||||
|
||||
// Extraire et trier les outputs selon leur position
|
||||
const sortedOutputs = Object.entries(outputs)
|
||||
.sort(([, a], [, b]) => a.position - b.position)
|
||||
.map(([, output]) => ({
|
||||
name: output.inputs.Name.value,
|
||||
type: output.inputs.type.value,
|
||||
}));
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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]);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+261
-195
@@ -5,229 +5,295 @@ import { hideWidget } from "./widgets.js";
|
||||
|
||||
// Graph-independent part of initialization (called onNodeCreated)
|
||||
function initialisation_preGraph(node) {
|
||||
node.color = "#004670";
|
||||
node.bgcolor = "#002942";
|
||||
node.color = "#004670";
|
||||
node.bgcolor = "#002942";
|
||||
|
||||
// Basic widget setup that doesn't depend on the graph
|
||||
if (node.widgets && node.widgets[0] && node.widgets[0].options) {
|
||||
if (app && app.lipsync_studio) {
|
||||
node.widgets[0].options.values = ["None", ...Object.keys(app.lipsync_studio)];
|
||||
} else {
|
||||
console.warn("App or lipsync_studio not available during preGraph setup for workflow node.");
|
||||
node.widgets[0].options.values = ["None"];
|
||||
// Basic widget setup that doesn't depend on the graph
|
||||
if (node.widgets && node.widgets[0] && node.widgets[0].options) {
|
||||
if (app && app.lipsync_studio) {
|
||||
node.widgets[0].options.values = [
|
||||
"None",
|
||||
...Object.keys(app.lipsync_studio),
|
||||
];
|
||||
} else {
|
||||
console.warn(
|
||||
"App or lipsync_studio not available during preGraph setup for workflow node."
|
||||
);
|
||||
node.widgets[0].options.values = ["None"];
|
||||
}
|
||||
}
|
||||
|
||||
const isReloading = node.title && node.title.startsWith("Workflow: ");
|
||||
if (!isReloading && node.widgets && node.widgets[0]) {
|
||||
node.widgets[0].value = "None"; // Default to "None"
|
||||
if (node.widgets[1]) {
|
||||
node.widgets[1].value = ""; // Clear workflow JSON
|
||||
}
|
||||
}
|
||||
|
||||
if (node.widgets && node.widgets[1]) {
|
||||
hideWidget(node, node.widgets[1], { holdSpace: false });
|
||||
}
|
||||
|
||||
// Setup the primary widget callback.
|
||||
// Operations inside this callback that need the graph (like cleanInputs, addInputs, addOutputs)
|
||||
// will be guarded by checking node.graph.
|
||||
if (node.widgets && node.widgets[0]) {
|
||||
node.widgets[0].callback = async (value) => {
|
||||
if (!node.graph) {
|
||||
console.warn(
|
||||
"Workflow widget callback triggered, but node.graph is not yet available. Action might be deferred or skipped."
|
||||
);
|
||||
// If critical, you might need a flag to re-process this in onAdded
|
||||
return;
|
||||
}
|
||||
|
||||
cleanInputs(node); // Requires node.graph
|
||||
|
||||
if (value === "None") {
|
||||
node.title = "Workflow (FlowChain ⛓️)";
|
||||
// Further cleanup of dynamic inputs/outputs might be needed here
|
||||
// For example, explicitly removing all but the essential widgets/inputs/outputs.
|
||||
} else if (app && app.lipsync_studio && app.lipsync_studio[value]) {
|
||||
try {
|
||||
const workflowJSON = await importWorkflow(node, value, app); // importWorkflow updates node.title
|
||||
if (node.widgets && node.widgets[1]) {
|
||||
node.widgets[1].value = workflowJSON;
|
||||
}
|
||||
|
||||
// Ensure app.lipsync_studio[value] (and its .inputs) is still valid after await
|
||||
if (app.lipsync_studio[value] && app.lipsync_studio[value].inputs) {
|
||||
const inputs = app.lipsync_studio[value].inputs;
|
||||
addInputs(node, inputs, []); // Requires node.graph
|
||||
addOutputs(node, value); // Requires node.graph
|
||||
fitHeight(node);
|
||||
} else {
|
||||
console.error(
|
||||
"Workflow data or inputs became unavailable after import for:",
|
||||
value
|
||||
);
|
||||
node.title = "Workflow (FlowChain ⛓️)"; // Reset title on error
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Error processing workflow selection in callback:",
|
||||
error
|
||||
);
|
||||
node.title = "Workflow (FlowChain ⛓️)"; // Reset title on error
|
||||
}
|
||||
}
|
||||
|
||||
const isReloading = node.title && node.title.startsWith("Workflow: ");
|
||||
if (!isReloading && node.widgets && node.widgets[0]) {
|
||||
node.widgets[0].value = "None"; // Default to "None"
|
||||
if (node.widgets[1]) {
|
||||
node.widgets[1].value = ""; // Clear workflow JSON
|
||||
}
|
||||
}
|
||||
|
||||
if (node.widgets && node.widgets[1]) {
|
||||
hideWidget(node, node.widgets[1], { holdSpace: false });
|
||||
}
|
||||
|
||||
// Setup the primary widget callback.
|
||||
// Operations inside this callback that need the graph (like cleanInputs, addInputs, addOutputs)
|
||||
// will be guarded by checking node.graph.
|
||||
if (node.widgets && node.widgets[0]) {
|
||||
node.widgets[0].callback = async (value) => {
|
||||
if (!node.graph) {
|
||||
console.warn("Workflow widget callback triggered, but node.graph is not yet available. Action might be deferred or skipped.");
|
||||
// If critical, you might need a flag to re-process this in onAdded
|
||||
return;
|
||||
}
|
||||
|
||||
cleanInputs(node); // Requires node.graph
|
||||
|
||||
if (value === "None") {
|
||||
node.title = "Workflow (FlowChain ⛓️)";
|
||||
// Further cleanup of dynamic inputs/outputs might be needed here
|
||||
// For example, explicitly removing all but the essential widgets/inputs/outputs.
|
||||
} else if (app && app.lipsync_studio && app.lipsync_studio[value]) {
|
||||
try {
|
||||
const workflowJSON = await importWorkflow(node, value, app); // importWorkflow updates node.title
|
||||
if (node.widgets && node.widgets[1]) {
|
||||
node.widgets[1].value = workflowJSON;
|
||||
}
|
||||
|
||||
// Ensure app.lipsync_studio[value] (and its .inputs) is still valid after await
|
||||
if (app.lipsync_studio[value] && app.lipsync_studio[value].inputs) {
|
||||
const inputs = app.lipsync_studio[value].inputs;
|
||||
addInputs(node, inputs, []); // Requires node.graph
|
||||
addOutputs(node, value); // Requires node.graph
|
||||
fitHeight(node);
|
||||
} else {
|
||||
console.error("Workflow data or inputs became unavailable after import for:", value);
|
||||
node.title = "Workflow (FlowChain ⛓️)"; // Reset title on error
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error processing workflow selection in callback:", error);
|
||||
node.title = "Workflow (FlowChain ⛓️)"; // Reset title on error
|
||||
}
|
||||
} else {
|
||||
node.title = "Workflow (FlowChain ⛓️)";
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
node.title = "Workflow (FlowChain ⛓️)";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Graph-dependent part of initialization (called onAdded)
|
||||
function initialisation_onAdded(node) {
|
||||
if (!node.graph) {
|
||||
console.error("CRITICAL: initialisation_onAdded called for workflow node, but node.graph is not set.");
|
||||
return;
|
||||
}
|
||||
if (!node.graph) {
|
||||
console.error(
|
||||
"CRITICAL: initialisation_onAdded called for workflow node, but node.graph is not set."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform an initial cleanInputs now that we are sure the graph exists.
|
||||
// This is important if the node was configured (e.g. from graph load) before being added.
|
||||
cleanInputs(node);
|
||||
// Perform an initial cleanInputs now that we are sure the graph exists.
|
||||
// This is important if the node was configured (e.g. from graph load) before being added.
|
||||
cleanInputs(node);
|
||||
|
||||
// If a workflow was already selected (e.g. loading a saved graph),
|
||||
// ensure its inputs/outputs are correctly set up.
|
||||
// The widget callback might have already run if `configure` set its value.
|
||||
// We re-evaluate or trigger necessary setup steps.
|
||||
if (node.widgets && node.widgets[0]) {
|
||||
const selectedWorkflow = node.widgets[0].value;
|
||||
if (selectedWorkflow && selectedWorkflow !== "None") {
|
||||
if (app && app.lipsync_studio && app.lipsync_studio[selectedWorkflow] && app.lipsync_studio[selectedWorkflow].inputs) {
|
||||
// If workflow JSON is loaded and inputs are known, ensure UI is consistent
|
||||
if (node.widgets[1] && (node.widgets[1].value === "" || typeof node.widgets[1].value !== 'string' || !node.widgets[1].value.startsWith('{'))) {
|
||||
// Workflow selected, but JSON not loaded in widget[1] or inputs/outputs not added by callback yet.
|
||||
// Trigger the callback logic.
|
||||
console.log(`Workflow node ${node.id}: Re-evaluating selected workflow '${selectedWorkflow}' onAdded.`);
|
||||
node.widgets[0].callback(selectedWorkflow);
|
||||
} else {
|
||||
// Workflow JSON likely loaded, ensure inputs/outputs are present
|
||||
// This can be a fallback if the callback didn't fully setup due to timing
|
||||
const inputs = app.lipsync_studio[selectedWorkflow].inputs;
|
||||
const currentWidgetValues = node.widgets_values || []; // from node.configure
|
||||
addInputs(node, inputs, currentWidgetValues);
|
||||
addOutputs(node, selectedWorkflow);
|
||||
removeInputs(node, inputs, currentWidgetValues); // Ensure this logic is sound for onAdded
|
||||
fitHeight(node);
|
||||
}
|
||||
} else if (selectedWorkflow !== "None") {
|
||||
// Workflow selected, but its definition isn't in app.lipsync_studio. Try to load it.
|
||||
console.warn(`Workflow node ${node.id}: '${selectedWorkflow}' selected but not in lipsync_studio. Attempting import via callback.`);
|
||||
node.widgets[0].callback(selectedWorkflow); // This will attempt importWorkflow
|
||||
}
|
||||
// If a workflow was already selected (e.g. loading a saved graph),
|
||||
// ensure its inputs/outputs are correctly set up.
|
||||
// The widget callback might have already run if `configure` set its value.
|
||||
// We re-evaluate or trigger necessary setup steps.
|
||||
if (node.widgets && node.widgets[0]) {
|
||||
const selectedWorkflow = node.widgets[0].value;
|
||||
if (selectedWorkflow && selectedWorkflow !== "None") {
|
||||
if (
|
||||
app &&
|
||||
app.lipsync_studio &&
|
||||
app.lipsync_studio[selectedWorkflow] &&
|
||||
app.lipsync_studio[selectedWorkflow].inputs
|
||||
) {
|
||||
// If workflow JSON is loaded and inputs are known, ensure UI is consistent
|
||||
if (
|
||||
node.widgets[1] &&
|
||||
(node.widgets[1].value === "" ||
|
||||
typeof node.widgets[1].value !== "string" ||
|
||||
!node.widgets[1].value.startsWith("{"))
|
||||
) {
|
||||
// Workflow selected, but JSON not loaded in widget[1] or inputs/outputs not added by callback yet.
|
||||
// Trigger the callback logic.
|
||||
console.log(
|
||||
`Workflow node ${node.id}: Re-evaluating selected workflow '${selectedWorkflow}' onAdded.`
|
||||
);
|
||||
node.widgets[0].callback(selectedWorkflow);
|
||||
} else {
|
||||
node.title = "Workflow (FlowChain ⛓️)";
|
||||
// Workflow JSON likely loaded, ensure inputs/outputs are present
|
||||
// This can be a fallback if the callback didn't fully setup due to timing
|
||||
const inputs = app.lipsync_studio[selectedWorkflow].inputs;
|
||||
const currentWidgetValues = node.widgets_values || []; // from node.configure
|
||||
addInputs(node, inputs, currentWidgetValues);
|
||||
addOutputs(node, selectedWorkflow);
|
||||
removeInputs(node, inputs, currentWidgetValues); // Ensure this logic is sound for onAdded
|
||||
fitHeight(node);
|
||||
}
|
||||
} else if (selectedWorkflow !== "None") {
|
||||
// Workflow selected, but its definition isn't in app.lipsync_studio. Try to load it.
|
||||
console.warn(
|
||||
`Workflow node ${node.id}: '${selectedWorkflow}' selected but not in lipsync_studio. Attempting import via callback.`
|
||||
);
|
||||
node.widgets[0].callback(selectedWorkflow); // This will attempt importWorkflow
|
||||
}
|
||||
} else {
|
||||
node.title = "Workflow (FlowChain ⛓️)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function configure(info) {
|
||||
// `this` is the node. Called when loading graph data.
|
||||
// `info.widgets_values` contains the saved values for widgets.
|
||||
if (!app || !app.lipsync_studio) {
|
||||
console.error("App or lipsync_studio not available during workflow node configure.");
|
||||
return;
|
||||
}
|
||||
// `this` is the node. Called when loading graph data.
|
||||
// `info.widgets_values` contains the saved values for widgets.
|
||||
if (!app || !app.lipsync_studio) {
|
||||
console.error(
|
||||
"App or lipsync_studio not available during workflow node configure."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update widget options first, in case lipsync_studio has changed since last save
|
||||
if (this.widgets && this.widgets[0] && this.widgets[0].options) {
|
||||
this.widgets[0].options.values = ["None", ...Object.keys(app.lipsync_studio)];
|
||||
}
|
||||
// Update widget options first, in case lipsync_studio has changed since last save
|
||||
if (this.widgets && this.widgets[0] && this.widgets[0].options) {
|
||||
this.widgets[0].options.values = [
|
||||
"None",
|
||||
...Object.keys(app.lipsync_studio),
|
||||
];
|
||||
}
|
||||
|
||||
const selectedWorkflowName = info.widgets_values ? info.widgets_values[0] : "None";
|
||||
let selectedWorkflowName = info.widgets_values
|
||||
? info.widgets_values[0]
|
||||
: "None";
|
||||
|
||||
if (this.widgets && this.widgets[0]) {
|
||||
this.widgets[0].value = selectedWorkflowName; // Set the widget value from saved data
|
||||
}
|
||||
if (this.widgets && this.widgets[1] && info.widgets_values && info.widgets_values[1]) {
|
||||
this.widgets[1].value = info.widgets_values[1]; // Set the hidden workflow JSON
|
||||
}
|
||||
if (this.widgets && this.widgets[0]) {
|
||||
this.widgets[0].value = selectedWorkflowName; // Set the widget value from saved data
|
||||
}
|
||||
if (
|
||||
this.widgets &&
|
||||
this.widgets[1] &&
|
||||
info.widgets_values &&
|
||||
info.widgets_values[1]
|
||||
) {
|
||||
this.widgets[1].value = info.widgets_values[1]; // Set the hidden workflow JSON
|
||||
}
|
||||
|
||||
|
||||
if (selectedWorkflowName === "None") {
|
||||
this.title = "Workflow (FlowChain ⛓️)";
|
||||
if (this.graph) { // Only clean if graph is available
|
||||
cleanInputs(this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the node is already on a graph, we can proceed with fuller setup.
|
||||
// If not, onAdded will handle the rest.
|
||||
if (selectedWorkflowName === "None") {
|
||||
this.title = "Workflow (FlowChain ⛓️)";
|
||||
if (this.graph) {
|
||||
if (app.lipsync_studio[selectedWorkflowName] && app.lipsync_studio[selectedWorkflowName].inputs) {
|
||||
this.title = app.lipsync_studio[selectedWorkflowName].title || `Workflow: ${selectedWorkflowName}`;
|
||||
const inputs = app.lipsync_studio[selectedWorkflowName].inputs;
|
||||
addInputs(this, inputs, info.widgets_values || []);
|
||||
addOutputs(this, selectedWorkflowName);
|
||||
removeInputs(this, inputs, info.widgets_values || []);
|
||||
fitHeight(this);
|
||||
} else {
|
||||
// Data not yet in lipsync_studio, try to import.
|
||||
// The callback of widget[0] will handle this if triggered by value change,
|
||||
// or onAdded will pick it up.
|
||||
// For configure, we might just set the title and let onAdded handle full setup.
|
||||
this.title = `Workflow: ${selectedWorkflowName} (loading...)`;
|
||||
// Avoid calling importWorkflow directly here if onAdded will robustly handle it,
|
||||
// to prevent multiple calls.
|
||||
}
|
||||
} else {
|
||||
// Graph not yet available, title will be set, onAdded will do the heavy lifting.
|
||||
this.title = `Workflow: ${selectedWorkflowName}`;
|
||||
// Only clean if graph is available
|
||||
cleanInputs(this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the node is already on a graph, we can proceed with fuller setup.
|
||||
// If not, onAdded will handle the rest.
|
||||
if (this.graph) {
|
||||
let inputs = [];
|
||||
if (selectedWorkflowName in app.lipsync_studio) {
|
||||
inputs = app.lipsync_studio[selectedWorkflowName].inputs;
|
||||
} else {
|
||||
if (selectedWorkflowName.replaceAll("\\", "/") in app.lipsync_studio) {
|
||||
selectedWorkflowName = selectedWorkflowName.replaceAll("\\", "/");
|
||||
info.widgets_values[0] = selectedWorkflowName;
|
||||
this.widgets[0].value = selectedWorkflowName;
|
||||
inputs = app.lipsync_studio[selectedWorkflowName].inputs;
|
||||
} else {
|
||||
selectedWorkflowName = selectedWorkflowName.replaceAll("/", "\\");
|
||||
info.widgets_values[0] = selectedWorkflowName;
|
||||
this.widgets[0].value = selectedWorkflowName;
|
||||
inputs = app.lipsync_studio[selectedWorkflowName].inputs;
|
||||
}
|
||||
}
|
||||
|
||||
if (inputs) {
|
||||
this.title =
|
||||
app.lipsync_studio[selectedWorkflowName].title ||
|
||||
`Workflow: ${selectedWorkflowName}`;
|
||||
//const inputs = app.lipsync_studio[selectedWorkflowName].inputs;
|
||||
addInputs(this, inputs, info.widgets_values || []);
|
||||
addOutputs(this, selectedWorkflowName);
|
||||
removeInputs(this, inputs, info.widgets_values || []);
|
||||
fitHeight(this);
|
||||
} else {
|
||||
// Data not yet in lipsync_studio, try to import.
|
||||
// The callback of widget[0] will handle this if triggered by value change,
|
||||
// or onAdded will pick it up.
|
||||
// For configure, we might just set the title and let onAdded handle full setup.
|
||||
this.title = `Workflow: ${selectedWorkflowName} (loading...)`;
|
||||
// Avoid calling importWorkflow directly here if onAdded will robustly handle it,
|
||||
// to prevent multiple calls.
|
||||
}
|
||||
} else {
|
||||
// Graph not yet available, title will be set, onAdded will do the heavy lifting.
|
||||
this.title = `Workflow: ${selectedWorkflowName}`;
|
||||
}
|
||||
}
|
||||
|
||||
function serialize(info) {
|
||||
// Standard serialization of widget values is usually handled by LiteGraph.
|
||||
// This custom serialize can ensure specific data is captured if needed.
|
||||
// info.widgets_values will be populated by LiteGraph based on current widget values.
|
||||
// Standard serialization of widget values is usually handled by LiteGraph.
|
||||
// This custom serialize can ensure specific data is captured if needed.
|
||||
// info.widgets_values will be populated by LiteGraph based on current widget values.
|
||||
|
||||
// If local_input_defs logic is still needed for type adjustments:
|
||||
if (this.inputs && this.local_input_defs && this.local_input_defs.required) {
|
||||
for (let inp of this.inputs) {
|
||||
if (inp.widget &&
|
||||
this.local_input_defs.required[inp.name] &&
|
||||
this.local_input_defs.required[inp.name][0] !== undefined &&
|
||||
inp.type !== this.local_input_defs.required[inp.name][0]) {
|
||||
inp.type = this.local_input_defs.required[inp.name][0];
|
||||
}
|
||||
}
|
||||
// If local_input_defs logic is still needed for type adjustments:
|
||||
if (this.inputs && this.local_input_defs && this.local_input_defs.required) {
|
||||
for (let inp of this.inputs) {
|
||||
if (
|
||||
inp.widget &&
|
||||
this.local_input_defs.required[inp.name] &&
|
||||
this.local_input_defs.required[inp.name][0] !== undefined &&
|
||||
inp.type !== this.local_input_defs.required[inp.name][0]
|
||||
) {
|
||||
inp.type = this.local_input_defs.required[inp.name][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setupWorkflowNode(nodeType) {
|
||||
const originalOnAdded = nodeType.prototype.onAdded;
|
||||
nodeType.prototype.onAdded = function(graph) {
|
||||
if (originalOnAdded) {
|
||||
originalOnAdded.apply(this, arguments);
|
||||
}
|
||||
initialisation_onAdded(this); // Our graph-dependent setup
|
||||
};
|
||||
const originalOnAdded = nodeType.prototype.onAdded;
|
||||
nodeType.prototype.onAdded = function (graph) {
|
||||
if (originalOnAdded) {
|
||||
originalOnAdded.apply(this, arguments);
|
||||
}
|
||||
initialisation_onAdded(this); // Our graph-dependent setup
|
||||
};
|
||||
|
||||
const originalOnRemoved = nodeType.prototype.onRemoved;
|
||||
nodeType.prototype.onRemoved = function() {
|
||||
// Perform any cleanup specific to this node when removed from graph
|
||||
// For example, disconnecting callbacks or releasing resources
|
||||
if (originalOnRemoved) {
|
||||
originalOnRemoved.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
const originalOnRemoved = nodeType.prototype.onRemoved;
|
||||
nodeType.prototype.onRemoved = function () {
|
||||
// Perform any cleanup specific to this node when removed from graph
|
||||
// For example, disconnecting callbacks or releasing resources
|
||||
if (originalOnRemoved) {
|
||||
originalOnRemoved.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
nodeType.prototype.onNodeCreated = function() {
|
||||
// This is one of the first lifecycle hooks.
|
||||
// Initialize properties that don't depend on the graph.
|
||||
this.local_input_defs = this.local_input_defs || { required: {} };
|
||||
|
||||
// Add essential widgets if not already present (LiteGraph usually handles this from type definition)
|
||||
// For a "COMBO" and a "STRING" (hidden)
|
||||
if (!this.widgets || this.widgets.length < 2) {
|
||||
this.addWidget("combo", "Workflow", "None", () => {}, { values: ["None"] });
|
||||
this.addWidget("string", "workflow_json", "", () => {}, { multiline: true });
|
||||
}
|
||||
|
||||
initialisation_preGraph(this); // Our graph-independent setup
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
// This is one of the first lifecycle hooks.
|
||||
// Initialize properties that don't depend on the graph.
|
||||
this.local_input_defs = this.local_input_defs || { required: {} };
|
||||
|
||||
chainCallback(this, "onConfigure", configure);
|
||||
chainCallback(this, "onSerialize", serialize);
|
||||
};
|
||||
}
|
||||
// Add essential widgets if not already present (LiteGraph usually handles this from type definition)
|
||||
// For a "COMBO" and a "STRING" (hidden)
|
||||
if (!this.widgets || this.widgets.length < 2) {
|
||||
this.addWidget("combo", "Workflow", "None", () => {}, {
|
||||
values: ["None"],
|
||||
});
|
||||
this.addWidget("string", "workflow_json", "", () => {}, {
|
||||
multiline: true,
|
||||
});
|
||||
}
|
||||
|
||||
initialisation_preGraph(this); // Our graph-independent setup
|
||||
|
||||
chainCallback(this, "onConfigure", configure);
|
||||
chainCallback(this, "onSerialize", serialize);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user