stable-diffusion-webui
/
script.js
199 строк · 6.0 Кб
1function gradioApp() {2const elems = document.getElementsByTagName('gradio-app');3const elem = elems.length == 0 ? document : elems[0];4
5if (elem !== document) {6elem.getElementById = function(id) {7return document.getElementById(id);8};9}10return 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() {17return 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() {24return 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) {40uiUpdateCallbacks.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 need
48* access to the MutationRecords, as your function will
49* not be called quite as often.
50*/
51function onAfterUiUpdate(callback) {52uiAfterUpdateCallbacks.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) {60uiLoadedCallbacks.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) {68uiTabChangeCallbacks.push(callback);69}
70
71/**
72* Register callback to be called when the options are changed.
73* The callback receives no arguments.
74* @param callback
75*/
76function onOptionsChanged(callback) {77optionsChangedCallbacks.push(callback);78}
79
80function executeCallbacks(queue, arg) {81for (const callback of queue) {82try {83callback(arg);84} catch (e) {85console.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 function
93* is made before that time. IOW, the callbacks are executed only once, even
94* when there are multiple mutations observed.
95*/
96function scheduleAfterUiUpdateCallbacks() {97clearTimeout(uiAfterUpdateTimeout);98uiAfterUpdateTimeout = setTimeout(function() {99executeCallbacks(uiAfterUpdateCallbacks);100}, 200);101}
102
103var executedOnLoaded = false;104
105document.addEventListener("DOMContentLoaded", function() {106var mutationObserver = new MutationObserver(function(m) {107if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) {108executedOnLoaded = true;109executeCallbacks(uiLoadedCallbacks);110}111
112executeCallbacks(uiUpdateCallbacks, m);113scheduleAfterUiUpdateCallbacks();114const newTab = get_uiCurrentTab();115if (newTab && (newTab !== uiCurrentTab)) {116uiCurrentTab = newTab;117executeCallbacks(uiTabChangeCallbacks);118}119});120mutationObserver.observe(gradioApp(), {childList: true, subtree: true});121});122
123/**
124* Add keyboard shortcuts:
125* Ctrl+Enter to start/restart a generation
126* Alt/Option+Enter to skip a generation
127* Esc to interrupt a generation
128*/
129document.addEventListener('keydown', function(e) {130const isEnter = e.key === 'Enter' || e.keyCode === 13;131const isCtrlKey = e.metaKey || e.ctrlKey;132const isAltKey = e.altKey;133const isEsc = e.key === 'Escape';134
135const generateButton = get_uiCurrentTabContent().querySelector('button[id$=_generate]');136const interruptButton = get_uiCurrentTabContent().querySelector('button[id$=_interrupt]');137const skipButton = get_uiCurrentTabContent().querySelector('button[id$=_skip]');138
139if (isCtrlKey && isEnter) {140if (interruptButton.style.display === 'block') {141interruptButton.click();142const callback = (mutationList) => {143for (const mutation of mutationList) {144if (mutation.type === 'attributes' && mutation.attributeName === 'style') {145if (interruptButton.style.display === 'none') {146generateButton.click();147observer.disconnect();148}149}150}151};152const observer = new MutationObserver(callback);153observer.observe(interruptButton, {attributes: true});154} else {155generateButton.click();156}157e.preventDefault();158}159
160if (isAltKey && isEnter) {161skipButton.click();162e.preventDefault();163}164
165if (isEsc) {166const globalPopup = document.querySelector('.global-popup');167const lightboxModal = document.querySelector('#lightboxModal');168if (!globalPopup || globalPopup.style.display === 'none') {169if (document.activeElement === lightboxModal) return;170if (interruptButton.style.display === 'block') {171interruptButton.click();172e.preventDefault();173}174}175}176});177
178/**
179* checks that a UI element is not in another hidden element or tab content
180*/
181function uiElementIsVisible(el) {182if (el === document) {183return true;184}185
186const computedStyle = getComputedStyle(el);187const isVisible = computedStyle.display !== 'none';188
189if (!isVisible) return false;190return uiElementIsVisible(el.parentNode);191}
192
193function uiElementInSight(el) {194const clRect = el.getBoundingClientRect();195const windowHeight = window.innerHeight;196const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight;197
198return isOnScreen;199}
200