diff --git a/3rd-PARTY-LICENSES b/3rd-PARTY-LICENSES
new file mode 100644
index 00000000..0cf2619f
--- /dev/null
+++ b/3rd-PARTY-LICENSES
@@ -0,0 +1,27 @@
+jquery-confirm
+==============
+https://craftpip.github.io/jquery-confirm/
+
+jquery-confirm is licensed under the MIT license:
+
+ The MIT License (MIT)
+
+ Copyright (c) 2019 Boniface Pereira
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
diff --git a/CHANGES.md b/CHANGES.md
index baafcb18..66283d6f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -19,8 +19,15 @@
- Configuration to prevent the browser from opening on startup
- Lots of minor bug fixes
- A `What's New?` tab in the UI
+- Ask for a confimation before clearing the results pane or stopping a render task. The dialog can be skipped by holding down the shift key while clicking on the button.
+- Show the network addresses of the server in the systems setting dialog
### Detailed changelog
+* 2.4.17 - 30 Nov 2022 - Scroll to generated image. Thanks @patriceac
+* 2.4.17 - 30 Nov 2022 - Show the network addresses of the server in the systems setting dialog. Thanks @JeLuf
+* 2.4.17 - 30 Nov 2022 - Fix a bug where GFPGAN wouldn't work properly when multiple GPUs tried to run it at the same time. Thanks @madrang
+* 2.4.17 - 30 Nov 2022 - Confirm before stopping or clearing all the tasks. Thanks @JeLuf
+* 2.4.16 - 29 Nov 2022 - Bug fixes for SD 2.0 - remove the need for patching, default to SD 1.4 model if trying to load an SD2 model in SD1.4.
* 2.4.15 - 25 Nov 2022 - Experimental support for SD 2.0. Uses lots of memory, not optimized, probably GPU-only.
* 2.4.14 - 22 Nov 2022 - Change the backend to a custom fork of Stable Diffusion
* 2.4.13 - 21 Nov 2022 - Change the modifier weight via mouse wheel, drag to reorder selected modifiers, and some more modifier-related fixes. Thanks @patriceac
diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat
index 87b6ada0..3d5218d5 100644
--- a/scripts/on_sd_start.bat
+++ b/scripts/on_sd_start.bat
@@ -42,13 +42,9 @@ if NOT DEFINED test_sd2 set test_sd2=N
if "%test_sd2%" == "N" (
@call git -c advice.detachedHead=false checkout 7f32368ed1030a6e710537047bacd908adea183a
-
- @call git apply --whitespace=warn ..\ui\sd_internal\ddim_callback.patch
)
if "%test_sd2%" == "Y" (
- @call git -c advice.detachedHead=false checkout 6e2f82187f8ecc4ea59ac37dc239cfcc78038f6d
-
- @call git apply ..\ui\sd_internal\ddim_callback_sd2.patch
+ @call git -c advice.detachedHead=false checkout 5d647c5459f4cd790672512222bc41903c01bb71
)
@cd ..
@@ -66,8 +62,6 @@ if NOT DEFINED test_sd2 set test_sd2=N
@cd stable-diffusion
@call git -c advice.detachedHead=false checkout 7f32368ed1030a6e710537047bacd908adea183a
- @call git apply --whitespace=warn ..\ui\sd_internal\ddim_callback.patch
-
@cd ..
)
diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh
old mode 100644
new mode 100755
index 460d58a7..ea1aaa93
--- a/scripts/on_sd_start.sh
+++ b/scripts/on_sd_start.sh
@@ -37,12 +37,8 @@ if [ -e "scripts/install_status.txt" ] && [ `grep -c sd_git_cloned scripts/insta
if [ "$test_sd2" == "N" ]; then
git -c advice.detachedHead=false checkout 7f32368ed1030a6e710537047bacd908adea183a
-
- git apply --whitespace=warn ../ui/sd_internal/ddim_callback.patch || fail "ddim patch failed"
elif [ "$test_sd2" == "Y" ]; then
- git -c advice.detachedHead=false checkout 992f111312afa9ec1a01beaa9733cb9728f5acd3
-
- git apply --whitespace=warn ../ui/sd_internal/ddim_callback_sd2.patch || fail "sd2 ddim patch failed"
+ git -c advice.detachedHead=false checkout 5d647c5459f4cd790672512222bc41903c01bb71
fi
cd ..
@@ -58,8 +54,6 @@ else
cd stable-diffusion
git -c advice.detachedHead=false checkout 7f32368ed1030a6e710537047bacd908adea183a
- git apply --whitespace=warn ../ui/sd_internal/ddim_callback.patch || fail "ddim patch failed"
-
cd ..
fi
diff --git a/ui/index.html b/ui/index.html
index c36c1aee..69bef7f1 100644
--- a/ui/index.html
+++ b/ui/index.html
@@ -3,6 +3,7 @@
@@ -348,7 +361,7 @@ async function init() {
await getAppConfig()
await loadModifiers()
await loadUIPlugins()
- await getDevices()
+ await getSystemInfo()
setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000)
healthCheck()
diff --git a/ui/media/css/jquery-confirm.min.css b/ui/media/css/jquery-confirm.min.css
new file mode 100644
index 00000000..400f0b8d
--- /dev/null
+++ b/ui/media/css/jquery-confirm.min.css
@@ -0,0 +1,9 @@
+/*!
+ * jquery-confirm v3.3.2 (http://craftpip.github.io/jquery-confirm/)
+ * Author: boniface pereira
+ * Website: www.craftpip.com
+ * Contact: hey@craftpip.com
+ *
+ * Copyright 2013-2017 jquery-confirm
+ * Licensed under MIT (https://github.com/craftpip/jquery-confirm/blob/master/LICENSE)
+ */@-webkit-keyframes jconfirm-spin{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes jconfirm-spin{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}body[class*=jconfirm-no-scroll-]{overflow:hidden!important}.jconfirm{position:fixed;top:0;left:0;right:0;bottom:0;z-index:99999999;font-family:inherit;overflow:hidden}.jconfirm .jconfirm-bg{position:fixed;top:0;left:0;right:0;bottom:0;-webkit-transition:opacity .4s;transition:opacity .4s}.jconfirm .jconfirm-bg.jconfirm-bg-h{opacity:0!important}.jconfirm .jconfirm-scrollpane{-webkit-perspective:500px;perspective:500px;-webkit-perspective-origin:center;perspective-origin:center;display:table;width:100%;height:100%}.jconfirm .jconfirm-row{display:table-row;width:100%}.jconfirm .jconfirm-cell{display:table-cell;vertical-align:middle}.jconfirm .jconfirm-holder{max-height:100%;padding:50px 0}.jconfirm .jconfirm-box-container{-webkit-transition:-webkit-transform;transition:-webkit-transform;transition:transform;transition:transform,-webkit-transform}.jconfirm .jconfirm-box-container.jconfirm-no-transition{-webkit-transition:none!important;transition:none!important}.jconfirm .jconfirm-box{background:white;border-radius:4px;position:relative;outline:0;padding:15px 15px 0;overflow:hidden;margin-left:auto;margin-right:auto}@-webkit-keyframes type-blue{1%,100%{border-color:#3498db}50%{border-color:#5faee3}}@keyframes type-blue{1%,100%{border-color:#3498db}50%{border-color:#5faee3}}@-webkit-keyframes type-green{1%,100%{border-color:#2ecc71}50%{border-color:#54d98c}}@keyframes type-green{1%,100%{border-color:#2ecc71}50%{border-color:#54d98c}}@-webkit-keyframes type-red{1%,100%{border-color:#e74c3c}50%{border-color:#ed7669}}@keyframes type-red{1%,100%{border-color:#e74c3c}50%{border-color:#ed7669}}@-webkit-keyframes type-orange{1%,100%{border-color:#f1c40f}50%{border-color:#f4d03f}}@keyframes type-orange{1%,100%{border-color:#f1c40f}50%{border-color:#f4d03f}}@-webkit-keyframes type-purple{1%,100%{border-color:#9b59b6}50%{border-color:#b07cc6}}@keyframes type-purple{1%,100%{border-color:#9b59b6}50%{border-color:#b07cc6}}@-webkit-keyframes type-dark{1%,100%{border-color:#34495e}50%{border-color:#46627f}}@keyframes type-dark{1%,100%{border-color:#34495e}50%{border-color:#46627f}}.jconfirm .jconfirm-box.jconfirm-type-animated{-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.jconfirm .jconfirm-box.jconfirm-type-blue{border-top:solid 7px #3498db;-webkit-animation-name:type-blue;animation-name:type-blue}.jconfirm .jconfirm-box.jconfirm-type-green{border-top:solid 7px #2ecc71;-webkit-animation-name:type-green;animation-name:type-green}.jconfirm .jconfirm-box.jconfirm-type-red{border-top:solid 7px #e74c3c;-webkit-animation-name:type-red;animation-name:type-red}.jconfirm .jconfirm-box.jconfirm-type-orange{border-top:solid 7px #f1c40f;-webkit-animation-name:type-orange;animation-name:type-orange}.jconfirm .jconfirm-box.jconfirm-type-purple{border-top:solid 7px #9b59b6;-webkit-animation-name:type-purple;animation-name:type-purple}.jconfirm .jconfirm-box.jconfirm-type-dark{border-top:solid 7px #34495e;-webkit-animation-name:type-dark;animation-name:type-dark}.jconfirm .jconfirm-box.loading{height:120px}.jconfirm .jconfirm-box.loading:before{content:'';position:absolute;left:0;background:white;right:0;top:0;bottom:0;border-radius:10px;z-index:1}.jconfirm .jconfirm-box.loading:after{opacity:.6;content:'';height:30px;width:30px;border:solid 3px transparent;position:absolute;left:50%;margin-left:-15px;border-radius:50%;-webkit-animation:jconfirm-spin 1s infinite linear;animation:jconfirm-spin 1s infinite linear;border-bottom-color:dodgerblue;top:50%;margin-top:-15px;z-index:2}.jconfirm .jconfirm-box div.jconfirm-closeIcon{height:20px;width:20px;position:absolute;top:10px;right:10px;cursor:pointer;opacity:.6;text-align:center;font-size:27px!important;line-height:14px!important;display:none;z-index:1}.jconfirm .jconfirm-box div.jconfirm-closeIcon:empty{display:none}.jconfirm .jconfirm-box div.jconfirm-closeIcon .fa{font-size:16px}.jconfirm .jconfirm-box div.jconfirm-closeIcon .glyphicon{font-size:16px}.jconfirm .jconfirm-box div.jconfirm-closeIcon .zmdi{font-size:16px}.jconfirm .jconfirm-box div.jconfirm-closeIcon:hover{opacity:1}.jconfirm .jconfirm-box div.jconfirm-title-c{display:block;font-size:22px;line-height:20px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default;padding-bottom:15px}.jconfirm .jconfirm-box div.jconfirm-title-c.jconfirm-hand{cursor:move}.jconfirm .jconfirm-box div.jconfirm-title-c .jconfirm-icon-c{font-size:inherit;display:inline-block;vertical-align:middle}.jconfirm .jconfirm-box div.jconfirm-title-c .jconfirm-icon-c i{vertical-align:middle}.jconfirm .jconfirm-box div.jconfirm-title-c .jconfirm-icon-c:empty{display:none}.jconfirm .jconfirm-box div.jconfirm-title-c .jconfirm-title{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:inherit;font-family:inherit;display:inline-block;vertical-align:middle}.jconfirm .jconfirm-box div.jconfirm-title-c .jconfirm-title:empty{display:none}.jconfirm .jconfirm-box div.jconfirm-content-pane{margin-bottom:15px;height:auto;-webkit-transition:height .4s ease-in;transition:height .4s ease-in;display:inline-block;width:100%;position:relative;overflow-x:hidden;overflow-y:auto}.jconfirm .jconfirm-box div.jconfirm-content-pane.no-scroll{overflow-y:hidden}.jconfirm .jconfirm-box div.jconfirm-content-pane::-webkit-scrollbar{width:3px}.jconfirm .jconfirm-box div.jconfirm-content-pane::-webkit-scrollbar-track{background:rgba(0,0,0,0.1)}.jconfirm .jconfirm-box div.jconfirm-content-pane::-webkit-scrollbar-thumb{background:#666;border-radius:3px}.jconfirm .jconfirm-box div.jconfirm-content-pane .jconfirm-content{overflow:auto}.jconfirm .jconfirm-box div.jconfirm-content-pane .jconfirm-content img{max-width:100%;height:auto}.jconfirm .jconfirm-box div.jconfirm-content-pane .jconfirm-content:empty{display:none}.jconfirm .jconfirm-box .jconfirm-buttons{padding-bottom:11px}.jconfirm .jconfirm-box .jconfirm-buttons>button{margin-bottom:4px;margin-left:2px;margin-right:2px}.jconfirm .jconfirm-box .jconfirm-buttons button{display:inline-block;padding:6px 12px;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;min-height:1em;-webkit-transition:opacity .1s ease,background-color .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease,-webkit-box-shadow .1s ease;-webkit-tap-highlight-color:transparent;border:0;background-image:none}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-blue{background-color:#3498db;color:#FFF;text-shadow:none;-webkit-transition:background .2s;transition:background .2s}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-blue:hover{background-color:#2980b9;color:#FFF}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-green{background-color:#2ecc71;color:#FFF;text-shadow:none;-webkit-transition:background .2s;transition:background .2s}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-green:hover{background-color:#27ae60;color:#FFF}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-red{background-color:#e74c3c;color:#FFF;text-shadow:none;-webkit-transition:background .2s;transition:background .2s}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-red:hover{background-color:#c0392b;color:#FFF}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-orange{background-color:#f1c40f;color:#FFF;text-shadow:none;-webkit-transition:background .2s;transition:background .2s}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-orange:hover{background-color:#f39c12;color:#FFF}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-default{background-color:#ecf0f1;color:#000;text-shadow:none;-webkit-transition:background .2s;transition:background .2s}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-default:hover{background-color:#bdc3c7;color:#000}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-purple{background-color:#9b59b6;color:#FFF;text-shadow:none;-webkit-transition:background .2s;transition:background .2s}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-purple:hover{background-color:#8e44ad;color:#FFF}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-dark{background-color:#34495e;color:#FFF;text-shadow:none;-webkit-transition:background .2s;transition:background .2s}.jconfirm .jconfirm-box .jconfirm-buttons button.btn-dark:hover{background-color:#2c3e50;color:#FFF}.jconfirm .jconfirm-box.jconfirm-type-red .jconfirm-title-c .jconfirm-icon-c{color:#e74c3c!important}.jconfirm .jconfirm-box.jconfirm-type-blue .jconfirm-title-c .jconfirm-icon-c{color:#3498db!important}.jconfirm .jconfirm-box.jconfirm-type-green .jconfirm-title-c .jconfirm-icon-c{color:#2ecc71!important}.jconfirm .jconfirm-box.jconfirm-type-purple .jconfirm-title-c .jconfirm-icon-c{color:#9b59b6!important}.jconfirm .jconfirm-box.jconfirm-type-orange .jconfirm-title-c .jconfirm-icon-c{color:#f1c40f!important}.jconfirm .jconfirm-box.jconfirm-type-dark .jconfirm-title-c .jconfirm-icon-c{color:#34495e!important}.jconfirm .jconfirm-clear{clear:both}.jconfirm.jconfirm-rtl{direction:rtl}.jconfirm.jconfirm-rtl div.jconfirm-closeIcon{left:5px;right:auto}.jconfirm.jconfirm-white .jconfirm-bg,.jconfirm.jconfirm-light .jconfirm-bg{background-color:#444;opacity:.2}.jconfirm.jconfirm-white .jconfirm-box,.jconfirm.jconfirm-light .jconfirm-box{-webkit-box-shadow:0 2px 6px rgba(0,0,0,0.2);box-shadow:0 2px 6px rgba(0,0,0,0.2);border-radius:5px}.jconfirm.jconfirm-white .jconfirm-box .jconfirm-title-c .jconfirm-icon-c,.jconfirm.jconfirm-light .jconfirm-box .jconfirm-title-c .jconfirm-icon-c{margin-right:8px;margin-left:0}.jconfirm.jconfirm-white .jconfirm-box .jconfirm-buttons,.jconfirm.jconfirm-light .jconfirm-box .jconfirm-buttons{float:right}.jconfirm.jconfirm-white .jconfirm-box .jconfirm-buttons button,.jconfirm.jconfirm-light .jconfirm-box .jconfirm-buttons button{text-transform:uppercase;font-size:14px;font-weight:bold;text-shadow:none}.jconfirm.jconfirm-white .jconfirm-box .jconfirm-buttons button.btn-default,.jconfirm.jconfirm-light .jconfirm-box .jconfirm-buttons button.btn-default{-webkit-box-shadow:none;box-shadow:none;color:#333}.jconfirm.jconfirm-white .jconfirm-box .jconfirm-buttons button.btn-default:hover,.jconfirm.jconfirm-light .jconfirm-box .jconfirm-buttons button.btn-default:hover{background:#ddd}.jconfirm.jconfirm-white.jconfirm-rtl .jconfirm-title-c .jconfirm-icon-c,.jconfirm.jconfirm-light.jconfirm-rtl .jconfirm-title-c .jconfirm-icon-c{margin-left:8px;margin-right:0}.jconfirm.jconfirm-black .jconfirm-bg,.jconfirm.jconfirm-dark .jconfirm-bg{background-color:darkslategray;opacity:.4}.jconfirm.jconfirm-black .jconfirm-box,.jconfirm.jconfirm-dark .jconfirm-box{-webkit-box-shadow:0 2px 6px rgba(0,0,0,0.2);box-shadow:0 2px 6px rgba(0,0,0,0.2);background:#444;border-radius:5px;color:white}.jconfirm.jconfirm-black .jconfirm-box .jconfirm-title-c .jconfirm-icon-c,.jconfirm.jconfirm-dark .jconfirm-box .jconfirm-title-c .jconfirm-icon-c{margin-right:8px;margin-left:0}.jconfirm.jconfirm-black .jconfirm-box .jconfirm-buttons,.jconfirm.jconfirm-dark .jconfirm-box .jconfirm-buttons{float:right}.jconfirm.jconfirm-black .jconfirm-box .jconfirm-buttons button,.jconfirm.jconfirm-dark .jconfirm-box .jconfirm-buttons button{border:0;background-image:none;text-transform:uppercase;font-size:14px;font-weight:bold;text-shadow:none;-webkit-transition:background .1s;transition:background .1s;color:white}.jconfirm.jconfirm-black .jconfirm-box .jconfirm-buttons button.btn-default,.jconfirm.jconfirm-dark .jconfirm-box .jconfirm-buttons button.btn-default{-webkit-box-shadow:none;box-shadow:none;color:#fff;background:0}.jconfirm.jconfirm-black .jconfirm-box .jconfirm-buttons button.btn-default:hover,.jconfirm.jconfirm-dark .jconfirm-box .jconfirm-buttons button.btn-default:hover{background:#666}.jconfirm.jconfirm-black.jconfirm-rtl .jconfirm-title-c .jconfirm-icon-c,.jconfirm.jconfirm-dark.jconfirm-rtl .jconfirm-title-c .jconfirm-icon-c{margin-left:8px;margin-right:0}.jconfirm .jconfirm-box.hilight.jconfirm-hilight-shake{-webkit-animation:shake .82s cubic-bezier(0.36,0.07,0.19,0.97) both;animation:shake .82s cubic-bezier(0.36,0.07,0.19,0.97) both;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.jconfirm .jconfirm-box.hilight.jconfirm-hilight-glow{-webkit-animation:glow .82s cubic-bezier(0.36,0.07,0.19,0.97) both;animation:glow .82s cubic-bezier(0.36,0.07,0.19,0.97) both;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}@-webkit-keyframes shake{10%,90%{-webkit-transform:translate3d(-2px,0,0);transform:translate3d(-2px,0,0)}20%,80%{-webkit-transform:translate3d(4px,0,0);transform:translate3d(4px,0,0)}30%,50%,70%{-webkit-transform:translate3d(-8px,0,0);transform:translate3d(-8px,0,0)}40%,60%{-webkit-transform:translate3d(8px,0,0);transform:translate3d(8px,0,0)}}@keyframes shake{10%,90%{-webkit-transform:translate3d(-2px,0,0);transform:translate3d(-2px,0,0)}20%,80%{-webkit-transform:translate3d(4px,0,0);transform:translate3d(4px,0,0)}30%,50%,70%{-webkit-transform:translate3d(-8px,0,0);transform:translate3d(-8px,0,0)}40%,60%{-webkit-transform:translate3d(8px,0,0);transform:translate3d(8px,0,0)}}@-webkit-keyframes glow{0%,100%{-webkit-box-shadow:0 0 0 red;box-shadow:0 0 0 red}50%{-webkit-box-shadow:0 0 30px red;box-shadow:0 0 30px red}}@keyframes glow{0%,100%{-webkit-box-shadow:0 0 0 red;box-shadow:0 0 0 red}50%{-webkit-box-shadow:0 0 30px red;box-shadow:0 0 30px red}}.jconfirm{-webkit-perspective:400px;perspective:400px}.jconfirm .jconfirm-box{opacity:1;-webkit-transition-property:all;transition-property:all}.jconfirm .jconfirm-box.jconfirm-animation-top,.jconfirm .jconfirm-box.jconfirm-animation-left,.jconfirm .jconfirm-box.jconfirm-animation-right,.jconfirm .jconfirm-box.jconfirm-animation-bottom,.jconfirm .jconfirm-box.jconfirm-animation-opacity,.jconfirm .jconfirm-box.jconfirm-animation-zoom,.jconfirm .jconfirm-box.jconfirm-animation-scale,.jconfirm .jconfirm-box.jconfirm-animation-none,.jconfirm .jconfirm-box.jconfirm-animation-rotate,.jconfirm .jconfirm-box.jconfirm-animation-rotatex,.jconfirm .jconfirm-box.jconfirm-animation-rotatey,.jconfirm .jconfirm-box.jconfirm-animation-scaley,.jconfirm .jconfirm-box.jconfirm-animation-scalex{opacity:0}.jconfirm .jconfirm-box.jconfirm-animation-rotate{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.jconfirm .jconfirm-box.jconfirm-animation-rotatex{-webkit-transform:rotateX(90deg);transform:rotateX(90deg);-webkit-transform-origin:center;transform-origin:center}.jconfirm .jconfirm-box.jconfirm-animation-rotatexr{-webkit-transform:rotateX(-90deg);transform:rotateX(-90deg);-webkit-transform-origin:center;transform-origin:center}.jconfirm .jconfirm-box.jconfirm-animation-rotatey{-webkit-transform:rotatey(90deg);transform:rotatey(90deg);-webkit-transform-origin:center;transform-origin:center}.jconfirm .jconfirm-box.jconfirm-animation-rotateyr{-webkit-transform:rotatey(-90deg);transform:rotatey(-90deg);-webkit-transform-origin:center;transform-origin:center}.jconfirm .jconfirm-box.jconfirm-animation-scaley{-webkit-transform:scaley(1.5);transform:scaley(1.5);-webkit-transform-origin:center;transform-origin:center}.jconfirm .jconfirm-box.jconfirm-animation-scalex{-webkit-transform:scalex(1.5);transform:scalex(1.5);-webkit-transform-origin:center;transform-origin:center}.jconfirm .jconfirm-box.jconfirm-animation-top{-webkit-transform:translate(0px,-100px);transform:translate(0px,-100px)}.jconfirm .jconfirm-box.jconfirm-animation-left{-webkit-transform:translate(-100px,0px);transform:translate(-100px,0px)}.jconfirm .jconfirm-box.jconfirm-animation-right{-webkit-transform:translate(100px,0px);transform:translate(100px,0px)}.jconfirm .jconfirm-box.jconfirm-animation-bottom{-webkit-transform:translate(0px,100px);transform:translate(0px,100px)}.jconfirm .jconfirm-box.jconfirm-animation-zoom{-webkit-transform:scale(1.2);transform:scale(1.2)}.jconfirm .jconfirm-box.jconfirm-animation-scale{-webkit-transform:scale(0.5);transform:scale(0.5)}.jconfirm .jconfirm-box.jconfirm-animation-none{visibility:hidden}.jconfirm.jconfirm-supervan .jconfirm-bg{background-color:rgba(54,70,93,0.95)}.jconfirm.jconfirm-supervan .jconfirm-box{background-color:transparent}.jconfirm.jconfirm-supervan .jconfirm-box.jconfirm-type-blue{border:0}.jconfirm.jconfirm-supervan .jconfirm-box.jconfirm-type-green{border:0}.jconfirm.jconfirm-supervan .jconfirm-box.jconfirm-type-red{border:0}.jconfirm.jconfirm-supervan .jconfirm-box.jconfirm-type-orange{border:0}.jconfirm.jconfirm-supervan .jconfirm-box.jconfirm-type-purple{border:0}.jconfirm.jconfirm-supervan .jconfirm-box.jconfirm-type-dark{border:0}.jconfirm.jconfirm-supervan .jconfirm-box div.jconfirm-closeIcon{color:white}.jconfirm.jconfirm-supervan .jconfirm-box div.jconfirm-title-c{text-align:center;color:white;font-size:28px;font-weight:normal}.jconfirm.jconfirm-supervan .jconfirm-box div.jconfirm-title-c>*{padding-bottom:25px}.jconfirm.jconfirm-supervan .jconfirm-box div.jconfirm-title-c .jconfirm-icon-c{margin-right:8px;margin-left:0}.jconfirm.jconfirm-supervan .jconfirm-box div.jconfirm-content-pane{margin-bottom:25px}.jconfirm.jconfirm-supervan .jconfirm-box div.jconfirm-content{text-align:center;color:white}.jconfirm.jconfirm-supervan .jconfirm-box .jconfirm-buttons{text-align:center}.jconfirm.jconfirm-supervan .jconfirm-box .jconfirm-buttons button{font-size:16px;border-radius:2px;background:#303f53;text-shadow:none;border:0;color:white;padding:10px;min-width:100px}.jconfirm.jconfirm-supervan.jconfirm-rtl .jconfirm-box div.jconfirm-title-c .jconfirm-icon-c{margin-left:8px;margin-right:0}.jconfirm.jconfirm-material .jconfirm-bg{background-color:rgba(0,0,0,0.67)}.jconfirm.jconfirm-material .jconfirm-box{background-color:white;-webkit-box-shadow:0 7px 8px -4px rgba(0,0,0,0.2),0 13px 19px 2px rgba(0,0,0,0.14),0 5px 24px 4px rgba(0,0,0,0.12);box-shadow:0 7px 8px -4px rgba(0,0,0,0.2),0 13px 19px 2px rgba(0,0,0,0.14),0 5px 24px 4px rgba(0,0,0,0.12);padding:30px 25px 10px 25px}.jconfirm.jconfirm-material .jconfirm-box .jconfirm-title-c .jconfirm-icon-c{margin-right:8px;margin-left:0}.jconfirm.jconfirm-material .jconfirm-box div.jconfirm-closeIcon{color:rgba(0,0,0,0.87)}.jconfirm.jconfirm-material .jconfirm-box div.jconfirm-title-c{color:rgba(0,0,0,0.87);font-size:22px;font-weight:bold}.jconfirm.jconfirm-material .jconfirm-box div.jconfirm-content{color:rgba(0,0,0,0.87)}.jconfirm.jconfirm-material .jconfirm-box .jconfirm-buttons{text-align:right}.jconfirm.jconfirm-material .jconfirm-box .jconfirm-buttons button{text-transform:uppercase;font-weight:500}.jconfirm.jconfirm-material.jconfirm-rtl .jconfirm-title-c .jconfirm-icon-c{margin-left:8px;margin-right:0}.jconfirm.jconfirm-bootstrap .jconfirm-bg{background-color:rgba(0,0,0,0.21)}.jconfirm.jconfirm-bootstrap .jconfirm-box{background-color:white;-webkit-box-shadow:0 3px 8px 0 rgba(0,0,0,0.2);box-shadow:0 3px 8px 0 rgba(0,0,0,0.2);border:solid 1px rgba(0,0,0,0.4);padding:15px 0 0}.jconfirm.jconfirm-bootstrap .jconfirm-box .jconfirm-title-c .jconfirm-icon-c{margin-right:8px;margin-left:0}.jconfirm.jconfirm-bootstrap .jconfirm-box div.jconfirm-closeIcon{color:rgba(0,0,0,0.87)}.jconfirm.jconfirm-bootstrap .jconfirm-box div.jconfirm-title-c{color:rgba(0,0,0,0.87);font-size:22px;font-weight:bold;padding-left:15px;padding-right:15px}.jconfirm.jconfirm-bootstrap .jconfirm-box div.jconfirm-content{color:rgba(0,0,0,0.87);padding:0 15px}.jconfirm.jconfirm-bootstrap .jconfirm-box .jconfirm-buttons{text-align:right;padding:10px;margin:-5px 0 0;border-top:solid 1px #ddd;overflow:hidden;border-radius:0 0 4px 4px}.jconfirm.jconfirm-bootstrap .jconfirm-box .jconfirm-buttons button{font-weight:500}.jconfirm.jconfirm-bootstrap.jconfirm-rtl .jconfirm-title-c .jconfirm-icon-c{margin-left:8px;margin-right:0}.jconfirm.jconfirm-modern .jconfirm-bg{background-color:slategray;opacity:.6}.jconfirm.jconfirm-modern .jconfirm-box{background-color:white;-webkit-box-shadow:0 7px 8px -4px rgba(0,0,0,0.2),0 13px 19px 2px rgba(0,0,0,0.14),0 5px 24px 4px rgba(0,0,0,0.12);box-shadow:0 7px 8px -4px rgba(0,0,0,0.2),0 13px 19px 2px rgba(0,0,0,0.14),0 5px 24px 4px rgba(0,0,0,0.12);padding:30px 30px 15px}.jconfirm.jconfirm-modern .jconfirm-box div.jconfirm-closeIcon{color:rgba(0,0,0,0.87);top:15px;right:15px}.jconfirm.jconfirm-modern .jconfirm-box div.jconfirm-title-c{color:rgba(0,0,0,0.87);font-size:24px;font-weight:bold;text-align:center;margin-bottom:10px}.jconfirm.jconfirm-modern .jconfirm-box div.jconfirm-title-c .jconfirm-icon-c{-webkit-transition:-webkit-transform .5s;transition:-webkit-transform .5s;transition:transform .5s;transition:transform .5s,-webkit-transform .5s;-webkit-transform:scale(0);transform:scale(0);display:block;margin-right:0;margin-left:0;margin-bottom:10px;font-size:69px;color:#aaa}.jconfirm.jconfirm-modern .jconfirm-box div.jconfirm-content{text-align:center;font-size:15px;color:#777;margin-bottom:25px}.jconfirm.jconfirm-modern .jconfirm-box .jconfirm-buttons{text-align:center}.jconfirm.jconfirm-modern .jconfirm-box .jconfirm-buttons button{font-weight:bold;text-transform:uppercase;-webkit-transition:background .1s;transition:background .1s;padding:10px 20px}.jconfirm.jconfirm-modern .jconfirm-box .jconfirm-buttons button+button{margin-left:4px}.jconfirm.jconfirm-modern.jconfirm-open .jconfirm-box .jconfirm-title-c .jconfirm-icon-c{-webkit-transform:scale(1);transform:scale(1)}
\ No newline at end of file
diff --git a/ui/media/css/main.css b/ui/media/css/main.css
index 0cf83302..48029fd4 100644
--- a/ui/media/css/main.css
+++ b/ui/media/css/main.css
@@ -210,7 +210,7 @@ code {
}
.collapsible-content {
display: block;
- padding-left: 15px;
+ padding-left: 10px;
}
.collapsible-content h5 {
padding: 5pt 0pt;
@@ -658,11 +658,15 @@ input::file-selector-button {
opacity: 1;
}
-/* MOBILE SUPPORT */
-@media screen and (max-width: 700px) {
+/* Small screens */
+@media screen and (max-width: 1265px) {
#top-nav {
flex-direction: column;
}
+}
+
+/* MOBILE SUPPORT */
+@media screen and (max-width: 700px) {
body {
margin: 0px;
}
@@ -712,7 +716,7 @@ input::file-selector-button {
padding-right: 0px;
}
#server-status {
- display: none;
+ top: 75%;
}
.popup > div {
padding-left: 5px !important;
@@ -730,6 +734,15 @@ input::file-selector-button {
}
}
+@media screen and (max-width: 500px) {
+ #server-status #server-status-msg {
+ display: none;
+ }
+ #server-status:hover #server-status-msg {
+ display: inline;
+ }
+}
+
@media (min-width: 700px) {
/* #editor {
max-width: 480px;
@@ -997,8 +1010,17 @@ button:hover {
button:active {
transition-duration: 0.1s;
background-color: hsl(var(--accent-hue), 100%, calc(var(--accent-lightness) + 24%));
+ position: relative;
+ top: 1px;
+ left: 1px;
}
button#save-system-settings-btn {
padding: 4pt 8pt;
}
+#ip-info a {
+ color:var(--text-color)
+}
+#ip-info div {
+ line-height: 200%;
+}
diff --git a/ui/media/css/themes.css b/ui/media/css/themes.css
index d98ff0ec..6a2db310 100644
--- a/ui/media/css/themes.css
+++ b/ui/media/css/themes.css
@@ -30,6 +30,9 @@
--primary-button-border: none;
--input-switch-padding: 1px;
--input-height: 18px;
+
+ /* Main theme color, hex color fallback. */
+ --theme-color-fallback: #673AB6;
}
.theme-light {
@@ -44,6 +47,8 @@
--input-text-color: black;
--input-background-color: #f8f9fa;
--input-border-color: grey;
+
+ --theme-color-fallback: #aaaaaa;
}
.theme-discord {
@@ -58,6 +63,8 @@
--input-border-size: 2px;
--input-background-color: #202225;
--input-border-color: var(--input-background-color);
+
+ --theme-color-fallback: #202225;
}
.theme-cool-blue {
@@ -71,8 +78,10 @@
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (3 * var(--value-step))));
--input-background-color: var(--background-color3);
-
+
--accent-hue: 212;
+
+ --theme-color-fallback: #0056b8;
}
@@ -87,6 +96,8 @@
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (3 * var(--value-step))));
--input-background-color: var(--background-color3);
+
+ --theme-color-fallback: #5300b8;
}
.theme-super-dark {
@@ -101,6 +112,8 @@
--input-background-color: var(--background-color3);
--input-border-size: 0px;
+
+ --theme-color-fallback: #000000;
}
.theme-wild {
@@ -117,8 +130,8 @@
--input-border-size: 1px;
--input-background-color: hsl(222, var(--main-saturation), calc(var(--value-base) - (2 * var(--value-step))));
- --input-text-color: red;
- --input-border-color: green;
+ --input-text-color: #FF0000;
+ --input-border-color: #005E05;
}
.theme-gnomie {
@@ -136,6 +149,8 @@
--input-background-color: #2a2a2a;
--input-border-size: 0px;
--input-border-color: var(--input-background-color);
+
+ --theme-color-fallback: #2168bf;
}
.theme-gnomie .panel-box {
diff --git a/ui/media/js/auto-save.js b/ui/media/js/auto-save.js
index 29e5298a..f6db831d 100644
--- a/ui/media/js/auto-save.js
+++ b/ui/media/js/auto-save.js
@@ -35,6 +35,7 @@ const SETTINGS_IDS_LIST = [
"sound_toggle",
"turbo",
"use_full_precision",
+ "confirm_dangerous_actions",
"auto_save_settings"
]
@@ -55,6 +56,9 @@ async function initSettings() {
if (!element) {
console.error(`Missing settings element ${id}`)
}
+ if (id in SETTINGS) { // don't create it again
+ return
+ }
SETTINGS[id] = {
key: id,
element: element,
diff --git a/ui/media/js/dnd.js b/ui/media/js/dnd.js
index f344863b..fd6751bb 100644
--- a/ui/media/js/dnd.js
+++ b/ui/media/js/dnd.js
@@ -192,9 +192,9 @@ const TASK_MAPPING = {
parse: (val) => val
},
- numOutputsParallel: { name: 'Parallel Images',
- setUI: (numOutputsParallel) => {
- numOutputsParallelField.value = numOutputsParallel
+ num_outputs: { name: 'Parallel Images',
+ setUI: (num_outputs) => {
+ numOutputsParallelField.value = num_outputs
},
readUI: () => parseInt(numOutputsParallelField.value),
parse: (val) => val
@@ -328,6 +328,7 @@ function getModelPath(filename, extensions)
filename = filename.slice(0, filename.length - ext.length)
}
})
+ return filename
}
const TASK_TEXT_MAPPING = {
diff --git a/ui/media/js/image-modifiers.js b/ui/media/js/image-modifiers.js
index 8ecb6a26..5e67c905 100644
--- a/ui/media/js/image-modifiers.js
+++ b/ui/media/js/image-modifiers.js
@@ -90,9 +90,7 @@ function createModifierGroup(modifierGroup, initiallyExpanded) {
if (activeTags.map(x => trimModifiers(x.name)).includes(trimModifiers(modifierName))) {
// remove modifier from active array
activeTags = activeTags.filter(x => trimModifiers(x.name) != trimModifiers(modifierName))
- modifierCard.classList.remove(activeCardClass)
-
- modifierCard.querySelector('.modifier-card-image-overlay').innerText = '+'
+ toggleCardState(modifierCard, false)
} else {
// add modifier to active array
activeTags.push({
@@ -101,10 +99,7 @@ function createModifierGroup(modifierGroup, initiallyExpanded) {
'originElement': modifierCard,
'previews': modifierPreviews
})
-
- modifierCard.classList.add(activeCardClass)
-
- modifierCard.querySelector('.modifier-card-image-overlay').innerText = '-'
+ toggleCardState(modifierCard, true)
}
refreshTagsList()
@@ -226,8 +221,7 @@ function refreshTagsList() {
let idx = activeTags.indexOf(tag)
if (idx !== -1 && activeTags[idx].originElement !== undefined) {
- activeTags[idx].originElement.classList.remove(activeCardClass)
- activeTags[idx].originElement.querySelector('.modifier-card-image-overlay').innerText = '+'
+ toggleCardState(activeTags[idx].originElement, false)
activeTags.splice(idx, 1)
refreshTagsList()
@@ -240,6 +234,16 @@ function refreshTagsList() {
editorModifierTagsList.appendChild(brk)
}
+function toggleCardState(card, makeActive) {
+ if (makeActive) {
+ card.classList.add(activeCardClass)
+ card.querySelector('.modifier-card-image-overlay').innerText = '-'
+ } else {
+ card.classList.remove(activeCardClass)
+ card.querySelector('.modifier-card-image-overlay').innerText = '+'
+ }
+}
+
function changePreviewImages(val) {
const previewImages = document.querySelectorAll('.modifier-card-image-container img')
diff --git a/ui/media/js/jquery-confirm.min.js b/ui/media/js/jquery-confirm.min.js
new file mode 100644
index 00000000..a2498bba
--- /dev/null
+++ b/ui/media/js/jquery-confirm.min.js
@@ -0,0 +1,10 @@
+/*!
+ * jquery-confirm v3.3.2 (http://craftpip.github.io/jquery-confirm/)
+ * Author: Boniface Pereira
+ * Website: www.craftpip.com
+ * Contact: hey@craftpip.com
+ *
+ * Copyright 2013-2017 jquery-confirm
+ * Licensed under MIT (https://github.com/craftpip/jquery-confirm/blob/master/LICENSE)
+ */
+if(typeof jQuery==="undefined"){throw new Error("jquery-confirm requires jQuery");}var jconfirm,Jconfirm;(function($,window){$.fn.confirm=function(options,option2){if(typeof options==="undefined"){options={};}if(typeof options==="string"){options={content:options,title:(option2)?option2:false};}$(this).each(function(){var $this=$(this);if($this.attr("jc-attached")){console.warn("jConfirm has already been attached to this element ",$this[0]);return;}$this.on("click",function(e){e.preventDefault();var jcOption=$.extend({},options);if($this.attr("data-title")){jcOption.title=$this.attr("data-title");}if($this.attr("data-content")){jcOption.content=$this.attr("data-content");}if(typeof jcOption.buttons=="undefined"){jcOption.buttons={};}jcOption["$target"]=$this;if($this.attr("href")&&Object.keys(jcOption.buttons).length==0){var buttons=$.extend(true,{},jconfirm.pluginDefaults.defaultButtons,(jconfirm.defaults||{}).defaultButtons||{});var firstBtn=Object.keys(buttons)[0];jcOption.buttons=buttons;jcOption.buttons[firstBtn].action=function(){location.href=$this.attr("href");};}jcOption.closeIcon=false;var instance=$.confirm(jcOption);});$this.attr("jc-attached",true);});return $(this);};$.confirm=function(options,option2){if(typeof options==="undefined"){options={};}if(typeof options==="string"){options={content:options,title:(option2)?option2:false};}var putDefaultButtons=!(options.buttons==false);if(typeof options.buttons!="object"){options.buttons={};}if(Object.keys(options.buttons).length==0&&putDefaultButtons){var buttons=$.extend(true,{},jconfirm.pluginDefaults.defaultButtons,(jconfirm.defaults||{}).defaultButtons||{});options.buttons=buttons;}return jconfirm(options);};$.alert=function(options,option2){if(typeof options==="undefined"){options={};}if(typeof options==="string"){options={content:options,title:(option2)?option2:false};}var putDefaultButtons=!(options.buttons==false);if(typeof options.buttons!="object"){options.buttons={};}if(Object.keys(options.buttons).length==0&&putDefaultButtons){var buttons=$.extend(true,{},jconfirm.pluginDefaults.defaultButtons,(jconfirm.defaults||{}).defaultButtons||{});var firstBtn=Object.keys(buttons)[0];options.buttons[firstBtn]=buttons[firstBtn];}return jconfirm(options);};$.dialog=function(options,option2){if(typeof options==="undefined"){options={};}if(typeof options==="string"){options={content:options,title:(option2)?option2:false,closeIcon:function(){}};}options.buttons={};if(typeof options.closeIcon=="undefined"){options.closeIcon=function(){};}options.confirmKeys=[13];return jconfirm(options);};jconfirm=function(options){if(typeof options==="undefined"){options={};}var pluginOptions=$.extend(true,{},jconfirm.pluginDefaults);if(jconfirm.defaults){pluginOptions=$.extend(true,pluginOptions,jconfirm.defaults);}pluginOptions=$.extend(true,{},pluginOptions,options);var instance=new Jconfirm(pluginOptions);jconfirm.instances.push(instance);return instance;};Jconfirm=function(options){$.extend(this,options);this._init();};Jconfirm.prototype={_init:function(){var that=this;if(!jconfirm.instances.length){jconfirm.lastFocused=$("body").find(":focus");}this._id=Math.round(Math.random()*99999);this.contentParsed=$(document.createElement("div"));if(!this.lazyOpen){setTimeout(function(){that.open();},0);}},_buildHTML:function(){var that=this;this._parseAnimation(this.animation,"o");this._parseAnimation(this.closeAnimation,"c");this._parseBgDismissAnimation(this.backgroundDismissAnimation);this._parseColumnClass(this.columnClass);this._parseTheme(this.theme);this._parseType(this.type);var template=$(this.template);template.find(".jconfirm-box").addClass(this.animationParsed).addClass(this.backgroundDismissAnimationParsed).addClass(this.typeParsed);if(this.typeAnimated){template.find(".jconfirm-box").addClass("jconfirm-type-animated");}if(this.useBootstrap){template.find(".jc-bs3-row").addClass(this.bootstrapClasses.row);template.find(".jc-bs3-row").addClass("justify-content-md-center justify-content-sm-center justify-content-xs-center justify-content-lg-center");template.find(".jconfirm-box-container").addClass(this.columnClassParsed);if(this.containerFluid){template.find(".jc-bs3-container").addClass(this.bootstrapClasses.containerFluid);}else{template.find(".jc-bs3-container").addClass(this.bootstrapClasses.container);}}else{template.find(".jconfirm-box").css("width",this.boxWidth);}if(this.titleClass){template.find(".jconfirm-title-c").addClass(this.titleClass);}template.addClass(this.themeParsed);var ariaLabel="jconfirm-box"+this._id;template.find(".jconfirm-box").attr("aria-labelledby",ariaLabel).attr("tabindex",-1);template.find(".jconfirm-content").attr("id",ariaLabel);if(this.bgOpacity!==null){template.find(".jconfirm-bg").css("opacity",this.bgOpacity);}if(this.rtl){template.addClass("jconfirm-rtl");}this.$el=template.appendTo(this.container);this.$jconfirmBoxContainer=this.$el.find(".jconfirm-box-container");this.$jconfirmBox=this.$body=this.$el.find(".jconfirm-box");this.$jconfirmBg=this.$el.find(".jconfirm-bg");this.$title=this.$el.find(".jconfirm-title");this.$titleContainer=this.$el.find(".jconfirm-title-c");this.$content=this.$el.find("div.jconfirm-content");this.$contentPane=this.$el.find(".jconfirm-content-pane");this.$icon=this.$el.find(".jconfirm-icon-c");this.$closeIcon=this.$el.find(".jconfirm-closeIcon");this.$holder=this.$el.find(".jconfirm-holder");this.$btnc=this.$el.find(".jconfirm-buttons");this.$scrollPane=this.$el.find(".jconfirm-scrollpane");that.setStartingPoint();this._contentReady=$.Deferred();this._modalReady=$.Deferred();this.$holder.css({"padding-top":this.offsetTop,"padding-bottom":this.offsetBottom,});this.setTitle();this.setIcon();this._setButtons();this._parseContent();this.initDraggable();if(this.isAjax){this.showLoading(false);}$.when(this._contentReady,this._modalReady).then(function(){if(that.isAjaxLoading){setTimeout(function(){that.isAjaxLoading=false;that.setContent();that.setTitle();that.setIcon();setTimeout(function(){that.hideLoading(false);that._updateContentMaxHeight();},100);if(typeof that.onContentReady==="function"){that.onContentReady();}},50);}else{that._updateContentMaxHeight();that.setTitle();that.setIcon();if(typeof that.onContentReady==="function"){that.onContentReady();}}if(that.autoClose){that._startCountDown();}});this._watchContent();if(this.animation==="none"){this.animationSpeed=1;this.animationBounce=1;}this.$body.css(this._getCSS(this.animationSpeed,this.animationBounce));this.$contentPane.css(this._getCSS(this.animationSpeed,1));this.$jconfirmBg.css(this._getCSS(this.animationSpeed,1));this.$jconfirmBoxContainer.css(this._getCSS(this.animationSpeed,1));},_typePrefix:"jconfirm-type-",typeParsed:"",_parseType:function(type){this.typeParsed=this._typePrefix+type;},setType:function(type){var oldClass=this.typeParsed;this._parseType(type);this.$jconfirmBox.removeClass(oldClass).addClass(this.typeParsed);},themeParsed:"",_themePrefix:"jconfirm-",setTheme:function(theme){var previous=this.theme;this.theme=theme||this.theme;this._parseTheme(this.theme);if(previous){this.$el.removeClass(previous);}this.$el.addClass(this.themeParsed);this.theme=theme;},_parseTheme:function(theme){var that=this;theme=theme.split(",");$.each(theme,function(k,a){if(a.indexOf(that._themePrefix)===-1){theme[k]=that._themePrefix+$.trim(a);}});this.themeParsed=theme.join(" ").toLowerCase();},backgroundDismissAnimationParsed:"",_bgDismissPrefix:"jconfirm-hilight-",_parseBgDismissAnimation:function(bgDismissAnimation){var animation=bgDismissAnimation.split(",");var that=this;$.each(animation,function(k,a){if(a.indexOf(that._bgDismissPrefix)===-1){animation[k]=that._bgDismissPrefix+$.trim(a);}});this.backgroundDismissAnimationParsed=animation.join(" ").toLowerCase();},animationParsed:"",closeAnimationParsed:"",_animationPrefix:"jconfirm-animation-",setAnimation:function(animation){this.animation=animation||this.animation;this._parseAnimation(this.animation,"o");},_parseAnimation:function(animation,which){which=which||"o";var animations=animation.split(",");var that=this;$.each(animations,function(k,a){if(a.indexOf(that._animationPrefix)===-1){animations[k]=that._animationPrefix+$.trim(a);}});var a_string=animations.join(" ").toLowerCase();if(which==="o"){this.animationParsed=a_string;}else{this.closeAnimationParsed=a_string;}return a_string;},setCloseAnimation:function(closeAnimation){this.closeAnimation=closeAnimation||this.closeAnimation;this._parseAnimation(this.closeAnimation,"c");},setAnimationSpeed:function(speed){this.animationSpeed=speed||this.animationSpeed;},columnClassParsed:"",setColumnClass:function(colClass){if(!this.useBootstrap){console.warn("cannot set columnClass, useBootstrap is set to false");return;}this.columnClass=colClass||this.columnClass;this._parseColumnClass(this.columnClass);this.$jconfirmBoxContainer.addClass(this.columnClassParsed);},_updateContentMaxHeight:function(){var height=$(window).height()-(this.$jconfirmBox.outerHeight()-this.$contentPane.outerHeight())-(this.offsetTop+this.offsetBottom);this.$contentPane.css({"max-height":height+"px"});},setBoxWidth:function(width){if(this.useBootstrap){console.warn("cannot set boxWidth, useBootstrap is set to true");return;}this.boxWidth=width;this.$jconfirmBox.css("width",width);},_parseColumnClass:function(colClass){colClass=colClass.toLowerCase();var p;switch(colClass){case"xl":case"xlarge":p="col-md-12";break;case"l":case"large":p="col-md-8 col-md-offset-2";break;case"m":case"medium":p="col-md-6 col-md-offset-3";break;case"s":case"small":p="col-md-4 col-md-offset-4";break;case"xs":case"xsmall":p="col-md-2 col-md-offset-5";break;default:p=colClass;}this.columnClassParsed=p;},initDraggable:function(){var that=this;var $t=this.$titleContainer;this.resetDrag();if(this.draggable){$t.on("mousedown",function(e){$t.addClass("jconfirm-hand");that.mouseX=e.clientX;that.mouseY=e.clientY;that.isDrag=true;});$(window).on("mousemove."+this._id,function(e){if(that.isDrag){that.movingX=e.clientX-that.mouseX+that.initialX;that.movingY=e.clientY-that.mouseY+that.initialY;that.setDrag();}});$(window).on("mouseup."+this._id,function(){$t.removeClass("jconfirm-hand");if(that.isDrag){that.isDrag=false;that.initialX=that.movingX;that.initialY=that.movingY;}});}},resetDrag:function(){this.isDrag=false;this.initialX=0;this.initialY=0;this.movingX=0;this.movingY=0;this.mouseX=0;this.mouseY=0;this.$jconfirmBoxContainer.css("transform","translate("+0+"px, "+0+"px)");},setDrag:function(){if(!this.draggable){return;}this.alignMiddle=false;var boxWidth=this.$jconfirmBox.outerWidth();var boxHeight=this.$jconfirmBox.outerHeight();var windowWidth=$(window).width();var windowHeight=$(window).height();var that=this;var dragUpdate=1;if(that.movingX%dragUpdate===0||that.movingY%dragUpdate===0){if(that.dragWindowBorder){var leftDistance=(windowWidth/2)-boxWidth/2;var topDistance=(windowHeight/2)-boxHeight/2;topDistance-=that.dragWindowGap;leftDistance-=that.dragWindowGap;if(leftDistance+that.movingX<0){that.movingX=-leftDistance;}else{if(leftDistance-that.movingX<0){that.movingX=leftDistance;}}if(topDistance+that.movingY<0){that.movingY=-topDistance;}else{if(topDistance-that.movingY<0){that.movingY=topDistance;}}}that.$jconfirmBoxContainer.css("transform","translate("+that.movingX+"px, "+that.movingY+"px)");}},_scrollTop:function(){if(typeof pageYOffset!=="undefined"){return pageYOffset;}else{var B=document.body;var D=document.documentElement;D=(D.clientHeight)?D:B;return D.scrollTop;}},_watchContent:function(){var that=this;if(this._timer){clearInterval(this._timer);}var prevContentHeight=0;this._timer=setInterval(function(){if(that.smoothContent){var contentHeight=that.$content.outerHeight()||0;if(contentHeight!==prevContentHeight){that.$contentPane.css({height:contentHeight}).scrollTop(0);prevContentHeight=contentHeight;}var wh=$(window).height();var total=that.offsetTop+that.offsetBottom+that.$jconfirmBox.height()-that.$contentPane.height()+that.$content.height();if(total
').html(that.buttons[key].text).addClass(that.buttons[key].btnClass).prop("disabled",that.buttons[key].isDisabled).css("display",that.buttons[key].isHidden?"none":"").click(function(e){e.preventDefault();var res=that.buttons[key].action.apply(that,[that.buttons[key]]);that.onAction.apply(that,[key,that.buttons[key]]);that._stopCountDown();if(typeof res==="undefined"||res){that.close();}});that.buttons[key].el=button_element;that.buttons[key].setText=function(text){button_element.html(text);};that.buttons[key].addClass=function(className){button_element.addClass(className);};that.buttons[key].removeClass=function(className){button_element.removeClass(className);};that.buttons[key].disable=function(){that.buttons[key].isDisabled=true;button_element.prop("disabled",true);};that.buttons[key].enable=function(){that.buttons[key].isDisabled=false;button_element.prop("disabled",false);};that.buttons[key].show=function(){that.buttons[key].isHidden=false;button_element.css("display","");};that.buttons[key].hide=function(){that.buttons[key].isHidden=true;button_element.css("display","none");};that["$_"+key]=that["$$"+key]=button_element;that.$btnc.append(button_element);});if(total_buttons===0){this.$btnc.hide();}if(this.closeIcon===null&&total_buttons===0){this.closeIcon=true;}if(this.closeIcon){if(this.closeIconClass){var closeHtml=' ';this.$closeIcon.html(closeHtml);}this.$closeIcon.click(function(e){e.preventDefault();var buttonName=false;var shouldClose=false;var str;if(typeof that.closeIcon=="function"){str=that.closeIcon();}else{str=that.closeIcon;}if(typeof str=="string"&&typeof that.buttons[str]!="undefined"){buttonName=str;shouldClose=false;}else{if(typeof str=="undefined"||!!(str)==true){shouldClose=true;}else{shouldClose=false;}}if(buttonName){var btnResponse=that.buttons[buttonName].action.apply(that);shouldClose=(typeof btnResponse=="undefined")||!!(btnResponse);}if(shouldClose){that.close();}});this.$closeIcon.show();}else{this.$closeIcon.hide();}},setTitle:function(string,force){force=force||false;if(typeof string!=="undefined"){if(typeof string=="string"){this.title=string;}else{if(typeof string=="function"){if(typeof string.promise=="function"){console.error("Promise was returned from title function, this is not supported.");}var response=string();if(typeof response=="string"){this.title=response;}else{this.title=false;}}else{this.title=false;}}}if(this.isAjaxLoading&&!force){return;}this.$title.html(this.title||"");this.updateTitleContainer();},setIcon:function(iconClass,force){force=force||false;if(typeof iconClass!=="undefined"){if(typeof iconClass=="string"){this.icon=iconClass;}else{if(typeof iconClass==="function"){var response=iconClass();if(typeof response=="string"){this.icon=response;}else{this.icon=false;}}else{this.icon=false;}}}if(this.isAjaxLoading&&!force){return;}this.$icon.html(this.icon?' ':"");this.updateTitleContainer();},updateTitleContainer:function(){if(!this.title&&!this.icon){this.$titleContainer.hide();}else{this.$titleContainer.show();}},setContentPrepend:function(content,force){if(!content){return;}this.contentParsed.prepend(content);},setContentAppend:function(content){if(!content){return;}this.contentParsed.append(content);},setContent:function(content,force){force=!!force;var that=this;if(content){this.contentParsed.html("").append(content);}if(this.isAjaxLoading&&!force){return;}this.$content.html("");this.$content.append(this.contentParsed);setTimeout(function(){that.$body.find("input[autofocus]:visible:first").focus();},100);},loadingSpinner:false,showLoading:function(disableButtons){this.loadingSpinner=true;this.$jconfirmBox.addClass("loading");if(disableButtons){this.$btnc.find("button").prop("disabled",true);}},hideLoading:function(enableButtons){this.loadingSpinner=false;this.$jconfirmBox.removeClass("loading");if(enableButtons){this.$btnc.find("button").prop("disabled",false);}},ajaxResponse:false,contentParsed:"",isAjax:false,isAjaxLoading:false,_parseContent:function(){var that=this;var e=" ";if(typeof this.content=="function"){var res=this.content.apply(this);if(typeof res=="string"){this.content=res;}else{if(typeof res=="object"&&typeof res.always=="function"){this.isAjax=true;this.isAjaxLoading=true;res.always(function(data,status,xhr){that.ajaxResponse={data:data,status:status,xhr:xhr};that._contentReady.resolve(data,status,xhr);if(typeof that.contentLoaded=="function"){that.contentLoaded(data,status,xhr);}});this.content=e;}else{this.content=e;}}}if(typeof this.content=="string"&&this.content.substr(0,4).toLowerCase()==="url:"){this.isAjax=true;this.isAjaxLoading=true;var u=this.content.substring(4,this.content.length);$.get(u).done(function(html){that.contentParsed.html(html);}).always(function(data,status,xhr){that.ajaxResponse={data:data,status:status,xhr:xhr};that._contentReady.resolve(data,status,xhr);if(typeof that.contentLoaded=="function"){that.contentLoaded(data,status,xhr);}});}if(!this.content){this.content=e;}if(!this.isAjax){this.contentParsed.html(this.content);this.setContent();that._contentReady.resolve();}},_stopCountDown:function(){clearInterval(this.autoCloseInterval);if(this.$cd){this.$cd.remove();}},_startCountDown:function(){var that=this;var opt=this.autoClose.split("|");if(opt.length!==2){console.error("Invalid option for autoClose. example 'close|10000'");return false;}var button_key=opt[0];var time=parseInt(opt[1]);if(typeof this.buttons[button_key]==="undefined"){console.error("Invalid button key '"+button_key+"' for autoClose");return false;}var seconds=Math.ceil(time/1000);this.$cd=$(' ('+seconds+") ").appendTo(this["$_"+button_key]);this.autoCloseInterval=setInterval(function(){that.$cd.html(" ("+(seconds-=1)+") ");if(seconds<=0){that["$$"+button_key].trigger("click");that._stopCountDown();}},1000);},_getKey:function(key){switch(key){case 192:return"tilde";case 13:return"enter";case 16:return"shift";case 9:return"tab";case 20:return"capslock";case 17:return"ctrl";case 91:return"win";case 18:return"alt";case 27:return"esc";case 32:return"space";}var initial=String.fromCharCode(key);if(/^[A-z0-9]+$/.test(initial)){return initial.toLowerCase();}else{return false;}},reactOnKey:function(e){var that=this;var a=$(".jconfirm");if(a.eq(a.length-1)[0]!==this.$el[0]){return false;}var key=e.which;if(this.$content.find(":input").is(":focus")&&/13|32/.test(key)){return false;}var keyChar=this._getKey(key);if(keyChar==="esc"&&this.escapeKey){if(this.escapeKey===true){this.$scrollPane.trigger("click");}else{if(typeof this.escapeKey==="string"||typeof this.escapeKey==="function"){var buttonKey;if(typeof this.escapeKey==="function"){buttonKey=this.escapeKey();}else{buttonKey=this.escapeKey;}if(buttonKey){if(typeof this.buttons[buttonKey]==="undefined"){console.warn("Invalid escapeKey, no buttons found with key "+buttonKey);}else{this["$_"+buttonKey].trigger("click");}}}}}$.each(this.buttons,function(key,button){if(button.keys.indexOf(keyChar)!=-1){that["$_"+key].trigger("click");}});},setDialogCenter:function(){console.info("setDialogCenter is deprecated, dialogs are centered with CSS3 tables");},_unwatchContent:function(){clearInterval(this._timer);},close:function(onClosePayload){var that=this;if(typeof this.onClose==="function"){this.onClose(onClosePayload);}this._unwatchContent();$(window).unbind("resize."+this._id);$(window).unbind("keyup."+this._id);$(window).unbind("jcKeyDown."+this._id);if(this.draggable){$(window).unbind("mousemove."+this._id);$(window).unbind("mouseup."+this._id);this.$titleContainer.unbind("mousedown");}that.$el.removeClass(that.loadedClass);$("body").removeClass("jconfirm-no-scroll-"+that._id);that.$jconfirmBoxContainer.removeClass("jconfirm-no-transition");setTimeout(function(){that.$body.addClass(that.closeAnimationParsed);that.$jconfirmBg.addClass("jconfirm-bg-h");var closeTimer=(that.closeAnimation==="none")?1:that.animationSpeed;setTimeout(function(){that.$el.remove();var l=jconfirm.instances;var i=jconfirm.instances.length-1;for(i;i>=0;i--){if(jconfirm.instances[i]._id===that._id){jconfirm.instances.splice(i,1);}}if(!jconfirm.instances.length){if(that.scrollToPreviousElement&&jconfirm.lastFocused&&jconfirm.lastFocused.length&&$.contains(document,jconfirm.lastFocused[0])){var $lf=jconfirm.lastFocused;if(that.scrollToPreviousElementAnimate){var st=$(window).scrollTop();var ot=jconfirm.lastFocused.offset().top;var wh=$(window).height();if(!(ot>st&&ot<(st+wh))){var scrollTo=(ot-Math.round((wh/3)));$("html, body").animate({scrollTop:scrollTo},that.animationSpeed,"swing",function(){$lf.focus();});}else{$lf.focus();}}else{$lf.focus();}jconfirm.lastFocused=false;}}if(typeof that.onDestroy==="function"){that.onDestroy();}},closeTimer*0.4);},50);return true;},open:function(){if(this.isOpen()){return false;}this._buildHTML();this._bindEvents();this._open();return true;},setStartingPoint:function(){var el=false;if(this.animateFromElement!==true&&this.animateFromElement){el=this.animateFromElement;jconfirm.lastClicked=false;}else{if(jconfirm.lastClicked&&this.animateFromElement===true){el=jconfirm.lastClicked;jconfirm.lastClicked=false;}else{return false;}}if(!el){return false;}var offset=el.offset();var iTop=el.outerHeight()/2;var iLeft=el.outerWidth()/2;iTop-=this.$jconfirmBox.outerHeight()/2;iLeft-=this.$jconfirmBox.outerWidth()/2;var sourceTop=offset.top+iTop;sourceTop=sourceTop-this._scrollTop();var sourceLeft=offset.left+iLeft;var wh=$(window).height()/2;var ww=$(window).width()/2;var targetH=wh-this.$jconfirmBox.outerHeight()/2;var targetW=ww-this.$jconfirmBox.outerWidth()/2;sourceTop-=targetH;sourceLeft-=targetW;if(Math.abs(sourceTop)>wh||Math.abs(sourceLeft)>ww){return false;}this.$jconfirmBoxContainer.css("transform","translate("+sourceLeft+"px, "+sourceTop+"px)");},_open:function(){var that=this;if(typeof that.onOpenBefore==="function"){that.onOpenBefore();}this.$body.removeClass(this.animationParsed);this.$jconfirmBg.removeClass("jconfirm-bg-h");this.$body.focus();that.$jconfirmBoxContainer.css("transform","translate("+0+"px, "+0+"px)");setTimeout(function(){that.$body.css(that._getCSS(that.animationSpeed,1));that.$body.css({"transition-property":that.$body.css("transition-property")+", margin"});that.$jconfirmBoxContainer.addClass("jconfirm-no-transition");that._modalReady.resolve();if(typeof that.onOpen==="function"){that.onOpen();}that.$el.addClass(that.loadedClass);},this.animationSpeed);},loadedClass:"jconfirm-open",isClosed:function(){return !this.$el||this.$el.css("display")==="";},isOpen:function(){return !this.isClosed();},toggle:function(){if(!this.isOpen()){this.open();}else{this.close();}}};jconfirm.instances=[];jconfirm.lastFocused=false;jconfirm.pluginDefaults={template:'',title:"Hello",titleClass:"",type:"default",typeAnimated:true,draggable:true,dragWindowGap:15,dragWindowBorder:true,animateFromElement:true,alignMiddle:true,smoothContent:true,content:"Are you sure to continue?",buttons:{},defaultButtons:{ok:{action:function(){}},close:{action:function(){}}},contentLoaded:function(){},icon:"",lazyOpen:false,bgOpacity:null,theme:"light",animation:"scale",closeAnimation:"scale",animationSpeed:400,animationBounce:1,escapeKey:true,rtl:false,container:"body",containerFluid:false,backgroundDismiss:false,backgroundDismissAnimation:"shake",autoClose:false,closeIcon:null,closeIconClass:false,watchInterval:100,columnClass:"col-md-4 col-md-offset-4 col-sm-6 col-sm-offset-3 col-xs-10 col-xs-offset-1",boxWidth:"50%",scrollToPreviousElement:true,scrollToPreviousElementAnimate:true,useBootstrap:true,offsetTop:40,offsetBottom:40,bootstrapClasses:{container:"container",containerFluid:"container-fluid",row:"row"},onContentReady:function(){},onOpenBefore:function(){},onOpen:function(){},onClose:function(){},onDestroy:function(){},onAction:function(){}};var keyDown=false;$(window).on("keydown",function(e){if(!keyDown){var $target=$(e.target);var pass=false;if($target.closest(".jconfirm-box").length){pass=true;}if(pass){$(window).trigger("jcKeyDown");}keyDown=true;}});$(window).on("keyup",function(){keyDown=false;});jconfirm.lastClicked=false;$(document).on("mousedown","button, a",function(){jconfirm.lastClicked=$(this);});})(jQuery,window);
\ No newline at end of file
diff --git a/ui/media/js/main.js b/ui/media/js/main.js
index 2811a50c..ec0d1e5a 100644
--- a/ui/media/js/main.js
+++ b/ui/media/js/main.js
@@ -138,6 +138,33 @@ function isServerAvailable() {
}
}
+// shiftOrConfirm(e, prompt, fn)
+// e : MouseEvent
+// prompt : Text to be shown as prompt. Should be a question to which "yes" is a good answer.
+// fn : function to be called if the user confirms the dialog or has the shift key pressed
+//
+// If the user had the shift key pressed while clicking, the function fn will be executed.
+// If the setting "confirm_dangerous_actions" in the system settings is disabled, the function
+// fn will be executed.
+// Otherwise, a confirmation dialog is shown. If the user confirms, the function fn will also
+// be executed.
+function shiftOrConfirm(e, prompt, fn) {
+ e.stopPropagation()
+ if (e.shiftKey || !confirmDangerousActionsField.checked) {
+ fn(e)
+ } else {
+ $.confirm({ theme: 'supervan',
+ title: prompt,
+ content: 'Tip: To skip this dialog, use shift-click or disable the setting "Confirm dangerous actions" in the systems setting.',
+ buttons: {
+ yes: () => { fn(e) },
+ cancel: () => {}
+ }
+ });
+ }
+}
+
+
function logMsg(msg, level, outputMsg) {
if (outputMsg.hasChildNodes()) {
outputMsg.appendChild(document.createElement('br'))
@@ -169,34 +196,6 @@ function playSound() {
})
}
}
-function setSystemInfo(devices) {
- let cpu = devices.all.cpu.name
- let allGPUs = Object.keys(devices.all).filter(d => d != 'cpu')
- let activeGPUs = Object.keys(devices.active)
-
- function ID_TO_TEXT(d) {
- let info = devices.all[d]
- if ("mem_free" in info && "mem_total" in info) {
- return `${info.name} (${d}) (${info.mem_free.toFixed(1)}Gb free / ${info.mem_total.toFixed(1)} Gb total) `
- } else {
- return `${info.name} (${d}) (no memory info) `
- }
- }
-
- allGPUs = allGPUs.map(ID_TO_TEXT)
- activeGPUs = activeGPUs.map(ID_TO_TEXT)
-
- let systemInfo = `
-
- Processor: ${cpu}
- Compatible Graphics Cards (all): ${allGPUs.join('')}
-
- Used for rendering 🔥: ${activeGPUs.join('')}
-
`
-
- let systemInfoEl = document.querySelector('#system-info')
- systemInfoEl.innerHTML = systemInfo
-}
async function healthCheck() {
try {
@@ -231,7 +230,7 @@ async function healthCheck() {
break
}
if (serverState.devices) {
- setSystemInfo(serverState.devices)
+ setDeviceInfo(serverState.devices)
}
serverState.time = Date.now()
} catch (e) {
@@ -887,8 +886,7 @@ function createTask(task) {
task['progressBar'] = taskEntry.querySelector('.progress-bar')
task['stopTask'] = taskEntry.querySelector('.stopTask')
- task['stopTask'].addEventListener('click', async function(e) {
- e.stopPropagation()
+ task['stopTask'].addEventListener('click', (e) => { shiftOrConfirm(e, "Are you sure? Should this task be stopped?", async function(e) {
if (task['isProcessing']) {
task.isProcessing = false
task.progressBar.classList.remove("active")
@@ -903,9 +901,9 @@ function createTask(task) {
taskQueue.splice(idx, 1)
}
- taskEntry.remove()
+ removeTask(taskEntry)
}
- })
+ })})
task['useSettings'] = taskEntry.querySelector('.useSettings')
task['useSettings'].addEventListener('click', function(e) {
@@ -934,10 +932,10 @@ function getPrompts() {
prompts = prompts.filter(prompt => prompt !== '')
if (activeTags.length > 0) {
- const promptTags = activeTags.map(x => x.name).join(", ")
- prompts = prompts.map((prompt) => `${prompt}, ${promptTags}`)
+ const promptTags = activeTags.map(x => x.name).join(", ")
+ prompts = prompts.map((prompt) => `${prompt}, ${promptTags}`)
}
-
+
let promptsToMake = applySetOperator(prompts)
promptsToMake = applyPermuteOperator(promptsToMake)
@@ -1047,21 +1045,25 @@ async function stopAllTasks() {
}
}
-clearAllPreviewsBtn.addEventListener('click', async function() {
+function removeTask(taskToRemove) {
+ taskToRemove.remove()
+
+ if (document.querySelector('.imageTaskContainer') === null) {
+ previewTools.style.display = 'none'
+ initialText.style.display = 'block'
+ }
+}
+
+clearAllPreviewsBtn.addEventListener('click', (e) => { shiftOrConfirm(e, "Are you sure? Remove all results and tasks from the results pane?", async function() {
await stopAllTasks()
let taskEntries = document.querySelectorAll('.imageTaskContainer')
- taskEntries.forEach(task => {
- task.remove()
- })
+ taskEntries.forEach(removeTask)
+})})
- previewTools.style.display = 'none'
- initialText.style.display = 'block'
-})
-
-stopImageBtn.addEventListener('click', async function() {
+stopImageBtn.addEventListener('click', (e) => { shiftOrConfirm(e, "Are you sure? Do you want to stop all the tasks?", async function(e) {
await stopAllTasks()
-})
+})})
widthField.addEventListener('change', onDimensionChange)
heightField.addEventListener('change', onDimensionChange)
diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js
index dac008ef..62203002 100644
--- a/ui/media/js/parameters.js
+++ b/ui/media/js/parameters.js
@@ -5,9 +5,9 @@
*/
var ParameterType = {
checkbox: "checkbox",
- select: "select",
- select_multiple: "select_multiple",
- custom: "custom",
+ select: "select",
+ select_multiple: "select_multiple",
+ custom: "custom",
};
/**
@@ -23,174 +23,182 @@
/** @type {Array.} */
var PARAMETERS = [
- {
- id: "theme",
- type: ParameterType.select,
- label: "Theme",
- default: "theme-default",
- note: "customize the look and feel of the ui",
- options: [ // Note: options expanded dynamically
- {
- value: "theme-default",
- label: "Default"
- }
- ],
- icon: "fa-palette"
- },
- {
- id: "save_to_disk",
- type: ParameterType.checkbox,
- label: "Auto-Save Images",
- note: "automatically saves images to the specified location",
- icon: "fa-download",
- default: false,
- },
- {
- id: "diskPath",
- type: ParameterType.custom,
- label: "Save Location",
- render: (parameter) => {
- return ` `
- }
- },
- {
- id: "sound_toggle",
- type: ParameterType.checkbox,
- label: "Enable Sound",
- note: "plays a sound on task completion",
- icon: "fa-volume-low",
- default: true,
- },
- {
- id: "ui_open_browser_on_start",
- type: ParameterType.checkbox,
- label: "Open browser on startup",
- note: "starts the default browser on startup",
- icon: "fa-window-restore",
- default: true,
- },
- {
- id: "turbo",
- type: ParameterType.checkbox,
- label: "Turbo Mode",
- note: "generates images faster, but uses an additional 1 GB of GPU memory",
- icon: "fa-forward",
- default: true,
- },
- {
- id: "use_cpu",
- type: ParameterType.checkbox,
- label: "Use CPU (not GPU)",
- note: "warning: this will be *very* slow",
- icon: "fa-microchip",
- default: false,
- },
- {
- id: "auto_pick_gpus",
- type: ParameterType.checkbox,
- label: "Automatically pick the GPUs (experimental)",
- default: false,
- },
- {
- id: "use_gpus",
- type: ParameterType.select_multiple,
- label: "GPUs to use (experimental)",
- note: "to process in parallel",
- default: false,
- },
- {
- id: "use_full_precision",
- type: ParameterType.checkbox,
- label: "Use Full Precision",
- note: "for GPU-only. warning: this will consume more VRAM",
- icon: "fa-crosshairs",
- default: false,
- },
- {
- id: "auto_save_settings",
- type: ParameterType.checkbox,
- label: "Auto-Save Settings",
- note: "restores settings on browser load",
- icon: "fa-gear",
- default: true,
- },
- {
- id: "listen_to_network",
- type: ParameterType.checkbox,
- label: "Make Stable Diffusion available on your network",
- note: "Other devices on your network can access this web page",
- icon: "fa-network-wired",
- default: true,
- },
- {
- id: "listen_port",
- type: ParameterType.custom,
- label: "Network port",
- note: "Port that this server listens to. The '9000' part in 'http://localhost:9000'",
- icon: "fa-anchor",
- render: (parameter) => {
- return ` `
- }
- },
- {
- id: "test_sd2",
- type: ParameterType.checkbox,
- label: "Test SD 2.0",
- note: "Experimental! High memory usage! GPU-only! Please restart the program after changing this.",
- icon: "fa-fire",
- default: false,
- },
- {
- id: "use_beta_channel",
- type: ParameterType.checkbox,
- label: "Beta channel",
- note: "Get the latest features immediately (but could be less stable). Please restart the program after changing this.",
- icon: "fa-fire",
- default: false,
- },
+ {
+ id: "theme",
+ type: ParameterType.select,
+ label: "Theme",
+ default: "theme-default",
+ note: "customize the look and feel of the ui",
+ options: [ // Note: options expanded dynamically
+ {
+ value: "theme-default",
+ label: "Default"
+ }
+ ],
+ icon: "fa-palette"
+ },
+ {
+ id: "save_to_disk",
+ type: ParameterType.checkbox,
+ label: "Auto-Save Images",
+ note: "automatically saves images to the specified location",
+ icon: "fa-download",
+ default: false,
+ },
+ {
+ id: "diskPath",
+ type: ParameterType.custom,
+ label: "Save Location",
+ render: (parameter) => {
+ return ` `
+ }
+ },
+ {
+ id: "sound_toggle",
+ type: ParameterType.checkbox,
+ label: "Enable Sound",
+ note: "plays a sound on task completion",
+ icon: "fa-volume-low",
+ default: true,
+ },
+ {
+ id: "ui_open_browser_on_start",
+ type: ParameterType.checkbox,
+ label: "Open browser on startup",
+ note: "starts the default browser on startup",
+ icon: "fa-window-restore",
+ default: true,
+ },
+ {
+ id: "turbo",
+ type: ParameterType.checkbox,
+ label: "Turbo Mode",
+ note: "generates images faster, but uses an additional 1 GB of GPU memory",
+ icon: "fa-forward",
+ default: true,
+ },
+ {
+ id: "use_cpu",
+ type: ParameterType.checkbox,
+ label: "Use CPU (not GPU)",
+ note: "warning: this will be *very* slow",
+ icon: "fa-microchip",
+ default: false,
+ },
+ {
+ id: "auto_pick_gpus",
+ type: ParameterType.checkbox,
+ label: "Automatically pick the GPUs (experimental)",
+ default: false,
+ },
+ {
+ id: "use_gpus",
+ type: ParameterType.select_multiple,
+ label: "GPUs to use (experimental)",
+ note: "to process in parallel",
+ default: false,
+ },
+ {
+ id: "use_full_precision",
+ type: ParameterType.checkbox,
+ label: "Use Full Precision",
+ note: "for GPU-only. warning: this will consume more VRAM",
+ icon: "fa-crosshairs",
+ default: false,
+ },
+ {
+ id: "auto_save_settings",
+ type: ParameterType.checkbox,
+ label: "Auto-Save Settings",
+ note: "restores settings on browser load",
+ icon: "fa-gear",
+ default: true,
+ },
+ {
+ id: "confirm_dangerous_actions",
+ type: ParameterType.checkbox,
+ label: "Confirm dangerous actions",
+ note: "Actions that might lead to data loss must either be clicked with the shift key pressed, or confirmed in an 'Are you sure?' dialog",
+ icon: "fa-check-double",
+ default: true,
+ },
+ {
+ id: "listen_to_network",
+ type: ParameterType.checkbox,
+ label: "Make Stable Diffusion available on your network",
+ note: "Other devices on your network can access this web page",
+ icon: "fa-network-wired",
+ default: true,
+ },
+ {
+ id: "listen_port",
+ type: ParameterType.custom,
+ label: "Network port",
+ note: "Port that this server listens to. The '9000' part in 'http://localhost:9000'",
+ icon: "fa-anchor",
+ render: (parameter) => {
+ return ` `
+ }
+ },
+ {
+ id: "test_sd2",
+ type: ParameterType.checkbox,
+ label: "Test SD 2.0",
+ note: "Experimental! High memory usage! GPU-only! Not the final version! Please restart the program after changing this.",
+ icon: "fa-fire",
+ default: false,
+ },
+ {
+ id: "use_beta_channel",
+ type: ParameterType.checkbox,
+ label: "Beta channel",
+ note: "Get the latest features immediately (but could be less stable). Please restart the program after changing this.",
+ icon: "fa-fire",
+ default: false,
+ },
];
function getParameterSettingsEntry(id) {
- let parameter = PARAMETERS.filter(p => p.id === id)
- if (parameter.length === 0) {
- return
- }
- return parameter[0].settingsEntry
+ let parameter = PARAMETERS.filter(p => p.id === id)
+ if (parameter.length === 0) {
+ return
+ }
+ return parameter[0].settingsEntry
}
function getParameterElement(parameter) {
- switch (parameter.type) {
- case ParameterType.checkbox:
- var is_checked = parameter.default ? " checked" : "";
- return ` `
- case ParameterType.select:
- case ParameterType.select_multiple:
- var options = (parameter.options || []).map(option => `${option.label} `).join("")
- var multiple = (parameter.type == ParameterType.select_multiple ? 'multiple' : '')
- return `${options} `
- case ParameterType.custom:
- return parameter.render(parameter)
- default:
- console.error(`Invalid type for parameter ${parameter.id}`);
- return "ERROR: Invalid Type"
- }
+ switch (parameter.type) {
+ case ParameterType.checkbox:
+ var is_checked = parameter.default ? " checked" : "";
+ return ` `
+ case ParameterType.select:
+ case ParameterType.select_multiple:
+ var options = (parameter.options || []).map(option => `${option.label} `).join("")
+ var multiple = (parameter.type == ParameterType.select_multiple ? 'multiple' : '')
+ return `${options} `
+ case ParameterType.custom:
+ return parameter.render(parameter)
+ default:
+ console.error(`Invalid type for parameter ${parameter.id}`);
+ return "ERROR: Invalid Type"
+ }
}
let parametersTable = document.querySelector("#system-settings .parameters-table")
/* fill in the system settings popup table */
function initParameters() {
- PARAMETERS.forEach(parameter => {
- var element = getParameterElement(parameter)
- var note = parameter.note ? `${parameter.note} ` : "";
- var icon = parameter.icon ? ` ` : "";
- var newrow = document.createElement('div')
- newrow.innerHTML = `
- ${icon}
- ${parameter.label} ${note}
- ${element}
`
- parametersTable.appendChild(newrow)
- parameter.settingsEntry = newrow
- })
+ PARAMETERS.forEach(parameter => {
+ var element = getParameterElement(parameter)
+ var note = parameter.note ? `${parameter.note} ` : "";
+ var icon = parameter.icon ? ` ` : "";
+ var newrow = document.createElement('div')
+ newrow.innerHTML = `
+ ${icon}
+ ${parameter.label} ${note}
+ ${element}
`
+ parametersTable.appendChild(newrow)
+ parameter.settingsEntry = newrow
+ })
}
initParameters()
@@ -207,9 +215,11 @@ let listenPortField = document.querySelector("#listen_port")
let testSD2Field = document.querySelector("#test_sd2")
let useBetaChannelField = document.querySelector("#use_beta_channel")
let uiOpenBrowserOnStartField = document.querySelector("#ui_open_browser_on_start")
+let confirmDangerousActionsField = document.querySelector("#confirm_dangerous_actions")
let saveSettingsBtn = document.querySelector('#save-system-settings-btn')
+
async function changeAppConfig(configDelta) {
try {
let res = await fetch('/app_config', {
@@ -242,12 +252,15 @@ async function getAppConfig() {
if ('test_sd2' in config) {
testSD2Field.checked = config['test_sd2']
}
- if (config.net && config.net.listen_to_network === false) {
- listenToNetworkField.checked = false
- }
- if (config.net && config.net.listen_port !== undefined) {
- listenPortField.value = config.net.listen_port
- }
+
+ let testSD2SettingEntry = getParameterSettingsEntry('test_sd2')
+ testSD2SettingEntry.style.display = (config.update_branch === 'beta' ? '' : 'none')
+ if (config.net && config.net.listen_to_network === false) {
+ listenToNetworkField.checked = false
+ }
+ if (config.net && config.net.listen_port !== undefined) {
+ listenPortField.value = config.net.listen_port
+ }
console.log('get config status response', config)
} catch (e) {
@@ -275,7 +288,6 @@ function getCurrentRenderDeviceSelection() {
useCPUField.addEventListener('click', function() {
let gpuSettingEntry = getParameterSettingsEntry('use_gpus')
let autoPickGPUSettingEntry = getParameterSettingsEntry('auto_pick_gpus')
- console.log("hello", this.checked);
if (this.checked) {
gpuSettingEntry.style.display = 'none'
autoPickGPUSettingEntry.style.display = 'none'
@@ -325,14 +337,45 @@ async function getDiskPath() {
}
}
-async function getDevices() {
+function setDeviceInfo(devices) {
+ let cpu = devices.all.cpu.name
+ let allGPUs = Object.keys(devices.all).filter(d => d != 'cpu')
+ let activeGPUs = Object.keys(devices.active)
+
+ function ID_TO_TEXT(d) {
+ let info = devices.all[d]
+ if ("mem_free" in info && "mem_total" in info) {
+ return `${info.name} (${d}) (${info.mem_free.toFixed(1)}Gb free / ${info.mem_total.toFixed(1)} Gb total) `
+ } else {
+ return `${info.name} (${d}) (no memory info) `
+ }
+ }
+
+ allGPUs = allGPUs.map(ID_TO_TEXT)
+ activeGPUs = activeGPUs.map(ID_TO_TEXT)
+
+ let systemInfoEl = document.querySelector('#system-info')
+ systemInfoEl.querySelector('#system-info-cpu').innerText = cpu
+ systemInfoEl.querySelector('#system-info-gpus-all').innerHTML = allGPUs.join('')
+ systemInfoEl.querySelector('#system-info-rendering-devices').innerHTML = activeGPUs.join('')
+}
+
+function setHostInfo(hosts) {
+ let port = listenPortField.value
+ hosts = hosts.map(addr => `http://${addr}:${port}/`).map(url => ``)
+ document.querySelector('#system-info-server-hosts').innerHTML = hosts.join('')
+}
+
+async function getSystemInfo() {
try {
- let res = await fetch('/get/devices')
+ let res = await fetch('/get/system_info')
if (res.status === 200) {
res = await res.json()
+ let devices = res['devices']
+ let hosts = res['hosts']
- let allDeviceIds = Object.keys(res['all']).filter(d => d !== 'cpu')
- let activeDeviceIds = Object.keys(res['active']).filter(d => d !== 'cpu')
+ let allDeviceIds = Object.keys(devices['all']).filter(d => d !== 'cpu')
+ let activeDeviceIds = Object.keys(devices['active']).filter(d => d !== 'cpu')
if (activeDeviceIds.length === 0) {
useCPUField.checked = true
@@ -350,11 +393,11 @@ async function getDevices() {
useCPUField.disabled = true // no compatible GPUs, so make the CPU mandatory
}
- autoPickGPUsField.checked = (res['config'] === 'auto')
+ autoPickGPUsField.checked = (devices['config'] === 'auto')
useGPUsField.innerHTML = ''
allDeviceIds.forEach(device => {
- let deviceName = res['all'][device]['name']
+ let deviceName = devices['all'][device]['name']
let deviceOption = `${deviceName} (${device}) `
useGPUsField.insertAdjacentHTML('beforeend', deviceOption)
})
@@ -365,6 +408,9 @@ async function getDevices() {
} else {
$('#use_gpus').val(activeDeviceIds)
}
+
+ setDeviceInfo(devices)
+ setHostInfo(hosts)
}
} catch (e) {
console.log('error fetching devices', e)
@@ -372,23 +418,23 @@ async function getDevices() {
}
saveSettingsBtn.addEventListener('click', function() {
- let updateBranch = (useBetaChannelField.checked ? 'beta' : 'main')
+ let updateBranch = (useBetaChannelField.checked ? 'beta' : 'main')
- if (listenPortField.value == '') {
- alert('The network port field must not be empty.')
- } else if (listenPortField.value<1 || listenPortField.value>65535) {
- alert('The network port must be a number from 1 to 65535')
- } else {
- changeAppConfig({
- 'render_devices': getCurrentRenderDeviceSelection(),
- 'update_branch': updateBranch,
- 'ui_open_browser_on_start': uiOpenBrowserOnStartField.checked,
- 'listen_to_network': listenToNetworkField.checked,
- 'listen_port': listenPortField.value,
- 'test_sd2': testSD2Field.checked
- })
- }
+ if (listenPortField.value == '') {
+ alert('The network port field must not be empty.')
+ } else if (listenPortField.value<1 || listenPortField.value>65535) {
+ alert('The network port must be a number from 1 to 65535')
+ } else {
+ changeAppConfig({
+ 'render_devices': getCurrentRenderDeviceSelection(),
+ 'update_branch': updateBranch,
+ 'ui_open_browser_on_start': uiOpenBrowserOnStartField.checked,
+ 'listen_to_network': listenToNetworkField.checked,
+ 'listen_port': listenPortField.value,
+ 'test_sd2': testSD2Field.checked
+ })
+ }
- saveSettingsBtn.classList.add('active')
- asyncDelay(300).then(() => saveSettingsBtn.classList.remove('active'))
+ saveSettingsBtn.classList.add('active')
+ asyncDelay(300).then(() => saveSettingsBtn.classList.remove('active'))
})
diff --git a/ui/media/js/themes.js b/ui/media/js/themes.js
index 8ffdb172..2915bc32 100644
--- a/ui/media/js/themes.js
+++ b/ui/media/js/themes.js
@@ -60,6 +60,7 @@ function themeFieldChanged() {
body.style = "";
var theme = THEMES.find(t => t.key == theme_key);
+ let borderColor = undefined
if (theme) {
// refresh variables incase they are back referencing
Array.from(DEFAULT_THEME.rule.style)
@@ -67,7 +68,14 @@ function themeFieldChanged() {
.forEach(cssVariable => {
body.style.setProperty(cssVariable, DEFAULT_THEME.rule.style.getPropertyValue(cssVariable));
});
+ borderColor = theme.rule.style.getPropertyValue('--input-border-color').trim()
+ if (!borderColor.startsWith('#')) {
+ borderColor = theme.rule.style.getPropertyValue('--theme-color-fallback')
+ }
+ } else {
+ borderColor = DEFAULT_THEME.rule.style.getPropertyValue('--theme-color-fallback')
}
+ document.querySelector('meta[name="theme-color"]').setAttribute("content", borderColor)
}
themeField.addEventListener('change', themeFieldChanged);
diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js
index 6fc3c402..a76f030e 100644
--- a/ui/media/js/utils.js
+++ b/ui/media/js/utils.js
@@ -1,17 +1,17 @@
// https://gomakethings.com/finding-the-next-and-previous-sibling-elements-that-match-a-selector-with-vanilla-js/
function getNextSibling(elem, selector) {
- // Get the next sibling element
- var sibling = elem.nextElementSibling
+ // Get the next sibling element
+ var sibling = elem.nextElementSibling
- // If there's no selector, return the first sibling
- if (!selector) return sibling
+ // If there's no selector, return the first sibling
+ if (!selector) return sibling
- // If the sibling matches our selector, use it
- // If not, jump to the next sibling and continue the loop
- while (sibling) {
- if (sibling.matches(selector)) return sibling
- sibling = sibling.nextElementSibling
- }
+ // If the sibling matches our selector, use it
+ // If not, jump to the next sibling and continue the loop
+ while (sibling) {
+ if (sibling.matches(selector)) return sibling
+ sibling = sibling.nextElementSibling
+ }
}
diff --git a/ui/media/manifest.webmanifest b/ui/media/manifest.webmanifest
new file mode 100644
index 00000000..bdb665e9
--- /dev/null
+++ b/ui/media/manifest.webmanifest
@@ -0,0 +1,8 @@
+{
+ "name": "Stable Diffusion UI",
+ "display": "standalone",
+ "display_override": [
+ "window-controls-overlay"
+ ],
+ "theme_color": "#000000"
+}
diff --git a/ui/plugins/ui/Autoscroll.plugin.js b/ui/plugins/ui/Autoscroll.plugin.js
new file mode 100644
index 00000000..0b15bb1f
--- /dev/null
+++ b/ui/plugins/ui/Autoscroll.plugin.js
@@ -0,0 +1,42 @@
+(function () {
+ "use strict"
+
+ var styleSheet = document.createElement("style");
+ styleSheet.textContent = `
+ .auto-scroll {
+ float: right;
+ }
+ `;
+ document.head.appendChild(styleSheet);
+
+ const autoScrollControl = document.createElement('div');
+ autoScrollControl.innerHTML = `
+ Scroll to generated image `
+ autoScrollControl.className = "auto-scroll"
+ clearAllPreviewsBtn.parentNode.insertBefore(autoScrollControl, clearAllPreviewsBtn.nextSibling)
+ prettifyInputs(document);
+ let autoScroll = document.querySelector("#auto_scroll")
+
+ SETTINGS_IDS_LIST.push("auto_scroll")
+ initSettings()
+
+ // observe for changes in the preview pane
+ var observer = new MutationObserver(function (mutations) {
+ mutations.forEach(function (mutation) {
+ if (mutation.target.className == 'img-batch') {
+ Autoscroll(mutation.target)
+ }
+ })
+ })
+
+ observer.observe(document.getElementById('preview'), {
+ childList: true,
+ subtree: true
+ })
+
+ function Autoscroll(target) {
+ if (autoScroll.checked && target !== null) {
+ target.parentElement.parentElement.parentElement.scrollIntoView();
+ }
+ }
+})()
diff --git a/ui/sd_internal/runtime.py b/ui/sd_internal/runtime.py
index 2a989e46..26c116ad 100644
--- a/ui/sd_internal/runtime.py
+++ b/ui/sd_internal/runtime.py
@@ -7,6 +7,7 @@ Notes:
import json
import os, re
import traceback
+import queue
import torch
import numpy as np
from gc import collect as gc_collect
@@ -27,6 +28,8 @@ from gfpgan import GFPGANer
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan import RealESRGANer
+from threading import Lock
+
import uuid
logging.set_verbosity_error()
@@ -34,7 +37,7 @@ logging.set_verbosity_error()
# consts
config_yaml = "optimizedSD/v1-inference.yaml"
filename_regex = re.compile('[^a-zA-Z0-9]')
-force_gfpgan_to_cuda0 = True # workaround: gfpgan currently works only on cuda:0
+gfpgan_temp_device_lock = Lock() # workaround: gfpgan currently can only start on one device at a time.
# api stuff
from sd_internal import device_manager
@@ -308,12 +311,6 @@ def move_to_cpu(model):
def load_model_gfpgan():
if thread_data.gfpgan_file is None: raise ValueError(f'Thread gfpgan_file is undefined.')
-
- # hack for a bug in facexlib: https://github.com/xinntao/facexlib/pull/19/files
- from facexlib.detection import retinaface
- retinaface.device = torch.device(thread_data.device)
- print('forced retinaface.device to', thread_data.device)
-
model_path = thread_data.gfpgan_file + ".pth"
thread_data.model_gfpgan = GFPGANer(device=torch.device(thread_data.device), model_path=model_path, upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None)
print('loaded', thread_data.gfpgan_file, 'to', thread_data.model_gfpgan.device, 'precision', thread_data.precision)
@@ -369,15 +366,23 @@ def apply_filters(filter_name, image_data, model_path=None):
image_data.to(thread_data.device)
if filter_name == 'gfpgan':
- if model_path is not None and model_path != thread_data.gfpgan_file:
- thread_data.gfpgan_file = model_path
- load_model_gfpgan()
- elif not thread_data.model_gfpgan:
- load_model_gfpgan()
- if thread_data.model_gfpgan is None: raise Exception('Model "gfpgan" not loaded.')
- print('enhance with', thread_data.gfpgan_file, 'on', thread_data.model_gfpgan.device, 'precision', thread_data.precision)
- _, _, output = thread_data.model_gfpgan.enhance(image_data[:,:,::-1], has_aligned=False, only_center_face=False, paste_back=True)
- image_data = output[:,:,::-1]
+ # This lock is only ever used here. No need to use timeout for the request. Should never deadlock.
+ with gfpgan_temp_device_lock: # Wait for any other devices to complete before starting.
+ # hack for a bug in facexlib: https://github.com/xinntao/facexlib/pull/19/files
+ from facexlib.detection import retinaface
+ retinaface.device = torch.device(thread_data.device)
+ print('forced retinaface.device to', thread_data.device)
+
+ if model_path is not None and model_path != thread_data.gfpgan_file:
+ thread_data.gfpgan_file = model_path
+ load_model_gfpgan()
+ elif not thread_data.model_gfpgan:
+ load_model_gfpgan()
+ if thread_data.model_gfpgan is None: raise Exception('Model "gfpgan" not loaded.')
+
+ print('enhance with', thread_data.gfpgan_file, 'on', thread_data.model_gfpgan.device, 'precision', thread_data.precision)
+ _, _, output = thread_data.model_gfpgan.enhance(image_data[:,:,::-1], has_aligned=False, only_center_face=False, paste_back=True)
+ image_data = output[:,:,::-1]
if filter_name == 'real_esrgan':
if model_path is not None and model_path != thread_data.real_esrgan_file:
@@ -392,9 +397,34 @@ def apply_filters(filter_name, image_data, model_path=None):
return image_data
-def mk_img(req: Request):
+def is_model_reload_necessary(req: Request):
+ # custom model support:
+ # the req.use_stable_diffusion_model needs to be a valid path
+ # to the ckpt file (without the extension).
+ if not os.path.exists(req.use_stable_diffusion_model + '.ckpt'): raise FileNotFoundError(f'Cannot find {req.use_stable_diffusion_model}.ckpt')
+
+ needs_model_reload = False
+ if not thread_data.model or thread_data.ckpt_file != req.use_stable_diffusion_model or thread_data.vae_file != req.use_vae_model:
+ thread_data.ckpt_file = req.use_stable_diffusion_model
+ thread_data.vae_file = req.use_vae_model
+ needs_model_reload = True
+
+ if thread_data.device != 'cpu':
+ if (thread_data.precision == 'autocast' and (req.use_full_precision or not thread_data.model_is_half)) or \
+ (thread_data.precision == 'full' and not req.use_full_precision and not thread_data.force_full_precision):
+ thread_data.precision = 'full' if req.use_full_precision else 'autocast'
+ needs_model_reload = True
+
+ return needs_model_reload
+
+def reload_model():
+ unload_models()
+ unload_filters()
+ load_model_ckpt()
+
+def mk_img(req: Request, data_queue: queue.Queue, task_temp_images: list, step_callback):
try:
- yield from do_mk_img(req)
+ return do_mk_img(req, data_queue, task_temp_images, step_callback)
except Exception as e:
print(traceback.format_exc())
@@ -405,12 +435,13 @@ def mk_img(req: Request):
thread_data.model.model2.to("cpu")
gc() # Release from memory.
- yield json.dumps({
+ data_queue.put(json.dumps({
"status": 'failed',
"detail": str(e)
- })
+ }))
+ raise e
-def update_temp_img(req, x_samples):
+def update_temp_img(req, x_samples, task_temp_images: list):
partial_images = []
for i in range(req.num_outputs):
if thread_data.test_sd2:
@@ -421,19 +452,18 @@ def update_temp_img(req, x_samples):
x_sample = 255.0 * rearrange(x_sample[0].cpu().numpy(), "c h w -> h w c")
x_sample = x_sample.astype(np.uint8)
img = Image.fromarray(x_sample)
- buf = BytesIO()
- img.save(buf, format='JPEG')
- buf.seek(0)
+ buf = img_to_buffer(img, output_format='JPEG')
del img, x_sample, x_sample_ddim
# don't delete x_samples, it is used in the code that called this callback
thread_data.temp_images[str(req.session_id) + '/' + str(i)] = buf
+ task_temp_images[i] = buf
partial_images.append({'path': f'/image/tmp/{req.session_id}/{i}'})
return partial_images
# Build and return the apropriate generator for do_mk_img
-def get_image_progress_generator(req, extra_props=None):
+def get_image_progress_generator(req, data_queue: queue.Queue, task_temp_images: list, step_callback, extra_props=None):
if not req.stream_progress_updates:
def empty_callback(x_samples, i): return x_samples
return empty_callback
@@ -452,15 +482,17 @@ def get_image_progress_generator(req, extra_props=None):
progress.update(extra_props)
if req.stream_image_progress and i % 5 == 0:
- progress['output'] = update_temp_img(req, x_samples)
+ progress['output'] = update_temp_img(req, x_samples, task_temp_images)
- yield json.dumps(progress)
+ data_queue.put(json.dumps(progress))
+
+ step_callback()
if thread_data.stop_processing:
raise UserInitiatedStop("User requested that we stop processing")
return img_callback
-def do_mk_img(req: Request):
+def do_mk_img(req: Request, data_queue: queue.Queue, task_temp_images: list, step_callback):
thread_data.stop_processing = False
res = Response()
@@ -469,28 +501,6 @@ def do_mk_img(req: Request):
thread_data.temp_images.clear()
- # custom model support:
- # the req.use_stable_diffusion_model needs to be a valid path
- # to the ckpt file (without the extension).
- if not os.path.exists(req.use_stable_diffusion_model + '.ckpt'): raise FileNotFoundError(f'Cannot find {req.use_stable_diffusion_model}.ckpt')
-
- needs_model_reload = False
- if not thread_data.model or thread_data.ckpt_file != req.use_stable_diffusion_model or thread_data.vae_file != req.use_vae_model:
- thread_data.ckpt_file = req.use_stable_diffusion_model
- thread_data.vae_file = req.use_vae_model
- needs_model_reload = True
-
- if thread_data.device != 'cpu':
- if (thread_data.precision == 'autocast' and (req.use_full_precision or not thread_data.model_is_half)) or \
- (thread_data.precision == 'full' and not req.use_full_precision and not thread_data.force_full_precision):
- thread_data.precision = 'full' if req.use_full_precision else 'autocast'
- needs_model_reload = True
-
- if needs_model_reload:
- unload_models()
- unload_filters()
- load_model_ckpt()
-
if thread_data.turbo != req.turbo and not thread_data.test_sd2:
thread_data.turbo = req.turbo
thread_data.model.turbo = req.turbo
@@ -606,7 +616,7 @@ def do_mk_img(req: Request):
thread_data.modelFS.to(thread_data.device)
n_steps = req.num_inference_steps if req.init_image is None else t_enc
- img_callback = get_image_progress_generator(req, {"total_steps": n_steps})
+ img_callback = get_image_progress_generator(req, data_queue, task_temp_images, step_callback, {"total_steps": n_steps})
# run the handler
try:
@@ -615,13 +625,6 @@ def do_mk_img(req: Request):
x_samples = _txt2img(req.width, req.height, req.num_outputs, req.num_inference_steps, req.guidance_scale, None, opt_C, opt_f, opt_ddim_eta, c, uc, opt_seed, img_callback, mask, req.sampler)
else:
x_samples = _img2img(init_latent, t_enc, batch_size, req.guidance_scale, c, uc, req.num_inference_steps, opt_ddim_eta, opt_seed, img_callback, mask, opt_C, req.height, req.width, opt_f)
-
- if req.stream_progress_updates:
- yield from x_samples
- if hasattr(thread_data, 'partial_x_samples'):
- if thread_data.partial_x_samples is not None:
- x_samples = thread_data.partial_x_samples
- del thread_data.partial_x_samples
except UserInitiatedStop:
if not hasattr(thread_data, 'partial_x_samples'):
continue
@@ -666,9 +669,11 @@ def do_mk_img(req: Request):
save_metadata(meta_out_path, req, prompts[0], opt_seed)
if return_orig_img:
- img_str = img_to_base64_str(img, req.output_format)
+ img_buffer = img_to_buffer(img, req.output_format)
+ img_str = buffer_to_base64_str(img_buffer, req.output_format)
res_image_orig = ResponseImage(data=img_str, seed=opt_seed)
res.images.append(res_image_orig)
+ task_temp_images[i] = img_buffer
if req.save_to_disk_path is not None:
res_image_orig.path_abs = img_out_path
@@ -684,9 +689,11 @@ def do_mk_img(req: Request):
filters_applied.append(req.use_upscale)
if (len(filters_applied) > 0):
filtered_image = Image.fromarray(img_data[i])
- filtered_img_data = img_to_base64_str(filtered_image, req.output_format)
+ filtered_buffer = img_to_buffer(filtered_image, req.output_format)
+ filtered_img_data = buffer_to_base64_str(filtered_buffer, req.output_format)
response_image = ResponseImage(data=filtered_img_data, seed=opt_seed)
res.images.append(response_image)
+ task_temp_images[i] = filtered_buffer
if req.save_to_disk_path is not None:
filtered_img_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], img_id, req.output_format, "_".join(filters_applied))
save_image(filtered_image, filtered_img_out_path)
@@ -705,7 +712,10 @@ def do_mk_img(req: Request):
print(f'memory_final = {round(torch.cuda.memory_allocated(thread_data.device) / 1e6, 2)}Mb')
print('Task completed')
- yield json.dumps(res.json())
+ res = res.json()
+ data_queue.put(json.dumps(res))
+
+ return res
def save_image(img, img_out_path):
try:
@@ -771,7 +781,7 @@ def _txt2img(opt_W, opt_H, opt_n_samples, opt_ddim_steps, opt_scale, start_code,
sampler.make_schedule(ddim_num_steps=opt_ddim_steps, ddim_eta=opt_ddim_eta, verbose=False)
- samples_ddim = sampler.sample(
+ samples_ddim, intermediates = sampler.sample(
S=opt_ddim_steps,
conditioning=c,
batch_size=opt_n_samples,
@@ -804,7 +814,7 @@ def _txt2img(opt_W, opt_H, opt_n_samples, opt_ddim_steps, opt_scale, start_code,
mask=mask,
sampler = sampler_name,
)
- yield from samples_ddim
+ return samples_ddim
def _img2img(init_latent, t_enc, batch_size, opt_scale, c, uc, opt_ddim_steps, opt_ddim_eta, opt_seed, img_callback, mask, opt_C=1, opt_H=1, opt_W=1, opt_f=1):
# encode (scaled latent)
@@ -842,7 +852,7 @@ def _img2img(init_latent, t_enc, batch_size, opt_scale, c, uc, opt_ddim_steps, o
x_T=x_T,
sampler = 'ddim'
)
- yield from samples_ddim
+ return samples_ddim
def gc():
gc_collect()
@@ -910,8 +920,16 @@ def load_mask(mask_str, h0, w0, newH, newW, invert=False):
# https://stackoverflow.com/a/61114178
def img_to_base64_str(img, output_format="PNG"):
+ buffered = img_to_buffer(img, output_format)
+ return buffer_to_base64_str(buffered, output_format)
+
+def img_to_buffer(img, output_format="PNG"):
buffered = BytesIO()
img.save(buffered, format=output_format)
+ buffered.seek(0)
+ return buffered
+
+def buffer_to_base64_str(buffered, output_format="PNG"):
buffered.seek(0)
img_byte = buffered.getvalue()
mime_type = "image/png" if output_format.lower() == "png" else "image/jpeg"
diff --git a/ui/sd_internal/task_manager.py b/ui/sd_internal/task_manager.py
index bd87517b..ff6cbb4c 100644
--- a/ui/sd_internal/task_manager.py
+++ b/ui/sd_internal/task_manager.py
@@ -283,45 +283,26 @@ def thread_render(device):
print(f'Session {task.request.session_id} starting task {id(task)} on {runtime.thread_data.device_name}')
if not task.lock.acquire(blocking=False): raise Exception('Got locked task from queue.')
try:
- if runtime.thread_data.device == 'cpu' and is_alive() > 1:
- # CPU is not the only device. Keep track of active time to unload resources later.
- runtime.thread_data.lastActive = time.time()
- # Open data generator.
- res = runtime.mk_img(task.request)
- if current_model_path == task.request.use_stable_diffusion_model:
- current_state = ServerStates.Rendering
- else:
+ if runtime.is_model_reload_necessary(task.request):
current_state = ServerStates.LoadingModel
- # Start reading from generator.
- dataQueue = None
- if task.request.stream_progress_updates:
- dataQueue = task.buffer_queue
- for result in res:
- if current_state == ServerStates.LoadingModel:
- current_state = ServerStates.Rendering
- current_model_path = task.request.use_stable_diffusion_model
- current_vae_path = task.request.use_vae_model
+ runtime.reload_model()
+ current_model_path = task.request.use_stable_diffusion_model
+ current_vae_path = task.request.use_vae_model
+
+ def step_callback():
+ global current_state_error
+
if isinstance(current_state_error, SystemExit) or isinstance(current_state_error, StopAsyncIteration) or isinstance(task.error, StopAsyncIteration):
runtime.thread_data.stop_processing = True
if isinstance(current_state_error, StopAsyncIteration):
task.error = current_state_error
current_state_error = None
print(f'Session {task.request.session_id} sent cancel signal for task {id(task)}')
- if dataQueue:
- dataQueue.put(result)
- if isinstance(result, str):
- result = json.loads(result)
- task.response = result
- if 'output' in result:
- for out_obj in result['output']:
- if 'path' in out_obj:
- img_id = out_obj['path'][out_obj['path'].rindex('/') + 1:]
- task.temp_images[int(img_id)] = runtime.thread_data.temp_images[out_obj['path'][11:]]
- elif 'data' in out_obj:
- buf = runtime.base64_str_to_buffer(out_obj['data'])
- task.temp_images[result['output'].index(out_obj)] = buf
- # Before looping back to the generator, mark cache as still alive.
- task_cache.keep(task.request.session_id, TASK_TTL)
+
+ task_cache.keep(task.request.session_id, TASK_TTL)
+
+ current_state = ServerStates.Rendering
+ task.response = runtime.mk_img(task.request, task.buffer_queue, task.temp_images, step_callback)
except Exception as e:
task.error = e
print(traceback.format_exc())
diff --git a/ui/server.py b/ui/server.py
index fffbb43c..8da5a64f 100644
--- a/ui/server.py
+++ b/ui/server.py
@@ -7,6 +7,7 @@ import traceback
import sys
import os
+import socket
import picklescan.scanner
import rich
@@ -144,12 +145,19 @@ def setConfig(config):
print(traceback.format_exc())
def resolve_model_to_use(model_name:str, model_type:str, model_dir:str, model_extensions:list, default_models=[]):
+ config = getConfig()
+
model_dirs = [os.path.join(MODELS_DIR, model_dir), SD_DIR]
if not model_name: # When None try user configured model.
- config = getConfig()
+ # config = getConfig()
if 'model' in config and model_type in config['model']:
model_name = config['model'][model_type]
if model_name:
+ is_sd2 = config.get('test_sd2', False)
+ if model_name.startswith('sd2_') and not is_sd2: # temp hack, until SD2 is unified with 1.4
+ print('ERROR: Cannot use SD 2.0 models with SD 1.0 code. Using the sd-v1-4 model instead!')
+ model_name = 'sd-v1-4'
+
# Check models directory
models_dir_path = os.path.join(MODELS_DIR, model_dir, model_name)
for model_extension in model_extensions:
@@ -237,9 +245,9 @@ def is_malicious_model(file_path):
return False
except Exception as e:
print('error while scanning', file_path, 'error:', e)
-
return False
+known_models = {}
def getModels():
models = {
'active': {
@@ -262,9 +270,14 @@ def getModels():
if not file.endswith(model_extension):
continue
- if is_malicious_model(os.path.join(models_dir, file)):
- models['scan-error'] = file
- return
+ model_path = os.path.join(models_dir, file)
+ mtime = os.path.getmtime(model_path)
+ mod_time = known_models[model_path] if model_path in known_models else -1
+ if mod_time != mtime:
+ if is_malicious_model(model_path):
+ models['scan-error'] = file
+ return
+ known_models[model_path] = mtime
model_name = file[:-len(model_extension)]
models['options'][model_type].append(model_name)
@@ -293,6 +306,11 @@ def getUIPlugins():
return plugins
+def getIPConfig():
+ ips = socket.gethostbyname_ex(socket.getfqdn())
+ ips[2].append(ips[0])
+ return ips[2]
+
@app.get('/get/{key:path}')
def read_web_data(key:str=None):
if not key: # /get without parameters, stable-diffusion easter egg.
@@ -302,11 +320,14 @@ def read_web_data(key:str=None):
if config is None:
config = APP_CONFIG_DEFAULTS
return JSONResponse(config, headers=NOCACHE_HEADERS)
- elif key == 'devices':
+ elif key == 'system_info':
config = getConfig()
- devices = task_manager.get_devices()
- devices['config'] = config.get('render_devices', "auto")
- return JSONResponse(devices, headers=NOCACHE_HEADERS)
+ system_info = {
+ 'devices': task_manager.get_devices(),
+ 'hosts': getIPConfig(),
+ }
+ system_info['devices']['config'] = config.get('render_devices', "auto")
+ return JSONResponse(system_info, headers=NOCACHE_HEADERS)
elif key == 'models':
return JSONResponse(getModels(), headers=NOCACHE_HEADERS)
elif key == 'modifiers': return FileResponse(os.path.join(SD_UI_DIR, 'modifiers.json'), headers=NOCACHE_HEADERS)
@@ -442,6 +463,9 @@ class LogSuppressFilter(logging.Filter):
return True
logging.getLogger('uvicorn.access').addFilter(LogSuppressFilter())
+# Check models and prepare cache for UI open
+getModels()
+
# Start the task_manager
task_manager.default_model_to_load = resolve_ckpt_to_use()
task_manager.default_vae_to_load = resolve_vae_to_use()