From f2844bd024ff3cf7ee712153a514afd9085980db Mon Sep 17 00:00:00 2001 From: anapnoe <124302297+anapnoe@users.noreply.github.com> Date: Tue, 31 Oct 2023 19:52:17 +0200 Subject: [PATCH] update to sdxl --- .../Lora/extra_networks_lora.py | 10 +- extensions-builtin/Lora/lora_patches.py | 31 + extensions-builtin/Lora/network.py | 8 +- extensions-builtin/Lora/network_full.py | 7 +- extensions-builtin/Lora/network_norm.py | 28 + extensions-builtin/Lora/networks.py | 183 +- .../Lora/scripts/lora_script.py | 44 +- .../Lora/ui_edit_user_metadata.py | 3 +- .../Lora/ui_extra_networks_lora.py | 7 +- .../canvas-zoom-and-pan/javascript/zoom.js | 216 +- .../scripts/hotkey_config.py | 1 + .../canvas-zoom-and-pan/style.css | 3 + .../scripts/extra_options_section.py | 46 +- .../mobile/javascript/mobile.js | 6 + extensions-builtin/sd_theme_editor/install.py | 1 - .../sd_theme_editor/javascript/ui_theme.js | 435 ---- .../sd_theme_editor/scripts/ui_theme.py | 177 -- extensions-builtin/sd_theme_editor/style.css | 113 - .../sd_theme_editor/themes/Golde.css | 1 - .../sd_theme_editor/themes/backup.css | 1 - .../sd_theme_editor/themes/d-230-52-94.css | 1 - .../sd_theme_editor/themes/default.css | 1 - .../sd_theme_editor/themes/default_cyan.css | 1 - .../sd_theme_editor/themes/default_orange.css | 1 - .../sd_theme_editor/themes/fun.css | 1 - .../sd_theme_editor/themes/minimal.css | 1 - .../sd_theme_editor/themes/minimal_orange.css | 1 - .../sd_theme_editor/themes/moonlight.css | 1 - .../sd_theme_editor/themes/ogxBGreen.css | 1 - .../sd_theme_editor/themes/ogxCyan.css | 1 - .../sd_theme_editor/themes/ogxCyanInvert.css | 1 - .../sd_theme_editor/themes/ogxGreen.css | 1 - .../sd_theme_editor/themes/ogxRed.css | 1 - .../sd_theme_editor/themes/retrog.css | 1 - .../sd_theme_editor/themes/tron.css | 1 - .../sd_theme_editor/themes/tron2.css | 1 - html/200w.webp | Bin 76140 -> 0 bytes html/card-no-preview.png | Bin 108 -> 84440 bytes html/extra-networks-card.html | 20 +- html/favicon.ico | Bin 4286 -> 0 bytes html/footer.html | 70 +- html/spotlight.css | 363 --- html/svg/add-box-line.svg | 1 - html/svg/arrow-go-back-line.svg | 1 - html/svg/artboard-line.svg | 1 - html/svg/aspect-ratio-line.svg | 1 - html/svg/ball-pen-line.svg | 1 - html/svg/bmc-logo2.svg | 17 - html/svg/brush-line.svg | 1 - html/svg/checkbox-blank-line.svg | 1 - html/svg/checkbox-indeterminate-line.svg | 1 - html/svg/clipboard-line.svg | 1 - html/svg/close-circle-line.svg | 1 - html/svg/close-line.svg | 1 - html/svg/compass-4-line.svg | 1 - html/svg/computer-fill.svg | 1 - html/svg/contrast-drop-2-line.svg | 1 - html/svg/cpu-fill.svg | 1 - html/svg/cpu-line.svg | 1 - html/svg/delete-bin-2-line.svg | 1 - html/svg/delete-bin-5-line.svg | 1 - html/svg/dice-1.svg | 15 - html/svg/download-line.svg | 1 - html/svg/drag-drop-line.svg | 1 - html/svg/drag-move-2-fill.svg | 1 - html/svg/file-download-line.svg | 1 - html/svg/file-zip-line.svg | 1 - html/svg/folder-open-line.svg | 1 - html/svg/fullscreen-exit-line.svg | 1 - html/svg/fullscreen-line.svg | 1 - html/svg/github-fill.svg | 1 - html/svg/gradio-fill.svg | 4 - html/svg/grid-fill.svg | 1 - html/svg/grid-line.svg | 1 - html/svg/heart-add-fill.svg | 1 - html/svg/heart-fill.svg | 1 - html/svg/heart-line.svg | 1 - html/svg/image-icon.svg | 1 - html/svg/image-update.svg | 7 - html/svg/infinity-fill.svg | 14 - html/svg/information-fill.svg | 1 - html/svg/information-line.svg | 1 - html/svg/landscape-line.svg | 1 - html/svg/magic-line.svg | 1 - html/svg/menu-4-fill.svg | 1 - html/svg/menu-add-fill.svg | 1 - html/svg/menu-line.svg | 1 - html/svg/more-2-fill.svg | 1 - html/svg/paint-brush-line.svg | 1 - html/svg/pantone-line.svg | 1 - html/svg/picture-in-picture-exit-line.svg | 1 - html/svg/play-line.svg | 1 - html/svg/pulse-line.svg | 1 - html/svg/question-answer-fill.svg | 1 - html/svg/question-answer-line.svg | 1 - html/svg/recycle-line.svg | 1 - html/svg/refresh-line.svg | 1 - html/svg/reply-fill.svg | 1 - html/svg/reply-line.svg | 1 - html/svg/restart-fill.svg | 1 - html/svg/rocket-fill.svg | 13 - html/svg/save-2-line.svg | 1 - html/svg/save-3-line.svg | 1 - html/svg/skip-forward-line.svg | 1 - html/svg/sort-asc.svg | 1 - html/svg/sort-desc.svg | 1 - html/svg/spotlight/arrow.svg | 1 - html/svg/spotlight/error.svg | 1 - html/svg/spotlight/pause.svg | 1 - html/svg/spotlight/pixel.gif | Bin 43 -> 0 bytes html/svg/spotlight/play.svg | 1 - html/svg/spotlight/preloader.svg | 1 - html/svg/spotlight/theme.svg | 1 - html/svg/stack-line.svg | 1 - html/svg/stop-line.svg | 1 - html/svg/t-box-fill.svg | 1 - html/svg/terminal-box-fill.svg | 1 - html/svg/terminal-box-line.svg | 1 - html/svg/zoom-in-line.svg | 1 - html/svg/zoom-out-line.svg | 1 - javascript/aspectRatioOverlay.js | 1 + javascript/badScaleChecker.js | 108 - javascript/contextMenus.js | 3 +- javascript/dragdrop.js | 199 +- javascript/extensions.js | 3 +- javascript/extraNetworks.js | 928 +++----- javascript/generationParams.js | 5 + javascript/hints.js | 23 +- javascript/hires_fix.js | 37 +- javascript/imageMaskFix.js | 300 +-- javascript/imageviewer.js | 447 ++-- javascript/imageviewerGamepad.js | 2 +- javascript/inputAccordion.js | 37 + javascript/localStorage.js | 26 + javascript/localization.js | 44 +- javascript/notification.js | 6 +- javascript/progressbar.js | 252 +- javascript/resizeHandle.js | 141 ++ javascript/spotlight.js | 13 - javascript/textualInversion.js | 3 + javascript/ui.js | 2037 ++--------------- javascript/ui_settings_hints.js | 2 + modules/api/api.py | 58 +- modules/api/models.py | 15 +- modules/cache.py | 8 +- modules/call_queue.py | 7 +- modules/cmd_args.py | 10 +- modules/config_states.py | 16 +- modules/devices.py | 42 +- modules/errors.py | 53 +- modules/extensions.py | 16 +- modules/extra_networks.py | 79 +- modules/extras.py | 39 +- modules/fifo_lock.py | 37 + modules/generation_parameters_copypaste.py | 54 +- modules/gradio_extensons.py | 73 + modules/hypernetworks/hypernetwork.py | 5 +- modules/images.py | 50 +- modules/img2img.py | 60 +- modules/initialize.py | 168 ++ modules/initialize_util.py | 202 ++ modules/interrogate.py | 5 +- modules/launch_utils.py | 88 +- modules/localization.py | 3 +- modules/logging_config.py | 16 + modules/lowvram.py | 21 +- modules/mac_specific.py | 7 +- modules/options.py | 245 ++ modules/patches.py | 64 + modules/postprocessing.py | 61 +- modules/processing.py | 890 ++++--- modules/processing_scripts/refiner.py | 49 + modules/processing_scripts/seed.py | 111 + modules/progress.py | 49 +- modules/prompt_parser.py | 58 +- modules/realesrgan_model.py | 1 + modules/rng.py | 170 ++ modules/rng_philox.py | 102 + modules/script_callbacks.py | 29 + modules/scripts.py | 194 +- modules/sd_disable_initialization.py | 149 +- modules/sd_hijack.py | 36 +- modules/sd_hijack_clip.py | 13 +- modules/sd_hijack_inpainting.py | 97 - modules/sd_hijack_optimizations.py | 17 +- modules/sd_models.py | 295 ++- modules/sd_models_config.py | 3 +- modules/sd_models_types.py | 31 + modules/sd_models_xl.py | 17 +- modules/sd_samplers.py | 19 +- modules/sd_samplers_cfg_denoiser.py | 230 ++ modules/sd_samplers_common.py | 270 ++- modules/sd_samplers_compvis.py | 224 -- modules/sd_samplers_extra.py | 74 + modules/sd_samplers_kdiffusion.py | 380 +-- modules/sd_samplers_timesteps.py | 167 ++ modules/sd_samplers_timesteps_impl.py | 137 ++ modules/sd_unet.py | 2 +- modules/sd_vae.py | 105 +- modules/sd_vae_approx.py | 2 +- modules/sd_vae_taesd.py | 52 +- modules/shared.py | 901 +------- modules/shared_cmd_options.py | 18 + modules/shared_gradio_themes.py | 67 + modules/shared_init.py | 49 + modules/shared_items.py | 54 +- modules/shared_options.py | 332 +++ modules/shared_state.py | 159 ++ modules/shared_total_tqdm.py | 37 + modules/styles.py | 5 +- modules/sub_quadratic_attention.py | 4 +- modules/sysinfo.py | 32 +- .../textual_inversion/textual_inversion.py | 23 +- modules/txt2img.py | 17 +- modules/ui.py | 1812 +++++---------- modules/ui_checkpoint_merger.py | 124 + modules/ui_common.py | 56 +- modules/ui_components.py | 75 +- modules/ui_extensions.py | 262 ++- modules/ui_extra_networks.py | 127 +- modules/ui_extra_networks_checkpoints.py | 10 +- ...xtra_networks_checkpoints_user_metadata.py | 66 + modules/ui_extra_networks_hypernets.py | 8 +- .../ui_extra_networks_textual_inversion.py | 5 +- modules/ui_extra_networks_user_metadata.py | 20 +- modules/ui_loadsave.py | 27 +- modules/ui_postprocessing.py | 40 +- modules/ui_prompt_styles.py | 110 + modules/ui_settings.py | 4 +- modules/ui_tempdir.py | 7 +- modules/util.py | 58 + scripts/postprocessing_upscale.py | 18 +- scripts/xyz_grid.py | 173 +- 233 files changed, 7985 insertions(+), 8738 deletions(-) create mode 100644 extensions-builtin/Lora/lora_patches.py create mode 100644 extensions-builtin/Lora/network_norm.py delete mode 100644 extensions-builtin/sd_theme_editor/install.py delete mode 100644 extensions-builtin/sd_theme_editor/javascript/ui_theme.js delete mode 100644 extensions-builtin/sd_theme_editor/scripts/ui_theme.py delete mode 100644 extensions-builtin/sd_theme_editor/style.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/Golde.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/backup.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/d-230-52-94.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/default.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/default_cyan.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/default_orange.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/fun.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/minimal.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/minimal_orange.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/moonlight.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/ogxBGreen.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/ogxCyan.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/ogxCyanInvert.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/ogxGreen.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/ogxRed.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/retrog.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/tron.css delete mode 100644 extensions-builtin/sd_theme_editor/themes/tron2.css delete mode 100644 html/200w.webp delete mode 100644 html/favicon.ico delete mode 100644 html/spotlight.css delete mode 100644 html/svg/add-box-line.svg delete mode 100644 html/svg/arrow-go-back-line.svg delete mode 100644 html/svg/artboard-line.svg delete mode 100644 html/svg/aspect-ratio-line.svg delete mode 100644 html/svg/ball-pen-line.svg delete mode 100644 html/svg/bmc-logo2.svg delete mode 100644 html/svg/brush-line.svg delete mode 100644 html/svg/checkbox-blank-line.svg delete mode 100644 html/svg/checkbox-indeterminate-line.svg delete mode 100644 html/svg/clipboard-line.svg delete mode 100644 html/svg/close-circle-line.svg delete mode 100644 html/svg/close-line.svg delete mode 100644 html/svg/compass-4-line.svg delete mode 100644 html/svg/computer-fill.svg delete mode 100644 html/svg/contrast-drop-2-line.svg delete mode 100644 html/svg/cpu-fill.svg delete mode 100644 html/svg/cpu-line.svg delete mode 100644 html/svg/delete-bin-2-line.svg delete mode 100644 html/svg/delete-bin-5-line.svg delete mode 100644 html/svg/dice-1.svg delete mode 100644 html/svg/download-line.svg delete mode 100644 html/svg/drag-drop-line.svg delete mode 100644 html/svg/drag-move-2-fill.svg delete mode 100644 html/svg/file-download-line.svg delete mode 100644 html/svg/file-zip-line.svg delete mode 100644 html/svg/folder-open-line.svg delete mode 100644 html/svg/fullscreen-exit-line.svg delete mode 100644 html/svg/fullscreen-line.svg delete mode 100644 html/svg/github-fill.svg delete mode 100644 html/svg/gradio-fill.svg delete mode 100644 html/svg/grid-fill.svg delete mode 100644 html/svg/grid-line.svg delete mode 100644 html/svg/heart-add-fill.svg delete mode 100644 html/svg/heart-fill.svg delete mode 100644 html/svg/heart-line.svg delete mode 100644 html/svg/image-icon.svg delete mode 100644 html/svg/image-update.svg delete mode 100644 html/svg/infinity-fill.svg delete mode 100644 html/svg/information-fill.svg delete mode 100644 html/svg/information-line.svg delete mode 100644 html/svg/landscape-line.svg delete mode 100644 html/svg/magic-line.svg delete mode 100644 html/svg/menu-4-fill.svg delete mode 100644 html/svg/menu-add-fill.svg delete mode 100644 html/svg/menu-line.svg delete mode 100644 html/svg/more-2-fill.svg delete mode 100644 html/svg/paint-brush-line.svg delete mode 100644 html/svg/pantone-line.svg delete mode 100644 html/svg/picture-in-picture-exit-line.svg delete mode 100644 html/svg/play-line.svg delete mode 100644 html/svg/pulse-line.svg delete mode 100644 html/svg/question-answer-fill.svg delete mode 100644 html/svg/question-answer-line.svg delete mode 100644 html/svg/recycle-line.svg delete mode 100644 html/svg/refresh-line.svg delete mode 100644 html/svg/reply-fill.svg delete mode 100644 html/svg/reply-line.svg delete mode 100644 html/svg/restart-fill.svg delete mode 100644 html/svg/rocket-fill.svg delete mode 100644 html/svg/save-2-line.svg delete mode 100644 html/svg/save-3-line.svg delete mode 100644 html/svg/skip-forward-line.svg delete mode 100644 html/svg/sort-asc.svg delete mode 100644 html/svg/sort-desc.svg delete mode 100644 html/svg/spotlight/arrow.svg delete mode 100644 html/svg/spotlight/error.svg delete mode 100644 html/svg/spotlight/pause.svg delete mode 100644 html/svg/spotlight/pixel.gif delete mode 100644 html/svg/spotlight/play.svg delete mode 100644 html/svg/spotlight/preloader.svg delete mode 100644 html/svg/spotlight/theme.svg delete mode 100644 html/svg/stack-line.svg delete mode 100644 html/svg/stop-line.svg delete mode 100644 html/svg/t-box-fill.svg delete mode 100644 html/svg/terminal-box-fill.svg delete mode 100644 html/svg/terminal-box-line.svg delete mode 100644 html/svg/zoom-in-line.svg delete mode 100644 html/svg/zoom-out-line.svg delete mode 100644 javascript/badScaleChecker.js create mode 100644 javascript/inputAccordion.js create mode 100644 javascript/localStorage.js create mode 100644 javascript/resizeHandle.js delete mode 100644 javascript/spotlight.js create mode 100644 modules/fifo_lock.py create mode 100644 modules/gradio_extensons.py create mode 100644 modules/initialize.py create mode 100644 modules/initialize_util.py create mode 100644 modules/logging_config.py create mode 100644 modules/options.py create mode 100644 modules/patches.py create mode 100644 modules/processing_scripts/refiner.py create mode 100644 modules/processing_scripts/seed.py create mode 100644 modules/rng.py create mode 100644 modules/rng_philox.py delete mode 100644 modules/sd_hijack_inpainting.py create mode 100644 modules/sd_models_types.py create mode 100644 modules/sd_samplers_cfg_denoiser.py create mode 100644 modules/sd_samplers_extra.py create mode 100644 modules/sd_samplers_timesteps.py create mode 100644 modules/sd_samplers_timesteps_impl.py create mode 100644 modules/shared_cmd_options.py create mode 100644 modules/shared_gradio_themes.py create mode 100644 modules/shared_init.py create mode 100644 modules/shared_options.py create mode 100644 modules/shared_state.py create mode 100644 modules/shared_total_tqdm.py create mode 100644 modules/ui_checkpoint_merger.py create mode 100644 modules/ui_extra_networks_checkpoints_user_metadata.py create mode 100644 modules/ui_prompt_styles.py create mode 100644 modules/util.py diff --git a/extensions-builtin/Lora/extra_networks_lora.py b/extensions-builtin/Lora/extra_networks_lora.py index ba2945c6..005ff32c 100644 --- a/extensions-builtin/Lora/extra_networks_lora.py +++ b/extensions-builtin/Lora/extra_networks_lora.py @@ -6,9 +6,14 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork): def __init__(self): super().__init__('lora') + self.errors = {} + """mapping of network names to the number of errors the network had during operation""" + def activate(self, p, params_list): additional = shared.opts.sd_lora + self.errors.clear() + if additional != "None" and additional in networks.available_networks and not any(x for x in params_list if x.items[0] == additional): p.all_prompts = [x + f"" for x in p.all_prompts] params_list.append(extra_networks.ExtraNetworkParams(items=[additional, shared.opts.extra_networks_default_multiplier])) @@ -56,4 +61,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork): p.extra_generation_params["Lora hashes"] = ", ".join(network_hashes) def deactivate(self, p): - pass + if self.errors: + p.comment("Networks with errors: " + ", ".join(f"{k} ({v})" for k, v in self.errors.items())) + + self.errors.clear() diff --git a/extensions-builtin/Lora/lora_patches.py b/extensions-builtin/Lora/lora_patches.py new file mode 100644 index 00000000..59859e6f --- /dev/null +++ b/extensions-builtin/Lora/lora_patches.py @@ -0,0 +1,31 @@ +import torch + +import networks +from modules import patches + + +class LoraPatches: + def __init__(self): + self.Linear_forward = patches.patch(__name__, torch.nn.Linear, 'forward', networks.network_Linear_forward) + self.Linear_load_state_dict = patches.patch(__name__, torch.nn.Linear, '_load_from_state_dict', networks.network_Linear_load_state_dict) + self.Conv2d_forward = patches.patch(__name__, torch.nn.Conv2d, 'forward', networks.network_Conv2d_forward) + self.Conv2d_load_state_dict = patches.patch(__name__, torch.nn.Conv2d, '_load_from_state_dict', networks.network_Conv2d_load_state_dict) + self.GroupNorm_forward = patches.patch(__name__, torch.nn.GroupNorm, 'forward', networks.network_GroupNorm_forward) + self.GroupNorm_load_state_dict = patches.patch(__name__, torch.nn.GroupNorm, '_load_from_state_dict', networks.network_GroupNorm_load_state_dict) + self.LayerNorm_forward = patches.patch(__name__, torch.nn.LayerNorm, 'forward', networks.network_LayerNorm_forward) + self.LayerNorm_load_state_dict = patches.patch(__name__, torch.nn.LayerNorm, '_load_from_state_dict', networks.network_LayerNorm_load_state_dict) + self.MultiheadAttention_forward = patches.patch(__name__, torch.nn.MultiheadAttention, 'forward', networks.network_MultiheadAttention_forward) + self.MultiheadAttention_load_state_dict = patches.patch(__name__, torch.nn.MultiheadAttention, '_load_from_state_dict', networks.network_MultiheadAttention_load_state_dict) + + def undo(self): + self.Linear_forward = patches.undo(__name__, torch.nn.Linear, 'forward') + self.Linear_load_state_dict = patches.undo(__name__, torch.nn.Linear, '_load_from_state_dict') + self.Conv2d_forward = patches.undo(__name__, torch.nn.Conv2d, 'forward') + self.Conv2d_load_state_dict = patches.undo(__name__, torch.nn.Conv2d, '_load_from_state_dict') + self.GroupNorm_forward = patches.undo(__name__, torch.nn.GroupNorm, 'forward') + self.GroupNorm_load_state_dict = patches.undo(__name__, torch.nn.GroupNorm, '_load_from_state_dict') + self.LayerNorm_forward = patches.undo(__name__, torch.nn.LayerNorm, 'forward') + self.LayerNorm_load_state_dict = patches.undo(__name__, torch.nn.LayerNorm, '_load_from_state_dict') + self.MultiheadAttention_forward = patches.undo(__name__, torch.nn.MultiheadAttention, 'forward') + self.MultiheadAttention_load_state_dict = patches.undo(__name__, torch.nn.MultiheadAttention, '_load_from_state_dict') + diff --git a/extensions-builtin/Lora/network.py b/extensions-builtin/Lora/network.py index 8ecfa29a..d8e8dfb7 100644 --- a/extensions-builtin/Lora/network.py +++ b/extensions-builtin/Lora/network.py @@ -1,3 +1,4 @@ +from __future__ import annotations import os from collections import namedtuple import enum @@ -132,7 +133,7 @@ class NetworkModule: return 1.0 - def finalize_updown(self, updown, orig_weight, output_shape): + def finalize_updown(self, updown, orig_weight, output_shape, ex_bias=None): if self.bias is not None: updown = updown.reshape(self.bias.shape) updown += self.bias.to(orig_weight.device, dtype=orig_weight.dtype) @@ -144,7 +145,10 @@ class NetworkModule: if orig_weight.size().numel() == updown.size().numel(): updown = updown.reshape(orig_weight.shape) - return updown * self.calc_scale() * self.multiplier() + if ex_bias is not None: + ex_bias = ex_bias * self.multiplier() + + return updown * self.calc_scale() * self.multiplier(), ex_bias def calc_updown(self, target): raise NotImplementedError() diff --git a/extensions-builtin/Lora/network_full.py b/extensions-builtin/Lora/network_full.py index 109b4c2c..bf6930e9 100644 --- a/extensions-builtin/Lora/network_full.py +++ b/extensions-builtin/Lora/network_full.py @@ -14,9 +14,14 @@ class NetworkModuleFull(network.NetworkModule): super().__init__(net, weights) self.weight = weights.w.get("diff") + self.ex_bias = weights.w.get("diff_b") def calc_updown(self, orig_weight): output_shape = self.weight.shape updown = self.weight.to(orig_weight.device, dtype=orig_weight.dtype) + if self.ex_bias is not None: + ex_bias = self.ex_bias.to(orig_weight.device, dtype=orig_weight.dtype) + else: + ex_bias = None - return self.finalize_updown(updown, orig_weight, output_shape) + return self.finalize_updown(updown, orig_weight, output_shape, ex_bias) diff --git a/extensions-builtin/Lora/network_norm.py b/extensions-builtin/Lora/network_norm.py new file mode 100644 index 00000000..ce450158 --- /dev/null +++ b/extensions-builtin/Lora/network_norm.py @@ -0,0 +1,28 @@ +import network + + +class ModuleTypeNorm(network.ModuleType): + def create_module(self, net: network.Network, weights: network.NetworkWeights): + if all(x in weights.w for x in ["w_norm", "b_norm"]): + return NetworkModuleNorm(net, weights) + + return None + + +class NetworkModuleNorm(network.NetworkModule): + def __init__(self, net: network.Network, weights: network.NetworkWeights): + super().__init__(net, weights) + + self.w_norm = weights.w.get("w_norm") + self.b_norm = weights.w.get("b_norm") + + def calc_updown(self, orig_weight): + output_shape = self.w_norm.shape + updown = self.w_norm.to(orig_weight.device, dtype=orig_weight.dtype) + + if self.b_norm is not None: + ex_bias = self.b_norm.to(orig_weight.device, dtype=orig_weight.dtype) + else: + ex_bias = None + + return self.finalize_updown(updown, orig_weight, output_shape, ex_bias) diff --git a/extensions-builtin/Lora/networks.py b/extensions-builtin/Lora/networks.py index 17cbe1bb..96f935b2 100644 --- a/extensions-builtin/Lora/networks.py +++ b/extensions-builtin/Lora/networks.py @@ -1,12 +1,15 @@ +import logging import os import re +import lora_patches import network import network_lora import network_hada import network_ia3 import network_lokr import network_full +import network_norm import torch from typing import Union @@ -19,6 +22,7 @@ module_types = [ network_ia3.ModuleTypeIa3(), network_lokr.ModuleTypeLokr(), network_full.ModuleTypeFull(), + network_norm.ModuleTypeNorm(), ] @@ -31,6 +35,8 @@ suffix_conversion = { "resnets": { "conv1": "in_layers_2", "conv2": "out_layers_3", + "norm1": "in_layers_0", + "norm2": "out_layers_0", "time_emb_proj": "emb_layers_1", "conv_shortcut": "skip_connection", } @@ -190,11 +196,19 @@ def load_network(name, network_on_disk): net.modules[key] = net_module if keys_failed_to_match: - print(f"Failed to match keys when loading network {network_on_disk.filename}: {keys_failed_to_match}") + logging.debug(f"Network {network_on_disk.filename} didn't match keys: {keys_failed_to_match}") return net +def purge_networks_from_memory(): + while len(networks_in_memory) > shared.opts.lora_in_memory_limit and len(networks_in_memory) > 0: + name = next(iter(networks_in_memory)) + networks_in_memory.pop(name, None) + + devices.torch_gc() + + def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=None): already_loaded = {} @@ -212,15 +226,19 @@ def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=No failed_to_load_networks = [] - for i, name in enumerate(names): + for i, (network_on_disk, name) in enumerate(zip(networks_on_disk, names)): net = already_loaded.get(name, None) - network_on_disk = networks_on_disk[i] - if network_on_disk is not None: + if net is None: + net = networks_in_memory.get(name) + if net is None or os.path.getmtime(network_on_disk.filename) > net.mtime: try: net = load_network(name, network_on_disk) + + networks_in_memory.pop(name, None) + networks_in_memory[name] = net except Exception as e: errors.display(e, f"loading network {network_on_disk.filename}") continue @@ -231,7 +249,7 @@ def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=No if net is None: failed_to_load_networks.append(name) - print(f"Couldn't find network with name {name}") + logging.info(f"Couldn't find network with name {name}") continue net.te_multiplier = te_multipliers[i] if te_multipliers else 1.0 @@ -240,23 +258,38 @@ def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=No loaded_networks.append(net) if failed_to_load_networks: - sd_hijack.model_hijack.comments.append("Failed to find networks: " + ", ".join(failed_to_load_networks)) + sd_hijack.model_hijack.comments.append("Networks not found: " + ", ".join(failed_to_load_networks)) + + purge_networks_from_memory() -def network_restore_weights_from_backup(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.MultiheadAttention]): +def network_restore_weights_from_backup(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.GroupNorm, torch.nn.LayerNorm, torch.nn.MultiheadAttention]): weights_backup = getattr(self, "network_weights_backup", None) + bias_backup = getattr(self, "network_bias_backup", None) - if weights_backup is None: + if weights_backup is None and bias_backup is None: return - if isinstance(self, torch.nn.MultiheadAttention): - self.in_proj_weight.copy_(weights_backup[0]) - self.out_proj.weight.copy_(weights_backup[1]) + if weights_backup is not None: + if isinstance(self, torch.nn.MultiheadAttention): + self.in_proj_weight.copy_(weights_backup[0]) + self.out_proj.weight.copy_(weights_backup[1]) + else: + self.weight.copy_(weights_backup) + + if bias_backup is not None: + if isinstance(self, torch.nn.MultiheadAttention): + self.out_proj.bias.copy_(bias_backup) + else: + self.bias.copy_(bias_backup) else: - self.weight.copy_(weights_backup) + if isinstance(self, torch.nn.MultiheadAttention): + self.out_proj.bias = None + else: + self.bias = None -def network_apply_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.MultiheadAttention]): +def network_apply_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.GroupNorm, torch.nn.LayerNorm, torch.nn.MultiheadAttention]): """ Applies the currently selected set of networks to the weights of torch layer self. If weights already have this particular set of networks applied, does nothing. @@ -271,7 +304,10 @@ def network_apply_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn wanted_names = tuple((x.name, x.te_multiplier, x.unet_multiplier, x.dyn_dim) for x in loaded_networks) weights_backup = getattr(self, "network_weights_backup", None) - if weights_backup is None: + if weights_backup is None and wanted_names != (): + if current_names != (): + raise RuntimeError("no backup weights found and current weights are not unchanged") + if isinstance(self, torch.nn.MultiheadAttention): weights_backup = (self.in_proj_weight.to(devices.cpu, copy=True), self.out_proj.weight.to(devices.cpu, copy=True)) else: @@ -279,21 +315,41 @@ def network_apply_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn self.network_weights_backup = weights_backup + bias_backup = getattr(self, "network_bias_backup", None) + if bias_backup is None: + if isinstance(self, torch.nn.MultiheadAttention) and self.out_proj.bias is not None: + bias_backup = self.out_proj.bias.to(devices.cpu, copy=True) + elif getattr(self, 'bias', None) is not None: + bias_backup = self.bias.to(devices.cpu, copy=True) + else: + bias_backup = None + self.network_bias_backup = bias_backup + if current_names != wanted_names: network_restore_weights_from_backup(self) for net in loaded_networks: module = net.modules.get(network_layer_name, None) if module is not None and hasattr(self, 'weight'): - with torch.no_grad(): - updown = module.calc_updown(self.weight) + try: + with torch.no_grad(): + updown, ex_bias = module.calc_updown(self.weight) - if len(self.weight.shape) == 4 and self.weight.shape[1] == 9: - # inpainting model. zero pad updown to make channel[1] 4 to 9 - updown = torch.nn.functional.pad(updown, (0, 0, 0, 0, 0, 5)) + if len(self.weight.shape) == 4 and self.weight.shape[1] == 9: + # inpainting model. zero pad updown to make channel[1] 4 to 9 + updown = torch.nn.functional.pad(updown, (0, 0, 0, 0, 0, 5)) - self.weight += updown - continue + self.weight += updown + if ex_bias is not None and hasattr(self, 'bias'): + if self.bias is None: + self.bias = torch.nn.Parameter(ex_bias) + else: + self.bias += ex_bias + except RuntimeError as e: + logging.debug(f"Network {net.name} layer {network_layer_name}: {e}") + extra_network_lora.errors[net.name] = extra_network_lora.errors.get(net.name, 0) + 1 + + continue module_q = net.modules.get(network_layer_name + "_q_proj", None) module_k = net.modules.get(network_layer_name + "_k_proj", None) @@ -301,21 +357,33 @@ def network_apply_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn module_out = net.modules.get(network_layer_name + "_out_proj", None) if isinstance(self, torch.nn.MultiheadAttention) and module_q and module_k and module_v and module_out: - with torch.no_grad(): - updown_q = module_q.calc_updown(self.in_proj_weight) - updown_k = module_k.calc_updown(self.in_proj_weight) - updown_v = module_v.calc_updown(self.in_proj_weight) - updown_qkv = torch.vstack([updown_q, updown_k, updown_v]) - updown_out = module_out.calc_updown(self.out_proj.weight) + try: + with torch.no_grad(): + updown_q, _ = module_q.calc_updown(self.in_proj_weight) + updown_k, _ = module_k.calc_updown(self.in_proj_weight) + updown_v, _ = module_v.calc_updown(self.in_proj_weight) + updown_qkv = torch.vstack([updown_q, updown_k, updown_v]) + updown_out, ex_bias = module_out.calc_updown(self.out_proj.weight) - self.in_proj_weight += updown_qkv - self.out_proj.weight += updown_out - continue + self.in_proj_weight += updown_qkv + self.out_proj.weight += updown_out + if ex_bias is not None: + if self.out_proj.bias is None: + self.out_proj.bias = torch.nn.Parameter(ex_bias) + else: + self.out_proj.bias += ex_bias + + except RuntimeError as e: + logging.debug(f"Network {net.name} layer {network_layer_name}: {e}") + extra_network_lora.errors[net.name] = extra_network_lora.errors.get(net.name, 0) + 1 + + continue if module is None: continue - print(f'failed to calculate network weights for layer {network_layer_name}') + logging.debug(f"Network {net.name} layer {network_layer_name}: couldn't find supported operation") + extra_network_lora.errors[net.name] = extra_network_lora.errors.get(net.name, 0) + 1 self.network_current_names = wanted_names @@ -342,7 +410,7 @@ def network_forward(module, input, original_forward): if module is None: continue - y = module.forward(y, input) + y = module.forward(input, y) return y @@ -354,44 +422,74 @@ def network_reset_cached_weight(self: Union[torch.nn.Conv2d, torch.nn.Linear]): def network_Linear_forward(self, input): if shared.opts.lora_functional: - return network_forward(self, input, torch.nn.Linear_forward_before_network) + return network_forward(self, input, originals.Linear_forward) network_apply_weights(self) - return torch.nn.Linear_forward_before_network(self, input) + return originals.Linear_forward(self, input) def network_Linear_load_state_dict(self, *args, **kwargs): network_reset_cached_weight(self) - return torch.nn.Linear_load_state_dict_before_network(self, *args, **kwargs) + return originals.Linear_load_state_dict(self, *args, **kwargs) def network_Conv2d_forward(self, input): if shared.opts.lora_functional: - return network_forward(self, input, torch.nn.Conv2d_forward_before_network) + return network_forward(self, input, originals.Conv2d_forward) network_apply_weights(self) - return torch.nn.Conv2d_forward_before_network(self, input) + return originals.Conv2d_forward(self, input) def network_Conv2d_load_state_dict(self, *args, **kwargs): network_reset_cached_weight(self) - return torch.nn.Conv2d_load_state_dict_before_network(self, *args, **kwargs) + return originals.Conv2d_load_state_dict(self, *args, **kwargs) + + +def network_GroupNorm_forward(self, input): + if shared.opts.lora_functional: + return network_forward(self, input, originals.GroupNorm_forward) + + network_apply_weights(self) + + return originals.GroupNorm_forward(self, input) + + +def network_GroupNorm_load_state_dict(self, *args, **kwargs): + network_reset_cached_weight(self) + + return originals.GroupNorm_load_state_dict(self, *args, **kwargs) + + +def network_LayerNorm_forward(self, input): + if shared.opts.lora_functional: + return network_forward(self, input, originals.LayerNorm_forward) + + network_apply_weights(self) + + return originals.LayerNorm_forward(self, input) + + +def network_LayerNorm_load_state_dict(self, *args, **kwargs): + network_reset_cached_weight(self) + + return originals.LayerNorm_load_state_dict(self, *args, **kwargs) def network_MultiheadAttention_forward(self, *args, **kwargs): network_apply_weights(self) - return torch.nn.MultiheadAttention_forward_before_network(self, *args, **kwargs) + return originals.MultiheadAttention_forward(self, *args, **kwargs) def network_MultiheadAttention_load_state_dict(self, *args, **kwargs): network_reset_cached_weight(self) - return torch.nn.MultiheadAttention_load_state_dict_before_network(self, *args, **kwargs) + return originals.MultiheadAttention_load_state_dict(self, *args, **kwargs) def list_available_networks(): @@ -459,9 +557,14 @@ def infotext_pasted(infotext, params): params["Prompt"] += "\n" + "".join(added) +originals: lora_patches.LoraPatches = None + +extra_network_lora = None + available_networks = {} available_network_aliases = {} loaded_networks = [] +networks_in_memory = {} available_network_hash_lookup = {} forbidden_network_aliases = {} diff --git a/extensions-builtin/Lora/scripts/lora_script.py b/extensions-builtin/Lora/scripts/lora_script.py index cd28afc9..ef23968c 100644 --- a/extensions-builtin/Lora/scripts/lora_script.py +++ b/extensions-builtin/Lora/scripts/lora_script.py @@ -1,57 +1,30 @@ import re -import torch import gradio as gr from fastapi import FastAPI import network import networks import lora # noqa:F401 +import lora_patches import extra_networks_lora import ui_extra_networks_lora from modules import script_callbacks, ui_extra_networks, extra_networks, shared + def unload(): - torch.nn.Linear.forward = torch.nn.Linear_forward_before_network - torch.nn.Linear._load_from_state_dict = torch.nn.Linear_load_state_dict_before_network - torch.nn.Conv2d.forward = torch.nn.Conv2d_forward_before_network - torch.nn.Conv2d._load_from_state_dict = torch.nn.Conv2d_load_state_dict_before_network - torch.nn.MultiheadAttention.forward = torch.nn.MultiheadAttention_forward_before_network - torch.nn.MultiheadAttention._load_from_state_dict = torch.nn.MultiheadAttention_load_state_dict_before_network + networks.originals.undo() def before_ui(): ui_extra_networks.register_page(ui_extra_networks_lora.ExtraNetworksPageLora()) - extra_network = extra_networks_lora.ExtraNetworkLora() - extra_networks.register_extra_network(extra_network) - extra_networks.register_extra_network_alias(extra_network, "lyco") + networks.extra_network_lora = extra_networks_lora.ExtraNetworkLora() + extra_networks.register_extra_network(networks.extra_network_lora) + extra_networks.register_extra_network_alias(networks.extra_network_lora, "lyco") -if not hasattr(torch.nn, 'Linear_forward_before_network'): - torch.nn.Linear_forward_before_network = torch.nn.Linear.forward - -if not hasattr(torch.nn, 'Linear_load_state_dict_before_network'): - torch.nn.Linear_load_state_dict_before_network = torch.nn.Linear._load_from_state_dict - -if not hasattr(torch.nn, 'Conv2d_forward_before_network'): - torch.nn.Conv2d_forward_before_network = torch.nn.Conv2d.forward - -if not hasattr(torch.nn, 'Conv2d_load_state_dict_before_network'): - torch.nn.Conv2d_load_state_dict_before_network = torch.nn.Conv2d._load_from_state_dict - -if not hasattr(torch.nn, 'MultiheadAttention_forward_before_network'): - torch.nn.MultiheadAttention_forward_before_network = torch.nn.MultiheadAttention.forward - -if not hasattr(torch.nn, 'MultiheadAttention_load_state_dict_before_network'): - torch.nn.MultiheadAttention_load_state_dict_before_network = torch.nn.MultiheadAttention._load_from_state_dict - -torch.nn.Linear.forward = networks.network_Linear_forward -torch.nn.Linear._load_from_state_dict = networks.network_Linear_load_state_dict -torch.nn.Conv2d.forward = networks.network_Conv2d_forward -torch.nn.Conv2d._load_from_state_dict = networks.network_Conv2d_load_state_dict -torch.nn.MultiheadAttention.forward = networks.network_MultiheadAttention_forward -torch.nn.MultiheadAttention._load_from_state_dict = networks.network_MultiheadAttention_load_state_dict +networks.originals = lora_patches.LoraPatches() script_callbacks.on_model_loaded(networks.assign_network_names_to_compvis_modules) script_callbacks.on_script_unloaded(unload) @@ -65,6 +38,7 @@ shared.options_templates.update(shared.options_section(('extra_networks', "Extra "lora_add_hashes_to_infotext": shared.OptionInfo(True, "Add Lora hashes to infotext"), "lora_show_all": shared.OptionInfo(False, "Always show all networks on the Lora page").info("otherwise, those detected as for incompatible version of Stable Diffusion will be hidden"), "lora_hide_unknown_for_versions": shared.OptionInfo([], "Hide networks of unknown versions for model versions", gr.CheckboxGroup, {"choices": ["SD1", "SD2", "SDXL"]}), + "lora_in_memory_limit": shared.OptionInfo(0, "Number of Lora networks to keep cached in memory", gr.Number, {"precision": 0}), })) @@ -121,3 +95,5 @@ def infotext_pasted(infotext, d): script_callbacks.on_infotext_pasted(infotext_pasted) + +shared.opts.onchange("lora_in_memory_limit", networks.purge_networks_from_memory) diff --git a/extensions-builtin/Lora/ui_edit_user_metadata.py b/extensions-builtin/Lora/ui_edit_user_metadata.py index 2ca997f7..c7011909 100644 --- a/extensions-builtin/Lora/ui_edit_user_metadata.py +++ b/extensions-builtin/Lora/ui_edit_user_metadata.py @@ -70,6 +70,7 @@ class LoraUserMetadataEditor(ui_extra_networks_user_metadata.UserMetadataEditor) metadata = item.get("metadata") or {} keys = { + 'ss_output_name': "Output name:", 'ss_sd_model_name': "Model:", 'ss_clip_skip': "Clip skip:", 'ss_network_module': "Kohya module:", @@ -167,7 +168,7 @@ class LoraUserMetadataEditor(ui_extra_networks_user_metadata.UserMetadataEditor) random_prompt = gr.Textbox(label='Random prompt', lines=4, max_lines=4, interactive=False) with gr.Column(scale=1, min_width=120): - generate_random_prompt = gr.Button('Generate').style(full_width=True, size="lg") + generate_random_prompt = gr.Button('Generate', size="lg", scale=1) self.edit_notes = gr.TextArea(label='Notes', lines=4) diff --git a/extensions-builtin/Lora/ui_extra_networks_lora.py b/extensions-builtin/Lora/ui_extra_networks_lora.py index 1311b2f4..55409a78 100644 --- a/extensions-builtin/Lora/ui_extra_networks_lora.py +++ b/extensions-builtin/Lora/ui_extra_networks_lora.py @@ -25,9 +25,10 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): item = { "name": name, "filename": lora_on_disk.filename, - "preview": self.find_preview(path) if self.find_preview(path) else './file=html/card-no-preview.png', + "shorthash": lora_on_disk.shorthash, + "preview": self.find_preview(path), "description": self.find_description(path), - "search_term": self.search_terms_from_path(lora_on_disk.filename), + "search_term": self.search_terms_from_path(lora_on_disk.filename) + " " + (lora_on_disk.hash or ""), "local_preview": f"{path}.{shared.opts.samples_format}", "metadata": lora_on_disk.metadata, "sort_keys": {'default': index, **self.get_sort_keys(lora_on_disk.filename)}, @@ -67,7 +68,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): def list_items(self): for index, name in enumerate(networks.available_networks): item = self.create_item(name, index) - + if item is not None: yield item diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index 30199dcd..45c7600a 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -12,8 +12,22 @@ onUiLoaded(async() => { "Sketch": elementIDs.sketch }; + // Helper functions // Get active tab + + /** + * Waits for an element to be present in the DOM. + */ + const waitForElement = (id) => new Promise(resolve => { + const checkForElement = () => { + const element = document.querySelector(id); + if (element) return resolve(element); + setTimeout(checkForElement, 100); + }; + checkForElement(); + }); + function getActiveTab(elements, all = false) { const tabs = elements.img2imgTabs.querySelectorAll("button"); @@ -34,7 +48,7 @@ onUiLoaded(async() => { // Wait until opts loaded async function waitForOpts() { - for (;;) { + for (; ;) { if (window.opts && Object.keys(window.opts).length) { return window.opts; } @@ -42,6 +56,11 @@ onUiLoaded(async() => { } } + // Detect whether the element has a horizontal scroll bar + function hasHorizontalScrollbar(element) { + return element.scrollWidth > element.clientWidth; + } + // Function for defining the "Ctrl", "Shift" and "Alt" keys function isModifierKey(event, key) { switch (key) { @@ -201,7 +220,8 @@ onUiLoaded(async() => { canvas_hotkey_overlap: "KeyO", canvas_disabled_functions: [], canvas_show_tooltip: true, - canvas_blur_prompt: false + canvas_auto_expand: true, + canvas_blur_prompt: false, }; const functionMap = { @@ -249,7 +269,7 @@ onUiLoaded(async() => { input?.addEventListener("input", () => restoreImgRedMask(elements)); } - function applyZoomAndPan(elemId) { + function applyZoomAndPan(elemId, isExtension = true) { const targetElement = gradioApp().querySelector(elemId); if (!targetElement) { @@ -361,6 +381,12 @@ onUiLoaded(async() => { panY: 0 }; + if (isExtension) { + targetElement.style.overflow = "hidden"; + } + + targetElement.isZoomed = false; + fixCanvas(); targetElement.style.transform = `scale(${elemData[elemId].zoomLevel}) translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px)`; @@ -371,8 +397,27 @@ onUiLoaded(async() => { toggleOverlap("off"); fullScreenMode = false; + const closeBtn = targetElement.querySelector("button[aria-label='Remove Image']"); + if (closeBtn) { + closeBtn.addEventListener("click", resetZoom); + } + + if (canvas && isExtension) { + const parentElement = targetElement.closest('[id^="component-"]'); + if ( + canvas && + parseFloat(canvas.style.width) > parentElement.offsetWidth && + parseFloat(targetElement.style.width) > parentElement.offsetWidth + ) { + fitToElement(); + return; + } + + } + if ( canvas && + !isExtension && parseFloat(canvas.style.width) > 865 && parseFloat(targetElement.style.width) > 865 ) { @@ -381,9 +426,6 @@ onUiLoaded(async() => { } targetElement.style.width = ""; - if (canvas) { - targetElement.style.height = canvas.style.height; - } } // Toggle the zIndex of the target element between two values, allowing it to overlap or be overlapped by other elements @@ -439,7 +481,7 @@ onUiLoaded(async() => { // Update the zoom level and pan position of the target element based on the values of the zoomLevel, panX and panY variables function updateZoom(newZoomLevel, mouseX, mouseY) { - newZoomLevel = Math.max(0.5, Math.min(newZoomLevel, 15)); + newZoomLevel = Math.max(0.1, Math.min(newZoomLevel, 15)); elemData[elemId].panX += mouseX - (mouseX * newZoomLevel) / elemData[elemId].zoomLevel; @@ -450,6 +492,10 @@ onUiLoaded(async() => { targetElement.style.transform = `translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px) scale(${newZoomLevel})`; toggleOverlap("on"); + if (isExtension) { + targetElement.style.overflow = "visible"; + } + return newZoomLevel; } @@ -472,10 +518,12 @@ onUiLoaded(async() => { fullScreenMode = false; elemData[elemId].zoomLevel = updateZoom( elemData[elemId].zoomLevel + - (operation === "+" ? delta : -delta), + (operation === "+" ? delta : -delta), zoomPosX - targetElement.getBoundingClientRect().left, zoomPosY - targetElement.getBoundingClientRect().top ); + + targetElement.isZoomed = true; } } @@ -489,10 +537,19 @@ onUiLoaded(async() => { //Reset Zoom targetElement.style.transform = `translate(${0}px, ${0}px) scale(${1})`; + let parentElement; + + if (isExtension) { + parentElement = targetElement.closest('[id^="component-"]'); + } else { + parentElement = targetElement.parentElement; + } + + // Get element and screen dimensions const elementWidth = targetElement.offsetWidth; const elementHeight = targetElement.offsetHeight; - const parentElement = targetElement.parentElement; + const screenWidth = parentElement.clientWidth; const screenHeight = parentElement.clientHeight; @@ -545,8 +602,12 @@ onUiLoaded(async() => { if (!canvas) return; - if (canvas.offsetWidth > 862) { - targetElement.style.width = canvas.offsetWidth + "px"; + if (canvas.offsetWidth > 862 || isExtension) { + targetElement.style.width = (canvas.offsetWidth + 2) + "px"; + } + + if (isExtension) { + targetElement.style.overflow = "visible"; } if (fullScreenMode) { @@ -648,8 +709,48 @@ onUiLoaded(async() => { mouseY = e.offsetY; } + // Simulation of the function to put a long image into the screen. + // We detect if an image has a scroll bar or not, make a fullscreen to reveal the image, then reduce it to fit into the element. + // We hide the image and show it to the user when it is ready. + + targetElement.isExpanded = false; + function autoExpand() { + const canvas = document.querySelector(`${elemId} canvas[key="interface"]`); + if (canvas) { + if (hasHorizontalScrollbar(targetElement) && targetElement.isExpanded === false) { + targetElement.style.visibility = "hidden"; + setTimeout(() => { + fitToScreen(); + resetZoom(); + targetElement.style.visibility = "visible"; + targetElement.isExpanded = true; + }, 10); + } + } + } + targetElement.addEventListener("mousemove", getMousePosition); + //observers + // Creating an observer with a callback function to handle DOM changes + const observer = new MutationObserver((mutationsList, observer) => { + for (let mutation of mutationsList) { + // If the style attribute of the canvas has changed, by observation it happens only when the picture changes + if (mutation.type === 'attributes' && mutation.attributeName === 'style' && + mutation.target.tagName.toLowerCase() === 'canvas') { + targetElement.isExpanded = false; + setTimeout(resetZoom, 10); + } + } + }); + + // Apply auto expand if enabled + if (hotkeysConfig.canvas_auto_expand) { + targetElement.addEventListener("mousemove", autoExpand); + // Set up an observer to track attribute changes + observer.observe(targetElement, {attributes: true, childList: true, subtree: true}); + } + // Handle events only inside the targetElement let isKeyDownHandlerAttached = false; @@ -754,6 +855,11 @@ onUiLoaded(async() => { if (isMoving && elemId === activeElement) { updatePanPosition(e.movementX, e.movementY); targetElement.style.pointerEvents = "none"; + + if (isExtension) { + targetElement.style.overflow = "visible"; + } + } else { targetElement.style.pointerEvents = "auto"; } @@ -764,13 +870,93 @@ onUiLoaded(async() => { isMoving = false; }; + // Checks for extension + function checkForOutBox() { + const parentElement = targetElement.closest('[id^="component-"]'); + if (parentElement.offsetWidth < targetElement.offsetWidth && !targetElement.isExpanded) { + resetZoom(); + targetElement.isExpanded = true; + } + + if (parentElement.offsetWidth < targetElement.offsetWidth && elemData[elemId].zoomLevel == 1) { + resetZoom(); + } + + if (parentElement.offsetWidth < targetElement.offsetWidth && targetElement.offsetWidth * elemData[elemId].zoomLevel > parentElement.offsetWidth && elemData[elemId].zoomLevel < 1 && !targetElement.isZoomed) { + resetZoom(); + } + } + + if (isExtension) { + targetElement.addEventListener("mousemove", checkForOutBox); + } + + + window.addEventListener('resize', (e) => { + resetZoom(); + + if (isExtension) { + targetElement.isExpanded = false; + targetElement.isZoomed = false; + } + }); + gradioApp().addEventListener("mousemove", handleMoveByKey); + + } - applyZoomAndPan(elementIDs.sketch); - applyZoomAndPan(elementIDs.inpaint); - applyZoomAndPan(elementIDs.inpaintSketch); + applyZoomAndPan(elementIDs.sketch, false); + applyZoomAndPan(elementIDs.inpaint, false); + applyZoomAndPan(elementIDs.inpaintSketch, false); // Make the function global so that other extensions can take advantage of this solution - window.applyZoomAndPan = applyZoomAndPan; + const applyZoomAndPanIntegration = async(id, elementIDs) => { + const mainEl = document.querySelector(id); + if (id.toLocaleLowerCase() === "none") { + for (const elementID of elementIDs) { + const el = await waitForElement(elementID); + if (!el) break; + applyZoomAndPan(elementID); + } + return; + } + + if (!mainEl) return; + mainEl.addEventListener("click", async() => { + for (const elementID of elementIDs) { + const el = await waitForElement(elementID); + if (!el) break; + applyZoomAndPan(elementID); + } + }, {once: true}); + }; + + window.applyZoomAndPan = applyZoomAndPan; // Only 1 elements, argument elementID, for example applyZoomAndPan("#txt2img_controlnet_ControlNet_input_image") + + window.applyZoomAndPanIntegration = applyZoomAndPanIntegration; // for any extension + + /* + The function `applyZoomAndPanIntegration` takes two arguments: + + 1. `id`: A string identifier for the element to which zoom and pan functionality will be applied on click. + If the `id` value is "none", the functionality will be applied to all elements specified in the second argument without a click event. + + 2. `elementIDs`: An array of string identifiers for elements. Zoom and pan functionality will be applied to each of these elements on click of the element specified by the first argument. + If "none" is specified in the first argument, the functionality will be applied to each of these elements without a click event. + + Example usage: + applyZoomAndPanIntegration("#txt2img_controlnet", ["#txt2img_controlnet_ControlNet_input_image"]); + In this example, zoom and pan functionality will be applied to the element with the identifier "txt2img_controlnet_ControlNet_input_image" upon clicking the element with the identifier "txt2img_controlnet". + */ + + // More examples + // Add integration with ControlNet txt2img One TAB + // applyZoomAndPanIntegration("#txt2img_controlnet", ["#txt2img_controlnet_ControlNet_input_image"]); + + // Add integration with ControlNet txt2img Tabs + // applyZoomAndPanIntegration("#txt2img_controlnet",Array.from({ length: 10 }, (_, i) => `#txt2img_controlnet_ControlNet-${i}_input_image`)); + + // Add integration with Inpaint Anything + // applyZoomAndPanIntegration("None", ["#ia_sam_image", "#ia_sel_mask"]); }); diff --git a/extensions-builtin/canvas-zoom-and-pan/scripts/hotkey_config.py b/extensions-builtin/canvas-zoom-and-pan/scripts/hotkey_config.py index 380176ce..2d8d2d1c 100644 --- a/extensions-builtin/canvas-zoom-and-pan/scripts/hotkey_config.py +++ b/extensions-builtin/canvas-zoom-and-pan/scripts/hotkey_config.py @@ -9,6 +9,7 @@ shared.options_templates.update(shared.options_section(('canvas_hotkey', "Canvas "canvas_hotkey_reset": shared.OptionInfo("R", "Reset zoom and canvas positon"), "canvas_hotkey_overlap": shared.OptionInfo("O", "Toggle overlap").info("Technical button, neededs for testing"), "canvas_show_tooltip": shared.OptionInfo(True, "Enable tooltip on the canvas"), + "canvas_auto_expand": shared.OptionInfo(True, "Automatically expands an image that does not fit completely in the canvas area, similar to manually pressing the S and R buttons"), "canvas_blur_prompt": shared.OptionInfo(False, "Take the focus off the prompt when working with a canvas"), "canvas_disabled_functions": shared.OptionInfo(["Overlap"], "Disable function that you don't use", gr.CheckboxGroup, {"choices": ["Zoom","Adjust brush size", "Moving canvas","Fullscreen","Reset Zoom","Overlap"]}), })) diff --git a/extensions-builtin/canvas-zoom-and-pan/style.css b/extensions-builtin/canvas-zoom-and-pan/style.css index 6bcc9570..5d8054e6 100644 --- a/extensions-builtin/canvas-zoom-and-pan/style.css +++ b/extensions-builtin/canvas-zoom-and-pan/style.css @@ -61,3 +61,6 @@ to {opacity: 1;} } +.styler { + overflow:inherit !important; +} \ No newline at end of file diff --git a/extensions-builtin/extra-options-section/scripts/extra_options_section.py b/extensions-builtin/extra-options-section/scripts/extra_options_section.py index a05e10d8..983f87ff 100644 --- a/extensions-builtin/extra-options-section/scripts/extra_options_section.py +++ b/extensions-builtin/extra-options-section/scripts/extra_options_section.py @@ -1,5 +1,7 @@ +import math + import gradio as gr -from modules import scripts, shared, ui_components, ui_settings +from modules import scripts, shared, ui_components, ui_settings, generation_parameters_copypaste from modules.ui_components import FormColumn @@ -19,18 +21,38 @@ class ExtraOptionsSection(scripts.Script): def ui(self, is_img2img): self.comps = [] self.setting_names = [] + self.infotext_fields = [] + extra_options = shared.opts.extra_options_img2img if is_img2img else shared.opts.extra_options_txt2img + + mapping = {k: v for v, k in generation_parameters_copypaste.infotext_to_setting_name_mapping} with gr.Blocks() as interface: - with gr.Accordion("Options", open=False) if shared.opts.extra_options_accordion and shared.opts.extra_options else gr.Group(), gr.Row(): - for setting_name in shared.opts.extra_options: - with FormColumn(): - comp = ui_settings.create_setting_component(setting_name) + with gr.Accordion("Options", open=False) if shared.opts.extra_options_accordion and extra_options else gr.Group(): - self.comps.append(comp) - self.setting_names.append(setting_name) + row_count = math.ceil(len(extra_options) / shared.opts.extra_options_cols) + + for row in range(row_count): + with gr.Row(): + for col in range(shared.opts.extra_options_cols): + index = row * shared.opts.extra_options_cols + col + if index >= len(extra_options): + break + + setting_name = extra_options[index] + + with FormColumn(): + comp = ui_settings.create_setting_component(setting_name) + + self.comps.append(comp) + self.setting_names.append(setting_name) + + setting_infotext_name = mapping.get(setting_name) + if setting_infotext_name is not None: + self.infotext_fields.append((comp, setting_infotext_name)) def get_settings_values(): - return [ui_settings.get_value_for_setting(key) for key in self.setting_names] + res = [ui_settings.get_value_for_setting(key) for key in self.setting_names] + return res[0] if len(res) == 1 else res interface.load(fn=get_settings_values, inputs=[], outputs=self.comps, queue=False, show_progress=False) @@ -43,6 +65,10 @@ class ExtraOptionsSection(scripts.Script): shared.options_templates.update(shared.options_section(('ui', "User interface"), { - "extra_options": shared.OptionInfo([], "Options in main UI", ui_components.DropdownMulti, lambda: {"choices": list(shared.opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that also appear in txt2img/img2img interfaces").needs_restart(), - "extra_options_accordion": shared.OptionInfo(False, "Place options in main UI into an accordion") + "extra_options_txt2img": shared.OptionInfo([], "Options in main UI - txt2img", ui_components.DropdownMulti, lambda: {"choices": list(shared.opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that also appear in txt2img interfaces").needs_reload_ui(), + "extra_options_img2img": shared.OptionInfo([], "Options in main UI - img2img", ui_components.DropdownMulti, lambda: {"choices": list(shared.opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that also appear in img2img interfaces").needs_reload_ui(), + "extra_options_cols": shared.OptionInfo(1, "Options in main UI - number of columns", gr.Number, {"precision": 0}).needs_reload_ui(), + "extra_options_accordion": shared.OptionInfo(False, "Options in main UI - place into an accordion").needs_reload_ui() })) + + diff --git a/extensions-builtin/mobile/javascript/mobile.js b/extensions-builtin/mobile/javascript/mobile.js index 12cae4b7..652f07ac 100644 --- a/extensions-builtin/mobile/javascript/mobile.js +++ b/extensions-builtin/mobile/javascript/mobile.js @@ -20,7 +20,13 @@ function reportWindowSize() { var button = gradioApp().getElementById(tab + '_generate_box'); var target = gradioApp().getElementById(currentlyMobile ? tab + '_results' : tab + '_actions_column'); target.insertBefore(button, target.firstElementChild); + + gradioApp().getElementById(tab + '_results').classList.toggle('mobile', currentlyMobile); } } window.addEventListener("resize", reportWindowSize); + +onUiLoaded(function() { + reportWindowSize(); +}); diff --git a/extensions-builtin/sd_theme_editor/install.py b/extensions-builtin/sd_theme_editor/install.py deleted file mode 100644 index 5416cb0a..00000000 --- a/extensions-builtin/sd_theme_editor/install.py +++ /dev/null @@ -1 +0,0 @@ -import launch diff --git a/extensions-builtin/sd_theme_editor/javascript/ui_theme.js b/extensions-builtin/sd_theme_editor/javascript/ui_theme.js deleted file mode 100644 index 1f065568..00000000 --- a/extensions-builtin/sd_theme_editor/javascript/ui_theme.js +++ /dev/null @@ -1,435 +0,0 @@ -function hexToRgb(color) { - let hex = color[0] === "#" ? color.slice(1) : color; - let c; - - // expand the short hex by doubling each character, fc0 -> ffcc00 - if (hex.length !== 6) { - hex = (() => { - const result = []; - for (c of Array.from(hex)) { - result.push(`${c}${c}`); - } - return result; - })().join(""); - } - const colorStr = hex.match(/#?(.{2})(.{2})(.{2})/).slice(1); - const rgb = colorStr.map((col) => parseInt(col, 16)); - rgb.push(1); - return rgb; -} - -function rgbToHsl(rgb) { - const r = rgb[0] / 255; - const g = rgb[1] / 255; - const b = rgb[2] / 255; - - const max = Math.max(r, g, b); - const min = Math.min(r, g, b); - const diff = max - min; - const add = max + min; - - const hue = - min === max - ? 0 - : r === max - ? ((60 * (g - b)) / diff + 360) % 360 - : g === max - ? (60 * (b - r)) / diff + 120 - : (60 * (r - g)) / diff + 240; - - const lum = 0.5 * add; - - const sat = - lum === 0 ? 0 : lum === 1 ? 1 : lum <= 0.5 ? diff / add : diff / (2 - add); - - const h = Math.round(hue); - const s = Math.round(sat * 100); - const l = Math.round(lum * 100); - const a = rgb[3] || 1; - - return [h, s, l, a]; -} - -function hexToHsl(color) { - const rgb = hexToRgb(color); - const hsl = rgbToHsl(rgb); - return "hsl(" + hsl[0] + "deg " + hsl[1] + "% " + hsl[2] + "%)"; -} - -function hslToHex(h, s, l) { - l /= 100; - const a = (s * Math.min(l, 1 - l)) / 100; - const f = (n) => { - const k = (n + h / 30) % 12; - const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); - return Math.round(255 * Math.max(0, Math.min(color, 1))) - .toString(16) - .padStart(2, "0"); // convert to Hex and prefix "0" if needed - }; - return `#${f(0)}${f(8)}${f(4)}`; -} - -function hsl2rgb(h, s, l) { - let a = s * Math.min(l, 1 - l); - let f = (n, k = (n + h / 30) % 12) => - l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); - return [f(0), f(8), f(4)]; -} - -function invertColor(hex) { - if (hex.indexOf("#") === 0) { - hex = hex.slice(1); - } - // convert 3-digit hex to 6-digits. - if (hex.length === 3) { - hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; - } - if (hex.length !== 6) { - throw new Error("Invalid HEX color."); - } - // invert color components - var r = (255 - parseInt(hex.slice(0, 2), 16)).toString(16), - g = (255 - parseInt(hex.slice(2, 4), 16)).toString(16), - b = (255 - parseInt(hex.slice(4, 6), 16)).toString(16); - // pad each with zeros and return - return "#" + padZero(r) + padZero(g) + padZero(b); -} - -function padZero(str, len) { - len = len || 2; - var zeros = new Array(len).join("0"); - return (zeros + str).slice(-len); -} - -function getValsWrappedIn(str, c1, c2) { - var rg = new RegExp("(?<=\\" + c1 + ")(.*?)(?=\\" + c2 + ")", "g"); - return str.match(rg); -} - -let styleobj = {}; -let hslobj = {}; -let isColorsInv; - -const toHSLArray = (hslStr) => hslStr.match(/\d+/g).map(Number); - -function offsetColorsHSV(ohsl) { - let inner_styles = ""; - - for (const key in styleobj) { - let keyVal = styleobj[key]; - - if (keyVal.indexOf("#") != -1 || keyVal.indexOf("hsl") != -1) { - let colcomp = gradioApp().querySelector("#" + key + " input"); - if (colcomp) { - let hsl; - - if (keyVal.indexOf("#") != -1) { - keyVal = keyVal.replace(/\s+/g, ""); - //inv ? keyVal = invertColor(keyVal) : 0; - if (isColorsInv) { - keyVal = invertColor(keyVal); - styleobj[key] = keyVal; - } - hsl = rgbToHsl(hexToRgb(keyVal)); - } else { - if (isColorsInv) { - let c = toHSLArray(keyVal); - let hex = hslToHex(c[0], c[1], c[2]); - keyVal = invertColor(hex); - styleobj[key] = keyVal; - hsl = rgbToHsl(hexToRgb(keyVal)); - } else { - hsl = toHSLArray(keyVal); - } - } - - let h = (parseInt(hsl[0]) + parseInt(ohsl[0])) % 360; - let s = parseInt(hsl[1]) + parseInt(ohsl[1]); - let l = parseInt(hsl[2]) + parseInt(ohsl[2]); - - let hex = hslToHex( - h, - Math.min(Math.max(s, 0), 100), - Math.min(Math.max(l, 0), 100) - ); - - colcomp.value = hex; - - hslobj[key] = "hsl(" + h + "deg " + s + "% " + l + "%)"; - inner_styles += key + ":" + hslobj[key] + ";"; - } - } else { - inner_styles += key + ":" + styleobj[key] + ";"; - } - } - - isColorsInv = false; - - const preview_styles = gradioApp().querySelector("#preview-styles"); - preview_styles.innerHTML = ":root {" + inner_styles + "}"; - preview_styles.innerHTML += - "@media only screen and (max-width: 860px) {:root{--ae-outside-gap-size: var(--ae-mobile-outside-gap-size);--ae-inside-padding-size: var(--ae-mobile-inside-padding-size);}}"; - - const vars_textarea = gradioApp().querySelector("#theme_vars textarea"); - vars_textarea.value = inner_styles; - - const inputEvent = new Event("input"); - Object.defineProperty(inputEvent, "target", { value: vars_textarea }); - vars_textarea.dispatchEvent(inputEvent); -} - -function updateTheme(vars) { - let inner_styles = ""; - - for (let i = 0; i < vars.length - 1; i++) { - let key = vars[i].split(":"); - let id = key[0].replace(/\s+/g, ""); - let val = key[1].trim(); - - styleobj[id] = val; - inner_styles += id + ":" + val + ";"; - - gradioApp() - .querySelectorAll("#" + id + " input") - .forEach((elem) => { - if (val.indexOf("hsl") != -1) { - let hsl = toHSLArray(val); - let hex = hslToHex(hsl[0], hsl[1], hsl[2]); - elem.value = hex; - } else { - elem.value = val.split("px")[0]; - } - }); - } - - const preview_styles = gradioApp().querySelector("#preview-styles"); - - if (preview_styles) { - preview_styles.innerHTML = ":root {" + inner_styles + "}"; - preview_styles.innerHTML += - "@media only screen and (max-width: 860px) {:root{--ae-outside-gap-size: var(--ae-mobile-outside-gap-size);--ae-inside-padding-size: var(--ae-mobile-inside-padding-size);}}"; - } else { - const r = gradioApp(); - const style = document.createElement("style"); - style.id = "preview-styles"; - style.innerHTML = ":root {" + inner_styles + "}"; - style.innerHTML += - "@media only screen and (max-width: 860px) {:root{--ae-outside-gap-size: var(--ae-mobile-outside-gap-size);--ae-inside-padding-size: var(--ae-mobile-inside-padding-size);}}"; - r.appendChild(style); - } - - const vars_textarea = gradioApp().querySelector("#theme_vars textarea"); - const css_textarea = gradioApp().querySelector("#theme_css textarea"); - - vars_textarea.value = inner_styles; - css_textarea.value = css_textarea.value; - - //console.log(Object); - - const vEvent = new Event("input"); - const cEvent = new Event("input"); - Object.defineProperty(vEvent, "target", { value: vars_textarea }); - Object.defineProperty(cEvent, "target", { value: css_textarea }); - vars_textarea.dispatchEvent(vEvent); - css_textarea.dispatchEvent(cEvent); -} - -function applyTheme() { - console.log("apply"); -} - -function initTheme() { - const current_style = gradioApp().querySelector(".gradio-container > style"); - //console.log(current_style); - //const head = document.head; - //head.appendChild(current_style); - - const css_styles = current_style.innerHTML.split( - "/*BREAKPOINT_CSS_CONTENT*/" - ); - let init_css_vars = css_styles[0].split("}")[0].split("{")[1]; - init_css_vars = init_css_vars.replace(/\n|\r/g, ""); - - let init_vars = init_css_vars.split(";"); - let vars = init_vars; - - //console.log(vars); - - const vars_textarea = gradioApp().querySelector("#theme_vars textarea"); - const css_textarea = gradioApp().querySelector("#theme_css textarea"); - //const result_textarea = gradioApp().querySelector('#theme_result textarea'); - vars_textarea.value = init_css_vars; - css_textarea.value = - "/*BREAKPOINT_CSS_CONTENT*/" + css_styles[1] + "/*BREAKPOINT_CSS_CONTENT*/"; - - updateTheme(vars); - - //vars_textarea.addEventListener("change", function(e) { - //e.preventDefault(); - //e.stopPropagation(); - //vars = vars_textarea.value.split(";"); - //console.log(e); - //updateTheme(vars); - - //}) - - const preview_styles = gradioApp().querySelector("#preview-styles"); - let intervalChange; - - gradioApp() - .querySelectorAll("#ui_theme_settings input") - .forEach((elem) => { - elem.addEventListener("input", function (e) { - let celem = e.currentTarget; - let val = e.currentTarget.value; - let curr_val; - - switch (e.currentTarget.type) { - case "range": - celem = celem.parentElement; - val = e.currentTarget.value + "px"; - break; - case "color": - celem = celem.parentElement.parentElement; - val = e.currentTarget.value; - break; - case "number": - celem = celem.parentElement.parentElement.parentElement; - val = e.currentTarget.value + "px"; - break; - } - - styleobj[celem.id] = val; - - //console.log(styleobj); - - if (intervalChange != null) clearInterval(intervalChange); - intervalChange = setTimeout(() => { - let inner_styles = ""; - - for (const key in styleobj) { - inner_styles += key + ":" + styleobj[key] + ";"; - } - - vars = inner_styles.split(";"); - preview_styles.innerHTML = ":root {" + inner_styles + "}"; - preview_styles.innerHTML += - "@media only screen and (max-width: 860px) {:root{--ae-outside-gap-size: var(--ae-mobile-outside-gap-size);--ae-inside-padding-size: var(--ae-mobile-inside-padding-size);}}"; - - vars_textarea.value = inner_styles; - const vEvent = new Event("input"); - Object.defineProperty(vEvent, "target", { value: vars_textarea }); - vars_textarea.dispatchEvent(vEvent); - - offsetColorsHSV(hsloffset); - }, 1000); - }); - }); - - const reset_btn = gradioApp().getElementById("theme_reset_btn"); - reset_btn.addEventListener("click", function (e) { - e.preventDefault(); - e.stopPropagation(); - gradioApp() - .querySelectorAll("#ui_theme_hsv input") - .forEach((elem) => { - elem.value = 0; - }); - hsloffset = [0, 0, 0]; - updateTheme(init_vars); - }); - - /* - const apply_btn = gradioApp().getElementById('theme_apply_btn'); - apply_btn.addEventListener("click", function(e) { - e.preventDefault(); - e.stopPropagation(); - init_css_vars = vars_textarea.value.replace(/\n|\r/g, ""); - vars_textarea.value = init_css_vars; - - init_vars = init_css_vars.split(";"); - vars = init_vars; - updateTheme(vars); - }) - */ - - let intervalCheck; - function dropDownOnChange() { - if (init_css_vars != vars_textarea.value) { - clearInterval(intervalCheck); - init_css_vars = vars_textarea.value.replace(/\n|\r/g, ""); - vars_textarea.value = init_css_vars; - init_vars = init_css_vars.split(";"); - vars = init_vars; - updateTheme(vars); - } - } - - const drop_down = gradioApp().querySelector("#themes_drop_down"); - drop_down.addEventListener("click", function (e) { - if (intervalCheck != null) clearInterval(intervalCheck); - intervalCheck = setInterval(dropDownOnChange, 100); - //console.log("ok"); - }); - - let hsloffset = [0, 0, 0]; - - const hue = gradioApp() - .querySelectorAll("#theme_hue input") - .forEach((elem) => { - elem.addEventListener("change", function (e) { - e.preventDefault(); - e.stopPropagation(); - hsloffset[0] = e.currentTarget.value; - offsetColorsHSV(hsloffset); - }); - }); - - const sat = gradioApp() - .querySelectorAll("#theme_sat input") - .forEach((elem) => { - elem.addEventListener("change", function (e) { - e.preventDefault(); - e.stopPropagation(); - hsloffset[1] = e.currentTarget.value; - offsetColorsHSV(hsloffset); - }); - }); - - const brt = gradioApp() - .querySelectorAll("#theme_brt input") - .forEach((elem) => { - elem.addEventListener("change", function (e) { - e.preventDefault(); - e.stopPropagation(); - hsloffset[2] = e.currentTarget.value; - offsetColorsHSV(hsloffset); - }); - }); - - const inv_btn = gradioApp().getElementById("theme_invert_btn"); - inv_btn.addEventListener("click", function (e) { - e.preventDefault(); - e.stopPropagation(); - isColorsInv = !isColorsInv; - offsetColorsHSV(hsloffset); - }); -} - -function observeGradioApp() { - const observer = new MutationObserver(() => { - const block = gradioApp().getElementById("tab_ui_theme"); - if (block) { - observer.disconnect(); - - setTimeout(() => { - initTheme(); - }, "500"); - } - }); - observer.observe(gradioApp(), { childList: true, subtree: true }); -} - -document.addEventListener("DOMContentLoaded", () => { - observeGradioApp(); -}); diff --git a/extensions-builtin/sd_theme_editor/scripts/ui_theme.py b/extensions-builtin/sd_theme_editor/scripts/ui_theme.py deleted file mode 100644 index 3a2c8a2b..00000000 --- a/extensions-builtin/sd_theme_editor/scripts/ui_theme.py +++ /dev/null @@ -1,177 +0,0 @@ -import os -import shutil -from pathlib import Path -import gradio as gr -import modules.scripts as scripts -from modules import script_callbacks, shared - -basedir = scripts.basedir() -webui_dir = Path(basedir).parents[1] - -themes_folder = os.path.join(basedir, "themes") -javascript_folder = os.path.join(basedir, "javascript") -webui_style_path = os.path.join(webui_dir, "style.css") - -def get_files(folder, file_filter=[], file_list=[], split=False): - file_list = [file_name if not split else os.path.splitext(file_name)[0] for file_name in os.listdir(folder) if os.path.isfile(os.path.join(folder, file_name)) and file_name not in file_filter] - return file_list - - -def on_ui_tabs(): - - with gr.Blocks(analytics_enabled=False) as ui_theme: - with gr.Row(): - with gr.Column(): - with gr.Row(): - themes_dropdown = gr.Dropdown(label="Themes", elem_id="themes_drop_down", interactive=True, choices=get_files(themes_folder,[".css, .txt"]), type="value") - save_as_filename = gr.Text(label="Save / Save as") - with gr.Row(): - reset_button = gr.Button(elem_id="theme_reset_btn", value="Reset", variant="primary") - #apply_button = gr.Button(elem_id="theme_apply_btn", value="Apply", variant="primary") - save_button = gr.Button(value="Save", variant="primary") - #delete_button = gr.Button(value="Delete", variant="primary") - - #with gr.Accordion(label="Debug View", open=True): - with gr.Row(elem_id="theme_hidden"): - vars_text = gr.Textbox(label="Vars", elem_id="theme_vars", show_label=True, lines=7, interactive=False, visible=True) - css_text = gr.Textbox(label="Css", elem_id="theme_css", show_label=True, lines=7, interactive=False, visible=True) - #result_text = gr.Text(elem_id="theme_result", interactive=False, visible=False) - with gr.Column(elem_id="theme_overflow_container"): - with gr.Accordion(label="Theme Color adjustments", open=True): - with gr.Row(): - with gr.Column(scale=6, elem_id="ui_theme_hsv"): - gr.Slider(elem_id="theme_hue", label='Hue', minimum=0, maximum=360, step=1) - gr.Slider(elem_id="theme_sat", label='Saturation', minimum=-100, maximum=100, step=1, value=0, interactive=True) - gr.Slider(elem_id="theme_brt", label='Lightness', minimum=-50, maximum=50, step=1, value=0, interactive=True) - - gr.Button(elem_id="theme_invert_btn", value="Invert", variant="primary") - - - with gr.Row(elem_id="ui_theme_settings"): - with gr.Column(): - with gr.Column(): - with gr.Accordion(label="Main", open=True): - gr.ColorPicker(elem_id="--ae-main-bg-color", interactive=True, label="Background color") - gr.ColorPicker(elem_id="--ae-primary-color", label="Primary color") - - with gr.Accordion(label="Focus", open=True): - gr.ColorPicker(elem_id="--ae-textarea-focus-color", label="Textarea color") - gr.ColorPicker(elem_id="--ae-input-focus-color", label="Input color") - - with gr.Accordion(label="Spacing", open=True): - gr.Slider(elem_id="--ae-outside-gap-size", label='Gap size', minimum=1, maximum=16, step=1, interactive=True) - gr.Slider(elem_id="--ae-inside-padding-size", label='Padding size', minimum=1, maximum=16, step=1, interactive=True) - - with gr.Accordion(label="Spacing (Mobile)", open=True): - gr.Slider(elem_id="--ae-mobile-outside-gap-size", label='Mobile Gap size', minimum=1, maximum=16, step=1, interactive=True) - gr.Slider(elem_id="--ae-mobile-inside-padding-size", label='Mobile Padding size', minimum=1, maximum=16, step=1, interactive=True) - - with gr.Accordion(label="Panel", open=True): - gr.ColorPicker(elem_id="--ae-label-color", label="Label color") - gr.ColorPicker(elem_id="--ae-frame-bg-color", label="Frame Background color") - gr.ColorPicker(elem_id="--ae-panel-bg-color", label="Background color") - gr.ColorPicker(elem_id="--ae-panel-border-color", label="Border color") - gr.Slider(elem_id="--ae-panel-border-radius", label='Border radius', minimum=0, maximum=16, step=1) - - gr.ColorPicker(elem_id="--ae-input-color", label="Input text color") - gr.ColorPicker(elem_id="--ae-input-bg-color", label="Input background color") - gr.ColorPicker(elem_id="--ae-input-border-color", label="Input border color") - with gr.Column(): - with gr.Row(elem_id="theme_sub-panel"): - - with gr.Accordion(label="SubPanel", open=True): - gr.ColorPicker(elem_id="--ae-subgroup-bg-color", label="Subgoup background color") - #gr.ColorPicker(elem_id="--ae-subgroup-label-color", label="Label color", value="#000000") - gr.ColorPicker(elem_id="--ae-subpanel-bg-color", label="Background color") - gr.ColorPicker(elem_id="--ae-subpanel-border-color", label="Border color") - gr.Slider(elem_id="--ae-subpanel-border-radius", label='Border radius', minimum=0, maximum=16, step=1) - - gr.ColorPicker(elem_id="--ae-subgroup-input-color", label="Input text color") - gr.ColorPicker(elem_id="--ae-subgroup-input-bg-color", label="Input background color") - gr.ColorPicker(elem_id="--ae-subgroup-input-border-color", label="Input border color") - - with gr.Row(): - with gr.Column(): - with gr.Accordion(label="Navigation menu", open=True): - gr.ColorPicker(elem_id="--ae-nav-bg-color", label="Background color") - gr.ColorPicker(elem_id="--ae-nav-color", label="Text color") - gr.ColorPicker(elem_id="--ae-nav-hover-color", label="Hover color") - - with gr.Accordion(label="Icon", open=True): - gr.ColorPicker(elem_id="--ae-icon-color", label="Color") - gr.ColorPicker(elem_id="--ae-icon-hover-color", label="Hover color") - - with gr.Accordion(label="Other", open=True): - gr.ColorPicker(elem_id="--ae-text-color", label="Text color") - gr.ColorPicker(elem_id="--ae-placeholder-color", label="Placeholder color") - gr.ColorPicker(elem_id="--ae-cancel-color", label="Cancel/Interrupt color") - - with gr.Accordion(label="Modal", open=True): - gr.ColorPicker(elem_id="--ae-modal-bg-color", label="Background color") - gr.ColorPicker(elem_id="--ae-modal-icon-color", label="Icon color") - - - - def save_theme( vars_text, css_text, filename): - style_data= ":root{" + vars_text + "}" + css_text - with open(os.path.join(themes_folder, f"{filename}.css"), 'w', encoding="utf-8") as file: - file.write(vars_text) - file.close() - with open(webui_style_path, 'w', encoding="utf-8") as file: - file.write(style_data) - file.close() - themes_dropdown.choices=get_files(themes_folder,[".css, .txt"]) - return gr.update(choices=themes_dropdown.choices, value=f"{filename}.css") - - def open_theme(filename, css_text): - with open(os.path.join(themes_folder, f"{filename}"), 'r') as file: - vars_text=file.read() - no_ext=filename.rsplit('.', 1)[0] - #save_theme( vars_text, css_text, no_ext) - # shared.state.interrupt() - # shared.state.need_restart = True - return [vars_text, no_ext] - - # def delete_theme(filename): - # try: - # os.remove(os.path.join(themes_folder, filename)) - # except FileNotFoundError: - # pass - - # delete_button.click( - # fn = lambda: delete_theme() - # ) - - save_button.click( - fn=save_theme, - inputs=[vars_text, css_text, save_as_filename], - outputs=themes_dropdown - ) - - themes_dropdown.change( - fn=open_theme, - #_js = "applyTheme", - inputs=[themes_dropdown, css_text], - outputs=[vars_text, save_as_filename] - ) - - # apply_button.click( - # fn=None, - # _js = "applyTheme" - # ) - - # vars_text.change( - # fn=None, - # _js = "applyTheme", - # inputs=[], - # outputs=[vars_text, css_text] - # ) - - - - - return (ui_theme, 'Theme', 'ui_theme'), - - - -script_callbacks.on_ui_tabs(on_ui_tabs) \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/style.css b/extensions-builtin/sd_theme_editor/style.css deleted file mode 100644 index 51f33101..00000000 --- a/extensions-builtin/sd_theme_editor/style.css +++ /dev/null @@ -1,113 +0,0 @@ -#theme_menu { - z-index: 9999; - background-color: var(--ae-input-bg-color); - position: relative; - width: 38px; - height: 38px; - border-radius: 100%; - cursor: pointer; - min-width: unset; - max-width: 38px; - align-self: center; -} - -#theme_menu::before { - content: " "; - display: inline-block; - -webkit-mask-size: cover; - mask-size: cover; - background-color: var(--ae-icon-color); - width: var(--ae-icon-size); - height: var(--ae-icon-size); - -webkit-mask: url(./file=html/svg/contrast-drop-2-line.svg) no-repeat 50% 50%; - mask: url(./file=html/svg/contrast-drop-2-line.svg) no-repeat 50% 50%; - cursor: pointer; - position: relative; - left: 50%; - top: 50%; - transform: translate(-50%, -50%) scale(1); -} - -#theme_menu.fixed, -#theme_menu:hover { - background-color: var(--ae-icon-color); -} - -#theme_menu.fixed::before, -#theme_menu:hover::before { - background-color: var(--ae-icon-hover-color); -} - -#theme_overflow_container { - overflow-y: auto; - height: calc( - 100vh - var(--ae-top-header-height) - (var(--ae-outside-gap-size) * 2) - - (var(--ae-inside-padding-size) * 4) - 96px - ); - overflow-x: hidden; -} - -#tab_ui_theme.open { - transform: translateX(0); - box-shadow: rgba(0, 0, 0, 0.4) -30px 0 30px -30px; -} - -#tab_ui_theme.aside { - display: block !important; -} - -#tab_ui_theme.aside { - position: fixed; - top: var(--ae-top-header-height); - width: 90%; - right: 0; - height: calc(100% - var(--ae-top-header-height)); - max-width: 480px; - z-index: 9999; - transform: translateX(100%); - transition: all 0.25s ease 0s; - box-shadow: rgba(0, 0, 0, 0) -30px 0 30px -30px; - padding: calc(1rem - var(--ae-outside-gap-size)); - background-color: var(--ae-main-bg-color) !important; -} -#tab_ui_theme.aside.open { - transform: translateX(0); - box-shadow: rgba(0, 0, 0, 0.4) -30px 0 30px -30px; -} - -#theme_hidden, -#setting_ui_header_tabs .theme, -#setting_ui_hidden_tabs .theme { - display: none !important; -} - -#tab_ui_theme [id*="color"] label { - display: flex; - align-items: center; - pointer-events: none; -} -#tab_ui_theme [id*="color"] label span { - min-width: 50% !important; -} -#tab_ui_theme [id*="color"] label input { - flex-grow: 1; - pointer-events: all; - cursor: pointer; -} - -#settings_ui_theme > div > div { - flex-direction: row; - flex-wrap: wrap; -} -#settings_ui_theme > div > div > div { - max-width: 30%; -} - -#tab_ui_theme > div { - padding: 16px !important; - padding-top: 0 !important; -} - -#ui_theme_hsv + button { - min-width: unset; -} diff --git a/extensions-builtin/sd_theme_editor/themes/Golde.css b/extensions-builtin/sd_theme_editor/themes/Golde.css deleted file mode 100644 index 55250596..00000000 --- a/extensions-builtin/sd_theme_editor/themes/Golde.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(99deg 11% 8%);--ae-primary-color:hsl(44deg 63% 55%);--ae-input-bg-color:hsl(106deg 8% 12%);--ae-input-border-color:hsl(104deg 9% 32%);--ae-panel-bg-color:hsl(104deg 9% 20%);--ae-panel-border-color:hsl(104deg 9% 32%);--ae-panel-border-radius:4px;--ae-subgroup-bg-color:hsl(99deg 11% 8%);--ae-subgroup-input-bg-color:hsl(99deg 11% 8%);--ae-subgroup-input-border-color:hsl(104deg 9% 32%);--ae-subpanel-bg-color:hsl(106deg 8% 12%);--ae-subpanel-border-color:hsl(104deg 9% 32%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(56deg 30% 36%);--ae-input-focus-color:hsl(44deg 63% 55%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(104deg 9% 32%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(105deg 9% 77%);--ae-icon-hover-color:hsl(99deg 11% 8%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(98deg 9% 4%);--ae-nav-color:hsl(105deg 9% 77%);--ae-nav-hover-color:hsl(98deg 9% 4%);--ae-input-color:hsl(44deg 63% 55%);--ae-label-color:hsl(105deg 9% 77%);--ae-subgroup-input-color:hsl(44deg 63% 55%);--ae-placeholder-color:hsl(104deg 9% 32%);--ae-text-color:hsl(105deg 9% 77%);--ae-mobile-outside-gap-size:2px;--ae-mobile-inside-padding-size:2px;--ae-frame-bg-color:hsl(108deg 8% 12%);--ae-modal-bg-color:hsl(96deg 12% 8%);--ae-modal-icon-color:hsl(44deg 63% 55%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/backup.css b/extensions-builtin/sd_theme_editor/themes/backup.css deleted file mode 100644 index f2537ed3..00000000 --- a/extensions-builtin/sd_theme_editor/themes/backup.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(0deg 0% 10%);--ae-primary-color:hsl(168deg 96% 42%);--ae-input-bg-color:hsl(225deg 6% 13%);--ae-input-border-color:hsl(214deg 5% 30%);--ae-panel-bg-color:hsl(225deg 5% 17%);--ae-panel-border-color:hsl(214deg 5% 30%);--ae-panel-border-radius:0px;--ae-subgroup-bg-color:hsl(0deg 0% 10%);--ae-subgroup-input-bg-color:hsl(225deg 6% 13%);--ae-subgroup-input-border-color:hsl(214deg 5% 30%);--ae-subpanel-bg-color:hsl(220deg 4% 14%);--ae-subpanel-border-color:hsl(214deg 5% 30%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(210deg 3% 36%);--ae-input-focus-color:hsl(168deg 97% 41%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(0deg 84% 60%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(168deg 96% 42%);--ae-icon-hover-color:hsl(0deg 0% 10%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(0deg 0% 4%);--ae-nav-color:hsl(210deg 4% 80%);--ae-nav-hover-color:hsl(0deg 0% 4%);--ae-input-color:hsl(210deg 4% 80%);--ae-label-color:hsl(210deg 4% 80%);--ae-subgroup-input-color:hsl(0deg 100% 100%);--ae-placeholder-color:hsl(214deg 5% 30%);--ae-text-color:hsl(210deg 4% 80%);--ae-mobile-outside-gap-size:3px;--ae-mobile-inside-padding-size:3px;--ae-frame-bg-color:hsl(225deg 6% 13%);--ae-modal-bg-color:hsl(0deg 0% 10%);--ae-modal-icon-color:hsl(168deg 97% 41%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/d-230-52-94.css b/extensions-builtin/sd_theme_editor/themes/d-230-52-94.css deleted file mode 100644 index 818b6d71..00000000 --- a/extensions-builtin/sd_theme_editor/themes/d-230-52-94.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(230deg 52% 4%);--ae-primary-color:hsl(38deg 148% 36%);--ae-input-bg-color:hsl(95deg 58% 7%);--ae-input-border-color:hsl(84deg 57% 24%);--ae-panel-bg-color:hsl(95deg 57% 11%);--ae-panel-border-color:hsl(84deg 57% 24%);--ae-panel-border-radius:0px;--ae-subgroup-bg-color:hsl(230deg 52% 4%);--ae-subgroup-input-bg-color:hsl(95deg 58% 7%);--ae-subgroup-input-border-color:hsl(84deg 57% 24%);--ae-subpanel-bg-color:hsl(90deg 56% 8%);--ae-subpanel-border-color:hsl(84deg 57% 24%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(80deg 55% 30%);--ae-input-focus-color:hsl(38deg 149% 35%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(230deg 136% 54%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(80deg 56% 74%);--ae-icon-hover-color:hsl(230deg 52% 4%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(230deg 52% 98%);--ae-nav-color:hsl(80deg 56% 74%);--ae-nav-hover-color:hsl(230deg 52% 98%);--ae-input-color:hsl(80deg 56% 74%);--ae-label-color:hsl(80deg 56% 74%);--ae-subgroup-input-color:hsl(230deg 152% 94%);--ae-placeholder-color:hsl(84deg 57% 24%);--ae-text-color:hsl(80deg 56% 74%);--ae-mobile-outside-gap-size:3px;--ae-mobile-inside-padding-size:3px;--ae-frame-bg-color:hsl(94deg 60% 7%);--ae-modal-bg-color:hsl(229deg 52% 4%);--ae-modal-icon-color:hsl(38deg 100% 36%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/default.css b/extensions-builtin/sd_theme_editor/themes/default.css deleted file mode 100644 index b2131f91..00000000 --- a/extensions-builtin/sd_theme_editor/themes/default.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(0deg 0% 10%);--ae-primary-color:hsl(168deg 97% 41%);--ae-input-bg-color:hsl(225deg 6% 13%);--ae-input-border-color:hsl(214deg 5% 30%);--ae-panel-bg-color:hsl(225deg 5% 17%);--ae-panel-border-color:hsl(214deg 5% 30%);--ae-panel-border-radius:0px;--ae-subgroup-bg-color:hsl(0deg 0% 10%);--ae-subgroup-input-bg-color:hsl(225deg 6% 13%);--ae-subgroup-input-border-color:hsl(214deg 5% 30%);--ae-subpanel-bg-color:hsl(220deg 4% 14%);--ae-subpanel-border-color:hsl(214deg 5% 30%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(210deg 3% 36%);--ae-input-focus-color:hsl(168deg 97% 41%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(0deg 84% 60%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(168deg 97% 41%);--ae-icon-hover-color:hsl(0deg 0% 10%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(0deg 0% 4%);--ae-nav-color:hsl(210deg 4% 80%);--ae-nav-hover-color:hsl(0deg 0% 4%);--ae-input-color:hsl(210deg 4% 80%);--ae-label-color:hsl(210deg 4% 80%);--ae-subgroup-input-color:hsl(210deg 4% 80%);--ae-placeholder-color:hsl(214deg 5% 30%);--ae-text-color:hsl(210deg 4% 80%);--ae-mobile-outside-gap-size:2px;--ae-mobile-inside-padding-size:2px;--ae-frame-bg-color:hsl(225deg 6% 13%);--ae-modal-bg-color:hsl(0deg 0% 10%);--ae-modal-icon-color:hsl(168deg 97% 41%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/default_cyan.css b/extensions-builtin/sd_theme_editor/themes/default_cyan.css deleted file mode 100644 index 3c1b32cd..00000000 --- a/extensions-builtin/sd_theme_editor/themes/default_cyan.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(0deg 0% 10%);--ae-primary-color:hsl(199deg 60% 60%);--ae-input-bg-color:hsl(225deg 6% 13%);--ae-input-border-color:hsl(214deg 5% 30%);--ae-panel-bg-color:hsl(225deg 5% 17%);--ae-panel-border-color:hsl(214deg 5% 30%);--ae-panel-border-radius:0px;--ae-subgroup-bg-color:hsl(0deg 0% 10%);--ae-subgroup-input-bg-color:hsl(225deg 6% 13%);--ae-subgroup-input-border-color:hsl(214deg 5% 30%);--ae-subpanel-bg-color:hsl(220deg 4% 14%);--ae-subpanel-border-color:hsl(214deg 5% 30%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(210deg 3% 36%);--ae-input-focus-color:hsl(199deg 60% 60%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(357deg 50% 57%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(210deg 4% 80%);--ae-icon-hover-color:hsl(0deg 0% 10%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(0deg 0% 4%);--ae-nav-color:hsl(210deg 4% 80%);--ae-nav-hover-color:hsl(0deg 0% 4%);--ae-input-color:hsl(210deg 4% 80%);--ae-label-color:hsl(210deg 4% 80%);--ae-subgroup-input-color:hsl(210deg 4% 80%);--ae-placeholder-color:hsl(214deg 5% 30%);--ae-text-color:hsl(210deg 4% 80%);--ae-mobile-outside-gap-size:2px;--ae-mobile-inside-padding-size:2px;--ae-frame-bg-color:hsl(225deg 6% 13%);--ae-modal-bg-color:hsl(0deg 0% 10%);--ae-modal-icon-color:hsl(199deg 60% 60%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/default_orange.css b/extensions-builtin/sd_theme_editor/themes/default_orange.css deleted file mode 100644 index 736775be..00000000 --- a/extensions-builtin/sd_theme_editor/themes/default_orange.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(0deg 0% 10%);--ae-primary-color:hsl(16deg 77% 60%);--ae-input-bg-color:hsl(225deg 6% 13%);--ae-input-border-color:hsl(214deg 5% 30%);--ae-panel-bg-color:hsl(225deg 5% 17%);--ae-panel-border-color:hsl(214deg 5% 30%);--ae-panel-border-radius:8px;--ae-subgroup-bg-color:hsl(0deg 0% 10%);--ae-subgroup-input-bg-color:hsl(225deg 6% 13%);--ae-subgroup-input-border-color:hsl(214deg 5% 30%);--ae-subpanel-bg-color:hsl(220deg 4% 14%);--ae-subpanel-border-color:hsl(214deg 5% 30%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(210deg 3% 36%);--ae-input-focus-color:hsl(16deg 77% 60%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(193deg 54% 55%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(210deg 4% 80%);--ae-icon-hover-color:hsl(0deg 0% 10%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(0deg 0% 4%);--ae-nav-color:hsl(210deg 4% 80%);--ae-nav-hover-color:hsl(0deg 0% 4%);--ae-input-color:hsl(210deg 4% 80%);--ae-label-color:hsl(210deg 4% 80%);--ae-subgroup-input-color:hsl(210deg 4% 80%);--ae-placeholder-color:hsl(214deg 5% 30%);--ae-text-color:hsl(210deg 4% 80%);--ae-mobile-outside-gap-size:2px;--ae-mobile-inside-padding-size:2px;--ae-frame-bg-color:hsl(225deg 6% 13%);--ae-modal-bg-color:hsl(0deg 0% 10%);--ae-modal-icon-color:hsl(16deg 77% 60%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/fun.css b/extensions-builtin/sd_theme_editor/themes/fun.css deleted file mode 100644 index 23d77ece..00000000 --- a/extensions-builtin/sd_theme_editor/themes/fun.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(253deg 22% 8%);--ae-primary-color:hsl(76deg 96% 55%);--ae-input-bg-color:hsl(260deg 25% 12%);--ae-input-border-color:hsl(258deg 24% 32%);--ae-panel-bg-color:hsl(258deg 24% 20%);--ae-panel-border-color:hsl(258deg 24% 32%);--ae-panel-border-radius:4px;--ae-subgroup-bg-color:hsl(253deg 22% 8%);--ae-subgroup-input-bg-color:hsl(258deg 24% 8%);--ae-subgroup-input-border-color:hsl(258deg 24% 32%);--ae-subpanel-bg-color:hsl(260deg 25% 12%);--ae-subpanel-border-color:hsl(258deg 24% 32%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(210deg 3% 36%);--ae-input-focus-color:hsl(296deg 96% 55%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(258deg 24% 32%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(259deg 24% 77%);--ae-icon-hover-color:hsl(253deg 22% 8%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(252deg 24% 4%);--ae-nav-color:hsl(259deg 24% 77%);--ae-nav-hover-color:hsl(252deg 24% 4%);--ae-input-color:hsl(305deg 96% 55%);--ae-label-color:hsl(259deg 24% 77%);--ae-subgroup-input-color:hsl(76deg 96% 55%);--ae-placeholder-color:hsl(258deg 24% 32%);--ae-text-color:hsl(259deg 24% 77%);--ae-mobile-outside-gap-size:2px;--ae-mobile-inside-padding-size:2px;--ae-frame-bg-color:hsl(260deg 25% 12%);--ae-modal-bg-color:hsl(253deg 22% 8%);--ae-modal-icon-color:hsl(76deg 96% 55%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/minimal.css b/extensions-builtin/sd_theme_editor/themes/minimal.css deleted file mode 100644 index 279de7ae..00000000 --- a/extensions-builtin/sd_theme_editor/themes/minimal.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(0deg 0% 8%);--ae-primary-color:hsl(168deg 96% 42%);--ae-input-bg-color:hsl(0deg 0% 10%);--ae-input-border-color:hsl(0deg 0% 10%);--ae-panel-bg-color:hsl(0deg 0% 17%);--ae-panel-border-color:hsl(0deg 0% 17%);--ae-panel-border-radius:4px;--ae-subgroup-bg-color:hsl(0deg 0% 10%);--ae-subgroup-input-bg-color:hsl(0deg 0% 10%);--ae-subgroup-input-border-color:hsl(0deg 0% 10%);--ae-subpanel-bg-color:hsl(0deg 0% 14%);--ae-subpanel-border-color:hsl(0deg 0% 15%);--ae-subpanel-border-radius:4px;--ae-textarea-focus-color:hsl(0deg 0% 36%);--ae-input-focus-color:hsl(168deg 97% 41%);--ae-outside-gap-size:1px;--ae-inside-padding-size:5px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(0deg 84% 60%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(168deg 96% 42%);--ae-icon-hover-color:hsl(0deg 0% 10%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(0deg 0% 4%);--ae-nav-color:hsl(0deg 0% 80%);--ae-nav-hover-color:hsl(0deg 0% 4%);--ae-input-color:hsl(210deg 4% 80%);--ae-label-color:hsl(0deg 0% 65%);--ae-subgroup-input-color:hsl(0deg 100% 100%);--ae-placeholder-color:hsl(0deg 0% 30%);--ae-text-color:hsl(0deg 0% 80%);--ae-mobile-outside-gap-size:3px;--ae-mobile-inside-padding-size:3px;--ae-frame-bg-color:hsl(0deg 0% 14%);--ae-modal-bg-color:hsl(0deg 0% 10%);--ae-modal-icon-color:hsl(168deg 97% 41%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/minimal_orange.css b/extensions-builtin/sd_theme_editor/themes/minimal_orange.css deleted file mode 100644 index ef2de97b..00000000 --- a/extensions-builtin/sd_theme_editor/themes/minimal_orange.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(210deg 28% 8%);--ae-primary-color:hsl(18deg 124% 42%);--ae-input-bg-color:hsl(210deg 28% 10%);--ae-input-border-color:hsl(210deg 28% 10%);--ae-panel-bg-color:hsl(210deg 28% 17%);--ae-panel-border-color:hsl(210deg 28% 17%);--ae-panel-border-radius:4px;--ae-subgroup-bg-color:hsl(210deg 28% 10%);--ae-subgroup-input-bg-color:hsl(210deg 28% 10%);--ae-subgroup-input-border-color:hsl(210deg 28% 10%);--ae-subpanel-bg-color:hsl(210deg 28% 14%);--ae-subpanel-border-color:hsl(210deg 28% 15%);--ae-subpanel-border-radius:4px;--ae-textarea-focus-color:hsl(210deg 28% 36%);--ae-input-focus-color:hsl(18deg 125% 41%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(210deg 112% 60%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(18deg 124% 42%);--ae-icon-hover-color:hsl(210deg 28% 10%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(210deg 28% 4%);--ae-nav-color:hsl(210deg 28% 80%);--ae-nav-hover-color:hsl(210deg 28% 4%);--ae-input-color:hsl(60deg 32% 80%);--ae-label-color:hsl(210deg 28% 65%);--ae-subgroup-input-color:hsl(210deg 128% 100%);--ae-placeholder-color:hsl(210deg 28% 30%);--ae-text-color:hsl(210deg 28% 80%);--ae-mobile-outside-gap-size:3px;--ae-mobile-inside-padding-size:3px;--ae-frame-bg-color:hsl(210deg 28% 14%);--ae-modal-bg-color:hsl(210deg 28% 10%);--ae-modal-icon-color:hsl(18deg 125% 41%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/moonlight.css b/extensions-builtin/sd_theme_editor/themes/moonlight.css deleted file mode 100644 index 4f70e44c..00000000 --- a/extensions-builtin/sd_theme_editor/themes/moonlight.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(240deg 16% 6%);--ae-primary-color:hsl(222deg 75% 62%);--ae-input-bg-color:hsl(240deg 17% 8%);--ae-input-border-color:hsl(240deg 20% 16%);--ae-panel-bg-color:hsl(240deg 18% 12%);--ae-panel-border-color:hsl(240deg 16% 16%);--ae-panel-border-radius:0px;--ae-subgroup-bg-color:hsl(240deg 17% 8%);--ae-subgroup-input-bg-color:hsl(240deg 17% 8%);--ae-subgroup-input-border-color:hsl(240deg 20% 16%);--ae-subpanel-bg-color:hsl(240deg 18% 10%);--ae-subpanel-border-color:hsl(240deg 20% 16%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(210deg 3% 36%);--ae-input-focus-color:hsl(222deg 75% 62%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(222deg 75% 62%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(222deg 75% 62%);--ae-icon-hover-color:hsl(240deg 18% 12%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(240deg 16% 6%);--ae-nav-color:hsl(185deg 66% 85%);--ae-nav-hover-color:hsl(0deg 0% 4%);--ae-input-color:hsl(185deg 66% 85%);--ae-label-color:hsl(185deg 66% 85%);--ae-subgroup-input-color:hsl(185deg 66% 85%);--ae-placeholder-color:hsl(240deg 20% 24%);--ae-text-color:hsl(185deg 66% 85%);--ae-mobile-outside-gap-size:2px;--ae-mobile-inside-padding-size:2px;--ae-frame-bg-color:hsl(240deg 18% 10%);--ae-modal-bg-color:hsl(240deg 16% 6%);--ae-modal-icon-color:hsl(222deg 75% 62%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/ogxBGreen.css b/extensions-builtin/sd_theme_editor/themes/ogxBGreen.css deleted file mode 100644 index 1e497888..00000000 --- a/extensions-builtin/sd_theme_editor/themes/ogxBGreen.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(195deg 22% 8%);--ae-primary-color:hsl(159deg 96% 55%);--ae-input-bg-color:hsl(202deg 25% 12%);--ae-input-border-color:hsl(200deg 24% 32%);--ae-panel-bg-color:hsl(200deg 24% 20%);--ae-panel-border-color:hsl(200deg 24% 32%);--ae-panel-border-radius:4px;--ae-subgroup-bg-color:hsl(195deg 22% 8%);--ae-subgroup-input-bg-color:hsl(200deg 24% 8%);--ae-subgroup-input-border-color:hsl(200deg 24% 32%);--ae-subpanel-bg-color:hsl(202deg 25% 12%);--ae-subpanel-border-color:hsl(200deg 24% 32%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(152deg 3% 36%);--ae-input-focus-color:hsl(159deg 96% 55%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(200deg 24% 32%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(159deg 96% 55%);--ae-icon-hover-color:hsl(195deg 22% 8%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(194deg 24% 4%);--ae-nav-color:hsl(201deg 24% 77%);--ae-nav-hover-color:hsl(194deg 24% 4%);--ae-input-color:hsl(159deg 96% 55%);--ae-label-color:hsl(201deg 24% 77%);--ae-subgroup-input-color:hsl(159deg 96% 55%);--ae-placeholder-color:hsl(200deg 24% 32%);--ae-text-color:hsl(201deg 24% 77%);--ae-mobile-outside-gap-size:3px;--ae-mobile-inside-padding-size:3px;--ae-frame-bg-color:hsl(200deg 25% 12%);--ae-modal-bg-color:hsl(193deg 22% 8%);--ae-modal-icon-color:hsl(159deg 96% 55%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/ogxCyan.css b/extensions-builtin/sd_theme_editor/themes/ogxCyan.css deleted file mode 100644 index 3ca6e23c..00000000 --- a/extensions-builtin/sd_theme_editor/themes/ogxCyan.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(253deg 22% 8%);--ae-primary-color:hsl(198deg 96% 55%);--ae-input-bg-color:hsl(260deg 25% 12%);--ae-input-border-color:hsl(258deg 24% 32%);--ae-panel-bg-color:hsl(258deg 24% 20%);--ae-panel-border-color:hsl(258deg 24% 32%);--ae-panel-border-radius:4px;--ae-subgroup-bg-color:hsl(253deg 22% 8%);--ae-subgroup-input-bg-color:hsl(258deg 24% 8%);--ae-subgroup-input-border-color:hsl(258deg 24% 32%);--ae-subpanel-bg-color:hsl(260deg 25% 12%);--ae-subpanel-border-color:hsl(258deg 24% 32%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(210deg 3% 36%);--ae-input-focus-color:hsl(198deg 96% 55%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(258deg 24% 32%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(259deg 24% 77%);--ae-icon-hover-color:hsl(253deg 22% 8%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(252deg 24% 4%);--ae-nav-color:hsl(259deg 24% 77%);--ae-nav-hover-color:hsl(252deg 24% 4%);--ae-input-color:hsl(198deg 96% 55%);--ae-label-color:hsl(259deg 24% 77%);--ae-subgroup-input-color:hsl(198deg 96% 55%);--ae-placeholder-color:hsl(258deg 24% 32%);--ae-text-color:hsl(259deg 24% 77%);--ae-mobile-outside-gap-size:2px;--ae-mobile-inside-padding-size:2px;--ae-frame-bg-color:hsl(260deg 25% 12%);--ae-modal-bg-color:hsl(253deg 22% 8%);--ae-modal-icon-color:hsl(198deg 96% 55%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/ogxCyanInvert.css b/extensions-builtin/sd_theme_editor/themes/ogxCyanInvert.css deleted file mode 100644 index 02b04e2d..00000000 --- a/extensions-builtin/sd_theme_editor/themes/ogxCyanInvert.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(73deg 22% 92%);--ae-primary-color:hsl(18deg 96% 45%);--ae-input-bg-color:hsl(80deg 25% 88%);--ae-input-border-color:hsl(78deg 24% 68%);--ae-panel-bg-color:hsl(78deg 24% 80%);--ae-panel-border-color:hsl(78deg 24% 68%);--ae-panel-border-radius:4px;--ae-subgroup-bg-color:hsl(73deg 22% 92%);--ae-subgroup-input-bg-color:hsl(73deg 22% 92%);--ae-subgroup-input-border-color:hsl(78deg 24% 68%);--ae-subpanel-bg-color:hsl(80deg 25% 88%);--ae-subpanel-border-color:hsl(78deg 24% 68%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(30deg 3% 64%);--ae-input-focus-color:hsl(18deg 96% 45%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(78deg 24% 68%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(79deg 24% 23%);--ae-icon-hover-color:hsl(73deg 22% 92%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(72deg 24% 96%);--ae-nav-color:hsl(79deg 24% 23%);--ae-nav-hover-color:hsl(72deg 24% 96%);--ae-input-color:hsl(18deg 96% 45%);--ae-label-color:hsl(79deg 24% 23%);--ae-subgroup-input-color:hsl(18deg 96% 45%);--ae-placeholder-color:hsl(78deg 24% 68%);--ae-text-color:hsl(79deg 24% 23%);--ae-mobile-outside-gap-size:2px;--ae-mobile-inside-padding-size:2px;--ae-frame-bg-color:hsl(80deg 25% 88%);--ae-modal-bg-color:hsl(73deg 22% 92%);--ae-modal-icon-color:hsl(18deg 96% 45%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/ogxGreen.css b/extensions-builtin/sd_theme_editor/themes/ogxGreen.css deleted file mode 100644 index 077e55e7..00000000 --- a/extensions-builtin/sd_theme_editor/themes/ogxGreen.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(253deg 22% 8%);--ae-primary-color:hsl(149deg 96% 55%);--ae-input-bg-color:hsl(260deg 25% 12%);--ae-input-border-color:hsl(258deg 24% 32%);--ae-panel-bg-color:hsl(258deg 24% 20%);--ae-panel-border-color:hsl(258deg 24% 32%);--ae-panel-border-radius:4px;--ae-subgroup-bg-color:hsl(253deg 22% 8%);--ae-subgroup-input-bg-color:hsl(258deg 24% 8%);--ae-subgroup-input-border-color:hsl(258deg 24% 32%);--ae-subpanel-bg-color:hsl(260deg 25% 12%);--ae-subpanel-border-color:hsl(258deg 24% 32%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(210deg 3% 36%);--ae-input-focus-color:hsl(149deg 96% 55%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(258deg 24% 32%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(259deg 24% 77%);--ae-icon-hover-color:hsl(253deg 22% 8%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(252deg 24% 4%);--ae-nav-color:hsl(259deg 24% 77%);--ae-nav-hover-color:hsl(252deg 24% 4%);--ae-input-color:hsl(149deg 96% 55%);--ae-label-color:hsl(259deg 24% 77%);--ae-subgroup-input-color:hsl(149deg 96% 55%);--ae-placeholder-color:hsl(258deg 24% 32%);--ae-text-color:hsl(259deg 24% 77%);--ae-mobile-outside-gap-size:3px;--ae-mobile-inside-padding-size:3px;--ae-frame-bg-color:hsl(260deg 25% 12%);--ae-modal-bg-color:hsl(253deg 22% 8%);--ae-modal-icon-color:hsl(253deg 22% 8%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/ogxRed.css b/extensions-builtin/sd_theme_editor/themes/ogxRed.css deleted file mode 100644 index 3d7d86a1..00000000 --- a/extensions-builtin/sd_theme_editor/themes/ogxRed.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(253deg 22% 8%);--ae-primary-color:hsl(347deg 96% 55%);--ae-input-bg-color:hsl(260deg 25% 12%);--ae-input-border-color:hsl(258deg 24% 32%);--ae-panel-bg-color:hsl(258deg 24% 20%);--ae-panel-border-color:hsl(258deg 24% 32%);--ae-panel-border-radius:4px;--ae-subgroup-bg-color:hsl(253deg 22% 8%);--ae-subgroup-input-bg-color:hsl(258deg 24% 8%);--ae-subgroup-input-border-color:hsl(258deg 24% 32%);--ae-subpanel-bg-color:hsl(260deg 25% 12%);--ae-subpanel-border-color:hsl(258deg 24% 32%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(210deg 3% 36%);--ae-input-focus-color:hsl(347deg 96% 55%);--ae-outside-gap-size:8px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(258deg 24% 32%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(259deg 24% 77%);--ae-icon-hover-color:hsl(253deg 22% 8%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(252deg 24% 4%);--ae-nav-color:hsl(259deg 24% 77%);--ae-nav-hover-color:hsl(252deg 24% 4%);--ae-input-color:hsl(347deg 96% 55%);--ae-label-color:hsl(259deg 24% 77%);--ae-subgroup-input-color:hsl(347deg 96% 55%);--ae-placeholder-color:hsl(258deg 24% 32%);--ae-text-color:hsl(259deg 24% 77%);--ae-mobile-outside-gap-size:3px;--ae-mobile-inside-padding-size:3px;--ae-frame-bg-color:hsl(260deg 25% 12%);--ae-modal-bg-color:hsl(253deg 22% 8%);--ae-modal-icon-color:hsl(347deg 96% 55%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/retrog.css b/extensions-builtin/sd_theme_editor/themes/retrog.css deleted file mode 100644 index 54839175..00000000 --- a/extensions-builtin/sd_theme_editor/themes/retrog.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(197deg 97% 14%);--ae-primary-color:hsl(27deg 99% 50%);--ae-input-bg-color:hsl(197deg 98% 16%);--ae-input-border-color:hsl(166deg 62% 33%);--ae-panel-bg-color:hsl(196deg 98% 18%);--ae-panel-border-color:hsl(166deg 62% 33%);--ae-panel-border-radius:0px;--ae-subgroup-bg-color:hsl(197deg 97% 14%);--ae-subgroup-input-bg-color:hsl(197deg 97% 14%);--ae-subgroup-input-border-color:hsl(166deg 62% 33%);--ae-subpanel-bg-color:hsl(197deg 98% 16%);--ae-subpanel-border-color:hsl(166deg 62% 33%);--ae-subpanel-border-radius:8px;--ae-textarea-focus-color:hsl(210deg 3% 36%);--ae-input-focus-color:hsl(222deg 75% 62%);--ae-outside-gap-size:6px;--ae-inside-padding-size:6px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(70deg 69% 54%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(27deg 99% 50%);--ae-icon-hover-color:hsl(196deg 98% 18%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(197deg 97% 14%);--ae-nav-color:hsl(70deg 69% 54%);--ae-nav-hover-color:hsl(197deg 97% 14%);--ae-input-color:hsl(70deg 69% 54%);--ae-label-color:hsl(70deg 69% 54%);--ae-subgroup-input-color:hsl(27deg 99% 50%);--ae-placeholder-color:hsl(166deg 62% 33%);--ae-text-color:hsl(185deg 66% 85%);--ae-mobile-outside-gap-size:3px;--ae-mobile-inside-padding-size:3px;--ae-frame-bg-color:hsl(197deg 98% 16%);--ae-modal-bg-color:hsl(197deg 97% 14%);--ae-modal-icon-color:hsl(27deg 99% 50%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/tron.css b/extensions-builtin/sd_theme_editor/themes/tron.css deleted file mode 100644 index 65c3248a..00000000 --- a/extensions-builtin/sd_theme_editor/themes/tron.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(185deg 75% 3%);--ae-primary-color:hsl(182deg 95% 51%);--ae-input-bg-color:hsl(185deg 73% 3%);--ae-input-border-color:hsl(185deg 72% 25%);--ae-panel-bg-color:hsl(180deg 76% 5%);--ae-panel-border-color:hsl(185deg 72% 25%);--ae-panel-border-radius:0px;--ae-subgroup-bg-color:hsl(185deg 73% 3%);--ae-subgroup-input-bg-color:hsl(185deg 73% 3%);--ae-subgroup-input-border-color:hsl(185deg 72% 25%);--ae-subpanel-bg-color:hsl(185deg 73% 3%);--ae-subpanel-border-color:hsl(185deg 72% 25%);--ae-subpanel-border-radius:0px;--ae-textarea-focus-color:hsl(182deg 95% 51%);--ae-input-focus-color:hsl(182deg 95% 51%);--ae-outside-gap-size:2px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(182deg 95% 51%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(182deg 95% 51%);--ae-icon-hover-color:hsl(185deg 73% 3%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(185deg 73% 3%);--ae-nav-color:hsl(182deg 95% 75%);--ae-nav-hover-color:hsl(0deg 100% 50%);--ae-input-color:hsl(182deg 95% 75%);--ae-label-color:hsl(182deg 95% 75%);--ae-subgroup-input-color:hsl(182deg 95% 51%);--ae-placeholder-color:hsl(185deg 72% 25%);--ae-text-color:hsl(182deg 95% 75%);--ae-mobile-outside-gap-size:2px;--ae-mobile-inside-padding-size:8px;--ae-frame-bg-color:hsl(180deg 76% 5%);--ae-modal-bg-color:hsl(185deg 73% 3%);--ae-modal-icon-color:hsl(182deg 95% 51%); \ No newline at end of file diff --git a/extensions-builtin/sd_theme_editor/themes/tron2.css b/extensions-builtin/sd_theme_editor/themes/tron2.css deleted file mode 100644 index 98eeacbc..00000000 --- a/extensions-builtin/sd_theme_editor/themes/tron2.css +++ /dev/null @@ -1 +0,0 @@ ---ae-main-bg-color:hsl(185deg 75% 3%);--ae-primary-color:hsl(182deg 95% 51%);--ae-input-bg-color:hsl(184deg 89% 7%);--ae-input-border-color:hsl(185deg 72% 25%);--ae-panel-bg-color:hsl(185deg 73% 3%);--ae-panel-border-color:hsl(185deg 72% 25%);--ae-panel-border-radius:0px;--ae-subgroup-bg-color:hsl(185deg 73% 3%);--ae-subgroup-input-bg-color:hsl(185deg 73% 3%);--ae-subgroup-input-border-color:hsl(185deg 72% 25%);--ae-subpanel-bg-color:hsl(185deg 73% 3%);--ae-subpanel-border-color:hsl(185deg 72% 25%);--ae-subpanel-border-radius:0px;--ae-textarea-focus-color:hsl(182deg 95% 51%);--ae-input-focus-color:hsl(182deg 95% 51%);--ae-outside-gap-size:2px;--ae-inside-padding-size:8px;--ae-tool-button-size:34px;--ae-tool-button-radius:16px;--ae-generate-button-height:70px;--ae-cancel-color:hsl(182deg 95% 51%);--ae-max-padding:max(var(--ae-outside-gap-size),var(--ae-inside-padding-size));--ae-icon-color:hsl(182deg 95% 51%);--ae-icon-hover-color:hsl(185deg 73% 3%);--ae-icon-size:22px;--ae-nav-bg-color:hsl(185deg 73% 3%);--ae-nav-color:hsl(182deg 95% 75%);--ae-nav-hover-color:hsl(0deg 100% 50%);--ae-input-color:hsl(182deg 95% 75%);--ae-label-color:hsl(182deg 95% 75%);--ae-subgroup-input-color:hsl(182deg 95% 51%);--ae-placeholder-color:hsl(185deg 72% 25%);--ae-text-color:hsl(182deg 95% 75%);--ae-mobile-outside-gap-size:2px;--ae-mobile-inside-padding-size:8px;--ae-frame-bg-color:hsl(185deg 73% 3%); \ No newline at end of file diff --git a/html/200w.webp b/html/200w.webp deleted file mode 100644 index cd3782d08e2179cc60316f2f661a92049ad1d48b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76140 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

i@87$~!ElDS*bH3*Gj_Nt}D>{kB{z*Va zr+NHlbz~q?iwKiB>J^Z63;> zwbRrRj_qn|AK&<=C3Txm$L&(D=OP$uc9PagG8P1f zR(6G(m!sv3f%@Y)D5|m1Y_hjEh`*xS-rh#b0}>ivA`YqP22%u*W55}2fxF1={KXZc zER*EcR5eLpxs`u+D!bHl9^e;yqVl`Dxbs->r-1H@no95Ir>7_HXZTt%qs=~K<2Ul| z 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 roT@D7znJ|p0Ht>&Lqv%5#_s -

- +
+ {background_image} +
{metadata_button} {edit_button} -
-
- - -
- {name} +
+
+
+
+ {name} {description}
-
diff --git a/html/favicon.ico b/html/favicon.ico deleted file mode 100644 index 0bdafb82b16fb3baa0b55c7d1ecaefeaf68424d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmdUx+mBsS9LM*xTH?|fD!nN75Cjp4`z;*$Ag)EwQo=(;BqU8;(}+wPVGz3UfEPqG z)I%>qixj;KGcVP;mNy;q56pR+hm$!zpPjXKmgCGhqx!&}eCGGtzx7?ewSH@_wPv!c zhoAob%-`9DzAT%UW!afPD7zYrmk(|K$EVqBW`V=7CqWJFg@*(BH8>H}l=^>LYs`b4 z3(Mgn_#XDb4)`RXZ_G9|cf)aT(DMj=563QpA=m@Y!!@u7j)YkOePfGZ5Wa)muoTQO z?WIr~Sb%*C_QMmf5IWWq+k^H=tbN}Eb9LoJByXR#mJQdrqp5;S9tZy~>cEGVv4zCR?!+OTN zLt@Nx>>2+!*m&NjV`HDYl6>A>p3OUxm}AX^58%a?__@3v0Qb)$uyUHH-2%VEIS}j7 zz6|TZF=O1jV=r85%qMAk8s9Ti-e+ZF%dbRx1HOBw555N1yUdm3ypEV-Rr?$N3h?ec z8LI61;CUQ|RpGsAk4}hNgSzKH?YB_gJ1x4L_z!S7#5(=<-j_!ki%-&i55Ds|J#AN) zXr94;7QCO;x_7GD6YVj?zkzfQJjna!FdO3VrS%<-g3a(6%z(7bcJH|@kV66Pm%r4K zjPD)u1v~(8{@un`cNOf1E0cc4owN7KeNY3>_zI}%ivNqi`CyUS~sV9(AsNbr0dY7u*v$RQl904rhS6<>B32pMvM( zC2)SO|0}RJ_^sc|zisBY6k-hv@zq~%tlg7nAH#QV&jJ7KC?W&e1ho7h@gfa6Fn_usUk7kHZG|+n$Sg678$_??JgPN!+=+2D!0zkMV%o zpWrO;8~+T(%JvR@`_b%!+fr@t`>=jT&vCarN1(CZx8WW55RA1OI?h=;0NbG#Dy@Ft zR_w2ES&UVp;KVrg5*Q6!9OE7I?s0Q3X{QY__ln=g?_1u*##~iNk8AF| zwh-8I~fgC!{gZ4OXKmJ;fub}r^-3;zo?=0^(@0U}- zJ?x$rePgz%QEMOHr~KaOc_oXoO;Mm|xS7?{k*`KF_s?XhNaWH`%K z$hs_BRpbZiMZU9M6uauh;M#gI(o-*nvU>3cZR1cc>LU~Rn2a@QBcqMl;P~V~??kPU PH+siwj^SA4m<;_Vx=CC- diff --git a/html/footer.html b/html/footer.html index 2418fd52..69b2372c 100644 --- a/html/footer.html +++ b/html/footer.html @@ -1,55 +1,15 @@ - +
+ API +  •  + Github +  •  + Gradio +  •  + Startup profile +  •  + Reload UI +
+
+
+{versions} +
diff --git a/html/spotlight.css b/html/spotlight.css deleted file mode 100644 index c5f52159..00000000 --- a/html/spotlight.css +++ /dev/null @@ -1,363 +0,0 @@ -:root{ - --primary-color:#ffffff; -} -#spotlight { - position: fixed; - top: -1px; - bottom: -1px; - width: 100%; - z-index: 99999; - color: #fff; - background-color: #000; - opacity: 0; - overflow: hidden; - user-select: none; - transition: opacity 0.2s ease-out; - font-family: Arial, sans-serif; - font-size: 16px; - font-weight: 400; - contain: strict; - touch-action: none; - pointer-events: none; -} -#spotlight.show { - opacity: 1; - transition: none; - pointer-events: auto; -} -#spotlight.white { - color: #212529; - background-color: #fff; -} -#spotlight.white .spl-spinner, -#spotlight.white .spl-prev, -#spotlight.white .spl-next, -#spotlight.white .spl-page ~ * { - filter: invert(1); -} -#spotlight.white .spl-progress { - background-color: rgba(0, 0, 0, 0.35); -} -#spotlight.white .spl-header, -#spotlight.white .spl-footer { - background-color: rgba(255, 255, 255, 0.65); -} -#spotlight.white .spl-button { - background: #212529; - color: #fff; -} -#spotlight .cover { - object-fit: cover; - height: 100%; - width: 100%; -} -#spotlight .contain { - object-fit: contain; - height: 100%; - width: 100%; -} -#spotlight .autofit { - object-fit: none; - width: auto; - height: auto; - max-height: none; - max-width: none; - transition: none; -} -.spl-track { - position: absolute; - width: 100%; - height: 100%; - contain: strict; -} -.spl-spinner { - position: absolute; - width: 100%; - height: 100%; - background-position: center; - background-repeat: no-repeat; - background-size: 42px; - opacity: 0; -} -.spl-spinner.spin { - background-image: url("svg/spotlight/preloader.svg"); - transition: opacity 0.2s linear 0.25s; - opacity: 1; -} -.spl-spinner.error { - background-image: url("svg/spotlight/error.svg"); - background-size: 128px; - transition: none; - opacity: 0.5; -} -.spl-scene { - position: absolute; - width: 100%; - height: 100%; - transition: transform 0.65s cubic-bezier(0.1, 1, 0.1, 1); - contain: layout size; - will-change: transform; -} -.spl-pane > * { - position: absolute; - width: auto; - height: auto; - max-width: 100%; - max-height: 100%; - left: 50%; - top: 50%; - margin: 0; - padding: 0; - border: 0; - transform: translate(-50%, -50%) scale(1); - transition: transform 0.65s cubic-bezier(0.3, 1, 0.3, 1), opacity 0.65s ease; - contain: layout style; - will-change: transform, opacity; - visibility: hidden; -} -.spl-pane { - position: absolute; - top: 0; - width: 100%; - height: 100%; - transition: transform 0.65s cubic-bezier(0.3, 1, 0.3, 1); - contain: layout size; - will-change: transform, contents; -} -.spl-pane.hide img { - display:none !important; -} -.spl-pane.hide { - background-size: 100%; - transition: background-size 0.65s cubic-bezier(0.3, 1, 0.3, 1) !important; - will-change: background-size !important; - transform: translate(0, 0) !important; -} -.spl-header { - position: absolute; - top: 0; - width: 100%; - height: 50px; - text-align: right; - background-color: rgba(0, 0, 0, 0.45); - transform: translateY(-100px); - transition: transform 0.35s ease; - overflow: hidden; - will-change: transform; -} -#spotlight.menu .spl-header, -.spl-header:hover { - transform: translateY(0); -} -.spl-header div { - display: inline-block; - vertical-align: middle; - white-space: nowrap; - width: 50px; - height: 50px; - opacity: 0.5; -} -.spl-progress { - position: absolute; - top: 0; - width: 100%; - height: 3px; - background-color: rgba(255, 255, 255, 0.45); - transform: translateX(-100%); - transition: transform linear; -} -.spl-footer { - position: absolute; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.45); - line-height: 20px; - padding: 20px 20px 0 20px; - padding-bottom: env(safe-area-inset-bottom, 0); - text-align: left; - font-size: 15px; - font-weight: 400; - transform: translateY(100%); - transition: transform 0.35s ease; - will-change: transform; -} -#spotlight.menu .spl-footer, -.spl-footer:hover { - transform: translateY(0); -} -.spl-title { - font-size: 22px; - margin-bottom: 20px; -} -.spl-description { - margin-bottom: 20px; -} -.spl-button { - display: inline-block; - background: #fff; - color: #000; - border-radius: 5px; - padding: 10px 20px; - margin-bottom: 20px; - cursor: pointer; -} -.spl-page { - float: left; - width: auto; - line-height: 50px; -} -.spl-page ~ * { - background-position: center; - background-repeat: no-repeat; - background-size: 21px; - float: right; -} -.spl-fullscreen { - background-color:var(--primary-color); - -webkit-mask: url("svg/fullscreen-line.svg") no-repeat 50% 50%; - mask: url("svg/fullscreen-line.svg") no-repeat 50% 50%; -} -.spl-fullscreen.on { - background-color:var(--primary-color); - -webkit-mask: url("svg/fullscreen-exit-line.svg") no-repeat 50% 50%; - mask: url("svg/fullscreen-exit-line.svg") no-repeat 50% 50%; -} -.spl-autofit { - background-color:var(--primary-color); - -webkit-mask: url("svg/aspect-ratio-line.svg") no-repeat 50% 50%; - mask: url("svg/aspect-ratio-line.svg") no-repeat 50% 50%; -} -.spl-zoom-out { - background-color:var(--primary-color); - -webkit-mask: url("svg/zoom-out-line.svg") no-repeat 50% 50%; - mask: url("svg/zoom-out-line.svg") no-repeat 50% 50%; - background-size: 22px; -} -.spl-zoom-in { - background-color:var(--primary-color); - -webkit-mask: url("svg/zoom-in-line.svg") no-repeat 50% 50%; - mask: url("svg/zoom-in-line.svg") no-repeat 50% 50%; - background-size: 22px; -} -.spl-download { - background-color:var(--primary-color); - -webkit-mask: url("svg/file-download-line.svg") no-repeat 50% 50%; - mask: url("svg/file-download-line.svg") no-repeat 50% 50%; - background-size: 20px; -} -.spl-theme { - background-image: url("svg/spotlight/theme.svg"); -} -.spl-play { - background-image: url("svg/spotlight/play.svg"); -} -.spl-play.on { - background-image: url("svg/spotlight/pause.svg"); - animation: pulsate 1s ease infinite; -} -.spl-close { - background-color:var(--primary-color); - -webkit-mask: url("svg/close-line.svg") no-repeat 50% 50%; - mask: url("svg/close-line.svg") no-repeat 50% 50%; -} -.spl-like{ - background-color:var(--primary-color); - -webkit-mask: url("svg/heart-line.svg") no-repeat 50% 50%; - mask: url("svg/heart-line.svg") no-repeat 50% 50%; - background-size: 22px; -} -.spl-like.on{ - background-color:var(--primary-color); - -webkit-mask: url("svg/heart-fill.svg") no-repeat 50% 50%; - mask: url("svg/heart-fill.svg") no-repeat 50% 50%; -} -.spl-tile{ - background-color:var(--primary-color); - -webkit-mask: url("svg/grid-line.svg") no-repeat 50% 50%; - mask: url("svg/grid-line.svg") no-repeat 50% 50%; - background-size: 22px; -} -.spl-tile.on{ - background-color:var(--primary-color); - -webkit-mask: url("svg/grid-fill.svg") no-repeat 50% 50%; - mask: url("svg/grid-fill.svg") no-repeat 50% 50%; -} - -.spl-prev, -.spl-next { - position: absolute; - top: 50%; - left: 20px; - width: 50px; - height: 50px; - opacity: 0.65; - background-color: rgba(0, 0, 0, 0.45); - border-radius: 100%; - cursor: pointer; - margin-top: -25px; - transform: translateX(-100px); - transition: transform 0.35s ease; - background-image: url("svg/spotlight/arrow.svg"); - background-position: center; - background-repeat: no-repeat; - background-size: 30px; - will-change: transform; -} -.spl-next { - left: auto; - right: 20px; - transform: translateX(100px) scaleX(-1); -} -#spotlight.menu .spl-prev { - transform: translateX(0); -} -#spotlight.menu .spl-next { - transform: translateX(0) scaleX(-1); -} -@media (hover: hover) { - .spl-page ~ div { - cursor: pointer; - transition: opacity 0.2s ease; - } - .spl-page ~ div:hover, - .spl-prev:hover, - .spl-next:hover { - opacity: 1; - } -} -@media (max-width: 500px) { - .spl-header div { - width: 44px; - } - .spl-footer .spl-title { - font-size: 20px; - } - .spl-footer { - font-size: 14px; - } - .spl-prev, - .spl-next { - width: 35px; - height: 35px; - margin-top: -17.5px; - background-size: 15px 15px; - } - .spl-spinner { - background-size: 30px 30px; - } -} -.hide-scrollbars { - overflow: hidden !important; -} -@keyframes pulsate { - 0% { - opacity: 1; - } - 50% { - opacity: 0.2; - } - 100% { - opacity: 1; - } -} \ No newline at end of file diff --git a/html/svg/add-box-line.svg b/html/svg/add-box-line.svg deleted file mode 100644 index dc56100a..00000000 --- a/html/svg/add-box-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/arrow-go-back-line.svg b/html/svg/arrow-go-back-line.svg deleted file mode 100644 index f01c233d..00000000 --- a/html/svg/arrow-go-back-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/artboard-line.svg b/html/svg/artboard-line.svg deleted file mode 100644 index 6998c0c1..00000000 --- a/html/svg/artboard-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/aspect-ratio-line.svg b/html/svg/aspect-ratio-line.svg deleted file mode 100644 index 855f9938..00000000 --- a/html/svg/aspect-ratio-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/ball-pen-line.svg b/html/svg/ball-pen-line.svg deleted file mode 100644 index e464a517..00000000 --- a/html/svg/ball-pen-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/bmc-logo2.svg b/html/svg/bmc-logo2.svg deleted file mode 100644 index 6766ec42..00000000 --- a/html/svg/bmc-logo2.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/html/svg/brush-line.svg b/html/svg/brush-line.svg deleted file mode 100644 index 6f8a957d..00000000 --- a/html/svg/brush-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/checkbox-blank-line.svg b/html/svg/checkbox-blank-line.svg deleted file mode 100644 index d6c2aa6d..00000000 --- a/html/svg/checkbox-blank-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/checkbox-indeterminate-line.svg b/html/svg/checkbox-indeterminate-line.svg deleted file mode 100644 index b4c69c05..00000000 --- a/html/svg/checkbox-indeterminate-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/clipboard-line.svg b/html/svg/clipboard-line.svg deleted file mode 100644 index 9ee97314..00000000 --- a/html/svg/clipboard-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/close-circle-line.svg b/html/svg/close-circle-line.svg deleted file mode 100644 index 90477e5f..00000000 --- a/html/svg/close-circle-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/close-line.svg b/html/svg/close-line.svg deleted file mode 100644 index e39564d9..00000000 --- a/html/svg/close-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/compass-4-line.svg b/html/svg/compass-4-line.svg deleted file mode 100644 index 7d894275..00000000 --- a/html/svg/compass-4-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/computer-fill.svg b/html/svg/computer-fill.svg deleted file mode 100644 index ea728890..00000000 --- a/html/svg/computer-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/contrast-drop-2-line.svg b/html/svg/contrast-drop-2-line.svg deleted file mode 100644 index abd8f3ea..00000000 --- a/html/svg/contrast-drop-2-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/cpu-fill.svg b/html/svg/cpu-fill.svg deleted file mode 100644 index fe016773..00000000 --- a/html/svg/cpu-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/cpu-line.svg b/html/svg/cpu-line.svg deleted file mode 100644 index 7d4922a1..00000000 --- a/html/svg/cpu-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/delete-bin-2-line.svg b/html/svg/delete-bin-2-line.svg deleted file mode 100644 index a6781f5a..00000000 --- a/html/svg/delete-bin-2-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/delete-bin-5-line.svg b/html/svg/delete-bin-5-line.svg deleted file mode 100644 index f0da62ec..00000000 --- a/html/svg/delete-bin-5-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/dice-1.svg b/html/svg/dice-1.svg deleted file mode 100644 index ece3905d..00000000 --- a/html/svg/dice-1.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/html/svg/download-line.svg b/html/svg/download-line.svg deleted file mode 100644 index 1d40a4fd..00000000 --- a/html/svg/download-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/drag-drop-line.svg b/html/svg/drag-drop-line.svg deleted file mode 100644 index d6d78a5b..00000000 --- a/html/svg/drag-drop-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/drag-move-2-fill.svg b/html/svg/drag-move-2-fill.svg deleted file mode 100644 index 80637b0e..00000000 --- a/html/svg/drag-move-2-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/file-download-line.svg b/html/svg/file-download-line.svg deleted file mode 100644 index 935f2ebe..00000000 --- a/html/svg/file-download-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/file-zip-line.svg b/html/svg/file-zip-line.svg deleted file mode 100644 index e5f83060..00000000 --- a/html/svg/file-zip-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/folder-open-line.svg b/html/svg/folder-open-line.svg deleted file mode 100644 index 94ef19f1..00000000 --- a/html/svg/folder-open-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/fullscreen-exit-line.svg b/html/svg/fullscreen-exit-line.svg deleted file mode 100644 index d3482777..00000000 --- a/html/svg/fullscreen-exit-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/fullscreen-line.svg b/html/svg/fullscreen-line.svg deleted file mode 100644 index 573526cb..00000000 --- a/html/svg/fullscreen-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/github-fill.svg b/html/svg/github-fill.svg deleted file mode 100644 index 206c5517..00000000 --- a/html/svg/github-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/gradio-fill.svg b/html/svg/gradio-fill.svg deleted file mode 100644 index c03c6b86..00000000 --- a/html/svg/gradio-fill.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/html/svg/grid-fill.svg b/html/svg/grid-fill.svg deleted file mode 100644 index fef4a086..00000000 --- a/html/svg/grid-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/grid-line.svg b/html/svg/grid-line.svg deleted file mode 100644 index 0050582b..00000000 --- a/html/svg/grid-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/heart-add-fill.svg b/html/svg/heart-add-fill.svg deleted file mode 100644 index 8d7234bf..00000000 --- a/html/svg/heart-add-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/heart-fill.svg b/html/svg/heart-fill.svg deleted file mode 100644 index 91e44eea..00000000 --- a/html/svg/heart-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/heart-line.svg b/html/svg/heart-line.svg deleted file mode 100644 index 8591aeaf..00000000 --- a/html/svg/heart-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/image-icon.svg b/html/svg/image-icon.svg deleted file mode 100644 index 42b648af..00000000 --- a/html/svg/image-icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/image-update.svg b/html/svg/image-update.svg deleted file mode 100644 index 3abf12df..00000000 --- a/html/svg/image-update.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/html/svg/infinity-fill.svg b/html/svg/infinity-fill.svg deleted file mode 100644 index 7204f234..00000000 --- a/html/svg/infinity-fill.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - diff --git a/html/svg/information-fill.svg b/html/svg/information-fill.svg deleted file mode 100644 index 39111d4d..00000000 --- a/html/svg/information-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/information-line.svg b/html/svg/information-line.svg deleted file mode 100644 index 0983ccf9..00000000 --- a/html/svg/information-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/landscape-line.svg b/html/svg/landscape-line.svg deleted file mode 100644 index 69231da2..00000000 --- a/html/svg/landscape-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/magic-line.svg b/html/svg/magic-line.svg deleted file mode 100644 index 70442fa7..00000000 --- a/html/svg/magic-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/menu-4-fill.svg b/html/svg/menu-4-fill.svg deleted file mode 100644 index 5d06e79b..00000000 --- a/html/svg/menu-4-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/menu-add-fill.svg b/html/svg/menu-add-fill.svg deleted file mode 100644 index 21ee2c54..00000000 --- a/html/svg/menu-add-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/menu-line.svg b/html/svg/menu-line.svg deleted file mode 100644 index dfdfea1f..00000000 --- a/html/svg/menu-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/more-2-fill.svg b/html/svg/more-2-fill.svg deleted file mode 100644 index e67d22af..00000000 --- a/html/svg/more-2-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/paint-brush-line.svg b/html/svg/paint-brush-line.svg deleted file mode 100644 index e8b93deb..00000000 --- a/html/svg/paint-brush-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/pantone-line.svg b/html/svg/pantone-line.svg deleted file mode 100644 index d93296fe..00000000 --- a/html/svg/pantone-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/picture-in-picture-exit-line.svg b/html/svg/picture-in-picture-exit-line.svg deleted file mode 100644 index ad5e2f04..00000000 --- a/html/svg/picture-in-picture-exit-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/play-line.svg b/html/svg/play-line.svg deleted file mode 100644 index a967d730..00000000 --- a/html/svg/play-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/pulse-line.svg b/html/svg/pulse-line.svg deleted file mode 100644 index 4f8aa2bd..00000000 --- a/html/svg/pulse-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/question-answer-fill.svg b/html/svg/question-answer-fill.svg deleted file mode 100644 index fb6b6fdf..00000000 --- a/html/svg/question-answer-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/question-answer-line.svg b/html/svg/question-answer-line.svg deleted file mode 100644 index 29bc6899..00000000 --- a/html/svg/question-answer-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/recycle-line.svg b/html/svg/recycle-line.svg deleted file mode 100644 index 53988a91..00000000 --- a/html/svg/recycle-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/refresh-line.svg b/html/svg/refresh-line.svg deleted file mode 100644 index ff547244..00000000 --- a/html/svg/refresh-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/reply-fill.svg b/html/svg/reply-fill.svg deleted file mode 100644 index 54aade62..00000000 --- a/html/svg/reply-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/reply-line.svg b/html/svg/reply-line.svg deleted file mode 100644 index e1b88438..00000000 --- a/html/svg/reply-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/restart-fill.svg b/html/svg/restart-fill.svg deleted file mode 100644 index e57e15eb..00000000 --- a/html/svg/restart-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/rocket-fill.svg b/html/svg/rocket-fill.svg deleted file mode 100644 index bb4e93ff..00000000 --- a/html/svg/rocket-fill.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - diff --git a/html/svg/save-2-line.svg b/html/svg/save-2-line.svg deleted file mode 100644 index 1536631d..00000000 --- a/html/svg/save-2-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/save-3-line.svg b/html/svg/save-3-line.svg deleted file mode 100644 index ee5c0d65..00000000 --- a/html/svg/save-3-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/skip-forward-line.svg b/html/svg/skip-forward-line.svg deleted file mode 100644 index 24eebda3..00000000 --- a/html/svg/skip-forward-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/sort-asc.svg b/html/svg/sort-asc.svg deleted file mode 100644 index 96552675..00000000 --- a/html/svg/sort-asc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/sort-desc.svg b/html/svg/sort-desc.svg deleted file mode 100644 index a25ce6c2..00000000 --- a/html/svg/sort-desc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/spotlight/arrow.svg b/html/svg/spotlight/arrow.svg deleted file mode 100644 index ba58879a..00000000 --- a/html/svg/spotlight/arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/spotlight/error.svg b/html/svg/spotlight/error.svg deleted file mode 100644 index fc950613..00000000 --- a/html/svg/spotlight/error.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/spotlight/pause.svg b/html/svg/spotlight/pause.svg deleted file mode 100644 index 621b4fd6..00000000 --- a/html/svg/spotlight/pause.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/spotlight/pixel.gif b/html/svg/spotlight/pixel.gif deleted file mode 100644 index 5bfd67a2d6f72ac3a55cbfcea5866e841d22f5d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43 mcmZ?wbhEHbWMp7uXkdT>#h)yUAf^t80Ld^gF}W}@SOWlZ0R#L1 diff --git a/html/svg/spotlight/play.svg b/html/svg/spotlight/play.svg deleted file mode 100644 index 73191945..00000000 --- a/html/svg/spotlight/play.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/spotlight/preloader.svg b/html/svg/spotlight/preloader.svg deleted file mode 100644 index 91a2fe3b..00000000 --- a/html/svg/spotlight/preloader.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/spotlight/theme.svg b/html/svg/spotlight/theme.svg deleted file mode 100644 index 6287d462..00000000 --- a/html/svg/spotlight/theme.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/stack-line.svg b/html/svg/stack-line.svg deleted file mode 100644 index 1bd316f6..00000000 --- a/html/svg/stack-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/stop-line.svg b/html/svg/stop-line.svg deleted file mode 100644 index 3c5290b7..00000000 --- a/html/svg/stop-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/t-box-fill.svg b/html/svg/t-box-fill.svg deleted file mode 100644 index 6b9e7293..00000000 --- a/html/svg/t-box-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/terminal-box-fill.svg b/html/svg/terminal-box-fill.svg deleted file mode 100644 index 776f80ae..00000000 --- a/html/svg/terminal-box-fill.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/terminal-box-line.svg b/html/svg/terminal-box-line.svg deleted file mode 100644 index 594c5296..00000000 --- a/html/svg/terminal-box-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/zoom-in-line.svg b/html/svg/zoom-in-line.svg deleted file mode 100644 index 48a29cd8..00000000 --- a/html/svg/zoom-in-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/html/svg/zoom-out-line.svg b/html/svg/zoom-out-line.svg deleted file mode 100644 index d85c8f20..00000000 --- a/html/svg/zoom-out-line.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/javascript/aspectRatioOverlay.js b/javascript/aspectRatioOverlay.js index fff1a992..2cf2d571 100644 --- a/javascript/aspectRatioOverlay.js +++ b/javascript/aspectRatioOverlay.js @@ -1,3 +1,4 @@ + let currentWidth = null; let currentHeight = null; let arFrameTimeout = setTimeout(function() {}, 0); diff --git a/javascript/badScaleChecker.js b/javascript/badScaleChecker.js deleted file mode 100644 index 625ad309..00000000 --- a/javascript/badScaleChecker.js +++ /dev/null @@ -1,108 +0,0 @@ -(function() { - var ignore = localStorage.getItem("bad-scale-ignore-it") == "ignore-it"; - - function getScale() { - var ratio = 0, - screen = window.screen, - ua = navigator.userAgent.toLowerCase(); - - if (window.devicePixelRatio !== undefined) { - ratio = window.devicePixelRatio; - } else if (~ua.indexOf('msie')) { - if (screen.deviceXDPI && screen.logicalXDPI) { - ratio = screen.deviceXDPI / screen.logicalXDPI; - } - } else if (window.outerWidth !== undefined && window.innerWidth !== undefined) { - ratio = window.outerWidth / window.innerWidth; - } - - return ratio == 0 ? 0 : Math.round(ratio * 100); - } - - var showing = false; - - var div = document.createElement("div"); - div.style.position = "fixed"; - div.style.top = "0px"; - div.style.left = "0px"; - div.style.width = "100vw"; - div.style.backgroundColor = "firebrick"; - div.style.textAlign = "center"; - div.style.zIndex = 99; - - var b = document.createElement("b"); - b.innerHTML = 'Bad Scale: ??% '; - - div.appendChild(b); - - var note1 = document.createElement("p"); - note1.innerHTML = "Change your browser or your computer settings!"; - note1.title = 'Just make sure "computer-scale" * "browser-scale" = 100% ,\n' + - "you can keep your computer-scale and only change this page's scale,\n" + - "for example: your computer-scale is 125%, just use [\"CTRL\"+\"-\"] to make your browser-scale of this page to 80%."; - div.appendChild(note1); - - var note2 = document.createElement("p"); - note2.innerHTML = " Otherwise, it will cause this page to not function properly!"; - note2.title = "When you click \"Copy image to: [inpaint sketch]\" in some img2img's tab,\n" + - "if scale<100% the canvas will be invisible,\n" + - "else if scale>100% this page will take large amount of memory and CPU performance."; - div.appendChild(note2); - - var btn = document.createElement("button"); - btn.innerHTML = "Click here to ignore"; - - div.appendChild(btn); - - function tryShowTopBar(scale) { - if (showing) return; - - b.innerHTML = 'Bad Scale: ' + scale + '% '; - - var updateScaleTimer = setInterval(function() { - var newScale = getScale(); - b.innerHTML = 'Bad Scale: ' + newScale + '% '; - if (newScale == 100) { - var p = div.parentNode; - if (p != null) p.removeChild(div); - showing = false; - clearInterval(updateScaleTimer); - check(); - } - }, 999); - - btn.onclick = function() { - clearInterval(updateScaleTimer); - var p = div.parentNode; - if (p != null) p.removeChild(div); - ignore = true; - showing = false; - localStorage.setItem("bad-scale-ignore-it", "ignore-it"); - }; - - document.body.appendChild(div); - } - - function check() { - if (!ignore) { - var timer = setInterval(function() { - var scale = getScale(); - if (scale != 100 && !ignore) { - tryShowTopBar(scale); - clearInterval(timer); - } - if (ignore) { - clearInterval(timer); - } - }, 999); - } - } - - if (document.readyState != "complete") { - document.onreadystatechange = function() { - if (document.readyState != "complete") check(); - }; - } else { - check(); - } -})(); diff --git a/javascript/contextMenus.js b/javascript/contextMenus.js index 8d3088cf..ccae242f 100644 --- a/javascript/contextMenus.js +++ b/javascript/contextMenus.js @@ -1,4 +1,4 @@ -/* + var contextMenuInit = function() { let eventListenerApplied = false; let menuSpecs = new Map(); @@ -174,4 +174,3 @@ var addContextMenuEventListener = initResponse[2]; //End example Context Menu Items onAfterUiUpdate(addContextMenuEventListener); -*/ diff --git a/javascript/dragdrop.js b/javascript/dragdrop.js index 488ef41d..5803daea 100644 --- a/javascript/dragdrop.js +++ b/javascript/dragdrop.js @@ -1,109 +1,130 @@ // allows drag-dropping files into gradio image elements, and also pasting images from clipboard function isValidImageList(files) { - return ( - files && - files?.length === 1 && - ["image/png", "image/gif", "image/jpeg"].includes(files[0].type) - ); + return files && files?.length === 1 && ['image/png', 'image/gif', 'image/jpeg'].includes(files[0].type); } function dropReplaceImage(imgWrap, files) { - if (!isValidImageList(files)) { - return; - } - - const tmpFile = files[0]; - - imgWrap.querySelector('[aria-label="Clear"]')?.click(); - const callback = () => { - const fileInput = imgWrap.querySelector('input[type="file"]'); - if (fileInput) { - if (files.length === 0) { - files = new DataTransfer(); - files.items.add(tmpFile); - fileInput.files = files.files; - } else { - fileInput.files = files; - } - fileInput.dispatchEvent(new Event("change")); + if (!isValidImageList(files)) { + return; } - }; - if (imgWrap.closest("#pnginfo_image")) { - // special treatment for PNG Info tab, wait for fetch request to finish - const oldFetch = window.fetch; - window.fetch = async (input, options) => { - const response = await oldFetch(input, options); - if ("api/predict/" === input) { - const content = await response.text(); - window.fetch = oldFetch; - window.requestAnimationFrame(() => callback()); - return new Response(content, { - status: response.status, - statusText: response.statusText, - headers: response.headers, - }); - } - return response; + const tmpFile = files[0]; + + imgWrap.querySelector('.modify-upload button + button, .touch-none + div button + button')?.click(); + const callback = () => { + const fileInput = imgWrap.querySelector('input[type="file"]'); + if (fileInput) { + if (files.length === 0) { + files = new DataTransfer(); + files.items.add(tmpFile); + fileInput.files = files.files; + } else { + fileInput.files = files; + } + fileInput.dispatchEvent(new Event('change')); + } }; - } else { - window.requestAnimationFrame(() => callback()); - } + + if (imgWrap.closest('#pnginfo_image')) { + // special treatment for PNG Info tab, wait for fetch request to finish + const oldFetch = window.fetch; + window.fetch = async(input, options) => { + const response = await oldFetch(input, options); + if ('api/predict/' === input) { + const content = await response.text(); + window.fetch = oldFetch; + window.requestAnimationFrame(() => callback()); + return new Response(content, { + status: response.status, + statusText: response.statusText, + headers: response.headers + }); + } + return response; + }; + } else { + window.requestAnimationFrame(() => callback()); + } } -window.document.addEventListener("dragover", (e) => { - const target = e.composedPath()[0]; - const imgWrap = target.closest('[data-testid="image"]'); - if ( - !imgWrap && - target.placeholder && - target.placeholder.indexOf("Prompt") == -1 - ) { - return; - } - e.stopPropagation(); - e.preventDefault(); - e.dataTransfer.dropEffect = "copy"; +function eventHasFiles(e) { + if (!e.dataTransfer || !e.dataTransfer.files) return false; + if (e.dataTransfer.files.length > 0) return true; + if (e.dataTransfer.items.length > 0 && e.dataTransfer.items[0].kind == "file") return true; + + return false; +} + +function dragDropTargetIsPrompt(target) { + if (target?.placeholder && target?.placeholder.indexOf("Prompt") >= 0) return true; + if (target?.parentNode?.parentNode?.className?.indexOf("prompt") > 0) return true; + return false; +} + +window.document.addEventListener('dragover', e => { + const target = e.composedPath()[0]; + if (!eventHasFiles(e)) return; + + var targetImage = target.closest('[data-testid="image"]'); + if (!dragDropTargetIsPrompt(target) && !targetImage) return; + + e.stopPropagation(); + e.preventDefault(); + e.dataTransfer.dropEffect = 'copy'; }); -window.document.addEventListener("drop", (e) => { - const target = e.composedPath()[0]; - if (target.placeholder.indexOf("Prompt") == -1) { - return; - } - const imgWrap = target.closest('[data-testid="image"]'); - if (!imgWrap) { - return; - } - e.stopPropagation(); - e.preventDefault(); - const files = e.dataTransfer.files; - dropReplaceImage(imgWrap, files); +window.document.addEventListener('drop', e => { + const target = e.composedPath()[0]; + if (!eventHasFiles(e)) return; + if (dragDropTargetIsPrompt(target)) { + e.stopPropagation(); + e.preventDefault(); + + let prompt_target = get_tab_index('tabs') == 1 ? "img2img_prompt_image" : "txt2img_prompt_image"; + + const imgParent = gradioApp().getElementById(prompt_target); + const files = e.dataTransfer.files; + const fileInput = imgParent.querySelector('input[type="file"]'); + if (fileInput) { + fileInput.files = files; + fileInput.dispatchEvent(new Event('change')); + } + } + + var targetImage = target.closest('[data-testid="image"]'); + if (targetImage) { + e.stopPropagation(); + e.preventDefault(); + const files = e.dataTransfer.files; + dropReplaceImage(targetImage, files); + return; + } }); -window.addEventListener("paste", (e) => { - const files = e.clipboardData.files; - if (!isValidImageList(files)) { - return; - } +window.addEventListener('paste', e => { + const files = e.clipboardData.files; + if (!isValidImageList(files)) { + return; + } - const visibleImageFields = [ - ...gradioApp().querySelectorAll('[data-testid="image"]'), - ].filter((el) => uiElementIsVisible(el)); - if (!visibleImageFields.length) { - return; - } + const visibleImageFields = [...gradioApp().querySelectorAll('[data-testid="image"]')] + .filter(el => uiElementIsVisible(el)) + .sort((a, b) => uiElementInSight(b) - uiElementInSight(a)); - const firstFreeImageField = visibleImageFields.filter((el) => - el.querySelector("input[type=file]") - )?.[0]; - dropReplaceImage( - firstFreeImageField - ? firstFreeImageField - : visibleImageFields[visibleImageFields.length - 1], - files - ); + if (!visibleImageFields.length) { + return; + } + + const firstFreeImageField = visibleImageFields + .filter(el => el.querySelector('input[type=file]'))?.[0]; + + dropReplaceImage( + firstFreeImageField ? + firstFreeImageField : + visibleImageFields[visibleImageFields.length - 1] + , files + ); }); diff --git a/javascript/extensions.js b/javascript/extensions.js index 29185b4e..312131b7 100644 --- a/javascript/extensions.js +++ b/javascript/extensions.js @@ -1,3 +1,4 @@ + function extensions_apply(_disabled_list, _update_list, disable_all) { var disable = []; var update = []; @@ -32,7 +33,7 @@ function extensions_check() { var id = randomId(); - requestProgress(id, gradioApp().getElementById('extensions_installed_top'), null, function() { + requestProgress(id, gradioApp().getElementById('extensions_installed_html'), null, function() { }); diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 9a260fba..493f31af 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -1,579 +1,349 @@ -function setupExtraNetworksForTab(tabname) { - gradioApp() - .querySelector("#" + tabname + "_extra_tabs") - .classList.add("extra-networks"); - - let tabs = gradioApp().querySelector("#" + tabname + "_extra_tabs > div"); - let search = gradioApp().querySelector("#" + tabname + "_extra_search textarea" ); - let refresh = gradioApp().getElementById(tabname + "_extra_refresh"); - - //let sort = gradioApp().getElementById(tabname + '_extra_sort'); - //let sortOrder = gradioApp().getElementById(tabname + '_extra_sortorder'); - - let clear = document.createElement("div"); - clear.id = tabname + "_extra_clear"; - clear.classList.add("token-remove", "remove-all", "svelte-a6vu2r", "hide"); - clear.title = "Clear search"; - clear.innerHTML = - ''; - - search.classList.add("search"); - - tabs.appendChild(search); - tabs.appendChild(clear); - tabs.appendChild(refresh); - -/* - sort.classList.add('sort'); - sortOrder.classList.add('sortorder'); - sort.dataset.sortkey = 'sortDefault'; - tabs.appendChild(sort); - tabs.appendChild(sortOrder); */ - - clear.addEventListener("click", function (evt) { - search.value = ""; - updateInput(search); - }); - - setTimeout(function () { - refresh.click(); - }, 500); - - - setTimeout(extraNetworksApplyFilter[tabname], 1); - - search.addEventListener("input", function (evt) { - searchTerm = search.value.toLowerCase(); - - gradioApp() - .querySelectorAll("#" + tabname + "_extra_tabs div.card") - .forEach(function (elem) { - let text = - elem.querySelector(".name").textContent.toLowerCase() + - " " + - elem.querySelector(".search_term").textContent.toLowerCase(); - elem.parentElement.style.display = - text.indexOf(searchTerm) == -1 ? "none" : ""; - }); - }); -} - -function applyExtraNetworkFilter(tabname){ - setTimeout(extraNetworksApplyFilter[tabname], 1); -} - -var extraNetworksApplyFilter = {} -var activePromptTextarea = {}; - -function setupExtraNetworks() { - setupExtraNetworksForTab("txt2img"); - setupExtraNetworksForTab("img2img"); - - function registerPrompt(tabname, id) { - var textarea = gradioApp().querySelector("#" + id + " > label > textarea"); - - if (!activePromptTextarea[tabname]) { - activePromptTextarea[tabname] = textarea; - } - - textarea.addEventListener("focus", function () { - activePromptTextarea[tabname] = textarea; - }); - } - - registerPrompt("txt2img", "txt2img_prompt"); - registerPrompt("txt2img", "txt2img_neg_prompt"); - registerPrompt("img2img", "img2img_prompt"); - registerPrompt("img2img", "img2img_neg_prompt"); -} - -onUiLoaded(setupExtraNetworks); - -var re_extranet = /<([^:]+:[^:]+):[\d\.]+>/; -var re_extranet_g = /\s+<([^:]+:[^:]+):[\d\.]+>/g; - -function tryToRemoveExtraNetworkFromPrompt(textarea, text) { - var m = text.match(re_extranet); - if (!m) return false; - - var partToSearch = m[1]; - var replaced = false; - var newTextareaText = textarea.value.replaceAll( - re_extranet_g, - function (found) { - m = found.match(re_extranet); - if (m[1] == partToSearch) { - replaced = true; - return ""; - } - return found; - } - ); - - if (replaced) { - textarea.value = newTextareaText; - return true; - } - - return false; -} - -function cardClicked(tabname, textToAdd, allowNegativePrompt) { - var textarea = allowNegativePrompt - ? activePromptTextarea[tabname] - : gradioApp().querySelector("#" + tabname + "_prompt > label > textarea"); - - if (!tryToRemoveExtraNetworkFromPrompt(textarea, textToAdd)) { - textarea.value = - textarea.value + opts.extra_networks_add_text_separator + textToAdd; - } - - updateInput(textarea); -} - -function saveCardPreview(event, tabname, filename) { - var textarea = gradioApp().querySelector( - "#" + tabname + "_preview_filename > label > textarea" - ); - var button = gradioApp().getElementById(tabname + "_save_preview"); - - textarea.value = filename; - updateInput(textarea); - - button.click(); - - var search = gradioApp().querySelector( - "#" + tabname + "_extra_tabs textarea" - ); - - setTimeout(function () { - search.value = search.value.toLowerCase(); - updateInput(search); - }, 1000); - - event.stopPropagation(); - event.preventDefault(); -} - -function extraNetworksSearchButton(tabs_id, event) { - var searchTextarea = gradioApp().querySelector( - "#" + tabs_id + " > div > textarea" - ); - var button = event.target; - var text = button.classList.contains("search-all") - ? "" - : button.textContent.trim(); - //text = event.target.selectedIndex == 0 ? "" : event.target.options[event.target.selectedIndex].text; - - searchTextarea.value = text; - updateInput(searchTextarea); -} - -var globalPopup = null; -var globalPopupInner = null; -function popup(contents) { - if (!globalPopup) { - globalPopup = document.createElement("div"); - globalPopup.onclick = function () { - globalPopup.style.display = "none"; - }; - globalPopup.classList.add("global-popup"); - - var close = document.createElement("div"); - close.classList.add("global-popup-close"); - close.onclick = function () { - globalPopup.style.display = "none"; - }; - close.title = "Close"; - globalPopup.appendChild(close); - - globalPopupInner = document.createElement("div"); - globalPopupInner.onclick = function (event) { - event.stopPropagation(); - return false; - }; - globalPopupInner.classList.add("global-popup-inner"); - globalPopup.appendChild(globalPopupInner); - - gradioApp().appendChild(globalPopup); - } - - globalPopupInner.innerHTML = ""; - globalPopupInner.appendChild(contents); - - globalPopup.style.display = "flex"; -} - -function extraNetworksShowMetadata(text) { - var elem = document.createElement("pre"); - elem.classList.add("popup-metadata"); - elem.textContent = text; - - popup(elem); -} - -function requestGet(url, data, handler, errorHandler) { - var xhr = new XMLHttpRequest(); - var args = Object.keys(data) - .map(function (k) { - return encodeURIComponent(k) + "=" + encodeURIComponent(data[k]); - }) - .join("&"); - xhr.open("GET", url + "?" + args, true); - - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - if (xhr.status === 200) { - try { - var js = JSON.parse(xhr.responseText); - handler(js); - } catch (error) { - console.error(error); - errorHandler(); - } - } else { - errorHandler(); - } - } - }; - var js = JSON.stringify(data); - xhr.send(js); -} - -function extraNetworksRequestMetadata(event, extraPage, cardName) { - var showError = function () { - extraNetworksShowMetadata("there was an error getting metadata"); - }; - - requestGet( - "./sd_extra_networks/metadata", - { page: extraPage, item: cardName }, - function (data) { - if (data && data.metadata) { - extraNetworksShowMetadata(data.metadata); - } else { - showError(); - } - }, - showError - ); - - event.stopPropagation(); -} - - -/* -function setupExtraNetworksForTab(tabname) { - gradioApp().querySelector('#' + tabname + '_extra_tabs').classList.add('extra-networks'); - - var tabs = gradioApp().querySelector('#' + tabname + '_extra_tabs > div'); - var search = gradioApp().querySelector('#' + tabname + '_extra_search textarea'); - var sort = gradioApp().getElementById(tabname + '_extra_sort'); - var sortOrder = gradioApp().getElementById(tabname + '_extra_sortorder'); - var refresh = gradioApp().getElementById(tabname + '_extra_refresh'); - - search.classList.add('search'); - sort.classList.add('sort'); - sortOrder.classList.add('sortorder'); - sort.dataset.sortkey = 'sortDefault'; - tabs.appendChild(search); - tabs.appendChild(sort); - tabs.appendChild(sortOrder); - tabs.appendChild(refresh); - - var applyFilter = function() { - var searchTerm = search.value.toLowerCase(); - - gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card').forEach(function(elem) { - var searchOnly = elem.querySelector('.search_only'); - var text = elem.querySelector('.name').textContent.toLowerCase() + " " + elem.querySelector('.search_term').textContent.toLowerCase(); - - var visible = text.indexOf(searchTerm) != -1; - - if (searchOnly && searchTerm.length < 4) { - visible = false; - } - - elem.style.display = visible ? "" : "none"; - }); - }; - - var applySort = function() { - var reverse = sortOrder.classList.contains("sortReverse"); - var sortKey = sort.querySelector("input").value.toLowerCase().replace("sort", "").replaceAll(" ", "_").replace(/_+$/, "").trim(); - sortKey = sortKey ? "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1) : ""; - var sortKeyStore = sortKey ? sortKey + (reverse ? "Reverse" : "") : ""; - if (!sortKey || sortKeyStore == sort.dataset.sortkey) { - return; - } - - sort.dataset.sortkey = sortKeyStore; - - var cards = gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card'); - cards.forEach(function(card) { - card.originalParentElement = card.parentElement; - }); - var sortedCards = Array.from(cards); - sortedCards.sort(function(cardA, cardB) { - var a = cardA.dataset[sortKey]; - var b = cardB.dataset[sortKey]; - if (!isNaN(a) && !isNaN(b)) { - return parseInt(a) - parseInt(b); - } - - return (a < b ? -1 : (a > b ? 1 : 0)); - }); - if (reverse) { - sortedCards.reverse(); - } - cards.forEach(function(card) { - card.remove(); - }); - sortedCards.forEach(function(card) { - card.originalParentElement.appendChild(card); - }); - }; - - search.addEventListener("input", applyFilter); - applyFilter(); - ["change", "blur", "click"].forEach(function(evt) { - sort.querySelector("input").addEventListener(evt, applySort); - }); - sortOrder.addEventListener("click", function() { - sortOrder.classList.toggle("sortReverse"); - applySort(); - }); - - extraNetworksApplyFilter[tabname] = applyFilter; -} - -function applyExtraNetworkFilter(tabname) { - setTimeout(extraNetworksApplyFilter[tabname], 1); -} - -var extraNetworksApplyFilter = {}; -var activePromptTextarea = {}; - -function setupExtraNetworks() { - setupExtraNetworksForTab('txt2img'); - setupExtraNetworksForTab('img2img'); - - function registerPrompt(tabname, id) { - var textarea = gradioApp().querySelector("#" + id + " > label > textarea"); - - if (!activePromptTextarea[tabname]) { - activePromptTextarea[tabname] = textarea; - } - - textarea.addEventListener("focus", function() { - activePromptTextarea[tabname] = textarea; - }); - } - - registerPrompt('txt2img', 'txt2img_prompt'); - registerPrompt('txt2img', 'txt2img_neg_prompt'); - registerPrompt('img2img', 'img2img_prompt'); - registerPrompt('img2img', 'img2img_neg_prompt'); -} - -onUiLoaded(setupExtraNetworks); - -var re_extranet = /<([^:]+:[^:]+):[\d.]+>(.*)/; -var re_extranet_g = /\s+<([^:]+:[^:]+):[\d.]+>/g; - -function tryToRemoveExtraNetworkFromPrompt(textarea, text) { - var m = text.match(re_extranet); - var replaced = false; - var newTextareaText; - if (m) { - var extraTextAfterNet = m[2]; - var partToSearch = m[1]; - var foundAtPosition = -1; - newTextareaText = textarea.value.replaceAll(re_extranet_g, function(found, net, pos) { - m = found.match(re_extranet); - if (m[1] == partToSearch) { - replaced = true; - foundAtPosition = pos; - return ""; - } - return found; - }); - - if (foundAtPosition >= 0 && newTextareaText.substr(foundAtPosition, extraTextAfterNet.length) == extraTextAfterNet) { - newTextareaText = newTextareaText.substr(0, foundAtPosition) + newTextareaText.substr(foundAtPosition + extraTextAfterNet.length); - } - } else { - newTextareaText = textarea.value.replaceAll(new RegExp(text, "g"), function(found) { - if (found == text) { - replaced = true; - return ""; - } - return found; - }); - } - - if (replaced) { - textarea.value = newTextareaText; - return true; - } - - return false; -} - -function cardClicked(tabname, textToAdd, allowNegativePrompt) { - var textarea = allowNegativePrompt ? activePromptTextarea[tabname] : gradioApp().querySelector("#" + tabname + "_prompt > label > textarea"); - - if (!tryToRemoveExtraNetworkFromPrompt(textarea, textToAdd)) { - textarea.value = textarea.value + opts.extra_networks_add_text_separator + textToAdd; - } - - updateInput(textarea); -} - -function saveCardPreview(event, tabname, filename) { - var textarea = gradioApp().querySelector("#" + tabname + '_preview_filename > label > textarea'); - var button = gradioApp().getElementById(tabname + '_save_preview'); - - textarea.value = filename; - updateInput(textarea); - - button.click(); - - event.stopPropagation(); - event.preventDefault(); -} - -function extraNetworksSearchButton(tabs_id, event) { - var searchTextarea = gradioApp().querySelector("#" + tabs_id + ' > div > textarea'); - var button = event.target; - var text = button.classList.contains("search-all") ? "" : button.textContent.trim(); - - searchTextarea.value = text; - updateInput(searchTextarea); -} - -var globalPopup = null; -var globalPopupInner = null; -function closePopup() { - if (!globalPopup) return; - - globalPopup.style.display = "none"; -} -function popup(contents) { - if (!globalPopup) { - globalPopup = document.createElement('div'); - globalPopup.onclick = closePopup; - globalPopup.classList.add('global-popup'); - - var close = document.createElement('div'); - close.classList.add('global-popup-close'); - close.onclick = closePopup; - close.title = "Close"; - globalPopup.appendChild(close); - - globalPopupInner = document.createElement('div'); - globalPopupInner.onclick = function(event) { - event.stopPropagation(); return false; - }; - globalPopupInner.classList.add('global-popup-inner'); - globalPopup.appendChild(globalPopupInner); - - gradioApp().querySelector('.main').appendChild(globalPopup); - } - - globalPopupInner.innerHTML = ''; - globalPopupInner.appendChild(contents); - - globalPopup.style.display = "flex"; -} - -function extraNetworksShowMetadata(text) { - var elem = document.createElement('pre'); - elem.classList.add('popup-metadata'); - elem.textContent = text; - - popup(elem); -} - -function requestGet(url, data, handler, errorHandler) { - var xhr = new XMLHttpRequest(); - var args = Object.keys(data).map(function(k) { - return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]); - }).join('&'); - xhr.open("GET", url + "?" + args, true); - - xhr.onreadystatechange = function() { - if (xhr.readyState === 4) { - if (xhr.status === 200) { - try { - var js = JSON.parse(xhr.responseText); - handler(js); - } catch (error) { - console.error(error); - errorHandler(); - } - } else { - errorHandler(); - } - } - }; - var js = JSON.stringify(data); - xhr.send(js); -} - -function extraNetworksRequestMetadata(event, extraPage, cardName) { - var showError = function() { - extraNetworksShowMetadata("there was an error getting metadata"); - }; - - requestGet("./sd_extra_networks/metadata", {page: extraPage, item: cardName}, function(data) { - if (data && data.metadata) { - extraNetworksShowMetadata(data.metadata); - } else { - showError(); - } - }, showError); - - event.stopPropagation(); -} - -var extraPageUserMetadataEditors = {}; - -function extraNetworksEditUserMetadata(event, tabname, extraPage, cardName) { - var id = tabname + '_' + extraPage + '_edit_user_metadata'; - - var editor = extraPageUserMetadataEditors[id]; - if (!editor) { - editor = {}; - editor.page = gradioApp().getElementById(id); - editor.nameTextarea = gradioApp().querySelector("#" + id + "_name" + ' textarea'); - editor.button = gradioApp().querySelector("#" + id + "_button"); - extraPageUserMetadataEditors[id] = editor; - } - - editor.nameTextarea.value = cardName; - updateInput(editor.nameTextarea); - - editor.button.click(); - - popup(editor.page); - - event.stopPropagation(); -} - -function extraNetworksRefreshSingleCard(page, tabname, name) { - requestGet("./sd_extra_networks/get-single-card", {page: page, tabname: tabname, name: name}, function(data) { - if (data && data.html) { - var card = gradioApp().querySelector('.card[data-name=' + JSON.stringify(name) + ']'); // likely using the wrong stringify function - - var newDiv = document.createElement('DIV'); - newDiv.innerHTML = data.html; - var newCard = newDiv.firstElementChild; - - newCard.style = ''; - card.parentElement.insertBefore(newCard, card); - card.parentElement.removeChild(card); - } - }); -} -*/ \ No newline at end of file +function toggleCss(key, css, enable) { + var style = document.getElementById(key); + if (enable && !style) { + style = document.createElement('style'); + style.id = key; + style.type = 'text/css'; + document.head.appendChild(style); + } + if (style && !enable) { + document.head.removeChild(style); + } + if (style) { + style.innerHTML == ''; + style.appendChild(document.createTextNode(css)); + } +} + +function setupExtraNetworksForTab(tabname) { + gradioApp().querySelector('#' + tabname + '_extra_tabs').classList.add('extra-networks'); + + var tabs = gradioApp().querySelector('#' + tabname + '_extra_tabs > div'); + var searchDiv = gradioApp().getElementById(tabname + '_extra_search'); + var search = searchDiv.querySelector('textarea'); + var sort = gradioApp().getElementById(tabname + '_extra_sort'); + var sortOrder = gradioApp().getElementById(tabname + '_extra_sortorder'); + var refresh = gradioApp().getElementById(tabname + '_extra_refresh'); + var showDirsDiv = gradioApp().getElementById(tabname + '_extra_show_dirs'); + var showDirs = gradioApp().querySelector('#' + tabname + '_extra_show_dirs input'); + + sort.dataset.sortkey = 'sortDefault'; + tabs.appendChild(searchDiv); + tabs.appendChild(sort); + tabs.appendChild(sortOrder); + tabs.appendChild(refresh); + tabs.appendChild(showDirsDiv); + + var applyFilter = function() { + var searchTerm = search.value.toLowerCase(); + + gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card').forEach(function(elem) { + var searchOnly = elem.querySelector('.search_only'); + var text = elem.querySelector('.name').textContent.toLowerCase() + " " + elem.querySelector('.search_term').textContent.toLowerCase(); + + var visible = text.indexOf(searchTerm) != -1; + + if (searchOnly && searchTerm.length < 4) { + visible = false; + } + + elem.style.display = visible ? "" : "none"; + }); + }; + + var applySort = function() { + var reverse = sortOrder.classList.contains("sortReverse"); + var sortKey = sort.querySelector("input").value.toLowerCase().replace("sort", "").replaceAll(" ", "_").replace(/_+$/, "").trim(); + sortKey = sortKey ? "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1) : ""; + var sortKeyStore = sortKey ? sortKey + (reverse ? "Reverse" : "") : ""; + if (!sortKey || sortKeyStore == sort.dataset.sortkey) { + return; + } + + sort.dataset.sortkey = sortKeyStore; + + var cards = gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card'); + cards.forEach(function(card) { + card.originalParentElement = card.parentElement; + }); + var sortedCards = Array.from(cards); + sortedCards.sort(function(cardA, cardB) { + var a = cardA.dataset[sortKey]; + var b = cardB.dataset[sortKey]; + if (!isNaN(a) && !isNaN(b)) { + return parseInt(a) - parseInt(b); + } + + return (a < b ? -1 : (a > b ? 1 : 0)); + }); + if (reverse) { + sortedCards.reverse(); + } + cards.forEach(function(card) { + card.remove(); + }); + sortedCards.forEach(function(card) { + card.originalParentElement.appendChild(card); + }); + }; + + search.addEventListener("input", applyFilter); + applyFilter(); + ["change", "blur", "click"].forEach(function(evt) { + sort.querySelector("input").addEventListener(evt, applySort); + }); + sortOrder.addEventListener("click", function() { + sortOrder.classList.toggle("sortReverse"); + applySort(); + }); + + extraNetworksApplyFilter[tabname] = applyFilter; + + var showDirsUpdate = function() { + var css = '#' + tabname + '_extra_tabs .extra-network-subdirs { display: none; }'; + toggleCss(tabname + '_extra_show_dirs_style', css, !showDirs.checked); + localSet('extra-networks-show-dirs', showDirs.checked ? 1 : 0); + }; + showDirs.checked = localGet('extra-networks-show-dirs', 1) == 1; + showDirs.addEventListener("change", showDirsUpdate); + showDirsUpdate(); +} + +function applyExtraNetworkFilter(tabname) { + setTimeout(extraNetworksApplyFilter[tabname], 1); +} + +var extraNetworksApplyFilter = {}; +var activePromptTextarea = {}; + +function setupExtraNetworks() { + setupExtraNetworksForTab('txt2img'); + setupExtraNetworksForTab('img2img'); + + function registerPrompt(tabname, id) { + var textarea = gradioApp().querySelector("#" + id + " > label > textarea"); + + if (!activePromptTextarea[tabname]) { + activePromptTextarea[tabname] = textarea; + } + + textarea.addEventListener("focus", function() { + activePromptTextarea[tabname] = textarea; + }); + } + + registerPrompt('txt2img', 'txt2img_prompt'); + registerPrompt('txt2img', 'txt2img_neg_prompt'); + registerPrompt('img2img', 'img2img_prompt'); + registerPrompt('img2img', 'img2img_neg_prompt'); +} + +onUiLoaded(setupExtraNetworks); + +var re_extranet = /<([^:]+:[^:]+):[\d.]+>(.*)/; +var re_extranet_g = /\s+<([^:]+:[^:]+):[\d.]+>/g; + +function tryToRemoveExtraNetworkFromPrompt(textarea, text) { + var m = text.match(re_extranet); + var replaced = false; + var newTextareaText; + if (m) { + var extraTextAfterNet = m[2]; + var partToSearch = m[1]; + var foundAtPosition = -1; + newTextareaText = textarea.value.replaceAll(re_extranet_g, function(found, net, pos) { + m = found.match(re_extranet); + if (m[1] == partToSearch) { + replaced = true; + foundAtPosition = pos; + return ""; + } + return found; + }); + + if (foundAtPosition >= 0 && newTextareaText.substr(foundAtPosition, extraTextAfterNet.length) == extraTextAfterNet) { + newTextareaText = newTextareaText.substr(0, foundAtPosition) + newTextareaText.substr(foundAtPosition + extraTextAfterNet.length); + } + } else { + newTextareaText = textarea.value.replaceAll(new RegExp(text, "g"), function(found) { + if (found == text) { + replaced = true; + return ""; + } + return found; + }); + } + + if (replaced) { + textarea.value = newTextareaText; + return true; + } + + return false; +} + +function cardClicked(tabname, textToAdd, allowNegativePrompt) { + var textarea = allowNegativePrompt ? activePromptTextarea[tabname] : gradioApp().querySelector("#" + tabname + "_prompt > label > textarea"); + + if (!tryToRemoveExtraNetworkFromPrompt(textarea, textToAdd)) { + textarea.value = textarea.value + opts.extra_networks_add_text_separator + textToAdd; + } + + updateInput(textarea); +} + +function saveCardPreview(event, tabname, filename) { + var textarea = gradioApp().querySelector("#" + tabname + '_preview_filename > label > textarea'); + var button = gradioApp().getElementById(tabname + '_save_preview'); + + textarea.value = filename; + updateInput(textarea); + + button.click(); + + event.stopPropagation(); + event.preventDefault(); +} + +function extraNetworksSearchButton(tabs_id, event) { + var searchTextarea = gradioApp().querySelector("#" + tabs_id + ' > label > textarea'); + var button = event.target; + var text = button.classList.contains("search-all") ? "" : button.textContent.trim(); + + searchTextarea.value = text; + updateInput(searchTextarea); +} + +var globalPopup = null; +var globalPopupInner = null; +function closePopup() { + if (!globalPopup) return; + + globalPopup.style.display = "none"; +} +function popup(contents) { + if (!globalPopup) { + globalPopup = document.createElement('div'); + globalPopup.onclick = closePopup; + globalPopup.classList.add('global-popup'); + + var close = document.createElement('div'); + close.classList.add('global-popup-close'); + close.onclick = closePopup; + close.title = "Close"; + globalPopup.appendChild(close); + + globalPopupInner = document.createElement('div'); + globalPopupInner.onclick = function(event) { + event.stopPropagation(); return false; + }; + globalPopupInner.classList.add('global-popup-inner'); + globalPopup.appendChild(globalPopupInner); + + gradioApp().querySelector('.main').appendChild(globalPopup); + } + + globalPopupInner.innerHTML = ''; + globalPopupInner.appendChild(contents); + + globalPopup.style.display = "flex"; +} + +var storedPopupIds = {}; +function popupId(id) { + if (!storedPopupIds[id]) { + storedPopupIds[id] = gradioApp().getElementById(id); + } + + popup(storedPopupIds[id]); +} + +function extraNetworksShowMetadata(text) { + var elem = document.createElement('pre'); + elem.classList.add('popup-metadata'); + elem.textContent = text; + + popup(elem); +} + +function requestGet(url, data, handler, errorHandler) { + var xhr = new XMLHttpRequest(); + var args = Object.keys(data).map(function(k) { + return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]); + }).join('&'); + xhr.open("GET", url + "?" + args, true); + + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + try { + var js = JSON.parse(xhr.responseText); + handler(js); + } catch (error) { + console.error(error); + errorHandler(); + } + } else { + errorHandler(); + } + } + }; + var js = JSON.stringify(data); + xhr.send(js); +} + +function extraNetworksRequestMetadata(event, extraPage, cardName) { + var showError = function() { + extraNetworksShowMetadata("there was an error getting metadata"); + }; + + requestGet("./sd_extra_networks/metadata", {page: extraPage, item: cardName}, function(data) { + if (data && data.metadata) { + extraNetworksShowMetadata(data.metadata); + } else { + showError(); + } + }, showError); + + event.stopPropagation(); +} + +var extraPageUserMetadataEditors = {}; + +function extraNetworksEditUserMetadata(event, tabname, extraPage, cardName) { + var id = tabname + '_' + extraPage + '_edit_user_metadata'; + + var editor = extraPageUserMetadataEditors[id]; + if (!editor) { + editor = {}; + editor.page = gradioApp().getElementById(id); + editor.nameTextarea = gradioApp().querySelector("#" + id + "_name" + ' textarea'); + editor.button = gradioApp().querySelector("#" + id + "_button"); + extraPageUserMetadataEditors[id] = editor; + } + + editor.nameTextarea.value = cardName; + updateInput(editor.nameTextarea); + + editor.button.click(); + + popup(editor.page); + + event.stopPropagation(); +} + +function extraNetworksRefreshSingleCard(page, tabname, name) { + requestGet("./sd_extra_networks/get-single-card", {page: page, tabname: tabname, name: name}, function(data) { + if (data && data.html) { + var card = gradioApp().querySelector('.card[data-name=' + JSON.stringify(name) + ']'); // likely using the wrong stringify function + + var newDiv = document.createElement('DIV'); + newDiv.innerHTML = data.html; + var newCard = newDiv.firstElementChild; + + newCard.style.display = ''; + card.parentElement.insertBefore(newCard, card); + card.parentElement.removeChild(card); + } + }); +} diff --git a/javascript/generationParams.js b/javascript/generationParams.js index 35ccd716..7c0fd221 100644 --- a/javascript/generationParams.js +++ b/javascript/generationParams.js @@ -1,4 +1,5 @@ // attaches listeners to the txt2img and img2img galleries to update displayed generation param text when the image changes + let txt2img_gallery, img2img_gallery, modal = undefined; onAfterUiUpdate(function() { if (!txt2img_gallery) { @@ -7,6 +8,10 @@ onAfterUiUpdate(function() { if (!img2img_gallery) { img2img_gallery = attachGalleryListeners("img2img"); } + if (!modal) { + modal = gradioApp().getElementById('lightboxModal'); + modalObserver.observe(modal, {attributes: true, attributeFilter: ['style']}); + } }); let modalObserver = new MutationObserver(function(mutations) { diff --git a/javascript/hints.js b/javascript/hints.js index f3dd9019..6de9372e 100644 --- a/javascript/hints.js +++ b/javascript/hints.js @@ -23,12 +23,7 @@ var titles = { "\u{1f4cb}": "Apply selected styles to current prompt", "\u{1f4d2}": "Paste available values into the field", "\u{1f3b4}": "Show/hide extra networks", - - "\u{1f5e8}": "Interogate Clip", - "\u{1f5ea}": "Interogate Deepbooru", - "\u{1F300}": "Restore progress", - "\u{267E}": "Toggle generate forever", - + "\u{1f300}": "Restore progress", "Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt", "SD upscale": "Upscale image normally, split result into tiles, improve each tile using img2img, merge whole image back", @@ -50,11 +45,6 @@ var titles = { "Skip": "Stop processing current image and continue processing.", "Interrupt": "Stop processing images and return any results accumulated so far.", "Save": "Write image to a directory (default - log/images) and generation parameters into csv file.", - "Zip": "Save as Zip", - "Send to txt2img": "Send to txt2img", - "Send to img2img": "Send to img2img", - "Send to inpaint": "Send to inpaint", - "Send to extras": "Send to extras", "X values": "Separate values for X axis using commas.", "Y values": "Separate values for Y axis using commas.", @@ -200,3 +190,14 @@ onUiUpdate(function(mutationRecords) { tooltipCheckTimer = setTimeout(processTooltipCheckNodes, 1000); } }); + +onUiLoaded(function() { + for (var comp of window.gradio_config.components) { + if (comp.props.webui_tooltip && comp.props.elem_id) { + var elem = gradioApp().getElementById(comp.props.elem_id); + if (elem) { + elem.title = comp.props.webui_tooltip; + } + } + } +}); diff --git a/javascript/hires_fix.js b/javascript/hires_fix.js index 2c111bd8..0d04ab3b 100644 --- a/javascript/hires_fix.js +++ b/javascript/hires_fix.js @@ -1,19 +1,18 @@ -function onCalcResolutionHires(enable, width, height, hr_scale, hr_resize_x, hr_resize_y) { - function setInactive(elem, inactive) { - elem.classList.toggle('inactive', !!inactive); - } - - var hrUpscaleBy = gradioApp().getElementById('txt2img_hr_scale'); - var hrResizeX = gradioApp().getElementById('txt2img_hr_resize_x'); - var hrResizeY = gradioApp().getElementById('txt2img_hr_resize_y'); - - gradioApp().getElementById('txt2img_hires_fix_row2').style.display = opts.use_old_hires_fix_width_height ? "none" : "block"; - - setInactive(hrUpscaleBy, opts.use_old_hires_fix_width_height || hr_resize_x > 0 || hr_resize_y > 0); - setInactive(hrResizeX, opts.use_old_hires_fix_width_height || hr_resize_x == 0); - setInactive(hrResizeY, opts.use_old_hires_fix_width_height || hr_resize_y == 0); - - setTimeout(function () { - return [enable, width, height, hr_scale, hr_resize_x, hr_resize_y]; - }, 100); -} + +function onCalcResolutionHires(enable, width, height, hr_scale, hr_resize_x, hr_resize_y) { + function setInactive(elem, inactive) { + elem.classList.toggle('inactive', !!inactive); + } + + var hrUpscaleBy = gradioApp().getElementById('txt2img_hr_scale'); + var hrResizeX = gradioApp().getElementById('txt2img_hr_resize_x'); + var hrResizeY = gradioApp().getElementById('txt2img_hr_resize_y'); + + gradioApp().getElementById('txt2img_hires_fix_row2').style.display = opts.use_old_hires_fix_width_height ? "none" : ""; + + setInactive(hrUpscaleBy, opts.use_old_hires_fix_width_height || hr_resize_x > 0 || hr_resize_y > 0); + setInactive(hrResizeX, opts.use_old_hires_fix_width_height || hr_resize_x == 0); + setInactive(hrResizeY, opts.use_old_hires_fix_width_height || hr_resize_y == 0); + + return [enable, width, height, hr_scale, hr_resize_x, hr_resize_y]; +} diff --git a/javascript/imageMaskFix.js b/javascript/imageMaskFix.js index d1a07807..900c56f3 100644 --- a/javascript/imageMaskFix.js +++ b/javascript/imageMaskFix.js @@ -2,10 +2,6 @@ * temporary fix for https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/668 * @see https://github.com/gradio-app/gradio/issues/1721 */ - -/* -window.addEventListener( 'resize', () => imageMaskResize()); - function imageMaskResize() { const canvases = gradioApp().querySelectorAll('#img2maskimg .touch-none canvas'); if (!canvases.length) { @@ -41,299 +37,7 @@ function imageMaskResize() { c.style.maxHeight = '100%'; c.style.objectFit = 'contain'; }); - } - - onUiUpdate(() => imageMaskResize()); -*/ - -onUiLoaded(function () { - const color_box = ''; - const brush_size = ''; - let is_drawing = false; - let img_src = []; - let spl_instances = []; - let spl_pan_instances = []; - let img2img_tab_index = 0; - let intervalLastUIUpdate; - const container = gradioApp().querySelector(".gradio-container"); - const observer = new MutationObserver(() => - gradioApp() - .querySelectorAll('div[data-testid="image"]') - .forEach(function (elem, i) { - let img_parent = elem.parentElement.querySelector( - 'div[data-testid="image"] > div' - ); - let img = img_parent.querySelector("img"); - if (img) { - if (img_src[i] != img.src) { - let tool_buttons = img_parent.querySelectorAll("button"); - //console.log(tool_buttons); - if (tool_buttons.length > 2) { - img_parent.style.visibility = "hidden"; - - if (intervalLastUIUpdate != null) - clearInterval(intervalLastUIUpdate); - - intervalLastUIUpdate = setInterval(function () { - clearInterval(intervalLastUIUpdate); - img_parent.addEventListener("mouseup", function (e) {}); - - let spl_parent = elem.parentElement; - let spl; - let spl_pan; - let isPanning; - let splid; - - if (spl_parent.className != "spl-pane") { - spl = new Spotlight(); - spl.init(spl_parent, "-" + spl_parent.id); - - spl.addControl("undo", spl_undo_handler); - spl_pan = spl.addControl("pan", spl_pan_handler); - spl.addControl("brush", spl_brush_handler, brush_size); - if (tool_buttons.length == 5) { - spl.addControl("color", spl_color_handler, color_box); - } - spl.addControl("clear", spl_clear_handler); - - spl_instances[i] = spl; - spl_pan_instances[i] = spl_pan; - } else { - spl = spl_instances[i]; - spl_pan = spl_pan_instances[i]; - } - - img_src[i] = img.src; - //console.log("NEWIMAGE"); - - function spl_undo_handler(e) { - tool_buttons[0].click(); - } - - function spl_clear_handler(e) { - tool_buttons[2].click(); - spl.panzoom(false); - img_parent.classList.remove("no-point-events"); - img_parent.parentElement.classList.remove("move"); - document.removeEventListener("wheel", preventDefault, false); - setTimeout(function () { - spl.close(false, true); - img_parent.style.flexGrow = "1"; - img_src[i] = ""; - elem.style.transform = "none"; - }, 500); - } - - function spl_color_handler(e) {} - function spl_brush_handler(e) {} - - function preventDefault(e) { - e = e || window.event; - if (e.preventDefault) { - e.preventDefault(); - } - e.returnValue = false; - } - - function pan_toggle(val, target) { - isPanning = val; - target.classList.toggle("on", isPanning); - spl.panzoom(val); - - if (isPanning) { - img_parent.classList.add("no-point-events"); - img_parent.parentElement.classList.add("move"); - document.addEventListener("wheel", preventDefault, { - passive: false, - }); - } else { - img_parent.classList.remove("no-point-events"); - img_parent.parentElement.classList.remove("move"); - document.removeEventListener( - "wheel", - preventDefault, - false - ); - } - } - - function spl_pan_handler(e) { - isPanning = !isPanning; - pan_toggle(isPanning, this); - } - - function update_color(listener) { - let input_color = img_parent.querySelector( - "input[type='color']" - ); - let spl = - img_parent.parentElement.parentElement.parentElement - .parentElement.parentElement; - let spl_color = spl.querySelector( - ".spl-color input[type='color']" - ); - spl_color.value = input_color.value; - - if (listener) { - spl_color.addEventListener("input", function (ev) { - input_color.value = ev.target.value; - updateInput(input_color); - pan_toggle(false, spl_pan); - }); - } - } - - function update_brush(listener) { - let input_range = img_parent.querySelector( - "input[type='range']" - ); - let spl = - img_parent.parentElement.parentElement.parentElement - .parentElement.parentElement; - let spl_brush = spl.querySelector( - ".spl-brush input[type='range']" - ); - spl_brush.value = input_range.value; - - if (listener) { - spl_brush.addEventListener("input", function (ev) { - input_range.value = ev.target.value; - updateInput(input_range); - }); - } - } - - function init_drawing_tools() { - let input_color = img_parent.querySelector( - "input[type='color']" - ); - if (!input_color) { - let tbcolor = img_parent.querySelector( - "button[aria-label='Select brush color']" - ); - if (tbcolor) { - tbcolor.click(); - setTimeout(function () { - update_color(true); - }, 100); - } - } else { - setTimeout(function () { - update_color(false); - }, 100); - } - - let input_range = img_parent.querySelector( - "input[type='range']" - ); - if (!input_range) { - let tbrange = img_parent.querySelector( - "button[aria-label='Use brush']" - ); - - if (tbrange) { - tbrange.click(); - setTimeout(function () { - update_brush(true); - }, 100); - } - } else { - setTimeout(function () { - update_brush(false); - }, 100); - } - } - - let w = img.naturalWidth; - let h = img.naturalHeight; - img_parent.style.width = `${w}px`; - img_parent.style.height = `${h}px`; - - spl.show([ - { - media: "node", - src: elem, - //autohide: true, - //control: ["pan","clear","undo","fullscreen","autofit","zoom-in","zoom-out","close"], - class: "relative", - }, - ]); - - img_parent.style.flexGrow = "0"; - pan_toggle(false, spl_pan); - spl.panzoom(false); - setTimeout(function () { - init_drawing_tools(); - }, 500); - img_parent.style.visibility = "visible"; - }, 1000); - } - } - } - }) - ); - observer.observe(container, { childList: true, subtree: true }); - - /* gradioApp().querySelectorAll('#mode_img2img > .tabitem').forEach(function (tb, ti){ - tb.querySelectorAll('[id^="img2img_copy_to_"] > button').forEach(function (btn, bi){ - - btn.addEventListener("click", function(e) { - console.log(ti + " > " + bi); - let src_canvas = tb.querySelector('[key="drawing"]'); - if(src_canvas){ - let dest_canvas = gradioApp().querySelector('#mode_img2img .tabitem:nth-child('+parseInt(bi+1)+') [key="drawing"]'); - //let destCtx = dest_canvas.getContext('2d'); - //destCtx.drawImage(src_canvas, 0, 0); - console.log(src_canvas); - console.log(dest_canvas); - } - }) - }) - }) - - */ -}); - -/* -let intervalLastUIUpdate; -function onLastUIUpdate(){ - clearInterval(intervalLastUIUpdate); - const img2img_tab = gradioApp().querySelector('#img2img_img2img_tab'); - if(img2img_tab && selectedTabItemId=="tab_img2img"){ - const current_img2img_tab_index = get_img2img_tab_index()[0]; - //if(img2img_tab_index != current_img2img_tab_index){ - console.log(current_img2img_tab_index); - if(current_img2img_tab_index > 3 || current_img2img_tab_index == 0) return; - img2img_tab_index = current_img2img_tab_index; - let tabid; - - if(img2img_tab_index == 1){ - tabid = "#img2img_img2img_sketch_tab";// #img2img_sketch"; - }else if(img2img_tab_index == 2){ - tabid = "#img2img_inpaint_tab";// #img2maskimg"; - }else if(img2img_tab_index == 3){ - tabid = "#img2img_inpaint_sketch_tab";// #inpaint_sketch"; - } - //const parent_img2img_tab_img = gradioApp().querySelector('#mode_img2img > div:nth-child('+(img2img_tab_index+2)+') div[data-testid="image"] > div'); - const parent_img2img_tab_img = gradioApp().querySelector('#mode_img2img '+tabid+' div[data-testid="image"] > div'); - const img2img_tab_img = parent_img2img_tab_img.querySelector('img'); - if(img2img_tab_img){ - parent_img2img_tab_img.style.flexGrow = "0"; - img2img_tab_img.onload = function() { - let w = this.naturalWidth; - let h = this.naturalHeight; - parent_img2img_tab_img.style.width = `${w}px`; - parent_img2img_tab_img.style.height = `${h}px`; - } - }else{ - parent_img2img_tab_img.style.flexGrow = "1"; - } - //} - } } -onUiUpdate(function() { - if(intervalLastUIUpdate != null) clearInterval(intervalLastUIUpdate); - intervalLastUIUpdate = setInterval(onLastUIUpdate, 1000); -}) */ - +onAfterUiUpdate(imageMaskResize); +window.addEventListener('resize', imageMaskResize); diff --git a/javascript/imageviewer.js b/javascript/imageviewer.js index 6a910b57..c21d396e 100644 --- a/javascript/imageviewer.js +++ b/javascript/imageviewer.js @@ -1,242 +1,259 @@ -//called from progressbar -function showGalleryImage() { - //need to clean up the old code +// A full size 'lightbox' preview modal shown when left clicking on gallery previews +function closeModal() { + gradioApp().getElementById("lightboxModal").style.display = "none"; } -let like; -let tile; +function showModal(event) { + const source = event.target || event.srcElement; + const modalImage = gradioApp().getElementById("modalImage"); + const lb = gradioApp().getElementById("lightboxModal"); + modalImage.src = source.src; + if (modalImage.style.display === 'none') { + lb.style.setProperty('background-image', 'url(' + source.src + ')'); + } + lb.style.display = "flex"; + lb.focus(); -let slide = 0; -let gallery = []; -let fullImg_src; -//let control = ["pan","undo","like","tile","page","fullscreen","autofit","zoom-in","zoom-out","clear","close","download","prev","next"]; -let control = [ - "like", - "tile", - "page", - "fullscreen", - "autofit", - "zoom-in", - "zoom-out", - "clear", - "close", - "download", - "prev", - "next", -]; - -let img_browser; -let img_file_name; - -let spl_pane; -let spl_zoom_out; -let spl_zoom_in; -let spotlight_gallery; - -function tile_zoom_update(val) { - let current_tile_state_size = gallery[slide].tile_size; - current_tile_state_size += val * 5; - current_tile_state_size = Math.max(5, Math.min(100, current_tile_state_size)); - spl_pane.style.setProperty("background-size", current_tile_state_size + "%"); - gallery[slide].tile_size = current_tile_state_size; + const tabTxt2Img = gradioApp().getElementById("tab_txt2img"); + const tabImg2Img = gradioApp().getElementById("tab_img2img"); + // show the save button in modal only on txt2img or img2img tabs + if (tabTxt2Img.style.display != "none" || tabImg2Img.style.display != "none") { + gradioApp().getElementById("modal_save").style.display = "inline"; + } else { + gradioApp().getElementById("modal_save").style.display = "none"; + } + event.stopPropagation(); } -function tile_wheel(event) { - let delta = event["deltaY"]; - delta = (delta < 0 ? 1 : delta ? -1 : 0) * 0.5; - tile_zoom_update(delta); -} -function tile_zoom_in(event) { - tile_zoom_update(1); -} -function tile_zoom_out(event) { - tile_zoom_update(-1); +function negmod(n, m) { + return ((n % m) + m) % m; } -function removeTile() { - spl_pane.removeEventListener("wheel", tile_wheel); - spl_zoom_out.removeEventListener("click", tile_zoom_out); - spl_zoom_in.removeEventListener("click", tile_zoom_in); +function updateOnBackgroundChange() { + const modalImage = gradioApp().getElementById("modalImage"); + if (modalImage && modalImage.offsetParent) { + let currentButton = selected_gallery_button(); - spl_pane.classList.remove("hide"); - spl_pane.style.setProperty("background-image", "none"); - spotlight_gallery.zoom(0.0); + if (currentButton?.children?.length > 0 && modalImage.src != currentButton.children[0].src) { + modalImage.src = currentButton.children[0].src; + if (modalImage.style.display === 'none') { + const modal = gradioApp().getElementById("lightboxModal"); + modal.style.setProperty('background-image', `url(${modalImage.src})`); + } + } + } } -function addTile(spl_img) { - spl_pane.addEventListener("wheel", tile_wheel); - spl_zoom_out.addEventListener("click", tile_zoom_out); - spl_zoom_in.addEventListener("click", tile_zoom_in); +function modalImageSwitch(offset) { + var galleryButtons = all_gallery_buttons(); - const current_tile_state_size = gallery[slide].tile_size; - spl_pane.classList.add("hide"); - spl_pane.style.setProperty("background-position", "center"); - spl_pane.style.setProperty("background-size", current_tile_state_size + "%"); - if (spl_img) { - spl_pane.style.setProperty("background-image", `url(${spl_img.src})`); - } + if (galleryButtons.length > 1) { + var currentButton = selected_gallery_button(); + + var result = -1; + galleryButtons.forEach(function(v, i) { + if (v == currentButton) { + result = i; + } + }); + + if (result != -1) { + var nextButton = galleryButtons[negmod((result + offset), galleryButtons.length)]; + nextButton.click(); + const modalImage = gradioApp().getElementById("modalImage"); + const modal = gradioApp().getElementById("lightboxModal"); + modalImage.src = nextButton.children[0].src; + if (modalImage.style.display === 'none') { + modal.style.setProperty('background-image', `url(${modalImage.src})`); + } + setTimeout(function() { + modal.focus(); + }, 10); + } + } } -function tile_handler(event) { - const current_tile_state = !gallery[slide].tile; - gallery[slide].tile = current_tile_state; - - this.classList.toggle("on"); - - if (current_tile_state) { - const spl_img = gradioApp().querySelector("#spotlight-gal .spl-pane img"); - addTile(spl_img); - } else { - removeTile(); - } -} -function like_handler(event) { - const current_like_state = !gallery[slide].like; - gallery[slide].like = current_like_state; - this.classList.toggle("on"); - - if (current_like_state) { - // add to favorites ... - //img_file_name.value = gallery[slide].src; - //console.log(gallery[slide].src); - } else { - // remove from favorites ... - } +function saveImage() { + const tabTxt2Img = gradioApp().getElementById("tab_txt2img"); + const tabImg2Img = gradioApp().getElementById("tab_img2img"); + const saveTxt2Img = "save_txt2img"; + const saveImg2Img = "save_img2img"; + if (tabTxt2Img.style.display != "none") { + gradioApp().getElementById(saveTxt2Img).click(); + } else if (tabImg2Img.style.display != "none") { + gradioApp().getElementById(saveImg2Img).click(); + } else { + console.error("missing implementation for saving modal of this type"); + } } -function createGallerySpotlight() { - //console.log("clicked"); - slide = 0; - gallery = []; - - gradioApp() - .querySelectorAll("#" + selectedTabItemId + " .thumbnails img") - .forEach(function (elem, i) { - elem.setAttribute("gal-id", i); - //if(fullImg_src == elem.src) slide = parseInt(i+1); - if (elem.parentElement.className.indexOf("selected") != -1) - slide = parseInt(i + 1); - //console.log(slide); - gallery[i] = { - src: elem.src, - title: "Seed:" + elem.src, - //description: "This is a description.", - like: false, - tile: false, - tile_size: 50, - }; - }); - - const options = { - class: "sd-gallery", - index: slide, - //control: ["like","page","theme","fullscreen","autofit","zoom-in","zoom-out","close","download","play","prev","next"], - control: control, - //animation: animation, - onshow: function (index) {}, - onchange: function (index, options) { - slide = index - 1; - tile.classList.toggle("on", gallery[slide].tile); - //if(img_browser){ - like.classList.toggle("on", gallery[slide].like); - - //} - - spl_pane = gradioApp().querySelector( - "#spotlight-gal .spl-pane:nth-child(" + index + ")" - ); - spl_zoom_out = gradioApp().querySelector("#spotlight-gal .spl-zoom-out"); - spl_zoom_in = gradioApp().querySelector("#spotlight-gal .spl-zoom-in"); - - const current_tile_state = gallery[slide].tile; - if (current_tile_state) { - addTile(); - } else { - removeTile(); - } - }, - onclose: function (index) { - gradioApp() - .querySelector( - "#" + - selectedTabItemId + - " .thumbnails .thumbnail-item:nth-child(" + - (slide + 1) + - ")" - ) - .click(); - }, - }; - - //assign(options, modifier); - - spotlight_gallery.show(gallery, options); - spotlight_gallery.panzoom(true); +function modalSaveImage(event) { + saveImage(); + event.stopPropagation(); } -function fullImg_click_handler(e) { - e.stopPropagation(); - e.preventDefault(); - createGallerySpotlight(); +function modalNextImage(event) { + modalImageSwitch(1); + event.stopPropagation(); } -let intervalUiUpdateIViewer; -function onUiHeaderTabUpdate() { - if (intervalUiUpdateIViewer != null) clearInterval(intervalUiUpdateIViewer); - intervalUiUpdateIViewer = setInterval(onUiUpdateIViewer, 500); +function modalPrevImage(event) { + modalImageSwitch(-1); + event.stopPropagation(); } -let fullImg_preview; -function onUiUpdateIViewer() { - clearInterval(intervalUiUpdateIViewer); - //update_performant_inputs(selectedTabItemId); - - //fullImg_preview = gradioApp().querySelector('#'+selectedTabItemId+' [id$="2img_results"] .modify-upload + img.w-full.object-contain'); - fullImg_preview = gradioApp().querySelector( - "#" + selectedTabItemId + " .preview > img" - ); - if (opts.js_modal_lightbox && fullImg_preview) { - fullImg_src = fullImg_preview.src; - fullImg_preview.removeEventListener("click", fullImg_click_handler); - fullImg_preview.addEventListener("click", fullImg_click_handler, true); //bubbling phase - - /* - // this is an idea to integrate image browser extension seamlesly, - // without the need to change to the image browser tab extension users will be able to review images after generation - // and add them to favorites or delete the ones that don't like on the spot - const img_browser = gradioApp().querySelector('[id$="2img_images_history"]'); - const tbname = selectedTabItemId.split("_")[1]; - if(img_browser && tbname ==("txt2img" || "img2img")){ - const images_history = gradioApp().querySelector('[id$="'+tbname+'_images_history"]'); - const history_button_panel = images_history.querySelector('[id$="'+tbname+'_images_history_button_panel"]'); - const labels = images_history.querySelectorAll('label.block span'); - for(let i=0;i -1; + + // For Firefox, listening on click first switched to next image then shows the lightbox. + // If you know how to fix this without switching to mousedown event, please. + // For other browsers the event is click to make it possiblr to drag picture. + var event = isFirefox ? 'mousedown' : 'click'; + + e.addEventListener(event, function(evt) { + if (evt.button == 1) { + open(evt.target.src); + evt.preventDefault(); + return; + } + if (!opts.js_modal_lightbox || evt.button != 0) return; + + modalZoomSet(gradioApp().getElementById('modalImage'), opts.js_modal_lightbox_initially_zoomed); + evt.preventDefault(); + showModal(evt); + }, true); + +} + +function modalZoomSet(modalImage, enable) { + if (modalImage) modalImage.classList.toggle('modalImageFullscreen', !!enable); +} + +function modalZoomToggle(event) { + var modalImage = gradioApp().getElementById("modalImage"); + modalZoomSet(modalImage, !modalImage.classList.contains('modalImageFullscreen')); + event.stopPropagation(); +} + +function modalTileImageToggle(event) { + const modalImage = gradioApp().getElementById("modalImage"); + const modal = gradioApp().getElementById("lightboxModal"); + const isTiling = modalImage.style.display === 'none'; + if (isTiling) { + modalImage.style.display = 'block'; + modal.style.setProperty('background-image', 'none'); + } else { + modalImage.style.display = 'none'; + modal.style.setProperty('background-image', `url(${modalImage.src})`); + } + + event.stopPropagation(); +} + +onAfterUiUpdate(function() { + var fullImg_preview = gradioApp().querySelectorAll('.gradio-gallery > div > img'); + if (fullImg_preview != null) { + fullImg_preview.forEach(setupImageForLightbox); + } + updateOnBackgroundChange(); }); -onUiLoaded(function () { - spotlight_gallery = new Spotlight(); - spotlight_gallery.init( - gradioApp().querySelector(".gradio-container"), - "-gal" - ); - tile = spotlight_gallery.addControl("tile", tile_handler); - like = spotlight_gallery.addControl("like", like_handler); -}); +document.addEventListener("DOMContentLoaded", function() { + //const modalFragment = document.createDocumentFragment(); + const modal = document.createElement('div'); + modal.onclick = closeModal; + modal.id = "lightboxModal"; + modal.tabIndex = 0; + modal.addEventListener('keydown', modalKeyHandler, true); -document.addEventListener("DOMContentLoaded", function () {}); + const modalControls = document.createElement('div'); + modalControls.className = 'modalControls gradio-container'; + modal.append(modalControls); + + const modalZoom = document.createElement('span'); + modalZoom.className = 'modalZoom cursor'; + modalZoom.innerHTML = '⤡'; + modalZoom.addEventListener('click', modalZoomToggle, true); + modalZoom.title = "Toggle zoomed view"; + modalControls.appendChild(modalZoom); + + const modalTileImage = document.createElement('span'); + modalTileImage.className = 'modalTileImage cursor'; + modalTileImage.innerHTML = '⊞'; + modalTileImage.addEventListener('click', modalTileImageToggle, true); + modalTileImage.title = "Preview tiling"; + modalControls.appendChild(modalTileImage); + + const modalSave = document.createElement("span"); + modalSave.className = "modalSave cursor"; + modalSave.id = "modal_save"; + modalSave.innerHTML = "🖫"; + modalSave.addEventListener("click", modalSaveImage, true); + modalSave.title = "Save Image(s)"; + modalControls.appendChild(modalSave); + + const modalClose = document.createElement('span'); + modalClose.className = 'modalClose cursor'; + modalClose.innerHTML = '×'; + modalClose.onclick = closeModal; + modalClose.title = "Close image viewer"; + modalControls.appendChild(modalClose); + + const modalImage = document.createElement('img'); + modalImage.id = 'modalImage'; + modalImage.onclick = closeModal; + modalImage.tabIndex = 0; + modalImage.addEventListener('keydown', modalKeyHandler, true); + modal.appendChild(modalImage); + + const modalPrev = document.createElement('a'); + modalPrev.className = 'modalPrev'; + modalPrev.innerHTML = '❮'; + modalPrev.tabIndex = 0; + modalPrev.addEventListener('click', modalPrevImage, true); + modalPrev.addEventListener('keydown', modalKeyHandler, true); + modal.appendChild(modalPrev); + + const modalNext = document.createElement('a'); + modalNext.className = 'modalNext'; + modalNext.innerHTML = '❯'; + modalNext.tabIndex = 0; + modalNext.addEventListener('click', modalNextImage, true); + modalNext.addEventListener('keydown', modalKeyHandler, true); + + modal.appendChild(modalNext); + + try { + gradioApp().appendChild(modal); + } catch (e) { + gradioApp().body.appendChild(modal); + } + + document.body.appendChild(modal); + +}); diff --git a/javascript/imageviewerGamepad.js b/javascript/imageviewerGamepad.js index 8f824148..a22c7e6e 100644 --- a/javascript/imageviewerGamepad.js +++ b/javascript/imageviewerGamepad.js @@ -60,4 +60,4 @@ function sleepUntil(f, timeout) { } }, 20); }); -} \ No newline at end of file +} diff --git a/javascript/inputAccordion.js b/javascript/inputAccordion.js new file mode 100644 index 00000000..f2839852 --- /dev/null +++ b/javascript/inputAccordion.js @@ -0,0 +1,37 @@ +var observerAccordionOpen = new MutationObserver(function(mutations) { + mutations.forEach(function(mutationRecord) { + var elem = mutationRecord.target; + var open = elem.classList.contains('open'); + + var accordion = elem.parentNode; + accordion.classList.toggle('input-accordion-open', open); + + var checkbox = gradioApp().querySelector('#' + accordion.id + "-checkbox input"); + checkbox.checked = open; + updateInput(checkbox); + + var extra = gradioApp().querySelector('#' + accordion.id + "-extra"); + if (extra) { + extra.style.display = open ? "" : "none"; + } + }); +}); + +function inputAccordionChecked(id, checked) { + var label = gradioApp().querySelector('#' + id + " .label-wrap"); + if (label.classList.contains('open') != checked) { + label.click(); + } +} + +onUiLoaded(function() { + for (var accordion of gradioApp().querySelectorAll('.input-accordion')) { + var labelWrap = accordion.querySelector('.label-wrap'); + observerAccordionOpen.observe(labelWrap, {attributes: true, attributeFilter: ['class']}); + + var extra = gradioApp().querySelector('#' + accordion.id + "-extra"); + if (extra) { + labelWrap.insertBefore(extra, labelWrap.lastElementChild); + } + } +}); diff --git a/javascript/localStorage.js b/javascript/localStorage.js new file mode 100644 index 00000000..dc1a36c3 --- /dev/null +++ b/javascript/localStorage.js @@ -0,0 +1,26 @@ + +function localSet(k, v) { + try { + localStorage.setItem(k, v); + } catch (e) { + console.warn(`Failed to save ${k} to localStorage: ${e}`); + } +} + +function localGet(k, def) { + try { + return localStorage.getItem(k); + } catch (e) { + console.warn(`Failed to load ${k} from localStorage: ${e}`); + } + + return def; +} + +function localRemove(k) { + try { + return localStorage.removeItem(k); + } catch (e) { + console.warn(`Failed to remove ${k} from localStorage: ${e}`); + } +} diff --git a/javascript/localization.js b/javascript/localization.js index f9028164..8f00c186 100644 --- a/javascript/localization.js +++ b/javascript/localization.js @@ -1,3 +1,4 @@ + // localization = {} -- the dict with translations is created by the backend var ignore_ids_for_localization = { @@ -10,11 +11,11 @@ var ignore_ids_for_localization = { train_hypernetwork: 'OPTION', txt2img_styles: 'OPTION', img2img_styles: 'OPTION', - setting_random_artist_categories: 'SPAN', - setting_face_restoration_model: 'SPAN', - setting_realesrgan_enabled_models: 'SPAN', - extras_upscaler_1: 'SPAN', - extras_upscaler_2: 'SPAN', + setting_random_artist_categories: 'OPTION', + setting_face_restoration_model: 'OPTION', + setting_realesrgan_enabled_models: 'OPTION', + extras_upscaler_1: 'OPTION', + extras_upscaler_2: 'OPTION', }; var re_num = /^[.\d]+$/; @@ -106,12 +107,41 @@ function processNode(node) { }); } +function localizeWholePage() { + processNode(gradioApp()); + + function elem(comp) { + var elem_id = comp.props.elem_id ? comp.props.elem_id : "component-" + comp.id; + return gradioApp().getElementById(elem_id); + } + + for (var comp of window.gradio_config.components) { + if (comp.props.webui_tooltip) { + let e = elem(comp); + + let tl = e ? getTranslation(e.title) : undefined; + if (tl !== undefined) { + e.title = tl; + } + } + if (comp.props.placeholder) { + let e = elem(comp); + let textbox = e ? e.querySelector('[placeholder]') : null; + + let tl = textbox ? getTranslation(textbox.placeholder) : undefined; + if (tl !== undefined) { + textbox.placeholder = tl; + } + } + } +} + function dumpTranslations() { if (!hasLocalization()) { // If we don't have any localization, // we will not have traversed the app to find // original_lines, so do that now. - processNode(gradioApp()); + localizeWholePage(); } var dumped = {}; if (localization.rtl) { @@ -153,7 +183,7 @@ document.addEventListener("DOMContentLoaded", function() { }); }); - processNode(gradioApp()); + localizeWholePage(); if (localization.rtl) { // if the language is from right to left, (new MutationObserver((mutations, observer) => { // wait for the style to load diff --git a/javascript/notification.js b/javascript/notification.js index 82577b2d..6d799561 100644 --- a/javascript/notification.js +++ b/javascript/notification.js @@ -1,3 +1,5 @@ +// Monitors the gallery and sends a browser notification when the leading image is new. + let lastHeadImg = null; let notificationButton = null; @@ -13,7 +15,7 @@ onAfterUiUpdate(function() { } } - const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] div[id$="_results"] .thumbnail-item > img'); + const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"] div[id$="_results"] .thumbnail-item > img'); if (galleryPreviews == null) return; @@ -44,4 +46,4 @@ onAfterUiUpdate(function() { parent.focus(); this.close(); }; -}); \ No newline at end of file +}); diff --git a/javascript/progressbar.js b/javascript/progressbar.js index 8606030b..77761495 100644 --- a/javascript/progressbar.js +++ b/javascript/progressbar.js @@ -36,13 +36,13 @@ function pad2(x) { } function formatTime(secs) { - if (secs > 3600) { - return pad2(Math.floor(secs / 60 / 60)) + ":" + pad2(Math.floor(secs / 60) % 60) + ":" + pad2(Math.floor(secs) % 60); - } else if (secs > 60) { - return pad2(Math.floor(secs / 60)) + ":" + pad2(Math.floor(secs) % 60); - } else { - return Math.floor(secs) + "s"; - } + if (secs > 3600) { + return pad2(Math.floor(secs / 60 / 60)) + ":" + pad2(Math.floor(secs / 60) % 60) + ":" + pad2(Math.floor(secs) % 60); + } else if (secs > 60) { + return pad2(Math.floor(secs / 60)) + ":" + pad2(Math.floor(secs) % 60); + } else { + return Math.floor(secs) + "s"; + } } function setTitle(progress) { @@ -57,6 +57,7 @@ function setTitle(progress) { } } + function randomId() { return "task(" + Math.random().toString(36).slice(2, 7) + Math.random().toString(36).slice(2, 7) + Math.random().toString(36).slice(2, 7) + ")"; } @@ -64,145 +65,122 @@ function randomId() { // starts sending progress requests to "/internal/progress" uri, creating progressbar above progressbarContainer element and // preview inside gallery element. Cleans up all created stuff when the task is over and calls atEnd. // calls onProgress every time there is a progress update -function requestProgress( - id_task, - progressbarContainer, - gallery, - atEnd, - onProgress, - inactivityTimeout = 40 -) { - var dateStart = new Date(); - var wasEverActive = false; - var parentProgressbar = progressbarContainer.parentNode; - var parentGallery = gallery ? gallery.parentNode : null; +function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgress, inactivityTimeout = 40) { + var dateStart = new Date(); + var wasEverActive = false; + var parentProgressbar = progressbarContainer.parentNode; - var divProgress = document.createElement("div"); - divProgress.className = "progressDiv"; + var divProgress = document.createElement('div'); + divProgress.className = 'progressDiv'; + divProgress.style.display = opts.show_progressbar ? "block" : "none"; + var divInner = document.createElement('div'); + divInner.className = 'progress'; - divProgress.style.display = opts.show_progressbar ? "block" : "none"; - var divInner = document.createElement("div"); - divInner.className = "progress"; + divProgress.appendChild(divInner); + parentProgressbar.insertBefore(divProgress, progressbarContainer); - divProgress.appendChild(divInner); - parentProgressbar.insertBefore(divProgress, progressbarContainer); + var livePreview = null; - if (parentGallery) { - var livePreview = gradioApp().querySelector(".livePreview"); - if (!livePreview) { - livePreview = document.createElement("div"); - livePreview.classList.add("livePreview", "init"); - parentGallery.insertBefore(livePreview, gallery); - } - livePreview.classList.remove("dropPreview"); - } + var removeProgressBar = function() { + if (!divProgress) return; - var removeProgressBar = function () { - setTitle(""); - if (divProgress) { - parentProgressbar.removeChild(divProgress); - } + setTitle(""); + parentProgressbar.removeChild(divProgress); + if (gallery && livePreview) gallery.removeChild(livePreview); + atEnd(); - if (progressbarContainer.id == "txt2img_gallery_container") { - showSubmitButtons("txt2img", true); - } else if (progressbarContainer.id == "img2img_gallery_container") { - showSubmitButtons("img2img", true); - } - if (livePreview) { - if (parentGallery) parentGallery.removeChild(livePreview); - } - gradioApp() - .querySelectorAll("#tabs + div, #tabs + div > *:not(ul)") - .forEach(function (elem) { - elem.style.setProperty("display", "block", "important"); - }); - atEnd(); - }; + divProgress = null; + }; - var fun = function (id_task, id_live_preview) { - request( - "./internal/progress", - { id_task: id_task, id_live_preview: id_live_preview }, - function (res) { - if (res.completed) { - removeProgressBar(); - return; - } - - var rect = progressbarContainer.getBoundingClientRect(); - - if (rect.width) { - divProgress.style.width = rect.width + "px"; - } - - let progressText = ""; - - divInner.style.width = (res.progress || 0) * 100.0 + "%"; - divInner.style.background = res.progress ? "" : "transparent"; - - if (res.progress > 0) { - progressText = ((res.progress || 0) * 100.0).toFixed(0) + "%"; - } - - if (res.eta) { - progressText += " ETA: " + formatTime(res.eta); - } - - setTitle(progressText); - - if (res.textinfo && res.textinfo.indexOf("\n") == -1) { - progressText = res.textinfo + " " + progressText; - } - - divInner.textContent = progressText; - - var elapsedFromStart = (new Date() - dateStart) / 1000; - - if (res.active) wasEverActive = true; - - if (!res.active && wasEverActive) { - removeProgressBar(); - return; - } - - if ( - elapsedFromStart > inactivityTimeout && - !res.queued && - !res.active - ) { - removeProgressBar(); - return; - } - - if (res.live_preview && gallery) { - - livePreview.classList.remove("init"); - - var img = new Image(); - img.onload = function () { - img.width = img.naturalWidth; - img.height = img.naturalHeight; - livePreview.appendChild(img); - if (livePreview.childElementCount > 2) { - livePreview.removeChild(livePreview.firstElementChild); + var funProgress = function(id_task) { + request("./internal/progress", {id_task: id_task, live_preview: false}, function(res) { + if (res.completed) { + removeProgressBar(); + return; } - }; - img.src = res.live_preview; - } - if (onProgress) { - onProgress(res); - } + let progressText = ""; - setTimeout(() => { - fun(id_task, res.id_live_preview); - }, opts.live_preview_refresh_period || 500); - }, - function () { - removeProgressBar(); - } - ); - }; + divInner.style.width = ((res.progress || 0) * 100.0) + '%'; + divInner.style.background = res.progress ? "" : "transparent"; + + if (res.progress > 0) { + progressText = ((res.progress || 0) * 100.0).toFixed(0) + '%'; + } + + if (res.eta) { + progressText += " ETA: " + formatTime(res.eta); + } + + setTitle(progressText); + + if (res.textinfo && res.textinfo.indexOf("\n") == -1) { + progressText = res.textinfo + " " + progressText; + } + + divInner.textContent = progressText; + + var elapsedFromStart = (new Date() - dateStart) / 1000; + + if (res.active) wasEverActive = true; + + if (!res.active && wasEverActive) { + removeProgressBar(); + return; + } + + if (elapsedFromStart > inactivityTimeout && !res.queued && !res.active) { + removeProgressBar(); + return; + } + + if (onProgress) { + onProgress(res); + } + + setTimeout(() => { + funProgress(id_task, res.id_live_preview); + }, opts.live_preview_refresh_period || 500); + }, function() { + removeProgressBar(); + }); + }; + + var funLivePreview = function(id_task, id_live_preview) { + request("./internal/progress", {id_task: id_task, id_live_preview: id_live_preview}, function(res) { + if (!divProgress) { + return; + } + + if (res.live_preview && gallery) { + var img = new Image(); + img.onload = function() { + if (!livePreview) { + livePreview = document.createElement('div'); + livePreview.className = 'livePreview'; + gallery.insertBefore(livePreview, gallery.firstElementChild); + } + + livePreview.appendChild(img); + if (livePreview.childElementCount > 2) { + livePreview.removeChild(livePreview.firstElementChild); + } + }; + img.src = res.live_preview; + } + + setTimeout(() => { + funLivePreview(id_task, res.id_live_preview); + }, opts.live_preview_refresh_period || 500); + }, function() { + removeProgressBar(); + }); + }; + + funProgress(id_task, 0); + + if (gallery) { + funLivePreview(id_task, 0); + } - fun(id_task, 0); } diff --git a/javascript/resizeHandle.js b/javascript/resizeHandle.js new file mode 100644 index 00000000..8c5c5169 --- /dev/null +++ b/javascript/resizeHandle.js @@ -0,0 +1,141 @@ +(function() { + const GRADIO_MIN_WIDTH = 320; + const GRID_TEMPLATE_COLUMNS = '1fr 16px 1fr'; + const PAD = 16; + const DEBOUNCE_TIME = 100; + + const R = { + tracking: false, + parent: null, + parentWidth: null, + leftCol: null, + leftColStartWidth: null, + screenX: null, + }; + + let resizeTimer; + let parents = []; + + function setLeftColGridTemplate(el, width) { + el.style.gridTemplateColumns = `${width}px 16px 1fr`; + } + + function displayResizeHandle(parent) { + if (window.innerWidth < GRADIO_MIN_WIDTH * 2 + PAD * 4) { + parent.style.display = 'flex'; + if (R.handle != null) { + R.handle.style.opacity = '0'; + } + return false; + } else { + parent.style.display = 'grid'; + if (R.handle != null) { + R.handle.style.opacity = '100'; + } + return true; + } + } + + function afterResize(parent) { + if (displayResizeHandle(parent) && parent.style.gridTemplateColumns != GRID_TEMPLATE_COLUMNS) { + const oldParentWidth = R.parentWidth; + const newParentWidth = parent.offsetWidth; + const widthL = parseInt(parent.style.gridTemplateColumns.split(' ')[0]); + + const ratio = newParentWidth / oldParentWidth; + + const newWidthL = Math.max(Math.floor(ratio * widthL), GRADIO_MIN_WIDTH); + setLeftColGridTemplate(parent, newWidthL); + + R.parentWidth = newParentWidth; + } + } + + function setup(parent) { + const leftCol = parent.firstElementChild; + const rightCol = parent.lastElementChild; + + parents.push(parent); + + parent.style.display = 'grid'; + parent.style.gap = '0'; + parent.style.gridTemplateColumns = GRID_TEMPLATE_COLUMNS; + + const resizeHandle = document.createElement('div'); + resizeHandle.classList.add('resize-handle'); + parent.insertBefore(resizeHandle, rightCol); + + resizeHandle.addEventListener('mousedown', (evt) => { + if (evt.button !== 0) return; + + evt.preventDefault(); + evt.stopPropagation(); + + document.body.classList.add('resizing'); + + R.tracking = true; + R.parent = parent; + R.parentWidth = parent.offsetWidth; + R.handle = resizeHandle; + R.leftCol = leftCol; + R.leftColStartWidth = leftCol.offsetWidth; + R.screenX = evt.screenX; + }); + + resizeHandle.addEventListener('dblclick', (evt) => { + evt.preventDefault(); + evt.stopPropagation(); + + parent.style.gridTemplateColumns = GRID_TEMPLATE_COLUMNS; + }); + + afterResize(parent); + } + + window.addEventListener('mousemove', (evt) => { + if (evt.button !== 0) return; + + if (R.tracking) { + evt.preventDefault(); + evt.stopPropagation(); + + const delta = R.screenX - evt.screenX; + const leftColWidth = Math.max(Math.min(R.leftColStartWidth - delta, R.parent.offsetWidth - GRADIO_MIN_WIDTH - PAD), GRADIO_MIN_WIDTH); + setLeftColGridTemplate(R.parent, leftColWidth); + } + }); + + window.addEventListener('mouseup', (evt) => { + if (evt.button !== 0) return; + + if (R.tracking) { + evt.preventDefault(); + evt.stopPropagation(); + + R.tracking = false; + + document.body.classList.remove('resizing'); + } + }); + + + window.addEventListener('resize', () => { + clearTimeout(resizeTimer); + + resizeTimer = setTimeout(function() { + for (const parent of parents) { + afterResize(parent); + } + }, DEBOUNCE_TIME); + }); + + setupResizeHandle = setup; +})(); + +onUiLoaded(function() { + for (var elem of gradioApp().querySelectorAll('.resize-handle-row')) { + if (!elem.querySelector('.resize-handle')) { + setupResizeHandle(elem); + } + } +}); diff --git a/javascript/spotlight.js b/javascript/spotlight.js deleted file mode 100644 index 88ae8ed2..00000000 --- a/javascript/spotlight.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Spotlight.js v0.7.8 - * Copyright 2019-2021 Nextapps GmbH - * Author: Thomas Wilkerling - * Licence: Apache-2.0 - * https://github.com/nextapps-de/spotlight - */ - /* - this library was rewritten by anapnoe to work with https://github.com/anapnoe/stable-diffusion-webui-ux - it supports multi-instancing, enable-disable panzoom functionallity and input type tools - */ - -!function(e){"use strict";let t=function(t,i={}){this.controls=["info","theme","download","play","page","close","autofit","zoom-in","zoom-out","prev","next","fullscreen"],this.controls_default={page:1,close:1,autofit:1,"zoom-in":1,"zoom-out":1,prev:1,next:1,fullscreen:1},this.keycodes={BACKSPACE:8,ESCAPE:27,SPACEBAR:32,LEFT:37,RIGHT:39,UP:38,NUMBLOCK_PLUS:107,PLUS:187,DOWN:40,NUMBLOCK_MINUS:109,MINUS:189,INFO:73};let s=this;Object.assign||(Object.assign=function(e,t){let i=Object.keys(t);for(let s=0,o;s{s.toggleClass(e,t,!0)},this.removeClass=(e,t)=>{s.toggleClass(e,t)},this.toggleClass=(e,t,i)=>{e.classList[i?"add":"remove"](t)},this.hasClass=(e,t)=>e.classList.contains(t),this.setStyle=(e,t,i)=>{i=""+i,e["_s_"+t]!==i&&(e.style.setProperty(t,i),e["_s_"+t]=i)};let o=0;this.prepareStyle=(e,t)=>{t&&(s.setStyle(e,"transition","none"),t()),o||(o=e.clientTop&&0),t&&s.setStyle(e,"transition","")},this.setText=(e,t)=>{e.firstChild.nodeValue=t},this.getByClass=(e,t)=>(t||document).getElementsByClassName(e),this.getByTag=(e,t)=>(t||document).getElementsByTagName(e),this.addListener=(e,t,i,o)=>{s.toggleListener(!0,e,t,i,o)},this.removeListener=(e,t,i,o)=>{s.toggleListener(!1,e,t,i,o)},this.toggleListener=(e,t,i,s,o)=>{t[(e?"add":"remove")+"EventListener"](i,s,!o&&!1!==o||o)},this.cancelEvent=(e,t)=>{e.stopPropagation(),t&&e.preventDefault()},this.downloadImage=(e,t)=>{let i=s.createElement("a"),o=t.src;i.href=o,i.download=o.substring(o.lastIndexOf("/")+1),s.body.appendChild(i),i.click(),s.body.removeChild(i)},this.createElement=e=>document.createElement(e),this.toggleDisplay=(e,t)=>{s.setStyle(e,"display",t?"":"none")},this.toggleVisibility=(e,t)=>{s.setStyle(e,"visibility",t?"":"hidden")},this.toggleAnimation=(e,t)=>{s.setStyle(e,"transition",t?"":"none")},this.widget=this.createElement("div"),this.widget.innerHTML="
",this.video_support={},this.tpl_video=this.createElement("video"),this.parse_src=(e,t,i,o)=>{let n,l;if("node"!==o){let a=Object.keys(i);for(let r=0,d;r3&&0===d.indexOf("src")){if("video"===o){let p=s.video_support[d];if(p){if(p>0){n=i[d];break}}else if(s.tpl_video.canPlayType("video/"+d.substring(3).replace("-","").toLowerCase())){s.video_support[d]=1,n=i[d];break}else s.video_support[d]=-1}else{let c=parseInt(d.substring(4),10);if(c){let h=Math.abs(t-c);(!l||h{s.body||(s.id="spotlight"+t,s.widget.id="spotlight"+t,s.doc=document.body,s.body=e,s.slider=s.getOneByClass("scene"),s.header=s.getOneByClass("header"),s.footer=s.getOneByClass("footer"),s.title=s.getOneByClass("title"),s.description=s.getOneByClass("description"),s.button=s.getOneByClass("button"),s.page_prev=s.getOneByClass("prev"),s.page_next=s.getOneByClass("next"),s.page=s.getOneByClass("page"),s.progress=s.getOneByClass("progress"),s.spinner=s.getOneByClass("spinner"),s.panes=[s.getOneByClass("pane")],s.addControl("close",s.close),s.doc[s.prefix_request="requestFullscreen"]||s.doc[s.prefix_request="msRequestFullscreen"]||s.doc[s.prefix_request="webkitRequestFullscreen"]||s.doc[s.prefix_request="mozRequestFullscreen"]||(s.prefix_request=""),s.prefix_request?(s.prefix_exit=s.prefix_request.replace("request","exit").replace("mozRequest","mozCancel").replace("Request","Exit"),s.maximize=s.addControl("fullscreen",s.fullscreen)):s.controls.pop(),s.addControl("info",s.info),s.addControl("autofit",s.autofit),s.addControl("zoom-in",s.zoom_in),s.addControl("zoom-out",s.zoom_out),s.addControl("theme",s.theme),s.player=s.addControl("play",s.play),s.addControl("download",s.download),s.addListener(s.page_prev,"click",s.prev),s.addListener(s.page_next,"click",s.next),s.track=s.getOneByClass("track"),s.addListener(s.track,"mousedown",s.start),s.addListener(s.track,"mousemove",s.move),s.addListener(s.track,"mouseleave",s.end),s.addListener(s.track,"mouseup",s.end),s.addListener(s.track,"touchstart",s.start,{passive:!1}),s.addListener(s.track,"touchmove",s.move,{passive:!0}),s.addListener(s.track,"touchend",s.end),s.addListener(s.button,"click",function(){s.options_click?s.options_click(s.current_slide,s.options):s.options_href&&(location.href=s.options_href)}))},this.getOneByClass=e=>this.controls_dom[e]=this.getByClass("spl-"+e,this.widget)[0],this.addControl=(e,t,i="")=>{let o=s.createElement("div");return o.className="spl-"+e,o.innerHTML=i,s.addListener(o,"click",t),s.header.appendChild(o),s.controls_dom[e]=o},this.removeControl=e=>{let t=s.controls_dom[e];t&&(s.header.removeChild(t),s.controls_dom[e]=null)},this.dispatch=e=>{let t=e.target.closest(".spotlight");if(t){s.cancelEvent(e,!0);let i=t.closest(".spotlight-group");s.anchors=s.getByClass("spotlight",i);for(let o=0;o{s.anchors=e,t&&(s.options_group=t,s.options_onshow=t.onshow,s.options_onchange=t.onchange,s.options_onclose=t.onclose,i=i||t.index),s.init_gallery(i)},this.panzoom=e=>{s.toggleListener(e,s.track,"wheel",s.wheel_listener,{passive:!1}),s.toggleListener(e,s.track,"mousedown",s.start),s.toggleListener(e,s.track,"mousemove",s.move),s.toggleListener(e,s.track,"mouseleave",s.end),s.toggleListener(e,s.track,"mouseup",s.end),s.toggleListener(e,s.track,"touchstart",s.start,{passive:!1}),s.toggleListener(e,s.track,"touchmove",s.move,{passive:!0}),s.toggleListener(e,s.track,"touchend",s.end)},this.init_gallery=e=>{if(s.slide_count=s.anchors.length,s.slide_count){s.body||s.init(),s.options_onshow&&s.options_onshow(e);let t=s.panes[0],i=t.parentElement;for(let o=s.panes.length;o{let s=i[e];return void 0!==s?"false"!=(s=""+s)&&(s||t):t},this.apply_options=e=>{s.options={},s.options_group&&Object.assign(s.options,s.options_group),Object.assign(s.options,e.dataset||e),s.options_media=s.options.media,s.options_click=s.options.onclick,s.options_theme=s.options.theme,s.options_class=s.options.class,s.options_autohide=s.parse_option("autohide",!1),s.options_infinite=s.parse_option("infinite"),s.options_progress=s.parse_option("progress",!0),s.options_autoslide=s.parse_option("autoslide"),s.options_preload=s.parse_option("preload",!0),s.options_href=s.options.buttonHref,s.delay=s.options_autoslide&&parseFloat(s.options_autoslide)||7,s.toggle_theme||s.options_theme&&s.theme(s.options_theme),s.options_class&&s.addClass(s.widget,s.options_class),s.options_class&&s.prepareStyle(s.widget);let t=s.options.control;if(t){let i="string"==typeof t?t.split(","):t;for(let o=0;o{e?s.prepareStyle(s.media,s.prepare_animation):(s.toggleAnimation(s.slider,s.animation_slide),s.setStyle(s.media,"opacity",s.animation_fade?0:1),s.update_scroll(s.animation_scale&&.8),s.animation_custom&&s.addClass(s.media,s.animation_custom))},this.init_slide=e=>{if(s.panel=s.panes[e-1],s.media=s.panel.firstChild,s.current_slide=e,s.media)s.disable_autoresizer(),s.options_fit&&s.addClass(s.media,s.options_fit),s.prepare_animation(!0),s.animation_custom&&s.removeClass(s.media,s.animation_custom),s.animation_fade&&s.setStyle(s.media,"opacity",1),s.animation_scale&&s.setStyle(s.media,"transform",""),s.setStyle(s.media,"visibility","visible"),s.gallery_next&&(s.media_next.src=s.gallery_next),s.options_autoslide&&s.animate_bar(s.playing);else{let t=s.gallery.media;if(s.parse_option("spinner",!0),"node"===t){s.media=s.gallery.src,"string"==typeof s.media&&(s.media=gradioApp().querySelector(s.media)),s.media&&(s.media._root||(s.media._root=s.media.parentElement),s.update_media_viewport(),s.panel.appendChild(s.media),s.init_slide(e));return}s.toggle_spinner(s.options_spinner,!0),s.media=s.createElement("img"),s.media.onload=function(){s.media===s&&(s.media.onerror=null,s.toggle_spinner(s.options_spinner),s.init_slide(e),s.update_media_viewport())},s.media.src=s.gallery.src,s.panel.appendChild(s.media),s.media&&(s.options_spinner||s.setStyle(s.media,"visibility","visible"),s.media.onerror=function(){s.media===s&&(s.checkout(s.media),s.addClass(s.spinner,"error"),s.toggle_spinner(s.options_spinner))})}},this.toggle_spinner=(e,t)=>{e&&s.toggleClass(s.spinner,"spin",t)},this.has_fullscreen=()=>document.fullscreen||document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement,this.resize_listener=()=>{if(s.update_widget_viewport(),s.media&&s.update_media_viewport(),s.prefix_request){let e=s.has_fullscreen();s.toggleClass(s.maximize,"on",e),e||s.detect_fullscreen()}},this.detect_fullscreen=()=>{s.toggleDisplay(s.maximize,screen.availHeight-e.innerHeight>0)},this.update_widget_viewport=()=>{s.viewport_w=s.widget.clientWidth,s.viewport_h=s.widget.clientHeight},this.update_media_viewport=()=>{s.media_w=s.media.clientWidth,s.media_h=s.media.clientHeight},this.update_scroll=e=>{s.setStyle(s.media,"transform","translate(-50%, -50%) scale("+(e||s.scale)+")")},this.update_panel=(e,t)=>{s.setStyle(s.panel,"transform",e||t?"translate("+e+"px, "+t+"px)":"")},this.update_slider=(e,t,i)=>{t?s.prepareStyle(s.slider,function(){s.update_slider(e,!1,i)}):s.setStyle(s.slider,"transform","translateX("+(-(100*e)+(i||0))+"%)")},this.toggle_listener=t=>{s.toggleListener(t,e,"keydown",s.key_listener),s.toggleListener(t,e,"resize",s.resize_listener)},this.history_listener=e=>{s.panel&&e.state.spl&&s.close(!0)},this.key_listener=e=>{if(s.panel){let t=!1!==s.options["zoom-in"];switch(e.keyCode){case s.keycodes.BACKSPACE:t&&s.autofit();break;case s.keycodes.SPACEBAR:s.options_autoslide&&s.play();break;case s.keycodes.LEFT:s.prev();break;case s.keycodes.RIGHT:s.next();break;case s.keycodes.NUMBLOCK_PLUS:case s.keycodes.PLUS:t&&s.zoom_in();break;case s.keycodes.NUMBLOCK_MINUS:case s.keycodes.MINUS:t&&s.zoom_out();break;case s.keycodes.INFO:s.info()}}},this.wheel_listener=e=>{if(s.panel&&!1!==s.options["zoom-in"]){let t=e.deltaY;(t=(t<0?1:t?-1:0)*.5)<0?s.zoom_out():s.zoom_in()}},this.play=(e,t)=>{let i="boolean"==typeof e?e:!this.playing;!s.playing===i&&(s.playing=s.playing?clearTimeout(s.playing):1,s.toggleClass(s.player,"on",s.playing),t||s.animate_bar(s.playing))},this.animate_bar=e=>{s.options_progress&&(s.prepareStyle(s.progress,function(){s.setStyle(s.progress,"transition-duration",""),s.setStyle(s.progress,"transform","")}),e&&(s.setStyle(s.progress,"transition-duration",s.delay+"s"),s.setStyle(s.progress,"transform","translateX(0)"))),e&&(s.playing=setTimeout(s.next,1e3*s.delay))},this.autohide=()=>{s.options_autohide&&(s.hide_cooldown=Date.now()+2950,s.hide||(s.addClass(s.widget,"menu"),s.schedule(3e3)))},this.schedule=e=>{s.hide=setTimeout(function(){let e=Date.now();e>=s.hide_cooldown?(s.removeClass(s.widget,"menu"),s.hide=0):s.schedule(s.hide_cooldown-e)},e)},this.menu=e=>{"boolean"==typeof e&&(s.hide=e?s.hide:0),s.hide?(s.hide=clearTimeout(s.hide),s.removeClass(s.widget,"menu")):s.autohide()},this.start=e=>{s.cancelEvent(e,!0),s.is_down=!0,s.dragged=!1;let t=e.touches;t&&(t=t[0])&&(e=t),s.slidable=s.media_w*s.scale<=s.viewport_w,s.startX=e.pageX,s.startY=e.pageY,s.toggleAnimation(s.panel)},this.end=e=>{if(s.cancelEvent(e),s.is_down){if(s.dragged){if(s.slidable&&s.dragged){let t=s.x<-(s.viewport_w/7)&&(s.current_slides.viewport_w/7&&(s.current_slide>1||s.options_infinite);(t||i)&&(s.update_slider(s.current_slide-1,!0,s.x/s.viewport_w*100),t&&s.next()||i&&s.prev()),s.x=0,s.update_panel()}s.toggleAnimation(s.panel,!0)}else s.menu();s.is_down=!1}},this.move=e=>{if(s.cancelEvent(e),s.is_down){let t=e.touches;t&&(t=t[0])&&(e=t);let i=(s.media_w*s.scale-s.viewport_w)/2;s.x-=s.startX-(s.startX=e.pageX),!s.slidable&&(s.x>i?s.x=i:s.x<-i&&(s.x=-i),s.media_h*s.scale>s.viewport_h&&(i=(s.media_h*s.scale-s.viewport_h)/2,s.y-=s.startY-(s.startY=e.pageY),s.y>i?s.y=i:s.y<-i&&(s.y=-i))),s.dragged=!0,s.update_panel(s.x,s.y)}else s.autohide()},this.fullscreen=e=>{let t=s.has_fullscreen();("boolean"!=typeof e||!!t!==e)&&(t?document[s.prefix_exit]():s.widget[s.prefix_request]())},this.theme=e=>{"string"!=typeof e&&(e=s.toggle_theme?"":s.options_theme||"white"),s.toggle_theme!==e&&(s.toggle_theme&&s.removeClass(s.widget,s.toggle_theme),e&&s.addClass(s.widget,e),s.toggle_theme=e)},this.autofit=e=>{"boolean"==typeof e&&(s.toggle_autofit=!e),s.toggle_autofit=1===s.scale&&!s.toggle_autofit,s.toggleClass(s.media,"autofit",s.toggle_autofit),s.setStyle(s.media,"transform",""),s.scale=1,s.x=0,s.y=0,s.update_media_viewport(),s.toggleAnimation(s.panel),s.update_panel()},this.zoom_in=e=>{let t=s.scale/.65;t<=50&&(s.disable_autoresizer(),s.x/=.65,s.y/=.65,s.update_panel(s.x,s.y),s.zoom(t))},this.zoom_out=e=>{let t=.65*s.scale;s.disable_autoresizer(),t>=1&&(1===t?s.x=s.y=0:(s.x*=.65,s.y*=.65),s.update_panel(s.x,s.y),s.zoom(t))},this.zoom=e=>{s.scale=e||1,s.update_scroll()},this.info=()=>{s.footer_visible=!s.footer_visible,s.toggleVisibility(s.footer,s.footer_visible)},this.disable_autoresizer=()=>{s.toggle_autofit&&s.autofit()},this.show_gallery=()=>{s.toggleAnimation(s.widget,!0),s.addClass(s.body,"hide-scrollbars"),s.addClass(s.widget,"show"),s.toggle_listener(!0),s.update_widget_viewport(),s.resize_listener(),s.autohide(),s.options_autoslide&&s.play(!0,!0)},this.download=()=>{s.downloadImage(s.body,s.media)},this.close=(e,t)=>{if(s.hasClass(s.body,"relative")&&!t){s.fullscreen(!1);return}t||setTimeout(function(){s.body.removeChild(s.widget),s.panel=s.media=s.gallery=s.options=s.options_group=s.anchors=s.options_onshow=s.options_onchange=s.options_onclose=s.options_click=null},200),s.removeClass(s.body,"hide-scrollbars"),s.removeClass(s.widget,"show"),s.fullscreen(!1),s.toggle_listener(),s.gallery_next&&(s.media_next.src=""),s.playing&&s.play(),s.media&&s.checkout(s.media),s.hide&&(s.hide=clearTimeout(s.hide)),s.toggle_theme&&s.theme(),s.options_class&&s.removeClass(s.widget,s.options_class),s.options_onclose&&s.options_onclose(),t&&(s.body.removeChild(s.widget),s.panel=s.media=s.gallery=s.options=s.options_group=s.anchors=s.options_onshow=s.options_onchange=s.options_onclose=s.options_click=null)},this.checkout=e=>{if(e._root)e._root.appendChild(e),e._root=null;else{let t=e.parentNode;t&&t.removeChild(e),e=e.src=e.onerror=""}},this.prev=e=>{if(e&&s.autohide(),s.slide_count>1){if(s.current_slide>1)return s.goto(s.current_slide-1);if(s.options_infinite)return s.update_slider(s.slide_count,!0),s.goto(s.slide_count)}},this.next=e=>{if(e&&s.autohide(),s.slide_count>1){if(s.current_slide{if(e!==s.current_slide){s.playing?(clearTimeout(s.playing),s.animate_bar()):s.autohide();let t=e>s.current_slide;return s.current_slide=e,s.setup_page(t),!0}},this.prepare=e=>{let t=s.anchors[s.current_slide-1];s.apply_options(t);let i=n&&n.downlink,o=Math.max(s.viewport_h,s.viewport_w)*l;i&&1200*i{if(s.x=0,s.y=0,s.scale=1,s.media){if(s.media.onerror)s.checkout(s.media);else{let t=s.media;setTimeout(function(){t&&s.media!==t&&(s.checkout(t),t=null)},650),s.prepare_animation(),s.update_panel()}}s.footer&&s.toggleVisibility(s.footer,0),s.prepare(e),s.update_slider(s.current_slide-1),s.removeClass(s.spinner,"error"),s.init_slide(s.current_slide),s.toggleAnimation(s.panel),s.update_panel();let i=s.gallery.title,o=s.parse_option("description"),n=s.parse_option("button"),l=i||o||n;l&&(i&&s.setText(s.title,i),o&&s.setText(s.description,o),n&&s.setText(s.button,n),s.toggleDisplay(s.title,i),s.toggleDisplay(s.description,o),s.toggleDisplay(s.button,n),s.setStyle(s.footer,"transform","all"===s.options_autohide?"":"none")),s.options_autohide||s.addClass(s.widget,"menu"),s.toggleVisibility(s.footer,s.footer_visible&&l),s.toggleVisibility(s.page_prev,s.options_infinite||s.current_slide>1),s.toggleVisibility(s.page_next,s.options_infinite||s.current_slide1?s.current_slide+" / "+s.slide_count:""),s.options_onchange&&s.options_onchange(s.current_slide,s.options)}};"undefined"!=typeof module&&void 0!==module.exports?module.exports=t:e.Spotlight=t}(window); \ No newline at end of file diff --git a/javascript/textualInversion.js b/javascript/textualInversion.js index 99360557..20443fcc 100644 --- a/javascript/textualInversion.js +++ b/javascript/textualInversion.js @@ -1,3 +1,6 @@ + + + function start_training_textual_inversion() { gradioApp().querySelector('#ti_error').innerHTML = ''; diff --git a/javascript/ui.js b/javascript/ui.js index da748446..dbab8334 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -1,1996 +1,323 @@ +// various functions for interaction with ui.py not large enough to warrant putting them in separate files + function set_theme(theme) { - /* - var gradioURL = window.location.href; - if (!gradioURL.includes('?__theme=')) { - window.location.replace(gradioURL + '?__theme=' + theme); - } - */ + var gradioURL = window.location.href; + if (!gradioURL.includes('?__theme=')) { + window.location.replace(gradioURL + '?__theme=' + theme); + } } -var selectedTabItemId = "tab_txt2img"; - function all_gallery_buttons() { - var allGalleryButtons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery].gradio-gallery .thumbnails > .thumbnail-item.thumbnail-small'); - var visibleGalleryButtons = []; - allGalleryButtons.forEach(function(elem) { - if (elem.parentElement.offsetParent) { - visibleGalleryButtons.push(elem); - } - }); - return visibleGalleryButtons; + var allGalleryButtons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery].gradio-gallery .thumbnails > .thumbnail-item.thumbnail-small'); + var visibleGalleryButtons = []; + allGalleryButtons.forEach(function(elem) { + if (elem.parentElement.offsetParent) { + visibleGalleryButtons.push(elem); + } + }); + return visibleGalleryButtons; } function selected_gallery_button() { - var allCurrentButtons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery].gradio-gallery .thumbnail-item.thumbnail-small.selected'); - var visibleCurrentButton = null; - allCurrentButtons.forEach(function(elem) { - if (elem.parentElement.offsetParent) { - visibleCurrentButton = elem; - } - }); - return visibleCurrentButton; + return all_gallery_buttons().find(elem => elem.classList.contains('selected')) ?? null; } function selected_gallery_index() { - var buttons = all_gallery_buttons(); - var button = selected_gallery_button(); - - var result = -1; - buttons.forEach(function(v, i) { - if (v == button) { - result = i; - } - }); - - return result; + return all_gallery_buttons().findIndex(elem => elem.classList.contains('selected')); } function extract_image_from_gallery(gallery) { - if (gallery.length == 0) { - return [null]; - } - if (gallery.length == 1) { - return [gallery[0]]; - } + if (gallery.length == 0) { + return [null]; + } + if (gallery.length == 1) { + return [gallery[0]]; + } - var index = selected_gallery_index(); + var index = selected_gallery_index(); - if (index < 0 || index >= gallery.length) { - // Use the first image in the gallery as the default - index = 0; - } + if (index < 0 || index >= gallery.length) { + // Use the first image in the gallery as the default + index = 0; + } - return [gallery[index]]; + return [gallery[index]]; } window.args_to_array = Array.from; // Compatibility with e.g. extensions that may expect this to be around function switch_to_txt2img() { - gradioApp().querySelector('#tabs').querySelectorAll('button')[0].click(); + gradioApp().querySelector('#tabs').querySelectorAll('button')[0].click(); - return Array.from(arguments); + return Array.from(arguments); } function switch_to_img2img_tab(no) { - gradioApp().querySelector('#tabs').querySelectorAll('button')[1].click(); - gradioApp().getElementById('mode_img2img').querySelectorAll('button')[no].click(); + gradioApp().querySelector('#tabs').querySelectorAll('button')[1].click(); + gradioApp().getElementById('mode_img2img').querySelectorAll('button')[no].click(); } function switch_to_img2img() { - switch_to_img2img_tab(0); - return Array.from(arguments); + switch_to_img2img_tab(0); + return Array.from(arguments); } function switch_to_sketch() { - switch_to_img2img_tab(1); - return Array.from(arguments); + switch_to_img2img_tab(1); + return Array.from(arguments); } function switch_to_inpaint() { - switch_to_img2img_tab(2); - return Array.from(arguments); + switch_to_img2img_tab(2); + return Array.from(arguments); } function switch_to_inpaint_sketch() { - switch_to_img2img_tab(3); - return Array.from(arguments); + switch_to_img2img_tab(3); + return Array.from(arguments); } function switch_to_extras() { - gradioApp().querySelector('#tabs').querySelectorAll('button')[2].click(); + gradioApp().querySelector('#tabs').querySelectorAll('button')[2].click(); - return Array.from(arguments); + return Array.from(arguments); } function get_tab_index(tabId) { - let buttons = gradioApp().getElementById(tabId).querySelector('div').querySelectorAll('button'); - for (let i = 0; i < buttons.length; i++) { - if (buttons[i].classList.contains('selected')) { - return i; - } - } - return 0; + let buttons = gradioApp().getElementById(tabId).querySelector('div').querySelectorAll('button'); + for (let i = 0; i < buttons.length; i++) { + if (buttons[i].classList.contains('selected')) { + return i; + } + } + return 0; } function create_tab_index_args(tabId, args) { - var res = Array.from(args); - res[0] = get_tab_index(tabId); - return res; + var res = Array.from(args); + res[0] = get_tab_index(tabId); + return res; } function get_img2img_tab_index() { - let res = Array.from(arguments); - res.splice(-2); - res[0] = get_tab_index('mode_img2img'); - return res; + let res = Array.from(arguments); + res.splice(-2); + res[0] = get_tab_index('mode_img2img'); + return res; } function create_submit_args(args) { - var res = Array.from(args); + var res = Array.from(args); - // As it is currently, txt2img and img2img send back the previous output args (txt2img_gallery, generation_info, html_info) whenever you generate a new image. - // This can lead to uploading a huge gallery of previously generated images, which leads to an unnecessary delay between submitting and beginning to generate. - // I don't know why gradio is sending outputs along with inputs, but we can prevent sending the image gallery here, which seems to be an issue for some. - // If gradio at some point stops sending outputs, this may break something - if (Array.isArray(res[res.length - 3])) { - res[res.length - 3] = null; - } + // As it is currently, txt2img and img2img send back the previous output args (txt2img_gallery, generation_info, html_info) whenever you generate a new image. + // This can lead to uploading a huge gallery of previously generated images, which leads to an unnecessary delay between submitting and beginning to generate. + // I don't know why gradio is sending outputs along with inputs, but we can prevent sending the image gallery here, which seems to be an issue for some. + // If gradio at some point stops sending outputs, this may break something + if (Array.isArray(res[res.length - 3])) { + res[res.length - 3] = null; + } - return res; + return res; } function showSubmitButtons(tabname, show) { - gradioApp().getElementById(tabname + "_interrupt").style.display = show ? "none" : "flex"; - gradioApp().getElementById(tabname + "_skip").style.display = show ? "none" : "flex"; - gradioApp().getElementById(tabname + "_generate").style.display = !show ? "none" : "flex"; + gradioApp().getElementById(tabname + '_interrupt').style.display = show ? "none" : "block"; + gradioApp().getElementById(tabname + '_skip').style.display = show ? "none" : "block"; } function showRestoreProgressButton(tabname, show) { - var button = gradioApp().getElementById(tabname + "_restore_progress"); - if (!button) return; + var button = gradioApp().getElementById(tabname + "_restore_progress"); + if (!button) return; - button.style.display = show ? "flex" : "none"; + button.style.display = show ? "flex" : "none"; } - function submit() { - showSubmitButtons('txt2img', false); + showSubmitButtons('txt2img', false); - var id = randomId(); - localStorage.setItem("txt2img_task_id", id); + var id = randomId(); + localSet("txt2img_task_id", id); - requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function() { - showSubmitButtons('txt2img', true); - localStorage.removeItem("txt2img_task_id"); - showRestoreProgressButton('txt2img', false); - }); + requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function() { + showSubmitButtons('txt2img', true); + localRemove("txt2img_task_id"); + showRestoreProgressButton('txt2img', false); + }); - var res = create_submit_args(arguments); + var res = create_submit_args(arguments); - res[0] = id; + res[0] = id; - return res; + return res; } function submit_img2img() { - showSubmitButtons('img2img', false); + showSubmitButtons('img2img', false); - var id = randomId(); - localStorage.setItem("img2img_task_id", id); + var id = randomId(); + localSet("img2img_task_id", id); - requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function() { - showSubmitButtons('img2img', true); - localStorage.removeItem("img2img_task_id"); - showRestoreProgressButton('img2img', false); - }); + requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function() { + showSubmitButtons('img2img', true); + localRemove("img2img_task_id"); + showRestoreProgressButton('img2img', false); + }); - var res = create_submit_args(arguments); + var res = create_submit_args(arguments); - res[0] = id; - res[1] = get_tab_index('mode_img2img'); + res[0] = id; + res[1] = get_tab_index('mode_img2img'); - return res; + return res; } function restoreProgressTxt2img() { - showRestoreProgressButton("txt2img", false); - var id = localStorage.getItem("txt2img_task_id"); + showRestoreProgressButton("txt2img", false); + var id = localGet("txt2img_task_id"); - id = localStorage.getItem("txt2img_task_id"); + if (id) { + requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function() { + showSubmitButtons('txt2img', true); + }, null, 0); + } - if (id) { - requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function() { - showSubmitButtons('txt2img', true); - }, null, 0); - } - - return id; + return id; } function restoreProgressImg2img() { - showRestoreProgressButton("img2img", false); + showRestoreProgressButton("img2img", false); - var id = localStorage.getItem("img2img_task_id"); + var id = localGet("img2img_task_id"); - if (id) { - requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function() { - showSubmitButtons('img2img', true); - }, null, 0); - } + if (id) { + requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function() { + showSubmitButtons('img2img', true); + }, null, 0); + } - return id; + return id; } + onUiLoaded(function() { - showRestoreProgressButton('txt2img', localStorage.getItem("txt2img_task_id")); - showRestoreProgressButton('img2img', localStorage.getItem("img2img_task_id")); + showRestoreProgressButton('txt2img', localGet("txt2img_task_id")); + showRestoreProgressButton('img2img', localGet("img2img_task_id")); }); function modelmerger() { - var id = randomId(); - requestProgress(id, gradioApp().getElementById('modelmerger_results_panel'), null, function() {}); + var id = randomId(); + requestProgress(id, gradioApp().getElementById('modelmerger_results_panel'), null, function() {}); - var res = create_submit_args(arguments); - res[0] = id; - return res; + var res = create_submit_args(arguments); + res[0] = id; + return res; } function ask_for_style_name(_, prompt_text, negative_prompt_text) { - var name_ = prompt('Style name:'); - return [name_, prompt_text, negative_prompt_text]; + var name_ = prompt('Style name:'); + return [name_, prompt_text, negative_prompt_text]; } function confirm_clear_prompt(prompt, negative_prompt) { - if (confirm("Delete prompt?")) { - prompt = ""; - negative_prompt = ""; - } + if (confirm("Delete prompt?")) { + prompt = ""; + negative_prompt = ""; + } - return [prompt, negative_prompt]; -} - - -var promptTokecountUpdateFuncs = {}; - -function recalculatePromptTokens(name) { - if (promptTokecountUpdateFuncs[name]) { - promptTokecountUpdateFuncs[name](); - } -} - -function recalculate_prompts_txt2img() { - recalculatePromptTokens('txt2img_prompt'); - recalculatePromptTokens('txt2img_neg_prompt'); - return Array.from(arguments); -} - -function recalculate_prompts_img2img() { - recalculatePromptTokens('img2img_prompt'); - recalculatePromptTokens('img2img_neg_prompt'); - return Array.from(arguments); + return [prompt, negative_prompt]; } var opts = {}; -onUiUpdate(function() { - - if (Object.keys(opts).length != 0) return; +onAfterUiUpdate(function() { + if (Object.keys(opts).length != 0) return; - var json_elem = gradioApp().getElementById("settings_json"); - if (json_elem == null) return; + var json_elem = gradioApp().getElementById('settings_json'); + if (json_elem == null) return; - var textarea = json_elem.querySelector("textarea"); - var jsdata = textarea.value; - opts = JSON.parse(jsdata); - executeCallbacks(optionsChangedCallbacks); + var textarea = json_elem.querySelector('textarea'); + var jsdata = textarea.value; + opts = JSON.parse(jsdata); - Object.defineProperty(textarea, "value", { - set: function (newValue) { - var valueProp = Object.getOwnPropertyDescriptor( - HTMLTextAreaElement.prototype, - "value" - ); - var oldValue = valueProp.get.call(textarea); - valueProp.set.call(textarea, newValue); + executeCallbacks(optionsChangedCallbacks); /*global optionsChangedCallbacks*/ - if (oldValue != newValue) { - opts = JSON.parse(textarea.value); - } + Object.defineProperty(textarea, 'value', { + set: function(newValue) { + var valueProp = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value'); + var oldValue = valueProp.get.call(textarea); + valueProp.set.call(textarea, newValue); - executeCallbacks(optionsChangedCallbacks); - }, - get: function () { - var valueProp = Object.getOwnPropertyDescriptor( - HTMLTextAreaElement.prototype, - "value" - ); - return valueProp.get.call(textarea); - }, - }); - - //console.log(opts); - - json_elem.parentElement.style.display = "none"; - - function registerTextarea(id, id_counter, id_button) { - var prompt = gradioApp().getElementById(id); - var counter = gradioApp().getElementById(id_counter); - var textarea = gradioApp().querySelector("#" + id + " > label > textarea"); - - if (counter.parentElement == prompt.parentElement) { - return; - } - - prompt.parentElement.insertBefore(counter, prompt); - counter.classList.add("token-counter"); - prompt.parentElement.style.position = "relative"; - - promptTokecountUpdateFuncs[id] = function () { - update_token_counter(id_button); - }; - textarea.addEventListener("input", promptTokecountUpdateFuncs[id]); - } - - registerTextarea( - "txt2img_prompt", - "txt2img_token_counter", - "txt2img_token_button" - ); - registerTextarea( - "txt2img_neg_prompt", - "txt2img_negative_token_counter", - "txt2img_negative_token_button" - ); - registerTextarea( - "img2img_prompt", - "img2img_token_counter", - "img2img_token_button" - ); - registerTextarea( - "img2img_neg_prompt", - "img2img_negative_token_counter", - "img2img_negative_token_button" - ); - - var show_all_pages = gradioApp().getElementById("settings_show_all_pages"); - var settings_tabs = gradioApp().querySelector("#settings div"); - if (show_all_pages && settings_tabs) { - settings_tabs.appendChild(show_all_pages); - show_all_pages.onclick = function () { - gradioApp() - .querySelectorAll("#settings > div") - .forEach(function (elem) { - elem.style.display = "block"; - }); - gradioApp() - .querySelectorAll("#settings > div > div > div") - .forEach(function (elem) { - elem.style.maxHeight = "none"; - }); - }; - } - /* - ^ matches the start - * matches any position - $ matches the end - */ - /* anapnoe ui start */ - - /* auto grow textarea */ - function autoGrowPromptTextarea() { - gradioApp() - .querySelectorAll('[id$="_prompt"] textarea') - .forEach(function (elem) { - elem.parentElement.click(); - }); - } - - gradioApp() - .querySelectorAll( - '[id$="_prompt"] textarea, [id^="setting_"] textarea, textarea' - ) - .forEach(function (elem) { - elem.style.boxSizing = "border-box"; - var offset = elem.offsetHeight - elem.clientHeight; - elem.addEventListener("input", function (e) { - e.target.style.minHeight = "auto"; - e.target.style.minHeight = e.target.scrollHeight + offset + 2 + "px"; - }); - - elem.parentElement.addEventListener("click", function (e) { - let textarea = e.currentTarget.querySelector("textarea"); - textarea.style.minHeight = "auto"; - textarea.style.minHeight = textarea.scrollHeight + offset + 2 + "px"; - }); - }); - - /* resizable split view */ - const resizeEvent = window.document.createEvent("UIEvents"); - resizeEvent.initUIEvent("resize", true, false, window, 0); - - gradioApp() - .querySelectorAll('[id $="2img_splitter"]') - .forEach((elem) => { - elem.addEventListener("mousedown", function (e) { - e.preventDefault(); - - let resizer = e.currentTarget; - let container = resizer.parentElement; - - let flexDir = window - .getComputedStyle(container) - .getPropertyValue("flex-direction"); - - let leftSide = resizer.previousElementSibling; - let rightSide = resizer.nextElementSibling; - - let dir = flexDir == "row-reverse" ? -1.0 : 1.0; - - let x = e.clientX; - let y = e.clientY; - let leftWidth = leftSide.getBoundingClientRect().width; - - function mouseMoveHandler(e) { - resizer.style.cursor = "col-resize"; - container.style.cursor = "col-resize"; - - const dx = (e.clientX - x) * dir; - const dy = (e.clientY - y) * dir; - - const newLeftWidth = - ((leftWidth + dx) * 100) / container.getBoundingClientRect().width; - leftSide.style.flexBasis = `${newLeftWidth}%`; - leftSide.style.userSelect = "none"; - leftSide.style.pointerEvents = "none"; - rightSide.style.userSelect = "none"; - rightSide.style.pointerEvents = "none"; - //window.dispatchEvent(resizeEvent); - } - - function mouseUpHandler() { - resizer.style.removeProperty("cursor"); - container.style.removeProperty("cursor"); - leftSide.style.removeProperty("user-select"); - leftSide.style.removeProperty("pointer-events"); - rightSide.style.removeProperty("user-select"); - rightSide.style.removeProperty("pointer-events"); - container.removeEventListener("mousemove", mouseMoveHandler); - container.removeEventListener("mouseup", mouseUpHandler); - //window.dispatchEvent(resizeEvent); - } - - container.addEventListener("mousemove", mouseMoveHandler); - container.addEventListener("mouseup", mouseUpHandler); - }); - - let flex_reverse = false; - elem.addEventListener("dblclick", function (e) { - flex_reverse = !flex_reverse; - e.preventDefault(); - - let resizer = e.currentTarget; - let container = resizer.parentElement; - //let flexDir = window.getComputedStyle(container).getPropertyValue('flex-direction'); - - if (flex_reverse) { - container.style.flexDirection = "row-reverse"; - } else { - container.style.flexDirection = "row"; - } - }); - }); - - // set this globally - //let selectedTabItemId = "tab_txt2img"; - /* switch tab item from instance button, this is the only method that works i havent found a workaround yet */ - const Observe = (sel, opt, cb) => { - const Obs = new MutationObserver((m) => [...m].forEach(cb)); - gradioApp() - .querySelectorAll(sel) - .forEach((el) => Obs.observe(el, opt)); - }; - Observe( - "#tabs > div.tabitem", - { - attributesList: ["style"], - attributeOldValue: true, - }, - (m) => { - if (m.target.style.display === "block") { - let idx = parseInt(m.target.getAttribute("tab-item")); - selectedTabItemId = m.target.id; - tabItemChanged(idx); - } - } - ); - - function tabItemChanged(idx) { - gradioApp() - .querySelectorAll( - "#tabs > div > button.selected, #nav_menu_header_tabs > button.selected" - ) - .forEach(function (tab) { - tab.classList.remove("selected"); - }); - - gradioApp() - .querySelectorAll( - "#tabs > div > button:nth-child(" + - (idx + 1) + - "), #nav_menu_header_tabs > button:nth-child(" + - (idx + 1) + - ")" - ) - .forEach(function (tab) { - tab.classList.add("selected"); - }); - //gardio removes listeners and attributes from tab buttons we add them again here - gradioApp() - .querySelectorAll("#tabs > div > button") - .forEach(function (tab, index) { - tab.setAttribute("tab-id", index); - tab.removeEventListener("mouseup", navTabClicked); - tab.addEventListener("mouseup", navTabClicked); - if (tab.innerHTML.indexOf("Theme") != -1) tab.style.display = "none"; - }); - - /* gradioApp().querySelectorAll('[id^="image_buttons_"] button').forEach(function (elem){ - - if(elem.id == "txt2img_tab"){ - elem.setAttribute("tab-id", 0); - elem.removeEventListener('click', navTabClicked); - elem.addEventListener('click', navTabClicked); - }else if(elem.id == "img2img_tab" || elem.id == "inpaint_tab"){ - elem.setAttribute("tab-id", 1); - elem.removeEventListener('click', navTabClicked); - elem.addEventListener('click', navTabClicked); - }if(elem.id == "extras_tab"){ - elem.setAttribute("tab-id", 2); - elem.removeEventListener('click', navTabClicked); - elem.addEventListener('click', navTabClicked); - } - }) */ - - /* const pdiv = gradioApp().querySelector("#"+selectedTabItemId+" .progressDiv"); - if(!pdiv && selectedTabItemId == "tab_txt2img"){ - showSubmitButtons('txt2img', true); - }else if(!pdiv && selectedTabItemId == "tab_img2img"){ - showSubmitButtons('img2img', true); - } */ - - //window.onUiHeaderTabUpdate(); - // also here the same issue - /* - gradioApp().querySelectorAll('[id^="image_buttons"] [id$="_tab"]').forEach(function (button, index){ - if(button.id == ("img2img_tab" || "inpaint_tab") ){ - button.setAttribute("tab-id", 1 ); - }else if(button.id == "extras_tab"){ - button.setAttribute("tab-id", 2 ); - } - - button.removeEventListener('mouseup', navTabClicked); - button.addEventListener('mouseup', navTabClicked); - - - }) - */ - - netMenuVisibility(); - } - - // menu - function disableScroll() { - scrollTop = window.pageYOffset || document.documentElement.scrollTop; - scrollLeft = window.pageXOffset || document.documentElement.scrollLeft; - window.scrollTo(scrollLeft, scrollTop); - - window.onscroll = function () { - window.scrollTo(scrollLeft, scrollTop); - }; - } - - function enableScroll() { - window.onscroll = function () {}; - } - - function getPos(el) { - let rect = el.getBoundingClientRect(); - return { x: rect.left, y: rect.top }; - } - - function toggleMenu(isopen, icon, panel, func) { - if (isopen) { - panel.classList.add("open"); - icon.classList.add("fixed"); - gradioApp().addEventListener("click", func); - disableScroll(); - } else { - panel.classList.remove("open"); - icon.classList.remove("fixed"); - gradioApp().removeEventListener("click", func); - enableScroll(); - } - } - - // close aside views - function closeAsideViews(menu) { - if (quick_menu != menu && quick_menu_open) quick_menu.click(); - if (net_menu != menu && net_menu_open) net_menu.click(); - if (theme_menu != menu && theme_menu_open) theme_menu.click(); - } - - // if we change to other view other than 2img and if aside mode is selected close extra networks aside and hide the net menu icon - let net_container = gradioApp().querySelector("#txt2img_extra_networks_row"); - let net_menu_open = false; - const net_menu = gradioApp().querySelector("#extra_networks_menu"); - function netMenuVisibility() { - if (selectedTabItemId.indexOf("2img") != -1) { - net_menu.style.display = "block"; - let nid = selectedTabItemId.split("_")[1]; - net_container = gradioApp().querySelector( - "#" + nid + "_extra_networks_row" - ); - if (net_container.classList.contains("aside")) { - net_menu.style.display = "block"; - toggleMenu(net_menu_open, net_menu, net_container, null); - } else { - net_menu.style.display = "none"; - } - } else { - net_menu.style.display = "none"; - } - } - - // mobile nav menu - const tabs_menu = gradioApp().querySelector("#tabs > div:first-child"); - const nav_menu = gradioApp().querySelector("#nav_menu"); - const header = gradioApp().querySelector("#header-top"); - let menu_open = false; - const z_menu = nav_menu.cloneNode(true); - z_menu.id = "clone_nav_menu"; - header.parentElement.append(z_menu); - function toggleNavMenu(e) { - e.stopPropagation(); - menu_open = !menu_open; - toggleMenu(menu_open, nav_menu, tabs_menu, toggleNavMenu); - } - z_menu.addEventListener("click", toggleNavMenu); - - // quicksettings nav menu - let quick_menu_open = false; - const quicksettings_overflow = gradioApp().querySelector( - "#quicksettings_overflow" - ); - const quick_menu = gradioApp().querySelector("#quick_menu"); - function toggleQuickMenu(e) { - closeAsideViews(quick_menu); - quick_menu_open = !quick_menu_open; - const withinBoundaries = e.composedPath().includes(quicksettings_overflow); - if (!quick_menu_open && withinBoundaries) { - quick_menu_open = true; - } else { - e.preventDefault(); - e.stopPropagation(); - toggleMenu( - quick_menu_open, - quick_menu, - quicksettings_overflow, - toggleQuickMenu - ); - } - } - quick_menu.addEventListener("click", toggleQuickMenu); - - // extra networks nav menu - function clickedOutside(e) { - //console.log(e.target.getAttribute('data-testid')); - if ( - e.target.getAttribute("data-testid") != "textbox" && - e.target.getAttribute("data-testid") - ) { - if (net_menu_open && e.target.closest('[id$="2img_settings_scroll"]')) - net_menu.click(); - } - } - function toggleNetMenu(e) { - closeAsideViews(net_menu); - net_menu_open = !net_menu_open; - e.preventDefault(); - e.stopPropagation(); - toggleMenu(net_menu_open, net_menu, net_container, clickedOutside); - } - net_menu.addEventListener("click", toggleNetMenu); - gradioApp() - .querySelectorAll('button[id*="2img_extra_networks"]') - .forEach((elem) => { - elem.addEventListener("click", toggleNetMenu); - }); - - // theme nav menu - let theme_container = gradioApp().querySelector("#tab_ui_theme"); - let theme_menu_open = false; - const theme_menu = gradioApp().querySelector("#theme_menu"); - function toggleThemeMenu(e) { - closeAsideViews(theme_menu); - theme_menu_open = !theme_menu_open; - e.preventDefault(); - e.stopPropagation(); - toggleMenu(theme_menu_open, theme_menu, theme_container, null); - } - theme_menu.addEventListener("click", toggleThemeMenu); - - const theme_tab = gradioApp().querySelector("#tab_ui_theme"); - function theme_aside(value) { - if (value) { - theme_tab.classList.add("aside"); - } else { - theme_tab.classList.remove("aside"); - } - } - if (theme_tab) theme_aside(true); - - function generateOnRepeat(elem, isforever) { - if (elem.generateOnRepeatInterval != null) - clearInterval(elem.generateOnRepeatInterval); - if (isforever) { - const generate_button = elem.previousElementSibling; - elem.generateOnRepeatInterval = setInterval(function () { - if (window.getComputedStyle(generate_button).display !== "none") { - generate_button.click(); - } - }, 500); - } - } - - function toggleGenerateForever(e) { - e.target.classList.toggle("active"); - if (e.target.className.indexOf("active") != -1) { - generateOnRepeat(e.target, true); - } else { - generateOnRepeat(e.target, false); - } - e.preventDefault(); - e.stopPropagation(); - } - - gradioApp() - .querySelectorAll('button[id*="2img_generate_forever"]') - .forEach((elem) => { - elem.addEventListener("click", toggleGenerateForever); - }); - - /* let generateOnRepeat = function (genbuttonid, interruptbuttonid) { - let genbutton = gradioApp().querySelector(genbuttonid); - let interruptbutton = gradioApp().querySelector(interruptbuttonid); - if (!interruptbutton.offsetParent) { - genbutton.click(); - } - clearInterval(window.generateOnRepeatInterval); - window.generateOnRepeatInterval = setInterval(function () { - if (!interruptbutton.offsetParent) { - genbutton.click(); - } - }, 500); - }; - - appendContextMenuOption("#txt2img_generate", "Generate forever", function () { - generateOnRepeat("#txt2img_generate", "#txt2img_interrupt"); - }); - appendContextMenuOption("#img2img_generate", "Generate forever", function () { - generateOnRepeat("#img2img_generate", "#img2img_interrupt"); - }); - - let cancelGenerateForever = function () { - clearInterval(window.generateOnRepeatInterval); - }; */ - - // - /* - function attachAccordionListeners(elem) { - elem.querySelectorAll(".gradio-accordion > div.wrap").forEach((elem) => { - elem.addEventListener("click", toggleAccordion); - }); - } - function toggleAccordion(e) { - //e.preventDefault(); - //e.stopPropagation(); - //e.stopImmediatePropagation(); - let accordion_content = - e.currentTarget.parentElement.querySelector(".gap.svelte-vt1mxs"); - - if (accordion_content) { - e.preventDefault(); - e.stopPropagation(); - let accordion_icon = e.currentTarget.parentElement.querySelector( - ".label-wrap > .icon" - ); - if (accordion_content.className.indexOf("hidden") != -1) { - accordion_content.classList.remove("hidden"); - accordion_icon.style.setProperty("transform", "rotate(0deg)"); - e.currentTarget.classList.add("hide"); - } else { - accordion_content.classList.add("hidden"); - accordion_icon.style.setProperty("transform", "rotate(90deg)"); - e.currentTarget.classList.remove("hide"); - } - } else { - let accordion_btn = - e.currentTarget.parentElement.querySelector(".label-wrap"); - e.currentTarget.classList.add("hide"); - accordion_btn.click(); - //maybe here we need to setInterval until the content is available - setTimeout(function () { - accordion_content = accordion_btn.parentElement; - //console.log(accordion_content); - attachAccordionListeners(accordion_content); - }, 1000); - } - } - - attachAccordionListeners(gradioApp()); - */ - // additional ui styles - let styleobj = {}; - const r = gradioApp(); - const style = document.createElement("style"); - style.id = "ui-styles"; - r.appendChild(style); - - function updateOpStyles() { - let ops_styles = ""; - for (const key in styleobj) { - ops_styles += styleobj[key]; - } - const ui_styles = gradioApp().getElementById("ui-styles"); - ui_styles.innerHTML = ops_styles; - //console.log(ui_styles); - } - - // generated image fit contain - scale - function imageGeneratedFitMethod(value) { - styleobj.ui_view_fit = - "[id$='2img_gallery'] div>img {object-fit:" + value + "!important;}"; - } - gradioApp() - .querySelector("#setting_ui_output_image_fit") - .addEventListener("click", function (e) { - if (e.target && e.target.matches("input[type='radio']")) { - imageGeneratedFitMethod(e.target.value.toLowerCase()); - updateOpStyles(); - } - }); - imageGeneratedFitMethod(opts.ui_output_image_fit.toLowerCase()); - - // livePreview fit contain - scale - function imagePreviewFitMethod(value) { - styleobj.ui_fit = ".livePreview img {object-fit:" + value + "!important;}"; - } - gradioApp() - .querySelector("#setting_live_preview_image_fit") - .addEventListener("click", function (e) { - if (e.target && e.target.matches("input[type='radio']")) { - imagePreviewFitMethod(e.target.value.toLowerCase()); - updateOpStyles(); - } - }); - imagePreviewFitMethod(opts.live_preview_image_fit.toLowerCase()); - - // viewports order left - right - function viewportOrder(value) { - styleobj.ui_views_order = - "[id$=_prompt_image] + div {flex-direction:" + value + ";}"; - } - gradioApp() - .querySelector("#setting_ui_views_order") - .addEventListener("click", function (e) { - if (e.target && e.target.matches("input[type='radio']")) { - viewportOrder(e.target.value.toLowerCase()); - updateOpStyles(); - } - }); - viewportOrder(opts.ui_views_order.toLowerCase()); - - // sd max resolution output - function sdMaxOutputResolution(value) { - gradioApp() - .querySelectorAll('[id$="2img_width"] input,[id$="2img_height"] input') - .forEach((elem) => { - elem.max = value; - }); - } - gradioApp() - .querySelector("#setting_sd_max_resolution") - .addEventListener("input", function (e) { - let intvalue = parseInt(e.target.value); - intvalue = Math.min(Math.max(intvalue, 512), 16384); - sdMaxOutputResolution(intvalue); - }); - sdMaxOutputResolution(opts.sd_max_resolution); - - function extra_networks_visibility(value) { - gradioApp() - .querySelectorAll('[id$="2img_extra_networks_row"]') - .forEach((elem) => { - if (value) { - elem.classList.remove("!hidden"); - } else { - elem.classList.add("!hidden"); - } - }); - } - gradioApp() - .querySelector("#setting_extra_networks_default_visibility input") - .addEventListener("click", function (e) { - extra_networks_visibility(e.target.checked); - }); - extra_networks_visibility(opts.extra_networks_default_visibility); - - function extra_networks_card_size(value) { - styleobj.extra_networks_card_size = - ":root{--ae-extra-networks-card-size:" + value + ";}"; - } - gradioApp() - .querySelectorAll("#setting_extra_networks_cards_size input") - .forEach(function (elem) { - elem.addEventListener("input", function (e) { - extra_networks_card_size(e.target.value); - updateOpStyles(); - }); - }); - extra_networks_card_size(opts.extra_networks_cards_size); - - function extra_networks_cards_visible_rows(value) { - styleobj.extra_networks_cards_visible_rows = - ":root{--ae-extra-networks-visible-rows:" + value + ";}"; - } - gradioApp() - .querySelectorAll("#setting_extra_networks_cards_visible_rows input") - .forEach(function (elem) { - elem.addEventListener("input", function (e) { - extra_networks_cards_visible_rows(e.target.value); - updateOpStyles(); - }); - }); - extra_networks_cards_visible_rows(opts.extra_networks_cards_visible_rows); - - function extra_networks_aside(value) { - gradioApp() - .querySelectorAll('[id$="2img_extra_networks_row"]') - .forEach((elem) => { - if (value) { - elem.classList.add("aside"); - } else { - elem.classList.remove("aside"); - } - netMenuVisibility(); - }); - } - gradioApp() - .querySelector("#setting_extra_networks_aside input") - .addEventListener("click", function (e) { - extra_networks_aside(e.target.checked); - }); - extra_networks_aside(opts.extra_networks_aside); - - // hidden - header ui tabs - let radio_hidden_html = ""; - let radio_header_html = ""; - let hiddentabs = {}; - let headertabs = {}; - const setting_ui_hidden_tabs = gradioApp().querySelector( - "#setting_ui_hidden_tabs textarea" - ); - const setting_ui_header_tabs = gradioApp().querySelector( - "#setting_ui_header_tabs textarea" - ); - const parent_header_tabs = gradioApp().querySelector("#nav_menu_header_tabs"); - setting_ui_hidden_tabs.style.display = "none"; - setting_ui_header_tabs.style.display = "none"; - - const maintabs = gradioApp().querySelectorAll( - "#tabs > div:first-child > button" - ); - const tabitems = gradioApp().querySelectorAll("#tabs > div.tabitem"); - - function tabOpsSave(setting) { - updateInput(setting); - - } - - function tabsHiddenChange() { - const keys = Object.keys(hiddentabs); - setting_ui_hidden_tabs.value = ""; - keys.forEach((key, index) => { - //console.log(`${key}: ${hiddentabs[key]} ${index}`); - if (hiddentabs[key] == true) { - styleobj[key] = - "#tabs > div:first-child > button:nth-child(" + - (index + 1) + - "){display:none;}"; - setting_ui_hidden_tabs.value += key + ","; - } else { - styleobj[key] = - "#tabs > div:first-child > button:nth-child(" + - (index + 1) + - "){display:block;}"; - } - }); - - tabOpsSave(setting_ui_hidden_tabs); - tabsHeaderChange(); - } - function tabsHeaderChange() { - const keys = Object.keys(headertabs); - setting_ui_header_tabs.value = ""; - keys.forEach((key, index) => { - //console.log(`${key}: ${hiddentabs[key]} ${index}`); - let nkey = key + "_hr"; - if (headertabs[key] == true && hiddentabs[key] == false) { - styleobj[nkey] = - "#nav_menu_header_tabs > button:nth-child(" + - (index + 1) + - "){display:block;}"; - setting_ui_header_tabs.value += key + ","; - } else { - styleobj[nkey] = - "#nav_menu_header_tabs > button:nth-child(" + - (index + 1) + - "){display:none;}"; - } - }); - - tabOpsSave(setting_ui_header_tabs); - } - - function navigate2TabItem(idx) { - gradioApp() - .querySelectorAll("#tabs > div.tabitem") - .forEach(function (tabitem, index) { - if (idx == index) { - tabitem.style.display = "block"; - } else { - tabitem.style.display = "none"; - } - }); - } - - function navTabClicked(e) { - const idx = parseInt(e.currentTarget.getAttribute("tab-id")); - navigate2TabItem(idx); - } - - maintabs.forEach(function (elem, index) { - let tabvalue = elem.innerText.replaceAll(" ", ""); - hiddentabs[tabvalue] = false; - headertabs[tabvalue] = false; - let checked_hidden = ""; - let checked_header = ""; - if (setting_ui_hidden_tabs.value.indexOf(tabvalue) != -1) { - hiddentabs[tabvalue] = true; - checked_hidden = "checked"; - } - if (setting_ui_header_tabs.value.indexOf(tabvalue) != -1) { - headertabs[tabvalue] = true; - checked_header = "checked"; - } - radio_hidden_html += - '"; - - radio_header_html += - '"; - - tabitems[index].setAttribute("tab-item", index); - elem.setAttribute("tab-id", index); - - let clonetab = elem.cloneNode(true); - clonetab.id = tabvalue + "_clone"; - parent_header_tabs.append(clonetab); - clonetab.addEventListener("click", navTabClicked); - }); - - let div = document.createElement("div"); - div.id = "hidden_radio_tabs_container"; - div.classList.add("flex", "flex-wrap", "gap-2", "wrap", "svelte-1qxcj04"); - div.innerHTML = radio_hidden_html; - setting_ui_hidden_tabs.parentElement.appendChild(div); - - div = document.createElement("div"); - div.id = "header_radio_tabs_container"; - div.classList.add("flex", "flex-wrap", "gap-2", "wrap", "svelte-1qxcj04"); - div.innerHTML = radio_header_html; - setting_ui_header_tabs.parentElement.appendChild(div); - - // hidden tabs - gradioApp() - .querySelector("#hidden_radio_tabs_container") - .addEventListener("click", function (e) { - if (e.target && e.target.matches("input[type='checkbox']")) { - let tabvalue = e.target.value.replaceAll(" ", ""); - hiddentabs[tabvalue] = e.target.checked; - tabsHiddenChange(); - updateOpStyles(); - } - }); - // header tabs - gradioApp() - .querySelector("#header_radio_tabs_container") - .addEventListener("click", function (e) { - if (e.target && e.target.matches("input[type='checkbox']")) { - let tabvalue = e.target.value.replaceAll(" ", ""); - headertabs[tabvalue] = e.target.checked; - tabsHeaderChange(); - updateOpStyles(); - } - }); - - tabsHiddenChange(); - - gradioApp() - .querySelectorAll('[id^="image_buttons_"] button, #png_2img_results button') - .forEach(function (elem) { - //console.log(opts.send_seed); - if (elem.id == "txt2img_tab") { - elem.setAttribute("tab-id", 0); - elem.addEventListener("click", navTabClicked); - } else if (elem.id == "img2img_tab" || elem.id == "inpaint_tab") { - elem.setAttribute("tab-id", 1); - elem.addEventListener("click", navTabClicked); - } - if (elem.id == "extras_tab") { - elem.setAttribute("tab-id", 2); - elem.addEventListener("click", navTabClicked); - } - }); - - gradioApp() - .querySelectorAll('[id$="2img_extra_tabs"] .search') - .forEach(function (elem) { - elem.addEventListener("keyup", function (e) { - if (e.defaultPrevented) { - return; // Do nothing if event already handled - } - switch (e.code) { - case "Escape": - if (e.target.value == "") { - net_menu.click(); - } else { - e.target.value = ""; - updateInput(e.target); + if (oldValue != newValue) { + opts = JSON.parse(textarea.value); } - break; + + executeCallbacks(optionsChangedCallbacks); + }, + get: function() { + var valueProp = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value'); + return valueProp.get.call(textarea); } - }); }); - // add - remove quicksettings - const settings_submit = gradioApp().querySelector("#settings_submit"); - const quick_parent = gradioApp().querySelector( - "#quicksettings_overflow_container" - ); - const setting_quicksettings = gradioApp().querySelector( - "#setting_quicksettings textarea" - ); - function saveQuickSettings() { - updateInput(setting_quicksettings); - const cEvent = new Event("click"); //submit - Object.defineProperty(cEvent, "target", { value: settings_submit }); - settings_submit.dispatchEvent(cEvent); - console.log(setting_quicksettings.value); - } + json_elem.parentElement.style.display = "none"; - /* - function addModelCheckpoint(){ - if(setting_quicksettings.value.indexOf("sd_model_checkpoint") === -1){ - setting_quicksettings.value += ",sd_model_checkpoint"; - } - saveQuickSettings(); - } - */ + setupTokenCounters(); - function add2quickSettings(id, section, checked) { - let field_settings = setting_quicksettings.value.replace(" ", ""); - if (checked) { - field_settings += "," + id; - let setting_row = gradioApp().querySelector("#row_setting_" + id); - quick_parent.append(setting_row); - setting_row.classList.add("warning"); - } else { - field_settings = field_settings.replaceAll(id, ","); - const setting_parent = gradioApp().querySelector( - "#" + section + "_settings_2img_settings" - ); - let quick_row = gradioApp().querySelector("#row_setting_" + id); - setting_parent.append(quick_row); - quick_row.classList.remove("warning"); + var show_all_pages = gradioApp().getElementById('settings_show_all_pages'); + var settings_tabs = gradioApp().querySelector('#settings div'); + if (show_all_pages && settings_tabs) { + settings_tabs.appendChild(show_all_pages); + show_all_pages.onclick = function() { + gradioApp().querySelectorAll('#settings > div').forEach(function(elem) { + if (elem.id == "settings_tab_licenses") { + return; + } + + elem.style.display = "block"; + }); + }; } - field_settings = field_settings.replace(/,{2,}/g, ","); - setting_quicksettings.value = field_settings; - //addModelCheckpoint(); - saveQuickSettings(); - //console.log(section + " - " + id + " - " + checked); - } - gradioApp() - .querySelectorAll('[id*="add2quick_"]') - .forEach(function (elem) { - let trg = elem.id.split("_add2quick_setting_"); - let sid = trg[0]; - let tid = trg[1]; - let elem_input = gradioApp().querySelector("#" + elem.id + " input"); - if (elem_input) { - elem_input.addEventListener("click", function (e) { - add2quickSettings(tid, sid, e.target.checked); - }); - } - }); - //addModelCheckpoint(); - - // input release component dispatcher - let cached_clone_range; - let cached_clone_num; - let active_clone_input = []; - let focus_input; - - function ui_input_release_component(elem) { - //console.log("ok"); - if (active_clone_input.length > 0) return; - - //img2img_width - let parent = elem.parentElement; - let comp_parent = parent.parentElement.parentElement; - - if ( - comp_parent.id == "img2img_width" || - comp_parent.id == "img2img_height" || - comp_parent.id == "img2img_scale" || - comp_parent.id.indexOf("--ae-") != -1 || - comp_parent.id.indexOf("theme") != -1 || - comp_parent.className.indexOf("posex") != -1 - ) - return; - - let clone_num = elem.cloneNode(); - active_clone_input.push(clone_num); - - let label = parent.querySelector("label"); - - clone_num.id = "num_clone"; - clone_num.value = elem.value; - parent.append(clone_num); - elem.classList.add("hidden"); - - clone_num.addEventListener("change", function (e) { - elem.value = clone_num.value; - updateInput(elem); - }); - - clone_num.addEventListener("focus", function (e) { - focus_input = clone_num; - }); - - cached_clone_num = clone_num; - cached_clone_range = false; - - if (label) { - let comp_range = comp_parent.querySelector("input[type='range']"); - let clone_range = comp_range.cloneNode(); - active_clone_input.push(clone_range); - - clone_range.id = comp_range.id + "_clone"; - clone_range.value = comp_range.value; - comp_range.parentElement.append(clone_range); - comp_range.classList.add("hidden"); - - clone_range.addEventListener("input", function (e) { - clone_num.value = e.target.value; - }); - clone_range.addEventListener("change", function (e) { - elem.value = clone_range.value; - updateInput(elem); - }); - clone_num.addEventListener("input", function (e) { - clone_range.value = e.target.value; - }); - - cached_clone_range = clone_range; - } - } - /* function ui_input_focus_handler(e) { - if (e.target != focus_input) { - focus_input = false; - ui_input_release_handler(e); - } - } */ - - function ui_input_release_handler(e) { - const len = active_clone_input.length; - if (focus_input) { - return; - } - if (len > 0) { - if (e.target.id.indexOf("_clone") == -1) { - for (var i = len - 1; i >= 0; i--) { - let relem = active_clone_input[i]; - relem.previousElementSibling.classList.remove("hidden"); - relem.remove(); - active_clone_input.pop(); - } - } - } - - let elem_type = e.target.tagName; - if (elem_type == "INPUT") { - let elem = e.target; - if (elem.type == "number") { - ui_input_release_component(elem); - } else if (elem.type == "range") { - elem = e.target.parentElement.querySelector("input[type='number']"); - if (elem) { - ui_input_release_component(elem); - } - } - } - } - let timeoutId; - - function ui_input_touchmove_handler(e) { - if (cached_clone_range && cached_clone_num) { - if (e.touches) { - const rect = cached_clone_range.getBoundingClientRect(); - const xoffset_min = rect.left + window.scrollX; - //const xoffset_max = (rect.right + window.scrollX); - //const yoffset_min = (rect.top + window.scrollY); - //const yoffset_max = (rect.bottom + window.scrollY); - //if( e.touches[0].pageY > yoffset_min && e.touches[0].pageY < yoffset_max && e.touches[0].pageX > xoffset_min && e.touches[0].pageX < xoffset_max){ - e.preventDefault(); - const percent = - parseInt(((e.touches[0].pageX - xoffset_min) / rect.width) * 10000) / - 10000; - cached_clone_range.value = - percent * (cached_clone_range.max - cached_clone_range.min) + - parseFloat(cached_clone_range.min); - cached_clone_num.value = cached_clone_range.value; - //} - } - } - } - function ui_input_touchend_handler(e) { - if (cached_clone_range && cached_clone_num) { - const elem = cached_clone_range.previousElementSibling; - elem.value = cached_clone_range.value; - updateInput(elem); - } - } - - function slider_contextmenu(e) { - e.preventDefault(); - } - function slider_touchend(e) { - if (timeoutId) clearTimeout(timeoutId); - } - function slider_touchmove(e) { - if (timeoutId) clearTimeout(timeoutId); - } - function slider_touchstart(e) { - const gcontainer = e.target; - //gcontainer.removeEventListener('contextmenu', slider_contextmenu); - gcontainer.removeEventListener("touchend", slider_touchend); - gcontainer.removeEventListener("touchmove", slider_touchmove); - gcontainer.removeEventListener("touchend", ui_input_touchend_handler); - gcontainer.removeEventListener("touchmove", ui_input_touchmove_handler); - - timeoutId = setTimeout(function () { - timeoutId = null; - focus_input = false; - e.stopPropagation(); - ui_input_release_handler(e); - ui_input_touchmove_handler(e); - gcontainer.addEventListener("touchmove", ui_input_touchmove_handler, { - passive: false, - }); - gcontainer.addEventListener("touchend", ui_input_touchend_handler); - }, 500); - - //gcontainer.addEventListener('contextmenu', slider_contextmenu); - gcontainer.addEventListener("touchend", slider_touchend); - gcontainer.addEventListener("touchmove", slider_touchmove, { - passive: false, - }); - } - - function ui_dispatch_input_release(value) { - const gcontainer = gradioApp().querySelector(".gradio-container"); - if (value) { - gcontainer.addEventListener("mouseover", ui_input_release_handler); - gcontainer.addEventListener("touchstart", slider_touchstart, { - passive: false, - }); - } else { - gcontainer.removeEventListener("mouseover", ui_input_release_handler); - gcontainer.removeEventListener("touchstart", slider_touchstart); - //gcontainer.removeEventListener('contextmenu', slider_contextmenu); - gcontainer.removeEventListener("touchend", slider_touchend); - gcontainer.removeEventListener("touchmove", slider_touchmove); - gcontainer.removeEventListener("touchend", ui_input_touchend_handler); - gcontainer.removeEventListener("touchmove", ui_input_touchmove_handler); - } - } - gradioApp() - .querySelector("#setting_ui_dispatch_input_release input") - .addEventListener("click", function (e) { - ui_dispatch_input_release(e.target.checked); - }); - ui_dispatch_input_release(opts.ui_dispatch_input_release); - - // step ticks for performant input range - function ui_show_range_ticks(value, interactive) { - if (value) { - const range_selectors = "input[type='range']"; - //const range_selectors = "[id$='_clone']:is(input[type='range'])"; - gradioApp() - .querySelectorAll(range_selectors) - .forEach(function (elem) { - let spacing = (elem.step / (elem.max - elem.min)) * 100.0; - let tsp = "max(3px, calc(" + spacing + "% - 1px))"; - let fsp = "max(4px, calc(" + spacing + "% + 0px))"; - var style = elem.style; - style.setProperty( - "--ae-slider-bg-overlay", - "repeating-linear-gradient( 90deg, transparent, transparent " + - tsp + - ", var(--ae-input-border-color) " + - tsp + - ", var(--ae-input-border-color) " + - fsp + - " )" - ); - }); - } else if (interactive) { - gradioApp() - .querySelectorAll("input[type='range']") - .forEach(function (elem) { - var style = elem.style; - style.setProperty("--ae-slider-bg-overlay", "transparent"); - }); - } - } - gradioApp() - .querySelector("#setting_ui_show_range_ticks input") - .addEventListener("click", function (e) { - ui_show_range_ticks(e.target.checked, true); - }); - ui_show_range_ticks(opts.ui_show_range_ticks); - - const gradio_main = gradioApp().querySelector(".gradio-container > div.main"); - function ui_no_slider_layout(value) { - if (value) { - gradio_main.classList.add("no-slider-layout"); - } else { - gradio_main.classList.remove("no-slider-layout"); - } - } - gradioApp() - .querySelector("#setting_ui_no_slider_layout input") - .addEventListener("click", function (e) { - ui_no_slider_layout(e.target.checked, true); - }); - ui_no_slider_layout(opts.ui_no_slider_layout); - - // draggable reordable quicksettings - const container = gradioApp().querySelector( - "#quicksettings_overflow_container" - ); - let draggables; - let lastElemAfter; - let islastChild; - let timeout; - - function preventBehavior(e) { - e.stopPropagation(); - e.preventDefault(); - return false; - } - let sdCheckpointModels = []; - function getSdCheckpointModels() { - gradioApp() - .querySelectorAll("#txt2img_checkpoints_cards .card") - .forEach(function (elem, i) { - sdCheckpointModels[i] = elem.getAttribute("onclick").split('"')[1]; - }); - } - getSdCheckpointModels(); - - function remove_overrides() { - let checked_overrides = []; - gradioApp() - .querySelectorAll("#setting_ignore_overrides input") - .forEach(function (elem, i) { - if (elem.checked) { - checked_overrides[i] = elem.nextElementSibling.innerHTML; - } - }); - //console.log(checked_overrides); - gradioApp() - .querySelectorAll("[id$='2img_override_settings'] .token") - .forEach(function (token) { - let token_arr = token.querySelector("span").innerHTML.split(":"); - let token_name = token_arr[0]; - let token_value = token_arr[1]; - token_value = token_value.replaceAll(" ", ""); - - if (token_name.indexOf("Model hash") != -1) { - const info_label = gradioApp().querySelector( - "[id$='2img_override_settings'] label span" - ); - info_label.innerHTML = "Override settings MDL: unknown"; - for (let m = 0; m < sdCheckpointModels.length; m++) { - let m_str = sdCheckpointModels[m]; - if (m_str.indexOf(token_value) != -1) { - info_label.innerHTML = - "Override settings MDL: " + m_str.split("[")[0] + ""; - break; - } - } - } - if (checked_overrides.indexOf(token_name) != -1) { - token.querySelector(".token-remove").click(); - gradioApp() - .querySelector( - "#" + selectedTabItemId + " [id$='2img_override_settings']" - ) - .parentElement.classList.add("show"); - } else { - // maybe we add them again, for now we can select and add the removed tokens manually from the drop down - } - }); - } - gradioApp() - .querySelector("#setting_ignore_overrides") - .addEventListener("click", function (e) { - setTimeout(function () { - remove_overrides(); - }, 100); - }); - - function update_input_fields(tab) { - /* - let input_selectors = "#tab_"+ tab + " [id^='num_clone']:is(input[type='number'])"; - gradioApp().querySelectorAll(input_selectors).forEach(function (elem){ - let elem_source = elem.previousElementSibling; - elem.value = elem_source.value; - updateInput(elem); - }) - */ - remove_overrides(); - autoGrowPromptTextarea(); - } - - gradioApp() - .querySelectorAll( - "#tab_pnginfo #png_2img_results button, [id$='2img_actions_column'] #paste" - ) - .forEach(function (elem) { - elem.addEventListener("click", function (e) { - let button_id; - if (e.target.id == "paste") { - button_id = e.target.nextElementSibling.id.split("_")[0]; - } else { - button_id = e.target.id.split("_")[0]; - } - setTimeout(function () { - update_input_fields(button_id); - }, 500); - }); - }); - - const pnginfo = gradioApp().querySelector("#tab_pnginfo"); - function forwardFromPngInfo() { - if (selectedTabItemId == "tab_txt2img") { - pnginfo.querySelector("#txt2img_tab").click(); - //close generation info - gradioApp() - .querySelector( - "#txt2img_results > div:last-child > div.gradio-accordion > div.hide" - ) - ?.click(); - - const img_src = pnginfo.querySelector("img"); - const gallery_parent = gradioApp().querySelector( - "#txt2img_gallery_container" - ); - const live_preview = gallery_parent.querySelector(".livePreview"); - if (live_preview) { - live_preview.innerHTML = - ''; - } else { - const div = document.createElement("div"); - div.classList.add("livePreview", "dropPreview"); - div.innerHTML = - ''; - gallery_parent.prepend(div); - } - } else if (selectedTabItemId == "tab_img2img") { - pnginfo.querySelector("#img2img_tab").click(); - //close generation info - gradioApp() - .querySelector( - "#img2img_results > div:last-child > div.gradio-accordion > div.hide" - ) - ?.click(); - } - } - - function fetchPngInfoData(files) { - /* const oldFetch = window.fetch; - - window.fetch = async (input, options) => { - const response = await oldFetch(input, options); - if( 'run/predict/' === input ) { - if(response.ok){ - window.fetch = oldFetch; - } - } - return response; - }; */ - - const fileInput = gradioApp().querySelector( - '#pnginfo_image input[type="file"]' - ); - if (fileInput.files != files) { - fileInput.files = files; - fileInput.dispatchEvent(new Event("change")); - } - - setTimeout(function () { - forwardFromPngInfo(); - }, 500); - } - - function drop2View(e) { - e.stopPropagation(); - e.preventDefault(); - const files = e.dataTransfer.files; - - if (!isValidImageList(files)) { - return; - } - const data_image = gradioApp().querySelector( - '#pnginfo_image [data-testid="image"]' - ); - data_image.querySelector('[aria-label="Clear"]')?.click(); - setTimeout(function () { - fetchPngInfoData(files); - }, 1000); - } - - gradioApp() - .querySelectorAll('[id$="2img_results"]') - .forEach((elem) => { - elem.addEventListener("drop", drop2View); - }); - - // function that gets the element next of cursor/touch - function getElementAfter(container, y) { - return draggables.reduce( - (closest, child) => { - const box = child.getBoundingClientRect(); - const offset = y - box.top - box.height / 2; - if (offset < 0 && offset > closest.offset) { - return { offset: offset, element: child }; - } else { - return closest; - } - }, - { offset: Number.NEGATIVE_INFINITY } - ).element; - } - - function dragOrderChange(elementAfter, isComplete, delem) { - if (lastElemAfter !== elementAfter || islastChild) { - if (lastElemAfter != null) { - lastElemAfter.classList.remove("marker-top", "marker-bottom"); - } - - if (elementAfter == null) { - islastChild = true; - lastElemAfter.classList.add("marker-bottom"); - } else { - islastChild = false; - elementAfter.classList.add("marker-top"); - lastElemAfter = elementAfter; - } - } - - if (isComplete) { - if (elementAfter == null) { - container.append(delem); - } else { - container.insertBefore(delem, elementAfter); - } - - let order_settings = ""; - - gradioApp() - .querySelectorAll("#quicksettings_overflow_container > div") - .forEach(function (el) { - el.classList.remove("marker-top", "marker-bottom", "dragging"); - order_settings += el.id.split("row_setting_")[1] + ","; - }); - //console.log(order_settings); - const setting_quicksettings = gradioApp().querySelector( - "#setting_quicksettings textarea" - ); - setting_quicksettings.value = order_settings; - updateInput(setting_quicksettings); - - const cEvent = new Event("click"); //submit - Object.defineProperty(cEvent, "target", { value: settings_submit }); - settings_submit.dispatchEvent(cEvent); - - container.classList.remove("no-scroll"); - } - } - - function touchmove(e) { - let y = e.touches[0].clientY; - let elementAfter = getElementAfter(container, y); - dragOrderChange(elementAfter); - } - function touchstart(e) { - e.currentTarget.draggable = "false"; - let target = e.currentTarget; - // touch should be hold for 1 second - timeout = setTimeout(function () { - target.classList.add("dragging"); - container.classList.add("no-scroll"); - target.addEventListener("touchmove", touchmove); - container.addEventListener("touchmove", preventBehavior, { - passive: false, - }); - }, 1000); - - target.addEventListener("touchend", touchend); - target.addEventListener("touchcancel", touchcancel); - } - function touchend(e) { - e.currentTarget.draggable = "true"; - clearTimeout(timeout); - e.currentTarget.removeEventListener("touchmove", touchmove); - container.removeEventListener("touchmove", preventBehavior); - let y = e.changedTouches[0].clientY; - let elementAfter = getElementAfter(container, y); - dragOrderChange(elementAfter, true, e.currentTarget); - } - function touchcancel(e) { - e.currentTarget.draggable = "true"; - clearTimeout(timeout); - e.currentTarget.classList.remove("dragging"); - e.currentTarget.removeEventListener("touchmove", touchmove); - container.removeEventListener("touchmove", preventBehavior); - } - - function dragstart(e) { - e.currentTarget.draggable = "false"; - e.currentTarget.classList.add("dragging"); - } - function dragend(e) { - e.stopPropagation(); - e.preventDefault(); - e.currentTarget.draggable = "true"; - let y = e.clientY; - let elementAfter = getElementAfter(container, y); - dragOrderChange(elementAfter, true, e.currentTarget); - } - function dragOver(e) { - e.preventDefault(); - let y = e.clientY; - let elementAfter = getElementAfter(container, y); - dragOrderChange(elementAfter); - } - - function actionQuickSettingsDraggable(checked) { - if (checked) { - draggables = Array.from( - gradioApp().querySelectorAll( - "#quicksettings_overflow_container > div:not(.dragging)" - ) - ); - gradioApp().addEventListener("drop", preventBehavior); - } else { - gradioApp().removeEventListener("drop", preventBehavior); - } - - gradioApp() - .querySelectorAll("#quicksettings_overflow_container > div") - .forEach(function (elem) { - elem.draggable = checked; - if (checked) { - elem.addEventListener("touchstart", touchstart); - elem.addEventListener("dragstart", dragstart); - elem.addEventListener("dragend", dragend); - elem.addEventListener("dragover", dragOver); - } else { - elem.removeEventListener("touchstart", touchstart, false); - elem.removeEventListener("touchend", touchend, false); - elem.removeEventListener("touchcancel", touchcancel, false); - elem.removeEventListener("touchmove", touchmove, false); - elem.removeEventListener("dragstart", dragstart, false); - elem.removeEventListener("dragend", dragend, false); - elem.removeEventListener("dragover", dragOver, false); - } - }); - } - - gradioApp() - .querySelector("#quicksettings_draggable") - .addEventListener("click", function (e) { - if (e.target && e.target.matches("input[type='checkbox']")) { - actionQuickSettingsDraggable(e.target.checked); - } - }); - - updateOpStyles(); - - /* anapnoe ui end */ }); onOptionsChanged(function() { - var elem = gradioApp().getElementById('sd_checkpoint_hash'); - var sd_checkpoint_hash = opts.sd_checkpoint_hash || ""; - var shorthash = sd_checkpoint_hash.substring(0, 10); + var elem = gradioApp().getElementById('sd_checkpoint_hash'); + var sd_checkpoint_hash = opts.sd_checkpoint_hash || ""; + var shorthash = sd_checkpoint_hash.substring(0, 10); - if (elem && elem.textContent != shorthash) { - elem.textContent = shorthash; - elem.title = sd_checkpoint_hash; - elem.href = "https://google.com/search?q=" + sd_checkpoint_hash; - } + if (elem && elem.textContent != shorthash) { + elem.textContent = shorthash; + elem.title = sd_checkpoint_hash; + elem.href = "https://google.com/search?q=" + sd_checkpoint_hash; + } }); let txt2img_textarea, img2img_textarea = undefined; -let wait_time = 800; -let token_timeouts = {}; - -function update_txt2img_tokens(...args) { - update_token_counter("txt2img_token_button"); - if (args.length == 2) { - return args[0]; - } - return args; -} - -function update_img2img_tokens(...args) { - update_token_counter( - "img2img_token_button" - ); - if (args.length == 2) { - return args[0]; - } - return args; -} - -function update_token_counter(button_id) { - if (token_timeouts[button_id]) { - clearTimeout(token_timeouts[button_id]); - } - token_timeouts[button_id] = setTimeout(() => gradioApp().getElementById(button_id)?.click(), wait_time); -} - - function restart_reload() { - let bg_color = window - .getComputedStyle(gradioApp().querySelector("#header-top")) - .getPropertyValue("--ae-main-bg-color"); - let primary_color = window - .getComputedStyle(gradioApp().querySelector(".icon-info")) - .getPropertyValue("--ae-primary-color"); - let panel_color = window - .getComputedStyle(gradioApp().querySelector(".gradio-box")) - .getPropertyValue("--ae-panel-bg-color"); + document.body.innerHTML = '

Reloading...

'; - localStorage.setItem("bg_color", bg_color); - localStorage.setItem("primary_color", primary_color); - localStorage.setItem("panel_color", panel_color); + var requestPing = function() { + requestGet("./internal/ping", {}, function(data) { + location.reload(); + }, function() { + setTimeout(requestPing, 500); + }); + }; - if (localStorage.hasOwnProperty("bg_color")) { - bg_color = localStorage.getItem("bg_color"); - primary_color = localStorage.getItem("primary_color"); - panel_color = localStorage.getItem("panel_color"); - } + setTimeout(requestPing, 2000); - document.body.style.backgroundColor = bg_color; - - let style = document.createElement("style"); - style.type = "text/css"; - style.innerHTML = - ".loader{position:absolute;top:50vh;left:50vw;height:60px;width:160px;margin:0;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)} .circles{position:absolute;left:-5px;top:0;height:60px;width:180px} .circles span{position:absolute;top:25px;height:12px;width:12px;border-radius:12px;background-color:" + - panel_color + - "} .circles span.one{right:80px} .circles span.two{right:40px} .circles span.three{right:0px} .circles{-webkit-animation:animcircles 0.5s infinite linear;animation:animcircles 0.5s infinite linear} @-webkit-keyframes animcircles{0%{-webkit-transform:translate(0px,0px);transform:translate(0px,0px)}100%{-webkit-transform:translate(-40px,0px);transform:translate(-40px,0px)}} @keyframes animcircles{0%{-webkit-transform:translate(0px,0px);transform:translate(0px,0px)}100%{-webkit-transform:translate(-40px,0px);transform:translate(-40px,0px)}} .pacman{position:absolute;left:0;top:0;height:60px;width:60px} .pacman .eye{position:absolute;top:10px;left:30px;height:7px;width:7px;border-radius:7px;background-color:" + - bg_color + - '} .pacman span{position:absolute;top:0;left:0;height:60px;width:60px} .pacman span::before{content:"";position:absolute;left:0;height:30px;width:60px;background-color:' + - primary_color + - "} .pacman .top::before{top:0;border-radius:60px 60px 0px 0px} .pacman .bottom::before{bottom:0;border-radius:0px 0px 60px 60px} .pacman .left::before{bottom:0;height:60px;width:30px;border-radius:60px 0px 0px 60px} .pacman .top{-webkit-animation:animtop 0.5s infinite;animation:animtop 0.5s infinite} @-webkit-keyframes animtop{0%,100%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}50%{-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}} @keyframes animtop{0%,100%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}50%{-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}} .pacman .bottom{-webkit-animation:animbottom 0.5s infinite;animation:animbottom 0.5s infinite} @-webkit-keyframes animbottom{0%,100%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}50%{-webkit-transform:rotate(45deg);transform:rotate(45deg)}} @keyframes animbottom{0%,100%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}50%{-webkit-transform:rotate(45deg);transform:rotate(45deg)}}"; - - document.getElementsByTagName("head")[0].appendChild(style); - document.body.innerHTML = - '
'; - - var requestPing = function () { - requestGet( - "./internal/ping", - {}, - function (data) { - location.reload(); - }, - function () { - setTimeout(requestPing, 500); - } - ); - }; - - setTimeout(requestPing, 2000); - - return []; + return []; } // Simulate an `input` DOM event for Gradio Textbox component. Needed after you edit its contents in javascript, otherwise your edits // will only visible on web page and not sent to python. function updateInput(target) { - const e = new Event("input", { bubbles: true }); - Object.defineProperty(e, "target", { value: target }); - target.dispatchEvent(e); - const eb = new Event("blur"); - Object.defineProperty(eb, "target", { value: target }); - target.dispatchEvent(eb); + let e = new Event("input", {bubbles: true}); + Object.defineProperty(e, "target", {value: target}); + target.dispatchEvent(e); } -document.addEventListener("readystatechange", function (e) { - document.body.style.display = "none"; - if (localStorage.hasOwnProperty("bg_color")) { - document.getElementsByTagName("html")[0].style.backgroundColor = - localStorage.getItem("bg_color"); - document.body.style.backgroundColor = localStorage.getItem("bg_color"); - } -}); - -window.onload = function () { - document.getElementsByTagName("html")[0].style.backgroundColor = - localStorage.getItem("bg_color"); - document.body.style.backgroundColor = localStorage.getItem("bg_color"); - document.body.style.display = "none"; - document.body.classList.add("dark"); - setTimeout(function () { - document.body.style.display = "block"; - }, 1000); -}; var desiredCheckpointName = null; function selectCheckpoint(name) { @@ -2038,4 +365,4 @@ function switchWidthHeight(tabname) { updateInput(width); updateInput(height); return []; -} \ No newline at end of file +} diff --git a/javascript/ui_settings_hints.js b/javascript/ui_settings_hints.js index 0f3c6acd..d088f949 100644 --- a/javascript/ui_settings_hints.js +++ b/javascript/ui_settings_hints.js @@ -1,3 +1,5 @@ +// various hints and extra info for the settings tab + var settingsHintsSetup = false; onOptionsChanged(function() { diff --git a/modules/api/api.py b/modules/api/api.py index 606db179..e6edffe7 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -4,6 +4,8 @@ import os import time import datetime import uvicorn +import ipaddress +import requests import gradio as gr from threading import Lock from io import BytesIO @@ -15,7 +17,7 @@ from fastapi.encoders import jsonable_encoder from secrets import compare_digest import modules.shared as shared -from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing, errors, restart +from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing, errors, restart, shared_items from modules.api import models from modules.shared import opts from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images @@ -23,8 +25,7 @@ from modules.textual_inversion.textual_inversion import create_embedding, train_ from modules.textual_inversion.preprocess import preprocess from modules.hypernetworks.hypernetwork import create_hypernetwork, train_hypernetwork from PIL import PngImagePlugin,Image -from modules.sd_models import checkpoints_list, unload_model_weights, reload_model_weights, checkpoint_aliases -from modules.sd_vae import vae_dict +from modules.sd_models import unload_model_weights, reload_model_weights, checkpoint_aliases from modules.sd_models_config import find_checkpoint_config_near_filename from modules.realesrgan_model import get_realesrgan_models from modules import devices @@ -56,7 +57,41 @@ def setUpscalers(req: dict): return reqDict +def verify_url(url): + """Returns True if the url refers to a global resource.""" + + import socket + from urllib.parse import urlparse + try: + parsed_url = urlparse(url) + domain_name = parsed_url.netloc + host = socket.gethostbyname_ex(domain_name) + for ip in host[2]: + ip_addr = ipaddress.ip_address(ip) + if not ip_addr.is_global: + return False + except Exception: + return False + + return True + + def decode_base64_to_image(encoding): + if encoding.startswith("http://") or encoding.startswith("https://"): + if not opts.api_enable_requests: + raise HTTPException(status_code=500, detail="Requests not allowed") + + if opts.api_forbid_local_requests and not verify_url(encoding): + raise HTTPException(status_code=500, detail="Request to local resource not allowed") + + headers = {'user-agent': opts.api_useragent} if opts.api_useragent else {} + response = requests.get(encoding, timeout=30, headers=headers) + try: + image = Image.open(BytesIO(response.content)) + return image + except Exception as e: + raise HTTPException(status_code=500, detail="Invalid image url") from e + if encoding.startswith("data:image/"): encoding = encoding.split(";")[1].split(",")[1] try: @@ -197,6 +232,7 @@ class Api: self.add_api_route("/sdapi/v1/prompt-styles", self.get_prompt_styles, methods=["GET"], response_model=List[models.PromptStyleItem]) self.add_api_route("/sdapi/v1/embeddings", self.get_embeddings, methods=["GET"], response_model=models.EmbeddingsResponse) self.add_api_route("/sdapi/v1/refresh-checkpoints", self.refresh_checkpoints, methods=["POST"]) + self.add_api_route("/sdapi/v1/refresh-vae", self.refresh_vae, methods=["POST"]) self.add_api_route("/sdapi/v1/create/embedding", self.create_embedding, methods=["POST"], response_model=models.CreateResponse) self.add_api_route("/sdapi/v1/create/hypernetwork", self.create_hypernetwork, methods=["POST"], response_model=models.CreateResponse) self.add_api_route("/sdapi/v1/preprocess", self.preprocess, methods=["POST"], response_model=models.PreprocessResponse) @@ -329,6 +365,7 @@ class Api: with self.queue_lock: with closing(StableDiffusionProcessingTxt2Img(sd_model=shared.sd_model, **args)) as p: + p.is_api = True p.scripts = script_runner p.outpath_grids = opts.outdir_txt2img_grids p.outpath_samples = opts.outdir_txt2img_samples @@ -343,6 +380,7 @@ class Api: processed = process_images(p) finally: shared.state.end() + shared.total_tqdm.clear() b64images = list(map(encode_pil_to_base64, processed.images)) if send_images else [] @@ -388,6 +426,7 @@ class Api: with self.queue_lock: with closing(StableDiffusionProcessingImg2Img(sd_model=shared.sd_model, **args)) as p: p.init_images = [decode_base64_to_image(x) for x in init_images] + p.is_api = True p.scripts = script_runner p.outpath_grids = opts.outdir_img2img_grids p.outpath_samples = opts.outdir_img2img_samples @@ -402,6 +441,7 @@ class Api: processed = process_images(p) finally: shared.state.end() + shared.total_tqdm.clear() b64images = list(map(encode_pil_to_base64, processed.images)) if send_images else [] @@ -530,7 +570,7 @@ class Api: raise RuntimeError(f"model {checkpoint_name!r} not found") for k, v in req.items(): - shared.opts.set(k, v) + shared.opts.set(k, v, is_api=True) shared.opts.save(shared.config_filename) return @@ -562,10 +602,12 @@ class Api: ] def get_sd_models(self): - return [{"title": x.title, "model_name": x.model_name, "hash": x.shorthash, "sha256": x.sha256, "filename": x.filename, "config": find_checkpoint_config_near_filename(x)} for x in checkpoints_list.values()] + import modules.sd_models as sd_models + return [{"title": x.title, "model_name": x.model_name, "hash": x.shorthash, "sha256": x.sha256, "filename": x.filename, "config": find_checkpoint_config_near_filename(x)} for x in sd_models.checkpoints_list.values()] def get_sd_vaes(self): - return [{"model_name": x, "filename": vae_dict[x]} for x in vae_dict.keys()] + import modules.sd_vae as sd_vae + return [{"model_name": x, "filename": sd_vae.vae_dict[x]} for x in sd_vae.vae_dict.keys()] def get_hypernetworks(self): return [{"name": name, "path": shared.hypernetworks[name]} for name in shared.hypernetworks] @@ -608,6 +650,10 @@ class Api: with self.queue_lock: shared.refresh_checkpoints() + def refresh_vae(self): + with self.queue_lock: + shared_items.refresh_vae_list() + def create_embedding(self, args: dict): try: shared.state.begin(job="create_embedding") diff --git a/modules/api/models.py b/modules/api/models.py index bf97b1a3..6a574771 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -50,10 +50,12 @@ class PydanticModelGenerator: additional_fields = None, ): def field_type_generator(k, v): - # field_type = str if not overrides.get(k) else overrides[k]["type"] - # print(k, v.annotation, v.default) field_type = v.annotation + if field_type == 'Image': + # images are sent as base64 strings via API + field_type = 'str' + return Optional[field_type] def merge_class_params(class_): @@ -63,7 +65,6 @@ class PydanticModelGenerator: parameters = {**parameters, **inspect.signature(classes.__init__).parameters} return parameters - self._model_name = model_name self._class_data = merge_class_params(class_instance) @@ -72,7 +73,7 @@ class PydanticModelGenerator: field=underscore(k), field_alias=k, field_type=field_type_generator(k, v), - field_value=v.default + field_value=None if isinstance(v.default, property) else v.default ) for (k,v) in self._class_data.items() if k not in API_NOT_ALLOWED ] @@ -208,11 +209,9 @@ class PreprocessResponse(BaseModel): fields = {} for key, metadata in opts.data_labels.items(): value = opts.data.get(key) - optType = opts.typemap.get(type(metadata.default), type(metadata.default)) + optType = opts.typemap.get(type(metadata.default), type(metadata.default)) if metadata.default else Any - if metadata.default is None: - pass - elif metadata is not None: + if metadata is not None: fields.update({key: (Optional[optType], Field(default=metadata.default, description=metadata.label))}) else: fields.update({key: (Optional[optType], Field())}) diff --git a/modules/cache.py b/modules/cache.py index 71fe6302..ff26a213 100644 --- a/modules/cache.py +++ b/modules/cache.py @@ -1,11 +1,12 @@ import json +import os import os.path import threading import time from modules.paths import data_path, script_path -cache_filename = os.path.join(data_path, "cache.json") +cache_filename = os.environ.get('SD_WEBUI_CACHE_FILE', os.path.join(data_path, "cache.json")) cache_data = None cache_lock = threading.Lock() @@ -29,9 +30,12 @@ def dump_cache(): time.sleep(1) with cache_lock: - with open(cache_filename, "w", encoding="utf8") as file: + cache_filename_tmp = cache_filename + "-" + with open(cache_filename_tmp, "w", encoding="utf8") as file: json.dump(cache_data, file, indent=4) + os.replace(cache_filename_tmp, cache_filename) + dump_cache_after = None dump_cache_thread = None diff --git a/modules/call_queue.py b/modules/call_queue.py index 61aa240f..ddf0d573 100644 --- a/modules/call_queue.py +++ b/modules/call_queue.py @@ -1,11 +1,10 @@ from functools import wraps import html -import threading import time -from modules import shared, progress, errors +from modules import shared, progress, errors, devices, fifo_lock -queue_lock = threading.Lock() +queue_lock = fifo_lock.FIFOLock() def wrap_queued_call(func): @@ -75,6 +74,8 @@ def wrap_gradio_call(func, extra_outputs=None, add_stats=False): error_message = f'{type(e).__name__}: {e}' res = extra_outputs_array + [f"
{html.escape(error_message)}
"] + devices.torch_gc() + shared.state.skipped = False shared.state.interrupted = False shared.state.job_count = 0 diff --git a/modules/cmd_args.py b/modules/cmd_args.py index dd5fadc4..aab62286 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -16,6 +16,8 @@ parser.add_argument("--test-server", action='store_true', help="launch.py argume parser.add_argument("--log-startup", action='store_true', help="launch.py argument: print a detailed log of what's happening at startup") parser.add_argument("--skip-prepare-environment", action='store_true', help="launch.py argument: skip all environment preparation") parser.add_argument("--skip-install", action='store_true', help="launch.py argument: skip installation of packages") +parser.add_argument("--dump-sysinfo", action='store_true', help="launch.py argument: dump limited sysinfo file (without information about extensions, options) to disk and quit") +parser.add_argument("--loglevel", type=str, help="log level; one of: CRITICAL, ERROR, WARNING, INFO, DEBUG", default=None) parser.add_argument("--do-not-download-clip", action='store_true', help="do not download CLIP model even if it's not included in the checkpoint") parser.add_argument("--data-dir", type=str, default=os.path.dirname(os.path.dirname(os.path.realpath(__file__))), help="base path where all user data is stored") parser.add_argument("--config", type=str, default=sd_default_config, help="path to config which constructs model",) @@ -34,9 +36,10 @@ parser.add_argument("--hypernetwork-dir", type=str, default=os.path.join(models_ parser.add_argument("--localizations-dir", type=str, default=os.path.join(script_path, 'localizations'), help="localizations directory") parser.add_argument("--allow-code", action='store_true', help="allow custom script execution from webui") parser.add_argument("--medvram", action='store_true', help="enable stable diffusion model optimizations for sacrificing a little speed for low VRM usage") +parser.add_argument("--medvram-sdxl", action='store_true', help="enable --medvram optimization just for SDXL models") parser.add_argument("--lowvram", action='store_true', help="enable stable diffusion model optimizations for sacrificing a lot of speed for very low VRM usage") parser.add_argument("--lowram", action='store_true', help="load stable diffusion checkpoint weights to VRAM instead of RAM") -parser.add_argument("--always-batch-cond-uncond", action='store_true', help="disables cond/uncond batching that is enabled to save memory with --medvram or --lowvram") +parser.add_argument("--always-batch-cond-uncond", action='store_true', help="does not do anything") parser.add_argument("--unload-gfpgan", action='store_true', help="does not do anything.") parser.add_argument("--precision", type=str, help="evaluate at this precision", choices=["full", "autocast"], default="autocast") parser.add_argument("--upcast-sampling", action='store_true', help="upcast sampling. No effect with --no-half. Usually produces similar results to --no-half with better performance while using less memory.") @@ -67,6 +70,7 @@ parser.add_argument("--opt-sdp-no-mem-attention", action='store_true', help="pre parser.add_argument("--disable-opt-split-attention", action='store_true', help="prefer no cross-attention layer optimization for automatic choice of optimization") parser.add_argument("--disable-nan-check", action='store_true', help="do not check if produced images/latent spaces have nans; useful for running without a checkpoint in CI") parser.add_argument("--use-cpu", nargs='+', help="use CPU as torch device for specified modules", default=[], type=str.lower) +parser.add_argument("--disable-model-loading-ram-optimization", action='store_true', help="disable an optimization that reduces RAM use when loading a model") parser.add_argument("--listen", action='store_true', help="launch gradio with 0.0.0.0 as server name, allowing to respond to network requests") parser.add_argument("--port", type=int, help="launch gradio with given server port, you need root/admin rights for ports < 1024, defaults to 7860 if available", default=None) parser.add_argument("--show-negative-prompt", action='store_true', help="does not do anything", default=False) @@ -79,7 +83,7 @@ parser.add_argument("--gradio-auth", type=str, help='set gradio authentication l parser.add_argument("--gradio-auth-path", type=str, help='set gradio authentication file path ex. "/path/to/auth/file" same auth format as --gradio-auth', default=None) parser.add_argument("--gradio-img2img-tool", type=str, help='does not do anything') parser.add_argument("--gradio-inpaint-tool", type=str, help="does not do anything") -parser.add_argument("--gradio-allowed-path", action='append', help="add path to gradio's allowed_paths, make it possible to serve files from it") +parser.add_argument("--gradio-allowed-path", action='append', help="add path to gradio's allowed_paths, make it possible to serve files from it", default=[data_path]) parser.add_argument("--opt-channelslast", action='store_true', help="change memory type for stable diffusion to channels last") parser.add_argument("--styles-file", type=str, help="filename to use for styles", default=os.path.join(data_path, 'styles.csv')) parser.add_argument("--autolaunch", action='store_true', help="open the webui URL in the system's default browser upon launch", default=False) @@ -111,3 +115,5 @@ parser.add_argument('--subpath', type=str, help='customize the subpath for gradi parser.add_argument('--add-stop-route', action='store_true', help='add /_stop route to stop server') parser.add_argument('--api-server-stop', action='store_true', help='enable server stop/restart/kill via api') parser.add_argument('--timeout-keep-alive', type=int, default=30, help='set timeout_keep_alive for uvicorn') +parser.add_argument("--disable-all-extensions", action='store_true', help="prevent all extensions from running regardless of any other settings", default=False) +parser.add_argument("--disable-extra-extensions", action='store_true', help=" prevent all extensions except built-in from running regardless of any other settings", default=False) diff --git a/modules/config_states.py b/modules/config_states.py index 6f1ab53f..b766aef1 100644 --- a/modules/config_states.py +++ b/modules/config_states.py @@ -8,14 +8,12 @@ import time import tqdm from datetime import datetime -from collections import OrderedDict import git from modules import shared, extensions, errors from modules.paths_internal import script_path, config_states_dir - -all_config_states = OrderedDict() +all_config_states = {} def list_config_states(): @@ -28,10 +26,14 @@ def list_config_states(): for filename in os.listdir(config_states_dir): if filename.endswith(".json"): path = os.path.join(config_states_dir, filename) - with open(path, "r", encoding="utf-8") as f: - j = json.load(f) - j["filepath"] = path - config_states.append(j) + try: + with open(path, "r", encoding="utf-8") as f: + j = json.load(f) + assert "created_at" in j, '"created_at" does not exist' + j["filepath"] = path + config_states.append(j) + except Exception as e: + print(f'[ERROR]: Config states {path}, {e}') config_states = sorted(config_states, key=lambda cs: cs["created_at"], reverse=True) diff --git a/modules/devices.py b/modules/devices.py index 57e51da3..c01f0602 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -3,7 +3,7 @@ import contextlib from functools import lru_cache import torch -from modules import errors +from modules import errors, shared if sys.platform == "darwin": from modules import mac_specific @@ -17,8 +17,6 @@ def has_mps() -> bool: def get_cuda_device_string(): - from modules import shared - if shared.cmd_opts.device_id is not None: return f"cuda:{shared.cmd_opts.device_id}" @@ -40,8 +38,6 @@ def get_optimal_device(): def get_device_for(task): - from modules import shared - if task in shared.cmd_opts.use_cpu: return cpu @@ -71,14 +67,17 @@ def enable_tf32(): torch.backends.cudnn.allow_tf32 = True - errors.run(enable_tf32, "Enabling TF32") -cpu = torch.device("cpu") -device = device_interrogate = device_gfpgan = device_esrgan = device_codeformer = None -dtype = torch.float16 -dtype_vae = torch.float16 -dtype_unet = torch.float16 +cpu: torch.device = torch.device("cpu") +device: torch.device = None +device_interrogate: torch.device = None +device_gfpgan: torch.device = None +device_esrgan: torch.device = None +device_codeformer: torch.device = None +dtype: torch.dtype = torch.float16 +dtype_vae: torch.dtype = torch.float16 +dtype_unet: torch.dtype = torch.float16 unet_needs_upcast = False @@ -90,26 +89,10 @@ def cond_cast_float(input): return input.float() if unet_needs_upcast else input -def randn(seed, shape): - from modules.shared import opts - - torch.manual_seed(seed) - if opts.randn_source == "CPU" or device.type == 'mps': - return torch.randn(shape, device=cpu).to(device) - return torch.randn(shape, device=device) - - -def randn_without_seed(shape): - from modules.shared import opts - - if opts.randn_source == "CPU" or device.type == 'mps': - return torch.randn(shape, device=cpu).to(device) - return torch.randn(shape, device=device) +nv_rng = None def autocast(disable=False): - from modules import shared - if disable: return contextlib.nullcontext() @@ -128,8 +111,6 @@ class NansException(Exception): def test_for_nans(x, where): - from modules import shared - if shared.cmd_opts.disable_nan_check: return @@ -169,3 +150,4 @@ def first_time_calculation(): x = torch.zeros((1, 1, 3, 3)).to(device, dtype) conv2d = torch.nn.Conv2d(1, 1, (3, 3)).to(device, dtype) conv2d(x) + diff --git a/modules/errors.py b/modules/errors.py index 5271a9fe..8c339464 100644 --- a/modules/errors.py +++ b/modules/errors.py @@ -14,7 +14,8 @@ def record_exception(): if exception_records and exception_records[-1] == e: return - exception_records.append((e, tb)) + from modules import sysinfo + exception_records.append(sysinfo.format_exception(e, tb)) if len(exception_records) > 5: exception_records.pop(0) @@ -83,3 +84,53 @@ def run(code, task): code() except Exception as e: display(task, e) + + +def check_versions(): + from packaging import version + from modules import shared + + import torch + import gradio + + expected_torch_version = "2.0.0" + expected_xformers_version = "0.0.20" + expected_gradio_version = "3.41.2" + + if version.parse(torch.__version__) < version.parse(expected_torch_version): + print_error_explanation(f""" +You are running torch {torch.__version__}. +The program is tested to work with torch {expected_torch_version}. +To reinstall the desired version, run with commandline flag --reinstall-torch. +Beware that this will cause a lot of large files to be downloaded, as well as +there are reports of issues with training tab on the latest version. + +Use --skip-version-check commandline argument to disable this check. + """.strip()) + + if shared.xformers_available: + import xformers + + if version.parse(xformers.__version__) < version.parse(expected_xformers_version): + print_error_explanation(f""" +You are running xformers {xformers.__version__}. +The program is tested to work with xformers {expected_xformers_version}. +To reinstall the desired version, run with commandline flag --reinstall-xformers. + +Use --skip-version-check commandline argument to disable this check. + """.strip()) + + if gradio.__version__ != expected_gradio_version: + print_error_explanation(f""" +You are running gradio {gradio.__version__}. +The program is designed to work with gradio {expected_gradio_version}. +Using a different version of gradio is extremely likely to break the program. + +Reasons why you have the mismatched gradio version can be: + - you use --skip-install flag. + - you use webui.py to start the program instead of launch.py. + - an extension installs the incompatible gradio version. + +Use --skip-version-check commandline argument to disable this check. + """.strip()) + diff --git a/modules/extensions.py b/modules/extensions.py index 09d1e550..bf9a1878 100644 --- a/modules/extensions.py +++ b/modules/extensions.py @@ -1,7 +1,7 @@ import os import threading -from modules import shared, errors, cache +from modules import shared, errors, cache, scripts from modules.gitpython_hack import Repo from modules.paths_internal import extensions_dir, extensions_builtin_dir, script_path # noqa: F401 @@ -11,9 +11,9 @@ os.makedirs(extensions_dir, exist_ok=True) def active(): - if shared.opts.disable_all_extensions == "all": + if shared.cmd_opts.disable_all_extensions or shared.opts.disable_all_extensions == "all": return [] - elif shared.opts.disable_all_extensions == "extra": + elif shared.cmd_opts.disable_extra_extensions or shared.opts.disable_all_extensions == "extra": return [x for x in extensions if x.enabled and x.is_builtin] else: return [x for x in extensions if x.enabled] @@ -61,7 +61,7 @@ class Extension: self.from_dict(d) except FileNotFoundError: pass - self.status = 'unknown' + self.status = 'unknown' if self.status == '' else self.status def do_read_info_from_repo(self): repo = None @@ -90,8 +90,6 @@ class Extension: self.have_info_from_repo = True def list_files(self, subdir, extension): - from modules import scripts - dirpath = os.path.join(self.path, subdir) if not os.path.isdir(dirpath): return [] @@ -141,8 +139,12 @@ def list_extensions(): if not os.path.isdir(extensions_dir): return - if shared.opts.disable_all_extensions == "all": + if shared.cmd_opts.disable_all_extensions: + print("*** \"--disable-all-extensions\" arg was used, will not load any extensions ***") + elif shared.opts.disable_all_extensions == "all": print("*** \"Disable all extensions\" option was set, will not load any extensions ***") + elif shared.cmd_opts.disable_extra_extensions: + print("*** \"--disable-extra-extensions\" arg was used, will only load built-in extensions ***") elif shared.opts.disable_all_extensions == "extra": print("*** \"Disable all extensions\" option was set, will only load built-in extensions ***") diff --git a/modules/extra_networks.py b/modules/extra_networks.py index 6ae07e91..b9533677 100644 --- a/modules/extra_networks.py +++ b/modules/extra_networks.py @@ -1,4 +1,7 @@ +import json +import os import re +import logging from collections import defaultdict from modules import errors @@ -84,27 +87,55 @@ class ExtraNetwork: raise NotImplementedError +def lookup_extra_networks(extra_network_data): + """returns a dict mapping ExtraNetwork objects to lists of arguments for those extra networks. + + Example input: + { + 'lora': [], + 'lyco': [], + 'hypernet': [] + } + + Example output: + + { + : [, ], + : [] + } + """ + + res = {} + + for extra_network_name, extra_network_args in list(extra_network_data.items()): + extra_network = extra_network_registry.get(extra_network_name, None) + alias = extra_network_aliases.get(extra_network_name, None) + + if alias is not None and extra_network is None: + extra_network = alias + + if extra_network is None: + logging.info(f"Skipping unknown extra network: {extra_network_name}") + continue + + res.setdefault(extra_network, []).extend(extra_network_args) + + return res + + def activate(p, extra_network_data): """call activate for extra networks in extra_network_data in specified order, then call activate for all remaining registered networks with an empty argument list""" activated = [] - for extra_network_name, extra_network_args in extra_network_data.items(): - extra_network = extra_network_registry.get(extra_network_name, None) - - if extra_network is None: - extra_network = extra_network_aliases.get(extra_network_name, None) - - if extra_network is None: - print(f"Skipping unknown extra network: {extra_network_name}") - continue + for extra_network, extra_network_args in lookup_extra_networks(extra_network_data).items(): try: extra_network.activate(p, extra_network_args) activated.append(extra_network) except Exception as e: - errors.display(e, f"activating extra network {extra_network_name} with arguments {extra_network_args}") + errors.display(e, f"activating extra network {extra_network.name} with arguments {extra_network_args}") for extra_network_name, extra_network in extra_network_registry.items(): if extra_network in activated: @@ -123,19 +154,16 @@ def deactivate(p, extra_network_data): """call deactivate for extra networks in extra_network_data in specified order, then call deactivate for all remaining registered networks""" - for extra_network_name in extra_network_data: - extra_network = extra_network_registry.get(extra_network_name, None) - if extra_network is None: - continue + data = lookup_extra_networks(extra_network_data) + for extra_network in data: try: extra_network.deactivate(p) except Exception as e: - errors.display(e, f"deactivating extra network {extra_network_name}") + errors.display(e, f"deactivating extra network {extra_network.name}") for extra_network_name, extra_network in extra_network_registry.items(): - args = extra_network_data.get(extra_network_name, None) - if args is not None: + if extra_network in data: continue try: @@ -177,3 +205,20 @@ def parse_prompts(prompts): return res, extra_data + +def get_user_metadata(filename): + if filename is None: + return {} + + basename, ext = os.path.splitext(filename) + metadata_filename = basename + '.json' + + metadata = {} + try: + if os.path.isfile(metadata_filename): + with open(metadata_filename, "r", encoding="utf8") as file: + metadata = json.load(file) + except Exception as e: + errors.display(e, f"reading extra network user metadata from {metadata_filename}") + + return metadata diff --git a/modules/extras.py b/modules/extras.py index e9c0263e..2a310ae3 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -7,7 +7,7 @@ import json import torch import tqdm -from modules import shared, images, sd_models, sd_vae, sd_models_config +from modules import shared, images, sd_models, sd_vae, sd_models_config, errors from modules.ui_common import plaintext_to_html import gradio as gr import safetensors.torch @@ -72,7 +72,20 @@ def to_half(tensor, enable): return tensor -def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_model_name, interp_method, multiplier, save_as_half, custom_name, checkpoint_format, config_source, bake_in_vae, discard_weights, save_metadata): +def read_metadata(primary_model_name, secondary_model_name, tertiary_model_name): + metadata = {} + + for checkpoint_name in [primary_model_name, secondary_model_name, tertiary_model_name]: + checkpoint_info = sd_models.checkpoints_list.get(checkpoint_name, None) + if checkpoint_info is None: + continue + + metadata.update(checkpoint_info.metadata) + + return json.dumps(metadata, indent=4, ensure_ascii=False) + + +def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_model_name, interp_method, multiplier, save_as_half, custom_name, checkpoint_format, config_source, bake_in_vae, discard_weights, save_metadata, add_merge_recipe, copy_metadata_fields, metadata_json): shared.state.begin(job="model-merge") def fail(message): @@ -241,11 +254,25 @@ def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_ shared.state.textinfo = "Saving" print(f"Saving to {output_modelname}...") - metadata = None + metadata = {} + + if save_metadata and copy_metadata_fields: + if primary_model_info: + metadata.update(primary_model_info.metadata) + if secondary_model_info: + metadata.update(secondary_model_info.metadata) + if tertiary_model_info: + metadata.update(tertiary_model_info.metadata) if save_metadata: - metadata = {"format": "pt"} + try: + metadata.update(json.loads(metadata_json)) + except Exception as e: + errors.display(e, "readin metadata from json") + metadata["format"] = "pt" + + if save_metadata and add_merge_recipe: merge_recipe = { "type": "webui", # indicate this model was merged with webui's built-in merger "primary_model_hash": primary_model_info.sha256, @@ -261,7 +288,6 @@ def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_ "is_inpainting": result_is_inpainting_model, "is_instruct_pix2pix": result_is_instruct_pix2pix_model } - metadata["sd_merge_recipe"] = json.dumps(merge_recipe) sd_merge_models = {} @@ -281,11 +307,12 @@ def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_ if tertiary_model_info: add_model_metadata(tertiary_model_info) + metadata["sd_merge_recipe"] = json.dumps(merge_recipe) metadata["sd_merge_models"] = json.dumps(sd_merge_models) _, extension = os.path.splitext(output_modelname) if extension.lower() == ".safetensors": - safetensors.torch.save_file(theta_0, output_modelname, metadata=metadata) + safetensors.torch.save_file(theta_0, output_modelname, metadata=metadata if len(metadata)>0 else None) else: torch.save(theta_0, output_modelname) diff --git a/modules/fifo_lock.py b/modules/fifo_lock.py new file mode 100644 index 00000000..c35b3ae2 --- /dev/null +++ b/modules/fifo_lock.py @@ -0,0 +1,37 @@ +import threading +import collections + + +# reference: https://gist.github.com/vitaliyp/6d54dd76ca2c3cdfc1149d33007dc34a +class FIFOLock(object): + def __init__(self): + self._lock = threading.Lock() + self._inner_lock = threading.Lock() + self._pending_threads = collections.deque() + + def acquire(self, blocking=True): + with self._inner_lock: + lock_acquired = self._lock.acquire(False) + if lock_acquired: + return True + elif not blocking: + return False + + release_event = threading.Event() + self._pending_threads.append(release_event) + + release_event.wait() + return self._lock.acquire() + + def release(self): + with self._inner_lock: + if self._pending_threads: + release_event = self._pending_threads.popleft() + release_event.set() + + self._lock.release() + + __enter__ = acquire + + def __exit__(self, t, v, tb): + self.release() diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 88f4381b..d39f2eba 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -6,10 +6,10 @@ import re import gradio as gr from modules.paths import data_path -from modules import shared, ui_tempdir, script_callbacks +from modules import shared, ui_tempdir, script_callbacks, processing from PIL import Image -re_param_code = r'\s*([\w ]+):\s*("(?:\\"[^,]|\\"|\\|[^\"])+"|[^,]*)(?:,|$)' +re_param_code = r'\s*([\w ]+):\s*("(?:\\.|[^\\"])+"|[^,]*)(?:,|$)' re_param = re.compile(re_param_code) re_imagesize = re.compile(r"^(\d+)x(\d+)$") re_hypernet_hash = re.compile("\(([0-9a-f]+)\)$") @@ -32,6 +32,7 @@ class ParamBinding: def reset(): paste_fields.clear() + registered_param_bindings.clear() def quote(text): @@ -198,7 +199,6 @@ def restore_old_hires_fix_params(res): height = int(res.get("Size-2", 512)) if firstpass_width == 0 or firstpass_height == 0: - from modules import processing firstpass_width, firstpass_height = processing.old_hires_fix_first_pass_dimensions(width, height) res['Size-1'] = firstpass_width @@ -280,6 +280,9 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model if "Hires sampler" not in res: res["Hires sampler"] = "Use same sampler" + if "Hires checkpoint" not in res: + res["Hires checkpoint"] = "Use same checkpoint" + if "Hires prompt" not in res: res["Hires prompt"] = "" @@ -304,32 +307,28 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model if "Schedule rho" not in res: res["Schedule rho"] = 0 + if "VAE Encoder" not in res: + res["VAE Encoder"] = "Full" + + if "VAE Decoder" not in res: + res["VAE Decoder"] = "Full" + return res infotext_to_setting_name_mapping = [ - ('Clip skip', 'CLIP_stop_at_last_layers', ), + +] +"""Mapping of infotext labels to setting names. Only left for backwards compatibility - use OptionInfo(..., infotext='...') instead. +Example content: + +infotext_to_setting_name_mapping = [ ('Conditional mask weight', 'inpainting_mask_weight'), ('Model hash', 'sd_model_checkpoint'), ('ENSD', 'eta_noise_seed_delta'), ('Schedule type', 'k_sched_type'), - ('Schedule max sigma', 'sigma_max'), - ('Schedule min sigma', 'sigma_min'), - ('Schedule rho', 'rho'), - ('Noise multiplier', 'initial_noise_multiplier'), - ('Eta', 'eta_ancestral'), - ('Eta DDIM', 'eta_ddim'), - ('Discard penultimate sigma', 'always_discard_next_to_last_sigma'), - ('UniPC variant', 'uni_pc_variant'), - ('UniPC skip type', 'uni_pc_skip_type'), - ('UniPC order', 'uni_pc_order'), - ('UniPC lower order final', 'uni_pc_lower_order_final'), - ('Token merging ratio', 'token_merging_ratio'), - ('Token merging ratio hr', 'token_merging_ratio_hr'), - ('RNG', 'randn_source'), - ('NGMS', 's_min_uncond'), - ('Pad conds', 'pad_cond_uncond'), ] +""" def create_override_settings_dict(text_pairs): @@ -350,7 +349,8 @@ def create_override_settings_dict(text_pairs): params[k] = v.strip() - for param_name, setting_name in infotext_to_setting_name_mapping: + mapping = [(info.infotext, k) for k, info in shared.opts.data_labels.items() if info.infotext] + for param_name, setting_name in mapping + infotext_to_setting_name_mapping: value = params.get(param_name, None) if value is None: @@ -399,14 +399,20 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component, return res if override_settings_component is not None: + already_handled_fields = {key: 1 for _, key in paste_fields} + def paste_settings(params): vals = {} - for param_name, setting_name in infotext_to_setting_name_mapping: + mapping = [(info.infotext, k) for k, info in shared.opts.data_labels.items() if info.infotext] + for param_name, setting_name in mapping + infotext_to_setting_name_mapping: + if param_name in already_handled_fields: + continue + v = params.get(param_name, None) if v is None: continue - + if setting_name == "sd_model_checkpoint" and shared.opts.disable_weights_auto_swap: continue @@ -416,7 +422,7 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component, if v == current_value: continue - vals[param_name] = v + vals[param_name] = v vals_pairs = [f"{k}: {v}" for k, v in vals.items()] diff --git a/modules/gradio_extensons.py b/modules/gradio_extensons.py new file mode 100644 index 00000000..aac742ef --- /dev/null +++ b/modules/gradio_extensons.py @@ -0,0 +1,73 @@ +import gradio as gr + +from modules import scripts, ui_tempdir, patches + + +def add_classes_to_gradio_component(comp): + """ + this adds gradio-* to the component for css styling (ie gradio-button to gr.Button), as well as some others + """ + + comp.elem_classes = [f"gradio-{comp.get_block_name()}", *(comp.elem_classes or [])] + + if getattr(comp, 'multiselect', False): + comp.elem_classes.append('multiselect') + + +def IOComponent_init(self, *args, **kwargs): + self.webui_tooltip = kwargs.pop('tooltip', None) + + if scripts.scripts_current is not None: + scripts.scripts_current.before_component(self, **kwargs) + + scripts.script_callbacks.before_component_callback(self, **kwargs) + + res = original_IOComponent_init(self, *args, **kwargs) + + add_classes_to_gradio_component(self) + + scripts.script_callbacks.after_component_callback(self, **kwargs) + + if scripts.scripts_current is not None: + scripts.scripts_current.after_component(self, **kwargs) + + return res + + +def Block_get_config(self): + config = original_Block_get_config(self) + + webui_tooltip = getattr(self, 'webui_tooltip', None) + if webui_tooltip: + config["webui_tooltip"] = webui_tooltip + + config.pop('example_inputs', None) + + return config + + +def BlockContext_init(self, *args, **kwargs): + res = original_BlockContext_init(self, *args, **kwargs) + + add_classes_to_gradio_component(self) + + return res + + +def Blocks_get_config_file(self, *args, **kwargs): + config = original_Blocks_get_config_file(self, *args, **kwargs) + + for comp_config in config["components"]: + if "example_inputs" in comp_config: + comp_config["example_inputs"] = {"serialized": []} + + return config + + +original_IOComponent_init = patches.patch(__name__, obj=gr.components.IOComponent, field="__init__", replacement=IOComponent_init) +original_Block_get_config = patches.patch(__name__, obj=gr.blocks.Block, field="get_config", replacement=Block_get_config) +original_BlockContext_init = patches.patch(__name__, obj=gr.blocks.BlockContext, field="__init__", replacement=BlockContext_init) +original_Blocks_get_config_file = patches.patch(__name__, obj=gr.blocks.Blocks, field="get_config_file", replacement=Blocks_get_config_file) + + +ui_tempdir.install_ui_tempdir_override() diff --git a/modules/hypernetworks/hypernetwork.py b/modules/hypernetworks/hypernetwork.py index c4821d21..70f1cbd2 100644 --- a/modules/hypernetworks/hypernetwork.py +++ b/modules/hypernetworks/hypernetwork.py @@ -10,7 +10,7 @@ import torch import tqdm from einops import rearrange, repeat from ldm.util import default -from modules import devices, processing, sd_models, shared, sd_samplers, hashes, sd_hijack_checkpoint, errors +from modules import devices, sd_models, shared, sd_samplers, hashes, sd_hijack_checkpoint, errors from modules.textual_inversion import textual_inversion, logging from modules.textual_inversion.learn_schedule import LearnRateScheduler from torch import einsum @@ -469,8 +469,7 @@ def create_hypernetwork(name, enable_sizes, overwrite_old, layer_structure=None, def train_hypernetwork(id_task, hypernetwork_name, learn_rate, batch_size, gradient_step, data_root, log_directory, training_width, training_height, varsize, steps, clip_grad_mode, clip_grad_value, shuffle_tags, tag_drop_out, latent_sampling_method, use_weight, create_image_every, save_hypernetwork_every, template_filename, preview_from_txt2img, preview_prompt, preview_negative_prompt, preview_steps, preview_sampler_index, preview_cfg_scale, preview_seed, preview_width, preview_height): - # images allows training previews to have infotext. Importing it at the top causes a circular import problem. - from modules import images + from modules import images, processing save_hypernetwork_every = save_hypernetwork_every or 0 create_image_every = create_image_every or 0 diff --git a/modules/images.py b/modules/images.py index 38aa933d..eb644733 100644 --- a/modules/images.py +++ b/modules/images.py @@ -21,8 +21,6 @@ from modules import sd_samplers, shared, script_callbacks, errors from modules.paths_internal import roboto_ttf_file from modules.shared import opts -import modules.sd_vae as sd_vae - LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS) @@ -318,7 +316,7 @@ def resize_image(resize_mode, im, width, height, upscaler_name=None): return res -invalid_filename_chars = '<>:"/\\|?*\n' +invalid_filename_chars = '<>:"/\\|?*\n\r\t' invalid_filename_prefix = ' ' invalid_filename_postfix = ' .' re_nonletters = re.compile(r'[\s' + string.punctuation + ']+') @@ -342,16 +340,6 @@ def sanitize_filename_part(text, replace_spaces=True): class FilenameGenerator: - def get_vae_filename(self): #get the name of the VAE file. - if sd_vae.loaded_vae_file is None: - return "NoneType" - file_name = os.path.basename(sd_vae.loaded_vae_file) - split_file_name = file_name.split('.') - if len(split_file_name) > 1 and split_file_name[0] == '': - return split_file_name[1] # if the first character of the filename is "." then [1] is obtained. - else: - return split_file_name[0] - replacements = { 'seed': lambda self: self.seed if self.seed is not None else '', 'seed_first': lambda self: self.seed if self.p.batch_size == 1 else self.p.all_seeds[0], @@ -367,7 +355,9 @@ class FilenameGenerator: 'date': lambda self: datetime.datetime.now().strftime('%Y-%m-%d'), 'datetime': lambda self, *args: self.datetime(*args), # accepts formats: [datetime], [datetime], [datetime