199 lines
6.9 KiB
JavaScript
199 lines
6.9 KiB
JavaScript
// resolution_machine.js
|
|
import { app } from "../../../scripts/app.js";
|
|
|
|
async function fetchConfig() {
|
|
try {
|
|
const resp = await fetch("/sata/resolution_machine/config");
|
|
if (!resp.ok) {
|
|
console.warn("ResolutionMachine: fetch failed", resp.status);
|
|
return null;
|
|
}
|
|
const json = await resp.json();
|
|
return json;
|
|
} catch (e) {
|
|
console.warn("ResolutionMachine: fetch exception", e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/* cross-version widget helpers */
|
|
function setComboOptions(widget, options) {
|
|
if (!widget) return;
|
|
if (typeof widget.setOptions === "function") {
|
|
widget.setOptions(options);
|
|
return;
|
|
}
|
|
if (widget.options && Object.prototype.hasOwnProperty.call(widget.options, "values")) {
|
|
widget.options.values = options;
|
|
return;
|
|
}
|
|
widget.options = { values: options };
|
|
}
|
|
function setWidgetValue(widget, v) {
|
|
if (!widget) return;
|
|
if (typeof widget.setValue === "function") return widget.setValue(v);
|
|
if (typeof widget.value === "function") return widget.value(v);
|
|
widget.value = v;
|
|
}
|
|
function getWidgetValue(widget) {
|
|
if (!widget) return undefined;
|
|
if (typeof widget.value === "function") return widget.value();
|
|
return widget.value;
|
|
}
|
|
function triggerWidgetChange(widget, value) {
|
|
// call onChange or callback if present to simulate user selection
|
|
if (!widget) return;
|
|
if (typeof widget.onChange === "function") {
|
|
try { widget.onChange(value); return; } catch (e) { /* continue */ }
|
|
}
|
|
if (typeof widget.callback === "function") {
|
|
try { widget.callback(value); return; } catch (e) { /* continue */ }
|
|
}
|
|
// fallback: set value
|
|
setWidgetValue(widget, value);
|
|
}
|
|
|
|
/* attach handler in tolerant way */
|
|
function attachHandler(widget, node, handler) {
|
|
if (!widget) return;
|
|
if (typeof widget.onChange === "function") {
|
|
widget.onChange(handler);
|
|
return;
|
|
}
|
|
// preserve existing callback if present
|
|
const orig = widget.callback;
|
|
widget.callback = function (v) {
|
|
try { if (orig) orig.call(node, v); } catch (e) { /* ignore */ }
|
|
try { handler(v); } catch (e) { console.warn("handler error", e); }
|
|
};
|
|
}
|
|
|
|
app.registerExtension({
|
|
name: "SATA_UtilityNode.ResolutionMachine",
|
|
async nodeCreated(node) {
|
|
if (node.comfyClass !== "Resolution_Machine") return;
|
|
|
|
const modelW = node.widgets.find(w => w.name === "model");
|
|
const resW = node.widgets.find(w => w.name === "resolution");
|
|
const wW = node.widgets.find(w => w.name === "width");
|
|
const hW = node.widgets.find(w => w.name === "height");
|
|
|
|
console.log("ResolutionMachine nodeCreated widgets:", { modelW, resW, wW, hW });
|
|
|
|
if (!modelW || !resW || !wW || !hW) {
|
|
console.warn("ResolutionMachine: missing widgets - ensure backend's INPUT_TYPES exposes model,resolution,width,height");
|
|
return;
|
|
}
|
|
|
|
const cfg = await fetchConfig();
|
|
if (!cfg) {
|
|
console.warn("ResolutionMachine: no config received from backend");
|
|
return;
|
|
}
|
|
|
|
const models = cfg.models.map(m => m.model);
|
|
if (models.length > 0) setComboOptions(modelW, models);
|
|
|
|
// helper to get resolution list for a model (ensures Custom present)
|
|
function getResNamesForModel(modelName) {
|
|
const m = cfg.models.find(x => x.model === modelName);
|
|
if (!m) return ["Custom (manual)"];
|
|
const names = m.resolutions.map(r => r.name.slice()); // copy
|
|
if (!names.includes("Custom (manual)")) names.push("Custom (manual)");
|
|
return names;
|
|
}
|
|
|
|
// helper to find resolution object
|
|
function findResObj(modelName, resName) {
|
|
const m = cfg.models.find(x => x.model === modelName);
|
|
if (!m) return null;
|
|
return m.resolutions.find(r => r.name === resName) || null;
|
|
}
|
|
|
|
// guard to avoid manual-edit handler firing during programmatic updates
|
|
let suppressManualWH = false;
|
|
function programmaticSetWH(wVal, hVal) {
|
|
suppressManualWH = true;
|
|
setWidgetValue(wW, wVal);
|
|
setWidgetValue(hW, hVal);
|
|
if (wW.inputEl) wW.inputEl.value = wVal;
|
|
if (hW.inputEl) hW.inputEl.value = hVal;
|
|
// clear suppression on next tick
|
|
setTimeout(() => { suppressManualWH = false; }, 0);
|
|
}
|
|
|
|
// Model change handler: update resolution options and pick first preset
|
|
const onModelChange = (newModel) => {
|
|
try {
|
|
const names = getResNamesForModel(newModel);
|
|
setComboOptions(resW, names);
|
|
|
|
// pick first non-custom if available
|
|
const pick = names[0] || "Custom (manual)";
|
|
setWidgetValue(resW, pick);
|
|
// call resolution handler (simulate user selection)
|
|
triggerWidgetChange(resW, pick);
|
|
|
|
// ensure node re-validation / UI update
|
|
try { node.setDirtyCanvas(true, true); } catch (e) { try { node.setDirtyCanvas(true); } catch(e){} }
|
|
} catch (e) {
|
|
console.warn("ResolutionMachine: onModelChange error", e);
|
|
}
|
|
};
|
|
|
|
// Resolution change handler: set width/height for presets, lock/unlock inputs accordingly
|
|
const onResolutionChange = (newRes) => {
|
|
try {
|
|
const modelName = getWidgetValue(modelW);
|
|
const resObj = findResObj(modelName, newRes);
|
|
if (resObj && newRes !== "Custom (manual)") {
|
|
programmaticSetWH(resObj.width, resObj.height);
|
|
// Lock width/height
|
|
if (wW.inputEl) wW.inputEl.disabled = true;
|
|
if (hW.inputEl) hW.inputEl.disabled = true;
|
|
} else {
|
|
// Custom: allow manual edits
|
|
if (wW.inputEl) wW.inputEl.disabled = false;
|
|
if (hW.inputEl) hW.inputEl.disabled = false;
|
|
}
|
|
try { node.setDirtyCanvas(true, true); } catch (e) { try { node.setDirtyCanvas(true); } catch(e){} }
|
|
} catch (e) {
|
|
console.warn("ResolutionMachine: onResolutionChange error", e);
|
|
}
|
|
};
|
|
|
|
// Manual width/height change handler: set resolution to Custom (manual)
|
|
const onManualWH = (_newVal) => {
|
|
if (suppressManualWH) return;
|
|
try {
|
|
const cur = getWidgetValue(resW);
|
|
if (cur !== "Custom (manual)") {
|
|
const names = getResNamesForModel(getWidgetValue(modelW));
|
|
if (!names.includes("Custom (manual)")) {
|
|
names.push("Custom (manual)");
|
|
setComboOptions(resW, names);
|
|
}
|
|
setWidgetValue(resW, "Custom (manual)");
|
|
try { triggerWidgetChange(resW, "Custom (manual)"); } catch(e){}
|
|
try { node.setDirtyCanvas(true, true); } catch (e) { try { node.setDirtyCanvas(true); } catch(e){} }
|
|
}
|
|
} catch (e) {
|
|
console.warn("ResolutionMachine: onManualWH error", e);
|
|
}
|
|
};
|
|
|
|
// Attach handlers (tolerant)
|
|
attachHandler(modelW, node, onModelChange);
|
|
attachHandler(resW, node, onResolutionChange);
|
|
attachHandler(wW, node, onManualWH);
|
|
attachHandler(hW, node, onManualWH);
|
|
|
|
// Initialize model + resolution UI
|
|
const initialModel = getWidgetValue(modelW) || (models.length ? models[0] : undefined);
|
|
if (initialModel) {
|
|
setWidgetValue(modelW, initialModel);
|
|
// ensure options & width/height set for initial model
|
|
onModelChange(initialModel);
|
|
}
|
|
}
|
|
}); |