151 lines
5.8 KiB
JavaScript
151 lines
5.8 KiB
JavaScript
import { app } from "/scripts/app.js";
|
||
|
||
// PixelForge Resolution Matrix - Frontend Extension
|
||
console.log("PixelForge: Extension script loaded");
|
||
|
||
app.registerExtension({
|
||
name: "PixelForge.ResolutionMatrix",
|
||
|
||
async nodeCreated(node) {
|
||
console.log("PixelForge: nodeCreated called for", node.comfyClass);
|
||
|
||
// Handle both Resolution Selector and Resize Image nodes
|
||
if (node.comfyClass !== "PixelForge" && node.comfyClass !== "PixelForgeResizeImage") return;
|
||
|
||
console.log("PixelForge: Initializing", node.comfyClass, "node");
|
||
|
||
const ASPECT_RATIOS = {
|
||
"1:1": [1, 1],
|
||
"3:2": [3, 2],
|
||
"4:3": [4, 3],
|
||
"16:9": [16, 9],
|
||
"16:10": [16, 10],
|
||
};
|
||
|
||
const MP_BASE = 1024 * 1024;
|
||
|
||
const aspectWidget = node.widgets.find(w => w.name === "aspect_ratio");
|
||
const orientationWidget = node.widgets.find(w => w.name === "orientation");
|
||
const divisibleWidget = node.widgets.find(w => w.name === "divisible_by");
|
||
const mpLimitWidget = node.widgets.find(w => w.name === "max_megapixels");
|
||
const resolutionWidget = node.widgets.find(w => w.name === "resolution");
|
||
|
||
console.log("PixelForge: Found widgets:", {
|
||
aspectWidget: !!aspectWidget,
|
||
orientationWidget: !!orientationWidget,
|
||
divisibleWidget: !!divisibleWidget,
|
||
mpLimitWidget: !!mpLimitWidget,
|
||
resolutionWidget: !!resolutionWidget
|
||
});
|
||
|
||
if (!aspectWidget || !resolutionWidget) {
|
||
console.error("PixelForge: Required widgets not found!");
|
||
return;
|
||
}
|
||
|
||
const updateResolutions = () => {
|
||
const aspect = aspectWidget.value;
|
||
const orientation = orientationWidget ? orientationWidget.value : "landscape";
|
||
const div = divisibleWidget ? parseInt(divisibleWidget.value) : 16;
|
||
const maxMpStr = mpLimitWidget ? mpLimitWidget.value : "4 MP";
|
||
const maxMp = parseInt(maxMpStr.split(" ")[0]);
|
||
|
||
console.log("PixelForge: updateResolutions called with:", { aspect, orientation, div, maxMp });
|
||
|
||
// Auto-adjust orientation based on aspect ratio
|
||
if (orientationWidget) {
|
||
if (aspect === "1:1" && orientation !== "square") {
|
||
// For 1:1, force square orientation
|
||
orientationWidget.value = "square";
|
||
console.log("PixelForge: Auto-selected square orientation for 1:1 ratio");
|
||
} else if (aspect !== "1:1" && orientation === "square") {
|
||
// For non-1:1 ratios, if currently square, switch to landscape
|
||
orientationWidget.value = "landscape";
|
||
console.log("PixelForge: Auto-switched from square to landscape for non-1:1 ratio");
|
||
}
|
||
}
|
||
|
||
const ratio = ASPECT_RATIOS[aspect];
|
||
if (!ratio) {
|
||
console.error("PixelForge: Invalid aspect ratio:", aspect);
|
||
return;
|
||
}
|
||
|
||
const [ratio_w, ratio_h] = ratio;
|
||
const maxPixels = maxMp * MP_BASE;
|
||
|
||
const resolutions = [];
|
||
let k = div;
|
||
|
||
while (true) {
|
||
let w = ratio_w * k;
|
||
let h = ratio_h * k;
|
||
const total = w * h;
|
||
|
||
if (total > maxPixels) break;
|
||
|
||
// For square aspect ratio (1:1), orientation doesn't matter
|
||
// For portrait, swap dimensions
|
||
const currentOrientation = orientationWidget ? orientationWidget.value : orientation;
|
||
if (currentOrientation === "portrait" && aspect !== "1:1") {
|
||
[w, h] = [h, w];
|
||
}
|
||
|
||
resolutions.push(`${w}×${h}`);
|
||
k += div;
|
||
}
|
||
|
||
if (resolutions.length === 0) {
|
||
resolutions.push("INVALID");
|
||
}
|
||
|
||
console.log("PixelForge: Generated resolutions:", resolutions.length, resolutions.slice(0, 5));
|
||
|
||
// Update the widget options
|
||
resolutionWidget.options.values = resolutions;
|
||
|
||
// If current value is not in the new list, pick the first one
|
||
if (!resolutions.includes(resolutionWidget.value)) {
|
||
console.log("PixelForge: Current value not in list, setting to:", resolutions[0]);
|
||
resolutionWidget.value = resolutions[0];
|
||
}
|
||
|
||
node.setDirtyCanvas(true, true);
|
||
console.log("PixelForge: Canvas marked dirty");
|
||
};
|
||
|
||
// Hook callbacks for all triggers
|
||
const widgetsToWatch = [aspectWidget, orientationWidget, divisibleWidget, mpLimitWidget];
|
||
widgetsToWatch.forEach(widget => {
|
||
if (!widget) return;
|
||
const origCallback = widget.callback;
|
||
widget.callback = function () {
|
||
console.log("PixelForge: Widget callback triggered for:", widget.name);
|
||
const result = origCallback ? origCallback.apply(this, arguments) : undefined;
|
||
updateResolutions();
|
||
return result;
|
||
};
|
||
});
|
||
|
||
console.log("PixelForge: Callbacks hooked, running initial update");
|
||
|
||
// Initial update
|
||
setTimeout(() => updateResolutions(), 10);
|
||
},
|
||
|
||
async loadedGraphNode(node) {
|
||
// Handle both Resolution Selector and Resize Image nodes
|
||
if (node.comfyClass !== "PixelForge" && node.comfyClass !== "PixelForgeResizeImage") return;
|
||
|
||
console.log("PixelForge: loadedGraphNode called for", node.comfyClass);
|
||
|
||
// Re-trigger the same logic when a node is loaded
|
||
const aspectWidget = node.widgets.find(w => w.name === "aspect_ratio");
|
||
if (aspectWidget && aspectWidget.callback) {
|
||
aspectWidget.callback();
|
||
}
|
||
}
|
||
});
|
||
|
||
console.log("PixelForge: Extension registered");
|