Files
CraftopiaStudio-ComfyUI-Cra…/js/smart_profile_switch.js
T
rubz 1750601338 fix: stale-skip detection, alpha/format controls, and remaining Minor issues from code review
- SmartBatchResize: only skip existing files if their actual dimensions still match current settings (was serving stale sizes after a settings change)
- SmartBatchResize: add alpha_mode (auto/flatten/keep) and output_format (keep_source/png/webp/jpg) with a quality control, replacing the undocumented silent alpha loss
- SmartProfileSwitch: warn when index is out of range instead of silently reading as slot 1 (catches an unconnected Smart Prompt Controller)
- SmartProfileSwitch JS: guard widget reorder against missing lookups, append rather than drop unrecognized widgets
- SmartPromptController JS: add the serialize override the other node JS files already have, preventing widget value drift
- Remove js/smart_batch_controller.js (dead code, referenced a node class that no longer exists)
- Untrack .claude/worktrees/* accidentally committed earlier; add .claude/ to .gitignore
2026-07-27 20:20:41 +02:00

112 lines
4.5 KiB
JavaScript

import { app } from "../../scripts/app.js";
app.registerExtension({
name: "Craftopia.SmartProfileSwitch",
async nodeCreated(node) {
if (node.comfyClass !== "SmartProfileSwitch") return;
// Slot header dividers — visual only, not serialized
const dividers = [];
for (let i = 1; i <= 4; i++) {
const div = node.addWidget("button", `_div_${i}`, null, () => {}, { serialize: false });
div.serialize = false;
const num = i;
div.draw = function (ctx, node, widgetWidth, y, height) {
const lx = 14;
const label = `SLOT ${num}`;
ctx.save();
ctx.font = "bold 10px sans-serif";
ctx.textBaseline = "middle";
ctx.textAlign = "left";
ctx.fillStyle = "#888";
ctx.fillText(label, lx, y + height / 2);
const lineX = lx + ctx.measureText(label).width + 8;
ctx.beginPath();
ctx.moveTo(lineX, y + height / 2);
ctx.lineTo(widgetWidth - 14, y + height / 2);
ctx.strokeStyle = "#333";
ctx.lineWidth = 1;
ctx.stroke();
ctx.restore();
};
dividers.push(div);
}
// Status widget — shows active slot after execution
const statusWidget = node.addWidget("button", "_sps_status", null, () => {}, { serialize: false });
statusWidget.serialize = false;
statusWidget._text = "";
statusWidget.draw = function (ctx, node, widgetWidth, y, height) {
const margin = 14;
const innerW = widgetWidth - margin * 2;
ctx.save();
ctx.beginPath();
if (ctx.roundRect) ctx.roundRect(margin, y + 2, innerW, height - 4, 4);
else ctx.rect(margin, y + 2, innerW, height - 4);
ctx.fillStyle = "#111";
ctx.fill();
ctx.strokeStyle = this._text ? "#3a3a3a" : "#222";
ctx.lineWidth = 1;
ctx.stroke();
ctx.font = "11px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = this._text ? "#f28f41" : "#666";
ctx.fillText(
this._text || "— RUN TO SEE RESULT —",
widgetWidth / 2,
y + height / 2
);
ctx.restore();
};
// Reorder: index, [div1, label_1, w_1, h_1], ..., [div4, label_4, w_4, h_4], status
const indexWidget = node.widgets.find(w => w.name === "index");
const slotWidgets = [];
for (let i = 1; i <= 4; i++) {
slotWidgets.push(
dividers[i - 1],
node.widgets.find(w => w.name === `label_${i}`),
node.widgets.find(w => w.name === `width_${i}`),
node.widgets.find(w => w.name === `height_${i}`),
);
}
// Guard against a lookup returning undefined (e.g. a widget converted to
// an input) and never drop a widget we didn't explicitly place — append
// anything unrecognized at the end instead of silently discarding it.
const orderedWidgets = [indexWidget, ...slotWidgets, statusWidget].filter(Boolean);
const orderedSet = new Set(orderedWidgets);
const leftoverWidgets = node.widgets.filter(w => !orderedSet.has(w));
node.widgets = [...orderedWidgets, ...leftoverWidgets];
// Serialize override — keeps non-serializable widgets out of widgets_values
const origSerialize = node.serialize;
node.serialize = function () {
const data = origSerialize.call(this);
if (data.widgets_values) {
data.widgets_values = this.widgets
.filter(w => w.serialize !== false && w.options?.serialize !== false)
.map(w => w.value);
}
return data;
};
// onExecuted — update status, hide default customtext textarea
const origOnExecuted = node.onExecuted;
node.onExecuted = function (output) {
origOnExecuted?.call(this, output);
if (output?.text?.[0]) {
statusWidget._text = output.text[0];
for (const w of node.widgets) {
if (w !== statusWidget && w.type === "customtext") {
w.computeSize = () => [0, -4];
}
}
}
node.setDirtyCanvas(true);
};
},
});