dikdimon commited on
Commit
7f2f13f
·
verified ·
1 Parent(s): 1933d1d

Upload sd-webui-weight-helper-main using SD-Hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. sd-webui-weight-helper-main/.flake8 +2 -0
  2. sd-webui-weight-helper-main/.github/FUNDING.yml +2 -0
  3. sd-webui-weight-helper-main/.github/ISSUE_TEMPLATE/bug_report.yml +67 -0
  4. sd-webui-weight-helper-main/.gitignore +164 -0
  5. sd-webui-weight-helper-main/.prettierrc.json +7 -0
  6. sd-webui-weight-helper-main/LICENSE +21 -0
  7. sd-webui-weight-helper-main/README.md +69 -0
  8. sd-webui-weight-helper-main/README_JP.md +69 -0
  9. sd-webui-weight-helper-main/backup_before_patch/scripts/analyzer.py +189 -0
  10. sd-webui-weight-helper-main/backup_before_patch/scripts/api.py +145 -0
  11. sd-webui-weight-helper-main/backup_before_patch/scripts/const.py +11 -0
  12. sd-webui-weight-helper-main/backup_before_patch/scripts/env.py +29 -0
  13. sd-webui-weight-helper-main/backup_before_patch/scripts/utils.py +58 -0
  14. sd-webui-weight-helper-main/backup_before_patch/scripts/weight_helper.py +82 -0
  15. sd-webui-weight-helper-main/esbuild.config.mjs +16 -0
  16. sd-webui-weight-helper-main/eslint.config.mjs +19 -0
  17. sd-webui-weight-helper-main/javascript/weight_helper.js +188 -0
  18. sd-webui-weight-helper-main/javascript/weight_helper.js.map +0 -0
  19. sd-webui-weight-helper-main/package.json +32 -0
  20. sd-webui-weight-helper-main/scripts/__pycache__/weight_helper.cpython-310.pyc +0 -0
  21. sd-webui-weight-helper-main/scripts/__pycache__/weight_helper.cpython-313.pyc +0 -0
  22. sd-webui-weight-helper-main/scripts/__pycache__/wh_analyzer.cpython-310.pyc +0 -0
  23. sd-webui-weight-helper-main/scripts/__pycache__/wh_analyzer.cpython-313.pyc +0 -0
  24. sd-webui-weight-helper-main/scripts/__pycache__/wh_api.cpython-310.pyc +0 -0
  25. sd-webui-weight-helper-main/scripts/__pycache__/wh_api.cpython-313.pyc +0 -0
  26. sd-webui-weight-helper-main/scripts/__pycache__/wh_const.cpython-310.pyc +0 -0
  27. sd-webui-weight-helper-main/scripts/__pycache__/wh_const.cpython-313.pyc +0 -0
  28. sd-webui-weight-helper-main/scripts/__pycache__/wh_env.cpython-310.pyc +0 -0
  29. sd-webui-weight-helper-main/scripts/__pycache__/wh_env.cpython-313.pyc +0 -0
  30. sd-webui-weight-helper-main/scripts/__pycache__/wh_utils.cpython-310.pyc +0 -0
  31. sd-webui-weight-helper-main/scripts/__pycache__/wh_utils.cpython-313.pyc +0 -0
  32. sd-webui-weight-helper-main/scripts/weight_helper.py +91 -0
  33. sd-webui-weight-helper-main/scripts/wh_analyzer.py +196 -0
  34. sd-webui-weight-helper-main/scripts/wh_api.py +154 -0
  35. sd-webui-weight-helper-main/scripts/wh_const.py +11 -0
  36. sd-webui-weight-helper-main/scripts/wh_env.py +36 -0
  37. sd-webui-weight-helper-main/scripts/wh_utils.py +65 -0
  38. sd-webui-weight-helper-main/src/components/sections/common-section.ts +139 -0
  39. sd-webui-weight-helper-main/src/components/sections/header-section.ts +129 -0
  40. sd-webui-weight-helper-main/src/components/sections/lbw-section.ts +42 -0
  41. sd-webui-weight-helper-main/src/components/sections/lora-settings-section.ts +143 -0
  42. sd-webui-weight-helper-main/src/components/sections/metadata-section.ts +84 -0
  43. sd-webui-weight-helper-main/src/components/sections/preview-section.ts +167 -0
  44. sd-webui-weight-helper-main/src/components/ui-template.ts +309 -0
  45. sd-webui-weight-helper-main/src/index.ts +61 -0
  46. sd-webui-weight-helper-main/src/shared/constants/common-const.ts +172 -0
  47. sd-webui-weight-helper-main/src/shared/hooks/use-global-state.ts +11 -0
  48. sd-webui-weight-helper-main/src/shared/manager/api-manager.ts +50 -0
  49. sd-webui-weight-helper-main/src/shared/manager/cache-manager.ts +62 -0
  50. sd-webui-weight-helper-main/src/shared/manager/config-manager.ts +167 -0
sd-webui-weight-helper-main/.flake8 ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ [flake8]
2
+ max-line-length = 160
sd-webui-weight-helper-main/.github/FUNDING.yml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ ko_fi: nihedon
2
+ buy_me_a_coffee: nihedon
sd-webui-weight-helper-main/.github/ISSUE_TEMPLATE/bug_report.yml ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Bug Report
2
+ description: Report a problem or unexpected behavior in the application.
3
+ title: "[Bug] Brief description of the issue"
4
+ labels: ["bug"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ Please fill out the following form to help us reproduce and resolve the issue.
10
+
11
+ - type: checkboxes
12
+ id: environment
13
+ attributes:
14
+ label: Operating Environment
15
+ description: Please select the environment you are using.
16
+ options:
17
+ - label: Windows
18
+ - label: macOS
19
+ - label: Other (e.g., virtual environment)
20
+ validations:
21
+ required: true
22
+
23
+ - type: input
24
+ id: browser
25
+ attributes:
26
+ label: Browser
27
+ description: Please specify the browser you are using (e.g., Chrome, Firefox).
28
+ validations:
29
+ required: true
30
+
31
+ - type: checkboxes
32
+ id: webui
33
+ attributes:
34
+ label: WebUI
35
+ description: Please select the WebUI(s) you are using.
36
+ options:
37
+ - label: A1111
38
+ - label: Forge
39
+ - label: ReForge
40
+ - label: SD-Next
41
+ - label: Other
42
+ validations:
43
+ required: true
44
+
45
+ - type: textarea
46
+ id: console_log
47
+ attributes:
48
+ label: Console Log
49
+ description: Paste any relevant console logs here.
50
+ validations:
51
+ required: false
52
+
53
+ - type: textarea
54
+ id: browser_log
55
+ attributes:
56
+ label: Browser Log
57
+ description: Paste any relevant browser logs here.
58
+ validations:
59
+ required: false
60
+
61
+ - type: textarea
62
+ id: description
63
+ attributes:
64
+ label: Description of the Issue
65
+ description: Please describe the issue you encountered in detail.
66
+ validations:
67
+ required: true
sd-webui-weight-helper-main/.gitignore ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
110
+ .pdm.toml
111
+ .pdm-python
112
+ .pdm-build/
113
+
114
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115
+ __pypackages__/
116
+
117
+ # Celery stuff
118
+ celerybeat-schedule
119
+ celerybeat.pid
120
+
121
+ # SageMath parsed files
122
+ *.sage.py
123
+
124
+ # Environments
125
+ .env
126
+ .venv
127
+ env/
128
+ venv/
129
+ ENV/
130
+ env.bak/
131
+ venv.bak/
132
+
133
+ # Spyder project settings
134
+ .spyderproject
135
+ .spyproject
136
+
137
+ # Rope project settings
138
+ .ropeproject
139
+
140
+ # mkdocs documentation
141
+ /site
142
+
143
+ # mypy
144
+ .mypy_cache/
145
+ .dmypy.json
146
+ dmypy.json
147
+
148
+ # Pyre type checker
149
+ .pyre/
150
+
151
+ # pytype static type analyzer
152
+ .pytype/
153
+
154
+ # Cython debug symbols
155
+ cython_debug/
156
+
157
+ # PyCharm
158
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
159
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
161
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
+ #.idea/
163
+ node_modules/
164
+ package-lock.json
sd-webui-weight-helper-main/.prettierrc.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true,
4
+ "tabWidth": 4,
5
+ "trailingComma": "all",
6
+ "printWidth": 160
7
+ }
sd-webui-weight-helper-main/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2023 t.nihei
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
sd-webui-weight-helper-main/README.md ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [English](README.md) | [日本語](README_JP.md)
2
+
3
+ <img src="https://github.com/nihedon/sd-webui-weight-helper/assets/66118290/08f8d818-ac5d-4125-b2aa-45c302f626f8" height="600px">
4
+
5
+ # Weight Helper Extension
6
+
7
+ ## Overview
8
+
9
+ **Weight Helper** is an extension that allows you to visually adjust the weights of Lora and Lyco with mouse operations.<br>
10
+ You can adjust the magnification of each weight block through the context menu.<br>
11
+ Note: The LoRA Block Weight extension is required to use this extension.
12
+
13
+ ## Installation
14
+
15
+ 1. Open the [Extensions] tab in Stable Diffusion Web UI.
16
+ 2. Select [Install from URL].
17
+ 3. Enter the following URL and execute the installation.
18
+ ```
19
+ https://github.com/nihedon/sd-webui-weight-helper.git
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ 1. Right-click on a Lora or Lyco tag to open the context menu.
25
+ - `<lora:lora_name:1.0>`
26
+ - `<lyco:lyco_name:1.0>`
27
+ 2. Choose "LoRA" or "LyCORIS" according to the tag, and select "SDXL" if it's for the SDXL version.
28
+ 3. Move the weight slider to adjust the magnification of each weight block.
29
+ 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.
30
+ 5. Save your favorite weight settings by clicking the "★" icon at the top left of the context menu.
31
+ Important: Saved settings are stored in LocalStorage, but any unsaved history will be deleted upon refreshing the screen.
32
+
33
+ ## Features
34
+
35
+ - Easy to set weight values with mouse operations.
36
+ - Use of preset values from Lora Block Weight is possible.
37
+ - You can check LoRA information and add trigger words from the icons displayed on the preview screen.
38
+ - Information from the readme file attached to LoRA can be displayed on the preview screen. ({loraname}.txt, {loraname}.description.txt)
39
+ - 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.)
40
+
41
+ ## Known Issues
42
+ - The algorithm is deduced from the metadata of LoRA, but it is incomplete. Bug reports and suggestions for the correct algorithm would be appreciated.
43
+
44
+ ## Configuration Options
45
+
46
+ - Scale adjustment in the context menu.
47
+ - Use of the `execCommand` function for text replacement (Note: execCommand is a deprecated function, but it allows for "undo" and "redo").
48
+ - Display of UNet, Dyn, Start, and Stop sliders.
49
+ - Setting of minimum, maximum, and step values for each weight slider.
50
+ - Detailed settings for LBW's Lora and Lyco blocks.
51
+ - Option to display or hide previews, and settings for preview size and position.
52
+ - Settings for block weight segmentation.
53
+
54
+ ## Guaranteed Operating Environment
55
+
56
+ - Stable Diffusion AUTOMATIC1111
57
+ - Windows
58
+ - Google Chrome
59
+
60
+ ## Acknowledgments
61
+
62
+ We would like to express our deep gratitude to **hako-mikan**, the author of **LoRA Block Weight**, for creating this extension.
63
+ ```
64
+ https://github.com/hako-mikan/sd-webui-lora-block-weight
65
+ ```
66
+
67
+ ## License
68
+
69
+ This project is published under the [MIT License](LICENSE).
sd-webui-weight-helper-main/README_JP.md ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [English](README.md) | [日本語](README_JP.md)
2
+
3
+ <img src="https://github.com/nihedon/sd-webui-weight-helper/assets/66118290/08f8d818-ac5d-4125-b2aa-45c302f626f8" height="600px">
4
+
5
+ # Weight Helper 拡張機能
6
+
7
+ ## 概要
8
+
9
+ **Weight Helper** はLoraやLycoのウェイトをマウス操作で視覚的に調整できる拡張機能です。<br>
10
+ コンテキストメニューを通じて各ウェイトブロックの倍率を調整できます。<br>
11
+ 注:この拡張機能を利用するにはLoRA Block Weight拡張機能が必要です。
12
+
13
+ ## インストール
14
+
15
+ 1. Stable Diffusion Web UIの[拡張機能]タブを開く
16
+ 2. [URLからインストール]を選択
17
+ 3. 以下のURLを入力し、インストールを実行
18
+ ```
19
+ https://github.com/nihedon/sd-webui-weight-helper.git
20
+ ```
21
+
22
+ ## 使い方
23
+
24
+ 1. LoraまたはLycoのタグを右クリックしてコンテキストメニューを開く
25
+ - `<lora:lora_name:1.0>`
26
+ - `<lyco:lyco_name:1.0>`
27
+ 2. LoRA(またはLyCORIS)に合わせて"LoRA"か"LyCORIS"を選択し、SDXLバージョンの場合は"SDXL"を選択します。
28
+ 3. ウェイトスライダーを動かして各ウェイトブロックの倍率を調整します。
29
+ 4. UNet、Dyn、Start、Stopは非表示となっているため、設定した場合は"show more options"ボタンを押下して表示できます。
30
+ 5. お気に入りのウェイト設定はコンテキストメニュー左上の"★"アイコンをクリックし保存します。
31
+ 重要:保存した場合はLocalStrageに保存されますが、保存していない履歴は画面更新時にすべて削除されます。
32
+
33
+ ## 特徴
34
+
35
+ - マウス操作でウェイト値を容易に設定できます。
36
+ - Lora Block Weightのプリセット値の利用が可能です。
37
+ - プレビュー画面に表示されるアイコンからLoRA情報の確認、トリガーワードの追加ができます。
38
+ - LoRAに付属するreadmeファイルの情報をプレビュー画面に表示できます。({loraname}.txt, {loraname}.description.txt)
39
+ - LoRAのメタ情報からLoRAのSDバージョンが表示されます。(Civitaiのメタテキストが存在する場合、civitaiに登録したSDバージョンも表示します。)
40
+
41
+ ## 既知の問題
42
+ - LoRAのメタ情報からアルゴリズムを割り出していますが不完全です。不具合報告や正しいアルゴリズムを提示していただけると助かります(_ _)
43
+
44
+ ## 設定オプション
45
+
46
+ - コンテキストメニューのスケール調整
47
+ - テキスト置換に `execCommand` 関数の使用(注:execCommandは非推奨関数ですが、「元に戻す」「やり直し」が使えます)
48
+ - UNet、Dyn、Start、およびStopスライダーの表示
49
+ - 各ウェイトスライダーの最小値、最大値、ステップ数の設定
50
+ - LBWのLoraおよびLycoブロックの詳細設定
51
+ - プレビューの表示有無、表示サイズと表示位置の設定
52
+ - ブロックウェイトの区切り設定
53
+
54
+ ## 動作保証環境
55
+
56
+ - Stable Difusion AUTOMATIC1111
57
+ - Windows
58
+ - Google Chrome
59
+
60
+ ## 謝辞
61
+
62
+ この拡張機能を作成するにあたり、**LoRA Block Weight** の作者である **hako-mikan** 様に深く感謝申し上げます。
63
+ ```
64
+ https://github.com/hako-mikan/sd-webui-lora-block-weight
65
+ ```
66
+
67
+ ## ライセンス
68
+
69
+ このプロジェクトは[MITライセンス](LICENSE)の下で公開されています。
sd-webui-weight-helper-main/backup_before_patch/scripts/analyzer.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pickle
3
+ import re
4
+ from typing import Optional
5
+ from scripts import const, env
6
+
7
+
8
+ def get_model_type(lora) -> Optional[str]:
9
+ if env.ui_type == const.UI_TYPE.A1111:
10
+ sd_version: Optional[str] = lora.sd_version.name
11
+ if sd_version and env.network:
12
+ if sd_version == env.network.SdVersion.SD1.name or sd_version == env.network.SdVersion.SD2.name:
13
+ return "SD"
14
+ elif sd_version == env.network.SdVersion.SDXL.name:
15
+ return "SDXL"
16
+ return sd_version
17
+ elif env.ui_type == const.UI_TYPE.NEW_FORGE:
18
+ base_model_version: Optional[str] = lora.metadata.get("ss_base_model_version")
19
+ if base_model_version:
20
+ base_model_version = base_model_version.lower()
21
+ if "sd_" in base_model_version:
22
+ return "SD"
23
+ if "sd3" in base_model_version:
24
+ return "SD"
25
+ elif "sdxl" in base_model_version:
26
+ return "SDXL"
27
+ elif "flux" in base_model_version:
28
+ return "Flux"
29
+ return None
30
+ return None
31
+
32
+
33
+ def get_algorithm(metadata, ss_network_args) -> Optional[str]:
34
+ ss_network_module = metadata.get("ss_network_module")
35
+ if not ss_network_module or ss_network_module == "Unknown":
36
+ return None
37
+
38
+ conv_dim = float(ss_network_args.get("conv_dim", "-1"))
39
+ conv_alpha = float(ss_network_args.get("conv_alpha", "-1"))
40
+ algo = ss_network_args.get("algo", "").lower()
41
+ unit = ss_network_args.get("unit", "").lower()
42
+ dora_wd = ss_network_args.get("dora_wd", False)
43
+
44
+ if ss_network_module.find("locon.locon_kohya") >= 0:
45
+ return "LoCon"
46
+
47
+ elif ss_network_module.find("lycoris.kohya") >= 0:
48
+ algoName = ""
49
+ if dora_wd:
50
+ algoName = "(DoRA)"
51
+ elif algo == "lora":
52
+ algoName += "(LoCon)"
53
+ elif algo == "locon":
54
+ if unit:
55
+ algoName += "(DyLoRA)"
56
+ else:
57
+ algoName += "(LoCon)"
58
+ elif algo == "loha":
59
+ algoName += "(LoHa)"
60
+ elif algo == "lokr":
61
+ algoName += "(Lokr)"
62
+ elif algo == "ia3":
63
+ algoName += "(IA3)"
64
+ elif algo == "full":
65
+ algoName += "(Full)"
66
+ elif algo == "glora":
67
+ algoName += "(GLoRA)"
68
+
69
+ return f"LyCORIS{algoName}"
70
+
71
+ elif ss_network_module.find("networks.dylora") >= 0:
72
+ if algo == "dylora" and unit:
73
+ if conv_dim > 0 or conv_alpha > 0:
74
+ return "DyLoRA(C3Lier)"
75
+ return "DyLoRA(LierLa)"
76
+
77
+ elif conv_dim > 0 or conv_alpha > 0:
78
+ return "LoRA(C3Lier)"
79
+
80
+ else:
81
+ return "LoRA(LierLa)"
82
+
83
+
84
+ def get_blocks(lora_on_disk, force):
85
+ baseKeys = __get_lora_state_keys(lora_on_disk.filename, force)
86
+
87
+ def formatForDownMidUp(key):
88
+ blocks = ["BASE"]
89
+ for key in keys:
90
+ k, v = key.split("_", 1)
91
+ if k == "down":
92
+ k = "IN"
93
+ v1, v2 = v.split("_")
94
+ val = int(v1) * 3 + int(v2) + 1
95
+ elif k == "mid":
96
+ blocks.append("M00")
97
+ continue
98
+ elif k == "up":
99
+ k = "OUT"
100
+ v1, v2 = v.split("_")
101
+ val = int(v1) * 3 + int(v2)
102
+ blocks.append(f'{k}{int(val):02}')
103
+ return sorted(blocks)
104
+
105
+ def formatForInputMiddleOutput(key):
106
+ blocks = ["BASE"]
107
+ for key in keys:
108
+ k, v = key.split("_")
109
+ if k == "input":
110
+ k = "IN"
111
+ elif k == "middle":
112
+ blocks.append("M00")
113
+ continue
114
+ elif k == "output":
115
+ k = "OUT"
116
+ blocks.append(f'{k}{int(v):02}')
117
+ return sorted(blocks)
118
+
119
+ def formatForFlux(key):
120
+ blocks = ["CLIP", "T5", "IN", "OUT"]
121
+ for key in keys:
122
+ k, v = key.split("_")
123
+ v = int(v)
124
+ if k == "double":
125
+ k = "D"
126
+ if k == "single":
127
+ k = "S"
128
+ blocks.append(f'{k}{v:02}')
129
+ return sorted(blocks)
130
+
131
+ keys = [k for k in baseKeys if k.find("lora_unet") == 0]
132
+ if len(keys) > 0:
133
+ keys_tmp = [k for k in keys if re.match("^lora_unet_(down|mid|up)_", k)]
134
+ if len(keys_tmp) > 0:
135
+ keys = set(["_".join([k.split("_")[2], k.split("_")[4] + "_" + k.split("_")[6]]) for k in keys_tmp if "transformer_blocks" in k])
136
+ return formatForDownMidUp(keys)
137
+
138
+ keys_tmp = [k for k in keys if re.match("^lora_unet_(input|middle|output)_", k)]
139
+ if len(keys_tmp) > 0:
140
+ keys = set(["_".join([k.split("_")[2], k.split("_")[4]]) for k in keys_tmp if "transformer_blocks" in k])
141
+ return formatForInputMiddleOutput(keys)
142
+
143
+ keys_tmp = [k for k in keys if re.match("^lora_unet_(single|double)_", k)]
144
+ if len(keys_tmp) > 0:
145
+ keys = set(["_".join([k.split("_")[2], k.split("_")[4]]) for k in keys_tmp])
146
+ return formatForFlux(keys)
147
+
148
+ keys_tmp = [k for k in keys if re.match("^lora_unet_joint_blocks_", k)]
149
+ if len(keys_tmp) > 0:
150
+ # TODO SD3? 38 blocks
151
+ return None
152
+
153
+ keys = [k for k in baseKeys if "transformer_blocks" in k and "attentions" in k]
154
+ if len(keys) > 0:
155
+ # TODO unknown
156
+ keys = set([".".join(k.split(".")[2:6]) for k in keys])
157
+ in_blocks = set([f"{k.split('.')[1]}_{k.split('.')[3]}" for k in keys if "down_block" in k])
158
+ mid_block = set([k.split(".")[2] for k in keys if "mid_block" in k])
159
+ out_blocks = set([f"{k.split('.')[1]}_{k.split('.')[3]}" for k in keys if "up_block" in k])
160
+ keys = list(in_blocks) + list(mid_block) + list(out_blocks)
161
+ return None
162
+
163
+ keys = [k for k in baseKeys if "transformer_blocks" in k and k.find("transformer") == 0]
164
+ if len(keys) > 0:
165
+ keys = set([".".join(k.split(".")[1:3]) for k in keys])
166
+ keys = [k.replace("single_transformer_blocks.", "single_") for k in keys]
167
+ keys = [k.replace("transformer_blocks.", "double_") for k in keys]
168
+ return formatForFlux(keys)
169
+
170
+ return None
171
+
172
+
173
+ def __get_lora_state_keys(filename, force):
174
+ cache_path = f"{os.path.splitext(filename)[0]}.whcache"
175
+ if not force and os.path.exists(cache_path):
176
+ try:
177
+ with open(cache_path, "rb") as f:
178
+ return pickle.load(f)
179
+ except Exception:
180
+ pass
181
+
182
+ lora_sd = env.network_lora.load_lora_state_dict(filename)
183
+ keys = list(lora_sd.keys())
184
+ try:
185
+ with open(cache_path, "wb") as f:
186
+ pickle.dump(keys, f)
187
+ except Exception:
188
+ pass
189
+ return keys
sd-webui-weight-helper-main/backup_before_patch/scripts/api.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import ast
3
+ import json
4
+ import os
5
+ from typing import Any, Optional
6
+ from modules import shared
7
+ from scripts import analyzer, const, utils
8
+
9
+
10
+ def get_metadata(key, force) -> dict[str, Optional[str]]:
11
+ ret: dict[str, Optional[str]] = {
12
+ "modelType": None,
13
+ "algorithm": None,
14
+ "baseModel": None,
15
+ "usingBlocks": None
16
+ }
17
+
18
+ lora_on_disk = utils.get_lora_on_disk(key)
19
+ if not lora_on_disk:
20
+ return ret
21
+
22
+ ret["modelType"] = analyzer.get_model_type(lora_on_disk)
23
+
24
+ metadata = lora_on_disk.metadata
25
+ ss_network_args = __get_network_args(lora_on_disk)
26
+ ret["algorithm"] = analyzer.get_algorithm(metadata, ss_network_args)
27
+
28
+ model_path, _ = os.path.splitext(lora_on_disk.filename)
29
+ civitai_info = __find_civitai_info(model_path)
30
+ ret["baseModel"] = civitai_info.get("baseModel", None)
31
+
32
+ if shared.opts.weight_helper_parse_lora_blocks:
33
+ try:
34
+ ret["usingBlocks"] = analyzer.get_blocks(lora_on_disk, force)
35
+ except Exception:
36
+ pass
37
+ return ret
38
+
39
+
40
+ def get_preview_info(key):
41
+ ret: dict[str, Optional[bool | str | list[str]]] = {
42
+ "modelId": None,
43
+ "triggerWords": [],
44
+ "negativeTriggerWords": [],
45
+ "modelName": None,
46
+ "thumbUrl": None,
47
+ "hasMetadata": False,
48
+ "description": None,
49
+ }
50
+
51
+ lora_on_disk = utils.get_lora_on_disk(key)
52
+ if lora_on_disk:
53
+ model_path, _ = os.path.splitext(lora_on_disk.filename)
54
+ ret["modelName"] = lora_on_disk.name
55
+ ret["thumbUrl"] = __find_preview(model_path)
56
+ ret["hasMetadata"] = __find_metadata(lora_on_disk)
57
+ ret["description"] = __find_description(model_path)
58
+
59
+ trigger_words = []
60
+ civitai_info = __find_civitai_info(model_path)
61
+ ret["modelId"] = civitai_info.get("modelId", None)
62
+ trigger_words = civitai_info.get("trainedWords", [])
63
+ trigger_words = ",".join(trigger_words).split(",")
64
+ ret["triggerWords"] = [w.strip() for w in trigger_words if w]
65
+
66
+ lora_json = __get_lora_json(model_path)
67
+
68
+ activation_text = lora_json.get("activation text", "")
69
+ if activation_text:
70
+ activation_text = activation_text.split(",")
71
+ activation_text = [w.strip() for w in activation_text if w]
72
+ ret["triggerWords"] = activation_text
73
+
74
+ negative_text = lora_json.get("negative text", "")
75
+ if negative_text:
76
+ negative_text = negative_text.split(",")
77
+ negative_text = [w.strip() for w in negative_text if w]
78
+ ret["negativeTriggerWords"] = negative_text
79
+
80
+ json_description = lora_json.get("description", None)
81
+ if json_description:
82
+ ret["description"] = json_description
83
+
84
+ return ret
85
+
86
+
87
+ def __get_network_args(lora) -> dict[str, Any]:
88
+ metadata = lora.metadata
89
+ ss_network_args = metadata.get("ss_network_args", {})
90
+ if type(ss_network_args) is str:
91
+ try:
92
+ ss_network_args = ast.literal_eval(ss_network_args)
93
+ except Exception:
94
+ ss_network_args = {}
95
+ return ss_network_args
96
+
97
+
98
+ def __get_lora_json(path) -> dict[str, Any]:
99
+ if path:
100
+ lora_json = f"{path}.json"
101
+ if os.path.exists(lora_json):
102
+ try:
103
+ with open(lora_json, "r", encoding="utf-8", errors="replace") as f:
104
+ return json.load(f)
105
+ except OSError:
106
+ pass
107
+ return {}
108
+
109
+
110
+ def __find_preview(path) -> str:
111
+ if path:
112
+ potential_files = sum([[f"{path}.{ext}", f"{path}.preview.{ext}"] for ext in const.ALLOWED_PREVIEW_EXTENSIONS], [])
113
+ for file in potential_files:
114
+ if os.path.exists(file):
115
+ dpi_scale = 1.5
116
+ return utils.resize_to_base64(file, int(shared.opts.weight_helper_preview_height * dpi_scale))
117
+ return "./file=html/card-no-preview.png"
118
+
119
+
120
+ def __find_metadata(lora) -> bool:
121
+ return len(lora.metadata) > 0 if lora else False
122
+
123
+
124
+ def __find_description(path) -> Optional[str]:
125
+ if path:
126
+ for file in [f"{path}.txt", f"{path}.description.txt"]:
127
+ if os.path.exists(file):
128
+ try:
129
+ with open(file, "r", encoding="utf-8", errors="replace") as f:
130
+ return f.read()
131
+ except OSError:
132
+ pass
133
+ return None
134
+
135
+
136
+ def __find_civitai_info(path) -> dict[str, Any]:
137
+ if path:
138
+ civitai_info = f"{path}.civitai.info"
139
+ if os.path.exists(civitai_info):
140
+ try:
141
+ with open(civitai_info, "r", encoding="utf-8", errors="replace") as f:
142
+ return json.load(f)
143
+ except OSError:
144
+ pass
145
+ return {}
sd-webui-weight-helper-main/backup_before_patch/scripts/const.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import enum
2
+
3
+
4
+ API_PREFIX = "/whapi/v1"
5
+ ALLOWED_PREVIEW_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "gif"]
6
+
7
+
8
+ class UI_TYPE(enum.Enum):
9
+ Unknown = 1
10
+ A1111 = 2
11
+ NEW_FORGE = 3
sd-webui-weight-helper-main/backup_before_patch/scripts/env.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import importlib
3
+ from scripts import const
4
+
5
+ network = None
6
+ network_lora = None
7
+ ui_type = None
8
+
9
+ try:
10
+ from modules_forge import forge_version # noqa: F401
11
+ ui_type = const.UI_TYPE.NEW_FORGE
12
+ except ImportError:
13
+ ui_type = const.UI_TYPE.A1111
14
+
15
+ try:
16
+ network = importlib.import_module("extensions-builtin.sd_forge_lora.network")
17
+ network_lora = importlib.import_module("extensions-builtin.sd_forge_lora.ui_extra_networks_lora").networks
18
+ except Exception:
19
+ pass
20
+
21
+ if network_lora is None:
22
+ try:
23
+ network = importlib.import_module("extensions-builtin.Lora.network")
24
+ network_lora = importlib.import_module("extensions-builtin.Lora.ui_extra_networks_lora").networks
25
+ except Exception:
26
+ pass
27
+
28
+ if network_lora is None:
29
+ ui_type = const.UI_TYPE.Unknown
sd-webui-weight-helper-main/backup_before_patch/scripts/utils.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import cv2
3
+ import urllib
4
+
5
+ import numpy as np
6
+ from scripts import env
7
+
8
+
9
+ def get_lora_on_disk(key):
10
+ if env.network_lora:
11
+ lora_on_disk = env.network_lora.available_network_aliases.get(key)
12
+ if not lora_on_disk:
13
+ # sdnext
14
+ lora_on_disk = next((v for v in env.network_lora.available_network_aliases.values() if v.alias == key), None)
15
+ return lora_on_disk
16
+ return None
17
+
18
+
19
+ def imread_unicode(path):
20
+ try:
21
+ with open(path, 'rb') as f:
22
+ data = np.frombuffer(f.read(), np.uint8)
23
+ img = cv2.imdecode(data, cv2.IMREAD_UNCHANGED)
24
+ return img
25
+ except Exception as e:
26
+ print(f"imread_unicode error: {e}")
27
+ return None
28
+
29
+
30
+ def resize_to_base64(input_path, target_height):
31
+ output_format = '.webp'
32
+ quality = 85
33
+
34
+ img = imread_unicode(input_path)
35
+
36
+ h, w = img.shape[:2]
37
+ if h < target_height:
38
+ quoted_filename = urllib.parse.quote(input_path.replace('\\', '/'))
39
+ return f"./sd_extra_networks/thumb?filename={quoted_filename}"
40
+
41
+ scale = target_height / h
42
+ target_size = (int(w * scale), target_height)
43
+
44
+ resized = cv2.resize(img, target_size, interpolation=cv2.INTER_AREA)
45
+
46
+ sharpen_kernel = np.array([
47
+ [0, -0.2, 0],
48
+ [-0.2, 1.8, -0.2],
49
+ [0, -0.2, 0]
50
+ ])
51
+ sharpened = cv2.filter2D(resized, -1, sharpen_kernel)
52
+ success, buffer = cv2.imencode(output_format, sharpened, [cv2.IMWRITE_WEBP_QUALITY, quality])
53
+ if not success:
54
+ quoted_filename = urllib.parse.quote(input_path.replace('\\', '/'))
55
+ return f"./sd_extra_networks/thumb?filename={quoted_filename}"
56
+
57
+ base64_str = base64.b64encode(buffer).decode('utf-8')
58
+ return f'data:image/webp;base64,{base64_str}'
sd-webui-weight-helper-main/backup_before_patch/scripts/weight_helper.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from fastapi import FastAPI
3
+ from modules import script_callbacks, shared
4
+ from scripts import api, const
5
+
6
+
7
+ def on_app_started(_: gr.Blocks, app: FastAPI):
8
+ @app.post(const.API_PREFIX + "/get_metadata")
9
+ async def _(key: str, force: bool = False):
10
+ return api.get_metadata(key, force)
11
+
12
+ @app.post(const.API_PREFIX + "/get_preview_info")
13
+ async def _(key: str):
14
+ return api.get_preview_info(key)
15
+
16
+
17
+ def on_ui_settings():
18
+ """
19
+ Set up the UI settings for the weight helper by adding options to the shared configuration.
20
+ """
21
+ section = ('weight_helper', 'Weight Helper')
22
+
23
+ shared.options_templates.update(shared.options_section(section, {
24
+ 'weight_helper_enabled': shared.OptionInfo(True, 'Enabled'),
25
+ 'weight_helper_context_menu_scale': shared.OptionInfo(0.8, "Context Menu Scale", gr.Number),
26
+ 'weight_helper_using_execCommand': shared.OptionInfo(True, "Use the deprecated execCommand function to replace text.").info(
27
+ "You can use 'undo' to revert the text to its previous state, "
28
+ "but it will no longer be updated in real-time."),
29
+
30
+ "weight_helper_slider_length": shared.OptionInfo(160, "Slider Length (px)", gr.Number),
31
+
32
+ "weight_helper_TEnc_min": shared.OptionInfo(0, "TEnc Min Value", gr.Number),
33
+ "weight_helper_TEnc_max": shared.OptionInfo(1, "TEnc Max Value", gr.Number),
34
+ "weight_helper_TEnc_step": shared.OptionInfo(0.05, "TEnc Step", gr.Number),
35
+
36
+ "weight_helper_UNet_min": shared.OptionInfo(0, "UNet Min Value", gr.Number),
37
+ "weight_helper_UNet_max": shared.OptionInfo(1, "UNet Max Value", gr.Number),
38
+ "weight_helper_UNet_step": shared.OptionInfo(0.05, "UNet Step", gr.Number),
39
+
40
+ "weight_helper_LBW_min": shared.OptionInfo(0, "LBW (LoRA Block Weight) Min Value", gr.Number),
41
+ "weight_helper_LBW_max": shared.OptionInfo(1, "LBW (LoRA Block Weight) Max Value", gr.Number),
42
+ "weight_helper_LBW_step": shared.OptionInfo(0.05, "LBW (LoRA Block Weight) Step", gr.Number),
43
+
44
+ 'weight_helper_show_preview': shared.OptionInfo(True, 'Show Preview'),
45
+ "weight_helper_preview_height": shared.OptionInfo(400, "Preview Height (px)", gr.Number),
46
+ "weight_helper_preview_position": shared.OptionInfo("Top Right", "Preview Position", gr.Radio, {
47
+ "choices": ["Top Right", "Bottom Right", "Top Left", "Bottom Left"]
48
+ }),
49
+
50
+ 'weight_helper_parse_lora_blocks': shared.OptionInfo(True, '[experimental] Parse LoRA file to get block information'),
51
+
52
+ "weight_helper_LBW_SD_lora_block_points": shared.OptionInfo(
53
+ "BASE, IN01-IN04, IN05-IN08, M00, OUT03-OUT06, OUT07-OUT11",
54
+ "Advanced option - LoRA-LierLa block points")
55
+ .info("default: BASE, IN01-IN04, IN05-IN08, M00, OUT03-OUT06, OUT07-OUT11"),
56
+ "weight_helper_LBW_SD_lycoris_block_points": shared.OptionInfo(
57
+ "BASE, IN00-IN05, IN06-IN11, M00, OUT00-OUT05, OUT06-OUT11",
58
+ "Advanced option - LyCORIS or LoRA-C3Lier block points")
59
+ .info("default: BASE, IN00-IN05, IN06-IN11, M00, OUT00-OUT05, OUT06-OUT11"),
60
+
61
+ "weight_helper_LBW_SDXL_lora_block_points": shared.OptionInfo(
62
+ "BASE, IN04-IN08, M00, OUT00-OUT05",
63
+ "Advanced option - SDXL LoRA-LierLa block points")
64
+ .info("default: BASE, IN04-IN08, M00, OUT00-OUT05"),
65
+ "weight_helper_LBW_SDXL_lycoris_block_points": shared.OptionInfo(
66
+ "BASE, IN00-IN03, IN04-IN08, M00, OUT00-OUT03, OUT04-OUT08",
67
+ "Advanced option - SDXL LyCORIS or LoRA-C3Lier block points")
68
+ .info("default: BASE, IN00-IN03, IN04-IN08, M00, OUT00-OUT03, OUT04-OUT08"),
69
+
70
+ "weight_helper_LBW_Flux_lora_block_points": shared.OptionInfo(
71
+ "CLIP, T5, IN, OUT, D00-D18, S00-S18, S19-S37",
72
+ "Advanced option - Flux LoRA-LierLa block points")
73
+ .info("default: CLIP, T5, IN, OUT, D00-D18, S00-S18, S19-S37"),
74
+ "weight_helper_LBW_Flux_lycoris_block_points": shared.OptionInfo(
75
+ "CLIP, T5, IN, OUT, D00-D18, S00-S18, S19-S37",
76
+ "Advanced option - Flux LyCORIS or LoRA-C3Lier block points")
77
+ .info("default: CLIP, T5, IN, OUT, D00-D18, S00-S18, S19-S37"),
78
+ }))
79
+
80
+
81
+ script_callbacks.on_ui_settings(on_ui_settings)
82
+ script_callbacks.on_app_started(on_app_started)
sd-webui-weight-helper-main/esbuild.config.mjs ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { build } from 'esbuild';
2
+
3
+ const baseOptions = {
4
+ entryPoints: ['src/index.ts'],
5
+ bundle: true,
6
+ outfile: 'javascript/weight_helper.js',
7
+ target: 'es2022',
8
+ format: 'iife',
9
+ platform: 'browser',
10
+ };
11
+
12
+ build({
13
+ ...baseOptions,
14
+ minify: process.argv.includes('--minify'),
15
+ sourcemap: process.argv.includes('--sourcemap'),
16
+ }).catch(() => process.exit(1));
sd-webui-weight-helper-main/eslint.config.mjs ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defineConfig } from "eslint/config";
2
+ import globals from "globals";
3
+ import tseslint from "typescript-eslint";
4
+ import prettier from 'eslint-config-prettier';
5
+
6
+ export default defineConfig([
7
+ ...tseslint.configs.recommended,
8
+ {
9
+ files: ['**/*.ts'],
10
+ languageOptions: {
11
+ globals: globals.browser,
12
+ },
13
+ rules: {
14
+ semi: ['error', 'always'],
15
+ quotes: ['error', 'single'],
16
+ },
17
+ },
18
+ prettier,
19
+ ]);
sd-webui-weight-helper-main/javascript/weight_helper.js ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "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(`
2
+ `).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<i?1:-1}),t}var wo=/<(lora|lyco):([^:]+):([^>]+)>/;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||(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:`<lora:${e}:${h}>`}}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<r.length;b++){let f,M;if(r[b].indexOf("=")>=0){let C=r[b].split("=");f=C[0].toLowerCase(),M=C[1]}else f=[P.te,P.unet][b],M=r[b];n[f]=M}let a=[];for(let b of Object.values(K))for(let f of Object.values(J))a.push({modelType:b,loraBlockType:f,presets:$(b,f),masks:V(b,f)});let s=T(m.TENC).default,l,i=T(m.START).default,g=T(m.STOP).default,u=[],c="",h=!1;Object.entries(n).forEach(([b,f])=>{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<b.length;f++)u.push(T(m.LBW).default);u.forEach((f,M)=>{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=`[
3
+ \f\r]`,ge=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Qt=/-->/g,eo=/>/g,Y=RegExp(`>|${Xe}(?:([^\\s"'>=/]+)(${Xe}*=${Xe}*(?:[^
4
+ \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?"<svg>":t===3?"<math>":"",s=ge;for(let l=0;l<o;l++){let i=e[l],g,u,c=-1,h=0;for(;h<i.length&&(s.lastIndex=h,u=s.exec(i),u!==null);)h=s.lastIndex,s===ge?u[1]==="!--"?s=Qt:u[1]!==void 0?s=eo:u[2]!==void 0?(ao.test(u[2])&&(n=RegExp("</"+u[2],"g")),s=Y):u[3]!==void 0&&(s=Y):s===Y?u[0]===">"?(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?"</svg>":t===3?"</math>":"")),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.length<l;){if(n.nodeType===1){if(n.hasAttributes())for(let c of n.getAttributeNames())if(c.endsWith(no)){let h=u[s++],p=n.getAttribute(c).split(R),w=/([.?@])?(.*)/.exec(h);i.push({type:1,index:a,name:w[2],strings:p,ctor:w[1]==="."?Ye:w[1]==="?"?je:w[1]==="@"?Ze:re}),n.removeAttribute(c)}else c.startsWith(R)&&(i.push({type:6,index:a}),n.removeAttribute(c));if(ao.test(n.tagName)){let c=n.textContent.split(R),h=c.length-1;if(h>0){n.textContent=We?We.emptyScript:"";for(let p=0;p<h;p++)n.append(c[p],de()),j.nextNode(),i.push({type:2,index:++a});n.append(c[h],de())}}}else if(n.nodeType===8)if(n.data===so)i.push({type:2,index:a});else{let c=-1;for(;(c=n.data.indexOf(R,c+1))!==-1;)i.push({type:7,index:a}),c+=R.length-1}a++}}static createElement(t,o){let r=Z.createElement("template");return r.innerHTML=t,r}};function oe(e,t,o=e,r){if(t===he)return t;let n=r!==void 0?o._$Co?.[r]:o._$Cl,a=pe(t)?void 0:t._$litDirective$;return n?.constructor!==a&&(n?._$AO?.(!1),a===void 0?n=void 0:(n=new a(e),n._$AT(e,o,r)),r!==void 0?(o._$Co??=[])[r]=n:o._$Cl=n),n!==void 0&&(t=oe(e,n._$AS(e,t.values),n,r)),t}var Fe=class{constructor(t,o){this._$AV=[],this._$AN=void 0,this._$AD=t,this._$AM=o}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(t){let{el:{content:o},parts:r}=this._$AD,n=(t?.creationScope??Z).importNode(o,!0);j.currentNode=n;let a=j.nextNode(),s=0,l=0,i=r[0];for(;i!==void 0;){if(s===i.index){let g;i.type===2?g=new fe(a,a.nextSibling,this,t):i.type===1?g=new i.ctor(a,i.name,i.strings,this,t):i.type===6&&(g=new qe(a,this,t)),this._$AV.push(g),i=r[++l]}s!==i?.index&&(a=j.nextNode(),s++)}return j.currentNode=Z,n}p(t){let o=0;for(let r of this._$AV)r!==void 0&&(r.strings!==void 0?(r._$AI(t,r,o),o+=r.strings.length-2):r._$AI(t[o])),o++}},fe=class e{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(t,o,r,n){this.type=2,this._$AH=x,this._$AN=void 0,this._$AA=t,this._$AB=o,this._$AM=r,this.options=n,this._$Cv=n?.isConnected??!0}get parentNode(){let t=this._$AA.parentNode,o=this._$AM;return o!==void 0&&t?.nodeType===11&&(t=o.parentNode),t}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(t,o=this){t=oe(this,t,o),pe(t)?t===x||t==null||t===""?(this._$AH!==x&&this._$AR(),this._$AH=x):t!==this._$AH&&t!==he&&this._(t):t._$litType$!==void 0?this.$(t):t.nodeType!==void 0?this.T(t):Lo(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==x&&pe(this._$AH)?this._$AA.nextSibling.data=t:this.T(Z.createTextNode(t)),this._$AH=t}$(t){let{values:o,_$litType$:r}=t,n=typeof r=="number"?this._$AC(t):(r.el===void 0&&(r.el=ue.createElement(io(r.h,r.h[0]),this.options)),r);if(this._$AH?._$AD===n)this._$AH.p(o);else{let a=new Fe(n,this),s=a.u(this.options);a.p(o),this.T(s),this._$AH=a}}_$AC(t){let o=ro.get(t.strings);return o===void 0&&ro.set(t.strings,o=new ue(t)),o}k(t){Je(this._$AH)||(this._$AH=[],this._$AR());let o=this._$AH,r,n=0;for(let a of t)n===o.length?o.push(r=new e(this.O(de()),this.O(de()),this,this.options)):r=o[n],r._$AI(a),n++;n<o.length&&(this._$AR(r&&r._$AB.nextSibling,n),o.length=n)}_$AR(t=this._$AA.nextSibling,o){for(this._$AP?.(!1,!0,o);t!==this._$AB;){let r=t.nextSibling;t.remove(),t=r}}setConnected(t){this._$AM===void 0&&(this._$Cv=t,this._$AP?.(t))}},re=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(t,o,r,n,a){this.type=1,this._$AH=x,this._$AN=void 0,this.element=t,this.name=o,this._$AM=n,this.options=a,r.length>2||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<a.length-1;i++)g=oe(this,l[r+i],o,i),g===he&&(g=this._$AH[i]),s||=!pe(g)||g!==this._$AH[i],g===x?t=x:t!==x&&(t+=(g??"")+a[i+1]),this._$AH[i]=g}s&&!n&&this.j(t)}j(t){t===x?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,t??"")}},Ye=class extends re{constructor(){super(...arguments),this.type=3}j(t){this.element[this.name]=t===x?void 0:t}},je=class extends re{constructor(){super(...arguments),this.type=4}j(t){this.element.toggleAttribute(this.name,!!t&&t!==x)}},Ze=class extends re{constructor(t,o,r,n,a){super(t,o,r,n,a),this.type=5}_$AI(t,o=this){if((t=oe(this,t,o,0)??x)===he)return;let r=this._$AH,n=t===x&&r!==x||t.capture!==r.capture||t.once!==r.once||t.passive!==r.passive,a=t!==x&&(r===x||n);n&&this.element.removeEventListener(this.name,this,r),a&&this.element.addEventListener(this.name,this,t),this._$AH=t}handleEvent(t){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,t):this._$AH.handleEvent(t)}},qe=class{constructor(t,o,r){this.element=t,this.type=6,this._$AN=void 0,this._$AM=o,this.options=r}get _$AU(){return this._$AM._$AU}_$AI(t){oe(this,t)}};var $o=Ke.litHtmlPolyfillSupport;$o?.(ue,fe),(Ke.litHtmlVersions??=[]).push("3.3.1");var et=(e,t,o)=>{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);M<b&&(b=M),M>f&&(f=M),b!==+p.min&&(p.min=String(b)),f!==+p.max&&(p.max=String(f)),p.value=String(M)};return k`
5
+ <span>
6
+ <label>${e}</label>
7
+ ${o.weightState.weights[e].checkState!==void 0?k`<input type="checkbox" .checked=${o.weightState.weights[e].checkState} @input=${n} />`:""}
8
+ </span>
9
+ <div class="f f-c g-4">
10
+ <input
11
+ class="slider"
12
+ type="range"
13
+ .min=${Math.round(l.sliderMin*100)}
14
+ .max=${Math.round(l.sliderMax*100)}
15
+ .step=${Math.round(r.step*100)}
16
+ .value=${Math.round(l.value*100)}
17
+ ?disabled=${I(o,e)}
18
+ @input=${i}
19
+ @change=${a}
20
+ />
21
+ <input
22
+ class="value"
23
+ type="number"
24
+ .value=${+l.value}
25
+ .step=${r.step}
26
+ ?disabled=${I(o,e)}
27
+ @input=${g}
28
+ @change=${s}
29
+ />
30
+ </div>
31
+ `}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`
32
+ <header @mousedown=${n}>
33
+ <span>
34
+ <span class="lock ${t?"like":"unlike"}" style="${D(!o.weightState.xyzMode)}" @click=${i}>
35
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
36
+ <path
37
+ d="M21,10H19V7A7,7,0,0,0,5,7v3H3a1,1,0,0,0-1,1v9a4,4,0,0,0,4,4H18a4,4,0,0,0,4-4V11A1,1,0,0,0,21,10Zm-9,9.5A2.5,2.5,0,1,1,14.5,17,2.5,2.5,0,0,1,12,19.5ZM15,10H9V7a3,3,0,0,1,6,0v3Z"
38
+ ></path>
39
+ </svg>
40
+ </span>
41
+ <label class="name">${e}</label>
42
+ </span>
43
+ <div class="history" style="${z(r.length>1)}">
44
+ <a class="icon" @click=${l}>clear</a>
45
+ <div class="page">
46
+ <a class="icon" @click=${g} style="${D(o.historyIndex>0)}">&lt;</a>
47
+ <label class="page-label">${o.historyIndex+1}/${r.length}</label>
48
+ <a class="icon" @click=${u} style="${D(o.historyIndex<r.length-1)}">&gt;</a>
49
+ </div>
50
+ </div>
51
+ </header>
52
+ `}function co(){let e=L(),{blockGroups:t,weightState:o}=e,{selectedModelType:r,selectedLoraBlockType:n}=o,a=new Set(Q(r,n));return k`
53
+ <div class="lbw-column">
54
+ ${t.map(s=>k`
55
+ <div class="f col g-2" style="display: flex">
56
+ ${s.map(l=>k`
57
+ <div class="border p f g-2 col">
58
+ ${xe(l).map(i=>a.has(i)&&o.weights[i]?k` <div class="f g-2">${me(i,o.weights)}</div> `:"")}
59
+ </div>
60
+ `)}
61
+ </div>
62
+ `)}
63
+ </div>
64
+ `}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`
65
+ <div class="f g-2 f-end">
66
+ <select .value="${e.weightState.selectedModelType}" style="flex-grow: 1" ?disabled="${I(e)}" @change="${o}">
67
+ ${Object.entries(K).map(([a,s])=>k` <option value="${s}">${a}</option> `)}
68
+ </select>
69
+ <select
70
+ .value="${e.weightState.selectedLoraBlockType}"
71
+ style="flex-grow: 1"
72
+ ?disabled="${I(e)}"
73
+ @change="${t}"
74
+ >
75
+ ${Object.entries(J).map(([a,s])=>k` <option value="${s}">${a}</option> `)}
76
+ </select>
77
+ </div>
78
+ <div class="f g-2 f-end">
79
+ <select .value="${e.preset}" style="flex-grow: 1" ?disabled="${I(e)}" @change="${n}">
80
+ <option value=""></option>
81
+ ${Object.keys(e.lbwPresets).map(a=>k` <option value="${a}">${a}</option> `)}
82
+ </select>
83
+ <div class="p f g-2 f-end">
84
+ <div class="f g-2 f-end">
85
+ <input
86
+ id="wh:xyz"
87
+ value="XYZ"
88
+ type="checkbox"
89
+ .checked="${e.weightState.xyzMode}"
90
+ ?disabled="${I(e)}"
91
+ @change="${r}"
92
+ />
93
+ <label class="radio-label" for="wh:xyz">
94
+ ${it(e.weightState.selectedModelType,e.weightState.selectedLoraBlockType)}
95
+ </label>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ `}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`
100
+ <span class="metadata">
101
+ <span>
102
+ <span>Model</span>
103
+ <span>${e.metadataState?.modelType||"Unknown"}</span>
104
+ </span>
105
+ <span>
106
+ <span>Base</span>
107
+ <span>${e.metadataState?.baseModel||"Unknown"}</span>
108
+ </span>
109
+ <a class="icon mini svg" @click=${t}>
110
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 492 492">
111
+ <g>
112
+ <path
113
+ d="M484.08,296.216c-5.1-5.128-11.848-7.936-19.032-7.936H330.516c-14.828,0-26.86,12.036-26.86,26.868v22.796c0,7.168,2.784,14.064,7.884,19.16c5.092,5.088,11.82,8.052,18.976,8.052H366.1c-31.544,30.752-74.928,50.08-120.388,50.08c-71.832,0-136.028-45.596-159.744-113.344c-5.392-15.404-19.972-25.784-36.28-25.784c-4.316,0-8.592,0.708-12.7,2.144c-9.692,3.396-17.48,10.352-21.932,19.596c-4.456,9.248-5.04,19.684-1.648,29.368c34.496,98.54,127.692,164.74,232.144,164.74c64.132,0,123.448-23.948,169.572-67.656v25.22c0,14.836,12.384,27.108,27.224,27.108h22.792c14.84,0,26.86-12.272,26.86-27.108V315.24C492,308.056,489.2,301.304,484.08,296.216z"
114
+ ></path>
115
+ </g>
116
+ <g>
117
+ <path
118
+ d="M478.628,164.78C444.132,66.244,350.916,0.044,246.464,0.044c-64.136,0-123.464,23.952-169.588,67.66v-25.22c0-14.832-12.344-27.112-27.184-27.112H26.896C12.06,15.372,0,27.652,0,42.484V176.76c0,7.18,2.824,13.868,7.944,18.964c5.096,5.128,11.86,7.932,19.044,7.932l-0.08,0.06h134.604c14.84,0,26.832-12.028,26.832-26.86v-22.8c0-14.836-11.992-27.216-26.832-27.216h-35.576c31.544-30.752,74.932-50.076,120.392-50.076c71.832,0,136.024,45.596,159.74,113.348c5.392,15.404,19.968,25.78,36.28,25.78c4.32,0,8.588-0.704,12.7-2.144c9.696-3.396,17.48-10.348,21.932-19.596C481.432,184.9,482.02,174.472,478.628,164.78z"
119
+ ></path>
120
+ </g>
121
+ </svg>
122
+ </a>
123
+ </span>
124
+ `}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`
125
+ <div class="${l.join(" ")}" data-name="${e.modelName}">
126
+ <img class="preview" src="${e.thumbUrl}" style="height: ${opts.weight_helper_preview_height}px" />
127
+ <div class="action-row button-top" style="${D(!e.isDescriptionVisible)}">
128
+ ${e.modelName&&e.hasMetadata&&typeof extraNetworksRequestMetadata=="function"?k` <div class="metadata-btn card-btn" title="Show internal metadata" @click="${t}"></div> `:""}
129
+ ${e.modelName&&typeof extraNetworksEditUserMetadata=="function"?k` <div class="edit-btn card-btn" title="Edit metadata" @click="${o}"></div> `:""}
130
+ ${e.modelId?k` <div class="civitai-btn card-btn" title="Open civitai" @click="${r}"></div> `:""}
131
+ ${e.triggerWords.length>0||e.negativeTriggerWords.length>0?k`
132
+ <div
133
+ class="add-trigger-btn card-btn"
134
+ title="Add trigger words"
135
+ @click="${s}"
136
+ style="${z(e.isTagInsertButtonVisible)}"
137
+ ></div>
138
+ `:""}
139
+ </div>
140
+
141
+ ${e.description?k`
142
+ <div class="action-row button-bottom" style="${D(!e.isDescriptionVisible)}">
143
+ <div class="card-btn note-btn" @click="${n}"></div>
144
+ </div>
145
+ <textarea class="description" style="${D(e.isDescriptionVisible)}">
146
+ ${e.description}
147
+ </textarea
148
+ >
149
+ <div
150
+ class="card-btn description-close-btn"
151
+ style="${D(e.isDescriptionVisible)}"
152
+ @click="${a}"
153
+ ></div>
154
+ `:""}
155
+ </div>
156
+ `}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`
157
+ <div id="weight-helper" class="${e.uiState.isWaiting?"waiting":""}" style="${n}">
158
+ ${lo(e.loraName,r)} ${po()}
159
+ ${o.weights[m.TENC]&&k`<section class="border p">${me(m.TENC,o.weights)}</section>`}
160
+ ${[m.UNET,m.START,m.STOP].filter(a=>!!o.weights[a]).map(a=>k`<section
161
+ class="border p"
162
+ style="${z(!t.isMoreButtonVisible||o.weights[a].initCheckState||o.weights[a].initValue!==T(a).default)}"
163
+ >
164
+ ${me(a,o.weights)}
165
+ </section>`)}
166
+
167
+ <button style="${t.isMoreButtonVisible?"":"display: none"}" ?disabled="${I(e)}" @click="${()=>Xt()}">
168
+ show more options
169
+ </button>
170
+
171
+ <section class="border p">
172
+ <label>LBW</label>
173
+ <div class="f col g-4 w-fill">${go()} ${co()}</div>
174
+ </section>
175
+
176
+ ${e.previewState.modelName?ho(e.previewState):""}
177
+ </div>
178
+ `}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)});})();
179
+ /*! Bundled license information:
180
+
181
+ lit-html/lit-html.js:
182
+ (**
183
+ * @license
184
+ * Copyright 2017 Google LLC
185
+ * SPDX-License-Identifier: BSD-3-Clause
186
+ *)
187
+ */
188
+ //# sourceMappingURL=weight_helper.js.map
sd-webui-weight-helper-main/javascript/weight_helper.js.map ADDED
The diff for this file is too large to render. See raw diff
 
sd-webui-weight-helper-main/package.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "sd-webui-weight-helper",
3
+ "version": "1.3.3",
4
+ "description": "",
5
+ "scripts": {
6
+ "type-check": "tsc --noEmit",
7
+ "lint": "eslint src/**/*.ts",
8
+ "lint-fix": "eslint src/**/*.ts --fix",
9
+ "format": "prettier --write 'src/**/*.{ts,js,json,css,md}'",
10
+ "build": "node esbuild.config.mjs",
11
+ "build-min-map": "node esbuild.config.mjs --minify --sourcemap"
12
+ },
13
+ "keywords": [],
14
+ "author": "",
15
+ "devDependencies": {
16
+ "@eslint/js": "^9.34.0",
17
+ "@trivago/prettier-plugin-sort-imports": "^5.2.2",
18
+ "@typescript-eslint/eslint-plugin": "^8.41.0",
19
+ "@typescript-eslint/parser": "^8.41.0",
20
+ "esbuild": "^0.25.9",
21
+ "eslint": "^9.34.0",
22
+ "eslint-config-prettier": "^10.1.8",
23
+ "eslint-plugin-prettier": "^5.5.4",
24
+ "globals": "^16.3.0",
25
+ "prettier": "^3.6.2",
26
+ "typescript": "^5.9.2",
27
+ "typescript-eslint": "^8.41.0"
28
+ },
29
+ "dependencies": {
30
+ "lit-html": "^3.3.1"
31
+ }
32
+ }
sd-webui-weight-helper-main/scripts/__pycache__/weight_helper.cpython-310.pyc ADDED
Binary file (4.17 kB). View file
 
sd-webui-weight-helper-main/scripts/__pycache__/weight_helper.cpython-313.pyc ADDED
Binary file (6.87 kB). View file
 
sd-webui-weight-helper-main/scripts/__pycache__/wh_analyzer.cpython-310.pyc ADDED
Binary file (6.53 kB). View file
 
sd-webui-weight-helper-main/scripts/__pycache__/wh_analyzer.cpython-313.pyc ADDED
Binary file (11.3 kB). View file
 
sd-webui-weight-helper-main/scripts/__pycache__/wh_api.cpython-310.pyc ADDED
Binary file (4.23 kB). View file
 
sd-webui-weight-helper-main/scripts/__pycache__/wh_api.cpython-313.pyc ADDED
Binary file (7.43 kB). View file
 
sd-webui-weight-helper-main/scripts/__pycache__/wh_const.cpython-310.pyc ADDED
Binary file (502 Bytes). View file
 
sd-webui-weight-helper-main/scripts/__pycache__/wh_const.cpython-313.pyc ADDED
Binary file (570 Bytes). View file
 
sd-webui-weight-helper-main/scripts/__pycache__/wh_env.cpython-310.pyc ADDED
Binary file (912 Bytes). View file
 
sd-webui-weight-helper-main/scripts/__pycache__/wh_env.cpython-313.pyc ADDED
Binary file (1.56 kB). View file
 
sd-webui-weight-helper-main/scripts/__pycache__/wh_utils.cpython-310.pyc ADDED
Binary file (2.05 kB). View file
 
sd-webui-weight-helper-main/scripts/__pycache__/wh_utils.cpython-313.pyc ADDED
Binary file (3.64 kB). View file
 
sd-webui-weight-helper-main/scripts/weight_helper.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ import gradio as gr
5
+ from fastapi import FastAPI
6
+ from modules import script_callbacks, shared
7
+
8
+ _THIS_DIR = os.path.dirname(os.path.abspath(__file__))
9
+ if _THIS_DIR not in sys.path:
10
+ sys.path.insert(0, _THIS_DIR)
11
+
12
+ import wh_api as api
13
+ import wh_const as const
14
+
15
+
16
+ def on_app_started(_: gr.Blocks, app: FastAPI):
17
+ @app.post(const.API_PREFIX + "/get_metadata")
18
+ async def _(key: str, force: bool = False):
19
+ return api.get_metadata(key, force)
20
+
21
+ @app.post(const.API_PREFIX + "/get_preview_info")
22
+ async def _(key: str):
23
+ return api.get_preview_info(key)
24
+
25
+
26
+ def on_ui_settings():
27
+ """
28
+ Set up the UI settings for the weight helper by adding options to the shared configuration.
29
+ """
30
+ section = ('weight_helper', 'Weight Helper')
31
+
32
+ shared.options_templates.update(shared.options_section(section, {
33
+ 'weight_helper_enabled': shared.OptionInfo(True, 'Enabled'),
34
+ 'weight_helper_context_menu_scale': shared.OptionInfo(0.8, "Context Menu Scale", gr.Number),
35
+ 'weight_helper_using_execCommand': shared.OptionInfo(True, "Use the deprecated execCommand function to replace text.").info(
36
+ "You can use 'undo' to revert the text to its previous state, "
37
+ "but it will no longer be updated in real-time."),
38
+
39
+ "weight_helper_slider_length": shared.OptionInfo(160, "Slider Length (px)", gr.Number),
40
+
41
+ "weight_helper_TEnc_min": shared.OptionInfo(0, "TEnc Min Value", gr.Number),
42
+ "weight_helper_TEnc_max": shared.OptionInfo(1, "TEnc Max Value", gr.Number),
43
+ "weight_helper_TEnc_step": shared.OptionInfo(0.05, "TEnc Step", gr.Number),
44
+
45
+ "weight_helper_UNet_min": shared.OptionInfo(0, "UNet Min Value", gr.Number),
46
+ "weight_helper_UNet_max": shared.OptionInfo(1, "UNet Max Value", gr.Number),
47
+ "weight_helper_UNet_step": shared.OptionInfo(0.05, "UNet Step", gr.Number),
48
+
49
+ "weight_helper_LBW_min": shared.OptionInfo(0, "LBW (LoRA Block Weight) Min Value", gr.Number),
50
+ "weight_helper_LBW_max": shared.OptionInfo(1, "LBW (LoRA Block Weight) Max Value", gr.Number),
51
+ "weight_helper_LBW_step": shared.OptionInfo(0.05, "LBW (LoRA Block Weight) Step", gr.Number),
52
+
53
+ 'weight_helper_show_preview': shared.OptionInfo(True, 'Show Preview'),
54
+ "weight_helper_preview_height": shared.OptionInfo(400, "Preview Height (px)", gr.Number),
55
+ "weight_helper_preview_position": shared.OptionInfo("Top Right", "Preview Position", gr.Radio, {
56
+ "choices": ["Top Right", "Bottom Right", "Top Left", "Bottom Left"]
57
+ }),
58
+
59
+ 'weight_helper_parse_lora_blocks': shared.OptionInfo(True, '[experimental] Parse LoRA file to get block information'),
60
+
61
+ "weight_helper_LBW_SD_lora_block_points": shared.OptionInfo(
62
+ "BASE, IN01-IN04, IN05-IN08, M00, OUT03-OUT06, OUT07-OUT11",
63
+ "Advanced option - LoRA-LierLa block points")
64
+ .info("default: BASE, IN01-IN04, IN05-IN08, M00, OUT03-OUT06, OUT07-OUT11"),
65
+ "weight_helper_LBW_SD_lycoris_block_points": shared.OptionInfo(
66
+ "BASE, IN00-IN05, IN06-IN11, M00, OUT00-OUT05, OUT06-OUT11",
67
+ "Advanced option - LyCORIS or LoRA-C3Lier block points")
68
+ .info("default: BASE, IN00-IN05, IN06-IN11, M00, OUT00-OUT05, OUT06-OUT11"),
69
+
70
+ "weight_helper_LBW_SDXL_lora_block_points": shared.OptionInfo(
71
+ "BASE, IN04-IN08, M00, OUT00-OUT05",
72
+ "Advanced option - SDXL LoRA-LierLa block points")
73
+ .info("default: BASE, IN04-IN08, M00, OUT00-OUT05"),
74
+ "weight_helper_LBW_SDXL_lycoris_block_points": shared.OptionInfo(
75
+ "BASE, IN00-IN03, IN04-IN08, M00, OUT00-OUT03, OUT04-OUT08",
76
+ "Advanced option - SDXL LyCORIS or LoRA-C3Lier block points")
77
+ .info("default: BASE, IN00-IN03, IN04-IN08, M00, OUT00-OUT03, OUT04-OUT08"),
78
+
79
+ "weight_helper_LBW_Flux_lora_block_points": shared.OptionInfo(
80
+ "CLIP, T5, IN, OUT, D00-D18, S00-S18, S19-S37",
81
+ "Advanced option - Flux LoRA-LierLa block points")
82
+ .info("default: CLIP, T5, IN, OUT, D00-D18, S00-S18, S19-S37"),
83
+ "weight_helper_LBW_Flux_lycoris_block_points": shared.OptionInfo(
84
+ "CLIP, T5, IN, OUT, D00-D18, S00-S18, S19-S37",
85
+ "Advanced option - Flux LyCORIS or LoRA-C3Lier block points")
86
+ .info("default: CLIP, T5, IN, OUT, D00-D18, S00-S18, S19-S37"),
87
+ }))
88
+
89
+
90
+ script_callbacks.on_ui_settings(on_ui_settings)
91
+ script_callbacks.on_app_started(on_app_started)
sd-webui-weight-helper-main/scripts/wh_analyzer.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pickle
3
+ import re
4
+ import sys
5
+ from typing import Optional
6
+
7
+ _THIS_DIR = os.path.dirname(os.path.abspath(__file__))
8
+ if _THIS_DIR not in sys.path:
9
+ sys.path.insert(0, _THIS_DIR)
10
+
11
+ import wh_const as const
12
+ import wh_env as env
13
+
14
+
15
+ def get_model_type(lora) -> Optional[str]:
16
+ if env.ui_type == const.UI_TYPE.A1111:
17
+ sd_version: Optional[str] = lora.sd_version.name
18
+ if sd_version and env.network:
19
+ if sd_version == env.network.SdVersion.SD1.name or sd_version == env.network.SdVersion.SD2.name:
20
+ return "SD"
21
+ elif sd_version == env.network.SdVersion.SDXL.name:
22
+ return "SDXL"
23
+ return sd_version
24
+ elif env.ui_type == const.UI_TYPE.NEW_FORGE:
25
+ base_model_version: Optional[str] = lora.metadata.get("ss_base_model_version")
26
+ if base_model_version:
27
+ base_model_version = base_model_version.lower()
28
+ if "sd_" in base_model_version:
29
+ return "SD"
30
+ if "sd3" in base_model_version:
31
+ return "SD"
32
+ elif "sdxl" in base_model_version:
33
+ return "SDXL"
34
+ elif "flux" in base_model_version:
35
+ return "Flux"
36
+ return None
37
+ return None
38
+
39
+
40
+ def get_algorithm(metadata, ss_network_args) -> Optional[str]:
41
+ ss_network_module = metadata.get("ss_network_module")
42
+ if not ss_network_module or ss_network_module == "Unknown":
43
+ return None
44
+
45
+ conv_dim = float(ss_network_args.get("conv_dim", "-1"))
46
+ conv_alpha = float(ss_network_args.get("conv_alpha", "-1"))
47
+ algo = ss_network_args.get("algo", "").lower()
48
+ unit = ss_network_args.get("unit", "").lower()
49
+ dora_wd = ss_network_args.get("dora_wd", False)
50
+
51
+ if ss_network_module.find("locon.locon_kohya") >= 0:
52
+ return "LoCon"
53
+
54
+ elif ss_network_module.find("lycoris.kohya") >= 0:
55
+ algoName = ""
56
+ if dora_wd:
57
+ algoName = "(DoRA)"
58
+ elif algo == "lora":
59
+ algoName += "(LoCon)"
60
+ elif algo == "locon":
61
+ if unit:
62
+ algoName += "(DyLoRA)"
63
+ else:
64
+ algoName += "(LoCon)"
65
+ elif algo == "loha":
66
+ algoName += "(LoHa)"
67
+ elif algo == "lokr":
68
+ algoName += "(Lokr)"
69
+ elif algo == "ia3":
70
+ algoName += "(IA3)"
71
+ elif algo == "full":
72
+ algoName += "(Full)"
73
+ elif algo == "glora":
74
+ algoName += "(GLoRA)"
75
+
76
+ return f"LyCORIS{algoName}"
77
+
78
+ elif ss_network_module.find("networks.dylora") >= 0:
79
+ if algo == "dylora" and unit:
80
+ if conv_dim > 0 or conv_alpha > 0:
81
+ return "DyLoRA(C3Lier)"
82
+ return "DyLoRA(LierLa)"
83
+
84
+ elif conv_dim > 0 or conv_alpha > 0:
85
+ return "LoRA(C3Lier)"
86
+
87
+ else:
88
+ return "LoRA(LierLa)"
89
+
90
+
91
+ def get_blocks(lora_on_disk, force):
92
+ baseKeys = __get_lora_state_keys(lora_on_disk.filename, force)
93
+
94
+ def formatForDownMidUp(key):
95
+ blocks = ["BASE"]
96
+ for key in keys:
97
+ k, v = key.split("_", 1)
98
+ if k == "down":
99
+ k = "IN"
100
+ v1, v2 = v.split("_")
101
+ val = int(v1) * 3 + int(v2) + 1
102
+ elif k == "mid":
103
+ blocks.append("M00")
104
+ continue
105
+ elif k == "up":
106
+ k = "OUT"
107
+ v1, v2 = v.split("_")
108
+ val = int(v1) * 3 + int(v2)
109
+ blocks.append(f'{k}{int(val):02}')
110
+ return sorted(blocks)
111
+
112
+ def formatForInputMiddleOutput(key):
113
+ blocks = ["BASE"]
114
+ for key in keys:
115
+ k, v = key.split("_")
116
+ if k == "input":
117
+ k = "IN"
118
+ elif k == "middle":
119
+ blocks.append("M00")
120
+ continue
121
+ elif k == "output":
122
+ k = "OUT"
123
+ blocks.append(f'{k}{int(v):02}')
124
+ return sorted(blocks)
125
+
126
+ def formatForFlux(key):
127
+ blocks = ["CLIP", "T5", "IN", "OUT"]
128
+ for key in keys:
129
+ k, v = key.split("_")
130
+ v = int(v)
131
+ if k == "double":
132
+ k = "D"
133
+ if k == "single":
134
+ k = "S"
135
+ blocks.append(f'{k}{v:02}')
136
+ return sorted(blocks)
137
+
138
+ keys = [k for k in baseKeys if k.find("lora_unet") == 0]
139
+ if len(keys) > 0:
140
+ keys_tmp = [k for k in keys if re.match("^lora_unet_(down|mid|up)_", k)]
141
+ if len(keys_tmp) > 0:
142
+ keys = set(["_".join([k.split("_")[2], k.split("_")[4] + "_" + k.split("_")[6]]) for k in keys_tmp if "transformer_blocks" in k])
143
+ return formatForDownMidUp(keys)
144
+
145
+ keys_tmp = [k for k in keys if re.match("^lora_unet_(input|middle|output)_", k)]
146
+ if len(keys_tmp) > 0:
147
+ keys = set(["_".join([k.split("_")[2], k.split("_")[4]]) for k in keys_tmp if "transformer_blocks" in k])
148
+ return formatForInputMiddleOutput(keys)
149
+
150
+ keys_tmp = [k for k in keys if re.match("^lora_unet_(single|double)_", k)]
151
+ if len(keys_tmp) > 0:
152
+ keys = set(["_".join([k.split("_")[2], k.split("_")[4]]) for k in keys_tmp])
153
+ return formatForFlux(keys)
154
+
155
+ keys_tmp = [k for k in keys if re.match("^lora_unet_joint_blocks_", k)]
156
+ if len(keys_tmp) > 0:
157
+ # TODO SD3? 38 blocks
158
+ return None
159
+
160
+ keys = [k for k in baseKeys if "transformer_blocks" in k and "attentions" in k]
161
+ if len(keys) > 0:
162
+ # TODO unknown
163
+ keys = set([".".join(k.split(".")[2:6]) for k in keys])
164
+ in_blocks = set([f"{k.split('.')[1]}_{k.split('.')[3]}" for k in keys if "down_block" in k])
165
+ mid_block = set([k.split(".")[2] for k in keys if "mid_block" in k])
166
+ out_blocks = set([f"{k.split('.')[1]}_{k.split('.')[3]}" for k in keys if "up_block" in k])
167
+ keys = list(in_blocks) + list(mid_block) + list(out_blocks)
168
+ return None
169
+
170
+ keys = [k for k in baseKeys if "transformer_blocks" in k and k.find("transformer") == 0]
171
+ if len(keys) > 0:
172
+ keys = set([".".join(k.split(".")[1:3]) for k in keys])
173
+ keys = [k.replace("single_transformer_blocks.", "single_") for k in keys]
174
+ keys = [k.replace("transformer_blocks.", "double_") for k in keys]
175
+ return formatForFlux(keys)
176
+
177
+ return None
178
+
179
+
180
+ def __get_lora_state_keys(filename, force):
181
+ cache_path = f"{os.path.splitext(filename)[0]}.whcache"
182
+ if not force and os.path.exists(cache_path):
183
+ try:
184
+ with open(cache_path, "rb") as f:
185
+ return pickle.load(f)
186
+ except Exception:
187
+ pass
188
+
189
+ lora_sd = env.network_lora.load_lora_state_dict(filename)
190
+ keys = list(lora_sd.keys())
191
+ try:
192
+ with open(cache_path, "wb") as f:
193
+ pickle.dump(keys, f)
194
+ except Exception:
195
+ pass
196
+ return keys
sd-webui-weight-helper-main/scripts/wh_api.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import ast
3
+ import json
4
+ import os
5
+ import sys
6
+ from typing import Any, Optional
7
+
8
+ from modules import shared
9
+
10
+ _THIS_DIR = os.path.dirname(os.path.abspath(__file__))
11
+ if _THIS_DIR not in sys.path:
12
+ sys.path.insert(0, _THIS_DIR)
13
+
14
+ import wh_analyzer as analyzer
15
+ import wh_const as const
16
+ import wh_utils as utils
17
+
18
+
19
+ def get_metadata(key, force) -> dict[str, Optional[str]]:
20
+ ret: dict[str, Optional[str]] = {
21
+ "modelType": None,
22
+ "algorithm": None,
23
+ "baseModel": None,
24
+ "usingBlocks": None
25
+ }
26
+
27
+ lora_on_disk = utils.get_lora_on_disk(key)
28
+ if not lora_on_disk:
29
+ return ret
30
+
31
+ ret["modelType"] = analyzer.get_model_type(lora_on_disk)
32
+
33
+ metadata = lora_on_disk.metadata
34
+ ss_network_args = __get_network_args(lora_on_disk)
35
+ ret["algorithm"] = analyzer.get_algorithm(metadata, ss_network_args)
36
+
37
+ model_path, _ = os.path.splitext(lora_on_disk.filename)
38
+ civitai_info = __find_civitai_info(model_path)
39
+ ret["baseModel"] = civitai_info.get("baseModel", None)
40
+
41
+ if shared.opts.weight_helper_parse_lora_blocks:
42
+ try:
43
+ ret["usingBlocks"] = analyzer.get_blocks(lora_on_disk, force)
44
+ except Exception:
45
+ pass
46
+ return ret
47
+
48
+
49
+ def get_preview_info(key):
50
+ ret: dict[str, Optional[bool | str | list[str]]] = {
51
+ "modelId": None,
52
+ "triggerWords": [],
53
+ "negativeTriggerWords": [],
54
+ "modelName": None,
55
+ "thumbUrl": None,
56
+ "hasMetadata": False,
57
+ "description": None,
58
+ }
59
+
60
+ lora_on_disk = utils.get_lora_on_disk(key)
61
+ if lora_on_disk:
62
+ model_path, _ = os.path.splitext(lora_on_disk.filename)
63
+ ret["modelName"] = lora_on_disk.name
64
+ ret["thumbUrl"] = __find_preview(model_path)
65
+ ret["hasMetadata"] = __find_metadata(lora_on_disk)
66
+ ret["description"] = __find_description(model_path)
67
+
68
+ trigger_words = []
69
+ civitai_info = __find_civitai_info(model_path)
70
+ ret["modelId"] = civitai_info.get("modelId", None)
71
+ trigger_words = civitai_info.get("trainedWords", [])
72
+ trigger_words = ",".join(trigger_words).split(",")
73
+ ret["triggerWords"] = [w.strip() for w in trigger_words if w]
74
+
75
+ lora_json = __get_lora_json(model_path)
76
+
77
+ activation_text = lora_json.get("activation text", "")
78
+ if activation_text:
79
+ activation_text = activation_text.split(",")
80
+ activation_text = [w.strip() for w in activation_text if w]
81
+ ret["triggerWords"] = activation_text
82
+
83
+ negative_text = lora_json.get("negative text", "")
84
+ if negative_text:
85
+ negative_text = negative_text.split(",")
86
+ negative_text = [w.strip() for w in negative_text if w]
87
+ ret["negativeTriggerWords"] = negative_text
88
+
89
+ json_description = lora_json.get("description", None)
90
+ if json_description:
91
+ ret["description"] = json_description
92
+
93
+ return ret
94
+
95
+
96
+ def __get_network_args(lora) -> dict[str, Any]:
97
+ metadata = lora.metadata
98
+ ss_network_args = metadata.get("ss_network_args", {})
99
+ if type(ss_network_args) is str:
100
+ try:
101
+ ss_network_args = ast.literal_eval(ss_network_args)
102
+ except Exception:
103
+ ss_network_args = {}
104
+ return ss_network_args
105
+
106
+
107
+ def __get_lora_json(path) -> dict[str, Any]:
108
+ if path:
109
+ lora_json = f"{path}.json"
110
+ if os.path.exists(lora_json):
111
+ try:
112
+ with open(lora_json, "r", encoding="utf-8", errors="replace") as f:
113
+ return json.load(f)
114
+ except OSError:
115
+ pass
116
+ return {}
117
+
118
+
119
+ def __find_preview(path) -> str:
120
+ if path:
121
+ potential_files = sum([[f"{path}.{ext}", f"{path}.preview.{ext}"] for ext in const.ALLOWED_PREVIEW_EXTENSIONS], [])
122
+ for file in potential_files:
123
+ if os.path.exists(file):
124
+ dpi_scale = 1.5
125
+ return utils.resize_to_base64(file, int(shared.opts.weight_helper_preview_height * dpi_scale))
126
+ return "./file=html/card-no-preview.png"
127
+
128
+
129
+ def __find_metadata(lora) -> bool:
130
+ return len(lora.metadata) > 0 if lora else False
131
+
132
+
133
+ def __find_description(path) -> Optional[str]:
134
+ if path:
135
+ for file in [f"{path}.txt", f"{path}.description.txt"]:
136
+ if os.path.exists(file):
137
+ try:
138
+ with open(file, "r", encoding="utf-8", errors="replace") as f:
139
+ return f.read()
140
+ except OSError:
141
+ pass
142
+ return None
143
+
144
+
145
+ def __find_civitai_info(path) -> dict[str, Any]:
146
+ if path:
147
+ civitai_info = f"{path}.civitai.info"
148
+ if os.path.exists(civitai_info):
149
+ try:
150
+ with open(civitai_info, "r", encoding="utf-8", errors="replace") as f:
151
+ return json.load(f)
152
+ except OSError:
153
+ pass
154
+ return {}
sd-webui-weight-helper-main/scripts/wh_const.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import enum
2
+
3
+
4
+ API_PREFIX = "/whapi/v1"
5
+ ALLOWED_PREVIEW_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "gif"]
6
+
7
+
8
+ class UI_TYPE(enum.Enum):
9
+ Unknown = 1
10
+ A1111 = 2
11
+ NEW_FORGE = 3
sd-webui-weight-helper-main/scripts/wh_env.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import importlib
3
+ import os
4
+ import sys
5
+
6
+ _THIS_DIR = os.path.dirname(os.path.abspath(__file__))
7
+ if _THIS_DIR not in sys.path:
8
+ sys.path.insert(0, _THIS_DIR)
9
+
10
+ import wh_const as const
11
+
12
+ network = None
13
+ network_lora = None
14
+ ui_type = None
15
+
16
+ try:
17
+ from modules_forge import forge_version # noqa: F401
18
+ ui_type = const.UI_TYPE.NEW_FORGE
19
+ except ImportError:
20
+ ui_type = const.UI_TYPE.A1111
21
+
22
+ try:
23
+ network = importlib.import_module("extensions-builtin.sd_forge_lora.network")
24
+ network_lora = importlib.import_module("extensions-builtin.sd_forge_lora.ui_extra_networks_lora").networks
25
+ except Exception:
26
+ pass
27
+
28
+ if network_lora is None:
29
+ try:
30
+ network = importlib.import_module("extensions-builtin.Lora.network")
31
+ network_lora = importlib.import_module("extensions-builtin.Lora.ui_extra_networks_lora").networks
32
+ except Exception:
33
+ pass
34
+
35
+ if network_lora is None:
36
+ ui_type = const.UI_TYPE.Unknown
sd-webui-weight-helper-main/scripts/wh_utils.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import os
3
+ import sys
4
+ import urllib
5
+
6
+ import cv2
7
+ import numpy as np
8
+
9
+ _THIS_DIR = os.path.dirname(os.path.abspath(__file__))
10
+ if _THIS_DIR not in sys.path:
11
+ sys.path.insert(0, _THIS_DIR)
12
+
13
+ import wh_env as env
14
+
15
+
16
+ def get_lora_on_disk(key):
17
+ if env.network_lora:
18
+ lora_on_disk = env.network_lora.available_network_aliases.get(key)
19
+ if not lora_on_disk:
20
+ # sdnext
21
+ lora_on_disk = next((v for v in env.network_lora.available_network_aliases.values() if v.alias == key), None)
22
+ return lora_on_disk
23
+ return None
24
+
25
+
26
+ def imread_unicode(path):
27
+ try:
28
+ with open(path, 'rb') as f:
29
+ data = np.frombuffer(f.read(), np.uint8)
30
+ img = cv2.imdecode(data, cv2.IMREAD_UNCHANGED)
31
+ return img
32
+ except Exception as e:
33
+ print(f"imread_unicode error: {e}")
34
+ return None
35
+
36
+
37
+ def resize_to_base64(input_path, target_height):
38
+ output_format = '.webp'
39
+ quality = 85
40
+
41
+ img = imread_unicode(input_path)
42
+
43
+ h, w = img.shape[:2]
44
+ if h < target_height:
45
+ quoted_filename = urllib.parse.quote(input_path.replace('\\', '/'))
46
+ return f"./sd_extra_networks/thumb?filename={quoted_filename}"
47
+
48
+ scale = target_height / h
49
+ target_size = (int(w * scale), target_height)
50
+
51
+ resized = cv2.resize(img, target_size, interpolation=cv2.INTER_AREA)
52
+
53
+ sharpen_kernel = np.array([
54
+ [0, -0.2, 0],
55
+ [-0.2, 1.8, -0.2],
56
+ [0, -0.2, 0]
57
+ ])
58
+ sharpened = cv2.filter2D(resized, -1, sharpen_kernel)
59
+ success, buffer = cv2.imencode(output_format, sharpened, [cv2.IMWRITE_WEBP_QUALITY, quality])
60
+ if not success:
61
+ quoted_filename = urllib.parse.quote(input_path.replace('\\', '/'))
62
+ return f"./sd_extra_networks/thumb?filename={quoted_filename}"
63
+
64
+ base64_str = base64.b64encode(buffer).decode('utf-8')
65
+ return f'data:image/webp;base64,{base64_str}'
sd-webui-weight-helper-main/src/components/sections/common-section.ts ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as configManager from '@/shared/manager/config-manager';
2
+ import * as globalState from '@/shared/state/global-weight-helper-state';
3
+ import { WeightControllerTypes } from '@/shared/types/lora-types';
4
+ import { disabled } from '@/shared/utils/helper-utils';
5
+
6
+ import { TemplateResult, html } from 'lit-html';
7
+
8
+ /**
9
+ * WeightController component provides UI controls for adjusting weight values.
10
+ * Displays a label, optional checkbox, slider, and number input for a weight parameter.
11
+ * @param label - The identifier for the weight block.
12
+ * @param values - The record of all weight values.
13
+ * @returns The WeightController template.
14
+ */
15
+ export function createWeightController(label: string, values: Record<string, globalState.WeightControlState>): TemplateResult {
16
+ const state = globalState.getGlobalState();
17
+ const config = configManager.getWeightControllerConfig(label) ?? configManager.getWeightControllerConfig(WeightControllerTypes.LBW);
18
+
19
+ /**
20
+ * Handles the change event for the weight checkbox.
21
+ * Updates the check state for the weight block.
22
+ * @param e - The input change event.
23
+ */
24
+ const handleChangeWeightCheck = (e: Event) => {
25
+ const target = e.target as HTMLInputElement;
26
+ globalState.setWeight({
27
+ block: label,
28
+ value: state.weightState.weights[label].value,
29
+ checkState: target.checked,
30
+ });
31
+ };
32
+
33
+ /**
34
+ * Handles the change event for the weight slider.
35
+ * Updates the value and check state for the weight block.
36
+ * @param e - The input change event.
37
+ */
38
+ const handleChangeWeightSlider = (e: Event) => {
39
+ const target = e.target as HTMLInputElement;
40
+ const value = +target.value / 100;
41
+ let checkState = state.weightState.weights[label].checkState;
42
+ if (checkState !== undefined) {
43
+ checkState = true;
44
+ }
45
+ globalState.setWeight({
46
+ block: label,
47
+ value: value,
48
+ checkState: checkState,
49
+ });
50
+ };
51
+
52
+ /**
53
+ * Handles the change event for the weight number input.
54
+ * Updates the value and check state for the weight block.
55
+ * @param e - The input change event.
56
+ */
57
+ const handleChangeWeightNumber = (e: Event) => {
58
+ const target = e.target as HTMLInputElement;
59
+ const value = +target.value;
60
+ let checkState = state.weightState.weights[label].checkState;
61
+ if (checkState !== undefined) {
62
+ checkState = true;
63
+ }
64
+ globalState.setWeight({
65
+ block: label,
66
+ value: value,
67
+ checkState: checkState,
68
+ });
69
+ };
70
+
71
+ const weightController = values[label];
72
+
73
+ /**
74
+ * Live-sync handler for slider @input (does not commit state yet).
75
+ * Mirrors the slider value (scaled back to original 0-? range) into the numeric input
76
+ * so the user sees real-time feedback while dragging before the @change event fires.
77
+ */
78
+ const immediateSliderInput = (e: Event) => {
79
+ const target = e.target as HTMLInputElement;
80
+ const raw = +target.value / 100;
81
+ const wrapper = target.parentElement as HTMLElement;
82
+ const numberInput = wrapper.querySelector('input.value') as HTMLInputElement | null;
83
+ if (numberInput) numberInput.value = raw.toString();
84
+ };
85
+ /**
86
+ * Live-sync handler for number @input.
87
+ * Expands the slider range first when user enters a value outside current min/max
88
+ * (matching the logic used in setWeight) and then updates the slider thumb position.
89
+ * State commit is deferred to the @change handler.
90
+ */
91
+ const immediateNumberInput = (e: Event) => {
92
+ const target = e.target as HTMLInputElement;
93
+ const wrapper = target.parentElement as HTMLElement;
94
+ const slider = wrapper.querySelector('input.slider') as HTMLInputElement | null;
95
+ if (!slider) return;
96
+ const raw = +target.value;
97
+ if (isNaN(raw)) return; // Unconfirmed / empty input etc.
98
+ let sliderMin = +slider.min; // Scaled (0-100x) current min
99
+ let sliderMax = +slider.max;
100
+ const scaled = Math.round(raw * 100);
101
+ // Expand range first if the input value is outside (same policy as setWeight)
102
+ if (scaled < sliderMin) sliderMin = scaled;
103
+ if (scaled > sliderMax) sliderMax = scaled;
104
+ if (sliderMin !== +slider.min) slider.min = String(sliderMin);
105
+ if (sliderMax !== +slider.max) slider.max = String(sliderMax);
106
+ // Finally assign the value (no clamp needed after expansion)
107
+ slider.value = String(scaled);
108
+ };
109
+ return html`
110
+ <span>
111
+ <label>${label}</label>
112
+ ${state.weightState.weights[label].checkState !== undefined
113
+ ? html`<input type="checkbox" .checked=${state.weightState.weights[label].checkState} @input=${handleChangeWeightCheck} />`
114
+ : ''}
115
+ </span>
116
+ <div class="f f-c g-4">
117
+ <input
118
+ class="slider"
119
+ type="range"
120
+ .min=${Math.round(weightController.sliderMin * 100)}
121
+ .max=${Math.round(weightController.sliderMax * 100)}
122
+ .step=${Math.round(config.step * 100)}
123
+ .value=${Math.round(weightController.value * 100)}
124
+ ?disabled=${disabled(state, label)}
125
+ @input=${immediateSliderInput}
126
+ @change=${handleChangeWeightSlider}
127
+ />
128
+ <input
129
+ class="value"
130
+ type="number"
131
+ .value=${+weightController.value}
132
+ .step=${config.step}
133
+ ?disabled=${disabled(state, label)}
134
+ @input=${immediateNumberInput}
135
+ @change=${handleChangeWeightNumber}
136
+ />
137
+ </div>
138
+ `;
139
+ }
sd-webui-weight-helper-main/src/components/sections/header-section.ts ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as historyManager from '@/shared/manager/history-manager';
2
+ import * as globalState from '@/shared/state/global-weight-helper-state';
3
+ import { getPreset } from '@/shared/utils/common-utils';
4
+ import { getDisplayStyle, getVisibilityStyle } from '@/shared/utils/helper-utils';
5
+ import { createLoraParamsState } from '@/shared/utils/state-utils';
6
+
7
+ import { TemplateResult, html } from 'lit-html';
8
+
9
+ // ドラッグ状態を管理するグローバル変数
10
+ let isDragging = false;
11
+ let offset = { x: 0, y: 0 };
12
+
13
+ /**
14
+ * HeaderSection component displays the title bar of the Weight Helper.
15
+ * Provides LoRA name display, lock toggle, and history navigation controls.
16
+ * @param loraName - The name of the LoRA model.
17
+ * @param lock - The current lock status.
18
+ * @returns The HeaderSection template.
19
+ */
20
+ export function createHeaderSection(loraName: string, lock: boolean): TemplateResult {
21
+ const state = globalState.getGlobalState();
22
+ const histories = historyManager.getHistories(state.loraName);
23
+
24
+ /**
25
+ * Handles the mouse down event for dragging the header.
26
+ * @param e - The mouse event.
27
+ */
28
+ const handleMouseDown = (e: MouseEvent) => {
29
+ isDragging = true;
30
+ const el = document.getElementById('weight-helper');
31
+ if (!el) return;
32
+ const rect = el.getBoundingClientRect();
33
+ offset = {
34
+ x: e.clientX - rect.left,
35
+ y: e.clientY - rect.top,
36
+ };
37
+ document.addEventListener('mousemove', handleMouseMove);
38
+ document.addEventListener('mouseup', handleMouseUp);
39
+ };
40
+
41
+ /**
42
+ * Handles the mouse move event for dragging the header.
43
+ * Updates the position of the header based on mouse movement.
44
+ * @param e - The mouse event.
45
+ */
46
+ const handleMouseMove = (e: MouseEvent) => {
47
+ if (!isDragging) return;
48
+
49
+ const x = e.clientX - offset.x + window.scrollX;
50
+ const y = e.clientY - offset.y + window.scrollY;
51
+
52
+ globalState.setPosition(y, x);
53
+ };
54
+
55
+ /**
56
+ * Handles the mouse up event to stop dragging the header.
57
+ * Removes the event listeners for mouse move and mouse up.
58
+ */
59
+ const handleMouseUp = () => {
60
+ isDragging = false;
61
+ document.removeEventListener('mousemove', handleMouseMove);
62
+ document.removeEventListener('mouseup', handleMouseUp);
63
+ };
64
+
65
+ /**
66
+ * Handles the clear history button click event.
67
+ * Clears the history of the Weight Helper.
68
+ */
69
+ const handleClearHistory = () => {
70
+ globalState.clearHistory();
71
+ };
72
+
73
+ /**
74
+ * Handles the lock toggle button click event.
75
+ * Toggles the lock status for the current LoRA and loraParams.
76
+ */
77
+ const handleToggleLockStatus = () => {
78
+ globalState.toggleLockStatus();
79
+ };
80
+
81
+ /**
82
+ * Handles the previous history button click event.
83
+ * Navigates to the previous history state.
84
+ */
85
+ const handlePrevHistory = () => {
86
+ const newIndex = Math.max(0, state.historyIndex - 1);
87
+ const history = histories[newIndex];
88
+ const loraParams = createLoraParamsState(history.loraParams, history.selectedModelType, history.selectedLoraBlockType);
89
+ const locked = historyManager.isLocked(state.loraName, history.loraParams);
90
+ const preset = getPreset(history.selectedModelType, history.selectedLoraBlockType, state.lbwPresets, loraParams.weights);
91
+ globalState.setHistory({ historyIndex: newIndex, weightState: loraParams, locked: locked, preset: preset });
92
+ };
93
+
94
+ /**
95
+ * Handles the next history button click event.
96
+ * Navigates to the next history state.
97
+ */
98
+ const handleNextHistory = () => {
99
+ const newIndex = Math.min(histories.length - 1, state.historyIndex + 1);
100
+ const history = histories[newIndex];
101
+ const loraParams = createLoraParamsState(history.loraParams, history.selectedModelType, history.selectedLoraBlockType);
102
+ const locked = historyManager.isLocked(state.loraName, history.loraParams);
103
+ const preset = getPreset(history.selectedModelType, history.selectedLoraBlockType, state.lbwPresets, loraParams.weights);
104
+ globalState.setHistory({ historyIndex: newIndex, weightState: loraParams, locked: locked, preset: preset });
105
+ };
106
+
107
+ return html`
108
+ <header @mousedown=${handleMouseDown}>
109
+ <span>
110
+ <span class="lock ${lock ? 'like' : 'unlike'}" style="${getVisibilityStyle(!state.weightState.xyzMode)}" @click=${handleToggleLockStatus}>
111
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
112
+ <path
113
+ d="M21,10H19V7A7,7,0,0,0,5,7v3H3a1,1,0,0,0-1,1v9a4,4,0,0,0,4,4H18a4,4,0,0,0,4-4V11A1,1,0,0,0,21,10Zm-9,9.5A2.5,2.5,0,1,1,14.5,17,2.5,2.5,0,0,1,12,19.5ZM15,10H9V7a3,3,0,0,1,6,0v3Z"
114
+ ></path>
115
+ </svg>
116
+ </span>
117
+ <label class="name">${loraName}</label>
118
+ </span>
119
+ <div class="history" style="${getDisplayStyle(histories.length > 1)}">
120
+ <a class="icon" @click=${handleClearHistory}>clear</a>
121
+ <div class="page">
122
+ <a class="icon" @click=${handlePrevHistory} style="${getVisibilityStyle(state.historyIndex > 0)}">&lt;</a>
123
+ <label class="page-label">${state.historyIndex + 1}/${histories.length}</label>
124
+ <a class="icon" @click=${handleNextHistory} style="${getVisibilityStyle(state.historyIndex < histories.length - 1)}">&gt;</a>
125
+ </div>
126
+ </div>
127
+ </header>
128
+ `;
129
+ }
sd-webui-weight-helper-main/src/components/sections/lbw-section.ts ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as configManager from '@/shared/manager/config-manager';
2
+ import * as globalState from '@/shared/state/global-weight-helper-state';
3
+ import { expandRange } from '@/shared/utils/helper-utils';
4
+
5
+ import { createWeightController } from '@/components/sections/common-section';
6
+
7
+ import { TemplateResult, html } from 'lit-html';
8
+
9
+ /**
10
+ * LbwSection creates weight controllers for LoRA block weights.
11
+ * Shows groups of blocks based on the current model and block type configuration.
12
+ * @returns The LbwSection template.
13
+ */
14
+ export function createLbwSection(): TemplateResult {
15
+ const state = globalState.getGlobalState();
16
+ const { blockGroups, weightState } = state;
17
+ const { selectedModelType, selectedLoraBlockType } = weightState;
18
+
19
+ const maskedBlockSet = new Set(configManager.getMaskedLbwBlocks(selectedModelType, selectedLoraBlockType));
20
+
21
+ return html`
22
+ <div class="lbw-column">
23
+ ${blockGroups.map(
24
+ (cols) => html`
25
+ <div class="f col g-2" style="display: flex">
26
+ ${cols.map(
27
+ (group) => html`
28
+ <div class="border p f g-2 col">
29
+ ${expandRange(group).map((blockLabel) =>
30
+ maskedBlockSet.has(blockLabel) && weightState.weights[blockLabel]
31
+ ? html` <div class="f g-2">${createWeightController(blockLabel, weightState.weights)}</div> `
32
+ : '',
33
+ )}
34
+ </div>
35
+ `,
36
+ )}
37
+ </div>
38
+ `,
39
+ )}
40
+ </div>
41
+ `;
42
+ }
sd-webui-weight-helper-main/src/components/sections/lora-settings-section.ts ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { SELECTABLE_LORA_BLOCK_TYPES, SELECTABLE_MODEL_TYPES } from '@/shared/constants/common-const';
2
+ import * as configManager from '@/shared/manager/config-manager';
3
+ import * as currentState from '@/shared/state/current-state';
4
+ import * as globalState from '@/shared/state/global-weight-helper-state';
5
+ import { LoraBlockTypes, ModelTypes } from '@/shared/types/lora-types';
6
+ import { disabled } from '@/shared/utils/helper-utils';
7
+ import { mergeWeightsWithGroups } from '@/shared/utils/weight-utils';
8
+
9
+ import { TemplateResult, html } from 'lit-html';
10
+
11
+ /**
12
+ * LoraSettingsSection creates controls for model settings.
13
+ * Allows selection of model type, LoRA block type, presets, and XYZ mode.
14
+ * @returns The LoraSettingsSection template.
15
+ */
16
+ export function createLoraSettingsSection(): TemplateResult {
17
+ const state = globalState.getGlobalState();
18
+
19
+ /**
20
+ * Handles the change event for the LoRA block type select box.
21
+ * Updates the block groups, presets, and weights for the selected block type.
22
+ * @param e - The select change event.
23
+ */
24
+ const handleChangeLoraBlockType = (e: Event) => {
25
+ const target = e.target as HTMLSelectElement;
26
+ const selectedLoraBlockType = target.value as LoraBlockTypes;
27
+
28
+ const blockGroups = configManager.getLbwBlockGroups(state.weightState.selectedModelType, selectedLoraBlockType);
29
+ const lbwPresets = currentState.getLbwPresets(state.weightState.selectedModelType, selectedLoraBlockType);
30
+ const weights = mergeWeightsWithGroups(state.weightState.weights, blockGroups);
31
+
32
+ globalState.setModelInfo({
33
+ selectedLoraBlockType: selectedLoraBlockType,
34
+ selectedModelType: state.weightState.selectedModelType,
35
+ blockGroups: blockGroups,
36
+ lbwPresets: lbwPresets,
37
+ weights: weights,
38
+ });
39
+ };
40
+
41
+ /**
42
+ * Handles the change event for the model type select box.
43
+ * Updates the block groups, presets, and weights for the selected model type.
44
+ * @param e - The select change event.
45
+ */
46
+ const handleChangeModelType = (e: Event) => {
47
+ const target = e.target as HTMLSelectElement;
48
+ const selectedModelType = target.value as ModelTypes;
49
+
50
+ const blockGroups = configManager.getLbwBlockGroups(selectedModelType, state.weightState.selectedLoraBlockType);
51
+ const lbwPresets = currentState.getLbwPresets(selectedModelType, state.weightState.selectedLoraBlockType);
52
+ const weights = mergeWeightsWithGroups(state.weightState.weights, blockGroups);
53
+
54
+ globalState.setModelInfo({
55
+ selectedModelType: selectedModelType,
56
+ selectedLoraBlockType: state.weightState.selectedLoraBlockType,
57
+ blockGroups: blockGroups,
58
+ lbwPresets: lbwPresets,
59
+ weights: weights,
60
+ });
61
+ };
62
+
63
+ /**
64
+ * Handles the toggle event for the XYZ mode checkbox.
65
+ * Dispatches an action to toggle the XYZ mode state.
66
+ */
67
+ const handleToggleXyzMode = () => {
68
+ globalState.toggleXyzMode();
69
+ };
70
+
71
+ /**
72
+ * Handles the change event for the preset select box.
73
+ * Applies the selected preset weights to the state.
74
+ * @param e - The select change event.
75
+ */
76
+ const handleApplyPresetWeights = (e: Event) => {
77
+ const target = e.target as HTMLSelectElement;
78
+ let preset = target.value as string;
79
+ if (preset === '') {
80
+ preset = state.preset;
81
+ if (preset === '') {
82
+ return;
83
+ }
84
+ }
85
+ const presetValues = currentState
86
+ .getLbwPresets(state.weightState.selectedModelType, state.weightState.selectedLoraBlockType)
87
+ [preset].split(',')
88
+ .map((v) => +v);
89
+
90
+ const weights: Record<string, globalState.WeightControlState> = {};
91
+ const maskedLbwBlocks = configManager.getMaskedLbwBlocks(state.weightState.selectedModelType, state.weightState.selectedLoraBlockType);
92
+ maskedLbwBlocks.forEach((block, i) => {
93
+ weights[block] = {
94
+ initValue: state.weightState.weights[block].initValue,
95
+ value: presetValues[i],
96
+ sliderMin: Math.min(presetValues[i], state.weightState.weights[block].sliderMin),
97
+ sliderMax: Math.max(presetValues[i], state.weightState.weights[block].sliderMax),
98
+ };
99
+ });
100
+
101
+ globalState.setPreset({
102
+ weights: weights,
103
+ preset: target.value,
104
+ });
105
+ };
106
+
107
+ return html`
108
+ <div class="f g-2 f-end">
109
+ <select .value="${state.weightState.selectedModelType}" style="flex-grow: 1" ?disabled="${disabled(state)}" @change="${handleChangeModelType}">
110
+ ${Object.entries(SELECTABLE_MODEL_TYPES).map(([name, modelType]) => html` <option value="${modelType}">${name}</option> `)}
111
+ </select>
112
+ <select
113
+ .value="${state.weightState.selectedLoraBlockType}"
114
+ style="flex-grow: 1"
115
+ ?disabled="${disabled(state)}"
116
+ @change="${handleChangeLoraBlockType}"
117
+ >
118
+ ${Object.entries(SELECTABLE_LORA_BLOCK_TYPES).map(([name, loraBlockType]) => html` <option value="${loraBlockType}">${name}</option> `)}
119
+ </select>
120
+ </div>
121
+ <div class="f g-2 f-end">
122
+ <select .value="${state.preset}" style="flex-grow: 1" ?disabled="${disabled(state)}" @change="${handleApplyPresetWeights}">
123
+ <option value=""></option>
124
+ ${Object.keys(state.lbwPresets).map((presetName) => html` <option value="${presetName}">${presetName}</option> `)}
125
+ </select>
126
+ <div class="p f g-2 f-end">
127
+ <div class="f g-2 f-end">
128
+ <input
129
+ id="wh:xyz"
130
+ value="XYZ"
131
+ type="checkbox"
132
+ .checked="${state.weightState.xyzMode}"
133
+ ?disabled="${disabled(state)}"
134
+ @change="${handleToggleXyzMode}"
135
+ />
136
+ <label class="radio-label" for="wh:xyz">
137
+ ${configManager.getXyzLabel(state.weightState.selectedModelType, state.weightState.selectedLoraBlockType)}
138
+ </label>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ `;
143
+ }
sd-webui-weight-helper-main/src/components/sections/metadata-section.ts ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { resetMetadataAbortController } from '@/shared/manager/api-manager';
2
+ import * as configManager from '@/shared/manager/config-manager';
3
+ import * as currentState from '@/shared/state/current-state';
4
+ import * as globalState from '@/shared/state/global-weight-helper-state';
5
+ import { ModelTypes } from '@/shared/types/lora-types';
6
+ import { fetchMetadata } from '@/shared/utils/api-utils';
7
+ import { mergeWeightsWithGroups } from '@/shared/utils/weight-utils';
8
+
9
+ import { TemplateResult, html } from 'lit-html';
10
+
11
+ /**
12
+ * MetadataSection component displays model and base information.
13
+ * Provides a button to fetch and analyze detailed metadata for the current LoRA model.
14
+ * @returns The MetadataSection template.
15
+ */
16
+ export function createMetadataSection(): TemplateResult {
17
+ const state = globalState.getGlobalState();
18
+
19
+ /**
20
+ * Handles the click event for the analyze full metadata button.
21
+ * Initiates fetching and loading of full metadata for the current LoRA model.
22
+ */
23
+ const handleAnalyzeFull = () => {
24
+ if (state.uiState.isWaiting) {
25
+ return;
26
+ }
27
+ const metadataAbortController = resetMetadataAbortController();
28
+
29
+ globalState.setWaiting(true);
30
+
31
+ fetchMetadata(state.loraName, true, metadataAbortController.signal)
32
+ .then((result) => {
33
+ let selectedModelType = state.weightState.selectedModelType;
34
+ if (!selectedModelType || selectedModelType === ModelTypes.Unknown) {
35
+ selectedModelType = result.modelType ?? ModelTypes.Unknown;
36
+ }
37
+ const blockGroups = configManager.getLbwBlockGroups(selectedModelType, state.weightState.selectedLoraBlockType);
38
+ const weights = mergeWeightsWithGroups(state.weightState.weights, blockGroups);
39
+ globalState.loadMetadata({
40
+ algorithm: result.algorithm || 'Unknown',
41
+ modelType: result.modelType || 'Unknown',
42
+ baseModel: result.baseModel || 'Unknown',
43
+ selectedModelType: selectedModelType,
44
+ usingBlocks: result.usingBlocks,
45
+ blockGroups: blockGroups,
46
+ lbwPresets: currentState.getLbwPresets(selectedModelType, state.weightState.selectedLoraBlockType),
47
+ weights: weights,
48
+ });
49
+ })
50
+ .catch((error) => {
51
+ if (error.name !== 'AbortError') {
52
+ console.error('Error fetching metadata:', error);
53
+ globalState.setWaiting(false);
54
+ }
55
+ });
56
+ };
57
+
58
+ return html`
59
+ <span class="metadata">
60
+ <span>
61
+ <span>Model</span>
62
+ <span>${state.metadataState?.modelType || 'Unknown'}</span>
63
+ </span>
64
+ <span>
65
+ <span>Base</span>
66
+ <span>${state.metadataState?.baseModel || 'Unknown'}</span>
67
+ </span>
68
+ <a class="icon mini svg" @click=${handleAnalyzeFull}>
69
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 492 492">
70
+ <g>
71
+ <path
72
+ d="M484.08,296.216c-5.1-5.128-11.848-7.936-19.032-7.936H330.516c-14.828,0-26.86,12.036-26.86,26.868v22.796c0,7.168,2.784,14.064,7.884,19.16c5.092,5.088,11.82,8.052,18.976,8.052H366.1c-31.544,30.752-74.928,50.08-120.388,50.08c-71.832,0-136.028-45.596-159.744-113.344c-5.392-15.404-19.972-25.784-36.28-25.784c-4.316,0-8.592,0.708-12.7,2.144c-9.692,3.396-17.48,10.352-21.932,19.596c-4.456,9.248-5.04,19.684-1.648,29.368c34.496,98.54,127.692,164.74,232.144,164.74c64.132,0,123.448-23.948,169.572-67.656v25.22c0,14.836,12.384,27.108,27.224,27.108h22.792c14.84,0,26.86-12.272,26.86-27.108V315.24C492,308.056,489.2,301.304,484.08,296.216z"
73
+ ></path>
74
+ </g>
75
+ <g>
76
+ <path
77
+ d="M478.628,164.78C444.132,66.244,350.916,0.044,246.464,0.044c-64.136,0-123.464,23.952-169.588,67.66v-25.22c0-14.832-12.344-27.112-27.184-27.112H26.896C12.06,15.372,0,27.652,0,42.484V176.76c0,7.18,2.824,13.868,7.944,18.964c5.096,5.128,11.86,7.932,19.044,7.932l-0.08,0.06h134.604c14.84,0,26.832-12.028,26.832-26.86v-22.8c0-14.836-11.992-27.216-26.832-27.216h-35.576c31.544-30.752,74.932-50.076,120.392-50.076c71.832,0,136.024,45.596,159.74,113.348c5.392,15.404,19.968,25.78,36.28,25.78c4.32,0,8.588-0.704,12.7-2.144c9.696-3.396,17.48-10.348,21.932-19.596C481.432,184.9,482.02,174.472,478.628,164.78z"
78
+ ></path>
79
+ </g>
80
+ </svg>
81
+ </a>
82
+ </span>
83
+ `;
84
+ }
sd-webui-weight-helper-main/src/components/sections/preview-section.ts ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { getEditor, getTabId } from '@/shared/state/current-state';
2
+ import * as globalState from '@/shared/state/global-weight-helper-state';
3
+ import { withoutPromptAssist } from '@/shared/utils/common-utils';
4
+ import { getDisplayStyle, getVisibilityStyle } from '@/shared/utils/helper-utils';
5
+
6
+ import { TemplateResult, html } from 'lit-html';
7
+
8
+ declare function extraNetworksRequestMetadata(e: Event, type: string, modelName: string): void;
9
+ declare function extraNetworksEditUserMetadata(e: Event, tabId: string, type: string, modelName: string): void;
10
+
11
+ /**
12
+ * PreviewSection creates information display about the selected model.
13
+ * Shows preview image, metadata actions, trigger word insertion buttons, and model description.
14
+ * @param preview - The preview state containing model information.
15
+ * @returns The PreviewSection template.
16
+ */
17
+ export function createPreviewSection(preview: globalState.PreviewState): TemplateResult {
18
+ /**
19
+ * Handles the click event to show internal metadata for the model.
20
+ * @param e - The click event.
21
+ */
22
+ const handleShowMetadata = (e: Event) => {
23
+ extraNetworksRequestMetadata(e, 'lora', preview.modelName);
24
+ };
25
+
26
+ /**
27
+ * Handles the click event to edit user metadata for the model.
28
+ * @param e - The click event.
29
+ */
30
+ const handleEditMetadata = (e: Event) => {
31
+ extraNetworksEditUserMetadata(e, getTabId(), 'lora', preview.modelName);
32
+ };
33
+
34
+ /**
35
+ * Handles the click event to open the Civitai model page in a new tab.
36
+ */
37
+ const handleOpenCivitai = () => {
38
+ window.open(`https://civitai.com/models/${preview.modelId}`, '_blank');
39
+ };
40
+
41
+ /**
42
+ * Handles the click event to show the preview description.
43
+ */
44
+ const handleOpenPreviewDescription = () => {
45
+ globalState.setPreviewDescriptionVisible(true);
46
+ };
47
+
48
+ /**
49
+ * Handles the click event to close the preview description.
50
+ */
51
+ const handleClosePreviewDescription = () => {
52
+ globalState.setPreviewDescriptionVisible(false);
53
+ };
54
+
55
+ /**
56
+ * Handles the click event to insert trigger words into the prompt or negative prompt textarea.
57
+ */
58
+ const handleInsertTriggerWords = () => {
59
+ const tabId = getTabId();
60
+ let promptTextarea = document.querySelector(`#${tabId}_prompt textarea`) as HTMLTextAreaElement;
61
+ let negativeTextarea = document.querySelector(`#${tabId}_neg_prompt textarea`) as HTMLTextAreaElement | undefined;
62
+
63
+ const textarea = getEditor();
64
+ if (textarea === negativeTextarea) {
65
+ } else if (textarea !== promptTextarea) {
66
+ promptTextarea = textarea;
67
+ negativeTextarea = undefined;
68
+ }
69
+ if (!opts.weight_helper_using_execCommand) {
70
+ const insert = () => {
71
+ const zipped = [
72
+ { triggerWords: preview.triggerWords, textarea: promptTextarea },
73
+ { triggerWords: preview.negativeTriggerWords, textarea: negativeTextarea },
74
+ ];
75
+ for (const { triggerWords, textarea } of zipped) {
76
+ if (triggerWords.length > 0 && textarea) {
77
+ let words = triggerWords.join(', ');
78
+ if (textarea.value) words = ', ' + words;
79
+ textarea.value += words;
80
+ }
81
+ }
82
+ };
83
+ insert();
84
+ } else {
85
+ const insert = () => {
86
+ const zipped = [
87
+ { triggerWords: preview.triggerWords, textarea: promptTextarea },
88
+ { triggerWords: preview.negativeTriggerWords, textarea: negativeTextarea },
89
+ ];
90
+ for (const { triggerWords, textarea } of zipped) {
91
+ if (triggerWords.length > 0 && textarea) {
92
+ let words = triggerWords.join(', ');
93
+ if (textarea.value) words = ', ' + words;
94
+ textarea.focus();
95
+ const eolIndex = textarea.value.length;
96
+ textarea.setSelectionRange(eolIndex, eolIndex);
97
+ document.execCommand('insertText', false, words);
98
+ }
99
+ }
100
+ };
101
+ withoutPromptAssist(() => {
102
+ insert();
103
+ });
104
+ }
105
+ globalState.hideTagInsertButton();
106
+ };
107
+
108
+ const classNames = ['preview-pane', 'card'];
109
+
110
+ // position based on settings
111
+ switch (opts.weight_helper_preview_position) {
112
+ case 'Bottom Right':
113
+ classNames.push('preview-bottom-right');
114
+ break;
115
+ case 'Top Left':
116
+ classNames.push('preview-top-left');
117
+ break;
118
+ case 'Bottom Left':
119
+ classNames.push('preview-bottom-left');
120
+ break;
121
+ default:
122
+ classNames.push('preview-top-right');
123
+ break;
124
+ }
125
+
126
+ return html`
127
+ <div class="${classNames.join(' ')}" data-name="${preview.modelName}">
128
+ <img class="preview" src="${preview.thumbUrl}" style="height: ${opts.weight_helper_preview_height}px" />
129
+ <div class="action-row button-top" style="${getVisibilityStyle(!preview.isDescriptionVisible)}">
130
+ ${preview.modelName && preview.hasMetadata && typeof extraNetworksRequestMetadata === 'function'
131
+ ? html` <div class="metadata-btn card-btn" title="Show internal metadata" @click="${handleShowMetadata}"></div> `
132
+ : ''}
133
+ ${preview.modelName && typeof extraNetworksEditUserMetadata === 'function'
134
+ ? html` <div class="edit-btn card-btn" title="Edit metadata" @click="${handleEditMetadata}"></div> `
135
+ : ''}
136
+ ${preview.modelId ? html` <div class="civitai-btn card-btn" title="Open civitai" @click="${handleOpenCivitai}"></div> ` : ''}
137
+ ${preview.triggerWords.length > 0 || preview.negativeTriggerWords.length > 0
138
+ ? html`
139
+ <div
140
+ class="add-trigger-btn card-btn"
141
+ title="Add trigger words"
142
+ @click="${handleInsertTriggerWords}"
143
+ style="${getDisplayStyle(preview.isTagInsertButtonVisible)}"
144
+ ></div>
145
+ `
146
+ : ''}
147
+ </div>
148
+
149
+ ${preview.description
150
+ ? html`
151
+ <div class="action-row button-bottom" style="${getVisibilityStyle(!preview.isDescriptionVisible)}">
152
+ <div class="card-btn note-btn" @click="${handleOpenPreviewDescription}"></div>
153
+ </div>
154
+ <textarea class="description" style="${getVisibilityStyle(preview.isDescriptionVisible)}">
155
+ ${preview.description}
156
+ </textarea
157
+ >
158
+ <div
159
+ class="card-btn description-close-btn"
160
+ style="${getVisibilityStyle(preview.isDescriptionVisible)}"
161
+ @click="${handleClosePreviewDescription}"
162
+ ></div>
163
+ `
164
+ : ''}
165
+ </div>
166
+ `;
167
+ }
sd-webui-weight-helper-main/src/components/ui-template.ts ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { CARD_NO_IMAGE_PATH } from '@/shared/constants/common-const';
2
+ import { resetMetadataAbortController, resetPreviewAbortController } from '@/shared/manager/api-manager';
3
+ import * as cacheManager from '@/shared/manager/cache-manager';
4
+ import * as configManager from '@/shared/manager/config-manager';
5
+ import * as historyManager from '@/shared/manager/history-manager';
6
+ import * as currentState from '@/shared/state/current-state';
7
+ import * as globalState from '@/shared/state/global-weight-helper-state';
8
+ import { ModelTypes, WeightControllerTypes } from '@/shared/types/lora-types';
9
+ import { fetchMetadata, fetchPreviewData } from '@/shared/utils/api-utils';
10
+ import { getLoraDefineFromEditor, getOutputStrings, syncEditorWithState } from '@/shared/utils/editor-utils';
11
+ import { disabled, getDisplayStyle } from '@/shared/utils/helper-utils';
12
+ import { createWeightHelperInitState, createWeightHelperState } from '@/shared/utils/state-utils';
13
+ import { mergeWeightsWithGroups } from '@/shared/utils/weight-utils';
14
+
15
+ import { createWeightController } from '@/components/sections/common-section';
16
+ import { createHeaderSection } from '@/components/sections/header-section';
17
+ import { createLbwSection } from '@/components/sections/lbw-section';
18
+ import { createLoraSettingsSection } from '@/components/sections/lora-settings-section';
19
+ import { createMetadataSection } from '@/components/sections/metadata-section';
20
+ import { createPreviewSection } from '@/components/sections/preview-section';
21
+
22
+ import { TemplateResult, html, render } from 'lit-html';
23
+
24
+ declare function gradioApp(): HTMLElement;
25
+
26
+ // Track if global (one-time) event listeners were attached
27
+ let isEventListenersInitialized = false;
28
+
29
+ /**
30
+ * Build main Weight Helper UI template.
31
+ * Note: top/left positioning styles are applied imperatively (drag) and excluded from state to avoid re-renders.
32
+ * @returns The main Weight Helper UI template
33
+ */
34
+ export function createUITemplate(): TemplateResult {
35
+ const state = globalState.getGlobalState();
36
+ const { uiState, weightState, lock } = state;
37
+
38
+ // Attach global listeners only once
39
+ if (!isEventListenersInitialized) {
40
+ setupEventListeners();
41
+ isEventListenersInitialized = true;
42
+ }
43
+
44
+ // top/left not included here (managed outside state for performance)
45
+ const containerStyle = `transform: scale(${uiState.scale}); ${getDisplayStyle(uiState.isVisible)}`;
46
+
47
+ return html`
48
+ <div id="weight-helper" class="${state.uiState.isWaiting ? 'waiting' : ''}" style="${containerStyle}">
49
+ ${createHeaderSection(state.loraName, lock)} ${createMetadataSection()}
50
+ ${weightState.weights[WeightControllerTypes.TENC] &&
51
+ html`<section class="border p">${createWeightController(WeightControllerTypes.TENC, weightState.weights)}</section>`}
52
+ ${[WeightControllerTypes.UNET, WeightControllerTypes.START, WeightControllerTypes.STOP]
53
+ .filter((wt) => !!weightState.weights[wt])
54
+ .map(
55
+ (weightType) =>
56
+ html`<section
57
+ class="border p"
58
+ style="${getDisplayStyle(
59
+ !uiState.isMoreButtonVisible ||
60
+ weightState.weights[weightType].initCheckState ||
61
+ weightState.weights[weightType].initValue !== configManager.getWeightControllerConfig(weightType).default,
62
+ )}"
63
+ >
64
+ ${createWeightController(weightType, weightState.weights)}
65
+ </section>`,
66
+ )}
67
+
68
+ <button style="${uiState.isMoreButtonVisible ? '' : 'display: none'}" ?disabled="${disabled(state)}" @click="${() => globalState.hideMoreButton()}">
69
+ show more options
70
+ </button>
71
+
72
+ <section class="border p">
73
+ <label>LBW</label>
74
+ <div class="f col g-4 w-fill">${createLoraSettingsSection()} ${createLbwSection()}</div>
75
+ </section>
76
+
77
+ ${state.previewState.modelName ? createPreviewSection(state.previewState) : ''}
78
+ </div>
79
+ `;
80
+ }
81
+
82
+ /**
83
+ * Register global event listeners (context menu open/close lifecycle, outside click, ESC key, generate button etc.).
84
+ */
85
+ function setupEventListeners(): void {
86
+ const handleClickGenButton = () => {
87
+ const state = globalState.getGlobalState();
88
+ if (state.uiState.isVisible) {
89
+ closeWeightHelperContext();
90
+ syncEditorWithState(state);
91
+ }
92
+ };
93
+
94
+ const handleRightClickTextarea = (e: MouseEvent) => {
95
+ const state = globalState.getGlobalState();
96
+ if (state.uiState.isVisible) {
97
+ e.preventDefault();
98
+ closeWeightHelperContext();
99
+ syncEditorWithState(state);
100
+ return;
101
+ }
102
+ const loraDefine = getLoraDefineFromEditor(e);
103
+ if (loraDefine) {
104
+ e.preventDefault();
105
+ openWeightHelperContext(loraDefine);
106
+ }
107
+ };
108
+
109
+ const handleClickAnyware = (e: MouseEvent) => {
110
+ const state = globalState.getGlobalState();
111
+ if (state.uiState.isVisible) {
112
+ const contextMenuElement = document.getElementById('weight-helper');
113
+ if (!contextMenuElement) return;
114
+ const tabId = currentState.getTabId();
115
+ if (e) {
116
+ const target = e.target as HTMLBodyElement;
117
+ if (contextMenuElement.contains(target)) return;
118
+ if (target.id === `${tabId}_token_button`) return;
119
+ if (target.id === `${tabId}_lora_edit_user_metadata_button`) return;
120
+ if (target.className === 'global-popup-close') return;
121
+ if (target.id.indexOf('_interrupt') > 0) {
122
+ closeWeightHelperContext();
123
+ return;
124
+ }
125
+ }
126
+ closeWeightHelperContext();
127
+ syncEditorWithState(state);
128
+ }
129
+ };
130
+
131
+ const handleKeyupAnyware = (e: KeyboardEvent) => {
132
+ if ((e as KeyboardEvent).key === 'Escape') {
133
+ const state = globalState.getGlobalState();
134
+ cancelWeightHelperContext(state);
135
+ }
136
+ };
137
+
138
+ // Attach listeners to generate buttons & prompt textareas
139
+ const genButtons = gradioApp().querySelectorAll("button:is([id*='_generate'])");
140
+ genButtons.forEach((genBtn) => {
141
+ genBtn.addEventListener('click', handleClickGenButton, true);
142
+ });
143
+
144
+ const textareas = gradioApp().querySelectorAll("*:is([id*='_toprow'] [id*='_prompt'], .prompt) textarea") as NodeListOf<HTMLTextAreaElement>;
145
+ textareas.forEach((textarea) => {
146
+ textarea.addEventListener('contextmenu', handleRightClickTextarea);
147
+ });
148
+
149
+ document.addEventListener('click', handleClickAnyware);
150
+ document.addEventListener('keyup', handleKeyupAnyware);
151
+ }
152
+
153
+ // One-time flag for state watching → subscribe only once
154
+ let isStateWatchingInitialized = false;
155
+
156
+ /**
157
+ * Render (and set up subscriptions) of the UI into container.
158
+ * Full state changes trigger template re-render; position changes only mutate style.
159
+ * @param container Target DOM element
160
+ */
161
+ export function renderUI(container: HTMLElement): void {
162
+ // 初期レンダリング
163
+ render(createUITemplate(), container);
164
+
165
+ // 状態変化を監視して自動再レンダリング(一度だけ設定)
166
+ if (!isStateWatchingInitialized) {
167
+ // Subscribe to full state updates
168
+ globalState.subscribeToStateChanges(() => {
169
+ render(createUITemplate(), container);
170
+ });
171
+
172
+ // Subscribe to position-only updates (avoid re-render)
173
+ globalState.subscribeToPositionChanges((pos) => {
174
+ const el = container.querySelector('#weight-helper') as HTMLDivElement;
175
+ if (!el) return;
176
+ el.style.top = pos.top + 'px';
177
+ el.style.left = pos.left + 'px';
178
+ });
179
+
180
+ isStateWatchingInitialized = true;
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Open Weight Helper context at given coordinates and load metadata / preview lazily.
186
+ */
187
+ function openWeightHelperContext(loraDefine: {
188
+ tabId: string;
189
+ textarea: HTMLTextAreaElement;
190
+ selectionStart: number;
191
+ selectionEnd: number;
192
+ namespace: string;
193
+ loraName: string;
194
+ loraParams: string;
195
+ top: number;
196
+ left: number;
197
+ }) {
198
+ const { tabId, textarea, selectionStart, selectionEnd, namespace, loraName, loraParams, top, left } = loraDefine;
199
+
200
+ currentState.setTabId(tabId);
201
+ currentState.setEditor(textarea);
202
+ currentState.setSelectionStart(selectionStart);
203
+ currentState.setSelectionEnd(selectionEnd);
204
+ currentState.setLoraDefineString(textarea.value.substring(selectionStart, selectionEnd));
205
+
206
+ currentState.loadStep(tabId);
207
+ currentState.loadLbwPresets(tabId);
208
+
209
+ configManager.getWeightControllerConfig(WeightControllerTypes.START).max = currentState.getStep();
210
+ configManager.getWeightControllerConfig(WeightControllerTypes.STOP).max = currentState.getStep();
211
+ configManager.getWeightControllerConfig(WeightControllerTypes.STOP).default = currentState.getStep();
212
+
213
+ const rect = document.getElementById('weight-helper')!.getBoundingClientRect();
214
+ const diffBottom = window.innerHeight - rect.bottom;
215
+ let arrangedTop = top;
216
+ if (diffBottom < 0) {
217
+ if (rect.top < 0) {
218
+ arrangedTop = window.scrollY;
219
+ } else {
220
+ arrangedTop += diffBottom;
221
+ }
222
+ }
223
+ const props = createWeightHelperState(namespace, loraName, loraParams);
224
+
225
+ globalState.openContext(props);
226
+ // Apply position directly (not part of state)
227
+ const container = document.getElementById('weight-helper');
228
+ if (container) {
229
+ container.style.top = arrangedTop + 'px';
230
+ container.style.left = left + 'px';
231
+ }
232
+
233
+ if (!props.metadataState) {
234
+ const metadataAbortController = resetMetadataAbortController();
235
+ fetchMetadata(loraName, false, metadataAbortController.signal)
236
+ .then((result) => {
237
+ let selectedModelType = props.weightState.selectedModelType;
238
+ if (!selectedModelType || selectedModelType === ModelTypes.Unknown) {
239
+ selectedModelType = result.modelType ?? ModelTypes.Unknown;
240
+ }
241
+ const selectedLoraBlockType = props.weightState.selectedLoraBlockType;
242
+ const blockGroups = configManager.getLbwBlockGroups(selectedModelType, selectedLoraBlockType);
243
+ const weights = mergeWeightsWithGroups(props.weightState.weights, blockGroups);
244
+
245
+ const weightState = {
246
+ ...props.weightState,
247
+ selectedModelType: selectedModelType,
248
+ selectedLoraBlockType: selectedLoraBlockType,
249
+ };
250
+ const loraParams = getOutputStrings(props.loraName, weightState).loraParams;
251
+ if (!props.weightState.xyzMode) {
252
+ historyManager.addHistory(loraName, {
253
+ loraParams: loraParams,
254
+ selectedLoraBlockType: selectedLoraBlockType,
255
+ selectedModelType: selectedModelType,
256
+ });
257
+ globalState.setHistoryIndex(historyManager.getHistories(loraName).length - 1);
258
+ }
259
+
260
+ globalState.loadMetadata({
261
+ algorithm: result.algorithm || 'Unknown',
262
+ modelType: result.modelType || 'Unknown',
263
+ baseModel: result.baseModel || 'Unknown',
264
+ selectedModelType: selectedModelType,
265
+ usingBlocks: result.usingBlocks,
266
+ blockGroups: blockGroups,
267
+ lbwPresets: currentState.getLbwPresets(selectedModelType, props.weightState.selectedLoraBlockType),
268
+ weights: weights,
269
+ });
270
+ })
271
+ .catch((error) => {
272
+ if (error.name !== 'AbortError') {
273
+ console.error('Error fetching metadata:', error);
274
+ globalState.setWaiting(false);
275
+ }
276
+ });
277
+ }
278
+
279
+ if (!props.previewState.modelId) {
280
+ const previewAbortController = resetPreviewAbortController();
281
+ fetchPreviewData(loraName, previewAbortController.signal)
282
+ .then(async (result) => {
283
+ if (result.thumbUrl !== CARD_NO_IMAGE_PATH) {
284
+ result.thumbUrl = result.thumbUrl;
285
+ }
286
+ cacheManager.setPreviewCache(loraName, result);
287
+ globalState.loadPreviewData(result);
288
+ })
289
+ .catch((error) => {
290
+ if (error.name !== 'AbortError') {
291
+ console.error('Error fetching metadata:', error);
292
+ }
293
+ });
294
+ }
295
+ }
296
+
297
+ /** Close context menu and reset to initial hidden state. */
298
+ function closeWeightHelperContext(): void {
299
+ const weightHelperState = createWeightHelperInitState();
300
+ globalState.closeContext(weightHelperState);
301
+ }
302
+
303
+ /** Cancel editing: revert editor text (if enabled) then close. */
304
+ function cancelWeightHelperContext(state: globalState.BasicState): void {
305
+ if (!opts.weight_helper_using_execCommand) {
306
+ syncEditorWithState(state, true);
307
+ }
308
+ closeWeightHelperContext();
309
+ }
sd-webui-weight-helper-main/src/index.ts ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as configManager from '@/shared/manager/config-manager';
2
+ import * as historyManager from '@/shared/manager/history-manager';
3
+ import * as globalState from '@/shared/state/global-weight-helper-state';
4
+ import { createWeightHelperInitState } from '@/shared/utils/state-utils';
5
+
6
+ import { renderUI } from '@/components/ui-template';
7
+
8
+ declare function gradioApp(): HTMLElement;
9
+ declare function onUiLoaded(callback: VoidFunction): void;
10
+ declare function onOptionsChanged(callback: VoidFunction): void;
11
+
12
+ /**
13
+ * Promise resolver for the initialization process.
14
+ * Used to signal when options have been loaded and initialization can proceed.
15
+ */
16
+ export let resolveInitialized: ((value: boolean) => void) | null;
17
+
18
+ /**
19
+ * Promise that resolves when the Weight Helper is ready to initialize.
20
+ * Used to defer initialization until options are loaded.
21
+ */
22
+ const initializedPromise = new Promise<boolean>((resolve) => {
23
+ resolveInitialized = resolve;
24
+ });
25
+
26
+ /**
27
+ * Entry point for the Weight Helper React application.
28
+ * Handles initialization, theme adaptation, and state setup when the UI is loaded or options are changed.
29
+ */
30
+ historyManager.loadLocalStorage();
31
+
32
+ onUiLoaded(() => {
33
+ if (!gradioApp().querySelector('#lbw_ratiospreset')) {
34
+ return;
35
+ }
36
+ const textColor = getComputedStyle(document.documentElement).getPropertyValue('--body-text-color').trim();
37
+ const textColorRgb = textColor
38
+ .slice(1)
39
+ .match(/.{1,2}/g)!
40
+ .map((hex) => parseInt(hex, 16));
41
+ const textColorRgba = [...textColorRgb, 0.3];
42
+ document.documentElement.style.setProperty('--weight-helper-shadow', `rgba(${textColorRgba.join(',')})`);
43
+ document.documentElement.style.setProperty('--weight-helper-slider_size', String(opts.weight_helper_slider_length));
44
+
45
+ initializedPromise.then(() => {
46
+ const weightHelperContainer = document.createElement('div');
47
+ weightHelperContainer.id = 'weight-helper-container';
48
+ gradioApp().appendChild(weightHelperContainer);
49
+
50
+ configManager.initialize();
51
+ globalState.initializeGlobalState(createWeightHelperInitState());
52
+ renderUI(weightHelperContainer);
53
+ });
54
+ });
55
+
56
+ onOptionsChanged(() => {
57
+ if (resolveInitialized) {
58
+ resolveInitialized(true);
59
+ resolveInitialized = null;
60
+ }
61
+ });
sd-webui-weight-helper-main/src/shared/constants/common-const.ts ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { LoraBlockTypes, ModelTypes } from '@/shared/types/lora-types';
2
+
3
+ const BASE_MODEL_TYPE = {
4
+ STABLE_DIFFUSION: [
5
+ 'BASE',
6
+ 'IN00',
7
+ 'IN01',
8
+ 'IN02',
9
+ 'IN03',
10
+ 'IN04',
11
+ 'IN05',
12
+ 'IN06',
13
+ 'IN07',
14
+ 'IN08',
15
+ 'IN09',
16
+ 'IN10',
17
+ 'IN11',
18
+ 'M00',
19
+ 'OUT00',
20
+ 'OUT01',
21
+ 'OUT02',
22
+ 'OUT03',
23
+ 'OUT04',
24
+ 'OUT05',
25
+ 'OUT06',
26
+ 'OUT07',
27
+ 'OUT08',
28
+ 'OUT09',
29
+ 'OUT10',
30
+ 'OUT11',
31
+ ],
32
+ BLACK_FOREST_LABS: [
33
+ 'CLIP',
34
+ 'T5',
35
+ 'IN',
36
+ 'D00',
37
+ 'D01',
38
+ 'D02',
39
+ 'D03',
40
+ 'D04',
41
+ 'D05',
42
+ 'D06',
43
+ 'D07',
44
+ 'D08',
45
+ 'D09',
46
+ 'D10',
47
+ 'D11',
48
+ 'D12',
49
+ 'D13',
50
+ 'D14',
51
+ 'D15',
52
+ 'D16',
53
+ 'D17',
54
+ 'D18',
55
+ 'S00',
56
+ 'S01',
57
+ 'S02',
58
+ 'S03',
59
+ 'S04',
60
+ 'S05',
61
+ 'S06',
62
+ 'S07',
63
+ 'S08',
64
+ 'S09',
65
+ 'S10',
66
+ 'S11',
67
+ 'S12',
68
+ 'S13',
69
+ 'S14',
70
+ 'S15',
71
+ 'S16',
72
+ 'S17',
73
+ 'S18',
74
+ 'S19',
75
+ 'S20',
76
+ 'S21',
77
+ 'S22',
78
+ 'S23',
79
+ 'S24',
80
+ 'S25',
81
+ 'S26',
82
+ 'S27',
83
+ 'S28',
84
+ 'S29',
85
+ 'S30',
86
+ 'S31',
87
+ 'S32',
88
+ 'S33',
89
+ 'S34',
90
+ 'S35',
91
+ 'S36',
92
+ 'S37',
93
+ 'OUT',
94
+ ],
95
+ };
96
+
97
+ export const LBW_BLOCKS = {
98
+ [ModelTypes.SD]: BASE_MODEL_TYPE.STABLE_DIFFUSION,
99
+ [ModelTypes.SDXL]: BASE_MODEL_TYPE.STABLE_DIFFUSION,
100
+ [ModelTypes.Flux]: BASE_MODEL_TYPE.BLACK_FOREST_LABS,
101
+ [ModelTypes.Unknown]: [],
102
+ };
103
+
104
+ export const lbwMaskDefine = {
105
+ [ModelTypes.SD]: {
106
+ [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],
107
+ [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],
108
+ [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],
109
+ },
110
+ [ModelTypes.SDXL]: {
111
+ [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],
112
+ [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],
113
+ [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],
114
+ },
115
+ [ModelTypes.Flux]: {
116
+ [LoraBlockTypes.lora]: [
117
+ 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,
118
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
119
+ ],
120
+ [LoraBlockTypes.lycoris]: [
121
+ 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,
122
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
123
+ ],
124
+ [LoraBlockTypes.Unknown]: [
125
+ 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,
126
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127
+ ],
128
+ },
129
+ [ModelTypes.Unknown]: {
130
+ [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],
131
+ [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],
132
+ [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],
133
+ },
134
+ };
135
+
136
+ export const XYZ_PRESETS = {
137
+ [ModelTypes.SD]: {
138
+ [LoraBlockTypes.lora]: 'XYZ(17)',
139
+ [LoraBlockTypes.lycoris]: 'XYZ(26)',
140
+ [LoraBlockTypes.Unknown]: 'XYZ(26)',
141
+ },
142
+ [ModelTypes.SDXL]: {
143
+ [LoraBlockTypes.lora]: 'XYZ(12)',
144
+ [LoraBlockTypes.lycoris]: 'XYZ(20)',
145
+ [LoraBlockTypes.Unknown]: 'XYZ(26)',
146
+ },
147
+ [ModelTypes.Flux]: {
148
+ [LoraBlockTypes.lora]: 'XYZ(19)',
149
+ [LoraBlockTypes.lycoris]: 'XYZ(19)',
150
+ [LoraBlockTypes.Unknown]: 'XYZ(19)',
151
+ },
152
+ [ModelTypes.Unknown]: {
153
+ [LoraBlockTypes.lora]: 'XYZ(17)',
154
+ [LoraBlockTypes.lycoris]: 'XYZ(26)',
155
+ [LoraBlockTypes.Unknown]: 'XYZ(26)',
156
+ },
157
+ };
158
+
159
+ export const SELECTABLE_MODEL_TYPES = {
160
+ '': ModelTypes.Unknown,
161
+ SD: ModelTypes.SD,
162
+ SDXL: ModelTypes.SDXL,
163
+ Flux: ModelTypes.Flux,
164
+ };
165
+
166
+ export const SELECTABLE_LORA_BLOCK_TYPES = {
167
+ '': LoraBlockTypes.Unknown,
168
+ 'LoRA(LierLa)': LoraBlockTypes.lora,
169
+ 'LyCORIS,etc': LoraBlockTypes.lycoris,
170
+ };
171
+
172
+ export const CARD_NO_IMAGE_PATH = './file=html/card-no-preview.png';
sd-webui-weight-helper-main/src/shared/hooks/use-global-state.ts ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // このファイルはlit-html移行により不要になりました
2
+ // React/Preactのhooksは使用しないため、ダミーファイルとして残しています
3
+ import { BasicState, getGlobalState } from '@/shared/state/global-weight-helper-state';
4
+
5
+ /**
6
+ * 互換性のためのダミー関数
7
+ * lit-htmlベースではこの関数は使用されません
8
+ */
9
+ export function useGlobalState(): BasicState {
10
+ return getGlobalState();
11
+ }
sd-webui-weight-helper-main/src/shared/manager/api-manager.ts ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ let metadataAbortController: AbortController | null = null;
2
+ let previewAbortController: AbortController | null = null;
3
+
4
+ /**
5
+ * Returns the global AbortController for metadata fetch requests.
6
+ * Creates a new controller if one doesn't exist.
7
+ * @returns The AbortController instance for metadata requests.
8
+ */
9
+ export function getMetadataAbortController(): AbortController {
10
+ if (!metadataAbortController) {
11
+ metadataAbortController = new AbortController();
12
+ }
13
+ return metadataAbortController;
14
+ }
15
+
16
+ /**
17
+ * Aborts any ongoing metadata fetch request and creates a new AbortController.
18
+ * @returns The new AbortController instance for metadata requests.
19
+ */
20
+ export function resetMetadataAbortController(): AbortController {
21
+ if (metadataAbortController) {
22
+ metadataAbortController.abort();
23
+ }
24
+ metadataAbortController = new AbortController();
25
+ return metadataAbortController;
26
+ }
27
+
28
+ /**
29
+ * Returns the global AbortController for preview fetch requests.
30
+ * Creates a new controller if one doesn't exist.
31
+ * @returns The AbortController instance for preview requests.
32
+ */
33
+ export function getPreviewAbortController(): AbortController {
34
+ if (!previewAbortController) {
35
+ previewAbortController = new AbortController();
36
+ }
37
+ return previewAbortController;
38
+ }
39
+
40
+ /**
41
+ * Aborts any ongoing preview fetch request and creates a new AbortController.
42
+ * @returns The new AbortController instance for preview requests.
43
+ */
44
+ export function resetPreviewAbortController(): AbortController {
45
+ if (previewAbortController) {
46
+ previewAbortController.abort();
47
+ }
48
+ previewAbortController = new AbortController();
49
+ return previewAbortController;
50
+ }
sd-webui-weight-helper-main/src/shared/manager/cache-manager.ts ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as globalState from '@/shared/state/global-weight-helper-state';
2
+ import { LoraBlockTypes, ModelTypes } from '@/shared/types/lora-types';
3
+
4
+ const _metadataCacheStore: Record<
5
+ string,
6
+ {
7
+ metadataState: globalState.MetadataState | undefined;
8
+ selectedLoraBlockType: LoraBlockTypes;
9
+ selectedModelType: ModelTypes;
10
+ usingBlocks: Set<string> | undefined;
11
+ }
12
+ > = {};
13
+
14
+ const _previewCacheStore: Record<string, globalState.PreviewState> = {};
15
+
16
+ /**
17
+ * Retrieves the metadata cache for the given LoRA name.
18
+ * @param loraName - The name of the LoRA model.
19
+ * @returns The cached metadata state, selected block type, model type, and used blocks, or undefined if not cached.
20
+ */
21
+ export function getMetadataCache(loraName: string):
22
+ | {
23
+ metadataState: globalState.MetadataState | undefined;
24
+ selectedLoraBlockType: LoraBlockTypes;
25
+ selectedModelType: ModelTypes;
26
+ usingBlocks: Set<string> | undefined;
27
+ }
28
+ | undefined {
29
+ return _metadataCacheStore[loraName];
30
+ }
31
+
32
+ /**
33
+ * Sets the metadata cache for the given LoRA name.
34
+ * @param loraName - The name of the LoRA model.
35
+ * @param cache - The BasicState containing metadata and related info to cache.
36
+ */
37
+ export function setMetadataCache(loraName: string, cache: globalState.BasicState) {
38
+ _metadataCacheStore[loraName] = {
39
+ metadataState: cache.metadataState,
40
+ selectedLoraBlockType: cache.weightState.selectedLoraBlockType,
41
+ selectedModelType: cache.weightState.selectedModelType,
42
+ usingBlocks: cache.usingBlocks,
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Retrieves the preview cache for the given LoRA name.
48
+ * @param loraName - The name of the LoRA model.
49
+ * @returns The cached PreviewState, or undefined if not cached.
50
+ */
51
+ export function getPreviewCache(loraName: string): globalState.PreviewState | undefined {
52
+ return _previewCacheStore[loraName];
53
+ }
54
+
55
+ /**
56
+ * Sets the preview cache for the given LoRA name.
57
+ * @param loraName - The name of the LoRA model.
58
+ * @param cache - The PreviewState to cache.
59
+ */
60
+ export function setPreviewCache(loraName: string, cache: globalState.PreviewState) {
61
+ _previewCacheStore[loraName] = cache;
62
+ }
sd-webui-weight-helper-main/src/shared/manager/config-manager.ts ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ LBW_BLOCKS,
3
+ lbwMaskDefine as LBW_MASK_DEFINE,
4
+ SELECTABLE_LORA_BLOCK_TYPES,
5
+ SELECTABLE_MODEL_TYPES,
6
+ XYZ_PRESETS,
7
+ } from '@/shared/constants/common-const';
8
+ import { LoraBlockTypes, ModelTypes, WeightControllerTypes } from '@/shared/types/lora-types';
9
+
10
+ interface WeightControllerInterface {
11
+ min: number;
12
+ max: number;
13
+ default: number;
14
+ step: number;
15
+ }
16
+
17
+ const WeightControllerConfig: Record<string, WeightControllerInterface> = {
18
+ [WeightControllerTypes.TENC]: {
19
+ min: 0,
20
+ max: 0,
21
+ default: 1,
22
+ step: 0.5,
23
+ },
24
+ [WeightControllerTypes.UNET]: {
25
+ min: 0,
26
+ max: 0,
27
+ default: 0,
28
+ step: 0.5,
29
+ },
30
+ [WeightControllerTypes.START]: {
31
+ min: 0,
32
+ max: 0,
33
+ default: 0,
34
+ step: 1,
35
+ },
36
+ [WeightControllerTypes.STOP]: {
37
+ min: 0,
38
+ max: 0,
39
+ default: 0,
40
+ step: 1,
41
+ },
42
+ [WeightControllerTypes.LBW]: {
43
+ min: 0,
44
+ max: 0,
45
+ default: 1,
46
+ step: 0.5,
47
+ },
48
+ };
49
+
50
+ const lbwGroupConfig = {
51
+ [ModelTypes.SD]: {
52
+ [LoraBlockTypes.lora]: [['BASE', 'IN01-IN04', 'IN05-IN08', 'M00', 'OUT03-OUT06', 'OUT07-OUT11']],
53
+ [LoraBlockTypes.lycoris]: [['BASE', 'IN00-IN05', 'IN06-IN11', 'M00', 'OUT00-OUT05', 'OUT06-OUT11']],
54
+ [LoraBlockTypes.Unknown]: [[]] as string[][],
55
+ },
56
+ [ModelTypes.SDXL]: {
57
+ [LoraBlockTypes.lora]: [['BASE', 'IN04-IN08', 'M00', 'OUT00-OUT05']],
58
+ [LoraBlockTypes.lycoris]: [['BASE', 'IN00-IN03', 'IN04-IN08', 'M00', 'OUT00-OUT03', 'OUT04-OUT08']],
59
+ [LoraBlockTypes.Unknown]: [[]] as string[][],
60
+ },
61
+ [ModelTypes.Flux]: {
62
+ [LoraBlockTypes.lora]: [['CLIP', 'T5', 'IN', 'OUT'], ['D00-D18'], ['S00-S18'], ['S19-S37']],
63
+ [LoraBlockTypes.lycoris]: [['CLIP', 'T5', 'IN', 'OUT'], ['D00-D18'], ['S00-S18'], ['S19-S37']],
64
+ [LoraBlockTypes.Unknown]: [[]] as string[][],
65
+ },
66
+ [ModelTypes.Unknown]: {
67
+ [LoraBlockTypes.lora]: [[]] as string[][],
68
+ [LoraBlockTypes.lycoris]: [[]] as string[][],
69
+ [LoraBlockTypes.Unknown]: [[]] as string[][],
70
+ },
71
+ };
72
+
73
+ /**
74
+ * Initializes the configuration for block groups and weight controllers.
75
+ * Updates configuration values with settings from opts.
76
+ */
77
+ export function initialize() {
78
+ const optBlockPattern = /((BASE|MID|M00|CLIP|T5|(IN|OUT|D|S)[0-9]{2}(-(IN|OUT|D|S)[0-9]{2})?) *(, *|$))+/;
79
+ for (const modelType of Object.values(SELECTABLE_MODEL_TYPES)) {
80
+ if (modelType === ModelTypes.Unknown || modelType === ModelTypes.Flux) continue;
81
+ for (const loraBlockType of Object.values(SELECTABLE_LORA_BLOCK_TYPES)) {
82
+ if (loraBlockType === LoraBlockTypes.Unknown) continue;
83
+ try {
84
+ let optBlockPoints = opts[`weight_helper_LBW_${modelType}_${loraBlockType}_block_points`] as string;
85
+ optBlockPoints = optBlockPoints.replace('MID', 'M00');
86
+ if (optBlockPattern.exec(optBlockPoints)) {
87
+ const blockPoints = optBlockPoints.split(',').map((v) => {
88
+ return v.trim().replace(/\d+/g, (match) => (match.length === 1 ? `0${match}` : match));
89
+ });
90
+ lbwGroupConfig[modelType][loraBlockType] = [blockPoints];
91
+ }
92
+ } catch (e) {
93
+ console.warn(`${modelType}_${loraBlockType} block definition format is invalid.`, e);
94
+ }
95
+ }
96
+ }
97
+
98
+ for (const k of [WeightControllerTypes.TENC, WeightControllerTypes.UNET, WeightControllerTypes.LBW]) {
99
+ WeightControllerConfig[k].min = +opts[`weight_helper_${k}_min`];
100
+ WeightControllerConfig[k].max = +opts[`weight_helper_${k}_max`];
101
+ WeightControllerConfig[k].step = +opts[`weight_helper_${k}_step`];
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Retrieves the configuration for a specific weight controller type.
107
+ * @param type - The weight controller type.
108
+ * @returns The configuration object for the specified controller type.
109
+ */
110
+ export function getWeightControllerConfig(type: string): WeightControllerInterface {
111
+ return WeightControllerConfig[type];
112
+ }
113
+
114
+ /**
115
+ * Returns the list of LBW blocks for the selected model type.
116
+ * @param selectedModelType - The selected model type.
117
+ * @returns An array of block labels for the given model type.
118
+ */
119
+ export function getLbwBlocks(selectedModelType: ModelTypes): string[] {
120
+ if (selectedModelType in LBW_BLOCKS) {
121
+ return LBW_BLOCKS[selectedModelType];
122
+ }
123
+ return LBW_BLOCKS[ModelTypes.Unknown];
124
+ }
125
+
126
+ /**
127
+ * Returns an array of enabled LBW block labels for the selected model and LoRA block type.
128
+ * Only blocks with a mask value of 1 are included.
129
+ * @param selectedModelType - The selected model type.
130
+ * @param selectedLoraBlockType - The selected LoRA block type.
131
+ * @returns An array of enabled block labels.
132
+ */
133
+ export function getMaskedLbwBlocks(selectedModelType: ModelTypes, selectedLoraBlockType: LoraBlockTypes): string[] {
134
+ const masks = getLbwMasks(selectedModelType, selectedLoraBlockType);
135
+ return LBW_BLOCKS[selectedModelType || ModelTypes.Unknown].filter((_, i) => masks[i] === 1);
136
+ }
137
+
138
+ /**
139
+ * Returns the mask array for LBW blocks for the selected model and block type.
140
+ * Each value in the array (1 or 0) indicates whether the corresponding block is enabled.
141
+ * @param selectedModelType - The selected model type.
142
+ * @param selectedLoraBlockType - The selected LoRA block type.
143
+ * @returns An array of binary values (1=enabled, 0=disabled).
144
+ */
145
+ export function getLbwMasks(selectedModelType: ModelTypes, selectedLoraBlockType: LoraBlockTypes): number[] {
146
+ return LBW_MASK_DEFINE[selectedModelType || ModelTypes.Unknown][selectedLoraBlockType || LoraBlockTypes.Unknown];
147
+ }
148
+
149
+ /**
150
+ * Returns the block groups configuration for the selected model and block type.
151
+ * @param selectedModelType - The selected model type.
152
+ * @param selectedLoraBlockType - The selected LoRA block type.
153
+ * @returns An array of block group identifiers.
154
+ */
155
+ export function getLbwBlockGroups(selectedModelType: ModelTypes, selectedLoraBlockType: LoraBlockTypes): string[][] {
156
+ return lbwGroupConfig[selectedModelType || ModelTypes.Unknown][selectedLoraBlockType || LoraBlockTypes.Unknown];
157
+ }
158
+
159
+ /**
160
+ * Returns the XYZ label for the selected model and block type.
161
+ * @param selectedModelType - The selected model type.
162
+ * @param selectedLoraBlockType - The selected LoRA block type.
163
+ * @returns The XYZ identifier string.
164
+ */
165
+ export function getXyzLabel(selectedModelType: ModelTypes, selectedLoraBlockType: LoraBlockTypes): string {
166
+ return XYZ_PRESETS[selectedModelType || ModelTypes.Unknown][selectedLoraBlockType || LoraBlockTypes.Unknown];
167
+ }