Merge remote-tracking branch 'origin/beta' into restart-needed

This commit is contained in:
Olivia Godone-Maresca 2023-07-15 13:22:41 -04:00
commit 3461bb669d
17 changed files with 272 additions and 148 deletions

View File

@ -22,6 +22,7 @@
Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed.
### Detailed changelog ### Detailed changelog
* 2.5.44 - 15 Jul 2023 - (beta-only) Support for multiple LoRA files.
* 2.5.43 - 9 Jul 2023 - (beta-only) Support for loading Textual Inversion embeddings. You can find the option in the Image Settings panel. Thanks @JeLuf. * 2.5.43 - 9 Jul 2023 - (beta-only) Support for loading Textual Inversion embeddings. You can find the option in the Image Settings panel. Thanks @JeLuf.
* 2.5.43 - 9 Jul 2023 - Improve the startup time of the UI. * 2.5.43 - 9 Jul 2023 - Improve the startup time of the UI.
* 2.5.42 - 4 Jul 2023 - Keyboard shortcuts for the Image Editor. Thanks @JeLuf. * 2.5.42 - 4 Jul 2023 - Keyboard shortcuts for the Image Editor. Thanks @JeLuf.

View File

@ -41,6 +41,10 @@ call python --version
echo PYTHONPATH=%PYTHONPATH% echo PYTHONPATH=%PYTHONPATH%
if exist "%cd%\profile" (
set HF_HOME=%cd%\profile\.cache\huggingface
)
@rem done @rem done
echo. echo.

View File

@ -18,7 +18,7 @@ os_name = platform.system()
modules_to_check = { modules_to_check = {
"torch": ("1.11.0", "1.13.1", "2.0.0"), "torch": ("1.11.0", "1.13.1", "2.0.0"),
"torchvision": ("0.12.0", "0.14.1", "0.15.1"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"),
"sdkit": "1.0.116", "sdkit": "1.0.125",
"stable-diffusion-sdkit": "2.1.4", "stable-diffusion-sdkit": "2.1.4",
"rich": "12.6.0", "rich": "12.6.0",
"uvicorn": "0.19.0", "uvicorn": "0.19.0",

View File

@ -104,18 +104,21 @@ call python --version
@FOR /F "tokens=* USEBACKQ" %%F IN (`python scripts\get_config.py --default=False net listen_to_network`) DO ( @FOR /F "tokens=* USEBACKQ" %%F IN (`python scripts\get_config.py --default=False net listen_to_network`) DO (
if "%%F" EQU "True" ( if "%%F" EQU "True" (
@SET ED_BIND_IP=0.0.0.0 @FOR /F "tokens=* USEBACKQ" %%G IN (`python scripts\get_config.py --default=0.0.0.0 net bind_ip`) DO (
@SET ED_BIND_IP=%%G
)
) else ( ) else (
@SET ED_BIND_IP=127.0.0.1 @SET ED_BIND_IP=127.0.0.1
) )
) )
@cd stable-diffusion @cd stable-diffusion
@rem set any overrides @rem set any overrides
set HF_HUB_DISABLE_SYMLINKS_WARNING=true set HF_HUB_DISABLE_SYMLINKS_WARNING=true
@uvicorn main:server_api --app-dir "%SD_UI_PATH%" --port %ED_BIND_PORT% --host %ED_BIND_IP% --log-level error @python -m uvicorn main:server_api --app-dir "%SD_UI_PATH%" --port %ED_BIND_PORT% --host %ED_BIND_IP% --log-level error
@pause @pause

View File

@ -72,7 +72,7 @@ export SD_UI_PATH=`pwd`/ui
export ED_BIND_PORT="$( python scripts/get_config.py --default=9000 net listen_port )" export ED_BIND_PORT="$( python scripts/get_config.py --default=9000 net listen_port )"
case "$( python scripts/get_config.py --default=False net listen_to_network )" in case "$( python scripts/get_config.py --default=False net listen_to_network )" in
"True") "True")
export ED_BIND_IP=0.0.0.0 export ED_BIND_IP=$( python scripts/get_config.py --default=0.0.0.0 net bind_ip)
;; ;;
"False") "False")
export ED_BIND_IP=127.0.0.1 export ED_BIND_IP=127.0.0.1

View File

@ -2,6 +2,7 @@ import os
import shutil import shutil
from glob import glob from glob import glob
import traceback import traceback
from typing import Union
from easydiffusion import app from easydiffusion import app
from easydiffusion.types import TaskData from easydiffusion.types import TaskData
@ -93,7 +94,14 @@ def unload_all(context: Context):
del context.model_load_errors[model_type] del context.model_load_errors[model_type]
def resolve_model_to_use(model_name: str = None, model_type: str = None, fail_if_not_found: bool = True): def resolve_model_to_use(model_name: Union[str, list] = None, model_type: str = None, fail_if_not_found: bool = True):
model_names = model_name if isinstance(model_name, list) else [model_name]
model_paths = [resolve_model_to_use_single(m, model_type, fail_if_not_found) for m in model_names]
return model_paths[0] if len(model_paths) == 1 else model_paths
def resolve_model_to_use_single(model_name: str = None, model_type: str = None, fail_if_not_found: bool = True):
model_extensions = MODEL_EXTENSIONS.get(model_type, []) model_extensions = MODEL_EXTENSIONS.get(model_type, [])
default_models = DEFAULT_MODELS.get(model_type, []) default_models = DEFAULT_MODELS.get(model_type, [])
config = app.getConfig() config = app.getConfig()

View File

@ -473,15 +473,15 @@ def start_render_thread(device):
render_threads.append(rthread) render_threads.append(rthread)
finally: finally:
manager_lock.release() manager_lock.release()
# timeout = DEVICE_START_TIMEOUT timeout = DEVICE_START_TIMEOUT
# while not rthread.is_alive() or not rthread in weak_thread_data or not "device" in weak_thread_data[rthread]: while not rthread.is_alive() or not rthread in weak_thread_data or not "device" in weak_thread_data[rthread]:
# if rthread in weak_thread_data and "error" in weak_thread_data[rthread]: if rthread in weak_thread_data and "error" in weak_thread_data[rthread]:
# log.error(f"{rthread}, {device}, error: {weak_thread_data[rthread]['error']}") log.error(f"{rthread}, {device}, error: {weak_thread_data[rthread]['error']}")
# return False return False
# if timeout <= 0: if timeout <= 0:
# return False return False
# timeout -= 1 timeout -= 1
# time.sleep(1) time.sleep(1)
return True return True
@ -535,12 +535,12 @@ def update_render_threads(render_devices, active_devices):
if not start_render_thread(device): if not start_render_thread(device):
log.warn(f"{device} failed to start.") log.warn(f"{device} failed to start.")
# if is_alive() <= 0: # No running devices, probably invalid user config. if is_alive() <= 0: # No running devices, probably invalid user config.
# raise EnvironmentError( raise EnvironmentError(
# 'ERROR: No active render devices! Please verify the "render_devices" value in config.json' 'ERROR: No active render devices! Please verify the "render_devices" value in config.json'
# ) )
# log.debug(f"active devices: {get_devices()['active']}") log.debug(f"active devices: {get_devices()['active']}")
def shutdown_event(): # Signal render thread to close on shutdown def shutdown_event(): # Signal render thread to close on shutdown

View File

@ -1,4 +1,4 @@
from typing import Any from typing import Any, List, Union
from pydantic import BaseModel from pydantic import BaseModel
@ -22,7 +22,7 @@ class GenerateImageRequest(BaseModel):
sampler_name: str = None # "ddim", "plms", "heun", "euler", "euler_a", "dpm2", "dpm2_a", "lms" sampler_name: str = None # "ddim", "plms", "heun", "euler", "euler_a", "dpm2", "dpm2_a", "lms"
hypernetwork_strength: float = 0 hypernetwork_strength: float = 0
lora_alpha: float = 0 lora_alpha: Union[float, List[float]] = 0
tiling: str = "none" # "none", "x", "y", "xy" tiling: str = "none" # "none", "x", "y", "xy"
@ -32,15 +32,14 @@ class TaskData(BaseModel):
save_to_disk_path: str = None save_to_disk_path: str = None
vram_usage_level: str = "balanced" # or "low" or "medium" vram_usage_level: str = "balanced" # or "low" or "medium"
use_face_correction: str = None # or "GFPGANv1.3" use_face_correction: Union[str, List[str]] = None # or "GFPGANv1.3"
use_upscale: str = None # or "RealESRGAN_x4plus" or "RealESRGAN_x4plus_anime_6B" or "latent_upscaler" use_upscale: Union[str, List[str]] = None
upscale_amount: int = 4 # or 2 upscale_amount: int = 4 # or 2
latent_upscaler_steps: int = 10 latent_upscaler_steps: int = 10
use_stable_diffusion_model: str = "sd-v1-4" use_stable_diffusion_model: Union[str, List[str]] = "sd-v1-4"
# use_stable_diffusion_config: str = "v1-inference" use_vae_model: Union[str, List[str]] = None
use_vae_model: str = None use_hypernetwork_model: Union[str, List[str]] = None
use_hypernetwork_model: str = None use_lora_model: Union[str, List[str]] = None
use_lora_model: str = None
show_only_filtered_image: bool = False show_only_filtered_image: bool = False
block_nsfw: bool = False block_nsfw: bool = False

View File

@ -1,6 +1,8 @@
import os import os
import re import re
import time import time
import regex
from datetime import datetime from datetime import datetime
from functools import reduce from functools import reduce
@ -30,11 +32,12 @@ TASK_TEXT_MAPPING = {
"lora_alpha": "LoRA Strength", "lora_alpha": "LoRA Strength",
"use_hypernetwork_model": "Hypernetwork model", "use_hypernetwork_model": "Hypernetwork model",
"hypernetwork_strength": "Hypernetwork Strength", "hypernetwork_strength": "Hypernetwork Strength",
"use_embedding_models": "Embedding models",
"tiling": "Seamless Tiling", "tiling": "Seamless Tiling",
"use_face_correction": "Use Face Correction", "use_face_correction": "Use Face Correction",
"use_upscale": "Use Upscaling", "use_upscale": "Use Upscaling",
"upscale_amount": "Upscale By", "upscale_amount": "Upscale By",
"latent_upscaler_steps": "Latent Upscaler Steps" "latent_upscaler_steps": "Latent Upscaler Steps",
} }
time_placeholders = { time_placeholders = {
@ -202,6 +205,9 @@ def get_printable_request(req: GenerateImageRequest, task_data: TaskData):
req_metadata = req.dict() req_metadata = req.dict()
task_data_metadata = task_data.dict() task_data_metadata = task_data.dict()
app_config = app.getConfig()
using_diffusers = app_config.get("test_diffusers", False)
# Save the metadata in the order defined in TASK_TEXT_MAPPING # Save the metadata in the order defined in TASK_TEXT_MAPPING
metadata = {} metadata = {}
for key in TASK_TEXT_MAPPING.keys(): for key in TASK_TEXT_MAPPING.keys():
@ -209,6 +215,24 @@ def get_printable_request(req: GenerateImageRequest, task_data: TaskData):
metadata[key] = req_metadata[key] metadata[key] = req_metadata[key]
elif key in task_data_metadata: elif key in task_data_metadata:
metadata[key] = task_data_metadata[key] metadata[key] = task_data_metadata[key]
elif key is "use_embedding_models" and using_diffusers:
embeddings_extensions = {".pt", ".bin", ".safetensors"}
def scan_directory(directory_path: str):
used_embeddings = []
for entry in os.scandir(directory_path):
if entry.is_file():
entry_extension = os.path.splitext(entry.name)[1]
if entry_extension not in embeddings_extensions:
continue
embedding_name_regex = regex.compile(r"(^|[\s,])" + regex.escape(os.path.splitext(entry.name)[0]) + r"([+-]*$|[\s,]|[+-]+[\s,])")
if embedding_name_regex.search(req.prompt) or embedding_name_regex.search(req.negative_prompt):
used_embeddings.append(entry.path)
elif entry.is_dir():
used_embeddings.extend(scan_directory(entry.path))
return used_embeddings
used_embeddings = scan_directory(os.path.join(app.MODELS_DIR, "embeddings"))
metadata["use_embedding_models"] = ", ".join(used_embeddings) if len(used_embeddings) > 0 else None
# Clean up the metadata # Clean up the metadata
if req.init_image is None and "prompt_strength" in metadata: if req.init_image is None and "prompt_strength" in metadata:
@ -222,8 +246,7 @@ def get_printable_request(req: GenerateImageRequest, task_data: TaskData):
if task_data.use_upscale != "latent_upscaler" and "latent_upscaler_steps" in metadata: if task_data.use_upscale != "latent_upscaler" and "latent_upscaler_steps" in metadata:
del metadata["latent_upscaler_steps"] del metadata["latent_upscaler_steps"]
app_config = app.getConfig() if not using_diffusers:
if not app_config.get("test_diffusers", False):
for key in (x for x in ["use_lora_model", "lora_alpha", "clip_skip", "tiling", "latent_upscaler_steps"] if x in metadata): for key in (x for x in ["use_lora_model", "lora_alpha", "clip_skip", "tiling", "latent_upscaler_steps"] if x in metadata):
del metadata[key] del metadata[key]

View File

@ -31,7 +31,7 @@
<h1> <h1>
<img id="logo_img" src="/media/images/icon-512x512.png" > <img id="logo_img" src="/media/images/icon-512x512.png" >
Easy Diffusion Easy Diffusion
<small><span id="version">v2.5.43</span> <span id="updateBranchLabel"></span></small> <small><span id="version">v2.5.44</span> <span id="updateBranchLabel"></span></small>
</h1> </h1>
</div> </div>
<div id="server-status"> <div id="server-status">
@ -162,9 +162,10 @@
<option value="dpm2_a">DPM2 Ancestral</option> <option value="dpm2_a">DPM2 Ancestral</option>
<option value="lms">LMS</option> <option value="lms">LMS</option>
<option value="dpm_solver_stability">DPM Solver (Stability AI)</option> <option value="dpm_solver_stability">DPM Solver (Stability AI)</option>
<option value="dpmpp_2s_a" class="k_diffusion-only">DPM++ 2s Ancestral (Karras)</option> <option value="dpmpp_2s_a">DPM++ 2s Ancestral (Karras)</option>
<option value="dpmpp_2m">DPM++ 2m (Karras)</option> <option value="dpmpp_2m">DPM++ 2m (Karras)</option>
<option value="dpmpp_sde" class="k_diffusion-only">DPM++ SDE (Karras)</option> <option value="dpmpp_2m_sde" class="diffusers-only">DPM++ 2m SDE (Karras)</option>
<option value="dpmpp_sde">DPM++ SDE (Karras)</option>
<option value="dpm_fast" class="k_diffusion-only">DPM Fast (Karras)</option> <option value="dpm_fast" class="k_diffusion-only">DPM Fast (Karras)</option>
<option value="dpm_adaptive" class="k_diffusion-only">DPM Adaptive (Karras)</option> <option value="dpm_adaptive" class="k_diffusion-only">DPM Adaptive (Karras)</option>
<option value="ddpm" class="diffusers-only">DDPM</option> <option value="ddpm" class="diffusers-only">DDPM</option>
@ -224,21 +225,14 @@
<label for="height"><small>(height)</small></label> <label for="height"><small>(height)</small></label>
<div id="small_image_warning" class="displayNone">Small image sizes can cause bad image quality</div> <div id="small_image_warning" class="displayNone">Small image sizes can cause bad image quality</div>
</td></tr> </td></tr>
<tr class="pl-5"><td><label for="num_inference_steps">Inference Steps:</label></td><td> <input id="num_inference_steps" name="num_inference_steps" size="4" value="25" onkeypress="preventNonNumericalInput(event)"></td></tr> <tr class="pl-5"><td><label for="num_inference_steps">Inference Steps:</label></td><td> <input id="num_inference_steps" name="num_inference_steps" type="number" min="1" step="1" style="width: 42pt" value="25" onkeypress="preventNonNumericalInput(event)"></td></tr>
<tr class="pl-5"><td><label for="guidance_scale_slider">Guidance Scale:</label></td><td> <input id="guidance_scale_slider" name="guidance_scale_slider" class="editor-slider" value="75" type="range" min="11" max="500"> <input id="guidance_scale" name="guidance_scale" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"></td></tr> <tr class="pl-5"><td><label for="guidance_scale_slider">Guidance Scale:</label></td><td> <input id="guidance_scale_slider" name="guidance_scale_slider" class="editor-slider" value="75" type="range" min="11" max="500"> <input id="guidance_scale" name="guidance_scale" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"></td></tr>
<tr id="prompt_strength_container" class="pl-5"><td><label for="prompt_strength_slider">Prompt Strength:</label></td><td> <input id="prompt_strength_slider" name="prompt_strength_slider" class="editor-slider" value="80" type="range" min="0" max="99"> <input id="prompt_strength" name="prompt_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"><br/></td></tr> <tr id="prompt_strength_container" class="pl-5"><td><label for="prompt_strength_slider">Prompt Strength:</label></td><td> <input id="prompt_strength_slider" name="prompt_strength_slider" class="editor-slider" value="80" type="range" min="0" max="99"> <input id="prompt_strength" name="prompt_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"><br/></td></tr>
<tr id="lora_model_container" class="pl-5"> <tr id="lora_model_container" class="pl-5">
<td><label for="lora_model">LoRA:</label></td> <td>
<td class="diffusers-restart-needed"> <label for="lora_model">LoRA:</label>
<input id="lora_model" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" />
</td>
</tr>
<tr id="lora_alpha_container" class="pl-5">
<td><label for="lora_alpha_slider">LoRA Strength:</label></td>
<td class="diffusers-restart-needed">
<small>-2</small> <input id="lora_alpha_slider" name="lora_alpha_slider" class="editor-slider" value="50" type="range" min="-200" max="200"> <small>2</small> &nbsp;
<input id="lora_alpha" name="lora_alpha" size="4" pattern="^-?[0-9]*\.?[0-9]*$" onkeypress="preventNonNumericalInput(event)"><br/>
</td> </td>
<td class="model_entries diffusers-restart-needed"></td>
</tr> </tr>
<tr class="pl-5"><td><label for="hypernetwork_model">Hypernetwork:</label></td><td> <tr class="pl-5"><td><label for="hypernetwork_model">Hypernetwork:</label></td><td>
<input id="hypernetwork_model" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" /> <input id="hypernetwork_model" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" />

View File

@ -1,12 +1,12 @@
from easydiffusion import model_manager, app, server from easydiffusion import model_manager, app, server
from easydiffusion.server import server_api # required for uvicorn from easydiffusion.server import server_api # required for uvicorn
server.init()
# Init the app # Init the app
model_manager.init() model_manager.init()
app.init() app.init()
server.init() app.init_render_threads()
# start the browser ui # start the browser ui
app.open_browser() app.open_browser()
app.init_render_threads()

View File

@ -5,6 +5,8 @@
html { html {
position: relative; position: relative;
overscroll-behavior-y: none;
color-scheme: dark !important;
} }
body { body {
@ -1677,6 +1679,10 @@ body.wait-pause {
background: var(--background-color3); background: var(--background-color3);
} }
.model_entry .model_name {
width: 70%;
}
.diffusers-disabled-on-startup .diffusers-restart-needed { .diffusers-disabled-on-startup .diffusers-restart-needed {
font-size: 0; font-size: 0;
} }

View File

@ -16,7 +16,9 @@ const SETTINGS_IDS_LIST = [
"clip_skip", "clip_skip",
"vae_model", "vae_model",
"hypernetwork_model", "hypernetwork_model",
"lora_model", "lora_model_0",
"lora_model_1",
"lora_model_2",
"sampler_name", "sampler_name",
"width", "width",
"height", "height",
@ -24,7 +26,9 @@ const SETTINGS_IDS_LIST = [
"guidance_scale", "guidance_scale",
"prompt_strength", "prompt_strength",
"hypernetwork_strength", "hypernetwork_strength",
"lora_alpha", "lora_alpha_0",
"lora_alpha_1",
"lora_alpha_2",
"tiling", "tiling",
"output_format", "output_format",
"output_quality", "output_quality",
@ -176,13 +180,14 @@ function loadSettings() {
// So this is likely the first time Easy Diffusion is running. // So this is likely the first time Easy Diffusion is running.
// Initialize vram_usage_level based on the available VRAM // Initialize vram_usage_level based on the available VRAM
function initGPUProfile(event) { function initGPUProfile(event) {
if ( "detail" in event if (
&& "active" in event.detail "detail" in event &&
&& "cuda:0" in event.detail.active "active" in event.detail &&
&& event.detail.active["cuda:0"].mem_total <4.5 ) "cuda:0" in event.detail.active &&
{ event.detail.active["cuda:0"].mem_total < 4.5
vramUsageLevelField.value = "low" ) {
vramUsageLevelField.dispatchEvent(new Event("change")) vramUsageLevelField.value = "low"
vramUsageLevelField.dispatchEvent(new Event("change"))
} }
document.removeEventListener("system_info_update", initGPUProfile) document.removeEventListener("system_info_update", initGPUProfile)
} }

View File

@ -292,29 +292,58 @@ const TASK_MAPPING = {
use_lora_model: { use_lora_model: {
name: "LoRA model", name: "LoRA model",
setUI: (use_lora_model) => { setUI: (use_lora_model) => {
const oldVal = loraModelField.value use_lora_model.forEach((model_name, i) => {
use_lora_model = let field = loraModels[i][0]
use_lora_model === undefined || use_lora_model === null || use_lora_model === "None" const oldVal = field.value
? ""
: use_lora_model
if (use_lora_model !== "") { if (model_name !== "") {
use_lora_model = getModelPath(use_lora_model, [".ckpt", ".safetensors"]) model_name = getModelPath(model_name, [".ckpt", ".safetensors"])
use_lora_model = use_lora_model !== "" ? use_lora_model : oldVal model_name = model_name !== "" ? model_name : oldVal
}
field.value = model_name
})
// clear the remaining entries
for (let i = use_lora_model.length; i < loraModels.length; i++) {
loraModels[i][0].value = ""
} }
loraModelField.value = use_lora_model
}, },
readUI: () => loraModelField.value, readUI: () => {
parse: (val) => val, let values = loraModels.map((e) => e[0].value)
values = values.filter((e) => e.trim() !== "")
values = values.length > 0 ? values : "None"
return values
},
parse: (val) => {
val = !val || val === "None" ? "" : val
val = Array.isArray(val) ? val : [val]
return val
},
}, },
lora_alpha: { lora_alpha: {
name: "LoRA Strength", name: "LoRA Strength",
setUI: (lora_alpha) => { setUI: (lora_alpha) => {
loraAlphaField.value = lora_alpha lora_alpha.forEach((model_strength, i) => {
updateLoraAlphaSlider() let field = loraModels[i][1]
field.value = model_strength
})
// clear the remaining entries
for (let i = lora_alpha.length; i < loraModels.length; i++) {
loraModels[i][1].value = 0
}
},
readUI: () => {
let models = loraModels.filter((e) => e[0].value.trim() !== "")
let values = models.map((e) => e[1].value)
values = values.length > 0 ? values : 0
return values
},
parse: (val) => {
val = Array.isArray(val) ? val : [val]
val = val.map((e) => parseFloat(e))
return val
}, },
readUI: () => parseFloat(loraAlphaField.value),
parse: (val) => parseFloat(val),
}, },
use_hypernetwork_model: { use_hypernetwork_model: {
name: "Hypernetwork model", name: "Hypernetwork model",
@ -426,8 +455,11 @@ function restoreTaskToUI(task, fieldsToSkip) {
} }
if (!("use_lora_model" in task.reqBody)) { if (!("use_lora_model" in task.reqBody)) {
loraModelField.value = "" loraModels.forEach((e) => {
loraModelField.dispatchEvent(new Event("change")) e[0].value = ""
e[1].value = 0
e[0].dispatchEvent(new Event("change"))
})
} }
// restore the original prompt if provided (e.g. use settings), fallback to prompt as needed (e.g. copy/paste or d&d) // restore the original prompt if provided (e.g. use settings), fallback to prompt as needed (e.g. copy/paste or d&d)

View File

@ -103,9 +103,6 @@ let vaeModelField = new ModelDropdown(document.querySelector("#vae_model"), "vae
let hypernetworkModelField = new ModelDropdown(document.querySelector("#hypernetwork_model"), "hypernetwork", "None") let hypernetworkModelField = new ModelDropdown(document.querySelector("#hypernetwork_model"), "hypernetwork", "None")
let hypernetworkStrengthSlider = document.querySelector("#hypernetwork_strength_slider") let hypernetworkStrengthSlider = document.querySelector("#hypernetwork_strength_slider")
let hypernetworkStrengthField = document.querySelector("#hypernetwork_strength") let hypernetworkStrengthField = document.querySelector("#hypernetwork_strength")
let loraModelField = new ModelDropdown(document.querySelector("#lora_model"), "lora", "None")
let loraAlphaSlider = document.querySelector("#lora_alpha_slider")
let loraAlphaField = document.querySelector("#lora_alpha")
let outputFormatField = document.querySelector("#output_format") let outputFormatField = document.querySelector("#output_format")
let outputLosslessField = document.querySelector("#output_lossless") let outputLosslessField = document.querySelector("#output_lossless")
let outputLosslessContainer = document.querySelector("#output_lossless_container") let outputLosslessContainer = document.querySelector("#output_lossless_container")
@ -159,6 +156,8 @@ let undoButton = document.querySelector("#undo")
let undoBuffer = [] let undoBuffer = []
const UNDO_LIMIT = 20 const UNDO_LIMIT = 20
let loraModels = []
imagePreview.addEventListener("drop", function(ev) { imagePreview.addEventListener("drop", function(ev) {
const data = ev.dataTransfer?.getData("text/plain") const data = ev.dataTransfer?.getData("text/plain")
if (!data) { if (!data) {
@ -1292,13 +1291,31 @@ function getCurrentUserRequest() {
newTask.reqBody.use_hypernetwork_model = hypernetworkModelField.value newTask.reqBody.use_hypernetwork_model = hypernetworkModelField.value
newTask.reqBody.hypernetwork_strength = parseFloat(hypernetworkStrengthField.value) newTask.reqBody.hypernetwork_strength = parseFloat(hypernetworkStrengthField.value)
} }
if (testDiffusers.checked && loraModelField.value) { if (testDiffusers.checked) {
newTask.reqBody.use_lora_model = loraModelField.value let [modelNames, modelStrengths] = getModelInfo(loraModels)
newTask.reqBody.lora_alpha = parseFloat(loraAlphaField.value)
if (modelNames.length > 0) {
modelNames = modelNames.length == 1 ? modelNames[0] : modelNames
modelStrengths = modelStrengths.length == 1 ? modelStrengths[0] : modelStrengths
newTask.reqBody.use_lora_model = modelNames
newTask.reqBody.lora_alpha = modelStrengths
}
} }
return newTask return newTask
} }
function getModelInfo(models) {
let modelInfo = models.map((e) => [e[0].value, e[1].value])
modelInfo = modelInfo.filter((e) => e[0].trim() !== "")
modelInfo = modelInfo.map((e) => [e[0], parseFloat(e[1])])
let modelNames = modelInfo.map((e) => e[0])
let modelStrengths = modelInfo.map((e) => e[1])
return [modelNames, modelStrengths]
}
function getPrompts(prompts) { function getPrompts(prompts) {
if (typeof prompts === "undefined") { if (typeof prompts === "undefined") {
prompts = promptField.value prompts = promptField.value
@ -1346,7 +1363,8 @@ function getPromptsNumber(prompts) {
let promptsToMake = [] let promptsToMake = []
let numberOfPrompts = 0 let numberOfPrompts = 0
if (prompts.trim() !== "") { // this needs to stay sort of the same, as the prompts have to be passed through to the other functions if (prompts.trim() !== "") {
// this needs to stay sort of the same, as the prompts have to be passed through to the other functions
prompts = prompts.split("\n") prompts = prompts.split("\n")
prompts = prompts.map((prompt) => prompt.trim()) prompts = prompts.map((prompt) => prompt.trim())
prompts = prompts.filter((prompt) => prompt !== "") prompts = prompts.filter((prompt) => prompt !== "")
@ -1354,7 +1372,11 @@ function getPromptsNumber(prompts) {
// estimate number of prompts // estimate number of prompts
let estimatedNumberOfPrompts = 0 let estimatedNumberOfPrompts = 0
prompts.forEach((prompt) => { prompts.forEach((prompt) => {
estimatedNumberOfPrompts += (prompt.match(/{[^}]*}/g) || []).map((e) => (e.match(/,/g) || []).length + 1).reduce( (p,a) => p*a, 1) * (2**(prompt.match(/\|/g) || []).length) estimatedNumberOfPrompts +=
(prompt.match(/{[^}]*}/g) || [])
.map((e) => (e.match(/,/g) || []).length + 1)
.reduce((p, a) => p * a, 1) *
2 ** (prompt.match(/\|/g) || []).length
}) })
if (estimatedNumberOfPrompts >= 10000) { if (estimatedNumberOfPrompts >= 10000) {
@ -1394,7 +1416,8 @@ function applySetOperator(prompts) {
return promptsToMake return promptsToMake
} }
function applyPermuteOperator(prompts) { // prompts is array of input, trimmed, filtered and split by \n function applyPermuteOperator(prompts) {
// prompts is array of input, trimmed, filtered and split by \n
let promptsToMake = [] let promptsToMake = []
prompts.forEach((prompt) => { prompts.forEach((prompt) => {
let promptMatrix = prompt.split("|") let promptMatrix = prompt.split("|")
@ -1414,13 +1437,14 @@ function applyPermuteOperator(prompts) { // prompts is array of input, trimmed,
} }
// returns how many prompts would have to be made with the given prompts // returns how many prompts would have to be made with the given prompts
function applyPermuteOperatorNumber(prompts) { // prompts is array of input, trimmed, filtered and split by \n function applyPermuteOperatorNumber(prompts) {
// prompts is array of input, trimmed, filtered and split by \n
let numberOfPrompts = 0 let numberOfPrompts = 0
prompts.forEach((prompt) => { prompts.forEach((prompt) => {
let promptCounter = 1 let promptCounter = 1
let promptMatrix = prompt.split("|") let promptMatrix = prompt.split("|")
promptMatrix.shift() promptMatrix.shift()
promptMatrix = promptMatrix.map((p) => p.trim()) promptMatrix = promptMatrix.map((p) => p.trim())
promptMatrix = promptMatrix.filter((p) => p !== "") promptMatrix = promptMatrix.filter((p) => p !== "")
@ -1510,8 +1534,12 @@ clearAllPreviewsBtn.addEventListener("click", (e) => {
}) })
/* Download images popup */ /* Download images popup */
showDownloadDialogBtn.addEventListener("click", (e) => { saveAllImagesDialog.showModal() }) showDownloadDialogBtn.addEventListener("click", (e) => {
saveAllImagesCloseBtn.addEventListener("click", (e) => { saveAllImagesDialog.close() }) saveAllImagesDialog.showModal()
})
saveAllImagesCloseBtn.addEventListener("click", (e) => {
saveAllImagesDialog.close()
})
modalDialogCloseOnBackdropClick(saveAllImagesDialog) modalDialogCloseOnBackdropClick(saveAllImagesDialog)
makeDialogDraggable(saveAllImagesDialog) makeDialogDraggable(saveAllImagesDialog)
@ -1629,15 +1657,11 @@ function renameMakeImageButton() {
imageLabel = totalImages + " Images" imageLabel = totalImages + " Images"
} }
if (SD.activeTasks.size == 0) { if (SD.activeTasks.size == 0) {
if (totalImages >= 10000) if (totalImages >= 10000) makeImageBtn.innerText = "Make 10000+ images"
makeImageBtn.innerText = "Make 10000+ images" else makeImageBtn.innerText = "Make " + imageLabel
else
makeImageBtn.innerText = "Make " + imageLabel
} else { } else {
if (totalImages >= 10000) if (totalImages >= 10000) makeImageBtn.innerText = "Enqueue 10000+ images"
makeImageBtn.innerText = "Enqueue 10000+ images" else makeImageBtn.innerText = "Enqueue Next " + imageLabel
else
makeImageBtn.innerText = "Enqueue Next " + imageLabel
} }
} }
numOutputsTotalField.addEventListener("change", renameMakeImageButton) numOutputsTotalField.addEventListener("change", renameMakeImageButton)
@ -1829,36 +1853,6 @@ function updateHypernetworkStrengthContainer() {
hypernetworkModelField.addEventListener("change", updateHypernetworkStrengthContainer) hypernetworkModelField.addEventListener("change", updateHypernetworkStrengthContainer)
updateHypernetworkStrengthContainer() updateHypernetworkStrengthContainer()
/********************* LoRA alpha **********************/
function updateLoraAlpha() {
loraAlphaField.value = loraAlphaSlider.value / 100
loraAlphaField.dispatchEvent(new Event("change"))
}
function updateLoraAlphaSlider() {
if (loraAlphaField.value < -2) {
loraAlphaField.value = -2
} else if (loraAlphaField.value > 2) {
loraAlphaField.value = 2
}
loraAlphaSlider.value = loraAlphaField.value * 100
loraAlphaSlider.dispatchEvent(new Event("change"))
}
loraAlphaSlider.addEventListener("input", updateLoraAlpha)
loraAlphaField.addEventListener("input", updateLoraAlphaSlider)
updateLoraAlpha()
function updateLoraAlphaContainer() {
const loraModelContainer = document.querySelector("#lora_model_container")
if (loraModelContainer && window.getComputedStyle(loraModelContainer).display !== "none") {
document.querySelector("#lora_alpha_container").style.display = loraModelField.value === "" ? "none" : ""
}
}
loraModelField.addEventListener("change", updateLoraAlphaContainer)
updateLoraAlphaContainer()
/********************* JPEG/WEBP Quality **********************/ /********************* JPEG/WEBP Quality **********************/
function updateOutputQuality() { function updateOutputQuality() {
outputQualityField.value = 0 | outputQualitySlider.value outputQualityField.value = 0 | outputQualitySlider.value
@ -2076,9 +2070,8 @@ function resumeClient() {
}) })
} }
function splashScreen(force = false) { function splashScreen(force = false) {
const splashVersion = splashScreenPopup.dataset['version'] const splashVersion = splashScreenPopup.dataset["version"]
const lastSplash = localStorage.getItem("lastSplashScreenVersion") || 0 const lastSplash = localStorage.getItem("lastSplashScreenVersion") || 0
if (testDiffusers.checked) { if (testDiffusers.checked) {
if (force || lastSplash < splashVersion) { if (force || lastSplash < splashVersion) {
@ -2088,8 +2081,9 @@ function splashScreen(force = false) {
} }
} }
document.getElementById("logo_img").addEventListener("click", (e) => {
document.getElementById("logo_img").addEventListener("click", (e) => { splashScreen(true) }) splashScreen(true)
})
promptField.addEventListener("input", debounce(renameMakeImageButton, 1000)) promptField.addEventListener("input", debounce(renameMakeImageButton, 1000))
@ -2142,21 +2136,21 @@ document.getElementById("toggle-cloudflare-tunnel").addEventListener("click", as
/* Embeddings */ /* Embeddings */
function updateEmbeddingsList(filter="") { function updateEmbeddingsList(filter = "") {
function html(model, prefix="", filter="") { function html(model, prefix = "", filter = "") {
filter = filter.toLowerCase() filter = filter.toLowerCase()
let toplevel="" let toplevel = ""
let folders="" let folders = ""
model?.forEach( m => { model?.forEach((m) => {
if (typeof(m) == "string") { if (typeof m == "string") {
if (m.toLowerCase().search(filter)!=-1) { if (m.toLowerCase().search(filter) != -1) {
toplevel += `<button data-embedding="${m}">${m}</button> ` toplevel += `<button data-embedding="${m}">${m}</button> `
} }
} else { } else {
let subdir = html(m[1], prefix+m[0]+"/", filter) let subdir = html(m[1], prefix + m[0] + "/", filter)
if (subdir != "") { if (subdir != "") {
folders += `<h4>${prefix}${m[0]}</h4>` + subdir folders += `<h4>${prefix}${m[0]}</h4>` + subdir
} }
} }
}) })
@ -2174,7 +2168,7 @@ function updateEmbeddingsList(filter="") {
insertAtCursor(promptField, text) insertAtCursor(promptField, text)
} }
} else { } else {
let pad="" let pad = ""
if (e.shiftKey) { if (e.shiftKey) {
if (!negativePromptField.value.endsWith(" ")) { if (!negativePromptField.value.endsWith(" ")) {
pad = " " pad = " "
@ -2189,14 +2183,26 @@ function updateEmbeddingsList(filter="") {
} }
} }
embeddingsList.innerHTML = html(modelsOptions.embeddings, "", filter) // Remove after fixing https://github.com/huggingface/diffusers/issues/3922
embeddingsList.querySelectorAll("button").forEach( (b) => { b.addEventListener("click", onButtonClick)}) let warning = ""
if (vramUsageLevelField.value == "low") {
warning = `
<div style="border-color: var(--accent-color); border-width: 4px; border-radius: 1em; border-style: solid; background: black; text-align: center; padding: 1em; margin: 1em; ">
<i class="fa fa-fire" style="color:#f7630c;"></i> Warning: Your GPU memory profile is set to "Low". Embeddings currently only work in "Balanced" mode!
</div>`
}
// END of remove block
embeddingsList.innerHTML = warning + html(modelsOptions.embeddings, "", filter)
embeddingsList.querySelectorAll("button").forEach((b) => {
b.addEventListener("click", onButtonClick)
})
} }
embeddingsButton.addEventListener("click", () => { embeddingsButton.addEventListener("click", () => {
updateEmbeddingsList() updateEmbeddingsList()
embeddingsSearchBox.value="" embeddingsSearchBox.value = ""
embeddingsDialog.showModal() embeddingsDialog.showModal()
}) })
embeddingsDialogCloseBtn.addEventListener("click", (e) => { embeddingsDialogCloseBtn.addEventListener("click", (e) => {
embeddingsDialog.close() embeddingsDialog.close()
@ -2208,7 +2214,6 @@ embeddingsSearchBox.addEventListener("input", (e) => {
modalDialogCloseOnBackdropClick(embeddingsDialog) modalDialogCloseOnBackdropClick(embeddingsDialog)
makeDialogDraggable(embeddingsDialog) makeDialogDraggable(embeddingsDialog)
if (testDiffusers.checked) { if (testDiffusers.checked) {
document.getElementById("embeddings-container").classList.remove("displayNone") document.getElementById("embeddings-container").classList.remove("displayNone")
} }
@ -2235,3 +2240,43 @@ prettifyInputs(document)
// set the textbox as focused on start // set the textbox as focused on start
promptField.focus() promptField.focus()
promptField.selectionStart = promptField.value.length promptField.selectionStart = promptField.value.length
// multi-models
function addModelEntry(i, modelContainer, modelsList, modelType, defaultValue, strengthStep) {
let nameId = modelType + "_model_" + i
let strengthId = modelType + "_alpha_" + i
const modelEntry = document.createElement("div")
modelEntry.className = "model_entry"
modelEntry.innerHTML = `
<input id="${nameId}" class="model_name" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" />
<input id="${strengthId}" class="model_strength" type="number" step="${strengthStep}" style="width: 50pt" value="${defaultValue}" pattern="^-?[0-9]*\.?[0-9]*$" onkeypress="preventNonNumericalInput(event)"><br/>
`
let modelName = new ModelDropdown(modelEntry.querySelector(".model_name"), modelType, "None")
let modelStrength = modelEntry.querySelector(".model_strength")
modelContainer.appendChild(modelEntry)
modelsList.push([modelName, modelStrength])
}
function createLoRAEntries() {
let container = document.querySelector("#lora_model_container .model_entries")
for (let i = 0; i < 3; i++) {
addModelEntry(i, container, loraModels, "lora", 0.5, 0.02)
}
}
createLoRAEntries()
// chrome-like spinners only on hover
function showSpinnerOnlyOnHover(e) {
e.addEventListener("mouseenter", () => {
e.setAttribute("type", "number")
})
e.addEventListener("mouseleave", () => {
e.removeAttribute("type")
})
e.removeAttribute("type")
}
document.querySelectorAll("input[type=number]").forEach(showSpinnerOnlyOnHover)

View File

@ -436,7 +436,6 @@ async function getAppConfig() {
if (!testDiffusersEnabled) { if (!testDiffusersEnabled) {
document.querySelector("#lora_model_container").style.display = "none" document.querySelector("#lora_model_container").style.display = "none"
document.querySelector("#lora_alpha_container").style.display = "none"
document.querySelector("#tiling_container").style.display = "none" document.querySelector("#tiling_container").style.display = "none"
document.querySelectorAll("#sampler_name option.diffusers-only").forEach((option) => { document.querySelectorAll("#sampler_name option.diffusers-only").forEach((option) => {
@ -444,7 +443,6 @@ async function getAppConfig() {
}) })
} else { } else {
document.querySelector("#lora_model_container").style.display = "" document.querySelector("#lora_model_container").style.display = ""
document.querySelector("#lora_alpha_container").style.display = loraModelField.value ? "" : "none"
document.querySelector("#tiling_container").style.display = "" document.querySelector("#tiling_container").style.display = ""
document.querySelectorAll("#sampler_name option.k_diffusion-only").forEach((option) => { document.querySelectorAll("#sampler_name option.k_diffusion-only").forEach((option) => {

View File

@ -1074,6 +1074,12 @@ async function deleteKeys(keyToDelete) {
function modalDialogCloseOnBackdropClick(dialog) { function modalDialogCloseOnBackdropClick(dialog) {
dialog.addEventListener('mousedown', function (event) { dialog.addEventListener('mousedown', function (event) {
// Firefox creates an event with clientX|Y = 0|0 when choosing an <option>.
// Test whether the element interacted with is a child of the dialog, but not the
// dialog itself (the backdrop would be a part of the dialog)
if (dialog.contains(event.target) && dialog != event.target) {
return
}
var rect = dialog.getBoundingClientRect() var rect = dialog.getBoundingClientRect()
var isInDialog=(rect.top <= event.clientY && event.clientY <= rect.top + rect.height var isInDialog=(rect.top <= event.clientY && event.clientY <= rect.top + rect.height
&& rect.left <= event.clientX && event.clientX <= rect.left + rect.width) && rect.left <= event.clientX && event.clientX <= rect.left + rect.width)