185 lines
5.7 KiB
JavaScript
185 lines
5.7 KiB
JavaScript
import { app } from "../../../scripts/app.js";
|
|
|
|
function normalizeHexColor(value, fallback = "#000000") {
|
|
if (typeof value !== "string") {
|
|
return fallback;
|
|
}
|
|
|
|
const normalized = value.startsWith("#") ? value : `#${value}`;
|
|
return /^#[0-9a-fA-F]{6}$/.test(normalized) ? normalized : fallback;
|
|
}
|
|
|
|
function getBrightness(hexColor) {
|
|
const hex = normalizeHexColor(hexColor).slice(1);
|
|
const r = parseInt(hex.slice(0, 2), 16);
|
|
const g = parseInt(hex.slice(2, 4), 16);
|
|
const b = parseInt(hex.slice(4, 6), 16);
|
|
return (r * 299 + g * 587 + b * 114) / 1000;
|
|
}
|
|
|
|
function createColorWidget(name, value) {
|
|
const widget = {
|
|
name,
|
|
type: "SMART_COLOR",
|
|
value: normalizeHexColor(value),
|
|
options: { default: "#000000" },
|
|
draw(ctx, node, widgetWidth, widgetY, height) {
|
|
const border = 3;
|
|
|
|
ctx.fillStyle = "#000";
|
|
ctx.fillRect(0, widgetY, widgetWidth, height);
|
|
|
|
ctx.fillStyle = this.value;
|
|
ctx.fillRect(
|
|
border,
|
|
widgetY + border,
|
|
widgetWidth - border * 2,
|
|
height - border * 2
|
|
);
|
|
|
|
ctx.fillStyle = getBrightness(this.value) > 125 ? "#000" : "#fff";
|
|
ctx.font = "14px Arial";
|
|
ctx.textAlign = "center";
|
|
ctx.fillText(this.name, widgetWidth * 0.5, widgetY + 14);
|
|
},
|
|
mouse(event, pos, node) {
|
|
if (event.type !== "pointerdown") {
|
|
return false;
|
|
}
|
|
|
|
const widgets = node.widgets.filter((widgetItem) => widgetItem.type === "SMART_COLOR");
|
|
|
|
for (const colorWidget of widgets) {
|
|
const rect = [colorWidget.last_y, colorWidget.last_y + 32];
|
|
if (pos[1] <= rect[0] || pos[1] >= rect[1]) {
|
|
continue;
|
|
}
|
|
|
|
const picker = document.createElement("input");
|
|
picker.type = "color";
|
|
picker.value = normalizeHexColor(this.value);
|
|
|
|
Object.assign(picker.style, {
|
|
position: "fixed",
|
|
left: `${event.clientX}px`,
|
|
top: `${event.clientY}px`,
|
|
height: "0px",
|
|
width: "0px",
|
|
padding: "0px",
|
|
opacity: 0,
|
|
});
|
|
|
|
picker.addEventListener("blur", () => {
|
|
this.callback?.(this.value);
|
|
node.graph._version++;
|
|
picker.remove();
|
|
});
|
|
|
|
picker.addEventListener("input", () => {
|
|
if (!picker.value) {
|
|
return;
|
|
}
|
|
|
|
this.value = normalizeHexColor(picker.value, this.value);
|
|
app.canvas.setDirty(true);
|
|
});
|
|
|
|
document.body.appendChild(picker);
|
|
|
|
requestAnimationFrame(() => {
|
|
if (picker.showPicker) {
|
|
picker.showPicker();
|
|
} else {
|
|
picker.click();
|
|
}
|
|
picker.focus();
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
computeSize(width) {
|
|
return [width, 32];
|
|
},
|
|
};
|
|
|
|
return widget;
|
|
}
|
|
|
|
function hideWidget(node, widget) {
|
|
if (widget._hidden) return;
|
|
widget._hidden = true;
|
|
widget._origType = widget.type;
|
|
widget._origComputeSize = widget.computeSize;
|
|
widget.type = "hidden";
|
|
widget.computeSize = () => [0, -4];
|
|
}
|
|
|
|
function showWidget(node, widget) {
|
|
if (!widget._hidden) return;
|
|
widget._hidden = false;
|
|
widget.type = widget._origType;
|
|
widget.computeSize = widget._origComputeSize;
|
|
}
|
|
|
|
function updateColorVisibility(node) {
|
|
const colorsWidget = node.widgets?.find(w => w.name === "colors");
|
|
if (!colorsWidget) return;
|
|
|
|
const count = colorsWidget.value;
|
|
for (let i = 1; i <= 5; i++) {
|
|
const cw = node.widgets.find(w => w.name === `color_${i}`);
|
|
if (!cw) continue;
|
|
if (i <= count) {
|
|
showWidget(node, cw);
|
|
} else {
|
|
hideWidget(node, cw);
|
|
}
|
|
}
|
|
|
|
node.setSize(node.computeSize());
|
|
app.canvas.setDirty(true);
|
|
}
|
|
|
|
app.registerExtension({
|
|
name: "Comfy.SmartNodes.SmartColorWidget",
|
|
getCustomWidgets() {
|
|
return {
|
|
SMART_COLOR: (node, inputName, inputData) => ({
|
|
widget: node.addCustomWidget(
|
|
createColorWidget(inputName, inputData?.[1]?.default || "#000000")
|
|
),
|
|
minWidth: 150,
|
|
minHeight: 32,
|
|
}),
|
|
};
|
|
},
|
|
beforeRegisterNodeDef(nodeType, nodeData) {
|
|
if (nodeData.name !== "SmartImagePaletteCreate") return;
|
|
|
|
const origOnNodeCreated = nodeType.prototype.onNodeCreated;
|
|
nodeType.prototype.onNodeCreated = function () {
|
|
origOnNodeCreated?.apply(this, arguments);
|
|
|
|
const colorsWidget = this.widgets?.find(w => w.name === "colors");
|
|
if (colorsWidget) {
|
|
const origCallback = colorsWidget.callback;
|
|
colorsWidget.callback = (value) => {
|
|
origCallback?.call(colorsWidget, value);
|
|
updateColorVisibility(this);
|
|
};
|
|
}
|
|
|
|
requestAnimationFrame(() => updateColorVisibility(this));
|
|
};
|
|
|
|
const origOnConfigure = nodeType.prototype.onConfigure;
|
|
nodeType.prototype.onConfigure = function (data) {
|
|
origOnConfigure?.apply(this, arguments);
|
|
requestAnimationFrame(() => updateColorVisibility(this));
|
|
};
|
|
},
|
|
});
|