diff --git a/README.md b/README.md index 88169b9..c9ca96f 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/pyproject.toml b/pyproject.toml index 77b5132..92e7179 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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] diff --git a/web/js/combine_images.js b/web/js/combine_images.js index 95381b8..ac111a7 100644 --- a/web/js/combine_images.js +++ b/web/js/combine_images.js @@ -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 diff --git a/web/js/combine_texts.js b/web/js/combine_texts.js index f44b1af..f4178d8 100644 --- a/web/js/combine_texts.js +++ b/web/js/combine_texts.js @@ -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 diff --git a/web/js/loop_images.js b/web/js/loop_images.js index e74326b..76b0d79 100644 --- a/web/js/loop_images.js +++ b/web/js/loop_images.js @@ -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 diff --git a/web/js/loop_lora_selector.js b/web/js/loop_lora_selector.js index 7f11e3c..8393cb2 100644 --- a/web/js/loop_lora_selector.js +++ b/web/js/loop_lora_selector.js @@ -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(); }; diff --git a/web/js/loop_model_clip_vae.js b/web/js/loop_model_clip_vae.js index 56235a3..9970d6b 100644 --- a/web/js/loop_model_clip_vae.js +++ b/web/js/loop_model_clip_vae.js @@ -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 diff --git a/web/js/loop_model_selector.js b/web/js/loop_model_selector.js index 6ded470..beaa73f 100644 --- a/web/js/loop_model_selector.js +++ b/web/js/loop_model_selector.js @@ -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(); } diff --git a/web/js/loop_texts.js b/web/js/loop_texts.js index 931d53a..b517d5b 100644 --- a/web/js/loop_texts.js +++ b/web/js/loop_texts.js @@ -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 diff --git a/web/js/pick_input.js b/web/js/pick_input.js index a58f598..a5d6799 100644 --- a/web/js/pick_input.js +++ b/web/js/pick_input.js @@ -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 diff --git a/web/js/random_image.js b/web/js/random_image.js index f06620e..00a4bf2 100644 --- a/web/js/random_image.js +++ b/web/js/random_image.js @@ -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 diff --git a/web/js/random_lora_selector.js b/web/js/random_lora_selector.js index db7cb82..b8cf054 100644 --- a/web/js/random_lora_selector.js +++ b/web/js/random_lora_selector.js @@ -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(); } } -}); \ No newline at end of file +});