clean up, add undo and some small changes (#1432)
This commit is contained in:
parent
a7f82b2545
commit
05ef3e6861
@ -1,18 +1,5 @@
|
|||||||
onUiLoaded(async() => {
|
onUiLoaded(async() => {
|
||||||
// Helper functions
|
// 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
|
// Detect whether the element has a horizontal scroll bar
|
||||||
function hasHorizontalScrollbar(element) {
|
function hasHorizontalScrollbar(element) {
|
||||||
@ -33,140 +20,40 @@ onUiLoaded(async() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// Create hotkey configuration with the provided options
|
||||||
function createHotkeyConfig(defaultHotkeysConfig) {
|
function createHotkeyConfig(defaultHotkeysConfig) {
|
||||||
const result = {}; // Resulting hotkey configuration
|
const result = {}; // Resulting hotkey configuration
|
||||||
|
|
||||||
for (const key in defaultHotkeysConfig) {
|
for (const key in defaultHotkeysConfig) {
|
||||||
result[key] = defaultHotkeysConfig[key];
|
result[key] = defaultHotkeysConfig[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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
|
// Default config
|
||||||
const defaultHotkeysConfig = {
|
const defaultHotkeysConfig = {
|
||||||
canvas_hotkey_zoom: "Alt",
|
canvas_hotkey_zoom: "Shift",
|
||||||
canvas_hotkey_adjust: "Ctrl",
|
canvas_hotkey_adjust: "Ctrl",
|
||||||
|
canvas_zoom_undo_extra_key: "Ctrl",
|
||||||
|
canvas_zoom_hotkey_undo: "KeyZ",
|
||||||
canvas_hotkey_reset: "KeyR",
|
canvas_hotkey_reset: "KeyR",
|
||||||
canvas_hotkey_fullscreen: "KeyS",
|
canvas_hotkey_fullscreen: "KeyS",
|
||||||
canvas_hotkey_move: "KeyF",
|
canvas_hotkey_move: "KeyF",
|
||||||
canvas_hotkey_overlap: "KeyO",
|
|
||||||
canvas_disabled_functions: [],
|
|
||||||
canvas_show_tooltip: true,
|
canvas_show_tooltip: true,
|
||||||
canvas_auto_expand: true,
|
canvas_auto_expand: true,
|
||||||
canvas_blur_prompt: false,
|
canvas_blur_prompt: true,
|
||||||
};
|
|
||||||
|
|
||||||
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
|
// Loading the configuration from opts
|
||||||
const preHotkeysConfig = createHotkeyConfig(
|
const hotkeysConfig = createHotkeyConfig(
|
||||||
defaultHotkeysConfig
|
defaultHotkeysConfig
|
||||||
);
|
);
|
||||||
|
|
||||||
// Disable functions that are not needed by the user
|
|
||||||
const hotkeysConfig = disableFunctions(
|
|
||||||
preHotkeysConfig,
|
|
||||||
preHotkeysConfig.canvas_disabled_functions
|
|
||||||
);
|
|
||||||
|
|
||||||
let isMoving = false;
|
let isMoving = false;
|
||||||
let mouseX, mouseY;
|
|
||||||
let activeElement;
|
let activeElement;
|
||||||
|
|
||||||
const elemData = {};
|
const elemData = {};
|
||||||
|
|
||||||
function applyZoomAndPan(elemId, isExtension = true) {
|
function applyZoomAndPan(elemId) {
|
||||||
const targetElement = gradioApp().querySelector(elemId);
|
const targetElement = gradioApp().querySelector(elemId);
|
||||||
|
|
||||||
if (!targetElement) {
|
if (!targetElement) {
|
||||||
@ -181,6 +68,7 @@ onUiLoaded(async() => {
|
|||||||
panX: 0,
|
panX: 0,
|
||||||
panY: 0
|
panY: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
let fullScreenMode = false;
|
let fullScreenMode = false;
|
||||||
|
|
||||||
// Create tooltip
|
// Create tooltip
|
||||||
@ -211,44 +99,46 @@ onUiLoaded(async() => {
|
|||||||
action: "Adjust brush size",
|
action: "Adjust brush size",
|
||||||
keySuffix: " + wheel"
|
keySuffix: " + wheel"
|
||||||
},
|
},
|
||||||
|
{configKey: "canvas_zoom_hotkey_undo", action: "Undo last action", keyPrefix: `${hotkeysConfig.canvas_zoom_undo_extra_key} + ` },
|
||||||
{configKey: "canvas_hotkey_reset", action: "Reset zoom"},
|
{configKey: "canvas_hotkey_reset", action: "Reset zoom"},
|
||||||
{
|
{
|
||||||
configKey: "canvas_hotkey_fullscreen",
|
configKey: "canvas_hotkey_fullscreen",
|
||||||
action: "Fullscreen mode"
|
action: "Fullscreen mode"
|
||||||
},
|
},
|
||||||
{configKey: "canvas_hotkey_move", action: "Move canvas"},
|
{configKey: "canvas_hotkey_move", action: "Move canvas"}
|
||||||
{configKey: "canvas_hotkey_overlap", action: "Overlap"}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create hotkeys array with disabled property based on the config values
|
// Create hotkeys array based on the config values
|
||||||
const hotkeys = hotkeysInfo.map(info => {
|
const hotkeys = hotkeysInfo.map((info) => {
|
||||||
const configValue = hotkeysConfig[info.configKey];
|
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) {
|
let key = configValue.slice(-1);
|
||||||
if (hotkey.disabled) {
|
|
||||||
continue;
|
if (info.keySuffix) {
|
||||||
|
key = `${configValue}${info.keySuffix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const p = document.createElement("p");
|
if (info.keyPrefix && info.keyPrefix !== "None + ") {
|
||||||
p.innerHTML = `<b>${hotkey.key}</b> - ${hotkey.action}`;
|
key = `${info.keyPrefix}${configValue[3]}`;
|
||||||
tooltipContent.appendChild(p);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Add information and content elements to the tooltip element
|
return {
|
||||||
tooltip.appendChild(info);
|
key,
|
||||||
tooltip.appendChild(tooltipContent);
|
action: info.action,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// Add a hint element to the target element
|
hotkeys
|
||||||
toolTipElemnt.appendChild(tooltip);
|
.forEach(hotkey => {
|
||||||
|
const p = document.createElement("p");
|
||||||
|
p.innerHTML = `<b>${hotkey.key}</b> - ${hotkey.action}`;
|
||||||
|
tooltipContent.appendChild(p);
|
||||||
|
});
|
||||||
|
|
||||||
|
tooltip.append(info, tooltipContent);
|
||||||
|
|
||||||
|
// Add a hint element to the target element
|
||||||
|
toolTipElemnt.appendChild(tooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Show tool tip if setting enable
|
//Show tool tip if setting enable
|
||||||
@ -264,9 +154,7 @@ onUiLoaded(async() => {
|
|||||||
panY: 0
|
panY: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isExtension) {
|
targetElement.style.overflow = "hidden";
|
||||||
targetElement.style.overflow = "hidden";
|
|
||||||
}
|
|
||||||
|
|
||||||
targetElement.isZoomed = false;
|
targetElement.isZoomed = false;
|
||||||
|
|
||||||
@ -284,7 +172,7 @@ onUiLoaded(async() => {
|
|||||||
closeBtn.addEventListener("click", resetZoom);
|
closeBtn.addEventListener("click", resetZoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canvas && isExtension) {
|
if (canvas) {
|
||||||
const parentElement = targetElement.closest('[id^="component-"]');
|
const parentElement = targetElement.closest('[id^="component-"]');
|
||||||
if (
|
if (
|
||||||
canvas &&
|
canvas &&
|
||||||
@ -297,16 +185,6 @@ onUiLoaded(async() => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
canvas &&
|
|
||||||
!isExtension &&
|
|
||||||
parseFloat(canvas.style.width) > 865 &&
|
|
||||||
parseFloat(targetElement.style.width) > 865
|
|
||||||
) {
|
|
||||||
fitToElement();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
targetElement.style.width = "";
|
targetElement.style.width = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,11 +250,9 @@ onUiLoaded(async() => {
|
|||||||
|
|
||||||
targetElement.style.transformOrigin = "0 0";
|
targetElement.style.transformOrigin = "0 0";
|
||||||
targetElement.style.transform = `translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px) scale(${newZoomLevel})`;
|
targetElement.style.transform = `translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px) scale(${newZoomLevel})`;
|
||||||
|
targetElement.style.overflow = "visible";
|
||||||
|
|
||||||
toggleOverlap("on");
|
toggleOverlap("on");
|
||||||
if (isExtension) {
|
|
||||||
targetElement.style.overflow = "visible";
|
|
||||||
}
|
|
||||||
|
|
||||||
return newZoomLevel;
|
return newZoomLevel;
|
||||||
}
|
}
|
||||||
@ -388,6 +264,7 @@ onUiLoaded(async() => {
|
|||||||
|
|
||||||
let zoomPosX, zoomPosY;
|
let zoomPosX, zoomPosY;
|
||||||
let delta = 0.2;
|
let delta = 0.2;
|
||||||
|
|
||||||
if (elemData[elemId].zoomLevel > 7) {
|
if (elemData[elemId].zoomLevel > 7) {
|
||||||
delta = 0.9;
|
delta = 0.9;
|
||||||
} else if (elemData[elemId].zoomLevel > 2) {
|
} else if (elemData[elemId].zoomLevel > 2) {
|
||||||
@ -421,12 +298,7 @@ onUiLoaded(async() => {
|
|||||||
|
|
||||||
let parentElement;
|
let parentElement;
|
||||||
|
|
||||||
if (isExtension) {
|
parentElement = targetElement.closest('[id^="component-"]');
|
||||||
parentElement = targetElement.closest('[id^="component-"]');
|
|
||||||
} else {
|
|
||||||
parentElement = targetElement.parentElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Get element and screen dimensions
|
// Get element and screen dimensions
|
||||||
const elementWidth = targetElement.offsetWidth;
|
const elementWidth = targetElement.offsetWidth;
|
||||||
@ -455,6 +327,26 @@ onUiLoaded(async() => {
|
|||||||
toggleOverlap("off");
|
toggleOverlap("off");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Undo last action
|
||||||
|
function undoLastAction(e) {
|
||||||
|
let isCtrlPressed = isModifierKey(e, hotkeysConfig.canvas_zoom_undo_extra_key)
|
||||||
|
const isAuxButton = e.button >= 3;
|
||||||
|
|
||||||
|
if (isAuxButton) {
|
||||||
|
isCtrlPressed = true
|
||||||
|
} else {
|
||||||
|
if (!isModifierKey(e, hotkeysConfig.canvas_zoom_undo_extra_key)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move undoBtn query outside the if statement to avoid unnecessary queries
|
||||||
|
const undoBtn = document.querySelector(`${activeElement} button[aria-label="Undo"]`);
|
||||||
|
|
||||||
|
if ((isCtrlPressed) && undoBtn ) {
|
||||||
|
e.preventDefault();
|
||||||
|
undoBtn.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function fits the target element to the screen by calculating
|
* This function fits the target element to the screen by calculating
|
||||||
* the required scale and offsets. It also updates the global variables
|
* the required scale and offsets. It also updates the global variables
|
||||||
@ -469,13 +361,8 @@ onUiLoaded(async() => {
|
|||||||
|
|
||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
|
|
||||||
if (canvas.offsetWidth > 862 || isExtension) {
|
targetElement.style.width = (canvas.offsetWidth + 2) + "px";
|
||||||
targetElement.style.width = (canvas.offsetWidth + 2) + "px";
|
targetElement.style.overflow = "visible";
|
||||||
}
|
|
||||||
|
|
||||||
if (isExtension) {
|
|
||||||
targetElement.style.overflow = "visible";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fullScreenMode) {
|
if (fullScreenMode) {
|
||||||
resetZoom();
|
resetZoom();
|
||||||
@ -549,11 +436,11 @@ onUiLoaded(async() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const hotkeyActions = {
|
const hotkeyActions = {
|
||||||
[hotkeysConfig.canvas_hotkey_reset]: resetZoom,
|
[hotkeysConfig.canvas_hotkey_reset]: resetZoom,
|
||||||
[hotkeysConfig.canvas_hotkey_overlap]: toggleOverlap,
|
[hotkeysConfig.canvas_hotkey_overlap]: toggleOverlap,
|
||||||
[hotkeysConfig.canvas_hotkey_fullscreen]: fitToScreen
|
[hotkeysConfig.canvas_hotkey_fullscreen]: fitToScreen,
|
||||||
|
[hotkeysConfig.canvas_zoom_hotkey_undo]: undoLastAction,
|
||||||
};
|
};
|
||||||
|
|
||||||
const action = hotkeyActions[event.code];
|
const action = hotkeyActions[event.code];
|
||||||
@ -597,26 +484,27 @@ onUiLoaded(async() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
targetElement.addEventListener("mousemove", getMousePosition);
|
targetElement.addEventListener("mousemove", getMousePosition);
|
||||||
|
targetElement.addEventListener("auxclick", undoLastAction);
|
||||||
|
|
||||||
//observers
|
//observers
|
||||||
// Creating an observer with a callback function to handle DOM changes
|
// Creating an observer with a callback function to handle DOM changes
|
||||||
const observer = new MutationObserver((mutationsList, observer) => {
|
const observer = new MutationObserver((mutationsList, observer) => {
|
||||||
for (let mutation of mutationsList) {
|
for (let mutation of mutationsList) {
|
||||||
// If the style attribute of the canvas has changed, by observation it happens only when the picture changes
|
// 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' &&
|
if (mutation.type === 'attributes' && mutation.attributeName === 'style' &&
|
||||||
mutation.target.tagName.toLowerCase() === 'canvas') {
|
mutation.target.tagName.toLowerCase() === 'canvas') {
|
||||||
targetElement.isExpanded = false;
|
targetElement.isExpanded = false;
|
||||||
setTimeout(resetZoom, 10);
|
setTimeout(resetZoom, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply auto expand if enabled
|
// Apply auto expand if enabled
|
||||||
if (hotkeysConfig.canvas_auto_expand) {
|
if (hotkeysConfig.canvas_auto_expand) {
|
||||||
targetElement.addEventListener("mousemove", autoExpand);
|
targetElement.addEventListener("mousemove", autoExpand);
|
||||||
// Set up an observer to track attribute changes
|
// Set up an observer to track attribute changes
|
||||||
observer.observe(targetElement, {attributes: true, childList: true, subtree: true});
|
observer.observe(targetElement, { attributes: true, childList: true, subtree: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle events only inside the targetElement
|
// Handle events only inside the targetElement
|
||||||
let isKeyDownHandlerAttached = false;
|
let isKeyDownHandlerAttached = false;
|
||||||
@ -661,7 +549,7 @@ onUiLoaded(async() => {
|
|||||||
function handleMoveKeyDown(e) {
|
function handleMoveKeyDown(e) {
|
||||||
|
|
||||||
// Disable key locks to make pasting from the buffer work correctly
|
// 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") {
|
if ((e.ctrlKey && e.code === 'KeyV') || (e.ctrlKey && e.code === 'KeyC') || e.code === "F5") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,11 +601,7 @@ onUiLoaded(async() => {
|
|||||||
if (isMoving && elemId === activeElement) {
|
if (isMoving && elemId === activeElement) {
|
||||||
updatePanPosition(e.movementX, e.movementY);
|
updatePanPosition(e.movementX, e.movementY);
|
||||||
targetElement.style.pointerEvents = "none";
|
targetElement.style.pointerEvents = "none";
|
||||||
|
targetElement.style.overflow = "visible";
|
||||||
if (isExtension) {
|
|
||||||
targetElement.style.overflow = "visible";
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
targetElement.style.pointerEvents = "auto";
|
targetElement.style.pointerEvents = "auto";
|
||||||
}
|
}
|
||||||
@ -745,18 +629,13 @@ onUiLoaded(async() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isExtension) {
|
targetElement.addEventListener("mousemove", checkForOutBox);
|
||||||
targetElement.addEventListener("mousemove", checkForOutBox);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
window.addEventListener('resize', (e) => {
|
window.addEventListener('resize', (e) => {
|
||||||
resetZoom();
|
resetZoom();
|
||||||
|
|
||||||
if (isExtension) {
|
targetElement.isExpanded = false;
|
||||||
targetElement.isExpanded = false;
|
targetElement.isZoomed = false;
|
||||||
targetElement.isZoomed = false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
gradioApp().addEventListener("mousemove", handleMoveByKey);
|
gradioApp().addEventListener("mousemove", handleMoveByKey);
|
||||||
|
Loading…
Reference in New Issue
Block a user