finally solve wide image inpaint UI problem, curse gradio
This commit is contained in:
		
							parent
							
								
									7075fd39c3
								
							
						
					
					
						commit
						df93f738de
					
				@ -26,3 +26,70 @@
 | 
				
			|||||||
.context-menu-items a:hover{
 | 
					.context-menu-items a:hover{
 | 
				
			||||||
    background: #a55000;
 | 
					    background: #a55000;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.canvas-tooltip-info {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  top: 10px;
 | 
				
			||||||
 | 
					  left: 10px;
 | 
				
			||||||
 | 
					  cursor: help;
 | 
				
			||||||
 | 
					  background-color: rgba(0, 0, 0, 0.3);
 | 
				
			||||||
 | 
					  width: 20px;
 | 
				
			||||||
 | 
					  height: 20px;
 | 
				
			||||||
 | 
					  border-radius: 50%;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  z-index: 100;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.canvas-tooltip-info::after {
 | 
				
			||||||
 | 
					  content: '';
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  width: 2px;
 | 
				
			||||||
 | 
					  height: 7px;
 | 
				
			||||||
 | 
					  background-color: white;
 | 
				
			||||||
 | 
					  margin-top: 2px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.canvas-tooltip-info::before {
 | 
				
			||||||
 | 
					  content: '';
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  width: 2px;
 | 
				
			||||||
 | 
					  height: 2px;
 | 
				
			||||||
 | 
					  background-color: white;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.canvas-tooltip-content {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					  background-color: #f9f9f9;
 | 
				
			||||||
 | 
					  color: #333;
 | 
				
			||||||
 | 
					  border: 1px solid #ddd;
 | 
				
			||||||
 | 
					  padding: 15px;
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  top: 40px;
 | 
				
			||||||
 | 
					  left: 10px;
 | 
				
			||||||
 | 
					  width: 250px;
 | 
				
			||||||
 | 
					  font-size: 16px;
 | 
				
			||||||
 | 
					  opacity: 0;
 | 
				
			||||||
 | 
					  border-radius: 8px;
 | 
				
			||||||
 | 
					  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  z-index: 100;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.canvas-tooltip:hover .canvas-tooltip-content {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  animation: fadeIn 0.5s;
 | 
				
			||||||
 | 
					  opacity: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes fadeIn {
 | 
				
			||||||
 | 
					  from {opacity: 0;}
 | 
				
			||||||
 | 
					  to {opacity: 1;}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.styler {
 | 
				
			||||||
 | 
					  overflow:inherit !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1 +1 @@
 | 
				
			|||||||
version = '2.1.719'
 | 
					version = '2.1.720'
 | 
				
			||||||
 | 
				
			|||||||
@ -105,7 +105,7 @@ var executedOnLoaded = false;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
document.addEventListener("DOMContentLoaded", function() {
 | 
					document.addEventListener("DOMContentLoaded", function() {
 | 
				
			||||||
    var mutationObserver = new MutationObserver(function(m) {
 | 
					    var mutationObserver = new MutationObserver(function(m) {
 | 
				
			||||||
        if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) {
 | 
					        if (!executedOnLoaded && gradioApp().querySelector('#generate_button')) {
 | 
				
			||||||
            executedOnLoaded = true;
 | 
					            executedOnLoaded = true;
 | 
				
			||||||
            executeCallbacks(uiLoadedCallbacks);
 | 
					            executeCallbacks(uiLoadedCallbacks);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -125,20 +125,16 @@ document.addEventListener("DOMContentLoaded", function() {
 | 
				
			|||||||
 * Add a ctrl+enter as a shortcut to start a generation
 | 
					 * Add a ctrl+enter as a shortcut to start a generation
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
document.addEventListener('keydown', function(e) {
 | 
					document.addEventListener('keydown', function(e) {
 | 
				
			||||||
    const isEnter = e.key === 'Enter' || e.keyCode === 13;
 | 
					    var handled = false;
 | 
				
			||||||
    const isModifierKey = e.metaKey || e.ctrlKey || e.altKey;
 | 
					    if (e.key !== undefined) {
 | 
				
			||||||
 | 
					        if ((e.key == "Enter" && (e.metaKey || e.ctrlKey || e.altKey))) handled = true;
 | 
				
			||||||
    const interruptButton = get_uiCurrentTabContent().querySelector('button[id$=_interrupt]');
 | 
					    } else if (e.keyCode !== undefined) {
 | 
				
			||||||
    const generateButton = get_uiCurrentTabContent().querySelector('button[id$=_generate]');
 | 
					        if ((e.keyCode == 13 && (e.metaKey || e.ctrlKey || e.altKey))) handled = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (isEnter && isModifierKey) {
 | 
					    if (handled) {
 | 
				
			||||||
        if (interruptButton.style.display === 'block') {
 | 
					        var button = gradioApp().querySelector('button[id=generate_button]');
 | 
				
			||||||
            interruptButton.click();
 | 
					        if (button) {
 | 
				
			||||||
            setTimeout(function() {
 | 
					            button.click();
 | 
				
			||||||
                generateButton.click();
 | 
					 | 
				
			||||||
            }, 500);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            generateButton.click();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        e.preventDefault();
 | 
					        e.preventDefault();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										766
									
								
								javascript/zoom.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										766
									
								
								javascript/zoom.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,766 @@
 | 
				
			|||||||
 | 
					onUiLoaded(async() => {
 | 
				
			||||||
 | 
					    // Helper functions
 | 
				
			||||||
 | 
					    // Get active tab
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Waits for an element to be present in the DOM.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    const waitForElement = (id) => new Promise(resolve => {
 | 
				
			||||||
 | 
					        const checkForElement = () => {
 | 
				
			||||||
 | 
					            const element = document.querySelector(id);
 | 
				
			||||||
 | 
					            if (element) return resolve(element);
 | 
				
			||||||
 | 
					            setTimeout(checkForElement, 100);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        checkForElement();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Detect whether the element has a horizontal scroll bar
 | 
				
			||||||
 | 
					    function hasHorizontalScrollbar(element) {
 | 
				
			||||||
 | 
					        return element.scrollWidth > element.clientWidth;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Function for defining the "Ctrl", "Shift" and "Alt" keys
 | 
				
			||||||
 | 
					    function isModifierKey(event, key) {
 | 
				
			||||||
 | 
					        switch (key) {
 | 
				
			||||||
 | 
					        case "Ctrl":
 | 
				
			||||||
 | 
					            return event.ctrlKey;
 | 
				
			||||||
 | 
					        case "Shift":
 | 
				
			||||||
 | 
					            return event.shiftKey;
 | 
				
			||||||
 | 
					        case "Alt":
 | 
				
			||||||
 | 
					            return event.altKey;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check if hotkey is valid
 | 
				
			||||||
 | 
					    function isValidHotkey(value) {
 | 
				
			||||||
 | 
					        const specialKeys = ["Ctrl", "Alt", "Shift", "Disable"];
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            (typeof value === "string" &&
 | 
				
			||||||
 | 
					                value.length === 1 &&
 | 
				
			||||||
 | 
					                /[a-z]/i.test(value)) ||
 | 
				
			||||||
 | 
					            specialKeys.includes(value)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Normalize hotkey
 | 
				
			||||||
 | 
					    function normalizeHotkey(hotkey) {
 | 
				
			||||||
 | 
					        return hotkey.length === 1 ? "Key" + hotkey.toUpperCase() : hotkey;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Format hotkey for display
 | 
				
			||||||
 | 
					    function formatHotkeyForDisplay(hotkey) {
 | 
				
			||||||
 | 
					        return hotkey.startsWith("Key") ? hotkey.slice(3) : hotkey;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create hotkey configuration with the provided options
 | 
				
			||||||
 | 
					    function createHotkeyConfig(defaultHotkeysConfig) {
 | 
				
			||||||
 | 
					        const result = {}; // Resulting hotkey configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (const key in defaultHotkeysConfig) {
 | 
				
			||||||
 | 
					            result[key] = defaultHotkeysConfig[key];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Disables functions in the config object based on the provided list of function names
 | 
				
			||||||
 | 
					    function disableFunctions(config, disabledFunctions) {
 | 
				
			||||||
 | 
					        // Bind the hasOwnProperty method to the functionMap object to avoid errors
 | 
				
			||||||
 | 
					        const hasOwnProperty =
 | 
				
			||||||
 | 
					            Object.prototype.hasOwnProperty.bind(functionMap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Loop through the disabledFunctions array and disable the corresponding functions in the config object
 | 
				
			||||||
 | 
					        disabledFunctions.forEach(funcName => {
 | 
				
			||||||
 | 
					            if (hasOwnProperty(funcName)) {
 | 
				
			||||||
 | 
					                const key = functionMap[funcName];
 | 
				
			||||||
 | 
					                config[key] = "disable";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Return the updated config object
 | 
				
			||||||
 | 
					        return config;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The restoreImgRedMask function displays a red mask around an image to indicate the aspect ratio.
 | 
				
			||||||
 | 
					     * If the image display property is set to 'none', the mask breaks. To fix this, the function
 | 
				
			||||||
 | 
					     * temporarily sets the display property to 'block' and then hides the mask again after 300 milliseconds
 | 
				
			||||||
 | 
					     * to avoid breaking the canvas. Additionally, the function adjusts the mask to work correctly on
 | 
				
			||||||
 | 
					     * very long images.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    function restoreImgRedMask(elements) {
 | 
				
			||||||
 | 
					        const mainTabId = getTabId(elements);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!mainTabId) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const mainTab = gradioApp().querySelector(mainTabId);
 | 
				
			||||||
 | 
					        const img = mainTab.querySelector("img");
 | 
				
			||||||
 | 
					        const imageARPreview = gradioApp().querySelector("#imageARPreview");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!img || !imageARPreview) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        imageARPreview.style.transform = "";
 | 
				
			||||||
 | 
					        if (parseFloat(mainTab.style.width) > 865) {
 | 
				
			||||||
 | 
					            const transformString = mainTab.style.transform;
 | 
				
			||||||
 | 
					            const scaleMatch = transformString.match(
 | 
				
			||||||
 | 
					                /scale\(([-+]?[0-9]*\.?[0-9]+)\)/
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            let zoom = 1; // default zoom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (scaleMatch && scaleMatch[1]) {
 | 
				
			||||||
 | 
					                zoom = Number(scaleMatch[1]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            imageARPreview.style.transformOrigin = "0 0";
 | 
				
			||||||
 | 
					            imageARPreview.style.transform = `scale(${zoom})`;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (img.style.display !== "none") return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        img.style.display = "block";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setTimeout(() => {
 | 
				
			||||||
 | 
					            img.style.display = "none";
 | 
				
			||||||
 | 
					        }, 400);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Default config
 | 
				
			||||||
 | 
					    const defaultHotkeysConfig = {
 | 
				
			||||||
 | 
					        canvas_hotkey_zoom: "Alt",
 | 
				
			||||||
 | 
					        canvas_hotkey_adjust: "Ctrl",
 | 
				
			||||||
 | 
					        canvas_hotkey_reset: "KeyR",
 | 
				
			||||||
 | 
					        canvas_hotkey_fullscreen: "KeyS",
 | 
				
			||||||
 | 
					        canvas_hotkey_move: "KeyF",
 | 
				
			||||||
 | 
					        canvas_hotkey_overlap: "KeyO",
 | 
				
			||||||
 | 
					        canvas_disabled_functions: [],
 | 
				
			||||||
 | 
					        canvas_show_tooltip: true,
 | 
				
			||||||
 | 
					        canvas_auto_expand: true,
 | 
				
			||||||
 | 
					        canvas_blur_prompt: false,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const functionMap = {
 | 
				
			||||||
 | 
					        "Zoom": "canvas_hotkey_zoom",
 | 
				
			||||||
 | 
					        "Adjust brush size": "canvas_hotkey_adjust",
 | 
				
			||||||
 | 
					        "Moving canvas": "canvas_hotkey_move",
 | 
				
			||||||
 | 
					        "Fullscreen": "canvas_hotkey_fullscreen",
 | 
				
			||||||
 | 
					        "Reset Zoom": "canvas_hotkey_reset",
 | 
				
			||||||
 | 
					        "Overlap": "canvas_hotkey_overlap"
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Loading the configuration from opts
 | 
				
			||||||
 | 
					    const preHotkeysConfig = createHotkeyConfig(
 | 
				
			||||||
 | 
					        defaultHotkeysConfig
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Disable functions that are not needed by the user
 | 
				
			||||||
 | 
					    const hotkeysConfig = disableFunctions(
 | 
				
			||||||
 | 
					        preHotkeysConfig,
 | 
				
			||||||
 | 
					        preHotkeysConfig.canvas_disabled_functions
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let isMoving = false;
 | 
				
			||||||
 | 
					    let mouseX, mouseY;
 | 
				
			||||||
 | 
					    let activeElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const elemData = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function applyZoomAndPan(elemId, isExtension = true) {
 | 
				
			||||||
 | 
					        const targetElement = gradioApp().querySelector(elemId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!targetElement) {
 | 
				
			||||||
 | 
					            console.log("Element not found");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        targetElement.style.transformOrigin = "0 0";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        elemData[elemId] = {
 | 
				
			||||||
 | 
					            zoom: 1,
 | 
				
			||||||
 | 
					            panX: 0,
 | 
				
			||||||
 | 
					            panY: 0
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let fullScreenMode = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Create tooltip
 | 
				
			||||||
 | 
					        function createTooltip() {
 | 
				
			||||||
 | 
					            const toolTipElemnt =
 | 
				
			||||||
 | 
					                targetElement.querySelector(".image-container");
 | 
				
			||||||
 | 
					            const tooltip = document.createElement("div");
 | 
				
			||||||
 | 
					            tooltip.className = "canvas-tooltip";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Creating an item of information
 | 
				
			||||||
 | 
					            const info = document.createElement("i");
 | 
				
			||||||
 | 
					            info.className = "canvas-tooltip-info";
 | 
				
			||||||
 | 
					            info.textContent = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Create a container for the contents of the tooltip
 | 
				
			||||||
 | 
					            const tooltipContent = document.createElement("div");
 | 
				
			||||||
 | 
					            tooltipContent.className = "canvas-tooltip-content";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Define an array with hotkey information and their actions
 | 
				
			||||||
 | 
					            const hotkeysInfo = [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    configKey: "canvas_hotkey_zoom",
 | 
				
			||||||
 | 
					                    action: "Zoom canvas",
 | 
				
			||||||
 | 
					                    keySuffix: " + wheel"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    configKey: "canvas_hotkey_adjust",
 | 
				
			||||||
 | 
					                    action: "Adjust brush size",
 | 
				
			||||||
 | 
					                    keySuffix: " + wheel"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {configKey: "canvas_hotkey_reset", action: "Reset zoom"},
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    configKey: "canvas_hotkey_fullscreen",
 | 
				
			||||||
 | 
					                    action: "Fullscreen mode"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {configKey: "canvas_hotkey_move", action: "Move canvas"},
 | 
				
			||||||
 | 
					                {configKey: "canvas_hotkey_overlap", action: "Overlap"}
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Create hotkeys array with disabled property based on the config values
 | 
				
			||||||
 | 
					            const hotkeys = hotkeysInfo.map(info => {
 | 
				
			||||||
 | 
					                const configValue = hotkeysConfig[info.configKey];
 | 
				
			||||||
 | 
					                const key = info.keySuffix ?
 | 
				
			||||||
 | 
					                    `${configValue}${info.keySuffix}` :
 | 
				
			||||||
 | 
					                    configValue.charAt(configValue.length - 1);
 | 
				
			||||||
 | 
					                return {
 | 
				
			||||||
 | 
					                    key,
 | 
				
			||||||
 | 
					                    action: info.action,
 | 
				
			||||||
 | 
					                    disabled: configValue === "disable"
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (const hotkey of hotkeys) {
 | 
				
			||||||
 | 
					                if (hotkey.disabled) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const p = document.createElement("p");
 | 
				
			||||||
 | 
					                p.innerHTML = `<b>${hotkey.key}</b> - ${hotkey.action}`;
 | 
				
			||||||
 | 
					                tooltipContent.appendChild(p);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Add information and content elements to the tooltip element
 | 
				
			||||||
 | 
					            tooltip.appendChild(info);
 | 
				
			||||||
 | 
					            tooltip.appendChild(tooltipContent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Add a hint element to the target element
 | 
				
			||||||
 | 
					            toolTipElemnt.appendChild(tooltip);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //Show tool tip if setting enable
 | 
				
			||||||
 | 
					        if (hotkeysConfig.canvas_show_tooltip) {
 | 
				
			||||||
 | 
					            createTooltip();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 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
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (isExtension) {
 | 
				
			||||||
 | 
					                targetElement.style.overflow = "hidden";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            targetElement.isZoomed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const closeBtn = targetElement.querySelector("button[aria-label='Remove Image']");
 | 
				
			||||||
 | 
					            if (closeBtn) {
 | 
				
			||||||
 | 
					                closeBtn.addEventListener("click", resetZoom);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (canvas && isExtension) {
 | 
				
			||||||
 | 
					                const parentElement = targetElement.closest('[id^="component-"]');
 | 
				
			||||||
 | 
					                if (
 | 
				
			||||||
 | 
					                    canvas &&
 | 
				
			||||||
 | 
					                    parseFloat(canvas.style.width) > parentElement.offsetWidth &&
 | 
				
			||||||
 | 
					                    parseFloat(targetElement.style.width) > parentElement.offsetWidth
 | 
				
			||||||
 | 
					                ) {
 | 
				
			||||||
 | 
					                    fitToElement();
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (
 | 
				
			||||||
 | 
					                canvas &&
 | 
				
			||||||
 | 
					                !isExtension &&
 | 
				
			||||||
 | 
					                parseFloat(canvas.style.width) > 865 &&
 | 
				
			||||||
 | 
					                parseFloat(targetElement.style.width) > 865
 | 
				
			||||||
 | 
					            ) {
 | 
				
			||||||
 | 
					                fitToElement();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            targetElement.style.width = "";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 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 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;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Adjust the brush size based on the deltaY value from a mouse wheel event
 | 
				
			||||||
 | 
					        function adjustBrushSize(
 | 
				
			||||||
 | 
					            elemId,
 | 
				
			||||||
 | 
					            deltaY,
 | 
				
			||||||
 | 
					            withoutValue = false,
 | 
				
			||||||
 | 
					            percentage = 5
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            const input =
 | 
				
			||||||
 | 
					                gradioApp().querySelector(
 | 
				
			||||||
 | 
					                    `${elemId} input[aria-label='Brush radius']`
 | 
				
			||||||
 | 
					                ) ||
 | 
				
			||||||
 | 
					                gradioApp().querySelector(
 | 
				
			||||||
 | 
					                    `${elemId} button[aria-label="Use brush"]`
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (input) {
 | 
				
			||||||
 | 
					                input.click();
 | 
				
			||||||
 | 
					                if (!withoutValue) {
 | 
				
			||||||
 | 
					                    const maxValue =
 | 
				
			||||||
 | 
					                        parseFloat(input.getAttribute("max")) || 100;
 | 
				
			||||||
 | 
					                    const changeAmount = maxValue * (percentage / 100);
 | 
				
			||||||
 | 
					                    const newValue =
 | 
				
			||||||
 | 
					                        parseFloat(input.value) +
 | 
				
			||||||
 | 
					                        (deltaY > 0 ? -changeAmount : changeAmount);
 | 
				
			||||||
 | 
					                    input.value = Math.min(Math.max(newValue, 0), maxValue);
 | 
				
			||||||
 | 
					                    input.dispatchEvent(new Event("change"));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Reset zoom when uploading a new image
 | 
				
			||||||
 | 
					        const fileInput = gradioApp().querySelector(
 | 
				
			||||||
 | 
					            `${elemId} input[type="file"][accept="image/*"].svelte-116rqfv`
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        fileInput.addEventListener("click", resetZoom);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Update the zoom level and pan position of the target element based on the values of the zoomLevel, panX and panY variables
 | 
				
			||||||
 | 
					        function updateZoom(newZoomLevel, mouseX, mouseY) {
 | 
				
			||||||
 | 
					            newZoomLevel = Math.max(0.1, Math.min(newZoomLevel, 15));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            elemData[elemId].panX +=
 | 
				
			||||||
 | 
					                mouseX - (mouseX * newZoomLevel) / elemData[elemId].zoomLevel;
 | 
				
			||||||
 | 
					            elemData[elemId].panY +=
 | 
				
			||||||
 | 
					                mouseY - (mouseY * newZoomLevel) / elemData[elemId].zoomLevel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            targetElement.style.transformOrigin = "0 0";
 | 
				
			||||||
 | 
					            targetElement.style.transform = `translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px) scale(${newZoomLevel})`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            toggleOverlap("on");
 | 
				
			||||||
 | 
					            if (isExtension) {
 | 
				
			||||||
 | 
					                targetElement.style.overflow = "visible";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return newZoomLevel;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Change the zoom level based on user interaction
 | 
				
			||||||
 | 
					        function changeZoomLevel(operation, e) {
 | 
				
			||||||
 | 
					            if (isModifierKey(e, hotkeysConfig.canvas_hotkey_zoom)) {
 | 
				
			||||||
 | 
					                e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let zoomPosX, zoomPosY;
 | 
				
			||||||
 | 
					                let delta = 0.2;
 | 
				
			||||||
 | 
					                if (elemData[elemId].zoomLevel > 7) {
 | 
				
			||||||
 | 
					                    delta = 0.9;
 | 
				
			||||||
 | 
					                } else if (elemData[elemId].zoomLevel > 2) {
 | 
				
			||||||
 | 
					                    delta = 0.6;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                zoomPosX = e.clientX;
 | 
				
			||||||
 | 
					                zoomPosY = e.clientY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                fullScreenMode = false;
 | 
				
			||||||
 | 
					                elemData[elemId].zoomLevel = updateZoom(
 | 
				
			||||||
 | 
					                    elemData[elemId].zoomLevel +
 | 
				
			||||||
 | 
					                    (operation === "+" ? delta : -delta),
 | 
				
			||||||
 | 
					                    zoomPosX - targetElement.getBoundingClientRect().left,
 | 
				
			||||||
 | 
					                    zoomPosY - targetElement.getBoundingClientRect().top
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                targetElement.isZoomed = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * 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})`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let parentElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (isExtension) {
 | 
				
			||||||
 | 
					                parentElement = targetElement.closest('[id^="component-"]');
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                parentElement = targetElement.parentElement;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Get element and screen dimensions
 | 
				
			||||||
 | 
					            const elementWidth = targetElement.offsetWidth;
 | 
				
			||||||
 | 
					            const elementHeight = targetElement.offsetHeight;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const screenWidth = parentElement.clientWidth - 24;
 | 
				
			||||||
 | 
					            const screenHeight = parentElement.clientHeight;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Calculate scale and offsets
 | 
				
			||||||
 | 
					            const scaleX = screenWidth / elementWidth;
 | 
				
			||||||
 | 
					            const scaleY = screenHeight / elementHeight;
 | 
				
			||||||
 | 
					            const scale = Math.min(scaleX, scaleY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const offsetX =0;
 | 
				
			||||||
 | 
					            const offsetY =0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 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");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * 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"]`
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!canvas) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (canvas.offsetWidth > 862 || isExtension) {
 | 
				
			||||||
 | 
					                targetElement.style.width = (canvas.offsetWidth + 2) + "px";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (isExtension) {
 | 
				
			||||||
 | 
					                targetElement.style.overflow = "visible";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            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");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 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 (!hotkeysConfig.canvas_blur_prompt) {
 | 
				
			||||||
 | 
					                if (event.target.nodeName === 'TEXTAREA' || event.target.nodeName === 'INPUT') {
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const hotkeyActions = {
 | 
				
			||||||
 | 
					                [hotkeysConfig.canvas_hotkey_reset]: resetZoom,
 | 
				
			||||||
 | 
					                [hotkeysConfig.canvas_hotkey_overlap]: toggleOverlap,
 | 
				
			||||||
 | 
					                [hotkeysConfig.canvas_hotkey_fullscreen]: fitToScreen
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const action = hotkeyActions[event.code];
 | 
				
			||||||
 | 
					            if (action) {
 | 
				
			||||||
 | 
					                event.preventDefault();
 | 
				
			||||||
 | 
					                action(event);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (
 | 
				
			||||||
 | 
					                isModifierKey(event, hotkeysConfig.canvas_hotkey_zoom) ||
 | 
				
			||||||
 | 
					                isModifierKey(event, hotkeysConfig.canvas_hotkey_adjust)
 | 
				
			||||||
 | 
					            ) {
 | 
				
			||||||
 | 
					                event.preventDefault();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get Mouse position
 | 
				
			||||||
 | 
					        function getMousePosition(e) {
 | 
				
			||||||
 | 
					            mouseX = e.offsetX;
 | 
				
			||||||
 | 
					            mouseY = e.offsetY;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Simulation of the function to put a long image into the screen.
 | 
				
			||||||
 | 
					        // We detect if an image has a scroll bar or not, make a fullscreen to reveal the image, then reduce it to fit into the element.
 | 
				
			||||||
 | 
					        // We hide the image and show it to the user when it is ready.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        targetElement.isExpanded = false;
 | 
				
			||||||
 | 
					        function autoExpand() {
 | 
				
			||||||
 | 
					            const canvas = document.querySelector(`${elemId} canvas[key="interface"]`);
 | 
				
			||||||
 | 
					            if (canvas) {
 | 
				
			||||||
 | 
					                if (hasHorizontalScrollbar(targetElement) && targetElement.isExpanded === false) {
 | 
				
			||||||
 | 
					                    targetElement.style.visibility = "hidden";
 | 
				
			||||||
 | 
					                    setTimeout(() => {
 | 
				
			||||||
 | 
					                        fitToScreen();
 | 
				
			||||||
 | 
					                        resetZoom();
 | 
				
			||||||
 | 
					                        targetElement.style.visibility = "visible";
 | 
				
			||||||
 | 
					                        targetElement.isExpanded = true;
 | 
				
			||||||
 | 
					                    }, 10);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        targetElement.addEventListener("mousemove", getMousePosition);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //observers
 | 
				
			||||||
 | 
					        // Creating an observer with a callback function to handle DOM changes
 | 
				
			||||||
 | 
					        const observer = new MutationObserver((mutationsList, observer) => {
 | 
				
			||||||
 | 
					            for (let mutation of mutationsList) {
 | 
				
			||||||
 | 
					                // If the style attribute of the canvas has changed, by observation it happens only when the picture changes
 | 
				
			||||||
 | 
					                if (mutation.type === 'attributes' && mutation.attributeName === 'style' &&
 | 
				
			||||||
 | 
					                    mutation.target.tagName.toLowerCase() === 'canvas') {
 | 
				
			||||||
 | 
					                    targetElement.isExpanded = false;
 | 
				
			||||||
 | 
					                    setTimeout(resetZoom, 10);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Apply auto expand if enabled
 | 
				
			||||||
 | 
					        if (hotkeysConfig.canvas_auto_expand) {
 | 
				
			||||||
 | 
					            targetElement.addEventListener("mousemove", autoExpand);
 | 
				
			||||||
 | 
					            // Set up an observer to track attribute changes
 | 
				
			||||||
 | 
					            observer.observe(targetElement, {attributes: true, childList: true, subtree: true});
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        targetElement.addEventListener("wheel", e => {
 | 
				
			||||||
 | 
					            // change zoom level
 | 
				
			||||||
 | 
					            const operation = e.deltaY > 0 ? "-" : "+";
 | 
				
			||||||
 | 
					            changeZoomLevel(operation, e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Handle brush size adjustment with ctrl key pressed
 | 
				
			||||||
 | 
					            if (isModifierKey(e, hotkeysConfig.canvas_hotkey_adjust)) {
 | 
				
			||||||
 | 
					                e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Increase or decrease brush size based on scroll direction
 | 
				
			||||||
 | 
					                adjustBrushSize(elemId, e.deltaY);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Handle the move event for pan functionality. Updates the panX and panY variables and applies the new transform to the target element.
 | 
				
			||||||
 | 
					        function handleMoveKeyDown(e) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Disable key locks to make pasting from the buffer work correctly
 | 
				
			||||||
 | 
					            if ((e.ctrlKey && e.code === 'KeyV') || (e.ctrlKey && event.code === 'KeyC') || e.code === "F5") {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // before activating shortcut, ensure user is not actively typing in an input field
 | 
				
			||||||
 | 
					            if (!hotkeysConfig.canvas_blur_prompt) {
 | 
				
			||||||
 | 
					                if (e.target.nodeName === 'TEXTAREA' || e.target.nodeName === 'INPUT') {
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (e.code === hotkeysConfig.canvas_hotkey_move) {
 | 
				
			||||||
 | 
					                if (!e.ctrlKey && !e.metaKey && isKeyDownHandlerAttached) {
 | 
				
			||||||
 | 
					                    e.preventDefault();
 | 
				
			||||||
 | 
					                    document.activeElement.blur();
 | 
				
			||||||
 | 
					                    isMoving = true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function handleMoveKeyUp(e) {
 | 
				
			||||||
 | 
					            if (e.code === hotkeysConfig.canvas_hotkey_move) {
 | 
				
			||||||
 | 
					                isMoving = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        document.addEventListener("keydown", handleMoveKeyDown);
 | 
				
			||||||
 | 
					        document.addEventListener("keyup", handleMoveKeyUp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Detect zoom level and update the pan speed.
 | 
				
			||||||
 | 
					        function updatePanPosition(movementX, movementY) {
 | 
				
			||||||
 | 
					            let panSpeed = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (elemData[elemId].zoomLevel > 8) {
 | 
				
			||||||
 | 
					                panSpeed = 3.5;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            elemData[elemId].panX += movementX * panSpeed;
 | 
				
			||||||
 | 
					            elemData[elemId].panY += movementY * panSpeed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Delayed redraw of an element
 | 
				
			||||||
 | 
					            requestAnimationFrame(() => {
 | 
				
			||||||
 | 
					                targetElement.style.transform = `translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px) scale(${elemData[elemId].zoomLevel})`;
 | 
				
			||||||
 | 
					                toggleOverlap("on");
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function handleMoveByKey(e) {
 | 
				
			||||||
 | 
					            if (isMoving && elemId === activeElement) {
 | 
				
			||||||
 | 
					                updatePanPosition(e.movementX, e.movementY);
 | 
				
			||||||
 | 
					                targetElement.style.pointerEvents = "none";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (isExtension) {
 | 
				
			||||||
 | 
					                    targetElement.style.overflow = "visible";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                targetElement.style.pointerEvents = "auto";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Prevents sticking to the mouse
 | 
				
			||||||
 | 
					        window.onblur = function() {
 | 
				
			||||||
 | 
					            isMoving = false;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Checks for extension
 | 
				
			||||||
 | 
					        function checkForOutBox() {
 | 
				
			||||||
 | 
					            const parentElement = targetElement.closest('[id^="component-"]');
 | 
				
			||||||
 | 
					            if (parentElement.offsetWidth < targetElement.offsetWidth && !targetElement.isExpanded) {
 | 
				
			||||||
 | 
					                resetZoom();
 | 
				
			||||||
 | 
					                targetElement.isExpanded = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (parentElement.offsetWidth < targetElement.offsetWidth && elemData[elemId].zoomLevel == 1) {
 | 
				
			||||||
 | 
					                resetZoom();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (parentElement.offsetWidth < targetElement.offsetWidth && targetElement.offsetWidth * elemData[elemId].zoomLevel > parentElement.offsetWidth && elemData[elemId].zoomLevel < 1 && !targetElement.isZoomed) {
 | 
				
			||||||
 | 
					                resetZoom();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (isExtension) {
 | 
				
			||||||
 | 
					            targetElement.addEventListener("mousemove", checkForOutBox);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        window.addEventListener('resize', (e) => {
 | 
				
			||||||
 | 
					            resetZoom();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (isExtension) {
 | 
				
			||||||
 | 
					                targetElement.isExpanded = false;
 | 
				
			||||||
 | 
					                targetElement.isZoomed = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gradioApp().addEventListener("mousemove", handleMoveByKey);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    applyZoomAndPan("#inpaint_canvas");
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -26,10 +26,12 @@ def javascript_html():
 | 
				
			|||||||
    script_js_path = webpath('javascript/script.js')
 | 
					    script_js_path = webpath('javascript/script.js')
 | 
				
			||||||
    context_menus_js_path = webpath('javascript/contextMenus.js')
 | 
					    context_menus_js_path = webpath('javascript/contextMenus.js')
 | 
				
			||||||
    localization_js_path = webpath('javascript/localization.js')
 | 
					    localization_js_path = webpath('javascript/localization.js')
 | 
				
			||||||
 | 
					    zoom_js_path = webpath('javascript/zoom.js')
 | 
				
			||||||
    head = f'<script type="text/javascript">{localization_js(args_manager.args.language)}</script>\n'
 | 
					    head = f'<script type="text/javascript">{localization_js(args_manager.args.language)}</script>\n'
 | 
				
			||||||
    head += f'<script type="text/javascript" src="{script_js_path}"></script>\n'
 | 
					    head += f'<script type="text/javascript" src="{script_js_path}"></script>\n'
 | 
				
			||||||
    head += f'<script type="text/javascript" src="{context_menus_js_path}"></script>\n'
 | 
					    head += f'<script type="text/javascript" src="{context_menus_js_path}"></script>\n'
 | 
				
			||||||
    head += f'<script type="text/javascript" src="{localization_js_path}"></script>\n'
 | 
					    head += f'<script type="text/javascript" src="{localization_js_path}"></script>\n'
 | 
				
			||||||
 | 
					    head += f'<script type="text/javascript" src="{zoom_js_path}"></script>\n'
 | 
				
			||||||
    return head
 | 
					    return head
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,8 @@
 | 
				
			|||||||
 | 
					# 2.1.720
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Added Canvas Zoom to inpaint canvas
 | 
				
			||||||
 | 
					* Fixed the problem that image will be cropped in UI when the uploaded image is too wide.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 2.1.719
 | 
					# 2.1.719
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* I18N
 | 
					* I18N
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								webui.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								webui.py
									
									
									
									
									
								
							@ -145,7 +145,7 @@ with shared.gradio_root:
 | 
				
			|||||||
                                           outputs=ip_ad_cols + ip_types + ip_stops + ip_weights, queue=False)
 | 
					                                           outputs=ip_ad_cols + ip_types + ip_stops + ip_weights, queue=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    with gr.TabItem(label='Inpaint or Outpaint (beta)') as inpaint_tab:
 | 
					                    with gr.TabItem(label='Inpaint or Outpaint (beta)') as inpaint_tab:
 | 
				
			||||||
                        inpaint_input_image = grh.Image(label='Drag above image to here', source='upload', type='numpy', tool='sketch', height=500, brush_color="#FFFFFF")
 | 
					                        inpaint_input_image = grh.Image(label='Drag above image to here', source='upload', type='numpy', tool='sketch', height=500, brush_color="#FFFFFF", elem_id='inpaint_canvas')
 | 
				
			||||||
                        gr.HTML('Outpaint Expansion (<a href="https://github.com/lllyasviel/Fooocus/discussions/414" target="_blank">\U0001F4D4 Document</a>):')
 | 
					                        gr.HTML('Outpaint Expansion (<a href="https://github.com/lllyasviel/Fooocus/discussions/414" target="_blank">\U0001F4D4 Document</a>):')
 | 
				
			||||||
                        outpaint_selections = gr.CheckboxGroup(choices=['Left', 'Right', 'Top', 'Bottom'], value=[], label='Outpaint', show_label=False, container=False)
 | 
					                        outpaint_selections = gr.CheckboxGroup(choices=['Left', 'Right', 'Top', 'Bottom'], value=[], label='Outpaint', show_label=False, container=False)
 | 
				
			||||||
                        gr.HTML('* \"Inpaint or Outpaint\" is powered by the sampler \"DPMPP Fooocus Seamless 2M SDE Karras Inpaint Sampler\" (beta)')
 | 
					                        gr.HTML('* \"Inpaint or Outpaint\" is powered by the sampler \"DPMPP Fooocus Seamless 2M SDE Karras Inpaint Sampler\" (beta)')
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user