Compare commits

...

40 Commits

Author SHA1 Message Date
3045f5211f Merge pull request #1321 from cmdr2/beta
Tiling and other bug fixes
2023-06-01 16:53:51 +05:30
16fcb4ed79 Merge pull request #1314 from JeLuF/dndgan
Fix GFPGAN settings import
2023-05-29 15:46:28 +05:30
9be48b3fc5 Merge pull request #1317 from ogmaresca/fix-metadata-SyntaxWarning
Fix SyntaxWarning on startup
2023-05-29 10:15:29 +05:30
7830ec7ca2 Fix SyntaxWarning on startup
Fixes
```
/ssd2/easydiffusion/ui/easydiffusion/utils/save_utils.py:222: SyntaxWarning: "is not" with a literal. Did you mean "!="?
  if task_data.use_upscale is not "latent_upscaler" and "latent_upscaler_steps" in metadata:
  ```
2023-05-28 14:39:36 -04:00
0ebf9df207 Merge pull request #1316 from JeLuF/fix1312
Fix #1312 - invert model A and B ratio in merge
2023-05-28 17:33:26 +05:30
40682405cc Merge pull request #1309 from ogmaresca/add-tiling-to-metadata
Add tiling and latent upscaler steps to metadata
2023-05-28 17:33:00 +05:30
9fdd482811 Merge pull request #1311 from patriceac/patch-3
Fix regression in restore task to UI flow
2023-05-28 17:32:26 +05:30
7202ffba6e Fix #1312 - invert model A and B ratio in merge 2023-05-28 02:36:56 +02:00
30dcc7477f Fix GFPGAN settings import
The word None which many txt metadata files contain as value for the GFPGAN field should not be considered to be a model name.
If the value is None, disable the checkbox
2023-05-28 01:43:58 +02:00
6826435046 Fix restore task to UI flow
Fixes a regression introduced by https://github.com/cmdr2/stable-diffusion-ui/pull/1304
2023-05-27 00:26:25 -07:00
69d937e0b1 Add tiling and latent upscaler steps to metadata
Also fix txt metadata labels when also embedding metadata
2023-05-26 19:51:30 -04:00
edd92b724f UniPC TU 2 isn't working with diffusers either 2023-05-26 19:47:00 +05:30
41ecc822df Merge pull request #1305 from JeLuF/patch-27
Update "How to install and run.txt"
2023-05-26 15:25:31 +05:30
0990d8fc4d Merge pull request #1304 from JeLuF/dndfix
Remove warning when reusing settings - Fixes #1290
2023-05-26 15:24:56 +05:30
ce2a42ca13 Update "How to install and run.txt" 2023-05-25 20:18:19 +02:00
1da35e89f6 Capitalization 2023-05-25 18:38:40 +05:30
d818107953 Remove warning when reusing settings - Fixes #1290 2023-05-25 13:36:45 +02:00
b3f65c0b3c changelog 2023-05-25 15:52:05 +05:30
59c322dc3b Show seamless tiling only in diffusers mode 2023-05-25 15:41:41 +05:30
096f9ad3a6 Merge branch 'beta' of github.com:cmdr2/stable-diffusion-ui into beta 2023-05-25 15:39:02 +05:30
5c8965b3ab changelog 2023-05-25 15:38:49 +05:30
090f8f6070 Merge pull request #1300 from JeLuF/tile
Add seamless tiling support
2023-05-25 15:38:15 +05:30
5f4fc63645 Merge branch 'beta' of github.com:cmdr2/stable-diffusion-ui into beta 2023-05-25 15:37:37 +05:30
a0b3b5af53 sdkit 1.0.98 - seamless tiling 2023-05-25 15:36:27 +05:30
351dd97500 Merge pull request #1303 from cmdr2/main
Main
2023-05-25 15:05:05 +05:30
b511000441 Merge pull request #1302 from cmdr2/beta
Beta
2023-05-25 14:57:51 +05:30
523131de79 Merge pull request #1298 from JeLuF/confix
Fix confirmation dialog
2023-05-25 07:19:21 +05:30
9dfa300083 Add seamless tiling support 2023-05-25 00:16:14 +02:00
3ea74af76d Fix confirmation dialog
By splitting the confirmation function into two halves, the closure was lost
2023-05-24 19:29:54 +02:00
3d7e16cfd9 changelog 2023-05-24 16:29:58 +05:30
db265309a5 Show an explanation for why the CPU toggle is disabled; utility class for alert() and confirm() that matches the ED theme; code formatting 2023-05-24 16:24:29 +05:30
8554b0eab2 Better reporting of model load errors - sends the report to the browser UI during the next image rendering task 2023-05-24 16:02:53 +05:30
f641e6e69d Merge branch 'beta' of github.com:cmdr2/stable-diffusion-ui into beta 2023-05-24 15:40:26 +05:30
30c07eab6b Cleaner reporting of errors in the UI; Suggest increasing the page size if that's the error 2023-05-24 15:30:55 +05:30
eba83386c1 make a note about a flood fill library 2023-05-24 10:08:00 +05:30
d3334f9dfa Merge branch 'beta' of github.com:cmdr2/stable-diffusion-ui into beta 2023-05-23 16:55:52 +05:30
a87dca1ef4 changelog 2023-05-23 16:55:42 +05:30
2bab4341a3 Add 'Latent Upscaler' as an option in the upscaling dropdown 2023-05-23 16:53:53 +05:30
01fb2fde47 Merge pull request #1293 from JeLuF/edready
Add 'ED is ready, go to localhost:9000' msg to log
2023-05-23 15:15:08 +05:30
0127714929 Add 'ED is ready, go to localhost:9000' msg to log
Sometimes the browser window does not open (esp. on Linux and Mac).
Show a prominent message to the log so that users don't wait for hours.
2023-05-22 21:19:31 +02:00
19 changed files with 293 additions and 105 deletions

View File

@ -22,6 +22,9 @@
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.39 - 25 May 2023 - (beta-only) Seamless Tiling - make seamlessly tiled images, e.g. rock and grass textures. Thanks @JeLuf.
* 2.5.38 - 24 May 2023 - Better reporting of errors, and show an explanation if the user cannot disable the "Use CPU" setting.
* 2.5.38 - 23 May 2023 - Add Latent Upscaler as another option for upscaling images. Thanks @JeLuf for the implementation of the Latent Upscaler model.
* 2.5.37 - 19 May 2023 - (beta-only) Two more samplers: DDPM and DEIS. Also disables the samplers that aren't working yet in the Diffusers version. Thanks @ogmaresca. * 2.5.37 - 19 May 2023 - (beta-only) Two more samplers: DDPM and DEIS. Also disables the samplers that aren't working yet in the Diffusers version. Thanks @ogmaresca.
* 2.5.37 - 19 May 2023 - (beta-only) Support CLIP-Skip. You can set this option under the models dropdown. Thanks @JeLuf. * 2.5.37 - 19 May 2023 - (beta-only) Support CLIP-Skip. You can set this option under the models dropdown. Thanks @JeLuf.
* 2.5.37 - 19 May 2023 - (beta-only) More VRAM optimizations for all modes in diffusers. The VRAM usage for diffusers in "low" and "balanced" should now be equal or less than the non-diffusers version. Performs softmax in half precision, like sdkit does. * 2.5.37 - 19 May 2023 - (beta-only) More VRAM optimizations for all modes in diffusers. The VRAM usage for diffusers in "low" and "balanced" should now be equal or less than the non-diffusers version. Performs softmax in half precision, like sdkit does.

View File

@ -5,10 +5,10 @@ If you haven't downloaded Stable Diffusion UI yet, please download from https://
After downloading, to install please follow these instructions: After downloading, to install please follow these instructions:
For Windows: For Windows:
- Please double-click the "Start Stable Diffusion UI.cmd" file inside the "stable-diffusion-ui" folder. - Please double-click the "Easy-Diffusion-Windows.exe" file and follow the instructions.
For Linux: For Linux:
- Please open a terminal, and go to the "stable-diffusion-ui" directory. Then run ./start.sh - Please open a terminal, unzip the Easy-Diffusion-Linux.zip file and go to the "easy-diffusion" directory. Then run ./start.sh
That file will automatically install everything. After that it will start the Stable Diffusion interface in a web browser. That file will automatically install everything. After that it will start the Stable Diffusion interface in a web browser.
@ -21,4 +21,4 @@ If you have any problems, please:
3. Or, file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues 3. Or, file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues
Thanks Thanks
cmdr2 (and contributors to the project) cmdr2 (and contributors to the project)

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.97", "sdkit": "1.0.98",
"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

@ -10,6 +10,8 @@ import warnings
from easydiffusion import task_manager from easydiffusion import task_manager
from easydiffusion.utils import log from easydiffusion.utils import log
from rich.logging import RichHandler from rich.logging import RichHandler
from rich.console import Console
from rich.panel import Panel
from sdkit.utils import log as sdkit_log # hack, so we can overwrite the log config from sdkit.utils import log as sdkit_log # hack, so we can overwrite the log config
# Remove all handlers associated with the root logger object. # Remove all handlers associated with the root logger object.
@ -213,11 +215,19 @@ def open_browser():
ui = config.get("ui", {}) ui = config.get("ui", {})
net = config.get("net", {}) net = config.get("net", {})
port = net.get("listen_port", 9000) port = net.get("listen_port", 9000)
if ui.get("open_browser_on_start", True): if ui.get("open_browser_on_start", True):
import webbrowser import webbrowser
webbrowser.open(f"http://localhost:{port}") webbrowser.open(f"http://localhost:{port}")
Console().print(Panel(
"\n" +
"[white]Easy Diffusion is ready to serve requests.\n\n" +
"A new browser tab should have been opened by now.\n" +
f"If not, please open your web browser and navigate to [bold yellow underline]http://localhost:{port}/\n",
title="Easy Diffusion is ready", style="bold yellow on blue"))
def get_image_modifiers(): def get_image_modifiers():
modifiers_json_path = os.path.join(SD_UI_DIR, "modifiers.json") modifiers_json_path = os.path.join(SD_UI_DIR, "modifiers.json")

View File

@ -53,15 +53,21 @@ def load_default_models(context: Context):
scan_model=context.model_paths[model_type] != None scan_model=context.model_paths[model_type] != None
and not context.model_paths[model_type].endswith(".safetensors"), and not context.model_paths[model_type].endswith(".safetensors"),
) )
if model_type in context.model_load_errors:
del context.model_load_errors[model_type]
except Exception as e: except Exception as e:
log.error(f"[red]Error while loading {model_type} model: {context.model_paths[model_type]}[/red]") log.error(f"[red]Error while loading {model_type} model: {context.model_paths[model_type]}[/red]")
log.exception(e) log.exception(e)
del context.model_paths[model_type] del context.model_paths[model_type]
context.model_load_errors[model_type] = str(e) # storing the entire Exception can lead to memory leaks
def unload_all(context: Context): def unload_all(context: Context):
for model_type in KNOWN_MODEL_TYPES: for model_type in KNOWN_MODEL_TYPES:
unload_model(context, model_type) unload_model(context, model_type)
if model_type in context.model_load_errors:
del context.model_load_errors[model_type]
def resolve_model_to_use(model_name: str = None, model_type: str = None): def resolve_model_to_use(model_name: str = None, model_type: str = None):
@ -107,12 +113,15 @@ def resolve_model_to_use(model_name: str = None, model_type: str = None):
def reload_models_if_necessary(context: Context, task_data: TaskData): def reload_models_if_necessary(context: Context, task_data: TaskData):
use_upscale_lower = task_data.use_upscale.lower() if task_data.use_upscale else ""
model_paths_in_req = { model_paths_in_req = {
"stable-diffusion": task_data.use_stable_diffusion_model, "stable-diffusion": task_data.use_stable_diffusion_model,
"vae": task_data.use_vae_model, "vae": task_data.use_vae_model,
"hypernetwork": task_data.use_hypernetwork_model, "hypernetwork": task_data.use_hypernetwork_model,
"gfpgan": task_data.use_face_correction, "gfpgan": task_data.use_face_correction,
"realesrgan": task_data.use_upscale, "realesrgan": task_data.use_upscale if "realesrgan" in use_upscale_lower else None,
"latent_upscaler": True if task_data.use_upscale == "latent_upscaler" else None,
"nsfw_checker": True if task_data.block_nsfw else None, "nsfw_checker": True if task_data.block_nsfw else None,
"lora": task_data.use_lora_model, "lora": task_data.use_lora_model,
} }
@ -129,7 +138,14 @@ def reload_models_if_necessary(context: Context, task_data: TaskData):
context.model_paths[model_type] = model_path_in_req context.model_paths[model_type] = model_path_in_req
action_fn = unload_model if context.model_paths[model_type] is None else load_model action_fn = unload_model if context.model_paths[model_type] is None else load_model
action_fn(context, model_type, scan_model=False) # we've scanned them already try:
action_fn(context, model_type, scan_model=False) # we've scanned them already
if model_type in context.model_load_errors:
del context.model_load_errors[model_type]
except Exception as e:
log.exception(e)
if action_fn == load_model:
context.model_load_errors[model_type] = str(e) # storing the entire Exception can lead to memory leaks
def resolve_model_paths(task_data: TaskData): def resolve_model_paths(task_data: TaskData):
@ -142,10 +158,18 @@ def resolve_model_paths(task_data: TaskData):
if task_data.use_face_correction: if task_data.use_face_correction:
task_data.use_face_correction = resolve_model_to_use(task_data.use_face_correction, "gfpgan") task_data.use_face_correction = resolve_model_to_use(task_data.use_face_correction, "gfpgan")
if task_data.use_upscale: if task_data.use_upscale and "realesrgan" in task_data.use_upscale.lower():
task_data.use_upscale = resolve_model_to_use(task_data.use_upscale, "realesrgan") task_data.use_upscale = resolve_model_to_use(task_data.use_upscale, "realesrgan")
def fail_if_models_did_not_load(context: Context):
for model_type in KNOWN_MODEL_TYPES:
if model_type in context.model_load_errors:
e = context.model_load_errors[model_type]
raise Exception(f"Could not load the {model_type} model! Reason: " + e)
# concat 'e', don't use in format string (injection attack)
def set_vram_optimizations(context: Context): def set_vram_optimizations(context: Context):
config = app.getConfig() config = app.getConfig()
vram_usage_level = config.get("vram_usage_level", "balanced") vram_usage_level = config.get("vram_usage_level", "balanced")

View File

@ -33,6 +33,7 @@ def init(device):
context.stop_processing = False context.stop_processing = False
context.temp_images = {} context.temp_images = {}
context.partial_x_samples = None context.partial_x_samples = None
context.model_load_errors = {}
from easydiffusion import app from easydiffusion import app
@ -95,7 +96,7 @@ def make_images_internal(
task_data.stream_image_progress_interval, task_data.stream_image_progress_interval,
) )
gc(context) gc(context)
filtered_images = filter_images(task_data, images, user_stopped) filtered_images = filter_images(req, task_data, images, user_stopped)
if task_data.save_to_disk_path is not None: if task_data.save_to_disk_path is not None:
save_images_to_disk(images, filtered_images, req, task_data) save_images_to_disk(images, filtered_images, req, task_data)
@ -151,22 +152,36 @@ def generate_images_internal(
return images, user_stopped return images, user_stopped
def filter_images(task_data: TaskData, images: list, user_stopped): def filter_images(req: GenerateImageRequest, task_data: TaskData, images: list, user_stopped):
if user_stopped: if user_stopped:
return images return images
filters_to_apply = [] filters_to_apply = []
filter_params = {}
if task_data.block_nsfw: if task_data.block_nsfw:
filters_to_apply.append("nsfw_checker") filters_to_apply.append("nsfw_checker")
if task_data.use_face_correction and "gfpgan" in task_data.use_face_correction.lower(): if task_data.use_face_correction and "gfpgan" in task_data.use_face_correction.lower():
filters_to_apply.append("gfpgan") filters_to_apply.append("gfpgan")
if task_data.use_upscale and "realesrgan" in task_data.use_upscale.lower(): if task_data.use_upscale:
filters_to_apply.append("realesrgan") if "realesrgan" in task_data.use_upscale.lower():
filters_to_apply.append("realesrgan")
elif task_data.use_upscale == "latent_upscaler":
filters_to_apply.append("latent_upscaler")
filter_params["latent_upscaler_options"] = {
"prompt": req.prompt,
"negative_prompt": req.negative_prompt,
"seed": req.seed,
"num_inference_steps": task_data.latent_upscaler_steps,
"guidance_scale": 0,
}
filter_params["scale"] = task_data.upscale_amount
if len(filters_to_apply) == 0: if len(filters_to_apply) == 0:
return images return images
return apply_filters(context, filters_to_apply, images, scale=task_data.upscale_amount) return apply_filters(context, filters_to_apply, images, **filter_params)
def construct_response(images: list, seeds: list, task_data: TaskData, base_seed: int): def construct_response(images: list, seeds: list, task_data: TaskData, base_seed: int):

View File

@ -336,6 +336,7 @@ def thread_render(device):
current_state = ServerStates.LoadingModel current_state = ServerStates.LoadingModel
model_manager.resolve_model_paths(task.task_data) model_manager.resolve_model_paths(task.task_data)
model_manager.reload_models_if_necessary(renderer.context, task.task_data) model_manager.reload_models_if_necessary(renderer.context, task.task_data)
model_manager.fail_if_models_did_not_load(renderer.context)
current_state = ServerStates.Rendering current_state = ServerStates.Rendering
task.response = renderer.make_images( task.response = renderer.make_images(

View File

@ -23,6 +23,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: float = 0
tiling: str = "none" # "none", "x", "y", "xy"
class TaskData(BaseModel): class TaskData(BaseModel):
@ -32,8 +33,9 @@ class TaskData(BaseModel):
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: str = None # or "GFPGANv1.3"
use_upscale: str = None # or "RealESRGAN_x4plus" or "RealESRGAN_x4plus_anime_6B" use_upscale: str = None # or "RealESRGAN_x4plus" or "RealESRGAN_x4plus_anime_6B" or "latent_upscaler"
upscale_amount: int = 4 # or 2 upscale_amount: int = 4 # or 2
latent_upscaler_steps: int = 10
use_stable_diffusion_model: str = "sd-v1-4" use_stable_diffusion_model: str = "sd-v1-4"
# use_stable_diffusion_config: str = "v1-inference" # use_stable_diffusion_config: str = "v1-inference"
use_vae_model: str = None use_vae_model: str = None

View File

@ -30,9 +30,11 @@ 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",
"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"
} }
time_placeholders = { time_placeholders = {
@ -169,21 +171,23 @@ def save_images_to_disk(images: list, filtered_images: list, req: GenerateImageR
output_quality=task_data.output_quality, output_quality=task_data.output_quality,
output_lossless=task_data.output_lossless, output_lossless=task_data.output_lossless,
) )
if task_data.metadata_output_format.lower() in ["json", "txt", "embed"]: if task_data.metadata_output_format:
save_dicts( for metadata_output_format in task_data.metadata_output_format.split(","):
metadata_entries, if metadata_output_format.lower() in ["json", "txt", "embed"]:
save_dir_path, save_dicts(
file_name=make_filter_filename, metadata_entries,
output_format=task_data.metadata_output_format, save_dir_path,
file_format=task_data.output_format, file_name=make_filter_filename,
) output_format=task_data.metadata_output_format,
file_format=task_data.output_format,
)
def get_metadata_entries_for_request(req: GenerateImageRequest, task_data: TaskData): def get_metadata_entries_for_request(req: GenerateImageRequest, task_data: TaskData):
metadata = get_printable_request(req, task_data) metadata = get_printable_request(req, task_data)
# if text, format it in the text format expected by the UI # if text, format it in the text format expected by the UI
is_txt_format = task_data.metadata_output_format.lower() == "txt" is_txt_format = task_data.metadata_output_format and "txt" in task_data.metadata_output_format.lower().split(",")
if is_txt_format: if is_txt_format:
metadata = {TASK_TEXT_MAPPING[key]: val for key, val in metadata.items() if key in TASK_TEXT_MAPPING} metadata = {TASK_TEXT_MAPPING[key]: val for key, val in metadata.items() if key in TASK_TEXT_MAPPING}
@ -215,10 +219,12 @@ def get_printable_request(req: GenerateImageRequest, task_data: TaskData):
del metadata["hypernetwork_strength"] del metadata["hypernetwork_strength"]
if task_data.use_lora_model is None and "lora_alpha" in metadata: if task_data.use_lora_model is None and "lora_alpha" in metadata:
del metadata["lora_alpha"] del metadata["lora_alpha"]
if task_data.use_upscale != "latent_upscaler" and "latent_upscaler_steps" in metadata:
del metadata["latent_upscaler_steps"]
app_config = app.getConfig() app_config = app.getConfig()
if not app_config.get("test_diffusers", False): if not app_config.get("test_diffusers", False):
for key in (x for x in ["use_lora_model", "lora_alpha", "clip_skip"] 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]
return metadata return metadata

View File

@ -30,7 +30,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>v2.5.37 <span id="updateBranchLabel"></span></small> <small>v2.5.39 <span id="updateBranchLabel"></span></small>
</h1> </h1>
</div> </div>
<div id="server-status"> <div id="server-status">
@ -167,7 +167,7 @@
<option value="unipc_snr" class="k_diffusion-only">UniPC SNR</option> <option value="unipc_snr" class="k_diffusion-only">UniPC SNR</option>
<option value="unipc_tu">UniPC TU</option> <option value="unipc_tu">UniPC TU</option>
<option value="unipc_snr_2" class="k_diffusion-only">UniPC SNR 2</option> <option value="unipc_snr_2" class="k_diffusion-only">UniPC SNR 2</option>
<option value="unipc_tu_2">UniPC TU 2</option> <option value="unipc_tu_2" class="k_diffusion-only">UniPC TU 2</option>
<option value="unipc_tq" class="k_diffusion-only">UniPC TQ</option> <option value="unipc_tq" class="k_diffusion-only">UniPC TQ</option>
</select> </select>
<a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/How-to-Use#samplers" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Click to learn more about samplers</span></i></a> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/How-to-Use#samplers" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Click to learn more about samplers</span></i></a>
@ -236,6 +236,15 @@
<td><label for="hypernetwork_strength_slider">Hypernetwork Strength:</label></td> <td><label for="hypernetwork_strength_slider">Hypernetwork Strength:</label></td>
<td> <input id="hypernetwork_strength_slider" name="hypernetwork_strength_slider" class="editor-slider" value="100" type="range" min="0" max="100"> <input id="hypernetwork_strength" name="hypernetwork_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"><br/></td> <td> <input id="hypernetwork_strength_slider" name="hypernetwork_strength_slider" class="editor-slider" value="100" type="range" min="0" max="100"> <input id="hypernetwork_strength" name="hypernetwork_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"><br/></td>
</tr> </tr>
<tr id="tiling_container" class="pl-5"><td><label for="tiling">Seamless Tiling:</label></td><td>
<select id="tiling" name="tiling">
<option value="none" selected>None</option>
<option value="x">Horizontal</option>
<option value="y">Vertical</option>
<option value="xy">Both</option>
</select>
<a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Seamless-Tiling" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Click to learn more about Seamless Tiling</span></i></a>
</td></tr>
<tr class="pl-5"><td><label for="output_format">Output Format:</label></td><td> <tr class="pl-5"><td><label for="output_format">Output Format:</label></td><td>
<select id="output_format" name="output_format"> <select id="output_format" name="output_format">
<option value="jpeg" selected>jpeg</option> <option value="jpeg" selected>jpeg</option>
@ -258,14 +267,18 @@
<li class="pl-5"> <li class="pl-5">
<input id="use_upscale" name="use_upscale" type="checkbox"> <label for="use_upscale">Scale up by</label> <input id="use_upscale" name="use_upscale" type="checkbox"> <label for="use_upscale">Scale up by</label>
<select id="upscale_amount" name="upscale_amount"> <select id="upscale_amount" name="upscale_amount">
<option value="2">2x</option> <option id="upscale_amount_2x" value="2">2x</option>
<option value="4" selected>4x</option> <option id="upscale_amount_4x" value="4" selected>4x</option>
</select> </select>
with with
<select id="upscale_model" name="upscale_model"> <select id="upscale_model" name="upscale_model">
<option value="RealESRGAN_x4plus" selected>RealESRGAN_x4plus</option> <option value="RealESRGAN_x4plus" selected>RealESRGAN_x4plus</option>
<option value="RealESRGAN_x4plus_anime_6B">RealESRGAN_x4plus_anime_6B</option> <option value="RealESRGAN_x4plus_anime_6B">RealESRGAN_x4plus_anime_6B</option>
<option value="latent_upscaler">Latent Upscaler 2x</option>
</select> </select>
<div id="latent_upscaler_settings" class="displayNone">
<label for="latent_upscaler_steps_slider">Upscaling Steps:</label></td><td> <input id="latent_upscaler_steps_slider" name="latent_upscaler_steps_slider" class="editor-slider" value="10" type="range" min="1" max="50"> <input id="latent_upscaler_steps" name="latent_upscaler_steps" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)">
</div>
</li> </li>
<li class="pl-5"><input id="show_only_filtered_image" name="show_only_filtered_image" type="checkbox" checked> <label for="show_only_filtered_image">Show only the corrected/upscaled image</label></li> <li class="pl-5"><input id="show_only_filtered_image" name="show_only_filtered_image" type="checkbox" checked> <label for="show_only_filtered_image">Show only the corrected/upscaled image</label></li>
</ul></div> </ul></div>

View File

@ -1303,6 +1303,12 @@ body.wait-pause {
display:none !important; display:none !important;
} }
#latent_upscaler_settings {
padding-top: 3pt;
padding-bottom: 3pt;
padding-left: 5pt;
}
/* TOAST NOTIFICATIONS */ /* TOAST NOTIFICATIONS */
.toast-notification { .toast-notification {
position: fixed; position: fixed;

View File

@ -25,6 +25,7 @@ const SETTINGS_IDS_LIST = [
"prompt_strength", "prompt_strength",
"hypernetwork_strength", "hypernetwork_strength",
"lora_alpha", "lora_alpha",
"tiling",
"output_format", "output_format",
"output_quality", "output_quality",
"output_lossless", "output_lossless",
@ -34,6 +35,7 @@ const SETTINGS_IDS_LIST = [
"gfpgan_model", "gfpgan_model",
"use_upscale", "use_upscale",
"upscale_amount", "upscale_amount",
"latent_upscaler_steps",
"block_nsfw", "block_nsfw",
"show_only_filtered_image", "show_only_filtered_image",
"upscale_model", "upscale_model",

View File

@ -79,6 +79,7 @@ const TASK_MAPPING = {
if (!widthField.value) { if (!widthField.value) {
widthField.value = oldVal widthField.value = oldVal
} }
widthField.dispatchEvent(new Event("change"))
}, },
readUI: () => parseInt(widthField.value), readUI: () => parseInt(widthField.value),
parse: (val) => parseInt(val), parse: (val) => parseInt(val),
@ -91,6 +92,7 @@ const TASK_MAPPING = {
if (!heightField.value) { if (!heightField.value) {
heightField.value = oldVal heightField.value = oldVal
} }
heightField.dispatchEvent(new Event("change"))
}, },
readUI: () => parseInt(heightField.value), readUI: () => parseInt(heightField.value),
parse: (val) => parseInt(val), parse: (val) => parseInt(val),
@ -172,16 +174,22 @@ const TASK_MAPPING = {
name: "Use Face Correction", name: "Use Face Correction",
setUI: (use_face_correction) => { setUI: (use_face_correction) => {
const oldVal = gfpganModelField.value const oldVal = gfpganModelField.value
gfpganModelField.value = getModelPath(use_face_correction, [".pth"]) console.log("use face correction", use_face_correction)
if (gfpganModelField.value) { if (use_face_correction == null || use_face_correction == "None") {
// Is a valid value for the field.
useFaceCorrectionField.checked = true
gfpganModelField.disabled = false
} else {
// Not a valid value, restore the old value and disable the filter.
gfpganModelField.disabled = true gfpganModelField.disabled = true
gfpganModelField.value = oldVal
useFaceCorrectionField.checked = false useFaceCorrectionField.checked = false
} else {
gfpganModelField.value = getModelPath(use_face_correction, [".pth"])
if (gfpganModelField.value) {
// Is a valid value for the field.
useFaceCorrectionField.checked = true
gfpganModelField.disabled = false
} else {
// Not a valid value, restore the old value and disable the filter.
gfpganModelField.disabled = true
gfpganModelField.value = oldVal
useFaceCorrectionField.checked = false
}
} }
//useFaceCorrectionField.checked = parseBoolean(use_face_correction) //useFaceCorrectionField.checked = parseBoolean(use_face_correction)
@ -218,6 +226,14 @@ const TASK_MAPPING = {
readUI: () => upscaleAmountField.value, readUI: () => upscaleAmountField.value,
parse: (val) => val, parse: (val) => val,
}, },
latent_upscaler_steps: {
name: "Latent Upscaler Steps",
setUI: (latent_upscaler_steps) => {
latentUpscalerStepsField.value = latent_upscaler_steps
},
readUI: () => latentUpscalerStepsField.value,
parse: (val) => val,
},
sampler_name: { sampler_name: {
name: "Sampler", name: "Sampler",
setUI: (sampler_name) => { setUI: (sampler_name) => {
@ -249,6 +265,14 @@ const TASK_MAPPING = {
readUI: () => clip_skip.checked, readUI: () => clip_skip.checked,
parse: (val) => Boolean(val), parse: (val) => Boolean(val),
}, },
tiling: {
name: "Tiling",
setUI: (val) => {
tilingField.value = val
},
readUI: () => tilingField.value,
parse: (val) => val,
},
use_vae_model: { use_vae_model: {
name: "VAE model", name: "VAE model",
setUI: (use_vae_model) => { setUI: (use_vae_model) => {
@ -411,6 +435,7 @@ function restoreTaskToUI(task, fieldsToSkip) {
if (!("original_prompt" in task.reqBody)) { if (!("original_prompt" in task.reqBody)) {
promptField.value = task.reqBody.prompt promptField.value = task.reqBody.prompt
} }
promptField.dispatchEvent(new Event("input"))
// properly reset checkboxes // properly reset checkboxes
if (!("use_face_correction" in task.reqBody)) { if (!("use_face_correction" in task.reqBody)) {

View File

@ -789,9 +789,10 @@
use_hypernetwork_model: "string", use_hypernetwork_model: "string",
hypernetwork_strength: "number", hypernetwork_strength: "number",
output_lossless: "boolean", output_lossless: "boolean",
tiling: "string",
} }
// Higer values will result in... // Higher values will result in...
// pytorch_lightning/utilities/seed.py:60: UserWarning: X is not in bounds, numpy accepts from 0 to 4294967295 // pytorch_lightning/utilities/seed.py:60: UserWarning: X is not in bounds, numpy accepts from 0 to 4294967295
const MAX_SEED_VALUE = 4294967295 const MAX_SEED_VALUE = 4294967295

View File

@ -834,6 +834,7 @@ function pixelCompare(int1, int2) {
} }
// adapted from https://ben.akrin.com/canvas_fill/fill_04.html // adapted from https://ben.akrin.com/canvas_fill/fill_04.html
// May 2023 - look at using a library instead of custom code: https://github.com/shaneosullivan/example-canvas-fill
function flood_fill(editor, the_canvas_context, x, y, color) { function flood_fill(editor, the_canvas_context, x, y, color) {
pixel_stack = [{ x: x, y: y }] pixel_stack = [{ x: x, y: y }]
pixels = the_canvas_context.getImageData(0, 0, editor.width, editor.height) pixels = the_canvas_context.getImageData(0, 0, editor.width, editor.height)

View File

@ -18,6 +18,11 @@ const taskConfigSetup = {
visible: ({ reqBody }) => reqBody?.clip_skip, visible: ({ reqBody }) => reqBody?.clip_skip,
value: ({ reqBody }) => "yes", value: ({ reqBody }) => "yes",
}, },
tiling: {
label: "Tiling",
visible: ({ reqBody }) => reqBody?.tiling != "none",
value: ({ reqBody }) => reqBody?.tiling,
},
use_vae_model: { use_vae_model: {
label: "VAE", label: "VAE",
visible: ({ reqBody }) => reqBody?.use_vae_model !== undefined && reqBody?.use_vae_model.trim() !== "", visible: ({ reqBody }) => reqBody?.use_vae_model !== undefined && reqBody?.use_vae_model.trim() !== "",
@ -86,8 +91,12 @@ let gfpganModelField = new ModelDropdown(document.querySelector("#gfpgan_model")
let useUpscalingField = document.querySelector("#use_upscale") let useUpscalingField = document.querySelector("#use_upscale")
let upscaleModelField = document.querySelector("#upscale_model") let upscaleModelField = document.querySelector("#upscale_model")
let upscaleAmountField = document.querySelector("#upscale_amount") let upscaleAmountField = document.querySelector("#upscale_amount")
let latentUpscalerSettings = document.querySelector("#latent_upscaler_settings")
let latentUpscalerStepsSlider = document.querySelector("#latent_upscaler_steps_slider")
let latentUpscalerStepsField = document.querySelector("#latent_upscaler_steps")
let stableDiffusionModelField = new ModelDropdown(document.querySelector("#stable_diffusion_model"), "stable-diffusion") let stableDiffusionModelField = new ModelDropdown(document.querySelector("#stable_diffusion_model"), "stable-diffusion")
let clipSkipField = document.querySelector("#clip_skip") let clipSkipField = document.querySelector("#clip_skip")
let tilingField = document.querySelector("#tiling")
let vaeModelField = new ModelDropdown(document.querySelector("#vae_model"), "vae", "None") let vaeModelField = new ModelDropdown(document.querySelector("#vae_model"), "vae", "None")
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")
@ -239,7 +248,7 @@ function setServerStatus(event) {
break break
} }
if (SD.serverState.devices) { if (SD.serverState.devices) {
document.dispatchEvent(new CustomEvent("system_info_update", { detail: SD.serverState.devices})) document.dispatchEvent(new CustomEvent("system_info_update", { detail: SD.serverState.devices }))
} }
} }
@ -258,20 +267,11 @@ function shiftOrConfirm(e, prompt, fn) {
if (e.shiftKey || !confirmDangerousActionsField.checked) { if (e.shiftKey || !confirmDangerousActionsField.checked) {
fn(e) fn(e)
} else { } else {
$.confirm({ confirm(
theme: "modern", '<small>Tip: To skip this dialog, use shift-click or disable the "Confirm dangerous actions" setting in the Settings tab.</small>',
title: prompt, prompt,
useBootstrap: false, () => { fn(e) }
animateFromElement: false, )
content:
'<small>Tip: To skip this dialog, use shift-click or disable the "Confirm dangerous actions" setting in the Settings tab.</small>',
buttons: {
yes: () => {
fn(e)
},
cancel: () => {},
},
})
} }
} }
@ -293,6 +293,7 @@ function logError(msg, res, outputMsg) {
logMsg(msg, "error", outputMsg) logMsg(msg, "error", outputMsg)
console.log("request error", res) console.log("request error", res)
console.trace()
setStatus("request", "error", "error") setStatus("request", "error", "error")
} }
@ -784,11 +785,6 @@ function getTaskUpdater(task, reqBody, outputContainer) {
} }
msg += "</pre>" msg += "</pre>"
logError(msg, event, outputMsg) logError(msg, event, outputMsg)
} else {
let msg = `Unexpected Read Error:<br/><pre>Error:${
this.exception
}<br/>EventInfo: ${JSON.stringify(event, undefined, 4)}</pre>`
logError(msg, event, outputMsg)
} }
break break
} }
@ -885,15 +881,15 @@ function onTaskCompleted(task, reqBody, instance, outputContainer, stepUpdate) {
1. If you have set an initial image, please try reducing its dimension to ${MAX_INIT_IMAGE_DIMENSION}x${MAX_INIT_IMAGE_DIMENSION} or smaller.<br/> 1. If you have set an initial image, please try reducing its dimension to ${MAX_INIT_IMAGE_DIMENSION}x${MAX_INIT_IMAGE_DIMENSION} or smaller.<br/>
2. Try picking a lower level in the '<em>GPU Memory Usage</em>' setting (in the '<em>Settings</em>' tab).<br/> 2. Try picking a lower level in the '<em>GPU Memory Usage</em>' setting (in the '<em>Settings</em>' tab).<br/>
3. Try generating a smaller image.<br/>` 3. Try generating a smaller image.<br/>`
} else if (msg.toLowerCase().includes("DefaultCPUAllocator: not enough memory")) { } else if (msg.includes("DefaultCPUAllocator: not enough memory")) {
msg += `<br/><br/> msg += `<br/><br/>
Reason: Your computer is running out of system RAM! Reason: Your computer is running out of system RAM!
<br/> <br/><br/>
<b>Suggestions</b>: <b>Suggestions</b>:
<br/> <br/>
1. Try closing unnecessary programs and browser tabs.<br/> 1. Try closing unnecessary programs and browser tabs.<br/>
2. If that doesn't help, please increase your computer's virtual memory by following these steps for 2. If that doesn't help, please increase your computer's virtual memory by following these steps for
<a href="https://www.ibm.com/docs/en/opw/8.2.0?topic=tuning-optional-increasing-paging-file-size-windows-computers" target="_blank">Windows</a>, or <a href="https://www.ibm.com/docs/en/opw/8.2.0?topic=tuning-optional-increasing-paging-file-size-windows-computers" target="_blank">Windows</a> or
<a href="https://linuxhint.com/increase-swap-space-linux/" target="_blank">Linux</a>.<br/> <a href="https://linuxhint.com/increase-swap-space-linux/" target="_blank">Linux</a>.<br/>
3. Try restarting your computer.<br/>` 3. Try restarting your computer.<br/>`
} }
@ -1231,6 +1227,7 @@ function getCurrentUserRequest() {
//render_device: undefined, // Set device affinity. Prefer this device, but wont activate. //render_device: undefined, // Set device affinity. Prefer this device, but wont activate.
use_stable_diffusion_model: stableDiffusionModelField.value, use_stable_diffusion_model: stableDiffusionModelField.value,
clip_skip: clipSkipField.checked, clip_skip: clipSkipField.checked,
tiling: tilingField.value,
use_vae_model: vaeModelField.value, use_vae_model: vaeModelField.value,
stream_progress_updates: true, stream_progress_updates: true,
stream_image_progress: numOutputsTotal > 50 ? false : streamImageProgressField.checked, stream_image_progress: numOutputsTotal > 50 ? false : streamImageProgressField.checked,
@ -1268,6 +1265,10 @@ function getCurrentUserRequest() {
if (useUpscalingField.checked) { if (useUpscalingField.checked) {
newTask.reqBody.use_upscale = upscaleModelField.value newTask.reqBody.use_upscale = upscaleModelField.value
newTask.reqBody.upscale_amount = upscaleAmountField.value newTask.reqBody.upscale_amount = upscaleAmountField.value
if (upscaleModelField.value === "latent_upscaler") {
newTask.reqBody.upscale_amount = "2"
newTask.reqBody.latent_upscaler_steps = latentUpscalerStepsField.value
}
} }
if (hypernetworkModelField.value) { if (hypernetworkModelField.value) {
newTask.reqBody.use_hypernetwork_model = hypernetworkModelField.value newTask.reqBody.use_hypernetwork_model = hypernetworkModelField.value
@ -1582,6 +1583,20 @@ useUpscalingField.addEventListener("change", function(e) {
upscaleAmountField.disabled = !this.checked upscaleAmountField.disabled = !this.checked
}) })
function onUpscaleModelChange() {
let upscale4x = document.querySelector("#upscale_amount_4x")
if (upscaleModelField.value === "latent_upscaler") {
upscale4x.disabled = true
upscaleAmountField.value = "2"
latentUpscalerSettings.classList.remove("displayNone")
} else {
upscale4x.disabled = false
latentUpscalerSettings.classList.add("displayNone")
}
}
upscaleModelField.addEventListener("change", onUpscaleModelChange)
onUpscaleModelChange()
makeImageBtn.addEventListener("click", makeImage) makeImageBtn.addEventListener("click", makeImage)
document.onkeydown = function(e) { document.onkeydown = function(e) {
@ -1591,6 +1606,27 @@ document.onkeydown = function(e) {
} }
} }
/********************* Latent Upscaler Steps **************************/
function updateLatentUpscalerSteps() {
latentUpscalerStepsField.value = latentUpscalerStepsSlider.value
latentUpscalerStepsField.dispatchEvent(new Event("change"))
}
function updateLatentUpscalerStepsSlider() {
if (latentUpscalerStepsField.value < 1) {
latentUpscalerStepsField.value = 1
} else if (latentUpscalerStepsField.value > 50) {
latentUpscalerStepsField.value = 50
}
latentUpscalerStepsSlider.value = latentUpscalerStepsField.value
latentUpscalerStepsSlider.dispatchEvent(new Event("change"))
}
latentUpscalerStepsSlider.addEventListener("input", updateLatentUpscalerSteps)
latentUpscalerStepsField.addEventListener("input", updateLatentUpscalerStepsSlider)
updateLatentUpscalerSteps()
/********************* Guidance **************************/ /********************* Guidance **************************/
function updateGuidanceScale() { function updateGuidanceScale() {
guidanceScaleField.value = guidanceScaleSlider.value / 10 guidanceScaleField.value = guidanceScaleSlider.value / 10

View File

@ -191,7 +191,8 @@ var PARAMETERS = [
id: "listen_port", id: "listen_port",
type: ParameterType.custom, type: ParameterType.custom,
label: "Network port", label: "Network port",
note: "Port that this server listens to. The '9000' part in 'http://localhost:9000'. Please restart the program after changing this.", note:
"Port that this server listens to. The '9000' part in 'http://localhost:9000'. Please restart the program after changing this.",
icon: "fa-anchor", icon: "fa-anchor",
render: (parameter) => { render: (parameter) => {
return `<input id="${parameter.id}" name="${parameter.id}" size="6" value="9000" onkeypress="preventNonNumericalInput(event)">` return `<input id="${parameter.id}" name="${parameter.id}" size="6" value="9000" onkeypress="preventNonNumericalInput(event)">`
@ -395,15 +396,17 @@ 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("#lora_alpha_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) => {
option.style.display = "none" option.style.display = "none"
}) })
} 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("#lora_alpha_container").style.display = loraModelField.value ? "" : "none"
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) => {
option.disabled = true option.disabled = true
}) })
document.querySelector("#clip_skip_config").classList.remove("displayNone") document.querySelector("#clip_skip_config").classList.remove("displayNone")
@ -568,6 +571,16 @@ async function getSystemInfo() {
if (allDeviceIds.length === 0) { if (allDeviceIds.length === 0) {
useCPUField.checked = true useCPUField.checked = true
useCPUField.disabled = true // no compatible GPUs, so make the CPU mandatory useCPUField.disabled = true // no compatible GPUs, so make the CPU mandatory
getParameterSettingsEntry("use_cpu").addEventListener("click", function() {
alert(
"Sorry, we could not find a compatible graphics card! Easy Diffusion supports graphics cards with minimum 2 GB of RAM. " +
"Only NVIDIA cards are supported on Windows. NVIDIA and AMD cards are supported on Linux.<br/><br/>" +
"If you have a compatible graphics card, please try updating to the latest drivers.<br/><br/>" +
"Only the CPU can be used for generating images, without a compatible graphics card.",
"No compatible graphics card found!"
)
})
} }
autoPickGPUsField.checked = devices["config"] === "auto" autoPickGPUsField.checked = devices["config"] === "auto"
@ -586,7 +599,7 @@ async function getSystemInfo() {
$("#use_gpus").val(activeDeviceIds) $("#use_gpus").val(activeDeviceIds)
} }
document.dispatchEvent(new CustomEvent("system_info_update", { detail: devices})) document.dispatchEvent(new CustomEvent("system_info_update", { detail: devices }))
setHostInfo(res["hosts"]) setHostInfo(res["hosts"])
let force = false let force = false
if (res["enforce_output_dir"] !== undefined) { if (res["enforce_output_dir"] !== undefined) {

View File

@ -843,57 +843,83 @@ function createTab(request) {
/* TOAST NOTIFICATIONS */ /* TOAST NOTIFICATIONS */
function showToast(message, duration = 5000, error = false) { function showToast(message, duration = 5000, error = false) {
const toast = document.createElement("div"); const toast = document.createElement("div")
toast.classList.add("toast-notification"); toast.classList.add("toast-notification")
if (error === true) { if (error === true) {
toast.classList.add("toast-notification-error"); toast.classList.add("toast-notification-error")
} }
toast.innerHTML = message; toast.innerHTML = message
document.body.appendChild(toast); document.body.appendChild(toast)
// Set the position of the toast on the screen // Set the position of the toast on the screen
const toastCount = document.querySelectorAll(".toast-notification").length; const toastCount = document.querySelectorAll(".toast-notification").length
const toastHeight = toast.offsetHeight; const toastHeight = toast.offsetHeight
const previousToastsHeight = Array.from(document.querySelectorAll(".toast-notification")) const previousToastsHeight = Array.from(document.querySelectorAll(".toast-notification"))
.slice(0, -1) // exclude current toast .slice(0, -1) // exclude current toast
.reduce((totalHeight, toast) => totalHeight + toast.offsetHeight + 10, 0); // add 10 pixels for spacing .reduce((totalHeight, toast) => totalHeight + toast.offsetHeight + 10, 0) // add 10 pixels for spacing
toast.style.bottom = `${10 + previousToastsHeight}px`; toast.style.bottom = `${10 + previousToastsHeight}px`
toast.style.right = "10px"; toast.style.right = "10px"
// Delay the removal of the toast until animation has completed // Delay the removal of the toast until animation has completed
const removeToast = () => { const removeToast = () => {
toast.classList.add("hide"); toast.classList.add("hide")
const removeTimeoutId = setTimeout(() => { const removeTimeoutId = setTimeout(() => {
toast.remove(); toast.remove()
// Adjust the position of remaining toasts // Adjust the position of remaining toasts
const remainingToasts = document.querySelectorAll(".toast-notification"); const remainingToasts = document.querySelectorAll(".toast-notification")
const removedToastBottom = toast.getBoundingClientRect().bottom; const removedToastBottom = toast.getBoundingClientRect().bottom
remainingToasts.forEach((toast) => { remainingToasts.forEach((toast) => {
if (toast.getBoundingClientRect().bottom < removedToastBottom) { if (toast.getBoundingClientRect().bottom < removedToastBottom) {
toast.classList.add("slide-down"); toast.classList.add("slide-down")
} }
}); })
// Wait for the slide-down animation to complete // Wait for the slide-down animation to complete
setTimeout(() => { setTimeout(() => {
// Remove the slide-down class after the animation has completed // Remove the slide-down class after the animation has completed
const slidingToasts = document.querySelectorAll(".slide-down"); const slidingToasts = document.querySelectorAll(".slide-down")
slidingToasts.forEach((toast) => { slidingToasts.forEach((toast) => {
toast.classList.remove("slide-down"); toast.classList.remove("slide-down")
}); })
// Adjust the position of remaining toasts again, in case there are multiple toasts being removed at once // Adjust the position of remaining toasts again, in case there are multiple toasts being removed at once
const remainingToastsDown = document.querySelectorAll(".toast-notification"); const remainingToastsDown = document.querySelectorAll(".toast-notification")
let heightSoFar = 0; let heightSoFar = 0
remainingToastsDown.forEach((toast) => { remainingToastsDown.forEach((toast) => {
toast.style.bottom = `${10 + heightSoFar}px`; toast.style.bottom = `${10 + heightSoFar}px`
heightSoFar += toast.offsetHeight + 10; // add 10 pixels for spacing heightSoFar += toast.offsetHeight + 10 // add 10 pixels for spacing
}); })
}, 0); // The duration of the slide-down animation (in milliseconds) }, 0) // The duration of the slide-down animation (in milliseconds)
}, 500); }, 500)
}; }
// Remove the toast after specified duration // Remove the toast after specified duration
setTimeout(removeToast, duration); setTimeout(removeToast, duration)
}
function alert(msg, title) {
title = title || ""
$.alert({
theme: "modern",
title: title,
useBootstrap: false,
animateFromElement: false,
content: msg,
})
}
function confirm(msg, title, fn) {
title = title || ""
$.confirm({
theme: "modern",
title: title,
useBootstrap: false,
animateFromElement: false,
content: msg,
buttons: {
yes: fn,
cancel: () => {},
},
})
} }

View File

@ -403,16 +403,19 @@
// Batch main loop // Batch main loop
for (let i = 0; i < iterations; i++) { for (let i = 0; i < iterations; i++) {
let alpha = (start + i * step) / 100 let alpha = (start + i * step) / 100
switch (document.querySelector("#merge-interpolation").value) {
case "SmoothStep": if (isTabActive(tabSettingsBatch)) {
alpha = smoothstep(alpha) switch (document.querySelector("#merge-interpolation").value) {
break case "SmoothStep":
case "SmootherStep": alpha = smoothstep(alpha)
alpha = smootherstep(alpha) break
break case "SmootherStep":
case "SmoothestStep": alpha = smootherstep(alpha)
alpha = smootheststep(alpha) break
break case "SmoothestStep":
alpha = smootheststep(alpha)
break
}
} }
addLogMessage(`merging batch job ${i + 1}/${iterations}, alpha = ${alpha.toFixed(5)}...`) addLogMessage(`merging batch job ${i + 1}/${iterations}, alpha = ${alpha.toFixed(5)}...`)
@ -420,7 +423,8 @@
request["out_path"] += "-" + alpha.toFixed(5) + "." + document.querySelector("#merge-format").value request["out_path"] += "-" + alpha.toFixed(5) + "." + document.querySelector("#merge-format").value
addLogMessage(`&nbsp;&nbsp;filename: ${request["out_path"]}`) addLogMessage(`&nbsp;&nbsp;filename: ${request["out_path"]}`)
request["ratio"] = alpha // sdkit documentation: "ratio - the ratio of the second model. 1 means only the second model will be used."
request["ratio"] = 1-alpha
let res = await fetch("/model/merge", { let res = await fetch("/model/merge", {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },