const inpaintAnything_waitForElement = async (parent, selector, exist) => { return new Promise((resolve) => { const observer = new MutationObserver(() => { if (!!parent.querySelector(selector) != exist) { return; } observer.disconnect(); resolve(undefined); }); observer.observe(parent, { childList: true, subtree: true, }); if (!!parent.querySelector(selector) == exist) { resolve(undefined); } }); }; const inpaintAnything_waitForStyle = async (parent, selector, style) => { return new Promise((resolve) => { const observer = new MutationObserver(() => { if (!parent.querySelector(selector) || !parent.querySelector(selector).style[style]) { return; } observer.disconnect(); resolve(undefined); }); observer.observe(parent, { childList: true, subtree: true, attributes: true, attributeFilter: ["style"], }); if (!!parent.querySelector(selector) && !!parent.querySelector(selector).style[style]) { resolve(undefined); } }); }; const inpaintAnything_timeout = (ms) => { return new Promise(function (resolve, reject) { setTimeout(() => reject("Timeout"), ms); }); }; async function inpaintAnything_clearSamMask() { const waitForElementToBeInDocument = (parent, selector) => Promise.race([inpaintAnything_waitForElement(parent, selector, true), inpaintAnything_timeout(1000)]); const elemId = "#ia_sam_image"; const targetElement = document.querySelector(elemId); if (!targetElement) { return; } await waitForElementToBeInDocument(targetElement, "button[aria-label='Clear']"); targetElement.style.transform = null; targetElement.style.zIndex = null; targetElement.style.overflow = "auto"; const samMaskClear = targetElement.querySelector("button[aria-label='Clear']"); if (!samMaskClear) { return; } const removeImageButton = targetElement.querySelector("button[aria-label='Remove Image']"); if (!removeImageButton) { return; } samMaskClear?.click(); if (typeof inpaintAnything_clearSamMask.clickRemoveImage === "undefined") { inpaintAnything_clearSamMask.clickRemoveImage = () => { targetElement.style.transform = null; targetElement.style.zIndex = null; }; } else { removeImageButton.removeEventListener("click", inpaintAnything_clearSamMask.clickRemoveImage); } removeImageButton.addEventListener("click", inpaintAnything_clearSamMask.clickRemoveImage); } async function inpaintAnything_clearSelMask() { const waitForElementToBeInDocument = (parent, selector) => Promise.race([inpaintAnything_waitForElement(parent, selector, true), inpaintAnything_timeout(1000)]); const elemId = "#ia_sel_mask"; const targetElement = document.querySelector(elemId); if (!targetElement) { return; } await waitForElementToBeInDocument(targetElement, "button[aria-label='Clear']"); targetElement.style.transform = null; targetElement.style.zIndex = null; targetElement.style.overflow = "auto"; const selMaskClear = targetElement.querySelector("button[aria-label='Clear']"); if (!selMaskClear) { return; } const removeImageButton = targetElement.querySelector("button[aria-label='Remove Image']"); if (!removeImageButton) { return; } selMaskClear?.click(); if (typeof inpaintAnything_clearSelMask.clickRemoveImage === "undefined") { inpaintAnything_clearSelMask.clickRemoveImage = () => { targetElement.style.transform = null; targetElement.style.zIndex = null; }; } else { removeImageButton.removeEventListener("click", inpaintAnything_clearSelMask.clickRemoveImage); } removeImageButton.addEventListener("click", inpaintAnything_clearSelMask.clickRemoveImage); } async function inpaintAnything_initSamSelMask() { inpaintAnything_clearSamMask(); inpaintAnything_clearSelMask(); } var uiLoadedCallbacks = []; function gradioApp() { const elems = document.getElementsByTagName("gradio-app"); const elem = elems.length == 0 ? document : elems[0]; if (elem !== document) { elem.getElementById = function (id) { return document.getElementById(id); }; } return elem.shadowRoot ? elem.shadowRoot : elem; } function onUiLoaded(callback) { uiLoadedCallbacks.push(callback); } function executeCallbacks(queue) { for (const callback of queue) { try { callback(); } catch (e) { console.error("error running callback", callback, ":", e); } } } onUiLoaded(async () => { const elementIDs = { ia_sam_image: "#ia_sam_image", ia_sel_mask: "#ia_sel_mask", ia_out_image: "#ia_out_image", ia_cleaner_out_image: "#ia_cleaner_out_image", }; function setStyleHeight(elemId, height) { const elem = gradioApp().querySelector(elemId); if (elem) { if (!elem.style.height) { elem.style.height = height; const observer = new MutationObserver(() => { const divPreview = elem.querySelector(".preview"); if (divPreview) { divPreview.classList.remove("fixed-height"); } }); observer.observe(elem, { childList: true, attributes: true, attributeFilter: ["class"], }); } } } setStyleHeight(elementIDs.ia_out_image, "520px"); setStyleHeight(elementIDs.ia_cleaner_out_image, "520px"); // Default config const defaultHotkeysConfig = { canvas_hotkey_reset: "KeyR", canvas_hotkey_fullscreen: "KeyS", }; const elemData = {}; let activeElement; function applyZoomAndPan(elemId) { const targetElement = gradioApp().querySelector(elemId); if (!targetElement) { console.log("Element not found"); return; } targetElement.style.transformOrigin = "0 0"; elemData[elemId] = { zoomLevel: 1, panX: 0, panY: 0, }; let fullScreenMode = false; // Toggle the zIndex of the target element between two values, allowing it to overlap or be overlapped by other elements function toggleOverlap(forced = "") { // const zIndex1 = "0"; const zIndex1 = null; const zIndex2 = "998"; targetElement.style.zIndex = targetElement.style.zIndex !== zIndex2 ? zIndex2 : zIndex1; if (forced === "off") { targetElement.style.zIndex = zIndex1; } else if (forced === "on") { targetElement.style.zIndex = zIndex2; } } /** * This function fits the target element to the screen by calculating * the required scale and offsets. It also updates the global variables * zoomLevel, panX, and panY to reflect the new state. */ function fitToElement() { //Reset Zoom targetElement.style.transform = `translate(${0}px, ${0}px) scale(${1})`; // Get element and screen dimensions const elementWidth = targetElement.offsetWidth; const elementHeight = targetElement.offsetHeight; const parentElement = targetElement.parentElement; const screenWidth = parentElement.clientWidth; const screenHeight = parentElement.clientHeight; // Get element's coordinates relative to the parent element const elementRect = targetElement.getBoundingClientRect(); const parentRect = parentElement.getBoundingClientRect(); const elementX = elementRect.x - parentRect.x; // Calculate scale and offsets const scaleX = screenWidth / elementWidth; const scaleY = screenHeight / elementHeight; const scale = Math.min(scaleX, scaleY); const transformOrigin = window.getComputedStyle(targetElement).transformOrigin; const [originX, originY] = transformOrigin.split(" "); const originXValue = parseFloat(originX); const originYValue = parseFloat(originY); const offsetX = (screenWidth - elementWidth * scale) / 2 - originXValue * (1 - scale); const offsetY = (screenHeight - elementHeight * scale) / 2.5 - originYValue * (1 - scale); // Apply scale and offsets to the element targetElement.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`; // Update global variables elemData[elemId].zoomLevel = scale; elemData[elemId].panX = offsetX; elemData[elemId].panY = offsetY; fullScreenMode = false; toggleOverlap("off"); } // Reset the zoom level and pan position of the target element to their initial values function resetZoom() { elemData[elemId] = { zoomLevel: 1, panX: 0, panY: 0, }; // fixCanvas(); targetElement.style.transform = `scale(${elemData[elemId].zoomLevel}) translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px)`; // const canvas = gradioApp().querySelector(`${elemId} canvas[key="interface"]`); toggleOverlap("off"); fullScreenMode = false; // if ( // canvas && // parseFloat(canvas.style.width) > 865 && // parseFloat(targetElement.style.width) > 865 // ) { // fitToElement(); // return; // } // targetElement.style.width = ""; // if (canvas) { // targetElement.style.height = canvas.style.height; // } targetElement.style.width = null; targetElement.style.height = 480; } /** * This function fits the target element to the screen by calculating * the required scale and offsets. It also updates the global variables * zoomLevel, panX, and panY to reflect the new state. */ // Fullscreen mode function fitToScreen() { const canvas = gradioApp().querySelector(`${elemId} canvas[key="interface"]`); const img = gradioApp().querySelector(`${elemId} img`); if (!canvas && !img) return; // if (canvas.offsetWidth > 862) { // targetElement.style.width = canvas.offsetWidth + "px"; // } if (fullScreenMode) { resetZoom(); fullScreenMode = false; return; } //Reset Zoom targetElement.style.transform = `translate(${0}px, ${0}px) scale(${1})`; // Get scrollbar width to right-align the image const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; // Get element and screen dimensions const elementWidth = targetElement.offsetWidth; const elementHeight = targetElement.offsetHeight; const screenWidth = window.innerWidth - scrollbarWidth; const screenHeight = window.innerHeight; // Get element's coordinates relative to the page const elementRect = targetElement.getBoundingClientRect(); const elementY = elementRect.y; const elementX = elementRect.x; // Calculate scale and offsets const scaleX = screenWidth / elementWidth; const scaleY = screenHeight / elementHeight; const scale = Math.min(scaleX, scaleY); // Get the current transformOrigin const computedStyle = window.getComputedStyle(targetElement); const transformOrigin = computedStyle.transformOrigin; const [originX, originY] = transformOrigin.split(" "); const originXValue = parseFloat(originX); const originYValue = parseFloat(originY); // Calculate offsets with respect to the transformOrigin const offsetX = (screenWidth - elementWidth * scale) / 2 - elementX - originXValue * (1 - scale); const offsetY = (screenHeight - elementHeight * scale) / 2 - elementY - originYValue * (1 - scale); // Apply scale and offsets to the element targetElement.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`; // Update global variables elemData[elemId].zoomLevel = scale; elemData[elemId].panX = offsetX; elemData[elemId].panY = offsetY; fullScreenMode = true; toggleOverlap("on"); } // Reset zoom when uploading a new image const fileInput = gradioApp().querySelector(`${elemId} input[type="file"][accept="image/*"].svelte-116rqfv`); if (fileInput) { fileInput.addEventListener("click", resetZoom); } // Handle keydown events function handleKeyDown(event) { // Disable key locks to make pasting from the buffer work correctly if ( (event.ctrlKey && event.code === "KeyV") || (event.ctrlKey && event.code === "KeyC") || event.code === "F5" ) { return; } // before activating shortcut, ensure user is not actively typing in an input field if (event.target.nodeName === "TEXTAREA" || event.target.nodeName === "INPUT") { return; } const hotkeyActions = { [defaultHotkeysConfig.canvas_hotkey_reset]: resetZoom, [defaultHotkeysConfig.canvas_hotkey_fullscreen]: fitToScreen, }; const action = hotkeyActions[event.code]; if (action) { event.preventDefault(); action(event); } } // Handle events only inside the targetElement let isKeyDownHandlerAttached = false; function handleMouseMove() { if (!isKeyDownHandlerAttached) { document.addEventListener("keydown", handleKeyDown); isKeyDownHandlerAttached = true; activeElement = elemId; } } function handleMouseLeave() { if (isKeyDownHandlerAttached) { document.removeEventListener("keydown", handleKeyDown); isKeyDownHandlerAttached = false; activeElement = null; } } // Add mouse event handlers targetElement.addEventListener("mousemove", handleMouseMove); targetElement.addEventListener("mouseleave", handleMouseLeave); } applyZoomAndPan(elementIDs.ia_sam_image); applyZoomAndPan(elementIDs.ia_sel_mask); // applyZoomAndPan(elementIDs.ia_out_image); // applyZoomAndPan(elementIDs.ia_cleaner_out_image); }); var executedOnLoaded = false; document.addEventListener("DOMContentLoaded", function () { var mutationObserver = new MutationObserver(function () { if ( !executedOnLoaded && gradioApp().querySelector("#ia_sam_image") && gradioApp().querySelector("#ia_sel_mask") ) { executedOnLoaded = true; executeCallbacks(uiLoadedCallbacks); } }); mutationObserver.observe(gradioApp(), { childList: true, subtree: true }); });