Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Includes 30 custom nodes committed directly, 7 Civitai-exclusive loras stored via Git LFS, and a setup script that installs all dependencies and downloads HuggingFace-hosted models on vast.ai. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
292 lines
11 KiB
JavaScript
292 lines
11 KiB
JavaScript
import { app } from "../../scripts/app.js";
|
|
import { drawNodeWidget, drawWidgetButton, fitString, isLowQuality } from "./utils_canvas.js";
|
|
export function drawLabelAndValue(ctx, label, value, width, posY, height, options) {
|
|
var _a;
|
|
const outerMargin = 15;
|
|
const innerMargin = 10;
|
|
const midY = posY + height / 2;
|
|
ctx.save();
|
|
ctx.textAlign = "left";
|
|
ctx.textBaseline = "middle";
|
|
ctx.fillStyle = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR;
|
|
const labelX = outerMargin + innerMargin + ((_a = options === null || options === void 0 ? void 0 : options.offsetLeft) !== null && _a !== void 0 ? _a : 0);
|
|
ctx.fillText(label, labelX, midY);
|
|
const valueXLeft = labelX + ctx.measureText(label).width + 7;
|
|
const valueXRight = width - (outerMargin + innerMargin);
|
|
ctx.fillStyle = LiteGraph.WIDGET_TEXT_COLOR;
|
|
ctx.textAlign = "right";
|
|
ctx.fillText(fitString(ctx, value, valueXRight - valueXLeft), valueXRight, midY);
|
|
ctx.restore();
|
|
}
|
|
export class RgthreeBaseWidget {
|
|
constructor(name) {
|
|
this.type = "custom";
|
|
this.options = {};
|
|
this.y = 0;
|
|
this.last_y = 0;
|
|
this.mouseDowned = null;
|
|
this.isMouseDownedAndOver = false;
|
|
this.hitAreas = {};
|
|
this.downedHitAreasForMove = [];
|
|
this.downedHitAreasForClick = [];
|
|
this.name = name;
|
|
}
|
|
serializeValue(node, index) {
|
|
return this.value;
|
|
}
|
|
clickWasWithinBounds(pos, bounds) {
|
|
let xStart = bounds[0];
|
|
let xEnd = xStart + (bounds.length > 2 ? bounds[2] : bounds[1]);
|
|
const clickedX = pos[0] >= xStart && pos[0] <= xEnd;
|
|
if (bounds.length === 2) {
|
|
return clickedX;
|
|
}
|
|
return clickedX && pos[1] >= bounds[1] && pos[1] <= bounds[1] + bounds[3];
|
|
}
|
|
mouse(event, pos, node) {
|
|
var _a, _b, _c;
|
|
const canvas = app.canvas;
|
|
if (event.type == "pointerdown") {
|
|
this.mouseDowned = [...pos];
|
|
this.isMouseDownedAndOver = true;
|
|
this.downedHitAreasForMove.length = 0;
|
|
this.downedHitAreasForClick.length = 0;
|
|
let anyHandled = false;
|
|
for (const part of Object.values(this.hitAreas)) {
|
|
if (this.clickWasWithinBounds(pos, part.bounds)) {
|
|
if (part.onMove) {
|
|
this.downedHitAreasForMove.push(part);
|
|
}
|
|
if (part.onClick) {
|
|
this.downedHitAreasForClick.push(part);
|
|
}
|
|
if (part.onDown) {
|
|
const thisHandled = part.onDown.apply(this, [event, pos, node, part]);
|
|
anyHandled = anyHandled || thisHandled == true;
|
|
}
|
|
part.wasMouseClickedAndIsOver = true;
|
|
}
|
|
}
|
|
return (_a = this.onMouseDown(event, pos, node)) !== null && _a !== void 0 ? _a : anyHandled;
|
|
}
|
|
if (event.type == "pointerup") {
|
|
if (!this.mouseDowned)
|
|
return true;
|
|
this.downedHitAreasForMove.length = 0;
|
|
const wasMouseDownedAndOver = this.isMouseDownedAndOver;
|
|
this.cancelMouseDown();
|
|
let anyHandled = false;
|
|
for (const part of Object.values(this.hitAreas)) {
|
|
if (part.onUp && this.clickWasWithinBounds(pos, part.bounds)) {
|
|
const thisHandled = part.onUp.apply(this, [event, pos, node, part]);
|
|
anyHandled = anyHandled || thisHandled == true;
|
|
}
|
|
part.wasMouseClickedAndIsOver = false;
|
|
}
|
|
for (const part of this.downedHitAreasForClick) {
|
|
if (this.clickWasWithinBounds(pos, part.bounds)) {
|
|
const thisHandled = part.onClick.apply(this, [event, pos, node, part]);
|
|
anyHandled = anyHandled || thisHandled == true;
|
|
}
|
|
}
|
|
this.downedHitAreasForClick.length = 0;
|
|
if (wasMouseDownedAndOver) {
|
|
const thisHandled = this.onMouseClick(event, pos, node);
|
|
anyHandled = anyHandled || thisHandled == true;
|
|
}
|
|
return (_b = this.onMouseUp(event, pos, node)) !== null && _b !== void 0 ? _b : anyHandled;
|
|
}
|
|
if (event.type == "pointermove") {
|
|
this.isMouseDownedAndOver = !!this.mouseDowned;
|
|
if (this.mouseDowned &&
|
|
(pos[0] < 15 ||
|
|
pos[0] > node.size[0] - 15 ||
|
|
pos[1] < this.last_y ||
|
|
pos[1] > this.last_y + LiteGraph.NODE_WIDGET_HEIGHT)) {
|
|
this.isMouseDownedAndOver = false;
|
|
}
|
|
for (const part of Object.values(this.hitAreas)) {
|
|
if (this.downedHitAreasForMove.includes(part)) {
|
|
part.onMove.apply(this, [event, pos, node, part]);
|
|
}
|
|
if (this.downedHitAreasForClick.includes(part)) {
|
|
part.wasMouseClickedAndIsOver = this.clickWasWithinBounds(pos, part.bounds);
|
|
}
|
|
}
|
|
return (_c = this.onMouseMove(event, pos, node)) !== null && _c !== void 0 ? _c : true;
|
|
}
|
|
return false;
|
|
}
|
|
cancelMouseDown() {
|
|
this.mouseDowned = null;
|
|
this.isMouseDownedAndOver = false;
|
|
this.downedHitAreasForMove.length = 0;
|
|
}
|
|
onMouseDown(event, pos, node) {
|
|
return;
|
|
}
|
|
onMouseUp(event, pos, node) {
|
|
return;
|
|
}
|
|
onMouseClick(event, pos, node) {
|
|
return;
|
|
}
|
|
onMouseMove(event, pos, node) {
|
|
return;
|
|
}
|
|
}
|
|
export class RgthreeBetterButtonWidget extends RgthreeBaseWidget {
|
|
constructor(name, mouseClickCallback, label) {
|
|
super(name);
|
|
this.type = "custom";
|
|
this.value = "";
|
|
this.label = "";
|
|
this.mouseClickCallback = mouseClickCallback;
|
|
this.label = label || name;
|
|
}
|
|
draw(ctx, node, width, y, height) {
|
|
drawWidgetButton(ctx, { size: [width - 30, height], pos: [15, y] }, this.label, this.isMouseDownedAndOver);
|
|
}
|
|
onMouseClick(event, pos, node) {
|
|
return this.mouseClickCallback(event, pos, node);
|
|
}
|
|
}
|
|
export class RgthreeBetterTextWidget extends RgthreeBaseWidget {
|
|
constructor(name, value) {
|
|
super(name);
|
|
this.name = name;
|
|
this.value = value;
|
|
}
|
|
draw(ctx, node, width, y, height) {
|
|
const widgetData = drawNodeWidget(ctx, { size: [width, height], pos: [15, y] });
|
|
if (!widgetData.lowQuality) {
|
|
drawLabelAndValue(ctx, this.name, this.value, width, y, height);
|
|
}
|
|
}
|
|
mouse(event, pos, node) {
|
|
const canvas = app.canvas;
|
|
if (event.type == "pointerdown") {
|
|
canvas.prompt("Label", this.value, (v) => (this.value = v), event);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
export class RgthreeDividerWidget extends RgthreeBaseWidget {
|
|
constructor(widgetOptions) {
|
|
super("divider");
|
|
this.value = {};
|
|
this.options = { serialize: false };
|
|
this.type = "custom";
|
|
this.widgetOptions = {
|
|
marginTop: 7,
|
|
marginBottom: 7,
|
|
marginLeft: 15,
|
|
marginRight: 15,
|
|
color: LiteGraph.WIDGET_OUTLINE_COLOR,
|
|
thickness: 1,
|
|
};
|
|
Object.assign(this.widgetOptions, widgetOptions || {});
|
|
}
|
|
draw(ctx, node, width, posY, h) {
|
|
if (this.widgetOptions.thickness) {
|
|
ctx.strokeStyle = this.widgetOptions.color;
|
|
const x = this.widgetOptions.marginLeft;
|
|
const y = posY + this.widgetOptions.marginTop;
|
|
const w = width - this.widgetOptions.marginLeft - this.widgetOptions.marginRight;
|
|
ctx.stroke(new Path2D(`M ${x} ${y} h ${w}`));
|
|
}
|
|
}
|
|
computeSize(width) {
|
|
return [
|
|
width,
|
|
this.widgetOptions.marginTop + this.widgetOptions.marginBottom + this.widgetOptions.thickness,
|
|
];
|
|
}
|
|
}
|
|
export class RgthreeLabelWidget extends RgthreeBaseWidget {
|
|
constructor(name, widgetOptions) {
|
|
super(name);
|
|
this.type = "custom";
|
|
this.options = { serialize: false };
|
|
this.value = "";
|
|
this.widgetOptions = {};
|
|
this.posY = 0;
|
|
Object.assign(this.widgetOptions, widgetOptions);
|
|
}
|
|
update(widgetOptions) {
|
|
Object.assign(this.widgetOptions, widgetOptions);
|
|
}
|
|
draw(ctx, node, width, posY, height) {
|
|
var _a;
|
|
this.posY = posY;
|
|
ctx.save();
|
|
let text = (_a = this.widgetOptions.text) !== null && _a !== void 0 ? _a : this.name;
|
|
if (typeof text === "function") {
|
|
text = text();
|
|
}
|
|
ctx.textAlign = this.widgetOptions.align || "left";
|
|
ctx.fillStyle = this.widgetOptions.color || LiteGraph.WIDGET_TEXT_COLOR;
|
|
const oldFont = ctx.font;
|
|
if (this.widgetOptions.italic) {
|
|
ctx.font = "italic " + ctx.font;
|
|
}
|
|
if (this.widgetOptions.size) {
|
|
ctx.font = ctx.font.replace(/\d+px/, `${this.widgetOptions.size}px`);
|
|
}
|
|
const midY = posY + height / 2;
|
|
ctx.textBaseline = "middle";
|
|
if (this.widgetOptions.align === "center") {
|
|
ctx.fillText(text, node.size[0] / 2, midY);
|
|
}
|
|
else {
|
|
ctx.fillText(text, 15, midY);
|
|
}
|
|
ctx.font = oldFont;
|
|
if (this.widgetOptions.actionLabel === "__PLUS_ICON__") {
|
|
const plus = new Path2D(`M${node.size[0] - 15 - 2} ${posY + 7} v4 h-4 v4 h-4 v-4 h-4 v-4 h4 v-4 h4 v4 h4 z`);
|
|
ctx.lineJoin = "round";
|
|
ctx.lineCap = "round";
|
|
ctx.fillStyle = "#3a3";
|
|
ctx.strokeStyle = "#383";
|
|
ctx.fill(plus);
|
|
ctx.stroke(plus);
|
|
}
|
|
ctx.restore();
|
|
}
|
|
mouse(event, nodePos, node) {
|
|
if (event.type !== "pointerdown" ||
|
|
isLowQuality() ||
|
|
!this.widgetOptions.actionLabel ||
|
|
!this.widgetOptions.actionCallback) {
|
|
return false;
|
|
}
|
|
const pos = [nodePos[0], nodePos[1] - this.posY];
|
|
const rightX = node.size[0] - 15;
|
|
if (pos[0] > rightX || pos[0] < rightX - 16) {
|
|
return false;
|
|
}
|
|
this.widgetOptions.actionCallback(event);
|
|
return true;
|
|
}
|
|
}
|
|
export class RgthreeInvisibleWidget extends RgthreeBaseWidget {
|
|
constructor(name, type, value, serializeValueFn) {
|
|
super(name);
|
|
this.type = "custom";
|
|
this.value = value;
|
|
this.serializeValueFn = serializeValueFn;
|
|
}
|
|
draw() {
|
|
return;
|
|
}
|
|
computeSize(width) {
|
|
return [0, 0];
|
|
}
|
|
serializeValue(node, index) {
|
|
return this.serializeValueFn != null
|
|
? this.serializeValueFn(node, index)
|
|
: super.serializeValue(node, index);
|
|
}
|
|
}
|