Add files via upload
This commit is contained in:
+8
-4
@@ -1,5 +1,8 @@
|
||||
import os
|
||||
import traceback
|
||||
|
||||
__version__ = "0.05.31"
|
||||
|
||||
from server import PromptServer
|
||||
from aiohttp import web
|
||||
|
||||
@@ -58,6 +61,7 @@ try:
|
||||
XB_WanAnimateToVideo)
|
||||
|
||||
# --- ROCm 节点:8个自包含节点 ---
|
||||
from .nodes_label import XB_CanvasLabel
|
||||
from .nodes_segmentation import XB_HumanSegModelLoader, XB_HumanSegmentation
|
||||
from .nodes_rocm import (XB_ROCmKSampler, XB_ROCmKSamplerAdvanced,
|
||||
XB_ROCmSamplerCustom, XB_ROCmSamplerCustomAdvanced,
|
||||
@@ -110,7 +114,8 @@ try:
|
||||
"XB_WanAnimate_ParamBus": XB_WanAnimate_ParamBus,
|
||||
"XB_WanAnimate_RelayNode": XB_WanAnimate_RelayNode,
|
||||
"XB_HumanSegmentation": XB_HumanSegmentation,
|
||||
"XB_HumanSegModelLoader": XB_HumanSegModelLoader
|
||||
"XB_HumanSegModelLoader": XB_HumanSegModelLoader,
|
||||
"XB_CanvasLabel": XB_CanvasLabel
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
@@ -156,11 +161,10 @@ try:
|
||||
"XB_WanAnimate_ParamBus": "XB-BOX - 🎬 Animate 动作迁移总线",
|
||||
"XB_WanAnimate_RelayNode": "XB-BOX - 🏃♀️ Animate 无限接力点",
|
||||
"XB_HumanSegmentation": "XB-BOX - ✂️ 人物分割 (DirectML/ROCm)",
|
||||
"XB_HumanSegModelLoader": "XB-BOX - 📥 人物分割模型加载"
|
||||
"XB_HumanSegModelLoader": "XB-BOX - 📥 人物分割模型加载",
|
||||
"XB_CanvasLabel": "XB-BOX - 🏷️ Canvas Label (文字标签)"
|
||||
}
|
||||
|
||||
print_success(" 🚀 ROCm: KSampler | KSamplerAdvanced | SamplerCustom | SamplerCustomAdvanced | VAE Decode | VAE Encode | VAE Decode Temporal | MemCleaner")
|
||||
|
||||
print_success("\n" + "="*50)
|
||||
print_success("🚀 [XB-BOX] XB_ToolBox Core Modules Loaded Successfully!")
|
||||
print_success("="*50 + "\n")
|
||||
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
// ============================================================
|
||||
// XB_CanvasLabel — 文字标签节点
|
||||
// ============================================================
|
||||
|
||||
// ---- 1. 劫持 drawNode(隐藏默认边框/标题)----
|
||||
const oldDrawNode = LGraphCanvas.prototype.drawNode;
|
||||
LGraphCanvas.prototype.drawNode = function (node, ctx) {
|
||||
if (node.comfyClass !== "XB_CanvasLabel" && node.type !== "XB_CanvasLabel")
|
||||
return oldDrawNode.apply(this, arguments);
|
||||
|
||||
node.bgcolor = node.color = "transparent";
|
||||
const savedTitle = node.title, savedCtor = node.constructor.title;
|
||||
node.title = node.constructor.title = "";
|
||||
const result = oldDrawNode.apply(this, arguments);
|
||||
node.title = savedTitle;
|
||||
node.constructor.title = savedCtor;
|
||||
if (node.draw) node.draw(ctx);
|
||||
return result;
|
||||
};
|
||||
|
||||
// ---- 2. 注册扩展 ----
|
||||
app.registerExtension({
|
||||
name: "XB_ToolBox.CanvasLabel",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData) {
|
||||
if (nodeData.name !== "XB_CanvasLabel") return;
|
||||
|
||||
nodeData.title_mode = LiteGraph.NO_TITLE;
|
||||
nodeData.collapsable = false;
|
||||
|
||||
// 属性面板定义(颜色不设 @ → 不会在画布上生成输入框)
|
||||
nodeData["@fontSize"] = { type: "number", default: 40 };
|
||||
nodeData["@textAlign"] = { type: "combo", values: ["left", "center", "right"], default: "left" };
|
||||
nodeData["@padding"] = { type: "number", default: 0 };
|
||||
nodeData["@borderRadius"] = { type: "number", default: 0 };
|
||||
nodeData["@angle"] = { type: "number", default: 0 };
|
||||
|
||||
// ---- onNodeCreated ----
|
||||
const origOnNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
origOnNodeCreated?.apply(this, arguments);
|
||||
|
||||
const p = this.properties = this.properties || {};
|
||||
p.fontSize = p.fontSize ?? 40;
|
||||
p.fontColor = p.fontColor ?? "#ffffff";
|
||||
p.textAlign = p.textAlign ?? "left";
|
||||
p.backgroundColor = p.backgroundColor ?? "transparent";
|
||||
p.padding = p.padding ?? 0;
|
||||
p.borderRadius = p.borderRadius ?? 0;
|
||||
p.angle = p.angle ?? 0;
|
||||
|
||||
this.title = this.title || "机智罗";
|
||||
this.size = [320, 80];
|
||||
};
|
||||
|
||||
// ---- draw():自定义绘制 ----
|
||||
nodeType.prototype.draw = function (ctx) {
|
||||
if (!this.flags) this.flags = {};
|
||||
this.flags.allow_interaction = !this.flags.pinned;
|
||||
ctx.save();
|
||||
|
||||
const p = this.properties || {};
|
||||
const fontSize = Math.max(1, Math.min(800, +p.fontSize || 40));
|
||||
const fontColor = p.fontColor || "#ffffff";
|
||||
const textAlign = p.textAlign || "left";
|
||||
const bgColor = p.backgroundColor || "";
|
||||
const pad = +p.padding || 0;
|
||||
const borderRadius = +p.borderRadius || 0;
|
||||
const angleDeg = parseInt(p.angle) || 0;
|
||||
|
||||
ctx.font = fontSize + "px Arial";
|
||||
|
||||
const raw = (this.title || "").replace(/\\n/g, "\n").replace(/\n*$/, "");
|
||||
const lines = raw.length === 0 ? [""] : raw.split("\n");
|
||||
|
||||
let maxW = 1;
|
||||
lines.forEach(s => { const w = ctx.measureText(s).width; if (w > maxW) maxW = w; });
|
||||
this.size[0] = maxW + pad * 2;
|
||||
this.size[1] = fontSize * lines.length + pad * 2;
|
||||
|
||||
if (angleDeg) {
|
||||
const cx = this.size[0] / 2, cy = this.size[1] / 2;
|
||||
ctx.translate(cx, cy);
|
||||
ctx.rotate((angleDeg * Math.PI) / 180);
|
||||
ctx.translate(-cx, -cy);
|
||||
}
|
||||
|
||||
if (bgColor && bgColor !== "transparent") {
|
||||
ctx.fillStyle = bgColor;
|
||||
ctx.beginPath();
|
||||
if (ctx.roundRect) {
|
||||
ctx.roundRect(0, 0, this.size[0], this.size[1], [borderRadius]);
|
||||
} else if (borderRadius > 0) {
|
||||
const r = borderRadius, w = this.size[0], h = this.size[1];
|
||||
ctx.moveTo(r, 0); ctx.lineTo(w - r, 0);
|
||||
ctx.quadraticCurveTo(w, 0, w, r); ctx.lineTo(w, h - r);
|
||||
ctx.quadraticCurveTo(w, h, w - r, h); ctx.lineTo(r, h);
|
||||
ctx.quadraticCurveTo(0, h, 0, h - r); ctx.lineTo(0, r);
|
||||
ctx.quadraticCurveTo(0, 0, r, 0);
|
||||
} else {
|
||||
ctx.rect(0, 0, this.size[0], this.size[1]);
|
||||
}
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
ctx.fillStyle = fontColor;
|
||||
ctx.textBaseline = "top";
|
||||
ctx.textAlign = textAlign;
|
||||
|
||||
let tx = textAlign === "center" ? this.size[0] / 2 : textAlign === "right" ? this.size[0] - pad : pad;
|
||||
lines.forEach((line, i) => ctx.fillText(line || " ", tx, pad + i * fontSize));
|
||||
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
// ---- 双击 → 属性面板 ----
|
||||
const origDblClick = nodeType.prototype.onDblClick;
|
||||
nodeType.prototype.onDblClick = function (e, pos, gc) {
|
||||
if (origDblClick) { const r = origDblClick.apply(this, arguments); if (r !== undefined && r !== false) return r; }
|
||||
LGraphCanvas.active_canvas.showShowNodePanel(this);
|
||||
return true;
|
||||
};
|
||||
|
||||
// ---- 右键菜单 ----
|
||||
const origMenu = nodeType.prototype.getExtraMenuOptions;
|
||||
nodeType.prototype.getExtraMenuOptions = function (_, options) {
|
||||
origMenu?.apply(this, arguments);
|
||||
const self = this;
|
||||
options.unshift(
|
||||
{ content: "✏️ 编辑文字", callback: () => {
|
||||
const old = self.title || "";
|
||||
const val = prompt("输入文字(\\n 换行):", old);
|
||||
if (val !== null && val !== old) {
|
||||
self.title = val || "机智罗";
|
||||
self.setDirtyCanvas(true, true);
|
||||
app.graph.setDirtyCanvas(true, false);
|
||||
}
|
||||
}},
|
||||
{ content: "🎨 字体颜色…", callback: () => {
|
||||
const inp = document.createElement("input");
|
||||
inp.type = "color";
|
||||
inp.value = self.properties.fontColor || "#ffffff";
|
||||
inp.style.cssText = "position:fixed;top:50%;left:50%;width:1px;height:1px;opacity:0.01;z-index:99999;";
|
||||
document.body.appendChild(inp);
|
||||
inp.oninput = function () {
|
||||
self.properties.fontColor = this.value;
|
||||
self.setDirtyCanvas(true, true);
|
||||
app.graph.setDirtyCanvas(true, false);
|
||||
};
|
||||
inp.onchange = function () {
|
||||
self.properties.fontColor = this.value;
|
||||
self.setDirtyCanvas(true, true);
|
||||
app.graph.setDirtyCanvas(true, false);
|
||||
if (document.body.contains(this)) document.body.removeChild(this);
|
||||
};
|
||||
requestAnimationFrame(() => inp.click());
|
||||
}},
|
||||
{ content: "🖌️ 背景颜色…", callback: () => {
|
||||
const inp = document.createElement("input");
|
||||
inp.type = "color";
|
||||
inp.value = self.properties.backgroundColor === "transparent" ? "#000000" : (self.properties.backgroundColor || "#000000");
|
||||
inp.style.cssText = "position:fixed;top:50%;left:50%;width:1px;height:1px;opacity:0.01;z-index:99999;";
|
||||
document.body.appendChild(inp);
|
||||
inp.oninput = function () {
|
||||
self.properties.backgroundColor = this.value;
|
||||
self.setDirtyCanvas(true, true);
|
||||
app.graph.setDirtyCanvas(true, false);
|
||||
};
|
||||
inp.onchange = function () {
|
||||
self.properties.backgroundColor = this.value;
|
||||
self.setDirtyCanvas(true, true);
|
||||
app.graph.setDirtyCanvas(true, false);
|
||||
if (document.body.contains(this)) document.body.removeChild(this);
|
||||
};
|
||||
requestAnimationFrame(() => inp.click());
|
||||
}}
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
+11
-9
@@ -16,8 +16,12 @@ class AnyType(str):
|
||||
return False
|
||||
anyType = AnyType("*")
|
||||
|
||||
def is_dynamic_vram_flag_used():
|
||||
return "--enable-dynamic-vram" in sys.argv
|
||||
def is_dynamic_vram_active():
|
||||
"""仅当用户显式传参 --enable-dynamic-vram 时视为动态显存激活,分块节点休眠。"""
|
||||
if "--enable-dynamic-vram" in sys.argv:
|
||||
print("\n\033[93m[XB Block Swap]\033[0m: ⚠️ 检测到 --enable-dynamic-vram,动态显存已启用。分块节点自动休眠。")
|
||||
return True
|
||||
return False
|
||||
|
||||
# ==============================================================================
|
||||
# 🛡️ 核心防爆雷达:判定是否为不可分块的异常/强耦合模型
|
||||
@@ -56,8 +60,7 @@ class XB_UNetBlockSwap:
|
||||
if not isinstance(unet_model, ModelPatcher):
|
||||
return (unet_model,)
|
||||
|
||||
if is_dynamic_vram_flag_used():
|
||||
print("\n\033[92m[XB UNet Block Swap]\033[0m: ⚠️ Detected parameter [--enable-dynamic-vram], block swap node auto-sleeping.")
|
||||
if is_dynamic_vram_active():
|
||||
return (unet_model,)
|
||||
|
||||
def swap_blocks(model_patcher: ModelPatcher, device_to, lowvram_model_memory, force_patch_weights, full_load):
|
||||
@@ -93,7 +96,7 @@ class XB_UNetBlockSwap:
|
||||
if not all_blocks:
|
||||
return
|
||||
|
||||
print(f"\033[96m[XB UNet Block Swap]\033[0m: Static physical block swap activated! Locked {len(all_blocks)} engine modules.")
|
||||
print(f"\033[96m[XB UNet Block Swap]\033[0m: 静态物理分块交换已激活!已锁定 {len(all_blocks)} 个引擎模块。")
|
||||
|
||||
for b, block in tqdm(enumerate(all_blocks), total=len(all_blocks), desc="Slicing UNet pipeline"):
|
||||
if b > blocks_to_swap:
|
||||
@@ -131,8 +134,7 @@ class XB_CheckpointBlockSwap:
|
||||
if not isinstance(checkpoint_model, ModelPatcher):
|
||||
return (checkpoint_model,)
|
||||
|
||||
if is_dynamic_vram_flag_used():
|
||||
print("\n\033[92m[XB Checkpoint Block Swap]\033[0m: ⚠️ Detected parameter [--enable-dynamic-vram], block swap node auto-sleeping.")
|
||||
if is_dynamic_vram_active():
|
||||
return (checkpoint_model,)
|
||||
|
||||
def swap_blocks(model_patcher: ModelPatcher, device_to, lowvram_model_memory, force_patch_weights, full_load):
|
||||
@@ -166,12 +168,12 @@ class XB_CheckpointBlockSwap:
|
||||
all_blocks.append(attr)
|
||||
|
||||
if all_blocks:
|
||||
print(f"\033[96m[XB Checkpoint Block Swap]\033[0m: Static physical block swap activated! Locked {len(all_blocks)} engine modules.")
|
||||
print(f"\033[96m[XB Checkpoint Block Swap]\033[0m: 静态物理分块交换已激活!已锁定 {len(all_blocks)} 个引擎模块。")
|
||||
for b, block in tqdm(enumerate(all_blocks), total=len(all_blocks), desc="Slicing Checkpoint pipeline"):
|
||||
if b > blocks_to_swap:
|
||||
block.to(main_device)
|
||||
else:
|
||||
block.to(model_patcher.offload_device)
|
||||
block.to(model_patcher.offload_device)
|
||||
|
||||
if offload_txt_emb:
|
||||
for path in ['text_embedding', 'caption_encoder', 'text_encoder']:
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
XB-BOX Canvas Label — 画布文字标签节点
|
||||
======================================
|
||||
纯展示型节点,用于在工作流画布上添加说明文字。
|
||||
所有渲染逻辑由前端 JS (xb_label.js) 处理。
|
||||
"""
|
||||
|
||||
|
||||
class XB_CanvasLabel:
|
||||
"""纯显示标签节点 - 在画布上展示自定义文字,兼容所有 ComfyUI 版本"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {}}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
FUNCTION = "noop"
|
||||
CATEGORY = "XB_ToolBox/Display"
|
||||
|
||||
def noop(self, **kwargs):
|
||||
return ()
|
||||
Reference in New Issue
Block a user