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) { 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(function () { 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 }