mirror of
https://github.com/justUmen/Bjornulf_custom_nodes.git
synced 2026-03-21 12:42:11 -03:00
0.59
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# 🔗 Comfyui : Bjornulf_custom_nodes v0.58 🔗
|
||||
# 🔗 Comfyui : Bjornulf_custom_nodes v0.59 🔗
|
||||
|
||||
A list of 68 custom nodes for Comfyui : Display, manipulate, and edit text, images, videos, loras and more.
|
||||
You can manage looping operations, generate randomized content, trigger logical conditions, pause and manually control your workflows and even work with external AI tools, like Ollama or Text To Speech.
|
||||
@@ -284,6 +284,7 @@ cd /where/you/installed/ComfyUI && python main.py
|
||||
- **0.56**: ❗Breaking changes : ollama node simplified, no ollama_ip.txt needed, waiting for collection ollama nodes to be ready.
|
||||
- **0.57**: ❗❗Huge changes, new Ollama node "Ollama Chat" with real functionalities. 5 Ollama nodes total. (Model selector + Job selector + Persona selector + Ollama vision + Ollama Talk) Ollama talk use context and can use context file. Add number of lines / current counter + next to sequential nodes. Add new node STT. (+ faster_whisper dep) better management of empty loras/checkpoints on selectors. (list preset) Add "default_for_language" for TTS node, taking the default voice for a language (ex: fr/default.wav) Otherwise take the first wav with the selected language.
|
||||
- **0.58**: small fix in model selector default value. (Set to None by default)
|
||||
- **0.59**: A lot of Javascript fixing to avoid resizing and better properties mangement / recoveries
|
||||
|
||||
# 📝 Nodes descriptions
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "bjornulf_custom_nodes"
|
||||
description = "61 ComfyUI nodes : Display, manipulate, and edit text, images, videos, loras and more. Manage looping operations, generate randomized content, use logical conditions and work with external AI tools, like Ollama or Text To Speech."
|
||||
version = "0.58"
|
||||
version = "0.59"
|
||||
license = {file = "LICENSE"}
|
||||
|
||||
[project.urls]
|
||||
|
||||
@@ -5,6 +5,7 @@ app.registerExtension({
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass === "Bjornulf_CombineImages") {
|
||||
const updateInputs = () => {
|
||||
const initialWidth = node.size[0];
|
||||
const numInputsWidget = node.widgets.find(w => w.name === "number_of_images");
|
||||
if (!numInputsWidget) return;
|
||||
|
||||
@@ -33,6 +34,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
node.setSize(node.computeSize());
|
||||
node.size[0] = initialWidth; // Keep width fixed
|
||||
};
|
||||
|
||||
// Move number_of_images to the top initially
|
||||
|
||||
@@ -5,6 +5,7 @@ app.registerExtension({
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass === "Bjornulf_CombineTexts") {
|
||||
const updateInputs = () => {
|
||||
const initialWidth = node.size[0];
|
||||
const numInputsWidget = node.widgets.find(w => w.name === "number_of_inputs");
|
||||
if (!numInputsWidget) return;
|
||||
|
||||
@@ -33,6 +34,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
node.setSize(node.computeSize());
|
||||
node.size[0] = initialWidth; // Keep width fixed
|
||||
};
|
||||
|
||||
// Move number_of_inputs to the top initially
|
||||
|
||||
@@ -5,6 +5,7 @@ app.registerExtension({
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass === "Bjornulf_LoopImages") {
|
||||
const updateInputs = () => {
|
||||
const initialWidth = node.size[0];
|
||||
const numInputsWidget = node.widgets.find(w => w.name === "number_of_images");
|
||||
if (!numInputsWidget) return;
|
||||
|
||||
@@ -33,6 +34,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
node.setSize(node.computeSize());
|
||||
node.size[0] = initialWidth; // Keep width fixed
|
||||
};
|
||||
|
||||
// Move number_of_images to the top initially
|
||||
|
||||
@@ -4,63 +4,95 @@ app.registerExtension({
|
||||
name: "Bjornulf.LoopLoraSelector",
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass === "Bjornulf_LoopLoraSelector") {
|
||||
node.properties = node.properties || {};
|
||||
|
||||
const updateLoraInputs = () => {
|
||||
const initialWidth = node.size[0];
|
||||
const numLorasWidget = node.widgets.find(w => w.name === "number_of_loras");
|
||||
if (!numLorasWidget) return;
|
||||
|
||||
const numLoras = numLorasWidget.value;
|
||||
const loraList = node.widgets.find(w => w.name === "lora_1").options.values;
|
||||
const loraList = node.widgets.find(w => w.name === "lora_1")?.options?.values || [];
|
||||
|
||||
// Remove excess lora widgets and their corresponding strength widgets
|
||||
// Remove excess lora widgets
|
||||
node.widgets = node.widgets.filter(w => !w.name.startsWith("lora_") || parseInt(w.name.split("_")[1]) <= numLoras);
|
||||
|
||||
// Store current widget values in properties
|
||||
node.widgets.forEach(w => {
|
||||
if (w.name.startsWith("lora_") ||
|
||||
w.name.startsWith("strength_model_") ||
|
||||
w.name.startsWith("strength_clip_")) {
|
||||
node.properties[w.name] = w.value;
|
||||
}
|
||||
});
|
||||
|
||||
// Remove all lora-related widgets
|
||||
node.widgets = node.widgets.filter(w =>
|
||||
!w.name.startsWith("lora_") &&
|
||||
!w.name.startsWith("strength_model_") &&
|
||||
!w.name.startsWith("strength_clip_") ||
|
||||
parseInt(w.name.split("_").pop()) <= numLoras
|
||||
!w.name.startsWith("strength_clip_")
|
||||
);
|
||||
|
||||
// Add new lora widgets and their corresponding strength widgets if needed
|
||||
// Add widgets only for the specified number of loras
|
||||
for (let i = 1; i <= numLoras; i++) {
|
||||
const loraWidgetName = `lora_${i}`;
|
||||
const strengthModelWidgetName = `strength_model_${i}`;
|
||||
const strengthClipWidgetName = `strength_clip_${i}`;
|
||||
|
||||
if (!node.widgets.find(w => w.name === loraWidgetName)) {
|
||||
const defaultIndex = Math.min(i - 1, loraList.length - 1);
|
||||
node.addWidget("combo", loraWidgetName, loraList[defaultIndex], () => {}, {
|
||||
// Add lora widget
|
||||
const savedLoraValue = node.properties[loraWidgetName];
|
||||
const loraWidget = node.addWidget("combo", loraWidgetName,
|
||||
savedLoraValue !== undefined ? savedLoraValue : loraList[0],
|
||||
(value) => {
|
||||
node.properties[loraWidgetName] = value;
|
||||
}, {
|
||||
values: loraList
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!node.widgets.find(w => w.name === strengthModelWidgetName)) {
|
||||
node.addWidget("number", strengthModelWidgetName, 1.0, () => {}, {
|
||||
// Add strength model widget
|
||||
const savedModelValue = node.properties[strengthModelWidgetName];
|
||||
const strengthModelWidget = node.addWidget("number", strengthModelWidgetName,
|
||||
savedModelValue !== undefined ? savedModelValue : 1.0,
|
||||
(value) => {
|
||||
node.properties[strengthModelWidgetName] = value;
|
||||
}, {
|
||||
min: -100.0, max: 100.0, step: 0.01
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!node.widgets.find(w => w.name === strengthClipWidgetName)) {
|
||||
node.addWidget("number", strengthClipWidgetName, 1.0, () => {}, {
|
||||
// Add strength clip widget
|
||||
const savedClipValue = node.properties[strengthClipWidgetName];
|
||||
const strengthClipWidget = node.addWidget("number", strengthClipWidgetName,
|
||||
savedClipValue !== undefined ? savedClipValue : 1.0,
|
||||
(value) => {
|
||||
node.properties[strengthClipWidgetName] = value;
|
||||
}, {
|
||||
min: -100.0, max: 100.0, step: 0.01
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Reorder widgets
|
||||
const orderedWidgets = [node.widgets.find(w => w.name === "number_of_loras")];
|
||||
for (let i = 1; i <= numLoras; i++) {
|
||||
orderedWidgets.push(
|
||||
node.widgets.find(w => w.name === `lora_${i}`),
|
||||
node.widgets.find(w => w.name === `strength_model_${i}`),
|
||||
node.widgets.find(w => w.name === `strength_clip_${i}`)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Reorder widgets: number_of_loras first, then grouped lora widgets
|
||||
const orderedWidgets = [node.widgets.find(w => w.name === "number_of_loras")];
|
||||
for (let i = 1; i <= numLoras; i++) {
|
||||
const loraWidgets = node.widgets.filter(w =>
|
||||
w.name === `lora_${i}` ||
|
||||
w.name === `strength_model_${i}` ||
|
||||
w.name === `strength_clip_${i}`
|
||||
);
|
||||
orderedWidgets.push(...loraWidgets);
|
||||
}
|
||||
|
||||
// Add any remaining widgets
|
||||
orderedWidgets.push(...node.widgets.filter(w => !orderedWidgets.includes(w)));
|
||||
node.widgets = orderedWidgets.filter(w => w !== undefined);
|
||||
node.widgets = orderedWidgets;
|
||||
|
||||
node.setSize(node.computeSize());
|
||||
node.size[0] = initialWidth; // Keep width fixed
|
||||
};
|
||||
|
||||
// Set up number_of_loras widget
|
||||
// Set up number_of_loras widget callback
|
||||
const numLorasWidget = node.widgets.find(w => w.name === "number_of_loras");
|
||||
if (numLorasWidget) {
|
||||
numLorasWidget.callback = () => {
|
||||
@@ -76,32 +108,47 @@ app.registerExtension({
|
||||
originalOnConfigure.call(this, info);
|
||||
}
|
||||
|
||||
// Restore lora widgets and strength widgets based on saved properties
|
||||
const savedProperties = info.properties;
|
||||
if (savedProperties) {
|
||||
Object.keys(savedProperties).forEach(key => {
|
||||
if (key.startsWith("lora_") || key.startsWith("strength_model_") || key.startsWith("strength_clip_")) {
|
||||
const widgetName = key;
|
||||
const widgetValue = savedProperties[key];
|
||||
const existingWidget = node.widgets.find(w => w.name === widgetName);
|
||||
if (existingWidget) {
|
||||
existingWidget.value = widgetValue;
|
||||
} else {
|
||||
if (key.startsWith("lora_")) {
|
||||
node.addWidget("combo", widgetName, widgetValue, () => {}, {
|
||||
values: node.widgets.find(w => w.name === "lora_1").options.values
|
||||
});
|
||||
} else {
|
||||
node.addWidget("number", widgetName, widgetValue, () => {}, {
|
||||
min: -100.0, max: 100.0, step: 0.01
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// if (info.properties) {
|
||||
// // Restore properties
|
||||
// Object.assign(this.properties, info.properties);
|
||||
// }
|
||||
// const savedProperties = info.properties;
|
||||
// if (savedProperties) {
|
||||
// Object.keys(savedProperties).forEach(key => {
|
||||
// if (key.startsWith("lora_") || key.startsWith("strength_model_") || key.startsWith("strength_clip_")) {
|
||||
// const widgetName = key;
|
||||
// const widgetValue = savedProperties[key];
|
||||
// const existingWidget = node.widgets.find(w =>
|
||||
// w.name === widgetName
|
||||
// );
|
||||
// if (existingWidget) {
|
||||
// existingWidget.value = widgetValue;
|
||||
// } else {
|
||||
// const baseWidget = node.widgets.find(w =>
|
||||
// w.name === "lora_1" ||
|
||||
// w.name === "strength_model_1" ||
|
||||
// w.name === "strength_clip_1"
|
||||
// );
|
||||
// if (baseWidget) {
|
||||
// node.addWidget("combo", widgetName, widgetValue, () => {}, {
|
||||
// values: baseWidget.options.values
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// Save properties during serialization
|
||||
const originalOnSerialize = node.onSerialize;
|
||||
node.onSerialize = function(info) {
|
||||
if (originalOnSerialize) {
|
||||
originalOnSerialize.call(this, info);
|
||||
}
|
||||
|
||||
// Update lora inputs after restoring saved state
|
||||
info.properties = { ...this.properties };
|
||||
};
|
||||
|
||||
|
||||
// Update the widgets based on the current number_of_loras value
|
||||
updateLoraInputs();
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ app.registerExtension({
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass === "Bjornulf_LoopModelClipVae") {
|
||||
const updateInputs = () => {
|
||||
const initialWidth = node.size[0];
|
||||
const numInputsWidget = node.widgets.find(w => w.name === "number_of_inputs");
|
||||
if (!numInputsWidget) return;
|
||||
|
||||
@@ -46,6 +47,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
node.setSize(node.computeSize());
|
||||
node.size[0] = initialWidth; // Keep width fixed
|
||||
};
|
||||
|
||||
// Move number_of_inputs to the top initially
|
||||
|
||||
@@ -4,43 +4,64 @@ app.registerExtension({
|
||||
name: "Bjornulf.LoopModelSelector",
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass === "Bjornulf_LoopModelSelector") {
|
||||
node.properties = node.properties || {};
|
||||
|
||||
const updateModelInputs = () => {
|
||||
const initialWidth = node.size[0];
|
||||
const numModelsWidget = node.widgets.find(w => w.name === "number_of_models");
|
||||
if (!numModelsWidget) return;
|
||||
|
||||
const numModels = numModelsWidget.value;
|
||||
const checkpointsList = node.widgets.find(w => w.name === "model_1").options.values;
|
||||
|
||||
const checkpointsList = node.widgets.find(w => w.name === "model_1")?.options?.values || [];
|
||||
|
||||
// Remove excess model widgets
|
||||
node.widgets = node.widgets.filter(w => !w.name.startsWith("model_") || parseInt(w.name.split("_")[1]) <= numModels);
|
||||
|
||||
// Add new model widgets if needed
|
||||
node.widgets = node.widgets.filter(w =>
|
||||
!w.name.startsWith("model_") || parseInt(w.name.split("_")[1]) <= numModels
|
||||
);
|
||||
|
||||
// Store current widget values in properties
|
||||
node.widgets.forEach(w => {
|
||||
if (w.name.startsWith("model_")) {
|
||||
node.properties[w.name] = w.value;
|
||||
}
|
||||
});
|
||||
|
||||
// Remove all model-related widgets
|
||||
node.widgets = node.widgets.filter(w =>
|
||||
!w.name.startsWith("model_")
|
||||
);
|
||||
|
||||
// Add new model widgets
|
||||
for (let i = 1; i <= numModels; i++) {
|
||||
const widgetName = `model_${i}`;
|
||||
if (!node.widgets.find(w => w.name === widgetName)) {
|
||||
const defaultIndex = Math.min(i - 1, checkpointsList.length - 1);
|
||||
node.addWidget("combo", widgetName, checkpointsList[defaultIndex], () => {}, {
|
||||
values: checkpointsList
|
||||
});
|
||||
}
|
||||
const savedValue = node.properties[widgetName];
|
||||
const defaultIndex = Math.min(i - 1, checkpointsList.length - 1);
|
||||
const defaultValue = savedValue !== undefined ? savedValue : checkpointsList[defaultIndex];
|
||||
|
||||
const modelWidget = node.addWidget("combo", widgetName, defaultValue, (value) => {
|
||||
node.properties[widgetName] = value;
|
||||
}, {
|
||||
values: checkpointsList
|
||||
});
|
||||
}
|
||||
|
||||
// Reorder widgets
|
||||
node.widgets.sort((a, b) => {
|
||||
if (a.name === "number_of_models") return -1;
|
||||
if (b.name === "number_of_models") return 1;
|
||||
if (a.name === "seed") return 1;
|
||||
if (b.name === "seed") return -1;
|
||||
if (a.name.startsWith("model_") && b.name.startsWith("model_")) {
|
||||
return parseInt(a.name.split("_")[1]) - parseInt(b.name.split("_")[1]);
|
||||
}
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
|
||||
// Reorder widgets: number_of_models first, then the model widgets
|
||||
const orderedWidgets = [node.widgets.find(w => w.name === "number_of_models")];
|
||||
for (let i = 1; i <= numModels; i++) {
|
||||
const modelWidgets = node.widgets.filter(w => w.name === `model_${i}`);
|
||||
orderedWidgets.push(...modelWidgets);
|
||||
}
|
||||
|
||||
// Add any remaining widgets
|
||||
orderedWidgets.push(...node.widgets.filter(w => !orderedWidgets.includes(w)));
|
||||
node.widgets = orderedWidgets;
|
||||
|
||||
// Adjust node size
|
||||
node.setSize(node.computeSize());
|
||||
node.size[0] = initialWidth; // Keep width fixed
|
||||
};
|
||||
|
||||
// Set up number_of_models widget
|
||||
// Set up number_of_models widget callback
|
||||
const numModelsWidget = node.widgets.find(w => w.name === "number_of_models");
|
||||
if (numModelsWidget) {
|
||||
numModelsWidget.callback = () => {
|
||||
@@ -49,48 +70,56 @@ app.registerExtension({
|
||||
};
|
||||
}
|
||||
|
||||
// Set seed widget to integer input
|
||||
const seedWidget = node.widgets.find((w) => w.name === "seed");
|
||||
if (seedWidget) {
|
||||
seedWidget.type = "HIDDEN"; // Hide seed widget after restoring saved state
|
||||
}
|
||||
|
||||
// Handle deserialization
|
||||
const originalOnConfigure = node.onConfigure;
|
||||
node.onConfigure = function(info) {
|
||||
if (originalOnConfigure) {
|
||||
originalOnConfigure.call(this, info);
|
||||
}
|
||||
|
||||
|
||||
// Restore model widgets based on saved properties
|
||||
const savedProperties = info.properties;
|
||||
if (savedProperties) {
|
||||
Object.keys(savedProperties).forEach(key => {
|
||||
if (key.startsWith("model_")) {
|
||||
const widgetName = key;
|
||||
const widgetValue = savedProperties[key];
|
||||
const existingWidget = node.widgets.find(w => w.name === widgetName);
|
||||
if (existingWidget) {
|
||||
existingWidget.value = widgetValue;
|
||||
} else {
|
||||
node.addWidget("combo", widgetName, widgetValue, () => {}, {
|
||||
values: node.widgets.find(w => w.name === "model_1").options.values
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// const savedProperties = info.properties;
|
||||
// if (savedProperties) {
|
||||
// Object.keys(savedProperties).forEach(key => {
|
||||
// if (key.startsWith("model_")) {
|
||||
// const widgetName = key;
|
||||
// const widgetValue = savedProperties[key];
|
||||
// const existingWidget = node.widgets.find(w => w.name === widgetName);
|
||||
|
||||
// if (existingWidget) {
|
||||
// existingWidget.value = widgetValue;
|
||||
// } else {
|
||||
// const checkpointsList = node.widgets.find(w => w.name === "model_1")?.options?.values || [];
|
||||
// const defaultIndex = Math.min(parseInt(widgetName.split("_")[1]) - 1, checkpointsList.length - 1);
|
||||
// node.addWidget("combo", widgetName, widgetValue || checkpointsList[defaultIndex], () => {}, {
|
||||
// values: checkpointsList
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// Ensure seed is a valid integer
|
||||
const seedWidget = node.widgets.find(w => w.name === "seed");
|
||||
if (seedWidget && isNaN(parseInt(seedWidget.value))) {
|
||||
seedWidget.value = 0; // Set a default value if invalid
|
||||
// Save properties during serialization
|
||||
const originalOnSerialize = node.onSerialize;
|
||||
node.onSerialize = function(info) {
|
||||
if (originalOnSerialize) {
|
||||
originalOnSerialize.call(this, info);
|
||||
}
|
||||
|
||||
info.properties = { ...this.properties };
|
||||
};
|
||||
|
||||
// Update model inputs after restoring saved state
|
||||
updateModelInputs();
|
||||
};
|
||||
|
||||
// Serialize method to save properties
|
||||
const originalSerialize = node.serialize;
|
||||
node.serialize = function() {
|
||||
const data = originalSerialize ? originalSerialize.call(this) : {};
|
||||
data.properties = { ...this.properties };
|
||||
return data;
|
||||
};
|
||||
|
||||
// Initial update
|
||||
updateModelInputs();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ app.registerExtension({
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass === "Bjornulf_LoopTexts") {
|
||||
const updateInputs = () => {
|
||||
const initialWidth = node.size[0];
|
||||
const numInputsWidget = node.widgets.find(w => w.name === "number_of_inputs");
|
||||
if (!numInputsWidget) return;
|
||||
|
||||
@@ -33,6 +34,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
node.setSize(node.computeSize());
|
||||
node.size[0] = initialWidth; // Keep width fixed
|
||||
};
|
||||
|
||||
// Move number_of_inputs to the top initially
|
||||
|
||||
@@ -43,6 +43,7 @@ app.registerExtension({
|
||||
};
|
||||
|
||||
const updateInputs = () => {
|
||||
const initialWidth = node.size[0];
|
||||
const numInputsWidget = node.widgets.find(
|
||||
(w) => w.name === "number_of_inputs"
|
||||
);
|
||||
@@ -82,6 +83,7 @@ app.registerExtension({
|
||||
updateInputButtons(numInputs);
|
||||
|
||||
node.setSize(node.computeSize());
|
||||
node.size[0] = initialWidth; // Keep width fixed
|
||||
};
|
||||
|
||||
// Move number_of_inputs to the top initially
|
||||
|
||||
@@ -5,6 +5,7 @@ app.registerExtension({
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass === "Bjornulf_RandomImage") {
|
||||
const updateInputs = () => {
|
||||
const initialWidth = node.size[0];
|
||||
const numInputsWidget = node.widgets.find(w => w.name === "number_of_images");
|
||||
if (!numInputsWidget) return;
|
||||
|
||||
@@ -33,6 +34,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
node.setSize(node.computeSize());
|
||||
node.size[0] = initialWidth; // Keep width fixed
|
||||
};
|
||||
|
||||
// Set seed widget to hidden input
|
||||
|
||||
@@ -4,43 +4,69 @@ app.registerExtension({
|
||||
name: "Bjornulf.RandomLoraSelector",
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass === "Bjornulf_RandomLoraSelector") {
|
||||
node.properties = node.properties || {};
|
||||
const updateLoraInputs = () => {
|
||||
const initialWidth = node.size[0];
|
||||
const numLorasWidget = node.widgets.find(w => w.name === "number_of_loras");
|
||||
if (!numLorasWidget) return;
|
||||
|
||||
const numLoras = numLorasWidget.value;
|
||||
const loraList = node.widgets.find(w => w.name === "lora_1").options.values;
|
||||
|
||||
// Remove excess lora widgets
|
||||
node.widgets = node.widgets.filter(w => !w.name.startsWith("lora_") || parseInt(w.name.split("_")[1]) <= numLoras);
|
||||
|
||||
// Add new lora widgets if needed
|
||||
for (let i = 1; i <= numLoras; i++) {
|
||||
const widgetName = `lora_${i}`;
|
||||
if (!node.widgets.find(w => w.name === widgetName)) {
|
||||
const defaultIndex = Math.min(i - 1, loraList.length - 1);
|
||||
node.addWidget("combo", widgetName, loraList[defaultIndex], () => {}, {
|
||||
values: loraList
|
||||
});
|
||||
const loraList = node.widgets.find(w => w.name === "lora_1")?.options?.values || [];
|
||||
|
||||
// Save existing properties before clearing widgets
|
||||
node.widgets.forEach(w => {
|
||||
if (w.name.startsWith("lora_") || ["strength_model", "strength_clip", "seed", "control_after_generate"].includes(w.name)) {
|
||||
node.properties[w.name] = w.value;
|
||||
}
|
||||
}
|
||||
|
||||
// Reorder widgets
|
||||
node.widgets.sort((a, b) => {
|
||||
if (a.name === "number_of_loras") return -1;
|
||||
if (b.name === "number_of_loras") return 1;
|
||||
if (a.name === "seed") return 1;
|
||||
if (b.name === "seed") return -1;
|
||||
if (a.name.startsWith("lora_") && b.name.startsWith("lora_")) {
|
||||
return parseInt(a.name.split("_")[1]) - parseInt(b.name.split("_")[1]);
|
||||
}
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
|
||||
// Remove all LORA widgets
|
||||
node.widgets = node.widgets.filter(w => !w.name.startsWith("lora_"));
|
||||
|
||||
// Ensure shared strength widgets exist (top section)
|
||||
const ensureWidget = (name, type, defaultValue, config) => {
|
||||
let widget = node.widgets.find(w => w.name === name);
|
||||
if (!widget) {
|
||||
const savedValue = node.properties[name];
|
||||
widget = node.addWidget(type, name,
|
||||
savedValue !== undefined ? savedValue : defaultValue,
|
||||
value => { node.properties[name] = value; },
|
||||
config
|
||||
);
|
||||
} else {
|
||||
widget.value = node.properties[name] || widget.value;
|
||||
}
|
||||
};
|
||||
|
||||
ensureWidget("number_of_loras", "number", 3, { min: 1, max: 20, step: 1 });
|
||||
ensureWidget("strength_model", "number", 1.0, { min: -100.0, max: 100.0, step: 0.01 });
|
||||
ensureWidget("strength_clip", "number", 1.0, { min: -100.0, max: 100.0, step: 0.01 });
|
||||
ensureWidget("seed", "number", 0, { step: 1 });
|
||||
ensureWidget("control_after_generate", "checkbox", false, {});
|
||||
|
||||
// Add LORA widgets (bottom section)
|
||||
for (let i = 1; i <= numLoras; i++) {
|
||||
const loraWidgetName = `lora_${i}`;
|
||||
const savedLoraValue = node.properties[loraWidgetName];
|
||||
node.addWidget("combo", loraWidgetName,
|
||||
savedLoraValue !== undefined ? savedLoraValue : loraList[0],
|
||||
value => { node.properties[loraWidgetName] = value; },
|
||||
{ values: loraList }
|
||||
);
|
||||
}
|
||||
|
||||
// Reorder widgets: shared widgets first, followed by LORA widgets
|
||||
const sharedWidgetNames = ["number_of_loras", "strength_model", "strength_clip", "seed", "control_after_generate"];
|
||||
const sharedWidgets = sharedWidgetNames.map(name => node.widgets.find(w => w.name === name));
|
||||
const loraWidgets = node.widgets.filter(w => w.name.startsWith("lora_"));
|
||||
const remainingWidgets = node.widgets.filter(w => !sharedWidgets.includes(w) && !loraWidgets.includes(w));
|
||||
|
||||
node.widgets = [...sharedWidgets, ...remainingWidgets, ...loraWidgets];
|
||||
node.setSize(node.computeSize());
|
||||
node.size[0] = initialWidth; // Keep width fixed
|
||||
};
|
||||
|
||||
// Set up number_of_loras widget
|
||||
// Set up number_of_loras widget callback
|
||||
const numLorasWidget = node.widgets.find(w => w.name === "number_of_loras");
|
||||
if (numLorasWidget) {
|
||||
numLorasWidget.callback = () => {
|
||||
@@ -49,50 +75,33 @@ app.registerExtension({
|
||||
};
|
||||
}
|
||||
|
||||
// Set seed widget to integer input
|
||||
const seedWidget = node.widgets.find((w) => w.name === "seed");
|
||||
if (seedWidget) {
|
||||
seedWidget.type = "HIDDEN"; // Hide seed widget after restoring saved state
|
||||
}
|
||||
|
||||
// Handle deserialization
|
||||
const originalOnConfigure = node.onConfigure;
|
||||
node.onConfigure = function(info) {
|
||||
if (originalOnConfigure) {
|
||||
originalOnConfigure.call(this, info);
|
||||
}
|
||||
|
||||
// Restore lora widgets based on saved properties
|
||||
const savedProperties = info.properties;
|
||||
if (savedProperties) {
|
||||
Object.keys(savedProperties).forEach(key => {
|
||||
if (key.startsWith("lora_")) {
|
||||
const widgetName = key;
|
||||
const widgetValue = savedProperties[key];
|
||||
const existingWidget = node.widgets.find(w => w.name === widgetName);
|
||||
if (existingWidget) {
|
||||
existingWidget.value = widgetValue;
|
||||
} else {
|
||||
node.addWidget("combo", widgetName, widgetValue, () => {}, {
|
||||
values: node.widgets.find(w => w.name === "lora_1").options.values
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Restore saved properties
|
||||
if (info.properties) {
|
||||
Object.assign(this.properties, info.properties);
|
||||
}
|
||||
|
||||
// Ensure seed is a valid integer
|
||||
const seedWidget = node.widgets.find(w => w.name === "seed");
|
||||
if (seedWidget && isNaN(parseInt(seedWidget.value))) {
|
||||
seedWidget.value = 0; // Set a default value if invalid
|
||||
}
|
||||
|
||||
// Update lora inputs after restoring saved state
|
||||
|
||||
// Update the widgets based on the current number_of_loras value
|
||||
updateLoraInputs();
|
||||
};
|
||||
|
||||
// Save properties during serialization
|
||||
const originalOnSerialize = node.onSerialize;
|
||||
node.onSerialize = function(info) {
|
||||
if (originalOnSerialize) {
|
||||
originalOnSerialize.call(this, info);
|
||||
}
|
||||
info.properties = { ...this.properties };
|
||||
};
|
||||
|
||||
// Initial update
|
||||
updateLoraInputs();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user