Add files via upload

This commit is contained in:
wjluoxiao
2026-06-03 08:10:39 +08:00
committed by GitHub
parent 1b0207d21b
commit 16962db9d0
4 changed files with 221 additions and 13 deletions
+8 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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']:
+21
View File
@@ -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 ()