Simplifies the preset-chip picker (Smart Resize / Smart Batch Resize) to a single canvas implementation - drops the dual DOM-widget path, so chips stay positioned correctly under "Longest side (px)" in both classic and Nodes 2.0 rendering instead of jumping to the bottom of the node. Adds a shared setWidgetVisible() helper so dynamic show/hide (Counter start, the hidden customtext output on every node) actually works under Nodes 2.0's Vue reactivity - it previously only worked in classic canvas mode. Fixes two silent Nodes 2.0 rendering bugs: widgets only repaint via their own triggerDraw(), not setDirtyCanvas(), so the active preset chip and status-box text were going visually stale after an update even though the underlying value was correct. And Nodes 2.0's WidgetLegacy bridge permanently stamps a widget.width that classic mode's own click hit-test then reads forever after - once a node had ever rendered under Nodes 2.0, classic mode silently lost clicks on the right side of a wide node. Both fixed by keeping widget state in sync on every draw() instead of trusting it to survive across renderer switches. Also centers section-divider labels with flanking lines (was label-left/line-right) and brightens the line color, which had become nearly invisible under Nodes 2.0's extra canvas scaling. README: reorders node sections into actual pipeline order (Prompt Controller -> Profile Switch -> Resize -> Resolution Multiplier -> Batch Resize, standalone tool last instead of mid-list), adds a Nodes 2.0 compatibility screenshot, and refreshes the Smart Batch Resize screenshot to match its current UI.
73 lines
3.1 KiB
JavaScript
73 lines
3.1 KiB
JavaScript
import { app } from "../../scripts/app.js";
|
|
import { createDividerWidget, createStatusWidget } from "./shared/canvas_widgets.mjs?v=3";
|
|
import { setWidgetVisible } from "./shared/widget_visibility.mjs?v=2";
|
|
|
|
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 = createDividerWidget(`SLOT ${i}`);
|
|
node.addCustomWidget(div);
|
|
dividers.push(div);
|
|
}
|
|
|
|
// Status widget — shows active slot after execution
|
|
const statusWidget = createStatusWidget("— RUN TO SEE RESULT —");
|
|
node.addCustomWidget(statusWidget);
|
|
|
|
// 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") {
|
|
setWidgetVisible(node, w, false);
|
|
}
|
|
}
|
|
// setDirtyCanvas alone doesn't repaint Nodes 2.0's canvas bridge —
|
|
// see the comment in preset_picker_widget.mjs.
|
|
statusWidget.triggerDraw?.();
|
|
}
|
|
node.setDirtyCanvas(true);
|
|
};
|
|
},
|
|
});
|