Files

96 lines
5.1 KiB
JavaScript

import { INPAINT_FIELDS } from "./inpaint_panel.js";
const hiddenWidgets = new WeakMap();
const targetSizes = new WeakMap();
function setHiddenOption(widget, value) {
widget.options ??= {};
// Recent frontends keep a reactive options proxy on BaseWidget state.
// Write through that proxy first so Nodes 2.0 observes runtime relocation.
const options = widget._state?.options || widget.options;
if (value === undefined) delete options.hidden; else options.hidden = value;
if (value === undefined) delete widget.options.hidden; else widget.options.hidden = value;
}
export function connectedEditor(target) {
const graph = target.graph; if (!graph) return null;
const input = target.inputs?.find(input => input.name === "image");
const link = graph.links?.[input?.link]; const queue = link ? [link.origin_id] : [], seen = new Set(), sources = new Set();
while (queue.length && seen.size < 256) {
const id = queue.shift(); if (seen.has(id)) continue; seen.add(id);
const current = graph.getNodeById(id); if (!current) continue;
if (current.type === "AdachiInlineMask") { sources.add(current.id); continue; }
for (const input of current.inputs || []) {
if (current.type === "AdachiImageCompare" && input.name !== "result") continue;
if (!["IMAGE", "*"].includes(input.type)) continue;
const next = graph.links?.[input.link]; if (next) queue.push(next.origin_id);
}
}
return sources.size === 1 ? graph.getNodeById([...sources][0]) : null;
}
export function connectedSamplers(source) {
return (source.graph?._nodes || [])
.filter(node => ["AdachiWebUIInpaint", "AdachiInpaintPrepare"].includes(node.type) && connectedEditor(node) === source)
.sort((a, b) => Number(b.type === "AdachiWebUIInpaint") - Number(a.type === "AdachiWebUIInpaint"));
}
// Use the same rounding, range and linked-input rules as Use source size.
export function syncCropDimensions(source, width, height, onlyTarget = null) {
const aligned = value => Math.max(64, Math.min(8192, Math.round(value / 8) * 8));
for (const target of connectedSamplers(source)) {
if (onlyTarget && target !== onlyTarget) continue;
target.adachiParameters?.refresh();
for (const [name, value] of [["width", width], ["height", height]]) {
if (target.inputs?.some(input => input.name === name && input.link != null)) continue;
const widget = target.widgets?.find(widget => widget.name === name);
if (widget && widget.value !== aligned(value)) {
widget.value = aligned(value);
widget.callback?.(widget.value);
}
}
target.adachiParameters?.refresh();
}
}
// The crop output does not have dimensions until the user locks a region.
export function syncCurrentDimensions(source, onlyTarget = null) {
const editor = source?.adachiEditor, crop = source?.adachiCrop;
if (!editor?.ready || !editor.image) return;
const region = crop?.mode === "crop" ? crop.region : null;
if (crop?.mode === "crop" && !region?.locked) return;
const image = region || editor.image;
syncCropDimensions(source, image.naturalWidth || image.width, image.naturalHeight || image.height, onlyTarget);
}
export function relocateWidgets(owner, previous, next) {
if (previous) {
for (const widget of previous.widgets || []) {
const state = hiddenWidgets.get(widget); if (!state) continue;
state.owners.delete(owner); if (state.owners.size) continue;
widget.type = state.type; widget.hidden = state.hidden;
setHiddenOption(widget, state.optionHidden);
if (state.ownComputeSize) widget.computeSize = state.computeSize; else delete widget.computeSize;
if (state.element) state.element.style.display = state.display;
hiddenWidgets.delete(widget);
}
const size = targetSizes.get(previous); if (size) { previous.setSize([previous.size[0], Math.max(size[1], previous.computeSize()[1])]); targetSizes.delete(previous); }
}
if (next) {
if (!targetSizes.has(next)) targetSizes.set(next, [...next.size]);
for (const widget of next.widgets || []) {
if (!INPAINT_FIELDS.includes(widget.name)) continue;
let state = hiddenWidgets.get(widget);
if (!state) {
const element = widget.element || widget.inputEl;
state = { type: widget.type, hidden: widget.hidden, optionHidden: widget.options?.hidden, ownComputeSize: Object.hasOwn(widget, "computeSize"), computeSize: widget.computeSize, element, display: element?.style.display || "", owners: new Set() };
hiddenWidgets.set(widget, state);
widget.type = "hidden"; widget.hidden = true; setHiddenOption(widget, true);
widget.computeSize = () => [0, -4]; if (element) element.style.display = "none";
}
state.owners.add(owner);
}
next.setSize([next.size[0], next.computeSize()[1]]);
}
}