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]]); } }