cymic commited on
Commit
6c530fa
·
1 Parent(s): d4f45dc

Upload 10 files

Browse files
javascript/aspectRatioOverlay.js ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ let currentWidth = null;
3
+ let currentHeight = null;
4
+ let arFrameTimeout = setTimeout(function(){},0);
5
+
6
+ function dimensionChange(e,dimname){
7
+
8
+ if(dimname == 'Width'){
9
+ currentWidth = e.target.value*1.0
10
+ }
11
+ if(dimname == 'Height'){
12
+ currentHeight = e.target.value*1.0
13
+ }
14
+
15
+ var inImg2img = Boolean(gradioApp().querySelector("button.rounded-t-lg.border-gray-200"))
16
+
17
+ if(!inImg2img){
18
+ return;
19
+ }
20
+
21
+ var img2imgMode = gradioApp().querySelector('#mode_img2img.tabs > div > button.rounded-t-lg.border-gray-200')
22
+ if(img2imgMode){
23
+ img2imgMode=img2imgMode.innerText
24
+ }else{
25
+ return;
26
+ }
27
+
28
+ var redrawImage = gradioApp().querySelector('div[data-testid=image] img');
29
+ var inpaintImage = gradioApp().querySelector('#img2maskimg div[data-testid=image] img')
30
+
31
+ var targetElement = null;
32
+
33
+ if(img2imgMode=='img2img' && redrawImage){
34
+ targetElement = redrawImage;
35
+ }else if(img2imgMode=='Inpaint' && inpaintImage){
36
+ targetElement = inpaintImage;
37
+ }
38
+
39
+ if(targetElement){
40
+
41
+ var arPreviewRect = gradioApp().querySelector('#imageARPreview');
42
+ if(!arPreviewRect){
43
+ arPreviewRect = document.createElement('div')
44
+ arPreviewRect.id = "imageARPreview";
45
+ gradioApp().getRootNode().appendChild(arPreviewRect)
46
+ }
47
+
48
+
49
+
50
+ var viewportOffset = targetElement.getBoundingClientRect();
51
+
52
+ viewportscale = Math.min( targetElement.clientWidth/targetElement.naturalWidth, targetElement.clientHeight/targetElement.naturalHeight )
53
+
54
+ scaledx = targetElement.naturalWidth*viewportscale
55
+ scaledy = targetElement.naturalHeight*viewportscale
56
+
57
+ cleintRectTop = (viewportOffset.top+window.scrollY)
58
+ cleintRectLeft = (viewportOffset.left+window.scrollX)
59
+ cleintRectCentreY = cleintRectTop + (targetElement.clientHeight/2)
60
+ cleintRectCentreX = cleintRectLeft + (targetElement.clientWidth/2)
61
+
62
+ viewRectTop = cleintRectCentreY-(scaledy/2)
63
+ viewRectLeft = cleintRectCentreX-(scaledx/2)
64
+ arRectWidth = scaledx
65
+ arRectHeight = scaledy
66
+
67
+ arscale = Math.min( arRectWidth/currentWidth, arRectHeight/currentHeight )
68
+ arscaledx = currentWidth*arscale
69
+ arscaledy = currentHeight*arscale
70
+
71
+ arRectTop = cleintRectCentreY-(arscaledy/2)
72
+ arRectLeft = cleintRectCentreX-(arscaledx/2)
73
+ arRectWidth = arscaledx
74
+ arRectHeight = arscaledy
75
+
76
+ arPreviewRect.style.top = arRectTop+'px';
77
+ arPreviewRect.style.left = arRectLeft+'px';
78
+ arPreviewRect.style.width = arRectWidth+'px';
79
+ arPreviewRect.style.height = arRectHeight+'px';
80
+
81
+ clearTimeout(arFrameTimeout);
82
+ arFrameTimeout = setTimeout(function(){
83
+ arPreviewRect.style.display = 'none';
84
+ },2000);
85
+
86
+ arPreviewRect.style.display = 'block';
87
+
88
+ }
89
+
90
+ }
91
+
92
+
93
+ onUiUpdate(function(){
94
+ var arPreviewRect = gradioApp().querySelector('#imageARPreview');
95
+ if(arPreviewRect){
96
+ arPreviewRect.style.display = 'none';
97
+ }
98
+ var inImg2img = Boolean(gradioApp().querySelector("button.rounded-t-lg.border-gray-200"))
99
+ if(inImg2img){
100
+ let inputs = gradioApp().querySelectorAll('input');
101
+ inputs.forEach(function(e){
102
+ let parentLabel = e.parentElement.querySelector('label')
103
+ if(parentLabel && parentLabel.innerText){
104
+ if(!e.classList.contains('scrollwatch')){
105
+ if(parentLabel.innerText == 'Width' || parentLabel.innerText == 'Height'){
106
+ e.addEventListener('input', function(e){dimensionChange(e,parentLabel.innerText)} )
107
+ e.classList.add('scrollwatch')
108
+ }
109
+ if(parentLabel.innerText == 'Width'){
110
+ currentWidth = e.value*1.0
111
+ }
112
+ if(parentLabel.innerText == 'Height'){
113
+ currentHeight = e.value*1.0
114
+ }
115
+ }
116
+ }
117
+ })
118
+ }
119
+ });
javascript/dragdrop.js ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // allows drag-dropping files into gradio image elements, and also pasting images from clipboard
2
+
3
+ function isValidImageList( files ) {
4
+ return files && files?.length === 1 && ['image/png', 'image/gif', 'image/jpeg'].includes(files[0].type);
5
+ }
6
+
7
+ function dropReplaceImage( imgWrap, files ) {
8
+ if ( ! isValidImageList( files ) ) {
9
+ return;
10
+ }
11
+
12
+ imgWrap.querySelector('.modify-upload button + button, .touch-none + div button + button')?.click();
13
+ const callback = () => {
14
+ const fileInput = imgWrap.querySelector('input[type="file"]');
15
+ if ( fileInput ) {
16
+ fileInput.files = files;
17
+ fileInput.dispatchEvent(new Event('change'));
18
+ }
19
+ };
20
+
21
+ if ( imgWrap.closest('#pnginfo_image') ) {
22
+ // special treatment for PNG Info tab, wait for fetch request to finish
23
+ const oldFetch = window.fetch;
24
+ window.fetch = async (input, options) => {
25
+ const response = await oldFetch(input, options);
26
+ if ( 'api/predict/' === input ) {
27
+ const content = await response.text();
28
+ window.fetch = oldFetch;
29
+ window.requestAnimationFrame( () => callback() );
30
+ return new Response(content, {
31
+ status: response.status,
32
+ statusText: response.statusText,
33
+ headers: response.headers
34
+ })
35
+ }
36
+ return response;
37
+ };
38
+ } else {
39
+ window.requestAnimationFrame( () => callback() );
40
+ }
41
+ }
42
+
43
+ window.document.addEventListener('dragover', e => {
44
+ const target = e.composedPath()[0];
45
+ const imgWrap = target.closest('[data-testid="image"]');
46
+ if ( !imgWrap ) {
47
+ return;
48
+ }
49
+ e.stopPropagation();
50
+ e.preventDefault();
51
+ e.dataTransfer.dropEffect = 'copy';
52
+ });
53
+
54
+ window.document.addEventListener('drop', e => {
55
+ const target = e.composedPath()[0];
56
+ const imgWrap = target.closest('[data-testid="image"]');
57
+ if ( !imgWrap ) {
58
+ return;
59
+ }
60
+ e.stopPropagation();
61
+ e.preventDefault();
62
+ const files = e.dataTransfer.files;
63
+ dropReplaceImage( imgWrap, files );
64
+ });
65
+
66
+ window.addEventListener('paste', e => {
67
+ const files = e.clipboardData.files;
68
+ if ( ! isValidImageList( files ) ) {
69
+ return;
70
+ }
71
+
72
+ const visibleImageFields = [...gradioApp().querySelectorAll('[data-testid="image"]')]
73
+ .filter(el => uiElementIsVisible(el));
74
+ if ( ! visibleImageFields.length ) {
75
+ return;
76
+ }
77
+
78
+ const firstFreeImageField = visibleImageFields
79
+ .filter(el => el.querySelector('input[type=file]'))?.[0];
80
+
81
+ dropReplaceImage(
82
+ firstFreeImageField ?
83
+ firstFreeImageField :
84
+ visibleImageFields[visibleImageFields.length - 1]
85
+ , files );
86
+ });
javascript/edit-attention.js ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ addEventListener('keydown', (event) => {
2
+ let target = event.originalTarget;
3
+ if (!target.hasAttribute("placeholder")) return;
4
+ if (!target.placeholder.toLowerCase().includes("prompt")) return;
5
+
6
+ let plus = "ArrowUp"
7
+ let minus = "ArrowDown"
8
+ if (event.key != plus && event.key != minus) return;
9
+
10
+ selectionStart = target.selectionStart;
11
+ selectionEnd = target.selectionEnd;
12
+ if(selectionStart == selectionEnd) return;
13
+
14
+ event.preventDefault();
15
+
16
+ if (selectionStart == 0 || target.value[selectionStart - 1] != "(") {
17
+ target.value = target.value.slice(0, selectionStart) +
18
+ "(" + target.value.slice(selectionStart, selectionEnd) + ":1.0)" +
19
+ target.value.slice(selectionEnd);
20
+
21
+ target.focus();
22
+ target.selectionStart = selectionStart + 1;
23
+ target.selectionEnd = selectionEnd + 1;
24
+
25
+ } else {
26
+ end = target.value.slice(selectionEnd + 1).indexOf(")") + 1;
27
+ weight = parseFloat(target.value.slice(selectionEnd + 1, selectionEnd + 1 + end));
28
+ if (event.key == minus) weight -= 0.1;
29
+ if (event.key == plus) weight += 0.1;
30
+
31
+ weight = parseFloat(weight.toPrecision(12));
32
+
33
+ target.value = target.value.slice(0, selectionEnd + 1) +
34
+ weight +
35
+ target.value.slice(selectionEnd + 1 + end - 1);
36
+
37
+ target.focus();
38
+ target.selectionStart = selectionStart;
39
+ target.selectionEnd = selectionEnd;
40
+ }
41
+ });
javascript/hints.js ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // mouseover tooltips for various UI elements
2
+
3
+ titles = {
4
+ "Sampling steps": "How many times to improve the generated image iteratively; higher values take longer; very low values can produce bad results",
5
+ "Sampling method": "Which algorithm to use to produce the image",
6
+ "GFPGAN": "Restore low quality faces using GFPGAN neural network",
7
+ "Euler a": "Euler Ancestral - very creative, each can get a completely different picture depending on step count, setting steps to higher than 30-40 does not help",
8
+ "DDIM": "Denoising Diffusion Implicit Models - best at inpainting",
9
+
10
+ "Batch count": "How many batches of images to create",
11
+ "Batch size": "How many image to create in a single batch",
12
+ "CFG Scale": "Classifier Free Guidance Scale - how strongly the image should conform to prompt - lower values produce more creative results",
13
+ "Seed": "A value that determines the output of random number generator - if you create an image with same parameters and seed as another image, you'll get the same result",
14
+ "\u{1f3b2}\ufe0f": "Set seed to -1, which will cause a new random number to be used every time",
15
+ "\u267b\ufe0f": "Reuse seed from last generation, mostly useful if it was randomed",
16
+ "\u{1f3a8}": "Add a random artist to the prompt.",
17
+ "\u2199\ufe0f": "Read generation parameters from prompt into user interface.",
18
+ "\u{1f4c2}": "Open images output directory",
19
+
20
+ "Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt",
21
+ "SD upscale": "Upscale image normally, split result into tiles, improve each tile using img2img, merge whole image back",
22
+
23
+ "Just resize": "Resize image to target resolution. Unless height and width match, you will get incorrect aspect ratio.",
24
+ "Crop and resize": "Resize the image so that entirety of target resolution is filled with the image. Crop parts that stick out.",
25
+ "Resize and fill": "Resize the image so that entirety of image is inside target resolution. Fill empty space with image's colors.",
26
+
27
+ "Mask blur": "How much to blur the mask before processing, in pixels.",
28
+ "Masked content": "What to put inside the masked area before processing it with Stable Diffusion.",
29
+ "fill": "fill it with colors of the image",
30
+ "original": "keep whatever was there originally",
31
+ "latent noise": "fill it with latent space noise",
32
+ "latent nothing": "fill it with latent space zeroes",
33
+ "Inpaint at full resolution": "Upscale masked region to target resolution, do inpainting, downscale back and paste into original image",
34
+
35
+ "Denoising strength": "Determines how little respect the algorithm should have for image's content. At 0, nothing will change, and at 1 you'll get an unrelated image. With values below 1.0, processing will take less steps than the Sampling Steps slider specifies.",
36
+ "Denoising strength change factor": "In loopback mode, on each loop the denoising strength is multiplied by this value. <1 means decreasing variety so your sequence will converge on a fixed picture. >1 means increasing variety so your sequence will become more and more chaotic.",
37
+
38
+ "Interrupt": "Stop processing images and return any results accumulated so far.",
39
+ "Save": "Write image to a directory (default - log/images) and generation parameters into csv file.",
40
+
41
+ "X values": "Separate values for X axis using commas.",
42
+ "Y values": "Separate values for Y axis using commas.",
43
+
44
+ "None": "Do not do anything special",
45
+ "Prompt matrix": "Separate prompts into parts using vertical pipe character (|) and the script will create a picture for every combination of them (except for the first part, which will be present in all combinations)",
46
+ "X/Y plot": "Create a grid where images will have different parameters. Use inputs below to specify which parameters will be shared by columns and rows",
47
+ "Custom code": "Run Python code. Advanced user only. Must run program with --allow-code for this to work",
48
+
49
+ "Prompt S/R": "Separate a list of words with commas, and the first word will be used as a keyword: script will search for this word in the prompt, and replace it with others",
50
+ "Prompt order": "Separate a list of words with commas, and the script will make a variation of prompt with those words for their every possible order",
51
+
52
+ "Tiling": "Produce an image that can be tiled.",
53
+ "Tile overlap": "For SD upscale, how much overlap in pixels should there be between tiles. Tiles overlap so that when they are merged back into one picture, there is no clearly visible seam.",
54
+
55
+ "Variation seed": "Seed of a different picture to be mixed into the generation.",
56
+ "Variation strength": "How strong of a variation to produce. At 0, there will be no effect. At 1, you will get the complete picture with variation seed (except for ancestral samplers, where you will just get something).",
57
+ "Resize seed from height": "Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution",
58
+ "Resize seed from width": "Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution",
59
+
60
+ "Interrogate": "Reconstruct prompt from existing image and put it into the prompt field.",
61
+
62
+ "Images filename pattern": "Use following tags to define how filenames for images are chosen: [steps], [cfg], [prompt], [prompt_no_styles], [prompt_spaces], [width], [height], [styles], [sampler], [seed], [model_hash], [prompt_words], [date], [datetime], [job_timestamp]; leave empty for default.",
63
+ "Directory name pattern": "Use following tags to define how subdirectories for images and grids are chosen: [steps], [cfg], [prompt], [prompt_no_styles], [prompt_spaces], [width], [height], [styles], [sampler], [seed], [model_hash], [prompt_words], [date], [datetime], [job_timestamp]; leave empty for default.",
64
+ "Max prompt words": "Set the maximum number of words to be used in the [prompt_words] option; ATTENTION: If the words are too long, they may exceed the maximum length of the file path that the system can handle",
65
+
66
+ "Loopback": "Process an image, use it as an input, repeat.",
67
+ "Loops": "How many times to repeat processing an image and using it as input for the next iteration",
68
+
69
+ "Style 1": "Style to apply; styles have components for both positive and negative prompts and apply to both",
70
+ "Style 2": "Style to apply; styles have components for both positive and negative prompts and apply to both",
71
+ "Apply style": "Insert selected styles into prompt fields",
72
+ "Create style": "Save current prompts as a style. If you add the token {prompt} to the text, the style use that as placeholder for your prompt when you use the style in the future.",
73
+
74
+ "Checkpoint name": "Loads weights from checkpoint before making images. You can either use hash or a part of filename (as seen in settings) for checkpoint name. Recommended to use with Y axis for less switching.",
75
+
76
+ "vram": "Torch active: Peak amount of VRAM used by Torch during generation, excluding cached data.\nTorch reserved: Peak amount of VRAM allocated by Torch, including all active and cached data.\nSys VRAM: Peak amount of VRAM allocation across all applications / total GPU VRAM (peak utilization%).",
77
+
78
+ "Highres. fix": "Use a two step process to partially create an image at smaller resolution, upscale, and then improve details in it without changing composition",
79
+ "Scale latent": "Uscale the image in latent space. Alternative is to produce the full image from latent representation, upscale that, and then move it back to latent space.",
80
+
81
+ }
82
+
83
+
84
+ onUiUpdate(function(){
85
+ gradioApp().querySelectorAll('span, button, select, p').forEach(function(span){
86
+ tooltip = titles[span.textContent];
87
+
88
+ if(!tooltip){
89
+ tooltip = titles[span.value];
90
+ }
91
+
92
+ if(!tooltip){
93
+ for (const c of span.classList) {
94
+ if (c in titles) {
95
+ tooltip = titles[c];
96
+ break;
97
+ }
98
+ }
99
+ }
100
+
101
+ if(tooltip){
102
+ span.title = tooltip;
103
+ }
104
+ })
105
+
106
+ gradioApp().querySelectorAll('select').forEach(function(select){
107
+ if (select.onchange != null) return;
108
+
109
+ select.onchange = function(){
110
+ select.title = titles[select.value] || "";
111
+ }
112
+ })
113
+ })
javascript/imageMaskFix.js ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * temporary fix for https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/668
3
+ * @see https://github.com/gradio-app/gradio/issues/1721
4
+ */
5
+ window.addEventListener( 'resize', () => imageMaskResize());
6
+ function imageMaskResize() {
7
+ const canvases = gradioApp().querySelectorAll('#img2maskimg .touch-none canvas');
8
+ if ( ! canvases.length ) {
9
+ canvases_fixed = false;
10
+ window.removeEventListener( 'resize', imageMaskResize );
11
+ return;
12
+ }
13
+
14
+ const wrapper = canvases[0].closest('.touch-none');
15
+ const previewImage = wrapper.previousElementSibling;
16
+
17
+ if ( ! previewImage.complete ) {
18
+ previewImage.addEventListener( 'load', () => imageMaskResize());
19
+ return;
20
+ }
21
+
22
+ const w = previewImage.width;
23
+ const h = previewImage.height;
24
+ const nw = previewImage.naturalWidth;
25
+ const nh = previewImage.naturalHeight;
26
+ const portrait = nh > nw;
27
+ const factor = portrait;
28
+
29
+ const wW = Math.min(w, portrait ? h/nh*nw : w/nw*nw);
30
+ const wH = Math.min(h, portrait ? h/nh*nh : w/nw*nh);
31
+
32
+ wrapper.style.width = `${wW}px`;
33
+ wrapper.style.height = `${wH}px`;
34
+ wrapper.style.left = `${(w-wW)/2}px`;
35
+ wrapper.style.top = `${(h-wH)/2}px`;
36
+
37
+ canvases.forEach( c => {
38
+ c.style.width = c.style.height = '';
39
+ c.style.maxWidth = '100%';
40
+ c.style.maxHeight = '100%';
41
+ c.style.objectFit = 'contain';
42
+ });
43
+ }
44
+
45
+ onUiUpdate(() => imageMaskResize());
javascript/imageviewer.js ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // A full size 'lightbox' preview modal shown when left clicking on gallery previews
2
+
3
+ function closeModal() {
4
+ gradioApp().getElementById("lightboxModal").style.display = "none";
5
+ }
6
+
7
+ function showModal(event) {
8
+ const source = event.target || event.srcElement;
9
+ const modalImage = gradioApp().getElementById("modalImage")
10
+ const lb = gradioApp().getElementById("lightboxModal")
11
+ modalImage.src = source.src
12
+ if (modalImage.style.display === 'none') {
13
+ lb.style.setProperty('background-image', 'url(' + source.src + ')');
14
+ }
15
+ lb.style.display = "block";
16
+ lb.focus()
17
+ event.stopPropagation()
18
+ }
19
+
20
+ function negmod(n, m) {
21
+ return ((n % m) + m) % m;
22
+ }
23
+
24
+ function modalImageSwitch(offset){
25
+ var allgalleryButtons = gradioApp().querySelectorAll(".gallery-item.transition-all")
26
+ var galleryButtons = []
27
+ allgalleryButtons.forEach(function(elem){
28
+ if(elem.parentElement.offsetParent){
29
+ galleryButtons.push(elem);
30
+ }
31
+ })
32
+
33
+ if(galleryButtons.length>1){
34
+ var allcurrentButtons = gradioApp().querySelectorAll(".gallery-item.transition-all.\\!ring-2")
35
+ var currentButton = null
36
+ allcurrentButtons.forEach(function(elem){
37
+ if(elem.parentElement.offsetParent){
38
+ currentButton = elem;
39
+ }
40
+ })
41
+
42
+ var result = -1
43
+ galleryButtons.forEach(function(v, i){ if(v==currentButton) { result = i } })
44
+
45
+ if(result != -1){
46
+ nextButton = galleryButtons[negmod((result+offset),galleryButtons.length)]
47
+ nextButton.click()
48
+ const modalImage = gradioApp().getElementById("modalImage");
49
+ const modal = gradioApp().getElementById("lightboxModal");
50
+ modalImage.src = nextButton.children[0].src;
51
+ if (modalImage.style.display === 'none') {
52
+ modal.style.setProperty('background-image', `url(${modalImage.src})`)
53
+ }
54
+ setTimeout( function(){modal.focus()},10)
55
+ }
56
+ }
57
+ }
58
+
59
+ function modalNextImage(event){
60
+ modalImageSwitch(1)
61
+ event.stopPropagation()
62
+ }
63
+
64
+ function modalPrevImage(event){
65
+ modalImageSwitch(-1)
66
+ event.stopPropagation()
67
+ }
68
+
69
+ function modalKeyHandler(event){
70
+ switch (event.key) {
71
+ case "ArrowLeft":
72
+ modalPrevImage(event)
73
+ break;
74
+ case "ArrowRight":
75
+ modalNextImage(event)
76
+ break;
77
+ case "Escape":
78
+ closeModal();
79
+ break;
80
+ }
81
+ }
82
+
83
+ function showGalleryImage(){
84
+ setTimeout(function() {
85
+ fullImg_preview = gradioApp().querySelectorAll('img.w-full.object-contain')
86
+
87
+ if(fullImg_preview != null){
88
+ fullImg_preview.forEach(function function_name(e) {
89
+ if(e && e.parentElement.tagName == 'DIV'){
90
+
91
+ e.style.cursor='pointer'
92
+
93
+ e.addEventListener('click', function (evt) {
94
+ if(!opts.js_modal_lightbox) return;
95
+ modalZoomSet(gradioApp().getElementById('modalImage'), opts.js_modal_lightbox_initialy_zoomed)
96
+ showModal(evt)
97
+ },true);
98
+ }
99
+ });
100
+ }
101
+
102
+ }, 100);
103
+ }
104
+
105
+ function modalZoomSet(modalImage, enable){
106
+ if( enable ){
107
+ modalImage.classList.add('modalImageFullscreen');
108
+ } else{
109
+ modalImage.classList.remove('modalImageFullscreen');
110
+ }
111
+ }
112
+
113
+ function modalZoomToggle(event){
114
+ modalImage = gradioApp().getElementById("modalImage");
115
+ modalZoomSet(modalImage, !modalImage.classList.contains('modalImageFullscreen'))
116
+ event.stopPropagation()
117
+ }
118
+
119
+ function modalTileImageToggle(event){
120
+ const modalImage = gradioApp().getElementById("modalImage");
121
+ const modal = gradioApp().getElementById("lightboxModal");
122
+ const isTiling = modalImage.style.display === 'none';
123
+ if (isTiling) {
124
+ modalImage.style.display = 'block';
125
+ modal.style.setProperty('background-image', 'none')
126
+ } else {
127
+ modalImage.style.display = 'none';
128
+ modal.style.setProperty('background-image', `url(${modalImage.src})`)
129
+ }
130
+
131
+ event.stopPropagation()
132
+ }
133
+
134
+ function galleryImageHandler(e){
135
+ if(e && e.parentElement.tagName == 'BUTTON'){
136
+ e.onclick = showGalleryImage;
137
+ }
138
+ }
139
+
140
+ onUiUpdate(function(){
141
+ fullImg_preview = gradioApp().querySelectorAll('img.w-full')
142
+ if(fullImg_preview != null){
143
+ fullImg_preview.forEach(galleryImageHandler);
144
+ }
145
+ })
146
+
147
+ document.addEventListener("DOMContentLoaded", function() {
148
+ const modalFragment = document.createDocumentFragment();
149
+ const modal = document.createElement('div')
150
+ modal.onclick = closeModal;
151
+ modal.id = "lightboxModal";
152
+ modal.tabIndex=0
153
+ modal.addEventListener('keydown', modalKeyHandler, true)
154
+
155
+ const modalControls = document.createElement('div')
156
+ modalControls.className = 'modalControls gradio-container';
157
+ modal.append(modalControls);
158
+
159
+ const modalZoom = document.createElement('span')
160
+ modalZoom.className = 'modalZoom cursor';
161
+ modalZoom.innerHTML = '&#10529;'
162
+ modalZoom.addEventListener('click', modalZoomToggle, true)
163
+ modalZoom.title = "Toggle zoomed view";
164
+ modalControls.appendChild(modalZoom)
165
+
166
+ const modalTileImage = document.createElement('span')
167
+ modalTileImage.className = 'modalTileImage cursor';
168
+ modalTileImage.innerHTML = '&#8862;'
169
+ modalTileImage.addEventListener('click', modalTileImageToggle, true)
170
+ modalTileImage.title = "Preview tiling";
171
+ modalControls.appendChild(modalTileImage)
172
+
173
+ const modalClose = document.createElement('span')
174
+ modalClose.className = 'modalClose cursor';
175
+ modalClose.innerHTML = '&times;'
176
+ modalClose.onclick = closeModal;
177
+ modalClose.title = "Close image viewer";
178
+ modalControls.appendChild(modalClose)
179
+
180
+ const modalImage = document.createElement('img')
181
+ modalImage.id = 'modalImage';
182
+ modalImage.onclick = closeModal;
183
+ modalImage.tabIndex=0
184
+ modalImage.addEventListener('keydown', modalKeyHandler, true)
185
+ modal.appendChild(modalImage)
186
+
187
+ const modalPrev = document.createElement('a')
188
+ modalPrev.className = 'modalPrev';
189
+ modalPrev.innerHTML = '&#10094;'
190
+ modalPrev.tabIndex=0
191
+ modalPrev.addEventListener('click',modalPrevImage,true);
192
+ modalPrev.addEventListener('keydown', modalKeyHandler, true)
193
+ modal.appendChild(modalPrev)
194
+
195
+ const modalNext = document.createElement('a')
196
+ modalNext.className = 'modalNext';
197
+ modalNext.innerHTML = '&#10095;'
198
+ modalNext.tabIndex=0
199
+ modalNext.addEventListener('click',modalNextImage,true);
200
+ modalNext.addEventListener('keydown', modalKeyHandler, true)
201
+
202
+ modal.appendChild(modalNext)
203
+
204
+
205
+ gradioApp().getRootNode().appendChild(modal)
206
+
207
+ document.body.appendChild(modalFragment);
208
+
209
+ });
javascript/notification.js ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Monitors the gallery and sends a browser notification when the leading image is new.
2
+
3
+ let lastHeadImg = null;
4
+
5
+ notificationButton = null
6
+
7
+ onUiUpdate(function(){
8
+ if(notificationButton == null){
9
+ notificationButton = gradioApp().getElementById('request_notifications')
10
+
11
+ if(notificationButton != null){
12
+ notificationButton.addEventListener('click', function (evt) {
13
+ Notification.requestPermission();
14
+ },true);
15
+ }
16
+ }
17
+
18
+ const galleryPreviews = gradioApp().querySelectorAll('img.h-full.w-full.overflow-hidden');
19
+
20
+ if (galleryPreviews == null) return;
21
+
22
+ const headImg = galleryPreviews[0]?.src;
23
+
24
+ if (headImg == null || headImg == lastHeadImg) return;
25
+
26
+ lastHeadImg = headImg;
27
+
28
+ // play notification sound if available
29
+ gradioApp().querySelector('#audio_notification audio')?.play();
30
+
31
+ if (document.hasFocus()) return;
32
+
33
+ // Multiple copies of the images are in the DOM when one is selected. Dedup with a Set to get the real number generated.
34
+ const imgs = new Set(Array.from(galleryPreviews).map(img => img.src));
35
+
36
+ const notification = new Notification(
37
+ 'Stable Diffusion',
38
+ {
39
+ body: `Generated ${imgs.size > 1 ? imgs.size - 1 : 1} image${imgs.size > 1 ? 's' : ''}`,
40
+ icon: headImg,
41
+ image: headImg,
42
+ }
43
+ );
44
+
45
+ notification.onclick = function(_){
46
+ parent.focus();
47
+ this.close();
48
+ };
49
+ });
javascript/progressbar.js ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // code related to showing and updating progressbar shown as the image is being made
2
+ global_progressbars = {}
3
+
4
+ function check_progressbar(id_part, id_progressbar, id_progressbar_span, id_interrupt, id_preview, id_gallery){
5
+ var progressbar = gradioApp().getElementById(id_progressbar)
6
+ var interrupt = gradioApp().getElementById(id_interrupt)
7
+
8
+ if(opts.show_progress_in_title && progressbar && progressbar.offsetParent){
9
+ if(progressbar.innerText){
10
+ let newtitle = 'Stable Diffusion - ' + progressbar.innerText
11
+ if(document.title != newtitle){
12
+ document.title = newtitle;
13
+ }
14
+ }else{
15
+ let newtitle = 'Stable Diffusion'
16
+ if(document.title != newtitle){
17
+ document.title = newtitle;
18
+ }
19
+ }
20
+ }
21
+
22
+ if(progressbar!= null && progressbar != global_progressbars[id_progressbar]){
23
+ global_progressbars[id_progressbar] = progressbar
24
+
25
+ var mutationObserver = new MutationObserver(function(m){
26
+ preview = gradioApp().getElementById(id_preview)
27
+ gallery = gradioApp().getElementById(id_gallery)
28
+
29
+ if(preview != null && gallery != null){
30
+ preview.style.width = gallery.clientWidth + "px"
31
+ preview.style.height = gallery.clientHeight + "px"
32
+
33
+ var progressDiv = gradioApp().querySelectorAll('#' + id_progressbar_span).length > 0;
34
+ if(!progressDiv){
35
+ interrupt.style.display = "none"
36
+ }
37
+ }
38
+
39
+ window.setTimeout(function(){ requestMoreProgress(id_part, id_progressbar_span, id_interrupt) }, 500)
40
+ });
41
+ mutationObserver.observe( progressbar, { childList:true, subtree:true })
42
+ }
43
+ }
44
+
45
+ onUiUpdate(function(){
46
+ check_progressbar('txt2img', 'txt2img_progressbar', 'txt2img_progress_span', 'txt2img_interrupt', 'txt2img_preview', 'txt2img_gallery')
47
+ check_progressbar('img2img', 'img2img_progressbar', 'img2img_progress_span', 'img2img_interrupt', 'img2img_preview', 'img2img_gallery')
48
+ check_progressbar('ti', 'ti_progressbar', 'ti_progress_span', 'ti_interrupt', 'ti_preview', 'ti_gallery')
49
+ })
50
+
51
+ function requestMoreProgress(id_part, id_progressbar_span, id_interrupt){
52
+ btn = gradioApp().getElementById(id_part+"_check_progress");
53
+ if(btn==null) return;
54
+
55
+ btn.click();
56
+ var progressDiv = gradioApp().querySelectorAll('#' + id_progressbar_span).length > 0;
57
+ var interrupt = gradioApp().getElementById(id_interrupt)
58
+ if(progressDiv && interrupt){
59
+ interrupt.style.display = "block"
60
+ }
61
+ }
62
+
63
+ function requestProgress(id_part){
64
+ btn = gradioApp().getElementById(id_part+"_check_progress_initial");
65
+ if(btn==null) return;
66
+
67
+ btn.click();
68
+ }
javascript/textualInversion.js ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ function start_training_textual_inversion(){
4
+ requestProgress('ti')
5
+ gradioApp().querySelector('#ti_error').innerHTML=''
6
+
7
+ return args_to_array(arguments)
8
+ }
javascript/ui.js ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // various functions for interation with ui.py not large enough to warrant putting them in separate files
2
+
3
+ function selected_gallery_index(){
4
+ var buttons = gradioApp().querySelectorAll('[style="display: block;"].tabitem .gallery-item')
5
+ var button = gradioApp().querySelector('[style="display: block;"].tabitem .gallery-item.\\!ring-2')
6
+
7
+ var result = -1
8
+ buttons.forEach(function(v, i){ if(v==button) { result = i } })
9
+
10
+ return result
11
+ }
12
+
13
+ function extract_image_from_gallery(gallery){
14
+ if(gallery.length == 1){
15
+ return gallery[0]
16
+ }
17
+
18
+ index = selected_gallery_index()
19
+
20
+ if (index < 0 || index >= gallery.length){
21
+ return [null]
22
+ }
23
+
24
+ return gallery[index];
25
+ }
26
+
27
+ function args_to_array(args){
28
+ res = []
29
+ for(var i=0;i<args.length;i++){
30
+ res.push(args[i])
31
+ }
32
+ return res
33
+ }
34
+
35
+ function switch_to_txt2img(){
36
+ gradioApp().querySelectorAll('button')[0].click();
37
+
38
+ return args_to_array(arguments);
39
+ }
40
+
41
+ function switch_to_img2img_img2img(){
42
+ gradioApp().querySelectorAll('button')[1].click();
43
+ gradioApp().getElementById('mode_img2img').querySelectorAll('button')[0].click();
44
+
45
+ return args_to_array(arguments);
46
+ }
47
+
48
+ function switch_to_img2img_inpaint(){
49
+ gradioApp().querySelectorAll('button')[1].click();
50
+ gradioApp().getElementById('mode_img2img').querySelectorAll('button')[1].click();
51
+
52
+ return args_to_array(arguments);
53
+ }
54
+
55
+ function switch_to_extras(){
56
+ gradioApp().querySelectorAll('button')[2].click();
57
+
58
+ return args_to_array(arguments);
59
+ }
60
+
61
+ function extract_image_from_gallery_txt2img(gallery){
62
+ switch_to_txt2img()
63
+ return extract_image_from_gallery(gallery);
64
+ }
65
+
66
+ function extract_image_from_gallery_img2img(gallery){
67
+ switch_to_img2img_img2img()
68
+ return extract_image_from_gallery(gallery);
69
+ }
70
+
71
+ function extract_image_from_gallery_inpaint(gallery){
72
+ switch_to_img2img_inpaint()
73
+ return extract_image_from_gallery(gallery);
74
+ }
75
+
76
+ function extract_image_from_gallery_extras(gallery){
77
+ switch_to_extras()
78
+ return extract_image_from_gallery(gallery);
79
+ }
80
+
81
+ function get_tab_index(tabId){
82
+ var res = 0
83
+
84
+ gradioApp().getElementById(tabId).querySelector('div').querySelectorAll('button').forEach(function(button, i){
85
+ if(button.className.indexOf('bg-white') != -1)
86
+ res = i
87
+ })
88
+
89
+ return res
90
+ }
91
+
92
+ function create_tab_index_args(tabId, args){
93
+ var res = []
94
+ for(var i=0; i<args.length; i++){
95
+ res.push(args[i])
96
+ }
97
+
98
+ res[0] = get_tab_index(tabId)
99
+
100
+ return res
101
+ }
102
+
103
+ function get_extras_tab_index(){
104
+ return create_tab_index_args('mode_extras', arguments)
105
+ }
106
+
107
+ function create_submit_args(args){
108
+ res = []
109
+ for(var i=0;i<args.length;i++){
110
+ res.push(args[i])
111
+ }
112
+
113
+ // 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.
114
+ // This can lead to uploading a huge gallery of previously generated images, which leads to an unnecessary delay between submitting and beginning to generate.
115
+ // I don't know why gradio is seding outputs along with inputs, but we can prevent sending the image gallery here, which seems to be an issue for some.
116
+ // If gradio at some point stops sending outputs, this may break something
117
+ if(Array.isArray(res[res.length - 3])){
118
+ res[res.length - 3] = null
119
+ }
120
+
121
+ return res
122
+ }
123
+
124
+ function submit(){
125
+ requestProgress('txt2img')
126
+
127
+ return create_submit_args(arguments)
128
+ }
129
+
130
+ function submit_img2img(){
131
+ requestProgress('img2img')
132
+
133
+ res = create_submit_args(arguments)
134
+
135
+ res[0] = get_tab_index('mode_img2img')
136
+
137
+ return res
138
+ }
139
+
140
+
141
+ function ask_for_style_name(_, prompt_text, negative_prompt_text) {
142
+ name_ = prompt('Style name:')
143
+ return name_ === null ? [null, null, null]: [name_, prompt_text, negative_prompt_text]
144
+ }
145
+
146
+
147
+
148
+ opts = {}
149
+ function apply_settings(jsdata){
150
+ console.log(jsdata)
151
+
152
+ opts = JSON.parse(jsdata)
153
+
154
+ return jsdata
155
+ }
156
+
157
+ onUiUpdate(function(){
158
+ if(Object.keys(opts).length != 0) return;
159
+
160
+ json_elem = gradioApp().getElementById('settings_json')
161
+ if(json_elem == null) return;
162
+
163
+ textarea = json_elem.querySelector('textarea')
164
+ jsdata = textarea.value
165
+ opts = JSON.parse(jsdata)
166
+
167
+
168
+ Object.defineProperty(textarea, 'value', {
169
+ set: function(newValue) {
170
+ var valueProp = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
171
+ var oldValue = valueProp.get.call(textarea);
172
+ valueProp.set.call(textarea, newValue);
173
+
174
+ if (oldValue != newValue) {
175
+ opts = JSON.parse(textarea.value)
176
+ }
177
+ },
178
+ get: function() {
179
+ var valueProp = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
180
+ return valueProp.get.call(textarea);
181
+ }
182
+ });
183
+
184
+ json_elem.parentElement.style.display="none"
185
+
186
+ if (!txt2img_textarea) {
187
+ txt2img_textarea = gradioApp().querySelector("#txt2img_prompt > label > textarea");
188
+ txt2img_textarea?.addEventListener("input", () => update_token_counter("txt2img_token_button"));
189
+ txt2img_textarea?.addEventListener("keyup", (event) => submit_prompt(event, "txt2img_generate"));
190
+ }
191
+ if (!img2img_textarea) {
192
+ img2img_textarea = gradioApp().querySelector("#img2img_prompt > label > textarea");
193
+ img2img_textarea?.addEventListener("input", () => update_token_counter("img2img_token_button"));
194
+ img2img_textarea?.addEventListener("keyup", (event) => submit_prompt(event, "img2img_generate"));
195
+ }
196
+ })
197
+
198
+ let txt2img_textarea, img2img_textarea = undefined;
199
+ let wait_time = 800
200
+ let token_timeout;
201
+
202
+ function update_txt2img_tokens(...args) {
203
+ update_token_counter("txt2img_token_button")
204
+ if (args.length == 2)
205
+ return args[0]
206
+ return args;
207
+ }
208
+
209
+ function update_img2img_tokens(...args) {
210
+ update_token_counter("img2img_token_button")
211
+ if (args.length == 2)
212
+ return args[0]
213
+ return args;
214
+ }
215
+
216
+ function update_token_counter(button_id) {
217
+ if (token_timeout)
218
+ clearTimeout(token_timeout);
219
+ token_timeout = setTimeout(() => gradioApp().getElementById(button_id)?.click(), wait_time);
220
+ }
221
+
222
+ function submit_prompt(event, generate_button_id) {
223
+ if (event.altKey && event.keyCode === 13) {
224
+ event.preventDefault();
225
+ gradioApp().getElementById(generate_button_id).click();
226
+ return;
227
+ }
228
+ }
229
+
230
+ function restart_reload(){
231
+ document.body.innerHTML='<h1 style="font-family:monospace;margin-top:20%;color:lightgray;text-align:center;">Reloading...</h1>';
232
+ setTimeout(function(){location.reload()},2000)
233
+ }