fluxdev/stable-diffusion-webui-forge
1
1function gradioApp() {2 const elems = document.getElementsByTagName('gradio-app');3 const elem = elems.length == 0 ? document : elems[0];4 5 if (elem !== document) {6 elem.getElementById = function(id) {7 return document.getElementById(id);8 };9 }10 return elem.shadowRoot ? elem.shadowRoot : elem;11}12 13/**14 * Get the currently selected top-level UI tab button (e.g. the button that says "Extras").15 */16function get_uiCurrentTab() {17 return gradioApp().querySelector('#tabs > .tab-nav > button.selected');18}19 20/**21 * Get the first currently visible top-level UI tab content (e.g. the div hosting the "txt2img" UI).22 */23function get_uiCurrentTabContent() {24 return gradioApp().querySelector('#tabs > .tabitem[id^=tab_]:not([style*="display: none"])');25}26 27var uiUpdateCallbacks = [];28var uiAfterUpdateCallbacks = [];29var uiLoadedCallbacks = [];30var uiTabChangeCallbacks = [];31var optionsChangedCallbacks = [];32var uiAfterUpdateTimeout = null;33var uiCurrentTab = null;34 35/**36 * Register callback to be called at each UI update.37 * The callback receives an array of MutationRecords as an argument.38 */39function onUiUpdate(callback) {40 uiUpdateCallbacks.push(callback);41}42 43/**44 * Register callback to be called soon after UI updates.45 * The callback receives no arguments.46 *47 * This is preferred over `onUiUpdate` if you don't need48 * access to the MutationRecords, as your function will49 * not be called quite as often.50 */51function onAfterUiUpdate(callback) {52 uiAfterUpdateCallbacks.push(callback);53}54 55/**56 * Register callback to be called when the UI is loaded.57 * The callback receives no arguments.58 */59function onUiLoaded(callback) {60 uiLoadedCallbacks.push(callback);61}62 63/**64 * Register callback to be called when the UI tab is changed.65 * The callback receives no arguments.66 */67function onUiTabChange(callback) {68 uiTabChangeCallbacks.push(callback);69}70 71/**72 * Register callback to be called when the options are changed.73 * The callback receives no arguments.74 * @param callback75 */76function onOptionsChanged(callback) {77 optionsChangedCallbacks.push(callback);78}79 80function executeCallbacks(queue, arg) {81 for (const callback of queue) {82 try {83 callback(arg);84 } catch (e) {85 console.error("error running callback", callback, ":", e);86 }87 }88}89 90/**91 * Schedule the execution of the callbacks registered with onAfterUiUpdate.92 * The callbacks are executed after a short while, unless another call to this function93 * is made before that time. IOW, the callbacks are executed only once, even94 * when there are multiple mutations observed.95 */96function scheduleAfterUiUpdateCallbacks() {97 clearTimeout(uiAfterUpdateTimeout);98 uiAfterUpdateTimeout = setTimeout(function() {99 executeCallbacks(uiAfterUpdateCallbacks);100 }, 200);101}102 103var executedOnLoaded = false;104 105document.addEventListener("DOMContentLoaded", function() {106 var mutationObserver = new MutationObserver(function(m) {107 if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) {108 executedOnLoaded = true;109 executeCallbacks(uiLoadedCallbacks);110 }111 112 executeCallbacks(uiUpdateCallbacks, m);113 scheduleAfterUiUpdateCallbacks();114 const newTab = get_uiCurrentTab();115 if (newTab && (newTab !== uiCurrentTab)) {116 uiCurrentTab = newTab;117 executeCallbacks(uiTabChangeCallbacks);118 }119 });120 mutationObserver.observe(gradioApp(), {childList: true, subtree: true});121});122 123/**124 * Add keyboard shortcuts:125 * Ctrl+Enter to start/restart a generation126 * Alt/Option+Enter to skip a generation127 * Esc to interrupt a generation128 */129document.addEventListener('keydown', function(e) {130 const isEnter = e.key === 'Enter' || e.keyCode === 13;131 const isCtrlKey = e.metaKey || e.ctrlKey;132 const isAltKey = e.altKey;133 const isEsc = e.key === 'Escape';134 135 const generateButton = get_uiCurrentTabContent().querySelector('button[id$=_generate]');136 const interruptButton = get_uiCurrentTabContent().querySelector('button[id$=_interrupt]');137 const skipButton = get_uiCurrentTabContent().querySelector('button[id$=_skip]');138 139 if (isCtrlKey && isEnter) {140 if (interruptButton.style.display === 'block') {141 interruptButton.click();142 const callback = (mutationList) => {143 for (const mutation of mutationList) {144 if (mutation.type === 'attributes' && mutation.attributeName === 'style') {145 if (interruptButton.style.display === 'none') {146 generateButton.click();147 observer.disconnect();148 }149 }150 }151 };152 const observer = new MutationObserver(callback);153 observer.observe(interruptButton, {attributes: true});154 } else {155 generateButton.click();156 }157 e.preventDefault();158 }159 160 if (isAltKey && isEnter) {161 skipButton.click();162 e.preventDefault();163 }164 165 if (isEsc) {166 const globalPopup = document.querySelector('.global-popup');167 const lightboxModal = document.querySelector('#lightboxModal');168 if (!globalPopup || globalPopup.style.display === 'none') {169 if (document.activeElement === lightboxModal) return;170 if (interruptButton.style.display === 'block') {171 interruptButton.click();172 e.preventDefault();173 }174 }175 }176});177 178/**179 * checks that a UI element is not in another hidden element or tab content180 */181function uiElementIsVisible(el) {182 if (el === document) {183 return true;184 }185 186 const computedStyle = getComputedStyle(el);187 const isVisible = computedStyle.display !== 'none';188 189 if (!isVisible) return false;190 return uiElementIsVisible(el.parentNode);191}192 193function uiElementInSight(el) {194 const clRect = el.getBoundingClientRect();195 const windowHeight = window.innerHeight;196 const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight;197 198 return isOnScreen;199}200 