Chen ZiLu commited on
Commit
43560ff
1 Parent(s): 6dc64a1
Files changed (2) hide show
  1. javascript/ui.js +387 -0
  2. modules/ui_components.py +74 -0
javascript/ui.js ADDED
@@ -0,0 +1,387 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // various functions for interaction with ui.py not large enough to warrant putting them in separate files
2
+
3
+ function set_theme(theme) {
4
+ var gradioURL = window.location.href;
5
+ if (!gradioURL.includes('?__theme=')) {
6
+ window.location.replace(gradioURL + '?__theme=' + theme);
7
+ }
8
+ }
9
+
10
+ function all_gallery_buttons() {
11
+ var allGalleryButtons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery].gradio-gallery .thumbnails > .thumbnail-item.thumbnail-small');
12
+ var visibleGalleryButtons = [];
13
+ allGalleryButtons.forEach(function(elem) {
14
+ if (elem.parentElement.offsetParent) {
15
+ visibleGalleryButtons.push(elem);
16
+ }
17
+ });
18
+ return visibleGalleryButtons;
19
+ }
20
+
21
+ function selected_gallery_button() {
22
+ var allCurrentButtons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery].gradio-gallery .thumbnail-item.thumbnail-small.selected');
23
+ var visibleCurrentButton = null;
24
+ allCurrentButtons.forEach(function(elem) {
25
+ if (elem.parentElement.offsetParent) {
26
+ visibleCurrentButton = elem;
27
+ }
28
+ });
29
+ return visibleCurrentButton;
30
+ }
31
+
32
+ function selected_gallery_index() {
33
+ var buttons = all_gallery_buttons();
34
+ var button = selected_gallery_button();
35
+
36
+ var result = -1;
37
+ buttons.forEach(function(v, i) {
38
+ if (v == button) {
39
+ result = i;
40
+ }
41
+ });
42
+
43
+ return result;
44
+ }
45
+
46
+ function extract_image_from_gallery(gallery) {
47
+ if (gallery.length == 0) {
48
+ return [null];
49
+ }
50
+ if (gallery.length == 1) {
51
+ return [gallery[0]];
52
+ }
53
+
54
+ var index = selected_gallery_index();
55
+
56
+ if (index < 0 || index >= gallery.length) {
57
+ // Use the first image in the gallery as the default
58
+ index = 0;
59
+ }
60
+
61
+ return [gallery[index]];
62
+ }
63
+
64
+ window.args_to_array = Array.from; // Compatibility with e.g. extensions that may expect this to be around
65
+
66
+ function switch_to_txt2img() {
67
+ gradioApp().querySelector('#tabs').querySelectorAll('button')[0].click();
68
+
69
+ return Array.from(arguments);
70
+ }
71
+
72
+ function switch_to_img2img_tab(no) {
73
+ gradioApp().querySelector('#tabs').querySelectorAll('button')[1].click();
74
+ gradioApp().getElementById('mode_img2img').querySelectorAll('button')[no].click();
75
+ }
76
+ function switch_to_img2img() {
77
+ switch_to_img2img_tab(0);
78
+ return Array.from(arguments);
79
+ }
80
+
81
+ function switch_to_sketch() {
82
+ switch_to_img2img_tab(1);
83
+ return Array.from(arguments);
84
+ }
85
+
86
+ function switch_to_inpaint() {
87
+ switch_to_img2img_tab(2);
88
+ return Array.from(arguments);
89
+ }
90
+
91
+ function switch_to_inpaint_sketch() {
92
+ switch_to_img2img_tab(3);
93
+ return Array.from(arguments);
94
+ }
95
+
96
+ function switch_to_extras() {
97
+ gradioApp().querySelector('#tabs').querySelectorAll('button')[2].click();
98
+
99
+ return Array.from(arguments);
100
+ }
101
+
102
+ function get_tab_index(tabId) {
103
+ let buttons = gradioApp().getElementById(tabId).querySelector('div').querySelectorAll('button');
104
+ for (let i = 0; i < buttons.length; i++) {
105
+ if (buttons[i].classList.contains('selected')) {
106
+ return i;
107
+ }
108
+ }
109
+ return 0;
110
+ }
111
+
112
+ function create_tab_index_args(tabId, args) {
113
+ var res = Array.from(args);
114
+ res[0] = get_tab_index(tabId);
115
+ return res;
116
+ }
117
+
118
+ function get_img2img_tab_index() {
119
+ let res = Array.from(arguments);
120
+ res.splice(-2);
121
+ res[0] = get_tab_index('mode_img2img');
122
+ return res;
123
+ }
124
+
125
+ function create_submit_args(args) {
126
+ var res = Array.from(args);
127
+
128
+ // As it is currently, txt2img and img2img send back the previous output args (txt2img_gallery, generation_info, html_info) whenever you generate a new image.
129
+ // This can lead to uploading a huge gallery of previously generated images, which leads to an unnecessary delay between submitting and beginning to generate.
130
+ // I don't know why gradio is sending outputs along with inputs, but we can prevent sending the image gallery here, which seems to be an issue for some.
131
+ // If gradio at some point stops sending outputs, this may break something
132
+ if (Array.isArray(res[res.length - 3])) {
133
+ res[res.length - 3] = null;
134
+ }
135
+
136
+ return res;
137
+ }
138
+
139
+ function showSubmitButtons(tabname, show) {
140
+ gradioApp().getElementById(tabname + '_interrupt').style.display = show ? "none" : "block";
141
+ gradioApp().getElementById(tabname + '_skip').style.display = show ? "none" : "block";
142
+ }
143
+
144
+ function showRestoreProgressButton(tabname, show) {
145
+ var button = gradioApp().getElementById(tabname + "_restore_progress");
146
+ if (!button) return;
147
+
148
+ button.style.display = show ? "flex" : "none";
149
+ }
150
+
151
+ function submit() {
152
+ showSubmitButtons('txt2img', false);
153
+
154
+ var id = randomId();
155
+ localStorage.setItem("txt2img_task_id", id);
156
+
157
+ requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function() {
158
+ showSubmitButtons('txt2img', true);
159
+ localStorage.removeItem("txt2img_task_id");
160
+ showRestoreProgressButton('txt2img', false);
161
+ });
162
+
163
+ var res = create_submit_args(arguments);
164
+
165
+ res[0] = id;
166
+
167
+ return res;
168
+ }
169
+
170
+ function submit_img2img() {
171
+ showSubmitButtons('img2img', false);
172
+
173
+ var id = randomId();
174
+ localStorage.setItem("img2img_task_id", id);
175
+
176
+ requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function() {
177
+ showSubmitButtons('img2img', true);
178
+ localStorage.removeItem("img2img_task_id");
179
+ showRestoreProgressButton('img2img', false);
180
+ });
181
+
182
+ var res = create_submit_args(arguments);
183
+
184
+ res[0] = id;
185
+ res[1] = get_tab_index('mode_img2img');
186
+
187
+ return res;
188
+ }
189
+
190
+ function restoreProgressTxt2img() {
191
+ showRestoreProgressButton("txt2img", false);
192
+ var id = localStorage.getItem("txt2img_task_id");
193
+
194
+ id = localStorage.getItem("txt2img_task_id");
195
+
196
+ if (id) {
197
+ requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function() {
198
+ showSubmitButtons('txt2img', true);
199
+ }, null, 0);
200
+ }
201
+
202
+ return id;
203
+ }
204
+
205
+ function restoreProgressImg2img() {
206
+ showRestoreProgressButton("img2img", false);
207
+
208
+ var id = localStorage.getItem("img2img_task_id");
209
+
210
+ if (id) {
211
+ requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function() {
212
+ showSubmitButtons('img2img', true);
213
+ }, null, 0);
214
+ }
215
+
216
+ return id;
217
+ }
218
+
219
+
220
+ onUiLoaded(function() {
221
+ showRestoreProgressButton('txt2img', localStorage.getItem("txt2img_task_id"));
222
+ showRestoreProgressButton('img2img', localStorage.getItem("img2img_task_id"));
223
+ });
224
+
225
+
226
+ function modelmerger() {
227
+ var id = randomId();
228
+ requestProgress(id, gradioApp().getElementById('modelmerger_results_panel'), null, function() {});
229
+
230
+ var res = create_submit_args(arguments);
231
+ res[0] = id;
232
+ return res;
233
+ }
234
+
235
+
236
+ function ask_for_style_name(_, prompt_text, negative_prompt_text) {
237
+ var name_ = prompt('Style name:');
238
+ return [name_, prompt_text, negative_prompt_text];
239
+ }
240
+
241
+ function confirm_clear_prompt(prompt, negative_prompt) {
242
+ if (confirm("Delete prompt?")) {
243
+ prompt = "";
244
+ negative_prompt = "";
245
+ }
246
+
247
+ return [prompt, negative_prompt];
248
+ }
249
+
250
+
251
+ var opts = {};
252
+ onAfterUiUpdate(function() {
253
+ if (Object.keys(opts).length != 0) return;
254
+
255
+ var json_elem = gradioApp().getElementById('settings_json');
256
+ if (json_elem == null) return;
257
+
258
+ var textarea = json_elem.querySelector('textarea');
259
+ var jsdata = textarea.value;
260
+ opts = JSON.parse(jsdata);
261
+
262
+ executeCallbacks(optionsChangedCallbacks); /*global optionsChangedCallbacks*/
263
+
264
+ Object.defineProperty(textarea, 'value', {
265
+ set: function(newValue) {
266
+ var valueProp = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
267
+ var oldValue = valueProp.get.call(textarea);
268
+ valueProp.set.call(textarea, newValue);
269
+
270
+ if (oldValue != newValue) {
271
+ opts = JSON.parse(textarea.value);
272
+ }
273
+
274
+ executeCallbacks(optionsChangedCallbacks);
275
+ },
276
+ get: function() {
277
+ var valueProp = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
278
+ return valueProp.get.call(textarea);
279
+ }
280
+ });
281
+
282
+ json_elem.parentElement.style.display = "none";
283
+
284
+ setupTokenCounters();
285
+
286
+ var show_all_pages = gradioApp().getElementById('settings_show_all_pages');
287
+ var settings_tabs = gradioApp().querySelector('#settings div');
288
+ if (show_all_pages && settings_tabs) {
289
+ settings_tabs.appendChild(show_all_pages);
290
+ show_all_pages.onclick = function() {
291
+ gradioApp().querySelectorAll('#settings > div').forEach(function(elem) {
292
+ if (elem.id == "settings_tab_licenses") {
293
+ return;
294
+ }
295
+
296
+ elem.style.display = "block";
297
+ });
298
+ };
299
+ }
300
+ });
301
+
302
+ onOptionsChanged(function() {
303
+ var elem = gradioApp().getElementById('sd_checkpoint_hash');
304
+ var sd_checkpoint_hash = opts.sd_checkpoint_hash || "";
305
+ var shorthash = sd_checkpoint_hash.substring(0, 10);
306
+
307
+ if (elem && elem.textContent != shorthash) {
308
+ elem.textContent = shorthash;
309
+ elem.title = sd_checkpoint_hash;
310
+ elem.href = "https://google.com/search?q=" + sd_checkpoint_hash;
311
+ }
312
+ });
313
+
314
+ let txt2img_textarea, img2img_textarea = undefined;
315
+
316
+ function restart_reload() {
317
+ document.body.innerHTML = '<h1 style="font-family:monospace;margin-top:20%;color:lightgray;text-align:center;">Reloading...</h1>';
318
+
319
+ var requestPing = function() {
320
+ requestGet("./internal/ping", {}, function(data) {
321
+ location.reload();
322
+ }, function() {
323
+ setTimeout(requestPing, 500);
324
+ });
325
+ };
326
+
327
+ setTimeout(requestPing, 2000);
328
+
329
+ return [];
330
+ }
331
+
332
+ // Simulate an `input` DOM event for Gradio Textbox component. Needed after you edit its contents in javascript, otherwise your edits
333
+ // will only visible on web page and not sent to python.
334
+ function updateInput(target) {
335
+ let e = new Event("input", {bubbles: true});
336
+ Object.defineProperty(e, "target", {value: target});
337
+ target.dispatchEvent(e);
338
+ }
339
+
340
+
341
+ var desiredCheckpointName = null;
342
+ function selectCheckpoint(name) {
343
+ desiredCheckpointName = name;
344
+ gradioApp().getElementById('change_checkpoint').click();
345
+ }
346
+
347
+ function currentImg2imgSourceResolution(w, h, scaleBy) {
348
+ var img = gradioApp().querySelector('#mode_img2img > div[style="display: block;"] img');
349
+ return img ? [img.naturalWidth, img.naturalHeight, scaleBy] : [0, 0, scaleBy];
350
+ }
351
+
352
+ function updateImg2imgResizeToTextAfterChangingImage() {
353
+ // At the time this is called from gradio, the image has no yet been replaced.
354
+ // There may be a better solution, but this is simple and straightforward so I'm going with it.
355
+
356
+ setTimeout(function() {
357
+ gradioApp().getElementById('img2img_update_resize_to').click();
358
+ }, 500);
359
+
360
+ return [];
361
+
362
+ }
363
+
364
+
365
+
366
+ function setRandomSeed(elem_id) {
367
+ var input = gradioApp().querySelector("#" + elem_id + " input");
368
+ if (!input) return [];
369
+
370
+ input.value = "-1";
371
+ updateInput(input);
372
+ return [];
373
+ }
374
+
375
+ function switchWidthHeight(tabname) {
376
+ var width = gradioApp().querySelector("#" + tabname + "_width input[type=number]");
377
+ var height = gradioApp().querySelector("#" + tabname + "_height input[type=number]");
378
+ if (!width || !height) return [];
379
+
380
+ var tmp = width.value;
381
+ width.value = height.value;
382
+ height.value = tmp;
383
+
384
+ updateInput(width);
385
+ updateInput(height);
386
+ return [];
387
+ }
modules/ui_components.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+
4
+ class FormComponent:
5
+ def get_expected_parent(self):
6
+ return gr.components.Form
7
+
8
+
9
+ gr.Dropdown.get_expected_parent = FormComponent.get_expected_parent
10
+
11
+
12
+ class ToolButton(FormComponent, gr.Button):
13
+ """Small button with single emoji as text, fits inside gradio forms"""
14
+
15
+ def __init__(self, *args, **kwargs):
16
+ classes = kwargs.pop("elem_classes", [])
17
+ super().__init__(*args, elem_classes=["tool", *classes], **kwargs)
18
+
19
+ def get_block_name(self):
20
+ return "button"
21
+
22
+
23
+ class FormRow(FormComponent, gr.Row):
24
+ """Same as gr.Row but fits inside gradio forms"""
25
+
26
+ def get_block_name(self):
27
+ return "row"
28
+
29
+
30
+ class FormColumn(FormComponent, gr.Column):
31
+ """Same as gr.Column but fits inside gradio forms"""
32
+
33
+ def get_block_name(self):
34
+ return "column"
35
+
36
+
37
+ class FormGroup(FormComponent, gr.Group):
38
+ """Same as gr.Row but fits inside gradio forms"""
39
+
40
+ def get_block_name(self):
41
+ return "group"
42
+
43
+
44
+ class FormHTML(FormComponent, gr.HTML):
45
+ """Same as gr.HTML but fits inside gradio forms"""
46
+
47
+ def get_block_name(self):
48
+ return "html"
49
+
50
+
51
+ class FormColorPicker(FormComponent, gr.ColorPicker):
52
+ """Same as gr.ColorPicker but fits inside gradio forms"""
53
+
54
+ def get_block_name(self):
55
+ return "colorpicker"
56
+
57
+
58
+ class DropdownMulti(FormComponent, gr.Dropdown):
59
+ """Same as gr.Dropdown but always multiselect"""
60
+ def __init__(self, **kwargs):
61
+ super().__init__(multiselect=True, **kwargs)
62
+
63
+ def get_block_name(self):
64
+ return "dropdown"
65
+
66
+
67
+ class DropdownEditable(FormComponent, gr.Dropdown):
68
+ """Same as gr.Dropdown but allows editing value"""
69
+ def __init__(self, **kwargs):
70
+ super().__init__(allow_custom_value=True, **kwargs)
71
+
72
+ def get_block_name(self):
73
+ return "dropdown"
74
+