Skip to content

Commit 4918eb6

Browse files
Merge branch 'master' into hn-activation
2 parents 80844ac + 2cf3d2a commit 4918eb6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+7037
-1305
lines changed

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ body:
4444
id: commit
4545
attributes:
4646
label: Commit where the problem happens
47-
description: Which commit are you running ? (copy the **Commit hash** shown in the cmd/terminal when you launch the UI)
47+
description: Which commit are you running ? (Do not write *Latest version/repo/commit*, as this means nothing and will have changed by the time we read your issue. Rather, copy the **Commit hash** shown in the cmd/terminal when you launch the UI)
4848
validations:
4949
required: true
5050
- type: dropdown

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ notification.mp3
2929
/textual_inversion
3030
.vscode
3131
/extensions
32-
32+
/test/stdout.txt
33+
/test/stderr.txt

CODEOWNERS

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,13 @@
11
* @AUTOMATIC1111
2+
/localizations/ar_AR.json @xmodar @blackneoo
3+
/localizations/de_DE.json @LunixWasTaken
4+
/localizations/es_ES.json @innovaciones
5+
/localizations/fr_FR.json @tumbly
6+
/localizations/it_IT.json @EugenioBuffo
7+
/localizations/ja_JP.json @yuuki76
8+
/localizations/ko_KR.json @36DB
9+
/localizations/pt_BR.json @M-art-ucci
10+
/localizations/ru_RU.json @kabachuha
11+
/localizations/tr_TR.json @camenduru
12+
/localizations/zh_CN.json @dtlnor @bgluminous
13+
/localizations/zh_TW.json @benlisquare

javascript/extensions.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
function extensions_apply(_, _){
3+
disable = []
4+
update = []
5+
gradioApp().querySelectorAll('#extensions input[type="checkbox"]').forEach(function(x){
6+
if(x.name.startsWith("enable_") && ! x.checked)
7+
disable.push(x.name.substr(7))
8+
9+
if(x.name.startsWith("update_") && x.checked)
10+
update.push(x.name.substr(7))
11+
})
12+
13+
restart_reload()
14+
15+
return [JSON.stringify(disable), JSON.stringify(update)]
16+
}
17+
18+
function extensions_check(){
19+
gradioApp().querySelectorAll('#extensions .extension_status').forEach(function(x){
20+
x.innerHTML = "Loading..."
21+
})
22+
23+
return []
24+
}
25+
26+
function install_extension_from_index(button, url){
27+
button.disabled = "disabled"
28+
button.value = "Installing..."
29+
30+
textarea = gradioApp().querySelector('#extension_to_install textarea')
31+
textarea.value = url
32+
textarea.dispatchEvent(new Event("input", { bubbles: true }))
33+
34+
gradioApp().querySelector('#install_extension_button').click()
35+
}

javascript/hints.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ titles = {
7575
"Create style": "Save current prompts as a style. If you add the token {prompt} to the text, the style use that as placeholder for your prompt when you use the style in the future.",
7676

7777
"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.",
78+
"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.",
7879

7980
"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%).",
8081

javascript/imageviewer.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ function showModal(event) {
1313
}
1414
lb.style.display = "block";
1515
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+
}
1625
event.stopPropagation()
1726
}
1827

@@ -81,6 +90,25 @@ function modalImageSwitch(offset) {
8190
}
8291
}
8392

93+
function saveImage(){
94+
const tabTxt2Img = gradioApp().getElementById("tab_txt2img")
95+
const tabImg2Img = gradioApp().getElementById("tab_img2img")
96+
const saveTxt2Img = "save_txt2img"
97+
const saveImg2Img = "save_img2img"
98+
if (tabTxt2Img.style.display != "none") {
99+
gradioApp().getElementById(saveTxt2Img).click()
100+
} else if (tabImg2Img.style.display != "none") {
101+
gradioApp().getElementById(saveImg2Img).click()
102+
} else {
103+
console.error("missing implementation for saving modal of this type")
104+
}
105+
}
106+
107+
function modalSaveImage(event) {
108+
saveImage()
109+
event.stopPropagation()
110+
}
111+
84112
function modalNextImage(event) {
85113
modalImageSwitch(1)
86114
event.stopPropagation()
@@ -93,6 +121,9 @@ function modalPrevImage(event) {
93121

94122
function modalKeyHandler(event) {
95123
switch (event.key) {
124+
case "s":
125+
saveImage()
126+
break;
96127
case "ArrowLeft":
97128
modalPrevImage(event)
98129
break;
@@ -198,6 +229,14 @@ document.addEventListener("DOMContentLoaded", function() {
198229
modalTileImage.title = "Preview tiling";
199230
modalControls.appendChild(modalTileImage)
200231

232+
const modalSave = document.createElement("span")
233+
modalSave.className = "modalSave cursor"
234+
modalSave.id = "modal_save"
235+
modalSave.innerHTML = "🖫"
236+
modalSave.addEventListener("click", modalSaveImage, true)
237+
modalSave.title = "Save Image(s)"
238+
modalControls.appendChild(modalSave)
239+
201240
const modalClose = document.createElement('span')
202241
modalClose.className = 'modalClose cursor';
203242
modalClose.innerHTML = '×'

javascript/progressbar.js

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,21 @@ global_progressbars = {}
33
galleries = {}
44
galleryObservers = {}
55

6+
// this tracks laumnches of window.setTimeout for progressbar to prevent starting a new timeout when the previous is still running
7+
timeoutIds = {}
8+
69
function check_progressbar(id_part, id_progressbar, id_progressbar_span, id_skip, id_interrupt, id_preview, id_gallery){
7-
var progressbar = gradioApp().getElementById(id_progressbar)
10+
// gradio 3.8's enlightened approach allows them to create two nested div elements inside each other with same id
11+
// every time you use gr.HTML(elem_id='xxx'), so we handle this here
12+
var progressbar = gradioApp().querySelector("#"+id_progressbar+" #"+id_progressbar)
13+
var progressbarParent
14+
if(progressbar){
15+
progressbarParent = gradioApp().querySelector("#"+id_progressbar)
16+
} else{
17+
progressbar = gradioApp().getElementById(id_progressbar)
18+
progressbarParent = null
19+
}
20+
821
var skip = id_skip ? gradioApp().getElementById(id_skip) : null
922
var interrupt = gradioApp().getElementById(id_interrupt)
1023

@@ -26,18 +39,26 @@ function check_progressbar(id_part, id_progressbar, id_progressbar_span, id_skip
2639
global_progressbars[id_progressbar] = progressbar
2740

2841
var mutationObserver = new MutationObserver(function(m){
42+
if(timeoutIds[id_part]) return;
43+
2944
preview = gradioApp().getElementById(id_preview)
3045
gallery = gradioApp().getElementById(id_gallery)
3146

3247
if(preview != null && gallery != null){
3348
preview.style.width = gallery.clientWidth + "px"
3449
preview.style.height = gallery.clientHeight + "px"
50+
if(progressbarParent) progressbar.style.width = progressbarParent.clientWidth + "px"
3551

3652
//only watch gallery if there is a generation process going on
3753
check_gallery(id_gallery);
3854

3955
var progressDiv = gradioApp().querySelectorAll('#' + id_progressbar_span).length > 0;
40-
if(!progressDiv){
56+
if(progressDiv){
57+
timeoutIds[id_part] = window.setTimeout(function() {
58+
timeoutIds[id_part] = null
59+
requestMoreProgress(id_part, id_progressbar_span, id_skip, id_interrupt)
60+
}, 500)
61+
} else{
4162
if (skip) {
4263
skip.style.display = "none"
4364
}
@@ -47,13 +68,10 @@ function check_progressbar(id_part, id_progressbar, id_progressbar_span, id_skip
4768
if (galleryObservers[id_gallery]) {
4869
galleryObservers[id_gallery].disconnect();
4970
galleries[id_gallery] = null;
50-
}
71+
}
5172
}
52-
53-
5473
}
5574

56-
window.setTimeout(function() { requestMoreProgress(id_part, id_progressbar_span, id_skip, id_interrupt) }, 500)
5775
});
5876
mutationObserver.observe( progressbar, { childList:true, subtree:true })
5977
}

javascript/ui.js

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ function switch_to_txt2img(){
4545
return args_to_array(arguments);
4646
}
4747

48-
function switch_to_img2img_img2img(){
48+
function switch_to_img2img(){
4949
gradioApp().querySelector('#tabs').querySelectorAll('button')[1].click();
5050
gradioApp().getElementById('mode_img2img').querySelectorAll('button')[0].click();
5151

5252
return args_to_array(arguments);
5353
}
5454

55-
function switch_to_img2img_inpaint(){
55+
function switch_to_inpaint(){
5656
gradioApp().querySelector('#tabs').querySelectorAll('button')[1].click();
5757
gradioApp().getElementById('mode_img2img').querySelectorAll('button')[1].click();
5858

@@ -65,26 +65,6 @@ function switch_to_extras(){
6565
return args_to_array(arguments);
6666
}
6767

68-
function extract_image_from_gallery_txt2img(gallery){
69-
switch_to_txt2img()
70-
return extract_image_from_gallery(gallery);
71-
}
72-
73-
function extract_image_from_gallery_img2img(gallery){
74-
switch_to_img2img_img2img()
75-
return extract_image_from_gallery(gallery);
76-
}
77-
78-
function extract_image_from_gallery_inpaint(gallery){
79-
switch_to_img2img_inpaint()
80-
return extract_image_from_gallery(gallery);
81-
}
82-
83-
function extract_image_from_gallery_extras(gallery){
84-
switch_to_extras()
85-
return extract_image_from_gallery(gallery);
86-
}
87-
8868
function get_tab_index(tabId){
8969
var res = 0
9070

launch.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import platform
88

99
dir_repos = "repositories"
10+
dir_extensions = "extensions"
1011
python = sys.executable
1112
git = os.environ.get('GIT', "git")
1213
index_url = os.environ.get('INDEX_URL', "")
@@ -16,11 +17,11 @@ def extract_arg(args, name):
1617
return [x for x in args if x != name], name in args
1718

1819

19-
def run(command, desc=None, errdesc=None):
20+
def run(command, desc=None, errdesc=None, custom_env=None):
2021
if desc is not None:
2122
print(desc)
2223

23-
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
24+
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, env=os.environ if custom_env is None else custom_env)
2425

2526
if result.returncode != 0:
2627

@@ -101,9 +102,27 @@ def version_check(commit):
101102
else:
102103
print("Not a git clone, can't perform version check.")
103104
except Exception as e:
104-
print("versipm check failed",e)
105+
print("version check failed", e)
106+
107+
108+
def run_extensions_installers():
109+
if not os.path.isdir(dir_extensions):
110+
return
111+
112+
for dirname_extension in os.listdir(dir_extensions):
113+
path_installer = os.path.join(dir_extensions, dirname_extension, "install.py")
114+
if not os.path.isfile(path_installer):
115+
continue
116+
117+
try:
118+
env = os.environ.copy()
119+
env['PYTHONPATH'] = os.path.abspath(".")
120+
121+
print(run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {dirname_extension}", custom_env=env))
122+
except Exception as e:
123+
print(e, file=sys.stderr)
124+
105125

106-
107126
def prepare_enviroment():
108127
torch_command = os.environ.get('TORCH_COMMAND', "pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 --extra-index-url https://download.pytorch.org/whl/cu113")
109128
requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")
@@ -128,10 +147,12 @@ def prepare_enviroment():
128147
blip_commit_hash = os.environ.get('BLIP_COMMIT_HASH', "48211a1594f1321b00f14c9f7a5b4813144b2fb9")
129148

130149
sys.argv += shlex.split(commandline_args)
150+
test_argv = [x for x in sys.argv if x != '--tests']
131151

132152
sys.argv, skip_torch_cuda_test = extract_arg(sys.argv, '--skip-torch-cuda-test')
133153
sys.argv, reinstall_xformers = extract_arg(sys.argv, '--reinstall-xformers')
134154
sys.argv, update_check = extract_arg(sys.argv, '--update-check')
155+
sys.argv, run_tests = extract_arg(sys.argv, '--tests')
135156
xformers = '--xformers' in sys.argv
136157
deepdanbooru = '--deepdanbooru' in sys.argv
137158
ngrok = '--ngrok' in sys.argv
@@ -187,13 +208,35 @@ def prepare_enviroment():
187208

188209
run_pip(f"install -r {requirements_file}", "requirements for Web UI")
189210

211+
run_extensions_installers()
212+
190213
if update_check:
191214
version_check(commit)
192215

193216
if "--exit" in sys.argv:
194217
print("Exiting because of --exit argument")
195218
exit(0)
196219

220+
if run_tests:
221+
tests(test_argv)
222+
exit(0)
223+
224+
225+
def tests(argv):
226+
if "--api" not in argv:
227+
argv.append("--api")
228+
229+
print(f"Launching Web UI in another process for testing with arguments: {' '.join(argv[1:])}")
230+
231+
with open('test/stdout.txt', "w", encoding="utf8") as stdout, open('test/stderr.txt', "w", encoding="utf8") as stderr:
232+
proc = subprocess.Popen([sys.executable, *argv], stdout=stdout, stderr=stderr)
233+
234+
import test.server_poll
235+
test.server_poll.run_tests()
236+
237+
print(f"Stopping Web UI process with id {proc.pid}")
238+
proc.kill()
239+
197240

198241
def start_webui():
199242
print(f"Launching Web UI with arguments: {' '.join(sys.argv[1:])}")

0 commit comments

Comments
 (0)