- Auto-strength now uses pairwise cosine similarity for exact vector-sum energy normalization instead of assuming orthogonal LoRAs - Save Merged LoRA defaults to ComfyUI loras folder; supports absolute paths - Add JS extension for LoRA Stack (Dynamic) widget show/hide - Fix LTX architecture detection for middle_block/mid_block keys - Update banner SVG with new feature tags Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
93 lines
2.9 KiB
JavaScript
93 lines
2.9 KiB
JavaScript
import { app } from "/scripts/app.js";
|
|
|
|
// Widget hide/show via type-mangling (standard ComfyUI pattern)
|
|
const HIDDEN_TAG = "loraopt_hidden";
|
|
const origProps = {};
|
|
|
|
function toggleWidget(node, widget, show) {
|
|
if (!widget) return;
|
|
|
|
if (!origProps[widget.name]) {
|
|
origProps[widget.name] = {
|
|
origType: widget.type,
|
|
origComputeSize: widget.computeSize,
|
|
};
|
|
}
|
|
|
|
widget.type = show ? origProps[widget.name].origType : HIDDEN_TAG;
|
|
widget.computeSize = show
|
|
? origProps[widget.name].origComputeSize
|
|
: () => [0, -4];
|
|
|
|
if (widget.linkedWidgets) {
|
|
for (const w of widget.linkedWidgets) {
|
|
toggleWidget(node, w, show);
|
|
}
|
|
}
|
|
}
|
|
|
|
function findWidget(node, name) {
|
|
return node.widgets ? node.widgets.find((w) => w.name === name) : null;
|
|
}
|
|
|
|
function updateVisibility(node) {
|
|
const modeWidget = findWidget(node, "mode");
|
|
const countWidget = findWidget(node, "lora_count");
|
|
if (!modeWidget || !countWidget) return;
|
|
|
|
const isSimple = modeWidget.value === "simple";
|
|
const count = countWidget.value;
|
|
const MAX = 10;
|
|
|
|
for (let i = 1; i <= MAX; i++) {
|
|
const visible = i <= count;
|
|
|
|
toggleWidget(node, findWidget(node, `lora_name_${i}`), visible);
|
|
toggleWidget(node, findWidget(node, `strength_${i}`), visible && isSimple);
|
|
toggleWidget(node, findWidget(node, `model_strength_${i}`), visible && !isSimple);
|
|
toggleWidget(node, findWidget(node, `clip_strength_${i}`), visible && !isSimple);
|
|
}
|
|
|
|
const newHeight = node.computeSize()[1];
|
|
node.setSize([node.size[0], newHeight]);
|
|
}
|
|
|
|
app.registerExtension({
|
|
name: "LoRAOptimizer.LoRAStackDynamic",
|
|
nodeCreated(node) {
|
|
if (node.comfyClass !== "LoRAStackDynamic") return;
|
|
|
|
// Intercept mode and lora_count changes to update visibility
|
|
for (const w of node.widgets || []) {
|
|
if (w.name !== "mode" && w.name !== "lora_count") continue;
|
|
|
|
let widgetValue = w.value;
|
|
const originalDescriptor =
|
|
Object.getOwnPropertyDescriptor(w, "value") ||
|
|
Object.getOwnPropertyDescriptor(
|
|
Object.getPrototypeOf(w),
|
|
"value"
|
|
);
|
|
|
|
Object.defineProperty(w, "value", {
|
|
get() {
|
|
return originalDescriptor?.get
|
|
? originalDescriptor.get.call(w)
|
|
: widgetValue;
|
|
},
|
|
set(newVal) {
|
|
if (originalDescriptor?.set) {
|
|
originalDescriptor.set.call(w, newVal);
|
|
} else {
|
|
widgetValue = newVal;
|
|
}
|
|
updateVisibility(node);
|
|
},
|
|
});
|
|
}
|
|
|
|
// Initial visibility update (after a tick so widgets are fully initialized)
|
|
requestAnimationFrame(() => updateVisibility(node));
|
|
},
|
|
});
|