script.js 5.9 KB

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