|
onUiLoaded(async() => { |
|
const elementIDs = { |
|
img2imgTabs: "#mode_img2img .tab-nav", |
|
inpaint: "#img2maskimg", |
|
inpaintSketch: "#inpaint_sketch", |
|
rangeGroup: "#img2img_column_size", |
|
sketch: "#img2img_sketch" |
|
}; |
|
const tabNameToElementId = { |
|
"Inpaint sketch": elementIDs.inpaintSketch, |
|
"Inpaint": elementIDs.inpaint, |
|
"Sketch": elementIDs.sketch |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const waitForElement = (id) => new Promise(resolve => { |
|
const checkForElement = () => { |
|
const element = document.querySelector(id); |
|
if (element) return resolve(element); |
|
setTimeout(checkForElement, 100); |
|
}; |
|
checkForElement(); |
|
}); |
|
|
|
function getActiveTab(elements, all = false) { |
|
const tabs = elements.img2imgTabs.querySelectorAll("button"); |
|
|
|
if (all) return tabs; |
|
|
|
for (let tab of tabs) { |
|
if (tab.classList.contains("selected")) { |
|
return tab; |
|
} |
|
} |
|
} |
|
|
|
|
|
function getTabId(elements) { |
|
const activeTab = getActiveTab(elements); |
|
return tabNameToElementId[activeTab.innerText]; |
|
} |
|
|
|
|
|
async function waitForOpts() { |
|
for (; ;) { |
|
if (window.opts && Object.keys(window.opts).length) { |
|
return window.opts; |
|
} |
|
await new Promise(resolve => setTimeout(resolve, 100)); |
|
} |
|
} |
|
|
|
|
|
function hasHorizontalScrollbar(element) { |
|
return element.scrollWidth > element.clientWidth; |
|
} |
|
|
|
|
|
function isModifierKey(event, key) { |
|
switch (key) { |
|
case "Ctrl": |
|
return event.ctrlKey; |
|
case "Shift": |
|
return event.shiftKey; |
|
case "Alt": |
|
return event.altKey; |
|
default: |
|
return false; |
|
} |
|
} |
|
|
|
|
|
function isValidHotkey(value) { |
|
const specialKeys = ["Ctrl", "Alt", "Shift", "Disable"]; |
|
return ( |
|
(typeof value === "string" && |
|
value.length === 1 && |
|
/[a-z]/i.test(value)) || |
|
specialKeys.includes(value) |
|
); |
|
} |
|
|
|
|
|
function normalizeHotkey(hotkey) { |
|
return hotkey.length === 1 ? "Key" + hotkey.toUpperCase() : hotkey; |
|
} |
|
|
|
|
|
function formatHotkeyForDisplay(hotkey) { |
|
return hotkey.startsWith("Key") ? hotkey.slice(3) : hotkey; |
|
} |
|
|
|
|
|
function createHotkeyConfig(defaultHotkeysConfig, hotkeysConfigOpts) { |
|
const result = {}; |
|
const usedKeys = new Set(); |
|
|
|
|
|
for (const key in defaultHotkeysConfig) { |
|
const userValue = hotkeysConfigOpts[key]; |
|
const defaultValue = defaultHotkeysConfig[key]; |
|
|
|
|
|
if ( |
|
userValue === undefined || |
|
typeof userValue === "boolean" || |
|
typeof userValue === "object" || |
|
userValue === "disable" |
|
) { |
|
result[key] = |
|
userValue === undefined ? defaultValue : userValue; |
|
} else if (isValidHotkey(userValue)) { |
|
const normalizedUserValue = normalizeHotkey(userValue); |
|
|
|
|
|
if (!usedKeys.has(normalizedUserValue)) { |
|
usedKeys.add(normalizedUserValue); |
|
result[key] = normalizedUserValue; |
|
} else { |
|
console.error( |
|
`Hotkey: ${formatHotkeyForDisplay( |
|
userValue |
|
)} for ${key} is repeated and conflicts with another hotkey. The default hotkey is used: ${formatHotkeyForDisplay( |
|
defaultValue |
|
)}` |
|
); |
|
result[key] = defaultValue; |
|
} |
|
} else { |
|
console.error( |
|
`Hotkey: ${formatHotkeyForDisplay( |
|
userValue |
|
)} for ${key} is not valid. The default hotkey is used: ${formatHotkeyForDisplay( |
|
defaultValue |
|
)}` |
|
); |
|
result[key] = defaultValue; |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
|
|
function disableFunctions(config, disabledFunctions) { |
|
|
|
const hasOwnProperty = |
|
Object.prototype.hasOwnProperty.bind(functionMap); |
|
|
|
|
|
disabledFunctions.forEach(funcName => { |
|
if (hasOwnProperty(funcName)) { |
|
const key = functionMap[funcName]; |
|
config[key] = "disable"; |
|
} |
|
}); |
|
|
|
|
|
return config; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
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); |
|
} |
|
|
|
const hotkeysConfigOpts = await waitForOpts(); |
|
|
|
|
|
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" |
|
}; |
|
|
|
|
|
const preHotkeysConfig = createHotkeyConfig( |
|
defaultHotkeysConfig, |
|
hotkeysConfigOpts |
|
); |
|
|
|
|
|
const hotkeysConfig = disableFunctions( |
|
preHotkeysConfig, |
|
preHotkeysConfig.canvas_disabled_functions |
|
); |
|
|
|
let isMoving = false; |
|
let mouseX, mouseY; |
|
let activeElement; |
|
|
|
const elements = Object.fromEntries( |
|
Object.keys(elementIDs).map(id => [ |
|
id, |
|
gradioApp().querySelector(elementIDs[id]) |
|
]) |
|
); |
|
const elemData = {}; |
|
|
|
|
|
const rangeInputs = elements.rangeGroup ? |
|
Array.from(elements.rangeGroup.querySelectorAll("input")) : |
|
[ |
|
gradioApp().querySelector("#img2img_width input[type='range']"), |
|
gradioApp().querySelector("#img2img_height input[type='range']") |
|
]; |
|
|
|
for (const input of rangeInputs) { |
|
input?.addEventListener("input", () => restoreImgRedMask(elements)); |
|
} |
|
|
|
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; |
|
|
|
|
|
function createTooltip() { |
|
const toolTipElemnt = |
|
targetElement.querySelector(".image-container"); |
|
const tooltip = document.createElement("div"); |
|
tooltip.className = "canvas-tooltip"; |
|
|
|
|
|
const info = document.createElement("i"); |
|
info.className = "canvas-tooltip-info"; |
|
info.textContent = ""; |
|
|
|
|
|
const tooltipContent = document.createElement("div"); |
|
tooltipContent.className = "canvas-tooltip-content"; |
|
|
|
|
|
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"} |
|
]; |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
tooltip.appendChild(info); |
|
tooltip.appendChild(tooltipContent); |
|
|
|
|
|
toolTipElemnt.appendChild(tooltip); |
|
} |
|
|
|
|
|
if (hotkeysConfig.canvas_show_tooltip) { |
|
createTooltip(); |
|
} |
|
|
|
|
|
function fixCanvas() { |
|
const activeTab = getActiveTab(elements).textContent.trim(); |
|
|
|
if (activeTab !== "img2img") { |
|
const img = targetElement.querySelector(`${elemId} img`); |
|
|
|
if (img && img.style.display !== "none") { |
|
img.style.display = "none"; |
|
img.style.visibility = "hidden"; |
|
} |
|
} |
|
} |
|
|
|
|
|
function resetZoom() { |
|
elemData[elemId] = { |
|
zoomLevel: 1, |
|
panX: 0, |
|
panY: 0 |
|
}; |
|
|
|
if (isExtension) { |
|
targetElement.style.overflow = "hidden"; |
|
} |
|
|
|
targetElement.isZoomed = false; |
|
|
|
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; |
|
|
|
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 = ""; |
|
} |
|
|
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
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")); |
|
} |
|
} |
|
} |
|
|
|
|
|
const fileInput = gradioApp().querySelector( |
|
`${elemId} input[type="file"][accept="image/*"].svelte-116rqfv` |
|
); |
|
fileInput.addEventListener("click", resetZoom); |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function fitToElement() { |
|
|
|
targetElement.style.transform = `translate(${0}px, ${0}px) scale(${1})`; |
|
|
|
let parentElement; |
|
|
|
if (isExtension) { |
|
parentElement = targetElement.closest('[id^="component-"]'); |
|
} else { |
|
parentElement = targetElement.parentElement; |
|
} |
|
|
|
|
|
|
|
const elementWidth = targetElement.offsetWidth; |
|
const elementHeight = targetElement.offsetHeight; |
|
|
|
const screenWidth = parentElement.clientWidth; |
|
const screenHeight = parentElement.clientHeight; |
|
|
|
|
|
const elementRect = targetElement.getBoundingClientRect(); |
|
const parentRect = parentElement.getBoundingClientRect(); |
|
const elementX = elementRect.x - parentRect.x; |
|
|
|
|
|
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); |
|
|
|
|
|
targetElement.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`; |
|
|
|
|
|
elemData[elemId].zoomLevel = scale; |
|
elemData[elemId].panX = offsetX; |
|
elemData[elemId].panY = offsetY; |
|
|
|
fullScreenMode = false; |
|
toggleOverlap("off"); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
targetElement.style.transform = `translate(${0}px, ${0}px) scale(${1})`; |
|
|
|
|
|
const scrollbarWidth = |
|
window.innerWidth - document.documentElement.clientWidth; |
|
|
|
|
|
const elementWidth = targetElement.offsetWidth; |
|
const elementHeight = targetElement.offsetHeight; |
|
const screenWidth = window.innerWidth - scrollbarWidth; |
|
const screenHeight = window.innerHeight; |
|
|
|
|
|
const elementRect = targetElement.getBoundingClientRect(); |
|
const elementY = elementRect.y; |
|
const elementX = elementRect.x; |
|
|
|
|
|
const scaleX = screenWidth / elementWidth; |
|
const scaleY = screenHeight / elementHeight; |
|
const scale = Math.min(scaleX, scaleY); |
|
|
|
|
|
const computedStyle = window.getComputedStyle(targetElement); |
|
const transformOrigin = computedStyle.transformOrigin; |
|
const [originX, originY] = transformOrigin.split(" "); |
|
const originXValue = parseFloat(originX); |
|
const originYValue = parseFloat(originY); |
|
|
|
|
|
const offsetX = |
|
(screenWidth - elementWidth * scale) / 2 - |
|
elementX - |
|
originXValue * (1 - scale); |
|
const offsetY = |
|
(screenHeight - elementHeight * scale) / 2 - |
|
elementY - |
|
originYValue * (1 - scale); |
|
|
|
|
|
targetElement.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`; |
|
|
|
|
|
elemData[elemId].zoomLevel = scale; |
|
elemData[elemId].panX = offsetX; |
|
elemData[elemId].panY = offsetY; |
|
|
|
fullScreenMode = true; |
|
toggleOverlap("on"); |
|
} |
|
|
|
|
|
function handleKeyDown(event) { |
|
|
|
if ((event.ctrlKey && event.code === 'KeyV') || (event.ctrlKey && event.code === 'KeyC') || event.code === "F5") { |
|
return; |
|
} |
|
|
|
|
|
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(); |
|
} |
|
} |
|
|
|
|
|
function getMousePosition(e) { |
|
mouseX = e.offsetX; |
|
mouseY = e.offsetY; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
const observer = new MutationObserver((mutationsList, observer) => { |
|
for (let mutation of mutationsList) { |
|
|
|
if (mutation.type === 'attributes' && mutation.attributeName === 'style' && |
|
mutation.target.tagName.toLowerCase() === 'canvas') { |
|
targetElement.isExpanded = false; |
|
setTimeout(resetZoom, 10); |
|
} |
|
} |
|
}); |
|
|
|
|
|
if (hotkeysConfig.canvas_auto_expand) { |
|
targetElement.addEventListener("mousemove", autoExpand); |
|
|
|
observer.observe(targetElement, {attributes: true, childList: true, subtree: true}); |
|
} |
|
|
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
targetElement.addEventListener("mousemove", handleMouseMove); |
|
targetElement.addEventListener("mouseleave", handleMouseLeave); |
|
|
|
|
|
elements.img2imgTabs.addEventListener("click", resetZoom); |
|
elements.img2imgTabs.addEventListener("click", () => { |
|
|
|
if (parseInt(targetElement.style.width) > 865) { |
|
setTimeout(fitToElement, 0); |
|
} |
|
}); |
|
|
|
targetElement.addEventListener("wheel", e => { |
|
|
|
const operation = e.deltaY > 0 ? "-" : "+"; |
|
changeZoomLevel(operation, e); |
|
|
|
|
|
if (isModifierKey(e, hotkeysConfig.canvas_hotkey_adjust)) { |
|
e.preventDefault(); |
|
|
|
|
|
adjustBrushSize(elemId, e.deltaY); |
|
} |
|
}); |
|
|
|
|
|
function handleMoveKeyDown(e) { |
|
|
|
|
|
if ((e.ctrlKey && e.code === 'KeyV') || (e.ctrlKey && event.code === 'KeyC') || e.code === "F5") { |
|
return; |
|
} |
|
|
|
|
|
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); |
|
|
|
|
|
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; |
|
|
|
|
|
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"; |
|
} |
|
} |
|
|
|
|
|
window.onblur = function() { |
|
isMoving = false; |
|
}; |
|
|
|
|
|
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(elementIDs.sketch, false); |
|
applyZoomAndPan(elementIDs.inpaint, false); |
|
applyZoomAndPan(elementIDs.inpaintSketch, false); |
|
|
|
|
|
const applyZoomAndPanIntegration = async(id, elementIDs) => { |
|
const mainEl = document.querySelector(id); |
|
if (id.toLocaleLowerCase() === "none") { |
|
for (const elementID of elementIDs) { |
|
const el = await waitForElement(elementID); |
|
if (!el) break; |
|
applyZoomAndPan(elementID); |
|
} |
|
return; |
|
} |
|
|
|
if (!mainEl) return; |
|
mainEl.addEventListener("click", async() => { |
|
for (const elementID of elementIDs) { |
|
const el = await waitForElement(elementID); |
|
if (!el) break; |
|
applyZoomAndPan(elementID); |
|
} |
|
}, {once: true}); |
|
}; |
|
|
|
window.applyZoomAndPan = applyZoomAndPan; |
|
|
|
window.applyZoomAndPanIntegration = applyZoomAndPanIntegration; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}); |
|
|