diff --git a/sd-webui-weight-helper-main/.flake8 b/sd-webui-weight-helper-main/.flake8 new file mode 100644 index 0000000000000000000000000000000000000000..905c8bbe4f14c1d47c7bb8ffa0aee63110389858 --- /dev/null +++ b/sd-webui-weight-helper-main/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 160 \ No newline at end of file diff --git a/sd-webui-weight-helper-main/.github/FUNDING.yml b/sd-webui-weight-helper-main/.github/FUNDING.yml new file mode 100644 index 0000000000000000000000000000000000000000..0a87b5c7824372770bc8682b2ab020413e73fd9b --- /dev/null +++ b/sd-webui-weight-helper-main/.github/FUNDING.yml @@ -0,0 +1,2 @@ +ko_fi: nihedon +buy_me_a_coffee: nihedon diff --git a/sd-webui-weight-helper-main/.github/ISSUE_TEMPLATE/bug_report.yml b/sd-webui-weight-helper-main/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000000000000000000000000000000000..93d9f03fa3c71c9bf68e1a89544f0f9aaa6aaf57 --- /dev/null +++ b/sd-webui-weight-helper-main/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,67 @@ +name: Bug Report +description: Report a problem or unexpected behavior in the application. +title: "[Bug] Brief description of the issue" +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Please fill out the following form to help us reproduce and resolve the issue. + + - type: checkboxes + id: environment + attributes: + label: Operating Environment + description: Please select the environment you are using. + options: + - label: Windows + - label: macOS + - label: Other (e.g., virtual environment) + validations: + required: true + + - type: input + id: browser + attributes: + label: Browser + description: Please specify the browser you are using (e.g., Chrome, Firefox). + validations: + required: true + + - type: checkboxes + id: webui + attributes: + label: WebUI + description: Please select the WebUI(s) you are using. + options: + - label: A1111 + - label: Forge + - label: ReForge + - label: SD-Next + - label: Other + validations: + required: true + + - type: textarea + id: console_log + attributes: + label: Console Log + description: Paste any relevant console logs here. + validations: + required: false + + - type: textarea + id: browser_log + attributes: + label: Browser Log + description: Paste any relevant browser logs here. + validations: + required: false + + - type: textarea + id: description + attributes: + label: Description of the Issue + description: Please describe the issue you encountered in detail. + validations: + required: true diff --git a/sd-webui-weight-helper-main/.gitignore b/sd-webui-weight-helper-main/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4a9111446fe600c154a66eb1b082ec3d95fa05af --- /dev/null +++ b/sd-webui-weight-helper-main/.gitignore @@ -0,0 +1,164 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ +node_modules/ +package-lock.json diff --git a/sd-webui-weight-helper-main/.prettierrc.json b/sd-webui-weight-helper-main/.prettierrc.json new file mode 100644 index 0000000000000000000000000000000000000000..9dbc6bf685a6eae9dca79f1297f198c9bb59dea0 --- /dev/null +++ b/sd-webui-weight-helper-main/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 4, + "trailingComma": "all", + "printWidth": 160 +} diff --git a/sd-webui-weight-helper-main/LICENSE b/sd-webui-weight-helper-main/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..6f0da084b8535f2e3c094505138597a1d0f178b1 --- /dev/null +++ b/sd-webui-weight-helper-main/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 t.nihei + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/sd-webui-weight-helper-main/README.md b/sd-webui-weight-helper-main/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9269223065a5660843893892cb47a69427551b51 --- /dev/null +++ b/sd-webui-weight-helper-main/README.md @@ -0,0 +1,69 @@ +[English](README.md) | [日本語](README_JP.md) + + + +# Weight Helper Extension + +## Overview + +**Weight Helper** is an extension that allows you to visually adjust the weights of Lora and Lyco with mouse operations.
+You can adjust the magnification of each weight block through the context menu.
+Note: The LoRA Block Weight extension is required to use this extension. + +## Installation + +1. Open the [Extensions] tab in Stable Diffusion Web UI. +2. Select [Install from URL]. +3. Enter the following URL and execute the installation. +``` +https://github.com/nihedon/sd-webui-weight-helper.git +``` + +## Usage + +1. Right-click on a Lora or Lyco tag to open the context menu. + - `` + - `` +2. Choose "LoRA" or "LyCORIS" according to the tag, and select "SDXL" if it's for the SDXL version. +3. Move the weight slider to adjust the magnification of each weight block. +4. UNet, Dyn, Start, and Stop will be hidden by default, but if configured, you can display them by pressing the "show more options" button. +5. Save your favorite weight settings by clicking the "★" icon at the top left of the context menu. + Important: Saved settings are stored in LocalStorage, but any unsaved history will be deleted upon refreshing the screen. + +## Features + +- Easy to set weight values with mouse operations. +- Use of preset values from Lora Block Weight is possible. +- You can check LoRA information and add trigger words from the icons displayed on the preview screen. +- Information from the readme file attached to LoRA can be displayed on the preview screen. ({loraname}.txt, {loraname}.description.txt) +- The SD version of LoRA is displayed based on the metadata of LoRA. (If there is metadata text from Civitai, the SD version registered with Civitai will also be displayed.) + +## Known Issues +- The algorithm is deduced from the metadata of LoRA, but it is incomplete. Bug reports and suggestions for the correct algorithm would be appreciated. + +## Configuration Options + +- Scale adjustment in the context menu. +- Use of the `execCommand` function for text replacement (Note: execCommand is a deprecated function, but it allows for "undo" and "redo"). +- Display of UNet, Dyn, Start, and Stop sliders. +- Setting of minimum, maximum, and step values for each weight slider. +- Detailed settings for LBW's Lora and Lyco blocks. +- Option to display or hide previews, and settings for preview size and position. +- Settings for block weight segmentation. + +## Guaranteed Operating Environment + +- Stable Diffusion AUTOMATIC1111 +- Windows +- Google Chrome + +## Acknowledgments + +We would like to express our deep gratitude to **hako-mikan**, the author of **LoRA Block Weight**, for creating this extension. +``` +https://github.com/hako-mikan/sd-webui-lora-block-weight +``` + +## License + +This project is published under the [MIT License](LICENSE). diff --git a/sd-webui-weight-helper-main/README_JP.md b/sd-webui-weight-helper-main/README_JP.md new file mode 100644 index 0000000000000000000000000000000000000000..8eed3fda05fbc2ef6341434cdcce6d38a6f705e4 --- /dev/null +++ b/sd-webui-weight-helper-main/README_JP.md @@ -0,0 +1,69 @@ +[English](README.md) | [日本語](README_JP.md) + + + +# Weight Helper 拡張機能 + +## 概要 + +**Weight Helper** はLoraやLycoのウェイトをマウス操作で視覚的に調整できる拡張機能です。
+コンテキストメニューを通じて各ウェイトブロックの倍率を調整できます。
+注:この拡張機能を利用するにはLoRA Block Weight拡張機能が必要です。 + +## インストール + +1. Stable Diffusion Web UIの[拡張機能]タブを開く +2. [URLからインストール]を選択 +3. 以下のURLを入力し、インストールを実行 +``` +https://github.com/nihedon/sd-webui-weight-helper.git +``` + +## 使い方 + +1. LoraまたはLycoのタグを右クリックしてコンテキストメニューを開く + - `` + - `` +2. LoRA(またはLyCORIS)に合わせて"LoRA"か"LyCORIS"を選択し、SDXLバージョンの場合は"SDXL"を選択します。 +3. ウェイトスライダーを動かして各ウェイトブロックの倍率を調整します。 +4. UNet、Dyn、Start、Stopは非表示となっているため、設定した場合は"show more options"ボタンを押下して表示できます。 +5. お気に入りのウェイト設定はコンテキストメニュー左上の"★"アイコンをクリックし保存します。 + 重要:保存した場合はLocalStrageに保存されますが、保存していない履歴は画面更新時にすべて削除されます。 + +## 特徴 + +- マウス操作でウェイト値を容易に設定できます。 +- Lora Block Weightのプリセット値の利用が可能です。 +- プレビュー画面に表示されるアイコンからLoRA情報の確認、トリガーワードの追加ができます。 +- LoRAに付属するreadmeファイルの情報をプレビュー画面に表示できます。({loraname}.txt, {loraname}.description.txt) +- LoRAのメタ情報からLoRAのSDバージョンが表示されます。(Civitaiのメタテキストが存在する場合、civitaiに登録したSDバージョンも表示します。) + +## 既知の問題 +- LoRAのメタ情報からアルゴリズムを割り出していますが不完全です。不具合報告や正しいアルゴリズムを提示していただけると助かります(_ _) + +## 設定オプション + +- コンテキストメニューのスケール調整 +- テキスト置換に `execCommand` 関数の使用(注:execCommandは非推奨関数ですが、「元に戻す」「やり直し」が使えます) +- UNet、Dyn、Start、およびStopスライダーの表示 +- 各ウェイトスライダーの最小値、最大値、ステップ数の設定 +- LBWのLoraおよびLycoブロックの詳細設定 +- プレビューの表示有無、表示サイズと表示位置の設定 +- ブロックウェイトの区切り設定 + +## 動作保証環境 + +- Stable Difusion AUTOMATIC1111 +- Windows +- Google Chrome + +## 謝辞 + +この拡張機能を作成するにあたり、**LoRA Block Weight** の作者である **hako-mikan** 様に深く感謝申し上げます。 +``` +https://github.com/hako-mikan/sd-webui-lora-block-weight +``` + +## ライセンス + +このプロジェクトは[MITライセンス](LICENSE)の下で公開されています。 diff --git a/sd-webui-weight-helper-main/backup_before_patch/scripts/analyzer.py b/sd-webui-weight-helper-main/backup_before_patch/scripts/analyzer.py new file mode 100644 index 0000000000000000000000000000000000000000..119f23a4a9bc143546145e38879191fb98544790 --- /dev/null +++ b/sd-webui-weight-helper-main/backup_before_patch/scripts/analyzer.py @@ -0,0 +1,189 @@ +import os +import pickle +import re +from typing import Optional +from scripts import const, env + + +def get_model_type(lora) -> Optional[str]: + if env.ui_type == const.UI_TYPE.A1111: + sd_version: Optional[str] = lora.sd_version.name + if sd_version and env.network: + if sd_version == env.network.SdVersion.SD1.name or sd_version == env.network.SdVersion.SD2.name: + return "SD" + elif sd_version == env.network.SdVersion.SDXL.name: + return "SDXL" + return sd_version + elif env.ui_type == const.UI_TYPE.NEW_FORGE: + base_model_version: Optional[str] = lora.metadata.get("ss_base_model_version") + if base_model_version: + base_model_version = base_model_version.lower() + if "sd_" in base_model_version: + return "SD" + if "sd3" in base_model_version: + return "SD" + elif "sdxl" in base_model_version: + return "SDXL" + elif "flux" in base_model_version: + return "Flux" + return None + return None + + +def get_algorithm(metadata, ss_network_args) -> Optional[str]: + ss_network_module = metadata.get("ss_network_module") + if not ss_network_module or ss_network_module == "Unknown": + return None + + conv_dim = float(ss_network_args.get("conv_dim", "-1")) + conv_alpha = float(ss_network_args.get("conv_alpha", "-1")) + algo = ss_network_args.get("algo", "").lower() + unit = ss_network_args.get("unit", "").lower() + dora_wd = ss_network_args.get("dora_wd", False) + + if ss_network_module.find("locon.locon_kohya") >= 0: + return "LoCon" + + elif ss_network_module.find("lycoris.kohya") >= 0: + algoName = "" + if dora_wd: + algoName = "(DoRA)" + elif algo == "lora": + algoName += "(LoCon)" + elif algo == "locon": + if unit: + algoName += "(DyLoRA)" + else: + algoName += "(LoCon)" + elif algo == "loha": + algoName += "(LoHa)" + elif algo == "lokr": + algoName += "(Lokr)" + elif algo == "ia3": + algoName += "(IA3)" + elif algo == "full": + algoName += "(Full)" + elif algo == "glora": + algoName += "(GLoRA)" + + return f"LyCORIS{algoName}" + + elif ss_network_module.find("networks.dylora") >= 0: + if algo == "dylora" and unit: + if conv_dim > 0 or conv_alpha > 0: + return "DyLoRA(C3Lier)" + return "DyLoRA(LierLa)" + + elif conv_dim > 0 or conv_alpha > 0: + return "LoRA(C3Lier)" + + else: + return "LoRA(LierLa)" + + +def get_blocks(lora_on_disk, force): + baseKeys = __get_lora_state_keys(lora_on_disk.filename, force) + + def formatForDownMidUp(key): + blocks = ["BASE"] + for key in keys: + k, v = key.split("_", 1) + if k == "down": + k = "IN" + v1, v2 = v.split("_") + val = int(v1) * 3 + int(v2) + 1 + elif k == "mid": + blocks.append("M00") + continue + elif k == "up": + k = "OUT" + v1, v2 = v.split("_") + val = int(v1) * 3 + int(v2) + blocks.append(f'{k}{int(val):02}') + return sorted(blocks) + + def formatForInputMiddleOutput(key): + blocks = ["BASE"] + for key in keys: + k, v = key.split("_") + if k == "input": + k = "IN" + elif k == "middle": + blocks.append("M00") + continue + elif k == "output": + k = "OUT" + blocks.append(f'{k}{int(v):02}') + return sorted(blocks) + + def formatForFlux(key): + blocks = ["CLIP", "T5", "IN", "OUT"] + for key in keys: + k, v = key.split("_") + v = int(v) + if k == "double": + k = "D" + if k == "single": + k = "S" + blocks.append(f'{k}{v:02}') + return sorted(blocks) + + keys = [k for k in baseKeys if k.find("lora_unet") == 0] + if len(keys) > 0: + keys_tmp = [k for k in keys if re.match("^lora_unet_(down|mid|up)_", k)] + if len(keys_tmp) > 0: + keys = set(["_".join([k.split("_")[2], k.split("_")[4] + "_" + k.split("_")[6]]) for k in keys_tmp if "transformer_blocks" in k]) + return formatForDownMidUp(keys) + + keys_tmp = [k for k in keys if re.match("^lora_unet_(input|middle|output)_", k)] + if len(keys_tmp) > 0: + keys = set(["_".join([k.split("_")[2], k.split("_")[4]]) for k in keys_tmp if "transformer_blocks" in k]) + return formatForInputMiddleOutput(keys) + + keys_tmp = [k for k in keys if re.match("^lora_unet_(single|double)_", k)] + if len(keys_tmp) > 0: + keys = set(["_".join([k.split("_")[2], k.split("_")[4]]) for k in keys_tmp]) + return formatForFlux(keys) + + keys_tmp = [k for k in keys if re.match("^lora_unet_joint_blocks_", k)] + if len(keys_tmp) > 0: + # TODO SD3? 38 blocks + return None + + keys = [k for k in baseKeys if "transformer_blocks" in k and "attentions" in k] + if len(keys) > 0: + # TODO unknown + keys = set([".".join(k.split(".")[2:6]) for k in keys]) + in_blocks = set([f"{k.split('.')[1]}_{k.split('.')[3]}" for k in keys if "down_block" in k]) + mid_block = set([k.split(".")[2] for k in keys if "mid_block" in k]) + out_blocks = set([f"{k.split('.')[1]}_{k.split('.')[3]}" for k in keys if "up_block" in k]) + keys = list(in_blocks) + list(mid_block) + list(out_blocks) + return None + + keys = [k for k in baseKeys if "transformer_blocks" in k and k.find("transformer") == 0] + if len(keys) > 0: + keys = set([".".join(k.split(".")[1:3]) for k in keys]) + keys = [k.replace("single_transformer_blocks.", "single_") for k in keys] + keys = [k.replace("transformer_blocks.", "double_") for k in keys] + return formatForFlux(keys) + + return None + + +def __get_lora_state_keys(filename, force): + cache_path = f"{os.path.splitext(filename)[0]}.whcache" + if not force and os.path.exists(cache_path): + try: + with open(cache_path, "rb") as f: + return pickle.load(f) + except Exception: + pass + + lora_sd = env.network_lora.load_lora_state_dict(filename) + keys = list(lora_sd.keys()) + try: + with open(cache_path, "wb") as f: + pickle.dump(keys, f) + except Exception: + pass + return keys diff --git a/sd-webui-weight-helper-main/backup_before_patch/scripts/api.py b/sd-webui-weight-helper-main/backup_before_patch/scripts/api.py new file mode 100644 index 0000000000000000000000000000000000000000..dba5960d16b10b082f5b674e3948101d15005e67 --- /dev/null +++ b/sd-webui-weight-helper-main/backup_before_patch/scripts/api.py @@ -0,0 +1,145 @@ + +import ast +import json +import os +from typing import Any, Optional +from modules import shared +from scripts import analyzer, const, utils + + +def get_metadata(key, force) -> dict[str, Optional[str]]: + ret: dict[str, Optional[str]] = { + "modelType": None, + "algorithm": None, + "baseModel": None, + "usingBlocks": None + } + + lora_on_disk = utils.get_lora_on_disk(key) + if not lora_on_disk: + return ret + + ret["modelType"] = analyzer.get_model_type(lora_on_disk) + + metadata = lora_on_disk.metadata + ss_network_args = __get_network_args(lora_on_disk) + ret["algorithm"] = analyzer.get_algorithm(metadata, ss_network_args) + + model_path, _ = os.path.splitext(lora_on_disk.filename) + civitai_info = __find_civitai_info(model_path) + ret["baseModel"] = civitai_info.get("baseModel", None) + + if shared.opts.weight_helper_parse_lora_blocks: + try: + ret["usingBlocks"] = analyzer.get_blocks(lora_on_disk, force) + except Exception: + pass + return ret + + +def get_preview_info(key): + ret: dict[str, Optional[bool | str | list[str]]] = { + "modelId": None, + "triggerWords": [], + "negativeTriggerWords": [], + "modelName": None, + "thumbUrl": None, + "hasMetadata": False, + "description": None, + } + + lora_on_disk = utils.get_lora_on_disk(key) + if lora_on_disk: + model_path, _ = os.path.splitext(lora_on_disk.filename) + ret["modelName"] = lora_on_disk.name + ret["thumbUrl"] = __find_preview(model_path) + ret["hasMetadata"] = __find_metadata(lora_on_disk) + ret["description"] = __find_description(model_path) + + trigger_words = [] + civitai_info = __find_civitai_info(model_path) + ret["modelId"] = civitai_info.get("modelId", None) + trigger_words = civitai_info.get("trainedWords", []) + trigger_words = ",".join(trigger_words).split(",") + ret["triggerWords"] = [w.strip() for w in trigger_words if w] + + lora_json = __get_lora_json(model_path) + + activation_text = lora_json.get("activation text", "") + if activation_text: + activation_text = activation_text.split(",") + activation_text = [w.strip() for w in activation_text if w] + ret["triggerWords"] = activation_text + + negative_text = lora_json.get("negative text", "") + if negative_text: + negative_text = negative_text.split(",") + negative_text = [w.strip() for w in negative_text if w] + ret["negativeTriggerWords"] = negative_text + + json_description = lora_json.get("description", None) + if json_description: + ret["description"] = json_description + + return ret + + +def __get_network_args(lora) -> dict[str, Any]: + metadata = lora.metadata + ss_network_args = metadata.get("ss_network_args", {}) + if type(ss_network_args) is str: + try: + ss_network_args = ast.literal_eval(ss_network_args) + except Exception: + ss_network_args = {} + return ss_network_args + + +def __get_lora_json(path) -> dict[str, Any]: + if path: + lora_json = f"{path}.json" + if os.path.exists(lora_json): + try: + with open(lora_json, "r", encoding="utf-8", errors="replace") as f: + return json.load(f) + except OSError: + pass + return {} + + +def __find_preview(path) -> str: + if path: + potential_files = sum([[f"{path}.{ext}", f"{path}.preview.{ext}"] for ext in const.ALLOWED_PREVIEW_EXTENSIONS], []) + for file in potential_files: + if os.path.exists(file): + dpi_scale = 1.5 + return utils.resize_to_base64(file, int(shared.opts.weight_helper_preview_height * dpi_scale)) + return "./file=html/card-no-preview.png" + + +def __find_metadata(lora) -> bool: + return len(lora.metadata) > 0 if lora else False + + +def __find_description(path) -> Optional[str]: + if path: + for file in [f"{path}.txt", f"{path}.description.txt"]: + if os.path.exists(file): + try: + with open(file, "r", encoding="utf-8", errors="replace") as f: + return f.read() + except OSError: + pass + return None + + +def __find_civitai_info(path) -> dict[str, Any]: + if path: + civitai_info = f"{path}.civitai.info" + if os.path.exists(civitai_info): + try: + with open(civitai_info, "r", encoding="utf-8", errors="replace") as f: + return json.load(f) + except OSError: + pass + return {} diff --git a/sd-webui-weight-helper-main/backup_before_patch/scripts/const.py b/sd-webui-weight-helper-main/backup_before_patch/scripts/const.py new file mode 100644 index 0000000000000000000000000000000000000000..423e2161ee36425a668780936bc4108c83875d7e --- /dev/null +++ b/sd-webui-weight-helper-main/backup_before_patch/scripts/const.py @@ -0,0 +1,11 @@ +import enum + + +API_PREFIX = "/whapi/v1" +ALLOWED_PREVIEW_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "gif"] + + +class UI_TYPE(enum.Enum): + Unknown = 1 + A1111 = 2 + NEW_FORGE = 3 diff --git a/sd-webui-weight-helper-main/backup_before_patch/scripts/env.py b/sd-webui-weight-helper-main/backup_before_patch/scripts/env.py new file mode 100644 index 0000000000000000000000000000000000000000..57b373e70a04e10af14d584e7e7172dcc02dabf2 --- /dev/null +++ b/sd-webui-weight-helper-main/backup_before_patch/scripts/env.py @@ -0,0 +1,29 @@ + +import importlib +from scripts import const + +network = None +network_lora = None +ui_type = None + +try: + from modules_forge import forge_version # noqa: F401 + ui_type = const.UI_TYPE.NEW_FORGE +except ImportError: + ui_type = const.UI_TYPE.A1111 + +try: + network = importlib.import_module("extensions-builtin.sd_forge_lora.network") + network_lora = importlib.import_module("extensions-builtin.sd_forge_lora.ui_extra_networks_lora").networks +except Exception: + pass + +if network_lora is None: + try: + network = importlib.import_module("extensions-builtin.Lora.network") + network_lora = importlib.import_module("extensions-builtin.Lora.ui_extra_networks_lora").networks + except Exception: + pass + +if network_lora is None: + ui_type = const.UI_TYPE.Unknown diff --git a/sd-webui-weight-helper-main/backup_before_patch/scripts/utils.py b/sd-webui-weight-helper-main/backup_before_patch/scripts/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..e15ca39369b658a7785f2e1b578773ae85debe7e --- /dev/null +++ b/sd-webui-weight-helper-main/backup_before_patch/scripts/utils.py @@ -0,0 +1,58 @@ +import base64 +import cv2 +import urllib + +import numpy as np +from scripts import env + + +def get_lora_on_disk(key): + if env.network_lora: + lora_on_disk = env.network_lora.available_network_aliases.get(key) + if not lora_on_disk: + # sdnext + lora_on_disk = next((v for v in env.network_lora.available_network_aliases.values() if v.alias == key), None) + return lora_on_disk + return None + + +def imread_unicode(path): + try: + with open(path, 'rb') as f: + data = np.frombuffer(f.read(), np.uint8) + img = cv2.imdecode(data, cv2.IMREAD_UNCHANGED) + return img + except Exception as e: + print(f"imread_unicode error: {e}") + return None + + +def resize_to_base64(input_path, target_height): + output_format = '.webp' + quality = 85 + + img = imread_unicode(input_path) + + h, w = img.shape[:2] + if h < target_height: + quoted_filename = urllib.parse.quote(input_path.replace('\\', '/')) + return f"./sd_extra_networks/thumb?filename={quoted_filename}" + + scale = target_height / h + target_size = (int(w * scale), target_height) + + resized = cv2.resize(img, target_size, interpolation=cv2.INTER_AREA) + + sharpen_kernel = np.array([ + [0, -0.2, 0], + [-0.2, 1.8, -0.2], + [0, -0.2, 0] + ]) + sharpened = cv2.filter2D(resized, -1, sharpen_kernel) + success, buffer = cv2.imencode(output_format, sharpened, [cv2.IMWRITE_WEBP_QUALITY, quality]) + if not success: + quoted_filename = urllib.parse.quote(input_path.replace('\\', '/')) + return f"./sd_extra_networks/thumb?filename={quoted_filename}" + + base64_str = base64.b64encode(buffer).decode('utf-8') + return f'data:image/webp;base64,{base64_str}' diff --git a/sd-webui-weight-helper-main/backup_before_patch/scripts/weight_helper.py b/sd-webui-weight-helper-main/backup_before_patch/scripts/weight_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..527e67b9c90cede8b6a72c6710c036a1b50866f7 --- /dev/null +++ b/sd-webui-weight-helper-main/backup_before_patch/scripts/weight_helper.py @@ -0,0 +1,82 @@ +import gradio as gr +from fastapi import FastAPI +from modules import script_callbacks, shared +from scripts import api, const + + +def on_app_started(_: gr.Blocks, app: FastAPI): + @app.post(const.API_PREFIX + "/get_metadata") + async def _(key: str, force: bool = False): + return api.get_metadata(key, force) + + @app.post(const.API_PREFIX + "/get_preview_info") + async def _(key: str): + return api.get_preview_info(key) + + +def on_ui_settings(): + """ + Set up the UI settings for the weight helper by adding options to the shared configuration. + """ + section = ('weight_helper', 'Weight Helper') + + shared.options_templates.update(shared.options_section(section, { + 'weight_helper_enabled': shared.OptionInfo(True, 'Enabled'), + 'weight_helper_context_menu_scale': shared.OptionInfo(0.8, "Context Menu Scale", gr.Number), + 'weight_helper_using_execCommand': shared.OptionInfo(True, "Use the deprecated execCommand function to replace text.").info( + "You can use 'undo' to revert the text to its previous state, " + "but it will no longer be updated in real-time."), + + "weight_helper_slider_length": shared.OptionInfo(160, "Slider Length (px)", gr.Number), + + "weight_helper_TEnc_min": shared.OptionInfo(0, "TEnc Min Value", gr.Number), + "weight_helper_TEnc_max": shared.OptionInfo(1, "TEnc Max Value", gr.Number), + "weight_helper_TEnc_step": shared.OptionInfo(0.05, "TEnc Step", gr.Number), + + "weight_helper_UNet_min": shared.OptionInfo(0, "UNet Min Value", gr.Number), + "weight_helper_UNet_max": shared.OptionInfo(1, "UNet Max Value", gr.Number), + "weight_helper_UNet_step": shared.OptionInfo(0.05, "UNet Step", gr.Number), + + "weight_helper_LBW_min": shared.OptionInfo(0, "LBW (LoRA Block Weight) Min Value", gr.Number), + "weight_helper_LBW_max": shared.OptionInfo(1, "LBW (LoRA Block Weight) Max Value", gr.Number), + "weight_helper_LBW_step": shared.OptionInfo(0.05, "LBW (LoRA Block Weight) Step", gr.Number), + + 'weight_helper_show_preview': shared.OptionInfo(True, 'Show Preview'), + "weight_helper_preview_height": shared.OptionInfo(400, "Preview Height (px)", gr.Number), + "weight_helper_preview_position": shared.OptionInfo("Top Right", "Preview Position", gr.Radio, { + "choices": ["Top Right", "Bottom Right", "Top Left", "Bottom Left"] + }), + + 'weight_helper_parse_lora_blocks': shared.OptionInfo(True, '[experimental] Parse LoRA file to get block information'), + + "weight_helper_LBW_SD_lora_block_points": shared.OptionInfo( + "BASE, IN01-IN04, IN05-IN08, M00, OUT03-OUT06, OUT07-OUT11", + "Advanced option - LoRA-LierLa block points") + .info("default: BASE, IN01-IN04, IN05-IN08, M00, OUT03-OUT06, OUT07-OUT11"), + "weight_helper_LBW_SD_lycoris_block_points": shared.OptionInfo( + "BASE, IN00-IN05, IN06-IN11, M00, OUT00-OUT05, OUT06-OUT11", + "Advanced option - LyCORIS or LoRA-C3Lier block points") + .info("default: BASE, IN00-IN05, IN06-IN11, M00, OUT00-OUT05, OUT06-OUT11"), + + "weight_helper_LBW_SDXL_lora_block_points": shared.OptionInfo( + "BASE, IN04-IN08, M00, OUT00-OUT05", + "Advanced option - SDXL LoRA-LierLa block points") + .info("default: BASE, IN04-IN08, M00, OUT00-OUT05"), + "weight_helper_LBW_SDXL_lycoris_block_points": shared.OptionInfo( + "BASE, IN00-IN03, IN04-IN08, M00, OUT00-OUT03, OUT04-OUT08", + "Advanced option - SDXL LyCORIS or LoRA-C3Lier block points") + .info("default: BASE, IN00-IN03, IN04-IN08, M00, OUT00-OUT03, OUT04-OUT08"), + + "weight_helper_LBW_Flux_lora_block_points": shared.OptionInfo( + "CLIP, T5, IN, OUT, D00-D18, S00-S18, S19-S37", + "Advanced option - Flux LoRA-LierLa block points") + .info("default: CLIP, T5, IN, OUT, D00-D18, S00-S18, S19-S37"), + "weight_helper_LBW_Flux_lycoris_block_points": shared.OptionInfo( + "CLIP, T5, IN, OUT, D00-D18, S00-S18, S19-S37", + "Advanced option - Flux LyCORIS or LoRA-C3Lier block points") + .info("default: CLIP, T5, IN, OUT, D00-D18, S00-S18, S19-S37"), + })) + + +script_callbacks.on_ui_settings(on_ui_settings) +script_callbacks.on_app_started(on_app_started) diff --git a/sd-webui-weight-helper-main/esbuild.config.mjs b/sd-webui-weight-helper-main/esbuild.config.mjs new file mode 100644 index 0000000000000000000000000000000000000000..366815ea8489e9ce1db48f0f50dbdd67faed5201 --- /dev/null +++ b/sd-webui-weight-helper-main/esbuild.config.mjs @@ -0,0 +1,16 @@ +import { build } from 'esbuild'; + +const baseOptions = { + entryPoints: ['src/index.ts'], + bundle: true, + outfile: 'javascript/weight_helper.js', + target: 'es2022', + format: 'iife', + platform: 'browser', +}; + +build({ + ...baseOptions, + minify: process.argv.includes('--minify'), + sourcemap: process.argv.includes('--sourcemap'), +}).catch(() => process.exit(1)); diff --git a/sd-webui-weight-helper-main/eslint.config.mjs b/sd-webui-weight-helper-main/eslint.config.mjs new file mode 100644 index 0000000000000000000000000000000000000000..e0aa414901de8303983b3b6b5e6deb5cf5fefab0 --- /dev/null +++ b/sd-webui-weight-helper-main/eslint.config.mjs @@ -0,0 +1,19 @@ +import { defineConfig } from "eslint/config"; +import globals from "globals"; +import tseslint from "typescript-eslint"; +import prettier from 'eslint-config-prettier'; + +export default defineConfig([ + ...tseslint.configs.recommended, + { + files: ['**/*.ts'], + languageOptions: { + globals: globals.browser, + }, + rules: { + semi: ['error', 'always'], + quotes: ['error', 'single'], + }, + }, + prettier, +]); diff --git a/sd-webui-weight-helper-main/javascript/weight_helper.js b/sd-webui-weight-helper-main/javascript/weight_helper.js new file mode 100644 index 0000000000000000000000000000000000000000..cf475bed1e4d2eb5b275eb426522987fcde6ff4d --- /dev/null +++ b/sd-webui-weight-helper-main/javascript/weight_helper.js @@ -0,0 +1,188 @@ +"use strict";(()=>{var S={lora:"lora",lycoris:"lycoris",Unknown:"Unknown"},y={SD:"SD",SDXL:"SDXL",Flux:"Flux",Unknown:"Unknown"},P={te:"te",unet:"unet",start:"start",stop:"stop",step:"step",lbw:"lbw",lbwe:"lbwe"},m={TENC:"TEnc",UNET:"UNet",START:"Start",STOP:"Stop",LBW:"LBW"};var De={STABLE_DIFFUSION:["BASE","IN00","IN01","IN02","IN03","IN04","IN05","IN06","IN07","IN08","IN09","IN10","IN11","M00","OUT00","OUT01","OUT02","OUT03","OUT04","OUT05","OUT06","OUT07","OUT08","OUT09","OUT10","OUT11"],BLACK_FOREST_LABS:["CLIP","T5","IN","D00","D01","D02","D03","D04","D05","D06","D07","D08","D09","D10","D11","D12","D13","D14","D15","D16","D17","D18","S00","S01","S02","S03","S04","S05","S06","S07","S08","S09","S10","S11","S12","S13","S14","S15","S16","S17","S18","S19","S20","S21","S22","S23","S24","S25","S26","S27","S28","S29","S30","S31","S32","S33","S34","S35","S36","S37","OUT"]},ne={[y.SD]:De.STABLE_DIFFUSION,[y.SDXL]:De.STABLE_DIFFUSION,[y.Flux]:De.BLACK_FOREST_LABS,[y.Unknown]:[]},rt={[y.SD]:{[S.lora]:[1,0,1,1,0,1,1,0,1,1,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1],[S.lycoris]:[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[S.Unknown]:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},[y.SDXL]:{[S.lora]:[1,0,0,0,0,1,1,0,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0],[S.lycoris]:[1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0],[S.Unknown]:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},[y.Flux]:{[S.lora]:[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[S.lycoris]:[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[S.Unknown]:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},[y.Unknown]:{[S.lora]:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[S.lycoris]:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[S.Unknown]:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},nt={[y.SD]:{[S.lora]:"XYZ(17)",[S.lycoris]:"XYZ(26)",[S.Unknown]:"XYZ(26)"},[y.SDXL]:{[S.lora]:"XYZ(12)",[S.lycoris]:"XYZ(20)",[S.Unknown]:"XYZ(26)"},[y.Flux]:{[S.lora]:"XYZ(19)",[S.lycoris]:"XYZ(19)",[S.Unknown]:"XYZ(19)"},[y.Unknown]:{[S.lora]:"XYZ(17)",[S.lycoris]:"XYZ(26)",[S.Unknown]:"XYZ(26)"}},K={"":y.Unknown,SD:y.SD,SDXL:y.SDXL,Flux:y.Flux},J={"":S.Unknown,"LoRA(LierLa)":S.lora,"LyCORIS,etc":S.lycoris},be="./file=html/card-no-preview.png";var ye={[m.TENC]:{min:0,max:0,default:1,step:.5},[m.UNET]:{min:0,max:0,default:0,step:.5},[m.START]:{min:0,max:0,default:0,step:1},[m.STOP]:{min:0,max:0,default:0,step:1},[m.LBW]:{min:0,max:0,default:1,step:.5}},st={[y.SD]:{[S.lora]:[["BASE","IN01-IN04","IN05-IN08","M00","OUT03-OUT06","OUT07-OUT11"]],[S.lycoris]:[["BASE","IN00-IN05","IN06-IN11","M00","OUT00-OUT05","OUT06-OUT11"]],[S.Unknown]:[[]]},[y.SDXL]:{[S.lora]:[["BASE","IN04-IN08","M00","OUT00-OUT05"]],[S.lycoris]:[["BASE","IN00-IN03","IN04-IN08","M00","OUT00-OUT03","OUT04-OUT08"]],[S.Unknown]:[[]]},[y.Flux]:{[S.lora]:[["CLIP","T5","IN","OUT"],["D00-D18"],["S00-S18"],["S19-S37"]],[S.lycoris]:[["CLIP","T5","IN","OUT"],["D00-D18"],["S00-S18"],["S19-S37"]],[S.Unknown]:[[]]},[y.Unknown]:{[S.lora]:[[]],[S.lycoris]:[[]],[S.Unknown]:[[]]}};function at(){let e=/((BASE|MID|M00|CLIP|T5|(IN|OUT|D|S)[0-9]{2}(-(IN|OUT|D|S)[0-9]{2})?) *(, *|$))+/;for(let t of Object.values(K))if(!(t===y.Unknown||t===y.Flux)){for(let o of Object.values(J))if(o!==S.Unknown)try{let r=opts[`weight_helper_LBW_${t}_${o}_block_points`];if(r=r.replace("MID","M00"),e.exec(r)){let n=r.split(",").map(a=>a.trim().replace(/\d+/g,s=>s.length===1?`0${s}`:s));st[t][o]=[n]}}catch(r){console.warn(`${t}_${o} block definition format is invalid.`,r)}}for(let t of[m.TENC,m.UNET,m.LBW])ye[t].min=+opts[`weight_helper_${t}_min`],ye[t].max=+opts[`weight_helper_${t}_max`],ye[t].step=+opts[`weight_helper_${t}_step`]}function T(e){return ye[e]}function we(e){return e in ne?ne[e]:ne[y.Unknown]}function Q(e,t){let o=V(e,t);return ne[e||y.Unknown].filter((r,n)=>o[n]===1)}function V(e,t){return rt[e||y.Unknown][t||S.Unknown]}function N(e,t){return st[e||y.Unknown][t||S.Unknown]}function it(e,t){return nt[e||y.Unknown][t||S.Unknown]}var A={},E={};function O(e){return A[e]??[]}function ee(e,t){t={loraParams:t.loraParams,selectedLoraBlockType:t.selectedLoraBlockType,selectedModelType:t.selectedModelType},e in A?(A[e]=A[e].filter(o=>o.loraParams!==t.loraParams),A[e].push(t)):A[e]=[t]}function lt(e){if(e in A){let t=E[e];A[e]=A[e].filter(o=>t&&t.has(o.loraParams))}}function ct(e,t){e in E?E[e].add(t):E[e]=new Set([t])}function gt(e,t){e in E&&E[e].delete(t)}function U(e,t){return e in E?E[e].has(t):!1}function dt(){localStorage.setItem("wh://histories",JSON.stringify(A));let e={};for(let t in E)e[t]=Array.from(E[t]);localStorage.setItem("wh://locks",JSON.stringify(e))}function pt(){let e=localStorage.getItem("wh://histories");e&&(A=JSON.parse(e));let t=localStorage.getItem("wh://locks");if(t){let o=JSON.parse(t);for(let r in o)E[r]=new Set(o[r])}}function G(e,t,o,r){let n=new Set(Q(e,t)),a=Object.entries(r).filter(([l])=>n.has(l)).map(l=>l[1].value).join(","),s=Object.entries(o).find(([,l])=>l===a);return s?s[0]:""}function Te(e){let t,o=typeof window.TAC_CFG<"u"&&window.TAC_CFG,r,n=typeof window.pilotIsActive<"u"&&window.pilotIsActive;try{o&&(t=window.TAC_CFG.activeIn.global,window.TAC_CFG.activeIn.global=!1),n&&(r=window.pilotIsActive,window.pilotIsActive=!1),e()}finally{o&&(window.TAC_CFG.activeIn.global=t),n&&(window.pilotIsActive=r)}}var ht={},ut={};function ft(e){return ht[e]}function mt(e,t){ht[e]={metadataState:t.metadataState,selectedLoraBlockType:t.weightState.selectedLoraBlockType,selectedModelType:t.weightState.selectedModelType,usingBlocks:t.usingBlocks}}function St(e){return ut[e]}function bt(e,t){ut[e]=t}var yt,wt,Tt,Mt,kt,xt={},vt=0;function se(){return yt}function Lt(e){yt=e}function ae(){return wt}function Ct(e){wt=e}function He(){return Tt}function $t(e){Tt=e}function Re(){return Mt}function Me(e){Mt=e}function _t(){return kt}function Bt(e){kt=e}function ke(){return vt||0}function At(e){vt=+gradioApp().querySelector(`div:is([id^='tab_${e}'][class*='tabitem'])`).querySelector('div:is([id$="_steps"]) input[type=number]').value}function $(e,t){return xt[e||y.Unknown][t||S.Unknown]}function Et(e){let n=(gradioApp().querySelector(`div:is([id^='tab_${e}'][class*='tabitem'])`).querySelector("#lbw_ratiospreset textarea").value??"").split(` +`).filter(s=>s.trim()!==""),a={};for(let s of Object.values(y)){a[s]={};for(let l of Object.values(S)){let i={},g={};a[s][l]=i;let u=V(s,l).filter(c=>c==1).length;for(let c of n){let h=c.split(":");h.length==2&&h[1].split(",").length==u&&(i[h[0]]=h[1],g[h[1]]=h[0])}}}xt=a}var Pt=e=>e.toLowerCase(),z=e=>e?"":"display: none",D=e=>e?"":"visibility: hidden",I=(e,t=void 0)=>{if(e.uiState.isWaiting)return!0;switch(t){case m.TENC:case m.UNET:case m.START:case m.STOP:case void 0:return!1}return e.usingBlocks!==void 0&&!e.usingBlocks.has(t)};function xe(e){let t=[];return e.split(",").map(o=>o.trim()).forEach(o=>{let r=o.match(/^([A-Z]+)(\d+)-\1(\d+)$/);if(!r){t.push(o);return}let[,n,a,s]=r,l=parseInt(a),i=parseInt(s),g=l;for(;t.push(`${n}${g.toString().padStart(2,"0")}`),g!=i;)g+=l]+)>/;function It(e){let t=e.target;if(!opts.weight_helper_enabled)return;let o=window.getSelection()?.toString();if(o)return;let r=t.value,n=t.selectionStart,a=r.lastIndexOf("<",n-1),s=r.indexOf(">",n);if(a<0||s<0||(o=r.substring(a,s+1),(o.match(//g)||[]).length!=1))return;n=a;let l=wo.exec(o);if(l){e.preventDefault();let i=l[1].toLowerCase(),g=l[2],u=l[3],c=t.closest("[id^='tab_'][class*='tabitem']").id.split("_")[1],h=n+l.index,p=h+l.input.trim().length;return{tabId:c,textarea:t,selectionStart:h,selectionEnd:p,namespace:i,loraName:g,loraParams:u,top:e.pageY+15,left:e.pageX}}}function X(e){let t=ae(),o=He(),r=Re();t.value=t.value.substring(0,o)+e+t.value.substring(r),Me(o+e.length)}function To(e){Te(()=>{let t=ae(),o=He(),r=Re();t.focus(),t.setSelectionRange(o,r),document.execCommand("insertText",!1,e)})}function _(e,t){let o=String(t.weights[m.TENC].value),r=0,n=0;for(let p of[m.UNET]){let w=t.weights[p],b=T(p).default,f=+w.value;(w.checkState||w.checkState===void 0&&f!=b)&&(n===r?o+=`:${f}`:o+=`:${Pt(p)}=${f}`,r++),n++}let a=T(m.START).default,s=t.weights[m.START].value,l=T(m.STOP).default,i=t.weights[m.STOP].value;i<0&&(i=T(m.STOP).default),s!=a&&i!=l?o+=`:step=${s}-${i}`:s!=a?o+=`:start=${s}`:i!=l&&(o+=`:stop=${i}`);let{selectedModelType:g,selectedLoraBlockType:u}=t,c=t.xyzMode,h=o;if(c)o+=":lbw=XYZ",h+=":lbw=XYZ";else if(g!==y.Unknown){let p=V(g,u).map((b,f)=>{if(b===1)return t.weights[we(g)[f]]}).filter(b=>b!==void 0),w=T(m.LBW).default;if(!p.every(b=>b.value===w)){let b=p.map(C=>C.value).join(",");o+=`:lbw=${b}`;let f=$(g,u),M=Object.entries(f).find(([,C])=>b===C);M?h+=`:lbw=${M[0]}`:h+=`:lbw=${b}`}}return t.lbwe&&(o+=`:lbwe=${t.lbwe}`,h+=`:lbwe=${t.lbwe}`),{loraParams:o,loraDefine:``}}function le(e,t=!1){let o=_(e.loraName,e.weightState);opts.weight_helper_using_execCommand?e.srcLoraParams!==o.loraParams&&To(o.loraDefine):t&&X(_t()),mt(e.loraName,e),e.weightState.xyzMode||ee(e.loraName,{loraParams:o.loraParams,...e.weightState}),dt()}var d,ve=[],Le=[];function v(){ve.forEach(e=>e(d))}function Mo(e){Le.forEach(t=>t(e))}function Wt(e){return ve.push(e),()=>{let t=ve.indexOf(e);t>-1&&ve.splice(t,1)}}function Nt(e){return Le.push(e),()=>{let t=Le.indexOf(e);t>-1&&Le.splice(t,1)}}function L(){return d}function Ut(e){d=e,v()}function Dt(e){d=e,v()}function Ot(e){d=e,v()}function Ht(e,t){Mo({top:e,left:t})}function Ce(e){let t={...d,metadataState:{...d.metadataState,algorithm:e.algorithm||"Unknown",baseModel:e.baseModel||"Unknown",modelType:e.modelType||"Unknown"},weightState:{...d.weightState,selectedModelType:e.selectedModelType,weights:e.weights},blockGroups:e.blockGroups,lbwPresets:e.lbwPresets,uiState:{...d.uiState,isWaiting:!1}};e.usingBlocks===null?t.usingBlocks=void 0:e.usingBlocks&&(t.usingBlocks=new Set(e.usingBlocks)),d=t,v()}function ce(e){d={...d,uiState:{...d.uiState,isWaiting:e}},v()}function Ve(e){let t={...d,weightState:{...d.weightState,selectedLoraBlockType:e.selectedLoraBlockType,selectedModelType:e.selectedModelType,weights:e.weights},blockGroups:e.blockGroups,lbwPresets:e.lbwPresets};opts.weight_helper_using_execCommand||X(_(t.loraName,t.weightState).loraDefine),d=t,v()}function Rt(){let e={...d,weightState:{...d.weightState,xyzMode:!d.weightState.xyzMode}};opts.weight_helper_using_execCommand||X(_(e.loraName,e.weightState).loraDefine),d=e,v()}function Vt(e){let t={...d.weightState,weights:{...d.weightState.weights,...e.weights}},o=_(d.loraName,t),r=U(d.loraName,o.loraParams),n={...d,weightState:t,preset:e.preset,lock:r};opts.weight_helper_using_execCommand||X(o.loraDefine),d=n,v()}function Gt(){let e=_(d.loraName,d.weightState).loraParams,t=U(d.loraName,e);t?gt(d.loraName,e):ct(d.loraName,e),d={...d,lock:!t},v()}function Ge(e){let{locked:t,historyIndex:o,preset:r}=e,{selectedLoraBlockType:n,selectedModelType:a,weights:s,lbwe:l,xyzMode:i}=e.weightState,g={...d.weightState,weights:{...d.weightState.weights,...s},selectedLoraBlockType:n,selectedModelType:a,lbwe:l,xyzMode:i},u={...d,historyIndex:o,preset:r,weightState:g,lock:t};opts.weight_helper_using_execCommand||X(_(u.loraName,u.weightState).loraDefine),d=u,v()}function zt(e){d={...d,historyIndex:e},v()}function $e(e){d.weightState.xyzMode=!1;let t={...d.weightState.weights,[e.block]:{...d.weightState.weights[e.block],value:e.value,sliderMin:Math.min(e.value,d.weightState.weights[e.block].sliderMin),sliderMax:Math.max(e.value,d.weightState.weights[e.block].sliderMax),checkState:e.checkState}},o={...d.weightState,weights:t,xyzMode:!1},r=_(d.loraName,o),n=U(d.loraName,r.loraParams),a={...d,weightState:o,preset:G(d.weightState.selectedModelType,d.weightState.selectedLoraBlockType,d.lbwPresets,t),lock:n};opts.weight_helper_using_execCommand||X(r.loraDefine),d=a,v()}function Xt(){d={...d,uiState:{...d.uiState,isMoreButtonVisible:!1}},v()}function Ft(){lt(d.loraName),d.weightState.xyzMode||ee(d.loraName,{loraParams:_(d.loraName,d.weightState).loraParams,...d.weightState});let e=O(d.loraName).length-1;d={...d,historyIndex:e<0?0:e},v()}function Yt(e){d={...d,previewState:e},v()}function ze(e){d={...d,previewState:{...d.previewState,isDescriptionVisible:e}},v()}function jt(){d={...d,previewState:{...d.previewState,isTagInsertButtonVisible:!1}},v()}function _e(){return{loraName:"",metadataState:void 0,srcLoraParams:"",weightState:{selectedLoraBlockType:S.lora,selectedModelType:y.SD,weights:{},lbwe:"",xyzMode:!1},blockGroups:[],usingBlocks:void 0,preset:"",lbwPresets:{},uiState:{isVisible:!1,scale:1,isMoreButtonVisible:!0,isWaiting:!1},lock:!1,historyIndex:0,previewState:{modelId:"",triggerWords:[],negativeTriggerWords:[],modelName:"",thumbUrl:"",hasMetadata:!1,description:"",isDescriptionVisible:!1,isTagInsertButtonVisible:!1}}}function Zt(e,t,o){let r=y.Unknown,n=S.Unknown,a=O(t);if(a&&a.length>0){let p=a[a.length-1];r=p.selectedModelType??y.Unknown,n=p.selectedLoraBlockType??S.Unknown}let s,l,i=ft(t);i&&(s=i.metadataState,r=i.selectedModelType??y.Unknown,n=i.selectedLoraBlockType??S.Unknown,l=i.usingBlocks);let g=ko(e,o,r,n),u=$(g.selectedModelType,g.selectedLoraBlockType),c=_(t,g).loraParams,h=!1;for(let p of[m.UNET,m.START,m.STOP]){let w=g.weights[p],b=T(p).default,f=+w.value;if(!(w.checkState||w.checkState===void 0&&f!=b)){h=!0;break}}return{loraName:t,metadataState:s,srcLoraParams:c,weightState:g,blockGroups:N(g.selectedModelType,g.selectedLoraBlockType),usingBlocks:l,uiState:{isVisible:!0,scale:opts.weight_helper_context_menu_scale,isMoreButtonVisible:h,isWaiting:!s},previewState:xo(t),preset:G(g.selectedModelType,g.selectedLoraBlockType,u,g.weights),lbwPresets:u,lock:U(t,c),historyIndex:a.length-1}}function ko(e,t,o,r){let n=Be(t,o,r),{weights:a,lbwe:s,xyzMode:l}=n;return r===S.Unknown&&(r=e==="lora"?S.lora:S.lycoris),{selectedLoraBlockType:r,selectedModelType:o,weights:a,lbwe:s,xyzMode:l}}function Be(e,t,o){let r=e.split(":"),n={};for(let b=0;b{switch(b){case P.lbw:if(f==="XYZ"){h=!0;break}let M=f.split(",");for(let W of a){if(M.length===1)if(f in W.presets)M=W.presets[f].split(",");else continue;if(M.length===W.masks.filter(q=>q==1).length){t=W.modelType,o=W.loraBlockType;let q=0;for(let Ue of W.masks)Ue?(u.push(+M[q]),q++):u.push(0);break}}break;case P.step:let C=f.split("-");i=Math.round(+C[0]),g=Math.round(+C[1]);break;case P.lbwe:c=f;break;case P.te:s=+f;break;case P.unet:l=+f;break;case P.start:i=Math.round(+f);break;case P.stop:g=Math.round(+f);break}});let p=!0;l===void 0&&(l=T(m.UNET).default,p=!1);let w={};if(t!==y.Unknown){let b=we(t);if(u.length===0)for(let f=0;f{w[b[M]]={initValue:f,value:f,sliderMin:Math.min(f,T(m.LBW).min),sliderMax:Math.max(f,T(m.LBW).max)}})}return{selectedLoraBlockType:o,selectedModelType:t,weights:{[m.TENC]:{initValue:s,value:s,sliderMin:Math.min(s,T(m.TENC).min),sliderMax:Math.max(s,T(m.TENC).max)},[m.UNET]:{initValue:l,value:l,sliderMin:Math.min(l,T(m.UNET).min),sliderMax:Math.max(l,T(m.UNET).max),initCheckState:p,checkState:p},[m.START]:{initValue:i,value:i,sliderMin:Math.min(i,T(m.START).min),sliderMax:Math.max(i,T(m.START).max)},[m.STOP]:{initValue:g,value:g,sliderMin:Math.min(g,T(m.STOP).min),sliderMax:Math.max(g,T(m.STOP).max)},...w},lbwe:c,xyzMode:h}}function xo(e){let t=St(e);return t||{modelId:"",triggerWords:[""],negativeTriggerWords:[""],modelName:"",thumbUrl:be,hasMetadata:!1,description:"",isDescriptionVisible:!1,isTagInsertButtonVisible:!1}}var Ae=null,Ee=null;function Pe(){return Ae&&Ae.abort(),Ae=new AbortController,Ae}function qt(){return Ee&&Ee.abort(),Ee=new AbortController,Ee}async function Ie(e,t,o){let r=await fetch(`/whapi/v1/get_metadata?key=${encodeURIComponent(e)}&force=${t}`,{method:"POST",signal:o});if(!r.ok)throw new Error(`Failed to set up API endpoints. Please reload the page.: ${r.status}`);return await r.json()}async function Kt(e,t){let o=await fetch(`/whapi/v1/get_preview_info?key=${encodeURIComponent(e)}`,{method:"POST",signal:t});if(!o.ok)throw new Error(`Failed to set up API endpoints. Please reload the page.: ${o.status}`);return{...await o.json(),isDescriptionVisible:!1,isTagInsertButtonVisible:!0}}function F(e,t){let o={...e},r=[];return t.forEach(n=>{n.forEach(a=>{xe(a).forEach(l=>{r.includes(l)||r.push(l)})})}),r.forEach(n=>{o[n]||(o[n]={initValue:T(m.LBW).default,value:T(m.LBW).default,sliderMin:T(m.LBW).min,sliderMax:T(m.LBW).max})}),o}var Ke=globalThis,We=Ke.trustedTypes,Jt=We?We.createPolicy("lit-html",{createHTML:e=>e}):void 0,no="$lit$",R=`lit$${Math.random().toFixed(9).slice(2)}$`,so="?"+R,vo=`<${so}>`,Z=document,de=()=>Z.createComment(""),pe=e=>e===null||typeof e!="object"&&typeof e!="function",Je=Array.isArray,Lo=e=>Je(e)||typeof e?.[Symbol.iterator]=="function",Xe=`[ +\f\r]`,ge=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Qt=/-->/g,eo=/>/g,Y=RegExp(`>|${Xe}(?:([^\\s"'>=/]+)(${Xe}*=${Xe}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),to=/'/g,oo=/"/g,ao=/^(?:script|style|textarea|title)$/i,Qe=e=>(t,...o)=>({_$litType$:e,strings:t,values:o}),k=Qe(1),rr=Qe(2),nr=Qe(3),he=Symbol.for("lit-noChange"),x=Symbol.for("lit-nothing"),ro=new WeakMap,j=Z.createTreeWalker(Z,129);function io(e,t){if(!Je(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return Jt!==void 0?Jt.createHTML(t):t}var Co=(e,t)=>{let o=e.length-1,r=[],n,a=t===2?"":t===3?"":"",s=ge;for(let l=0;l"?(s=n??ge,c=-1):u[1]===void 0?c=-2:(c=s.lastIndex-u[2].length,g=u[1],s=u[3]===void 0?Y:u[3]==='"'?oo:to):s===oo||s===to?s=Y:s===Qt||s===eo?s=ge:(s=Y,n=void 0);let p=s===Y&&e[l+1].startsWith("/>")?" ":"";a+=s===ge?i+vo:c>=0?(r.push(g),i.slice(0,c)+no+i.slice(c)+R+p):i+R+(c===-2?l:p)}return[io(e,a+(e[o]||"")+(t===2?"":t===3?"":"")),r]},ue=class e{constructor({strings:t,_$litType$:o},r){let n;this.parts=[];let a=0,s=0,l=t.length-1,i=this.parts,[g,u]=Co(t,o);if(this.el=e.createElement(g,r),j.currentNode=this.el.content,o===2||o===3){let c=this.el.content.firstChild;c.replaceWith(...c.childNodes)}for(;(n=j.nextNode())!==null&&i.length0){n.textContent=We?We.emptyScript:"";for(let p=0;p2||r[0]!==""||r[1]!==""?(this._$AH=Array(r.length-1).fill(new String),this.strings=r):this._$AH=x}_$AI(t,o=this,r,n){let a=this.strings,s=!1;if(a===void 0)t=oe(this,t,o,0),s=!pe(t)||t!==this._$AH&&t!==he,s&&(this._$AH=t);else{let l=t,i,g;for(t=a[0],i=0;i{let r=o?.renderBefore??t,n=r._$litPart$;if(n===void 0){let a=o?.renderBefore??null;r._$litPart$=n=new fe(t.insertBefore(de(),a),a,void 0,o??{})}return n._$AI(e),n};function me(e,t){let o=L(),r=T(e)??T(m.LBW),n=u=>{let c=u.target;$e({block:e,value:o.weightState.weights[e].value,checkState:c.checked})},a=u=>{let h=+u.target.value/100,p=o.weightState.weights[e].checkState;p!==void 0&&(p=!0),$e({block:e,value:h,checkState:p})},s=u=>{let h=+u.target.value,p=o.weightState.weights[e].checkState;p!==void 0&&(p=!0),$e({block:e,value:h,checkState:p})},l=t[e],i=u=>{let c=u.target,h=+c.value/100,w=c.parentElement.querySelector("input.value");w&&(w.value=h.toString())},g=u=>{let c=u.target,p=c.parentElement.querySelector("input.slider");if(!p)return;let w=+c.value;if(isNaN(w))return;let b=+p.min,f=+p.max,M=Math.round(w*100);Mf&&(f=M),b!==+p.min&&(p.min=String(b)),f!==+p.max&&(p.max=String(f)),p.value=String(M)};return k` + + + ${o.weightState.weights[e].checkState!==void 0?k``:""} + +
+ + +
+ `}var tt=!1,ot={x:0,y:0};function lo(e,t){let o=L(),r=O(o.loraName),n=c=>{tt=!0;let h=document.getElementById("weight-helper");if(!h)return;let p=h.getBoundingClientRect();ot={x:c.clientX-p.left,y:c.clientY-p.top},document.addEventListener("mousemove",a),document.addEventListener("mouseup",s)},a=c=>{if(!tt)return;let h=c.clientX-ot.x+window.scrollX,p=c.clientY-ot.y+window.scrollY;Ht(p,h)},s=()=>{tt=!1,document.removeEventListener("mousemove",a),document.removeEventListener("mouseup",s)},l=()=>{Ft()},i=()=>{Gt()},g=()=>{let c=Math.max(0,o.historyIndex-1),h=r[c],p=Be(h.loraParams,h.selectedModelType,h.selectedLoraBlockType),w=U(o.loraName,h.loraParams),b=G(h.selectedModelType,h.selectedLoraBlockType,o.lbwPresets,p.weights);Ge({historyIndex:c,weightState:p,locked:w,preset:b})},u=()=>{let c=Math.min(r.length-1,o.historyIndex+1),h=r[c],p=Be(h.loraParams,h.selectedModelType,h.selectedLoraBlockType),w=U(o.loraName,h.loraParams),b=G(h.selectedModelType,h.selectedLoraBlockType,o.lbwPresets,p.weights);Ge({historyIndex:c,weightState:p,locked:w,preset:b})};return k` +
+ + + + + + + + +
+ clear +
+ < + + > +
+
+
+ `}function co(){let e=L(),{blockGroups:t,weightState:o}=e,{selectedModelType:r,selectedLoraBlockType:n}=o,a=new Set(Q(r,n));return k` +
+ ${t.map(s=>k` +
+ ${s.map(l=>k` +
+ ${xe(l).map(i=>a.has(i)&&o.weights[i]?k`
${me(i,o.weights)}
`:"")} +
+ `)} +
+ `)} +
+ `}function go(){let e=L(),t=a=>{let l=a.target.value,i=N(e.weightState.selectedModelType,l),g=$(e.weightState.selectedModelType,l),u=F(e.weightState.weights,i);Ve({selectedLoraBlockType:l,selectedModelType:e.weightState.selectedModelType,blockGroups:i,lbwPresets:g,weights:u})},o=a=>{let l=a.target.value,i=N(l,e.weightState.selectedLoraBlockType),g=$(l,e.weightState.selectedLoraBlockType),u=F(e.weightState.weights,i);Ve({selectedModelType:l,selectedLoraBlockType:e.weightState.selectedLoraBlockType,blockGroups:i,lbwPresets:g,weights:u})},r=()=>{Rt()},n=a=>{let s=a.target,l=s.value;if(l===""&&(l=e.preset,l===""))return;let i=$(e.weightState.selectedModelType,e.weightState.selectedLoraBlockType)[l].split(",").map(c=>+c),g={};Q(e.weightState.selectedModelType,e.weightState.selectedLoraBlockType).forEach((c,h)=>{g[c]={initValue:e.weightState.weights[c].initValue,value:i[h],sliderMin:Math.min(i[h],e.weightState.weights[c].sliderMin),sliderMax:Math.max(i[h],e.weightState.weights[c].sliderMax)}}),Vt({weights:g,preset:s.value})};return k` +
+ + +
+
+ +
+
+ + +
+
+
+ `}function po(){let e=L(),t=()=>{if(e.uiState.isWaiting)return;let o=Pe();ce(!0),Ie(e.loraName,!0,o.signal).then(r=>{let n=e.weightState.selectedModelType;(!n||n===y.Unknown)&&(n=r.modelType??y.Unknown);let a=N(n,e.weightState.selectedLoraBlockType),s=F(e.weightState.weights,a);Ce({algorithm:r.algorithm||"Unknown",modelType:r.modelType||"Unknown",baseModel:r.baseModel||"Unknown",selectedModelType:n,usingBlocks:r.usingBlocks,blockGroups:a,lbwPresets:$(n,e.weightState.selectedLoraBlockType),weights:s})}).catch(r=>{r.name!=="AbortError"&&(console.error("Error fetching metadata:",r),ce(!1))})};return k` + + `}function ho(e){let t=i=>{extraNetworksRequestMetadata(i,"lora",e.modelName)},o=i=>{extraNetworksEditUserMetadata(i,se(),"lora",e.modelName)},r=()=>{window.open(`https://civitai.com/models/${e.modelId}`,"_blank")},n=()=>{ze(!0)},a=()=>{ze(!1)},s=()=>{let i=se(),g=document.querySelector(`#${i}_prompt textarea`),u=document.querySelector(`#${i}_neg_prompt textarea`),c=ae();if(c===u||c!==g&&(g=c,u=void 0),!opts.weight_helper_using_execCommand)(()=>{let p=[{triggerWords:e.triggerWords,textarea:g},{triggerWords:e.negativeTriggerWords,textarea:u}];for(let{triggerWords:w,textarea:b}of p)if(w.length>0&&b){let f=w.join(", ");b.value&&(f=", "+f),b.value+=f}})();else{let h=()=>{let p=[{triggerWords:e.triggerWords,textarea:g},{triggerWords:e.negativeTriggerWords,textarea:u}];for(let{triggerWords:w,textarea:b}of p)if(w.length>0&&b){let f=w.join(", ");b.value&&(f=", "+f),b.focus();let M=b.value.length;b.setSelectionRange(M,M),document.execCommand("insertText",!1,f)}};Te(()=>{h()})}jt()},l=["preview-pane","card"];switch(opts.weight_helper_preview_position){case"Bottom Right":l.push("preview-bottom-right");break;case"Top Left":l.push("preview-top-left");break;case"Bottom Left":l.push("preview-bottom-left");break;default:l.push("preview-top-right");break}return k` +
+ +
+ ${e.modelName&&e.hasMetadata&&typeof extraNetworksRequestMetadata=="function"?k` `:""} + ${e.modelName&&typeof extraNetworksEditUserMetadata=="function"?k`
`:""} + ${e.modelId?k`
`:""} + ${e.triggerWords.length>0||e.negativeTriggerWords.length>0?k` +
+ `:""} +
+ + ${e.description?k` +
+
+
+ +
+ `:""} +
+ `}var uo=!1;function fo(){let e=L(),{uiState:t,weightState:o,lock:r}=e;uo||(_o(),uo=!0);let n=`transform: scale(${t.scale}); ${z(t.isVisible)}`;return k` +
+ ${lo(e.loraName,r)} ${po()} + ${o.weights[m.TENC]&&k`
${me(m.TENC,o.weights)}
`} + ${[m.UNET,m.START,m.STOP].filter(a=>!!o.weights[a]).map(a=>k`
+ ${me(a,o.weights)} +
`)} + + + +
+ +
${go()} ${co()}
+
+ + ${e.previewState.modelName?ho(e.previewState):""} +
+ `}function _o(){let e=()=>{let s=L();s.uiState.isVisible&&(Se(),le(s))},t=s=>{let l=L();if(l.uiState.isVisible){s.preventDefault(),Se(),le(l);return}let i=It(s);i&&(s.preventDefault(),Bo(i))},o=s=>{let l=L();if(l.uiState.isVisible){let i=document.getElementById("weight-helper");if(!i)return;let g=se();if(s){let u=s.target;if(i.contains(u)||u.id===`${g}_token_button`||u.id===`${g}_lora_edit_user_metadata_button`||u.className==="global-popup-close")return;if(u.id.indexOf("_interrupt")>0){Se();return}}Se(),le(l)}},r=s=>{if(s.key==="Escape"){let l=L();Ao(l)}};gradioApp().querySelectorAll("button:is([id*='_generate'])").forEach(s=>{s.addEventListener("click",e,!0)}),gradioApp().querySelectorAll("*:is([id*='_toprow'] [id*='_prompt'], .prompt) textarea").forEach(s=>{s.addEventListener("contextmenu",t)}),document.addEventListener("click",o),document.addEventListener("keyup",r)}var mo=!1;function So(e){et(fo(),e),mo||(Wt(()=>{et(fo(),e)}),Nt(t=>{let o=e.querySelector("#weight-helper");o&&(o.style.top=t.top+"px",o.style.left=t.left+"px")}),mo=!0)}function Bo(e){let{tabId:t,textarea:o,selectionStart:r,selectionEnd:n,namespace:a,loraName:s,loraParams:l,top:i,left:g}=e;Lt(t),Ct(o),$t(r),Me(n),Bt(o.value.substring(r,n)),At(t),Et(t),T(m.START).max=ke(),T(m.STOP).max=ke(),T(m.STOP).default=ke();let u=document.getElementById("weight-helper").getBoundingClientRect(),c=window.innerHeight-u.bottom,h=i;c<0&&(u.top<0?h=window.scrollY:h+=c);let p=Zt(a,s,l);Dt(p);let w=document.getElementById("weight-helper");if(w&&(w.style.top=h+"px",w.style.left=g+"px"),!p.metadataState){let b=Pe();Ie(s,!1,b.signal).then(f=>{let M=p.weightState.selectedModelType;(!M||M===y.Unknown)&&(M=f.modelType??y.Unknown);let C=p.weightState.selectedLoraBlockType,W=N(M,C),q=F(p.weightState.weights,W),Ue={...p.weightState,selectedModelType:M,selectedLoraBlockType:C},bo=_(p.loraName,Ue).loraParams;p.weightState.xyzMode||(ee(s,{loraParams:bo,selectedLoraBlockType:C,selectedModelType:M}),zt(O(s).length-1)),Ce({algorithm:f.algorithm||"Unknown",modelType:f.modelType||"Unknown",baseModel:f.baseModel||"Unknown",selectedModelType:M,usingBlocks:f.usingBlocks,blockGroups:W,lbwPresets:$(M,p.weightState.selectedLoraBlockType),weights:q})}).catch(f=>{f.name!=="AbortError"&&(console.error("Error fetching metadata:",f),ce(!1))})}if(!p.previewState.modelId){let b=qt();Kt(s,b.signal).then(async f=>{f.thumbUrl!==be&&(f.thumbUrl=f.thumbUrl),bt(s,f),Yt(f)}).catch(f=>{f.name!=="AbortError"&&console.error("Error fetching metadata:",f)})}}function Se(){let e=_e();Ot(e)}function Ao(e){opts.weight_helper_using_execCommand||le(e,!0),Se()}var Ne,Eo=new Promise(e=>{Ne=e});pt();onUiLoaded(()=>{if(!gradioApp().querySelector("#lbw_ratiospreset"))return;let o=[...getComputedStyle(document.documentElement).getPropertyValue("--body-text-color").trim().slice(1).match(/.{1,2}/g).map(r=>parseInt(r,16)),.3];document.documentElement.style.setProperty("--weight-helper-shadow",`rgba(${o.join(",")})`),document.documentElement.style.setProperty("--weight-helper-slider_size",String(opts.weight_helper_slider_length)),Eo.then(()=>{let r=document.createElement("div");r.id="weight-helper-container",gradioApp().appendChild(r),at(),Ut(_e()),So(r)})});onOptionsChanged(()=>{Ne&&(Ne(!0),Ne=null)});})(); +/*! Bundled license information: + +lit-html/lit-html.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) +*/ +//# sourceMappingURL=weight_helper.js.map diff --git a/sd-webui-weight-helper-main/javascript/weight_helper.js.map b/sd-webui-weight-helper-main/javascript/weight_helper.js.map new file mode 100644 index 0000000000000000000000000000000000000000..3c3a4100be5f1f53565b80ec77cc1676d09dcb55 --- /dev/null +++ b/sd-webui-weight-helper-main/javascript/weight_helper.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../src/shared/types/lora-types.ts", "../src/shared/constants/common-const.ts", "../src/shared/manager/config-manager.ts", "../src/shared/manager/history-manager.ts", "../src/shared/utils/common-utils.ts", "../src/shared/manager/cache-manager.ts", "../src/shared/state/current-state.ts", "../src/shared/utils/helper-utils.ts", "../src/shared/utils/editor-utils.ts", "../src/shared/state/global-weight-helper-state.ts", "../src/shared/utils/state-utils.ts", "../src/shared/manager/api-manager.ts", "../src/shared/utils/api-utils.ts", "../src/shared/utils/weight-utils.ts", "../node_modules/lit-html/src/lit-html.ts", "../src/components/sections/common-section.ts", "../src/components/sections/header-section.ts", "../src/components/sections/lbw-section.ts", "../src/components/sections/lora-settings-section.ts", "../src/components/sections/metadata-section.ts", "../src/components/sections/preview-section.ts", "../src/components/ui-template.ts", "../src/index.ts"], + "sourcesContent": ["export const NamespaceTypes = {\n lora: 'lora',\n lyco: 'lyco',\n} as const;\nexport type NamespaceTypes = (typeof NamespaceTypes)[keyof typeof NamespaceTypes];\n\nexport const LoraBlockTypes = {\n lora: 'lora',\n lycoris: 'lycoris',\n Unknown: 'Unknown',\n} as const;\nexport type LoraBlockTypes = (typeof LoraBlockTypes)[keyof typeof LoraBlockTypes];\n\nexport const ModelTypes = {\n SD: 'SD',\n SDXL: 'SDXL',\n Flux: 'Flux',\n Unknown: 'Unknown',\n} as const;\nexport type ModelTypes = (typeof ModelTypes)[keyof typeof ModelTypes];\n\nexport const LoraDefineParams = {\n te: 'te',\n unet: 'unet',\n start: 'start',\n stop: 'stop',\n step: 'step',\n lbw: 'lbw',\n lbwe: 'lbwe',\n} as const;\nexport type LoraDefineParams = (typeof LoraDefineParams)[keyof typeof LoraDefineParams];\n\nexport const WeightControllerTypes = {\n TENC: 'TEnc',\n UNET: 'UNet',\n START: 'Start',\n STOP: 'Stop',\n LBW: 'LBW',\n} as const;\nexport type WeightControllerTypes = (typeof WeightControllerTypes)[keyof typeof WeightControllerTypes];\n", "import { LoraBlockTypes, ModelTypes } from '@/shared/types/lora-types';\n\nconst BASE_MODEL_TYPE = {\n STABLE_DIFFUSION: [\n 'BASE',\n 'IN00',\n 'IN01',\n 'IN02',\n 'IN03',\n 'IN04',\n 'IN05',\n 'IN06',\n 'IN07',\n 'IN08',\n 'IN09',\n 'IN10',\n 'IN11',\n 'M00',\n 'OUT00',\n 'OUT01',\n 'OUT02',\n 'OUT03',\n 'OUT04',\n 'OUT05',\n 'OUT06',\n 'OUT07',\n 'OUT08',\n 'OUT09',\n 'OUT10',\n 'OUT11',\n ],\n BLACK_FOREST_LABS: [\n 'CLIP',\n 'T5',\n 'IN',\n 'D00',\n 'D01',\n 'D02',\n 'D03',\n 'D04',\n 'D05',\n 'D06',\n 'D07',\n 'D08',\n 'D09',\n 'D10',\n 'D11',\n 'D12',\n 'D13',\n 'D14',\n 'D15',\n 'D16',\n 'D17',\n 'D18',\n 'S00',\n 'S01',\n 'S02',\n 'S03',\n 'S04',\n 'S05',\n 'S06',\n 'S07',\n 'S08',\n 'S09',\n 'S10',\n 'S11',\n 'S12',\n 'S13',\n 'S14',\n 'S15',\n 'S16',\n 'S17',\n 'S18',\n 'S19',\n 'S20',\n 'S21',\n 'S22',\n 'S23',\n 'S24',\n 'S25',\n 'S26',\n 'S27',\n 'S28',\n 'S29',\n 'S30',\n 'S31',\n 'S32',\n 'S33',\n 'S34',\n 'S35',\n 'S36',\n 'S37',\n 'OUT',\n ],\n};\n\nexport const LBW_BLOCKS = {\n [ModelTypes.SD]: BASE_MODEL_TYPE.STABLE_DIFFUSION,\n [ModelTypes.SDXL]: BASE_MODEL_TYPE.STABLE_DIFFUSION,\n [ModelTypes.Flux]: BASE_MODEL_TYPE.BLACK_FOREST_LABS,\n [ModelTypes.Unknown]: [],\n};\n\nexport const lbwMaskDefine = {\n [ModelTypes.SD]: {\n [LoraBlockTypes.lora]: [1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1],\n [LoraBlockTypes.lycoris]: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],\n [LoraBlockTypes.Unknown]: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n },\n [ModelTypes.SDXL]: {\n [LoraBlockTypes.lora]: [1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],\n [LoraBlockTypes.lycoris]: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],\n [LoraBlockTypes.Unknown]: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n },\n [ModelTypes.Flux]: {\n [LoraBlockTypes.lora]: [\n 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n ],\n [LoraBlockTypes.lycoris]: [\n 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n ],\n [LoraBlockTypes.Unknown]: [\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n ],\n },\n [ModelTypes.Unknown]: {\n [LoraBlockTypes.lora]: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n [LoraBlockTypes.lycoris]: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n [LoraBlockTypes.Unknown]: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n },\n};\n\nexport const XYZ_PRESETS = {\n [ModelTypes.SD]: {\n [LoraBlockTypes.lora]: 'XYZ(17)',\n [LoraBlockTypes.lycoris]: 'XYZ(26)',\n [LoraBlockTypes.Unknown]: 'XYZ(26)',\n },\n [ModelTypes.SDXL]: {\n [LoraBlockTypes.lora]: 'XYZ(12)',\n [LoraBlockTypes.lycoris]: 'XYZ(20)',\n [LoraBlockTypes.Unknown]: 'XYZ(26)',\n },\n [ModelTypes.Flux]: {\n [LoraBlockTypes.lora]: 'XYZ(19)',\n [LoraBlockTypes.lycoris]: 'XYZ(19)',\n [LoraBlockTypes.Unknown]: 'XYZ(19)',\n },\n [ModelTypes.Unknown]: {\n [LoraBlockTypes.lora]: 'XYZ(17)',\n [LoraBlockTypes.lycoris]: 'XYZ(26)',\n [LoraBlockTypes.Unknown]: 'XYZ(26)',\n },\n};\n\nexport const SELECTABLE_MODEL_TYPES = {\n '': ModelTypes.Unknown,\n SD: ModelTypes.SD,\n SDXL: ModelTypes.SDXL,\n Flux: ModelTypes.Flux,\n};\n\nexport const SELECTABLE_LORA_BLOCK_TYPES = {\n '': LoraBlockTypes.Unknown,\n 'LoRA(LierLa)': LoraBlockTypes.lora,\n 'LyCORIS,etc': LoraBlockTypes.lycoris,\n};\n\nexport const CARD_NO_IMAGE_PATH = './file=html/card-no-preview.png';\n", "import {\n LBW_BLOCKS,\n lbwMaskDefine as LBW_MASK_DEFINE,\n SELECTABLE_LORA_BLOCK_TYPES,\n SELECTABLE_MODEL_TYPES,\n XYZ_PRESETS,\n} from '@/shared/constants/common-const';\nimport { LoraBlockTypes, ModelTypes, WeightControllerTypes } from '@/shared/types/lora-types';\n\ninterface WeightControllerInterface {\n min: number;\n max: number;\n default: number;\n step: number;\n}\n\nconst WeightControllerConfig: Record = {\n [WeightControllerTypes.TENC]: {\n min: 0,\n max: 0,\n default: 1,\n step: 0.5,\n },\n [WeightControllerTypes.UNET]: {\n min: 0,\n max: 0,\n default: 0,\n step: 0.5,\n },\n [WeightControllerTypes.START]: {\n min: 0,\n max: 0,\n default: 0,\n step: 1,\n },\n [WeightControllerTypes.STOP]: {\n min: 0,\n max: 0,\n default: 0,\n step: 1,\n },\n [WeightControllerTypes.LBW]: {\n min: 0,\n max: 0,\n default: 1,\n step: 0.5,\n },\n};\n\nconst lbwGroupConfig = {\n [ModelTypes.SD]: {\n [LoraBlockTypes.lora]: [['BASE', 'IN01-IN04', 'IN05-IN08', 'M00', 'OUT03-OUT06', 'OUT07-OUT11']],\n [LoraBlockTypes.lycoris]: [['BASE', 'IN00-IN05', 'IN06-IN11', 'M00', 'OUT00-OUT05', 'OUT06-OUT11']],\n [LoraBlockTypes.Unknown]: [[]] as string[][],\n },\n [ModelTypes.SDXL]: {\n [LoraBlockTypes.lora]: [['BASE', 'IN04-IN08', 'M00', 'OUT00-OUT05']],\n [LoraBlockTypes.lycoris]: [['BASE', 'IN00-IN03', 'IN04-IN08', 'M00', 'OUT00-OUT03', 'OUT04-OUT08']],\n [LoraBlockTypes.Unknown]: [[]] as string[][],\n },\n [ModelTypes.Flux]: {\n [LoraBlockTypes.lora]: [['CLIP', 'T5', 'IN', 'OUT'], ['D00-D18'], ['S00-S18'], ['S19-S37']],\n [LoraBlockTypes.lycoris]: [['CLIP', 'T5', 'IN', 'OUT'], ['D00-D18'], ['S00-S18'], ['S19-S37']],\n [LoraBlockTypes.Unknown]: [[]] as string[][],\n },\n [ModelTypes.Unknown]: {\n [LoraBlockTypes.lora]: [[]] as string[][],\n [LoraBlockTypes.lycoris]: [[]] as string[][],\n [LoraBlockTypes.Unknown]: [[]] as string[][],\n },\n};\n\n/**\n * Initializes the configuration for block groups and weight controllers.\n * Updates configuration values with settings from opts.\n */\nexport function initialize() {\n const optBlockPattern = /((BASE|MID|M00|CLIP|T5|(IN|OUT|D|S)[0-9]{2}(-(IN|OUT|D|S)[0-9]{2})?) *(, *|$))+/;\n for (const modelType of Object.values(SELECTABLE_MODEL_TYPES)) {\n if (modelType === ModelTypes.Unknown || modelType === ModelTypes.Flux) continue;\n for (const loraBlockType of Object.values(SELECTABLE_LORA_BLOCK_TYPES)) {\n if (loraBlockType === LoraBlockTypes.Unknown) continue;\n try {\n let optBlockPoints = opts[`weight_helper_LBW_${modelType}_${loraBlockType}_block_points`] as string;\n optBlockPoints = optBlockPoints.replace('MID', 'M00');\n if (optBlockPattern.exec(optBlockPoints)) {\n const blockPoints = optBlockPoints.split(',').map((v) => {\n return v.trim().replace(/\\d+/g, (match) => (match.length === 1 ? `0${match}` : match));\n });\n lbwGroupConfig[modelType][loraBlockType] = [blockPoints];\n }\n } catch (e) {\n console.warn(`${modelType}_${loraBlockType} block definition format is invalid.`, e);\n }\n }\n }\n\n for (const k of [WeightControllerTypes.TENC, WeightControllerTypes.UNET, WeightControllerTypes.LBW]) {\n WeightControllerConfig[k].min = +opts[`weight_helper_${k}_min`];\n WeightControllerConfig[k].max = +opts[`weight_helper_${k}_max`];\n WeightControllerConfig[k].step = +opts[`weight_helper_${k}_step`];\n }\n}\n\n/**\n * Retrieves the configuration for a specific weight controller type.\n * @param type - The weight controller type.\n * @returns The configuration object for the specified controller type.\n */\nexport function getWeightControllerConfig(type: string): WeightControllerInterface {\n return WeightControllerConfig[type];\n}\n\n/**\n * Returns the list of LBW blocks for the selected model type.\n * @param selectedModelType - The selected model type.\n * @returns An array of block labels for the given model type.\n */\nexport function getLbwBlocks(selectedModelType: ModelTypes): string[] {\n if (selectedModelType in LBW_BLOCKS) {\n return LBW_BLOCKS[selectedModelType];\n }\n return LBW_BLOCKS[ModelTypes.Unknown];\n}\n\n/**\n * Returns an array of enabled LBW block labels for the selected model and LoRA block type.\n * Only blocks with a mask value of 1 are included.\n * @param selectedModelType - The selected model type.\n * @param selectedLoraBlockType - The selected LoRA block type.\n * @returns An array of enabled block labels.\n */\nexport function getMaskedLbwBlocks(selectedModelType: ModelTypes, selectedLoraBlockType: LoraBlockTypes): string[] {\n const masks = getLbwMasks(selectedModelType, selectedLoraBlockType);\n return LBW_BLOCKS[selectedModelType || ModelTypes.Unknown].filter((_, i) => masks[i] === 1);\n}\n\n/**\n * Returns the mask array for LBW blocks for the selected model and block type.\n * Each value in the array (1 or 0) indicates whether the corresponding block is enabled.\n * @param selectedModelType - The selected model type.\n * @param selectedLoraBlockType - The selected LoRA block type.\n * @returns An array of binary values (1=enabled, 0=disabled).\n */\nexport function getLbwMasks(selectedModelType: ModelTypes, selectedLoraBlockType: LoraBlockTypes): number[] {\n return LBW_MASK_DEFINE[selectedModelType || ModelTypes.Unknown][selectedLoraBlockType || LoraBlockTypes.Unknown];\n}\n\n/**\n * Returns the block groups configuration for the selected model and block type.\n * @param selectedModelType - The selected model type.\n * @param selectedLoraBlockType - The selected LoRA block type.\n * @returns An array of block group identifiers.\n */\nexport function getLbwBlockGroups(selectedModelType: ModelTypes, selectedLoraBlockType: LoraBlockTypes): string[][] {\n return lbwGroupConfig[selectedModelType || ModelTypes.Unknown][selectedLoraBlockType || LoraBlockTypes.Unknown];\n}\n\n/**\n * Returns the XYZ label for the selected model and block type.\n * @param selectedModelType - The selected model type.\n * @param selectedLoraBlockType - The selected LoRA block type.\n * @returns The XYZ identifier string.\n */\nexport function getXyzLabel(selectedModelType: ModelTypes, selectedLoraBlockType: LoraBlockTypes): string {\n return XYZ_PRESETS[selectedModelType || ModelTypes.Unknown][selectedLoraBlockType || LoraBlockTypes.Unknown];\n}\n", "import { LoraBlockTypes, ModelTypes } from '@/shared/types/lora-types';\n\nlet _historiesStore: Record = {};\nconst _lockStore: Record> = {};\n\n/**\n * Retrieves the history list for the given LoRA name.\n * @param loraName - The name of the LoRA model.\n * @returns An array of history objects for the LoRA.\n */\nexport function getHistories(loraName: string): { loraParams: string; selectedLoraBlockType: LoraBlockTypes; selectedModelType: ModelTypes }[] {\n return _historiesStore[loraName] ?? [];\n}\n\n/**\n * Adds a new history entry for the given LoRA name.\n * If a history with the same loraParams exists, it is replaced.\n * @param loraName - The name of the LoRA model.\n * @param history - The history object to add.\n */\nexport function addHistory(loraName: string, history: { loraParams: string; selectedLoraBlockType: LoraBlockTypes; selectedModelType: ModelTypes }) {\n history = {\n loraParams: history.loraParams,\n selectedLoraBlockType: history.selectedLoraBlockType,\n selectedModelType: history.selectedModelType,\n };\n if (loraName in _historiesStore) {\n _historiesStore[loraName] = _historiesStore[loraName].filter((h) => h.loraParams !== history.loraParams);\n _historiesStore[loraName].push(history);\n } else {\n _historiesStore[loraName] = [history];\n }\n}\n\n/**\n * Clears all unlocked history entries for the given LoRA name.\n * Only locked histories are retained.\n * @param loraName - The name of the LoRA model.\n */\nexport function clearHistories(loraName: string) {\n if (loraName in _historiesStore) {\n const lockSet = _lockStore[loraName];\n _historiesStore[loraName] = _historiesStore[loraName].filter((h) => lockSet && lockSet.has(h.loraParams));\n }\n}\n\n/**\n * Adds a lock for the given LoRA name and loraParams.\n * @param loraName - The name of the LoRA model.\n * @param loraParams - The loraParams string to lock.\n */\nexport function addLock(loraName: string, loraParams: string) {\n if (loraName in _lockStore) {\n _lockStore[loraName].add(loraParams);\n } else {\n _lockStore[loraName] = new Set([loraParams]);\n }\n}\n\n/**\n * Removes a lock for the given LoRA name and loraParams.\n * @param loraName - The name of the LoRA model.\n * @param loraParams - The loraParams string to unlock.\n */\nexport function removeLock(loraName: string, loraParams: string) {\n if (loraName in _lockStore) {\n _lockStore[loraName].delete(loraParams);\n }\n}\n\n/**\n * Checks if the given loraParams is locked for the specified LoRA name.\n * @param loraName - The name of the LoRA model.\n * @param loraParams - The loraParams string to check.\n * @returns True if locked, false otherwise.\n */\nexport function isLocked(loraName: string, loraParams: string) {\n if (loraName in _lockStore) {\n return _lockStore[loraName].has(loraParams);\n }\n return false;\n}\n\n/**\n * Stores the histories and lock information to localStorage.\n */\nexport function storeLocalStorage() {\n localStorage.setItem('wh://histories', JSON.stringify(_historiesStore));\n const lockObj: Record = {};\n for (const key in _lockStore) {\n lockObj[key] = Array.from(_lockStore[key]);\n }\n localStorage.setItem('wh://locks', JSON.stringify(lockObj));\n}\n\n/**\n * Loads the histories and lock information from localStorage.\n */\nexport function loadLocalStorage() {\n const histories = localStorage.getItem('wh://histories');\n if (histories) {\n _historiesStore = JSON.parse(histories);\n }\n const locks = localStorage.getItem('wh://locks');\n if (locks) {\n const lockObj = JSON.parse(locks);\n for (const key in lockObj) {\n _lockStore[key] = new Set(lockObj[key]);\n }\n }\n}\n", "import * as configManager from '@/shared/manager/config-manager';\nimport { LoraBlockTypes, ModelTypes } from '@/shared/types/lora-types';\n\n/**\n * Returns the preset name that matches the given weights for the selected model and block type.\n * @param selectedModelType - The selected model type.\n * @param selectedLoraBlockType - The selected LoRA block type.\n * @param lbwPresets - The record of preset names to values.\n * @param weights - The record of block weights.\n * @returns The matching preset name, or an empty string if no match is found.\n */\nexport function getPreset(\n selectedModelType: ModelTypes,\n selectedLoraBlockType: LoraBlockTypes,\n lbwPresets: Record,\n weights: Record,\n): string {\n const maskedBlockSet = new Set(configManager.getMaskedLbwBlocks(selectedModelType, selectedLoraBlockType));\n const weightValue = Object.entries(weights)\n .filter(([key]) => maskedBlockSet.has(key))\n .map((entries) => entries[1].value)\n .join(',');\n const foundPreset = Object.entries(lbwPresets).find(([, value]) => value === weightValue);\n if (foundPreset) {\n return foundPreset[0];\n }\n return '';\n}\n\n/**\n * Temporarily disables prompt assist features (TagAutocomplete, pilot) while executing the given function.\n * Saves the original state, executes the function, then restores the original state.\n * @param func - The function to execute with prompt assist disabled.\n */\nexport function withoutPromptAssist(func: () => void): void {\n let tacActiveInOrg: boolean | undefined;\n const tacEnabled = typeof window.TAC_CFG !== 'undefined' && window.TAC_CFG;\n\n let pilotOrg: boolean | undefined;\n const pilotEnabled = typeof window.pilotIsActive !== 'undefined' && window.pilotIsActive;\n try {\n if (tacEnabled) {\n tacActiveInOrg = window.TAC_CFG.activeIn.global;\n window.TAC_CFG.activeIn.global = false;\n }\n if (pilotEnabled) {\n pilotOrg = window.pilotIsActive;\n window.pilotIsActive = false;\n }\n func();\n } finally {\n if (tacEnabled) {\n window.TAC_CFG.activeIn.global = tacActiveInOrg;\n }\n if (pilotEnabled) {\n window.pilotIsActive = pilotOrg;\n }\n }\n}\n\n/**\n * Computes a simple hash code for the given string.\n * Uses a bitwise XOR and shift algorithm to generate a 32-bit integer hash.\n * @param s - The input string.\n * @returns The computed hash code as a number.\n */\nexport function strHashCode(s: string): number {\n let hash = 0;\n if (!s) return hash;\n for (let i = 0; i < s.length; i++) {\n const char = s.charCodeAt(i);\n hash = hash ^ char;\n hash = (hash << 5) - hash;\n }\n return hash & 0xffffffff;\n}\n", "import * as globalState from '@/shared/state/global-weight-helper-state';\nimport { LoraBlockTypes, ModelTypes } from '@/shared/types/lora-types';\n\nconst _metadataCacheStore: Record<\n string,\n {\n metadataState: globalState.MetadataState | undefined;\n selectedLoraBlockType: LoraBlockTypes;\n selectedModelType: ModelTypes;\n usingBlocks: Set | undefined;\n }\n> = {};\n\nconst _previewCacheStore: Record = {};\n\n/**\n * Retrieves the metadata cache for the given LoRA name.\n * @param loraName - The name of the LoRA model.\n * @returns The cached metadata state, selected block type, model type, and used blocks, or undefined if not cached.\n */\nexport function getMetadataCache(loraName: string):\n | {\n metadataState: globalState.MetadataState | undefined;\n selectedLoraBlockType: LoraBlockTypes;\n selectedModelType: ModelTypes;\n usingBlocks: Set | undefined;\n }\n | undefined {\n return _metadataCacheStore[loraName];\n}\n\n/**\n * Sets the metadata cache for the given LoRA name.\n * @param loraName - The name of the LoRA model.\n * @param cache - The BasicState containing metadata and related info to cache.\n */\nexport function setMetadataCache(loraName: string, cache: globalState.BasicState) {\n _metadataCacheStore[loraName] = {\n metadataState: cache.metadataState,\n selectedLoraBlockType: cache.weightState.selectedLoraBlockType,\n selectedModelType: cache.weightState.selectedModelType,\n usingBlocks: cache.usingBlocks,\n };\n}\n\n/**\n * Retrieves the preview cache for the given LoRA name.\n * @param loraName - The name of the LoRA model.\n * @returns The cached PreviewState, or undefined if not cached.\n */\nexport function getPreviewCache(loraName: string): globalState.PreviewState | undefined {\n return _previewCacheStore[loraName];\n}\n\n/**\n * Sets the preview cache for the given LoRA name.\n * @param loraName - The name of the LoRA model.\n * @param cache - The PreviewState to cache.\n */\nexport function setPreviewCache(loraName: string, cache: globalState.PreviewState) {\n _previewCacheStore[loraName] = cache;\n}\n", "import * as configManager from '@/shared/manager/config-manager';\nimport { LoraBlockTypes, ModelTypes } from '@/shared/types/lora-types';\n\nlet _tabId: string;\nlet _editor: HTMLTextAreaElement;\nlet _selectionStart: number;\nlet _selectionEnd: number;\nlet _loraDefineString: string;\nlet _lbwPresetsMap: Record>> = {};\nlet _step: number = 0;\n\n/**\n * Gets the current tab ID.\n * @returns The tab ID string.\n */\nexport function getTabId(): string {\n return _tabId;\n}\n\n/**\n * Sets the current tab ID.\n * @param tabId - The tab ID string.\n */\nexport function setTabId(tabId: string): void {\n _tabId = tabId;\n}\n\n/**\n * Gets the current editor textarea element.\n * @returns The HTMLTextAreaElement for the editor.\n */\nexport function getEditor(): HTMLTextAreaElement {\n return _editor;\n}\n\n/**\n * Sets the current editor textarea element.\n * @param e - The HTMLTextAreaElement to set as the editor.\n */\nexport function setEditor(e: HTMLTextAreaElement): void {\n _editor = e;\n}\n\n/**\n * Gets the current selection start index in the editor.\n * @returns The selection start index.\n */\nexport function getSelectionStart(): number {\n return _selectionStart;\n}\n\n/**\n * Sets the selection start index in the editor.\n * @param selectionStart - The selection start index.\n */\nexport function setSelectionStart(selectionStart: number): void {\n _selectionStart = selectionStart;\n}\n\n/**\n * Gets the current selection end index in the editor.\n * @returns The selection end index.\n */\nexport function getSelectionEnd(): number {\n return _selectionEnd;\n}\n\n/**\n * Sets the selection end index in the editor.\n * @param selectionEnd - The selection end index.\n */\nexport function setSelectionEnd(selectionEnd: number): void {\n _selectionEnd = selectionEnd;\n}\n\n/**\n * Gets the current LoRA define string from the editor.\n * @returns The LoRA define string.\n */\nexport function getLoraDefineString(): string {\n return _loraDefineString;\n}\n\n/**\n * Sets the current LoRA define string in the editor.\n * @param loraDefineString - The LoRA define string to set.\n */\nexport function setLoraDefineString(loraDefineString: string): void {\n _loraDefineString = loraDefineString;\n}\n\n/**\n * Gets the current step value from the editor.\n * @returns The step value as a number.\n */\nexport function getStep(): number {\n return _step || 0;\n}\n\n/**\n * Loads the step value from the editor UI for the specified tab ID.\n * @param tabId - The tab ID string.\n */\nexport function loadStep(tabId: string) {\n const tab = gradioApp().querySelector(`div:is([id^='tab_${tabId}'][class*='tabitem'])`) as HTMLDivElement;\n const input = tab.querySelector('div:is([id$=\"_steps\"]) input[type=number]') as HTMLInputElement;\n _step = +input.value;\n}\n\n/**\n * Gets the LBW presets for the specified model and block type.\n * @param selectedModelType - The selected model type.\n * @param selectedLoraBlockType - The selected LoRA block type.\n * @returns A record of preset names to values.\n */\nexport function getLbwPresets(selectedModelType: ModelTypes, selectedLoraBlockType: LoraBlockTypes): Record {\n return _lbwPresetsMap[selectedModelType || ModelTypes.Unknown][selectedLoraBlockType || LoraBlockTypes.Unknown];\n}\n\n/**\n * Loads the LBW presets from the editor UI for the specified tab ID.\n * @param tabId - The tab ID string.\n */\nexport function loadLbwPresets(tabId: string) {\n const tab = gradioApp().querySelector(`div:is([id^='tab_${tabId}'][class*='tabitem'])`) as HTMLDivElement;\n const textarea = tab.querySelector('#lbw_ratiospreset textarea') as HTMLTextAreaElement;\n const lbwPresetValue = textarea.value ?? '';\n const lbwPresets = lbwPresetValue.split('\\n').filter((e) => e.trim() !== '');\n\n const lbwPresetsMap: Record>> = {};\n for (const modelType of Object.values(ModelTypes)) {\n lbwPresetsMap[modelType] = {};\n for (const loraBlockType of Object.values(LoraBlockTypes)) {\n const lbwPreset: Record = {};\n const lbwPresetValueKey: Record = {};\n\n lbwPresetsMap[modelType][loraBlockType] = lbwPreset;\n\n const blockLength = configManager.getLbwMasks(modelType, loraBlockType).filter((b) => b == 1).length;\n for (const line of lbwPresets) {\n const kv = line.split(':');\n if (kv.length == 2 && kv[1].split(',').length == blockLength) {\n lbwPreset[kv[0]] = kv[1];\n lbwPresetValueKey[kv[1]] = kv[0];\n }\n }\n }\n }\n _lbwPresetsMap = lbwPresetsMap;\n}\n", "import * as globalState from '@/shared/state/global-weight-helper-state';\nimport { WeightControllerTypes } from '@/shared/types/lora-types';\n\n/**\n * Converts a string to lowercase.\n * @param str - The string to convert.\n * @returns The lowercase version of the string.\n */\nexport const lower = (str: string) => str.toLowerCase();\n\n/**\n * Returns a CSS display string to control the display property.\n * @param visible - When true, returns empty string; when false, returns 'display: none'.\n * @returns A CSS display string.\n */\nexport const getDisplayStyle = (visible: boolean) => (visible ? '' : 'display: none');\n\n/**\n * Returns a CSS visibility string to control the visibility property.\n * @param visible - When true, returns empty string; when false, returns 'visibility: hidden'.\n * @returns A CSS visibility string.\n */\nexport const getVisibilityStyle = (visible: boolean) => (visible ? '' : 'visibility: hidden');\n\n/**\n * Determines if a weight controller should be disabled based on the current state and block label.\n * @param state - The current BasicState.\n * @param blockLabel - Optional label of the weight block.\n * @returns True if the controller should be disabled, false otherwise.\n */\nexport const disabled = (state: globalState.BasicState, blockLabel: string | undefined = undefined) => {\n if (state.uiState.isWaiting) {\n return true;\n }\n switch (blockLabel) {\n case WeightControllerTypes.TENC:\n case WeightControllerTypes.UNET:\n case WeightControllerTypes.START:\n case WeightControllerTypes.STOP:\n case undefined:\n return false;\n }\n return state.usingBlocks !== undefined && !state.usingBlocks.has(blockLabel);\n};\n\n/**\n * Expands a range pattern (e.g., 'IN01-IN04') into an array of block labels.\n * If the pattern doesn't contain a valid range format, returns the pattern as a single-element array.\n * @param patterns - The range pattern string.\n * @returns An array of expanded block labels.\n */\nexport function expandRange(patterns: string): string[] {\n const result: string[] = [];\n patterns\n .split(',')\n .map((b) => b.trim())\n .forEach((pattern) => {\n const match = pattern.match(/^([A-Z]+)(\\d+)-\\1(\\d+)$/);\n if (!match) {\n result.push(pattern);\n return;\n }\n\n const [, prefix, start, end] = match;\n const startNum = parseInt(start);\n const endNum = parseInt(end);\n\n let i = startNum;\n while (true) {\n result.push(`${prefix}${i.toString().padStart(2, '0')}`);\n if (i == endNum) {\n break;\n }\n i += startNum < endNum ? 1 : -1;\n }\n });\n return result;\n}\n", "import * as cacheManager from '@/shared/manager/cache-manager';\nimport * as configManager from '@/shared/manager/config-manager';\nimport * as historyManager from '@/shared/manager/history-manager';\nimport * as currentState from '@/shared/state/current-state';\nimport * as globalState from '@/shared/state/global-weight-helper-state';\nimport { ModelTypes, WeightControllerTypes } from '@/shared/types/lora-types';\nimport { withoutPromptAssist } from '@/shared/utils/common-utils';\nimport { lower } from '@/shared/utils/helper-utils';\n\nconst REGEX = /<(lora|lyco):([^:]+):([^>]+)>/;\n\n/**\n * Extracts the LoRA definition from the editor based on the mouse event position.\n * @param e - The mouse event from the context menu.\n * @returns Context information if a valid LoRA/lyco tag is found at the cursor position, or undefined if not found.\n */\nexport function getLoraDefineFromEditor(e: MouseEvent):\n | {\n tabId: string;\n textarea: HTMLTextAreaElement;\n selectionStart: number;\n selectionEnd: number;\n namespace: string;\n loraName: string;\n loraParams: string;\n top: number;\n left: number;\n }\n | undefined {\n const textarea = e.target as HTMLTextAreaElement;\n if (!opts.weight_helper_enabled) {\n return undefined;\n }\n let selectedText = window.getSelection()?.toString();\n if (selectedText) {\n return undefined;\n }\n const prompt = textarea.value;\n let tmpSelectionStart = textarea.selectionStart;\n const lCar = prompt.lastIndexOf('<', tmpSelectionStart - 1);\n const rCar = prompt.indexOf('>', tmpSelectionStart);\n if (lCar < 0 || rCar < 0) {\n return undefined;\n }\n selectedText = prompt.substring(lCar, rCar + 1) as string;\n if ((selectedText.match(//g) || []).length != 1) {\n return undefined;\n }\n tmpSelectionStart = lCar;\n const match = REGEX.exec(selectedText);\n if (match) {\n e.preventDefault();\n const namespace = match[1].toLowerCase();\n const loraName = match[2];\n const loraParams = match[3];\n\n const tabId = textarea.closest(\"[id^='tab_'][class*='tabitem']\")!.id.split('_')[1];\n const selectionStart = tmpSelectionStart + match.index;\n const selectionEnd = selectionStart + match.input.trim().length;\n\n return {\n tabId: tabId,\n textarea: textarea,\n selectionStart: selectionStart,\n selectionEnd: selectionEnd,\n namespace: namespace,\n loraName: loraName,\n loraParams: loraParams,\n top: (e.pageY + 15) as number,\n left: e.pageX as number,\n };\n }\n return undefined;\n}\n\n/**\n * Updates the editor textarea with the given text at the current selection.\n * @param updatedText - The text to insert into the editor.\n */\nexport function updateEditor(updatedText: string): void {\n const textarea = currentState.getEditor();\n const start = currentState.getSelectionStart();\n const end = currentState.getSelectionEnd();\n textarea.value = textarea.value.substring(0, start) + updatedText + textarea.value.substring(end);\n currentState.setSelectionEnd(start + updatedText.length);\n}\n\n/**\n * Updates the editor textarea with the given text using execCommand.\n * Uses this method to ensure compatibility with certain browser features.\n * @param updatedText - The text to insert into the editor.\n */\nexport function updateEditorWithExecCommand(updatedText: string): void {\n withoutPromptAssist(() => {\n const textarea = currentState.getEditor();\n const start = currentState.getSelectionStart();\n const end = currentState.getSelectionEnd();\n textarea.focus();\n textarea.setSelectionRange(start, end);\n document.execCommand('insertText', false, updatedText);\n });\n}\n\n/**\n * Generates output strings for the current LoRA configuration.\n * @param loraName - The name of the LoRA model.\n * @param weightState - The current weight state.\n * @returns An object containing the loraParams string and the complete LoRA tag.\n */\nexport function getOutputStrings(\n loraName: string,\n weightState: globalState.WeightState,\n): {\n loraParams: string;\n loraDefine: string;\n} {\n let updatedText = String(weightState.weights[WeightControllerTypes.TENC].value);\n let refIdx = 0;\n let idx = 0;\n for (const keyType of [WeightControllerTypes.UNET]) {\n const weight = weightState.weights[keyType];\n const defVal = configManager.getWeightControllerConfig(keyType as WeightControllerTypes).default;\n const val = +weight.value;\n if (weight.checkState || (weight.checkState === undefined && val != defVal)) {\n if (idx === refIdx) {\n updatedText += `:${val}`;\n } else {\n updatedText += `:${lower(keyType)}=${val}`;\n }\n refIdx++;\n }\n idx++;\n }\n const startDefVal = configManager.getWeightControllerConfig(WeightControllerTypes.START).default;\n const startVal = weightState.weights[WeightControllerTypes.START].value;\n const stopDefVal = configManager.getWeightControllerConfig(WeightControllerTypes.STOP).default;\n let stopVal = weightState.weights[WeightControllerTypes.STOP].value;\n if (stopVal < 0) {\n stopVal = configManager.getWeightControllerConfig(WeightControllerTypes.STOP).default;\n }\n if (startVal != startDefVal && stopVal != stopDefVal) {\n updatedText += `:step=${startVal}-${stopVal}`;\n } else if (startVal != startDefVal) {\n updatedText += `:start=${startVal}`;\n } else if (stopVal != stopDefVal) {\n updatedText += `:stop=${stopVal}`;\n }\n\n const { selectedModelType, selectedLoraBlockType } = weightState;\n const xyzValue = weightState.xyzMode;\n\n let updatedTextWithPreset = updatedText;\n if (xyzValue) {\n updatedText += `:lbw=XYZ`;\n updatedTextWithPreset += `:lbw=XYZ`;\n } else if (selectedModelType !== ModelTypes.Unknown) {\n const lbwWeights = configManager\n .getLbwMasks(selectedModelType, selectedLoraBlockType)\n .map((mask, i) => {\n if (mask === 1) {\n return weightState.weights[configManager.getLbwBlocks(selectedModelType)[i]];\n }\n return undefined;\n })\n .filter((block) => block !== undefined);\n const lbwDefault = configManager.getWeightControllerConfig(WeightControllerTypes.LBW).default;\n if (!lbwWeights.every((weight) => weight.value === lbwDefault)) {\n const lbwValues = lbwWeights.map((weight) => weight.value).join(',');\n\n updatedText += `:lbw=${lbwValues}`;\n const lbwPresets = currentState.getLbwPresets(selectedModelType, selectedLoraBlockType);\n const preset = Object.entries(lbwPresets).find(([, value]) => lbwValues === value);\n if (preset) {\n updatedTextWithPreset += `:lbw=${preset[0]}`;\n } else {\n updatedTextWithPreset += `:lbw=${lbwValues}`;\n }\n }\n }\n if (weightState.lbwe) {\n updatedText += `:lbwe=${weightState.lbwe}`;\n updatedTextWithPreset += `:lbwe=${weightState.lbwe}`;\n }\n return {\n loraParams: updatedText,\n loraDefine: ``,\n };\n}\n\n/**\n * Synchronizes the editor with the current Weight Helper state.\n * Updates the cache and history, and optionally cancels changes.\n * @param state - The current Weight Helper state.\n * @param cancel - When true, cancels changes and restores the original editor content.\n */\nexport function syncEditorWithState(state: globalState.BasicState, cancel: boolean = false): void {\n const outputStrings = getOutputStrings(state.loraName, state.weightState);\n if (!opts.weight_helper_using_execCommand) {\n if (cancel) {\n updateEditor(currentState.getLoraDefineString());\n }\n } else {\n if (state.srcLoraParams !== outputStrings.loraParams) {\n updateEditorWithExecCommand(outputStrings.loraDefine);\n }\n }\n cacheManager.setMetadataCache(state.loraName, state);\n if (!state.weightState.xyzMode) {\n historyManager.addHistory(state.loraName, { loraParams: outputStrings.loraParams, ...state.weightState });\n }\n historyManager.storeLocalStorage();\n}\n", "import * as historyManager from '@/shared/manager/history-manager';\nimport { LoraBlockTypes, ModelTypes } from '@/shared/types/lora-types';\nimport { getPreset } from '@/shared/utils/common-utils';\nimport { getOutputStrings, updateEditor } from '@/shared/utils/editor-utils';\n\n// Type definitions migrated from former context-based implementation\nexport interface BasicState {\n loraName: string;\n metadataState: MetadataState | undefined;\n srcLoraParams: string;\n weightState: WeightState;\n blockGroups: string[][];\n preset: string;\n lbwPresets: Record;\n usingBlocks: Set | undefined;\n uiState: UiState;\n lock: boolean;\n historyIndex: number;\n previewState: PreviewState;\n}\n\nexport interface MetadataState {\n baseModel: string;\n modelType: string;\n algorithm: string;\n}\n\nexport interface WeightState {\n selectedModelType: ModelTypes;\n selectedLoraBlockType: LoraBlockTypes;\n weights: Record;\n lbwe: string;\n xyzMode: boolean;\n}\n\nexport interface WeightControlState {\n initValue: number;\n value: number;\n sliderMin: number;\n sliderMax: number;\n initCheckState?: boolean | undefined;\n checkState?: boolean | undefined;\n}\n\nexport interface UiState {\n isVisible: boolean;\n scale: number;\n isMoreButtonVisible: boolean;\n isWaiting: boolean;\n}\n\nexport interface PreviewState {\n modelId: string;\n triggerWords: string[];\n negativeTriggerWords: string[];\n modelName: string;\n thumbUrl: string;\n hasMetadata: boolean;\n description: string;\n isDescriptionVisible: boolean;\n isTagInsertButtonVisible: boolean;\n}\n\n// Global mutable state (single source of truth outside of position which is applied directly to DOM)\nlet globalState: BasicState;\n\n// Callbacks for full state changes (require template re-render)\nconst stateChangeCallbacks: Array<(state: BasicState) => void> = [];\n// Callbacks for position-only changes (avoid full re-render; DOM style mutation only)\nconst positionChangeCallbacks: Array<(pos: { top: number; left: number }) => void> = [];\n\n// Notify subscribers about a full state change\nfunction notifyStateChange(): void {\n stateChangeCallbacks.forEach((callback) => callback(globalState));\n}\n\n// Notify subscribers about position-only change\nfunction notifyPositionChange(pos: { top: number; left: number }): void {\n positionChangeCallbacks.forEach((callback) => callback(pos));\n}\n\n/**\n * Subscribe to full state changes.\n * @param callback invoked whenever global state mutates.\n * @returns unsubscribe function\n */\nexport function subscribeToStateChanges(callback: (state: BasicState) => void): () => void {\n stateChangeCallbacks.push(callback);\n return () => {\n const index = stateChangeCallbacks.indexOf(callback);\n if (index > -1) {\n stateChangeCallbacks.splice(index, 1);\n }\n };\n}\n\n/**\n * Subscribe to position-only changes (top/left) used for dragging the UI.\n * @param callback invoked with new {top,left}\n * @returns unsubscribe function\n */\nexport function subscribeToPositionChanges(callback: (pos: { top: number; left: number }) => void): () => void {\n positionChangeCallbacks.push(callback);\n return () => {\n const index = positionChangeCallbacks.indexOf(callback);\n if (index > -1) {\n positionChangeCallbacks.splice(index, 1);\n }\n };\n}\n\n/** Get current global state snapshot (do not mutate directly). */\nexport function getGlobalState(): BasicState {\n return globalState;\n}\n\n/** Initialize state (triggers initial render). */\nexport function initializeGlobalState(initialState: BasicState): void {\n globalState = initialState;\n notifyStateChange();\n}\n\n// OPEN_CONTEXT\nexport function openContext(payload: BasicState): void {\n globalState = payload;\n notifyStateChange();\n}\n\n// CLOSE_CONTEXT\nexport function closeContext(payload: BasicState): void {\n globalState = payload;\n notifyStateChange();\n}\n\n// SET_POSITION (excluded from main state to avoid re-render on drag)\nexport function setPosition(top: number, left: number): void {\n // Don't persist in state; this is transient UI positioning.\n notifyPositionChange({ top, left });\n}\n\n// LOAD_METADATA\nexport function loadMetadata(payload: {\n algorithm: string;\n baseModel: string;\n modelType: string;\n selectedModelType: ModelTypes;\n usingBlocks: string[] | null | undefined;\n blockGroups: string[][];\n weights: Record;\n lbwPresets: Record;\n}): void {\n const newState = {\n ...globalState,\n metadataState: {\n ...globalState.metadataState,\n algorithm: payload.algorithm || 'Unknown',\n baseModel: payload.baseModel || 'Unknown',\n modelType: payload.modelType || 'Unknown',\n },\n weightState: {\n ...globalState.weightState,\n selectedModelType: payload.selectedModelType,\n weights: payload.weights,\n },\n blockGroups: payload.blockGroups,\n lbwPresets: payload.lbwPresets,\n uiState: {\n ...globalState.uiState,\n isWaiting: false,\n },\n };\n if (payload.usingBlocks === null) {\n newState.usingBlocks = undefined;\n } else if (payload.usingBlocks) {\n newState.usingBlocks = new Set(payload.usingBlocks);\n }\n globalState = newState;\n notifyStateChange();\n}\n\n// SET_WAITING\nexport function setWaiting(isWaiting: boolean): void {\n globalState = {\n ...globalState,\n uiState: {\n ...globalState.uiState,\n isWaiting: isWaiting,\n },\n };\n notifyStateChange();\n}\n\n// SET_MODELINFO\nexport function setModelInfo(payload: {\n selectedModelType: ModelTypes;\n selectedLoraBlockType: LoraBlockTypes;\n blockGroups: string[][];\n lbwPresets: Record;\n weights: Record;\n}): void {\n const newState = {\n ...globalState,\n weightState: {\n ...globalState.weightState,\n selectedLoraBlockType: payload.selectedLoraBlockType,\n selectedModelType: payload.selectedModelType,\n weights: payload.weights,\n },\n blockGroups: payload.blockGroups,\n lbwPresets: payload.lbwPresets,\n };\n if (!opts.weight_helper_using_execCommand) {\n updateEditor(getOutputStrings(newState.loraName, newState.weightState).loraDefine);\n }\n globalState = newState;\n notifyStateChange();\n}\n\n// TOGGLE_XYZ_MODE\nexport function toggleXyzMode(): void {\n const newState = {\n ...globalState,\n weightState: {\n ...globalState.weightState,\n xyzMode: !globalState.weightState.xyzMode,\n },\n };\n if (!opts.weight_helper_using_execCommand) {\n updateEditor(getOutputStrings(newState.loraName, newState.weightState).loraDefine);\n }\n globalState = newState;\n notifyStateChange();\n}\n\n// SET_PRESET\nexport function setPreset(payload: { weights: Record; preset: string }): void {\n const weightState = {\n ...globalState.weightState,\n weights: {\n ...globalState.weightState.weights,\n ...payload.weights,\n },\n };\n const outputStrings = getOutputStrings(globalState.loraName, weightState);\n const locked = historyManager.isLocked(globalState.loraName, outputStrings.loraParams);\n\n const newState = {\n ...globalState,\n weightState: weightState,\n preset: payload.preset,\n lock: locked,\n };\n if (!opts.weight_helper_using_execCommand) {\n updateEditor(outputStrings.loraDefine);\n }\n globalState = newState;\n notifyStateChange();\n}\n\n// TOGGLE_LOCK_STATUS\nexport function toggleLockStatus(): void {\n const outputLoraParams = getOutputStrings(globalState.loraName, globalState.weightState).loraParams;\n const locked = historyManager.isLocked(globalState.loraName, outputLoraParams);\n if (locked) {\n historyManager.removeLock(globalState.loraName, outputLoraParams);\n } else {\n historyManager.addLock(globalState.loraName, outputLoraParams);\n }\n globalState = {\n ...globalState,\n lock: !locked,\n };\n notifyStateChange();\n}\n\n// SET_HISTORY\nexport function setHistory(payload: {\n historyIndex: number;\n weightState: {\n selectedModelType: ModelTypes;\n selectedLoraBlockType: LoraBlockTypes;\n weights: Record;\n lbwe: string;\n xyzMode: boolean;\n };\n preset: string;\n locked: boolean;\n}): void {\n const { locked, historyIndex, preset } = payload;\n const { selectedLoraBlockType, selectedModelType, weights, lbwe, xyzMode } = payload.weightState;\n const weightState = {\n ...globalState.weightState,\n weights: {\n ...globalState.weightState.weights,\n ...weights,\n },\n selectedLoraBlockType: selectedLoraBlockType,\n selectedModelType: selectedModelType,\n lbwe: lbwe,\n xyzMode: xyzMode,\n };\n const newState = {\n ...globalState,\n historyIndex: historyIndex,\n preset: preset,\n weightState: weightState,\n lock: locked,\n };\n if (!opts.weight_helper_using_execCommand) {\n updateEditor(getOutputStrings(newState.loraName, newState.weightState).loraDefine);\n }\n globalState = newState;\n notifyStateChange();\n}\n\n// SET_HISTORY_INDEX\nexport function setHistoryIndex(historyIndex: number): void {\n globalState = {\n ...globalState,\n historyIndex: historyIndex,\n };\n notifyStateChange();\n}\n\n// SET_WEIGHT\nexport function setWeight(payload: { block: string; value: number; checkState?: boolean }): void {\n globalState.weightState.xyzMode = false;\n const weights = {\n ...globalState.weightState.weights,\n [payload.block]: {\n ...globalState.weightState.weights[payload.block],\n value: payload.value,\n sliderMin: Math.min(payload.value, globalState.weightState.weights[payload.block].sliderMin),\n sliderMax: Math.max(payload.value, globalState.weightState.weights[payload.block].sliderMax),\n checkState: payload.checkState,\n },\n };\n const weightState = {\n ...globalState.weightState,\n weights,\n xyzMode: false,\n };\n\n const outputStrings = getOutputStrings(globalState.loraName, weightState);\n const locked = historyManager.isLocked(globalState.loraName, outputStrings.loraParams);\n\n const newState = {\n ...globalState,\n weightState: weightState,\n preset: getPreset(globalState.weightState.selectedModelType, globalState.weightState.selectedLoraBlockType, globalState.lbwPresets, weights),\n lock: locked,\n };\n if (!opts.weight_helper_using_execCommand) {\n updateEditor(outputStrings.loraDefine);\n }\n globalState = newState;\n notifyStateChange();\n}\n\n// HIDE_MORE_BUTTON\nexport function hideMoreButton(): void {\n globalState = {\n ...globalState,\n uiState: {\n ...globalState.uiState,\n isMoreButtonVisible: false,\n },\n };\n notifyStateChange();\n}\n\n// CLEAR_HISTORY\nexport function clearHistory(): void {\n historyManager.clearHistories(globalState.loraName);\n if (!globalState.weightState.xyzMode) {\n historyManager.addHistory(globalState.loraName, {\n loraParams: getOutputStrings(globalState.loraName, globalState.weightState).loraParams,\n ...globalState.weightState,\n });\n }\n const historyIndex = historyManager.getHistories(globalState.loraName).length - 1;\n globalState = {\n ...globalState,\n historyIndex: historyIndex < 0 ? 0 : historyIndex,\n };\n notifyStateChange();\n}\n\n// LOAD_PREVIEW_DATA\nexport function loadPreviewData(payload: PreviewState): void {\n globalState = {\n ...globalState,\n previewState: payload,\n };\n notifyStateChange();\n}\n\n// SET_PREVIEW_DESCRIPTION_VISIBLE\nexport function setPreviewDescriptionVisible(isVisible: boolean): void {\n globalState = {\n ...globalState,\n previewState: {\n ...globalState.previewState,\n isDescriptionVisible: isVisible,\n },\n };\n notifyStateChange();\n}\n\n// HIDE_TAG_INSERT_BUTTON\nexport function hideTagInsertButton(): void {\n globalState = {\n ...globalState,\n previewState: {\n ...globalState.previewState,\n isTagInsertButtonVisible: false,\n },\n };\n notifyStateChange();\n}\n", "import { CARD_NO_IMAGE_PATH, SELECTABLE_LORA_BLOCK_TYPES, SELECTABLE_MODEL_TYPES } from '@/shared/constants/common-const';\nimport * as cacheManager from '@/shared/manager/cache-manager';\nimport * as configManager from '@/shared/manager/config-manager';\nimport * as historyManager from '@/shared/manager/history-manager';\nimport * as currentState from '@/shared/state/current-state';\nimport * as globalState from '@/shared/state/global-weight-helper-state';\nimport * as loraTypes from '@/shared/types/lora-types';\nimport { LoraDefineParams, WeightControllerTypes } from '@/shared/types/lora-types';\nimport { getPreset } from '@/shared/utils/common-utils';\nimport { getOutputStrings } from '@/shared/utils/editor-utils';\n\n/**\n * Creates and returns the initial state for the Weight Helper UI.\n * @returns The initial BasicState object for the Weight Helper.\n */\nexport function createWeightHelperInitState(): globalState.BasicState {\n const weightHelperProps: globalState.BasicState = {\n loraName: '',\n metadataState: undefined,\n srcLoraParams: '',\n weightState: {\n selectedLoraBlockType: loraTypes.LoraBlockTypes.lora,\n selectedModelType: loraTypes.ModelTypes.SD,\n weights: {},\n lbwe: '',\n xyzMode: false,\n },\n blockGroups: [],\n usingBlocks: undefined,\n preset: '',\n lbwPresets: {},\n uiState: {\n isVisible: false,\n scale: 1,\n isMoreButtonVisible: true,\n isWaiting: false,\n },\n lock: false,\n historyIndex: 0,\n previewState: {\n modelId: '',\n triggerWords: [],\n negativeTriggerWords: [],\n modelName: '',\n thumbUrl: '',\n hasMetadata: false,\n description: '',\n isDescriptionVisible: false,\n isTagInsertButtonVisible: false,\n },\n };\n return weightHelperProps;\n}\n\n/**\n * Creates and returns the Weight Helper state based on the given parameters.\n * @param namespace - The namespace (e.g., 'lora' or 'lycoris').\n * @param loraName - The name of the LoRA model.\n * @param loraParams - The loraParams string.\n * @param top - The top position for the UI.\n * @param left - The left position for the UI.\n * @returns The constructed BasicState object.\n */\nexport function createWeightHelperState(namespace: string, loraName: string, loraParams: string): globalState.BasicState {\n let selectedModelType: loraTypes.ModelTypes = loraTypes.ModelTypes.Unknown;\n let selectedLoraBlockType: loraTypes.LoraBlockTypes = loraTypes.LoraBlockTypes.Unknown;\n const histories = historyManager.getHistories(loraName);\n if (histories && histories.length > 0) {\n const latest = histories[histories.length - 1];\n selectedModelType = latest.selectedModelType ?? loraTypes.ModelTypes.Unknown;\n selectedLoraBlockType = latest.selectedLoraBlockType ?? loraTypes.LoraBlockTypes.Unknown;\n }\n\n let metadataState: globalState.MetadataState | undefined;\n let usingBlocks: Set | undefined;\n const metadataCache = cacheManager.getMetadataCache(loraName);\n if (metadataCache) {\n metadataState = metadataCache.metadataState;\n selectedModelType = metadataCache.selectedModelType ?? loraTypes.ModelTypes.Unknown;\n selectedLoraBlockType = metadataCache.selectedLoraBlockType ?? loraTypes.LoraBlockTypes.Unknown;\n usingBlocks = metadataCache.usingBlocks;\n }\n\n const weightState = createWeightState(namespace, loraParams, selectedModelType, selectedLoraBlockType);\n\n const lbwPresets = currentState.getLbwPresets(weightState.selectedModelType, weightState.selectedLoraBlockType);\n\n const outputLoraParams = getOutputStrings(loraName, weightState).loraParams;\n\n // Determine if the \"show more options\" button should appear (hidden when any advanced param differs)\n let isMoreButtonVisible = false;\n for (const keyType of [WeightControllerTypes.UNET, WeightControllerTypes.START, WeightControllerTypes.STOP]) {\n const weight = weightState.weights[keyType];\n const defVal = configManager.getWeightControllerConfig(keyType as WeightControllerTypes).default;\n const val = +weight.value;\n if (weight.checkState || (weight.checkState === undefined && val != defVal)) {\n } else {\n isMoreButtonVisible = true;\n break;\n }\n }\n return {\n loraName: loraName,\n metadataState: metadataState,\n srcLoraParams: outputLoraParams,\n weightState: weightState,\n blockGroups: configManager.getLbwBlockGroups(weightState.selectedModelType, weightState.selectedLoraBlockType),\n usingBlocks: usingBlocks,\n uiState: {\n isVisible: true,\n scale: opts.weight_helper_context_menu_scale,\n isMoreButtonVisible: isMoreButtonVisible,\n isWaiting: !metadataState,\n },\n previewState: createPreviewState(loraName),\n preset: getPreset(weightState.selectedModelType, weightState.selectedLoraBlockType, lbwPresets, weightState.weights),\n lbwPresets: lbwPresets,\n lock: historyManager.isLocked(loraName, outputLoraParams),\n historyIndex: histories.length - 1,\n };\n}\n\n/**\n * Creates and returns the WeightState object from the given parameters.\n * @param namespace - The namespace (e.g., 'lora' or 'lycoris').\n * @param loraParams - The loraParams string.\n * @param modelType - The model type.\n * @param loraBlockType - The LoRA block type.\n * @returns The constructed WeightState object.\n */\nexport function createWeightState(\n namespace: string,\n loraParams: string,\n modelType: loraTypes.ModelTypes,\n loraBlockType: loraTypes.LoraBlockTypes,\n): {\n selectedLoraBlockType: loraTypes.LoraBlockTypes;\n selectedModelType: loraTypes.ModelTypes;\n weights: Record;\n lbwe: string;\n xyzMode: boolean;\n} {\n const loraParamsProps = createLoraParamsState(loraParams, modelType, loraBlockType);\n const { weights, lbwe, xyzMode } = loraParamsProps;\n\n if (loraBlockType === loraTypes.LoraBlockTypes.Unknown) {\n loraBlockType = namespace === 'lora' ? loraTypes.LoraBlockTypes.lora : loraTypes.LoraBlockTypes.lycoris;\n }\n\n return {\n selectedLoraBlockType: loraBlockType,\n selectedModelType: modelType,\n weights: weights,\n lbwe: lbwe,\n xyzMode: xyzMode,\n };\n}\n\n/**\n * Parses the loraParams string and creates the corresponding state for loraParamss, weights, and related flags.\n * @param loraParams - The loraParams string.\n * @param modelType - The model type.\n * @param loraBlockType - The LoRA block type.\n * @returns The parsed state including weights, lbwe, and xyzMode.\n */\nexport function createLoraParamsState(\n loraParams: string,\n modelType: loraTypes.ModelTypes,\n loraBlockType: loraTypes.LoraBlockTypes,\n): {\n selectedModelType: loraTypes.ModelTypes;\n selectedLoraBlockType: loraTypes.LoraBlockTypes;\n weights: Record;\n lbwe: string;\n xyzMode: boolean;\n} {\n const loraParamss = loraParams.split(':');\n\n const loraParamsMap: Record = {};\n for (let i = 0; i < loraParamss.length; i++) {\n let key: string;\n let value: string;\n if (loraParamss[i].indexOf('=') >= 0) {\n const keyValue = loraParamss[i].split('=');\n key = keyValue[0].toLowerCase();\n value = keyValue[1];\n } else {\n key = [LoraDefineParams.te, LoraDefineParams.unet][i];\n value = loraParamss[i];\n }\n loraParamsMap[key] = value;\n }\n\n const loraSdCombination: {\n modelType: loraTypes.ModelTypes;\n loraBlockType: loraTypes.LoraBlockTypes;\n presets: Record;\n masks: number[];\n }[] = [];\n for (const modelType of Object.values(SELECTABLE_MODEL_TYPES)) {\n for (const loraBlockType of Object.values(SELECTABLE_LORA_BLOCK_TYPES)) {\n loraSdCombination.push({\n modelType: modelType,\n loraBlockType: loraBlockType,\n presets: currentState.getLbwPresets(modelType, loraBlockType),\n masks: configManager.getLbwMasks(modelType, loraBlockType),\n });\n }\n }\n\n let te = configManager.getWeightControllerConfig(WeightControllerTypes.TENC).default;\n let unet: number | undefined = undefined;\n\n let vStart = configManager.getWeightControllerConfig(WeightControllerTypes.START).default;\n let vStop = configManager.getWeightControllerConfig(WeightControllerTypes.STOP).default;\n\n const lbwVals: number[] = [];\n let lbwe: string = '';\n let xyzModeFlag = false;\n\n Object.entries(loraParamsMap).forEach(([loraParamKey, value]) => {\n switch (loraParamKey) {\n case LoraDefineParams.lbw:\n if (value === 'XYZ') {\n xyzModeFlag = true;\n break;\n }\n let blocks = value.split(',');\n for (const loraSd of loraSdCombination) {\n if (blocks.length === 1) {\n if (value in loraSd.presets) {\n blocks = loraSd.presets[value].split(',');\n } else {\n continue;\n }\n }\n if (blocks.length === loraSd.masks.filter((b) => b == 1).length) {\n modelType = loraSd.modelType;\n loraBlockType = loraSd.loraBlockType;\n let refIdx = 0;\n for (const enable of loraSd.masks) {\n if (enable) {\n lbwVals.push(+blocks[refIdx]);\n refIdx++;\n } else {\n lbwVals.push(0);\n }\n }\n break;\n }\n }\n break;\n case LoraDefineParams.step:\n const startStop = value.split('-');\n vStart = Math.round(+startStop[0]);\n vStop = Math.round(+startStop[1]);\n break;\n case LoraDefineParams.lbwe:\n lbwe = value;\n break;\n case LoraDefineParams.te:\n te = +value;\n break;\n case LoraDefineParams.unet:\n unet = +value;\n break;\n case LoraDefineParams.start:\n vStart = Math.round(+value);\n break;\n case LoraDefineParams.stop:\n vStop = Math.round(+value);\n break;\n }\n });\n\n // Flag indicating whether UNET weight was explicitly specified\n let useUnet = true;\n if (unet === undefined) {\n unet = configManager.getWeightControllerConfig(WeightControllerTypes.UNET).default;\n useUnet = false;\n }\n\n const lbws: Record = {};\n if (modelType !== loraTypes.ModelTypes.Unknown) {\n const blocks = configManager.getLbwBlocks(modelType);\n if (lbwVals.length === 0) {\n for (let i = 0; i < blocks.length; i++) {\n lbwVals.push(configManager.getWeightControllerConfig(WeightControllerTypes.LBW).default);\n }\n }\n lbwVals.forEach((lbw, i) => {\n lbws[blocks[i]] = {\n initValue: lbw,\n value: lbw,\n sliderMin: Math.min(lbw, configManager.getWeightControllerConfig(WeightControllerTypes.LBW).min),\n sliderMax: Math.max(lbw, configManager.getWeightControllerConfig(WeightControllerTypes.LBW).max),\n };\n });\n }\n\n return {\n selectedLoraBlockType: loraBlockType,\n selectedModelType: modelType,\n weights: {\n [WeightControllerTypes.TENC]: {\n initValue: te,\n value: te,\n sliderMin: Math.min(te, configManager.getWeightControllerConfig(WeightControllerTypes.TENC).min),\n sliderMax: Math.max(te, configManager.getWeightControllerConfig(WeightControllerTypes.TENC).max),\n },\n [WeightControllerTypes.UNET]: {\n initValue: unet,\n value: unet,\n sliderMin: Math.min(unet, configManager.getWeightControllerConfig(WeightControllerTypes.UNET).min),\n sliderMax: Math.max(unet, configManager.getWeightControllerConfig(WeightControllerTypes.UNET).max),\n initCheckState: useUnet,\n checkState: useUnet,\n },\n [WeightControllerTypes.START]: {\n initValue: vStart,\n value: vStart,\n sliderMin: Math.min(vStart, configManager.getWeightControllerConfig(WeightControllerTypes.START).min),\n sliderMax: Math.max(vStart, configManager.getWeightControllerConfig(WeightControllerTypes.START).max),\n },\n [WeightControllerTypes.STOP]: {\n initValue: vStop,\n value: vStop,\n sliderMin: Math.min(vStop, configManager.getWeightControllerConfig(WeightControllerTypes.STOP).min),\n sliderMax: Math.max(vStop, configManager.getWeightControllerConfig(WeightControllerTypes.STOP).max),\n },\n ...lbws,\n },\n lbwe: lbwe,\n xyzMode: xyzModeFlag,\n };\n}\n\n/**\n * Creates and returns the PreviewState for the given loraName, using cache if available.\n * @param loraName - The name of the LoRA model.\n * @returns The PreviewState object for the given LoRA model.\n */\nfunction createPreviewState(loraName: string): globalState.PreviewState {\n const previewCache = cacheManager.getPreviewCache(loraName);\n if (previewCache) {\n return previewCache;\n }\n return {\n modelId: '',\n triggerWords: [''],\n negativeTriggerWords: [''],\n modelName: '',\n thumbUrl: CARD_NO_IMAGE_PATH,\n hasMetadata: false,\n description: '',\n isDescriptionVisible: false,\n isTagInsertButtonVisible: false,\n };\n}\n", "let metadataAbortController: AbortController | null = null;\nlet previewAbortController: AbortController | null = null;\n\n/**\n * Returns the global AbortController for metadata fetch requests.\n * Creates a new controller if one doesn't exist.\n * @returns The AbortController instance for metadata requests.\n */\nexport function getMetadataAbortController(): AbortController {\n if (!metadataAbortController) {\n metadataAbortController = new AbortController();\n }\n return metadataAbortController;\n}\n\n/**\n * Aborts any ongoing metadata fetch request and creates a new AbortController.\n * @returns The new AbortController instance for metadata requests.\n */\nexport function resetMetadataAbortController(): AbortController {\n if (metadataAbortController) {\n metadataAbortController.abort();\n }\n metadataAbortController = new AbortController();\n return metadataAbortController;\n}\n\n/**\n * Returns the global AbortController for preview fetch requests.\n * Creates a new controller if one doesn't exist.\n * @returns The AbortController instance for preview requests.\n */\nexport function getPreviewAbortController(): AbortController {\n if (!previewAbortController) {\n previewAbortController = new AbortController();\n }\n return previewAbortController;\n}\n\n/**\n * Aborts any ongoing preview fetch request and creates a new AbortController.\n * @returns The new AbortController instance for preview requests.\n */\nexport function resetPreviewAbortController(): AbortController {\n if (previewAbortController) {\n previewAbortController.abort();\n }\n previewAbortController = new AbortController();\n return previewAbortController;\n}\n", "import * as globalState from '@/shared/state/global-weight-helper-state';\nimport { ModelTypes } from '@/shared/types/lora-types';\n\n/**\n * Fetches metadata for the specified LoRA model from the backend API.\n * @param loraName - The name of the LoRA model.\n * @param force - Whether to force a refresh of the metadata regardless of cache.\n * @param signal - Optional AbortSignal for canceling the request.\n * @returns A promise resolving to the metadata object.\n */\nexport async function fetchMetadata(\n loraName: string,\n force: boolean,\n signal?: AbortSignal,\n): Promise<{ algorithm: string; baseModel: string; modelType: ModelTypes; usingBlocks: string[] | null }> {\n const res = await fetch(`/whapi/v1/get_metadata?key=${encodeURIComponent(loraName)}&force=${force}`, {\n method: 'POST',\n signal: signal,\n });\n if (!res.ok) {\n throw new Error(`Failed to set up API endpoints. Please reload the page.: ${res.status}`);\n }\n return await res.json();\n}\n\n/**\n * Fetches preview information for the specified LoRA model from the backend API.\n * @param loraName - The name of the LoRA model.\n * @param signal - Optional AbortSignal for canceling the request.\n * @returns A promise resolving to the PreviewState object.\n */\nexport async function fetchPreviewData(loraName: string, signal?: AbortSignal): Promise {\n const res = await fetch(`/whapi/v1/get_preview_info?key=${encodeURIComponent(loraName)}`, {\n method: 'POST',\n signal: signal,\n });\n if (!res.ok) {\n throw new Error(`Failed to set up API endpoints. Please reload the page.: ${res.status}`);\n }\n const json = await res.json();\n return {\n ...json,\n isDescriptionVisible: false,\n isTagInsertButtonVisible: true,\n };\n}\n", "import * as configManager from '@/shared/manager/config-manager';\nimport * as globalState from '@/shared/state/global-weight-helper-state';\nimport { WeightControllerTypes } from '@/shared/types/lora-types';\nimport { expandRange } from '@/shared/utils/helper-utils';\n\n/**\n * Merges the current weights with the given block groups.\n * Ensures that all blocks from the expanded groups are present in the weights object.\n * If a block is missing, it is initialized with the default LBW value.\n * @param currentWeights - The current record of block weights.\n * @param groups - The array of block group strings (may include range patterns).\n * @returns The updated record of block weights including all blocks from the groups.\n */\nexport function mergeWeightsWithGroups(\n currentWeights: Record,\n groups: string[][],\n): Record {\n const updatedWeights = { ...currentWeights };\n\n const allBlocks: string[] = [];\n groups.forEach((cols) => {\n cols.forEach((group) => {\n const expandedBlocks = expandRange(group);\n expandedBlocks.forEach((block) => {\n if (!allBlocks.includes(block)) {\n allBlocks.push(block);\n }\n });\n });\n });\n\n allBlocks.forEach((block) => {\n if (!updatedWeights[block]) {\n updatedWeights[block] = {\n initValue: configManager.getWeightControllerConfig(WeightControllerTypes.LBW).default,\n value: configManager.getWeightControllerConfig(WeightControllerTypes.LBW).default,\n sliderMin: configManager.getWeightControllerConfig(WeightControllerTypes.LBW).min,\n sliderMax: configManager.getWeightControllerConfig(WeightControllerTypes.LBW).max,\n };\n }\n });\n\n return updatedWeights;\n}\n", "/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n// IMPORTANT: these imports must be type-only\nimport type {Directive, DirectiveResult, PartInfo} from './directive.js';\nimport type {TrustedHTML, TrustedTypesWindow} from 'trusted-types/lib/index.js';\n\nconst DEV_MODE = true;\nconst ENABLE_EXTRA_SECURITY_HOOKS = true;\nconst ENABLE_SHADYDOM_NOPATCH = true;\nconst NODE_MODE = false;\n\n// Allows minifiers to rename references to globalThis\nconst global = globalThis;\n\n/**\n * Contains types that are part of the unstable debug API.\n *\n * Everything in this API is not stable and may change or be removed in the future,\n * even on patch releases.\n */\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport namespace LitUnstable {\n /**\n * When Lit is running in dev mode and `window.emitLitDebugLogEvents` is true,\n * we will emit 'lit-debug' events to window, with live details about the update and render\n * lifecycle. These can be useful for writing debug tooling and visualizations.\n *\n * Please be aware that running with window.emitLitDebugLogEvents has performance overhead,\n * making certain operations that are normally very cheap (like a no-op render) much slower,\n * because we must copy data and dispatch events.\n */\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace DebugLog {\n export type Entry =\n | TemplatePrep\n | TemplateInstantiated\n | TemplateInstantiatedAndUpdated\n | TemplateUpdating\n | BeginRender\n | EndRender\n | CommitPartEntry\n | SetPartValue;\n export interface TemplatePrep {\n kind: 'template prep';\n template: Template;\n strings: TemplateStringsArray;\n clonableTemplate: HTMLTemplateElement;\n parts: TemplatePart[];\n }\n export interface BeginRender {\n kind: 'begin render';\n id: number;\n value: unknown;\n container: HTMLElement | DocumentFragment;\n options: RenderOptions | undefined;\n part: ChildPart | undefined;\n }\n export interface EndRender {\n kind: 'end render';\n id: number;\n value: unknown;\n container: HTMLElement | DocumentFragment;\n options: RenderOptions | undefined;\n part: ChildPart;\n }\n export interface TemplateInstantiated {\n kind: 'template instantiated';\n template: Template | CompiledTemplate;\n instance: TemplateInstance;\n options: RenderOptions | undefined;\n fragment: Node;\n parts: Array;\n values: unknown[];\n }\n export interface TemplateInstantiatedAndUpdated {\n kind: 'template instantiated and updated';\n template: Template | CompiledTemplate;\n instance: TemplateInstance;\n options: RenderOptions | undefined;\n fragment: Node;\n parts: Array;\n values: unknown[];\n }\n export interface TemplateUpdating {\n kind: 'template updating';\n template: Template | CompiledTemplate;\n instance: TemplateInstance;\n options: RenderOptions | undefined;\n parts: Array;\n values: unknown[];\n }\n export interface SetPartValue {\n kind: 'set part';\n part: Part;\n value: unknown;\n valueIndex: number;\n values: unknown[];\n templateInstance: TemplateInstance;\n }\n\n export type CommitPartEntry =\n | CommitNothingToChildEntry\n | CommitText\n | CommitNode\n | CommitAttribute\n | CommitProperty\n | CommitBooleanAttribute\n | CommitEventListener\n | CommitToElementBinding;\n\n export interface CommitNothingToChildEntry {\n kind: 'commit nothing to child';\n start: ChildNode;\n end: ChildNode | null;\n parent: Disconnectable | undefined;\n options: RenderOptions | undefined;\n }\n\n export interface CommitText {\n kind: 'commit text';\n node: Text;\n value: unknown;\n options: RenderOptions | undefined;\n }\n\n export interface CommitNode {\n kind: 'commit node';\n start: Node;\n parent: Disconnectable | undefined;\n value: Node;\n options: RenderOptions | undefined;\n }\n\n export interface CommitAttribute {\n kind: 'commit attribute';\n element: Element;\n name: string;\n value: unknown;\n options: RenderOptions | undefined;\n }\n\n export interface CommitProperty {\n kind: 'commit property';\n element: Element;\n name: string;\n value: unknown;\n options: RenderOptions | undefined;\n }\n\n export interface CommitBooleanAttribute {\n kind: 'commit boolean attribute';\n element: Element;\n name: string;\n value: boolean;\n options: RenderOptions | undefined;\n }\n\n export interface CommitEventListener {\n kind: 'commit event listener';\n element: Element;\n name: string;\n value: unknown;\n oldListener: unknown;\n options: RenderOptions | undefined;\n // True if we're removing the old event listener (e.g. because settings changed, or value is nothing)\n removeListener: boolean;\n // True if we're adding a new event listener (e.g. because first render, or settings changed)\n addListener: boolean;\n }\n\n export interface CommitToElementBinding {\n kind: 'commit to element binding';\n element: Element;\n value: unknown;\n options: RenderOptions | undefined;\n }\n }\n}\n\ninterface DebugLoggingWindow {\n // Even in dev mode, we generally don't want to emit these events, as that's\n // another level of cost, so only emit them when DEV_MODE is true _and_ when\n // window.emitLitDebugEvents is true.\n emitLitDebugLogEvents?: boolean;\n}\n\n/**\n * Useful for visualizing and logging insights into what the Lit template system is doing.\n *\n * Compiled out of prod mode builds.\n */\nconst debugLogEvent = DEV_MODE\n ? (event: LitUnstable.DebugLog.Entry) => {\n const shouldEmit = (global as unknown as DebugLoggingWindow)\n .emitLitDebugLogEvents;\n if (!shouldEmit) {\n return;\n }\n global.dispatchEvent(\n new CustomEvent('lit-debug', {\n detail: event,\n })\n );\n }\n : undefined;\n// Used for connecting beginRender and endRender events when there are nested\n// renders when errors are thrown preventing an endRender event from being\n// called.\nlet debugLogRenderId = 0;\n\nlet issueWarning: (code: string, warning: string) => void;\n\nif (DEV_MODE) {\n global.litIssuedWarnings ??= new Set();\n\n /**\n * Issue a warning if we haven't already, based either on `code` or `warning`.\n * Warnings are disabled automatically only by `warning`; disabling via `code`\n * can be done by users.\n */\n issueWarning = (code: string, warning: string) => {\n warning += code\n ? ` See https://lit.dev/msg/${code} for more information.`\n : '';\n if (\n !global.litIssuedWarnings!.has(warning) &&\n !global.litIssuedWarnings!.has(code)\n ) {\n console.warn(warning);\n global.litIssuedWarnings!.add(warning);\n }\n };\n\n queueMicrotask(() => {\n issueWarning(\n 'dev-mode',\n `Lit is in dev mode. Not recommended for production!`\n );\n });\n}\n\nconst wrap =\n ENABLE_SHADYDOM_NOPATCH &&\n global.ShadyDOM?.inUse &&\n global.ShadyDOM?.noPatch === true\n ? (global.ShadyDOM!.wrap as (node: T) => T)\n : (node: T) => node;\n\nconst trustedTypes = (global as unknown as TrustedTypesWindow).trustedTypes;\n\n/**\n * Our TrustedTypePolicy for HTML which is declared using the html template\n * tag function.\n *\n * That HTML is a developer-authored constant, and is parsed with innerHTML\n * before any untrusted expressions have been mixed in. Therefor it is\n * considered safe by construction.\n */\nconst policy = trustedTypes\n ? trustedTypes.createPolicy('lit-html', {\n createHTML: (s) => s,\n })\n : undefined;\n\n/**\n * Used to sanitize any value before it is written into the DOM. This can be\n * used to implement a security policy of allowed and disallowed values in\n * order to prevent XSS attacks.\n *\n * One way of using this callback would be to check attributes and properties\n * against a list of high risk fields, and require that values written to such\n * fields be instances of a class which is safe by construction. Closure's Safe\n * HTML Types is one implementation of this technique (\n * https://github.com/google/safe-html-types/blob/master/doc/safehtml-types.md).\n * The TrustedTypes polyfill in API-only mode could also be used as a basis\n * for this technique (https://github.com/WICG/trusted-types).\n *\n * @param node The HTML node (usually either a #text node or an Element) that\n * is being written to. Note that this is just an exemplar node, the write\n * may take place against another instance of the same class of node.\n * @param name The name of an attribute or property (for example, 'href').\n * @param type Indicates whether the write that's about to be performed will\n * be to a property or a node.\n * @return A function that will sanitize this class of writes.\n */\nexport type SanitizerFactory = (\n node: Node,\n name: string,\n type: 'property' | 'attribute'\n) => ValueSanitizer;\n\n/**\n * A function which can sanitize values that will be written to a specific kind\n * of DOM sink.\n *\n * See SanitizerFactory.\n *\n * @param value The value to sanitize. Will be the actual value passed into\n * the lit-html template literal, so this could be of any type.\n * @return The value to write to the DOM. Usually the same as the input value,\n * unless sanitization is needed.\n */\nexport type ValueSanitizer = (value: unknown) => unknown;\n\nconst identityFunction: ValueSanitizer = (value: unknown) => value;\nconst noopSanitizer: SanitizerFactory = (\n _node: Node,\n _name: string,\n _type: 'property' | 'attribute'\n) => identityFunction;\n\n/** Sets the global sanitizer factory. */\nconst setSanitizer = (newSanitizer: SanitizerFactory) => {\n if (!ENABLE_EXTRA_SECURITY_HOOKS) {\n return;\n }\n if (sanitizerFactoryInternal !== noopSanitizer) {\n throw new Error(\n `Attempted to overwrite existing lit-html security policy.` +\n ` setSanitizeDOMValueFactory should be called at most once.`\n );\n }\n sanitizerFactoryInternal = newSanitizer;\n};\n\n/**\n * Only used in internal tests, not a part of the public API.\n */\nconst _testOnlyClearSanitizerFactoryDoNotCallOrElse = () => {\n sanitizerFactoryInternal = noopSanitizer;\n};\n\nconst createSanitizer: SanitizerFactory = (node, name, type) => {\n return sanitizerFactoryInternal(node, name, type);\n};\n\n// Added to an attribute name to mark the attribute as bound so we can find\n// it easily.\nconst boundAttributeSuffix = '$lit$';\n\n// This marker is used in many syntactic positions in HTML, so it must be\n// a valid element name and attribute name. We don't support dynamic names (yet)\n// but this at least ensures that the parse tree is closer to the template\n// intention.\nconst marker = `lit$${Math.random().toFixed(9).slice(2)}$`;\n\n// String used to tell if a comment is a marker comment\nconst markerMatch = '?' + marker;\n\n// Text used to insert a comment marker node. We use processing instruction\n// syntax because it's slightly smaller, but parses as a comment node.\nconst nodeMarker = `<${markerMatch}>`;\n\nconst d =\n NODE_MODE && global.document === undefined\n ? ({\n createTreeWalker() {\n return {};\n },\n } as unknown as Document)\n : document;\n\n// Creates a dynamic marker. We never have to search for these in the DOM.\nconst createMarker = () => d.createComment('');\n\n// https://tc39.github.io/ecma262/#sec-typeof-operator\ntype Primitive = null | undefined | boolean | number | string | symbol | bigint;\nconst isPrimitive = (value: unknown): value is Primitive =>\n value === null || (typeof value != 'object' && typeof value != 'function');\nconst isArray = Array.isArray;\nconst isIterable = (value: unknown): value is Iterable =>\n isArray(value) ||\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n typeof (value as any)?.[Symbol.iterator] === 'function';\n\nconst SPACE_CHAR = `[ \\t\\n\\f\\r]`;\nconst ATTR_VALUE_CHAR = `[^ \\t\\n\\f\\r\"'\\`<>=]`;\nconst NAME_CHAR = `[^\\\\s\"'>=/]`;\n\n// These regexes represent the five parsing states that we care about in the\n// Template's HTML scanner. They match the *end* of the state they're named\n// after.\n// Depending on the match, we transition to a new state. If there's no match,\n// we stay in the same state.\n// Note that the regexes are stateful. We utilize lastIndex and sync it\n// across the multiple regexes used. In addition to the five regexes below\n// we also dynamically create a regex to find the matching end tags for raw\n// text elements.\n\n/**\n * End of text is: `<` followed by:\n * (comment start) or (tag) or (dynamic tag binding)\n */\nconst textEndRegex = /<(?:(!--|\\/[^a-zA-Z])|(\\/?[a-zA-Z][^>\\s]*)|(\\/?$))/g;\nconst COMMENT_START = 1;\nconst TAG_NAME = 2;\nconst DYNAMIC_TAG_NAME = 3;\n\nconst commentEndRegex = /-->/g;\n/**\n * Comments not started with