fix bug
This commit is contained in:
+1
-36
@@ -1,42 +1,7 @@
|
||||
import os, json
|
||||
import comfy.utils, comfy.sd, folder_paths
|
||||
from .multi_lora_loader import MultiLoraLoader
|
||||
from .prompt_segments import PromptSegments
|
||||
from .resolution_switcher import ResolutionSwitcher
|
||||
|
||||
class MultiLoraLoader:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"model": ("MODEL",),
|
||||
"lora_stack": ("STRING", {"default": "[]", "multiline": False}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MODEL",)
|
||||
FUNCTION = "apply"
|
||||
CATEGORY = "loaders"
|
||||
|
||||
def apply(self, model, lora_stack="[]"):
|
||||
try:
|
||||
stack = json.loads(lora_stack)
|
||||
except Exception:
|
||||
return (model,)
|
||||
|
||||
for entry in stack:
|
||||
if not entry.get("enabled", False):
|
||||
continue
|
||||
lora_name = entry.get("lora", "None")
|
||||
strength = float(entry.get("strength", 1.0))
|
||||
if lora_name == "None" or strength == 0:
|
||||
continue
|
||||
path = folder_paths.get_full_path("loras", lora_name)
|
||||
if path and os.path.exists(path):
|
||||
lora = comfy.utils.load_torch_file(path)
|
||||
model, _ = comfy.sd.load_lora_for_models(model, None, lora, strength, 0)
|
||||
|
||||
return (model,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"MultiLoraLoader": MultiLoraLoader,
|
||||
"PromptSegments": PromptSegments,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
// 语言配置:将下面一行的 "en" 改成 "zh" 即可切换中文
|
||||
const LANG = "en";
|
||||
const LANG = "zh";
|
||||
|
||||
const I18N = {
|
||||
en: { on_off: "On/Off", lora: "LoRA", strength: "Strength", note: "Note", del: "Del",
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import os, json
|
||||
import comfy.utils, comfy.sd, folder_paths
|
||||
|
||||
class MultiLoraLoader:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"model": ("MODEL",),
|
||||
"lora_stack": ("STRING", {"default": "[]", "multiline": False}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MODEL",)
|
||||
FUNCTION = "apply"
|
||||
CATEGORY = "loaders"
|
||||
|
||||
def apply(self, model, lora_stack="[]"):
|
||||
try:
|
||||
stack = json.loads(lora_stack)
|
||||
except Exception:
|
||||
return (model,)
|
||||
|
||||
for entry in stack:
|
||||
if not entry.get("enabled", False):
|
||||
continue
|
||||
lora_name = entry.get("lora", "None")
|
||||
strength = float(entry.get("strength", 1.0))
|
||||
if lora_name == "None" or strength == 0:
|
||||
continue
|
||||
path = folder_paths.get_full_path("loras", lora_name)
|
||||
if path and os.path.exists(path):
|
||||
lora = comfy.utils.load_torch_file(path)
|
||||
model, _ = comfy.sd.load_lora_for_models(model, None, lora, strength, 0)
|
||||
|
||||
return (model,)
|
||||
+31
-13
@@ -1,7 +1,7 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
// 语言配置:将下面一行的 "en" 改成 "zh" 即可切换中文
|
||||
const LANG = "en";
|
||||
const LANG = "zh";
|
||||
|
||||
const I18N = {
|
||||
en: { label_ph: "Label...", add: "+ Add Segment", default_label: "Quality Tags",
|
||||
@@ -34,20 +34,20 @@ app.registerExtension({
|
||||
// When the node is off-screen or zoomed too small, ComfyUI hides
|
||||
// the DOM widget (display:none), which makes scrollHeight return 0.
|
||||
if (!domH || domH <= 0) {
|
||||
// No reliable layout info. If we already have a measured size,
|
||||
// keep it (don't collapse). Otherwise fall back to a content
|
||||
// based estimate so a brand-new hidden node still gets a size.
|
||||
if (!this._minH) {
|
||||
const h = this._calcHeight();
|
||||
this._minH = h;
|
||||
this.size[1] = h;
|
||||
app.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
// No reliable layout info. Use the content-based estimate so
|
||||
// the node still gets a reasonable height — and so deleting
|
||||
// segments while hidden actually shrinks the node (otherwise
|
||||
// _minH would be stuck at the old, larger value and the
|
||||
// deleted segments leave behind empty, non-interactive space).
|
||||
const h = this._calcHeight();
|
||||
this._minH = h;
|
||||
this.setSize([this.size[0], h]);
|
||||
app.graph.setDirtyCanvas(true, true);
|
||||
return;
|
||||
}
|
||||
const h = domH + 50;
|
||||
this._minH = h;
|
||||
this.size[1] = h;
|
||||
this.setSize([this.size[0], h]);
|
||||
app.graph.setDirtyCanvas(true, true);
|
||||
});
|
||||
};
|
||||
@@ -127,7 +127,16 @@ app.registerExtension({
|
||||
del.style.cssText = "background:transparent;color:#666;border:none;border-radius:3px;padding:2px 5px;cursor:pointer;font-size:11px;flex-shrink:0;line-height:1;transition:color 0.15s,background 0.15s;";
|
||||
del.onmouseenter = () => { del.style.color = "#fff"; del.style.background = "#c0392b"; };
|
||||
del.onmouseleave = () => { del.style.color = "#666"; del.style.background = "transparent"; };
|
||||
del.onclick = () => { this._segments.splice(i, 1); this._sync(); this._render(); };
|
||||
del.onclick = () => {
|
||||
this._segments.splice(i, 1);
|
||||
this._sync();
|
||||
// Clear the cached min height so _applySize can actually shrink
|
||||
// the node. Without this, _minH stays at the old (larger) value
|
||||
// and the deleted segment's area becomes dead, non-interactive
|
||||
// space — most visible after copy/paste.
|
||||
this._minH = 0;
|
||||
this._render();
|
||||
};
|
||||
|
||||
top.append(toggle, labelNum, label, del);
|
||||
|
||||
@@ -219,7 +228,10 @@ app.registerExtension({
|
||||
const onSerialize = nodeType.prototype.onSerialize;
|
||||
nodeType.prototype.onSerialize = function (o) {
|
||||
onSerialize?.apply(this, arguments);
|
||||
o.prompt_segments = this._segments;
|
||||
// Deep copy so a pasted/duplicated node doesn't share the same array
|
||||
// reference with the original (which would cause edits in one to
|
||||
// leak into the other).
|
||||
o.prompt_segments = JSON.parse(JSON.stringify(this._segments));
|
||||
};
|
||||
|
||||
const onRemoved = nodeType.prototype.onRemoved;
|
||||
@@ -233,6 +245,12 @@ app.registerExtension({
|
||||
onConfigure?.apply(this, arguments);
|
||||
if (o.prompt_segments) {
|
||||
this._segments = o.prompt_segments;
|
||||
// On paste/duplicate, ComfyUI may carry over a stale cached min
|
||||
// height and a stale DOM widget element height from the original.
|
||||
// Reset both so the new node measures fresh and shrinks correctly
|
||||
// when segments are deleted.
|
||||
this._minH = 0;
|
||||
if (this._domWidget?.element) this._domWidget.element.style.height = "auto";
|
||||
requestAnimationFrame(() => this._render());
|
||||
}
|
||||
};
|
||||
|
||||
+12
-6
@@ -1,7 +1,7 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
// 语言配置:将下面一行的 "en" 改成 "zh" 即可切换中文
|
||||
const LANG = "en";
|
||||
const LANG = "zh";
|
||||
|
||||
const I18N = {
|
||||
en: { w: "W", h: "H", batch: "Batch", add: "+ Add Resolution",
|
||||
@@ -62,14 +62,14 @@ app.registerExtension({
|
||||
if (!this._minH) {
|
||||
const h = this._calcHeight();
|
||||
this._minH = h;
|
||||
this.size[1] = h;
|
||||
this.setSize([this.size[0], h]);
|
||||
app.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const h = domH + 50;
|
||||
this._minH = h;
|
||||
this.size[1] = h;
|
||||
this.setSize([this.size[0], h]);
|
||||
app.graph.setDirtyCanvas(true, true);
|
||||
});
|
||||
};
|
||||
@@ -113,8 +113,12 @@ app.registerExtension({
|
||||
capsule.style.cssText = `width:30px;height:16px;border-radius:8px;background:${active ? "#43a047" : "#555"};position:relative;transition:background 0.15s;border:1px solid ${active ? "#2e7d32" : "#424242"};`;
|
||||
capsule.innerHTML = `<div style="position:absolute;top:1px;left:${active ? "15px" : "2px"};width:12px;height:12px;border-radius:50%;background:#fff;transition:left 0.15s;"></div>`;
|
||||
toggle.onclick = () => {
|
||||
// Mutually exclusive: turning one on turns all others off.
|
||||
this._presets.forEach((q, j) => { q.active = (j === i) ? !q.active : false; });
|
||||
// Radio behavior: clicking the active one is a no-op (must
|
||||
// always keep exactly one active — this node switches
|
||||
// resolutions, it can't disable resolution). Clicking any
|
||||
// other moves the active flag to it.
|
||||
if (active) return;
|
||||
this._presets.forEach((q, j) => { q.active = (j === i); });
|
||||
this._sync();
|
||||
this._render();
|
||||
};
|
||||
@@ -240,7 +244,9 @@ app.registerExtension({
|
||||
const onSerialize = nodeType.prototype.onSerialize;
|
||||
nodeType.prototype.onSerialize = function (o) {
|
||||
onSerialize?.apply(this, arguments);
|
||||
o.resolution_presets = this._presets;
|
||||
// Deep copy so a pasted/duplicated node doesn't share the same array
|
||||
// reference with the original.
|
||||
o.resolution_presets = JSON.parse(JSON.stringify(this._presets));
|
||||
};
|
||||
|
||||
const onConfigure = nodeType.prototype.onConfigure;
|
||||
|
||||
+1
-10
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
import torch
|
||||
|
||||
class ResolutionSwitcher:
|
||||
@@ -21,8 +22,6 @@ class ResolutionSwitcher:
|
||||
CATEGORY = "latent"
|
||||
|
||||
def build(self, presets="[]"):
|
||||
import json
|
||||
|
||||
# Default: 1024x1024, batch 1
|
||||
width, height, batch = 1024, 1024, 1
|
||||
|
||||
@@ -49,11 +48,3 @@ class ResolutionSwitcher:
|
||||
|
||||
latent = torch.zeros([batch, 4, height // 8, width // 8])
|
||||
return ({"samples": latent},)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"ResolutionSwitcher": ResolutionSwitcher,
|
||||
}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ResolutionSwitcher": "Resolution Switcher",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user