diff options
Diffstat (limited to 'javascript')
-rw-r--r-- | javascript/aspectRatioOverlay.js | 55 | ||||
-rw-r--r-- | javascript/contextMenus.js | 177 | ||||
-rw-r--r-- | javascript/dragdrop.js | 15 | ||||
-rw-r--r-- | javascript/edit-attention.js | 75 | ||||
-rw-r--r-- | javascript/extensions.js | 35 | ||||
-rw-r--r-- | javascript/generationParams.js | 33 | ||||
-rw-r--r-- | javascript/hints.js | 32 | ||||
-rw-r--r-- | javascript/imageMaskFix.js | 6 | ||||
-rw-r--r-- | javascript/imageParams.js | 19 | ||||
-rw-r--r-- | javascript/imageviewer.js | 219 | ||||
-rw-r--r-- | javascript/localization.js | 167 | ||||
-rw-r--r-- | javascript/notification.js | 4 | ||||
-rw-r--r-- | javascript/progressbar.js | 111 | ||||
-rw-r--r-- | javascript/textualInversion.js | 8 | ||||
-rw-r--r-- | javascript/ui.js | 103 |
15 files changed, 890 insertions, 169 deletions
diff --git a/javascript/aspectRatioOverlay.js b/javascript/aspectRatioOverlay.js index 96f1c00d..66f26a22 100644 --- a/javascript/aspectRatioOverlay.js +++ b/javascript/aspectRatioOverlay.js @@ -3,12 +3,12 @@ let currentWidth = null; let currentHeight = null;
let arFrameTimeout = setTimeout(function(){},0);
-function dimensionChange(e,dimname){
+function dimensionChange(e, is_width, is_height){
- if(dimname == 'Width'){
+ if(is_width){
currentWidth = e.target.value*1.0
}
- if(dimname == 'Height'){
+ if(is_height){
currentHeight = e.target.value*1.0
}
@@ -18,22 +18,13 @@ function dimensionChange(e,dimname){ return;
}
- var img2imgMode = gradioApp().querySelector('#mode_img2img.tabs > div > button.rounded-t-lg.border-gray-200')
- if(img2imgMode){
- img2imgMode=img2imgMode.innerText
- }else{
- return;
- }
-
- var redrawImage = gradioApp().querySelector('div[data-testid=image] img');
- var inpaintImage = gradioApp().querySelector('#img2maskimg div[data-testid=image] img')
-
var targetElement = null;
- if(img2imgMode=='img2img' && redrawImage){
- targetElement = redrawImage;
- }else if(img2imgMode=='Inpaint' && inpaintImage){
- targetElement = inpaintImage;
+ var tabIndex = get_tab_index('mode_img2img')
+ if(tabIndex == 0){
+ targetElement = gradioApp().querySelector('div[data-testid=image] img');
+ } else if(tabIndex == 1){
+ targetElement = gradioApp().querySelector('#img2maskimg div[data-testid=image] img');
}
if(targetElement){
@@ -98,22 +89,20 @@ onUiUpdate(function(){ var inImg2img = Boolean(gradioApp().querySelector("button.rounded-t-lg.border-gray-200"))
if(inImg2img){
let inputs = gradioApp().querySelectorAll('input');
- inputs.forEach(function(e){
- let parentLabel = e.parentElement.querySelector('label')
- if(parentLabel && parentLabel.innerText){
- if(!e.classList.contains('scrollwatch')){
- if(parentLabel.innerText == 'Width' || parentLabel.innerText == 'Height'){
- e.addEventListener('input', function(e){dimensionChange(e,parentLabel.innerText)} )
- e.classList.add('scrollwatch')
- }
- if(parentLabel.innerText == 'Width'){
- currentWidth = e.value*1.0
- }
- if(parentLabel.innerText == 'Height'){
- currentHeight = e.value*1.0
- }
- }
- }
+ inputs.forEach(function(e){
+ var is_width = e.parentElement.id == "img2img_width"
+ var is_height = e.parentElement.id == "img2img_height"
+
+ if((is_width || is_height) && !e.classList.contains('scrollwatch')){
+ e.addEventListener('input', function(e){dimensionChange(e, is_width, is_height)} )
+ e.classList.add('scrollwatch')
+ }
+ if(is_width){
+ currentWidth = e.value*1.0
+ }
+ if(is_height){
+ currentHeight = e.value*1.0
+ }
})
}
});
diff --git a/javascript/contextMenus.js b/javascript/contextMenus.js new file mode 100644 index 00000000..11bcce1b --- /dev/null +++ b/javascript/contextMenus.js @@ -0,0 +1,177 @@ +
+contextMenuInit = function(){
+ let eventListenerApplied=false;
+ let menuSpecs = new Map();
+
+ const uid = function(){
+ return Date.now().toString(36) + Math.random().toString(36).substr(2);
+ }
+
+ function showContextMenu(event,element,menuEntries){
+ let posx = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
+ let posy = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
+
+ let oldMenu = gradioApp().querySelector('#context-menu')
+ if(oldMenu){
+ oldMenu.remove()
+ }
+
+ let tabButton = uiCurrentTab
+ let baseStyle = window.getComputedStyle(tabButton)
+
+ const contextMenu = document.createElement('nav')
+ contextMenu.id = "context-menu"
+ contextMenu.style.background = baseStyle.background
+ contextMenu.style.color = baseStyle.color
+ contextMenu.style.fontFamily = baseStyle.fontFamily
+ contextMenu.style.top = posy+'px'
+ contextMenu.style.left = posx+'px'
+
+
+
+ const contextMenuList = document.createElement('ul')
+ contextMenuList.className = 'context-menu-items';
+ contextMenu.append(contextMenuList);
+
+ menuEntries.forEach(function(entry){
+ let contextMenuEntry = document.createElement('a')
+ contextMenuEntry.innerHTML = entry['name']
+ contextMenuEntry.addEventListener("click", function(e) {
+ entry['func']();
+ })
+ contextMenuList.append(contextMenuEntry);
+
+ })
+
+ gradioApp().getRootNode().appendChild(contextMenu)
+
+ let menuWidth = contextMenu.offsetWidth + 4;
+ let menuHeight = contextMenu.offsetHeight + 4;
+
+ let windowWidth = window.innerWidth;
+ let windowHeight = window.innerHeight;
+
+ if ( (windowWidth - posx) < menuWidth ) {
+ contextMenu.style.left = windowWidth - menuWidth + "px";
+ }
+
+ if ( (windowHeight - posy) < menuHeight ) {
+ contextMenu.style.top = windowHeight - menuHeight + "px";
+ }
+
+ }
+
+ function appendContextMenuOption(targetElementSelector,entryName,entryFunction){
+
+ currentItems = menuSpecs.get(targetElementSelector)
+
+ if(!currentItems){
+ currentItems = []
+ menuSpecs.set(targetElementSelector,currentItems);
+ }
+ let newItem = {'id':targetElementSelector+'_'+uid(),
+ 'name':entryName,
+ 'func':entryFunction,
+ 'isNew':true}
+
+ currentItems.push(newItem)
+ return newItem['id']
+ }
+
+ function removeContextMenuOption(uid){
+ menuSpecs.forEach(function(v,k) {
+ let index = -1
+ v.forEach(function(e,ei){if(e['id']==uid){index=ei}})
+ if(index>=0){
+ v.splice(index, 1);
+ }
+ })
+ }
+
+ function addContextMenuEventListener(){
+ if(eventListenerApplied){
+ return;
+ }
+ gradioApp().addEventListener("click", function(e) {
+ let source = e.composedPath()[0]
+ if(source.id && source.id.indexOf('check_progress')>-1){
+ return
+ }
+
+ let oldMenu = gradioApp().querySelector('#context-menu')
+ if(oldMenu){
+ oldMenu.remove()
+ }
+ });
+ gradioApp().addEventListener("contextmenu", function(e) {
+ let oldMenu = gradioApp().querySelector('#context-menu')
+ if(oldMenu){
+ oldMenu.remove()
+ }
+ menuSpecs.forEach(function(v,k) {
+ if(e.composedPath()[0].matches(k)){
+ showContextMenu(e,e.composedPath()[0],v)
+ e.preventDefault()
+ return
+ }
+ })
+ });
+ eventListenerApplied=true
+
+ }
+
+ return [appendContextMenuOption, removeContextMenuOption, addContextMenuEventListener]
+}
+
+initResponse = contextMenuInit();
+appendContextMenuOption = initResponse[0];
+removeContextMenuOption = initResponse[1];
+addContextMenuEventListener = initResponse[2];
+
+(function(){
+ //Start example Context Menu Items
+ let generateOnRepeat = function(genbuttonid,interruptbuttonid){
+ let genbutton = gradioApp().querySelector(genbuttonid);
+ let interruptbutton = gradioApp().querySelector(interruptbuttonid);
+ if(!interruptbutton.offsetParent){
+ genbutton.click();
+ }
+ clearInterval(window.generateOnRepeatInterval)
+ window.generateOnRepeatInterval = setInterval(function(){
+ if(!interruptbutton.offsetParent){
+ genbutton.click();
+ }
+ },
+ 500)
+ }
+
+ appendContextMenuOption('#txt2img_generate','Generate forever',function(){
+ generateOnRepeat('#txt2img_generate','#txt2img_interrupt');
+ })
+ appendContextMenuOption('#img2img_generate','Generate forever',function(){
+ generateOnRepeat('#img2img_generate','#img2img_interrupt');
+ })
+
+ let cancelGenerateForever = function(){
+ clearInterval(window.generateOnRepeatInterval)
+ }
+
+ appendContextMenuOption('#txt2img_interrupt','Cancel generate forever',cancelGenerateForever)
+ appendContextMenuOption('#txt2img_generate', 'Cancel generate forever',cancelGenerateForever)
+ appendContextMenuOption('#img2img_interrupt','Cancel generate forever',cancelGenerateForever)
+ appendContextMenuOption('#img2img_generate', 'Cancel generate forever',cancelGenerateForever)
+
+ appendContextMenuOption('#roll','Roll three',
+ function(){
+ let rollbutton = get_uiCurrentTabContent().querySelector('#roll');
+ setTimeout(function(){rollbutton.click()},100)
+ setTimeout(function(){rollbutton.click()},200)
+ setTimeout(function(){rollbutton.click()},300)
+ }
+ )
+})();
+//End example Context Menu Items
+
+onUiUpdate(function(){
+ addContextMenuEventListener()
+});
diff --git a/javascript/dragdrop.js b/javascript/dragdrop.js index 5aac57f7..fe008924 100644 --- a/javascript/dragdrop.js +++ b/javascript/dragdrop.js @@ -9,11 +9,19 @@ function dropReplaceImage( imgWrap, files ) { return; } + const tmpFile = files[0]; + imgWrap.querySelector('.modify-upload button + button, .touch-none + div button + button')?.click(); const callback = () => { const fileInput = imgWrap.querySelector('input[type="file"]'); if ( fileInput ) { - fileInput.files = files; + if ( files.length === 0 ) { + files = new DataTransfer(); + files.items.add(tmpFile); + fileInput.files = files.files; + } else { + fileInput.files = files; + } fileInput.dispatchEvent(new Event('change')); } }; @@ -43,7 +51,7 @@ function dropReplaceImage( imgWrap, files ) { window.document.addEventListener('dragover', e => { const target = e.composedPath()[0]; const imgWrap = target.closest('[data-testid="image"]'); - if ( !imgWrap ) { + if ( !imgWrap && target.placeholder && target.placeholder.indexOf("Prompt") == -1) { return; } e.stopPropagation(); @@ -53,6 +61,9 @@ window.document.addEventListener('dragover', e => { window.document.addEventListener('drop', e => { const target = e.composedPath()[0]; + if (target.placeholder.indexOf("Prompt") == -1) { + return; + } const imgWrap = target.closest('[data-testid="image"]'); if ( !imgWrap ) { return; diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js new file mode 100644 index 00000000..b947cbec --- /dev/null +++ b/javascript/edit-attention.js @@ -0,0 +1,75 @@ +addEventListener('keydown', (event) => {
+ let target = event.originalTarget || event.composedPath()[0];
+ if (!target.matches("#toprow textarea.gr-text-input[placeholder]")) return;
+ if (! (event.metaKey || event.ctrlKey)) return;
+
+
+ let plus = "ArrowUp"
+ let minus = "ArrowDown"
+ if (event.key != plus && event.key != minus) return;
+
+ let selectionStart = target.selectionStart;
+ let selectionEnd = target.selectionEnd;
+ // If the user hasn't selected anything, let's select their current parenthesis block
+ if (selectionStart === selectionEnd) {
+ // Find opening parenthesis around current cursor
+ const before = target.value.substring(0, selectionStart);
+ let beforeParen = before.lastIndexOf("(");
+ if (beforeParen == -1) return;
+ let beforeParenClose = before.lastIndexOf(")");
+ while (beforeParenClose !== -1 && beforeParenClose > beforeParen) {
+ beforeParen = before.lastIndexOf("(", beforeParen - 1);
+ beforeParenClose = before.lastIndexOf(")", beforeParenClose - 1);
+ }
+
+ // Find closing parenthesis around current cursor
+ const after = target.value.substring(selectionStart);
+ let afterParen = after.indexOf(")");
+ if (afterParen == -1) return;
+ let afterParenOpen = after.indexOf("(");
+ while (afterParenOpen !== -1 && afterParen > afterParenOpen) {
+ afterParen = after.indexOf(")", afterParen + 1);
+ afterParenOpen = after.indexOf("(", afterParenOpen + 1);
+ }
+ if (beforeParen === -1 || afterParen === -1) return;
+
+ // Set the selection to the text between the parenthesis
+ const parenContent = target.value.substring(beforeParen + 1, selectionStart + afterParen);
+ const lastColon = parenContent.lastIndexOf(":");
+ selectionStart = beforeParen + 1;
+ selectionEnd = selectionStart + lastColon;
+ target.setSelectionRange(selectionStart, selectionEnd);
+ }
+
+ event.preventDefault();
+
+ if (selectionStart == 0 || target.value[selectionStart - 1] != "(") {
+ target.value = target.value.slice(0, selectionStart) +
+ "(" + target.value.slice(selectionStart, selectionEnd) + ":1.0)" +
+ target.value.slice(selectionEnd);
+
+ target.focus();
+ target.selectionStart = selectionStart + 1;
+ target.selectionEnd = selectionEnd + 1;
+
+ } else {
+ end = target.value.slice(selectionEnd + 1).indexOf(")") + 1;
+ weight = parseFloat(target.value.slice(selectionEnd + 1, selectionEnd + 1 + end));
+ if (isNaN(weight)) return;
+ if (event.key == minus) weight -= 0.1;
+ if (event.key == plus) weight += 0.1;
+
+ weight = parseFloat(weight.toPrecision(12));
+
+ target.value = target.value.slice(0, selectionEnd + 1) +
+ weight +
+ target.value.slice(selectionEnd + 1 + end - 1);
+
+ target.focus();
+ target.selectionStart = selectionStart;
+ target.selectionEnd = selectionEnd;
+ }
+ // Since we've modified a Gradio Textbox component manually, we need to simulate an `input` DOM event to ensure its
+ // internal Svelte data binding remains in sync.
+ target.dispatchEvent(new Event("input", { bubbles: true }));
+});
diff --git a/javascript/extensions.js b/javascript/extensions.js new file mode 100644 index 00000000..59179ca6 --- /dev/null +++ b/javascript/extensions.js @@ -0,0 +1,35 @@ +
+function extensions_apply(_, _){
+ disable = []
+ update = []
+ gradioApp().querySelectorAll('#extensions input[type="checkbox"]').forEach(function(x){
+ if(x.name.startsWith("enable_") && ! x.checked)
+ disable.push(x.name.substr(7))
+
+ if(x.name.startsWith("update_") && x.checked)
+ update.push(x.name.substr(7))
+ })
+
+ restart_reload()
+
+ return [JSON.stringify(disable), JSON.stringify(update)]
+}
+
+function extensions_check(){
+ gradioApp().querySelectorAll('#extensions .extension_status').forEach(function(x){
+ x.innerHTML = "Loading..."
+ })
+
+ return []
+}
+
+function install_extension_from_index(button, url){
+ button.disabled = "disabled"
+ button.value = "Installing..."
+
+ textarea = gradioApp().querySelector('#extension_to_install textarea')
+ textarea.value = url
+ textarea.dispatchEvent(new Event("input", { bubbles: true }))
+
+ gradioApp().querySelector('#install_extension_button').click()
+}
diff --git a/javascript/generationParams.js b/javascript/generationParams.js new file mode 100644 index 00000000..95f05093 --- /dev/null +++ b/javascript/generationParams.js @@ -0,0 +1,33 @@ +// attaches listeners to the txt2img and img2img galleries to update displayed generation param text when the image changes + +let txt2img_gallery, img2img_gallery, modal = undefined; +onUiUpdate(function(){ + if (!txt2img_gallery) { + txt2img_gallery = attachGalleryListeners("txt2img") + } + if (!img2img_gallery) { + img2img_gallery = attachGalleryListeners("img2img") + } + if (!modal) { + modal = gradioApp().getElementById('lightboxModal') + modalObserver.observe(modal, { attributes : true, attributeFilter : ['style'] }); + } +}); + +let modalObserver = new MutationObserver(function(mutations) { + mutations.forEach(function(mutationRecord) { + let selectedTab = gradioApp().querySelector('#tabs div button.bg-white')?.innerText + if (mutationRecord.target.style.display === 'none' && selectedTab === 'txt2img' || selectedTab === 'img2img') + gradioApp().getElementById(selectedTab+"_generation_info_button").click() + }); +}); + +function attachGalleryListeners(tab_name) { + gallery = gradioApp().querySelector('#'+tab_name+'_gallery') + gallery?.addEventListener('click', () => gradioApp().getElementById(tab_name+"_generation_info_button").click()); + gallery?.addEventListener('keydown', (e) => { + if (e.keyCode == 37 || e.keyCode == 39) // left or right arrow + gradioApp().getElementById(tab_name+"_generation_info_button").click() + }); + return gallery; +} diff --git a/javascript/hints.js b/javascript/hints.js index 84694eeb..63e17e05 100644 --- a/javascript/hints.js +++ b/javascript/hints.js @@ -6,6 +6,7 @@ titles = { "GFPGAN": "Restore low quality faces using GFPGAN neural network", "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", "DDIM": "Denoising Diffusion Implicit Models - best at inpainting", + "DPM adaptive": "Ignores step count - uses a number of steps determined by the CFG and resolution", "Batch count": "How many batches of images to create", "Batch size": "How many image to create in a single batch", @@ -14,8 +15,11 @@ titles = { "\u{1f3b2}\ufe0f": "Set seed to -1, which will cause a new random number to be used every time", "\u267b\ufe0f": "Reuse seed from last generation, mostly useful if it was randomed", "\u{1f3a8}": "Add a random artist to the prompt.", - "\u2199\ufe0f": "Read generation parameters from prompt into user interface.", - "\uD83D\uDCC2": "Open images output directory", + "\u2199\ufe0f": "Read generation parameters from prompt or last generation if prompt is empty into user interface.", + "\u{1f4c2}": "Open images output directory", + "\u{1f4be}": "Save style", + "\U0001F5D1": "Clear prompt", + "\u{1f4cb}": "Apply selected styles to current prompt", "Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt", "SD upscale": "Upscale image normally, split result into tiles, improve each tile using img2img, merge whole image back", @@ -35,6 +39,7 @@ titles = { "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.", "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.", + "Skip": "Stop processing current image and continue processing.", "Interrupt": "Stop processing images and return any results accumulated so far.", "Save": "Write image to a directory (default - log/images) and generation parameters into csv file.", @@ -47,6 +52,7 @@ titles = { "Custom code": "Run Python code. Advanced user only. Must run program with --allow-code for this to work", "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", + "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", "Tiling": "Produce an image that can be tiled.", "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.", @@ -58,8 +64,8 @@ titles = { "Interrogate": "Reconstruct prompt from existing image and put it into the prompt field.", - "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.", - "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.", + "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], [model_name], [prompt_words], [date], [datetime], [datetime<Format>], [datetime<Format><Time Zone>], [job_timestamp]; leave empty for default.", + "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], [model_name], [prompt_words], [date], [datetime], [datetime<Format>], [datetime<Format><Time Zone>], [job_timestamp]; leave empty for default.", "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", "Loopback": "Process an image, use it as an input, repeat.", @@ -71,12 +77,30 @@ titles = { "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.", "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.", + "Inpainting conditioning mask strength": "Only applies to inpainting models. Determines how strongly to mask off the original image for inpainting and img2img. 1.0 means fully masked, which is the default behaviour. 0.0 means a fully unmasked conditioning. Lower values will help preserve the overall composition of the image, but will struggle with large changes.", "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%).", "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", "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.", + "Eta noise seed delta": "If this values is non-zero, it will be added to seed and used to initialize RNG for noises when using samplers with Eta. You can use this to produce even more variation of images, or you can use this to match images of other software if you know what you are doing.", + "Do not add watermark to images": "If this option is enabled, watermark will not be added to created images. Warning: if you do not add watermark, you may be behaving in an unethical manner.", + + "Filename word regex": "This regular expression will be used extract words from filename, and they will be joined using the option below into label text used for training. Leave empty to keep filename text as it is.", + "Filename join string": "This string will be used to join split words into a single line if the option above is enabled.", + + "Quicksettings list": "List of setting names, separated by commas, for settings that should go to the quick access bar at the top, rather than the usual setting tab. See modules/shared.py for setting names. Requires restarting to apply.", + + "Weighted sum": "Result = A * (1 - M) + B * M", + "Add difference": "Result = A + (B - C) * M", + + "Learning rate": "how fast should the training go. Low values will take longer to train, high values may fail to converge (not generate accurate results) and/or may break the embedding (This has happened if you see Loss: nan in the training info textbox. If this happens, you need to manually restore your embedding from an older not-broken backup).\n\nYou can set a single numeric value, or multiple learning rates using the syntax:\n\n rate_1:max_steps_1, rate_2:max_steps_2, ...\n\nEG: 0.005:100, 1e-3:1000, 1e-5\n\nWill train with rate of 0.005 for first 100 steps, then 1e-3 until 1000 steps, then 1e-5 for all remaining steps.", + + "Clip skip": "Early stopping parameter for CLIP model; 1 is stop at last layer as usual, 2 is stop at penultimate layer, etc.", + + "Approx NN": "Cheap neural network approximation. Very fast compared to VAE, but produces pictures with 4 times smaller horizontal/vertical resoluton and lower quality.", + "Approx cheap": "Very cheap approximation. Very fast compared to VAE, but produces pictures with 8 times smaller horizontal/vertical resoluton and extremely low quality." } diff --git a/javascript/imageMaskFix.js b/javascript/imageMaskFix.js index 3d77bfe9..9fe7a603 100644 --- a/javascript/imageMaskFix.js +++ b/javascript/imageMaskFix.js @@ -31,8 +31,8 @@ function imageMaskResize() { wrapper.style.width = `${wW}px`; wrapper.style.height = `${wH}px`; - wrapper.style.left = `${(w-wW)/2}px`; - wrapper.style.top = `${(h-wH)/2}px`; + wrapper.style.left = `0px`; + wrapper.style.top = `0px`; canvases.forEach( c => { c.style.width = c.style.height = ''; @@ -42,4 +42,4 @@ function imageMaskResize() { }); } - onUiUpdate(() => imageMaskResize());
\ No newline at end of file + onUiUpdate(() => imageMaskResize()); diff --git a/javascript/imageParams.js b/javascript/imageParams.js new file mode 100644 index 00000000..67404a89 --- /dev/null +++ b/javascript/imageParams.js @@ -0,0 +1,19 @@ +window.onload = (function(){ + window.addEventListener('drop', e => { + const target = e.composedPath()[0]; + const idx = selected_gallery_index(); + if (target.placeholder.indexOf("Prompt") == -1) return; + + let prompt_target = get_tab_index('tabs') == 1 ? "img2img_prompt_image" : "txt2img_prompt_image"; + + e.stopPropagation(); + e.preventDefault(); + const imgParent = gradioApp().getElementById(prompt_target); + const files = e.dataTransfer.files; + const fileInput = imgParent.querySelector('input[type="file"]'); + if ( fileInput ) { + fileInput.files = files; + fileInput.dispatchEvent(new Event('change')); + } + }); +}); diff --git a/javascript/imageviewer.js b/javascript/imageviewer.js index 3a0baac8..67916536 100644 --- a/javascript/imageviewer.js +++ b/javascript/imageviewer.js @@ -1,73 +1,129 @@ // A full size 'lightbox' preview modal shown when left clicking on gallery previews - function closeModal() { - gradioApp().getElementById("lightboxModal").style.display = "none"; + gradioApp().getElementById("lightboxModal").style.display = "none"; } function showModal(event) { - const source = event.target || event.srcElement; - const modalImage = gradioApp().getElementById("modalImage") - const lb = gradioApp().getElementById("lightboxModal") - modalImage.src = source.src - if (modalImage.style.display === 'none') { - lb.style.setProperty('background-image', 'url(' + source.src + ')'); - } - lb.style.display = "block"; - lb.focus() - event.stopPropagation() + const source = event.target || event.srcElement; + const modalImage = gradioApp().getElementById("modalImage") + const lb = gradioApp().getElementById("lightboxModal") + modalImage.src = source.src + if (modalImage.style.display === 'none') { + lb.style.setProperty('background-image', 'url(' + source.src + ')'); + } + lb.style.display = "block"; + lb.focus() + + const tabTxt2Img = gradioApp().getElementById("tab_txt2img") + const tabImg2Img = gradioApp().getElementById("tab_img2img") + // show the save button in modal only on txt2img or img2img tabs + if (tabTxt2Img.style.display != "none" || tabImg2Img.style.display != "none") { + gradioApp().getElementById("modal_save").style.display = "inline" + } else { + gradioApp().getElementById("modal_save").style.display = "none" + } + event.stopPropagation() } function negmod(n, m) { - return ((n % m) + m) % m; + return ((n % m) + m) % m; } -function modalImageSwitch(offset){ - var allgalleryButtons = gradioApp().querySelectorAll(".gallery-item.transition-all") - var galleryButtons = [] - allgalleryButtons.forEach(function(elem){ - if(elem.parentElement.offsetParent){ - galleryButtons.push(elem); +function updateOnBackgroundChange() { + const modalImage = gradioApp().getElementById("modalImage") + if (modalImage && modalImage.offsetParent) { + let allcurrentButtons = gradioApp().querySelectorAll(".gallery-item.transition-all.\\!ring-2") + let currentButton = null + allcurrentButtons.forEach(function(elem) { + if (elem.parentElement.offsetParent) { + currentButton = elem; + } + }) + + if (currentButton?.children?.length > 0 && modalImage.src != currentButton.children[0].src) { + modalImage.src = currentButton.children[0].src; + if (modalImage.style.display === 'none') { + modal.style.setProperty('background-image', `url(${modalImage.src})`) + } + } } - }) - - if(galleryButtons.length>1){ - var allcurrentButtons = gradioApp().querySelectorAll(".gallery-item.transition-all.\\!ring-2") - var currentButton = null - allcurrentButtons.forEach(function(elem){ - if(elem.parentElement.offsetParent){ - currentButton = elem; +} + +function modalImageSwitch(offset) { + var allgalleryButtons = gradioApp().querySelectorAll(".gallery-item.transition-all") + var galleryButtons = [] + allgalleryButtons.forEach(function(elem) { + if (elem.parentElement.offsetParent) { + galleryButtons.push(elem); } - }) - - var result = -1 - galleryButtons.forEach(function(v, i){ if(v==currentButton) { result = i } }) - - if(result != -1){ - nextButton = galleryButtons[negmod((result+offset),galleryButtons.length)] - nextButton.click() - const modalImage = gradioApp().getElementById("modalImage"); - const modal = gradioApp().getElementById("lightboxModal"); - modalImage.src = nextButton.children[0].src; - if (modalImage.style.display === 'none') { - modal.style.setProperty('background-image', `url(${modalImage.src})`) + }) + + if (galleryButtons.length > 1) { + var allcurrentButtons = gradioApp().querySelectorAll(".gallery-item.transition-all.\\!ring-2") + var currentButton = null + allcurrentButtons.forEach(function(elem) { + if (elem.parentElement.offsetParent) { + currentButton = elem; + } + }) + + var result = -1 + galleryButtons.forEach(function(v, i) { + if (v == currentButton) { + result = i + } + }) + + if (result != -1) { + nextButton = galleryButtons[negmod((result + offset), galleryButtons.length)] + nextButton.click() + const modalImage = gradioApp().getElementById("modalImage"); + const modal = gradioApp().getElementById("lightboxModal"); + modalImage.src = nextButton.children[0].src; + if (modalImage.style.display === 'none') { + modal.style.setProperty('background-image', `url(${modalImage.src})`) + } + setTimeout(function() { + modal.focus() + }, 10) } - setTimeout( function(){modal.focus()},10) - } - } + } +} + +function saveImage(){ + const tabTxt2Img = gradioApp().getElementById("tab_txt2img") + const tabImg2Img = gradioApp().getElementById("tab_img2img") + const saveTxt2Img = "save_txt2img" + const saveImg2Img = "save_img2img" + if (tabTxt2Img.style.display != "none") { + gradioApp().getElementById(saveTxt2Img).click() + } else if (tabImg2Img.style.display != "none") { + gradioApp().getElementById(saveImg2Img).click() + } else { + console.error("missing implementation for saving modal of this type") + } +} + +function modalSaveImage(event) { + saveImage() + event.stopPropagation() } -function modalNextImage(event){ - modalImageSwitch(1) - event.stopPropagation() +function modalNextImage(event) { + modalImageSwitch(1) + event.stopPropagation() } -function modalPrevImage(event){ - modalImageSwitch(-1) - event.stopPropagation() +function modalPrevImage(event) { + modalImageSwitch(-1) + event.stopPropagation() } -function modalKeyHandler(event){ +function modalKeyHandler(event) { switch (event.key) { + case "s": + saveImage() + break; case "ArrowLeft": modalPrevImage(event) break; @@ -80,21 +136,23 @@ function modalKeyHandler(event){ } } -function showGalleryImage(){ +function showGalleryImage() { setTimeout(function() { fullImg_preview = gradioApp().querySelectorAll('img.w-full.object-contain') - - if(fullImg_preview != null){ + + if (fullImg_preview != null) { fullImg_preview.forEach(function function_name(e) { + if (e.dataset.modded) + return; + e.dataset.modded = true; if(e && e.parentElement.tagName == 'DIV'){ - e.style.cursor='pointer' - + e.style.userSelect='none' e.addEventListener('click', function (evt) { if(!opts.js_modal_lightbox) return; - modalZoomSet(gradioApp().getElementById('modalImage'), opts.js_modal_lightbox_initialy_zoomed) + modalZoomSet(gradioApp().getElementById('modalImage'), opts.js_modal_lightbox_initially_zoomed) showModal(evt) - },true); + }, true); } }); } @@ -102,21 +160,21 @@ function showGalleryImage(){ }, 100); } -function modalZoomSet(modalImage, enable){ - if( enable ){ +function modalZoomSet(modalImage, enable) { + if (enable) { modalImage.classList.add('modalImageFullscreen'); - } else{ + } else { modalImage.classList.remove('modalImageFullscreen'); } } -function modalZoomToggle(event){ +function modalZoomToggle(event) { modalImage = gradioApp().getElementById("modalImage"); modalZoomSet(modalImage, !modalImage.classList.contains('modalImageFullscreen')) event.stopPropagation() } -function modalTileImageToggle(event){ +function modalTileImageToggle(event) { const modalImage = gradioApp().getElementById("modalImage"); const modal = gradioApp().getElementById("lightboxModal"); const isTiling = modalImage.style.display === 'none'; @@ -131,17 +189,18 @@ function modalTileImageToggle(event){ event.stopPropagation() } -function galleryImageHandler(e){ - if(e && e.parentElement.tagName == 'BUTTON'){ +function galleryImageHandler(e) { + if (e && e.parentElement.tagName == 'BUTTON') { e.onclick = showGalleryImage; } } -onUiUpdate(function(){ +onUiUpdate(function() { fullImg_preview = gradioApp().querySelectorAll('img.w-full') - if(fullImg_preview != null){ - fullImg_preview.forEach(galleryImageHandler); + if (fullImg_preview != null) { + fullImg_preview.forEach(galleryImageHandler); } + updateOnBackgroundChange(); }) document.addEventListener("DOMContentLoaded", function() { @@ -149,13 +208,13 @@ document.addEventListener("DOMContentLoaded", function() { const modal = document.createElement('div') modal.onclick = closeModal; modal.id = "lightboxModal"; - modal.tabIndex=0 + modal.tabIndex = 0 modal.addEventListener('keydown', modalKeyHandler, true) const modalControls = document.createElement('div') modalControls.className = 'modalControls gradio-container'; modal.append(modalControls); - + const modalZoom = document.createElement('span') modalZoom.className = 'modalZoom cursor'; modalZoom.innerHTML = '⤡' @@ -170,6 +229,14 @@ document.addEventListener("DOMContentLoaded", function() { modalTileImage.title = "Preview tiling"; modalControls.appendChild(modalTileImage) + const modalSave = document.createElement("span") + modalSave.className = "modalSave cursor" + modalSave.id = "modal_save" + modalSave.innerHTML = "🖫" + modalSave.addEventListener("click", modalSaveImage, true) + modalSave.title = "Save Image(s)" + modalControls.appendChild(modalSave) + const modalClose = document.createElement('span') modalClose.className = 'modalClose cursor'; modalClose.innerHTML = '×' @@ -180,30 +247,30 @@ document.addEventListener("DOMContentLoaded", function() { const modalImage = document.createElement('img') modalImage.id = 'modalImage'; modalImage.onclick = closeModal; - modalImage.tabIndex=0 + modalImage.tabIndex = 0 modalImage.addEventListener('keydown', modalKeyHandler, true) modal.appendChild(modalImage) const modalPrev = document.createElement('a') modalPrev.className = 'modalPrev'; modalPrev.innerHTML = '❮' - modalPrev.tabIndex=0 - modalPrev.addEventListener('click',modalPrevImage,true); + modalPrev.tabIndex = 0 + modalPrev.addEventListener('click', modalPrevImage, true); modalPrev.addEventListener('keydown', modalKeyHandler, true) modal.appendChild(modalPrev) const modalNext = document.createElement('a') modalNext.className = 'modalNext'; modalNext.innerHTML = '❯' - modalNext.tabIndex=0 - modalNext.addEventListener('click',modalNextImage,true); + modalNext.tabIndex = 0 + modalNext.addEventListener('click', modalNextImage, true); modalNext.addEventListener('keydown', modalKeyHandler, true) modal.appendChild(modalNext) gradioApp().getRootNode().appendChild(modal) - + document.body.appendChild(modalFragment); - + }); diff --git a/javascript/localization.js b/javascript/localization.js new file mode 100644 index 00000000..f92d2d24 --- /dev/null +++ b/javascript/localization.js @@ -0,0 +1,167 @@ +
+// localization = {} -- the dict with translations is created by the backend
+
+ignore_ids_for_localization={
+ setting_sd_hypernetwork: 'OPTION',
+ setting_sd_model_checkpoint: 'OPTION',
+ setting_realesrgan_enabled_models: 'OPTION',
+ modelmerger_primary_model_name: 'OPTION',
+ modelmerger_secondary_model_name: 'OPTION',
+ modelmerger_tertiary_model_name: 'OPTION',
+ train_embedding: 'OPTION',
+ train_hypernetwork: 'OPTION',
+ txt2img_style_index: 'OPTION',
+ txt2img_style2_index: 'OPTION',
+ img2img_style_index: 'OPTION',
+ img2img_style2_index: 'OPTION',
+ setting_random_artist_categories: 'SPAN',
+ setting_face_restoration_model: 'SPAN',
+ setting_realesrgan_enabled_models: 'SPAN',
+ extras_upscaler_1: 'SPAN',
+ extras_upscaler_2: 'SPAN',
+}
+
+re_num = /^[\.\d]+$/
+re_emoji = /[\p{Extended_Pictographic}\u{1F3FB}-\u{1F3FF}\u{1F9B0}-\u{1F9B3}]/u
+
+original_lines = {}
+translated_lines = {}
+
+function textNodesUnder(el){
+ var n, a=[], walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false);
+ while(n=walk.nextNode()) a.push(n);
+ return a;
+}
+
+function canBeTranslated(node, text){
+ if(! text) return false;
+ if(! node.parentElement) return false;
+
+ parentType = node.parentElement.nodeName
+ if(parentType=='SCRIPT' || parentType=='STYLE' || parentType=='TEXTAREA') return false;
+
+ if (parentType=='OPTION' || parentType=='SPAN'){
+ pnode = node
+ for(var level=0; level<4; level++){
+ pnode = pnode.parentElement
+ if(! pnode) break;
+
+ if(ignore_ids_for_localization[pnode.id] == parentType) return false;
+ }
+ }
+
+ if(re_num.test(text)) return false;
+ if(re_emoji.test(text)) return false;
+ return true
+}
+
+function getTranslation(text){
+ if(! text) return undefined
+
+ if(translated_lines[text] === undefined){
+ original_lines[text] = 1
+ }
+
+ tl = localization[text]
+ if(tl !== undefined){
+ translated_lines[tl] = 1
+ }
+
+ return tl
+}
+
+function processTextNode(node){
+ text = node.textContent.trim()
+
+ if(! canBeTranslated(node, text)) return
+
+ tl = getTranslation(text)
+ if(tl !== undefined){
+ node.textContent = tl
+ }
+}
+
+function processNode(node){
+ if(node.nodeType == 3){
+ processTextNode(node)
+ return
+ }
+
+ if(node.title){
+ tl = getTranslation(node.title)
+ if(tl !== undefined){
+ node.title = tl
+ }
+ }
+
+ if(node.placeholder){
+ tl = getTranslation(node.placeholder)
+ if(tl !== undefined){
+ node.placeholder = tl
+ }
+ }
+
+ textNodesUnder(node).forEach(function(node){
+ processTextNode(node)
+ })
+}
+
+function dumpTranslations(){
+ dumped = {}
+ if (localization.rtl) {
+ dumped.rtl = true
+ }
+
+ Object.keys(original_lines).forEach(function(text){
+ if(dumped[text] !== undefined) return
+
+ dumped[text] = localization[text] || text
+ })
+
+ return dumped
+}
+
+onUiUpdate(function(m){
+ m.forEach(function(mutation){
+ mutation.addedNodes.forEach(function(node){
+ processNode(node)
+ })
+ });
+})
+
+
+document.addEventListener("DOMContentLoaded", function() {
+ processNode(gradioApp())
+
+ if (localization.rtl) { // if the language is from right to left,
+ (new MutationObserver((mutations, observer) => { // wait for the style to load
+ mutations.forEach(mutation => {
+ mutation.addedNodes.forEach(node => {
+ if (node.tagName === 'STYLE') {
+ observer.disconnect();
+
+ for (const x of node.sheet.rules) { // find all rtl media rules
+ if (Array.from(x.media || []).includes('rtl')) {
+ x.media.appendMedium('all'); // enable them
+ }
+ }
+ }
+ })
+ });
+ })).observe(gradioApp(), { childList: true });
+ }
+})
+
+function download_localization() {
+ text = JSON.stringify(dumpTranslations(), null, 4)
+
+ var element = document.createElement('a');
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
+ element.setAttribute('download', "localization.json");
+ element.style.display = 'none';
+ document.body.appendChild(element);
+
+ element.click();
+
+ document.body.removeChild(element);
+}
diff --git a/javascript/notification.js b/javascript/notification.js index bdf614ad..040a3afa 100644 --- a/javascript/notification.js +++ b/javascript/notification.js @@ -15,7 +15,7 @@ onUiUpdate(function(){ } } - const galleryPreviews = gradioApp().querySelectorAll('img.h-full.w-full.overflow-hidden'); + const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] img.h-full.w-full.overflow-hidden'); if (galleryPreviews == null) return; @@ -36,7 +36,7 @@ onUiUpdate(function(){ const notification = new Notification( 'Stable Diffusion', { - body: `Generated ${imgs.size > 1 ? imgs.size - 1 : 1} image${imgs.size > 1 ? 's' : ''}`, + body: `Generated ${imgs.size > 1 ? imgs.size - opts.return_grid : 1} image${imgs.size > 1 ? 's' : ''}`, icon: headImg, image: headImg, } diff --git a/javascript/progressbar.js b/javascript/progressbar.js index 21f25b38..d6323ed9 100644 --- a/javascript/progressbar.js +++ b/javascript/progressbar.js @@ -1,45 +1,142 @@ // code related to showing and updating progressbar shown as the image is being made global_progressbars = {} +galleries = {} +galleryObservers = {} -function check_progressbar(id_part, id_progressbar, id_progressbar_span, id_interrupt, id_preview, id_gallery){ - var progressbar = gradioApp().getElementById(id_progressbar) +// this tracks launches of window.setTimeout for progressbar to prevent starting a new timeout when the previous is still running +timeoutIds = {} + +function check_progressbar(id_part, id_progressbar, id_progressbar_span, id_skip, id_interrupt, id_preview, id_gallery){ + // gradio 3.8's enlightened approach allows them to create two nested div elements inside each other with same id + // every time you use gr.HTML(elem_id='xxx'), so we handle this here + var progressbar = gradioApp().querySelector("#"+id_progressbar+" #"+id_progressbar) + var progressbarParent + if(progressbar){ + progressbarParent = gradioApp().querySelector("#"+id_progressbar) + } else{ + progressbar = gradioApp().getElementById(id_progressbar) + progressbarParent = null + } + + var skip = id_skip ? gradioApp().getElementById(id_skip) : null var interrupt = gradioApp().getElementById(id_interrupt) + + if(opts.show_progress_in_title && progressbar && progressbar.offsetParent){ + if(progressbar.innerText){ + let newtitle = '[' + progressbar.innerText.trim() + '] Stable Diffusion'; + if(document.title != newtitle){ + document.title = newtitle; + } + }else{ + let newtitle = 'Stable Diffusion' + if(document.title != newtitle){ + document.title = newtitle; + } + } + } + if(progressbar!= null && progressbar != global_progressbars[id_progressbar]){ global_progressbars[id_progressbar] = progressbar var mutationObserver = new MutationObserver(function(m){ + if(timeoutIds[id_part]) return; + preview = gradioApp().getElementById(id_preview) gallery = gradioApp().getElementById(id_gallery) if(preview != null && gallery != null){ preview.style.width = gallery.clientWidth + "px" preview.style.height = gallery.clientHeight + "px" + if(progressbarParent) progressbar.style.width = progressbarParent.clientWidth + "px" + + //only watch gallery if there is a generation process going on + check_gallery(id_gallery); var progressDiv = gradioApp().querySelectorAll('#' + id_progressbar_span).length > 0; - if(!progressDiv){ + if(progressDiv){ + timeoutIds[id_part] = window.setTimeout(function() { + timeoutIds[id_part] = null + requestMoreProgress(id_part, id_progressbar_span, id_skip, id_interrupt) + }, 500) + } else{ + if (skip) { + skip.style.display = "none" + } interrupt.style.display = "none" + + //disconnect observer once generation finished, so user can close selected image if they want + if (galleryObservers[id_gallery]) { + galleryObservers[id_gallery].disconnect(); + galleries[id_gallery] = null; + } } } - window.setTimeout(function(){ requestMoreProgress(id_part, id_progressbar_span, id_interrupt) }, 500) }); mutationObserver.observe( progressbar, { childList:true, subtree:true }) } } +function check_gallery(id_gallery){ + let gallery = gradioApp().getElementById(id_gallery) + // if gallery has no change, no need to setting up observer again. + if (gallery && galleries[id_gallery] !== gallery){ + galleries[id_gallery] = gallery; + if(galleryObservers[id_gallery]){ + galleryObservers[id_gallery].disconnect(); + } + let prevSelectedIndex = selected_gallery_index(); + galleryObservers[id_gallery] = new MutationObserver(function (){ + let galleryButtons = gradioApp().querySelectorAll('#'+id_gallery+' .gallery-item') + let galleryBtnSelected = gradioApp().querySelector('#'+id_gallery+' .gallery-item.\\!ring-2') + if (prevSelectedIndex !== -1 && galleryButtons.length>prevSelectedIndex && !galleryBtnSelected) { + // automatically re-open previously selected index (if exists) + activeElement = gradioApp().activeElement; + let scrollX = window.scrollX; + let scrollY = window.scrollY; + + galleryButtons[prevSelectedIndex].click(); + showGalleryImage(); + + // When the gallery button is clicked, it gains focus and scrolls itself into view + // We need to scroll back to the previous position + setTimeout(function (){ + window.scrollTo(scrollX, scrollY); + }, 50); + + if(activeElement){ + // i fought this for about an hour; i don't know why the focus is lost or why this helps recover it + // if someone has a better solution please by all means + setTimeout(function (){ + activeElement.focus({ + preventScroll: true // Refocus the element that was focused before the gallery was opened without scrolling to it + }) + }, 1); + } + } + }) + galleryObservers[id_gallery].observe( gallery, { childList:true, subtree:false }) + } +} + onUiUpdate(function(){ - check_progressbar('txt2img', 'txt2img_progressbar', 'txt2img_progress_span', 'txt2img_interrupt', 'txt2img_preview', 'txt2img_gallery') - check_progressbar('img2img', 'img2img_progressbar', 'img2img_progress_span', 'img2img_interrupt', 'img2img_preview', 'img2img_gallery') + check_progressbar('txt2img', 'txt2img_progressbar', 'txt2img_progress_span', 'txt2img_skip', 'txt2img_interrupt', 'txt2img_preview', 'txt2img_gallery') + check_progressbar('img2img', 'img2img_progressbar', 'img2img_progress_span', 'img2img_skip', 'img2img_interrupt', 'img2img_preview', 'img2img_gallery') + check_progressbar('ti', 'ti_progressbar', 'ti_progress_span', '', 'ti_interrupt', 'ti_preview', 'ti_gallery') }) -function requestMoreProgress(id_part, id_progressbar_span, id_interrupt){ +function requestMoreProgress(id_part, id_progressbar_span, id_skip, id_interrupt){ btn = gradioApp().getElementById(id_part+"_check_progress"); if(btn==null) return; btn.click(); var progressDiv = gradioApp().querySelectorAll('#' + id_progressbar_span).length > 0; + var skip = id_skip ? gradioApp().getElementById(id_skip) : null var interrupt = gradioApp().getElementById(id_interrupt) if(progressDiv && interrupt){ + if (skip) { + skip.style.display = "block" + } interrupt.style.display = "block" } } diff --git a/javascript/textualInversion.js b/javascript/textualInversion.js new file mode 100644 index 00000000..8061be08 --- /dev/null +++ b/javascript/textualInversion.js @@ -0,0 +1,8 @@ +
+
+function start_training_textual_inversion(){
+ requestProgress('ti')
+ gradioApp().querySelector('#ti_error').innerHTML=''
+
+ return args_to_array(arguments)
+}
diff --git a/javascript/ui.js b/javascript/ui.js index bfe02410..ee226927 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -1,8 +1,15 @@ -// various functions for interation with ui.py not large enough to warrant putting them in separate files +// various functions for interaction with ui.py not large enough to warrant putting them in separate files + +function set_theme(theme){ + gradioURL = window.location.href + if (!gradioURL.includes('?__theme=')) { + window.location.replace(gradioURL + '?__theme=' + theme); + } +} function selected_gallery_index(){ - var buttons = gradioApp().querySelectorAll('[style="display: block;"].tabitem .gallery-item') - var button = gradioApp().querySelector('[style="display: block;"].tabitem .gallery-item.\\!ring-2') + var buttons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery] .gallery-item') + var button = gradioApp().querySelector('[style="display: block;"].tabitem div[id$=_gallery] .gallery-item.\\!ring-2') var result = -1 buttons.forEach(function(v, i){ if(v==button) { result = i } }) @@ -12,7 +19,7 @@ function selected_gallery_index(){ function extract_image_from_gallery(gallery){ if(gallery.length == 1){ - return gallery[0] + return [gallery[0]] } index = selected_gallery_index() @@ -21,7 +28,7 @@ function extract_image_from_gallery(gallery){ return [null] } - return gallery[index]; + return [gallery[index]]; } function args_to_array(args){ @@ -33,51 +40,31 @@ function args_to_array(args){ } function switch_to_txt2img(){ - gradioApp().querySelectorAll('button')[0].click(); + gradioApp().querySelector('#tabs').querySelectorAll('button')[0].click(); return args_to_array(arguments); } -function switch_to_img2img_img2img(){ - gradioApp().querySelectorAll('button')[1].click(); +function switch_to_img2img(){ + gradioApp().querySelector('#tabs').querySelectorAll('button')[1].click(); gradioApp().getElementById('mode_img2img').querySelectorAll('button')[0].click(); return args_to_array(arguments); } -function switch_to_img2img_inpaint(){ - gradioApp().querySelectorAll('button')[1].click(); +function switch_to_inpaint(){ + gradioApp().querySelector('#tabs').querySelectorAll('button')[1].click(); gradioApp().getElementById('mode_img2img').querySelectorAll('button')[1].click(); return args_to_array(arguments); } function switch_to_extras(){ - gradioApp().querySelectorAll('button')[2].click(); + gradioApp().querySelector('#tabs').querySelectorAll('button')[2].click(); return args_to_array(arguments); } -function extract_image_from_gallery_txt2img(gallery){ - switch_to_txt2img() - return extract_image_from_gallery(gallery); -} - -function extract_image_from_gallery_img2img(gallery){ - switch_to_img2img_img2img() - return extract_image_from_gallery(gallery); -} - -function extract_image_from_gallery_inpaint(gallery){ - switch_to_img2img_inpaint() - return extract_image_from_gallery(gallery); -} - -function extract_image_from_gallery_extras(gallery){ - switch_to_extras() - return extract_image_from_gallery(gallery); -} - function get_tab_index(tabId){ var res = 0 @@ -101,7 +88,8 @@ function create_tab_index_args(tabId, args){ } function get_extras_tab_index(){ - return create_tab_index_args('mode_extras', arguments) + const [,,...args] = [...arguments] + return [get_tab_index('mode_extras'), get_tab_index('extras_resize_mode'), ...args] } function create_submit_args(args){ @@ -112,7 +100,7 @@ function create_submit_args(args){ // 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. // This can lead to uploading a huge gallery of previously generated images, which leads to an unnecessary delay between submitting and beginning to generate. - // 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. + // 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. // If gradio at some point stops sending outputs, this may break something if(Array.isArray(res[res.length - 3])){ res[res.length - 3] = null @@ -140,7 +128,16 @@ function submit_img2img(){ function ask_for_style_name(_, prompt_text, negative_prompt_text) { name_ = prompt('Style name:') - return name_ === null ? [null, null, null]: [name_, prompt_text, negative_prompt_text] + return [name_, prompt_text, negative_prompt_text] +} + +function confirm_clear_prompt(prompt, negative_prompt) { + if(confirm("Delete prompt?")) { + prompt = "" + negative_prompt = "" + } + + return [prompt, negative_prompt] } @@ -186,25 +183,40 @@ onUiUpdate(function(){ if (!txt2img_textarea) { txt2img_textarea = gradioApp().querySelector("#txt2img_prompt > label > textarea"); txt2img_textarea?.addEventListener("input", () => update_token_counter("txt2img_token_button")); - txt2img_textarea?.addEventListener("keyup", (event) => submit_prompt(event, "txt2img_generate")); } if (!img2img_textarea) { img2img_textarea = gradioApp().querySelector("#img2img_prompt > label > textarea"); img2img_textarea?.addEventListener("input", () => update_token_counter("img2img_token_button")); - img2img_textarea?.addEventListener("keyup", (event) => submit_prompt(event, "img2img_generate")); } + + show_all_pages = gradioApp().getElementById('settings_show_all_pages') + settings_tabs = gradioApp().querySelector('#settings div') + if(show_all_pages && settings_tabs){ + settings_tabs.appendChild(show_all_pages) + show_all_pages.onclick = function(){ + gradioApp().querySelectorAll('#settings > div').forEach(function(elem){ + elem.style.display = "block"; + }) + } + } }) let txt2img_textarea, img2img_textarea = undefined; let wait_time = 800 let token_timeout; -function submit_prompt(event, generate_button_id) { - if (event.altKey && event.keyCode === 13) { - event.preventDefault(); - gradioApp().getElementById(generate_button_id).click(); - return; - } +function update_txt2img_tokens(...args) { + update_token_counter("txt2img_token_button") + if (args.length == 2) + return args[0] + return args; +} + +function update_img2img_tokens(...args) { + update_token_counter("img2img_token_button") + if (args.length == 2) + return args[0] + return args; } function update_token_counter(button_id) { @@ -212,3 +224,10 @@ function update_token_counter(button_id) { clearTimeout(token_timeout); token_timeout = setTimeout(() => gradioApp().getElementById(button_id)?.click(), wait_time); } + +function restart_reload(){ + document.body.innerHTML='<h1 style="font-family:monospace;margin-top:20%;color:lightgray;text-align:center;">Reloading...</h1>'; + setTimeout(function(){location.reload()},2000) + + return [] +} |