diff --git a/html/locale_en.json b/html/locale_en.json index 25a6516b9..2c6e77e49 100644 --- a/html/locale_en.json +++ b/html/locale_en.json @@ -402,22 +402,22 @@ {"id":"","label":"Directory for temporary images; leave empty for default","localized":"","hint":""}, {"id":"","label":"Enable IPEX Optimize for Intel GPUs","localized":"","hint":""}, {"id":"","label":"Cleanup non-default temporary directory when starting webui","localized":"","hint":""}, - {"id":"","label":"Path to directory with stable diffusion checkpoints","localized":"","hint":""}, - {"id":"","label":"Path to directory with stable diffusion diffusers","localized":"","hint":""}, - {"id":"","label":"Path to directory with VAE files","localized":"","hint":""}, - {"id":"","label":"Embeddings directory for textual inversion","localized":"","hint":""}, - {"id":"","label":"Hypernetwork directory","localized":"","hint":""}, - {"id":"","label":"Path to directory with codeformer model file(s)","localized":"","hint":""}, - {"id":"","label":"Path to directory with GFPGAN model file(s)","localized":"","hint":""}, - {"id":"","label":"Path to directory with ESRGAN model file(s)","localized":"","hint":""}, - {"id":"","label":"Path to directory with BSRGAN model file(s)","localized":"","hint":""}, - {"id":"","label":"Path to directory with RealESRGAN model file(s)","localized":"","hint":""}, - {"id":"","label":"Path to directory with ScuNET model file(s)","localized":"","hint":""}, - {"id":"","label":"Path to directory with SwinIR model file(s)","localized":"","hint":""}, - {"id":"","label":"Path to directory with LDSR model file(s)","localized":"","hint":""}, - {"id":"","label":"Path to directory with CLIP model file(s)","localized":"","hint":""}, - {"id":"","label":"Path to directory with Lora network(s)","localized":"","hint":""}, - {"id":"","label":"Path to directory with LyCORIS network(s)","localized":"","hint":""}, + {"id":"","label":"Folder with stable diffusion models","localized":"","hint":""}, + {"id":"","label":"Folder with stable diffusion diffusers","localized":"","hint":""}, + {"id":"","label":"Folder with VAE files","localized":"","hint":""}, + {"id":"","label":"Folder with textual inversion embeddings","localized":"","hint":""}, + {"id":"","label":"Folder with Hypernetwork models","localized":"","hint":""}, + {"id":"","label":"Folder with codeformer Folder","localized":"","hint":""}, + {"id":"","label":"Folder with GFPGAN models","localized":"","hint":""}, + {"id":"","label":"Folder with ESRGAN models","localized":"","hint":""}, + {"id":"","label":"Folder with BSRGAN models","localized":"","hint":""}, + {"id":"","label":"Folder with RealESRGAN models","localized":"","hint":""}, + {"id":"","label":"Folder with ScuNET models","localized":"","hint":""}, + {"id":"","label":"Folder with SwinIR models","localized":"","hint":""}, + {"id":"","label":"Folder with LDSR models","localized":"","hint":""}, + {"id":"","label":"Folder with CLIP models","localized":"","hint":""}, + {"id":"","label":"Folder with Lora networks","localized":"","hint":""}, + {"id":"","label":"Folder with LyCORIS networks","localized":"","hint":""}, {"id":"","label":"Path to user-defined styles file","localized":"","hint":""}, {"id":"","label":"Always save all generated images","localized":"","hint":""}, {"id":"","label":"File format for generated images","localized":"","hint":"Select file format for images"}, diff --git a/javascript/black-teal.css b/javascript/black-teal.css index 1f76b8476..9bbcc4276 100644 --- a/javascript/black-teal.css +++ b/javascript/black-teal.css @@ -81,9 +81,10 @@ svg.feather.feather-image, .feather .feather-image { display: none } .label-wrap { margin: 16px 0px 8px 0px; } .gradio-slider input[type="number"] { width: 4.5em; font-size: 0.8rem; height: 20px; } .gradio-button.tool { border: none; background: none; box-shadow: none; filter: hue-rotate(340deg) saturate(0.5); } -#tab_extensions table td, #tab_extensions table th { border: none; padding: 0.5em; } -#tab_extensions table { width: 96vw } -#tab_extensions table thead { background-color: var(--neutral-700); } +#tab_extensions table td, #tab_extensions table th, #tab_config table td, #tab_config table th { border: none; padding: 0.5em; } +#tab_extensions table, #tab_config table { width: 96vw } +#tab_extensions table thead, #tab_config table thead { background-color: var(--neutral-700); } +#tab_extensions table, #tab_config table { background-color: #222222; } /* automatic style classes */ .progressDiv { border-radius: var(--radius-sm) !important; position: fixed; top: 44px; right: 26px; max-width: 262px; height: 48px; z-index: 99; box-shadow: var(--button-shadow); } @@ -108,7 +109,6 @@ svg.feather.feather-image, .feather .feather-image { display: none } #save-animation { border-radius: var(--radius-sm) !important; margin-bottom: 16px; background-color: #111111; } #script_list { padding: 4px; margin-top: 20px; margin-bottom: 20px; } #settings > div.flex-wrap { width: 15em; } -#tab_extensions table { background-color: #222222; } #txt2img_cfg_scale { min-width: 200px; } #txt2img_checkboxes, #img2img_checkboxes { background-color: transparent; } #txt2img_checkboxes, #img2img_checkboxes { margin-bottom: 0.2em; } diff --git a/javascript/style.css b/javascript/style.css index 11e6a5a88..396c2d5ed 100644 --- a/javascript/style.css +++ b/javascript/style.css @@ -149,7 +149,6 @@ div#extras_scale_to_tab div.form{ flex-direction: row; } .global-popup-close:before { content: "×"; } .global-popup-close{ position: fixed; right: 0.5em; top: 0; cursor: pointer; color: white; font-size: 32pt; } .global-popup-inner{ display: inline-block; margin: auto; padding: 2em; } -.ui-defaults-none{ color: #aaa !important; } /* fullpage image viewer */ @@ -210,8 +209,8 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt .context-menu-items a:hover { background: #a55000; } /* extensions */ -#tab_extensions table{ border-collapse: collapse; } -#tab_extensions table td, #tab_extensions table th { border: 1px solid #ccc; padding: 0.25em 0.5em; } +#tab_extensions table, #tab_config table{ border-collapse: collapse; } +#tab_extensions table td, #tab_extensions table th, #tab_config table td, #tab_config table th { border: 1px solid #ccc; padding: 0.25em 0.5em; } #tab_extensions table input[type="checkbox"] { margin-right: 0.5em; appearance: checkbox; } #tab_extensions button{ max-width: 16em; } #tab_extensions input[disabled="disabled"]{ opacity: 0.5; } diff --git a/modules/shared.py b/modules/shared.py index 277ae33af..f8a2f5d02 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -443,24 +443,24 @@ options_templates.update(options_section(('diffusers', "Diffusers Settings"), { options_templates.update(options_section(('system-paths', "System Paths"), { "temp_dir": OptionInfo("", "Directory for temporary images; leave empty for default"), "clean_temp_dir_at_start": OptionInfo(True, "Cleanup non-default temporary directory when starting webui"), - "ckpt_dir": OptionInfo(os.path.join(paths.models_path, 'Stable-diffusion'), "Path to directory with stable diffusion checkpoints"), - "diffusers_dir": OptionInfo(os.path.join(paths.models_path, 'Diffusers'), "Path to directory with stable diffusion diffusers"), - "vae_dir": OptionInfo(os.path.join(paths.models_path, 'VAE'), "Path to directory with VAE files"), + "ckpt_dir": OptionInfo(os.path.join(paths.models_path, 'Stable-diffusion'), "Folder with stable diffusion models"), + "diffusers_dir": OptionInfo(os.path.join(paths.models_path, 'Diffusers'), "Folder with Hugggingface models"), + "vae_dir": OptionInfo(os.path.join(paths.models_path, 'VAE'), "Folder with VAE files"), "sd_lora": OptionInfo("", "Add LoRA to prompt", gr.Textbox, {"visible": False}), - "lora_dir": OptionInfo(os.path.join(paths.models_path, 'Lora'), "Path to directory with LoRA network(s)"), - "lyco_dir": OptionInfo(os.path.join(paths.models_path, 'LyCORIS'), "Path to directory with LyCORIS network(s)"), - "styles_dir": OptionInfo(os.path.join(paths.data_path, 'styles.csv'), "Path to user-defined styles file"), - "embeddings_dir": OptionInfo(os.path.join(paths.models_path, 'embeddings'), "Embeddings directory for textual inversion"), - "hypernetwork_dir": OptionInfo(os.path.join(paths.models_path, 'hypernetworks'), "Hypernetwork directory"), - "codeformer_models_path": OptionInfo(os.path.join(paths.models_path, 'Codeformer'), "Path to directory with codeformer model file(s)"), - "gfpgan_models_path": OptionInfo(os.path.join(paths.models_path, 'GFPGAN'), "Path to directory with GFPGAN model file(s)"), - "esrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'ESRGAN'), "Path to directory with ESRGAN model file(s)"), - "bsrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'BSRGAN'), "Path to directory with BSRGAN model file(s)"), - "realesrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'RealESRGAN'), "Path to directory with RealESRGAN model file(s)"), - "scunet_models_path": OptionInfo(os.path.join(paths.models_path, 'ScuNET'), "Path to directory with ScuNET model file(s)"), - "swinir_models_path": OptionInfo(os.path.join(paths.models_path, 'SwinIR'), "Path to directory with SwinIR model file(s)"), - "ldsr_models_path": OptionInfo(os.path.join(paths.models_path, 'LDSR'), "Path to directory with LDSR model file(s)"), - "clip_models_path": OptionInfo(os.path.join(paths.models_path, 'CLIP'), "Path to directory with CLIP model file(s)"), + "lora_dir": OptionInfo(os.path.join(paths.models_path, 'Lora'), "Folder with LoRA network(s)"), + "lyco_dir": OptionInfo(os.path.join(paths.models_path, 'LyCORIS'), "Folder with LyCORIS network(s)"), + "styles_dir": OptionInfo(os.path.join(paths.data_path, 'styles.csv'), "File or Folder with user-defined styles"), + "embeddings_dir": OptionInfo(os.path.join(paths.models_path, 'embeddings'), "Folder with textual inversion embeddings"), + "hypernetwork_dir": OptionInfo(os.path.join(paths.models_path, 'hypernetworks'), "Folder with Hypernetwork models"), + "codeformer_models_path": OptionInfo(os.path.join(paths.models_path, 'Codeformer'), "Folder with codeformer models"), + "gfpgan_models_path": OptionInfo(os.path.join(paths.models_path, 'GFPGAN'), "Folder with GFPGAN models"), + "esrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'ESRGAN'), "Folder with ESRGAN models"), + "bsrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'BSRGAN'), "Folder with BSRGAN models"), + "realesrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'RealESRGAN'), "Folder with RealESRGAN models"), + "scunet_models_path": OptionInfo(os.path.join(paths.models_path, 'ScuNET'), "Folder with ScuNET models"), + "swinir_models_path": OptionInfo(os.path.join(paths.models_path, 'SwinIR'), "Folder with SwinIR models"), + "ldsr_models_path": OptionInfo(os.path.join(paths.models_path, 'LDSR'), "Folder with LDSR models"), + "clip_models_path": OptionInfo(os.path.join(paths.models_path, 'CLIP'), "Folder with CLIP models"), })) options_templates.update(options_section(('saving-images', "Image Options"), { diff --git a/modules/styles.py b/modules/styles.py index efb2bfc42..54815350f 100644 --- a/modules/styles.py +++ b/modules/styles.py @@ -71,7 +71,7 @@ class StyleDatabase: self.styles[style["name"]] = Style(style["name"], style.get("prompt", ""), style.get("negative", ""), style.get("extra", ""), fn, style.get("preview", "")) except Exception as e: log.error(f'Failed to load style: file={fn} error={e}') - elif os.path.isdir(fn): + elif os.path.isdir(fn) and not fn.startswith('.'): list_folder(fn) list_folder(self.path) diff --git a/modules/ui.py b/modules/ui.py index fdac4969b..34d777990 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -235,11 +235,11 @@ def create_toprow(is_img2img): with gr.Row(): with gr.Column(scale=80): with gr.Row(): - prompt = gr.Textbox(elem_id=f"{id_part}_prompt", show_label=False, lines=3, placeholder="Prompt", elem_classes=["prompt"]) + prompt = gr.Textbox(elem_id=f"{id_part}_prompt", label="Prompt", show_label=False, lines=3, placeholder="Prompt", elem_classes=["prompt"]) with gr.Row(): with gr.Column(scale=80): with gr.Row(): - negative_prompt = gr.Textbox(elem_id=f"{id_part}_neg_prompt", show_label=False, lines=3, placeholder="Negative prompt", elem_classes=["prompt"]) + negative_prompt = gr.Textbox(elem_id=f"{id_part}_neg_prompt", label="Negative prompt", show_label=False, lines=3, placeholder="Negative prompt", elem_classes=["prompt"]) button_interrogate = None button_deepbooru = None if is_img2img: @@ -1068,7 +1068,7 @@ def create_ui(startup_timer = None): with gr.TabItem("Show all pages", variant='primary', elem_id="settings_show_all_pages"): create_dirty_indicator("show_all_pages", [], interactive=False) - with gr.TabItem("UI Config", id="system_config", elem_id="system_config_tab"): + with gr.TabItem("UI Config", id="system_config", elem_id="tab_config"): loadsave.create_ui() create_dirty_indicator("tab_defaults", [], interactive=False) diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index ab6f86724..c789391a1 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -290,7 +290,7 @@ def refresh_extensions_list_from_data(search_text, sort_column): Current version - + """ if len(extensions_list) == 0: update_extension_list() diff --git a/modules/ui_extra_networks_textual_inversion.py b/modules/ui_extra_networks_textual_inversion.py index 51d077855..520c630d1 100644 --- a/modules/ui_extra_networks_textual_inversion.py +++ b/modules/ui_extra_networks_textual_inversion.py @@ -21,12 +21,18 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage): def list_items(self): if sd_models.model_data.sd_model is None: embeddings = [] - for root, _dirs, fns in os.walk(shared.opts.embeddings_dir, followlinks=True): - for fn in fns: - if fn.lower().endswith(".pt") or fn.lower().endswith(".safetensors"): - embedding = Embedding(0, fn) - embedding.filename = os.path.join(root, fn) + + def list_folder(folder): + for filename in os.listdir(folder): + fn = os.path.join(folder, filename) + if os.path.isfile(fn) and (fn.lower().endswith(".pt") or fn.lower().endswith(".safetensors")): + embedding = Embedding(0, os.path.basename(fn)) + embedding.filename = fn embeddings.append(embedding) + elif os.path.isdir(fn) and not fn.startswith('.'): + list_folder(fn) + + list_folder(shared.opts.embeddings_dir) elif shared.backend == shared.Backend.ORIGINAL: embeddings = list(sd_hijack.model_hijack.embedding_db.word_embeddings.values()) elif hasattr(sd_models.model_data.sd_model, 'embedding_db'): diff --git a/modules/ui_loadsave.py b/modules/ui_loadsave.py index b0c647e05..0bd24ff74 100644 --- a/modules/ui_loadsave.py +++ b/modules/ui_loadsave.py @@ -112,7 +112,6 @@ class UiLoadsave: self.write_to_file(self.ui_settings) def iter_changes(self, values): - from modules.shared import log """ given a dictionary with defaults from a file and current values from gradio elements, returns an iterator over tuples of values that are not the same between the file and the current; @@ -138,27 +137,49 @@ class UiLoadsave: continue if (new_value == default_value) and (old_value is None): continue - log.debug(f'Settings: name={name} component={component} old={old_value} default={default_value} new={new_value}') yield name, old_value, new_value, default_value return [] def ui_view(self, *values): - text = [''] - for path, old_value, new_value, default_value in self.iter_changes(values): + text = """ +
VariableUser valueNew valueDefault value
+ + + + + + + + + + + + + + + """ + changed = 0 + for name, old_value, new_value, default_value in self.iter_changes(values): + changed += 1 if old_value is None: - old_value = "None" - text.append(f"") - if len(text) == 1: - text.append("") - text.append("") - return "".join(text) + old_value = "None" + text += f"" + text += "
NameSaved valueNew valueDefault value
{path}{old_value}{new_value}{default_value}
No changes
{name}{old_value}{new_value}{default_value}
" + if changed == 0: + text = '

No changes

' + else: + text = f'

Changed values: {changed}

' + text + return text def ui_apply(self, *values): + from modules.shared import log num_changed = 0 current_ui_settings = self.read_from_file() - for path, _, new_value, _ in self.iter_changes(values): + for name, old_value, new_value, default_value in self.iter_changes(values): + component = self.component_mapping[name] + log.debug(f'Settings: name={name} component={component} old={old_value} default={default_value} new={new_value}') num_changed += 1 - current_ui_settings[path] = new_value + current_ui_settings[name] = new_value if num_changed == 0: return "No changes" self.write_to_file(current_ui_settings) @@ -173,12 +194,12 @@ class UiLoadsave: def create_ui(self): """creates ui elements for editing defaults UI, without adding any logic to them""" - gr.HTML(f"Review changed values and apply them as new user interface defaults
Config file: {self.filename}") with gr.Row(elem_id="config_row"): self.ui_defaults_view = gr.Button(value='View changes', elem_id="ui_defaults_view", variant="secondary") self.ui_defaults_apply = gr.Button(value='Set new defaults', elem_id="ui_defaults_apply", variant="primary") self.ui_defaults_restore = gr.Button(value='Restore system defaults', elem_id="ui_defaults_restore", variant="primary") self.ui_defaults_review = gr.HTML("") + gr.HTML(f"Review changed values and apply them as new user interface defaults

Config file: {self.filename}") def setup_ui(self): """adds logic to elements created with create_ui; all add_block class must be made before this"""