diff --git a/script.js b/script.js index 34cca7651..8bf1257da 100644 --- a/script.js +++ b/script.js @@ -1,163 +1,165 @@ -function gradioApp() { - const elems = document.getElementsByTagName('gradio-app'); - const elem = elems.length == 0 ? document : elems[0]; - - if (elem !== document) { - elem.getElementById = function(id) { - return document.getElementById(id); - }; - } - return elem.shadowRoot ? elem.shadowRoot : elem; -} - -/** - * Get the currently selected top-level UI tab button (e.g. the button that says "Extras"). - */ -function get_uiCurrentTab() { - return gradioApp().querySelector('#tabs > .tab-nav > button.selected'); -} - -/** - * Get the first currently visible top-level UI tab content (e.g. the div hosting the "txt2img" UI). - */ -function get_uiCurrentTabContent() { - return gradioApp().querySelector('#tabs > .tabitem[id^=tab_]:not([style*="display: none"])'); -} - -var uiUpdateCallbacks = []; -var uiAfterUpdateCallbacks = []; -var uiLoadedCallbacks = []; -var uiTabChangeCallbacks = []; -var optionsChangedCallbacks = []; -var uiAfterUpdateTimeout = null; -var uiCurrentTab = null; - -/** - * Register callback to be called at each UI update. - * The callback receives an array of MutationRecords as an argument. - */ -function onUiUpdate(callback) { - uiUpdateCallbacks.push(callback); -} - -/** - * Register callback to be called soon after UI updates. - * The callback receives no arguments. - * - * This is preferred over `onUiUpdate` if you don't need - * access to the MutationRecords, as your function will - * not be called quite as often. - */ -function onAfterUiUpdate(callback) { - uiAfterUpdateCallbacks.push(callback); -} - -/** - * Register callback to be called when the UI is loaded. - * The callback receives no arguments. - */ -function onUiLoaded(callback) { - uiLoadedCallbacks.push(callback); -} - -/** - * Register callback to be called when the UI tab is changed. - * The callback receives no arguments. - */ -function onUiTabChange(callback) { - uiTabChangeCallbacks.push(callback); -} - -/** - * Register callback to be called when the options are changed. - * The callback receives no arguments. - * @param callback - */ -function onOptionsChanged(callback) { - optionsChangedCallbacks.push(callback); -} - -function executeCallbacks(queue, arg) { - for (const callback of queue) { - try { - callback(arg); - } catch (e) { - console.error("error running callback", callback, ":", e); - } - } -} - -/** - * Schedule the execution of the callbacks registered with onAfterUiUpdate. - * The callbacks are executed after a short while, unless another call to this function - * is made before that time. IOW, the callbacks are executed only once, even - * when there are multiple mutations observed. - */ -function scheduleAfterUiUpdateCallbacks() { - clearTimeout(uiAfterUpdateTimeout); - uiAfterUpdateTimeout = setTimeout(function() { - executeCallbacks(uiAfterUpdateCallbacks); - }, 200); -} - -var executedOnLoaded = false; - -document.addEventListener("DOMContentLoaded", function() { - var mutationObserver = new MutationObserver(function(m) { - if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) { - executedOnLoaded = true; - executeCallbacks(uiLoadedCallbacks); - } - - executeCallbacks(uiUpdateCallbacks, m); - scheduleAfterUiUpdateCallbacks(); - const newTab = get_uiCurrentTab(); - if (newTab && (newTab !== uiCurrentTab)) { - uiCurrentTab = newTab; - executeCallbacks(uiTabChangeCallbacks); - } - }); - mutationObserver.observe(gradioApp(), {childList: true, subtree: true}); -}); - -/** - * Add a ctrl+enter as a shortcut to start a generation - */ -document.addEventListener('keydown', function(e) { - var handled = false; - if (e.key !== undefined) { - if ((e.key == "Enter" && (e.metaKey || e.ctrlKey || e.altKey))) handled = true; - } else if (e.keyCode !== undefined) { - if ((e.keyCode == 13 && (e.metaKey || e.ctrlKey || e.altKey))) handled = true; - } - if (handled) { - var button = get_uiCurrentTabContent().querySelector('button[id$=_generate]'); - if (button) { - button.click(); - } - e.preventDefault(); - } -}); - -/** - * checks that a UI element is not in another hidden element or tab content - */ -function uiElementIsVisible(el) { - if (el === document) { - return true; - } - - const computedStyle = getComputedStyle(el); - const isVisible = computedStyle.display !== 'none'; - - if (!isVisible) return false; - return uiElementIsVisible(el.parentNode); -} - -function uiElementInSight(el) { - const clRect = el.getBoundingClientRect(); - const windowHeight = window.innerHeight; - const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight; - - return isOnScreen; -} +function gradioApp() { + const elems = document.getElementsByTagName('gradio-app'); + const elem = elems.length == 0 ? document : elems[0]; + + if (elem !== document) { + elem.getElementById = function(id) { + return document.getElementById(id); + }; + } + return elem.shadowRoot ? elem.shadowRoot : elem; +} + +/** + * Get the currently selected top-level UI tab button (e.g. the button that says "Extras"). + */ +function get_uiCurrentTab() { + return gradioApp().querySelector('#tabs > .tab-nav > button.selected'); +} + +/** + * Get the first currently visible top-level UI tab content (e.g. the div hosting the "txt2img" UI). + */ +function get_uiCurrentTabContent() { + return gradioApp().querySelector('#tabs > .tabitem[id^=tab_]:not([style*="display: none"])'); +} + +var uiUpdateCallbacks = []; +var uiAfterUpdateCallbacks = []; +var uiLoadedCallbacks = []; +var uiTabChangeCallbacks = []; +var optionsChangedCallbacks = []; +var uiAfterUpdateTimeout = null; +var uiCurrentTab = null; + +/** + * Register callback to be called at each UI update. + * The callback receives an array of MutationRecords as an argument. + */ +function onUiUpdate(callback) { + uiUpdateCallbacks.push(callback); +} + +/** + * Register callback to be called soon after UI updates. + * The callback receives no arguments. + * + * This is preferred over `onUiUpdate` if you don't need + * access to the MutationRecords, as your function will + * not be called quite as often. + */ +function onAfterUiUpdate(callback) { + uiAfterUpdateCallbacks.push(callback); +} + +/** + * Register callback to be called when the UI is loaded. + * The callback receives no arguments. + */ +function onUiLoaded(callback) { + uiLoadedCallbacks.push(callback); +} + +/** + * Register callback to be called when the UI tab is changed. + * The callback receives no arguments. + */ +function onUiTabChange(callback) { + uiTabChangeCallbacks.push(callback); +} + +/** + * Register callback to be called when the options are changed. + * The callback receives no arguments. + * @param callback + */ +function onOptionsChanged(callback) { + optionsChangedCallbacks.push(callback); +} + +function executeCallbacks(queue, arg) { + for (const callback of queue) { + try { + callback(arg); + } catch (e) { + console.error("error running callback", callback, ":", e); + } + } +} + +/** + * Schedule the execution of the callbacks registered with onAfterUiUpdate. + * The callbacks are executed after a short while, unless another call to this function + * is made before that time. IOW, the callbacks are executed only once, even + * when there are multiple mutations observed. + */ +function scheduleAfterUiUpdateCallbacks() { + clearTimeout(uiAfterUpdateTimeout); + uiAfterUpdateTimeout = setTimeout(function() { + executeCallbacks(uiAfterUpdateCallbacks); + }, 200); +} + +var executedOnLoaded = false; + +document.addEventListener("DOMContentLoaded", function() { + var mutationObserver = new MutationObserver(function(m) { + if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) { + executedOnLoaded = true; + executeCallbacks(uiLoadedCallbacks); + } + + executeCallbacks(uiUpdateCallbacks, m); + scheduleAfterUiUpdateCallbacks(); + const newTab = get_uiCurrentTab(); + if (newTab && (newTab !== uiCurrentTab)) { + uiCurrentTab = newTab; + executeCallbacks(uiTabChangeCallbacks); + } + }); + mutationObserver.observe(gradioApp(), {childList: true, subtree: true}); +}); + +/** + * Add a Ctrl (Alt) + Enter as a shortcut to start / restart a generation + */ +document.addEventListener('keydown', (e) => { + const isEnter = e.key === 'Enter' || e.keyCode === 13 + const isModifierKey = e.metaKey || e.ctrlKey || e.altKey + + const interruptButton = get_uiCurrentTabContent().querySelector('button[id$=_interrupt]') + const generateButton = get_uiCurrentTabContent().querySelector('button[id$=_generate]') + + if (isEnter && isModifierKey) { + if (interruptButton.style.display === 'block') { + interruptButton.click() + setTimeout(() => generateButton.click(), 500) + } else { + generateButton.click() + } + e.preventDefault() + } +}) + +/** + * checks that a UI element is not in another hidden element or tab content + */ +function uiElementIsVisible(el) { + if (el === document) { + return true; + } + + const computedStyle = getComputedStyle(el); + const isVisible = computedStyle.display !== 'none'; + + if (!isVisible) return false; + return uiElementIsVisible(el.parentNode); +} + +function uiElementInSight(el) { + const clRect = el.getBoundingClientRect(); + const windowHeight = window.innerHeight; + const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight; + + return isOnScreen; +}