javascript/
Browse files- javascript/aspectRatioOverlay.js +116 -0
- javascript/contextMenus.js +177 -0
- javascript/dragdrop.js +97 -0
- javascript/edit-attention.js +96 -0
- javascript/extensions.js +49 -0
- javascript/extraNetworks.js +179 -0
- javascript/generationParams.js +33 -0
- javascript/hints.js +147 -0
- javascript/hires_fix.js +22 -0
- javascript/imageMaskFix.js +45 -0
- javascript/imageParams.js +19 -0
- javascript/imageviewer.js +259 -0
- javascript/localization.js +165 -0
- javascript/notification.js +49 -0
- javascript/progressbar.js +178 -0
- javascript/textualInversion.js +17 -0
- javascript/ui.js +363 -0
javascript/aspectRatioOverlay.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
let currentWidth = null;
|
| 3 |
+
let currentHeight = null;
|
| 4 |
+
let arFrameTimeout = setTimeout(function(){},0);
|
| 5 |
+
|
| 6 |
+
function dimensionChange(e, is_width, is_height){
|
| 7 |
+
|
| 8 |
+
if(is_width){
|
| 9 |
+
currentWidth = e.target.value*1.0
|
| 10 |
+
}
|
| 11 |
+
if(is_height){
|
| 12 |
+
currentHeight = e.target.value*1.0
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
var inImg2img = gradioApp().querySelector("#tab_img2img").style.display == "block";
|
| 16 |
+
|
| 17 |
+
if(!inImg2img){
|
| 18 |
+
return;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
var targetElement = null;
|
| 22 |
+
|
| 23 |
+
var tabIndex = get_tab_index('mode_img2img')
|
| 24 |
+
if(tabIndex == 0){ // img2img
|
| 25 |
+
targetElement = gradioApp().querySelector('#img2img_image div[data-testid=image] img');
|
| 26 |
+
} else if(tabIndex == 1){ //Sketch
|
| 27 |
+
targetElement = gradioApp().querySelector('#img2img_sketch div[data-testid=image] img');
|
| 28 |
+
} else if(tabIndex == 2){ // Inpaint
|
| 29 |
+
targetElement = gradioApp().querySelector('#img2maskimg div[data-testid=image] img');
|
| 30 |
+
} else if(tabIndex == 3){ // Inpaint sketch
|
| 31 |
+
targetElement = gradioApp().querySelector('#inpaint_sketch div[data-testid=image] img');
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
if(targetElement){
|
| 36 |
+
|
| 37 |
+
var arPreviewRect = gradioApp().querySelector('#imageARPreview');
|
| 38 |
+
if(!arPreviewRect){
|
| 39 |
+
arPreviewRect = document.createElement('div')
|
| 40 |
+
arPreviewRect.id = "imageARPreview";
|
| 41 |
+
gradioApp().appendChild(arPreviewRect)
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
var viewportOffset = targetElement.getBoundingClientRect();
|
| 47 |
+
|
| 48 |
+
viewportscale = Math.min( targetElement.clientWidth/targetElement.naturalWidth, targetElement.clientHeight/targetElement.naturalHeight )
|
| 49 |
+
|
| 50 |
+
scaledx = targetElement.naturalWidth*viewportscale
|
| 51 |
+
scaledy = targetElement.naturalHeight*viewportscale
|
| 52 |
+
|
| 53 |
+
cleintRectTop = (viewportOffset.top+window.scrollY)
|
| 54 |
+
cleintRectLeft = (viewportOffset.left+window.scrollX)
|
| 55 |
+
cleintRectCentreY = cleintRectTop + (targetElement.clientHeight/2)
|
| 56 |
+
cleintRectCentreX = cleintRectLeft + (targetElement.clientWidth/2)
|
| 57 |
+
|
| 58 |
+
viewRectTop = cleintRectCentreY-(scaledy/2)
|
| 59 |
+
viewRectLeft = cleintRectCentreX-(scaledx/2)
|
| 60 |
+
arRectWidth = scaledx
|
| 61 |
+
arRectHeight = scaledy
|
| 62 |
+
|
| 63 |
+
arscale = Math.min( arRectWidth/currentWidth, arRectHeight/currentHeight )
|
| 64 |
+
arscaledx = currentWidth*arscale
|
| 65 |
+
arscaledy = currentHeight*arscale
|
| 66 |
+
|
| 67 |
+
arRectTop = cleintRectCentreY-(arscaledy/2)
|
| 68 |
+
arRectLeft = cleintRectCentreX-(arscaledx/2)
|
| 69 |
+
arRectWidth = arscaledx
|
| 70 |
+
arRectHeight = arscaledy
|
| 71 |
+
|
| 72 |
+
arPreviewRect.style.top = arRectTop+'px';
|
| 73 |
+
arPreviewRect.style.left = arRectLeft+'px';
|
| 74 |
+
arPreviewRect.style.width = arRectWidth+'px';
|
| 75 |
+
arPreviewRect.style.height = arRectHeight+'px';
|
| 76 |
+
|
| 77 |
+
clearTimeout(arFrameTimeout);
|
| 78 |
+
arFrameTimeout = setTimeout(function(){
|
| 79 |
+
arPreviewRect.style.display = 'none';
|
| 80 |
+
},2000);
|
| 81 |
+
|
| 82 |
+
arPreviewRect.style.display = 'block';
|
| 83 |
+
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
onUiUpdate(function(){
|
| 90 |
+
var arPreviewRect = gradioApp().querySelector('#imageARPreview');
|
| 91 |
+
if(arPreviewRect){
|
| 92 |
+
arPreviewRect.style.display = 'none';
|
| 93 |
+
}
|
| 94 |
+
var tabImg2img = gradioApp().querySelector("#tab_img2img");
|
| 95 |
+
if (tabImg2img) {
|
| 96 |
+
var inImg2img = tabImg2img.style.display == "block";
|
| 97 |
+
if(inImg2img){
|
| 98 |
+
let inputs = gradioApp().querySelectorAll('input');
|
| 99 |
+
inputs.forEach(function(e){
|
| 100 |
+
var is_width = e.parentElement.id == "img2img_width"
|
| 101 |
+
var is_height = e.parentElement.id == "img2img_height"
|
| 102 |
+
|
| 103 |
+
if((is_width || is_height) && !e.classList.contains('scrollwatch')){
|
| 104 |
+
e.addEventListener('input', function(e){dimensionChange(e, is_width, is_height)} )
|
| 105 |
+
e.classList.add('scrollwatch')
|
| 106 |
+
}
|
| 107 |
+
if(is_width){
|
| 108 |
+
currentWidth = e.value*1.0
|
| 109 |
+
}
|
| 110 |
+
if(is_height){
|
| 111 |
+
currentHeight = e.value*1.0
|
| 112 |
+
}
|
| 113 |
+
})
|
| 114 |
+
}
|
| 115 |
+
}
|
| 116 |
+
});
|
javascript/contextMenus.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
contextMenuInit = function(){
|
| 3 |
+
let eventListenerApplied=false;
|
| 4 |
+
let menuSpecs = new Map();
|
| 5 |
+
|
| 6 |
+
const uid = function(){
|
| 7 |
+
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
function showContextMenu(event,element,menuEntries){
|
| 11 |
+
let posx = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
|
| 12 |
+
let posy = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
|
| 13 |
+
|
| 14 |
+
let oldMenu = gradioApp().querySelector('#context-menu')
|
| 15 |
+
if(oldMenu){
|
| 16 |
+
oldMenu.remove()
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
let tabButton = uiCurrentTab
|
| 20 |
+
let baseStyle = window.getComputedStyle(tabButton)
|
| 21 |
+
|
| 22 |
+
const contextMenu = document.createElement('nav')
|
| 23 |
+
contextMenu.id = "context-menu"
|
| 24 |
+
contextMenu.style.background = baseStyle.background
|
| 25 |
+
contextMenu.style.color = baseStyle.color
|
| 26 |
+
contextMenu.style.fontFamily = baseStyle.fontFamily
|
| 27 |
+
contextMenu.style.top = posy+'px'
|
| 28 |
+
contextMenu.style.left = posx+'px'
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
const contextMenuList = document.createElement('ul')
|
| 33 |
+
contextMenuList.className = 'context-menu-items';
|
| 34 |
+
contextMenu.append(contextMenuList);
|
| 35 |
+
|
| 36 |
+
menuEntries.forEach(function(entry){
|
| 37 |
+
let contextMenuEntry = document.createElement('a')
|
| 38 |
+
contextMenuEntry.innerHTML = entry['name']
|
| 39 |
+
contextMenuEntry.addEventListener("click", function(e) {
|
| 40 |
+
entry['func']();
|
| 41 |
+
})
|
| 42 |
+
contextMenuList.append(contextMenuEntry);
|
| 43 |
+
|
| 44 |
+
})
|
| 45 |
+
|
| 46 |
+
gradioApp().appendChild(contextMenu)
|
| 47 |
+
|
| 48 |
+
let menuWidth = contextMenu.offsetWidth + 4;
|
| 49 |
+
let menuHeight = contextMenu.offsetHeight + 4;
|
| 50 |
+
|
| 51 |
+
let windowWidth = window.innerWidth;
|
| 52 |
+
let windowHeight = window.innerHeight;
|
| 53 |
+
|
| 54 |
+
if ( (windowWidth - posx) < menuWidth ) {
|
| 55 |
+
contextMenu.style.left = windowWidth - menuWidth + "px";
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
if ( (windowHeight - posy) < menuHeight ) {
|
| 59 |
+
contextMenu.style.top = windowHeight - menuHeight + "px";
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
function appendContextMenuOption(targetElementSelector,entryName,entryFunction){
|
| 65 |
+
|
| 66 |
+
currentItems = menuSpecs.get(targetElementSelector)
|
| 67 |
+
|
| 68 |
+
if(!currentItems){
|
| 69 |
+
currentItems = []
|
| 70 |
+
menuSpecs.set(targetElementSelector,currentItems);
|
| 71 |
+
}
|
| 72 |
+
let newItem = {'id':targetElementSelector+'_'+uid(),
|
| 73 |
+
'name':entryName,
|
| 74 |
+
'func':entryFunction,
|
| 75 |
+
'isNew':true}
|
| 76 |
+
|
| 77 |
+
currentItems.push(newItem)
|
| 78 |
+
return newItem['id']
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
function removeContextMenuOption(uid){
|
| 82 |
+
menuSpecs.forEach(function(v,k) {
|
| 83 |
+
let index = -1
|
| 84 |
+
v.forEach(function(e,ei){if(e['id']==uid){index=ei}})
|
| 85 |
+
if(index>=0){
|
| 86 |
+
v.splice(index, 1);
|
| 87 |
+
}
|
| 88 |
+
})
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
function addContextMenuEventListener(){
|
| 92 |
+
if(eventListenerApplied){
|
| 93 |
+
return;
|
| 94 |
+
}
|
| 95 |
+
gradioApp().addEventListener("click", function(e) {
|
| 96 |
+
let source = e.composedPath()[0]
|
| 97 |
+
if(source.id && source.id.indexOf('check_progress')>-1){
|
| 98 |
+
return
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
let oldMenu = gradioApp().querySelector('#context-menu')
|
| 102 |
+
if(oldMenu){
|
| 103 |
+
oldMenu.remove()
|
| 104 |
+
}
|
| 105 |
+
});
|
| 106 |
+
gradioApp().addEventListener("contextmenu", function(e) {
|
| 107 |
+
let oldMenu = gradioApp().querySelector('#context-menu')
|
| 108 |
+
if(oldMenu){
|
| 109 |
+
oldMenu.remove()
|
| 110 |
+
}
|
| 111 |
+
menuSpecs.forEach(function(v,k) {
|
| 112 |
+
if(e.composedPath()[0].matches(k)){
|
| 113 |
+
showContextMenu(e,e.composedPath()[0],v)
|
| 114 |
+
e.preventDefault()
|
| 115 |
+
return
|
| 116 |
+
}
|
| 117 |
+
})
|
| 118 |
+
});
|
| 119 |
+
eventListenerApplied=true
|
| 120 |
+
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
return [appendContextMenuOption, removeContextMenuOption, addContextMenuEventListener]
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
initResponse = contextMenuInit();
|
| 127 |
+
appendContextMenuOption = initResponse[0];
|
| 128 |
+
removeContextMenuOption = initResponse[1];
|
| 129 |
+
addContextMenuEventListener = initResponse[2];
|
| 130 |
+
|
| 131 |
+
(function(){
|
| 132 |
+
//Start example Context Menu Items
|
| 133 |
+
let generateOnRepeat = function(genbuttonid,interruptbuttonid){
|
| 134 |
+
let genbutton = gradioApp().querySelector(genbuttonid);
|
| 135 |
+
let interruptbutton = gradioApp().querySelector(interruptbuttonid);
|
| 136 |
+
if(!interruptbutton.offsetParent){
|
| 137 |
+
genbutton.click();
|
| 138 |
+
}
|
| 139 |
+
clearInterval(window.generateOnRepeatInterval)
|
| 140 |
+
window.generateOnRepeatInterval = setInterval(function(){
|
| 141 |
+
if(!interruptbutton.offsetParent){
|
| 142 |
+
genbutton.click();
|
| 143 |
+
}
|
| 144 |
+
},
|
| 145 |
+
500)
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
appendContextMenuOption('#txt2img_generate','Generate forever',function(){
|
| 149 |
+
generateOnRepeat('#txt2img_generate','#txt2img_interrupt');
|
| 150 |
+
})
|
| 151 |
+
appendContextMenuOption('#img2img_generate','Generate forever',function(){
|
| 152 |
+
generateOnRepeat('#img2img_generate','#img2img_interrupt');
|
| 153 |
+
})
|
| 154 |
+
|
| 155 |
+
let cancelGenerateForever = function(){
|
| 156 |
+
clearInterval(window.generateOnRepeatInterval)
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
appendContextMenuOption('#txt2img_interrupt','Cancel generate forever',cancelGenerateForever)
|
| 160 |
+
appendContextMenuOption('#txt2img_generate', 'Cancel generate forever',cancelGenerateForever)
|
| 161 |
+
appendContextMenuOption('#img2img_interrupt','Cancel generate forever',cancelGenerateForever)
|
| 162 |
+
appendContextMenuOption('#img2img_generate', 'Cancel generate forever',cancelGenerateForever)
|
| 163 |
+
|
| 164 |
+
appendContextMenuOption('#roll','Roll three',
|
| 165 |
+
function(){
|
| 166 |
+
let rollbutton = get_uiCurrentTabContent().querySelector('#roll');
|
| 167 |
+
setTimeout(function(){rollbutton.click()},100)
|
| 168 |
+
setTimeout(function(){rollbutton.click()},200)
|
| 169 |
+
setTimeout(function(){rollbutton.click()},300)
|
| 170 |
+
}
|
| 171 |
+
)
|
| 172 |
+
})();
|
| 173 |
+
//End example Context Menu Items
|
| 174 |
+
|
| 175 |
+
onUiUpdate(function(){
|
| 176 |
+
addContextMenuEventListener()
|
| 177 |
+
});
|
javascript/dragdrop.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
const tmpFile = files[0];
|
| 13 |
+
|
| 14 |
+
imgWrap.querySelector('.modify-upload button + button, .touch-none + div button + button')?.click();
|
| 15 |
+
const callback = () => {
|
| 16 |
+
const fileInput = imgWrap.querySelector('input[type="file"]');
|
| 17 |
+
if ( fileInput ) {
|
| 18 |
+
if ( files.length === 0 ) {
|
| 19 |
+
files = new DataTransfer();
|
| 20 |
+
files.items.add(tmpFile);
|
| 21 |
+
fileInput.files = files.files;
|
| 22 |
+
} else {
|
| 23 |
+
fileInput.files = files;
|
| 24 |
+
}
|
| 25 |
+
fileInput.dispatchEvent(new Event('change'));
|
| 26 |
+
}
|
| 27 |
+
};
|
| 28 |
+
|
| 29 |
+
if ( imgWrap.closest('#pnginfo_image') ) {
|
| 30 |
+
// special treatment for PNG Info tab, wait for fetch request to finish
|
| 31 |
+
const oldFetch = window.fetch;
|
| 32 |
+
window.fetch = async (input, options) => {
|
| 33 |
+
const response = await oldFetch(input, options);
|
| 34 |
+
if ( 'api/predict/' === input ) {
|
| 35 |
+
const content = await response.text();
|
| 36 |
+
window.fetch = oldFetch;
|
| 37 |
+
window.requestAnimationFrame( () => callback() );
|
| 38 |
+
return new Response(content, {
|
| 39 |
+
status: response.status,
|
| 40 |
+
statusText: response.statusText,
|
| 41 |
+
headers: response.headers
|
| 42 |
+
})
|
| 43 |
+
}
|
| 44 |
+
return response;
|
| 45 |
+
};
|
| 46 |
+
} else {
|
| 47 |
+
window.requestAnimationFrame( () => callback() );
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
window.document.addEventListener('dragover', e => {
|
| 52 |
+
const target = e.composedPath()[0];
|
| 53 |
+
const imgWrap = target.closest('[data-testid="image"]');
|
| 54 |
+
if ( !imgWrap && target.placeholder && target.placeholder.indexOf("Prompt") == -1) {
|
| 55 |
+
return;
|
| 56 |
+
}
|
| 57 |
+
e.stopPropagation();
|
| 58 |
+
e.preventDefault();
|
| 59 |
+
e.dataTransfer.dropEffect = 'copy';
|
| 60 |
+
});
|
| 61 |
+
|
| 62 |
+
window.document.addEventListener('drop', e => {
|
| 63 |
+
const target = e.composedPath()[0];
|
| 64 |
+
if (target.placeholder.indexOf("Prompt") == -1) {
|
| 65 |
+
return;
|
| 66 |
+
}
|
| 67 |
+
const imgWrap = target.closest('[data-testid="image"]');
|
| 68 |
+
if ( !imgWrap ) {
|
| 69 |
+
return;
|
| 70 |
+
}
|
| 71 |
+
e.stopPropagation();
|
| 72 |
+
e.preventDefault();
|
| 73 |
+
const files = e.dataTransfer.files;
|
| 74 |
+
dropReplaceImage( imgWrap, files );
|
| 75 |
+
});
|
| 76 |
+
|
| 77 |
+
window.addEventListener('paste', e => {
|
| 78 |
+
const files = e.clipboardData.files;
|
| 79 |
+
if ( ! isValidImageList( files ) ) {
|
| 80 |
+
return;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
const visibleImageFields = [...gradioApp().querySelectorAll('[data-testid="image"]')]
|
| 84 |
+
.filter(el => uiElementIsVisible(el));
|
| 85 |
+
if ( ! visibleImageFields.length ) {
|
| 86 |
+
return;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
const firstFreeImageField = visibleImageFields
|
| 90 |
+
.filter(el => el.querySelector('input[type=file]'))?.[0];
|
| 91 |
+
|
| 92 |
+
dropReplaceImage(
|
| 93 |
+
firstFreeImageField ?
|
| 94 |
+
firstFreeImageField :
|
| 95 |
+
visibleImageFields[visibleImageFields.length - 1]
|
| 96 |
+
, files );
|
| 97 |
+
});
|
javascript/edit-attention.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
function keyupEditAttention(event){
|
| 2 |
+
let target = event.originalTarget || event.composedPath()[0];
|
| 3 |
+
if (! target.matches("[id*='_toprow'] [id*='_prompt'] textarea")) return;
|
| 4 |
+
if (! (event.metaKey || event.ctrlKey)) return;
|
| 5 |
+
|
| 6 |
+
let isPlus = event.key == "ArrowUp"
|
| 7 |
+
let isMinus = event.key == "ArrowDown"
|
| 8 |
+
if (!isPlus && !isMinus) return;
|
| 9 |
+
|
| 10 |
+
let selectionStart = target.selectionStart;
|
| 11 |
+
let selectionEnd = target.selectionEnd;
|
| 12 |
+
let text = target.value;
|
| 13 |
+
|
| 14 |
+
function selectCurrentParenthesisBlock(OPEN, CLOSE){
|
| 15 |
+
if (selectionStart !== selectionEnd) return false;
|
| 16 |
+
|
| 17 |
+
// Find opening parenthesis around current cursor
|
| 18 |
+
const before = text.substring(0, selectionStart);
|
| 19 |
+
let beforeParen = before.lastIndexOf(OPEN);
|
| 20 |
+
if (beforeParen == -1) return false;
|
| 21 |
+
let beforeParenClose = before.lastIndexOf(CLOSE);
|
| 22 |
+
while (beforeParenClose !== -1 && beforeParenClose > beforeParen) {
|
| 23 |
+
beforeParen = before.lastIndexOf(OPEN, beforeParen - 1);
|
| 24 |
+
beforeParenClose = before.lastIndexOf(CLOSE, beforeParenClose - 1);
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
// Find closing parenthesis around current cursor
|
| 28 |
+
const after = text.substring(selectionStart);
|
| 29 |
+
let afterParen = after.indexOf(CLOSE);
|
| 30 |
+
if (afterParen == -1) return false;
|
| 31 |
+
let afterParenOpen = after.indexOf(OPEN);
|
| 32 |
+
while (afterParenOpen !== -1 && afterParen > afterParenOpen) {
|
| 33 |
+
afterParen = after.indexOf(CLOSE, afterParen + 1);
|
| 34 |
+
afterParenOpen = after.indexOf(OPEN, afterParenOpen + 1);
|
| 35 |
+
}
|
| 36 |
+
if (beforeParen === -1 || afterParen === -1) return false;
|
| 37 |
+
|
| 38 |
+
// Set the selection to the text between the parenthesis
|
| 39 |
+
const parenContent = text.substring(beforeParen + 1, selectionStart + afterParen);
|
| 40 |
+
const lastColon = parenContent.lastIndexOf(":");
|
| 41 |
+
selectionStart = beforeParen + 1;
|
| 42 |
+
selectionEnd = selectionStart + lastColon;
|
| 43 |
+
target.setSelectionRange(selectionStart, selectionEnd);
|
| 44 |
+
return true;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
// If the user hasn't selected anything, let's select their current parenthesis block
|
| 48 |
+
if(! selectCurrentParenthesisBlock('<', '>')){
|
| 49 |
+
selectCurrentParenthesisBlock('(', ')')
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
event.preventDefault();
|
| 53 |
+
|
| 54 |
+
closeCharacter = ')'
|
| 55 |
+
delta = opts.keyedit_precision_attention
|
| 56 |
+
|
| 57 |
+
if (selectionStart > 0 && text[selectionStart - 1] == '<'){
|
| 58 |
+
closeCharacter = '>'
|
| 59 |
+
delta = opts.keyedit_precision_extra
|
| 60 |
+
} else if (selectionStart == 0 || text[selectionStart - 1] != "(") {
|
| 61 |
+
|
| 62 |
+
// do not include spaces at the end
|
| 63 |
+
while(selectionEnd > selectionStart && text[selectionEnd-1] == ' '){
|
| 64 |
+
selectionEnd -= 1;
|
| 65 |
+
}
|
| 66 |
+
if(selectionStart == selectionEnd){
|
| 67 |
+
return
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
text = text.slice(0, selectionStart) + "(" + text.slice(selectionStart, selectionEnd) + ":1.0)" + text.slice(selectionEnd);
|
| 71 |
+
|
| 72 |
+
selectionStart += 1;
|
| 73 |
+
selectionEnd += 1;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
end = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1;
|
| 77 |
+
weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + 1 + end));
|
| 78 |
+
if (isNaN(weight)) return;
|
| 79 |
+
|
| 80 |
+
weight += isPlus ? delta : -delta;
|
| 81 |
+
weight = parseFloat(weight.toPrecision(12));
|
| 82 |
+
if(String(weight).length == 1) weight += ".0"
|
| 83 |
+
|
| 84 |
+
text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + 1 + end - 1);
|
| 85 |
+
|
| 86 |
+
target.focus();
|
| 87 |
+
target.value = text;
|
| 88 |
+
target.selectionStart = selectionStart;
|
| 89 |
+
target.selectionEnd = selectionEnd;
|
| 90 |
+
|
| 91 |
+
updateInput(target)
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
addEventListener('keydown', (event) => {
|
| 95 |
+
keyupEditAttention(event);
|
| 96 |
+
});
|
javascript/extensions.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
function extensions_apply(_, _, disable_all){
|
| 3 |
+
var disable = []
|
| 4 |
+
var update = []
|
| 5 |
+
|
| 6 |
+
gradioApp().querySelectorAll('#extensions input[type="checkbox"]').forEach(function(x){
|
| 7 |
+
if(x.name.startsWith("enable_") && ! x.checked)
|
| 8 |
+
disable.push(x.name.substr(7))
|
| 9 |
+
|
| 10 |
+
if(x.name.startsWith("update_") && x.checked)
|
| 11 |
+
update.push(x.name.substr(7))
|
| 12 |
+
})
|
| 13 |
+
|
| 14 |
+
restart_reload()
|
| 15 |
+
|
| 16 |
+
return [JSON.stringify(disable), JSON.stringify(update), disable_all]
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
function extensions_check(_, _){
|
| 20 |
+
var disable = []
|
| 21 |
+
|
| 22 |
+
gradioApp().querySelectorAll('#extensions input[type="checkbox"]').forEach(function(x){
|
| 23 |
+
if(x.name.startsWith("enable_") && ! x.checked)
|
| 24 |
+
disable.push(x.name.substr(7))
|
| 25 |
+
})
|
| 26 |
+
|
| 27 |
+
gradioApp().querySelectorAll('#extensions .extension_status').forEach(function(x){
|
| 28 |
+
x.innerHTML = "Loading..."
|
| 29 |
+
})
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
var id = randomId()
|
| 33 |
+
requestProgress(id, gradioApp().getElementById('extensions_installed_top'), null, function(){
|
| 34 |
+
|
| 35 |
+
})
|
| 36 |
+
|
| 37 |
+
return [id, JSON.stringify(disable)]
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
function install_extension_from_index(button, url){
|
| 41 |
+
button.disabled = "disabled"
|
| 42 |
+
button.value = "Installing..."
|
| 43 |
+
|
| 44 |
+
textarea = gradioApp().querySelector('#extension_to_install textarea')
|
| 45 |
+
textarea.value = url
|
| 46 |
+
updateInput(textarea)
|
| 47 |
+
|
| 48 |
+
gradioApp().querySelector('#install_extension_button').click()
|
| 49 |
+
}
|
javascript/extraNetworks.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
function setupExtraNetworksForTab(tabname){
|
| 3 |
+
gradioApp().querySelector('#'+tabname+'_extra_tabs').classList.add('extra-networks')
|
| 4 |
+
|
| 5 |
+
var tabs = gradioApp().querySelector('#'+tabname+'_extra_tabs > div')
|
| 6 |
+
var search = gradioApp().querySelector('#'+tabname+'_extra_search textarea')
|
| 7 |
+
var refresh = gradioApp().getElementById(tabname+'_extra_refresh')
|
| 8 |
+
|
| 9 |
+
search.classList.add('search')
|
| 10 |
+
tabs.appendChild(search)
|
| 11 |
+
tabs.appendChild(refresh)
|
| 12 |
+
|
| 13 |
+
search.addEventListener("input", function(evt){
|
| 14 |
+
searchTerm = search.value.toLowerCase()
|
| 15 |
+
|
| 16 |
+
gradioApp().querySelectorAll('#'+tabname+'_extra_tabs div.card').forEach(function(elem){
|
| 17 |
+
text = elem.querySelector('.name').textContent.toLowerCase() + " " + elem.querySelector('.search_term').textContent.toLowerCase()
|
| 18 |
+
elem.style.display = text.indexOf(searchTerm) == -1 ? "none" : ""
|
| 19 |
+
})
|
| 20 |
+
});
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
var activePromptTextarea = {};
|
| 24 |
+
|
| 25 |
+
function setupExtraNetworks(){
|
| 26 |
+
setupExtraNetworksForTab('txt2img')
|
| 27 |
+
setupExtraNetworksForTab('img2img')
|
| 28 |
+
|
| 29 |
+
function registerPrompt(tabname, id){
|
| 30 |
+
var textarea = gradioApp().querySelector("#" + id + " > label > textarea");
|
| 31 |
+
|
| 32 |
+
if (! activePromptTextarea[tabname]){
|
| 33 |
+
activePromptTextarea[tabname] = textarea
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
textarea.addEventListener("focus", function(){
|
| 37 |
+
activePromptTextarea[tabname] = textarea;
|
| 38 |
+
});
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
registerPrompt('txt2img', 'txt2img_prompt')
|
| 42 |
+
registerPrompt('txt2img', 'txt2img_neg_prompt')
|
| 43 |
+
registerPrompt('img2img', 'img2img_prompt')
|
| 44 |
+
registerPrompt('img2img', 'img2img_neg_prompt')
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
onUiLoaded(setupExtraNetworks)
|
| 48 |
+
|
| 49 |
+
var re_extranet = /<([^:]+:[^:]+):[\d\.]+>/;
|
| 50 |
+
var re_extranet_g = /\s+<([^:]+:[^:]+):[\d\.]+>/g;
|
| 51 |
+
|
| 52 |
+
function tryToRemoveExtraNetworkFromPrompt(textarea, text){
|
| 53 |
+
var m = text.match(re_extranet)
|
| 54 |
+
if(! m) return false
|
| 55 |
+
|
| 56 |
+
var partToSearch = m[1]
|
| 57 |
+
var replaced = false
|
| 58 |
+
var newTextareaText = textarea.value.replaceAll(re_extranet_g, function(found, index){
|
| 59 |
+
m = found.match(re_extranet);
|
| 60 |
+
if(m[1] == partToSearch){
|
| 61 |
+
replaced = true;
|
| 62 |
+
return ""
|
| 63 |
+
}
|
| 64 |
+
return found;
|
| 65 |
+
})
|
| 66 |
+
|
| 67 |
+
if(replaced){
|
| 68 |
+
textarea.value = newTextareaText
|
| 69 |
+
return true;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
return false
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
function cardClicked(tabname, textToAdd, allowNegativePrompt){
|
| 76 |
+
var textarea = allowNegativePrompt ? activePromptTextarea[tabname] : gradioApp().querySelector("#" + tabname + "_prompt > label > textarea")
|
| 77 |
+
|
| 78 |
+
if(! tryToRemoveExtraNetworkFromPrompt(textarea, textToAdd)){
|
| 79 |
+
textarea.value = textarea.value + opts.extra_networks_add_text_separator + textToAdd
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
updateInput(textarea)
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
function saveCardPreview(event, tabname, filename){
|
| 86 |
+
var textarea = gradioApp().querySelector("#" + tabname + '_preview_filename > label > textarea')
|
| 87 |
+
var button = gradioApp().getElementById(tabname + '_save_preview')
|
| 88 |
+
|
| 89 |
+
textarea.value = filename
|
| 90 |
+
updateInput(textarea)
|
| 91 |
+
|
| 92 |
+
button.click()
|
| 93 |
+
|
| 94 |
+
event.stopPropagation()
|
| 95 |
+
event.preventDefault()
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
function extraNetworksSearchButton(tabs_id, event){
|
| 99 |
+
searchTextarea = gradioApp().querySelector("#" + tabs_id + ' > div > textarea')
|
| 100 |
+
button = event.target
|
| 101 |
+
text = button.classList.contains("search-all") ? "" : button.textContent.trim()
|
| 102 |
+
|
| 103 |
+
searchTextarea.value = text
|
| 104 |
+
updateInput(searchTextarea)
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
var globalPopup = null;
|
| 108 |
+
var globalPopupInner = null;
|
| 109 |
+
function popup(contents){
|
| 110 |
+
if(! globalPopup){
|
| 111 |
+
globalPopup = document.createElement('div')
|
| 112 |
+
globalPopup.onclick = function(){ globalPopup.style.display = "none"; };
|
| 113 |
+
globalPopup.classList.add('global-popup');
|
| 114 |
+
|
| 115 |
+
var close = document.createElement('div')
|
| 116 |
+
close.classList.add('global-popup-close');
|
| 117 |
+
close.onclick = function(){ globalPopup.style.display = "none"; };
|
| 118 |
+
close.title = "Close";
|
| 119 |
+
globalPopup.appendChild(close)
|
| 120 |
+
|
| 121 |
+
globalPopupInner = document.createElement('div')
|
| 122 |
+
globalPopupInner.onclick = function(event){ event.stopPropagation(); return false; };
|
| 123 |
+
globalPopupInner.classList.add('global-popup-inner');
|
| 124 |
+
globalPopup.appendChild(globalPopupInner)
|
| 125 |
+
|
| 126 |
+
gradioApp().appendChild(globalPopup);
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
globalPopupInner.innerHTML = '';
|
| 130 |
+
globalPopupInner.appendChild(contents);
|
| 131 |
+
|
| 132 |
+
globalPopup.style.display = "flex";
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
function extraNetworksShowMetadata(text){
|
| 136 |
+
elem = document.createElement('pre')
|
| 137 |
+
elem.classList.add('popup-metadata');
|
| 138 |
+
elem.textContent = text;
|
| 139 |
+
|
| 140 |
+
popup(elem);
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
function requestGet(url, data, handler, errorHandler){
|
| 144 |
+
var xhr = new XMLHttpRequest();
|
| 145 |
+
var args = Object.keys(data).map(function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) }).join('&')
|
| 146 |
+
xhr.open("GET", url + "?" + args, true);
|
| 147 |
+
|
| 148 |
+
xhr.onreadystatechange = function () {
|
| 149 |
+
if (xhr.readyState === 4) {
|
| 150 |
+
if (xhr.status === 200) {
|
| 151 |
+
try {
|
| 152 |
+
var js = JSON.parse(xhr.responseText);
|
| 153 |
+
handler(js)
|
| 154 |
+
} catch (error) {
|
| 155 |
+
console.error(error);
|
| 156 |
+
errorHandler()
|
| 157 |
+
}
|
| 158 |
+
} else{
|
| 159 |
+
errorHandler()
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
};
|
| 163 |
+
var js = JSON.stringify(data);
|
| 164 |
+
xhr.send(js);
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
function extraNetworksRequestMetadata(event, extraPage, cardName){
|
| 168 |
+
showError = function(){ extraNetworksShowMetadata("there was an error getting metadata"); }
|
| 169 |
+
|
| 170 |
+
requestGet("./sd_extra_networks/metadata", {"page": extraPage, "item": cardName}, function(data){
|
| 171 |
+
if(data && data.metadata){
|
| 172 |
+
extraNetworksShowMetadata(data.metadata)
|
| 173 |
+
} else{
|
| 174 |
+
showError()
|
| 175 |
+
}
|
| 176 |
+
}, showError)
|
| 177 |
+
|
| 178 |
+
event.stopPropagation()
|
| 179 |
+
}
|
javascript/generationParams.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// attaches listeners to the txt2img and img2img galleries to update displayed generation param text when the image changes
|
| 2 |
+
|
| 3 |
+
let txt2img_gallery, img2img_gallery, modal = undefined;
|
| 4 |
+
onUiUpdate(function(){
|
| 5 |
+
if (!txt2img_gallery) {
|
| 6 |
+
txt2img_gallery = attachGalleryListeners("txt2img")
|
| 7 |
+
}
|
| 8 |
+
if (!img2img_gallery) {
|
| 9 |
+
img2img_gallery = attachGalleryListeners("img2img")
|
| 10 |
+
}
|
| 11 |
+
if (!modal) {
|
| 12 |
+
modal = gradioApp().getElementById('lightboxModal')
|
| 13 |
+
modalObserver.observe(modal, { attributes : true, attributeFilter : ['style'] });
|
| 14 |
+
}
|
| 15 |
+
});
|
| 16 |
+
|
| 17 |
+
let modalObserver = new MutationObserver(function(mutations) {
|
| 18 |
+
mutations.forEach(function(mutationRecord) {
|
| 19 |
+
let selectedTab = gradioApp().querySelector('#tabs div button.bg-white')?.innerText
|
| 20 |
+
if (mutationRecord.target.style.display === 'none' && selectedTab === 'txt2img' || selectedTab === 'img2img')
|
| 21 |
+
gradioApp().getElementById(selectedTab+"_generation_info_button").click()
|
| 22 |
+
});
|
| 23 |
+
});
|
| 24 |
+
|
| 25 |
+
function attachGalleryListeners(tab_name) {
|
| 26 |
+
gallery = gradioApp().querySelector('#'+tab_name+'_gallery')
|
| 27 |
+
gallery?.addEventListener('click', () => gradioApp().getElementById(tab_name+"_generation_info_button").click());
|
| 28 |
+
gallery?.addEventListener('keydown', (e) => {
|
| 29 |
+
if (e.keyCode == 37 || e.keyCode == 39) // left or right arrow
|
| 30 |
+
gradioApp().getElementById(tab_name+"_generation_info_button").click()
|
| 31 |
+
});
|
| 32 |
+
return gallery;
|
| 33 |
+
}
|
javascript/hints.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 higher than 30-40 does not help",
|
| 8 |
+
"DDIM": "Denoising Diffusion Implicit Models - best at inpainting",
|
| 9 |
+
"UniPC": "Unified Predictor-Corrector Framework for Fast Sampling of Diffusion Models",
|
| 10 |
+
"DPM adaptive": "Ignores step count - uses a number of steps determined by the CFG and resolution",
|
| 11 |
+
|
| 12 |
+
"Batch count": "How many batches of images to create (has no impact on generation performance or VRAM usage)",
|
| 13 |
+
"Batch size": "How many image to create in a single batch (increases generation performance at cost of higher VRAM usage)",
|
| 14 |
+
"CFG Scale": "Classifier Free Guidance Scale - how strongly the image should conform to prompt - lower values produce more creative results",
|
| 15 |
+
"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",
|
| 16 |
+
"\u{1f3b2}\ufe0f": "Set seed to -1, which will cause a new random number to be used every time",
|
| 17 |
+
"\u267b\ufe0f": "Reuse seed from last generation, mostly useful if it was randomed",
|
| 18 |
+
"\u2199\ufe0f": "Read generation parameters from prompt or last generation if prompt is empty into user interface.",
|
| 19 |
+
"\u{1f4c2}": "Open images output directory",
|
| 20 |
+
"\u{1f4be}": "Save style",
|
| 21 |
+
"\u{1f5d1}\ufe0f": "Clear prompt",
|
| 22 |
+
"\u{1f4cb}": "Apply selected styles to current prompt",
|
| 23 |
+
"\u{1f4d2}": "Paste available values into the field",
|
| 24 |
+
"\u{1f3b4}": "Show/hide extra networks",
|
| 25 |
+
|
| 26 |
+
"Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt",
|
| 27 |
+
"SD upscale": "Upscale image normally, split result into tiles, improve each tile using img2img, merge whole image back",
|
| 28 |
+
|
| 29 |
+
"Just resize": "Resize image to target resolution. Unless height and width match, you will get incorrect aspect ratio.",
|
| 30 |
+
"Crop and resize": "Resize the image so that entirety of target resolution is filled with the image. Crop parts that stick out.",
|
| 31 |
+
"Resize and fill": "Resize the image so that entirety of image is inside target resolution. Fill empty space with image's colors.",
|
| 32 |
+
|
| 33 |
+
"Mask blur": "How much to blur the mask before processing, in pixels.",
|
| 34 |
+
"Masked content": "What to put inside the masked area before processing it with Stable Diffusion.",
|
| 35 |
+
"fill": "fill it with colors of the image",
|
| 36 |
+
"original": "keep whatever was there originally",
|
| 37 |
+
"latent noise": "fill it with latent space noise",
|
| 38 |
+
"latent nothing": "fill it with latent space zeroes",
|
| 39 |
+
"Inpaint at full resolution": "Upscale masked region to target resolution, do inpainting, downscale back and paste into original image",
|
| 40 |
+
|
| 41 |
+
"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.",
|
| 42 |
+
|
| 43 |
+
"Skip": "Stop processing current image and continue processing.",
|
| 44 |
+
"Interrupt": "Stop processing images and return any results accumulated so far.",
|
| 45 |
+
"Save": "Write image to a directory (default - log/images) and generation parameters into csv file.",
|
| 46 |
+
|
| 47 |
+
"X values": "Separate values for X axis using commas.",
|
| 48 |
+
"Y values": "Separate values for Y axis using commas.",
|
| 49 |
+
|
| 50 |
+
"None": "Do not do anything special",
|
| 51 |
+
"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)",
|
| 52 |
+
"X/Y/Z plot": "Create grid(s) where images will have different parameters. Use inputs below to specify which parameters will be shared by columns and rows",
|
| 53 |
+
"Custom code": "Run Python code. Advanced user only. Must run program with --allow-code for this to work",
|
| 54 |
+
|
| 55 |
+
"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",
|
| 56 |
+
"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",
|
| 57 |
+
|
| 58 |
+
"Tiling": "Produce an image that can be tiled.",
|
| 59 |
+
"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.",
|
| 60 |
+
|
| 61 |
+
"Variation seed": "Seed of a different picture to be mixed into the generation.",
|
| 62 |
+
"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).",
|
| 63 |
+
"Resize seed from height": "Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution",
|
| 64 |
+
"Resize seed from width": "Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution",
|
| 65 |
+
|
| 66 |
+
"Interrogate": "Reconstruct prompt from existing image and put it into the prompt field.",
|
| 67 |
+
|
| 68 |
+
"Images filename pattern": "Use following tags to define how filenames for images are chosen: [steps], [cfg], [prompt_hash], [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.",
|
| 69 |
+
"Directory name pattern": "Use following tags to define how subdirectories for images and grids are chosen: [steps], [cfg],[prompt_hash], [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.",
|
| 70 |
+
"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",
|
| 71 |
+
|
| 72 |
+
"Loopback": "Performs img2img processing multiple times. Output images are used as input for the next loop.",
|
| 73 |
+
"Loops": "How many times to process an image. Each output is used as the input of the next loop. If set to 1, behavior will be as if this script were not used.",
|
| 74 |
+
"Final denoising strength": "The denoising strength for the final loop of each image in the batch.",
|
| 75 |
+
"Denoising strength curve": "The denoising curve controls the rate of denoising strength change each loop. Aggressive: Most of the change will happen towards the start of the loops. Linear: Change will be constant through all loops. Lazy: Most of the change will happen towards the end of the loops.",
|
| 76 |
+
|
| 77 |
+
"Style 1": "Style to apply; styles have components for both positive and negative prompts and apply to both",
|
| 78 |
+
"Style 2": "Style to apply; styles have components for both positive and negative prompts and apply to both",
|
| 79 |
+
"Apply style": "Insert selected styles into prompt fields",
|
| 80 |
+
"Create style": "Save current prompts as a style. If you add the token {prompt} to the text, the style uses that as a placeholder for your prompt when you use the style in the future.",
|
| 81 |
+
|
| 82 |
+
"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.",
|
| 83 |
+
"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.",
|
| 84 |
+
|
| 85 |
+
"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%).",
|
| 86 |
+
|
| 87 |
+
"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.",
|
| 88 |
+
"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.",
|
| 89 |
+
|
| 90 |
+
"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.",
|
| 91 |
+
"Filename join string": "This string will be used to join split words into a single line if the option above is enabled.",
|
| 92 |
+
|
| 93 |
+
"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.",
|
| 94 |
+
|
| 95 |
+
"Weighted sum": "Result = A * (1 - M) + B * M",
|
| 96 |
+
"Add difference": "Result = A + (B - C) * M",
|
| 97 |
+
"No interpolation": "Result = A",
|
| 98 |
+
|
| 99 |
+
"Initialization text": "If the number of tokens is more than the number of vectors, some may be skipped.\nLeave the textbox empty to start with zeroed out vectors",
|
| 100 |
+
"Learning rate": "How fast should 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.",
|
| 101 |
+
|
| 102 |
+
"Clip skip": "Early stopping parameter for CLIP model; 1 is stop at last layer as usual, 2 is stop at penultimate layer, etc.",
|
| 103 |
+
|
| 104 |
+
"Approx NN": "Cheap neural network approximation. Very fast compared to VAE, but produces pictures with 4 times smaller horizontal/vertical resolution and lower quality.",
|
| 105 |
+
"Approx cheap": "Very cheap approximation. Very fast compared to VAE, but produces pictures with 8 times smaller horizontal/vertical resolution and extremely low quality.",
|
| 106 |
+
|
| 107 |
+
"Hires. fix": "Use a two step process to partially create an image at smaller resolution, upscale, and then improve details in it without changing composition",
|
| 108 |
+
"Hires steps": "Number of sampling steps for upscaled picture. If 0, uses same as for original.",
|
| 109 |
+
"Upscale by": "Adjusts the size of the image by multiplying the original width and height by the selected value. Ignored if either Resize width to or Resize height to are non-zero.",
|
| 110 |
+
"Resize width to": "Resizes image to this width. If 0, width is inferred from either of two nearby sliders.",
|
| 111 |
+
"Resize height to": "Resizes image to this height. If 0, height is inferred from either of two nearby sliders.",
|
| 112 |
+
"Multiplier for extra networks": "When adding extra network such as Hypernetwork or Lora to prompt, use this multiplier for it.",
|
| 113 |
+
"Discard weights with matching name": "Regular expression; if weights's name matches it, the weights is not written to the resulting checkpoint. Use ^model_ema to discard EMA weights.",
|
| 114 |
+
"Extra networks tab order": "Comma-separated list of tab names; tabs listed here will appear in the extra networks UI first and in order lsited."
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
onUiUpdate(function(){
|
| 119 |
+
gradioApp().querySelectorAll('span, button, select, p').forEach(function(span){
|
| 120 |
+
tooltip = titles[span.textContent];
|
| 121 |
+
|
| 122 |
+
if(!tooltip){
|
| 123 |
+
tooltip = titles[span.value];
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
if(!tooltip){
|
| 127 |
+
for (const c of span.classList) {
|
| 128 |
+
if (c in titles) {
|
| 129 |
+
tooltip = titles[c];
|
| 130 |
+
break;
|
| 131 |
+
}
|
| 132 |
+
}
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
if(tooltip){
|
| 136 |
+
span.title = tooltip;
|
| 137 |
+
}
|
| 138 |
+
})
|
| 139 |
+
|
| 140 |
+
gradioApp().querySelectorAll('select').forEach(function(select){
|
| 141 |
+
if (select.onchange != null) return;
|
| 142 |
+
|
| 143 |
+
select.onchange = function(){
|
| 144 |
+
select.title = titles[select.value] || "";
|
| 145 |
+
}
|
| 146 |
+
})
|
| 147 |
+
})
|
javascript/hires_fix.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
function setInactive(elem, inactive){
|
| 3 |
+
if(inactive){
|
| 4 |
+
elem.classList.add('inactive')
|
| 5 |
+
} else{
|
| 6 |
+
elem.classList.remove('inactive')
|
| 7 |
+
}
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
function onCalcResolutionHires(enable, width, height, hr_scale, hr_resize_x, hr_resize_y){
|
| 11 |
+
hrUpscaleBy = gradioApp().getElementById('txt2img_hr_scale')
|
| 12 |
+
hrResizeX = gradioApp().getElementById('txt2img_hr_resize_x')
|
| 13 |
+
hrResizeY = gradioApp().getElementById('txt2img_hr_resize_y')
|
| 14 |
+
|
| 15 |
+
gradioApp().getElementById('txt2img_hires_fix_row2').style.display = opts.use_old_hires_fix_width_height ? "none" : ""
|
| 16 |
+
|
| 17 |
+
setInactive(hrUpscaleBy, opts.use_old_hires_fix_width_height || hr_resize_x > 0 || hr_resize_y > 0)
|
| 18 |
+
setInactive(hrResizeX, opts.use_old_hires_fix_width_height || hr_resize_x == 0)
|
| 19 |
+
setInactive(hrResizeY, opts.use_old_hires_fix_width_height || hr_resize_y == 0)
|
| 20 |
+
|
| 21 |
+
return [enable, width, height, hr_scale, hr_resize_x, hr_resize_y]
|
| 22 |
+
}
|
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 = `0px`;
|
| 35 |
+
wrapper.style.top = `0px`;
|
| 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/imageParams.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
window.onload = (function(){
|
| 2 |
+
window.addEventListener('drop', e => {
|
| 3 |
+
const target = e.composedPath()[0];
|
| 4 |
+
const idx = selected_gallery_index();
|
| 5 |
+
if (target.placeholder.indexOf("Prompt") == -1) return;
|
| 6 |
+
|
| 7 |
+
let prompt_target = get_tab_index('tabs') == 1 ? "img2img_prompt_image" : "txt2img_prompt_image";
|
| 8 |
+
|
| 9 |
+
e.stopPropagation();
|
| 10 |
+
e.preventDefault();
|
| 11 |
+
const imgParent = gradioApp().getElementById(prompt_target);
|
| 12 |
+
const files = e.dataTransfer.files;
|
| 13 |
+
const fileInput = imgParent.querySelector('input[type="file"]');
|
| 14 |
+
if ( fileInput ) {
|
| 15 |
+
fileInput.files = files;
|
| 16 |
+
fileInput.dispatchEvent(new Event('change'));
|
| 17 |
+
}
|
| 18 |
+
});
|
| 19 |
+
});
|
javascript/imageviewer.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// A full size 'lightbox' preview modal shown when left clicking on gallery previews
|
| 2 |
+
function closeModal() {
|
| 3 |
+
gradioApp().getElementById("lightboxModal").style.display = "none";
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
function showModal(event) {
|
| 7 |
+
const source = event.target || event.srcElement;
|
| 8 |
+
const modalImage = gradioApp().getElementById("modalImage")
|
| 9 |
+
const lb = gradioApp().getElementById("lightboxModal")
|
| 10 |
+
modalImage.src = source.src
|
| 11 |
+
if (modalImage.style.display === 'none') {
|
| 12 |
+
lb.style.setProperty('background-image', 'url(' + source.src + ')');
|
| 13 |
+
}
|
| 14 |
+
lb.style.display = "flex";
|
| 15 |
+
lb.focus()
|
| 16 |
+
|
| 17 |
+
const tabTxt2Img = gradioApp().getElementById("tab_txt2img")
|
| 18 |
+
const tabImg2Img = gradioApp().getElementById("tab_img2img")
|
| 19 |
+
// show the save button in modal only on txt2img or img2img tabs
|
| 20 |
+
if (tabTxt2Img.style.display != "none" || tabImg2Img.style.display != "none") {
|
| 21 |
+
gradioApp().getElementById("modal_save").style.display = "inline"
|
| 22 |
+
} else {
|
| 23 |
+
gradioApp().getElementById("modal_save").style.display = "none"
|
| 24 |
+
}
|
| 25 |
+
event.stopPropagation()
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
function negmod(n, m) {
|
| 29 |
+
return ((n % m) + m) % m;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
function updateOnBackgroundChange() {
|
| 33 |
+
const modalImage = gradioApp().getElementById("modalImage")
|
| 34 |
+
if (modalImage && modalImage.offsetParent) {
|
| 35 |
+
let currentButton = selected_gallery_button();
|
| 36 |
+
|
| 37 |
+
if (currentButton?.children?.length > 0 && modalImage.src != currentButton.children[0].src) {
|
| 38 |
+
modalImage.src = currentButton.children[0].src;
|
| 39 |
+
if (modalImage.style.display === 'none') {
|
| 40 |
+
modal.style.setProperty('background-image', `url(${modalImage.src})`)
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
function modalImageSwitch(offset) {
|
| 47 |
+
var galleryButtons = all_gallery_buttons();
|
| 48 |
+
|
| 49 |
+
if (galleryButtons.length > 1) {
|
| 50 |
+
var currentButton = selected_gallery_button();
|
| 51 |
+
|
| 52 |
+
var result = -1
|
| 53 |
+
galleryButtons.forEach(function(v, i) {
|
| 54 |
+
if (v == currentButton) {
|
| 55 |
+
result = i
|
| 56 |
+
}
|
| 57 |
+
})
|
| 58 |
+
|
| 59 |
+
if (result != -1) {
|
| 60 |
+
nextButton = galleryButtons[negmod((result + offset), galleryButtons.length)]
|
| 61 |
+
nextButton.click()
|
| 62 |
+
const modalImage = gradioApp().getElementById("modalImage");
|
| 63 |
+
const modal = gradioApp().getElementById("lightboxModal");
|
| 64 |
+
modalImage.src = nextButton.children[0].src;
|
| 65 |
+
if (modalImage.style.display === 'none') {
|
| 66 |
+
modal.style.setProperty('background-image', `url(${modalImage.src})`)
|
| 67 |
+
}
|
| 68 |
+
setTimeout(function() {
|
| 69 |
+
modal.focus()
|
| 70 |
+
}, 10)
|
| 71 |
+
}
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
function saveImage(){
|
| 76 |
+
const tabTxt2Img = gradioApp().getElementById("tab_txt2img")
|
| 77 |
+
const tabImg2Img = gradioApp().getElementById("tab_img2img")
|
| 78 |
+
const saveTxt2Img = "save_txt2img"
|
| 79 |
+
const saveImg2Img = "save_img2img"
|
| 80 |
+
if (tabTxt2Img.style.display != "none") {
|
| 81 |
+
gradioApp().getElementById(saveTxt2Img).click()
|
| 82 |
+
} else if (tabImg2Img.style.display != "none") {
|
| 83 |
+
gradioApp().getElementById(saveImg2Img).click()
|
| 84 |
+
} else {
|
| 85 |
+
console.error("missing implementation for saving modal of this type")
|
| 86 |
+
}
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
function modalSaveImage(event) {
|
| 90 |
+
saveImage()
|
| 91 |
+
event.stopPropagation()
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
function modalNextImage(event) {
|
| 95 |
+
modalImageSwitch(1)
|
| 96 |
+
event.stopPropagation()
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
function modalPrevImage(event) {
|
| 100 |
+
modalImageSwitch(-1)
|
| 101 |
+
event.stopPropagation()
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
function modalKeyHandler(event) {
|
| 105 |
+
switch (event.key) {
|
| 106 |
+
case "s":
|
| 107 |
+
saveImage()
|
| 108 |
+
break;
|
| 109 |
+
case "ArrowLeft":
|
| 110 |
+
modalPrevImage(event)
|
| 111 |
+
break;
|
| 112 |
+
case "ArrowRight":
|
| 113 |
+
modalNextImage(event)
|
| 114 |
+
break;
|
| 115 |
+
case "Escape":
|
| 116 |
+
closeModal();
|
| 117 |
+
break;
|
| 118 |
+
}
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
function setupImageForLightbox(e) {
|
| 122 |
+
if (e.dataset.modded)
|
| 123 |
+
return;
|
| 124 |
+
|
| 125 |
+
e.dataset.modded = true;
|
| 126 |
+
e.style.cursor='pointer'
|
| 127 |
+
e.style.userSelect='none'
|
| 128 |
+
|
| 129 |
+
var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1
|
| 130 |
+
|
| 131 |
+
// For Firefox, listening on click first switched to next image then shows the lightbox.
|
| 132 |
+
// If you know how to fix this without switching to mousedown event, please.
|
| 133 |
+
// For other browsers the event is click to make it possiblr to drag picture.
|
| 134 |
+
var event = isFirefox ? 'mousedown' : 'click'
|
| 135 |
+
|
| 136 |
+
e.addEventListener(event, function (evt) {
|
| 137 |
+
if(!opts.js_modal_lightbox || evt.button != 0) return;
|
| 138 |
+
|
| 139 |
+
modalZoomSet(gradioApp().getElementById('modalImage'), opts.js_modal_lightbox_initially_zoomed)
|
| 140 |
+
evt.preventDefault()
|
| 141 |
+
showModal(evt)
|
| 142 |
+
}, true);
|
| 143 |
+
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
function modalZoomSet(modalImage, enable) {
|
| 147 |
+
if (enable) {
|
| 148 |
+
modalImage.classList.add('modalImageFullscreen');
|
| 149 |
+
} else {
|
| 150 |
+
modalImage.classList.remove('modalImageFullscreen');
|
| 151 |
+
}
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
function modalZoomToggle(event) {
|
| 155 |
+
modalImage = gradioApp().getElementById("modalImage");
|
| 156 |
+
modalZoomSet(modalImage, !modalImage.classList.contains('modalImageFullscreen'))
|
| 157 |
+
event.stopPropagation()
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
function modalTileImageToggle(event) {
|
| 161 |
+
const modalImage = gradioApp().getElementById("modalImage");
|
| 162 |
+
const modal = gradioApp().getElementById("lightboxModal");
|
| 163 |
+
const isTiling = modalImage.style.display === 'none';
|
| 164 |
+
if (isTiling) {
|
| 165 |
+
modalImage.style.display = 'block';
|
| 166 |
+
modal.style.setProperty('background-image', 'none')
|
| 167 |
+
} else {
|
| 168 |
+
modalImage.style.display = 'none';
|
| 169 |
+
modal.style.setProperty('background-image', `url(${modalImage.src})`)
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
event.stopPropagation()
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
function galleryImageHandler(e) {
|
| 176 |
+
//if (e && e.parentElement.tagName == 'BUTTON') {
|
| 177 |
+
e.onclick = showGalleryImage;
|
| 178 |
+
//}
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
onUiUpdate(function() {
|
| 182 |
+
fullImg_preview = gradioApp().querySelectorAll('.gradio-gallery > div > img')
|
| 183 |
+
if (fullImg_preview != null) {
|
| 184 |
+
fullImg_preview.forEach(setupImageForLightbox);
|
| 185 |
+
}
|
| 186 |
+
updateOnBackgroundChange();
|
| 187 |
+
})
|
| 188 |
+
|
| 189 |
+
document.addEventListener("DOMContentLoaded", function() {
|
| 190 |
+
//const modalFragment = document.createDocumentFragment();
|
| 191 |
+
const modal = document.createElement('div')
|
| 192 |
+
modal.onclick = closeModal;
|
| 193 |
+
modal.id = "lightboxModal";
|
| 194 |
+
modal.tabIndex = 0
|
| 195 |
+
modal.addEventListener('keydown', modalKeyHandler, true)
|
| 196 |
+
|
| 197 |
+
const modalControls = document.createElement('div')
|
| 198 |
+
modalControls.className = 'modalControls gradio-container';
|
| 199 |
+
modal.append(modalControls);
|
| 200 |
+
|
| 201 |
+
const modalZoom = document.createElement('span')
|
| 202 |
+
modalZoom.className = 'modalZoom cursor';
|
| 203 |
+
modalZoom.innerHTML = '⤡'
|
| 204 |
+
modalZoom.addEventListener('click', modalZoomToggle, true)
|
| 205 |
+
modalZoom.title = "Toggle zoomed view";
|
| 206 |
+
modalControls.appendChild(modalZoom)
|
| 207 |
+
|
| 208 |
+
const modalTileImage = document.createElement('span')
|
| 209 |
+
modalTileImage.className = 'modalTileImage cursor';
|
| 210 |
+
modalTileImage.innerHTML = '⊞'
|
| 211 |
+
modalTileImage.addEventListener('click', modalTileImageToggle, true)
|
| 212 |
+
modalTileImage.title = "Preview tiling";
|
| 213 |
+
modalControls.appendChild(modalTileImage)
|
| 214 |
+
|
| 215 |
+
const modalSave = document.createElement("span")
|
| 216 |
+
modalSave.className = "modalSave cursor"
|
| 217 |
+
modalSave.id = "modal_save"
|
| 218 |
+
modalSave.innerHTML = "🖫"
|
| 219 |
+
modalSave.addEventListener("click", modalSaveImage, true)
|
| 220 |
+
modalSave.title = "Save Image(s)"
|
| 221 |
+
modalControls.appendChild(modalSave)
|
| 222 |
+
|
| 223 |
+
const modalClose = document.createElement('span')
|
| 224 |
+
modalClose.className = 'modalClose cursor';
|
| 225 |
+
modalClose.innerHTML = '×'
|
| 226 |
+
modalClose.onclick = closeModal;
|
| 227 |
+
modalClose.title = "Close image viewer";
|
| 228 |
+
modalControls.appendChild(modalClose)
|
| 229 |
+
|
| 230 |
+
const modalImage = document.createElement('img')
|
| 231 |
+
modalImage.id = 'modalImage';
|
| 232 |
+
modalImage.onclick = closeModal;
|
| 233 |
+
modalImage.tabIndex = 0
|
| 234 |
+
modalImage.addEventListener('keydown', modalKeyHandler, true)
|
| 235 |
+
modal.appendChild(modalImage)
|
| 236 |
+
|
| 237 |
+
const modalPrev = document.createElement('a')
|
| 238 |
+
modalPrev.className = 'modalPrev';
|
| 239 |
+
modalPrev.innerHTML = '❮'
|
| 240 |
+
modalPrev.tabIndex = 0
|
| 241 |
+
modalPrev.addEventListener('click', modalPrevImage, true);
|
| 242 |
+
modalPrev.addEventListener('keydown', modalKeyHandler, true)
|
| 243 |
+
modal.appendChild(modalPrev)
|
| 244 |
+
|
| 245 |
+
const modalNext = document.createElement('a')
|
| 246 |
+
modalNext.className = 'modalNext';
|
| 247 |
+
modalNext.innerHTML = '❯'
|
| 248 |
+
modalNext.tabIndex = 0
|
| 249 |
+
modalNext.addEventListener('click', modalNextImage, true);
|
| 250 |
+
modalNext.addEventListener('keydown', modalKeyHandler, true)
|
| 251 |
+
|
| 252 |
+
modal.appendChild(modalNext)
|
| 253 |
+
|
| 254 |
+
gradioApp().appendChild(modal)
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
document.body.appendChild(modal);
|
| 258 |
+
|
| 259 |
+
});
|
javascript/localization.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
// localization = {} -- the dict with translations is created by the backend
|
| 3 |
+
|
| 4 |
+
ignore_ids_for_localization={
|
| 5 |
+
setting_sd_hypernetwork: 'OPTION',
|
| 6 |
+
setting_sd_model_checkpoint: 'OPTION',
|
| 7 |
+
setting_realesrgan_enabled_models: 'OPTION',
|
| 8 |
+
modelmerger_primary_model_name: 'OPTION',
|
| 9 |
+
modelmerger_secondary_model_name: 'OPTION',
|
| 10 |
+
modelmerger_tertiary_model_name: 'OPTION',
|
| 11 |
+
train_embedding: 'OPTION',
|
| 12 |
+
train_hypernetwork: 'OPTION',
|
| 13 |
+
txt2img_styles: 'OPTION',
|
| 14 |
+
img2img_styles: 'OPTION',
|
| 15 |
+
setting_random_artist_categories: 'SPAN',
|
| 16 |
+
setting_face_restoration_model: 'SPAN',
|
| 17 |
+
setting_realesrgan_enabled_models: 'SPAN',
|
| 18 |
+
extras_upscaler_1: 'SPAN',
|
| 19 |
+
extras_upscaler_2: 'SPAN',
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
re_num = /^[\.\d]+$/
|
| 23 |
+
re_emoji = /[\p{Extended_Pictographic}\u{1F3FB}-\u{1F3FF}\u{1F9B0}-\u{1F9B3}]/u
|
| 24 |
+
|
| 25 |
+
original_lines = {}
|
| 26 |
+
translated_lines = {}
|
| 27 |
+
|
| 28 |
+
function textNodesUnder(el){
|
| 29 |
+
var n, a=[], walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false);
|
| 30 |
+
while(n=walk.nextNode()) a.push(n);
|
| 31 |
+
return a;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
function canBeTranslated(node, text){
|
| 35 |
+
if(! text) return false;
|
| 36 |
+
if(! node.parentElement) return false;
|
| 37 |
+
|
| 38 |
+
parentType = node.parentElement.nodeName
|
| 39 |
+
if(parentType=='SCRIPT' || parentType=='STYLE' || parentType=='TEXTAREA') return false;
|
| 40 |
+
|
| 41 |
+
if (parentType=='OPTION' || parentType=='SPAN'){
|
| 42 |
+
pnode = node
|
| 43 |
+
for(var level=0; level<4; level++){
|
| 44 |
+
pnode = pnode.parentElement
|
| 45 |
+
if(! pnode) break;
|
| 46 |
+
|
| 47 |
+
if(ignore_ids_for_localization[pnode.id] == parentType) return false;
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
if(re_num.test(text)) return false;
|
| 52 |
+
if(re_emoji.test(text)) return false;
|
| 53 |
+
return true
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
function getTranslation(text){
|
| 57 |
+
if(! text) return undefined
|
| 58 |
+
|
| 59 |
+
if(translated_lines[text] === undefined){
|
| 60 |
+
original_lines[text] = 1
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
tl = localization[text]
|
| 64 |
+
if(tl !== undefined){
|
| 65 |
+
translated_lines[tl] = 1
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
return tl
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
function processTextNode(node){
|
| 72 |
+
text = node.textContent.trim()
|
| 73 |
+
|
| 74 |
+
if(! canBeTranslated(node, text)) return
|
| 75 |
+
|
| 76 |
+
tl = getTranslation(text)
|
| 77 |
+
if(tl !== undefined){
|
| 78 |
+
node.textContent = tl
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
function processNode(node){
|
| 83 |
+
if(node.nodeType == 3){
|
| 84 |
+
processTextNode(node)
|
| 85 |
+
return
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
if(node.title){
|
| 89 |
+
tl = getTranslation(node.title)
|
| 90 |
+
if(tl !== undefined){
|
| 91 |
+
node.title = tl
|
| 92 |
+
}
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
if(node.placeholder){
|
| 96 |
+
tl = getTranslation(node.placeholder)
|
| 97 |
+
if(tl !== undefined){
|
| 98 |
+
node.placeholder = tl
|
| 99 |
+
}
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
textNodesUnder(node).forEach(function(node){
|
| 103 |
+
processTextNode(node)
|
| 104 |
+
})
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
function dumpTranslations(){
|
| 108 |
+
dumped = {}
|
| 109 |
+
if (localization.rtl) {
|
| 110 |
+
dumped.rtl = true
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
Object.keys(original_lines).forEach(function(text){
|
| 114 |
+
if(dumped[text] !== undefined) return
|
| 115 |
+
|
| 116 |
+
dumped[text] = localization[text] || text
|
| 117 |
+
})
|
| 118 |
+
|
| 119 |
+
return dumped
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
onUiUpdate(function(m){
|
| 123 |
+
m.forEach(function(mutation){
|
| 124 |
+
mutation.addedNodes.forEach(function(node){
|
| 125 |
+
processNode(node)
|
| 126 |
+
})
|
| 127 |
+
});
|
| 128 |
+
})
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
document.addEventListener("DOMContentLoaded", function() {
|
| 132 |
+
processNode(gradioApp())
|
| 133 |
+
|
| 134 |
+
if (localization.rtl) { // if the language is from right to left,
|
| 135 |
+
(new MutationObserver((mutations, observer) => { // wait for the style to load
|
| 136 |
+
mutations.forEach(mutation => {
|
| 137 |
+
mutation.addedNodes.forEach(node => {
|
| 138 |
+
if (node.tagName === 'STYLE') {
|
| 139 |
+
observer.disconnect();
|
| 140 |
+
|
| 141 |
+
for (const x of node.sheet.rules) { // find all rtl media rules
|
| 142 |
+
if (Array.from(x.media || []).includes('rtl')) {
|
| 143 |
+
x.media.appendMedium('all'); // enable them
|
| 144 |
+
}
|
| 145 |
+
}
|
| 146 |
+
}
|
| 147 |
+
})
|
| 148 |
+
});
|
| 149 |
+
})).observe(gradioApp(), { childList: true });
|
| 150 |
+
}
|
| 151 |
+
})
|
| 152 |
+
|
| 153 |
+
function download_localization() {
|
| 154 |
+
text = JSON.stringify(dumpTranslations(), null, 4)
|
| 155 |
+
|
| 156 |
+
var element = document.createElement('a');
|
| 157 |
+
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
|
| 158 |
+
element.setAttribute('download', "localization.json");
|
| 159 |
+
element.style.display = 'none';
|
| 160 |
+
document.body.appendChild(element);
|
| 161 |
+
|
| 162 |
+
element.click();
|
| 163 |
+
|
| 164 |
+
document.body.removeChild(element);
|
| 165 |
+
}
|
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('div[id^="tab_"][style*="display: block"] div[id$="_results"] .thumbnail-item > img');
|
| 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 - opts.return_grid : 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,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// code related to showing and updating progressbar shown as the image is being made
|
| 2 |
+
|
| 3 |
+
function rememberGallerySelection(id_gallery){
|
| 4 |
+
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
function getGallerySelectedIndex(id_gallery){
|
| 8 |
+
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
function request(url, data, handler, errorHandler){
|
| 12 |
+
var xhr = new XMLHttpRequest();
|
| 13 |
+
var url = url;
|
| 14 |
+
xhr.open("POST", url, true);
|
| 15 |
+
xhr.setRequestHeader("Content-Type", "application/json");
|
| 16 |
+
xhr.onreadystatechange = function () {
|
| 17 |
+
if (xhr.readyState === 4) {
|
| 18 |
+
if (xhr.status === 200) {
|
| 19 |
+
try {
|
| 20 |
+
var js = JSON.parse(xhr.responseText);
|
| 21 |
+
handler(js)
|
| 22 |
+
} catch (error) {
|
| 23 |
+
console.error(error);
|
| 24 |
+
errorHandler()
|
| 25 |
+
}
|
| 26 |
+
} else{
|
| 27 |
+
errorHandler()
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
};
|
| 31 |
+
var js = JSON.stringify(data);
|
| 32 |
+
xhr.send(js);
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
function pad2(x){
|
| 36 |
+
return x<10 ? '0'+x : x
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
function formatTime(secs){
|
| 40 |
+
if(secs > 3600){
|
| 41 |
+
return pad2(Math.floor(secs/60/60)) + ":" + pad2(Math.floor(secs/60)%60) + ":" + pad2(Math.floor(secs)%60)
|
| 42 |
+
} else if(secs > 60){
|
| 43 |
+
return pad2(Math.floor(secs/60)) + ":" + pad2(Math.floor(secs)%60)
|
| 44 |
+
} else{
|
| 45 |
+
return Math.floor(secs) + "s"
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
function setTitle(progress){
|
| 50 |
+
var title = 'Stable Diffusion'
|
| 51 |
+
|
| 52 |
+
if(opts.show_progress_in_title && progress){
|
| 53 |
+
title = '[' + progress.trim() + '] ' + title;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
if(document.title != title){
|
| 57 |
+
document.title = title;
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
function randomId(){
|
| 63 |
+
return "task(" + Math.random().toString(36).slice(2, 7) + Math.random().toString(36).slice(2, 7) + Math.random().toString(36).slice(2, 7)+")"
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
// starts sending progress requests to "/internal/progress" uri, creating progressbar above progressbarContainer element and
|
| 67 |
+
// preview inside gallery element. Cleans up all created stuff when the task is over and calls atEnd.
|
| 68 |
+
// calls onProgress every time there is a progress update
|
| 69 |
+
function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgress){
|
| 70 |
+
var dateStart = new Date()
|
| 71 |
+
var wasEverActive = false
|
| 72 |
+
var parentProgressbar = progressbarContainer.parentNode
|
| 73 |
+
var parentGallery = gallery ? gallery.parentNode : null
|
| 74 |
+
|
| 75 |
+
var divProgress = document.createElement('div')
|
| 76 |
+
divProgress.className='progressDiv'
|
| 77 |
+
divProgress.style.display = opts.show_progressbar ? "block" : "none"
|
| 78 |
+
var divInner = document.createElement('div')
|
| 79 |
+
divInner.className='progress'
|
| 80 |
+
|
| 81 |
+
divProgress.appendChild(divInner)
|
| 82 |
+
parentProgressbar.insertBefore(divProgress, progressbarContainer)
|
| 83 |
+
|
| 84 |
+
if(parentGallery){
|
| 85 |
+
var livePreview = document.createElement('div')
|
| 86 |
+
livePreview.className='livePreview'
|
| 87 |
+
parentGallery.insertBefore(livePreview, gallery)
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
var removeProgressBar = function(){
|
| 91 |
+
setTitle("")
|
| 92 |
+
parentProgressbar.removeChild(divProgress)
|
| 93 |
+
if(parentGallery) parentGallery.removeChild(livePreview)
|
| 94 |
+
atEnd()
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
var fun = function(id_task, id_live_preview){
|
| 98 |
+
request("./internal/progress", {"id_task": id_task, "id_live_preview": id_live_preview}, function(res){
|
| 99 |
+
if(res.completed){
|
| 100 |
+
removeProgressBar()
|
| 101 |
+
return
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
var rect = progressbarContainer.getBoundingClientRect()
|
| 105 |
+
|
| 106 |
+
if(rect.width){
|
| 107 |
+
divProgress.style.width = rect.width + "px";
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
progressText = ""
|
| 111 |
+
|
| 112 |
+
divInner.style.width = ((res.progress || 0) * 100.0) + '%'
|
| 113 |
+
divInner.style.background = res.progress ? "" : "transparent"
|
| 114 |
+
|
| 115 |
+
if(res.progress > 0){
|
| 116 |
+
progressText = ((res.progress || 0) * 100.0).toFixed(0) + '%'
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
if(res.eta){
|
| 120 |
+
progressText += " ETA: " + formatTime(res.eta)
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
setTitle(progressText)
|
| 125 |
+
|
| 126 |
+
if(res.textinfo && res.textinfo.indexOf("\n") == -1){
|
| 127 |
+
progressText = res.textinfo + " " + progressText
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
divInner.textContent = progressText
|
| 131 |
+
|
| 132 |
+
var elapsedFromStart = (new Date() - dateStart) / 1000
|
| 133 |
+
|
| 134 |
+
if(res.active) wasEverActive = true;
|
| 135 |
+
|
| 136 |
+
if(! res.active && wasEverActive){
|
| 137 |
+
removeProgressBar()
|
| 138 |
+
return
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
if(elapsedFromStart > 5 && !res.queued && !res.active){
|
| 142 |
+
removeProgressBar()
|
| 143 |
+
return
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
if(res.live_preview && gallery){
|
| 148 |
+
var rect = gallery.getBoundingClientRect()
|
| 149 |
+
if(rect.width){
|
| 150 |
+
livePreview.style.width = rect.width + "px"
|
| 151 |
+
livePreview.style.height = rect.height + "px"
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
var img = new Image();
|
| 155 |
+
img.onload = function() {
|
| 156 |
+
livePreview.appendChild(img)
|
| 157 |
+
if(livePreview.childElementCount > 2){
|
| 158 |
+
livePreview.removeChild(livePreview.firstElementChild)
|
| 159 |
+
}
|
| 160 |
+
}
|
| 161 |
+
img.src = res.live_preview;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
if(onProgress){
|
| 166 |
+
onProgress(res)
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
setTimeout(() => {
|
| 170 |
+
fun(id_task, res.id_live_preview);
|
| 171 |
+
}, opts.live_preview_refresh_period || 500)
|
| 172 |
+
}, function(){
|
| 173 |
+
removeProgressBar()
|
| 174 |
+
})
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
fun(id_task, 0)
|
| 178 |
+
}
|
javascript/textualInversion.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
function start_training_textual_inversion(){
|
| 5 |
+
gradioApp().querySelector('#ti_error').innerHTML=''
|
| 6 |
+
|
| 7 |
+
var id = randomId()
|
| 8 |
+
requestProgress(id, gradioApp().getElementById('ti_output'), gradioApp().getElementById('ti_gallery'), function(){}, function(progress){
|
| 9 |
+
gradioApp().getElementById('ti_progress').innerHTML = progress.textinfo
|
| 10 |
+
})
|
| 11 |
+
|
| 12 |
+
var res = args_to_array(arguments)
|
| 13 |
+
|
| 14 |
+
res[0] = id
|
| 15 |
+
|
| 16 |
+
return res
|
| 17 |
+
}
|
javascript/ui.js
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// various functions for interaction with ui.py not large enough to warrant putting them in separate files
|
| 2 |
+
|
| 3 |
+
function set_theme(theme){
|
| 4 |
+
gradioURL = window.location.href
|
| 5 |
+
if (!gradioURL.includes('?__theme=')) {
|
| 6 |
+
window.location.replace(gradioURL + '?__theme=' + theme);
|
| 7 |
+
}
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
function all_gallery_buttons() {
|
| 11 |
+
var allGalleryButtons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery].gradio-gallery .thumbnails > .thumbnail-item.thumbnail-small');
|
| 12 |
+
var visibleGalleryButtons = [];
|
| 13 |
+
allGalleryButtons.forEach(function(elem) {
|
| 14 |
+
if (elem.parentElement.offsetParent) {
|
| 15 |
+
visibleGalleryButtons.push(elem);
|
| 16 |
+
}
|
| 17 |
+
})
|
| 18 |
+
return visibleGalleryButtons;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
function selected_gallery_button() {
|
| 22 |
+
var allCurrentButtons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery].gradio-gallery .thumbnail-item.thumbnail-small.selected');
|
| 23 |
+
var visibleCurrentButton = null;
|
| 24 |
+
allCurrentButtons.forEach(function(elem) {
|
| 25 |
+
if (elem.parentElement.offsetParent) {
|
| 26 |
+
visibleCurrentButton = elem;
|
| 27 |
+
}
|
| 28 |
+
})
|
| 29 |
+
return visibleCurrentButton;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
function selected_gallery_index(){
|
| 33 |
+
var buttons = all_gallery_buttons();
|
| 34 |
+
var button = selected_gallery_button();
|
| 35 |
+
|
| 36 |
+
var result = -1
|
| 37 |
+
buttons.forEach(function(v, i){ if(v==button) { result = i } })
|
| 38 |
+
|
| 39 |
+
return result
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
function extract_image_from_gallery(gallery){
|
| 43 |
+
if (gallery.length == 0){
|
| 44 |
+
return [null];
|
| 45 |
+
}
|
| 46 |
+
if (gallery.length == 1){
|
| 47 |
+
return [gallery[0]];
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
index = selected_gallery_index()
|
| 51 |
+
|
| 52 |
+
if (index < 0 || index >= gallery.length){
|
| 53 |
+
// Use the first image in the gallery as the default
|
| 54 |
+
index = 0;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
return [gallery[index]];
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
function args_to_array(args){
|
| 61 |
+
res = []
|
| 62 |
+
for(var i=0;i<args.length;i++){
|
| 63 |
+
res.push(args[i])
|
| 64 |
+
}
|
| 65 |
+
return res
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
function switch_to_txt2img(){
|
| 69 |
+
gradioApp().querySelector('#tabs').querySelectorAll('button')[0].click();
|
| 70 |
+
|
| 71 |
+
return args_to_array(arguments);
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
function switch_to_img2img_tab(no){
|
| 75 |
+
gradioApp().querySelector('#tabs').querySelectorAll('button')[1].click();
|
| 76 |
+
gradioApp().getElementById('mode_img2img').querySelectorAll('button')[no].click();
|
| 77 |
+
}
|
| 78 |
+
function switch_to_img2img(){
|
| 79 |
+
switch_to_img2img_tab(0);
|
| 80 |
+
return args_to_array(arguments);
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
function switch_to_sketch(){
|
| 84 |
+
switch_to_img2img_tab(1);
|
| 85 |
+
return args_to_array(arguments);
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
function switch_to_inpaint(){
|
| 89 |
+
switch_to_img2img_tab(2);
|
| 90 |
+
return args_to_array(arguments);
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
function switch_to_inpaint_sketch(){
|
| 94 |
+
switch_to_img2img_tab(3);
|
| 95 |
+
return args_to_array(arguments);
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
function switch_to_inpaint(){
|
| 99 |
+
gradioApp().querySelector('#tabs').querySelectorAll('button')[1].click();
|
| 100 |
+
gradioApp().getElementById('mode_img2img').querySelectorAll('button')[2].click();
|
| 101 |
+
|
| 102 |
+
return args_to_array(arguments);
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
function switch_to_extras(){
|
| 106 |
+
gradioApp().querySelector('#tabs').querySelectorAll('button')[2].click();
|
| 107 |
+
|
| 108 |
+
return args_to_array(arguments);
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
function get_tab_index(tabId){
|
| 112 |
+
var res = 0
|
| 113 |
+
|
| 114 |
+
gradioApp().getElementById(tabId).querySelector('div').querySelectorAll('button').forEach(function(button, i){
|
| 115 |
+
if(button.className.indexOf('selected') != -1)
|
| 116 |
+
res = i
|
| 117 |
+
})
|
| 118 |
+
|
| 119 |
+
return res
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
function create_tab_index_args(tabId, args){
|
| 123 |
+
var res = []
|
| 124 |
+
for(var i=0; i<args.length; i++){
|
| 125 |
+
res.push(args[i])
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
res[0] = get_tab_index(tabId)
|
| 129 |
+
|
| 130 |
+
return res
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
function get_img2img_tab_index() {
|
| 134 |
+
let res = args_to_array(arguments)
|
| 135 |
+
res.splice(-2)
|
| 136 |
+
res[0] = get_tab_index('mode_img2img')
|
| 137 |
+
return res
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
function create_submit_args(args){
|
| 141 |
+
res = []
|
| 142 |
+
for(var i=0;i<args.length;i++){
|
| 143 |
+
res.push(args[i])
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
// 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.
|
| 147 |
+
// This can lead to uploading a huge gallery of previously generated images, which leads to an unnecessary delay between submitting and beginning to generate.
|
| 148 |
+
// 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.
|
| 149 |
+
// If gradio at some point stops sending outputs, this may break something
|
| 150 |
+
if(Array.isArray(res[res.length - 3])){
|
| 151 |
+
res[res.length - 3] = null
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
return res
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
function showSubmitButtons(tabname, show){
|
| 158 |
+
gradioApp().getElementById(tabname+'_interrupt').style.display = show ? "none" : "block"
|
| 159 |
+
gradioApp().getElementById(tabname+'_skip').style.display = show ? "none" : "block"
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
function submit(){
|
| 163 |
+
rememberGallerySelection('txt2img_gallery')
|
| 164 |
+
showSubmitButtons('txt2img', false)
|
| 165 |
+
|
| 166 |
+
var id = randomId()
|
| 167 |
+
requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function(){
|
| 168 |
+
showSubmitButtons('txt2img', true)
|
| 169 |
+
|
| 170 |
+
})
|
| 171 |
+
|
| 172 |
+
var res = create_submit_args(arguments)
|
| 173 |
+
|
| 174 |
+
res[0] = id
|
| 175 |
+
|
| 176 |
+
return res
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
function submit_img2img(){
|
| 180 |
+
rememberGallerySelection('img2img_gallery')
|
| 181 |
+
showSubmitButtons('img2img', false)
|
| 182 |
+
|
| 183 |
+
var id = randomId()
|
| 184 |
+
requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function(){
|
| 185 |
+
showSubmitButtons('img2img', true)
|
| 186 |
+
})
|
| 187 |
+
|
| 188 |
+
var res = create_submit_args(arguments)
|
| 189 |
+
|
| 190 |
+
res[0] = id
|
| 191 |
+
res[1] = get_tab_index('mode_img2img')
|
| 192 |
+
|
| 193 |
+
return res
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
function modelmerger(){
|
| 197 |
+
var id = randomId()
|
| 198 |
+
requestProgress(id, gradioApp().getElementById('modelmerger_results_panel'), null, function(){})
|
| 199 |
+
|
| 200 |
+
var res = create_submit_args(arguments)
|
| 201 |
+
res[0] = id
|
| 202 |
+
return res
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
function ask_for_style_name(_, prompt_text, negative_prompt_text) {
|
| 207 |
+
name_ = prompt('Style name:')
|
| 208 |
+
return [name_, prompt_text, negative_prompt_text]
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
function confirm_clear_prompt(prompt, negative_prompt) {
|
| 212 |
+
if(confirm("Delete prompt?")) {
|
| 213 |
+
prompt = ""
|
| 214 |
+
negative_prompt = ""
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
return [prompt, negative_prompt]
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
promptTokecountUpdateFuncs = {}
|
| 222 |
+
|
| 223 |
+
function recalculatePromptTokens(name){
|
| 224 |
+
if(promptTokecountUpdateFuncs[name]){
|
| 225 |
+
promptTokecountUpdateFuncs[name]()
|
| 226 |
+
}
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
function recalculate_prompts_txt2img(){
|
| 230 |
+
recalculatePromptTokens('txt2img_prompt')
|
| 231 |
+
recalculatePromptTokens('txt2img_neg_prompt')
|
| 232 |
+
return args_to_array(arguments);
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
function recalculate_prompts_img2img(){
|
| 236 |
+
recalculatePromptTokens('img2img_prompt')
|
| 237 |
+
recalculatePromptTokens('img2img_neg_prompt')
|
| 238 |
+
return args_to_array(arguments);
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
opts = {}
|
| 243 |
+
onUiUpdate(function(){
|
| 244 |
+
if(Object.keys(opts).length != 0) return;
|
| 245 |
+
|
| 246 |
+
json_elem = gradioApp().getElementById('settings_json')
|
| 247 |
+
if(json_elem == null) return;
|
| 248 |
+
|
| 249 |
+
var textarea = json_elem.querySelector('textarea')
|
| 250 |
+
var jsdata = textarea.value
|
| 251 |
+
opts = JSON.parse(jsdata)
|
| 252 |
+
executeCallbacks(optionsChangedCallbacks);
|
| 253 |
+
|
| 254 |
+
Object.defineProperty(textarea, 'value', {
|
| 255 |
+
set: function(newValue) {
|
| 256 |
+
var valueProp = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
|
| 257 |
+
var oldValue = valueProp.get.call(textarea);
|
| 258 |
+
valueProp.set.call(textarea, newValue);
|
| 259 |
+
|
| 260 |
+
if (oldValue != newValue) {
|
| 261 |
+
opts = JSON.parse(textarea.value)
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
executeCallbacks(optionsChangedCallbacks);
|
| 265 |
+
},
|
| 266 |
+
get: function() {
|
| 267 |
+
var valueProp = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
|
| 268 |
+
return valueProp.get.call(textarea);
|
| 269 |
+
}
|
| 270 |
+
});
|
| 271 |
+
|
| 272 |
+
json_elem.parentElement.style.display="none"
|
| 273 |
+
|
| 274 |
+
function registerTextarea(id, id_counter, id_button){
|
| 275 |
+
var prompt = gradioApp().getElementById(id)
|
| 276 |
+
var counter = gradioApp().getElementById(id_counter)
|
| 277 |
+
var textarea = gradioApp().querySelector("#" + id + " > label > textarea");
|
| 278 |
+
|
| 279 |
+
if(counter.parentElement == prompt.parentElement){
|
| 280 |
+
return
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
prompt.parentElement.insertBefore(counter, prompt)
|
| 284 |
+
prompt.parentElement.style.position = "relative"
|
| 285 |
+
|
| 286 |
+
promptTokecountUpdateFuncs[id] = function(){ update_token_counter(id_button); }
|
| 287 |
+
textarea.addEventListener("input", promptTokecountUpdateFuncs[id]);
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
registerTextarea('txt2img_prompt', 'txt2img_token_counter', 'txt2img_token_button')
|
| 291 |
+
registerTextarea('txt2img_neg_prompt', 'txt2img_negative_token_counter', 'txt2img_negative_token_button')
|
| 292 |
+
registerTextarea('img2img_prompt', 'img2img_token_counter', 'img2img_token_button')
|
| 293 |
+
registerTextarea('img2img_neg_prompt', 'img2img_negative_token_counter', 'img2img_negative_token_button')
|
| 294 |
+
|
| 295 |
+
show_all_pages = gradioApp().getElementById('settings_show_all_pages')
|
| 296 |
+
settings_tabs = gradioApp().querySelector('#settings div')
|
| 297 |
+
if(show_all_pages && settings_tabs){
|
| 298 |
+
settings_tabs.appendChild(show_all_pages)
|
| 299 |
+
show_all_pages.onclick = function(){
|
| 300 |
+
gradioApp().querySelectorAll('#settings > div').forEach(function(elem){
|
| 301 |
+
elem.style.display = "block";
|
| 302 |
+
})
|
| 303 |
+
}
|
| 304 |
+
}
|
| 305 |
+
})
|
| 306 |
+
|
| 307 |
+
onOptionsChanged(function(){
|
| 308 |
+
elem = gradioApp().getElementById('sd_checkpoint_hash')
|
| 309 |
+
sd_checkpoint_hash = opts.sd_checkpoint_hash || ""
|
| 310 |
+
shorthash = sd_checkpoint_hash.substr(0,10)
|
| 311 |
+
|
| 312 |
+
if(elem && elem.textContent != shorthash){
|
| 313 |
+
elem.textContent = shorthash
|
| 314 |
+
elem.title = sd_checkpoint_hash
|
| 315 |
+
elem.href = "https://google.com/search?q=" + sd_checkpoint_hash
|
| 316 |
+
}
|
| 317 |
+
})
|
| 318 |
+
|
| 319 |
+
let txt2img_textarea, img2img_textarea = undefined;
|
| 320 |
+
let wait_time = 800
|
| 321 |
+
let token_timeouts = {};
|
| 322 |
+
|
| 323 |
+
function update_txt2img_tokens(...args) {
|
| 324 |
+
update_token_counter("txt2img_token_button")
|
| 325 |
+
if (args.length == 2)
|
| 326 |
+
return args[0]
|
| 327 |
+
return args;
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
function update_img2img_tokens(...args) {
|
| 331 |
+
update_token_counter("img2img_token_button")
|
| 332 |
+
if (args.length == 2)
|
| 333 |
+
return args[0]
|
| 334 |
+
return args;
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
function update_token_counter(button_id) {
|
| 338 |
+
if (token_timeouts[button_id])
|
| 339 |
+
clearTimeout(token_timeouts[button_id]);
|
| 340 |
+
token_timeouts[button_id] = setTimeout(() => gradioApp().getElementById(button_id)?.click(), wait_time);
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
function restart_reload(){
|
| 344 |
+
document.body.innerHTML='<h1 style="font-family:monospace;margin-top:20%;color:lightgray;text-align:center;">Reloading...</h1>';
|
| 345 |
+
setTimeout(function(){location.reload()},2000)
|
| 346 |
+
|
| 347 |
+
return []
|
| 348 |
+
}
|
| 349 |
+
|
| 350 |
+
// Simulate an `input` DOM event for Gradio Textbox component. Needed after you edit its contents in javascript, otherwise your edits
|
| 351 |
+
// will only visible on web page and not sent to python.
|
| 352 |
+
function updateInput(target){
|
| 353 |
+
let e = new Event("input", { bubbles: true })
|
| 354 |
+
Object.defineProperty(e, "target", {value: target})
|
| 355 |
+
target.dispatchEvent(e);
|
| 356 |
+
}
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
var desiredCheckpointName = null;
|
| 360 |
+
function selectCheckpoint(name){
|
| 361 |
+
desiredCheckpointName = name;
|
| 362 |
+
gradioApp().getElementById('change_checkpoint').click()
|
| 363 |
+
}
|