From bfeafa8d5e602074fd8dc08143bfb3efc924424d Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Sun, 15 Jan 2023 17:04:14 -0500 Subject: [PATCH 01/41] improve UI of textual inversion frontend - File selection box now accepts directories that don't exist yet. - Fixed crash when resume is selected and no files available to resume from. --- ldm/invoke/textual_inversion_training.py | 38 +++++++++++++----------- scripts/textual_inversion_fe.py | 24 ++++++++++----- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/ldm/invoke/textual_inversion_training.py b/ldm/invoke/textual_inversion_training.py index ab53f0801d..3c7ffd5663 100644 --- a/ldm/invoke/textual_inversion_training.py +++ b/ldm/invoke/textual_inversion_training.py @@ -518,10 +518,10 @@ def do_textual_inversion_training( pretrained_model_name_or_path = model_conf.get('repo_id',None) or Path(model_conf.get('path')) assert pretrained_model_name_or_path, f"models.yaml error: neither 'repo_id' nor 'path' is defined for {model}" pipeline_args = dict(cache_dir=global_cache_dir('diffusers')) - + # Load tokenizer if tokenizer_name: - tokenizer = CLIPTokenizer.from_pretrained(tokenizer_name,cache_dir=global_cache_dir('transformers')) + tokenizer = CLIPTokenizer.from_pretrained(tokenizer_name,**pipeline_args) else: tokenizer = CLIPTokenizer.from_pretrained(pretrained_model_name_or_path, subfolder="tokenizer", **pipeline_args) @@ -670,24 +670,28 @@ def do_textual_inversion_training( logger.info(f" Total optimization steps = {max_train_steps}") global_step = 0 first_epoch = 0 + resume_step = None # Potentially load in the weights and states from a previous save if resume_from_checkpoint: - if resume_from_checkpoint != "latest": - path = os.path.basename(resume_from_checkpoint) - else: - # Get the most recent checkpoint - dirs = os.listdir(output_dir) - dirs = [d for d in dirs if d.startswith("checkpoint")] - dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) - path = dirs[-1] - accelerator.print(f"Resuming from checkpoint {path}") - accelerator.load_state(os.path.join(output_dir, path)) - global_step = int(path.split("-")[1]) + try: + if resume_from_checkpoint != "latest": + path = os.path.basename(resume_from_checkpoint) + else: + # Get the most recent checkpoint + dirs = os.listdir(output_dir) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] + accelerator.print(f"Resuming from checkpoint {path}") + accelerator.load_state(os.path.join(output_dir, path)) + global_step = int(path.split("-")[1]) - resume_global_step = global_step * gradient_accumulation_steps - first_epoch = resume_global_step // num_update_steps_per_epoch - resume_step = resume_global_step % num_update_steps_per_epoch + resume_global_step = global_step * gradient_accumulation_steps + first_epoch = resume_global_step // num_update_steps_per_epoch + resume_step = resume_global_step % num_update_steps_per_epoch + except: + logger.warn("No checkpoint available to resume from") # Only show the progress bar once on each machine. progress_bar = tqdm(range(global_step, max_train_steps), disable=not accelerator.is_local_main_process) @@ -700,7 +704,7 @@ def do_textual_inversion_training( text_encoder.train() for step, batch in enumerate(train_dataloader): # Skip steps until we reach the resumed step - if resume_from_checkpoint and epoch == first_epoch and step < resume_step: + if resume_step and resume_from_checkpoint and epoch == first_epoch and step < resume_step: if step % gradient_accumulation_steps == 0: progress_bar.update(1) continue diff --git a/scripts/textual_inversion_fe.py b/scripts/textual_inversion_fe.py index 941afcf613..4e31812f09 100755 --- a/scripts/textual_inversion_fe.py +++ b/scripts/textual_inversion_fe.py @@ -6,6 +6,7 @@ import sys import re import shutil import traceback +import curses from ldm.invoke.globals import Globals, global_set_root from omegaconf import OmegaConf from pathlib import Path @@ -43,6 +44,11 @@ class textualInversionForm(npyscreen.FormMultiPageAction): except: pass + self.add_widget_intelligent( + npyscreen.FixedText, + value='Use ctrl-N and ctrl-P to move to the ext and

revious fields, cursor arrows to make a selection, and space to toggle checkboxes.' + ) + self.model = self.add_widget_intelligent( npyscreen.TitleSelectOne, name='Model Name:', @@ -82,18 +88,18 @@ class textualInversionForm(npyscreen.FormMultiPageAction): max_height=4, ) self.train_data_dir = self.add_widget_intelligent( - npyscreen.TitleFilenameCombo, + npyscreen.TitleFilename, name='Data Training Directory:', select_dir=True, - must_exist=True, - value=saved_args.get('train_data_dir',Path(Globals.root) / TRAINING_DATA / default_placeholder_token) + must_exist=False, + value=str(saved_args.get('train_data_dir',Path(Globals.root) / TRAINING_DATA / default_placeholder_token)) ) self.output_dir = self.add_widget_intelligent( - npyscreen.TitleFilenameCombo, + npyscreen.TitleFilename, name='Output Destination Directory:', select_dir=True, must_exist=False, - value=saved_args.get('output_dir',Path(Globals.root) / TRAINING_DIR / default_placeholder_token) + value=str(saved_args.get('output_dir',Path(Globals.root) / TRAINING_DIR / default_placeholder_token)) ) self.resolution = self.add_widget_intelligent( npyscreen.TitleSelectOne, @@ -174,8 +180,8 @@ class textualInversionForm(npyscreen.FormMultiPageAction): def initializer_changed(self): placeholder = self.placeholder_token.value self.prompt_token.value = f'(Trigger by using <{placeholder}> in your prompts)' - self.train_data_dir.value = Path(Globals.root) / TRAINING_DATA / placeholder - self.output_dir.value = Path(Globals.root) / TRAINING_DIR / placeholder + self.train_data_dir.value = str(Path(Globals.root) / TRAINING_DATA / placeholder) + self.output_dir.value = str(Path(Globals.root) / TRAINING_DIR / placeholder) self.resume_from_checkpoint.value = Path(self.output_dir.value).exists() def on_ok(self): @@ -280,7 +286,9 @@ def save_args(args:dict): ''' Save the current argument values to an omegaconf file ''' - conf_file = Path(Globals.root) / TRAINING_DIR / CONF_FILE + dest_dir = Path(Globals.root) / TRAINING_DIR + os.makedirs(dest_dir, exist_ok=True) + conf_file = dest_dir / CONF_FILE conf = OmegaConf.create(args) OmegaConf.save(config=conf, f=conf_file) From 8a31e5c5e3dd7be361fdd984170fd1bac801712a Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Tue, 17 Jan 2023 00:18:09 -0500 Subject: [PATCH 02/41] allow safetensors models to be imported --- ldm/invoke/CLI.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ldm/invoke/CLI.py b/ldm/invoke/CLI.py index cfe9a64ed5..83b5281847 100644 --- a/ldm/invoke/CLI.py +++ b/ldm/invoke/CLI.py @@ -572,7 +572,7 @@ def import_model(model_path:str, gen, opt, completer): if model_path.startswith(('http:','https:','ftp:')): model_name = import_ckpt_model(model_path, gen, opt, completer) - elif os.path.exists(model_path) and model_path.endswith('.ckpt') and os.path.isfile(model_path): + elif os.path.exists(model_path) and model_path.endswith(('.ckpt','.safetensors')) and os.path.isfile(model_path): model_name = import_ckpt_model(model_path, gen, opt, completer) elif re.match('^[\w.+-]+/[\w.+-]+$',model_path): model_name = import_diffuser_model(model_path, gen, opt, completer) @@ -628,9 +628,9 @@ def import_ckpt_model(path_or_url:str, gen, opt, completer)->str: model_description=default_description ) config_file = None - + default = Path(Globals.root,'configs/stable-diffusion/v1-inference.yaml') completer.complete_extensions(('.yaml','.yml')) - completer.set_line('configs/stable-diffusion/v1-inference.yaml') + completer.set_line(str(default)) done = False while not done: config_file = input('Configuration file for this model: ').strip() From fc2098834d51f673eabdc58e311cff4e6e6bfe3e Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Tue, 17 Jan 2023 08:11:19 -0500 Subject: [PATCH 03/41] support direct loading of .safetensors models - Small fix to allow ckpt files with the .safetensors suffix to be directly loaded, rather than undergo a conversion step first. --- ldm/invoke/model_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldm/invoke/model_manager.py b/ldm/invoke/model_manager.py index 880d75476f..eeb2208c91 100644 --- a/ldm/invoke/model_manager.py +++ b/ldm/invoke/model_manager.py @@ -142,7 +142,7 @@ class ModelManager(object): Return true if this is a legacy (.ckpt) model ''' info = self.model_info(model_name) - if 'weights' in info and info['weights'].endswith('.ckpt'): + if 'weights' in info and info['weights'].endswith(('.ckpt','.safetensors')): return True return False From 0b5c0c374e235b160238d36c6ba30d150df81d1a Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Tue, 17 Jan 2023 22:51:57 -0500 Subject: [PATCH 04/41] load safetensors vaes --- ldm/invoke/model_manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ldm/invoke/model_manager.py b/ldm/invoke/model_manager.py index eeb2208c91..9b0d21546a 100644 --- a/ldm/invoke/model_manager.py +++ b/ldm/invoke/model_manager.py @@ -359,7 +359,9 @@ class ModelManager(object): vae = os.path.normpath(os.path.join(Globals.root,vae)) if os.path.exists(vae): print(f' | Loading VAE weights from: {vae}') - vae_ckpt = torch.load(vae, map_location="cpu") + vae_ckpt = safetensors.torch.load_file(vae) \ + if vae.endswith('.safetensors') \ + else torch.load(vae, map_location="cpu") vae_dict = {k: v for k, v in vae_ckpt["state_dict"].items() if k[0:4] != "loss"} model.first_stage_model.load_state_dict(vae_dict, strict=False) else: From a7b207410662d9efe2ea19bc6304820c5ff08ecb Mon Sep 17 00:00:00 2001 From: Daya Adianto Date: Wed, 18 Jan 2023 18:50:08 +0700 Subject: [PATCH 05/41] =?UTF-8?q?Ignore=20free=5Fgpu=5Fmem=20when=20using?= =?UTF-8?q?=20=F0=9F=A4=97=20diffuser=20model=20(#2326)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ldm/generate.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ldm/generate.py b/ldm/generate.py index 63eaf79b50..9f19f1aefe 100644 --- a/ldm/generate.py +++ b/ldm/generate.py @@ -460,10 +460,13 @@ class Generate: init_image = None mask_image = None - - if self.free_gpu_mem and self.model.cond_stage_model.device != self.model.device: - self.model.cond_stage_model.device = self.model.device - self.model.cond_stage_model.to(self.model.device) + try: + if self.free_gpu_mem and self.model.cond_stage_model.device != self.model.device: + self.model.cond_stage_model.device = self.model.device + self.model.cond_stage_model.to(self.model.device) + except AttributeError: + print(">> Warning: '--free_gpu_mem' is not yet supported when generating image using model based on HuggingFace Diffuser.") + pass try: uc, c, extra_conditioning_info = get_uc_and_c_and_ec( From 1c62ae461e410c9c933571351cbae2e719cf3c68 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Wed, 18 Jan 2023 12:15:57 -0500 Subject: [PATCH 06/41] fix vae safetensor loading --- ldm/invoke/model_manager.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ldm/invoke/model_manager.py b/ldm/invoke/model_manager.py index 8aeeda650a..671e00aacd 100644 --- a/ldm/invoke/model_manager.py +++ b/ldm/invoke/model_manager.py @@ -359,10 +359,14 @@ class ModelManager(object): vae = os.path.normpath(os.path.join(Globals.root,vae)) if os.path.exists(vae): print(f' | Loading VAE weights from: {vae}') - vae_ckpt = safetensors.torch.load_file(vae) \ - if vae.endswith('.safetensors') \ - else torch.load(vae, map_location="cpu") - vae_dict = {k: v for k, v in vae_ckpt["state_dict"].items() if k[0:4] != "loss"} + vae_ckpt = None + vae_dict = None + if vae.endswith('.safetensors'): + vae_ckpt = safetensors.torch.load_file(vae) + vae_dict = {k: v for k, v in vae_ckpt.items() if k[0:4] != "loss"} + else: + vae_ckpt = torch.load(vae, map_location="cpu") + vae_dict = {k: v for k, v in vae_ckpt['state_dict'].items() if k[0:4] != "loss"} model.first_stage_model.load_state_dict(vae_dict, strict=False) else: print(f' | VAE file {vae} not found. Skipping.') From 5a40aadbee2a0ffb7b14bf9f05a7e1b66d72d62d Mon Sep 17 00:00:00 2001 From: Daya Adianto Date: Wed, 18 Jan 2023 23:23:18 +0700 Subject: [PATCH 07/41] Ensure free_gpu_mem option is passed into the generator (#2326) --- ldm/generate.py | 3 ++- ldm/invoke/ckpt_generator/base.py | 2 ++ ldm/invoke/generator/base.py | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ldm/generate.py b/ldm/generate.py index 9f19f1aefe..83cdc6b852 100644 --- a/ldm/generate.py +++ b/ldm/generate.py @@ -146,7 +146,7 @@ class Generate: gfpgan=None, codeformer=None, esrgan=None, - free_gpu_mem=False, + free_gpu_mem: bool=False, safety_checker:bool=False, max_loaded_models:int=2, # these are deprecated; if present they override values in the conf file @@ -534,6 +534,7 @@ class Generate: inpaint_height = inpaint_height, inpaint_width = inpaint_width, enable_image_debugging = enable_image_debugging, + free_gpu_mem=self.free_gpu_mem, ) if init_color: diff --git a/ldm/invoke/ckpt_generator/base.py b/ldm/invoke/ckpt_generator/base.py index c84550a6e3..9d137b74d6 100644 --- a/ldm/invoke/ckpt_generator/base.py +++ b/ldm/invoke/ckpt_generator/base.py @@ -56,9 +56,11 @@ class CkptGenerator(): image_callback=None, step_callback=None, threshold=0.0, perlin=0.0, safety_checker:dict=None, attention_maps_callback = None, + free_gpu_mem: bool=False, **kwargs): scope = choose_autocast(self.precision) self.safety_checker = safety_checker + self.free_gpu_mem = free_gpu_mem attention_maps_images = [] attention_maps_callback = lambda saver: attention_maps_images.append(saver.get_stacked_maps_image()) make_image = self.get_make_image( diff --git a/ldm/invoke/generator/base.py b/ldm/invoke/generator/base.py index 3fd34765c6..a17badd022 100644 --- a/ldm/invoke/generator/base.py +++ b/ldm/invoke/generator/base.py @@ -62,9 +62,11 @@ class Generator: def generate(self,prompt,init_image,width,height,sampler, iterations=1,seed=None, image_callback=None, step_callback=None, threshold=0.0, perlin=0.0, safety_checker:dict=None, + free_gpu_mem: bool=False, **kwargs): scope = nullcontext self.safety_checker = safety_checker + self.free_gpu_mem = free_gpu_mem attention_maps_images = [] attention_maps_callback = lambda saver: attention_maps_images.append(saver.get_stacked_maps_image()) make_image = self.get_make_image( From 7bd2220a24556fa54e167daaeba1ca4121e7c859 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Thu, 19 Jan 2023 01:30:58 -0500 Subject: [PATCH 08/41] fix two bugs in model import 1. !import_model did not allow user to specify VAE file. This is now fixed. 2. !del_model did not offer the user the opportunity to delete the underlying weights file or diffusers directory. This is now fixed. --- ldm/invoke/CLI.py | 33 +++++++++++---- ldm/invoke/model_manager.py | 84 ++++++++++++++++++++++++++----------- 2 files changed, 84 insertions(+), 33 deletions(-) diff --git a/ldm/invoke/CLI.py b/ldm/invoke/CLI.py index f0ca1d98e7..4891004d66 100644 --- a/ldm/invoke/CLI.py +++ b/ldm/invoke/CLI.py @@ -124,7 +124,7 @@ def main(): # preload the model try: gen.load_model() - except KeyError as e: + except KeyError: pass except Exception as e: report_model_error(opt, e) @@ -589,7 +589,7 @@ def import_model(model_path:str, gen, opt, completer): gen.model_manager.del_model(model_name) return - if input('Make this the default model? [n] ') in ('y','Y'): + if input('Make this the default model? [n] ').strip() in ('y','Y'): gen.model_manager.set_default_model(model_name) gen.model_manager.commit(opt.conf) @@ -606,10 +606,14 @@ def import_diffuser_model(path_or_repo:str, gen, opt, completer)->str: model_name=default_name, model_description=default_description ) + vae = None + if input('Replace this model\'s VAE with "stabilityai/sd-vae-ft-se"? [n] ').strip() in ('y','Y'): + vae = dict(repo_id='stabilityai/sd-vae-ft-mse') if not manager.import_diffuser_model( path_or_repo, model_name = model_name, + vae = vae, description = model_description): print('** model failed to import') return None @@ -627,17 +631,28 @@ def import_ckpt_model(path_or_url:str, gen, opt, completer)->str: ) config_file = None default = Path(Globals.root,'configs/stable-diffusion/v1-inference.yaml') + completer.complete_extensions(('.yaml','.yml')) completer.set_line(str(default)) done = False while not done: config_file = input('Configuration file for this model: ').strip() done = os.path.exists(config_file) + + completer.complete_extensions(('.ckpt','.safetensors')) + vae = None + default = Path(Globals.root,'models/ldm/stable-diffusion-v1/vae-ft-mse-840000-ema-pruned.ckpt') + completer.set_line(str(default)) + done = False + while not done: + vae = input('VAE file for this model (leave blank for none): ').strip() or None + done = (not vae) or os.path.exists(vae) completer.complete_extensions(None) if not manager.import_ckpt_model( path_or_url, config = config_file, + vae = vae, model_name = model_name, model_description = model_description, commit_to_conf = opt.conf, @@ -709,7 +724,7 @@ def optimize_model(model_name_or_path:str, gen, opt, completer): return completer.update_models(gen.model_manager.list_models()) - if input(f'Load optimized model {model_name}? [y] ') not in ('n','N'): + if input(f'Load optimized model {model_name}? [y] ').strip() not in ('n','N'): gen.set_model(model_name) response = input(f'Delete the original .ckpt file at ({ckpt_path} ? [n] ') @@ -725,17 +740,17 @@ def del_config(model_name:str, gen, opt, completer): if model_name not in gen.model_manager.config: print(f"** Unknown model {model_name}") return - gen.model_manager.del_model(model_name) + + if input(f'Remove {model_name} from the list of models known to InvokeAI? [y] ').strip().startswith(('n','N')): + return + + delete_completely = input('Completely remove the model file or directory from disk? [n] ').startswith(('y','Y')) + gen.model_manager.del_model(model_name,delete_files=delete_completely) gen.model_manager.commit(opt.conf) print(f'** {model_name} deleted') completer.update_models(gen.model_manager.list_models()) def edit_model(model_name:str, gen, opt, completer): - current_model = gen.model_name -# if model_name == current_model: -# print("** Can't edit the active model. !switch to another model first. **") -# return - manager = gen.model_manager if not (info := manager.model_info(model_name)): print(f'** Unknown model {model_name}') diff --git a/ldm/invoke/model_manager.py b/ldm/invoke/model_manager.py index 1b97907cc5..da6f0f8194 100644 --- a/ldm/invoke/model_manager.py +++ b/ldm/invoke/model_manager.py @@ -18,7 +18,9 @@ import traceback import warnings import safetensors.torch from pathlib import Path +from shutil import move, rmtree from typing import Union, Any +from huggingface_hub import scan_cache_dir from ldm.util import download_with_progress_bar import torch @@ -225,7 +227,7 @@ class ModelManager(object): line = f'\033[1m{line}\033[0m' print(line) - def del_model(self, model_name:str) -> None: + def del_model(self, model_name:str, delete_files:bool=False) -> None: ''' Delete the named model. ''' @@ -233,9 +235,25 @@ class ModelManager(object): if model_name not in omega: print(f'** Unknown model {model_name}') return + # save these for use in deletion later + conf = omega[model_name] + repo_id = conf.get('repo_id',None) + path = self._relativize(conf.get('path',None)) + weights = self._relativize(conf.get('weights',None)) + del omega[model_name] if model_name in self.stack: self.stack.remove(model_name) + if delete_files: + if weights: + print(f'** deleting file {weights}') + Path(weights).unlink(missing_ok=True) + elif path: + print(f'** deleting directory {path}') + rmtree(path,ignore_errors=True) + elif repo_id: + print(f'** deleting the cached model directory for {repo_id}') + self._delete_model_from_cache(repo_id) def add_model(self, model_name:str, model_attributes:dict, clobber:bool=False) -> None: ''' @@ -412,7 +430,7 @@ class ModelManager(object): safety_checker=None, local_files_only=not Globals.internet_available ) - if 'vae' in mconfig: + if 'vae' in mconfig and mconfig['vae'] is not None: vae = self._load_vae(mconfig['vae']) pipeline_args.update(vae=vae) if not isinstance(name_or_path,Path): @@ -518,11 +536,12 @@ class ModelManager(object): print('>> Model scanned ok!') def import_diffuser_model(self, - repo_or_path:Union[str,Path], - model_name:str=None, - description:str=None, - commit_to_conf:Path=None, - )->bool: + repo_or_path:Union[str,Path], + model_name:str=None, + description:str=None, + vae:dict=None, + commit_to_conf:Path=None, + )->bool: ''' Attempts to install the indicated diffuser model and returns True if successful. @@ -538,6 +557,7 @@ class ModelManager(object): description = description or f'imported diffusers model {model_name}' new_config = dict( description=description, + vae=vae, format='diffusers', ) if isinstance(repo_or_path,Path) and repo_or_path.exists(): @@ -551,18 +571,22 @@ class ModelManager(object): return True def import_ckpt_model(self, - weights:Union[str,Path], - config:Union[str,Path]='configs/stable-diffusion/v1-inference.yaml', - model_name:str=None, - model_description:str=None, - commit_to_conf:Path=None, - )->bool: + weights:Union[str,Path], + config:Union[str,Path]='configs/stable-diffusion/v1-inference.yaml', + vae:Union[str,Path]=None, + model_name:str=None, + model_description:str=None, + commit_to_conf:Path=None, + )->bool: ''' Attempts to install the indicated ckpt file and returns True if successful. "weights" can be either a path-like object corresponding to a local .ckpt file or a http/https URL pointing to a remote model. + "vae" is a Path or str object pointing to a ckpt or safetensors file to be used + as the VAE for this model. + "config" is the model config file to use with this ckpt file. It defaults to v1-inference.yaml. If a URL is provided, the config will be downloaded. @@ -589,6 +613,8 @@ class ModelManager(object): width=512, height=512 ) + if vae: + new_config['vae'] = vae self.add_model(model_name, new_config, True) if commit_to_conf: self.commit(commit_to_conf) @@ -670,16 +696,6 @@ class ModelManager(object): print('done.') return new_config - def del_config(self, model_name:str, gen, opt, completer): - current_model = gen.model_name - if model_name == current_model: - print("** Can't delete active model. !switch to another model first. **") - return - gen.model_manager.del_model(model_name) - gen.model_manager.commit(opt.conf) - print(f'** {model_name} deleted') - completer.del_model(model_name) - def search_models(self, search_folder): print(f'>> Finding Models In: {search_folder}') models_folder_ckpt = Path(search_folder).glob('**/*.ckpt') @@ -761,7 +777,6 @@ class ModelManager(object): print('** Legacy version <= 2.2.5 model directory layout detected. Reorganizing.') print('** This is a quick one-time operation.') - from shutil import move, rmtree # transformer files get moved into the hub directory if cls._is_huggingface_hub_directory_present(): @@ -977,6 +992,27 @@ class ModelManager(object): return vae + @staticmethod + def _delete_model_from_cache(repo_id): + cache_info = scan_cache_dir(global_cache_dir('diffusers')) + + # I'm sure there is a way to do this with comprehensions + # but the code quickly became incomprehensible! + hashes_to_delete = set() + for repo in cache_info.repos: + if repo.repo_id==repo_id: + for revision in repo.revisions: + hashes_to_delete.add(revision.commit_hash) + strategy = cache_info.delete_revisions(*hashes_to_delete) + print(f'** deletion of this model is expected to free {strategy.expected_freed_size_str}') + strategy.execute() + + @staticmethod + def _relativize(path:Union(str,Path))->Path: + if path is None or Path(path).is_absolute(): + return path + return Path(Globals.root,path).resolve() + @staticmethod def _is_huggingface_hub_directory_present() -> bool: return os.getenv('HF_HOME') is not None or os.getenv('XDG_CACHE_HOME') is not None From 7ee0edcb9ebde442a021914a3a398745d009abc0 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Thu, 19 Jan 2023 11:02:49 -0500 Subject: [PATCH 09/41] when converting a ckpt/safetensors model, preserve vae in diffusers config - After successfully converting a ckt file to diffusers, model_manager will attempt to create an equivalent 'vae' entry to the resulting diffusers stanza. - This is a bit of a hack, as it relies on a hard-coded dictionary to map ckpt VAEs to diffusers VAEs. The correct way to do this would be to convert the VAE to a diffusers model and then point to that. But since (almost) all models are using vae-ft-mse-840000-ema-pruned, I did it the easy way first and will work on the better solution later. --- ldm/invoke/model_manager.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/ldm/invoke/model_manager.py b/ldm/invoke/model_manager.py index da6f0f8194..f6939750e2 100644 --- a/ldm/invoke/model_manager.py +++ b/ldm/invoke/model_manager.py @@ -36,6 +36,10 @@ from ldm.invoke.globals import Globals, global_models_dir, global_autoscan_dir, from ldm.util import instantiate_from_config, ask_user DEFAULT_MAX_MODELS=2 +VAE_TO_REPO_ID = { # hack, see note in convert_and_import() + 'vae-ft-mse-840000-ema-pruned': 'stabilityai/sd-vae-ft-mse', + 'autoencoder_fix_kl-f8-trinart_characters': 'stabilityai/sd-vae-ft-mse', + } class ModelManager(object): def __init__(self, config:OmegaConf, device_type:str, precision:str, max_loaded_models=DEFAULT_MAX_MODELS): @@ -672,19 +676,34 @@ class ModelManager(object): model_name = model_name or diffuser_path.name model_description = model_description or 'Optimized version of {model_name}' - print(f'>> {model_name}: optimizing (30-60s).') + print(f'>> Optimizing {model_name} (30-60s)') try: verbosity =transformers.logging.get_verbosity() transformers.logging.set_verbosity_error() convert_ckpt_to_diffuser(ckpt_path, diffuser_path,extract_ema=True) transformers.logging.set_verbosity(verbosity) print(f'>> Success. Optimized model is now located at {str(diffuser_path)}') - print(f'>> Writing new config file entry for {model_name}...',end='') + print(f'>> Writing new config file entry for {model_name}') new_config = dict( path=str(diffuser_path), description=model_description, format='diffusers', ) + + # HACK (LS): in the event that the original entry had a custom ckpt VAE, we try to + # map that VAE onto a diffuser VAE using a hard-coded dictionary. This is not the + # preferred way to do it. Instead we should should load the model into memory, + # adding the VAE to the first_stage_model, and let the conversion function copy + # the VAE into the new model. However, the simple implementation of this, which + # uses _load_ckpt_model() method, causes the conversion to error out with + # KeyError: 'time_embed.0.weight' + if model_name in self.config and (vae_ckpt := self.model_info(model_name)['vae']): + basename = Path(vae_ckpt).stem + if (diffusers_vae := VAE_TO_REPO_ID.get(basename,None)): + print(f'>> Adding VAE entry {diffusers_vae}') + new_config.update( + vae = {'repo_id': diffusers_vae} + ) self.del_model(model_name) self.add_model(model_name, new_config, True) if commit_to_conf: @@ -693,7 +712,7 @@ class ModelManager(object): print(f'** Conversion failed: {str(e)}') traceback.print_exc() - print('done.') + print('>> Conversion succeeded') return new_config def search_models(self, search_folder): From 33a5c83c740114cd2703abdadbd10aa69d722a0c Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Thu, 19 Jan 2023 12:05:49 -0500 Subject: [PATCH 10/41] during ckpt->diffusers tell user when custom autoencoder can't be loaded - When a ckpt or safetensors file uses an external autoencoder and we don't know which diffusers model corresponds to this (if any!), then we fallback to using stabilityai/sd-vae-ft-mse - This commit improves error reporting so that user knows what is happening. --- ldm/invoke/model_manager.py | 52 ++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/ldm/invoke/model_manager.py b/ldm/invoke/model_manager.py index f6939750e2..44628aac75 100644 --- a/ldm/invoke/model_manager.py +++ b/ldm/invoke/model_manager.py @@ -38,7 +38,6 @@ from ldm.util import instantiate_from_config, ask_user DEFAULT_MAX_MODELS=2 VAE_TO_REPO_ID = { # hack, see note in convert_and_import() 'vae-ft-mse-840000-ema-pruned': 'stabilityai/sd-vae-ft-mse', - 'autoencoder_fix_kl-f8-trinart_characters': 'stabilityai/sd-vae-ft-mse', } class ModelManager(object): @@ -242,8 +241,8 @@ class ModelManager(object): # save these for use in deletion later conf = omega[model_name] repo_id = conf.get('repo_id',None) - path = self._relativize(conf.get('path',None)) - weights = self._relativize(conf.get('weights',None)) + path = self._abs_path(conf.get('path',None)) + weights = self._abs_path(conf.get('weights',None)) del omega[model_name] if model_name in self.stack: @@ -658,7 +657,7 @@ class ModelManager(object): def convert_and_import(self, ckpt_path:Path, - diffuser_path:Path, + diffusers_path:Path, model_name=None, model_description=None, commit_to_conf:Path=None, @@ -670,49 +669,54 @@ class ModelManager(object): new_config = None from ldm.invoke.ckpt_to_diffuser import convert_ckpt_to_diffuser import transformers - if diffuser_path.exists(): - print(f'ERROR: The path {str(diffuser_path)} already exists. Please move or remove it and try again.') + if diffusers_path.exists(): + print(f'ERROR: The path {str(diffusers_path)} already exists. Please move or remove it and try again.') return - model_name = model_name or diffuser_path.name + model_name = model_name or diffusers_path.name model_description = model_description or 'Optimized version of {model_name}' print(f'>> Optimizing {model_name} (30-60s)') try: verbosity =transformers.logging.get_verbosity() transformers.logging.set_verbosity_error() - convert_ckpt_to_diffuser(ckpt_path, diffuser_path,extract_ema=True) + convert_ckpt_to_diffuser(ckpt_path, diffusers_path,extract_ema=True) transformers.logging.set_verbosity(verbosity) - print(f'>> Success. Optimized model is now located at {str(diffuser_path)}') + print(f'>> Success. Optimized model is now located at {str(diffusers_path)}') print(f'>> Writing new config file entry for {model_name}') new_config = dict( - path=str(diffuser_path), + path=str(diffusers_path), description=model_description, format='diffusers', ) - # HACK (LS): in the event that the original entry had a custom ckpt VAE, we try to - # map that VAE onto a diffuser VAE using a hard-coded dictionary. This is not the - # preferred way to do it. Instead we should should load the model into memory, - # adding the VAE to the first_stage_model, and let the conversion function copy - # the VAE into the new model. However, the simple implementation of this, which - # uses _load_ckpt_model() method, causes the conversion to error out with - # KeyError: 'time_embed.0.weight' - if model_name in self.config and (vae_ckpt := self.model_info(model_name)['vae']): - basename = Path(vae_ckpt).stem - if (diffusers_vae := VAE_TO_REPO_ID.get(basename,None)): - print(f'>> Adding VAE entry {diffusers_vae}') + # HACK (LS): in the event that the original entry is using a custom ckpt VAE, we try to + # map that VAE onto a diffuser VAE using a hard-coded dictionary. + # I would prefer to do this differently: We load the ckpt model into memory, swap the + # VAE in memory, and then pass that to convert_ckpt_to_diffuser() so that the swapped + # VAE is built into the model. However, when I tried this I got obscure key errors. + if model_name in self.config and (vae_ckpt_path := self.model_info(model_name)['vae']): + vae_basename = Path(vae_ckpt_path).stem + diffusers_vae = None + if (diffusers_vae := VAE_TO_REPO_ID.get(vae_basename,None)): + print(f'>> {vae_basename} VAE corresponds to known {diffusers_vae} diffusers version') new_config.update( vae = {'repo_id': diffusers_vae} ) + else: + print(f'** Custom VAE "{vae_basename}" found, but corresponding diffusers model unknown') + print(f'** Using "stabilityai/sd-vae-ft-mse"; If this isn\'t right, please edit the model config') + new_config.update( + vae = {'repo_id': 'stabilityai/sd-vae-ft-mse'} + ) + self.del_model(model_name) self.add_model(model_name, new_config, True) if commit_to_conf: self.commit(commit_to_conf) + print('>> Conversion succeeded') except Exception as e: print(f'** Conversion failed: {str(e)}') - traceback.print_exc() - print('>> Conversion succeeded') return new_config def search_models(self, search_folder): @@ -1027,7 +1031,7 @@ class ModelManager(object): strategy.execute() @staticmethod - def _relativize(path:Union(str,Path))->Path: + def _abs_path(path:Union(str,Path))->Path: if path is None or Path(path).is_absolute(): return path return Path(Globals.root,path).resolve() From 3c3d893b9d17b4360acbab3ac1f179847065b4d7 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Thu, 19 Jan 2023 15:43:52 -0500 Subject: [PATCH 11/41] improve status reporting when loading local and remote embeddings - During trigger token processing, emit better status messages indicating which triggers were found. - Suppress message " is not known to HuggingFace library, when token is in fact a local embed. --- ldm/generate.py | 6 +++++- ldm/invoke/concepts_lib.py | 10 +++++++++- ldm/modules/textual_inversion_manager.py | 6 +++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/ldm/generate.py b/ldm/generate.py index 63eaf79b50..ad40b5f564 100644 --- a/ldm/generate.py +++ b/ldm/generate.py @@ -445,7 +445,11 @@ class Generate: self._set_sampler() # apply the concepts library to the prompt - prompt = self.huggingface_concepts_library.replace_concepts_with_triggers(prompt, lambda concepts: self.load_huggingface_concepts(concepts)) + prompt = self.huggingface_concepts_library.replace_concepts_with_triggers( + prompt, + lambda concepts: self.load_huggingface_concepts(concepts), + self.model.textual_inversion_manager.get_all_trigger_strings() + ) # bit of a hack to change the cached sampler's karras threshold to # whatever the user asked for diff --git a/ldm/invoke/concepts_lib.py b/ldm/invoke/concepts_lib.py index 246dea362a..0d4f2e296d 100644 --- a/ldm/invoke/concepts_lib.py +++ b/ldm/invoke/concepts_lib.py @@ -115,13 +115,19 @@ class HuggingFaceConceptsLibrary(object): return self.trigger_to_concept(match.group(1)) or f'<{match.group(1)}>' return self.match_trigger.sub(do_replace, prompt) - def replace_concepts_with_triggers(self, prompt:str, load_concepts_callback: Callable[[list], any])->str: + def replace_concepts_with_triggers(self, + prompt:str, + load_concepts_callback: Callable[[list], any], + excluded_tokens:list[str])->str: ''' Given a prompt string that contains `` tags, replace these tags with the appropriate trigger. If any `` tags are found, `load_concepts_callback()` is called with a list of `concepts_name` strings. + + `excluded_tokens` are any tokens that should not be replaced, typically because they + are trigger tokens from a locally-loaded embedding. ''' concepts = self.match_concept.findall(prompt) if not concepts: @@ -129,6 +135,8 @@ class HuggingFaceConceptsLibrary(object): load_concepts_callback(concepts) def do_replace(match)->str: + if excluded_tokens and f'<{match.group(1)}>' in excluded_tokens: + return f'<{match.group(1)}>' return self.concept_to_trigger(match.group(1)) or f'<{match.group(1)}>' return self.match_concept.sub(do_replace, prompt) diff --git a/ldm/modules/textual_inversion_manager.py b/ldm/modules/textual_inversion_manager.py index f7ced79a52..dcb77d6098 100644 --- a/ldm/modules/textual_inversion_manager.py +++ b/ldm/modules/textual_inversion_manager.py @@ -38,11 +38,15 @@ class TextualInversionManager(): if concept_name in self.hf_concepts_library.concepts_loaded: continue trigger = self.hf_concepts_library.concept_to_trigger(concept_name) - if self.has_textual_inversion_for_trigger_string(trigger): + if self.has_textual_inversion_for_trigger_string(trigger) \ + or self.has_textual_inversion_for_trigger_string(concept_name) \ + or self.has_textual_inversion_for_trigger_string(f'<{concept_name}>'): # in case a token with literal angle brackets encountered + print(f'>> Loaded local embedding for trigger {concept_name}') continue bin_file = self.hf_concepts_library.get_concept_model_path(concept_name) if not bin_file: continue + print(f'>> Loaded remote embedding for trigger {concept_name}') self.load_textual_inversion(bin_file) self.hf_concepts_library.concepts_loaded[concept_name]=True From 775e1a21c7a7095f6abb35e8858a34cb9786a2fe Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Thu, 19 Jan 2023 15:46:58 -0500 Subject: [PATCH 12/41] improve embed trigger token not found error - Now indicates that the trigger is *neither* a huggingface concept, nor the trigger of a locally loaded embed. --- ldm/invoke/concepts_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldm/invoke/concepts_lib.py b/ldm/invoke/concepts_lib.py index 0d4f2e296d..c774f29674 100644 --- a/ldm/invoke/concepts_lib.py +++ b/ldm/invoke/concepts_lib.py @@ -59,7 +59,7 @@ class HuggingFaceConceptsLibrary(object): be downloaded. ''' if not concept_name in self.list_concepts(): - print(f'This concept is not known to the Hugging Face library. Generation will continue without the concept.') + print(f'This concept is not a local embedding trigger, nor is it a HuggingFace concept. Generation will continue without the concept.') return None return self.get_concept_file(concept_name.lower(),'learned_embeds.bin') From 195294e74f345b0ae19a2d332db75ac804c4e65e Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Fri, 20 Jan 2023 15:17:54 -0500 Subject: [PATCH 13/41] sort models alphabetically --- ldm/modules/textual_inversion_manager.py | 8 +++++--- scripts/textual_inversion_fe.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ldm/modules/textual_inversion_manager.py b/ldm/modules/textual_inversion_manager.py index f7ced79a52..e2e9b25c27 100644 --- a/ldm/modules/textual_inversion_manager.py +++ b/ldm/modules/textual_inversion_manager.py @@ -66,8 +66,9 @@ class TextualInversionManager(): self._add_textual_inversion(embedding_info['name'], embedding_info['embedding'], defer_injecting_tokens=defer_injecting_tokens) - except ValueError: - print(f' | ignoring incompatible embedding {embedding_info["name"]}') + except ValueError as e: + print(f' | Ignoring incompatible embedding {embedding_info["name"]}') + print(f' | The error was {str(e)}') else: print(f'>> Failed to load embedding located at {ckpt_path}. Unsupported file.') @@ -151,7 +152,8 @@ class TextualInversionManager(): try: self._inject_tokens_and_assign_embeddings(ti) except ValueError as e: - print(f' | ignoring incompatible embedding trigger {ti.trigger_string}') + print(f' | Ignoring incompatible embedding trigger {ti.trigger_string}') + print(f' | The error was {str(e)}') continue injected_token_ids.append(ti.trigger_token_id) injected_token_ids.extend(ti.pad_token_ids) diff --git a/scripts/textual_inversion_fe.py b/scripts/textual_inversion_fe.py index 4e31812f09..0bde21c95c 100755 --- a/scripts/textual_inversion_fe.py +++ b/scripts/textual_inversion_fe.py @@ -219,7 +219,7 @@ class textualInversionForm(npyscreen.FormMultiPageAction): def get_model_names(self)->(List[str],int): conf = OmegaConf.load(os.path.join(Globals.root,'configs/models.yaml')) - model_names = list(conf.keys()) + model_names = sorted(list(conf.keys())) defaults = [idx for idx in range(len(model_names)) if 'default' in conf[model_names[idx]]] return (model_names,defaults[0]) From 080fc4b3805dbbec8268786319eb6cb256fad0d3 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Fri, 20 Jan 2023 16:55:50 -0500 Subject: [PATCH 14/41] add documentation and minor bug fixes - Added new documentation for textual inversion training process - Move `main.py` into the deprecated scripts folder - Fix bug in `textual_inversion.py` which was causing it to not load the globals module correctly. - Sort models alphabetically in console front end - Only show diffusers models in console front end --- docs/assets/textual-inversion/ti-frontend.png | Bin 0 -> 127229 bytes docs/features/TEXTUAL_INVERSION.md | 290 ++++++++++++++---- scripts/configure_invokeai.py | 2 +- main.py => scripts/orig_scripts/main.py | 0 scripts/textual_inversion.py | 4 +- scripts/textual_inversion_fe.py | 6 +- 6 files changed, 239 insertions(+), 63 deletions(-) create mode 100644 docs/assets/textual-inversion/ti-frontend.png rename main.py => scripts/orig_scripts/main.py (100%) diff --git a/docs/assets/textual-inversion/ti-frontend.png b/docs/assets/textual-inversion/ti-frontend.png new file mode 100644 index 0000000000000000000000000000000000000000..0500e9b13291d8e5111dabfb72371b133adf8a5a GIT binary patch literal 127229 zcmeEsWmH?;)-EjtS}4%sPFq^s-Dyj4cXxMpNGP;WytumrcefNTF2P-jy97^i^Pcmb z_dDOOJI4KeN5; z$&o#x$b9*v?!9=l;^j{uLH_G}ZPkFa<7d{#r{7kEq_qU;e+0deus4=K`%+`-H2?9n z%9q}d=hFBE^zpb)72I zcX!;~{1^ENvG@sMxSHypv7Xz#MBx+(|C<%aemz=C6$Afy?H&Cba*d?YhXmz8 za#5^_re(qE`lm3MSTpI+m8Eq4pEg@*(ih0-aBQQi{^@@I?bW7jQr!q}iz})Bz?%Pv zXZ7?r^Wme}_dG~GePB^u(#{|#@}bT4lwJ@`LS;goPyr+kva$9-5T=fmCGxJ!sut`i z6MX-i%3wBCGE(;XW~cEl@n$ymlhrRz9vXh<5g zE88%u%wqdJDf89m9|+tQw*qw!MkFkj12M&F=RGUfYoLc={IFEGd ze1K^2W#V!|!~HymI&BZntk^7a$uiC=7Jjg<_;T(w8tb<_zOvu=rBrn>3m()umg*|m zG8^$L&`F&NaFC2!06VhS-DGddAAN}9VkTKZwVuAlk;Ju^`U%@+vvhMD#suhsW6WGg z_w*largCv{acsqNR<;QkwOJ`G?k+qY{rEHHUlL+yvDxzOkK*Sq;cJ`Ai!)GwpVWVg zYQMNQjZTfYZ5<131GV=Dza;~~;bGKGSs#SoCUWERH{UCRWH3T-PClu5_4Oa`Nei~8 zQO{EU>0O}TntUrM|| zdxF&qqQxqTaw{X2hqre(sYJ>V|8N0-YK zx6pF?@2O@%O@`Sz8uIVHf1OOw*Tpi- zpK3gCC1Y@2V zxqOxj;yGAZdhXTdY5_#2iIx;8Wk?^9Q=zzZ;uz?uyW1;Vea0j)qfo6q@&_Ho^)heR z>6MR2KI>fOyTdkYkJ4V>lH7y|bcV)bTg~BjRp}uL4yu!+u9pQm)x}yDiQp{TG4dj_ z!pLwz3^a_aPY-4C3c%|EQe^4{DGAZ1awS@FTM zza&@~y=Avox$$aRN5&6@o&vAliZtA}6%Xgrzii9>c4>(8e+S49F5a<-dp-4x@;=C? z+dp;>69HBl4_ZCT9 z4XiyoS| z>-+`Vadv{i>>Cjky{CG8;anBo^zTD6}bEe5s9GlxETxtr!O=G?Y}Cl3H{a zFEKyzw(Tfxcd#&+hZ?Aj(*Ttw=7_L&Fr|*yhF8Z@|#Pe4*nI zzeOJ^z!@B z6C8~z-1ALQbbdkE5@n~l!#+Yw<>={)^Dr7_#^{Q+aW3IzqWka?StLXqKk6(9pzzO= zM?C5iaY`YN#~*ow=VsMp4PQyc9_3OFzBu<3-5x^1BEr{y#XeU(z3SnrMu&$H|B%7x z>q~#-B*lKaQ6y)w)INUau7$^cH(4OVzQ3zj`%rZ%K!|vgEj$}yyO?P7{Sufnj!6Hm zW(RmbMVInXO9*d^X1@JQE9g*Z(-4@L4}=Yho<5N$pQMQSmg#>EeFu-?-kK}@&7I|vy&yLSbm$xTvbU`}&kSVI65xeD z@`Miz3(riSmwuuOhn};CvjSOPDyUVB3tP{#HLY^&r;ocB1}6_e(~i5{ zrnMOf!)T6S^~ z`k5^RG5EL@{7ysyw|Az#Ow>jjZ!4_Mdhe;w`JY=q>pwXtw5)v~QzvH{>*eT((aOZMVil{+<>Ru7BhOgFDRTTiG%-#f4N1WXwmEz!y%I`2E5QR4bJq9)Ffj zew=i+o=D@xaT9Q#?WJUKZwV%NdClL}Y&^pIIvsEHE0EZgai}+R+-pG0mJ7?vs5?jm z`!wq}xfj}2adCl^M7$%&BznI4boN4BJk(QQHfK*c4!73T??H2H|6sAFO{)+JWewc zSu_A@4e7>e_jgtg2m7hGlZDZF)!j{?u^t3iPZd#824zk@NnxwCa(V~V{l1=ul63iv zYVa4LQBqxt8*@y?UbOM&K(_8TTMiS>U39ghU|k67$4_ci1#3sENeoqydBN#H3+9qh zw-J30Y7_3P%hzX2?emkUYV(|ikJci)Y>&gWs7+iY8n2IsLj-x$8FCF5{WjF($wvgv z@aw|u+FFOjC&Sqyn50HSczyK91$`Yh#=qeo@h3^_HezC2Q_+a4o!QSM74ThTw^KKbI>aCx9e4b^6P z?;zE@a;yR=M8jQ0#w*Flh$3V)6>&}jpxEI>xeS=ml4dPqSDf`yNTp!(>XV1fUVZ#Z z?4gg(Be2=$L5->4Nv7XbffWz-j4A8cS)oZy%4BXV6_sKF8h5wO?G6_+bN!6>nAG;i zk1~Fupu=6R-R8GX!h28;^T?!egs~^uf~93 zH@;l{trL4%j^UU5ua>u7bat|l05Q;_UPiOPgZO6k-)vpc@@5>4tfwq`nd&g*@I8mCn9CERTH zMzqfg9k|4ZvZ8tkC?ygwPkbrilp&Y0Qk7|-9b3lTIY3vLT=(zaSf;QRThf`JgGv5h zA2(CCGR{dy$~E7%taUnn3t*tCchJl%lftwi;?f~PlK>4&Ze5tytrV9K2Xq4(BU?yrDpOitKzdrA7_m;ArL8fEW%Q6qf^p|on(As9+ z=dE~4teV4lzqd5x)4v$SvpTfvbkJr6`^CgBs1JYxfSN_SLAX8F5`Q*{KmZlATeeAe(CVF622jxhXluPosKT#$ygC$b9|Kf_tk>nFRjU=l7F&C|M z_|Oisn33pe4~m)A7bK&`PV%zlGIQ^k9fj!QU>J??Aexa9jkTcg(_m$0q7h3J8t5WV z7jOlQwRrG7tr3isn!eKSdxJ28Af*jR^KxhLV~+`^6=o0Uq=~w zj(8QD;7ZQxHkgrTp$$DNOUq%S?e`(Q$!&DJk-*7(R!zX{NrN}|Otd^{bNMp`pNUx=718N*KGNqqGj z-$+B6op0&SE2FIO%iAoQe;Pnk{P}9+PSW-2+rxBNe0siJfI&SoeWZm2ehe&LFw^0D zZV$$xMqeKVpT!AJImQUPXgwJRRfr16z^=5)NW6Cvb~ImRWfT%J1|l@D-LGQ7Dzhjs zJ~ft7Yz+IINxtCnAnp!rwoTiN8mJs9fC&$ZXSeH89XI6WueEmQ=U^Y+aIf-a9AO$d8j2IiB1+>O1VAc%@gv7QnM zaA{&tvdr^j{S!>>_1eNEe>MY++THov(0g2^l^5BTU!@^Z73=;16lJX9MHu`;)y? zZqqu7Rs?Rz#sULrQC5SK?&_ygnX_ns=(#DSy8RztiH;99Ehc`&jbx$j4o?0K;EL&M zJVwbG_8XRkqgDpxapmysO)y(lj@0Jixvam64JcDni!i^*)mA5ngn#vMdaoL`P?JJ^ zt^cfwS6H|lyHlbwO;c&>Q_O_C)X9>yu-&AC@$;s&Og_Hw(%jOdc4KMb2i$>;W$#ze zh;|xPj&%7HIVh$+8t7o=e2$uS*)=IMRILTZi|g8rQ*)Y!9*)Wr>_7S1>)2QGFhNA| zKJKs(RJq4%EhER=a;zy=uS!&yU$z>;Ix|=I=8+xDJhQD>wA_g8ab>|kBhqjmnRmB= z#*DKxtYy@?b>1z)?e;5#lPk~S$PyA;-p&vtZ*HmYA)XERrVz?yGgtI@NA&pzc^~qS zX1VeiDJ={I~9!)PFN_76Xlzw(*H?D^3Wnx`+ERP#yH9F$O z56PiDDU$ml`Q>${M)o;15AFQMB8)IG#LjYw{o6$A*Y5)9*IczSdiS{C;FY@B^ZSTH z=jX)|f$`t*nRerp!A#?#E6Iz#*rOREaE;59nqC|}He3p~xF2$ZpJoX3$yYvA8KY%R zeK$GwJX;u(qtN4xa?yJ{pDKKnsU8Xv1EeVO*#C}q4KD;AzvGk=pDc8B-UmhMq$RtH zKgU%J2nS?glL+Tf2@z$zH(enXCOQaDa}QJ3=<1k3L7$z>-H?d+N~ta6M$Yj7*#3^& zv=Qr`6rXB*cG#Q4%eoguxu>@)wv#Epd3q|krHbzqr$n*w`KydSR+7yk95J7glQ%zS z_3`G-6`B>x;7hBO$3lOOwJh5#M-om}ylhKJ@vdA;a=~ZxcWbORLOCurOa8069#85R zqMl(_f2qByYtjbsO_j~z-PA>x-i||CRR#1?(S&I0C9Gr6!Lyu1?I{C{UWS`?un$ORI0S@ zvD3A!!3M`az5ULUFRyu=temkz-_6Y}sDeuMN|LW-V;#=IsKVqQ>Tes%l1iAvZQ`wS zZYOFCu!+8B(=m{;Pv^)zps>zmOeMu`D#x2BMbW~0d>)BwVwF0t*DI_}bBeGnb{c`W z7+nm{w@*Jou2E!Qu0e^U?QLn>)@lnUj?_6b$GcR0HiB z!a3*XM=g6$-&7ij=WoAQAYBF?ci41-H?IU6`IYusNEE+(kFDU})F>ldIc*K?6ved0 z>{>ZBIZIvB@nZZse`ysqULr;0=_ZimIAJ z>8`M(GzCj^)XvA|)7!IXmv)<`xNOAgKv=W6^NRw72T@^fFQ)d3p?fP4D?{WCI~U zoY*49=BCa}$C*mOtFce+)@y;~YO%eBvTw*~sNkQttyjZxy;dcwS)BlQc1ur2QU`l` z`*szzu>!-Ud%aa_X!zBboFk#rCpT|I@a8TbP9o<0rVF+VXh@chhRFReHNRf01=+l( z7V5SM5XrJPZnq8doWXIhqt?Kku!*OU|*^zXv7-SvuD8~UwrQaZB-yYsA&Ghflm^&I5Ajd{AW0cld+kNa9Q`P z7lImwFc*+gWZr_EX8Jr#dVWYwJ;z(}%A%E0t1g?1+`*x(ykhst@wwEUrxcZ$JzqJJ zFLl_eAw~+iwQV06R<2anQekY3SR$lyRkiAeFYzQ;w8`&?kGdnc=<6q=QdZD+`UqmS zDfCsIvsQ~Q|8n$xaRnFE{l0Jwy6( z&jT~fw%@+9wJjuQEJsS~jGtId^n()yLFSX-+~N{B9`(8x5xQsIy2{8d7<~4dj8(9X z6r7U%An`_^p-;o>V5ikT7Gi|+U<&SAxb(D!MP3jeb5{nclE zg&xzPqBGpxq~*{kJT0aZA+j`y!rPgMc_h;z1W((9yXyl-AKT4ExIh)}^+x@ExnURd zP8#@?$Bf5qhVy;?7vkYwcJUw@m3;E{h#`QH?i{B{A= z_X&S}!{3Ccm&Ztr;_vF!vCmZhDSB(4AS4z3zlvpj3;t8})#rKXrwsqJe8n60@xNk# zQSX2L*BCS}{-2uVU!bXuNTuHW>hbp&QN3N`Bj5_{1pIuxmi5ckBdzSc0H3XZ2FK+S zQOa`?H^sHj+o~!9_mZVte5$&Ce?%T8iT!;NS^u(&68mob-O$<Y=f`(!vE2cw16AjU zeLd0jPBsdk!vTycU}|E9bsP@!=HBwXRfYA;YqdsGsoitnVyCCvD`;0A~(ZYb`gM4n(MF2Ezw7*xQ zS!01n`pK_oRyNq!gUBUSCjq5z@RNY8td{K2MyHa;N?EL&y`$1 zMs<=Y1Oh-HhMaU5V>rY?N|Gnxi za`X~!(A?2!B=gUQsQ+52vjO@l{{aXT6F5iF7Tm(sX|@fg=ZySXPksNAEz0(*6q<)i zGcC&V==Zm^A54U1%`(Pi7-Y30}585?+760l<+JmpJ*mQ>xUu~eND=+e_@-1Vwk z+4*!nJ(>$_dAt#7v&hL8pgZL`8={%NYlU6Iq-_~>)t8{=rhQ3v<8(6CeRIEFnPq<6 zv!$c{K&aX7AvCtrGva;bb_4MW7~LC zfuuBC)4!IXjeIgn`Z#?VX?Oa4%s)_x`ZlQi$#HW%OTt-3l6)47n+3m#<0iuAu|+KW zWe0gfYxH)zcQg`73to?k4UbmHAt0AVx zXUR@!Xbkp+wkXF=IEasRSBWW?c{m-$Ne@pUew;$?0+HY=(F*AWU|eqjfqPWQ`B1M= z$(Ax&7}3Ai>&Ib^Yu4n5)$KksWj0d&A<Y7nb$!w3Lk^t_9@Q>d#Jo>4l*W@~PW}Y;-wCnFBJ?FKDaM<3TKEfW<7qioQw17sX z?Sf_%S%#Sapl23Pp~QEg;r0L6I5V7<2D*S1%iSG{42G{g9Pe@IoR^F^9cA zFjBC?SNXDZinS!e^=aDV$T|H(_lP-#fV_U0$;%7T{;$uu7(#JhtAGWf9LR>vzq|Qm zWuG$!P~z5|)iFd;2&cS_x4d*pSh%lI7q)hhB0lh2%!df5*4+eLxr&W_t@G(3>5F%~ zxyoy$3=F!v^__k1v0Wo2gtwlP!{w_CviY58C>xHka!M`Bg^TKYV|jJF*K-HiP@kiD0Zq-X|$!#I;!ebqXRy zWmB>0%VUlS%VpyBshQU(ee02+6aCmVb^I+E>6L+cDO|(Urrw0FxsK?mWLmy&5@;6C zAWt#T>2R+;D!Ss87(&lSPs=v~!QclEN-C4N0^*KkBh%4ng4St7OaqAAb@%YeTtRNH z>r@j+BVQdLK+;!J%r$KTWp-H6JX^`-KcE(oaE>@{R-a&Jc=)yCy?(dkjl%wc%H*8R z>0*`}NaoK=QWZBXVETQt3~-35n;E# zT)YElNg#;4_DoTePn@MG{NsB7P`GCUP0}EoJm<-w&3?^0H|Dimi@9l|5;xM50$ZC2 z)I8nBpaJ`_;q0WYbBzxP7T;T2VIQv>$4Tm@lU!Q_l}^y2N%xk^Hqt2MYB7HoH2f_S z^pufU#gx=?@(;jlUnxkT9`=|KKEFB{JX{k~7!Kd#F_F_{JzZKsZK{7Cr}2F1iMF(? ztT>d#QICueUf@_Pl}*(tTm#_BCFXs#YWm#E+lU-W-;%+d)>z!r!UC+>&ZA=qX;B$3 zd=hJiFn=JmO2fSY z&nSZyt(N=f>$?HqNF%|YuC4mko2(kP?Ih}~aL*L~!T$AWvUU>T{^E7~w(oXefS?5YU2irK)rPny4l>m}V)TPyB|Nl0RGLZ^ePHu&3|hrTCVSHb2OWLD2T* zK&Ap!>>QqbUVjKF`7Prq$4ZA5Q#ATqxIS)1C(336)HB~Kz;jb&t{_XJ8=^ik4zrz> z>`Vt|1rX!OaHS6Z5c%cAbe%U-qQnv~Xrs=>HCTJ|_V*f^2$@W7**r-uQ5&0hPu7*k zT6cy|+mFH733-_e0oSAg*=&AL^OZV4q#;prsJ7i@=tE{j1d%JfgoJ`fe-PS|^XXKg z?-V)GK>tf1xkNC|==s@OYQ==E1w64F+o;LiD6qltM&~PB>%rgCek;UYv)$vje{J$L zlG{5W+bSk6N!*PJ=)isoyrwV%T=!P!8l*KsXvlei9KR z0NL_OB8IHSfrBl*^6q1u(++-or=l~sMwB^BZ=)Pew@a2*x3+3xHa50Gq>hWYu~|1> zELJ|j1wRNEk=FT`pXr&CFBdO3P@M9V8r(Xt&&d_esV$L`CD$@LCAdyvOt!^`wJ70k zNxysM2}o$_=dM52`12pF1z(kj&9DG_Zw*`uLFSMwq~M;a+vsTt!msp(@2kL*T#=RaNME+Zge*cx= zZ}=fsgZzJYfgZS*l2WP|`X6=t?UzC~XJ_WQd-TAcKLs^1asO%g6(`g(t+WuXU_)nUN_fO+z$Ie)GH zQsHmG7M!X4q@Kuh!8kC`DG4yT_Frbe4}@N6DN)-rSi@uS zZ{8z0K2+g!`oL(f_o1@^^v(;J6!@Rj>(FqQdQG}W^AE-3|Ixbm|ILti{lD00jhyv6 zcOSrYp0IbbaGt2x2FL3>wKJ+K^x>j4i9ppEn++mnBNfJQAym=FFt!kkA({OU=ip50 zN^(k`5a6#`TtAg#^wFjOX==kePgbTm_*mLn|KybNlYKVm-3_7v@Iz0{u}ufQfa34e zckkb`Y_xK_I~V?TcR){8qbXY!EY|1>R}&_^Jy(*aJ_uFcwbPjAP|xCt+ZYp28oAEdYvWP^Cjl```@3R8-eRCE}7^w z4JzLHLwEgNVz+xxQI?hZtLX1PtX8$qwR(Ur=Srz>_lLtGt<@^j6Wd4j!4m48aHr{u z4wc2-C|%KQ%}>X57d8di*cN8i1PdCO3GW1^PT;S(1-W7dLPbV?O6|D))<=Hb>7CYd z^5xpyCh`>z+qK!4(d@Pa^ZBfhyw-J12gYi5kK5psJ& z09QT2%V2>|W3;9?j>adEX!bD;^UbJe$sqxxe2yrTLeWvDi?43ZjZu8~_#WD&_43DUW-yQM z1TxzC&B=6D$ZdfjJ>9i0pMfTudU#!PHD%n-iPlETgQmKi0ml zMwiE{y=9m)rEYp3mhp-@YZKxZJ~$Z}odP=H&AbuR_dh*kS{~Gv+s=`Z+Ap2Lu>u3k z+qPnfnZU9ouzrs|Ip@(RG0D^{#&8P)b7t)#n;KKqxpXziFxXe^W*Jw0P5i~NT36Ye znJ|PsXl58;;pL4kvD5tRieXH_=VCbQ8H-<{mR4_09?g1Xa;l5bQ|cH*l5$L&gSFU) zq%&-10DY&wm{B|$!JKI3!lZox^rFENPM`beU?b*fKv(+*syNSNj0p?DFodVeq2a5l z!nBr2&${IAkJG!qeOD8b2iP{+_FK^fFvh3n-)bt@vI+qmI zVTax}R}|OjHCb@iM(Q$XzVKaLjUu5JOy*a}MUy75ym0N{u1a{0kLh@>=vYt3Q2*YI z?5y`@e2e>%L3Byuc2`q;xaRRJuba+nr+Q8KdP@&Fr=>U4WUS#w2(nKJBi?8boSYiZy23w9 zy&F6|YAaaK2FvmTZMVzBGyn+eo@>Gz+Q^hglAFehce_WHNfeb`DUw1yrkmMc>}Hdb z!7y#LDJaV+`F1N(csyc#*e<;^l0CT>L_|ksuf}9~P$E;7vvjhVjkP|n!sY0Jb~oTQ zHK&u}1CN&ld(YeLn3cH0)-MOS_%n6_fm=!XcN|k$lDHisSQSVg(h38Gw0qwE8C~j? zD`q@x#hc|2w?fuxzN$_!iUAL|)cXaOxTE0xWk{&4p!9R2elA#Yt`mQn6X@=u=jZsm zzBw@q+D+xnl-u1+JLkQ4T*l$V6kBMxzzC6I#Zu&Ovye5cN5wPI)&R|lkH7PX93b#o zyp$sau$66j&`i=l#-B2$pel7d2GxOW5=i!{Z;kP-?Vi7Gn-_qgEQAZKWT*b>s3gC& zeiZA~IvwaGJ~ZKPgIv>i_ar-+=qlnYlm-Q7;-JYchuZSMuDOAK(6Vg(zmc+y{wdat zF<}$!Iy5u$*^CG_U;Ihc(e@qnG@v)v#pz5M{M;~;J3i|)GFK~@Fhkgy-si!jZ6Rwu zM$y4Q8{x%BhB;QTG|4THAOT`dXo4YSW`(2ClL`FgHlfhY`#2df9}eTGI$@UdAuDcsARD}@p701B@YpKJ|X+w&bjNpjww;X zsc0RhHKse}`$v<)$BIU3b_aOIrvOW3vs=Frdk-jJcnMx%fGD4c(}7J%Ycc^|-aX(l@S^z~n$5zvvv_{K6Ecc+MM4=lft zZK)D#q*^nz1AbT&(=!6M-vGD8z)8-q+6uCy%UkswC?x3;lqE|xN22iR1I(!^uMP3qLM8`C2Yie9v zmZkYU4!Z&MFV5NK3Bvgi3@sk@j;A;juv^qD{?mTSEjo6K&)hNK&acl#W$*M_5FHB! z10TbKKvi_QSLoN@`{K(#eYWQhWn=TS9bAD$mIJr^W+?|@Ec}jRuC4Ip?C7sRJSvk4 z+JllMClrNu$>-BedcR@5PvyF0vlZLsfvI)tZ#Wo#x@1tE6KY}F-qOw*c*k^RF3-EQ za@*(OZZHdG8NxqC8PNE;ds&PhWzRdm z!;aG4flp;QZnaI>Uw`u4)?WMe;riLbct+pJmv{SFQUb0UFQ2He*5B|38K5#Pi49yI z4=yj12#g`@KPb2rXbjL69N(tkTQcUr)B$jZ*cLa2OTO>1qdH0DBBOz;El|GhT>m?@ zj50{2lGq8=bQG=U?n134M^!*)u-S_iEwetl8^6AW@uSP7TX?3!;~$-5#Q;CIIS4iT z&Y+sT{vGk~+|hQstw;bV1~I4EieI~^%lpoqlUs+3u?Ih<9LJs6N=6YeZ6>3H?ym`D zOQ4yH(U@WaH@NeGF%sML)OO`Fz_L$lOmpBN&9TeiP1_0paub&81MP9<3A|vF$r*t3 zaiB#v9SXt2J<|F}fRopZ~tkK>{5bUrI(j&&#?paFVI1d2P&uBfb1pMV*W#(-bQwgM7w zu8T^mvKuqSc<}hDObxto$inOmS`#@StrPtg@G9%5gY`zCre&X?Iqm#rq4iVhX z?yb;R>iY6KBKww1zTivQwZcD0^j=@utwJk~ZU!0~*G$KNK2uF>7VxA?^QRFz2&wEC zK}hGKq$WsLtTWLMq1RAp_NK3U9|5O%?2t;~siVxrP;zmfBjP=Ypdp`lz)*wqWeXLt z8B2G~O!J!x6a1)d4DbuG^^5mEGC#rol3G%bfy1=LOVJ!~;3dDG@i6hTZi`8>vnK3w zuD8AT{vuYVuo%5y1BM+eiJEMCnRbeByVE>TQ=Anm8x~Xq@jBg#FNg8BD-ArM^*O zAm~fMDpeX3aYhd0lZ+-`Tv_(*NF$A?cT zXgj(q-)Z;&svG<(n#MPWyf!!I+kLgJ?s|M-^er&$(cFf6C#XrwpF<-HrqjDFUs&$FDqxP^%7l0{o z+kT*48|v(2q~~8y?hNt0Ir~gQbI%nK*6jYFRRw-nRx!}Tk6dp109d#!urDE6VRvq` z%82l15Mw8}u`r;dSE_HEVOsgZ zotSa?Xil_RnKxe_a5-pjB%3WikQ9By*^sX>W+*D`c?UWRw-uXvi}*kgSwy4-9F7-@ zl1m5dsKp48P6P4koP>t@!Ej6HeMg2uOCxr|w5SIwkM&X+G3vcnexVeF5V3zxE%pm* zs>Z_cb^OmOwv3jAM9={2BQdQ{g!QuHxQ$6w&}$w0JEB{~$z9yTZ6nu*nlW7%*tbeB z;qzgFJFM*SAuOC9c}R;@D#63WFlUDlwVRb z0?WgeJ1^{x8eYqtqXU(jJ{K8}hFF$utGym*Qmv}uO+vCV@^tM!{U>qu#)3(%Jp7JX zz^8;s>w%%~oJDbaRcxshu)Tt&y;j?Bs3owBax>Rh&z4#Fi`_#a^yC8WP-)ahI6n$R zXdfKw>E)`Ew^NN*5xM~NU5moS5QzzlOA%#LC%`i8zK({01jW|Dz~G)lKEpi<3idQE z>ERYv z#udpB*=Npl4m-)CD2YN<8m_l#XJ^vgJv3p`znSmRX%5-#5OpA_Y+n+G<%&&%^wtxT zas*AnnL0k1;A8M3$Go7V0Baga1Rq>98A~e0C{T%k@z{LiL`1h41e&C}&hp!)H6S8y8~ z3C(Z6t9zem(i3CEzVX${dplW;-yApG_le;;*stt3Tz${;{=$+1!sN!uc?ChVs?sg}V`rh)6T7{7T z<@%YQL39IdMh|JX4_crNhXC)RLO&Zz2c;u9;P~HW@9sh#aPP_=B?qmxIKDTl zc&w<2gUkLKYZfdpTW&ff^1W%HZ*>pjF7S^epE?|S_D5{GSqk|(HPvUJ4er}zv8@lp zmJXh~oiDu9=A@B|{IP27a&hWDFsUE$Zo@ z4YO8buoro5NgDQklk217^@9}C(%jTdoe3!G1Q#bi;3qN9;&M+wrvAmLTsh0Q*3PwA zl*4hEHsLhjQg&Db(8$~dQRHQ9C17e$A=uowW=FgaerXe{;W zZbt?NDc1&Nf3vvlkq?f8j!b)$8Rtwfv>g#1T@$l1E`-`qEf=$Mc8_MSg+=Q|4t|^p zc`+_;HCTqu+-9|O>pM9t6lT0#E@pkvUFYxCgnF>*R22}F&XV3z9o8UqQfSCUuJ!x5 z#p-TU%HtnyKmd1qL5euD5uxlMpph!u9RA>n{wbc?Vy6l>F3~x>!mpcsNjT(khjFcz zQ53d7k@E1OF55RDR1{93q{`m`wOUOzTOvm*uXA`P%5Pi9goHl1j~WrpP_u?zG-e;h zB21Afhj;2<;FZ-s{e6YN0j-qHoC`-5-@7#T=~ovA1FYnLI2##j-t+oRc=TWB59!cEy?P1Ie>`?6GWo$$nXjp; znqu~&*{IRcm`M>~`pgz>EO-CLA^@mbvWvKAG%7>7W=8h6b|<|QEfF$YmeA)QX4sh! zpvBIsZ+eZftuv|7zd_w|G<6_JXZ&KhS~&}GXMa~Iwk`TEEZRRbb^=;X5boO|;KkC& z-DE~EU~#IGIYYk*KlF9grNa-(u!p&`$z(m((40jI`0cJ`P7x{ zjOtNl_yadK*&hWN69*r_+oTSpK$m-9H0-NiV?qmkrA#> zim|judh}HWx1A1=pp?~V_m1T?|E>HO@EMauyAAL`2X zPBoxh`w@mqL(9mBxa`W8lGO+MY|-8!R$Bw~&~W~p_Cz0&>qv9Z2kXfjZJH2|ezh6$ zW|vrnVgNmJ4?_FHHu}1G=Aip0)ydJ4>o;cdWGt18!`pkazXB_dV9wL*9@MQ;K|*8# zS8RsSmzoUQsoi}k@1Zp`16zJSP5IqnV0_iH<#HC{JQBRW61V?q_k%nt=~t}`ZgqJn z#07tNxhZfkhME4-^=&wfxZgifjkosyJ8ZP_>nj18LGpF8h#FQVOI6<7!}_hF(X!ZK z=)*u}HE#7e44Q|x_uNJ+c~@7^c$jITO8Lx=s2i(EU=8n-h%JN13*G+~-{>W7{nVcO zGzt94u;`HlPf7QG!YVx_IY^toQV#>!WrUnkG86D{;q}e<{km(L+V*ZQp3=^toeW_6 z4XqpL?9KE3yDdO7@yq3B(_Py=OVu|?bc<8U`oen}Orb1vwV24HamJ`-wXk0FyQ$TN zQa2Ue`;h$9HdDO+!`xd%#kFni+9W|ju;9T-aCf)huEE`cySuv-7TjHf6Wrb1-Q67u z_$w>P+G~Gj@6*0^uKsz!MYSqw)|_*U@%Fbry$_A!fd+{1tEx;h*zl}@l@g1B5w3J# zUsCIeexx+nLCZ=YU{h1?1h2JRcOTvD&It8_cIj@7oIHcE)>|T1@W>QEal#!P%M;`J0{(`!b{FLMPM;!Hm?$szYId z!uuWxS`0<9WyPbI;tY zt=FDW<){Yg*cgZQ-sIar_?0~zssh7L{i<$AlilxJHmdct;U{%wTQF4ysWoe4cxRiq z;kkgccWhonFoT&@`cw$L(GZLl0gKD<303N}bvcnwT96LSrWv7Z{13G{$`U{Fw0Ny6 zgxkZC6yr*n6eKG0jmx88geM2*viY_p^odV~)uHF&MfLq5knOTsbmyFc}-!4a2I?Q2O_s z>MWYWdzyxo5N13|V#~JNff3%2x%8Z#(fcs=E^uLA-@VFzN`<6+$|z{^GYtw@chGVq z+8jqu^!dHw#0#C??20c%CJNWXv$>VYH~x``MUhlAF~w*WK(@*DjaOj;toA&P|4b9R z1N^-19YQ+7;<_M%jmkF!-Y2GM&P0_A%Cd9c46UrpBoHr!;+h)$*x!-F~l zh#kFg*+yIUPc2CuL76mXwl>1Jcu|e&aH}J&I!yaB-8`-1b1a7tPIx&=R^Wdh-O^v&np272AFbWXI z^}k058{-D8fU(|d&&qUH-!SeZz)}WEMcMb+eVx2nnR6VAsk-{Wrn6rV+@GMB){VUu zk*Wm2UC=EUg?t#U@nw_Axyp=(*TLvh+?~W!fm3CzhCrdhu^9fAF49f`$`CsJ_$L}& zKw}V5JAAi>=hK8%H;N$vZi46trK=JW)uaHqgA==we!cQf3)16p_kI4SjbLb)uj>QZ zb0g*s`eCWokCM#Yor}uFp?qig%;%62y4f)zNI4g+8loMSmcgrs zkx=P(TALr&;dc zo-B$?_J(zz=g6#xn8e7TY-Ut!>YJOJIg0x9%^zkvd!6okm`!0?x=CZ-eG`xIu*teB zMO8$9{3DYq&C@Rqf6+o`}Uc0KVhH=?l=5UXRY9dFDThteeo7E7$V?$ zj&JZun(7RzVY_H`ayVGOr8PN4EUitE1Jm1C+da&~8bWLUIS>jfgU2qi8V^Z^LEdQP zb*(p%e*W5vR_~P(5!IpuF@O~_vt%57^L!s&?5_(Fth~XgEReyyh3`*we!_~Wp(RAtLed)^`c8X>-IXTgG z2$pz8(5J#vy)wU2sEZP@)rc7k|7KQ##AKmqNTy|^g`?kOpd=U(61-tre3e@E>XQu~ zH>wu8U7p00JX|G%%ruzYG>Q#Ltjw4*o2E0GgoGdylwN_c@JJ>SuBHO&SmrkpLpJpO z`LfQ!)ZW-YSx1&DYO*aLdPGuGJhzr1N_UTu_xF8aUTfRW?S4r%my*oId0&xDZ#sGv zyy>|=AF3apSL#>gzQ$`qJzbcxN6>r1%k;}P@q&NB_k&lghSsgcN-B2OgOWkzft`yteh99iv~-H-?nZYQuwK1-$TkJwu=NAdd(T1ZvtXds z(|dJ21Y2gwKQKy=TJLr8$dnuO2?57P!J~1eINp;5`H`L2^`nxDhCY z-~^#?*nx{i;tL@a=q_OJr!Fy)wM<*@?U_6hn@hHiU~JA=)BzgVNRqpw+k=(;wY47U z6{#V#cu0;mCs~!etqG~f0``VJCo$c2fz3fJk0fxI#?gmkl5y0&KbQ}E(!xnnPr!lFn4}4^=pIcfN9IO)&a3WlH zei7`-)tjlTM?n?jbMY?y%I*o-{77`Ug7;aby`xH9yK{j793H#7nacG2QVHHBBoyKg z3uMEs@ZbHyg49Hau0+TH5SK;n`pc6a0!bP(KuE$mvVMs~-&2j+V0-lz*ux{%CVkCE zRU7&|WHO~4k#?W$GA{iD%nd6oC~Wo5jR_(5YxR8D-ZGPYDlU+neLHmybZz|vIc``0 z#Ra_X&8$8)HK^XmrqTIx{U_Y;%{8gIWn&>p2|`0x>;U*5yQ_Iut5=lR2;$qNXcl*P zeV>Y|oxg-JLepDaSdr(tSxfZO^CjLo6A<&Tg=^sr6O!$`A5`+6XwV|7jcG}LaWELG zS+`(DKbO)vo?!O4&xJ!yOB{(-f>U-FrM`OD)ZPCR)3FsP#;?wB!S8Gic{q`a>wPaN z%dOll-UpZV2qUX{j6wd9*zK8~$NMJs5V=<-SosD5&y}lvjwSCLADyk?go0ZY$vX9h z=C{x7J#$I^YD}1ij#ypd~wW#b_I5*u5!6SqdX&wvfi(2ndxS{gBG__JH%7 zD`w#3Ef2ay9e#+Ouhf+>vH+8?>ynARjtGL|jX4{iq5_G;Z~D#RJlg+A z1~o6y^!|6!M;=8T^XBVP-77n@wQvnpl)zaU?Vj6A!nF}E1cM37RTN^%Fg^%u*Ooq? z$I(k5G7K9QYXZh9Aj?BD)WiJx6PQF}i@}yDmPmyv0Mpntp23u64@*(6Zbzlz;(sgq zLA6H(G9kVMOd67YT5@Eju!N7g#KF`f;2eDVLV|fx(y8#gC26ct5m_4h?Dsf%VG(P= zjZDq5j|X-+{=u2&b)@+Y2xsI6D-Au z^Gq0{DWx-!gX*_?dj={>aiQUg_Z+3j<{|8_ho^e783WVMw$ya@WCa}V&|g<=63#}0TuQi;HZfb(5yFUSUa3&jW!j zES!^aqTQ&DvmxAoKf2XENBHN>k>ZUA$i+r@Nh;FeZI@Zs;f}p4a2kr|>F!3#H`5HAghfJbiOG{Xm9PLo4Fnbe#NLU*R`-Iji9_qd*^tvz z)&kwdq$@RrhqyftJbcj7OqrRTJbie+x{D8S2{0*x&{?AV`Ermf)Pux8v)3o#2kTn$ z#ZwYn`o7~U3AOGxxSAHTC*lgSs9XV`_;WB5p~w9Kg^_S^MFsc_Fm@`&H;?vx%ksZu zl9t3d1184-Q>O0|9OcH>m3Y8s=WJ6XWBwHRZOnWG&6*XI7%4#tZ*OX7^tR>Bu5CNG zxx;OD=*=^_%sKvpt&OQF2RokV&1iZO{Hh$GA(hT?rimP(yG*)=Td`Csb24VLzRX<` z_*6W3_Jf=AW}=y~s&F;lf6`RZHs?V*4R1Cvz+3Zf#$==|&_z}WA#!-0q2jLARGi9n z%$ZS7yKG^$NmW@gh#+mD+W0gyrWwZpa?Ww+- zElA)AG0|c z>$|{zb7olxQs1~kg0CaF1n^{uv)C4;rt=_k7*Q&tiaQB%dsD2yK3~~3Sfq7r<(}u_ zO~|cW|E|>GlMgQ5#G*Ub=@Jm!_T+ZD5(z$Jv0Ng2SyijG1TEuPNChfp3y9J_sq0CZ zBt&1V7?S4dxV+!)_u&5-x{!qYt^&DzVj{X<3TG!oV5JEwije*VRD3>lw)`Kq~lOvn5W zX9KmiD)l#xGeBlQT)+es4c_5jwl?Uu1Qmho-G7J3J&+!-U?Yy;^s%(%Q)Zw z$X;E?eW4e!ft{}3jlom7oK1$kh%$=&rGRQNp>u-X<^Aza`zfCmR29ix^}_55c1dHa z?@?+AXzOa5UFn9i%Up7IyhZC-!y3*t0-zX|G z*nq>u`vxyF{$q3wy(RxIVF==wYj1)=a_1%)=lcA!_rD&=X4nwZBi|a7f*%bMR(`_E z!}*WE@{bG{%%Lz#VOgFs!KNQBh+ugdS_~I8a5+Z*jVFEy*5ch=jXIxv9Iq;UBN{l% za2Nw|RC!&vv?lpDj&N!9vIEYR9K=@VUC)44&NcHx#(!h{ThwU8S1St3A;a3rtAfli zT7t$>WR(jSk|R`Fe~->LG%v%sz??>gHQzGlQb|pvl)Tl-9kwx~+4G4#wxim)N;l%C z8%dpx4pRBHirrAkx);t-o{C6@xah%ElLs(t(}~mmim)S9C}|D){90i9P}%d2T|iv? z^MA5lXW}kqMHU-VR9tyi%P~8dM(Gvk_+@y~POv(Gr?yTeCdc%O^1prYa0!sW1b#cOFM?vAi{ z0~`78z2)fXYu6POoaf!V^p`h3ty0BSR)0jg>AK6|TW7Z}Y6mGEEB63sjNb+(Yi#rW zt!4z*yI+^D$SW-EJ*t>B^M-fg)=;G*(Kx+ij&5MSt>6ev=Gy*YFq(U`yUsabSAeb4 zrr0yv*TXgxn0%$DF3T6}4S4}hI-N{Pb!U`W;_w1ZZK36>WAVh+ZBDxNKxM`k9ywLJ zyk@GZJof*pw^S|jLQH>Qjdg5)1{gkVtB=Ptyms3b~Ykk=;iobdP7NU;f*QTK~g0z&U|tBJ;xpwPvs-#2^GGHC$o^&Vaapr~i` zAZ})U2KRbXEByo@OSKK0-8#ApiLx~Ngfn)3x0xS7%m};~Dr56Y4{eug{xn#AFra09 z*}X~@rMI4V-=6s|_{|9{Jf}7jrJo}s&ZsChHrzjPj2tq?PZVRM__r450E>W<*M z2sb%d7}!^p-$;@aAeHh}1LX?L1{9v>XkFgINluC>_p9E=rfMt;G2R%9#yGdq;(i|2 z8>8Ul_T)Ct;~7pql(WoO;r(^)K^Kuf6{(3TbAwlp)naz`$TTjEIJ4S7ormbq zUyRXf*O{`fjstcp=301^X3DH$=z1)jv=k>15!&puxa`6u|7DC~_P^7E@E)ZQLZU}U@ zi!^$5bq8^$-df?B6r5s4yMT46i_Xs3v{VZ1p-lC%!JLjHED?ato5(B!%XNK%J?y@WB|BJ#$^FrMZ)PdH?kvIQ9BImr zb)!%+`KE<+eo@D;&z*55S~q9}Hb=!)8rI8w>^Q8xYKO_$?!*ZJ!4AHMrhx1=d_Q1t zh3bg>F2nNOu0#$Cj!W&QzC~XXYG?1j|IXL&*L@M^Xlr#%*<-HbT7jemx6`p=DLb5N zt73B{MtgBEFZ_zY9$^4`q>xN8**mBB+)a<8x4FZ(0C4ldy?#0?oAnnKswLLDM1xn5 z871l-4_%8?^^Id{QM75>fJF7YDa~LE9$i3iDRw< zeaic*o_|B}6PKI-M3ogMWo)Br-MS}!Xu-rYO4$cvB>lCm^{ID>jT`U6DPt(ig}DaL z+Dzt>k=SasNw^=V!hGInSBo#+ZO1c9I2o{uYcgrib8*~W_hMuYJvJf6q^I$(ol&kX9eXw0E;h{q z4Q#gpZ=Gp&d3z+B0_jd7FZM4(@NI$!d5Uh)99CBgc+ zq*k)g-Xa_DT$3WW1KHAYtXvL9n;3R4uv`bESJ!a46K2p)n=FXu5vb#Il1&;}%*p1}jo`3hpG_r%6^{5; z=W*F1dGXS0|HurgP9*nE>dwPCC!E802YX!Z?~^><)pHCjAoBiC{b(1QNWe{ySk%9} zWSYNCG|+AdSATc`&`bZLEz3_b;JIuM+Q=R1>CrwbGf-|ixE^|vf%9LDjYGc`4X+F_nLLX7>`9Ge8y~!o5KpBcZY#cMs~wP{O?wLSG1=e1WPnpgFrQy zwC0iMv6#eTv+L{itrZT}gICs6btD{^@R+@R!36t?%>_G_(#@{Kbf)VOC91csh=*!w znwm2czrXvvf;e$9Dyo$Ad4|$=>>t3y?(O)~J+3C_gAo$}GuFyd+0wJ;TY!1F1a-IBVK`Gh#P;si<3#0V7YuIh zeLb_nWNEM|H>jIxXq>sKL3hVYxXG@V&d{;H6)^li6!1AP!FjtZE+@2v^`yOyS@TF4 zN4mNw+rn1sfQaO*8v{8Wulp~y;BK-kjX>?zKv5Y#+r&5rPqVW&&>lR(OyrgvE=RxO zd^pOC(x7ldF5KkiU0l@*{cb1^YgwGdPq}=tcWyZ!{G**seL&utdh_E%B@H5CVi+`7 zY(nV#(mS0$$O9?}8bKBRlhp60uKv8;k@?|fwy52UU35tLO8aw=zvrwvAuk26nIS5% zj>VQpTZ=df^$x}4%yclpP-rY)5T9D#hU@_L#qE~p(lI~7@|Li*W%kW&lPtMC-g8PT zE+myL%qeVQyJAe5sypu2qcyatVof-7ceL~T+sdP4XG)$pk8K?l>zsA>FHY7@woB0w zm2M9b9t-hilbzKYGm-7mVFp&c@0;K-1aKCH%Cu;-^_&1yrkgVxn#4Sg=v2)UY~@Bw z*8W0Ed&Uo2@osKq(kD*bOmUzz^yVUgvpW&&HOAvI$C9Hnt7J2xTcZPb!p#K;n^cKc z86CJ#PffVe#&MXZ4m=96iYhw9`o1ey_^axFOJmbUEOH#a4d>2KJFNu?M+z`&jd9 zv0faEsgo-ZPrZ)mlh^MR`BNVu_}{YHUbJ~H)0-6sJU61z@zMKjKb77rHObQ>Bt7(W8k)kSBcARe*`j5(-k{VnnA zeQohm$W)#;TAqo$t`K_M^)ca%x>hF6Bs5Jg_}^2_lvfLhA|x<`0x{wBdvZ5CZ{I%L zp_{E>1_!?AZHKf%hg0ij1aBh=zi1AI*b9rw@QSnd_n?|8pBs{*+b|vu4yrsWPUhc@ z-R8zSS32TgBS~4Y_5d2Vdd_Aj+~1OZprvPAzftTzn#1ruSPVmqSOT<4eyKCcAt8)3 zpms%lGdA0lJR1Kp5Yo-y^wLiazHArs^m{rhiNgIEqKpV}(%Q0C?FAlA*;<=fs0Y3( z?H_vNV6c2Ep!3_$RK(BrX~@~*gg#^8bNHZ!PGcaeOpNIgCNu_i+<~?;vJ`Tgfq7+g z!rK8L8X{*D4he47;vP)+W|qRaY{lGQO}3_iYd#T~=fMsj+TPX**4-bZx#Ukkzx4o; zMKGE3?-Dy4&3z@ObAD*x`Y<&VRkB=_2wZ@qHipFHy1__?KjwS+!Bge7mA2v~x5QEn9Wj*FxmHCoS9^@-p;euF$sC?yojal-eSS2iDDOC<;k*gx#!y0sh_>M= zLXW7pErF&yKU&2LU!Z)Cr-hC}9edYyqRjAtr`f0CDF*oCq~8a+m!<6uPNGriva{a$ zWOePJn*PG*Rn+OF5>nmW{aTh$7cQqn4#voI|f?Xb|Bkt8{FpvFwVo#iMB$`f5WjtL;$0f{?EomRy%RR>%5^HG8 zALE*M^%)CTIdU<2YdVkoHE+C}q`naVpSw1OC`oZP`iVK|mSp>2o%kK8Q4Y@PkQNj* z+S`T#!j!)9idnyaZrzc`QVdILBd#)IpTZ3@ptVq-k(QXl83`^(Ks_xL#6&pSY>1XzD;zM=S)X zlT$-B0o_YYT_XCY;4f;M=In!7JU~(X6&aM?1N$>c$$kxgqkK~?zt?P*zGij`6oLiGWhj<08yqKB$3 z)g_@_N=972dtb>jfrduN*Zwtuk`A1fo4u^ci#LQJ%@j{+9LSEeVeS}U7(iG2)Zk;F zku!Bl^L>m6>96pkuii!qs1mfnTgJkz?X?dE)q&LKU@BqiSe_C@@{uPbI|?oNb39W@I{E7`}x(CE0`Y0^{5Yth6Z&np;*R~)ParcKeC512VOVF@ctw()1 z$CvpQGHZYhY9gCz2X+-LhsF!M}MfX`}hLE6%s|v3n`T*O4Q6Z8QVWUmYs9Q zxD9ALH4quaw*gpe71PWhs^I%XR#`&W&ZH2oC?}u1d1P>sp?Gbt%~q2#x?{FkQi_4l z${j^_O;i4OR}+OxrNh8O-5R76i4`};PYJv;bjZ=zkJ9FD3QG({K+XmqzaM$E#88KP zoq{SupmHWhGyAd9-_SWOAo1yiO|>>7dy~voWq$5K zdBkU5=JIkq*%x2@%tb0Sb-a@9dV=w}6-{%S2hk`Pc-g6Y(?bTnI`gWvbCFHCz)6hS zvv_}UL*;QjaOn9FcD@D97)_hlY*V`F{FyzALZ37O2Y1>O2}uswQwb+Tw~dST*48U| z$CKD$Ou3vq?yM+f^S5pLv5RW`4?ZA!pN@db zCAaY~jK$LbayxUp_%pQ!{s$gtd$7ERAgjZWG+Rph><2FUd*TA^g{BWcktM4{ik_1{ zs9{9vL>4VM==yC;`MvLs#Qop5kqi>cAjr|sNHg7+Ip{ZdO7cJJFdvyyvPTmWKi6OD z<)L#gz!Q@lg!}I7(SQ0^Oyeo3r~iGx!sV|(Xa1`3E$KJ>znhatlDSAeMshb@9IGpq zKBY3XhG*wy+6@l&x;*?o`z-Q){i>`EGHTqbqS($jo+{GiK}W_7DIz3}vG^O|kxI7f zhkT8Hn5gse-dN11+mjzV@22XpEUD`b9}Fqeg&yW*n2Tif(d(C@9Bv~r#uMu~pF6mt zC5bDCa#b|v_lpU|m{j3#M#?p*3bmHu+*Ju5XQ}jWB3y$i=sW@850_g7)QqO`$zb^m zZkInR35r^veAWI<$lLaPFxb-7X!}WG6m@F9Jw*xXjq&@1MCCxL0~(QM7z2nHV4Ecp!8O;?`XUO}sD%Gpp{n9jU+#0Jd_ z@sbtH-&*f!S6kig26j7jln)%&nwcffAIZW*ij&9wk%y?+cumF*Yz)^-2!&c@jZ8=d&!4+BLLC##dv5O=Az>wkxR}-U1{^@Sf44 zpfX^u!#-!@pRWuL#i*J{Q?)+z~HKNIWG=6co?WDa`qyQ~ZY%CG{J-ZeWCAkMaG`|X?1+ozXx^@1zoS03 z%#W!IG|qHhZoobquXdp9eKGD8uhPu-FY}*!@?^Lz{(97!<4xp4&`ZC+QcA@%^CIcD zBi(z`PI#)(tAa8A3k(zRkM1`R{1;Oqo&GxpEA5*XLgjSl8qbohq@hgno3nzqRPX1> zH*+7TwsM)42HuQ*loIShMn)R|HEpmqp9ngDFqo2DZTWVuHiC@o5hQr|LXazHxUfiX zTJ%NyQ_<)Qh)rI740%PkrA1Yp3%E+Mk9Np7RRjh)Z7H$yo^>BtZ<#4Je zAJ&kK-^%jj@LNC6prh8y=a3Dld~T}#rOIGmmH3w}@4)d0&0FSAM}fvocMH=svNX2`)0m$Q3ZLv5~(W~683-Mf)%R|bovR1gpq*yxKZ_=<@fHxS4gEH-rC2Rv5! zQ&sL8N`xM?!jq6h{k%ly{)+z{wajMFxeWg6Ht)rJkKS)1^_`|Re}m3$!1GCAWK0id zSYi0}UEOl|vOfBfh6|-7w{u6aEoCPI4=d73KJasR$+u@R__&`5$mrNCHF3gAji+rAtS~v_eCq>I&Z-oZg!&QDW+Vw!MIGY?_W@ws@&z?)E0`#9gP} z^mJpjW>}Zc->nmNKCWj?Oa764_MYt50wYi_f-m@w{7jjKY}n73E9%RS>(Cf`{g23a zOHzY3!^SIl1PCpT=&7wK9aF6v-*1;_nb$K5vkR;4VTfDZy@KSCLH&;|9$62wRn6Qi zVM6X{wE!;3JjkJH1j!)2X05`x9+8;p@{_+XcVPiU0GP@E@-Vq?@t5!XWb(}0RR7}>tc6y+yTG^er<0SFoJF|G^7x~enM z#f{uO?%|KW-aCgmtM|~mWkhD1*RE#S>87@Ga;n!r29~Qncbjt)0TN(oOd#2^QpEV* zVEdY^#h?n?MSs7}xx0AN)`I4nFO}zbvRyCME_R)JQ9iZZtVZ(Iik3x**EbqBN5y*tz91?aE8>-f=$1xf;ONI zja)N<2(XBiu)g}lnuGRndYSIv=p4ROGBpxxNgT2Chvh;lhnsZOdh1?C+4GN-ubTmH z58&K`+Np|=Wg3|XgdIE9+u-?Pn5zAE^j znXFHu5h$|l|Bh7i_)!QYfn%^bvrY%plZf&E$C}~nHLwr=2Sh#M{$wpgddY3swMCMD zewdv|*p01wUnHY?OHqdqJL!4ap>tB`v^+QXB0e|J;9CoCt&^4 z+Z`$9xYp^bueRDJBtsqtvbYa-ww$8tS2O9(h9m980juAlDyVB~E<5_?(v@0{Jh!d7 zf&E*7yS81RwYsI9%Z>KC<;hlA+t@9)ZoxgFEv|cNw`@E@mVb;b$%RamnpKW{I-m~s zA$+((2@e5A2PXeAc##!1g+09DCQ;x>trLoY8L8~m4lP;}7jj_!iCzc#?V1>-Y+&u# zzFH_7SsC{)R|90J{@fxX02U*pRazty!gQ7<3smk^#O}UfkYk%M>V>b)H+m*a<_TAO z3F<@w6%J9X3mrfE<2U-?N1Fe?q_HyF#^BXR|6^ol1ZqR10xfDreyxfbydF%bXut6X z_6pJ!iTLlGPyeom{hi+uAR0rQD5V~)u_L|Kuksk?b6RIMJSt{~$<1}5Kif=C;GROr zE!Ym2;cVSFqt5p2T!Ub{4YgumgpaG5|774)YNWJw>NkRj#!G>!DiYrcABx2=Dg2p58My*7a_`tcKA{0rM*4k*@iD`K>9t-AYqZKk&EK`HvgC%jV|CT0bPQ-XMA-#~Zru=gcyFSF z0q;{&q+Eog*0mFAwqsLU2C~Yeif7)l2kJoWEp`jY-_Q%B?A?_8_Sw8P{`Q^Wn|mp_T``TLSoqJy+)y|H2g$>%F3|sJq8- z!d{9MCBj#ie09Cb;C>(xX9ylwM2nd|6zdpaC|~x0lOz6O-(XQ^P~gb-WNS0J(ar5z4o^&9))+)Tg2+I=337p3{?v;}acWUjcxDU>H^ea;C&P})dUCr@BnyrEfC?$!5gQjf@xzO>6 z<24^Lro+?0deYNxz8~HpA%c!5da)c48H|sa)xmZ#m8-($w)jMk>RSSSlk6xrXb=?i zFjOO0qv6Z9XB|0(W|VI~wx~&O3^2g8d}CW;*Og)tcZKc9UYlhrqti2GzX!~~wr-zt za(+xb|1uj-5>9D{y%0Y<%H1E{DtJDtcA|ZOW4v@WcB}tr4ojj@)*Rs~wLF0Jk~Gwi zLMzpBM10af6VZ6`5QgH(av?-8(sT_^4FIO)Tiydo;yUXu@kn$Q>$5hfPP%EV+RF>8 z8;-2=uGu%77~*M8o1=*N7abKttE8 z&wF|S^_Us*8qPN@f%m4X>;O=X7IYM5Od})73)T7dIQ7|2Kc7t6)%W*G**VeHE;9xJ z!h0kZdlt4RD;ebG>a1{A%@MB3&NZW3oFWif7Ta#PRB}klb~>GtgO65X<6<4AsMMdy zPATuhufD+zx$FvGNMU?V|LhnVIw#d)-QqB~Scs(i`-$Cjr9kqiX)_fNNX9;T4Si}AGXJ(!;$5wZd| z*ODb}sP3aJ<-};0F(@^XK!An=V{vzW%J*xTo-4=md3+M;(U`0G|a34B&%9G ze$%83u>@qyVR9 zDt*j&tk&7r=#@DyF0hZl?e!OW=g;#ONfYUs8w0{x{(bf_3Q`QQ)R_P=VJl2 zUuhT1*B4fULWsC21F6VLL$)GKZ*eR68YY`6Ua5c)L)qAnC9CVmsmr~SA{hyJE^K&$ z=TOQP=;5J#x7aI9=N?WG)KBWUTV?^g1BSI8EV7iJ6Mg!5c*}ee^ckQ$U;Fe;25Rc2 zf+C<(?5$>qm@k&vc^*RjWzzQ5QH+T0sfYUIoinVHH9dl7?DmVn{CKJ<9-Q!7m!aj{0Yvak+=hCKc}$PkLbEQuOp^0IUW6IXBXm9C6>kC^LpR! zvY0Z2f_$yhP`P5>OUyOr1Ksi79BCp%dI>SnDmY~qNtYhOjRGL$ZidRL5*=OJd_Dxt z08*UX(&6BIjShzuComtPX}@t^fj#U*qvKB3SV7!ILVeyi2mAXn!=%V9Jbp_egiVuI zY~9WA_uCnr~Mc;PMX?j8X5m#raSQ8sV zy~ZI?sKo;t`4Q&ggv8IVvW`bn#^#p2_)f7SI~zJb()}@{U_K`ODWx^(9C_}pIM4kJ zrq*9~5a}%yAb#xvt%9jD!@i^{&UX->nHttm@l~UVL;OKtY|IEwZvsQ(1am3$@WQQu zl`cvm*?#OL(Oo4&e-=*jV;9m+wO(y(?(3m9`#JThV+3@VSZfk@FiPo*JC63Sr;dn9 za7L2q9sObTF0_cIapjd4;Vm5gIIeno9M$O1bm7KGvT07Fs)J25mwTR*RAKv*0e5$C zc5HedkWZTva9`zc4Exvv&ul;V3SWQhf&8)a27{??pRTD*e6F6r_ij=;`#UmaxU1Fk z{4r~_yM*?!7SSyl8a4G=HAR`ONIvU^G-lswPpa7X29ep@o*^P}?9P+M_+qS=E;ZX5 zr1>9IIP9SILWjh$aTc%QoT%?+Lz}LIBtf-xq6h-3NtODW!B(YqR+ucNmL! z8ybqvH$8#(#(&RsYyw)DM14)bq?A$Td;!GP(uAp`=lw;iF|PKWAK~|{i;YU^ASWQ}a`uGcErnOct>T_@OIjP+B$!MC)timcM%mX_A&(xD)8b4|FtAhB!RW!7w@ z1z$~?9mKm->&UB3Upt>Lt$P6U)-@n*83KC(h%WoyZ3UAXQ1gJcvGo(3WjKB^B^_BA znMjX0$6g}nUWhB!au2C6N62Glzi4M+@!D0=Y(CN9mMlBOhM?itZ5dqZN_KYGncq5k z@E?u0?3m~D9vpPPq!T$FOKV(B+j|$wD}O2kcufnqR8L@1n_unHUc)q z1%4jnAF2#T>4hU&{O$~n5nOeieN<8y>?HVmNk~44zwHzveqZ4Nm7X(wL_Lg)KAP$- zY2XpXJ5aH_!T;CC1)jYCLzX#)Ae<&i_5AW-IJt<6yF%?Y#FIv&MMB&)2lp;SCkWHOR9cMC^Sk_;&dr+UX2<=3l-QD9_Ie+@FwA0&Jtxg1<&BTpA$i&G4_&0&L0m+IX#h7N0HJJg~= zwc^1v{gE$*lU+u8%D+x{OZf|EavcdW8k#p3SE;N?NaAwqCA4*g$AGT53NA^XE79no zd#Y;upkHPVP3+I1RApsPPIQf|3xcj0GiMX9oG)sW?(Z&d6EEAVI3H>!gUb3HCT9$; zlF#rbc@o~WaoKXPUZKbk#lXrwjW%08bC4>NMVP~&1Oa<>UPsuG=B{q_p{YCj`@iEHt*9#xVqkGQ9NCW-2ony^ zHq2q===&4tpnTE76VJaud|8XH0<$V}^QXWArg0*-NwvDJuJv@VBx<@5&jR-N!mPcR zCh<1Y)ARLk>`3rU2B}VOG}HC-463`JKZT9oVAA+JAI?&$hCV z^s$pY@?pMtNHf_dq8aEo0;Y+}9CTAPQl5tpRKc96Bh|c=`d1U+SHbdP|#^x_s96E8c0=xd~pb=P>HDX5`U4=H@=CJ7ea}em>_<$8=8~jZvY9$9?>P{pMGS#c*Rj!3!aS`k zYjNY$8MU)r0uWYVRCw> z&h?Rml37nKhM6O_x-;4dw$niGuJxnIM3r) zQll*D^#`H9&QVWBklC*kodlDOX;GB@jN6;Z0N46q%*IJ{Vw_T4a}K9$R4S%O?K+>J zQmCRr>vo4F23dd`MPC5+=4i1KRm95zvU@P~^7|s}_-ctKK}U%}sQ$HEreZ%6kf(&2 zl4Zed^oyqa3hxk@StZ^G;<2fk_et5%zbXJJ><_+=1(5wV^k||uLUih97J@~Z86f(l zDyou)>g~%T_1Cl9E-V~{`yt9oC&3jRtqdVqVpEdhk$7RK_H1yGiE0xi{EOJ7MO$X| z_%(vD!2Objsp(CEFRwma*wHkOU^0Y_w(+9@C2(VrT>ef2t-XPyht#9TYAvCOwD0a3 z?#_hC9rr%8Pzd{NQfPK#!nxdEmk$(rj)X1V2CdU1$T2H7GGtdCn z&!wiOWGr8foycCR=P7-Z@hGY=cks0hf~jbaUtRq?CZ;S=Vn%jA(4oq7ygioeGZYWM zv0-CGrZ%XQkY*u#daIxG?V=9XXK){F}Ble>6CD+n%IV~^Qa!jRy0|} zLwhE~$NXU%4T#_m7es<^;DQ^MrVuiudSj2V%tlw#*+LOMsf-8Ic^9(j2X5@qRSjpz zbhKYw00F>Q97DAIof5h~>$>fk!WEuerbzFN5P99~NL5PFi{ZaTXqC6zRL-g;r8XwW z!}Ig|_nc6?W&>M;)YU305G-qV%|?o8h<~9??bSpc+1&Z^Pzm+ratdY330twj1$TxS zOr+^=f^;{FkF4yFa3iZxB*8ptH8>Qtr-(|GpK5GpYL;iC_1ZuLnWrrFz+}@`D9%eS zQxe^;AQCK5H<1F&)U`KLKJYV(>uHlVyxkzF*^p!tmwIdj+8feC*j5saEmg4C%4{hx zCou8PI2U>_a^u9aBmW&#+U z!aM+Cz4h$1M)li7iQd$_q1+IMQ#au`dAFlWbI} z{pNd;D|J?po!!h-pUcP23p{C*2il57bIPTgQlZ67kPA}aHTJ%!UrHBVw(OKEKmJ^+ z)s)0Q-nQ8wPFdyJ*Sz$#z=Pff&>0G<{g$Ke(XZvBi4xFm<`X&p;dB5AIo9;rkISEC z4w6=*)Ta`xr^3{!OmNL1vz>xLV(NY5Oz;HTr!m!5Hnwct>l`f$}ChOTj@#ax)n4(1Y$vFTQ>3!zRdlj)Bs0bgeROZi2 z_qNtOCNFNR`>?<6hB$xWb_{)W?Le_?a804PaKQn`?=t5%UW+ZZ_pE#b>h#nm0xVK9I>%?&MJw-GdV*U0`R z0))t*ea$m^hB(1!TlI7JHyga{cNt-+!S*^qZHr%@+*zL)EEd}fKX?>ox^95C;7ZBA zJDpHMy%e)25FFtL+-&FyFWg=e)8RuTG@l#=ICrV)t70!3Aa0Z5rYPk1)0pcN(+?SK z9IrQO9_J8rZ;>Jgx0e?CyfbKp$M3l;;CEPbBv&uCc1ACqoN_EhPdEy@At4ObVw$fT za#WydQ!tJSVLNdl*QIux*xa(xCjl^9iI99}y~_WexJ$QL@~1+^%_UdCnM28e&uw8* zo#emGZ%x=4RFwXSD1qSM3lEK>er1M&@lALZ*Q$>ui9NNV;1I2PdI!fQFluv>ps;lH zi+(prwvBRb%W2GMrx)mG;a=5YFP(&k0PWD6(z}bIih`NQ+Ox5uXo<^RO9LN}8ws16 zX^GP|)_|6}*6zDE5>j%`*8FC(f|E?<(P`0bT^~JuXO#TmR41M&TjR9+=>ujpO5L}sZ4KW@hW zz9)Iwy|>N#e?d!tH)zRvY3Lv=mplL|*9QSZ)-ChOJ+EB_i}l*tDwA&wlJI;r4#Dp? zQVh+f6Iq*W3@Z3^z;9>$oa95VZ|eLpxinKJW6rYkYQa{zS=~>X8ataX8tRSc=0QX0GY<( z19SGDH@Nh^op^=lOp#n0vPu{$SEUPSggXpEHhGpid&LoN-ah9uM^u-GSbmnh7QRnVRAE`#F>UWqZIE*yQQC{9*i0 znq8|CZjui6@9R)m6B34UfosRQDDmXr2;gy=7gb`$T!dhPCIsM`9vsHmD1T=60JSrQy{V~93}^dWoCNDlHE_8gKvtGW9s%B4V$^)*ye}6~1BhvHx?Sh#; zN7KF7Sf5%04T&*^!dyDDCr?$+0{suL1*>cZ@cR}?g52e<&V&ju9WPB&u%hgU<{H~W z;4aKL7}EJ3yB~{`>bA)jn-@EMPah6k8w=#&aRKK4i7(*&JHBwCsrEPU!qK3f56Iye zc5IRPEPU#()W)>*k3-A#qc|GM+#B*oBS*KnT5=suLa9}i2HxZ z%5FCGzoHX2MV^$@?QE(4>%+?goH6Kp6^0jQMxNebkj&M874+oyp`kmI3LzG;O=Ss* zshfNiLUNP65hW6`Iu1+-8aaMtc!HQH{xPK*cXfjVAd=A-$=MGk`B;DgT`N%H$NSi< z>kEKF%FT`uYAonOn3uybk^ciJYgOHc+8&I|3ptHVK`Ew@e-uS-WaLYZNQg&_fg&MfG? zfiYgxsg@ZxAF#K1zJ{*b_sbk3>#@dg3;6jsaH_SZM{LFn&j9HbBnltIt=w%-bHqs{ zlVr`)H3un`AGj5nWpc&GJPCS4JI2XUt`T0-%zHlIyfKfSq{Mg$+s(3bJj8&jEgc$M zKG2keNqF3fIK0Yl{xEwX>_P>Sypx+b%r#N!O5VLY+;M!qwW+;VwEfe-6^y?7uu<^y z3a#Dp*f`M<4*JdZI541bFxvFmepXl)2Cg;z)gxoYuZ9LHGyR=K6buAP103=-Rs7Cl z{I9R0R29D53F}41mnYYi`6vLURVy>%jdQM#$}O9H1p6;&sVW!?Jl7>{eTFft84ND& zPtBS0Y@eOg+ybfz-W8~6+AT+ZJ(nmIa7GE7Ugk(9*XCayBwE$Fx{FDA@sZoT!a|2D z$J;pSbe$dZi9n>{j9;gz@vI)~e8T~D9e)=--AuST`?9p2@u@eZ30aQ2BvAV2F`e9= z{I~$rRME4_mhSK@uAS)7%1>dg!^m=t`)2Q-<|aa%R0d^zZ%Vrauk@NGUadSiVoW`-2PcH?{r zRytbzi(54E3<`f1+@lXv8&)>5nAOa#*jF7EX8x#4%DultTYA3y&_hmF2H`EPiEFJ| zQJA!+p8|WgLoBIyY;5VogG!!Ep|u4C&v<#89=fHs?b|KLY_U}zkFV0TJo(x$qLjoP zAqx<%=AuNpTBGsNo?c&GicJs~gPV2cw%raX=e1x=V`0N< zvjqCVJa{E$S0;#)6Xyb-9XtM=p~taIjt<4SaR+(>-ITfxE>n-^-8VP0laauSCBQ`Y z<_VyuwBrJjz}ha7Z*m_?kaKw*Jp9hIimL^xtN-(VscIH?ifNl6{T3ZP%s}I{^eC;R z_xE4b34V-|4$`OfnX8t;ZPQ6_n<2%`ffQAHtXRe3oL&A^I1836Xcf1*UF|y4p6CQN ze4{{6**iDof_TmR1&L$Rew5PXwEhK8O4m_d=NNm;;3^@Tj;~}li*w5DNpmwI9;l2s z=d(qtdH%HKRQe*DMLmEN_P+%gqBEQ776+m7tl0{Yh=c$&C2w!`w7%w23D`ROa`4Ot zA4%Fi;&Pvfx5k`@iuGCQ($bZ&nnTtz)q+BMVg}DU2Y18jY2njhc$)t#&z2R z)R$o2cGVwomZwf2jQKF&vY%0+!!ofo6a}KVog%2uw9+teCdXD(0{{8NUSjG6w`;-yXOS(Flx2V1D@5X>);$0WXn|YQf_&+rF`ejM+4Y z_65X6k!1n;?vHXY-RtzFMBX4>*>Iz^LZ=ys&QP;VuhOk z1x&@+vOw7$oha;leIb}4aGs}*(8p^cq}}knu5lxQ16cU)p)Z2Cn3{9Qt39$=e9?O~ z+5&XzTHdqDIUVDOVSdj@#*Xwucj9>@MA)yLS?}gO%yyHSiOJiVwiu|eDNr7w6QlnafwKu+ zI6nr2T#jlt*kctAi~GMN!C(&YSN-@{QCs(pVGiUd+IdMV2;AHh0K71ZO>YQ{T1px| zR{+0pDul(kkub)VLChu7Go*gC`pYqlBx>zz{O@u#_6kv|_sS`+!i6@)%=L)5MU4zhA)IO%% zW~EFz8;V*>iE1~0h)&B= zpHnPDyO}l?^*{oRs#b)7o$6AS8?D7sk|1*FB!^o~i~NL&7G&qTLDWc6{tO)8olkGN zVF>K5O9=y|mLnLqge14}k)KiV>Bk<;=e;K2*C?i13FlJXtthOX%`Oct zE{#H~@b><^X>q~p>c+=Y32uip>JeiW1XjM@1^`HP5keo}%PsOLR=266HM=I8xUU!= zyss)}&2bZ4ECGTsMprBS_GUkBCJzeKJA zasJxjr1Y{C%E_To8uOof=-S=NXzZKX{LB|DnM8wcus2P)JePq^@Mz`z5zhn`9y;f3 z^|qdMJX1KKU}-W;UlLzM(K54>;B>W?;Nm1G0L#LJynE87*z?RYqzJt(AfLO3O4>3u13fH!?kNjQ+dy2dfi&g5^i~c9)K=6s{Z8 zKA7KzgX(@XX|NL%3h(cglZzO0OHi7Z8E;q(TuO?`H8Z^z_K6gqRYhI0^(~z_{H0%X zxYL!_d~9W3OXZX5u+{T?2pInyKpTE!-^Tic>xjAy9ii7)R8nix;GsvCZ6#ea2s5Kr zLQ>bQ{;=jLr(>uGU1??M)LKES)4-6Oq0~p?kRM)=1skd#N=F*>`$Nfvs@@h_Y!5OB z?2ar1{YE`tsY2*`e3&KOqNmYfoaCZrGyslyxv>}E^{-DQSas^+fNV|XiJ!{&Qe^a1 z>6%EgCR3VH^mDMC4+&kzxf+@V_(vOgIy46~stKnWv^(#v9>QKdKK(taBA^^RUg#tH zV;>UbP0qT<8OfM=SvpvH#j7rVBA92>)_O8)nfxe7pk)g4AVakD(Ndjsr+a0UwKNW& z|G0mO&08^_Ywm@(gb(f8EKy4@A-QVJ`HGcOsuESrfJIC(7;*t+-~FcA5f#24@USlZ z`(blO9GuhSuM~d*biiwpq@^mn;y$NEt)(TaI(2_yaRr?cPes)yGA|*0IpC48)YAFe z0KR{W!W3FGj|9BsXB%SJ|3H3prY3~!HlKKNLIf@uIy_n7E13QPbH`gdX`L;5Jg9h4 z!d7@IPZC}GA#SA#hs3Q~&!;@xYZo&Pd)PW!D=`$i@7rt;qui^SGc9x0G9y5!=zM3= zwh)_q?w0ORHQ8mOiHnb-a3s_}B=yn!pcy{r$@laZ?#*GfX3wowiBwj~^08L+%EKqn zpw@qeZh{U3l#k!C8@ZfvbJG6RQzk9;%*JzzaeJ3*X55k3O_l63QgT} z1QoC~tu3;V=ahOjEFh_iKg9kH%(OZ3EME=qCd-)(U{~^UeJ$bEk`$J|HVkK}xWsWo zAaRsAleN$j*pY-Jcx;1?MjzmLk!^7OjyIS(M2YsiCPg$#9iJHs#O*CSwI(1_8HBC} zJx`FQV-~x26d^RJVApyD??*jn8sL3e?%`Wi9I1olZiuw}1NWc|1u-6O=yL87+FNA| zWBGrJg*!gjP4c-I)bpMLyC2rutlcp&yz@rjdULU@t@CcG>-rnqZDLu3Jm%$GjRvbu zL%_qFWz^OEK(vj~`atl9F1`P?i8zNBnU1zKc81jXIOKgk0T=^T)|;jJ81p*AVB#&=J)S$h}5u3m&-)lvEf~JsLvUn z^Z{o;__m_loh0}%w15lD@7QRybuu*+U9SFPiLbN2%I@tW$Ie#`i;F5lT)>mlFvULF zOiT5>$hC^nY6tBwCgBAUYv3`BD)`fY5txeu@sU=OnCDynGZNZTwRA3*TdMPRMu?7n zk3B^0G|t#`jUiJ%=2g26cFLlUU$KmLGT{={(AkeqVUrC8=VWS%xF3rZ6b9v;M7--`!W%I9SjaIF*eF`SMi6BALoU~&JGRfvl69r0W;Qu%o%fx#* zK@i8pWp-XI?|iVwMoF8SF@?FGAqtH?-B)GerSRJdn`J0mZTbG0O%Y9f4T$74q1 zLVts4aiE-Un8|#K3)BQ6N=H7XMkl|JVsAdgS$*?)AuCs{d_P*le%%y6!LxPZ>v;v@ z?nPcnBcU#ZqCb@MyH&sZ*lQ-7R7MFEFM=edB;mJzu0>azg2m6fJoVNg->-J*$$n5~ zAYaph4Nv~M&RF`N6I1}CaLaD@XsFLMW%-qVMz2I;VYO|g~! zP}WF_@3@y$uVmc_i5x1@S?sv3eLcrjX=K&^22~r@g=!2Uo#G$Q;3#x%*dR~0z2n(k>w&DsX{=e!X|S%lGY++CC`O zeA9ifA2jUe%S&;oPHD{MsPGOr(LG95?`)e;$AJ6)A@2E)U;! z8l@1$MK@*v!9?zsQB+)rK{;$>1Tw>a_RVh?GyA}=v0}3W4PbZY(A#ZADPi34#*YH9 zFsj_3oRMI3qk4q8HmWZ=a)B@&qM>{qcUfL>14;ITtO_e-lW7H}zUYwm>H$mKE$_lP zweo1FB+raW52c^Su%qc5=E`Fj#5K4CtII0hc^xkY%NpqUw!cx&tZ;p^1aiPHNv`w; zsH1{D`JznOlhRZzLrL=4QoClbEvtFDjxSp1qdH=Qt45+yxarv5Ig0A4HwB_B6 zbyqE@A0&UTS)2*?0$dJ(!PiwEAaUXU_a&*3ZG52?u`dTTWkWi*V-1@BIXSi5#p zoq_)lqrp@(W_}siVHAdQ%Z_L9p%`m=ieI&G%d;!iB~fDxf>-o-0JCnAV<7Tj2Da9J zLdvjkN)2h@nlJTqSzN0bvh7T8vxtW`^I3EZUOUjxw}tp(0ezW2eM51_efiyxCx{Mo z4Zo1%DLJ=L+GgwTo!jhiSRfBbP4hK>>#E&mdd;=A;BAi|LytG8l)Ie1wv%}N+D+Ur zz~2qZq~J+ay6F z_OA1XRky^I-by2h-FGj$6IQ(~nI)Gp1$k90qr7`b9?;H3khDJyb95zqprw;81BB}&93J0bx4)p}7pC962n38)by1W#$p%NnD~q3ke)2wmBuN>q z>Nmy5Oo+Ib>!C-IShTC~vuudiTSc3Pv1n-BIi>?$yH^%NtdPEC3=w?y%zPjE3F|_mCYxh#` zQ(dI^*sJq|i2_B5MY@}w0KxZK6BG;*=&4u2@%fXLsL4V}x?goU^zr2rDDl@J{9})= zpNTx>`5H1f^7v1J`#c@(B}zj_#R1A|l6X_QFtwQUwkBWtWas^DemAlfL))5sw*1AE zt`nU2e+;t#0!j;S(;kI-nH`s zl#ml2T(zO$(@m?QM-6S8#)eX|#x~?3=$oTQ{$l6IDv_BIaIg>1BbEGG;_|SwM7M=v zxh~a@4#c=JEgHvBz+(H)Vf0Dxsl%L$ z50S#FXstT#rh;=U`BmzhgeO_F(WnT&Ht2*zHM%!nCDXZ)zU(?>WVk)xed#`V_{@v{ znDzs~OnBQc?Z)KCdWU2uZIBuGvjDH$3<~S}mz%#Cu}%JWgTTM+3;j{IlHCv*Esv2` zRW5{iTyH2(&}2*(dUZA@aspj%AN=*HSO~Edv1a7{Q2CVe=Jyq4OY8Av zt#%cZ(SrN==L2yuPT%wwAmNjYkn(Hp1F*N~$p*M%idIQja`xwc>kR3xAN??Y#hubcQ<%;?X>Ph{S@{{d z)*M9!k^jhJ)Lq%86?^iKC0p-?QnTD>H9nFAwA)5EY4qLaHQ4y$m z2U?FP>LG(Ktzt2y=V2d{NDz02i`wYxED zKZ?ND9p)0-js_l8)aAwAp>}vl>-xM&vWv9=jTZ~Ol)4Q*@TvdNlNq-@3wjKy%5_Bzo`?g#gHuGG zeuh1{IG=4Bi(N8OHqelf5c-&D35*`C0!TnoiEZ^;D!|~5+3R&IO+F;X|Dg8a(dY;w z|62#;@fl7Z>?UfjsMgOE_%`;+P{z1nx?QauZ#OeJU3V}iB~OIaABZL1 zQPkF`{4+R}5^K9T3ynHDRIO6e@)y&$qUYMp6(59uUmzEn9|RwM?)qW&N~FGA;KK?0 zW>U*>s6^M@TZ6|cPsI>6!h_q4FeF_6Yk~4%T~c}I26~{{s@8cm4AhuUOvD@lt`AD7 z&p94Kt9luY;<~!l1qO9pJmLQW+g)T{Q9IgAopL|gm`fJk<`Ar^OATT^{bC)hdA5J3 znA~1S-^G1AS-a=Ju?eK_wjUbdNxgXfMF?HZRA0dwdh@l?$C8nG(s9fH+%{E-v{)z2 za_jmjFp45V_+(P7M%a;Yc+pYl0{mA;%L?-Z%b2AlPaIZyO}OA3V0DGk7Lp5J+9XxP zReshL6+iiEm|+J_TDyDxot^)l6XY@0+!P9S0z|gvKFFG@0drrTzRMLq;UkvSU<~$|9M-iC!3$6 zjV;?eREL-KaBn=62=3E_LcUFh3_dAJHgXvb5IC5HWb2OU|h!Lmkx)LyIpx3eI z@zhuY0tQAC1N$G=OQLy!t>WXnBd|heD#@4wuTA@tT}2j3&KXduo|*)P9-HKPnsjem zNo>?6GmoQ%*k|p@Qsxd6thfsCKtnA5R@fy!+^!=V9Vs z>9Z>IB@$lxA7|uwF#JZ46UUlp?4Ltdc@Y4&e?P5Ga#=T4oEP>O^(;kV#SWyM3hmGy z(@Jx+FOp_utsjNnOO#0ap=-0yO^4;mCP4*4vj75*~!}?JAXrAKw8PV@x zh@Ak?q`S*?xy`z#a9m$*XJP=J7UrY=`fRc(XK>YlUNnT&k`>br;UNZog-`wquT$P3bfi ziF4*-*9w2Gh!+rI`SuOqChjl8Vh^HkySqZy8~Cq)-gq_z%gRfGMWc7>+_3Y{7yEy6 z7C}QPsuF!f8TE2|W8bZ#u>V0LgmCcNQanEL*88$q%w=Z4v@NY{8T&}b0r?N;^!Xzq zF&ccDG2bxrIjQT@lT^(Lrxj_$G`s6nwO$L(nEh(o0%=W$b zgn2>-;!clq2pRY0qhBDl^L|cZCv-^&qxM|yG(5~-I+^18d(xlC0f5139VG!aC;2T0yj|9yz>C{8Pqg7sZ# z1DeJ?cC#W_xUwCf6_PV$e?}eU;6mRs42yWExDmaM$<692>o_|-S5qKOlzKo_#pMx| z2awCPz7?35=CovLyE-T?c4F=5dfm3!FSN2vYr)UxGXz{x&=R^{YVbTc(}VBfNX5#I zj-sH`l)DbM&P5Y}L)AeZT;P`%Ubc`*oL5JM9*uiERev)kSiHpn*asquuKSaCvV`2` zML$N-maOQXz5ftZeWua01na92DmH$z%`Kxtdc4!sM~(rww%Ry`glWhpIZKQ!HV3Zm z?XzEtkgK+MWq;^{k37u$PcImQvTngGYI}5lC_h2D8;8ObY>(F}vn$-7L0-kLFwc zSy$?|v-Re~|A-th85+v(N%edZwsv^B%+XbE#N6qmaYRDP-s0!I2gG);$(&w@`P3A_ z6Rs|vQT%m_89y?_e6>fRjcATgcK5FyrOvY z4~p(T3NvVB?nkCztxR7~Ho-QgzZNFdyFsoDvC`QitiBzzxIKhQ;+j|Z#^J%{Jeq^aBy$5Q+pM1_YSS)45$M!Cx_WJsa z$^Awy0+S)S&`H|V4HH8tVVSe3F6# z1p-jG1^V=?X;lGVsl?9v;e@UZPcIL1jXno0I-OqWmPYJJ%hh|=7|eZ`n!3OFm^pF6 z&Nb)ijkeDhsHOvQ;N61*c~0F~Ck>WP{A))T;kN?*`S1Is7x%N);B93wq8%pXW@-(U zqm&#g!c2HiI;LN=;>f#Wz8AXr`OzkjX^wq|zndZX5aM?ER^jdD@}T32;^5bJGm_y| z$|Y0;Ii-O3)kDXlco;YSUU%ckavUMi($x~n;Hh8U{fj{H@B}q7lG~h1^KnYOS0GTb zNE|3Fbf#3Ks>g7gi@WKpzHjw{1Y(dHI8!T9HG;8YXXV$JhdJz5SmQ>zG8ZcY^3X&=g*BqZ$8B64lTPw6{sApiR-W*{D_sE6txPO7qPzO#c zVHjmCa&k}B5LMGeZQNA%$eu^$@1Zogc`i2IinO;6r5qvyOdO<$(hi>ft4Y?^*!RJ=~)v1cHFRXGP{k;8EP}L zTavQ;338ZtFxDkpe&o(vb6wr#WZnvO^alxBW=4^p)Ad(J1Jm6`9J_2^)-Equs{c5R z?ktmf*-&PzDWZIqK`|YMp8I>hfDA4@@vD2pDn5RA7iqW z1fJo%UYDhL!xNn|do`7Vz&HutgExP10~AHD+OgJlAvRe`Km zAzD$}TT8|AD<*QE2j z^ew8bnJBL`%DIst^qDBBc(2kkVwSW!cI0`R1t15F-EEU4t}4nTbH&lZ9}Y-P(SS-Z zEi0Wa8zjQv5VQex;WIhD)GhE7uD6bBZfj!ftLu>Xhbjx$w2l3vA5o3h{ObR_0pK5 zlhlQNE!S6zrL*3FP5PvajYH)}5yG$oe=Sd~;S0G@78Zd^{9PXoFap}j8(QvQ2Q{=y z^Yv0ye((o0wsikHcI34O*;8R#ngH*1!Uv?aI|hrToXt#VI6fFs9Zw9>PaJ&rCzzlG zXq%(cN%_7G?1(_IutucC8~zst?-6o_m^eH!l6#Ivm25{ccAnj(_~} zH#fZB2P0cR?`kIwe&^O_qlv_xtH4h{F|6R(&YJ%WiO}X3EOS0>j5Ob)2ZFChUma}p6{dXB(AKjP2#!Qaf+{E zOGvuf$!#W96_U-YU>vUTo4xbm0)QH$tWSE85O$-r*P3Q!-0REbF?;;9+<}xuqOlr;ZP0_I{;orF z=%_BB995Cxj)Ferf41bD199Oxns70!OE{X|`p)hmhF=YAr|t61)9`l(f&Tk4KFAgvB;(s)8fm~N$9#c@MPjMJ4iOcE@TrC zbN~q~*M-bf`F&&?!`}7aybyPIm=_q|$XGBnA&y*T8M4@XV%~37b%>t_2NGG@-UW8u z+W?;nEENp52KD-KC?;LZObx(|sV$qHKl|0$MSr@{iWX>M{R$oh#3*ZbOGEzH&9tmW z7fVKF<9M=~Gg9zp+l`=8%`NHHnb?_Sk?1;AAezVR zrrr+`QolTHg1d{=vXTPeR79!=gC7oDeRQ_FQV^KMs_^Jwqd{T#_*Q#gwJTNIU5S^h z24~M=X%Okxxe-M~8Jo3=_jp$;P^D(25k6T%*&i#DfChC@SevW>0q3(JG6iNMjL zGm97H)Aw=DHS`sjE0nEP3+_cq3PI6|DQ`SG6|GOYwAcwMZ1!7@tj(Y?xZI7BV0&}v%qV@`| zg}fTBEvK@u_R}b5HmszJH9i}|MyKN)Sxii%+;U~DD0kfiGG&dO*v)vflkfUC(H1Ru z^`53FgnKaq8?QyYBrxt}a)>=j$q3p$f$D}*mYd4n!wp{F^&ofW3AP{z65vSlhc8@# zOuhQG_62eOxKH-KGN}|Q8&~fpNp2BC+v{E)h)YDK z*Q;x3K)=27pRVCf)fU-1zQcu1w!@ODH}npKuU{h1R^=JsI%8Rl@okkSmu5kvc(&HN zR=td>;xC2^}^QV zY&otClRM;#(?B5^it!c|J`>31nNfPIUI}R*-r{EM>B$|b7%^MBa}c7i`oRk8WJ#w( zMZ#SB#tYYc$vc<(QWC}Z`CtKwEN*Cq3 z^2N(9!ba?v71vD5jN~N4GgPyA;f1_4sVD!%r-ma58jsbK_ViE_Gp>z=TDZmMM<0bu zFli;>0H0^VAjwcQNzJvRJi$ZLA(X^WJ`kng&pKZR=RmP7NHU*!vx|n)pvz ze+D0zSGB+2C^eC-qETi4?Z1wh(FbZgY;_Nl{RdQyNrY8ojogHnqOUl5Sqo|e03qy&wdh=8CBgKrKz>tMm zQ>rmQeb|e03YsnLcBd?^^;9*kviSw>(wmLSUH?)B8u;ST%~a7N+`^b+DBR(%{jrCX9KSEVKwJ{IqO82>)83!K%bf& zp2auzdOWCZcA?BHp6}BL3qxcqk-W-qm~vPMmLhCh$Sy{uZ)=#BiD#LexZYTK_R23Z zpJ_nADATBNpAT*2pl{rsQ#28Q%I;s&l+S(WxJh(Uh0-bO4_7|QYs_dpdI_pd?;tlV zzEapnHzg}ou054HApd=iU1gZ1IxZp(6P}o$(ETcTJHiYcL$dzFGROJ z_r2$xcHS<+rru4i2RIy?3OP)9d(68`nP5iPS4f`V>S$x6GT=!*e#>rZ z^TZ>rBwbHraV_QH_jlP$&}&Mknei&xYn?6L8$BoywOJoCRQ>$wx{l_&mP)Z|1T8q{!as`ZxAIu zRHXQ}gY!1m!iDaSqwF4>5=h(&G^gi^yk^eWyyFD{K` zQYlf(9ge3Q02nrw>Vd6~!I6laa)a;ZFsDxMJ*BDk40Q&{S(iJ z^y|M%(XFs3rV)3d;mVoHnYtY$9bLmz8}A1-%#`pcnNx65`U{as6%?@+xY*jmtvx{ zh*;#?e+Y=mDee50i;RBVW9!pVZ9(nW0|A3;m=#-%yCZ7Ct2+WYb|8#OcOjdFapgUs|hkpG6L zjS6GvCLdIiK_4Y~#d;pGF!?9@5u)BbfrWh7axa$YGVRb+jjxv8cDOq0s1S_ai0jK0 z8gAS8Dg19ulGs97XZi{dafMa8dh!3a#^wKH)%>pN{~NRI|BpAv6~gNM;w3Tn<)hK_ zb{N#-ccM^2QLpN24C!3#0iw>ZB3@Z1$j**18!w7=N=>c8QOA|}Fh@Cv{^Djei&l$v z8PJu?NHo8T+GgtYJowBou|+T`A@=jk6YJ6yt85(P!~;yL5!Y|cTH?{u7P+iz@g30v zS_&3nBv#dY0wsGsC&*ROB6f>KON>6Guh_zmV`xY{Q^6I$yfiXkJ6ltJQ~r;7J#z|97qFMgwGZ1!)6l`r+-HHg{*+kJb%@6WgxniyzoV zMt)=7xPjW`jR^luJMOzPsR15BMwPMm@1OP^l!Mj!$Y4&MC*DSTy->W5H-mfIzt8cek?|shG2e`^7`8&blxi&60HMV8HO5! zZ{I$z6+8!hJ;ex^9~MF~cM)HygV&6Io*CEIV2Xl~k?|dJ9niEjJs9LH zVb>}|`xQQC=H_UlH7Jzuqb%DEI2|~6>$#1>q;J$y4V#>UOl17&>z&3|z4!RNp#Jw~ zx(s~3&-iaw61agv?F}3pwb6aWB4YUgTDx~814se@N!X%qA8}8 z)ZyC0PQp6|uOG`j{Vj*2Ng-|dbd^5^V#)blk9Ui+%Qkf;oPR>zH2Op~cDL=_47S`0 zmC+M;GbKWm4aIp27)W%~>@_PK?DO91zXabF0kQvXP<(I-q@5Y0^rZ# zC7F~lUSZX><)d2rlKzfip5g{Tk(QDLPSWtf^W&c9zl*c&=t@YmY|nmMs4uqofL*ZY zL;N7-UF@pU7>>USTw(h{r`7wX<&ZahV;qyDJ?p|U&MOUTq zQgF3}aJESkk+#cn# z9jG3?rtWmW?Uv&h16ws)RNV7rg~o z`*&XR?ytNi7`$MeXvRM>&WQ5(3Z01a6N3Q$?xn)-`ozRpNEqcDwgIW@>4vTcwvTrj zbWk_IhxI9nsMR5A`IudRns3P?Y`LBE*{{UZ-W|JlM2Jmemklb2dfAHXQ>lDv#RvxX zPosFBW{8(b&?a^3{eF)2#IJH zPv-Z0*wUXR;_<~Lp|v$NkqBG?649E8RTPTg?@;LOlEdHklej&)(AIa6VuoHij~Zx6 zn7v<|WvY5^d+(3xZ#XL>Aawa=|7~i~fNup)4y?{sY_LR|n&83^rl2qNh$U+;d&1X@ z8L8WZ^GH%aZDb_UtEXfe_Lgl=aJy7*KBZK~<^gX^WSUc1RdGvs^47&5&=YZ)0u4SZ zNMmz!eP}8V6lZ9jjSDviWlQs{@8oO{+!ow_)Vhy*U6Xh|t$Ha>@4fm;+wq^&W2-8v zd;#h+GNRLQ%9=BpUT3x6XN=+L+rd<`Fgg$}tbdyRwDt04g@E}+ykD4-Wq-4f=d~pV znV*m-FZpT>!F0-p7}u#CHY0ndX`sNv{puZtzy2?#YbrhX2{d|0h)z29Hh%8?On1EG zMp{gKRrMaz0~}w0vg3`96*d?E={W)I?_@BB zWD+xC99^{D_%fX4eU)l0VkP_+N$?2r!H<20Pc52ENB{KEU$IiDIXU6eRZZ}G?O^z$ zDuclpV4cLS$GA!?m7o3q^fz|%Z0)9@^u^}@;-myZgUYKP(+2fmSO?-hgc#yMBwQB(T(?>y00+I7hh$Ek>=C~&ElN!o_z4?N+ z8cCmp`K@>i->&xpAD`m>!1Vy}JrBj$s}Uxc4S(J(92KP0v-Wadme@6w^OvnsrsSNU zV>+p{as2&adiV3P%_lvq=p349za{?A^`4is0aZUEzyBo3X*}kTk!1dVARMm$nWoWj z9jaOMr78rDa4V*mOns-zJyp*K>mFtEjQ!aW`7|i6Bs^R7{Qk**pl?UOo_|1X;QU4M z{mcz!Evo77}~%9I!8z z6V|;prE6gByn=^}+m~)UOIz56{xht1rC{yi_BU`oHGr224Ix)XYv@Acm$#mwZUjJz zq12&uP90f+db0&=*}~*UuucuVIp>@F^XGyV*F(56VcmM}XKqp_K`j+dY-zSt>OEjh zF!J0Eh@P;kOan^cXQn2#{`g+<+F~Xx)j{!2HK*Y;1Yo?|B9*^)RNMb=9fQ8FPe5tu zwAz;OI7Zjq(t+6U?$9a?vpj_iWPp1T@68mZGf#CvPr;=Fs=1_96dxX#CyM1MNCZ{&3=%^Nae)vf9{{x=k`ag$czrxgY+5Xl{`emw}cHz?S zGnN_FCuruKe+Z~Hwsb)dUm&-L$~N9S2aDq+V?c{ z#aOnd-QnZd_4hew3*uEw`_8{Zto&b51%|#uHnV_u&kxU3>D;QH1TI&Deh<8WE%sOQ zI1KArTRRI9DG8{7(I*9Upsi`}+a;;_)bfD%0%m(#JoeZ%bBEqahbO2(G+b~2|Eni@ zs~&x$-V2}Vzk|9XgC|P>)Qe7~k1J@_8+PdYE%FeUCg{>xXgwq>s=)1S{Ii`3XOKazSc*g~!W2l|0&vU#fZ@g9Kp^>x zc9-xHOrw0jsIS*&*&l*+DQ&g5+X)C~Drr>FWaTmj@(?ZG6P9-F=5=q0c6zAq#$^4o zpM9^rywIXe3yCkMPnqP?%KQRad^VO?g~yZ1Vt_6?rfuUiccSAe2G zuqv==fMfW7lCcn0MheRXiT_DrB&r0aY_I2f?sLX+4X*w~1^QB7OF!P4NcoheQ9eeZ zg^qC;hDFMVxVbQwh#df9*Wov;F_^@EKskp#)~-Lr{fZ762X~FyQSs~zu-EQY^#wE9qQb)O3`5x#Ir z(6jHqg{|=`l!=>+!R6XhMkI4v-W}ghGuPgW0Iq;cv&~ehG*e}a((y(x#OaU!<}Ls^ z_3O{R+#gO^3ndu85nb0C=3LLlablQAH)~reY(2TGJHpJ63Q>av zMu6xD?b8Ko7X5ZneOfKozPE#@R||{C>hPCtx#{xp_MDiWfg#rRAfu05yun`*cOa_0 zr?`dapegt~wddJzrk=5GGbr39t5iz=ZYz)u#T9icdUsU&Vnj~wyo=#Z_)GLnF)E$ZimJ2Nkiv@mzHE;@0*Xj26&!jCiI;`=4Z*dZ0RV^s3ygk7ygO8lLjroB5Auo&8 zpE5QI`yf7Op?>BZPNY2g9{kX?w<_}(lzpTLL-a13m^f%GG_{-~l~Z19v0$n-Z~8BN zG`k}qM2N$O%MN+M%CF3vvmd|(?NmW)H!=o4?LjeExbYn>Pl6BK6~|ml#-N&w%L??4 z>%6_2Q!Ekf^T?Vt*b&9SvMC+vG5bfzB@S5{Bq-_a*skWp#X0*zg$)xs9?MXPIV<~I z?nuFV0wo&<`rRV+w3f=x{OEn%Y4nPQZT)-RC{%US@QZ1v!?Qnu!NvPexr;#v>~V0_ z+uhB{c7NzQxm+!3$j;&DBF%5orx}}F0f=;rwX^ORxVTdB{rmAZbffKr^aJFE-Q`3j zGCwaZ)8lSu8hV@m8x+0A3*KF6)7)g9*)Dpno?d)Hbl?V_$n7w0`Fg4O(WvFkrXl9g z{+Y_*t*UR6k3gy;PfT_fb3s0bh=+HfGsXLu1Ioh5u^aKAd}R01=)2C*{iCx{dli{~ zWsG6!T+h?*_XqLF2rr_8v-v^%^|B-z^Gt<_B|+EQ*RQ6dNON`kSAKc(fEOi3FV+^c z8$fDi2;6NcD_tyms$pz-!doGnZ zQhV?O4{yCNtFu6MR#&U0=Hp0lL(s#NgrB%YZ#?2xNULs7_;#$;eecBY2;xz$2aOQ4ErPL}GqU1HOYeEB=+eX%lM z!QDxsc{c33K%XkGVM=?fnU(!nW61VJOMbR)-bNzTNtz5QpR%E$hS+4U=1UkMde?;R zHB0~4C%V9)$!q4Vz?zF$KHiIKcv&FJ1+dGsz9W{V1m{n{e$h(Y*}|d|;^wS_JEH9# zu6it3UTp;IOqb@ZPp|H;0o?|?sI);FhCnV%$xy9l?1jnh{n^Jd?_>++$+>gD63sH) z6^68cOPbC_6;ME#chUI^DFRy^708QipgID_u>rk3@}Oa%LyzYxFAO)=ergQMp;)_f z%QLFEGfiebh^a7)u5%UZj&{{M>1+k<#z!C|Q<6^D@*XbM#&8B(nGJ~`db(nhqKob+ zN(_RT0o6PKjDe(G=9&CbYJa+7$eo|s#e>rsH&d!U&hU+|4dnijL|R5iZb-BU|6>eW z<@8qXwUwh?qxs&nAIQ`hx82seo>m52;E*htGU3RP&iPI=j>N_H>!qM>4-w?cXE7}A zk`Qs+CQ^2LHO_bF0fue$TVsb%@I~O@znH6D1-o6h7G|%d0^LvQ{Li0fhV25^4}1S- zn%ZbRuLZmCn7WP!z|&lZkME z7iM8VsjB1YL&XQIZn^BS7!E7L23*SQ)-FZ zzO5S={#dS$KejOR*rSOd?(bsBwQq^_KSv1H-;jHT5o)^VTmPB=V1Y3a{f8!HIyFbm zKfqw_Q6Czhxff$xmEo5|pI!C_zXp(M*bKB&yW(W}KR4c;Nn*9#Xug3tKe^tF4a5$O zoJ$$DY<+1}x-!3P$vhH_vBuih2ZSH{_=PREhbo;E04?dy+wiQ~N-@2hNh^${KXVU` z%vIfB{<91@tmcPAU34z%~!O7E=X z0Mv0TojJF>6qZ{E2gOyfSnn(dMk318x)VYbAXBsKdVN<#c$p7a$zr|c3C{RK7QA_t?u41 zKaSNm@`bQQ(!qH@51{)}*lQYcXJ`V&=Z>wGxTds7ZHRK+gzx0FWx3z{Q;jSB=_?2N zLa@fY)X7)@9e?q+kBc#*pXcpv{MpNK1^_9TrS1Kz%4v{vx^2ti>bRxw+*%1{*Wsltc6~ zEYd8Odu7{Frs+JwpUM(4-x2;heINMbf28ltaG8G}!Nf%yKI*s7W}nUc;{`oi`5mp+ zPvoU`NKRRvMoha!qX0pLlaqBiw77*(^|whgK#>e8!T%Q@@usdl5gy&6?rehT{=gwgiqH1SW7eryxc$wb5b&E^V2 z_Pset_-U3(!vj3VvYX>U@AaKwV`WII=E*c!n5!UM)-(Rr>B_t|%?mW8*5JEPUiO3x zcMG?Qpx$Zw`Mu`I``0U#cwy&yH4|&bo0OXKK#juO=ROJ+ZegoE&;I)25rEnUdl8OL z=pu}b*VKAOcU+e$p0`c6H}B?y@?PeB7dJCyN60;H3e*p*VI&(2 z!*Z+6TAJUxvxVY*Ymi-USxvMv9+b*`@7`@e2*;`KQ+$6FGS@J60ex~ZzYgsX{-Yl| zw|OeAr7=>;WnI5@3DwL!C2IKm^p_FzG_TJNtzFjcZ;WzTJcHt7f*!~6s}s{RH(}E2 z(vJ+oXJlj)pPv?siSV4#ih2D7;h?H5IHKXR0 zWAh3y&-dHaVYsG5Sj?iKlIVI|om5|?dLSWG#i>x{9L8cQF1$m}m1!?|x8jAmnc1K0 z9~8H3h2h)K$JJm3aeK`OT-XTs8gx0KN4+yH&Re*aB&_|mtm<-EJrCP-UZpT*r-1o^ zzB!0J-5G@H(tN61O^2*gb7VY~J%pJhnNfePyN+m4rJ_&?TZl8km^}sKnJIrI{FPBz z+Sp_xpZ8ys_LShVR}CmUeRj6Oan12JI8PI-^%BGHbX5^CVUp~{47}hI6QL*l4iEeB z?}h@EUkwFlF)rnqBl5rd1o+L{r3@YFEt5BH_Yv;`HN6mn!CZ5nhw4;L zwSzqR#h&DTZ*{lCU%PlN0-0Hd{zV13Kg*KzJCBusDKxVP`x3U!(>P86#h~`-hu%Lb zGPo!9I#lm0c2rm2mW$IWBz%y;7B-UM1;~3e@j0B;TK^UI4p}K3iZIjTR{jwgO8qb=3G zj$eorTG^Sxw)##Y7yNcoSl}s7FkxDjN*p|2RT*P^(dhb@OT-b+M1Jv#fPjHEgZqDW zk#M$`xW_8N`scfVf3To5Rn&LMvrf7BbC(0`HxCC;;(YxHd%LSM&NL zJ~0GGNdT-BvhiksXE2nGj^A~OqZs0X1pjhfj{XtBe&4-;yztm4WoqwH`R9ZDE843ah}SI&xJ4gH~l3>2F1;bQS0UQL+1z?l?7 z4L}Bi*Mlobfuo^cl5k>A(49r3{RRCb?v_I+GBE5sxD||!s7T7-fAF&*>NbQ0TTjvr zJnxt5IHVp{@}TmU56D5cQ^fPBL8a`<^~7$C7xjo0{BX?F&Tj>t#KXYjmoeyFDxoIX z2*1S|_q(SAa;$ozUxa&uocw zA#Zwxq@Ff0y$sXP@PKodIO2opivSY2a6*9@vPFZGg&)_iyueE~3u)O15^pB_bpov0 zflf;(N7`&{o$;$^&G22tWQibc`As=IR+|k0393&eUn27sl;501UKN)tLd-Vj>fgWS zK3#pCs|f>w@*hqIF4!h9K<(W56+m32fY_~+U9~+FobgvXG8VIFw zHXH^zA4_xHJ7NqhnmFQ9Lx#nWG-P;YDFS`jLQQvgjL> z?-obikn%b$;OYbDkwy4Y)Y}q>q9<{&7Lu2-=zlj|qEqO1}~;TLEDHs!yDysWqo9 zu$;m%S*VB=|9fmuj9mu)V1TS*b$%s6kf71L@EX%f7S_0)>@dXY&M3&tQ^GgU}ha%VtRwbt~O=YQB!2V*tJ8}8`!5`oVW8i9 zdU#k+gqhc!XN9I*VsRP?c8z?xew_~fgqEHxG0l};6>564!%(+{u&J>d*I?#}Du^577#S87;E;x^+~RG#hS=(|hJ7Q6F*z{pe`@Q4K% zjXJ4PB%a=9vq6mT49F~v-;_t3Nf*x1`yLWe(@WPj5dhZD&d$fyj8QyA{D4{b-3p?& zZj($6V_bu+sAp4ILuUsBingpT#q|U&k2v}k5B2eimPXW2>oGT~ps%HBC zfRv;@Hh#zdmBhm!vG0D|Y#Z7U;qb~GadQ~u%(jr!5VR_ovI*9aIPVXA3D3)Qa@@0w zwK=jvK*y}X!c0vl0-Zma}sMhxdq zkQbVK2>L>OPRw~1P+`w#r=%^>r%obJjRA@uRn)9b3y`7L!I#N)ILb}nlQ}S|)7*W1 z=nuenjLd8&O7eCBI!*((mXlebM>SQg@aG`ghlcn zqgUXF`Mk~R;fpn;&_{AHs zi)Z1T3->*|Ew#p~PT=Ym8*$AE1_U+&tMnVKRn zs4_v5`n}?ANKAEXhqfdsxoO4aj1v5VzUhMBJ>f)>T!Do1(O%EWCvl%CsO()!6>Y`CQxb|xw@p-a$f)R4=>^~B`oO{*M@`BH-`mCw_pt( zusfTE?gBmWNm|zhx;Sp(^nxqlgc4|mD80edcWy`+bBn$+9==g*IqiwI*}OEGW{Dmw zwBzX(FcxW)zedgocDj2h;NLNJjSoj&Fx4mt@H2hgyyAls_qlek8~QWlbL-`|fQ7bW zE=Hs0(sVRG5feFOh9=jk*BaWADySP=GY-$EBD;!ztyHE-NEgdNTVRw4*yzzyR90IV z=*Vs@mbd9MzDl&F#4c43nYudNlQ_J%>r%##Ey|b*drn8uu$`&sR}mf*b5R6jSn$hA z=miq)dHo6d#|0w7Tv`tuoW_+Afy1F{!OA^2@S6B%Py4nscNh0kv8IK~6JkzIB)=-^ zTstbxzN3BQI@~oAitt2>PND1KbJIk8Kg&&&Iks3g>FZGpWqht;Ue4rfx-`XdWXRJC zE@Cos=2s${r=lMn1Dc*J#2)$-rqzEr1c$ADofN#hOV#N+MCa8|e>yW~zbkFDYjTRE zxR&!!(K=zId#W5s$mEHnXeUT+&yGuN_I)|`+HvM7{_Rz=CEI?`7U#H<>F&DO`8L~{iGP0Wf+cd#0xjPzPrAaLKGUb`_%kk^4{V;ml(R6z~(N9?SElKciYDK^;}r9%=eo(FeCqRcN^n znGz1dzwjUV!;&1Xug%qG?75R~f>c>`E2{#l?Ceo*#TMF5xJF{6_;u;i?CLVNuEVX{+jB*=aebd5&;Y!Yuhbaj9$h=IcS zuD0hA=WKam38Zsk;zFNIhZ9ihavd+<*0gnlf(%Zy^kRWwPj}UBE4x3fr6MQ}4Fg;t zS1nPVo#M8hCRk0cx1fT_c>5x8s0zCEPB2Pb@iRSiX#zH%U9`b(lb_Z#Jvd)Uk0tc^ zHU)?x-qO6g^9B2WN?W+nLc3W>ytA{qtaE{}wH!in9sm*pMumH5HuO4^_n90|MugDt zc;lf?gb6{*YquBZBfgu|pcWjFpV`5=l+P)Owsf!MSQuNg_deyOCcPVT*mk@SJ3wCy zDvV5n+iun-A12|8=?HuKGy-5H_d}}*E!mc1#TIn;fK{k#55);0DOwEe{Xb2k&M9$s z1Ap})MNF;doJt6Bn1j>2M#O0FQ-l%>f}0Gb*w0qJ_uzGx?1}Fm$6o-qZXNNL4aE(Vwv=&PveCTs^uDGva2}w`LKyJPPVeI4#v#>lWq-v!gXv@`zpRQB z84x-Kmbj77hb;5MVhMZO*B*gQ_QY@oihBYoud4{+gmK8(>!$XQrfFpn)<6iFKq1Jn$5 z3p(l=n3s1sed`z`I+Q{-DvppW0OR!V!H)Wr+|QdJPJ_90Qem_dLsZN*Ahdr0q;yr+ z%aeEUz2J5#zsz1_jg$WONiXI(#AzYKz@x$>%8Z5_FW2W)$;1kks(QWVi3#)%2WS$0}qm3*asqOt;cNbkYPn zQq$M{9bmP$R79=#dOeA0fQ|7=VqE6SF`5n+lk-{GC#DDRZatS?wvf_l1~D)nrrVx^ zfAht#YUCy2sgULZM>sB%iFjqX_u=AC$Tf<--;q`pGZHU*JdoU z++(<0K{ZQ~EanPYeX~o{s7#aWP_jEbE_|d2IhDW>XqyPHO?*l?AhK=d0%hlP&THNE0G}F+AJ&pinUx*BOten#XEmfprlF<62KZ}5iTEBac^+P& zJ?Z|;oKB}>mi`&_lO8HlWL#QB#=6X`{boA;-q7b!F~)=sOWgPYrlA@3`};jW_};pf zm(#G;n;pY_FA5f9PVecNYZkI%YH;6v+6{!cQOXylwlQ}~-9f>QEwd;y?O=RE!lnCp z@$C8NL9ZMUY$pxX^_fUa+`LB{uuib|!HA8nnYabUj&dSGAL=)sPt13m%P6ZHu=l+`6L*sLEJfv_drOm0eNHTOxEvpvY?I{iN&wzIgXcWpjUn7Ph_7=6aJ@9dOW1mU zMh#sE-f4biXAX*KHsDI4#gr0G|7Oe7#tlU=)^{ez>e@>kR15$X zW%mp^)3iE8J22vi*lo`@Y{4seir54=^;6>2E`<_sR0B`M4ctv{^BbIBvqKKGjbEC1 z9#4rACf-{*S*0efgjAYw+BFIKS(SK2pP?;T;3E~zGlNyLkYp^ypLKxtgVo&;t)=T) z(3x;hM~z3xVqx0eBA~#kHJUzW+4U8o@)CucyLkph?wb;ad;{rBfps|a&R_C*9#heW zUXlj*(s?L($B0C&A@@El*Po#f&9*zkGV z_dz~&IXDVxNl1T5QbAQzPSumrEn1zR=Qx!x%Ug*TmM;=J=$YhBd=mZ?+530}!9vG~ z>;xSC)Fof!Fn}hfRop(dHbYzQ9nIiRpbPm7eyub+b|IT1x!Sp@AE+$!5xWQNAqgV_ zF2kF&w7|*sz({6~+v|6W-Ik>4nm*9Ff>oO^w)ND}$_xj{O{oMOmU9ttYMNy~+!k&R zFWjz$`E-r@TEy5R`E}O$g`6%M!w(Vau7}_fTM6p%U&~rkhJzO)gf;kbcJ^W+u2^qq z8OG>OSb%mPXsGpA3y6-j6^Rj;;-T#3ywl_s$Ys8pz@JA;30x7wAQbs8L7IrOR0A}v z!U>~C#pyl-XQ1X$9jzM`3sL{u&ZwlTIoTF1bWyKW4o&yp-sfEMaTO8@YGiEkLkjB1 z#E5QQoJ!==PbF1)pm|3GHk-gsFF!m4G##?0eTN#4{gOWsUkDYlzJC`e>_UCb9(p`P zk;lML@2%eFKamFY$Iy~rxi`3;Xg5r#z5o^5sI+8NK8~jAuY~`|^ z$GjLKNPm6Q%A|gby3*^Xkkh;o8AB{ZhTF1z3q-Ay<}XTV_95#kQ4@5%cc|i}Ls?KC zX&$Ezc5Z!%;kTJoO8hue5#2+%>LGQhk_X0`;a&+)(2-t6FsbO1I?i}VWvX2%JVLRQ zjTzPgoQd+oklZi82BX|ZvO2O07vN!EL*i87x5}8Q+?U>b(NWw1z8=ym?;c9NUA?~q z)E#q}n670h9vXwMRq+K5hry(?Q5>I+z)S)efB{m*I@p-S9%yvb<2RN)gDZ&fjkqZQ zKC2jhN+a;aFb|ShqPnH6RWQDyJ*KkGUmB_ajZKho?wF?lQ^ZKZCkR@2`MeVcT{%=p z^J1m)SdFg6k`IezM83ATRB*d+jw3eV%v>JxEVPk{PG@{AGNjN<9WL6GoVZ`$AiwoF zAx&oNNE;i8v)1KZ6Taowm_+@_;8QQ(B9b-fYjyEr%|Oinji1$5YIc6|g-$m;%A0Cp zwGm%#PR>rU5VeSM-qe-Ht%tCe|daGUy_2kYjcbs4ib@$M*8uG^6D-QVK58FXh5^FXuS90TG+RuVkV(L!$*RhJm62U{Zj7mH z&@WAsf4t^BrW+twM_t9$i@Wzd_?tKJe(Qr5-^w{&g;!Zwa@=S7eeq07C*5OHP*}K5 zBJFgmT^Ab&l(65@nl)k1``}0!CWo`sH~Eo;Bt+o$fX>VQ9Lp!tA_Gy<_k=4Gdn}l= z{79hPnf4v4%kJ+eqj_K=`8b_A8-jP&p@)mTU*lUV$OGSPD3iX>3&OXh7aEc*dx0d&GgVt-! zuwTj43{V+(fJQP)Q>Bc^V))I*6nAT%;q24Oxuj;4zsFtWx7Ibua=0GRZf-~&5wf4) z9vxR_Z}6P%jQb{b&V6Y*u->yOQm&SU94R)+Nxw&n9*%rtOoTM%VT`BIj~^TK7AVAj zCxujF?|;`@=A!U(>K}v`8nnv?cA(d|DrKVi&cf|?>r-NM6~8%|Tf^>Ji?DWtw7E~E z*M!PHrq^DO$jG*#3V)*_e#K>ZROez58r`|gEzxi)v#D{Tb`_h?y_7HR9_te(t)1rA z<=4qt+^xQ8Yl)Y3y<)8nNUY7K00|i-zL38YmQIG^nphe$9SCRK^u)X4;~a2)H^#>c@-8pCF&{`Lo*jct zX4jsk8fRSU+R8mGW4K0jnUs&a`g18o+n2liR@Gb+t}_Wk&>+M2V@=P`FK?@@05!sf zc!)E38^5XIGxPfPBq=uf#@ZHg05&=R>_k?|t~=Zj_ifCI+m510&M2`Q(mq^UTfXl$ zG0tS2@dKb^z2Mi=sMT)hVwvXrhG7UUn#cS5D$B?mt3FT&7Y|f^5O9;i`>w_V?AV(x zpVCyiE=zq7+RFpkXF-hx2%dhtr?d+e?l4XqgWsv1tMR*iU(Lk108uG-mcCq!wSu#@EVIq*;AA78q(C#r-gQ6#(Ijd9kMPso?OS4W0t4-EJr7 zmpMN>(3E}yczJ_IW4Ye;38yZfdP$6R+QrBkiZ};EeMd_xo(gP==Z!O60 zdvud<+RjE#dR6p%@|J6h7%Rb9Hprm?5SLFcy2CS_xSw}wWCXRRRvyEc z=IcE>Js-n ziejPzIGSh{&Lk^kNb&ndYj)4Zvd4VI%O>Aciy9ic1kZVu=)ZZ%`sHI|b>>)Z*z2rS zO^v&7cgA^sa%EBek2bFzZ9>Mzd@X0=^Jbhhr7T5q7G8bJcq#sn^iz%$kJiH{fW^si zQyi%br}ZOU4V!1J<2~EK&zq_q7d_etZn1XsFvP0xm_6m&r3$xjziktGa9QQ1XO7F{4jglgTr1V6-IpjJcGfss$L)8E=GvDI^+_83}eV{O`6;otA#9L3-8#~~dR028p z&8b_RG^0cl2RLREq^dj%{>Uui8ox(|E@4o<=U8Wg$g*m_ydTk}tJIUs_^rHkBvk{P zeRTQ`$F7*mXo7J0wsi|Sm>!Y!QefDG+rt?un#VpqEb@tf{4;^ty(d8}UiO-OkI1F^ zsuq2G`7})k6+70jm+Kcr1RU0r;LnI2-YX*t%u9J@7HqF2odv5@H^lRPLZZ6>XHMhu zJAv{tT<77vIZ-D1bo}Q^_&Zboz}@OLhmN0%=lSu?XpQBHvxjRNbwWYk!OwNkM$@i{ z`-aN+nCE#}6%Y{;5C*%zC|EJ%w<$)%d*>^Cj*A#4Qz zv4bh&1{R_Y(5g{9{5B`>)d@?eO9QU@GU5RVVv12lppVe1+UCfLIJtrfh$e2IO9+e6 zl!ufOf7U$Zhk>=Ey0mHTe)iV+dfMLCkIRG}z( z@_e}Nv;bV{j2lgeM>`Y!Dsa0hHAwR))1!iGw>LHbkvK$=HqcU0Wjm(b7J8)QzoNyL zluv&;&ubQfdKCZ=e0`E>l_?yVOS?HTUD zSvbAmd&F%&!K1Z3kxFx=ro3m*hV2PMp!QQw>&}{aJ7o+l@*-J1k_-iTM|f~+XK86= zqA4kCiFil&cX(w;f2dvEE)nDJFp?lwFxU@=GD-9kJ28~)m4mCx$K1%~9pJ$enDcwJ za=b!?B-a_T=6YEE?V`!9`AJ4wN7T4 z2|wX0coZbzF9UgnW8eP{Gf9uu{D+MMz?O>MzaW##2#_;){ptv9 z;fy8Z5s7rDXHvVIKM>rX9Wdi6VhNDx%mO%e?@M!AzjzFL%S}J;#yj|=p#FwAeU6?x z8B55f(g#@@wV!2<{v^aCO-e*X+XlS@q z0jOqmdY}ayogtgzt|ZN-&0T7Qtn6zm@?a#{cx?42$#ca{+Pf7;^Rp_3W7ZFatr)en zsk?hFVidA@o!6@(_&zY)G8Z`bATDf^XH#=wzmfIuQEagZiYR!JLVo2dNy*PWcEU$h znFS5B)(DXw$G;~}&{{|YNyIG~yZJ>auoqC-*wGk|lzYY=Z+5_t)rXcc&!A^`hjVe+ zq`JaQoK6+~S;V;SfjdS{(YHLX`9a-)k_)aR@z6Bf(&d;Wft847egK#akOngJc1amNLZ(iHl=rrF3F@$@*rwU7Chy{y=nj54l zxDb4lRCnNBe0S0O0yoK#ip&&htA#Bm%Szw}D8X#He(k%cr%g_Mcc38xEZx|wN6$s0 zvy4q1R){#@D}DPJY({sf(9n4Z&uf0UF5vU6+|!z)%dBl$AzHA`=|{Jw!-N@QpoNX# zDIE=Xlk`HlZ(@HSLs>5C_gnK@N@r=2z2BV}D(ez!eoA1ig`Gs1ZN%Kk^SnEwGsw(7 zs5`$QWo^>%^OH{$lbH!?6+Q4%pyr{rHt)mc`4ue`Y?PkT>3E3da`??#Nuk4zo!0b; z`PNkv{Mg3Jr-LXh@5A2|H$?cft~z7S!ey^lZ$9o~%cIVz_9(xoGL!nqODjSyP6GSI z7PjN>yeH47Z9l0YGAOmb^wd4N^WuRWF~IJ&ag9(!4lptwBu`!m z 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 cKFq}0{yo59AHGo#`Xv}CF?rDf5q and its associated notebooks. + and its associated +notebooks. -## **Training** +## **Hardware and Software Requirements** -To train, prepare a folder that contains images sized at 512x512 and execute the -following: +You will need a GPU to perform training in a reasonable length of +time, and at least 12 GB of VRAM. We recommend using the [`xformers` +library](../installation/070_INSTALL_XFORMERS) to accelerate the +training process further. During training, about ~8 GB is temporarily +needed in order to store intermediate models, checkpoints and logs. -### WINDOWS +## **Preparing for Training** -As the default backend is not available on Windows, if you're using that -platform, set the environment variable `PL_TORCH_DISTRIBUTED_BACKEND` to `gloo` +To train, prepare a folder that contains 3-5 images that illustrate +the object or concept. It is good to provide a variety of examples or +poses to avoid overtraining the system. Format these images as PNG +(preferred) or JPG. You do not need to resize or crop the images in +advance, but for more control you may wish to do so. -```bash -python3 ./main.py -t \ - --base ./configs/stable-diffusion/v1-finetune.yaml \ - --actual_resume ./models/ldm/stable-diffusion-v1/model.ckpt \ - -n my_cat \ - --gpus 0 \ - --data_root D:/textual-inversion/my_cat \ - --init_word 'cat' +Place the training images in a directory on the machine InvokeAI runs +on. We recommend placing them in a subdirectory of the +`text-inversion-training-data` folder located in the InvokeAI root +directory, ordinarily `~/invokeai` (Linux/Mac), or +`C:\Users\your_name\invokeai` (Windows). For example, to create an +embedding for the "psychedelic" style, you'd place the training images +into the directory +`~invokeai/text-inversion-training-data/psychedelic`. + +## **Launching Training Using the Console Front End** + +InvokeAI 2.3 and higher comes with a text console-based training front +end. From within the `invoke.sh`/`invoke.bat` Invoke launcher script, +start the front end by selecting choice (3): + +```sh +Do you want to generate images using the +1. command-line +2. browser-based UI +3. textual inversion training +4. open the developer console +Please enter 1, 2, 3, or 4: [1] 3 ``` -During the training process, files will be created in -`/logs/[project][time][project]/` where you can see the process. +From the command line, with the InvokeAI virtual environment active, +you can launch the front end with the command +`textual_inversion_fe`. -Conditioning contains the training prompts inputs, reconstruction the input -images for the training epoch samples, samples scaled for a sample of the prompt -and one with the init word provided. +This will launch a text-based front end that will look like this: -On a RTX3090, the process for SD will take ~1h @1.6 iterations/sec. +

+![ti-frontend](../assets/textual-inversion/ti-frontend.png) +
-!!! note +The interface is keyboard-based. Move from field to field using +control-N (^N) to move to the next field and control-P (^P) to the +previous one. and work as well. Once a field is +active, use the cursor keys. In a checkbox group, use the up and down +cursor keys to move from choice to choice, and to select a +choice. In a scrollbar, use the left and right cursor keys to increase +and decrease the value of the scroll. In textfields, type the desired +values. - According to the associated paper, the optimal number of - images is 3-5. Your model may not converge if you use more images than - that. +The number of parameters may look intimidating, but in most cases the +predefined defaults work fine. The red circled fields in the above +illustration are the ones you will adjust most frequently. -Training will run indefinitely, but you may wish to stop it (with ctrl-c) before -the heat death of the universe, when you find a low loss epoch or around ~5000 -iterations. Note that you can set a fixed limit on the number of training steps -by decreasing the "max_steps" option in -configs/stable_diffusion/v1-finetune.yaml (currently set to 4000000) +### Model Name -## **Run the Model** +This will list all the diffusers models that are currently +installed. Select the one you wish to use as the basis for your +embedding. Be aware that if you use a SD-1.X-based model for your +training, you will only be able to use this embedding with other +SD-1.X-based models. Similarly, if you train on SD-2.X, you will only +be able to use the embeddings with models based on SD-2.X. -Once the model is trained, specify the trained .pt or .bin file when starting -invoke using +### Trigger Term -```bash -python3 ./scripts/invoke.py \ - --embedding_path /path/to/embedding.pt +This is the prompt term you will use to trigger the embedding. Type a +single word or phrase you wish to use as the trigger, example +"psychedelic" (without angle brackets). Within InvokeAI, you will then +be able to activate the trigger using the syntax ``. + +### Initializer + +This is a single character that is used internally during the training +process as a placeholder for the trigger term. It defaults to "*" and +can usually be left alone. + +### Resume from last saved checkpoint + +As training proceeds, textual inversion will write a series of +intermediate files that can be used to resume training from where it +was left off in the case of an interruption. This checkbox will be +automatically selected if you provide a previously used trigger term +and at least one checkpoint file is found on disk. + +Note that as of 20 January 2023, resume does not seem to be working +properly due to an issue with the upstream code. + +### Data Training Directory + +This is the location of the images to be used for training. When you +select a trigger term like "my-trigger", the frontend will prepopulate +this field with `~/invokeai/text-inversion-training-data/my-trigger`, +but you can change the path to wherever you want. + +### Output Destination Directory + +This is the location of the logs, checkpoint files, and embedding +files created during training. When you select a trigger term like +"my-trigger", the frontend will prepopulate this field with +`~/invokeai/text-inversion-output/my-trigger`, but you can change the +path to wherever you want. + +### Image resolution + +The images in the training directory will be automatically scaled to +the value you use here. For best results, you will want to use the +same default resolution of the underlying model (512 pixels for +SD-1.5, 768 for the larger version of SD-2.1). + +### Center crop images + +If this is selected, your images will be center cropped to make them +square before resizing them to the desired resolution. Center cropping +can indiscriminately cut off the top of subjects' heads for portrait +aspect images, so if you have images like this, you may wish to use a +photoeditor to manually crop them to a square aspect ratio. + +### Mixed precision + +Select the floating point precision for the embedding. "no" will +result in a full 32-bit precision, "fp16" will provide 16-bit +precision, and "bf16" will provide mixed precision (only available +when XFormers is used). + +### Max training steps + +How many steps the training will take before the model converges. Most +training sets will converge with 2000-3000 steps. + +### Batch size + +This adjusts how many training images are processed simultaneously in +each step. Higher values will cause the training process to run more +quickly, but use more memory. The default size will run with GPUs with +as little as 12 GB. + +### Learning rate + +The rate at which the system adjusts its internal weights during +training. Higher values risk overtraining (getting the same image each +time), and lower values will take more steps to train a good +model. The default of 0.0005 is conservative; you may wish to increase +it to 0.005 to speed up training. + +### Scale learning rate by number of GPUs, steps and batch size + +If this is selected (the default) the system will adjust the provided +learning rate to improve performance. + +### Use xformers acceleration + +This will activate XFormers memory-efficient attention. You need to +have XFormers installed for this to have an effect. + +### Learning rate scheduler + +This adjusts how the learning rate changes over the course of +training. The default "constant" means to use a constant learning rate +for the entire training session. The other values scale the learning +rate according to various formulas. + +Only "constant" is supported by the XFormers library. + +### Gradient accumulation steps + +This is a parameter that allows you to use bigger batch sizes than +your GPU's VRAM would ordinarily accommodate, at the cost of some +performance. + +### Warmup steps + +If "constant_with_warmup" is selected in the learning rate scheduler, +then this provides the number of warmup steps. Warmup steps have a +very low learning rate, and are one way of preventing early +overtraining. + +## The training run + +Start the training run by advancing to the OK button (bottom right) +and pressing . A series of progress messages will be displayed +as the training process proceeds. This may take an hour or two, +depending on settings and the speed of your system. Various log and +checkpoint files will be written into the output directory (ordinarily +`~/invokeai/text-inversion-output/my-model/`) + +At the end of successful training, the system will copy the file +`learned_embeds.bin` into the InvokeAI root directory's `embeddings` +directory, using a subdirectory named after the trigger token. For +example, if the trigger token was `psychedelic`, then look for the +embeddings file in +`~/invokeai/embeddings/psychedelic/learned_embeds.bin` + +You may now launch InvokeAI and try out a prompt that uses the trigger +term. For example `a plate of banana sushi in style`. + +## **Training with the Command-Line Script** + +InvokeAI also comes with a traditional command-line script for +launching textual inversion training. It is named +`textual_inversion`, and can be launched from within the +"developer's console", or from the command line after activating +InvokeAI's virtual environment. + +It accepts a large number of arguments, which can be summarized by +passing the `--help` argument: + +```sh +textual_inversion --help ``` -Then, to utilize your subject at the invoke prompt - -```bash -invoke> "a photo of *" +Typical usage is shown here: +```sh +python textual_inversion.py \ + --model=stable-diffusion-1.5 \ + --resolution=512 \ + --learnable_property=style \ + --initializer_token='*' \ + --placeholder_token='' \ + --train_data_dir=/home/lstein/invokeai/training-data/psychedelic \ + --output_dir=/home/lstein/invokeai/text-inversion-training/psychedelic \ + --scale_lr \ + --train_batch_size=8 \ + --gradient_accumulation_steps=4 \ + --max_train_steps=3000 \ + --learning_rate=0.0005 \ + --resume_from_checkpoint=latest \ + --lr_scheduler=constant \ + --mixed_precision=fp16 \ + --only_save_embeds ``` -This also works with image2image +## Reading -```bash -invoke> "waterfall and rainbow in the style of *" --init_img=./init-images/crude_drawing.png --strength=0.5 -s100 -n4 -``` +For more information on textual inversion, please see the following +resources: -For .pt files it's also possible to train multiple tokens (modify the -placeholder string in `configs/stable-diffusion/v1-finetune.yaml`) and combine -LDM checkpoints using: +* The [textual inversion repository](https://github.com/rinongal/textual_inversion) and + associated paper for details and limitations. +* [HuggingFace's textual inversion training + page](https://huggingface.co/docs/diffusers/training/text_inversion) -```bash -python3 ./scripts/merge_embeddings.py \ - --manager_ckpts /path/to/first/embedding.pt \ - [,[...]] \ - --output_path /path/to/output/embedding.pt -``` +--- -Credit goes to rinongal and the repository - -Please see [the repository](https://github.com/rinongal/textual_inversion) and -associated paper for details and limitations. +copyright (c) 2023, Lincoln Stein and the InvokeAI Development Team \ No newline at end of file diff --git a/scripts/configure_invokeai.py b/scripts/configure_invokeai.py index 9d17a73317..9030ed6b4c 100755 --- a/scripts/configure_invokeai.py +++ b/scripts/configure_invokeai.py @@ -746,7 +746,7 @@ def initialize_rootdir(root:str,yes_to_all:bool=False): safety_checker = '--nsfw_checker' if enable_safety_checker else '--no-nsfw_checker' - for name in ('models','configs','embeddings'): + for name in ('models','configs','embeddings','text-inversion-data','text-inversion-training-data'): os.makedirs(os.path.join(root,name), exist_ok=True) for src in (['configs']): dest = os.path.join(root,src) diff --git a/main.py b/scripts/orig_scripts/main.py similarity index 100% rename from main.py rename to scripts/orig_scripts/main.py diff --git a/scripts/textual_inversion.py b/scripts/textual_inversion.py index fb176a5eec..a7aae8f40f 100755 --- a/scripts/textual_inversion.py +++ b/scripts/textual_inversion.py @@ -1,11 +1,11 @@ #!/usr/bin/env python # Copyright 2023, Lincoln Stein @lstein -from ldm.invoke.globals import Globals, set_root +from ldm.invoke.globals import Globals, global_set_root from ldm.invoke.textual_inversion_training import parse_args, do_textual_inversion_training if __name__ == "__main__": args = parse_args() - set_root(args.root_dir or Globals.root) + global_set_root(args.root_dir or Globals.root) kwargs = vars(args) do_textual_inversion_training(**kwargs) diff --git a/scripts/textual_inversion_fe.py b/scripts/textual_inversion_fe.py index 0bde21c95c..cc260057c2 100755 --- a/scripts/textual_inversion_fe.py +++ b/scripts/textual_inversion_fe.py @@ -13,8 +13,8 @@ from pathlib import Path from typing import List import argparse -TRAINING_DATA = 'training-data' -TRAINING_DIR = 'text-inversion-training' +TRAINING_DATA = 'text-inversion-training-data' +TRAINING_DIR = 'text-inversion-output' CONF_FILE = 'preferences.conf' class textualInversionForm(npyscreen.FormMultiPageAction): @@ -219,7 +219,7 @@ class textualInversionForm(npyscreen.FormMultiPageAction): def get_model_names(self)->(List[str],int): conf = OmegaConf.load(os.path.join(Globals.root,'configs/models.yaml')) - model_names = sorted(list(conf.keys())) + model_names = [idx for idx in sorted(list(conf.keys())) if conf[idx].get('format',None)=='diffusers'] defaults = [idx for idx in range(len(model_names)) if 'default' in conf[model_names[idx]]] return (model_names,defaults[0]) From 4b8aebabfb0b18a4210f65a07339e35dbfe41ae7 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Fri, 20 Jan 2023 16:59:34 -0500 Subject: [PATCH 15/41] add diffusers repo as a reference for further reading --- docs/features/TEXTUAL_INVERSION.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/features/TEXTUAL_INVERSION.md b/docs/features/TEXTUAL_INVERSION.md index 2413659099..7d54ea971c 100644 --- a/docs/features/TEXTUAL_INVERSION.md +++ b/docs/features/TEXTUAL_INVERSION.md @@ -262,6 +262,10 @@ resources: associated paper for details and limitations. * [HuggingFace's textual inversion training page](https://huggingface.co/docs/diffusers/training/text_inversion) +* [HuggingFace example script + documentation](https://github.com/huggingface/diffusers/tree/main/examples/textual_inversion) + (Note that this script is similar to, but not identical, to + `textual_inversion`, but produces embed files that are completely compatible. --- From c3aa3d48a08a2054860f653820a7ee7f95e182bd Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Fri, 20 Jan 2023 17:13:32 -0500 Subject: [PATCH 16/41] ignore .DS_Store files when scanning Mac embeddings --- ldm/modules/textual_inversion_manager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ldm/modules/textual_inversion_manager.py b/ldm/modules/textual_inversion_manager.py index e2e9b25c27..2dbc1de2f5 100644 --- a/ldm/modules/textual_inversion_manager.py +++ b/ldm/modules/textual_inversion_manager.py @@ -50,6 +50,8 @@ class TextualInversionManager(): return [ti.trigger_string for ti in self.textual_inversions] def load_textual_inversion(self, ckpt_path, defer_injecting_tokens: bool=False): + if str(ckpt_path).endswith('.DS_Store'): + return try: scan_result = scan_file_path(ckpt_path) if scan_result.infected_files == 1: From a1c0818a081bab8aace84c0ee7ecbcf27bf2ea46 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Fri, 20 Jan 2023 17:13:32 -0500 Subject: [PATCH 17/41] ignore .DS_Store files when scanning Mac embeddings --- ldm/modules/textual_inversion_manager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ldm/modules/textual_inversion_manager.py b/ldm/modules/textual_inversion_manager.py index dcb77d6098..cf28cf8c7a 100644 --- a/ldm/modules/textual_inversion_manager.py +++ b/ldm/modules/textual_inversion_manager.py @@ -54,6 +54,8 @@ class TextualInversionManager(): return [ti.trigger_string for ti in self.textual_inversions] def load_textual_inversion(self, ckpt_path, defer_injecting_tokens: bool=False): + if str(ckpt_path).endswith('.DS_Store'): + return try: scan_result = scan_file_path(ckpt_path) if scan_result.infected_files == 1: From 5afb63e41b3e0b2cb0ac2c67d7ba4fbb532fe762 Mon Sep 17 00:00:00 2001 From: mauwii Date: Tue, 17 Jan 2023 03:00:02 +0100 Subject: [PATCH 18/41] replace legacy setup.py with pyproject.toml other changes which where required: - move configure_invokeai.py into ldm.invoke - update files which imported configure_invokeai to use new location: - ldm/invoke/CLI.py - scripts/load_models.py - scripts/preload_models.py - update test-invoke-pip.yml: - remove pr type "converted_to_draft" - remove reference to dev/diffusers - remove no more needed requirements from matrix - add pytorch to matrix - install via `pip3 install --use-pep517 .` - use the created executables - this should also fix configure_invoke not executed in windows To install use `pip install --use-pep517 -e .` where `-e` is optional --- .github/workflows/test-invoke-pip.yml | 60 ++++---- ldm/invoke/CLI.py | 18 +-- {scripts => ldm/invoke}/configure_invokeai.py | 0 pyproject.toml | 130 ++++++++++++++++++ scripts/load_models.py | 2 +- scripts/preload_models.py | 2 +- setup.py | 99 ------------- 7 files changed, 169 insertions(+), 142 deletions(-) rename {scripts => ldm/invoke}/configure_invokeai.py (100%) create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/.github/workflows/test-invoke-pip.yml b/.github/workflows/test-invoke-pip.yml index 4985202782..1b197283d4 100644 --- a/.github/workflows/test-invoke-pip.yml +++ b/.github/workflows/test-invoke-pip.yml @@ -8,47 +8,49 @@ on: - 'ready_for_review' - 'opened' - 'synchronize' - - 'converted_to_draft' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: - # fail_if_pull_request_is_draft: - # if: github.event.pull_request.draft == true && github.head_ref != 'dev/diffusers' - # runs-on: ubuntu-18.04 - # steps: - # - name: Fails in order to indicate that pull request needs to be marked as ready to review and unit tests workflow needs to pass. - # run: exit 1 matrix: - if: github.event.pull_request.draft == false || github.head_ref == 'dev/diffusers' + if: github.event.pull_request.draft == false strategy: matrix: stable-diffusion-model: - stable-diffusion-1.5 - requirements-file: - - requirements-lin-cuda.txt - - requirements-lin-amd.txt - - requirements-mac-mps-cpu.txt - - requirements-win-colab-cuda.txt python-version: # - '3.9' - '3.10' + pytorch: + - linux-cuda-11_7 + - linux-rocm-5_2 + # - linux-cpu + - macos-default + # - windows-cpu + - windows-cuda-11_7 include: - - requirements-file: requirements-lin-cuda.txt + - pytorch: linux-cuda-11_7 os: ubuntu-22.04 github-env: $GITHUB_ENV - - requirements-file: requirements-lin-amd.txt + - pytorch: linux-rocm-5_2 os: ubuntu-22.04 + extra-index-url: 'https://download.pytorch.org/whl/rocm5.2' github-env: $GITHUB_ENV - - requirements-file: requirements-mac-mps-cpu.txt + # - pytorch: linux-cpu + # os: ubuntu-22.04 + # extra-index-url: 'https://download.pytorch.org/whl/cpu' + - pytorch: macos os: macOS-12 github-env: $GITHUB_ENV - - requirements-file: requirements-win-colab-cuda.txt + # - pytorch: windows-cpu + # os: windows-2022 + - pytorch: windows-cuda-11_7 os: windows-2022 + extra-index-url: 'https://download.pytorch.org/whl/cu117' github-env: $env:GITHUB_ENV - name: ${{ matrix.requirements-file }} on ${{ matrix.python-version }} + name: ${{ matrix.pytorch }} on ${{ matrix.python-version }} runs-on: ${{ matrix.os }} env: INVOKE_MODEL_RECONFIGURE: '--yes' @@ -96,28 +98,22 @@ jobs: if: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/development' }} run: echo "TEST_PROMPTS=tests/validate_pr_prompt.txt" >> ${{ matrix.github-env }} - - name: create requirements.txt - run: cp 'environments-and-requirements/${{ matrix.requirements-file }}' '${{ matrix.requirements-file }}' - - name: setup python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - # cache: 'pip' - # cache-dependency-path: ${{ matrix.requirements-file }} - - name: install dependencies - run: pip3 install --upgrade pip setuptools wheel + - name: install invokeai + run: pip3 install --use-pep517 . + env: + PIP_EXTRA_INDEX_URL: ${{ matrix.extra-index-url }} - - name: install requirements - run: pip3 install -r '${{ matrix.requirements-file }}' - - - name: run configure_invokeai.py + - name: run configure_invokeai id: run-preload-models env: HUGGING_FACE_HUB_TOKEN: ${{ secrets.HUGGINGFACE_TOKEN }} run: > - configure_invokeai.py + configure_invokeai --yes --default_only --full-precision # can't use fp16 weights without a GPU @@ -131,7 +127,7 @@ jobs: HF_DATASETS_OFFLINE: 1 TRANSFORMERS_OFFLINE: 1 run: > - python3 scripts/invoke.py + invoke --no-patchmatch --no-nsfw_checker --model ${{ matrix.stable-diffusion-model }} @@ -144,5 +140,5 @@ jobs: if: matrix.os != 'windows-2022' uses: actions/upload-artifact@v3 with: - name: results_${{ matrix.requirements-file }}_${{ matrix.python-version }} + name: results_${{ matrix.pytorch }}_${{ matrix.python-version }} path: ${{ env.INVOKEAI_ROOT }}/outputs diff --git a/ldm/invoke/CLI.py b/ldm/invoke/CLI.py index ef6389c7cc..3bd8866d97 100644 --- a/ldm/invoke/CLI.py +++ b/ldm/invoke/CLI.py @@ -485,7 +485,7 @@ def do_command(command:str, gen, opt:Args, completer) -> tuple: optimize_model(path[1], gen, opt, completer) completer.add_history(command) operation = None - + elif command.startswith('!optimize'): path = shlex.split(command) @@ -570,11 +570,11 @@ def import_model(model_path:str, gen, opt, completer): (3) a huggingface repository id ''' model_name = None - + if model_path.startswith(('http:','https:','ftp:')): model_name = import_ckpt_model(model_path, gen, opt, completer) elif os.path.exists(model_path) and model_path.endswith('.ckpt') and os.path.isfile(model_path): - model_name = import_ckpt_model(model_path, gen, opt, completer) + model_name = import_ckpt_model(model_path, gen, opt, completer) elif re.match('^[\w.+-]+/[\w.+-]+$',model_path): model_name = import_diffuser_model(model_path, gen, opt, completer) elif os.path.isdir(model_path): @@ -584,12 +584,12 @@ def import_model(model_path:str, gen, opt, completer): if not model_name: return - + if not _verify_load(model_name, gen): print('** model failed to load. Discarding configuration entry') gen.model_manager.del_model(model_name) return - + if input('Make this the default model? [n] ') in ('y','Y'): gen.model_manager.set_default_model(model_name) @@ -690,7 +690,7 @@ def optimize_model(model_name_or_path:str, gen, opt, completer): else: print(f'** {model_name_or_path} is neither an existing model nor the path to a .ckpt file') return - + if not ckpt_path.is_absolute(): ckpt_path = Path(Globals.root,ckpt_path) @@ -698,7 +698,7 @@ def optimize_model(model_name_or_path:str, gen, opt, completer): if diffuser_path.exists(): print(f'** {model_name_or_path} is already optimized. Will not overwrite. If this is an error, please remove the directory {diffuser_path} and try again.') return - + new_config = gen.model_manager.convert_and_import( ckpt_path, diffuser_path, @@ -747,7 +747,7 @@ def edit_model(model_name:str, gen, opt, completer): continue completer.set_line(info[attribute]) info[attribute] = input(f'{attribute}: ') or info[attribute] - + if new_name != model_name: manager.del_model(model_name) @@ -1099,7 +1099,7 @@ def report_model_error(opt:Namespace, e:Exception): if yes_to_all is not None: sys.argv.append(yes_to_all) - import configure_invokeai + import ldm.invoke.configure_invokeai as configure_invokeai configure_invokeai.main() print('** InvokeAI will now restart') sys.argv = previous_args diff --git a/scripts/configure_invokeai.py b/ldm/invoke/configure_invokeai.py similarity index 100% rename from scripts/configure_invokeai.py rename to ldm/invoke/configure_invokeai.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..5ae6a9b5ea --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,130 @@ +[build-system] +requires = ["setuptools~=65.5.0", "pip~=22.3.1", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "InvokeAI" +description = "An implementation of Stable Diffusion which provides various new features and options to aid the image generation process" +requires-python = ">=3.9, <3.11" +readme = { content-type = "text/markdown", file = "README.md" } +keywords = ["stable-diffusion", "AI"] +dynamic = ["version"] +license = { file = "LICENSE" } +authors = [{ name = "The InvokeAI Project", email = "lincoln.stein@gmail.com" }] +classifiers = [ + 'Development Status :: 4 - Beta', + 'Environment :: GPU', + 'Environment :: GPU :: NVIDIA CUDA', + 'Environment :: MacOS X', + 'Intended Audience :: End Users/Desktop', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Operating System :: POSIX :: Linux', + 'Operating System :: MacOS', + 'Operating System :: Microsoft :: Windows', + 'Programming Language :: Python :: 3 :: Only', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Topic :: Artistic Software', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Server', + 'Topic :: Multimedia :: Graphics', + 'Topic :: Scientific/Engineering :: Artificial Intelligence', + 'Topic :: Scientific/Engineering :: Image Processing', +] +dependencies = [ + "accelerate", + "albumentations", + "clip_anytorch", # replaceing "clip @ https://github.com/openai/CLIP/archive/eaa22acb90a5876642d0507623e859909230a52d.zip", + "clipseg @ https://github.com/invoke-ai/clipseg/archive/relaxed-python-requirement.zip", # is this still necesarry with diffusers? + "datasets", + "diffusers[torch]~=0.11", + "dnspython==2.2.1", + "einops", + "eventlet", + "facexlib", + "flask==2.1.3", + "flask_cors==3.0.10", + "flask_socketio==5.3.0", + "flaskwebgui==1.0.3", + "getpass_asterisk", + "gfpgan==1.3.8", + "huggingface-hub>=0.11.1", + "imageio", + "imageio-ffmpeg", + "k-diffusion", # replaceing "k-diffusion @ https://github.com/Birch-san/k-diffusion/archive/refs/heads/mps.zip", + "kornia", + "npyscreen", + "numpy<1.24,>=1.23", + "omegaconf", + "opencv-python", + "picklescan", + "pillow", + "pudb", + "pypatchmatch", + "pyreadline3", + "pytorch-lightning==1.7.7", + "realesrgan", + "requests==2.25.1", + "safetensors", + "scikit-image>=0.19", + "send2trash", + "streamlit", + "taming-transformers-rom1504", + "test-tube>=0.7.5", + "torch>=1.13.1", + "torch-fidelity", + "torchvision>=0.14.1", + "torchmetrics", + "transformers~=4.25", + "windows-curses; sys_platform=='win32'", +] + +[project.optional-dependencies] +"dist" = ["pip-tools", "pipdeptree", "twine"] +"docs" = [ + "mkdocs-material<9.0", + "mkdocs-git-revision-date-localized-plugin", + "mkdocs-redirects==1.2.0", +] +test = ["pytest>6.0.0", "pytest-cov"] + +[project.scripts] +"configure_invokeai" = "ldm.invoke.configure_invokeai:main" +"dream" = "ldm.invoke:CLI.main" +"invoke" = "ldm.invoke:CLI.main" +"legacy_api" = "scripts:legacy_api.main" +"load_models" = "scripts:configure_invokeai.main" +"merge_embeddings" = "scripts:merge_embeddings.main" +"preload_models" = "ldm.invoke.configure_invokeai:main" + +[project.urls] +"Homepage" = "https://invoke-ai.github.io/InvokeAI/" +"Documentation" = "https://invoke-ai.github.io/InvokeAI/" +"Source" = "https://github.com/invoke-ai/InvokeAI/" +"Bug Reports" = "https://github.com/invoke-ai/InvokeAI/issues" +"Discord" = "https://discord.gg/ZmtBAhwWhy" + +[tool.setuptools.dynamic] +version = { attr = "ldm.invoke.__version__" } + +[tool.setuptools.packages.find] +"where" = ["."] +"include" = ["assets", "backend*", "configs*", "frontend.dist*", "ldm*"] + +[tool.setuptools.package-data] +"assets" = ["caution.png"] +"backend" = ["**.png"] +"configs" = ["*.example", "**/*.yaml", "*.txt"] +"frontend.dist" = ["**"] + +[tool.setuptools.exclude-package-data] +configs = ["models.yaml"] + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "-p pytest_cov --junitxml=junit/test-results.xml --cov=./ldm/ --cov=./backend --cov-branch" +python_files = ["test_*.py"] +pythonpath = [".venv/lib/python3.9", ".venv/lib/python3.10"] +testpaths = ["tests"] diff --git a/scripts/load_models.py b/scripts/load_models.py index e64d0b821d..89ae5d7588 100644 --- a/scripts/load_models.py +++ b/scripts/load_models.py @@ -5,7 +5,7 @@ # two machines must share a common .cache directory. import warnings -import configure_invokeai +import ldm.invoke.configure_invokeai as configure_invokeai if __name__ == '__main__': configure_invokeai.main() diff --git a/scripts/preload_models.py b/scripts/preload_models.py index e64d0b821d..89ae5d7588 100755 --- a/scripts/preload_models.py +++ b/scripts/preload_models.py @@ -5,7 +5,7 @@ # two machines must share a common .cache directory. import warnings -import configure_invokeai +import ldm.invoke.configure_invokeai as configure_invokeai if __name__ == '__main__': configure_invokeai.main() diff --git a/setup.py b/setup.py deleted file mode 100644 index afec71b967..0000000000 --- a/setup.py +++ /dev/null @@ -1,99 +0,0 @@ -import sys -import os -import re -from setuptools import setup, find_packages - -def list_files(directory): - listing = list() - for root, dirs, files in os.walk(directory,topdown=False): - pair = (root,[os.path.join(root,f) for f in files]) - listing.append(pair) - return listing - - -def get_version()->str: - from ldm.invoke import __version__ as version - return version - -# The canonical version number is stored in the file ldm/invoke/_version.py -VERSION = get_version() -DESCRIPTION = ('An implementation of Stable Diffusion which provides various new features' - ' and options to aid the image generation process') -LONG_DESCRIPTION = ('This version of Stable Diffusion features a slick WebGUI, an' - ' interactive command-line script that combines text2img and img2img' - ' functionality in a "dream bot" style interface, and multiple features' - ' and other enhancements.') -HOMEPAGE = 'https://github.com/invoke-ai/InvokeAI' -FRONTEND_FILES = list_files('frontend/dist') -FRONTEND_FILES.append(('assets',['assets/caution.png'])) -print(FRONTEND_FILES) - -REQUIREMENTS=[ - 'accelerate', - 'albumentations', - 'diffusers', - 'eventlet', - 'flask_cors', - 'flask_socketio', - 'flaskwebgui', - 'getpass_asterisk', - 'imageio-ffmpeg', - 'pyreadline3', - 'realesrgan', - 'send2trash', - 'streamlit', - 'taming-transformers-rom1504', - 'test-tube', - 'torch-fidelity', - 'torch', - 'torchvision', - 'transformers', - 'picklescan', - 'clip', - 'clipseg', - 'gfpgan', - 'k-diffusion', - 'pypatchmatch', -] - -setup( - name='InvokeAI', - version=VERSION, - description=DESCRIPTION, - long_description=LONG_DESCRIPTION, - author='The InvokeAI Project', - author_email='lincoln.stein@gmail.com', - url=HOMEPAGE, - license='MIT', - packages=find_packages(exclude=['tests.*']), - install_requires=REQUIREMENTS, - dependency_links=['https://download.pytorch.org/whl/torch_stable.html'], - python_requires='>=3.9, <4', - classifiers=[ - 'Development Status :: 4 - Beta', - 'Environment :: GPU', - 'Environment :: GPU :: NVIDIA CUDA', - 'Environment :: MacOS X', - 'Intended Audience :: End Users/Desktop', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Operating System :: POSIX :: Linux', - 'Operating System :: MacOS', - 'Operating System :: Microsoft :: Windows', - 'Programming Language :: Python :: 3 :: Only,' - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Topic :: Artistic Software', - 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', - 'Topic :: Internet :: WWW/HTTP :: WSGI :: Server', - 'Topic :: Multimedia :: Graphics', - 'Topic :: Scientific/Engineering :: Artificial Intelligence', - 'Topic :: Scientific/Engineering :: Image Processing', - ], - scripts = ['scripts/invoke.py','scripts/configure_invokeai.py', 'scripts/sd-metadata.py', - 'scripts/preload_models.py', 'scripts/images2prompt.py','scripts/merge_embeddings.py', - 'scripts/textual_inversion_fe.py','scripts/textual_inversion.py' - ], - data_files=FRONTEND_FILES, -) From 5a788f8f736e223f42bc4045b490539deafd2f68 Mon Sep 17 00:00:00 2001 From: mauwii Date: Tue, 17 Jan 2023 03:10:34 +0100 Subject: [PATCH 19/41] fix test-invoke-pip.yml matrix --- .github/workflows/test-invoke-pip.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-invoke-pip.yml b/.github/workflows/test-invoke-pip.yml index 1b197283d4..451effeba0 100644 --- a/.github/workflows/test-invoke-pip.yml +++ b/.github/workflows/test-invoke-pip.yml @@ -41,7 +41,7 @@ jobs: # - pytorch: linux-cpu # os: ubuntu-22.04 # extra-index-url: 'https://download.pytorch.org/whl/cpu' - - pytorch: macos + - pytorch: macos-default os: macOS-12 github-env: $GITHUB_ENV # - pytorch: windows-cpu From 963b666844cb27548a14dbcc97481dfd50758a9c Mon Sep 17 00:00:00 2001 From: mauwii Date: Tue, 17 Jan 2023 06:14:54 +0100 Subject: [PATCH 20/41] fix memory issue on windows runner - use cpu version which is only 162.6 MB - set `INVOKEAI_ROOT=C:\InvokeAI` on Windows runners --- .github/workflows/test-invoke-pip.yml | 36 ++++++++++++--------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/.github/workflows/test-invoke-pip.yml b/.github/workflows/test-invoke-pip.yml index 451effeba0..0feecb6eb1 100644 --- a/.github/workflows/test-invoke-pip.yml +++ b/.github/workflows/test-invoke-pip.yml @@ -3,6 +3,7 @@ on: push: branches: - 'main' + - 'debug/dev/pyproject-toml' pull_request: types: - 'ready_for_review' @@ -28,8 +29,8 @@ jobs: - linux-rocm-5_2 # - linux-cpu - macos-default - # - windows-cpu - - windows-cuda-11_7 + - windows-cpu + # - windows-cuda-11_7 include: - pytorch: linux-cuda-11_7 os: ubuntu-22.04 @@ -41,38 +42,34 @@ jobs: # - pytorch: linux-cpu # os: ubuntu-22.04 # extra-index-url: 'https://download.pytorch.org/whl/cpu' + # github-env: $GITHUB_ENV - pytorch: macos-default os: macOS-12 github-env: $GITHUB_ENV - # - pytorch: windows-cpu - # os: windows-2022 - - pytorch: windows-cuda-11_7 + - pytorch: windows-cpu os: windows-2022 - extra-index-url: 'https://download.pytorch.org/whl/cu117' github-env: $env:GITHUB_ENV + # - pytorch: windows-cuda-11_7 + # os: windows-2022 + # extra-index-url: 'https://download.pytorch.org/whl/cu117' + # github-env: $env:GITHUB_ENV name: ${{ matrix.pytorch }} on ${{ matrix.python-version }} runs-on: ${{ matrix.os }} env: + INVOKEAI_ROOT: ${{ github.workspace }} INVOKE_MODEL_RECONFIGURE: '--yes' - INVOKEAI_ROOT: '${{ github.workspace }}/invokeai' PYTHONUNBUFFERED: 1 - HAVE_SECRETS: ${{ secrets.HUGGINGFACE_TOKEN != '' }} steps: - name: Checkout sources id: checkout-sources uses: actions/checkout@v3 - name: set INVOKEAI_ROOT Windows - if: matrix.os == 'windows-2022' + if: runner.os == 'Windows' run: | - echo "INVOKEAI_ROOT=${{ github.workspace }}\invokeai" >> ${{ matrix.github-env }} - echo "INVOKEAI_OUTDIR=${{ github.workspace }}\invokeai\outputs" >> ${{ matrix.github-env }} - - - name: set INVOKEAI_ROOT others - if: matrix.os != 'windows-2022' - run: | - echo "INVOKEAI_ROOT=${{ github.workspace }}/invokeai" >> ${{ matrix.github-env }} - echo "INVOKEAI_OUTDIR=${{ github.workspace }}/invokeai/outputs" >> ${{ matrix.github-env }} + Get-PSDrive + mkdir C:\InvokeAI + echo "INVOKEAI_ROOT=C:\InvokeAI" >> ${{ matrix.github-env }} - name: Use Cached diffusers-1.5 id: cache-sd-model @@ -120,7 +117,7 @@ jobs: - name: Run the tests id: run-tests - if: matrix.os != 'windows-2022' + if: runner.os != 'Windows' env: # Set offline mode to make sure configure preloaded successfully. HF_HUB_OFFLINE: 1 @@ -133,11 +130,10 @@ jobs: --model ${{ matrix.stable-diffusion-model }} --from_file ${{ env.TEST_PROMPTS }} --root="${{ env.INVOKEAI_ROOT }}" - --outdir="${{ env.INVOKEAI_OUTDIR }}" - name: Archive results id: archive-results - if: matrix.os != 'windows-2022' + if: runner.os != 'Windows' uses: actions/upload-artifact@v3 with: name: results_${{ matrix.pytorch }}_${{ matrix.python-version }} From ef4b03289a5deca7d0a66627fb248b5f312d8aea Mon Sep 17 00:00:00 2001 From: mauwii Date: Tue, 17 Jan 2023 07:05:33 +0100 Subject: [PATCH 21/41] enable image generating step for windows as well - also remove left over debug lines and development branch leftover --- .github/workflows/test-invoke-pip.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/test-invoke-pip.yml b/.github/workflows/test-invoke-pip.yml index 0feecb6eb1..169bb96928 100644 --- a/.github/workflows/test-invoke-pip.yml +++ b/.github/workflows/test-invoke-pip.yml @@ -3,7 +3,6 @@ on: push: branches: - 'main' - - 'debug/dev/pyproject-toml' pull_request: types: - 'ready_for_review' @@ -67,7 +66,6 @@ jobs: - name: set INVOKEAI_ROOT Windows if: runner.os == 'Windows' run: | - Get-PSDrive mkdir C:\InvokeAI echo "INVOKEAI_ROOT=C:\InvokeAI" >> ${{ matrix.github-env }} @@ -87,12 +85,8 @@ jobs: if: ${{ github.ref == 'refs/heads/main' }} run: echo "TEST_PROMPTS=tests/preflight_prompts.txt" >> ${{ matrix.github-env }} - - name: set test prompt to development branch validation - if: ${{ github.ref == 'refs/heads/development' }} - run: echo "TEST_PROMPTS=tests/dev_prompts.txt" >> ${{ matrix.github-env }} - - name: set test prompt to Pull Request validation - if: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/development' }} + if: ${{ github.ref != 'refs/heads/main' }} run: echo "TEST_PROMPTS=tests/validate_pr_prompt.txt" >> ${{ matrix.github-env }} - name: setup python @@ -117,7 +111,6 @@ jobs: - name: Run the tests id: run-tests - if: runner.os != 'Windows' env: # Set offline mode to make sure configure preloaded successfully. HF_HUB_OFFLINE: 1 @@ -133,7 +126,6 @@ jobs: - name: Archive results id: archive-results - if: runner.os != 'Windows' uses: actions/upload-artifact@v3 with: name: results_${{ matrix.pytorch }}_${{ matrix.python-version }} From 6a3a0af6761c00400e20ebb0a9942c7b4c8d6bdf Mon Sep 17 00:00:00 2001 From: mauwii Date: Thu, 19 Jan 2023 00:50:53 +0100 Subject: [PATCH 22/41] update test-invoke-pip.yml - remove stable-diffusion-model from matrix - add windows-cuda-11_6 and linux-cuda-11_6 - enable linux-cpu - disable windows-cpu - change step order - remove job env - set runner.os specific env - install editable - cache models folder - remove `--model` and `--root` arguments from invoke command --- .github/workflows/test-invoke-pip.yml | 93 ++++++++++++++------------- 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/.github/workflows/test-invoke-pip.yml b/.github/workflows/test-invoke-pip.yml index 169bb96928..a1a217fd84 100644 --- a/.github/workflows/test-invoke-pip.yml +++ b/.github/workflows/test-invoke-pip.yml @@ -18,19 +18,23 @@ jobs: if: github.event.pull_request.draft == false strategy: matrix: - stable-diffusion-model: - - stable-diffusion-1.5 python-version: # - '3.9' - '3.10' pytorch: + - linux-cuda-11_6 - linux-cuda-11_7 - linux-rocm-5_2 - # - linux-cpu + - linux-cpu - macos-default - - windows-cpu + # - windows-cpu + - windows-cuda-11_6 # - windows-cuda-11_7 include: + - pytorch: linux-cuda-11_6 + os: ubuntu-22.04 + extra-index-url: 'https://download.pytorch.org/whl/cu116' + github-env: $GITHUB_ENV - pytorch: linux-cuda-11_7 os: ubuntu-22.04 github-env: $GITHUB_ENV @@ -38,15 +42,19 @@ jobs: os: ubuntu-22.04 extra-index-url: 'https://download.pytorch.org/whl/rocm5.2' github-env: $GITHUB_ENV - # - pytorch: linux-cpu - # os: ubuntu-22.04 - # extra-index-url: 'https://download.pytorch.org/whl/cpu' - # github-env: $GITHUB_ENV + - pytorch: linux-cpu + os: ubuntu-22.04 + extra-index-url: 'https://download.pytorch.org/whl/cpu' + github-env: $GITHUB_ENV - pytorch: macos-default os: macOS-12 github-env: $GITHUB_ENV - - pytorch: windows-cpu + # - pytorch: windows-cpu + # os: windows-2022 + # github-env: $env:GITHUB_ENV + - pytorch: windows-cuda-11_6 os: windows-2022 + extra-index-url: 'https://download.pytorch.org/whl/cu116' github-env: $env:GITHUB_ENV # - pytorch: windows-cuda-11_7 # os: windows-2022 @@ -54,32 +62,27 @@ jobs: # github-env: $env:GITHUB_ENV name: ${{ matrix.pytorch }} on ${{ matrix.python-version }} runs-on: ${{ matrix.os }} - env: - INVOKEAI_ROOT: ${{ github.workspace }} - INVOKE_MODEL_RECONFIGURE: '--yes' - PYTHONUNBUFFERED: 1 steps: - name: Checkout sources id: checkout-sources uses: actions/checkout@v3 - - name: set INVOKEAI_ROOT Windows - if: runner.os == 'Windows' - run: | - mkdir C:\InvokeAI - echo "INVOKEAI_ROOT=C:\InvokeAI" >> ${{ matrix.github-env }} - - - name: Use Cached diffusers-1.5 - id: cache-sd-model - uses: actions/cache@v3 - env: - cache-name: huggingface-${{ matrix.stable-diffusion-model }} + - name: setup python + uses: actions/setup-python@v4 with: - path: | - ${{ env.INVOKEAI_ROOT }}/models/runwayml - ${{ env.INVOKEAI_ROOT }}/models/stabilityai - ${{ env.INVOKEAI_ROOT }}/models/CompVis - key: ${{ env.cache-name }} + python-version: ${{ matrix.python-version }} + + - name: Set Cache-Directory Windows + if: runner.os == 'Windows' + id: set-cache-dir-windows + run: | + echo "CACHE_DIR=$HOME\invokeai\models" >> ${{ matrix.github-env }} + echo "PIP_NO_CACHE_DIR=1" >> ${{ matrix.github-env }} + + - name: Set Cache-Directory others + if: runner.os != 'Windows' + id: set-cache-dir-others + run: echo "CACHE_DIR=$HOME/invokeai/models" >> ${{ matrix.github-env }} - name: set test prompt to main branch validation if: ${{ github.ref == 'refs/heads/main' }} @@ -89,16 +92,21 @@ jobs: if: ${{ github.ref != 'refs/heads/main' }} run: echo "TEST_PROMPTS=tests/validate_pr_prompt.txt" >> ${{ matrix.github-env }} - - name: setup python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: install invokeai - run: pip3 install --use-pep517 . + run: pip3 install --use-pep517 -e . env: PIP_EXTRA_INDEX_URL: ${{ matrix.extra-index-url }} + - name: Use Cached models + id: cache-sd-model + uses: actions/cache@v3 + env: + cache-name: huggingface-models + with: + path: ${{ env.CACHE_DIR }} + key: ${{ env.cache-name }} + enableCrossOsArchive: true + - name: run configure_invokeai id: run-preload-models env: @@ -107,22 +115,21 @@ jobs: configure_invokeai --yes --default_only - --full-precision # can't use fp16 weights without a GPU + --full-precision + # can't use fp16 weights without a GPU - name: Run the tests id: run-tests - env: - # Set offline mode to make sure configure preloaded successfully. - HF_HUB_OFFLINE: 1 - HF_DATASETS_OFFLINE: 1 - TRANSFORMERS_OFFLINE: 1 + # env: + # # Set offline mode to make sure configure preloaded successfully. + # HF_HUB_OFFLINE: 1 + # HF_DATASETS_OFFLINE: 1 + # TRANSFORMERS_OFFLINE: 1 run: > invoke --no-patchmatch --no-nsfw_checker - --model ${{ matrix.stable-diffusion-model }} --from_file ${{ env.TEST_PROMPTS }} - --root="${{ env.INVOKEAI_ROOT }}" - name: Archive results id: archive-results From ec2a509e010cdd51c0539e47a2528209981a8d12 Mon Sep 17 00:00:00 2001 From: mauwii Date: Thu, 19 Jan 2023 03:11:40 +0100 Subject: [PATCH 23/41] make images in README.md compatible to pypi also add missing new-lines before/after headings --- README.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 07501a4242..765bed663a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
-![project logo](docs/assets/invoke_ai_banner.png) +![project logo](https://github.com/mauwii/InvokeAI/raw/main/docs/assets/invoke_ai_banner.png) # InvokeAI: A Stable Diffusion Toolkit @@ -28,6 +28,7 @@ [latest commit to main link]: https://github.com/invoke-ai/InvokeAI/commits/main [latest release badge]: https://flat.badgen.net/github/release/invoke-ai/InvokeAI/development?icon=github [latest release link]: https://github.com/invoke-ai/InvokeAI/releases +
InvokeAI is a leading creative engine built to empower professionals and enthusiasts alike. Generate and create stunning visual media using the latest AI-driven technologies. InvokeAI offers an industry leading Web Interface, interactive Command Line Interface, and also serves as the foundation for multiple commercial products. @@ -38,8 +39,11 @@ _Note: InvokeAI is rapidly evolving. Please use the [Issues](https://github.com/invoke-ai/InvokeAI/issues) tab to report bugs and make feature requests. Be sure to use the provided templates. They will help us diagnose issues faster._ +
-![canvas preview](docs/assets/canvas_preview.png) +![canvas preview](https://github.com/mauwii/InvokeAI/raw/main/docs/assets/canvas_preview.png) + +
# Getting Started with InvokeAI @@ -81,6 +85,7 @@ instructions, please see: InvokeAI is supported across Linux, Windows and macOS. Linux users can use either an Nvidia-based card (with CUDA support) or an AMD card (using the ROCm driver). + #### System You will need one of the following: @@ -104,19 +109,24 @@ to render 512x512 images. Feature documentation can be reviewed by navigating to [the InvokeAI Documentation page](https://invoke-ai.github.io/InvokeAI/features/) -### *Web Server & UI* +### *Web Server & UI* + InvokeAI offers a locally hosted Web Server & React Frontend, with an industry leading user experience. The Web-based UI allows for simple and intuitive workflows, and is responsive for use on mobile devices and tablets accessing the web server. ### *Unified Canvas* + The Unified Canvas is a fully integrated canvas implementation with support for all core generation capabilities, in/outpainting, brush tools, and more. This creative tool unlocks the capability for artists to create with AI as a creative collaborator, and can be used to augment AI-generated imagery, sketches, photography, renders, and more. ### *Advanced Prompt Syntax* + InvokeAI's advanced prompt syntax allows for token weighting, cross-attention control, and prompt blending, allowing for fine-tuned tweaking of your invocations and exploration of the latent space. ### *Command Line Interface* + For users utilizing a terminal-based environment, or who want to take advantage of CLI features, InvokeAI offers an extensive and actively supported command-line interface that provides the full suite of generation functionality available in the tool. ### Other features + - *Support for both ckpt and diffusers models* - *SD 2.0, 2.1 support* - *Noise Control & Tresholding* @@ -126,6 +136,7 @@ For users utilizing a terminal-based environment, or who want to take advantage - *Model Manager & Support* ### Coming Soon + - *Node-Based Architecture & UI* - And more... @@ -149,7 +160,7 @@ To join, just raise your hand on the InvokeAI Discord server (#dev-chat) or the If you are unfamiliar with how to contribute to GitHub projects, here is a -[Getting Started Guide](https://opensource.com/article/19/7/create-pull-request-github). A full set of contribution guidelines, along with templates, are in progress. You can **make your pull request against the "main" branch**. +[Getting Started Guide](https://opensource.com/article/19/7/create-pull-request-github). A full set of contribution guidelines, along with templates, are in progress. You can **make your pull request against the "main" branch**. We hope you enjoy using our software as much as we enjoy creating it, and we hope that some of those of you who are reading this will elect From a491644e563449ebe68ee888dae9ee7c23c5f1ef Mon Sep 17 00:00:00 2001 From: mauwii Date: Thu, 19 Jan 2023 03:12:59 +0100 Subject: [PATCH 24/41] fix dependencies/requirements --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5ae6a9b5ea..8359f14e6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools~=65.5.0", "pip~=22.3.1", "wheel"] +requires = ["setuptools~=65.5", "pip~=22.3", "wheel"] build-backend = "setuptools.build_meta" [project] @@ -56,7 +56,7 @@ dependencies = [ "k-diffusion", # replaceing "k-diffusion @ https://github.com/Birch-san/k-diffusion/archive/refs/heads/mps.zip", "kornia", "npyscreen", - "numpy<1.24,>=1.23", + "numpy~=1.23", "omegaconf", "opencv-python", "picklescan", From fc5fdae5629aeba42f27693746105f5b22f6906e Mon Sep 17 00:00:00 2001 From: mauwii Date: Thu, 19 Jan 2023 03:44:00 +0100 Subject: [PATCH 25/41] update installation instructions --- docs/installation/020_INSTALL_MANUAL.md | 8 ++- docs/installation/060_INSTALL_PATCHMATCH.md | 23 ++++---- docs/installation/INSTALL_MANUAL.md | 59 +-------------------- 3 files changed, 16 insertions(+), 74 deletions(-) diff --git a/docs/installation/020_INSTALL_MANUAL.md b/docs/installation/020_INSTALL_MANUAL.md index ead1eb6536..1fad83121f 100644 --- a/docs/installation/020_INSTALL_MANUAL.md +++ b/docs/installation/020_INSTALL_MANUAL.md @@ -119,10 +119,8 @@ manager, please follow these steps: 6. Run PIP - Be sure that the `invokeai` environment is active before doing this: - ```bash - pip install --prefer-binary -r requirements.txt + pip --python invokeai install --use-pep517 . ``` 7. Set up the runtime directory @@ -137,7 +135,7 @@ manager, please follow these steps: default to `invokeai` in your home directory. ```bash - configure_invokeai.py --root_dir ~/Programs/invokeai + configure_invokeai --root_dir ~/Programs/invokeai ``` The script `configure_invokeai.py` will interactively guide you through the @@ -452,7 +450,7 @@ time. Note that this method only works with the PIP method. step. 3. Run one additional step while you are in the source code repository - directory `pip install .` (note the dot at the end). + directory `pip install --use-pep517 .` (note the dot at the end). 4. That's all! Now, whenever you activate the virtual environment, `invoke.py` will know where to look for the runtime directory without diff --git a/docs/installation/060_INSTALL_PATCHMATCH.md b/docs/installation/060_INSTALL_PATCHMATCH.md index 592e1f56c3..989be24731 100644 --- a/docs/installation/060_INSTALL_PATCHMATCH.md +++ b/docs/installation/060_INSTALL_PATCHMATCH.md @@ -18,7 +18,13 @@ Windows systems with no extra intervention. ## Macintosh -PyPatchMatch is not currently supported, but the team is working on it. +You need to have opencv installed so that pypatchmatch can be built: + +```bash +brew install opencv +``` + +The next time you start `invoke`, after sucesfully installing opencv, pypatchmatch will be built. ## Linux @@ -39,23 +45,16 @@ Prior to installing PyPatchMatch, you need to take the following steps: sudo apt install python3-opencv libopencv-dev ``` -3. Fix the naming of the `opencv` package configuration file: - - ```sh - cd /usr/lib/x86_64-linux-gnu/pkgconfig/ - ln -sf opencv4.pc opencv.pc - ``` - -4. Activate the environment you use for invokeai, either with `conda` or with a +3. Activate the environment you use for invokeai, either with `conda` or with a virtual environment. -5. Install pypatchmatch: +4. Install pypatchmatch: ```sh - pip install "git+https://github.com/invoke-ai/PyPatchMatch@0.1.3#egg=pypatchmatch" + pip install pypatchmatch ``` -6. Confirm that pypatchmatch is installed. At the command-line prompt enter +5. Confirm that pypatchmatch is installed. At the command-line prompt enter `python`, and then at the `>>>` line type `from patchmatch import patch_match`: It should look like the follwing: diff --git a/docs/installation/INSTALL_MANUAL.md b/docs/installation/INSTALL_MANUAL.md index 4d62df9415..f45463c535 100644 --- a/docs/installation/INSTALL_MANUAL.md +++ b/docs/installation/INSTALL_MANUAL.md @@ -254,65 +254,10 @@ steps: source invokeai/bin/activate ``` -4. Pick the correct `requirements*.txt` file for your hardware and operating - system. - - We have created a series of environment files suited for different operating - systems and GPU hardware. They are located in the - `environments-and-requirements` directory: - -
- - | filename | OS | - | :---------------------------------: | :-------------------------------------------------------------: | - | requirements-lin-amd.txt | Linux with an AMD (ROCm) GPU | - | requirements-lin-arm64.txt | Linux running on arm64 systems | - | requirements-lin-cuda.txt | Linux with an NVIDIA (CUDA) GPU | - | requirements-mac-mps-cpu.txt | Macintoshes with MPS acceleration | - | requirements-lin-win-colab-cuda.txt | Windows with an NVIDA (CUDA) GPU
(supports Google Colab too) | - -
- - Select the appropriate requirements file, and make a link to it from - `requirements.txt` in the top-level InvokeAI directory. The command to do - this from the top-level directory is: - - !!! example "" - - === "Macintosh and Linux" - - !!! info "Replace `xxx` and `yyy` with the appropriate OS and GPU codes." - - ```bash - ln -sf environments-and-requirements/requirements-xxx-yyy.txt requirements.txt - ``` - - === "Windows" - - !!! info "on Windows, admin privileges are required to make links, so we use the copy command instead" - - ```cmd - copy environments-and-requirements\requirements-lin-win-colab-cuda.txt requirements.txt - ``` - - !!! warning - - Please do not link or copy `environments-and-requirements/requirements-base.txt`. - This is a base requirements file that does not have the platform-specific - libraries. Also, be sure to link or copy the platform-specific file to - a top-level file named `requirements.txt` as shown here. Running pip on - a requirements file in a subdirectory will not work as expected. - - When this is done, confirm that a file named `requirements.txt` has been - created in the InvokeAI root directory and that it points to the correct - file in `environments-and-requirements`. - -5. Run PIP - - Be sure that the `invokeai` environment is active before doing this: +4. Run PIP ```bash - pip install --prefer-binary -r requirements.txt + pip --python invokeai install --use-pep517 . ``` --- From d7217e38013dd1b98f73c1f09a3ef5621267c894 Mon Sep 17 00:00:00 2001 From: mauwii Date: Thu, 19 Jan 2023 03:48:23 +0100 Subject: [PATCH 26/41] disable instable CI tests for windows runners therefore enable all pytorch versions to verify installation --- .github/workflows/test-invoke-pip.yml | 29 ++++++++++++++------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test-invoke-pip.yml b/.github/workflows/test-invoke-pip.yml index a1a217fd84..a88a2fb883 100644 --- a/.github/workflows/test-invoke-pip.yml +++ b/.github/workflows/test-invoke-pip.yml @@ -27,9 +27,9 @@ jobs: - linux-rocm-5_2 - linux-cpu - macos-default - # - windows-cpu + - windows-cpu - windows-cuda-11_6 - # - windows-cuda-11_7 + - windows-cuda-11_7 include: - pytorch: linux-cuda-11_6 os: ubuntu-22.04 @@ -49,17 +49,17 @@ jobs: - pytorch: macos-default os: macOS-12 github-env: $GITHUB_ENV - # - pytorch: windows-cpu - # os: windows-2022 - # github-env: $env:GITHUB_ENV + - pytorch: windows-cpu + os: windows-2022 + github-env: $env:GITHUB_ENV - pytorch: windows-cuda-11_6 os: windows-2022 extra-index-url: 'https://download.pytorch.org/whl/cu116' github-env: $env:GITHUB_ENV - # - pytorch: windows-cuda-11_7 - # os: windows-2022 - # extra-index-url: 'https://download.pytorch.org/whl/cu117' - # github-env: $env:GITHUB_ENV + - pytorch: windows-cuda-11_7 + os: windows-2022 + extra-index-url: 'https://download.pytorch.org/whl/cu117' + github-env: $env:GITHUB_ENV name: ${{ matrix.pytorch }} on ${{ matrix.python-version }} runs-on: ${{ matrix.os }} steps: @@ -119,12 +119,13 @@ jobs: # can't use fp16 weights without a GPU - name: Run the tests + if: runner.os != 'Windows' id: run-tests - # env: - # # Set offline mode to make sure configure preloaded successfully. - # HF_HUB_OFFLINE: 1 - # HF_DATASETS_OFFLINE: 1 - # TRANSFORMERS_OFFLINE: 1 + env: + # Set offline mode to make sure configure preloaded successfully. + HF_HUB_OFFLINE: 1 + HF_DATASETS_OFFLINE: 1 + TRANSFORMERS_OFFLINE: 1 run: > invoke --no-patchmatch From 96a900d1fe730ee44748d7a0203173f7e5119c27 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Fri, 20 Jan 2023 20:13:43 -0500 Subject: [PATCH 27/41] correctly import diffusers models by their local path - Corrects a bug in which the local path was treated as a repo_id --- ldm/invoke/CLI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldm/invoke/CLI.py b/ldm/invoke/CLI.py index 4891004d66..0de25bf458 100644 --- a/ldm/invoke/CLI.py +++ b/ldm/invoke/CLI.py @@ -577,7 +577,7 @@ def import_model(model_path:str, gen, opt, completer): elif re.match('^[\w.+-]+/[\w.+-]+$',model_path): model_name = import_diffuser_model(model_path, gen, opt, completer) elif os.path.isdir(model_path): - model_name = import_diffuser_model(model_path, gen, opt, completer) + model_name = import_diffuser_model(Path(model_path), gen, opt, completer) else: print(f'** {model_path} is neither the path to a .ckpt file nor a diffusers repository id. Can\'t import.') From d35ec3398d8255f5621ec29f8c0eb0ec8a7c3129 Mon Sep 17 00:00:00 2001 From: Kevin Turner <83819+keturn@users.noreply.github.com> Date: Fri, 20 Jan 2023 19:23:12 -0800 Subject: [PATCH 28/41] fix: use pad_token for padding Stable Diffusion does not use the eos_token for padding. --- ldm/modules/encoders/modules.py | 18 ++++++++---------- ldm/modules/prompt_to_embeddings_converter.py | 11 ++++++----- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/ldm/modules/encoders/modules.py b/ldm/modules/encoders/modules.py index aafb1299ad..6715b229f1 100644 --- a/ldm/modules/encoders/modules.py +++ b/ldm/modules/encoders/modules.py @@ -1,18 +1,16 @@ import math -import os.path +from functools import partial from typing import Optional +import clip +import kornia import torch import torch.nn as nn -from functools import partial -import clip -from einops import rearrange, repeat +from einops import repeat from transformers import CLIPTokenizer, CLIPTextModel -import kornia -from ldm.invoke.devices import choose_torch_device -from ldm.invoke.globals import Globals, global_cache_dir -#from ldm.modules.textual_inversion_manager import TextualInversionManager +from ldm.invoke.devices import choose_torch_device +from ldm.invoke.globals import global_cache_dir from ldm.modules.x_transformer import ( Encoder, TransformerWrapper, @@ -663,12 +661,12 @@ class WeightedFrozenCLIPEmbedder(FrozenCLIPEmbedder): all_token_ids = all_token_ids[0:self.max_length] per_token_weights = per_token_weights[0:self.max_length] - # pad out to a 77-entry array: [eos_token, , eos_token, ..., eos_token] + # pad out to a 77-entry array: [bos_token, , eos_token, pad_token…] # (77 = self.max_length) all_token_ids = [self.tokenizer.bos_token_id] + all_token_ids + [self.tokenizer.eos_token_id] per_token_weights = [1.0] + per_token_weights + [1.0] pad_length = self.max_length - len(all_token_ids) - all_token_ids += [self.tokenizer.eos_token_id] * pad_length + all_token_ids += [self.tokenizer.pad_token_id] * pad_length per_token_weights += [1.0] * pad_length all_token_ids_tensor = torch.tensor(all_token_ids, dtype=torch.long).to(self.device) diff --git a/ldm/modules/prompt_to_embeddings_converter.py b/ldm/modules/prompt_to_embeddings_converter.py index ab989e4892..dea15d61b4 100644 --- a/ldm/modules/prompt_to_embeddings_converter.py +++ b/ldm/modules/prompt_to_embeddings_converter.py @@ -3,8 +3,9 @@ import math import torch from transformers import CLIPTokenizer, CLIPTextModel -from ldm.modules.textual_inversion_manager import TextualInversionManager from ldm.invoke.devices import torch_dtype +from ldm.modules.textual_inversion_manager import TextualInversionManager + class WeightedPromptFragmentsToEmbeddingsConverter(): @@ -22,8 +23,8 @@ class WeightedPromptFragmentsToEmbeddingsConverter(): return self.tokenizer.model_max_length def get_embeddings_for_weighted_prompt_fragments(self, - text: list[str], - fragment_weights: list[float], + text: list[list[str]], + fragment_weights: list[list[float]], should_return_tokens: bool = False, device='cpu' ) -> torch.Tensor: @@ -198,12 +199,12 @@ class WeightedPromptFragmentsToEmbeddingsConverter(): all_token_ids = all_token_ids[0:max_token_count_without_bos_eos_markers] per_token_weights = per_token_weights[0:max_token_count_without_bos_eos_markers] - # pad out to a self.max_length-entry array: [eos_token, , eos_token, ..., eos_token] + # pad out to a self.max_length-entry array: [bos_token, , eos_token, pad_token…] # (typically self.max_length == 77) all_token_ids = [self.tokenizer.bos_token_id] + all_token_ids + [self.tokenizer.eos_token_id] per_token_weights = [1.0] + per_token_weights + [1.0] pad_length = self.max_length - len(all_token_ids) - all_token_ids += [self.tokenizer.eos_token_id] * pad_length + all_token_ids += [self.tokenizer.pad_token_id] * pad_length per_token_weights += [1.0] * pad_length all_token_ids_tensor = torch.tensor(all_token_ids, dtype=torch.long, device=device) From e94c8fa285da208fc042ad16ebcb8303d19dc345 Mon Sep 17 00:00:00 2001 From: Damian Stewart Date: Sat, 21 Jan 2023 12:06:23 +0100 Subject: [PATCH 29/41] fix long prompt weighting bug in ckpt codepath --- ldm/modules/encoders/modules.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ldm/modules/encoders/modules.py b/ldm/modules/encoders/modules.py index aafb1299ad..5b5f71cffd 100644 --- a/ldm/modules/encoders/modules.py +++ b/ldm/modules/encoders/modules.py @@ -654,14 +654,15 @@ class WeightedFrozenCLIPEmbedder(FrozenCLIPEmbedder): per_token_weights += [weight] * len(this_fragment_token_ids) # leave room for bos/eos - if len(all_token_ids) > self.max_length - 2: - excess_token_count = len(all_token_ids) - self.max_length - 2 + max_token_count_without_bos_eos_markers = self.max_length - 2 + if len(all_token_ids) > max_token_count_without_bos_eos_markers: + excess_token_count = len(all_token_ids) - max_token_count_without_bos_eos_markers # TODO build nice description string of how the truncation was applied # this should be done by calling self.tokenizer.convert_ids_to_tokens() then passing the result to # self.tokenizer.convert_tokens_to_string() for the token_ids on each side of the truncation limit. print(f">> Prompt is {excess_token_count} token(s) too long and has been truncated") - all_token_ids = all_token_ids[0:self.max_length] - per_token_weights = per_token_weights[0:self.max_length] + all_token_ids = all_token_ids[0:max_token_count_without_bos_eos_markers] + per_token_weights = per_token_weights[0:max_token_count_without_bos_eos_markers] # pad out to a 77-entry array: [eos_token, , eos_token, ..., eos_token] # (77 = self.max_length) From 4ee8d104f07ea519959d18075dd8ebb597cdb44c Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Sat, 21 Jan 2023 18:39:13 -0500 Subject: [PATCH 30/41] working, but needs diffusers PR to be accepted --- scripts/merge_fe.py | 111 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 11 deletions(-) mode change 100644 => 100755 scripts/merge_fe.py diff --git a/scripts/merge_fe.py b/scripts/merge_fe.py old mode 100644 new mode 100755 index df01235599..4f24961a18 --- a/scripts/merge_fe.py +++ b/scripts/merge_fe.py @@ -3,15 +3,17 @@ import npyscreen import os import sys -import re -import shutil import traceback import argparse -from ldm.invoke.globals import Globals, global_set_root +import safetensors.torch +from ldm.invoke.globals import Globals, global_set_root, global_cache_dir +from ldm.invoke.model_manager import ModelManager from omegaconf import OmegaConf from pathlib import Path from typing import List +CONFIG_FILE = None + class FloatSlider(npyscreen.Slider): # this is supposed to adjust display precision, but doesn't def translate_value(self): @@ -30,6 +32,14 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): 'inv_sigmoid', 'add_difference'] + def __init__(self, parentApp, name): + self.parentApp = parentApp + super().__init__(parentApp, name) + + @property + def model_manager(self): + return self.parentApp.model_manager + def afterEditing(self): self.parentApp.setNextForm(None) @@ -83,6 +93,11 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): lowest=0, value=0.5, ) + self.force = self.add_widget_intelligent( + npyscreen.Checkbox, + name='Force merge of incompatible models', + value=False, + ) self.merged_model_name = self.add_widget_intelligent( npyscreen.TitleText, name='Name for merged model', @@ -108,17 +123,44 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): if self.validate_field_values(): self.parentApp.setNextForm(None) self.editing = False + self.parentApp.merge_arguments = self.marshall_arguments() + npyscreen.notify('Starting the merge...') + import diffusers # this keeps the message up while diffusers loads else: self.editing = True def ok_cancel(self): sys.exit(0) + def marshall_arguments(self)->dict: + model_names = self.model_names + models = [ + model_names[self.model1.value[0]], + model_names[self.model2.value[0]], + ] + if self.model3.value[0] > 0: + models.append(model_names[self.model3.value[0]-1]) + + models = [self.model_manager.model_name_or_path(x) for x in models] + + args = dict( + pretrained_model_name_or_path_list=models, + alpha = self.alpha.value, + interp = self.interpolations[self.merge_method.value[0]], + force = self.force.value, + cache_dir = global_cache_dir('diffusers'), + merged_model_name = self.merged_model_name.value, + ) + return args + def validate_field_values(self)->bool: bad_fields = [] - selected_models = set((self.model1.value[0],self.model2.value[0],self.model3.value[0])) - if len(selected_models) < 3: - bad_fields.append('Please select two or three DIFFERENT models to compare') + model_names = self.model_names + selected_models = set((model_names[self.model1.value[0]],model_names[self.model2.value[0]])) + if self.model3.value[0] > 0: + selected_models.add(model_names[self.model3.value[0]-1]) + if len(selected_models) < 2: + bad_fields.append(f'Please select two or three DIFFERENT models to compare. You selected {selected_models}') if len(bad_fields) > 0: message = 'The following problems were detected and must be corrected:' for problem in bad_fields: @@ -129,13 +171,15 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): return True def get_model_names(self)->List[str]: - conf = OmegaConf.load(os.path.join(Globals.root,'configs/models.yaml')) - model_names = [name for name in conf.keys() if conf[name].get('format',None)=='diffusers'] + model_names = [name for name in self.model_manager.model_names() if self.model_manager.model_info(name).get('format') == 'diffusers'] + print(model_names) return sorted(model_names) -class MyApplication(npyscreen.NPSAppManaged): +class Mergeapp(npyscreen.NPSAppManaged): def __init__(self): super().__init__() + conf = OmegaConf.load(Path(Globals.root) / 'configs' / 'models.yaml') + self.model_manager = ModelManager(conf,'cpu','float16') # precision doesn't really matter here def onStart(self): npyscreen.setTheme(npyscreen.Themes.DefaultTheme) @@ -151,6 +195,51 @@ if __name__ == '__main__': ) args = parser.parse_args() global_set_root(args.root_dir) + + CONFIG_FILE = os.path.join(Globals.root,'configs/models.yaml') + os.environ['HF_HOME'] = str(global_cache_dir('diffusers')) + + mergeapp = Mergeapp() + mergeapp.run() + from diffusers import DiffusionPipeline + args = mergeapp.merge_arguments + merged_model_name = args['merged_model_name'] + merged_pipe = None + print(args) + + try: + print(f'DEBUG: {args["pretrained_model_name_or_path_list"][0]}') + pipe = DiffusionPipeline.from_pretrained(args['pretrained_model_name_or_path_list'][0], + custom_pipeline='checkpoint_merger' + ) + merged_pipe = pipe.merge(**args) + dump_path = Path(Globals.root) / 'models' / 'merged_diffusers' + os.makedirs(dump_path,exist_ok=True) + dump_path = dump_path / merged_model_name + merged_pipe.save_pretrained ( + dump_path, + safe_serialization=1 + ) + except Exception as e: + print(f'** An error occurred while merging the pipelines: {str(e)}') + print('** DETAILS:') + print(traceback.format_exc()) + sys.exit(-1) + + print(f'>> Merged model is saved to {dump_path}') + response = input('Import this model into InvokeAI? [y]').strip() or 'y' + if response.startswith(('y','Y')): + try: + mergeapp.model_manager.import_diffuser_model( + dump_path, + model_name = merged_model_name, + description = f'Merge of models {args["pretrained_model_name_or_path_list"]}' + ) + mergeapp.model_manager.commit(CONFIG_FILE) + print('>> Merged model imported.') + except Exception as e: + print(f'** New model could not be committed to config.yaml: {str(e)}') + print('** DETAILS:') + print(traceback.format_exc()) + - myapplication = MyApplication() - myapplication.run() From 6c31225d195ad185e48c165fa8558e3724646a92 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Sun, 22 Jan 2023 18:07:53 -0500 Subject: [PATCH 31/41] create small module for merge importation logic --- ldm/invoke/globals.py | 4 ++ ldm/invoke/merge_diffusers.py | 59 ++++++++++++++++++++++++++++ ldm/invoke/model_manager.py | 8 +++- scripts/load_models.py | 0 scripts/merge_embeddings.py | 0 scripts/merge_fe.py | 72 +++++++++++------------------------ 6 files changed, 91 insertions(+), 52 deletions(-) create mode 100644 ldm/invoke/merge_diffusers.py mode change 100644 => 100755 scripts/load_models.py mode change 100644 => 100755 scripts/merge_embeddings.py diff --git a/ldm/invoke/globals.py b/ldm/invoke/globals.py index 5bd5597b78..538ff17703 100644 --- a/ldm/invoke/globals.py +++ b/ldm/invoke/globals.py @@ -29,6 +29,7 @@ else: # Where to look for the initialization file Globals.initfile = 'invokeai.init' +Globals.models_file = 'models.yaml' Globals.models_dir = 'models' Globals.config_dir = 'configs' Globals.autoscan_dir = 'weights' @@ -49,6 +50,9 @@ Globals.disable_xformers = False # whether we are forcing full precision Globals.full_precision = False +def global_config_file()->Path: + return Path(Globals.root, Globals.config_dir, Globals.models_file) + def global_config_dir()->Path: return Path(Globals.root, Globals.config_dir) diff --git a/ldm/invoke/merge_diffusers.py b/ldm/invoke/merge_diffusers.py new file mode 100644 index 0000000000..6a1d742aee --- /dev/null +++ b/ldm/invoke/merge_diffusers.py @@ -0,0 +1,59 @@ +''' +ldm.invoke.merge_diffusers exports a single function call merge_diffusion_models() +used to merge 2-3 models together and create a new InvokeAI-registered diffusion model. +''' +import os +from typing import List +from diffusers import DiffusionPipeline +from ldm.invoke.globals import global_config_file, global_models_dir, global_cache_dir +from ldm.invoke.model_manager import ModelManager +from omegaconf import OmegaConf + +def merge_diffusion_models(models:List['str'], + merged_model_name:str, + alpha:float=0.5, + interp:str=None, + force:bool=False, + **kwargs): + ''' + models - up to three models, designated by their InvokeAI models.yaml model name + merged_model_name = name for new model + alpha - The interpolation parameter. Ranges from 0 to 1. It affects the ratio in which the checkpoints are merged. A 0.8 alpha + would mean that the first model checkpoints would affect the final result far less than an alpha of 0.2 + interp - The interpolation method to use for the merging. Supports "sigmoid", "inv_sigmoid", "add_difference" and None. + Passing None uses the default interpolation which is weighted sum interpolation. For merging three checkpoints, only "add_difference" is supported. + force - Whether to ignore mismatch in model_config.json for the current models. Defaults to False. + + **kwargs - the default DiffusionPipeline.get_config_dict kwargs: + cache_dir, resume_download, force_download, proxies, local_files_only, use_auth_token, revision, torch_dtype, device_map + ''' + config_file = global_config_file() + model_manager = ModelManager(OmegaConf.load(config_file)) + model_ids_or_paths = [model_manager.model_name_or_path(x) for x in models] + + pipe = DiffusionPipeline.from_pretrained(model_ids_or_paths[0], + cache_dir=kwargs.get('cache_dir',global_cache_dir()), + custom_pipeline='checkpoint_merger') + merged_pipe = pipe.merge(pretrained_model_name_or_path_list=model_ids_or_paths, + alpha=alpha, + interp=interp, + force=force, + **kwargs) + dump_path = global_models_dir() / 'merged_diffusers' + os.makedirs(dump_path,exist_ok=True) + dump_path = dump_path / merged_model_name + merged_pipe.save_pretrained ( + dump_path, + safe_serialization=1 + ) + model_manager.import_diffuser_model( + dump_path, + model_name = merged_model_name, + description = f'Merge of models {", ".join(models)}' + ) + print('REMINDER: When PR 2369 is merged, replace merge_diffusers.py line 56 with vae= argument to impormodel()') + if vae := model_manager.config[models[0]].get('vae',None): + print(f'>> Using configured VAE assigned to {models[0]}') + model_manager.config[merged_model_name]['vae'] = vae + + model_manager.commit(config_file) diff --git a/ldm/invoke/model_manager.py b/ldm/invoke/model_manager.py index a5f9a47d41..650a497ad5 100644 --- a/ldm/invoke/model_manager.py +++ b/ldm/invoke/model_manager.py @@ -37,7 +37,11 @@ from ldm.util import instantiate_from_config, ask_user DEFAULT_MAX_MODELS=2 class ModelManager(object): - def __init__(self, config:OmegaConf, device_type:str, precision:str, max_loaded_models=DEFAULT_MAX_MODELS): + def __init__(self, + config:OmegaConf, + device_type:str='cpu', + precision:str='float16', + max_loaded_models=DEFAULT_MAX_MODELS): ''' Initialize with the path to the models.yaml config file, the torch device type, and precision. The optional @@ -536,7 +540,7 @@ class ModelManager(object): format='diffusers', ) if isinstance(repo_or_path,Path) and repo_or_path.exists(): - new_config.update(path=repo_or_path) + new_config.update(path=str(repo_or_path)) else: new_config.update(repo_id=repo_or_path) diff --git a/scripts/load_models.py b/scripts/load_models.py old mode 100644 new mode 100755 diff --git a/scripts/merge_embeddings.py b/scripts/merge_embeddings.py old mode 100644 new mode 100755 diff --git a/scripts/merge_fe.py b/scripts/merge_fe.py index 4f24961a18..a15e4440e5 100755 --- a/scripts/merge_fe.py +++ b/scripts/merge_fe.py @@ -5,15 +5,12 @@ import os import sys import traceback import argparse -import safetensors.torch -from ldm.invoke.globals import Globals, global_set_root, global_cache_dir +from ldm.invoke.globals import Globals, global_set_root, global_cache_dir, global_config_file from ldm.invoke.model_manager import ModelManager from omegaconf import OmegaConf from pathlib import Path from typing import List -CONFIG_FILE = None - class FloatSlider(npyscreen.Slider): # this is supposed to adjust display precision, but doesn't def translate_value(self): @@ -120,16 +117,16 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): self.merge_method.value=0 def on_ok(self): - if self.validate_field_values(): + if self.validate_field_values() and self.check_for_overwrite(): self.parentApp.setNextForm(None) self.editing = False self.parentApp.merge_arguments = self.marshall_arguments() npyscreen.notify('Starting the merge...') - import diffusers # this keeps the message up while diffusers loads + import ldm.invoke.merge_diffusers # this keeps the message up while diffusers loads else: self.editing = True - def ok_cancel(self): + def on_cancel(self): sys.exit(0) def marshall_arguments(self)->dict: @@ -141,18 +138,22 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): if self.model3.value[0] > 0: models.append(model_names[self.model3.value[0]-1]) - models = [self.model_manager.model_name_or_path(x) for x in models] - args = dict( - pretrained_model_name_or_path_list=models, + models=models, alpha = self.alpha.value, interp = self.interpolations[self.merge_method.value[0]], force = self.force.value, - cache_dir = global_cache_dir('diffusers'), merged_model_name = self.merged_model_name.value, ) return args + def check_for_overwrite(self)->bool: + model_out = self.merged_model_name.value + if model_out not in self.model_names: + return True + else: + return npyscreen.notify_yes_no(f'The chosen merged model destination, {model_out}, is already in use. Overwrite?') + def validate_field_values(self)->bool: bad_fields = [] model_names = self.model_names @@ -178,7 +179,7 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): class Mergeapp(npyscreen.NPSAppManaged): def __init__(self): super().__init__() - conf = OmegaConf.load(Path(Globals.root) / 'configs' / 'models.yaml') + conf = OmegaConf.load(global_config_file()) self.model_manager = ModelManager(conf,'cpu','float16') # precision doesn't really matter here def onStart(self): @@ -195,51 +196,22 @@ if __name__ == '__main__': ) args = parser.parse_args() global_set_root(args.root_dir) - - CONFIG_FILE = os.path.join(Globals.root,'configs/models.yaml') - os.environ['HF_HOME'] = str(global_cache_dir('diffusers')) + + cache_dir = str(global_cache_dir('diffusers')) # because not clear the merge pipeline is honoring cache_dir + os.environ['HF_HOME'] = cache_dir mergeapp = Mergeapp() mergeapp.run() - from diffusers import DiffusionPipeline - args = mergeapp.merge_arguments - merged_model_name = args['merged_model_name'] - merged_pipe = None - print(args) + + args = mergeapp.merge_arguments + args.update(cache_dir = cache_dir) + from ldm.invoke.merge_diffusers import merge_diffusion_models try: - print(f'DEBUG: {args["pretrained_model_name_or_path_list"][0]}') - pipe = DiffusionPipeline.from_pretrained(args['pretrained_model_name_or_path_list'][0], - custom_pipeline='checkpoint_merger' - ) - merged_pipe = pipe.merge(**args) - dump_path = Path(Globals.root) / 'models' / 'merged_diffusers' - os.makedirs(dump_path,exist_ok=True) - dump_path = dump_path / merged_model_name - merged_pipe.save_pretrained ( - dump_path, - safe_serialization=1 - ) + merge_diffusion_models(**args) + print(f'>> Models merged into new model: "{args["merged_model_name"]}".') except Exception as e: print(f'** An error occurred while merging the pipelines: {str(e)}') print('** DETAILS:') print(traceback.format_exc()) sys.exit(-1) - - print(f'>> Merged model is saved to {dump_path}') - response = input('Import this model into InvokeAI? [y]').strip() or 'y' - if response.startswith(('y','Y')): - try: - mergeapp.model_manager.import_diffuser_model( - dump_path, - model_name = merged_model_name, - description = f'Merge of models {args["pretrained_model_name_or_path_list"]}' - ) - mergeapp.model_manager.commit(CONFIG_FILE) - print('>> Merged model imported.') - except Exception as e: - print(f'** New model could not be committed to config.yaml: {str(e)}') - print('** DETAILS:') - print(traceback.format_exc()) - - From 48deb3e49dec2991d7f47800910726736f5d5b7b Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Mon, 23 Jan 2023 00:20:28 -0500 Subject: [PATCH 32/41] add model merging documentation and launcher script menu entries --- docs/features/MODEL_MERGING.md | 77 ++++++++++++++++ docs/index.md | 2 + installer/templates/invoke.bat.in | 10 +- installer/templates/invoke.sh.in | 8 +- ldm/invoke/merge_diffusers.py | 3 + scripts/merge_models.py | 92 +++++++++++++++++++ scripts/{merge_fe.py => merge_models_fe.py} | 0 .../{ => orig_scripts}/merge_embeddings.py | 0 8 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 docs/features/MODEL_MERGING.md create mode 100755 scripts/merge_models.py rename scripts/{merge_fe.py => merge_models_fe.py} (100%) rename scripts/{ => orig_scripts}/merge_embeddings.py (100%) diff --git a/docs/features/MODEL_MERGING.md b/docs/features/MODEL_MERGING.md new file mode 100644 index 0000000000..b94e1e4314 --- /dev/null +++ b/docs/features/MODEL_MERGING.md @@ -0,0 +1,77 @@ +--- +title: Model Merging +--- + +# :material-image-off: Model Merging + +## How to Merge Models + +As of version 2.3, InvokeAI comes with a script that allows you to +merge two or three diffusers-type models into a new merged model. The +resulting model will combine characteristics of the original, and can +be used to teach an old model new tricks. + +You may run the merge script by starting the invoke launcher +(`invoke.sh` or `invoke.bat`) and choosing the option for _merge +models_. This will launch a text-based interactive user interface that +prompts you to select the models to merge, how to merge them, and the +merged model name. + +Alternatively you may activate InvokeAI's virtual environment from the +command line, and call the script via `merge_models_fe.py` (the "fe" +stands for "front end"). There is also a version that accepts +command-line arguments, which you can run with the command +`merge_models.py`. + +The user interface for the text-based interactive script is +straightforward. It shows you a series of setting fields. Use control-N (^N) +to move to the next field, and control-P (^P) to move to the previous +one. You can also use TAB and shift-TAB to move forward and +backward. Once you are in a multiple choice field, use the up and down +cursor arrows to move to your desired selection, and press or + to select it. Change text fields by typing in them, and adjust +scrollbars using the left and right arrow keys. + +Once you are happy with your settings, press the OK button. Note that +there may be two pages of settings, depending on the height of your +screen, and the OK button may be on the second page. Advance past the +last field of the first page to get to the second page, and reverse +this to get back. + +If the merge runs successfully, it will create a new diffusers model +under the selected name and register it with InvokeAI. + +## The Settings + +* Model Selection -- there are three multiple choice fields that + display all the diffusers-style models that InvokeAI knows about. + If you do not see the model you are looking for, then it is probably + a legacy checkpoint model and needs to be converted using the + `invoke.py` command-line client and its `!optimize` command. You + must select at least two models to merge. The third can be left at + "None" if you desire. + +* Alpha -- This is the ratio to use when combining models. It ranges + from 0 to 1. The higher the value, the more weight is given to the + 2d and (optionally) 3d models. So if you have two models named "A" + and "B", an alpha value of 0.25 will give you a merged model that is + 25% A and 75% B. + +* Interpolation Method -- This is the method used to combine + weights. The options are "weighted_sum" (the default), "sigmoid", + "inv_sigmoid" and "add_difference". Each produces slightly different + results. When three models are in use, only "add_difference" is + available. (TODO: cite a reference that describes what these + interpolation methods actually do and how to decide among them). + +* Force -- Not all models are compatible with each other. The merge + script will check for compatibility and refuse to merge ones that + are incompatible. Set this checkbox to try merging anyway. + +* Name for merged model - This is the name for the new model. Please + use InvokeAI conventions - only alphanumeric letters and the + characters ".+-". + +## Caveats + +This is a new script and may contain bugs. diff --git a/docs/index.md b/docs/index.md index c38f840d32..e20535d591 100644 --- a/docs/index.md +++ b/docs/index.md @@ -157,6 +157,8 @@ images in full-precision mode: - [Prompt Engineering](features/PROMPTS.md) +- [Model Merging](features/MODEL_MERGING.md) + - Miscellaneous - [NSFW Checker](features/NSFW.md) - [Embiggen upscaling](features/EMBIGGEN.md) diff --git a/installer/templates/invoke.bat.in b/installer/templates/invoke.bat.in index 114b8c5070..4b2e982d13 100644 --- a/installer/templates/invoke.bat.in +++ b/installer/templates/invoke.bat.in @@ -10,8 +10,9 @@ echo Do you want to generate images using the echo 1. command-line echo 2. browser-based UI echo 3. run textual inversion training -echo 4. open the developer console -echo 5. re-run the configure script to download new models +echo 4. merge models (diffusers type only) +echo 5. open the developer console +echo 6. re-run the configure script to download new models set /P restore="Please enter 1, 2, 3, 4 or 5: [5] " if not defined restore set restore=2 IF /I "%restore%" == "1" ( @@ -24,6 +25,9 @@ IF /I "%restore%" == "1" ( echo Starting textual inversion training.. python .venv\Scripts\textual_inversion_fe.py --web %* ) ELSE IF /I "%restore%" == "4" ( + echo Starting model merging script.. + python .venv\Scripts\merge_models_fe.py --web %* +) ELSE IF /I "%restore%" == "5" ( echo Developer Console echo Python command is: where python @@ -35,7 +39,7 @@ IF /I "%restore%" == "1" ( echo ************************* echo *** Type `exit` to quit this shell and deactivate the Python virtual environment *** call cmd /k -) ELSE IF /I "%restore%" == "5" ( +) ELSE IF /I "%restore%" == "6" ( echo Running configure_invokeai.py... python .venv\Scripts\configure_invokeai.py --web %* ) ELSE ( diff --git a/installer/templates/invoke.sh.in b/installer/templates/invoke.sh.in index 44ee8c5b90..d871bb7e58 100644 --- a/installer/templates/invoke.sh.in +++ b/installer/templates/invoke.sh.in @@ -20,16 +20,18 @@ if [ "$0" != "bash" ]; then echo "1. command-line" echo "2. browser-based UI" echo "3. run textual inversion training" - echo "4. open the developer console" + echo "4. merge models (diffusers type only)" echo "5. re-run the configure script to download new models" + echo "6. open the developer console" read -p "Please enter 1, 2, 3, 4 or 5: [1] " yn choice=${yn:='2'} case $choice in 1 ) printf "\nStarting the InvokeAI command-line..\n"; .venv/bin/python .venv/bin/invoke.py $*;; 2 ) printf "\nStarting the InvokeAI browser-based UI..\n"; .venv/bin/python .venv/bin/invoke.py --web $*;; 3 ) printf "\nStarting Textual Inversion:\n"; .venv/bin/python .venv/bin/textual_inversion_fe.py $*;; - 4 ) printf "\nDeveloper Console:\n"; file_name=$(basename "${BASH_SOURCE[0]}"); bash --init-file "$file_name";; - 5 ) printf "\nRunning configure_invokeai.py:\n"; .venv/bin/python .venv/bin/configure_invokeai.py $*;; + 4 ) printf "\nMerging Models:\n"; .venv/bin/python .venv/bin/merge_models_fe.py $*;; + 5 ) printf "\nDeveloper Console:\n"; file_name=$(basename "${BASH_SOURCE[0]}"); bash --init-file "$file_name";; + 6 ) printf "\nRunning configure_invokeai.py:\n"; .venv/bin/python .venv/bin/configure_invokeai.py $*;; * ) echo "Invalid selection"; exit;; esac else # in developer console diff --git a/ldm/invoke/merge_diffusers.py b/ldm/invoke/merge_diffusers.py index 6a1d742aee..b140e48369 100644 --- a/ldm/invoke/merge_diffusers.py +++ b/ldm/invoke/merge_diffusers.py @@ -29,6 +29,9 @@ def merge_diffusion_models(models:List['str'], ''' config_file = global_config_file() model_manager = ModelManager(OmegaConf.load(config_file)) + for mod in models: + assert (mod in model_manager.model_names()), f'** Unknown model "{mod}"' + assert (model_manager.model_info(mod).get('format',None) == 'diffusers'), f'** {mod} is not a diffusers model. It must be optimized before merging.' model_ids_or_paths = [model_manager.model_name_or_path(x) for x in models] pipe = DiffusionPipeline.from_pretrained(model_ids_or_paths[0], diff --git a/scripts/merge_models.py b/scripts/merge_models.py new file mode 100755 index 0000000000..1d8ac10018 --- /dev/null +++ b/scripts/merge_models.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python + +import argparse +import os +import sys +import traceback +from pathlib import Path + +from omegaconf import OmegaConf + +from ldm.invoke.globals import (Globals, global_cache_dir, global_config_file, + global_set_root) +from ldm.invoke.model_manager import ModelManager + +parser = argparse.ArgumentParser(description="InvokeAI textual inversion training") +parser.add_argument( + "--root_dir", + "--root-dir", + type=Path, + default=Globals.root, + help="Path to the invokeai runtime directory", +) +parser.add_argument( + "--models", + required=True, + type=str, + nargs="+", + help="Two to three model names to be merged", +) +parser.add_argument( + "--merged_model_name", + "--destination", + dest="merged_model_name", + type=str, + help="Name of the output model. If not specified, will be the concatenation of the input model names.", +) +parser.add_argument( + "--alpha", + type=float, + default=0.5, + help="The interpolation parameter, ranging from 0 to 1. It affects the ratio in which the checkpoints are merged. Higher values give more weight to the 2d and 3d models", +) +parser.add_argument( + "--interpolation", + dest="interp", + type=str, + choices=["weighted_sum", "sigmoid", "inv_sigmoid", "add_difference"], + default="weighted_sum", + help='Interpolation method to use. If three models are present, only "add_difference" will work.', +) +parser.add_argument( + "--force", + action="store_true", + help="Try to merge models even if they are incompatible with each other", +) +parser.add_argument( + "--clobber", + "--overwrite", + dest='clobber', + action="store_true", + help="Overwrite the merged model if --merged_model_name already exists", +) + +args = parser.parse_args() +global_set_root(args.root_dir) + +assert args.alpha >= 0 and args.alpha <= 1.0, "alpha must be between 0 and 1" +assert len(args.models) >= 1 and len(args.models) <= 3, "provide 2 or 3 models to merge" + +if not args.merged_model_name: + args.merged_model_name = "+".join(args.models) + print( + f'>> No --merged_model_name provided. Defaulting to "{args.merged_model_name}"' + ) + +model_manager = ModelManager(OmegaConf.load(global_config_file())) +assert (args.clobber or args.merged_model_name not in model_manager.model_names()), f'A model named "{args.merged_model_name}" already exists. Use --clobber to overwrite.' + +# It seems that the merge pipeline is not honoring cache_dir, so we set the +# HF_HOME environment variable here *before* we load diffusers. +cache_dir = str(global_cache_dir("diffusers")) +os.environ["HF_HOME"] = cache_dir +from ldm.invoke.merge_diffusers import merge_diffusion_models + +try: + merge_diffusion_models(**vars(args)) + print(f'>> Models merged into new model: "{args.merged_model_name}".') +except Exception as e: + print(f"** An error occurred while merging the pipelines: {str(e)}") + print("** DETAILS:") + print(traceback.format_exc()) + sys.exit(-1) diff --git a/scripts/merge_fe.py b/scripts/merge_models_fe.py similarity index 100% rename from scripts/merge_fe.py rename to scripts/merge_models_fe.py diff --git a/scripts/merge_embeddings.py b/scripts/orig_scripts/merge_embeddings.py similarity index 100% rename from scripts/merge_embeddings.py rename to scripts/orig_scripts/merge_embeddings.py From ffcc5ad79582575fb145c69173226a808999e43b Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Mon, 23 Jan 2023 00:35:16 -0500 Subject: [PATCH 33/41] conversion script uses invokeai models cache by default --- ldm/invoke/ckpt_to_diffuser.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/ldm/invoke/ckpt_to_diffuser.py b/ldm/invoke/ckpt_to_diffuser.py index 86281623a6..9b1735f831 100644 --- a/ldm/invoke/ckpt_to_diffuser.py +++ b/ldm/invoke/ckpt_to_diffuser.py @@ -21,7 +21,7 @@ import os import re import torch from pathlib import Path -from ldm.invoke.globals import Globals +from ldm.invoke.globals import Globals, global_cache_dir from safetensors.torch import load_file try: @@ -637,7 +637,7 @@ def convert_ldm_bert_checkpoint(checkpoint, config): def convert_ldm_clip_checkpoint(checkpoint): - text_model = CLIPTextModel.from_pretrained("openai/clip-vit-large-patch14") + text_model = CLIPTextModel.from_pretrained("openai/clip-vit-large-patch14",cache_dir=global_cache_dir('hub')) keys = list(checkpoint.keys()) @@ -677,7 +677,8 @@ textenc_pattern = re.compile("|".join(protected.keys())) def convert_paint_by_example_checkpoint(checkpoint): - config = CLIPVisionConfig.from_pretrained("openai/clip-vit-large-patch14") + cache_dir = global_cache_dir('hub') + config = CLIPVisionConfig.from_pretrained("openai/clip-vit-large-patch14",cache_dir=cache_dir) model = PaintByExampleImageEncoder(config) keys = list(checkpoint.keys()) @@ -744,7 +745,8 @@ def convert_paint_by_example_checkpoint(checkpoint): def convert_open_clip_checkpoint(checkpoint): - text_model = CLIPTextModel.from_pretrained("stabilityai/stable-diffusion-2", subfolder="text_encoder") + cache_dir=global_cache_dir('hub') + text_model = CLIPTextModel.from_pretrained("stabilityai/stable-diffusion-2", subfolder="text_encoder", cache_dir=cache_dir) keys = list(checkpoint.keys()) @@ -795,6 +797,7 @@ def convert_ckpt_to_diffuser(checkpoint_path:str, ): checkpoint = load_file(checkpoint_path) if Path(checkpoint_path).suffix == '.safetensors' else torch.load(checkpoint_path) + cache_dir = global_cache_dir('hub') # Sometimes models don't have the global_step item if "global_step" in checkpoint: @@ -904,7 +907,7 @@ def convert_ckpt_to_diffuser(checkpoint_path:str, if model_type == "FrozenOpenCLIPEmbedder": text_model = convert_open_clip_checkpoint(checkpoint) - tokenizer = CLIPTokenizer.from_pretrained("stabilityai/stable-diffusion-2", subfolder="tokenizer") + tokenizer = CLIPTokenizer.from_pretrained("stabilityai/stable-diffusion-2", subfolder="tokenizer",cache_dir=global_cache_dir('diffusers')) pipe = StableDiffusionPipeline( vae=vae, text_encoder=text_model, @@ -917,8 +920,8 @@ def convert_ckpt_to_diffuser(checkpoint_path:str, ) elif model_type == "PaintByExample": vision_model = convert_paint_by_example_checkpoint(checkpoint) - tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14") - feature_extractor = AutoFeatureExtractor.from_pretrained("CompVis/stable-diffusion-safety-checker") + tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14",cache_dir=cache_dir) + feature_extractor = AutoFeatureExtractor.from_pretrained("CompVis/stable-diffusion-safety-checker",cache_dir=cache_dir) pipe = PaintByExamplePipeline( vae=vae, image_encoder=vision_model, @@ -929,9 +932,9 @@ def convert_ckpt_to_diffuser(checkpoint_path:str, ) elif model_type in ['FrozenCLIPEmbedder','WeightedFrozenCLIPEmbedder']: text_model = convert_ldm_clip_checkpoint(checkpoint) - tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14") - safety_checker = StableDiffusionSafetyChecker.from_pretrained("CompVis/stable-diffusion-safety-checker") - feature_extractor = AutoFeatureExtractor.from_pretrained("CompVis/stable-diffusion-safety-checker") + tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14",cache_dir=cache_dir) + safety_checker = StableDiffusionSafetyChecker.from_pretrained("CompVis/stable-diffusion-safety-checker",cache_dir=cache_dir) + feature_extractor = AutoFeatureExtractor.from_pretrained("CompVis/stable-diffusion-safety-checker",cache_dir=cache_dir) pipe = StableDiffusionPipeline( vae=vae, text_encoder=text_model, @@ -944,7 +947,7 @@ def convert_ckpt_to_diffuser(checkpoint_path:str, else: text_config = create_ldm_bert_config(original_config) text_model = convert_ldm_bert_checkpoint(checkpoint, text_config) - tokenizer = BertTokenizerFast.from_pretrained("bert-base-uncased") + tokenizer = BertTokenizerFast.from_pretrained("bert-base-uncased",cache_dir=cache_dir) pipe = LDMTextToImagePipeline(vqvae=vae, bert=text_model, tokenizer=tokenizer, unet=unet, scheduler=scheduler) pipe.save_pretrained( From 7dead7696c55370050125ae60f010d2e4856859d Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Mon, 23 Jan 2023 00:43:15 -0500 Subject: [PATCH 34/41] fixed setup.py to install the new scripts --- setup.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index afec71b967..c9809c0c92 100644 --- a/setup.py +++ b/setup.py @@ -92,8 +92,9 @@ setup( 'Topic :: Scientific/Engineering :: Image Processing', ], scripts = ['scripts/invoke.py','scripts/configure_invokeai.py', 'scripts/sd-metadata.py', - 'scripts/preload_models.py', 'scripts/images2prompt.py','scripts/merge_embeddings.py', - 'scripts/textual_inversion_fe.py','scripts/textual_inversion.py' + 'scripts/preload_models.py', 'scripts/images2prompt.py', + 'scripts/textual_inversion_fe.py','scripts/textual_inversion.py', + 'scripts/merge_models_fe.py', 'scripts/merge_models.py', ], data_files=FRONTEND_FILES, ) From d7554b01fd9c1d5fa0da58983ad99522f3794a83 Mon Sep 17 00:00:00 2001 From: Kevin Turner <83819+keturn@users.noreply.github.com> Date: Mon, 23 Jan 2023 00:24:06 -0800 Subject: [PATCH 35/41] fix typo in prompt --- ldm/invoke/CLI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldm/invoke/CLI.py b/ldm/invoke/CLI.py index abf83bc112..4d637e6ea2 100644 --- a/ldm/invoke/CLI.py +++ b/ldm/invoke/CLI.py @@ -608,7 +608,7 @@ def import_diffuser_model(path_or_repo:str, gen, opt, completer)->str: model_description=default_description ) vae = None - if input('Replace this model\'s VAE with "stabilityai/sd-vae-ft-se"? [n] ').strip() in ('y','Y'): + if input('Replace this model\'s VAE with "stabilityai/sd-vae-ft-mse"? [n] ').strip() in ('y','Y'): vae = dict(repo_id='stabilityai/sd-vae-ft-mse') if not manager.import_diffuser_model( From 1eee08a07085386f7b05948a2bc777f626e74017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=BE=A4=20=E5=85=8B=E5=B9=B8?= Date: Mon, 23 Jan 2023 18:32:48 +0900 Subject: [PATCH 36/41] add Japanese Translation --- frontend/public/locales/common/ja.json | 60 +++++++++++++++++ frontend/public/locales/gallery/ja.json | 17 +++++ frontend/public/locales/modelmanager/ja.json | 68 ++++++++++++++++++++ frontend/public/locales/settings/ja.json | 14 ++++ frontend/public/locales/tooltip/ja.json | 16 +++++ 5 files changed, 175 insertions(+) create mode 100644 frontend/public/locales/common/ja.json create mode 100644 frontend/public/locales/gallery/ja.json create mode 100644 frontend/public/locales/modelmanager/ja.json create mode 100644 frontend/public/locales/settings/ja.json create mode 100644 frontend/public/locales/tooltip/ja.json diff --git a/frontend/public/locales/common/ja.json b/frontend/public/locales/common/ja.json new file mode 100644 index 0000000000..be225e9d34 --- /dev/null +++ b/frontend/public/locales/common/ja.json @@ -0,0 +1,60 @@ +{ + "hotkeysLabel": "Hotkeys", + "themeLabel": "テーマ", + "languagePickerLabel": "言語選択", + "reportBugLabel": "バグ報告", + "githubLabel": "Github", + "discordLabel": "Discord", + "settingsLabel": "設定", + "darkTheme": "ダーク", + "lightTheme": "ライト", + "greenTheme": "緑", + "langEnglish": "English", + "langRussian": "Russian", + "langItalian": "Italian", + "langBrPortuguese": "Portuguese (Brazilian)", + "langGerman": "German", + "langPortuguese": "Portuguese", + "langFrench": "French", + "langPolish": "Polish", + "langSimplifiedChinese": "Simplified Chinese", + "langSpanish": "Spanish", + "text2img": "Text To Image", + "img2img": "Image To Image", + "unifiedCanvas": "Unified Canvas", + "nodes": "Nodes", + "nodesDesc": "現在、画像生成のためのノードベースシステムを開発中です。機能についてのアップデートにご期待ください。", + "postProcessing": "後処理", + "postProcessDesc1": "Invoke AIは、多彩な後処理の機能を備えています。アップスケーリングと顔修復は、すでにWebUI上で利用可能です。これらは、[Text To Image]および[Image To Image]タブの[詳細オプション]メニューからアクセスできます。また、現在の画像表示の上やビューア内の画像アクションボタンを使って、画像を直接処理することもできます。", + "postProcessDesc2": "より高度な後処理の機能を実現するための専用UIを近日中にリリース予定です。", + "postProcessDesc3": "Invoke AI CLIでは、この他にもEmbiggenをはじめとする様々な機能を利用することができます。", + "training": "追加学習", + "trainingDesc1": "Textual InversionとDreamboothを使って、WebUIから独自のEmbeddingとチェックポイントを追加学習するための専用ワークフローです。", + "trainingDesc2": "InvokeAIは、すでにメインスクリプトを使ったTextual Inversionによるカスタム埋め込み追加学習にも対応しています。", + "upload": "アップロード", + "close": "閉じる", + "load": "ロード", + "back": "戻る", + "statusConnected": "接続済", + "statusDisconnected": "切断済", + "statusError": "エラー", + "statusPreparing": "準備中", + "statusProcessingCanceled": "処理をキャンセル", + "statusProcessingComplete": "処理完了", + "statusGenerating": "生成中", + "statusGeneratingTextToImage": "Text To Imageで生成中", + "statusGeneratingImageToImage": "Image To Imageで生成中", + "statusGeneratingInpainting": "Generating Inpainting", + "statusGeneratingOutpainting": "Generating Outpainting", + "statusGenerationComplete": "生成完了", + "statusIterationComplete": "Iteration Complete", + "statusSavingImage": "画像を保存", + "statusRestoringFaces": "顔の修復", + "statusRestoringFacesGFPGAN": "顔の修復 (GFPGAN)", + "statusRestoringFacesCodeFormer": "顔の修復 (CodeFormer)", + "statusUpscaling": "アップスケーリング", + "statusUpscalingESRGAN": "アップスケーリング (ESRGAN)", + "statusLoadingModel": "モデルを読み込む", + "statusModelChanged": "モデルを変更" + } + \ No newline at end of file diff --git a/frontend/public/locales/gallery/ja.json b/frontend/public/locales/gallery/ja.json new file mode 100644 index 0000000000..923dab1c8e --- /dev/null +++ b/frontend/public/locales/gallery/ja.json @@ -0,0 +1,17 @@ +{ + "generations": "Generations", + "showGenerations": "Show Generations", + "uploads": "アップロード", + "showUploads": "アップロードした画像を見る", + "galleryImageSize": "画像のサイズ", + "galleryImageResetSize": "サイズをリセット", + "gallerySettings": "ギャラリーの設定", + "maintainAspectRatio": "アスペクト比を維持", + "autoSwitchNewImages": "Auto-Switch to New Images", + "singleColumnLayout": "シングルカラムレイアウト", + "pinGallery": "ギャラリーにピン留め", + "allImagesLoaded": "すべての画像を読み込む", + "loadMore": "さらに読み込む", + "noImagesInGallery": "ギャラリーに画像がありません" + } + \ No newline at end of file diff --git a/frontend/public/locales/modelmanager/ja.json b/frontend/public/locales/modelmanager/ja.json new file mode 100644 index 0000000000..b8a64794c9 --- /dev/null +++ b/frontend/public/locales/modelmanager/ja.json @@ -0,0 +1,68 @@ +{ + "modelManager": "モデルマネージャ", + "model": "モデル", + "allModels": "すべてのモデル", + "checkpointModels": "Checkpoints", + "diffusersModels": "Diffusers", + "safetensorModels": "SafeTensors", + "modelAdded": "モデルを追加", + "modelUpdated": "モデルをアップデート", + "modelEntryDeleted": "Model Entry Deleted", + "cannotUseSpaces": "Cannot Use Spaces", + "addNew": "新規に追加", + "addNewModel": "新規モデル追加", + "addCheckpointModel": "Checkpointを追加 / Safetensorモデル", + "addDiffuserModel": "Diffusersを追加", + "addManually": "手動で追加", + "manual": "手動", + "name": "名前", + "nameValidationMsg": "モデルの名前を入力", + "description": "概要", + "descriptionValidationMsg": "モデルの概要を入力", + "config": "Config", + "configValidationMsg": "モデルの設定ファイルへのパス", + "modelLocation": "モデルの場所", + "modelLocationValidationMsg": "モデルが配置されている場所へのパス。", + "repo_id": "Repo ID", + "repoIDValidationMsg": "モデルのリモートリポジトリ", + "vaeLocation": "VAEの場所", + "vaeLocationValidationMsg": "Vaeが配置されている場所へのパス", + "vaeRepoID": "VAE Repo ID", + "vaeRepoIDValidationMsg": "Vaeのリモートリポジトリ", + "width": "幅", + "widthValidationMsg": "モデルのデフォルトの幅", + "height": "高さ", + "heightValidationMsg": "モデルのデフォルトの高さ", + "addModel": "モデルを追加", + "updateModel": "モデルをアップデート", + "availableModels": "モデルを有効化", + "search": "検索", + "load": "Load", + "active": "active", + "notLoaded": "読み込まれていません", + "cached": "キャッシュ済", + "checkpointFolder": "Checkpointフォルダ", + "clearCheckpointFolder": "Checkpointフォルダ内を削除", + "findModels": "モデルを見つける", + "scanAgain": "再度スキャン", + "modelsFound": "モデルを発見", + "selectFolder": "フォルダを選択", + "selected": "選択済", + "selectAll": "すべて選択", + "deselectAll": "すべて選択解除", + "showExisting": "既存を表示", + "addSelected": "選択済を追加", + "modelExists": "モデルの有無", + "selectAndAdd": "以下のモデルを選択し、追加できます。", + "noModelsFound": "モデルが見つかりません。", + "delete": "削除", + "deleteModel": "モデルを削除", + "deleteConfig": "設定を削除", + "deleteMsg1": "InvokeAIからこのモデルエントリーを削除してよろしいですか?", + "deleteMsg2": "これは、ドライブからモデルのCheckpointファイルを削除するものではありません。必要であればそれらを読み込むことができます。", + "formMessageDiffusersModelLocation": "Diffusersモデルの場所", + "formMessageDiffusersModelLocationDesc": "最低でも1つは入力してください。", + "formMessageDiffusersVAELocation": "VAEの場所s", + "formMessageDiffusersVAELocationDesc": "指定しない場合、InvokeAIは上記のモデルの場所にあるVAEファイルを探します。" + } + \ No newline at end of file diff --git a/frontend/public/locales/settings/ja.json b/frontend/public/locales/settings/ja.json new file mode 100644 index 0000000000..0af09521b2 --- /dev/null +++ b/frontend/public/locales/settings/ja.json @@ -0,0 +1,14 @@ +{ + "models": "モデル", + "displayInProgress": "生成中の画像を表示する", + "saveSteps": "nステップごとに画像を保存", + "confirmOnDelete": "削除時に確認", + "displayHelpIcons": "ヘルプアイコンを表示", + "useCanvasBeta": "キャンバスレイアウト(Beta)を使用する", + "enableImageDebugging": "画像のデバッグを有効化", + "resetWebUI": "WebUIをリセット", + "resetWebUIDesc1": "WebUIのリセットは、画像と保存された設定のキャッシュをリセットするだけです。画像を削除するわけではありません。", + "resetWebUIDesc2": "もしギャラリーに画像が表示されないなど、何か問題が発生した場合はGitHubにissueを提出する前にリセットを試してください。", + "resetComplete": "WebUIはリセットされました。F5を押して再読み込みしてください。" + } + \ No newline at end of file diff --git a/frontend/public/locales/tooltip/ja.json b/frontend/public/locales/tooltip/ja.json new file mode 100644 index 0000000000..7f3eaaa53b --- /dev/null +++ b/frontend/public/locales/tooltip/ja.json @@ -0,0 +1,16 @@ +{ + "feature": { + "prompt": "これはプロンプトフィールドです。プロンプトには生成オブジェクトや文法用語が含まれます。プロンプトにも重み(Tokenの重要度)を付けることができますが、CLIコマンドやパラメータは機能しません。", + "gallery": "ギャラリーは、出力先フォルダから生成物を表示します。設定はファイル内に保存され、コンテキストメニューからアクセスできます。.", + "other": "These options will enable alternative processing modes for Invoke. 'Seamless tiling' will create repeating patterns in the output. 'High resolution' is generation in two steps with img2img: use this setting when you want a larger and more coherent image without artifacts. It will take longer that usual txt2img.", + "seed": "シード値は、画像が形成される際の初期ノイズに影響します。以前の画像から既に存在するシードを使用することができます。ノイズしきい値は高いCFG値でのアーティファクトを軽減するために使用され、Perlinは生成中にPerlinノイズを追加します(0-10の範囲を試してみてください): どちらも出力にバリエーションを追加するのに役立ちます。", + "variations": "0.1から1.0の間の値で試し、付与されたシードに対する結果を変えてみてください。面白いバリュエーションは0.1〜0.3の間です。", + "upscale": "生成直後の画像をアップスケールするには、ESRGANを使用します。", + "faceCorrection": "GFPGANまたはCodeformerによる顔の修復: 画像内の顔を検出し不具合を修正するアルゴリズムです。高い値を設定すると画像がより変化し、より魅力的な顔になります。Codeformerは顔の修復を犠牲にして、元の画像をできる限り保持します。", + "imageToImage": "Image To Imageは任意の画像を初期値として読み込み、プロンプトとともに新しい画像を生成するために使用されます。値が高いほど結果画像はより変化します。0.0から1.0までの値が可能で、推奨範囲は0.25から0.75です。", + "boundingBox": "バウンディングボックスは、Text To ImageまたはImage To Imageの幅/高さの設定と同じです。ボックス内の領域のみが処理されます。", + "seamCorrection": "キャンバス上の生成された画像間に発生する可視可能な境界の処理を制御します。", + "infillAndScaling": "Manage infill methods (used on masked or erased areas of the canvas) and scaling (useful for small bounding box sizes)." + } + } + \ No newline at end of file From b9c9b947cdb65f2204742e1a7c484fa188dc8ce2 Mon Sep 17 00:00:00 2001 From: Katsuyuki-Karasawa <4ranci0ne@gmail.com> Date: Tue, 24 Jan 2023 03:25:29 +0900 Subject: [PATCH 37/41] update japanese translation --- frontend/public/locales/hotkeys/ja.json | 208 ++++++++++++++++++ frontend/public/locales/options/ja.json | 63 ++++++ frontend/public/locales/toast/ja.json | 32 +++ frontend/public/locales/unifiedcanvas/ja.json | 60 +++++ 4 files changed, 363 insertions(+) create mode 100644 frontend/public/locales/hotkeys/ja.json create mode 100644 frontend/public/locales/options/ja.json create mode 100644 frontend/public/locales/toast/ja.json create mode 100644 frontend/public/locales/unifiedcanvas/ja.json diff --git a/frontend/public/locales/hotkeys/ja.json b/frontend/public/locales/hotkeys/ja.json new file mode 100644 index 0000000000..d5e448246f --- /dev/null +++ b/frontend/public/locales/hotkeys/ja.json @@ -0,0 +1,208 @@ +{ + "keyboardShortcuts": "キーボードショートカット", + "appHotkeys": "アプリのホットキー", + "generalHotkeys": "Generalのホットキー", + "galleryHotkeys": "ギャラリーのホットキー", + "unifiedCanvasHotkeys": "Unified Canvasのホットキー", + "invoke": { + "title": "Invoke", + "desc": "画像を生成" + }, + "cancel": { + "title": "キャンセル", + "desc": "画像の生成をキャンセル" + }, + "focusPrompt": { + "title": "Focus Prompt", + "desc": "プロンプトテキストボックスにフォーカス" + }, + "toggleOptions": { + "title": "オプションパネルのトグル", + "desc": "オプションパネルの開閉" + }, + "pinOptions": { + "title": "ピン", + "desc": "オプションパネルを固定" + }, + "toggleViewer": { + "title": "ビュワーのトグル", + "desc": "ビュワーを開閉" + }, + "toggleGallery": { + "title": "ギャラリーのトグル", + "desc": "ギャラリードロワーの開閉" + }, + "maximizeWorkSpace": { + "title": "作業領域の最大化", + "desc": "パネルを閉じて、作業領域を最大に" + }, + "changeTabs": { + "title": "タブの切替", + "desc": "他の作業領域と切替" + }, + "consoleToggle": { + "title": "コンソールのトグル", + "desc": "コンソールの開閉" + }, + "setPrompt": { + "title": "プロンプトをセット", + "desc": "現在の画像のプロンプトを使用" + }, + "setSeed": { + "title": "シード値をセット", + "desc": "現在の画像のシード値を使用" + }, + "setParameters": { + "title": "パラメータをセット", + "desc": "現在の画像のすべてのパラメータを使用" + }, + "restoreFaces": { + "title": "顔の修復", + "desc": "現在の画像を修復" + }, + "upscale": { + "title": "アップスケール", + "desc": "現在の画像をアップスケール" + }, + "showInfo": { + "title": "情報を見る", + "desc": "現在の画像のメタデータ情報を表示" + }, + "sendToImageToImage": { + "title": "Image To Imageに転送", + "desc": "現在の画像をImage to Imageに転送" + }, + "deleteImage": { + "title": "画像を削除", + "desc": "現在の画像を削除" + }, + "closePanels": { + "title": "パネルを閉じる", + "desc": "開いているパネルを閉じる" + }, + "previousImage": { + "title": "前の画像", + "desc": "ギャラリー内の1つ前の画像を表示" + }, + "nextImage": { + "title": "次の画像", + "desc": "ギャラリー内の1つ後の画像を表示" + }, + "toggleGalleryPin": { + "title": "ギャラリードロワーの固定", + "desc": "ギャラリーをUIにピン留め/解除" + }, + "increaseGalleryThumbSize": { + "title": "ギャラリーの画像を拡大", + "desc": "ギャラリーのサムネイル画像を拡大" + }, + "decreaseGalleryThumbSize": { + "title": "ギャラリーの画像サイズを縮小", + "desc": "ギャラリーのサムネイル画像を縮小" + }, + "selectBrush": { + "title": "ブラシを選択", + "desc": "ブラシを選択" + }, + "selectEraser": { + "title": "消しゴムを選択", + "desc": "消しゴムを選択" + }, + "decreaseBrushSize": { + "title": "ブラシサイズを縮小", + "desc": "ブラシ/消しゴムのサイズを縮小" + }, + "increaseBrushSize": { + "title": "ブラシサイズを拡大", + "desc": "ブラシ/消しゴムのサイズを拡大" + }, + "decreaseBrushOpacity": { + "title": "ブラシの不透明度を下げる", + "desc": "キャンバスブラシの不透明度を下げる" + }, + "increaseBrushOpacity": { + "title": "ブラシの不透明度を上げる", + "desc": "キャンバスブラシの不透明度を上げる" + }, + "moveTool": { + "title": "Move Tool", + "desc": "Allows canvas navigation" + }, + "fillBoundingBox": { + "title": "バウンディングボックスを塗りつぶす", + "desc": "ブラシの色でバウンディングボックス領域を塗りつぶす" + }, + "eraseBoundingBox": { + "title": "バウンディングボックスを消す", + "desc": "バウンディングボックス領域を消す" + }, + "colorPicker": { + "title": "カラーピッカーを選択", + "desc": "カラーピッカーを選択" + }, + "toggleSnap": { + "title": "Toggle Snap", + "desc": "Toggles Snap to Grid" + }, + "quickToggleMove": { + "title": "Quick Toggle Move", + "desc": "Temporarily toggles Move mode" + }, + "toggleLayer": { + "title": "レイヤーを切替", + "desc": "マスク/ベースレイヤの選択を切替" + }, + "clearMask": { + "title": "マスクを消す", + "desc": "マスク全体を消す" + }, + "hideMask": { + "title": "マスクを非表示", + "desc": "マスクを表示/非表示" + }, + "showHideBoundingBox": { + "title": "バウンディングボックスを表示/非表示", + "desc": "バウンディングボックスの表示/非表示を切替" + }, + "mergeVisible": { + "title": "Merge Visible", + "desc": "Merge all visible layers of canvas" + }, + "saveToGallery": { + "title": "ギャラリーに保存", + "desc": "現在のキャンバスをギャラリーに保存" + }, + "copyToClipboard": { + "title": "クリップボードにコピー", + "desc": "現在のキャンバスをクリップボードにコピー" + }, + "downloadImage": { + "title": "画像をダウンロード", + "desc": "現在の画像をダウンロード" + }, + "undoStroke": { + "title": "Undo Stroke", + "desc": "Undo a brush stroke" + }, + "redoStroke": { + "title": "Redo Stroke", + "desc": "Redo a brush stroke" + }, + "resetView": { + "title": "キャンバスをリセット", + "desc": "キャンバスをリセット" + }, + "previousStagingImage": { + "title": "Previous Staging Image", + "desc": "Previous Staging Area Image" + }, + "nextStagingImage": { + "title": "Next Staging Image", + "desc": "Next Staging Area Image" + }, + "acceptStagingImage": { + "title": "Accept Staging Image", + "desc": "Accept Current Staging Area Image" + } + } + \ No newline at end of file diff --git a/frontend/public/locales/options/ja.json b/frontend/public/locales/options/ja.json new file mode 100644 index 0000000000..ffc6dd3f7d --- /dev/null +++ b/frontend/public/locales/options/ja.json @@ -0,0 +1,63 @@ +{ + "images": "画像", + "steps": "ステップ数", + "cfgScale": "CFG Scale", + "width": "幅", + "height": "高さ", + "sampler": "Sampler", + "seed": "シード値", + "randomizeSeed": "ランダムなシード値", + "shuffle": "シャッフル", + "noiseThreshold": "Noise Threshold", + "perlinNoise": "Perlin Noise", + "variations": "Variations", + "variationAmount": "Variation Amount", + "seedWeights": "シード値の重み", + "faceRestoration": "顔の修復", + "restoreFaces": "顔の修復", + "type": "Type", + "strength": "強度", + "upscaling": "アップスケーリング", + "upscale": "アップスケール", + "upscaleImage": "画像をアップスケール", + "scale": "Scale", + "otherOptions": "その他のオプション", + "seamlessTiling": "Seamless Tiling", + "hiresOptim": "High Res Optimization", + "imageFit": "Fit Initial Image To Output Size", + "codeformerFidelity": "Fidelity", + "seamSize": "Seam Size", + "seamBlur": "Seam Blur", + "seamStrength": "Seam Strength", + "seamSteps": "Seam Steps", + "inpaintReplace": "Inpaint Replace", + "scaleBeforeProcessing": "処理前のスケール", + "scaledWidth": "幅のスケール", + "scaledHeight": "高さのスケール", + "infillMethod": "Infill Method", + "tileSize": "Tile Size", + "boundingBoxHeader": "バウンディングボックス", + "seamCorrectionHeader": "Seam Correction", + "infillScalingHeader": "Infill and Scaling", + "img2imgStrength": "Image To Imageの強度", + "toggleLoopback": "Toggle Loopback", + "invoke": "Invoke", + "cancel": "キャンセル", + "promptPlaceholder": "Type prompt here. [negative tokens], (upweight)++, (downweight)--, swap and blend are available (see docs)", + "sendTo": "転送", + "sendToImg2Img": "Image to Imageに転送", + "sendToUnifiedCanvas": "Unified Canvasに転送", + "copyImageToLink": "Copy Image To Link", + "downloadImage": "画像をダウンロード", + "openInViewer": "ビュワーを開く", + "closeViewer": "ビュワーを閉じる", + "usePrompt": "プロンプトを使用", + "useSeed": "シード値を使用", + "useAll": "すべてを使用", + "useInitImg": "Use Initial Image", + "info": "情報", + "deleteImage": "画像を削除", + "initialImage": "Inital Image", + "showOptionsPanel": "オプションパネルを表示" + } + \ No newline at end of file diff --git a/frontend/public/locales/toast/ja.json b/frontend/public/locales/toast/ja.json new file mode 100644 index 0000000000..e43a03a2b5 --- /dev/null +++ b/frontend/public/locales/toast/ja.json @@ -0,0 +1,32 @@ +{ + "tempFoldersEmptied": "Temp Folder Emptied", + "uploadFailed": "アップロード失敗", + "uploadFailedMultipleImagesDesc": "一度にアップロードできる画像は1枚のみです。", + "uploadFailedUnableToLoadDesc": "ファイルを読み込むことができません。", + "downloadImageStarted": "画像ダウンロード開始", + "imageCopied": "画像をコピー", + "imageLinkCopied": "画像のURLをコピー", + "imageNotLoaded": "画像を読み込めません。", + "imageNotLoadedDesc": "Image To Imageに転送する画像が見つかりません。", + "imageSavedToGallery": "画像をギャラリーに保存する", + "canvasMerged": "Canvas Merged", + "sentToImageToImage": "Image To Imageに転送", + "sentToUnifiedCanvas": "Unified Canvasに転送", + "parametersSet": "Parameters Set", + "parametersNotSet": "Parameters Not Set", + "parametersNotSetDesc": "この画像にはメタデータがありません。", + "parametersFailed": "パラメータ読み込みの不具合", + "parametersFailedDesc": "initイメージを読み込めません。", + "seedSet": "Seed Set", + "seedNotSet": "Seed Not Set", + "seedNotSetDesc": "この画像のシード値が見つかりません。", + "promptSet": "Prompt Set", + "promptNotSet": "Prompt Not Set", + "promptNotSetDesc": "この画像のプロンプトが見つかりませんでした。", + "upscalingFailed": "アップスケーリング失敗", + "faceRestoreFailed": "顔の修復に失敗", + "metadataLoadFailed": "メタデータの読み込みに失敗。", + "initialImageSet": "Initial Image Set", + "initialImageNotSet": "Initial Image Not Set", + "initialImageNotSetDesc": "Could not load initial image" + } \ No newline at end of file diff --git a/frontend/public/locales/unifiedcanvas/ja.json b/frontend/public/locales/unifiedcanvas/ja.json new file mode 100644 index 0000000000..2a221519ff --- /dev/null +++ b/frontend/public/locales/unifiedcanvas/ja.json @@ -0,0 +1,60 @@ +{ + "layer": "Layer", + "base": "Base", + "mask": "マスク", + "maskingOptions": "マスクのオプション", + "enableMask": "マスクを有効化", + "preserveMaskedArea": "マスク領域の保存", + "clearMask": "マスクを解除", + "brush": "ブラシ", + "eraser": "消しゴム", + "fillBoundingBox": "バウンディングボックスの塗りつぶし", + "eraseBoundingBox": "バウンディングボックスの消去", + "colorPicker": "カラーピッカー", + "brushOptions": "ブラシオプション", + "brushSize": "サイズ", + "move": "Move", + "resetView": "Reset View", + "mergeVisible": "Merge Visible", + "saveToGallery": "ギャラリーに保存", + "copyToClipboard": "クリップボードにコピー", + "downloadAsImage": "画像としてダウンロード", + "undo": "取り消し", + "redo": "やり直し", + "clearCanvas": "キャンバスを片付ける", + "canvasSettings": "キャンバスの設定", + "showIntermediates": "Show Intermediates", + "showGrid": "グリッドを表示", + "snapToGrid": "Snap to Grid", + "darkenOutsideSelection": "外周を暗くする", + "autoSaveToGallery": "ギャラリーに自動保存", + "saveBoxRegionOnly": "ボックス領域のみ保存", + "limitStrokesToBox": "Limit Strokes to Box", + "showCanvasDebugInfo": "キャンバスのデバッグ情報を表示", + "clearCanvasHistory": "キャンバスの履歴を削除", + "clearHistory": "履歴を削除", + "clearCanvasHistoryMessage": "履歴を消去すると現在のキャンバスは残りますが、取り消しややり直しの履歴は不可逆的に消去されます。", + "clearCanvasHistoryConfirm": "履歴を削除しますか?", + "emptyTempImageFolder": "Empty Temp Image Folde", + "emptyFolder": "空のフォルダ", + "emptyTempImagesFolderMessage": "一時フォルダを空にすると、Unified Canvasも完全にリセットされます。これには、すべての取り消し/やり直しの履歴、ステージング領域の画像、およびキャンバスのベースレイヤーが含まれます。", + "emptyTempImagesFolderConfirm": "一時フォルダを削除しますか?", + "activeLayer": "Active Layer", + "canvasScale": "Canvas Scale", + "boundingBox": "バウンディングボックス", + "scaledBoundingBox": "Scaled Bounding Box", + "boundingBoxPosition": "バウンディングボックスの位置", + "canvasDimensions": "キャンバスの大きさ", + "canvasPosition": "キャンバスの位置", + "cursorPosition": "カーソルの位置", + "previous": "前", + "next": "次", + "accept": "同意", + "showHide": "表示/非表示", + "discardAll": "すべて破棄", + "betaClear": "Clear", + "betaDarkenOutside": "Darken Outside", + "betaLimitToBox": "Limit To Box", + "betaPreserveMasked": "Preserve Masked" + } + \ No newline at end of file From 31a967965bcdb91176d389f65d065e9adc1d463c Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Tue, 24 Jan 2023 08:37:12 +1300 Subject: [PATCH 38/41] Add Japanese Localization --- frontend/public/locales/common/en-US.json | 1 + frontend/public/locales/common/en.json | 1 + frontend/src/features/system/components/LanguagePicker.tsx | 1 + 3 files changed, 3 insertions(+) diff --git a/frontend/public/locales/common/en-US.json b/frontend/public/locales/common/en-US.json index b67799e789..64fbef7c02 100644 --- a/frontend/public/locales/common/en-US.json +++ b/frontend/public/locales/common/en-US.json @@ -19,6 +19,7 @@ "langPolish": "Polish", "langSimplifiedChinese": "Simplified Chinese", "langSpanish": "Spanish", + "langJapanese": "Japanese", "text2img": "Text To Image", "img2img": "Image To Image", "unifiedCanvas": "Unified Canvas", diff --git a/frontend/public/locales/common/en.json b/frontend/public/locales/common/en.json index b67799e789..64fbef7c02 100644 --- a/frontend/public/locales/common/en.json +++ b/frontend/public/locales/common/en.json @@ -19,6 +19,7 @@ "langPolish": "Polish", "langSimplifiedChinese": "Simplified Chinese", "langSpanish": "Spanish", + "langJapanese": "Japanese", "text2img": "Text To Image", "img2img": "Image To Image", "unifiedCanvas": "Unified Canvas", diff --git a/frontend/src/features/system/components/LanguagePicker.tsx b/frontend/src/features/system/components/LanguagePicker.tsx index 29c6f9071a..23d102a236 100644 --- a/frontend/src/features/system/components/LanguagePicker.tsx +++ b/frontend/src/features/system/components/LanguagePicker.tsx @@ -20,6 +20,7 @@ export default function LanguagePicker() { pl: t('common:langPolish'), zh_cn: t('common:langSimplifiedChinese'), es: t('common:langSpanish'), + ja: t('common:langJapanese'), }; const renderLanguagePicker = () => { From 66babb2e81f7de52fe2d93a6c924aebabf89bb72 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Tue, 24 Jan 2023 08:39:18 +1300 Subject: [PATCH 39/41] Japanese Localization Build --- ...y-474a75fe.js => index-legacy-6edbec57.js} | 12 +- frontend/dist/assets/index.0dadf5d0.css | 1 - frontend/dist/assets/index.1b59e83a.js | 625 ------------------ frontend/dist/assets/index.8badc8b4.css | 1 + frontend/dist/assets/index.dd470915.js | 625 ++++++++++++++++++ frontend/dist/index.html | 6 +- frontend/dist/locales/common/en-US.json | 4 + frontend/dist/locales/common/en.json | 2 + frontend/dist/locales/common/ja.json | 60 ++ frontend/dist/locales/gallery/ja.json | 17 + frontend/dist/locales/hotkeys/ja.json | 208 ++++++ frontend/dist/locales/modelmanager/en-US.json | 19 +- frontend/dist/locales/modelmanager/en.json | 16 +- frontend/dist/locales/modelmanager/ja.json | 68 ++ frontend/dist/locales/options/ja.json | 63 ++ frontend/dist/locales/settings/ja.json | 14 + frontend/dist/locales/toast/ja.json | 32 + frontend/dist/locales/tooltip/it.json | 16 +- frontend/dist/locales/tooltip/ja.json | 16 + frontend/dist/locales/tooltips/it.json | 15 - frontend/dist/locales/unifiedcanvas/ja.json | 60 ++ 21 files changed, 1227 insertions(+), 653 deletions(-) rename frontend/dist/assets/{index-legacy-474a75fe.js => index-legacy-6edbec57.js} (59%) delete mode 100644 frontend/dist/assets/index.0dadf5d0.css delete mode 100644 frontend/dist/assets/index.1b59e83a.js create mode 100644 frontend/dist/assets/index.8badc8b4.css create mode 100644 frontend/dist/assets/index.dd470915.js create mode 100644 frontend/dist/locales/common/ja.json create mode 100644 frontend/dist/locales/gallery/ja.json create mode 100644 frontend/dist/locales/hotkeys/ja.json create mode 100644 frontend/dist/locales/modelmanager/ja.json create mode 100644 frontend/dist/locales/options/ja.json create mode 100644 frontend/dist/locales/settings/ja.json create mode 100644 frontend/dist/locales/toast/ja.json create mode 100644 frontend/dist/locales/tooltip/ja.json delete mode 100644 frontend/dist/locales/tooltips/it.json create mode 100644 frontend/dist/locales/unifiedcanvas/ja.json diff --git a/frontend/dist/assets/index-legacy-474a75fe.js b/frontend/dist/assets/index-legacy-6edbec57.js similarity index 59% rename from frontend/dist/assets/index-legacy-474a75fe.js rename to frontend/dist/assets/index-legacy-6edbec57.js index b48c03f8f8..df48761d1e 100644 --- a/frontend/dist/assets/index-legacy-474a75fe.js +++ b/frontend/dist/assets/index-legacy-6edbec57.js @@ -1,4 +1,4 @@ -!function(){function e(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var r=n.call(e,t||"default");if("object"!=typeof r)return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}System.register([],(function(t,n){"use strict";var r=document.createElement("style");return r.textContent="@font-face{font-family:Inter;src:url("+new URL("Inter.b9a8e5e2.ttf",n.meta.url).href+");font-display:swap;font-weight:400;font-style:normal}@font-face{font-family:Inter;src:url("+new URL("Inter-Bold.790c108b.ttf",n.meta.url).href+');font-display:swap;font-weight:600;font-style:normal}@keyframes slideOut{0%{transform:translate(10rem)}to{transform:translate(0)}}@keyframes pulse{0%{transform:scale(1)}50%{transform:scale(1.1)}to{transform:scale(1)}}*{scrollbar-width:thick;scrollbar-color:var(--scrollbar-color) transparent}*::-webkit-scrollbar{width:8px;height:8px}*::-webkit-scrollbar-track{background:transparent}*::-webkit-scrollbar-thumb{background:var(--scrollbar-color);border-radius:8px;border:2px solid var(--scrollbar-color)}*::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-color-hover);border:2px solid var(--scrollbar-color-hover)}::-webkit-scrollbar-button{background:transparent}[data-theme=dark]{--white: rgb(255, 255, 255);--accent-color-dim: rgb(57, 25, 153);--accent-color: rgb(80, 40, 200);--accent-color-bright: rgb(104, 60, 230);--accent-color-hover: var(--accent-color-bright);--root-bg-color: rgb(10, 10, 10);--background-color: rgb(26, 26, 32);--background-color-light: rgb(40, 44, 48);--background-color-secondary: rgb(16, 16, 22);--text-color: rgb(255, 255, 255);--text-color-secondary: rgb(160, 162, 188);--subtext-color: rgb(24, 24, 34);--subtext-color-bright: rgb(48, 48, 64);--border-color: rgb(30, 30, 46);--border-color-light: rgb(60, 60, 76);--svg-color: rgb(255, 255, 255);--invalid: rgb(255, 75, 75);--invalid-secondary: rgb(120, 5, 5);--destructive-color: rgb(185, 55, 55);--destructive-color-hover: rgb(255, 75, 75);--warning-color: rgb(200, 88, 40);--warning-color-hover: rgb(230, 117, 60);--border-color-invalid: rgb(255, 80, 50);--box-shadow-color-invalid: rgb(210, 30, 10);--tab-color: rgb(30, 32, 42);--tab-hover-color: rgb(46, 48, 58);--tab-panel-bg: rgb(36, 38, 48);--tab-list-bg: var(--accent-color);--tab-list-text: rgb(202, 204, 216);--tab-list-text-inactive: rgb(92, 94, 114);--btn-base-color: rgb(30, 32, 42);--btn-base-color-hover: rgb(46, 48, 68);--btn-load-more: rgb(30, 32, 42);--btn-load-more-hover: rgb(54, 56, 66);--btn-svg-color: rgb(255, 255, 255);--btn-delete-image: rgb(182, 46, 46);--btn-checkbox-border-hover: rgb(46, 48, 68);--progress-bar-color: var(--accent-color);--prompt-bg-color: rgb(10, 10, 10);--switch-bg-color: rgb(100, 102, 110);--switch-bg-active-color: var(--accent-color);--slider-color: var(--accent-color-bright);--slider-color: rgb(151, 113, 255);--slider-mark-color: rgb(151, 113, 255);--resizeable-handle-border-color: var(--accent-color);--metadata-bg-color: rgba(0, 0, 0, .7);--metadata-json-bg-color: rgba(255, 255, 255, .1);--status-good-color: rgb(125, 255, 100);--status-good-glow: rgb(40, 215, 40);--status-working-color: rgb(255, 175, 55);--status-working-glow: rgb(255, 160, 55);--status-bad-color: rgb(255, 90, 90);--status-bad-glow: rgb(255, 40, 40);--notice-color: rgb(130, 71, 19);--settings-modal-bg: rgb(30, 32, 42);--input-checkbox-bg: rgb(60, 64, 68);--input-checkbox-checked-bg: var(--accent-color);--input-checkbox-checked-tick: rgb(0, 0, 0);--input-border-color: var(--accent-color-bright);--input-box-shadow-color: var(--accent-color);--error-level-info: rgb(200, 202, 224);--error-level-warning: rgb(255, 225, 105);--error-level-error: rgb(255, 81, 46);--console-bg-color: rgb(30, 30, 36);--console-icon-button-bg-color: rgb(50, 53, 64);--console-icon-button-bg-color-hover: rgb(70, 73, 84);--img2img-img-bg-color: rgb(30, 32, 42);--context-menu-bg-color: rgb(46, 48, 58);--context-menu-box-shadow: none;--context-menu-bg-color-hover: rgb(30, 32, 42);--floating-button-drop-shadow-color: var(--accent-color);--inpainting-alerts-bg: rgba(20, 20, 26, .75);--inpainting-alerts-icon-color: rgb(255, 255, 255);--inpainting-alerts-bg-active: var(--accent-color);--inpainting-alerts-icon-active: rgb(255, 255, 255);--inpainting-alerts-bg-alert: var(--invalid);--inpainting-alerts-icon-alert: rgb(255, 255, 255);--checkboard-dots-color: rgb(35, 35, 39);--scrollbar-color: var(--accent-color);--scrollbar-color-hover: var(--accent-color-bright)}[data-theme=light]{--white: rgb(255, 255, 255);--accent-color-dim: rgb(186, 146, 0);--accent-color: rgb(235, 185, 5);--accent-color-bright: rgb(255, 200, 0);--accent-color-hover: var(--accent-color-bright);--root-bg-color: rgb(255, 255, 255);--background-color: rgb(220, 222, 224);--background-color-light: rgb(250, 252, 254);--background-color-secondary: rgb(208, 210, 212);--text-color: rgb(0, 0, 0);--text-color-secondary: rgb(40, 40, 40);--subtext-color: rgb(24, 24, 34);--subtext-color-bright: rgb(142, 144, 146);--border-color: rgb(200, 200, 200);--border-color-light: rgb(147, 147, 147);--svg-color: rgb(50, 50, 50);--invalid: rgb(255, 75, 75);--invalid-secondary: rgb(120, 5, 5);--destructive-color: rgb(237, 51, 51);--destructive-color-hover: rgb(255, 55, 55);--warning-color: rgb(224, 142, 42);--warning-color-hover: rgb(255, 167, 60);--border-color-invalid: rgb(255, 80, 50);--box-shadow-color-invalid: none;--tab-color: rgb(202, 204, 206);--tab-hover-color: rgb(196, 198, 200);--tab-panel-bg: rgb(206, 208, 210);--tab-list-bg: rgb(235, 185, 5);--tab-list-text: rgb(0, 0, 0);--tab-list-text-inactive: rgb(106, 108, 110);--btn-base-color: rgb(184, 186, 188);--btn-base-color-hover: rgb(230, 232, 234);--btn-load-more: rgb(202, 204, 206);--btn-load-more-hover: rgb(178, 180, 182);--btn-svg-color: rgb(0, 0, 0);--btn-delete-image: rgb(213, 49, 49);--btn-checkbox-border-hover: rgb(176, 178, 182);--progress-bar-color: rgb(235, 185, 5);--prompt-bg-color: rgb(225, 227, 229);--switch-bg-color: rgb(178, 180, 182);--switch-bg-active-color: rgb(235, 185, 5);--slider-color: var(--accent-color);--slider-mark-color: rgb(0, 0, 0);--resizeable-handle-border-color: rgb(160, 162, 164);--metadata-bg-color: rgba(230, 230, 230, .9);--metadata-json-bg-color: rgba(0, 0, 0, .1);--status-good-color: rgb(21, 126, 0);--status-good-glow: var(--background-color);--status-working-color: rgb(235, 141, 0);--status-working-glow: var(--background-color);--status-bad-color: rgb(202, 0, 0);--status-bad-glow: var(--background-color);--notice-color: rgb(255, 71, 90);--settings-modal-bg: rgb(202, 204, 206);--input-checkbox-bg: rgb(167, 167, 171);--input-checkbox-checked-bg: rgb(235, 185, 5);--input-checkbox-checked-tick: rgb(0, 0, 0);--input-border-color: rgb(0, 0, 0);--input-box-shadow-color: none;--error-level-info: rgb(42, 42, 42);--error-level-warning: rgb(173, 121, 0);--error-level-error: rgb(145, 14, 0);--console-bg-color: rgb(220, 224, 230);--console-icon-button-bg-color: var(--switch-bg-color);--console-icon-button-bg-color-hover: var(--resizeable-handle-border-color);--img2img-img-bg-color: rgb(180, 182, 184);--context-menu-bg-color: var(--background-color);--context-menu-box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, .35), 0px 10px 20px -15px rgba(22, 23, 24, .2);--context-menu-bg-color-hover: var(--background-color-secondary);--floating-button-drop-shadow-color: rgba(0, 0, 0, .7);--inpainting-alerts-bg: rgba(220, 222, 224, .75);--inpainting-alerts-icon-color: rgb(0, 0, 0);--inpainting-alerts-bg-active: rgb(255, 200, 0);--inpainting-alerts-icon-active: rgb(0, 0, 0);--inpainting-alerts-bg-alert: var(--invalid);--inpainting-alerts-icon-alert: rgb(0, 0, 0);--checkboard-dots-color: rgb(160, 160, 172);--scrollbar-color: rgb(180, 180, 184);--scrollbar-color-hover: rgb(150, 150, 154)}[data-theme=green]{--white: rgb(255, 255, 255);--accent-color-dim: rgb(10, 60, 40);--accent-color: rgb(20, 110, 70);--accent-color-bright: rgb(30, 180, 100);--accent-color-hover: var(--accent-color-bright);--root-bg-color: rgb(10, 10, 14);--background-color: rgb(30, 32, 37);--background-color-light: rgb(40, 44, 48);--background-color-secondary: rgb(22, 24, 28);--text-color: rgb(255, 255, 255);--text-color-secondary: rgb(160, 164, 168);--subtext-color: rgb(24, 24, 28);--subtext-color-bright: rgb(68, 72, 76);--border-color: rgb(40, 44, 48);--border-color-light: rgb(60, 60, 64);--svg-color: rgb(220, 224, 228);--invalid: rgb(255, 75, 75);--invalid-secondary: rgb(120, 5, 5);--destructive-color: rgb(185, 55, 55);--destructive-color-hover: rgb(255, 75, 75);--warning-color: rgb(200, 88, 40);--warning-color-hover: rgb(230, 117, 60);--border-color-invalid: rgb(255, 80, 50);--box-shadow-color-invalid: rgb(210, 30, 10);--tab-color: rgb(40, 44, 48);--tab-hover-color: rgb(48, 52, 56);--tab-panel-bg: rgb(36, 40, 44);--tab-list-bg: var(--accent-color);--tab-list-text: rgb(202, 204, 206);--tab-list-text-inactive: rgb(92, 94, 96);--btn-base-color: rgb(40, 44, 48);--btn-base-color-hover: rgb(56, 60, 64);--btn-load-more: rgb(30, 32, 42);--btn-load-more-hover: rgb(54, 56, 66);--btn-svg-color: rgb(255, 255, 255);--btn-delete-image: rgb(182, 46, 46);--btn-checkbox-border-hover: rgb(46, 48, 68);--progress-bar-color: var(--accent-color);--prompt-bg-color: rgb(10, 10, 14);--switch-bg-color: rgb(100, 102, 110);--switch-bg-active-color: var(--accent-color);--slider-color: var(--accent-color-bright);--slider-mark-color: var(--accent-color-bright);--resizeable-handle-border-color: var(--accent-color);--metadata-bg-color: rgba(0, 0, 0, .7);--metadata-json-bg-color: rgba(255, 255, 255, .1);--status-good-color: rgb(125, 255, 100);--status-good-glow: rgb(40, 215, 40);--status-working-color: rgb(255, 175, 55);--status-working-glow: rgb(255, 160, 55);--status-bad-color: rgb(255, 90, 90);--status-bad-glow: rgb(255, 40, 40);--notice-color: rgb(130, 71, 19);--settings-modal-bg: rgb(30, 32, 42);--input-checkbox-bg: rgb(60, 64, 68);--input-checkbox-checked-bg: var(--accent-color);--input-checkbox-checked-tick: rgb(0, 0, 0);--input-border-color: var(--accent-color-bright);--input-box-shadow-color: var(--accent-color);--error-level-info: rgb(200, 202, 224);--error-level-warning: rgb(255, 225, 105);--error-level-error: rgb(255, 81, 46);--console-bg-color: rgb(30, 30, 36);--console-icon-button-bg-color: rgb(50, 53, 64);--console-icon-button-bg-color-hover: rgb(70, 73, 84);--img2img-img-bg-color: rgb(30, 32, 42);--context-menu-bg-color: rgb(46, 48, 58);--context-menu-box-shadow: none;--context-menu-bg-color-hover: rgb(30, 32, 42);--floating-button-drop-shadow-color: var(--accent-color);--inpainting-alerts-bg: rgba(20, 20, 26, .75);--inpainting-alerts-icon-color: rgb(255, 255, 255);--inpainting-alerts-bg-active: var(--accent-color);--inpainting-alerts-icon-active: rgb(255, 255, 255);--inpainting-alerts-bg-alert: var(--invalid);--inpainting-alerts-icon-alert: rgb(255, 255, 255);--checkboard-dots-color: rgb(35, 35, 39);--scrollbar-color: var(--accent-color);--scrollbar-color-hover: var(--accent-color-bright)}@media (max-width: 600px){#root .app-content{padding:5px}#root .app-content .site-header{position:fixed;display:flex;height:100px;z-index:1}#root .app-content .site-header .site-header-left-side{position:absolute;display:flex;min-width:145px;float:left;padding-left:0}#root .app-content .site-header .site-header-right-side{display:grid;grid-template-columns:1fr 1fr 1fr 1fr 1fr 1fr;grid-template-rows:25px 25px 25px;grid-template-areas:"logoSpace logoSpace logoSpace sampler sampler sampler" "status status status status status status" "btn1 btn2 btn3 btn4 btn5 btn6";row-gap:15px}#root .app-content .site-header .site-header-right-side .chakra-popover__popper{grid-area:logoSpace}#root .app-content .site-header .site-header-right-side>:nth-child(1).chakra-text{grid-area:status;width:100%;display:flex;justify-content:center}#root .app-content .site-header .site-header-right-side>:nth-child(2){grid-area:sampler;display:flex;justify-content:center;align-items:center}#root .app-content .site-header .site-header-right-side>:nth-child(2) select{width:185px;margin-top:10px}#root .app-content .site-header .site-header-right-side>:nth-child(2) .chakra-select__icon-wrapper{right:10px}#root .app-content .site-header .site-header-right-side>:nth-child(2) .chakra-select__icon-wrapper svg{margin-top:10px}#root .app-content .site-header .site-header-right-side>:nth-child(3){grid-area:btn1}#root .app-content .site-header .site-header-right-side>:nth-child(4){grid-area:btn2}#root .app-content .site-header .site-header-right-side>:nth-child(6){grid-area:btn3}#root .app-content .site-header .site-header-right-side>:nth-child(7){grid-area:btn4}#root .app-content .site-header .site-header-right-side>:nth-child(8){grid-area:btn5}#root .app-content .site-header .site-header-right-side>:nth-child(9){grid-area:btn6}#root .app-content .app-tabs{position:fixed;display:flex;flex-direction:column;row-gap:15px;max-width:100%;overflow:hidden;margin-top:120px}#root .app-content .app-tabs .app-tabs-list{display:flex;justify-content:space-between}#root .app-content .app-tabs .app-tabs-panels{overflow:hidden;overflow-y:scroll}#root .app-content .app-tabs .app-tabs-panels .workarea-main{display:grid;grid-template-areas:"workarea" "options" "gallery";row-gap:15px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .options-panel-wrapper{grid-area:options;width:100%;max-width:100%;height:inherit;overflow:inherit;padding:0 10px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .options-panel-wrapper .main-options-row,#root .app-content .app-tabs .app-tabs-panels .workarea-main .options-panel-wrapper .advanced-settings-item{max-width:100%}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper{grid-area:workarea}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .workarea-split-view{display:flex;flex-direction:column}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .current-image-options{column-gap:3px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .text-to-image-area{padding:0}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .current-image-preview{height:430px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .image-upload-button{row-gap:10px;padding:5px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .image-upload-button svg{width:2rem;height:2rem;margin-top:10px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .inpainting-settings{display:flex;flex-wrap:wrap;row-gap:10px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .inpainting-canvas-area .konvajs-content{height:400px!important}#root .app-content .app-tabs .app-tabs-panels .workarea-main .image-gallery-wrapper{grid-area:gallery;min-height:400px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .image-gallery-wrapper .image-gallery-popup{width:100%!important;max-width:100%!important}}svg{fill:var(--svg-color)}.App{display:grid;width:100vw;height:100vh;background-color:var(--background-color)}.app-content{display:grid;row-gap:1rem;padding:1rem;grid-auto-rows:min-content auto;width:calc(100vw + -0px);height:calc(100vh - .3rem)}.site-header{display:grid;grid-template-columns:auto max-content}.site-header-left-side{display:flex;align-items:center;column-gap:.7rem;padding-left:.5rem}.site-header-left-side img{width:32px;height:32px}.site-header-left-side h1{font-size:1.4rem}.site-header-right-side{display:flex;align-items:center;column-gap:.5rem}.site-header-right-side .lang-select-btn[data-selected=true],.site-header-right-side .lang-select-btn[data-selected=true]:hover{background-color:var(--accent-color)}.status{font-size:.8rem;font-weight:700}.status-good{color:var(--status-good-color);text-shadow:0 0 10px var(--status-good-glow)}.status-bad{color:var(--status-bad-color);text-shadow:0 0 10px var(--status-bad-glow)}.status-working{color:var(--status-working-color);text-shadow:0 0 10px var(--status-working-glow)}.settings-modal{max-height:36rem;font-family:Inter}.settings-modal .settings-modal-content{display:grid;row-gap:2rem;overflow-y:scroll}.settings-modal .settings-modal-header{font-weight:700}.settings-modal .settings-modal-items{display:grid;row-gap:.5rem}.settings-modal .settings-modal-items .settings-modal-item{display:grid;grid-auto-flow:column;background-color:var(--background-color);padding:.4rem 1rem;border-radius:.5rem;align-items:center;width:100%}.settings-modal .settings-modal-reset{display:grid;row-gap:1rem}.settings-modal .settings-modal-reset button{min-width:100%;min-height:100%;background-color:var(--destructive-color)!important}.settings-modal .settings-modal-reset button:hover{background-color:var(--destructive-color-hover)}.settings-modal .settings-modal-reset button:disabled{background-color:var(--btn-base-color)}.settings-modal .settings-modal-reset button:disabled:hover{background-color:var(--btn-base-color)}.settings-modal .settings-modal-reset button svg{width:20px;height:20px;color:var(--btn-svg-color)}.add-model-modal{display:flex}.add-model-modal-body{display:flex;flex-direction:column;row-gap:1rem;padding-bottom:2rem}.add-model-form{display:flex;flex-direction:column;row-gap:.5rem}.hotkeys-modal{width:36rem;max-width:36rem;display:grid;padding:1rem;row-gap:1rem;font-family:Inter}.hotkeys-modal h1{font-size:1.2rem;font-weight:700}.hotkeys-modal h2{font-weight:700}.hotkeys-modal-button{display:flex;align-items:center;justify-content:space-between}.hotkeys-modal-items{max-height:36rem;overflow-y:scroll;-ms-overflow-style:none;scrollbar-width:none}.hotkeys-modal-items::-webkit-scrollbar{display:none}.hotkeys-modal-items .chakra-accordion{display:grid;row-gap:.5rem}.hotkeys-modal-items .chakra-accordion__item{border:none;border-radius:.3rem;background-color:var(--tab-hover-color)}.hotkeys-modal-items button{border-radius:.3rem}.hotkeys-modal-items button[aria-expanded=true]{background-color:var(--tab-hover-color);border-radius:.3rem}.hotkey-modal-category{display:grid;row-gap:.5rem}.hotkey-modal-item{display:grid;grid-template-columns:auto max-content;justify-content:space-between;align-items:center;background-color:var(--background-color);padding:.5rem 1rem;border-radius:.3rem}.hotkey-modal-item .hotkey-info{display:grid}.hotkey-modal-item .hotkey-info .hotkey-title{font-weight:700}.hotkey-modal-item .hotkey-info .hotkey-description{font-size:.9rem;color:var(--text-color-secondary)}.hotkey-modal-item .hotkey-key{font-size:.8rem;font-weight:700;background-color:var(--background-color-light);padding:.2rem .5rem;border-radius:.3rem}.console{width:100vw;display:flex;flex-direction:column;background:var(--console-bg-color);overflow:auto;direction:column;font-family:monospace;padding:0 1rem 1rem 3rem;border-top-width:.3rem;border-color:var(--resizeable-handle-border-color)}.console .console-info-color{color:var(--error-level-info)}.console .console-warning-color{color:var(--error-level-warning)}.console .console-error-color{color:var(--status-bad-color)}.console .console-entry{display:flex;column-gap:.5rem}.console .console-entry .console-timestamp{font-weight:semibold}.console .console-entry .console-message{word-break:break-all}.console-toggle-icon-button{background:var(--console-icon-button-bg-color);position:fixed;left:.5rem;bottom:.5rem;z-index:10000}.console-toggle-icon-button:hover{background:var(--console-icon-button-bg-color-hover)}.console-toggle-icon-button[data-error-seen=true],.console-toggle-icon-button[data-error-seen=true]:hover{background:var(--status-bad-color)}.console-autoscroll-icon-button{background:var(--console-icon-button-bg-color);position:fixed;left:.5rem;bottom:3rem;z-index:10000}.console-autoscroll-icon-button:hover{background:var(--console-icon-button-bg-color-hover)}.console-autoscroll-icon-button[data-autoscroll-enabled=true]{background:var(--accent-color)}.console-autoscroll-icon-button[data-autoscroll-enabled=true]:hover{background:var(--accent-color-hover)}.prompt-bar{display:grid;row-gap:1rem}.prompt-bar input,.prompt-bar textarea{background-color:var(--prompt-bg-color);font-size:1rem;border:2px solid var(--border-color)}.prompt-bar input:hover,.prompt-bar textarea:hover{border:2px solid var(--border-color-light)}.prompt-bar input:focus-visible,.prompt-bar textarea:focus-visible{border:2px solid var(--input-border-color);box-shadow:0 0 10px 0 var(--input-box-shadow-color)}.prompt-bar input[aria-invalid=true],.prompt-bar textarea[aria-invalid=true]{border:2px solid var(--border-color-invalid);box-shadow:0 0 10px 0 var(--box-shadow-color-invalid)}.prompt-bar input:disabled,.prompt-bar textarea:disabled{border:2px solid var(--border-color);box-shadow:none}.prompt-bar textarea{min-height:10rem}.process-buttons{display:flex;column-gap:.5rem}.invoke-btn{flex-grow:1;width:100%;min-width:100%;min-height:100%;background-color:var(--accent-color)!important}.invoke-btn:hover{background-color:var(--accent-color-hover)}.invoke-btn:disabled{background-color:var(--btn-base-color)}.invoke-btn:disabled:hover{background-color:var(--btn-base-color)}.invoke-btn svg{width:16px;height:16px;color:var(--btn-svg-color)}.cancel-btn{min-width:3rem;min-height:100%;background-color:var(--destructive-color)!important}.cancel-btn:hover{background-color:var(--destructive-color-hover)}.cancel-btn:disabled{background-color:var(--btn-base-color)}.cancel-btn:disabled:hover{background-color:var(--btn-base-color)}.cancel-btn svg{width:20px;height:20px;color:var(--btn-svg-color)}.loopback-btn[data-as-checkbox=true]{background-color:var(--btn-btn-base-color);border:3px solid var(--btn-btn-base-color)}.loopback-btn[data-as-checkbox=true] svg{fill:var(--text-color)}.loopback-btn[data-as-checkbox=true]:hover{background-color:var(--btn-btn-base-color);border-color:var(--btn-checkbox-border-hover)}.loopback-btn[data-as-checkbox=true]:hover svg{fill:var(--text-color)}.loopback-btn[data-as-checkbox=true][data-selected=true]{border-color:var(--accent-color);background-color:var(--btn-btn-base-color)}.loopback-btn[data-as-checkbox=true][data-selected=true] svg{fill:var(--text-color)}.loopback-btn[data-as-checkbox=true][data-selected=true]:hover{border-color:var(--accent-color);background-color:var(--btn-btn-base-color)}.loopback-btn[data-as-checkbox=true][data-selected=true]:hover svg{fill:var(--text-color)}.main-options,.main-options-list{display:grid;row-gap:1rem}.main-options-row{display:grid;grid-template-columns:repeat(3,auto);column-gap:.5rem;max-width:22.5rem}.main-option-block{border-radius:.5rem;display:grid!important;grid-template-columns:auto!important;row-gap:.5rem}.main-option-block .invokeai__number-input-form-label,.main-option-block .invokeai__select-label{font-weight:700;font-size:.9rem!important}.main-option-block .invokeai__select-label{margin:0}.advanced-options-checkbox{background-color:var(--background-color-secondary);padding:.5rem 1rem;border-radius:.4rem;font-weight:700}.advanced-settings{padding-top:.5rem;display:grid;row-gap:.5rem}.advanced-settings-item{display:grid;max-width:22.5rem;border:none;border-top:0px;border-radius:.4rem;background-color:var(--tab-panel-bg)}.advanced-settings-item[aria-expanded=true]{background-color:var(--tab-hover-color);border-radius:0 0 .4rem .4rem}.advanced-settings-panel{background-color:var(--tab-panel-bg);border-radius:0 0 .4rem .4rem;padding:1rem}.advanced-settings-panel button{background-color:var(--btn-base-color)}.advanced-settings-panel button:hover{background-color:var(--btn-base-color-hover)}.advanced-settings-panel button:disabled:hover{background-color:var(--btn-base-color)}.advanced-settings-header{border-radius:.4rem;font-weight:700}.advanced-settings-header[aria-expanded=true]{background-color:var(--tab-hover-color);border-radius:.4rem .4rem 0 0}.advanced-settings-header:hover{background-color:var(--tab-hover-color)}.upscale-options{display:grid;grid-template-columns:auto 1fr;column-gap:1rem}.inpainting-bounding-box-settings{display:flex;flex-direction:column;border-radius:.4rem;border:2px solid var(--tab-color)}.inpainting-bounding-box-header{background-color:var(--tab-color);display:flex;flex-direction:row;justify-content:space-between;padding:.5rem 1rem;border-radius:.3rem .3rem 0 0;align-items:center}.inpainting-bounding-box-header button{width:.5rem;height:1.2rem;background:none}.inpainting-bounding-box-header button:hover{background:none}.inpainting-bounding-box-settings-items{padding:1rem;display:flex;flex-direction:column;row-gap:1rem}.inpainting-bounding-box-settings-items .inpainting-bounding-box-reset-icon-btn{background-color:var(--btn-base-color)}.inpainting-bounding-box-settings-items .inpainting-bounding-box-reset-icon-btn:hover{background-color:var(--btn-base-color-hover)}.inpainting-bounding-box-dimensions-slider-numberinput{display:grid;grid-template-columns:repeat(3,auto);column-gap:1rem}.inpainting-bounding-box-darken{width:max-content}.progress-bar{background-color:var(--root-bg-color);height:.3rem;z-index:99}.progress-bar div{background-color:var(--progress-bar-color)}.progress-bar div[data-indeterminate]{background-color:unset;background-image:linear-gradient(to right,transparent 0%,var(--progress-bar-color) 50%,transparent 100%)}.current-image-area{display:flex;flex-direction:column;height:100%;row-gap:1rem;background-color:var(--background-color-secondary);border-radius:.5rem}.current-image-preview{position:relative;justify-content:center;align-items:center;display:flex;width:100%;height:100%}.current-image-preview img{border-radius:.5rem;object-fit:contain;max-width:100%;max-height:100%;height:auto;position:absolute}.current-image-metadata{grid-area:current-image-preview}.current-image-next-prev-buttons{grid-area:current-image-content;display:flex;justify-content:space-between;z-index:1;height:100%;width:100%;pointer-events:none}.next-prev-button-trigger-area{width:7rem;height:100%;width:15%;display:grid;align-items:center;pointer-events:auto}.next-prev-button-trigger-area.prev-button-trigger-area{justify-content:flex-start}.next-prev-button-trigger-area.next-button-trigger-area{justify-content:flex-end}.next-prev-button{font-size:4rem;fill:var(--white);filter:drop-shadow(0 0 1rem var(--text-color-secondary));opacity:70%}.current-image-display-placeholder{background-color:var(--background-color-secondary);display:grid;display:flex;align-items:center;justify-content:center;width:100%;height:100%;border-radius:.5rem}.current-image-display-placeholder svg{width:10rem;height:10rem;color:var(--svg-color)}.current-image-options{width:100%;display:flex;justify-content:center;align-items:center;column-gap:.5em}.current-image-options .current-image-send-to-popover,.current-image-options .current-image-postprocessing-popover{display:flex;flex-direction:column;row-gap:.5rem;max-width:25rem}.current-image-options .current-image-send-to-popover .invokeai__button{place-content:start}.current-image-options .chakra-popover__popper{z-index:11}.current-image-options .delete-image-btn{background-color:var(--btn-base-color)}.current-image-options .delete-image-btn svg{fill:var(--btn-delete-image)}.image-gallery-wrapper-enter{transform:translate(150%)}.image-gallery-wrapper-enter-active{transform:translate(0);transition:all .12s ease-out}.image-gallery-wrapper-exit{transform:translate(0)}.image-gallery-wrapper-exit-active{transform:translate(150%);transition:all .12s ease-out}.image-gallery-wrapper[data-pinned=false]{position:fixed;height:100vh;top:0;right:0}.image-gallery-wrapper[data-pinned=false] .image-gallery-popup{border-radius:0;box-shadow:0 0 1rem var(--text-color-a3)}.image-gallery-wrapper[data-pinned=false] .image-gallery-popup .image-gallery-container{max-height:calc(100vh + 4.7rem)}.image-gallery-wrapper .image-gallery-popup{background-color:var(--background-color-secondary);padding:1rem;display:flex;flex-direction:column;row-gap:1rem;border-radius:.5rem;border-left-width:.3rem;border-color:var(--tab-list-text-inactive)}.image-gallery-wrapper .image-gallery-popup[data-resize-alert=true]{border-color:var(--status-bad-color)}.image-gallery-wrapper .image-gallery-popup .image-gallery-header{display:flex;align-items:center;column-gap:.5rem;justify-content:space-between}.image-gallery-wrapper .image-gallery-popup .image-gallery-header .image-gallery-header-right-icons{display:flex;flex-direction:row;column-gap:.5rem}.image-gallery-wrapper .image-gallery-popup .image-gallery-header .image-gallery-icon-btn{background-color:var(--btn-load-more)}.image-gallery-wrapper .image-gallery-popup .image-gallery-header .image-gallery-icon-btn:hover{background-color:var(--btn-load-more-hover)}.image-gallery-wrapper .image-gallery-popup .image-gallery-header .image-gallery-settings-popover{display:flex;flex-direction:column;row-gap:.5rem}.image-gallery-wrapper .image-gallery-popup .image-gallery-header .image-gallery-settings-popover div{display:flex;column-gap:.5rem;align-items:center;justify-content:space-between}.image-gallery-wrapper .image-gallery-popup .image-gallery-header h1{font-weight:700}.image-gallery-wrapper .image-gallery-popup .image-gallery-container{display:flex;flex-direction:column;max-height:calc(100vh - (70px + 7rem));overflow-y:scroll;-ms-overflow-style:none;scrollbar-width:none}.image-gallery-wrapper .image-gallery-popup .image-gallery-container::-webkit-scrollbar{display:none}.image-gallery-wrapper .image-gallery-popup .image-gallery-container .image-gallery-container-placeholder{display:flex;flex-direction:column;row-gap:.5rem;background-color:var(--background-color);border-radius:.5rem;place-items:center;padding:2rem;text-align:center}.image-gallery-wrapper .image-gallery-popup .image-gallery-container .image-gallery-container-placeholder p{color:var(--subtext-color-bright);font-family:Inter}.image-gallery-wrapper .image-gallery-popup .image-gallery-container .image-gallery-container-placeholder svg{width:4rem;height:4rem;color:var(--svg-color)}.image-gallery-wrapper .image-gallery-popup .image-gallery-container .image-gallery-load-more-btn{background-color:var(--btn-load-more);font-size:.85rem;padding:.5rem;margin-top:1rem}.image-gallery-wrapper .image-gallery-popup .image-gallery-container .image-gallery-load-more-btn:disabled:hover{background-color:var(--btn-load-more)}.image-gallery-wrapper .image-gallery-popup .image-gallery-container .image-gallery-load-more-btn:hover{background-color:var(--btn-load-more-hover)}.image-gallery-category-btn-group{width:max-content;column-gap:0;justify-content:stretch}.image-gallery-category-btn-group button{background-color:var(--btn-base-color);flex-grow:1}.image-gallery-category-btn-group button:hover{background-color:var(--btn-base-color-hover)}.image-gallery-category-btn-group button[data-selected=true]{background-color:var(--accent-color)}.image-gallery-category-btn-group button[data-selected=true]:hover{background-color:var(--accent-color-hover)}.image-gallery{display:grid;grid-gap:.5rem}.image-gallery .hoverable-image{padding:.5rem;position:relative}.image-gallery .hoverable-image:before{content:"";display:block;padding-bottom:100%}.image-gallery .hoverable-image .hoverable-image-image{position:absolute;max-width:100%;top:50%;left:50%;transform:translate(-50%,-50%)}.hoverable-image{display:flex;justify-content:center;transition:transform .2s ease-out}.hoverable-image:hover{cursor:pointer;border-radius:.5rem;z-index:2}.hoverable-image .hoverable-image-image{width:100%;height:100%;max-width:100%;max-height:100%}.hoverable-image .hoverable-image-delete-button{position:absolute;top:.25rem;right:.25rem}.hoverable-image .hoverable-image-content{display:flex;position:absolute;top:0;left:0;width:100%;height:100%;align-items:center;justify-content:center}.hoverable-image .hoverable-image-content .hoverable-image-check{fill:var(--status-good-color)}.hoverable-image .hoverable-image-icons{position:absolute;bottom:-2rem;display:grid;width:min-content;grid-template-columns:repeat(2,max-content);border-radius:.4rem;background-color:var(--background-color-secondary);padding:.2rem;gap:.2rem;grid-auto-rows:max-content}.hoverable-image .hoverable-image-icons button{width:12px;height:12px;border-radius:.2rem;padding:10px 0;flex-shrink:2}.hoverable-image .hoverable-image-icons button svg{width:12px;height:12px}.hoverable-image-context-menu{z-index:15;padding:.4rem;border-radius:.25rem;background-color:var(--context-menu-bg-color);box-shadow:var(--context-menu-box-shadow)}.hoverable-image-context-menu [role=menuitem]{font-size:.8rem;line-height:1rem;border-radius:3px;display:flex;align-items:center;height:1.75rem;padding:0 .5rem;position:relative;user-select:none;cursor:pointer;outline:none}.hoverable-image-context-menu [role=menuitem][data-disabled]{color:gray;pointer-events:none;cursor:not-allowed}.hoverable-image-context-menu [role=menuitem][data-warning]{color:var(--status-bad-color)}.hoverable-image-context-menu [role=menuitem][data-highlighted]{background-color:var(--context-menu-bg-color-hover)}.image-metadata-viewer{position:absolute;top:0;width:100%;border-radius:.5rem;padding:1rem;background-color:var(--metadata-bg-color);overflow:scroll;max-height:calc(100vh - (70px + 5.4rem));height:100%;z-index:10}.image-json-viewer{border-radius:.5rem;margin:0 .5rem 1rem;padding:1rem;overflow-x:scroll;word-break:break-all;background-color:var(--metadata-json-bg-color)}.lightbox-container{width:100%;height:100%;color:var(--text-color);overflow:hidden;position:absolute;left:0;top:0;background-color:var(--background-color-secondary);z-index:30;animation:popIn .3s ease-in}.lightbox-container .image-gallery-wrapper{max-height:100%!important}.lightbox-container .image-gallery-wrapper .image-gallery-container{max-height:calc(100vh - 5rem)}.lightbox-container .current-image-options{z-index:2;position:absolute;top:1rem}.lightbox-container .image-metadata-viewer{left:0;max-height:100%}.lightbox-close-btn{z-index:3;position:absolute;left:1rem;top:1rem;background-color:var(--btn-base-color)}.lightbox-close-btn:hover{background-color:var(--btn-base-color-hover)}.lightbox-close-btn:disabled:hover{background-color:var(--btn-base-color)}.lightbox-display-container{display:flex;flex-direction:row}.lightbox-preview-wrapper{overflow:hidden;background-color:var(--background-color-secondary);display:grid;grid-template-columns:auto max-content;place-items:center;width:100vw;height:100vh}.lightbox-preview-wrapper .current-image-next-prev-buttons{position:absolute}.lightbox-preview-wrapper .lightbox-image{grid-area:lightbox-content;border-radius:.5rem}.lightbox-preview-wrapper .lightbox-image-options{position:absolute;z-index:2;left:1rem;top:4.5rem;user-select:none;border-radius:.5rem;display:flex;flex-direction:column;row-gap:.5rem}@keyframes popIn{0%{opacity:0;filter:blur(100)}to{opacity:1;filter:blur(0)}}.app-tabs{display:grid;grid-template-columns:min-content auto;column-gap:1rem;height:calc(100vh - (70px + 1rem))}.app-tabs-list{display:grid;row-gap:.3rem;grid-auto-rows:min-content;color:var(--tab-list-text-inactive)}.app-tabs-list button{font-size:.85rem;padding:.5rem}.app-tabs-list button:hover{background-color:var(--tab-hover-color);border-radius:.3rem}.app-tabs-list button svg{width:26px;height:26px}.app-tabs-list button[aria-selected=true]{background-color:var(--tab-list-bg);color:var(--tab-list-text);font-weight:700;border-radius:.3rem;border:none}.app-tabs-panels .app-tabs-panel{padding:0;height:100%}.workarea-wrapper{position:relative;width:100%;height:100%}.workarea-wrapper .workarea-main{display:flex;column-gap:1rem;height:100%}.workarea-wrapper .workarea-main .workarea-children-wrapper{position:relative;width:100%;height:100%}.workarea-wrapper .workarea-main .workarea-split-view{width:100%;height:100%;display:grid;grid-template-columns:1fr 1fr;background-color:var(--background-color-secondary);border-radius:.5rem}.workarea-wrapper .workarea-main .workarea-split-view .workarea-split-view-left{padding-right:.5rem}.workarea-wrapper .workarea-main .workarea-split-view .workarea-split-view-right{padding-left:.5rem}.workarea-wrapper .workarea-main .workarea-single-view{width:100%;height:100%;background-color:var(--background-color-secondary);border-radius:.5rem}.workarea-wrapper .workarea-main .workarea-split-view-left,.workarea-wrapper .workarea-main .workarea-split-view-right{display:flex;flex-direction:column;height:100%;width:100%;row-gap:1rem;background-color:var(--background-color-secondary);border-radius:.5rem;padding:1rem}.workarea-split-button{position:absolute;cursor:pointer;padding:.5rem;top:0;right:0}.workarea-split-button[data-selected=true]{top:0;right:0}.workarea-split-button[data-selected=true] svg{opacity:1}.workarea-split-button svg{opacity:.5}.options-panel-wrapper-enter{transform:translate(-150%)}.options-panel-wrapper-enter-active{transform:translate(0);transition:all .12s ease-out}.options-panel-wrapper-exit{transform:translate(0)}.options-panel-wrapper-exit-active{transform:translate(-150%);transition:all .12s ease-out}.options-panel-wrapper{background-color:var(--background-color);height:calc(100vh - (70px + 1rem));width:22.5rem;max-width:22.5rem;flex-shrink:0;position:relative;overflow-y:scroll;-ms-overflow-style:none;scrollbar-width:none}.options-panel-wrapper::-webkit-scrollbar{display:none}.options-panel-wrapper .options-panel{display:flex;flex-direction:column;row-gap:1rem;height:100%;-ms-overflow-style:none;scrollbar-width:none;background-color:var(--background-color)}.options-panel-wrapper .options-panel::-webkit-scrollbar{display:none}.options-panel-wrapper[data-pinned=false]{z-index:20;position:fixed;top:0;left:0;filter:var(--floating-panel-drop-shadow);width:24.5rem;max-width:24.5rem;height:100%}.options-panel-wrapper[data-pinned=false] .options-panel-margin{margin:1rem}.options-panel-wrapper .options-panel-pin-button{position:absolute;cursor:pointer;padding:.5rem;top:1rem;right:1rem;z-index:20}.options-panel-wrapper .options-panel-pin-button[data-selected=true]{top:0;right:0}.options-panel-wrapper .options-panel-pin-button svg{opacity:.5}.invoke-ai-logo-wrapper{display:flex;align-items:center;column-gap:.7rem;padding-left:.5rem;padding-top:.3rem}.invoke-ai-logo-wrapper img{width:32px;height:32px}.invoke-ai-logo-wrapper h1{font-size:1.4rem}.text-to-image-area{padding:1rem;height:100%}.image-to-image-area{display:flex;flex-direction:column;row-gap:1rem;width:100%;height:100%}.image-to-image-strength-main-option{display:flex;row-gap:.5rem!important}.image-to-image-strength-main-option .invokeai__slider-component-label{color:var(--text-color-secondary);font-size:.9rem!important}.init-image-preview-header{display:flex;align-items:center;justify-content:space-between;width:100%}.init-image-preview-header h2{font-weight:700;font-size:.9rem}.init-image-preview{position:relative;height:100%;width:100%;display:flex;align-items:center;justify-content:center}.init-image-preview img{border-radius:.5rem;object-fit:contain;position:absolute}.image-to-image-current-image-display{position:relative}.floating-show-hide-button{position:absolute;top:50%;transform:translateY(-50%);z-index:20;padding:0;background-color:red!important;min-width:2rem;min-height:12rem;background-color:var(--btn-btn-base-color)!important}.floating-show-hide-button.left{left:0;border-radius:0 .5rem .5rem 0}.floating-show-hide-button.right{right:0;border-radius:.5rem 0 0 .5rem}.floating-show-hide-button:hover{background-color:var(--btn-btn-base-color-hover)}.floating-show-hide-button:disabled{background-color:var(--btn-base-color)}.floating-show-hide-button:disabled:hover{background-color:var(--btn-base-color)}.floating-show-hide-button svg{width:20px;height:20px;color:var(--btn-svg-color)}.show-hide-button-options{position:absolute;transform:translateY(-50%);z-index:20;min-width:2rem;top:50%;left:calc(42px + 2rem);border-radius:0 .5rem .5rem 0;display:flex;flex-direction:column;row-gap:.5rem}.show-hide-button-options button{border-radius:0 .3rem .3rem 0}.show-hide-button-gallery{padding-left:.75rem;padding-right:.75rem;background-color:var(--background-color)!important}.inpainting-main-area{display:flex;flex-direction:column;align-items:center;row-gap:1rem;width:100%;height:100%}.inpainting-main-area .inpainting-settings{display:flex;align-items:center;column-gap:.5rem}.inpainting-main-area .inpainting-settings svg{transform:scale(.9)}.inpainting-main-area .inpainting-settings .inpainting-buttons-group{display:flex;align-items:center;column-gap:.5rem}.inpainting-main-area .inpainting-settings .inpainting-button-dropdown{display:flex;flex-direction:column;row-gap:.5rem}.inpainting-main-area .inpainting-settings .inpainting-color-picker{margin-left:1rem}.inpainting-main-area .inpainting-settings .inpainting-brush-options{display:flex;align-items:center;column-gap:1rem}.inpainting-canvas-area{display:flex;flex-direction:column;align-items:center;justify-content:center;row-gap:1rem;width:100%;height:100%}.inpainting-canvas-spiner{display:flex;align-items:center;width:100%;height:100%}.inpainting-canvas-container{display:flex;position:relative;height:100%;width:100%;border-radius:.5rem}.inpainting-canvas-wrapper{position:relative}.inpainting-canvas-stage{outline:none;border-radius:.5rem;box-shadow:0 0 0 1px var(--border-color-light);overflow:hidden}.inpainting-canvas-stage canvas{outline:none;border-radius:.5rem}.inpainting-options-btn{min-height:2rem}.canvas-status-text{position:absolute;top:0;left:0;background-color:var(--background-color);opacity:.65;display:flex;flex-direction:column;font-size:.8rem;padding:.25rem;min-width:12rem;border-radius:.25rem;margin:.25rem;pointer-events:none}.invokeai__number-input-form-control{display:flex;align-items:center;column-gap:1rem}.invokeai__number-input-form-control .invokeai__number-input-form-label{color:var(--text-color-secondary)}.invokeai__number-input-form-control .invokeai__number-input-form-label[data-focus]+.invokeai__number-input-root{outline:none;border:2px solid var(--input-border-color);box-shadow:0 0 10px 0 var(--input-box-shadow-color)}.invokeai__number-input-form-control .invokeai__number-input-form-label[aria-invalid=true]+.invokeai__number-input-root{outline:none;border:2px solid var(--border-color-invalid);box-shadow:0 0 10px 0 var(--box-shadow-color-invalid)}.invokeai__number-input-form-control .invokeai__number-input-root{height:2rem;display:grid;grid-template-columns:auto max-content;column-gap:.5rem;align-items:center;background-color:var(--background-color-secondary);border:2px solid var(--border-color);border-radius:.3rem}.invokeai__number-input-form-control .invokeai__number-input-field{border:none;font-weight:700;width:100%;height:auto;font-size:.9rem;padding:0 .5rem}.invokeai__number-input-form-control .invokeai__number-input-field:focus{outline:none;box-shadow:none}.invokeai__number-input-form-control .invokeai__number-input-field:disabled{opacity:.2}.invokeai__number-input-form-control .invokeai__number-input-stepper{display:grid;padding-right:.5rem}.invokeai__number-input-form-control .invokeai__number-input-stepper .invokeai__number-input-stepper-button{border:none;padding:0 .5rem;margin:0 -.5rem}.invokeai__number-input-form-control .invokeai__number-input-stepper .invokeai__number-input-stepper-button svg{width:10px;height:10px}.input{display:grid;grid-template-columns:max-content auto;column-gap:1rem;align-items:center}.input .input-label{color:var(--text-color-secondary)}.input .input-entry{background-color:var(--background-color-secondary);border:2px solid var(--border-color);border-radius:.2rem;font-weight:700}.input .input-entry:focus{outline:none;border:2px solid var(--input-border-color);box-shadow:0 0 10px 0 var(--input-box-shadow-color)}.input .input-entry:disabled{opacity:.2}.input .input-entry[aria-invalid=true]{outline:none;border:2px solid var(--border-color-invalid);box-shadow:0 0 10px 0 var(--box-shadow-color-invalid)}.invokeai__icon-button{background:var(--btn-base-color);cursor:pointer}.invokeai__icon-button:hover{background-color:var(--btn-base-color-hover)}.invokeai__icon-button[data-selected=true]{background-color:var(--accent-color)}.invokeai__icon-button[data-selected=true]:hover{background-color:var(--accent-color-hover)}.invokeai__icon-button[disabled]{cursor:not-allowed}.invokeai__icon-button[data-variant=link],.invokeai__icon-button[data-variant=link]:hover{background:none}.invokeai__icon-button[data-as-checkbox=true]{background-color:var(--btn-base-color);border:3px solid var(--btn-base-color)}.invokeai__icon-button[data-as-checkbox=true] svg{fill:var(--text-color)}.invokeai__icon-button[data-as-checkbox=true]:hover{background-color:var(--btn-base-color);border-color:var(--btn-checkbox-border-hover)}.invokeai__icon-button[data-as-checkbox=true]:hover svg{fill:var(--text-color)}.invokeai__icon-button[data-as-checkbox=true][data-selected=true]{border-color:var(--accent-color)}.invokeai__icon-button[data-as-checkbox=true][data-selected=true] svg{fill:var(--accent-color-hover)}.invokeai__icon-button[data-as-checkbox=true][data-selected=true]:hover svg{fill:var(--accent-color-hover)}.invokeai__icon-button[data-alert=true]{animation-name:pulseColor;animation-duration:1s;animation-timing-function:ease-in-out;animation-iteration-count:infinite}.invokeai__icon-button[data-alert=true]:hover{animation:none;background-color:var(--accent-color-hover)}@keyframes pulseColor{0%{background-color:var(--accent-color)}50%{background-color:var(--accent-color-dim)}to{background-color:var(--accent-color)}}.invokeai__button{background-color:var(--btn-base-color);place-content:center}.invokeai__button:hover{background-color:var(--btn-base-color-hover)}.invokeai__switch-form-control .invokeai__switch-form-label{color:var(--text-color-secondary)}.invokeai__switch-form-control .invokeai__switch-root span{background-color:var(--switch-bg-color)}.invokeai__switch-form-control .invokeai__switch-root span span{background-color:var(--white)}.invokeai__switch-form-control .invokeai__switch-root[data-checked] span{background:var(--switch-bg-active-color)}.invokeai__switch-form-control .invokeai__switch-root[data-checked] span span{background-color:var(--white)}.invokeai__select{display:flex;column-gap:1rem;align-items:center}.invokeai__select .invokeai__select-label{color:var(--text-color-secondary)}.invokeai__select .invokeai__select-picker{border:2px solid var(--border-color);background-color:var(--background-color-secondary);font-weight:700;font-size:.9rem;height:2rem;border-radius:.2rem}.invokeai__select .invokeai__select-picker:focus{outline:none;border:2px solid var(--input-border-color);box-shadow:0 0 10px 0 var(--input-box-shadow-color)}.invokeai__select .invokeai__select-option{background-color:var(--background-color-secondary);color:var(--text-color-secondary)}.invokeai__slider-component{padding-bottom:.5rem;border-radius:.5rem}.invokeai__slider-component .invokeai__slider-component-label{min-width:max-content;margin:0;font-weight:700;font-size:.9rem;color:var(--text-color-secondary)}.invokeai__slider-component .invokeai__slider_track{background-color:var(--tab-color)}.invokeai__slider-component .invokeai__slider_track-filled{background-color:var(--slider-color)}.invokeai__slider-component .invokeai__slider-thumb{width:4px}.invokeai__slider-component .invokeai__slider-mark{font-size:.75rem;font-weight:700;color:var(--slider-mark-color);margin-top:.3rem}.invokeai__slider-component .invokeai__slider-number-input{border:none;font-size:.9rem;font-weight:700;height:2rem;background-color:var(--background-color-secondary);border:2px solid var(--border-color)}.invokeai__slider-component .invokeai__slider-number-input:focus{outline:none;box-shadow:none;border:2px solid var(--input-border-color);box-shadow:0 0 10px 0 var(--input-box-shadow-color)}.invokeai__slider-component .invokeai__slider-number-input:disabled{opacity:.2}.invokeai__slider-component .invokeai__slider-number-stepper{border:none}.invokeai__slider-component[data-markers=true] .invokeai__slider_container{margin-top:-1rem}.invokeai__checkbox .chakra-checkbox__label{margin-top:1px;color:var(--text-color-secondary);font-size:.9rem}.invokeai__checkbox .chakra-checkbox__control{width:1rem;height:1rem;border:none;border-radius:.2rem;background-color:var(--input-checkbox-bg)}.invokeai__checkbox .chakra-checkbox__control svg{width:.6rem;height:.6rem;stroke-width:3px}.invokeai__checkbox .chakra-checkbox__control[data-checked]{color:var(--text-color);background-color:var(--input-checkbox-checked-bg)}.invokeai__popover-content{min-width:unset;width:unset;padding:1rem;border-radius:.5rem;background-color:var(--background-color);border:2px solid var(--border-color)}.invokeai__popover-content .invokeai__popover-arrow{background-color:var(--background-color)!important}.invokeai__color-picker .react-colorful__hue-pointer,.invokeai__color-picker .react-colorful__saturation-pointer{width:1.5rem;height:1.5rem;border-color:var(--white)}.dropzone-container{position:absolute;top:0;left:0;width:100vw;height:100vh;z-index:999;backdrop-filter:blur(20px)}.dropzone-container .dropzone-overlay{opacity:.5;width:100%;height:100%;display:flex;flex-direction:column;row-gap:1rem;align-items:center;justify-content:center;background-color:var(--background-color)}.dropzone-container .dropzone-overlay.is-drag-accept{box-shadow:inset 0 0 20rem 1rem var(--accent-color)}.dropzone-container .dropzone-overlay.is-drag-reject{box-shadow:inset 0 0 20rem 1rem var(--status-bad-color)}.dropzone-container .dropzone-overlay.is-handling-upload{box-shadow:inset 0 0 20rem 1rem var(--status-working-color)}.image-uploader-button-outer{width:100%;height:100%;display:flex;align-items:center;justify-content:center;cursor:pointer;border-radius:.5rem;color:var(--tab-list-text-inactive);background-color:var(--background-color)}.image-uploader-button-outer:hover{background-color:var(--background-color-light)}.image-upload-button-inner{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.image-upload-button{display:flex;flex-direction:column;row-gap:2rem;align-items:center;justify-content:center;text-align:center}.image-upload-button svg{width:4rem;height:4rem}.image-upload-button h2{font-size:1.2rem}.work-in-progress{display:grid;width:100%;height:calc(100vh - (70px + 1rem));grid-auto-rows:max-content;background-color:var(--background-color-secondary);border-radius:.4rem;place-content:center;place-items:center;row-gap:1rem}.work-in-progress h1{font-size:2rem;font-weight:700}.work-in-progress p{text-align:center;max-width:50rem;color:var(--subtext-color-bright)}.guide-popover-arrow{background-color:var(--tab-panel-bg);box-shadow:none}.guide-popover-content{background-color:var(--background-color-secondary);border:none}.guide-popover-guide-content{background:var(--tab-panel-bg);border:2px solid var(--tab-hover-color);border-radius:.4rem;padding:.75rem 1rem;display:grid;grid-template-rows:repeat(auto-fill,1fr);grid-row-gap:.5rem;justify-content:space-between}.modal{background-color:var(--background-color-secondary);color:var(--text-color)}.modal-close-btn{background-color:var(--btn-base-color)}.modal-close-btn:hover{background-color:var(--btn-base-color-hover)}.modal-close-btn:disabled:hover{background-color:var(--btn-base-color)}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body{-ms-overflow-style:none;scrollbar-width:none;background-color:var(--root-bg-color);overflow:hidden}html::-webkit-scrollbar,body::-webkit-scrollbar{display:none}#root{background-color:var(--root-bg-color);color:var(--text-color);font-family:Inter,Arial,Helvetica,sans-serif}\n',document.head.appendChild(r),{execute:function(){function r(e,t){for(var n=0;nr[t]})}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}var o="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function i(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var a={exports:{}},s={},l=Symbol.for("react.element"),c=Symbol.for("react.portal"),u=Symbol.for("react.fragment"),d=Symbol.for("react.strict_mode"),h=Symbol.for("react.profiler"),f=Symbol.for("react.provider"),p=Symbol.for("react.context"),g=Symbol.for("react.forward_ref"),m=Symbol.for("react.suspense"),v=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),b=Symbol.iterator;var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},w=Object.assign,k={};function S(e,t,n){this.props=e,this.context=t,this.refs=k,this.updater=n||x}function C(){}function _(e,t,n){this.props=e,this.context=t,this.refs=k,this.updater=n||x}S.prototype.isReactComponent={},S.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")},S.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},C.prototype=S.prototype;var E=_.prototype=new C;E.constructor=_,w(E,S.prototype),E.isPureReactComponent=!0;var P=Array.isArray,L=Object.prototype.hasOwnProperty,O={current:null},M={key:!0,ref:!0,__self:!0,__source:!0};function T(e,t,n){var r,o={},i=null,a=null;if(null!=t)for(r in void 0!==t.ref&&(a=t.ref),void 0!==t.key&&(i=""+t.key),t)L.call(t,r)&&!M.hasOwnProperty(r)&&(o[r]=t[r]);var s=arguments.length-2;if(1===s)o.children=n;else if(1:nth-child(1).chakra-text{grid-area:status;width:100%;display:flex;justify-content:center}#root .app-content .site-header .site-header-right-side>:nth-child(2){grid-area:sampler;display:flex;justify-content:center;align-items:center}#root .app-content .site-header .site-header-right-side>:nth-child(2) select{width:185px;margin-top:10px}#root .app-content .site-header .site-header-right-side>:nth-child(2) .chakra-select__icon-wrapper{right:10px}#root .app-content .site-header .site-header-right-side>:nth-child(2) .chakra-select__icon-wrapper svg{margin-top:10px}#root .app-content .site-header .site-header-right-side>:nth-child(3){grid-area:btn1}#root .app-content .site-header .site-header-right-side>:nth-child(4){grid-area:btn2}#root .app-content .site-header .site-header-right-side>:nth-child(6){grid-area:btn3}#root .app-content .site-header .site-header-right-side>:nth-child(7){grid-area:btn4}#root .app-content .site-header .site-header-right-side>:nth-child(8){grid-area:btn5}#root .app-content .site-header .site-header-right-side>:nth-child(9){grid-area:btn6}#root .app-content .app-tabs{position:fixed;display:flex;flex-direction:column;row-gap:15px;max-width:100%;overflow:hidden;margin-top:120px}#root .app-content .app-tabs .app-tabs-list{display:flex;justify-content:space-between}#root .app-content .app-tabs .app-tabs-panels{overflow:hidden;overflow-y:scroll}#root .app-content .app-tabs .app-tabs-panels .workarea-main{display:grid;grid-template-areas:"workarea" "options" "gallery";row-gap:15px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .options-panel-wrapper{grid-area:options;width:100%;max-width:100%;height:inherit;overflow:inherit;padding:0 10px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .options-panel-wrapper .main-options-row,#root .app-content .app-tabs .app-tabs-panels .workarea-main .options-panel-wrapper .advanced-settings-item{max-width:100%}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper{grid-area:workarea}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .workarea-split-view{display:flex;flex-direction:column}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .current-image-options{column-gap:3px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .text-to-image-area{padding:0}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .current-image-preview{height:430px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .image-upload-button{row-gap:10px;padding:5px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .image-upload-button svg{width:2rem;height:2rem;margin-top:10px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .inpainting-settings{display:flex;flex-wrap:wrap;row-gap:10px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .workarea-children-wrapper .inpainting-canvas-area .konvajs-content{height:400px!important}#root .app-content .app-tabs .app-tabs-panels .workarea-main .image-gallery-wrapper{grid-area:gallery;min-height:400px}#root .app-content .app-tabs .app-tabs-panels .workarea-main .image-gallery-wrapper .image-gallery-popup{width:100%!important;max-width:100%!important}}svg{fill:var(--svg-color)}.App{display:grid;width:100vw;height:100vh;background-color:var(--background-color)}.app-content{display:grid;row-gap:1rem;padding:1rem;grid-auto-rows:min-content auto;width:calc(100vw + -0px);height:calc(100vh - .3rem)}.site-header{display:grid;grid-template-columns:auto max-content}.site-header-left-side{display:flex;align-items:center;column-gap:.7rem;padding-left:.5rem}.site-header-left-side img{width:32px;height:32px}.site-header-right-side{display:flex;align-items:center;column-gap:.5rem}.site-header-right-side .lang-select-btn[data-selected=true],.site-header-right-side .lang-select-btn[data-selected=true]:hover{background-color:var(--accent-color)}.status{font-size:.8rem;font-weight:700}.status-good{color:var(--status-good-color);text-shadow:0 0 10px var(--status-good-glow)}.status-bad{color:var(--status-bad-color);text-shadow:0 0 10px var(--status-bad-glow)}.status-working{color:var(--status-working-color);text-shadow:0 0 10px var(--status-working-glow)}.settings-modal{max-height:36rem;font-family:Inter}.settings-modal .settings-modal-content{display:grid;row-gap:2rem;overflow-y:scroll}.settings-modal .settings-modal-header{font-weight:700}.settings-modal .settings-modal-items{display:grid;row-gap:.5rem}.settings-modal .settings-modal-items .settings-modal-item{display:grid;grid-auto-flow:column;background-color:var(--background-color);padding:.4rem 1rem;border-radius:.5rem;align-items:center;width:100%}.settings-modal .settings-modal-reset{display:grid;row-gap:1rem}.settings-modal .settings-modal-reset button{min-width:100%;min-height:100%;background-color:var(--destructive-color)!important}.settings-modal .settings-modal-reset button:hover{background-color:var(--destructive-color-hover)}.settings-modal .settings-modal-reset button:disabled{background-color:var(--btn-base-color)}.settings-modal .settings-modal-reset button:disabled:hover{background-color:var(--btn-base-color)}.settings-modal .settings-modal-reset button svg{width:20px;height:20px;color:var(--btn-svg-color)}.add-model-modal{display:flex}.add-model-modal-body{display:flex;flex-direction:column;row-gap:1rem;padding-bottom:2rem}.add-model-form{display:flex;flex-direction:column;row-gap:.5rem}.hotkeys-modal{width:36rem;max-width:36rem;display:grid;padding:1rem;row-gap:1rem;font-family:Inter}.hotkeys-modal h1{font-size:1.2rem;font-weight:700}.hotkeys-modal h2{font-weight:700}.hotkeys-modal-button{display:flex;align-items:center;justify-content:space-between}.hotkeys-modal-items{max-height:36rem;overflow-y:scroll;-ms-overflow-style:none;scrollbar-width:none}.hotkeys-modal-items::-webkit-scrollbar{display:none}.hotkeys-modal-items .chakra-accordion{display:grid;row-gap:.5rem}.hotkeys-modal-items .chakra-accordion__item{border:none;border-radius:.3rem;background-color:var(--tab-hover-color)}.hotkeys-modal-items button{border-radius:.3rem}.hotkeys-modal-items button[aria-expanded=true]{background-color:var(--tab-hover-color);border-radius:.3rem}.hotkey-modal-category{display:grid;row-gap:.5rem}.hotkey-modal-item{display:grid;grid-template-columns:auto max-content;justify-content:space-between;align-items:center;background-color:var(--background-color);padding:.5rem 1rem;border-radius:.3rem}.hotkey-modal-item .hotkey-info{display:grid}.hotkey-modal-item .hotkey-info .hotkey-title{font-weight:700}.hotkey-modal-item .hotkey-info .hotkey-description{font-size:.9rem;color:var(--text-color-secondary)}.hotkey-modal-item .hotkey-key{font-size:.8rem;font-weight:700;background-color:var(--background-color-light);padding:.2rem .5rem;border-radius:.3rem}.console{width:100vw;display:flex;flex-direction:column;background:var(--console-bg-color);overflow:auto;direction:column;font-family:monospace;padding:0 1rem 1rem 3rem;border-top-width:.3rem;border-color:var(--resizeable-handle-border-color)}.console .console-info-color{color:var(--error-level-info)}.console .console-warning-color{color:var(--error-level-warning)}.console .console-error-color{color:var(--status-bad-color)}.console .console-entry{display:flex;column-gap:.5rem}.console .console-entry .console-timestamp{font-weight:semibold}.console .console-entry .console-message{word-break:break-all}.console-toggle-icon-button{background:var(--console-icon-button-bg-color);position:fixed;left:.5rem;bottom:.5rem;z-index:10000}.console-toggle-icon-button:hover{background:var(--console-icon-button-bg-color-hover)}.console-toggle-icon-button[data-error-seen=true],.console-toggle-icon-button[data-error-seen=true]:hover{background:var(--status-bad-color)}.console-autoscroll-icon-button{background:var(--console-icon-button-bg-color);position:fixed;left:.5rem;bottom:3rem;z-index:10000}.console-autoscroll-icon-button:hover{background:var(--console-icon-button-bg-color-hover)}.console-autoscroll-icon-button[data-autoscroll-enabled=true]{background:var(--accent-color)}.console-autoscroll-icon-button[data-autoscroll-enabled=true]:hover{background:var(--accent-color-hover)}.prompt-bar{display:grid;row-gap:1rem}.prompt-bar input,.prompt-bar textarea{background-color:var(--prompt-bg-color);font-size:1rem;border:2px solid var(--border-color)}.prompt-bar input:hover,.prompt-bar textarea:hover{border:2px solid var(--border-color-light)}.prompt-bar input:focus-visible,.prompt-bar textarea:focus-visible{border:2px solid var(--input-border-color);box-shadow:0 0 10px 0 var(--input-box-shadow-color)}.prompt-bar input[aria-invalid=true],.prompt-bar textarea[aria-invalid=true]{border:2px solid var(--border-color-invalid);box-shadow:0 0 10px 0 var(--box-shadow-color-invalid)}.prompt-bar input:disabled,.prompt-bar textarea:disabled{border:2px solid var(--border-color);box-shadow:none}.prompt-bar textarea{min-height:10rem}.process-buttons{display:flex;column-gap:.5rem}.invoke-btn{flex-grow:1;width:100%;min-width:100%;min-height:100%;background-color:var(--accent-color)!important}.invoke-btn:hover{background-color:var(--accent-color-hover)}.invoke-btn:disabled{background-color:var(--btn-base-color)}.invoke-btn:disabled:hover{background-color:var(--btn-base-color)}.invoke-btn svg{width:16px;height:16px;color:var(--btn-svg-color)}.cancel-btn{min-width:3rem;min-height:100%;background-color:var(--destructive-color)!important}.cancel-btn:hover{background-color:var(--destructive-color-hover)}.cancel-btn:disabled{background-color:var(--btn-base-color)}.cancel-btn:disabled:hover{background-color:var(--btn-base-color)}.cancel-btn svg{width:20px;height:20px;color:var(--btn-svg-color)}.loopback-btn[data-as-checkbox=true]{background-color:var(--btn-btn-base-color);border:3px solid var(--btn-btn-base-color)}.loopback-btn[data-as-checkbox=true] svg{fill:var(--text-color)}.loopback-btn[data-as-checkbox=true]:hover{background-color:var(--btn-btn-base-color);border-color:var(--btn-checkbox-border-hover)}.loopback-btn[data-as-checkbox=true]:hover svg{fill:var(--text-color)}.loopback-btn[data-as-checkbox=true][data-selected=true]{border-color:var(--accent-color);background-color:var(--btn-btn-base-color)}.loopback-btn[data-as-checkbox=true][data-selected=true] svg{fill:var(--text-color)}.loopback-btn[data-as-checkbox=true][data-selected=true]:hover{border-color:var(--accent-color);background-color:var(--btn-btn-base-color)}.loopback-btn[data-as-checkbox=true][data-selected=true]:hover svg{fill:var(--text-color)}.main-options,.main-options-list{display:grid;row-gap:1rem}.main-options-row{display:grid;grid-template-columns:repeat(3,auto);column-gap:.5rem;max-width:22.5rem}.main-option-block{border-radius:.5rem;display:grid!important;grid-template-columns:auto!important;row-gap:.5rem}.main-option-block .invokeai__number-input-form-label,.main-option-block .invokeai__select-label{font-weight:700;font-size:.9rem!important}.main-option-block .invokeai__select-label{margin:0}.advanced-options-checkbox{background-color:var(--background-color-secondary);padding:.5rem 1rem;border-radius:.4rem;font-weight:700}.advanced-settings{padding-top:.5rem;display:grid;row-gap:.5rem}.advanced-settings-item{display:grid;max-width:22.5rem;border:none;border-top:0px;border-radius:.4rem;background-color:var(--tab-panel-bg)}.advanced-settings-item[aria-expanded=true]{background-color:var(--tab-hover-color);border-radius:0 0 .4rem .4rem}.advanced-settings-panel{background-color:var(--tab-panel-bg);border-radius:0 0 .4rem .4rem;padding:1rem}.advanced-settings-panel button{background-color:var(--btn-base-color)}.advanced-settings-panel button:hover{background-color:var(--btn-base-color-hover)}.advanced-settings-panel button:disabled:hover{background-color:var(--btn-base-color)}.advanced-settings-header{border-radius:.4rem;font-weight:700}.advanced-settings-header[aria-expanded=true]{background-color:var(--tab-hover-color);border-radius:.4rem .4rem 0 0}.advanced-settings-header:hover{background-color:var(--tab-hover-color)}.upscale-options{display:grid;grid-template-columns:auto 1fr;column-gap:1rem}.inpainting-bounding-box-settings{display:flex;flex-direction:column;border-radius:.4rem;border:2px solid var(--tab-color)}.inpainting-bounding-box-header{background-color:var(--tab-color);display:flex;flex-direction:row;justify-content:space-between;padding:.5rem 1rem;border-radius:.3rem .3rem 0 0;align-items:center}.inpainting-bounding-box-header button{width:.5rem;height:1.2rem;background:none}.inpainting-bounding-box-header button:hover{background:none}.inpainting-bounding-box-settings-items{padding:1rem;display:flex;flex-direction:column;row-gap:1rem}.inpainting-bounding-box-settings-items .inpainting-bounding-box-reset-icon-btn{background-color:var(--btn-base-color)}.inpainting-bounding-box-settings-items .inpainting-bounding-box-reset-icon-btn:hover{background-color:var(--btn-base-color-hover)}.inpainting-bounding-box-dimensions-slider-numberinput{display:grid;grid-template-columns:repeat(3,auto);column-gap:1rem}.inpainting-bounding-box-darken{width:max-content}.progress-bar{background-color:var(--root-bg-color);height:.3rem;z-index:99}.progress-bar div{background-color:var(--progress-bar-color)}.progress-bar div[data-indeterminate]{background-color:unset;background-image:linear-gradient(to right,transparent 0%,var(--progress-bar-color) 50%,transparent 100%)}.current-image-area{display:flex;flex-direction:column;height:100%;row-gap:1rem;background-color:var(--background-color-secondary);border-radius:.5rem}.current-image-preview{position:relative;justify-content:center;align-items:center;display:flex;width:100%;height:100%}.current-image-preview img{border-radius:.5rem;object-fit:contain;max-width:100%;max-height:100%;height:auto;position:absolute}.current-image-metadata{grid-area:current-image-preview}.current-image-next-prev-buttons{grid-area:current-image-content;display:flex;justify-content:space-between;z-index:1;height:100%;width:100%;pointer-events:none}.next-prev-button-trigger-area{width:7rem;height:100%;width:15%;display:grid;align-items:center;pointer-events:auto}.next-prev-button-trigger-area.prev-button-trigger-area{justify-content:flex-start}.next-prev-button-trigger-area.next-button-trigger-area{justify-content:flex-end}.next-prev-button{font-size:4rem;fill:var(--white);filter:drop-shadow(0 0 1rem var(--text-color-secondary));opacity:70%}.current-image-display-placeholder{background-color:var(--background-color-secondary);display:grid;display:flex;align-items:center;justify-content:center;width:100%;height:100%;border-radius:.5rem}.current-image-display-placeholder svg{width:10rem;height:10rem;color:var(--svg-color)}.current-image-options{width:100%;display:flex;justify-content:center;align-items:center;column-gap:.5em}.current-image-options .current-image-send-to-popover,.current-image-options .current-image-postprocessing-popover{display:flex;flex-direction:column;row-gap:.5rem;max-width:25rem}.current-image-options .current-image-send-to-popover .invokeai__button{place-content:start}.current-image-options .chakra-popover__popper{z-index:11}.current-image-options .delete-image-btn{background-color:var(--btn-base-color)}.current-image-options .delete-image-btn svg{fill:var(--btn-delete-image)}.image-gallery-wrapper-enter{transform:translate(150%)}.image-gallery-wrapper-enter-active{transform:translate(0);transition:all .12s ease-out}.image-gallery-wrapper-exit{transform:translate(0)}.image-gallery-wrapper-exit-active{transform:translate(150%);transition:all .12s ease-out}.image-gallery-wrapper[data-pinned=false]{position:fixed;height:100vh;top:0;right:0}.image-gallery-wrapper[data-pinned=false] .image-gallery-popup{border-radius:0;box-shadow:0 0 1rem var(--text-color-a3)}.image-gallery-wrapper[data-pinned=false] .image-gallery-popup .image-gallery-container{max-height:calc(100vh + 4.7rem)}.image-gallery-wrapper .image-gallery-popup{background-color:var(--background-color-secondary);padding:1rem;display:flex;flex-direction:column;row-gap:1rem;border-radius:.5rem;border-left-width:.3rem;border-color:var(--tab-list-text-inactive)}.image-gallery-wrapper .image-gallery-popup[data-resize-alert=true]{border-color:var(--status-bad-color)}.image-gallery-wrapper .image-gallery-popup .image-gallery-header{display:flex;align-items:center;column-gap:.5rem;justify-content:space-between}.image-gallery-wrapper .image-gallery-popup .image-gallery-header .image-gallery-header-right-icons{display:flex;flex-direction:row;column-gap:.5rem}.image-gallery-wrapper .image-gallery-popup .image-gallery-header .image-gallery-icon-btn{background-color:var(--btn-load-more)}.image-gallery-wrapper .image-gallery-popup .image-gallery-header .image-gallery-icon-btn:hover{background-color:var(--btn-load-more-hover)}.image-gallery-wrapper .image-gallery-popup .image-gallery-header .image-gallery-settings-popover{display:flex;flex-direction:column;row-gap:.5rem}.image-gallery-wrapper .image-gallery-popup .image-gallery-header .image-gallery-settings-popover div{display:flex;column-gap:.5rem;align-items:center;justify-content:space-between}.image-gallery-wrapper .image-gallery-popup .image-gallery-header h1{font-weight:700}.image-gallery-wrapper .image-gallery-popup .image-gallery-container{display:flex;flex-direction:column;max-height:calc(100vh - (70px + 7rem));overflow-y:scroll;-ms-overflow-style:none;scrollbar-width:none}.image-gallery-wrapper .image-gallery-popup .image-gallery-container::-webkit-scrollbar{display:none}.image-gallery-wrapper .image-gallery-popup .image-gallery-container .image-gallery-container-placeholder{display:flex;flex-direction:column;row-gap:.5rem;background-color:var(--background-color);border-radius:.5rem;place-items:center;padding:2rem;text-align:center}.image-gallery-wrapper .image-gallery-popup .image-gallery-container .image-gallery-container-placeholder p{color:var(--subtext-color-bright);font-family:Inter}.image-gallery-wrapper .image-gallery-popup .image-gallery-container .image-gallery-container-placeholder svg{width:4rem;height:4rem;color:var(--svg-color)}.image-gallery-wrapper .image-gallery-popup .image-gallery-container .image-gallery-load-more-btn{background-color:var(--btn-load-more);font-size:.85rem;padding:.5rem;margin-top:1rem}.image-gallery-wrapper .image-gallery-popup .image-gallery-container .image-gallery-load-more-btn:disabled:hover{background-color:var(--btn-load-more)}.image-gallery-wrapper .image-gallery-popup .image-gallery-container .image-gallery-load-more-btn:hover{background-color:var(--btn-load-more-hover)}.image-gallery-category-btn-group{width:max-content;column-gap:0;justify-content:stretch}.image-gallery-category-btn-group button{background-color:var(--btn-base-color);flex-grow:1}.image-gallery-category-btn-group button:hover{background-color:var(--btn-base-color-hover)}.image-gallery-category-btn-group button[data-selected=true]{background-color:var(--accent-color)}.image-gallery-category-btn-group button[data-selected=true]:hover{background-color:var(--accent-color-hover)}.image-gallery{display:grid;grid-gap:.5rem}.image-gallery .hoverable-image{padding:.5rem;position:relative}.image-gallery .hoverable-image:before{content:"";display:block;padding-bottom:100%}.image-gallery .hoverable-image .hoverable-image-image{position:absolute;max-width:100%;top:50%;left:50%;transform:translate(-50%,-50%)}.hoverable-image{display:flex;justify-content:center;transition:transform .2s ease-out}.hoverable-image:hover{cursor:pointer;border-radius:.5rem;z-index:2}.hoverable-image .hoverable-image-image{width:100%;height:100%;max-width:100%;max-height:100%}.hoverable-image .hoverable-image-delete-button{position:absolute;top:.25rem;right:.25rem}.hoverable-image .hoverable-image-content{display:flex;position:absolute;top:0;left:0;width:100%;height:100%;align-items:center;justify-content:center}.hoverable-image .hoverable-image-content .hoverable-image-check{fill:var(--status-good-color)}.hoverable-image .hoverable-image-icons{position:absolute;bottom:-2rem;display:grid;width:min-content;grid-template-columns:repeat(2,max-content);border-radius:.4rem;background-color:var(--background-color-secondary);padding:.2rem;gap:.2rem;grid-auto-rows:max-content}.hoverable-image .hoverable-image-icons button{width:12px;height:12px;border-radius:.2rem;padding:10px 0;flex-shrink:2}.hoverable-image .hoverable-image-icons button svg{width:12px;height:12px}.hoverable-image-context-menu{z-index:15;padding:.4rem;border-radius:.25rem;background-color:var(--context-menu-bg-color);box-shadow:var(--context-menu-box-shadow)}.hoverable-image-context-menu [role=menuitem]{font-size:.8rem;line-height:1rem;border-radius:3px;display:flex;align-items:center;height:1.75rem;padding:0 .5rem;position:relative;user-select:none;cursor:pointer;outline:none}.hoverable-image-context-menu [role=menuitem][data-disabled]{color:gray;pointer-events:none;cursor:not-allowed}.hoverable-image-context-menu [role=menuitem][data-warning]{color:var(--status-bad-color)}.hoverable-image-context-menu [role=menuitem][data-highlighted]{background-color:var(--context-menu-bg-color-hover)}.image-metadata-viewer{position:absolute;top:0;width:100%;border-radius:.5rem;padding:1rem;background-color:var(--metadata-bg-color);overflow:scroll;max-height:calc(100vh - (70px + 5.4rem));height:100%;z-index:10}.image-json-viewer{border-radius:.5rem;margin:0 .5rem 1rem;padding:1rem;overflow-x:scroll;word-break:break-all;background-color:var(--metadata-json-bg-color)}.lightbox-container{width:100%;height:100%;color:var(--text-color);overflow:hidden;position:absolute;left:0;top:0;background-color:var(--background-color-secondary);z-index:30;animation:popIn .3s ease-in}.lightbox-container .image-gallery-wrapper{max-height:100%!important}.lightbox-container .image-gallery-wrapper .image-gallery-container{max-height:calc(100vh - 5rem)}.lightbox-container .current-image-options{z-index:2;position:absolute;top:1rem}.lightbox-container .image-metadata-viewer{left:0;max-height:100%}.lightbox-close-btn{z-index:3;position:absolute;left:1rem;top:1rem;background-color:var(--btn-base-color)}.lightbox-close-btn:hover{background-color:var(--btn-base-color-hover)}.lightbox-close-btn:disabled:hover{background-color:var(--btn-base-color)}.lightbox-display-container{display:flex;flex-direction:row}.lightbox-preview-wrapper{overflow:hidden;background-color:var(--background-color-secondary);display:grid;grid-template-columns:auto max-content;place-items:center;width:100vw;height:100vh}.lightbox-preview-wrapper .current-image-next-prev-buttons{position:absolute}.lightbox-preview-wrapper .lightbox-image{grid-area:lightbox-content;border-radius:.5rem}.lightbox-preview-wrapper .lightbox-image-options{position:absolute;z-index:2;left:1rem;top:4.5rem;user-select:none;border-radius:.5rem;display:flex;flex-direction:column;row-gap:.5rem}@keyframes popIn{0%{opacity:0;filter:blur(100)}to{opacity:1;filter:blur(0)}}.app-tabs{display:grid;grid-template-columns:min-content auto;column-gap:1rem;height:calc(100vh - (70px + 1rem))}.app-tabs-list{display:grid;row-gap:.3rem;grid-auto-rows:min-content;color:var(--tab-list-text-inactive)}.app-tabs-list button{font-size:.85rem;padding:.5rem}.app-tabs-list button:hover{background-color:var(--tab-hover-color);border-radius:.3rem}.app-tabs-list button svg{width:26px;height:26px}.app-tabs-list button[aria-selected=true]{background-color:var(--tab-list-bg);color:var(--tab-list-text);font-weight:700;border-radius:.3rem;border:none}.app-tabs-panels .app-tabs-panel{padding:0;height:100%}.workarea-wrapper{position:relative;width:100%;height:100%}.workarea-wrapper .workarea-main{display:flex;column-gap:1rem;height:100%}.workarea-wrapper .workarea-main .workarea-children-wrapper{position:relative;width:100%;height:100%}.workarea-wrapper .workarea-main .workarea-split-view{width:100%;height:100%;display:grid;grid-template-columns:1fr 1fr;background-color:var(--background-color-secondary);border-radius:.5rem}.workarea-wrapper .workarea-main .workarea-split-view .workarea-split-view-left{padding-right:.5rem}.workarea-wrapper .workarea-main .workarea-split-view .workarea-split-view-right{padding-left:.5rem}.workarea-wrapper .workarea-main .workarea-single-view{width:100%;height:100%;background-color:var(--background-color-secondary);border-radius:.5rem}.workarea-wrapper .workarea-main .workarea-split-view-left,.workarea-wrapper .workarea-main .workarea-split-view-right{display:flex;flex-direction:column;height:100%;width:100%;row-gap:1rem;background-color:var(--background-color-secondary);border-radius:.5rem;padding:1rem}.workarea-split-button{position:absolute;cursor:pointer;padding:.5rem;top:0;right:0}.workarea-split-button[data-selected=true]{top:0;right:0}.workarea-split-button[data-selected=true] svg{opacity:1}.workarea-split-button svg{opacity:.5}.options-panel-wrapper-enter{transform:translate(-150%)}.options-panel-wrapper-enter-active{transform:translate(0);transition:all .12s ease-out}.options-panel-wrapper-exit{transform:translate(0)}.options-panel-wrapper-exit-active{transform:translate(-150%);transition:all .12s ease-out}.options-panel-wrapper{background-color:var(--background-color);height:calc(100vh - (70px + 1rem));width:22.5rem;max-width:22.5rem;flex-shrink:0;position:relative;overflow-y:scroll;-ms-overflow-style:none;scrollbar-width:none}.options-panel-wrapper::-webkit-scrollbar{display:none}.options-panel-wrapper .options-panel{display:flex;flex-direction:column;row-gap:1rem;height:100%;-ms-overflow-style:none;scrollbar-width:none;background-color:var(--background-color)}.options-panel-wrapper .options-panel::-webkit-scrollbar{display:none}.options-panel-wrapper[data-pinned=false]{z-index:20;position:fixed;top:0;left:0;filter:var(--floating-panel-drop-shadow);width:24.5rem;max-width:24.5rem;height:100%}.options-panel-wrapper[data-pinned=false] .options-panel-margin{margin:1rem}.options-panel-wrapper .options-panel-pin-button{position:absolute;cursor:pointer;padding:.5rem;top:1rem;right:1rem;z-index:20}.options-panel-wrapper .options-panel-pin-button[data-selected=true]{top:0;right:0}.options-panel-wrapper .options-panel-pin-button svg{opacity:.5}.invoke-ai-logo-wrapper{display:flex;align-items:center;column-gap:.7rem;padding-left:.5rem;padding-top:.3rem}.invoke-ai-logo-wrapper img{width:32px;height:32px}.invoke-ai-logo-wrapper h1{font-size:1.4rem}.text-to-image-area{padding:1rem;height:100%}.image-to-image-area{display:flex;flex-direction:column;row-gap:1rem;width:100%;height:100%}.image-to-image-strength-main-option{display:flex;row-gap:.5rem!important}.image-to-image-strength-main-option .invokeai__slider-component-label{color:var(--text-color-secondary);font-size:.9rem!important}.init-image-preview-header{display:flex;align-items:center;justify-content:space-between;width:100%}.init-image-preview-header h2{font-weight:700;font-size:.9rem}.init-image-preview{position:relative;height:100%;width:100%;display:flex;align-items:center;justify-content:center}.init-image-preview img{border-radius:.5rem;object-fit:contain;position:absolute}.image-to-image-current-image-display{position:relative}.floating-show-hide-button{position:absolute;top:50%;transform:translateY(-50%);z-index:20;padding:0;background-color:red!important;min-width:2rem;min-height:12rem;background-color:var(--btn-btn-base-color)!important}.floating-show-hide-button.left{left:0;border-radius:0 .5rem .5rem 0}.floating-show-hide-button.right{right:0;border-radius:.5rem 0 0 .5rem}.floating-show-hide-button:hover{background-color:var(--btn-btn-base-color-hover)}.floating-show-hide-button:disabled{background-color:var(--btn-base-color)}.floating-show-hide-button:disabled:hover{background-color:var(--btn-base-color)}.floating-show-hide-button svg{width:20px;height:20px;color:var(--btn-svg-color)}.show-hide-button-options{position:absolute;transform:translateY(-50%);z-index:20;min-width:2rem;top:50%;left:calc(42px + 2rem);border-radius:0 .5rem .5rem 0;display:flex;flex-direction:column;row-gap:.5rem}.show-hide-button-options button{border-radius:0 .3rem .3rem 0}.show-hide-button-gallery{padding-left:.75rem;padding-right:.75rem;background-color:var(--background-color)!important}.inpainting-main-area{display:flex;flex-direction:column;align-items:center;row-gap:1rem;width:100%;height:100%}.inpainting-main-area .inpainting-settings{display:flex;align-items:center;column-gap:.5rem}.inpainting-main-area .inpainting-settings svg{transform:scale(.9)}.inpainting-main-area .inpainting-settings .inpainting-buttons-group{display:flex;align-items:center;column-gap:.5rem}.inpainting-main-area .inpainting-settings .inpainting-button-dropdown{display:flex;flex-direction:column;row-gap:.5rem}.inpainting-main-area .inpainting-settings .inpainting-color-picker{margin-left:1rem}.inpainting-main-area .inpainting-settings .inpainting-brush-options{display:flex;align-items:center;column-gap:1rem}.inpainting-canvas-area{display:flex;flex-direction:column;align-items:center;justify-content:center;row-gap:1rem;width:100%;height:100%}.inpainting-canvas-spiner{display:flex;align-items:center;width:100%;height:100%}.inpainting-canvas-container{display:flex;position:relative;height:100%;width:100%;border-radius:.5rem}.inpainting-canvas-wrapper{position:relative}.inpainting-canvas-stage{outline:none;border-radius:.5rem;box-shadow:0 0 0 1px var(--border-color-light);overflow:hidden}.inpainting-canvas-stage canvas{outline:none;border-radius:.5rem}.inpainting-options-btn{min-height:2rem}.canvas-status-text{position:absolute;top:0;left:0;background-color:var(--background-color);opacity:.65;display:flex;flex-direction:column;font-size:.8rem;padding:.25rem;min-width:12rem;border-radius:.25rem;margin:.25rem;pointer-events:none}.invokeai__number-input-form-control{display:flex;align-items:center;column-gap:1rem}.invokeai__number-input-form-control .invokeai__number-input-form-label{color:var(--text-color-secondary)}.invokeai__number-input-form-control .invokeai__number-input-form-label[data-focus]+.invokeai__number-input-root{outline:none;border:2px solid var(--input-border-color);box-shadow:0 0 10px 0 var(--input-box-shadow-color)}.invokeai__number-input-form-control .invokeai__number-input-form-label[aria-invalid=true]+.invokeai__number-input-root{outline:none;border:2px solid var(--border-color-invalid);box-shadow:0 0 10px 0 var(--box-shadow-color-invalid)}.invokeai__number-input-form-control .invokeai__number-input-root{height:2rem;display:grid;grid-template-columns:auto max-content;column-gap:.5rem;align-items:center;background-color:var(--background-color-secondary);border:2px solid var(--border-color);border-radius:.3rem}.invokeai__number-input-form-control .invokeai__number-input-field{border:none;font-weight:700;width:100%;height:auto;font-size:.9rem;padding:0 .5rem}.invokeai__number-input-form-control .invokeai__number-input-field:focus{outline:none;box-shadow:none}.invokeai__number-input-form-control .invokeai__number-input-field:disabled{opacity:.2}.invokeai__number-input-form-control .invokeai__number-input-stepper{display:grid;padding-right:.5rem}.invokeai__number-input-form-control .invokeai__number-input-stepper .invokeai__number-input-stepper-button{border:none;padding:0 .5rem;margin:0 -.5rem}.invokeai__number-input-form-control .invokeai__number-input-stepper .invokeai__number-input-stepper-button svg{width:10px;height:10px}.input{display:grid;grid-template-columns:max-content auto;column-gap:1rem;align-items:center}.input .input-label{color:var(--text-color-secondary)}.input .input-entry{background-color:var(--background-color-secondary);border:2px solid var(--border-color);border-radius:.2rem;font-weight:700}.input .input-entry:focus{outline:none;border:2px solid var(--input-border-color);box-shadow:0 0 10px 0 var(--input-box-shadow-color)}.input .input-entry:disabled{opacity:.2}.input .input-entry[aria-invalid=true]{outline:none;border:2px solid var(--border-color-invalid);box-shadow:0 0 10px 0 var(--box-shadow-color-invalid)}.invokeai__icon-button{background:var(--btn-base-color);cursor:pointer}.invokeai__icon-button:hover{background-color:var(--btn-base-color-hover)}.invokeai__icon-button[data-selected=true]{background-color:var(--accent-color)}.invokeai__icon-button[data-selected=true]:hover{background-color:var(--accent-color-hover)}.invokeai__icon-button[disabled]{cursor:not-allowed}.invokeai__icon-button[data-variant=link],.invokeai__icon-button[data-variant=link]:hover{background:none}.invokeai__icon-button[data-as-checkbox=true]{background-color:var(--btn-base-color);border:3px solid var(--btn-base-color)}.invokeai__icon-button[data-as-checkbox=true] svg{fill:var(--text-color)}.invokeai__icon-button[data-as-checkbox=true]:hover{background-color:var(--btn-base-color);border-color:var(--btn-checkbox-border-hover)}.invokeai__icon-button[data-as-checkbox=true]:hover svg{fill:var(--text-color)}.invokeai__icon-button[data-as-checkbox=true][data-selected=true]{border-color:var(--accent-color)}.invokeai__icon-button[data-as-checkbox=true][data-selected=true] svg{fill:var(--accent-color-hover)}.invokeai__icon-button[data-as-checkbox=true][data-selected=true]:hover svg{fill:var(--accent-color-hover)}.invokeai__icon-button[data-alert=true]{animation-name:pulseColor;animation-duration:1s;animation-timing-function:ease-in-out;animation-iteration-count:infinite}.invokeai__icon-button[data-alert=true]:hover{animation:none;background-color:var(--accent-color-hover)}@keyframes pulseColor{0%{background-color:var(--accent-color)}50%{background-color:var(--accent-color-dim)}to{background-color:var(--accent-color)}}.invokeai__button{background-color:var(--btn-base-color);place-content:center}.invokeai__button:hover{background-color:var(--btn-base-color-hover)}.invokeai__switch-form-control .invokeai__switch-form-label{color:var(--text-color-secondary)}.invokeai__switch-form-control .invokeai__switch-root span{background-color:var(--switch-bg-color)}.invokeai__switch-form-control .invokeai__switch-root span span{background-color:var(--white)}.invokeai__switch-form-control .invokeai__switch-root[data-checked] span{background:var(--switch-bg-active-color)}.invokeai__switch-form-control .invokeai__switch-root[data-checked] span span{background-color:var(--white)}.invokeai__select{display:flex;column-gap:1rem;align-items:center}.invokeai__select .invokeai__select-label{color:var(--text-color-secondary)}.invokeai__select .invokeai__select-picker{border:2px solid var(--border-color);background-color:var(--background-color-secondary);font-weight:700;font-size:.9rem;height:2rem;border-radius:.2rem}.invokeai__select .invokeai__select-picker:focus{outline:none;border:2px solid var(--input-border-color);box-shadow:0 0 10px 0 var(--input-box-shadow-color)}.invokeai__select .invokeai__select-option{background-color:var(--background-color-secondary);color:var(--text-color-secondary)}.invokeai__slider-component{padding-bottom:.5rem;border-radius:.5rem}.invokeai__slider-component .invokeai__slider-component-label{min-width:max-content;margin:0;font-weight:700;font-size:.9rem;color:var(--text-color-secondary)}.invokeai__slider-component .invokeai__slider_track{background-color:var(--tab-color)}.invokeai__slider-component .invokeai__slider_track-filled{background-color:var(--slider-color)}.invokeai__slider-component .invokeai__slider-thumb{width:4px}.invokeai__slider-component .invokeai__slider-mark{font-size:.75rem;font-weight:700;color:var(--slider-mark-color);margin-top:.3rem}.invokeai__slider-component .invokeai__slider-number-input{border:none;font-size:.9rem;font-weight:700;height:2rem;background-color:var(--background-color-secondary);border:2px solid var(--border-color)}.invokeai__slider-component .invokeai__slider-number-input:focus{outline:none;box-shadow:none;border:2px solid var(--input-border-color);box-shadow:0 0 10px 0 var(--input-box-shadow-color)}.invokeai__slider-component .invokeai__slider-number-input:disabled{opacity:.2}.invokeai__slider-component .invokeai__slider-number-stepper{border:none}.invokeai__slider-component[data-markers=true] .invokeai__slider_container{margin-top:-1rem}.invokeai__checkbox .chakra-checkbox__label{margin-top:1px;color:var(--text-color-secondary);font-size:.9rem}.invokeai__checkbox .chakra-checkbox__control{width:1rem;height:1rem;border:none;border-radius:.2rem;background-color:var(--input-checkbox-bg)}.invokeai__checkbox .chakra-checkbox__control svg{width:.6rem;height:.6rem;stroke-width:3px}.invokeai__checkbox .chakra-checkbox__control[data-checked]{color:var(--text-color);background-color:var(--input-checkbox-checked-bg)}.invokeai__popover-content{min-width:unset;width:unset;padding:1rem;border-radius:.5rem;background-color:var(--background-color);border:2px solid var(--border-color)}.invokeai__popover-content .invokeai__popover-arrow{background-color:var(--background-color)!important}.invokeai__color-picker .react-colorful__hue-pointer,.invokeai__color-picker .react-colorful__saturation-pointer{width:1.5rem;height:1.5rem;border-color:var(--white)}.dropzone-container{position:absolute;top:0;left:0;width:100vw;height:100vh;z-index:999;backdrop-filter:blur(20px)}.dropzone-container .dropzone-overlay{opacity:.5;width:100%;height:100%;display:flex;flex-direction:column;row-gap:1rem;align-items:center;justify-content:center;background-color:var(--background-color)}.dropzone-container .dropzone-overlay.is-drag-accept{box-shadow:inset 0 0 20rem 1rem var(--accent-color)}.dropzone-container .dropzone-overlay.is-drag-reject{box-shadow:inset 0 0 20rem 1rem var(--status-bad-color)}.dropzone-container .dropzone-overlay.is-handling-upload{box-shadow:inset 0 0 20rem 1rem var(--status-working-color)}.image-uploader-button-outer{width:100%;height:100%;display:flex;align-items:center;justify-content:center;cursor:pointer;border-radius:.5rem;color:var(--tab-list-text-inactive);background-color:var(--background-color)}.image-uploader-button-outer:hover{background-color:var(--background-color-light)}.image-upload-button-inner{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.image-upload-button{display:flex;flex-direction:column;row-gap:2rem;align-items:center;justify-content:center;text-align:center}.image-upload-button svg{width:4rem;height:4rem}.image-upload-button h2{font-size:1.2rem}.work-in-progress{display:grid;width:100%;height:calc(100vh - (70px + 1rem));grid-auto-rows:max-content;background-color:var(--background-color-secondary);border-radius:.4rem;place-content:center;place-items:center;row-gap:1rem}.work-in-progress h1{font-size:2rem;font-weight:700}.work-in-progress p{text-align:center;max-width:50rem;color:var(--subtext-color-bright)}.guide-popover-arrow{background-color:var(--tab-panel-bg);box-shadow:none}.guide-popover-content{background-color:var(--background-color-secondary);border:none}.guide-popover-guide-content{background:var(--tab-panel-bg);border:2px solid var(--tab-hover-color);border-radius:.4rem;padding:.75rem 1rem;display:grid;grid-template-rows:repeat(auto-fill,1fr);grid-row-gap:.5rem;justify-content:space-between}.modal{background-color:var(--background-color-secondary);color:var(--text-color);font-family:Inter}.modal-close-btn{background-color:var(--btn-base-color)}.modal-close-btn:hover{background-color:var(--btn-base-color-hover)}.modal-close-btn:disabled:hover{background-color:var(--btn-base-color)}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body{-ms-overflow-style:none;scrollbar-width:none;background-color:var(--root-bg-color);overflow:hidden}html::-webkit-scrollbar,body::-webkit-scrollbar{display:none}#root{background-color:var(--root-bg-color);color:var(--text-color);font-family:Inter,Arial,Helvetica,sans-serif}\n',document.head.appendChild(r),{execute:function(){function r(e,t){for(var n=0;nr[t]})}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}var o="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function i(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var a={exports:{}},s={},l=Symbol.for("react.element"),c=Symbol.for("react.portal"),u=Symbol.for("react.fragment"),d=Symbol.for("react.strict_mode"),h=Symbol.for("react.profiler"),f=Symbol.for("react.provider"),p=Symbol.for("react.context"),g=Symbol.for("react.forward_ref"),m=Symbol.for("react.suspense"),v=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),b=Symbol.iterator;var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},w=Object.assign,k={};function S(e,t,n){this.props=e,this.context=t,this.refs=k,this.updater=n||x}function C(){}function _(e,t,n){this.props=e,this.context=t,this.refs=k,this.updater=n||x}S.prototype.isReactComponent={},S.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")},S.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},C.prototype=S.prototype;var E=_.prototype=new C;E.constructor=_,w(E,S.prototype),E.isPureReactComponent=!0;var L=Array.isArray,P=Object.prototype.hasOwnProperty,O={current:null},M={key:!0,ref:!0,__self:!0,__source:!0};function T(e,t,n){var r,o={},i=null,a=null;if(null!=t)for(r in void 0!==t.ref&&(a=t.ref),void 0!==t.key&&(i=""+t.key),t)P.call(t,r)&&!M.hasOwnProperty(r)&&(o[r]=t[r]);var s=arguments.length-2;if(1===s)o.children=n;else if(1>>1,i=e[r];if(!(0>>1;ro(l,n))co(u,l)?(e[r]=u,e[c]=n,r=c):(e[r]=l,e[s]=n,r=s);else{if(!(co(u,n)))break e;e[r]=u,e[c]=n,r=c}}}return t}function o(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}if("object"==typeof performance&&"function"==typeof performance.now){var i=performance;e.unstable_now=function(){return i.now()}}else{var a=Date,s=a.now();e.unstable_now=function(){return a.now()-s}}var l=[],c=[],u=1,d=null,h=3,f=!1,p=!1,g=!1,m="function"==typeof setTimeout?setTimeout:null,v="function"==typeof clearTimeout?clearTimeout:null,y="undefined"!=typeof setImmediate?setImmediate:null;function b(e){for(var o=n(c);null!==o;){if(null===o.callback)r(c);else{if(!(o.startTime<=e))break;r(c),o.sortIndex=o.expirationTime,t(l,o)}o=n(c)}}function x(e){if(g=!1,b(e),!p)if(null!==n(l))p=!0,A(w);else{var t=n(c);null!==t&&I(x,t.startTime-e)}}function w(t,o){p=!1,g&&(g=!1,v(_),_=-1),f=!0;var i=h;try{for(b(o),d=n(l);null!==d&&(!(d.expirationTime>o)||t&&!L());){var a=d.callback;if("function"==typeof a){d.callback=null,h=d.priorityLevel;var s=a(d.expirationTime<=o);o=e.unstable_now(),"function"==typeof s?d.callback=s:d===n(l)&&r(l),b(o)}else r(l);d=n(l)}if(null!==d)var u=!0;else{var m=n(c);null!==m&&I(x,m.startTime-o),u=!1}return u}finally{d=null,h=i,f=!1}}"undefined"!=typeof navigator&&void 0!==navigator.scheduling&&void 0!==navigator.scheduling.isInputPending&&navigator.scheduling.isInputPending.bind(navigator.scheduling);var k,S=!1,C=null,_=-1,E=5,P=-1;function L(){return!(e.unstable_now()-Pe||125a?(r.sortIndex=i,t(c,r),null===n(l)&&r===n(c)&&(g?(v(_),_=-1):g=!0,I(x,i-a))):(r.sortIndex=s,t(l,r),p||f||(p=!0,A(w))),r},e.unstable_shouldYield=L,e.unstable_wrapCallback=function(e){var t=h;return function(){var n=h;h=t;try{return e.apply(this,arguments)}finally{h=n}}}}(q),function(e){e.exports=q}(G); +!function(e){function t(e,t){var n=e.length;e.push(t);e:for(;0>>1,i=e[r];if(!(0>>1;ro(l,n))co(u,l)?(e[r]=u,e[c]=n,r=c):(e[r]=l,e[s]=n,r=s);else{if(!(co(u,n)))break e;e[r]=u,e[c]=n,r=c}}}return t}function o(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}if("object"==typeof performance&&"function"==typeof performance.now){var i=performance;e.unstable_now=function(){return i.now()}}else{var a=Date,s=a.now();e.unstable_now=function(){return a.now()-s}}var l=[],c=[],u=1,d=null,h=3,f=!1,p=!1,g=!1,m="function"==typeof setTimeout?setTimeout:null,v="function"==typeof clearTimeout?clearTimeout:null,y="undefined"!=typeof setImmediate?setImmediate:null;function b(e){for(var o=n(c);null!==o;){if(null===o.callback)r(c);else{if(!(o.startTime<=e))break;r(c),o.sortIndex=o.expirationTime,t(l,o)}o=n(c)}}function x(e){if(g=!1,b(e),!p)if(null!==n(l))p=!0,A(w);else{var t=n(c);null!==t&&I(x,t.startTime-e)}}function w(t,o){p=!1,g&&(g=!1,v(_),_=-1),f=!0;var i=h;try{for(b(o),d=n(l);null!==d&&(!(d.expirationTime>o)||t&&!P());){var a=d.callback;if("function"==typeof a){d.callback=null,h=d.priorityLevel;var s=a(d.expirationTime<=o);o=e.unstable_now(),"function"==typeof s?d.callback=s:d===n(l)&&r(l),b(o)}else r(l);d=n(l)}if(null!==d)var u=!0;else{var m=n(c);null!==m&&I(x,m.startTime-o),u=!1}return u}finally{d=null,h=i,f=!1}}"undefined"!=typeof navigator&&void 0!==navigator.scheduling&&void 0!==navigator.scheduling.isInputPending&&navigator.scheduling.isInputPending.bind(navigator.scheduling);var k,S=!1,C=null,_=-1,E=5,L=-1;function P(){return!(e.unstable_now()-Le||125a?(r.sortIndex=i,t(c,r),null===n(l)&&r===n(c)&&(g?(v(_),_=-1):g=!0,I(x,i-a))):(r.sortIndex=s,t(l,r),p||f||(p=!0,A(w))),r},e.unstable_shouldYield=P,e.unstable_wrapCallback=function(e){var t=h;return function(){var n=h;h=t;try{return e.apply(this,arguments)}finally{h=n}}}}(q),function(e){e.exports=q}(G); /** * @license React * react-dom.production.min.js @@ -18,7 +18,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -var Y=a.exports,Z=G.exports;function X(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n