Compare commits

...

144 Commits

Author SHA1 Message Date
AUTOMATIC1111 bef51aed03 Merge branch 'release_candidate' 2024-03-02 07:03:13 +03:00
AUTOMATIC1111 1398485789 update changelog 2024-03-02 07:00:08 +03:00
AUTOMATIC1111 141a17e969 style changes for #14979 2024-03-02 06:55:03 +03:00
AUTOMATIC1111 da67afe5f6 call apply_alpha_schedule_override in load_model_weights for #14979 2024-03-02 06:54:58 +03:00
AUTOMATIC1111 28bc85a20a Merge pull request #14979 from drhead/refiner_cumprod_fix
Protect alphas_cumprod during refiner switchover
2024-03-02 06:54:55 +03:00
AUTOMATIC1111 978c7fadb3 Merge pull request #15039 from light-and-ray/dat_cmd_flag
dat cmd flag
2024-03-02 06:54:52 +03:00
AUTOMATIC1111 d558cb69b0 Merge pull request #15065 from light-and-ray/resizeHandle_handle_double_tap
resizeHandle handle double tap
2024-03-02 06:54:49 +03:00
AUTOMATIC1111 0b07a6cf26 Merge pull request #15035 from AUTOMATIC1111/fix/normalized-filepath-absolute
Use `absolute` path for normalized filepath
2024-03-02 06:54:48 +03:00
AUTOMATIC1111 024a32a09b Merge pull request #15012 from light-and-ray/register_tmp_file-also-with-mtime
register_tmp_file also for mtime
2024-03-02 06:54:46 +03:00
AUTOMATIC1111 b47756385d Merge pull request #15010 from light-and-ray/fix_resize-handle_for_vertical_layout
Fix resize-handle visability for vertical layout (mobile)
2024-03-02 06:54:44 +03:00
AUTOMATIC1111 eaf5e0289a update cahngelog 2024-02-26 07:37:26 +03:00
AUTOMATIC1111 f97e3548e5 Merge pull request #15006 from imnodb/master
fix: the `split_threshold` parameter does not work when running Split oversized images
2024-02-26 07:35:42 +03:00
AUTOMATIC1111 e651ca8adb Merge pull request #15004 from light-and-ray/ResizeHandleRow_-_allow_overriden_column_scale_parametr
ResizeHandleRow - allow overriden column scale parametr
2024-02-26 07:35:36 +03:00
AUTOMATIC1111 78e421e4ea Merge pull request #14995 from dtlnor/14591-bug-the-categories-layout-is-different-when-localization-is-on
Fix #14591 using translated content to do categories mapping
2024-02-26 07:35:32 +03:00
AUTOMATIC1111 a10c8df876 Merge pull request #14973 from AUTOMATIC1111/Fix-new-oft-boft
Fix the OFT/BOFT bugs when using new LyCORIS implementation
2024-02-26 07:35:28 +03:00
AUTOMATIC1111 3069716510 update changelog 2024-02-22 23:00:14 +03:00
AUTOMATIC1111 dfab42c949 Merge pull request #15002 from light-and-ray/support_resizable_columns_for_touch_(tablets)
support resizable columns for touch (tablets)
2024-02-22 22:59:44 +03:00
AUTOMATIC1111 6bc35be10a update changelog 2024-02-22 21:29:58 +03:00
AUTOMATIC1111 b53989b195 update changelog 2024-02-22 21:29:10 +03:00
AUTOMATIC1111 726aaea0fe make extra network card description plaintext by default, with an option to re-enable HTML as it was 2024-02-22 21:27:28 +03:00
AUTOMATIC1111 052fbde3ac possible fix for reload button not appearing in some cases for extra networks. 2024-02-22 10:48:30 +03:00
AUTOMATIC1111 140d58b512 Merge pull request #14966 from light-and-ray/avoid_doble_upscaling_in_inpaint
[bug] avoid doble upscaling in inpaint
2024-02-19 18:06:16 +03:00
AUTOMATIC1111 532c340829 update changelog 2024-02-19 10:07:57 +03:00
AUTOMATIC1111 92ab0ef7d6 Merge pull request #14871 from v0xie/boft
Support inference with LyCORIS BOFT networks
2024-02-19 10:05:44 +03:00
AUTOMATIC1111 c7808825b1 update changelog 2024-02-17 21:38:19 +03:00
AUTOMATIC1111 cb52279c3e Merge pull request #14947 from AUTOMATIC1111/open-button
option "open image button" open the actual dir
2024-02-17 21:31:22 +03:00
AUTOMATIC1111 3345218439 Update comment for Pad prompt/negative prompt v0 to add a warning about truncation, make it override the v1 implementation 2024-02-17 13:21:37 +03:00
AUTOMATIC1111 4652fc5ac3 prevent escape button causing an interrupt when no generation has been made yet 2024-02-17 11:41:30 +03:00
AUTOMATIC1111 310d6b9075 update changelog 2024-02-17 11:35:36 +03:00
AUTOMATIC1111 db19c46d6d lint 2024-02-17 10:32:10 +03:00
AUTOMATIC1111 1466daeafc Disable prompt token counters option actually disables token counting rather than just hiding results.
Disable prompt token counters option does not require reload UI.
token counters do not become visible until they are positioned correctly.
2024-02-17 10:31:16 +03:00
AUTOMATIC1111 dd1641ecc4 fix an exception when filtering extra networks very early 2024-02-17 09:46:04 +03:00
AUTOMATIC1111 7dae6bb3b5 fix search UI invisible in an extra network tab that just loaded 2024-02-17 09:45:48 +03:00
AUTOMATIC1111 2e1b61e590 change condition for scheduleAfterScriptsCallbacks() to properly reflect the needed amount of search fields 2024-02-17 09:45:03 +03:00
AUTOMATIC1111 f293dbbf97 Merge pull request #14900 from AUTOMATIC1111/fix-css-color-extra-network-control--enabled
fix extra-network-control--enabled color
2024-02-17 09:00:06 +03:00
AUTOMATIC1111 bf08a5b75e Merge pull request #14910 from analysisjp/wsl2_flx
fixed webui.sh issue that occurred in WSL environment (fix: #14883)
2024-02-17 08:59:41 +03:00
AUTOMATIC1111 48ce0379bc Merge pull request #14916 from light-and-ray/use_original_document_title
Use original App Title in progress bar
2024-02-17 08:57:56 +03:00
AUTOMATIC1111 d235aa068d Merge pull request #14932 from AUTOMATIC1111/fix/esc-interrupt
Only trigger interrupt on `Esc` when interrupt button visible
2024-02-17 08:57:25 +03:00
AUTOMATIC1111 ce57a6c6db Merge pull request #14933 from AUTOMATIC1111/fix/graceful-mtime-hash-cache-exception
Gracefully handle mtime read exception from cache
2024-02-17 08:56:48 +03:00
AUTOMATIC1111 d70632a7cf Merge pull request #14934 from AUTOMATIC1111/fix/normalize-cmd-arg-paths
Normalize command-line argument paths
2024-02-17 08:54:06 +03:00
AUTOMATIC1111 4333ecc43f Merge pull request #14939 from AUTOMATIC1111/Fix-extranetworks-search-reload
Fix the bugs that search/reload will disappear when using other ExtraNetworks extensions
2024-02-17 08:51:11 +03:00
AUTOMATIC1111 a56125b0a8 Merge pull request #14930 from RedDeltas/feat/launch_utils/file_mode_for_clone
Added core.filemode=false so doesn't track changes in file permission…
2024-02-17 08:48:37 +03:00
Kohaku-Blueleaf 23f03d4796 Update extraNetworks.js 2024-02-16 16:43:43 +08:00
catboxanon 06ab10a1be Normalize cmd arg paths
In particular, this fixes an issue on Windows where some functions
will misbehave if forward slashes are provided rather than
double backslashes.
2024-02-15 14:22:13 -05:00
catboxanon 6ee4012c0a Gracefully handle mtime read exception from cache 2024-02-15 13:31:44 -05:00
catboxanon 46988af636 Fix Esc interrupt when button not visible 2024-02-15 13:05:39 -05:00
RedDeltas 18ec22bffe Added core.filemode=false so doesn't track changes in file permissions in more restrictive environments 2024-02-15 12:26:14 +00:00
Andray 1142201a3a Use original App Title in progress bar 2024-02-14 15:26:57 +04:00
analysisjp 69f9564a6d fixed webui.sh issue that occurred in WSL environment (fix: #14883) 2024-02-13 21:49:23 +09:00
w-e-w 13fd466c18 fix extra-network-control--enabled color
also add forgotten semicolon
2024-02-12 04:07:14 +09:00
AUTOMATIC1111 b7f45e67dc add before_token_counter callback and use it for prompt comments 2024-02-11 12:56:53 +03:00
AUTOMATIC1111 02ab75b86a Count tokens of enabled styles 2024-02-11 12:40:27 +03:00
AUTOMATIC1111 f6e476d7a8 call the right function for token counter in img2img 2024-02-11 12:24:02 +03:00
AUTOMATIC1111 b531b0bbef add propmpt comments support 2024-02-11 12:23:21 +03:00
AUTOMATIC1111 e2b19900ec add infotext entry for emphasis; put emphasis into a separate file, add an option to parse but still ignore emphasis 2024-02-11 09:39:51 +03:00
AUTOMATIC1111 3732cf2f97 Merge pull request #14874 from hako-mikan/master
Add option to disable normalize embeddings after after calculating emphasis.
2024-02-11 08:34:40 +03:00
AUTOMATIC1111 2f1e2c492f Merge pull request #14873 from AUTOMATIC1111/check_extensions_list_on_apply_js_method
if extensions page not loaded, prevent apply
2024-02-11 08:29:54 +03:00
AUTOMATIC1111 860534399b Merge pull request #14879 from AUTOMATIC1111/walk_files-extensions-case-insensitive
util.walk_files extensions case insensitive
2024-02-11 08:29:05 +03:00
AUTOMATIC1111 4d46f8c25c Merge pull request #14883 from analysisjp/dev_fix_memleak_new
fix prepare_tcmalloc (fix: #14227)(Fixed memory leak issue in Ubuntu 22.04 or modern linux environment)
2024-02-11 08:28:42 +03:00
AUTOMATIC1111 5ddd5d29e5 Merge pull request #14884 from light-and-ray/ResizeHandleRow_png_info_and_train
ResizeHandleRow png_info and train
2024-02-11 08:26:20 +03:00
AUTOMATIC1111 440fff64a2 Merge pull request #14890 from AUTOMATIC1111/always-append-timestamp
Always add timestamp to displayed image
2024-02-11 08:26:00 +03:00
AUTOMATIC1111 d2246df160 Merge pull request #14885 from AUTOMATIC1111/extensions-tab-table-row-hover-highlight
extensions tab table row hover highlight
2024-02-11 08:24:41 +03:00
missionfloyd c04c4b95de Always add timestamp to displayed image 2024-02-10 14:49:08 -07:00
w-e-w 7583351760 extensions tab table row hover highlight 2024-02-10 18:09:10 +09:00
Andray 82e2e25325 ResizeHandleRow png_info and train 2024-02-10 13:00:16 +04:00
analysisjp 2ba0277b52 fix: prepare_tcmalloc (Fixed memory leak issue in Ubuntu 22.04 or modern linux environment) 2024-02-10 10:09:19 +09:00
w-e-w 542611cce4 walk_files extensions case insensitive 2024-02-10 05:39:01 +09:00
hako-mikan c3c88ca8b4 Update sd_hijack_clip.py 2024-02-10 00:18:08 +09:00
hako-mikan 6b3f7039b6 add option 2024-02-09 23:57:46 +09:00
w-e-w 6b8458eb9f if extensions page not loaded, prevent apply
since they are built-in extensions we can make the assumption that they will be at least one or more extensions

Co-Authored-By: Andray <33491867+light-and-ray@users.noreply.github.com>
2024-02-09 23:19:39 +09:00
hako-mikan 0bc7867ccd Merge branch 'AUTOMATIC1111:master' into master 2024-02-09 23:17:40 +09:00
AUTOMATIC1111 d69a7944c9 Merge pull request #14857 from light-and-ray/refresh_extensions_list
Button for refresh extensions list
2024-02-09 16:06:02 +03:00
Andray 99c6c4a51b add button for refreshing extensions list 2024-02-07 16:06:17 +04:00
AUTOMATIC1111 321b2db067 fix extra networks metadata failing to work properly when you create the .json file with metadata for the first time. 2024-02-02 22:47:51 +03:00
AUTOMATIC1111 1ff1c5be64 fix refresh button forgetting sort order for extra networks #14588 2024-02-02 20:51:54 +03:00
AUTOMATIC1111 5084b39ea5 fix checkpoint selection not working for #14588 2024-02-02 19:41:07 +03:00
AUTOMATIC1111 5904e3f6b3 fix page refresh not re-applying sort/filter for #14588
fix path sortkey not including the filename for #14588
2024-02-02 19:30:59 +03:00
AUTOMATIC1111 2600370659 fix error when editing extra networks card 2024-02-01 23:54:57 +03:00
AUTOMATIC1111 9f3ba38314 Add "Interrupting..." placeholder. 2024-02-01 22:34:29 +03:00
AUTOMATIC1111 b594f518b7 Merge pull request #14814 from AUTOMATIC1111/catch-load-style.csv-error
catch load style.csv error
2024-02-01 22:02:28 +03:00
w-e-w bbe8e02d74 catch load style.csv error 2024-02-01 15:40:15 +09:00
AUTOMATIC1111 652a7bbf80 Merge pull request #14809 from Cyberbeing/fix_upscaler_autocast_nans
Fix potential autocast NaNs in image upscale
2024-01-31 22:41:22 +03:00
AUTOMATIC1111 1b0931fd92 Merge pull request #14803 from AUTOMATIC1111/create_submit_box-tooltip
add tooltip create_submit_box
2024-01-31 22:39:50 +03:00
AUTOMATIC1111 96b550430a Merge pull request #14801 from wangshuai09/npu_support
Add NPU Support
2024-01-31 22:39:29 +03:00
Cyberbeing 74b214a92a Fix potential autocast NaNs in image upscale 2024-01-30 22:32:31 -08:00
wangshuai09 cc3f604310 Update 2024-01-31 12:29:58 +08:00
w-e-w c4255d12f7 add tooltip create_submit_box 2024-01-31 04:36:11 +09:00
wangshuai09 74ff85a1a1 Merge branch 'dev' into npu_support 2024-01-30 19:15:41 +08:00
AUTOMATIC1111 ce168ab5db Merge pull request #14791 from AUTOMATIC1111/fix-mha-manual-cast
Fix dtype error in MHA layer/change dtype checking mechanism for manual cast
2024-01-29 20:39:06 +03:00
Kohaku-Blueleaf f9ba7e648a Revert "Try to reverse the dtype checking mechanism"
This reverts commit d243e24f53.
2024-01-29 22:54:12 +08:00
Kohaku-Blueleaf d243e24f53 Try to reverse the dtype checking mechanism 2024-01-29 22:49:45 +08:00
Kohaku-Blueleaf 6e7f0860f7 linting 2024-01-29 22:46:43 +08:00
Kohaku-Blueleaf 750dd6014a Fix potential bugs 2024-01-29 22:27:53 +08:00
AUTOMATIC1111 6484053037 Merge pull request #14782 from AUTOMATIC1111/safetensors-0.4.2
Bump safetensors' version to 0.4.2
2024-01-29 16:35:07 +03:00
wangshuai09 ec124607f4 Add NPU Support 2024-01-29 19:25:06 +08:00
AUTOMATIC1111 baaf39b6f9 fix the typo -- thanks Cyberbeing 2024-01-29 10:20:27 +03:00
Kohaku-Blueleaf 2cb1b65309 Bump safetensors' version to 0.4.2 2024-01-28 22:18:46 +08:00
AUTOMATIC1111 757dda9ade Add Pad conds v0 option 2024-01-27 22:30:47 +03:00
AUTOMATIC1111 e717eaff86 Merge pull request #14773 from AUTOMATIC1111/rework-set_named_arg
rework set_named_arg
2024-01-27 20:54:47 +03:00
w-e-w eae0bb89fd set_named_arg fuzzy option 2024-01-27 21:55:52 +09:00
AUTOMATIC1111 0a3a83382f Merge pull request #14775 from AUTOMATIC1111/fix-CLIP-Interrogator-topN-regex
fix CLIP Interrogator topN regex
2024-01-27 14:36:27 +03:00
w-e-w 486aeda3a7 fix CLIP Interrogator topN regex
Co-Authored-By: Martin Rizzo <60830236+martin-rizzo@users.noreply.github.com>
2024-01-27 19:35:07 +09:00
AUTOMATIC1111 7ea0859362 Merge pull request #14728 from AUTOMATIC1111/another-Hr-button-fix-
Another hr button fix
2024-01-27 10:06:40 +03:00
w-e-w 2996e43ff7 fix txt2img_upscale
use force_enable_hr to set p.enable_hr = True
allow Script.setup() have access to the correct value

add a comment for p.txt2img_upscale
2024-01-27 14:55:47 +09:00
w-e-w 36d1fefc19 rework set_named_arg
change identifying a script from using Scripts class name to Scripts internal name an
as not all Script have unique names

raise RuntimeError when there's issue
2024-01-27 14:42:52 +09:00
AUTOMATIC1111 4d5db58a3e Merge pull request #14767 from AUTOMATIC1111/minor-fix-to-#14525
minor fix to #14525
2024-01-26 19:04:13 +03:00
w-e-w 47cf92039b minor fix to #14525 2024-01-26 17:16:53 +09:00
AUTOMATIC1111 c6c9d12275 Merge pull request #14754 from AUTOMATIC1111/xyz-filter-blank-for-number-axes
XYZ filter blank for number axes
2024-01-26 00:41:24 +03:00
w-e-w 0466ee2a83 xyz filter blank for number axes 2024-01-25 05:45:45 +09:00
AUTOMATIC1111 19c95de8eb Merge pull request #14715 from stefanbenten/sb/embedding-refresh
modules/api/api.py: add api endpoint to refresh embeddings list
2024-01-23 22:35:41 +03:00
AUTOMATIC1111 358e9e2847 Merge pull request #14726 from v0xie/fix-oft-device
Fix kohya-ss OFT network wrong device for eye and constraint
2024-01-23 22:34:24 +03:00
AUTOMATIC1111 c17f7ee694 Merge pull request #14707 from AUTOMATIC1111/multi-styles-base-styles-file
re-work multi --styles-file
2024-01-23 22:22:37 +03:00
AUTOMATIC1111 de5a8c5cb4 add an option to not overlay original image for inpainting for #14727 2024-01-23 22:19:38 +03:00
AUTOMATIC1111 bac30eb3e7 Merge pull request #14740 from light-and-ray/make_extras_tab_collumns_resizable
Make extras tab columns resizable
2024-01-23 22:00:56 +03:00
Andray 695e24dbce make extras tab collumns resizable 2024-01-23 21:56:49 +04:00
AUTOMATIC1111 94d4b3c8e7 lint 2024-01-23 00:36:31 +03:00
AUTOMATIC1111 f4e931f18f put extra networks controls row into the tabs UI element for #14588 2024-01-22 23:20:30 +03:00
AUTOMATIC1111 569dc1919c Merge pull request #14588 from Sj-Si/feature/extra-networks-tree-view
Feature: Extra Networks Tree View
2024-01-22 22:24:06 +03:00
v0xie fd383140cf fix: wrong devices for eye and constraint 2024-01-22 02:52:34 -08:00
Sj-Si 26e1cd7ec4 Remove unnecessary template and simplify tree list. 2024-01-21 11:34:08 -05:00
Sj-Si d7d3166a27 Fix broken scrollbars 2024-01-21 11:27:24 -05:00
Stefan Benten 2974b9cee9 modules/api/api.py: add api endpoint to refresh embeddings list 2024-01-21 14:05:47 +01:00
w-e-w 25e8273d2f re-work multi --styles-file
--styles-file change to append str
--styles-file is [] then defaults to [styles.csv]

--styles-file accepts paths or paths with wildcard "*"

the first `--styles-file` entry is use as the default styles file path
if filename a wildcard then the first matching file is used
if no match is found, create a new "styles.csv" in the same dir as the first path

when saving a new style it will be save in the default styles file
when saving a existing style, it will be saved to file it belongs to

order of the styles files in the styles dropdown can be controlled to a certain degree by the order of --styles-file
2024-01-21 03:53:58 +09:00
Sj-Si b67a49441f Add option in settings to enable/disable tree view by default. 2024-01-20 13:28:37 -05:00
Sj-Si 2310cd66e5 Add toggle button for tree view. Use default settings for sortmode and direction. 2024-01-20 11:43:45 -05:00
Sj-Si 69f4f148dc Fix various bugs including refresh bug. 2024-01-18 12:13:33 -05:00
Sj-Si 50e444fa1d Fix missing important style. 2024-01-18 12:13:09 -05:00
Sj-Si f25c81a744 Fix embeddings add/remove to/from prompt on click bugs. 2024-01-17 22:38:51 -05:00
Sj-Si ccee26b065 fix bugs 2024-01-16 14:54:07 -05:00
Sj-Si 4f96267033 Finish cleanup. 2024-01-16 13:35:01 -05:00
Sj-Si 1fdc18e6a0 Run linting 2024-01-15 18:01:13 -05:00
Sj-Si f49c220c03 Move extra network tab buttons into tree view; 2024-01-15 17:34:44 -05:00
Sj-Si d88424ef2a fix bugs. introduce new ones. 2024-01-15 13:40:47 -05:00
Sj-Si 019f6e3c5a fix linting 2024-01-14 13:49:39 -05:00
Sj-Si 261972f929 fix search box handling. sort maybe broken not sure. need to bug test and finish cleanup. 2024-01-14 13:39:01 -05:00
Sj-Si 02e6963325 continue cleanup and redesign. 2024-01-13 13:16:39 -05:00
Sj-Si 036500223d Merge changes from dev 2024-01-11 16:37:35 -05:00
Sj-Si 0726a6e12e Finish base layout. Fix bugs. Need to test for stability and clean up. 2024-01-11 15:06:57 -05:00
Sj-Si 3db6938caa begin redesign of tree module. 2024-01-10 18:11:48 -05:00
Sj-Si 34fc215249 fix linting 2024-01-08 14:23:01 -05:00
Sj-Si 67a70ad112 fix indentation 2024-01-08 14:11:24 -05:00
Sj-Si df8aa69a99 Add tree-view display for extra networks. 2024-01-08 14:10:03 -05:00
hako-mikan 816096e642 Merge branch 'dev' into master 2023-11-09 21:57:57 +09:00
hako-mikan 6b9795849d Fix model switch bug 2023-11-09 20:23:37 +09:00
68 changed files with 2366 additions and 690 deletions
-2
View File
@@ -86,8 +86,6 @@ module.exports = {
// imageviewer.js // imageviewer.js
modalPrevImage: "readonly", modalPrevImage: "readonly",
modalNextImage: "readonly", modalNextImage: "readonly",
// token-counters.js
setupTokenCounters: "readonly",
// localStorage.js // localStorage.js
localSet: "readonly", localSet: "readonly",
localGet: "readonly", localGet: "readonly",
+132
View File
@@ -1,3 +1,134 @@
## 1.8.0-RC
### Features:
* Update torch to version 2.1.2
* Soft Inpainting ([#14208](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14208))
* FP8 support ([#14031](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14031), [#14327](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14327))
* Support for SDXL-Inpaint Model ([#14390](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14390))
* Use Spandrel for upscaling and face restoration architectures ([#14425](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14425), [#14467](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14467), [#14473](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14473), [#14474](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14474), [#14477](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14477), [#14476](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14476), [#14484](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14484), [#14500](https://github.com/AUTOMATIC1111/stable-difusion-webui/pull/14500), [#14501](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14501), [#14504](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14504), [#14524](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14524), [#14809](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14809))
* Automatic backwards version compatibility (when loading infotexts from old images with program version specified, will add compatibility settings)
* Implement zero terminal SNR noise schedule option (**[SEED BREAKING CHANGE](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Seed-breaking-changes#180-dev-170-225-2024-01-01---zero-terminal-snr-noise-schedule-option)**, [#14145](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14145), [#14979](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14979))
* Add a [✨] button to run hires fix on selected image in the gallery (with help from [#14598](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14598), [#14626](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14626), [#14728](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14728))
* [Separate assets repository](https://github.com/AUTOMATIC1111/stable-diffusion-webui-assets); serve fonts locally rather than from google's servers
* Official LCM Sampler Support ([#14583](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14583))
* Add support for DAT upscaler models ([#14690](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14690), [#15039](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15039))
* Extra Networks Tree View ([#14588](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14588), [#14900](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14900))
* NPU Support ([#14801](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14801))
* Propmpt comments support
### Minor:
* Allow pasting in WIDTHxHEIGHT strings into the width/height fields ([#14296](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14296))
* add option: Live preview in full page image viewer ([#14230](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14230), [#14307](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14307))
* Add keyboard shortcuts for generate/skip/interrupt ([#14269](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14269))
* Better TCMALLOC support on different platforms ([#14227](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14227), [#14883](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14883), [#14910](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14910))
* Lora not found warning ([#14464](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14464))
* Adding negative prompts to Loras in extra networks ([#14475](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14475))
* xyz_grid: allow varying the seed along an axis separate from axis options ([#12180](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12180))
* option to convert VAE to bfloat16 (implementation of [#9295](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/9295))
* Better IPEX support ([#14229](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14229), [#14353](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14353), [#14559](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14559), [#14562](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14562), [#14597](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14597))
* Option to interrupt after current generation rather than immediately ([#13653](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/13653), [#14659](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14659))
* Fullscreen Preview control fading/disable ([#14291](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14291))
* Finer settings freezing control ([#13789](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/13789))
* Increase Upscaler Limits ([#14589](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14589))
* Adjust brush size with hotkeys ([#14638](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14638))
* Add checkpoint info to csv log file when saving images ([#14663](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14663))
* Make more columns resizable ([#14740](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14740), [#14884](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14884))
* Add an option to not overlay original image for inpainting for #14727
* Add Pad conds v0 option to support same generation with DDIM as before 1.6.0
* Add "Interrupting..." placeholder.
* Button for refresh extensions list ([#14857](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14857))
* Add an option to disable normalization after calculating emphasis. ([#14874](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14874))
* When counting tokens, also include enabled styles (can be disabled in settings to revert to previous behavior)
* Configuration for the [📂] button for image gallery ([#14947](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14947))
* Support inference with LyCORIS BOFT networks ([#14871](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14871), [#14973](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14973))
* support resizable columns for touch (tablets) ([#15002](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15002))
### Extensions and API:
* Removed packages from requirements: basicsr, gfpgan, realesrgan; as well as their dependencies: absl-py, addict, beautifulsoup4, future, gdown, grpcio, importlib-metadata, lmdb, lpips, Markdown, platformdirs, PySocks, soupsieve, tb-nightly, tensorboard-data-server, tomli, Werkzeug, yapf, zipp, soupsieve
* Enable task ids for API ([#14314](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14314))
* add override_settings support for infotext API
* rename generation_parameters_copypaste module to infotext_utils
* prevent crash due to Script __init__ exception ([#14407](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14407))
* Bump numpy to 1.26.2 ([#14471](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14471))
* Add utility to inspect a model's dtype/device ([#14478](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14478))
* Implement general forward method for all method in built-in lora ext ([#14547](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14547))
* Execute model_loaded_callback after moving to target device ([#14563](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14563))
* Add self to CFGDenoiserParams ([#14573](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14573))
* Allow TLS with API only mode (--nowebui) ([#14593](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14593))
* New callback: postprocess_image_after_composite ([#14657](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14657))
* modules/api/api.py: add api endpoint to refresh embeddings list ([#14715](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14715))
* set_named_arg ([#14773](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14773))
* add before_token_counter callback and use it for prompt comments
* ResizeHandleRow - allow overriden column scale parameter ([#15004](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15004))
### Performance
* Massive performance improvement for extra networks directories with a huge number of files in them in an attempt to tackle #14507 ([#14528](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14528))
* Reduce unnecessary re-indexing extra networks directory ([#14512](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14512))
* Avoid unnecessary `isfile`/`exists` calls ([#14527](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14527))
### Bug Fixes:
* fix multiple bugs related to styles multi-file support ([#14203](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14203), [#14276](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14276), [#14707](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14707))
* Lora fixes ([#14300](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14300), [#14237](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14237), [#14546](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14546), [#14726](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14726))
* Re-add setting lost as part of e294e46 ([#14266](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14266))
* fix extras caption BLIP ([#14330](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14330))
* include infotext into saved init image for img2img ([#14452](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14452))
* xyz grid handle axis_type is None ([#14394](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14394))
* Update Added (Fixed) IPV6 Functionality When there is No Webui Argument Passed webui.py ([#14354](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14354))
* fix API thread safe issues of txt2img and img2img ([#14421](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14421))
* handle selectable script_index is None ([#14487](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14487))
* handle config.json failed to load ([#14525](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14525), [#14767](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14767))
* paste infotext cast int as float ([#14523](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14523))
* Ensure GRADIO_ANALYTICS_ENABLED is set early enough ([#14537](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14537))
* Fix logging configuration again ([#14538](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14538))
* Handle CondFunc exception when resolving attributes ([#14560](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14560))
* Fix extras big batch crashes ([#14699](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14699))
* Fix using wrong model caused by alias ([#14655](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14655))
* Add # to the invalid_filename_chars list ([#14640](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14640))
* Fix extension check for requirements ([#14639](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14639))
* Fix tab indexes are reset after restart UI ([#14637](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14637))
* Fix nested manual cast ([#14689](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14689))
* Keep postprocessing upscale selected tab after restart ([#14702](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14702))
* XYZ grid: filter out blank vals when axis is int or float type (like int axis seed) ([#14754](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14754))
* fix CLIP Interrogator topN regex ([#14775](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14775))
* Fix dtype error in MHA layer/change dtype checking mechanism for manual cast ([#14791](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14791))
* catch load style.csv error ([#14814](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14814))
* fix error when editing extra networks card
* fix extra networks metadata failing to work properly when you create the .json file with metadata for the first time.
* util.walk_files extensions case insensitive ([#14879](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14879))
* if extensions page not loaded, prevent apply ([#14873](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14873))
* call the right function for token counter in img2img
* Fix the bugs that search/reload will disappear when using other ExtraNetworks extensions ([#14939](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14939))
* Gracefully handle mtime read exception from cache ([#14933](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14933))
* Only trigger interrupt on `Esc` when interrupt button visible ([#14932](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14932))
* Disable prompt token counters option actually disables token counting rather than just hiding results.
* avoid doble upscaling in inpaint ([#14966](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14966))
* Fix #14591 using translated content to do categories mapping ([#14995](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14995))
* fix: the `split_threshold` parameter does not work when running Split oversized images ([#15006](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15006))
* Fix resize-handle for mobile ([#15010](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15010), [#15065](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15065))
### Other:
* Assign id for "extra_options". Replace numeric field with slider. ([#14270](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14270))
* change state dict comparison to ref compare ([#14216](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14216))
* Bump torch-rocm to 5.6/5.7 ([#14293](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14293))
* Base output path off data path ([#14446](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14446))
* reorder training preprocessing modules in extras tab ([#14367](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14367))
* Remove `cleanup_models` code ([#14472](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14472))
* only rewrite ui-config when there is change ([#14352](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14352))
* Fix lint issue from 501993eb ([#14495](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14495))
* Update README.md ([#14548](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14548))
* hires button, fix seeds ()
* Logging: set formatter correctly for fallback logger too ([#14618](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14618))
* Read generation info from infotexts rather than json for internal needs (save, extract seed from generated pic) ([#14645](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14645))
* improve get_crop_region ([#14709](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14709))
* Bump safetensors' version to 0.4.2 ([#14782](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14782))
* add tooltip create_submit_box ([#14803](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14803))
* extensions tab table row hover highlight ([#14885](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14885))
* Always add timestamp to displayed image ([#14890](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14890))
* Added core.filemode=false so doesn't track changes in file permission… ([#14930](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14930))
* Normalize command-line argument paths ([#14934](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14934), [#15035](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15035))
* Use original App Title in progress bar ([#14916](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14916))
* register_tmp_file also for mtime ([#15012](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15012))
## 1.7.0 ## 1.7.0
### Features: ### Features:
@@ -41,6 +172,7 @@
* add FP32 fallback support on sd_vae_approx ([#14046](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14046)) * add FP32 fallback support on sd_vae_approx ([#14046](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14046))
* support XYZ scripts / split hires path from unet ([#14126](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14126)) * support XYZ scripts / split hires path from unet ([#14126](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14126))
* allow use of mutiple styles csv files ([#14125](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14125)) * allow use of mutiple styles csv files ([#14125](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14125))
* make extra network card description plaintext by default, with an option (Treat card description as HTML) to re-enable HTML as it was (originally by [#13241](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/13241))
### Extensions and API: ### Extensions and API:
* update gradio to 3.41.2 * update gradio to 3.41.2
+50 -14
View File
@@ -1,6 +1,5 @@
import torch import torch
import network import network
from lyco_helpers import factorization
from einops import rearrange from einops import rearrange
@@ -22,20 +21,28 @@ class NetworkModuleOFT(network.NetworkModule):
self.org_module: list[torch.Module] = [self.sd_module] self.org_module: list[torch.Module] = [self.sd_module]
self.scale = 1.0 self.scale = 1.0
self.is_R = False
self.is_boft = False
# kohya-ss # kohya-ss/New LyCORIS OFT/BOFT
if "oft_blocks" in weights.w.keys(): if "oft_blocks" in weights.w.keys():
self.is_kohya = True
self.oft_blocks = weights.w["oft_blocks"] # (num_blocks, block_size, block_size) self.oft_blocks = weights.w["oft_blocks"] # (num_blocks, block_size, block_size)
self.alpha = weights.w["alpha"] # alpha is constraint self.alpha = weights.w.get("alpha", None) # alpha is constraint
self.dim = self.oft_blocks.shape[0] # lora dim self.dim = self.oft_blocks.shape[0] # lora dim
# LyCORIS # Old LyCORIS OFT
elif "oft_diag" in weights.w.keys(): elif "oft_diag" in weights.w.keys():
self.is_kohya = False self.is_R = True
self.oft_blocks = weights.w["oft_diag"] self.oft_blocks = weights.w["oft_diag"]
# self.alpha is unused # self.alpha is unused
self.dim = self.oft_blocks.shape[1] # (num_blocks, block_size, block_size) self.dim = self.oft_blocks.shape[1] # (num_blocks, block_size, block_size)
# LyCORIS BOFT
if self.oft_blocks.dim() == 4:
self.is_boft = True
self.rescale = weights.w.get('rescale', None)
if self.rescale is not None:
self.rescale = self.rescale.reshape(-1, *[1]*(self.org_module[0].weight.dim() - 1))
is_linear = type(self.sd_module) in [torch.nn.Linear, torch.nn.modules.linear.NonDynamicallyQuantizableLinear] is_linear = type(self.sd_module) in [torch.nn.Linear, torch.nn.modules.linear.NonDynamicallyQuantizableLinear]
is_conv = type(self.sd_module) in [torch.nn.Conv2d] is_conv = type(self.sd_module) in [torch.nn.Conv2d]
is_other_linear = type(self.sd_module) in [torch.nn.MultiheadAttention] # unsupported is_other_linear = type(self.sd_module) in [torch.nn.MultiheadAttention] # unsupported
@@ -47,27 +54,34 @@ class NetworkModuleOFT(network.NetworkModule):
elif is_other_linear: elif is_other_linear:
self.out_dim = self.sd_module.embed_dim self.out_dim = self.sd_module.embed_dim
if self.is_kohya:
self.constraint = self.alpha * self.out_dim
self.num_blocks = self.dim self.num_blocks = self.dim
self.block_size = self.out_dim // self.dim self.block_size = self.out_dim // self.dim
else: self.constraint = (0 if self.alpha is None else self.alpha) * self.out_dim
if self.is_R:
self.constraint = None self.constraint = None
self.block_size, self.num_blocks = factorization(self.out_dim, self.dim) self.block_size = self.dim
self.num_blocks = self.out_dim // self.dim
elif self.is_boft:
self.boft_m = self.oft_blocks.shape[0]
self.num_blocks = self.oft_blocks.shape[1]
self.block_size = self.oft_blocks.shape[2]
self.boft_b = self.block_size
def calc_updown(self, orig_weight): def calc_updown(self, orig_weight):
oft_blocks = self.oft_blocks.to(orig_weight.device) oft_blocks = self.oft_blocks.to(orig_weight.device)
eye = torch.eye(self.block_size, device=self.oft_blocks.device) eye = torch.eye(self.block_size, device=oft_blocks.device)
if self.is_kohya: if not self.is_R:
block_Q = oft_blocks - oft_blocks.transpose(1, 2) # ensure skew-symmetric orthogonal matrix block_Q = oft_blocks - oft_blocks.transpose(-1, -2) # ensure skew-symmetric orthogonal matrix
if self.constraint != 0:
norm_Q = torch.norm(block_Q.flatten()) norm_Q = torch.norm(block_Q.flatten())
new_norm_Q = torch.clamp(norm_Q, max=self.constraint) new_norm_Q = torch.clamp(norm_Q, max=self.constraint.to(oft_blocks.device))
block_Q = block_Q * ((new_norm_Q + 1e-8) / (norm_Q + 1e-8)) block_Q = block_Q * ((new_norm_Q + 1e-8) / (norm_Q + 1e-8))
oft_blocks = torch.matmul(eye + block_Q, (eye - block_Q).float().inverse()) oft_blocks = torch.matmul(eye + block_Q, (eye - block_Q).float().inverse())
R = oft_blocks.to(orig_weight.device) R = oft_blocks.to(orig_weight.device)
if not self.is_boft:
# This errors out for MultiheadAttention, might need to be handled up-stream # This errors out for MultiheadAttention, might need to be handled up-stream
merged_weight = rearrange(orig_weight, '(k n) ... -> k n ...', k=self.num_blocks, n=self.block_size) merged_weight = rearrange(orig_weight, '(k n) ... -> k n ...', k=self.num_blocks, n=self.block_size)
merged_weight = torch.einsum( merged_weight = torch.einsum(
@@ -76,6 +90,28 @@ class NetworkModuleOFT(network.NetworkModule):
merged_weight merged_weight
) )
merged_weight = rearrange(merged_weight, 'k m ... -> (k m) ...') merged_weight = rearrange(merged_weight, 'k m ... -> (k m) ...')
else:
# TODO: determine correct value for scale
scale = 1.0
m = self.boft_m
b = self.boft_b
r_b = b // 2
inp = orig_weight
for i in range(m):
bi = R[i] # b_num, b_size, b_size
if i == 0:
# Apply multiplier/scale and rescale into first weight
bi = bi * scale + (1 - scale) * eye
inp = rearrange(inp, "(c g k) ... -> (c k g) ...", g=2, k=2**i * r_b)
inp = rearrange(inp, "(d b) ... -> d b ...", b=b)
inp = torch.einsum("b i j, b j ... -> b i ...", bi, inp)
inp = rearrange(inp, "d b ... -> (d b) ...")
inp = rearrange(inp, "(c k g) ... -> (c g k) ...", g=2, k=2**i * r_b)
merged_weight = inp
# Rescale mechanism
if self.rescale is not None:
merged_weight = self.rescale.to(merged_weight) * merged_weight
updown = merged_weight.to(orig_weight.device) - orig_weight.to(merged_weight.dtype) updown = merged_weight.to(orig_weight.device) - orig_weight.to(merged_weight.dtype)
output_shape = orig_weight.shape output_shape = orig_weight.shape
+3 -2
View File
@@ -1,7 +1,8 @@
import os import os
from modules import paths from modules import paths
from modules.paths_internal import normalized_filepath
def preload(parser): def preload(parser):
parser.add_argument("--lora-dir", type=str, help="Path to directory with Lora networks.", default=os.path.join(paths.models_path, 'Lora')) parser.add_argument("--lora-dir", type=normalized_filepath, help="Path to directory with Lora networks.", default=os.path.join(paths.models_path, 'Lora'))
parser.add_argument("--lyco-dir-backcompat", type=str, help="Path to directory with LyCORIS networks (for backawards compatibility; can also use --lyco-dir).", default=os.path.join(paths.models_path, 'LyCORIS')) parser.add_argument("--lyco-dir-backcompat", type=normalized_filepath, help="Path to directory with LyCORIS networks (for backawards compatibility; can also use --lyco-dir).", default=os.path.join(paths.models_path, 'LyCORIS'))
@@ -24,13 +24,16 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
alias = lora_on_disk.get_alias() alias = lora_on_disk.get_alias()
search_terms = [self.search_terms_from_path(lora_on_disk.filename)]
if lora_on_disk.hash:
search_terms.append(lora_on_disk.hash)
item = { item = {
"name": name, "name": name,
"filename": lora_on_disk.filename, "filename": lora_on_disk.filename,
"shorthash": lora_on_disk.shorthash, "shorthash": lora_on_disk.shorthash,
"preview": self.find_preview(path), "preview": self.find_preview(path),
"description": self.find_description(path), "description": self.find_description(path),
"search_term": self.search_terms_from_path(lora_on_disk.filename) + " " + (lora_on_disk.hash or ""), "search_terms": search_terms,
"local_preview": f"{path}.{shared.opts.samples_format}", "local_preview": f"{path}.{shared.opts.samples_format}",
"metadata": lora_on_disk.metadata, "metadata": lora_on_disk.metadata,
"sort_keys": {'default': index, **self.get_sort_keys(lora_on_disk.filename)}, "sort_keys": {'default': index, **self.get_sort_keys(lora_on_disk.filename)},
+6 -11
View File
@@ -1,14 +1,9 @@
<div class='card' style={style} onclick={card_clicked} data-name="{name}" {sort_keys}> <div class="card" style="{style}" onclick="{card_clicked}" data-name="{name}" {sort_keys}>
{background_image} {background_image}
<div class="button-row"> <div class="button-row">{copy_path_button}{metadata_button}{edit_button}</div>
{metadata_button} <div class="actions">
{edit_button} <div class="additional">{search_terms}</div>
</div> <span class="name">{name}</span>
<div class='actions'> <span class="description">{description}</span>
<div class='additional'>
<span style="display:none" class='search_term{search_only}'>{search_term}</span>
</div>
<span class='name'>{name}</span>
<span class='description'>{description}</span>
</div> </div>
</div> </div>
@@ -0,0 +1,5 @@
<div class="copy-path-button card-button"
title="Copy path to clipboard"
onclick="extraNetworksCopyCardPath(event, '{filename}')"
data-clipboard-text="{filename}">
</div>
@@ -0,0 +1,4 @@
<div class="edit-button card-button"
title="Edit metadata"
onclick="extraNetworksEditUserMetadata(event, '{tabname}', '{extra_networks_tabname}', '{name}')">
</div>
+4
View File
@@ -0,0 +1,4 @@
<div class="metadata-button card-button"
title="Show internal metadata"
onclick="extraNetworksRequestMetadata(event, '{extra_networks_tabname}', '{name}')">
</div>
+55
View File
@@ -0,0 +1,55 @@
<div id='{tabname}_{extra_networks_tabname}_pane' class='extra-network-pane'>
<div class="extra-network-control" id="{tabname}_{extra_networks_tabname}_controls" style="display:none" >
<div class="extra-network-control--search">
<input
id="{tabname}_{extra_networks_tabname}_extra_search"
class="extra-network-control--search-text"
type="search"
placeholder="Filter files"
>
</div>
<div
id="{tabname}_{extra_networks_tabname}_extra_sort"
class="extra-network-control--sort"
data-sortmode="{data_sortmode}"
data-sortkey="{data_sortkey}"
title="Sort by path"
onclick="extraNetworksControlSortOnClick(event, '{tabname}', '{extra_networks_tabname}');"
>
<i class="extra-network-control--sort-icon"></i>
</div>
<div
id="{tabname}_{extra_networks_tabname}_extra_sort_dir"
class="extra-network-control--sort-dir"
data-sortdir="{data_sortdir}"
title="Sort ascending"
onclick="extraNetworksControlSortDirOnClick(event, '{tabname}', '{extra_networks_tabname}');"
>
<i class="extra-network-control--sort-dir-icon"></i>
</div>
<div
id="{tabname}_{extra_networks_tabname}_extra_tree_view"
class="extra-network-control--tree-view {tree_view_btn_extra_class}"
title="Enable Tree View"
onclick="extraNetworksControlTreeViewOnClick(event, '{tabname}', '{extra_networks_tabname}');"
>
<i class="extra-network-control--tree-view-icon"></i>
</div>
<div
id="{tabname}_{extra_networks_tabname}_extra_refresh"
class="extra-network-control--refresh"
title="Refresh page"
onclick="extraNetworksControlRefreshOnClick(event, '{tabname}', '{extra_networks_tabname}');"
>
<i class="extra-network-control--refresh-icon"></i>
</div>
</div>
<div class="extra-network-pane-content">
<div id='{tabname}_{extra_networks_tabname}_tree' class='extra-network-tree {tree_view_div_extra_class}'>
{tree_html}
</div>
<div id='{tabname}_{extra_networks_tabname}_cards' class='extra-network-cards'>
{items_html}
</div>
</div>
</div>
+23
View File
@@ -0,0 +1,23 @@
<span data-filterable-item-text hidden>{search_terms}</span>
<div class="tree-list-content {subclass}"
type="button"
onclick="extraNetworksTreeOnClick(event, '{tabname}', '{extra_networks_tabname}');{onclick_extra}"
data-path="{data_path}"
data-hash="{data_hash}"
>
<span class='tree-list-item-action tree-list-item-action--leading'>
{action_list_item_action_leading}
</span>
<span class="tree-list-item-visual tree-list-item-visual--leading">
{action_list_item_visual_leading}
</span>
<span class="tree-list-item-label tree-list-item-label--truncate">
{action_list_item_label}
</span>
<span class="tree-list-item-visual tree-list-item-visual--trailing">
{action_list_item_visual_trailing}
</span>
<span class="tree-list-item-action tree-list-item-action--trailing">
{action_list_item_action_trailing}
</span>
</div>
+5 -2
View File
@@ -2,8 +2,11 @@
function extensions_apply(_disabled_list, _update_list, disable_all) { function extensions_apply(_disabled_list, _update_list, disable_all) {
var disable = []; var disable = [];
var update = []; var update = [];
const extensions_input = gradioApp().querySelectorAll('#extensions input[type="checkbox"]');
gradioApp().querySelectorAll('#extensions input[type="checkbox"]').forEach(function(x) { if (extensions_input.length == 0) {
throw Error("Extensions page not yet loaded.");
}
extensions_input.forEach(function(x) {
if (x.name.startsWith("enable_") && !x.checked) { if (x.name.startsWith("enable_") && !x.checked) {
disable.push(x.name.substring(7)); disable.push(x.name.substring(7));
} }
+320 -84
View File
@@ -16,56 +16,71 @@ function toggleCss(key, css, enable) {
} }
function setupExtraNetworksForTab(tabname) { function setupExtraNetworksForTab(tabname) {
gradioApp().querySelector('#' + tabname + '_extra_tabs').classList.add('extra-networks'); function registerPrompt(tabname, id) {
var textarea = gradioApp().querySelector("#" + id + " > label > textarea");
var tabs = gradioApp().querySelector('#' + tabname + '_extra_tabs > div'); if (!activePromptTextarea[tabname]) {
var searchDiv = gradioApp().getElementById(tabname + '_extra_search'); activePromptTextarea[tabname] = textarea;
var search = searchDiv.querySelector('textarea'); }
var sort = gradioApp().getElementById(tabname + '_extra_sort');
var sortOrder = gradioApp().getElementById(tabname + '_extra_sortorder');
var refresh = gradioApp().getElementById(tabname + '_extra_refresh');
var showDirsDiv = gradioApp().getElementById(tabname + '_extra_show_dirs');
var showDirs = gradioApp().querySelector('#' + tabname + '_extra_show_dirs input');
var promptContainer = gradioApp().querySelector('.prompt-container-compact#' + tabname + '_prompt_container');
var negativePrompt = gradioApp().querySelector('#' + tabname + '_neg_prompt');
tabs.appendChild(searchDiv); textarea.addEventListener("focus", function() {
tabs.appendChild(sort); activePromptTextarea[tabname] = textarea;
tabs.appendChild(sortOrder); });
tabs.appendChild(refresh); }
tabs.appendChild(showDirsDiv);
var applyFilter = function() { var tabnav = gradioApp().querySelector('#' + tabname + '_extra_tabs > div.tab-nav');
var controlsDiv = document.createElement('DIV');
controlsDiv.classList.add('extra-networks-controls-div');
tabnav.appendChild(controlsDiv);
tabnav.insertBefore(controlsDiv, null);
var this_tab = gradioApp().querySelector('#' + tabname + '_extra_tabs');
this_tab.querySelectorAll(":scope > [id^='" + tabname + "_']").forEach(function(elem) {
// tabname_full = {tabname}_{extra_networks_tabname}
var tabname_full = elem.id;
var search = gradioApp().querySelector("#" + tabname_full + "_extra_search");
var sort_mode = gradioApp().querySelector("#" + tabname_full + "_extra_sort");
var sort_dir = gradioApp().querySelector("#" + tabname_full + "_extra_sort_dir");
var refresh = gradioApp().querySelector("#" + tabname_full + "_extra_refresh");
// If any of the buttons above don't exist, we want to skip this iteration of the loop.
if (!search || !sort_mode || !sort_dir || !refresh) {
return; // `return` is equivalent of `continue` but for forEach loops.
}
var applyFilter = function(force) {
var searchTerm = search.value.toLowerCase(); var searchTerm = search.value.toLowerCase();
gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card').forEach(function(elem) { gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card').forEach(function(elem) {
var searchOnly = elem.querySelector('.search_only'); var searchOnly = elem.querySelector('.search_only');
var text = elem.querySelector('.name').textContent.toLowerCase() + " " + elem.querySelector('.search_term').textContent.toLowerCase(); var text = Array.prototype.map.call(elem.querySelectorAll('.search_terms'), function(t) {
return t.textContent.toLowerCase();
}).join(" ");
var visible = text.indexOf(searchTerm) != -1; var visible = text.indexOf(searchTerm) != -1;
if (searchOnly && searchTerm.length < 4) { if (searchOnly && searchTerm.length < 4) {
visible = false; visible = false;
} }
if (visible) {
elem.style.display = visible ? "" : "none"; elem.classList.remove("hidden");
} else {
elem.classList.add("hidden");
}
}); });
applySort(); applySort(force);
}; };
var applySort = function() { var applySort = function(force) {
var cards = gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card'); var cards = gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card');
var reverse = sort_dir.dataset.sortdir == "Descending";
var reverse = sortOrder.classList.contains("sortReverse"); var sortKey = sort_mode.dataset.sortmode.toLowerCase().replace("sort", "").replaceAll(" ", "_").replace(/_+$/, "").trim() || "name";
var sortKey = sort.querySelector("input").value.toLowerCase().replace("sort", "").replaceAll(" ", "_").replace(/_+$/, "").trim() || "name";
sortKey = "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1); sortKey = "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1);
var sortKeyStore = sortKey + "-" + (reverse ? "Descending" : "Ascending") + "-" + cards.length; var sortKeyStore = sortKey + "-" + (reverse ? "Descending" : "Ascending") + "-" + cards.length;
if (sortKeyStore == sort.dataset.sortkey) { if (sortKeyStore == sort_mode.dataset.sortkey && !force) {
return; return;
} }
sort.dataset.sortkey = sortKeyStore; sort_mode.dataset.sortkey = sortKeyStore;
cards.forEach(function(card) { cards.forEach(function(card) {
card.originalParentElement = card.parentElement; card.originalParentElement = card.parentElement;
@@ -92,23 +107,21 @@ function setupExtraNetworksForTab(tabname) {
}; };
search.addEventListener("input", applyFilter); search.addEventListener("input", applyFilter);
sortOrder.addEventListener("click", function() {
sortOrder.classList.toggle("sortReverse");
applySort(); applySort();
});
applyFilter(); applyFilter();
extraNetworksApplySort[tabname_full] = applySort;
extraNetworksApplyFilter[tabname_full] = applyFilter;
extraNetworksApplySort[tabname] = applySort; var controls = gradioApp().querySelector("#" + tabname_full + "_controls");
extraNetworksApplyFilter[tabname] = applyFilter; controlsDiv.insertBefore(controls, null);
var showDirsUpdate = function() { if (elem.style.display != "none") {
var css = '#' + tabname + '_extra_tabs .extra-network-subdirs { display: none; }'; extraNetworksShowControlsForPage(tabname, tabname_full);
toggleCss(tabname + '_extra_show_dirs_style', css, !showDirs.checked); }
localSet('extra-networks-show-dirs', showDirs.checked ? 1 : 0); });
};
showDirs.checked = localGet('extra-networks-show-dirs', 1) == 1; registerPrompt(tabname, tabname + "_prompt");
showDirs.addEventListener("change", showDirsUpdate); registerPrompt(tabname, tabname + "_neg_prompt");
showDirsUpdate();
} }
function extraNetworksMovePromptToTab(tabname, id, showPrompt, showNegativePrompt) { function extraNetworksMovePromptToTab(tabname, id, showPrompt, showNegativePrompt) {
@@ -137,21 +150,42 @@ function extraNetworksMovePromptToTab(tabname, id, showPrompt, showNegativePromp
} }
function extraNetworksUrelatedTabSelected(tabname) { // called from python when user selects an unrelated tab (generate) function extraNetworksShowControlsForPage(tabname, tabname_full) {
extraNetworksMovePromptToTab(tabname, '', false, false); gradioApp().querySelectorAll('#' + tabname + '_extra_tabs .extra-networks-controls-div > div').forEach(function(elem) {
var targetId = tabname_full + "_controls";
elem.style.display = elem.id == targetId ? "" : "none";
});
} }
function extraNetworksTabSelected(tabname, id, showPrompt, showNegativePrompt) { // called from python when user selects an extra networks tab
function extraNetworksUnrelatedTabSelected(tabname) { // called from python when user selects an unrelated tab (generate)
extraNetworksMovePromptToTab(tabname, '', false, false);
extraNetworksShowControlsForPage(tabname, null);
}
function extraNetworksTabSelected(tabname, id, showPrompt, showNegativePrompt, tabname_full) { // called from python when user selects an extra networks tab
extraNetworksMovePromptToTab(tabname, id, showPrompt, showNegativePrompt); extraNetworksMovePromptToTab(tabname, id, showPrompt, showNegativePrompt);
extraNetworksShowControlsForPage(tabname, tabname_full);
} }
function applyExtraNetworkFilter(tabname) { function applyExtraNetworkFilter(tabname_full) {
setTimeout(extraNetworksApplyFilter[tabname], 1); var doFilter = function() {
var applyFunction = extraNetworksApplyFilter[tabname_full];
if (applyFunction) {
applyFunction(true);
}
};
setTimeout(doFilter, 1);
} }
function applyExtraNetworkSort(tabname) { function applyExtraNetworkSort(tabname_full) {
setTimeout(extraNetworksApplySort[tabname], 1); var doSort = function() {
extraNetworksApplySort[tabname_full](true);
};
setTimeout(doSort, 1);
} }
var extraNetworksApplyFilter = {}; var extraNetworksApplyFilter = {};
@@ -161,27 +195,8 @@ var activePromptTextarea = {};
function setupExtraNetworks() { function setupExtraNetworks() {
setupExtraNetworksForTab('txt2img'); setupExtraNetworksForTab('txt2img');
setupExtraNetworksForTab('img2img'); setupExtraNetworksForTab('img2img');
function registerPrompt(tabname, id) {
var textarea = gradioApp().querySelector("#" + id + " > label > textarea");
if (!activePromptTextarea[tabname]) {
activePromptTextarea[tabname] = textarea;
}
textarea.addEventListener("focus", function() {
activePromptTextarea[tabname] = textarea;
});
}
registerPrompt('txt2img', 'txt2img_prompt');
registerPrompt('txt2img', 'txt2img_neg_prompt');
registerPrompt('img2img', 'img2img_prompt');
registerPrompt('img2img', 'img2img_neg_prompt');
} }
onUiLoaded(setupExtraNetworks);
var re_extranet = /<([^:^>]+:[^:]+):[\d.]+>(.*)/; var re_extranet = /<([^:^>]+:[^:]+):[\d.]+>(.*)/;
var re_extranet_g = /<([^:^>]+:[^:]+):[\d.]+>/g; var re_extranet_g = /<([^:^>]+:[^:]+):[\d.]+>/g;
@@ -191,8 +206,8 @@ function tryToRemoveExtraNetworkFromPrompt(textarea, text, isNeg) {
var m = text.match(isNeg ? re_extranet_neg : re_extranet); var m = text.match(isNeg ? re_extranet_neg : re_extranet);
var replaced = false; var replaced = false;
var newTextareaText; var newTextareaText;
if (m) {
var extraTextBeforeNet = opts.extra_networks_add_text_separator; var extraTextBeforeNet = opts.extra_networks_add_text_separator;
if (m) {
var extraTextAfterNet = m[2]; var extraTextAfterNet = m[2];
var partToSearch = m[1]; var partToSearch = m[1];
var foundAtPosition = -1; var foundAtPosition = -1;
@@ -205,7 +220,6 @@ function tryToRemoveExtraNetworkFromPrompt(textarea, text, isNeg) {
} }
return found; return found;
}); });
if (foundAtPosition >= 0) { if (foundAtPosition >= 0) {
if (extraTextAfterNet && newTextareaText.substr(foundAtPosition, extraTextAfterNet.length) == extraTextAfterNet) { if (extraTextAfterNet && newTextareaText.substr(foundAtPosition, extraTextAfterNet.length) == extraTextAfterNet) {
newTextareaText = newTextareaText.substr(0, foundAtPosition) + newTextareaText.substr(foundAtPosition + extraTextAfterNet.length); newTextareaText = newTextareaText.substr(0, foundAtPosition) + newTextareaText.substr(foundAtPosition + extraTextAfterNet.length);
@@ -215,13 +229,8 @@ function tryToRemoveExtraNetworkFromPrompt(textarea, text, isNeg) {
} }
} }
} else { } else {
newTextareaText = textarea.value.replaceAll(new RegExp(text, "g"), function(found) { newTextareaText = textarea.value.replaceAll(new RegExp(`((?:${extraTextBeforeNet})?${text})`, "g"), "");
if (found == text) { replaced = (newTextareaText != textarea.value);
replaced = true;
return "";
}
return found;
});
} }
if (replaced) { if (replaced) {
@@ -233,7 +242,6 @@ function tryToRemoveExtraNetworkFromPrompt(textarea, text, isNeg) {
} }
function updatePromptArea(text, textArea, isNeg) { function updatePromptArea(text, textArea, isNeg) {
if (!tryToRemoveExtraNetworkFromPrompt(textArea, text, isNeg)) { if (!tryToRemoveExtraNetworkFromPrompt(textArea, text, isNeg)) {
textArea.value = textArea.value + opts.extra_networks_add_text_separator + text; textArea.value = textArea.value + opts.extra_networks_add_text_separator + text;
} }
@@ -264,13 +272,200 @@ function saveCardPreview(event, tabname, filename) {
event.preventDefault(); event.preventDefault();
} }
function extraNetworksSearchButton(tabs_id, event) { function extraNetworksTreeProcessFileClick(event, btn, tabname, extra_networks_tabname) {
var searchTextarea = gradioApp().querySelector("#" + tabs_id + ' > label > textarea'); /**
var button = event.target; * Processes `onclick` events when user clicks on files in tree.
var text = button.classList.contains("search-all") ? "" : button.textContent.trim(); *
* @param event The generated event.
* @param btn The clicked `tree-list-item` button.
* @param tabname The name of the active tab in the sd webui. Ex: txt2img, img2img, etc.
* @param extra_networks_tabname The id of the active extraNetworks tab. Ex: lora, checkpoints, etc.
*/
// NOTE: Currently unused.
return;
}
searchTextarea.value = text; function extraNetworksTreeProcessDirectoryClick(event, btn, tabname, extra_networks_tabname) {
updateInput(searchTextarea); /**
* Processes `onclick` events when user clicks on directories in tree.
*
* Here is how the tree reacts to clicks for various states:
* unselected unopened directory: Diretory is selected and expanded.
* unselected opened directory: Directory is selected.
* selected opened directory: Directory is collapsed and deselected.
* chevron is clicked: Directory is expanded or collapsed. Selected state unchanged.
*
* @param event The generated event.
* @param btn The clicked `tree-list-item` button.
* @param tabname The name of the active tab in the sd webui. Ex: txt2img, img2img, etc.
* @param extra_networks_tabname The id of the active extraNetworks tab. Ex: lora, checkpoints, etc.
*/
var ul = btn.nextElementSibling;
// This is the actual target that the user clicked on within the target button.
// We use this to detect if the chevron was clicked.
var true_targ = event.target;
function _expand_or_collapse(_ul, _btn) {
// Expands <ul> if it is collapsed, collapses otherwise. Updates button attributes.
if (_ul.hasAttribute("hidden")) {
_ul.removeAttribute("hidden");
_btn.dataset.expanded = "";
} else {
_ul.setAttribute("hidden", "");
delete _btn.dataset.expanded;
}
}
function _remove_selected_from_all() {
// Removes the `selected` attribute from all buttons.
var sels = document.querySelectorAll("div.tree-list-content");
[...sels].forEach(el => {
delete el.dataset.selected;
});
}
function _select_button(_btn) {
// Removes `data-selected` attribute from all buttons then adds to passed button.
_remove_selected_from_all();
_btn.dataset.selected = "";
}
function _update_search(_tabname, _extra_networks_tabname, _search_text) {
// Update search input with select button's path.
var search_input_elem = gradioApp().querySelector("#" + tabname + "_" + extra_networks_tabname + "_extra_search");
search_input_elem.value = _search_text;
updateInput(search_input_elem);
}
// If user clicks on the chevron, then we do not select the folder.
if (true_targ.matches(".tree-list-item-action--leading, .tree-list-item-action-chevron")) {
_expand_or_collapse(ul, btn);
} else {
// User clicked anywhere else on the button.
if ("selected" in btn.dataset && !(ul.hasAttribute("hidden"))) {
// If folder is select and open, collapse and deselect button.
_expand_or_collapse(ul, btn);
delete btn.dataset.selected;
_update_search(tabname, extra_networks_tabname, "");
} else if (!(!("selected" in btn.dataset) && !(ul.hasAttribute("hidden")))) {
// If folder is open and not selected, then we don't collapse; just select.
// NOTE: Double inversion sucks but it is the clearest way to show the branching here.
_expand_or_collapse(ul, btn);
_select_button(btn, tabname, extra_networks_tabname);
_update_search(tabname, extra_networks_tabname, btn.dataset.path);
} else {
// All other cases, just select the button.
_select_button(btn, tabname, extra_networks_tabname);
_update_search(tabname, extra_networks_tabname, btn.dataset.path);
}
}
}
function extraNetworksTreeOnClick(event, tabname, extra_networks_tabname) {
/**
* Handles `onclick` events for buttons within an `extra-network-tree .tree-list--tree`.
*
* Determines whether the clicked button in the tree is for a file entry or a directory
* then calls the appropriate function.
*
* @param event The generated event.
* @param tabname The name of the active tab in the sd webui. Ex: txt2img, img2img, etc.
* @param extra_networks_tabname The id of the active extraNetworks tab. Ex: lora, checkpoints, etc.
*/
var btn = event.currentTarget;
var par = btn.parentElement;
if (par.dataset.treeEntryType === "file") {
extraNetworksTreeProcessFileClick(event, btn, tabname, extra_networks_tabname);
} else {
extraNetworksTreeProcessDirectoryClick(event, btn, tabname, extra_networks_tabname);
}
}
function extraNetworksControlSortOnClick(event, tabname, extra_networks_tabname) {
/**
* Handles `onclick` events for the Sort Mode button.
*
* Modifies the data attributes of the Sort Mode button to cycle between
* various sorting modes.
*
* @param event The generated event.
* @param tabname The name of the active tab in the sd webui. Ex: txt2img, img2img, etc.
* @param extra_networks_tabname The id of the active extraNetworks tab. Ex: lora, checkpoints, etc.
*/
var curr_mode = event.currentTarget.dataset.sortmode;
var el_sort_dir = gradioApp().querySelector("#" + tabname + "_" + extra_networks_tabname + "_extra_sort_dir");
var sort_dir = el_sort_dir.dataset.sortdir;
if (curr_mode == "path") {
event.currentTarget.dataset.sortmode = "name";
event.currentTarget.dataset.sortkey = "sortName-" + sort_dir + "-640";
event.currentTarget.setAttribute("title", "Sort by filename");
} else if (curr_mode == "name") {
event.currentTarget.dataset.sortmode = "date_created";
event.currentTarget.dataset.sortkey = "sortDate_created-" + sort_dir + "-640";
event.currentTarget.setAttribute("title", "Sort by date created");
} else if (curr_mode == "date_created") {
event.currentTarget.dataset.sortmode = "date_modified";
event.currentTarget.dataset.sortkey = "sortDate_modified-" + sort_dir + "-640";
event.currentTarget.setAttribute("title", "Sort by date modified");
} else {
event.currentTarget.dataset.sortmode = "path";
event.currentTarget.dataset.sortkey = "sortPath-" + sort_dir + "-640";
event.currentTarget.setAttribute("title", "Sort by path");
}
applyExtraNetworkSort(tabname + "_" + extra_networks_tabname);
}
function extraNetworksControlSortDirOnClick(event, tabname, extra_networks_tabname) {
/**
* Handles `onclick` events for the Sort Direction button.
*
* Modifies the data attributes of the Sort Direction button to cycle between
* ascending and descending sort directions.
*
* @param event The generated event.
* @param tabname The name of the active tab in the sd webui. Ex: txt2img, img2img, etc.
* @param extra_networks_tabname The id of the active extraNetworks tab. Ex: lora, checkpoints, etc.
*/
if (event.currentTarget.dataset.sortdir == "Ascending") {
event.currentTarget.dataset.sortdir = "Descending";
event.currentTarget.setAttribute("title", "Sort descending");
} else {
event.currentTarget.dataset.sortdir = "Ascending";
event.currentTarget.setAttribute("title", "Sort ascending");
}
applyExtraNetworkSort(tabname + "_" + extra_networks_tabname);
}
function extraNetworksControlTreeViewOnClick(event, tabname, extra_networks_tabname) {
/**
* Handles `onclick` events for the Tree View button.
*
* Toggles the tree view in the extra networks pane.
*
* @param event The generated event.
* @param tabname The name of the active tab in the sd webui. Ex: txt2img, img2img, etc.
* @param extra_networks_tabname The id of the active extraNetworks tab. Ex: lora, checkpoints, etc.
*/
gradioApp().getElementById(tabname + "_" + extra_networks_tabname + "_tree").classList.toggle("hidden");
event.currentTarget.classList.toggle("extra-network-control--enabled");
}
function extraNetworksControlRefreshOnClick(event, tabname, extra_networks_tabname) {
/**
* Handles `onclick` events for the Refresh Page button.
*
* In order to actually call the python functions in `ui_extra_networks.py`
* to refresh the page, we created an empty gradio button in that file with an
* event handler that refreshes the page. So what this function here does
* is it manually raises a `click` event on that button.
*
* @param event The generated event.
* @param tabname The name of the active tab in the sd webui. Ex: txt2img, img2img, etc.
* @param extra_networks_tabname The id of the active extraNetworks tab. Ex: lora, checkpoints, etc.
*/
var btn_refresh_internal = gradioApp().getElementById(tabname + "_" + extra_networks_tabname + "_extra_refresh_internal");
btn_refresh_internal.dispatchEvent(new Event("click"));
} }
var globalPopup = null; var globalPopup = null;
@@ -348,6 +543,11 @@ function requestGet(url, data, handler, errorHandler) {
xhr.send(js); xhr.send(js);
} }
function extraNetworksCopyCardPath(event, path) {
navigator.clipboard.writeText(path);
event.stopPropagation();
}
function extraNetworksRequestMetadata(event, extraPage, cardName) { function extraNetworksRequestMetadata(event, extraPage, cardName) {
var showError = function() { var showError = function() {
extraNetworksShowMetadata("there was an error getting metadata"); extraNetworksShowMetadata("there was an error getting metadata");
@@ -409,3 +609,39 @@ window.addEventListener("keydown", function(event) {
closePopup(); closePopup();
} }
}); });
/**
* Setup custom loading for this script.
* We need to wait for all of our HTML to be generated in the extra networks tabs
* before we can actually run the `setupExtraNetworks` function.
* The `onUiLoaded` function actually runs before all of our extra network tabs are
* finished generating. Thus we needed this new method.
*
*/
var uiAfterScriptsCallbacks = [];
var uiAfterScriptsTimeout = null;
var executedAfterScripts = false;
function scheduleAfterScriptsCallbacks() {
clearTimeout(uiAfterScriptsTimeout);
uiAfterScriptsTimeout = setTimeout(function() {
executeCallbacks(uiAfterScriptsCallbacks);
}, 200);
}
onUiLoaded(function() {
var mutationObserver = new MutationObserver(function(m) {
let existingSearchfields = gradioApp().querySelectorAll("[id$='_extra_search']").length;
let neededSearchfields = gradioApp().querySelectorAll("[id$='_extra_tabs'] > .tab-nav > button").length - 2;
if (!executedAfterScripts && existingSearchfields >= neededSearchfields) {
mutationObserver.disconnect();
executedAfterScripts = true;
scheduleAfterScriptsCallbacks();
}
});
mutationObserver.observe(gradioApp(), {childList: true, subtree: true});
});
uiAfterScriptsCallbacks.push(setupExtraNetworks);
+8 -1
View File
@@ -45,8 +45,15 @@ function formatTime(secs) {
} }
} }
var originalAppTitle = undefined;
onUiLoaded(function() {
originalAppTitle = document.title;
});
function setTitle(progress) { function setTitle(progress) {
var title = 'Stable Diffusion'; var title = originalAppTitle;
if (opts.show_progress_in_title && progress) { if (opts.show_progress_in_title && progress) {
title = '[' + progress.trim() + '] ' + title; title = '[' + progress.trim() + '] ' + title;
+59 -20
View File
@@ -1,8 +1,8 @@
(function() { (function() {
const GRADIO_MIN_WIDTH = 320; const GRADIO_MIN_WIDTH = 320;
const GRID_TEMPLATE_COLUMNS = '1fr 16px 1fr';
const PAD = 16; const PAD = 16;
const DEBOUNCE_TIME = 100; const DEBOUNCE_TIME = 100;
const DOUBLE_TAP_DELAY = 200; //ms
const R = { const R = {
tracking: false, tracking: false,
@@ -11,6 +11,7 @@
leftCol: null, leftCol: null,
leftColStartWidth: null, leftColStartWidth: null,
screenX: null, screenX: null,
lastTapTime: null,
}; };
let resizeTimer; let resizeTimer;
@@ -23,21 +24,17 @@
function displayResizeHandle(parent) { function displayResizeHandle(parent) {
if (window.innerWidth < GRADIO_MIN_WIDTH * 2 + PAD * 4) { if (window.innerWidth < GRADIO_MIN_WIDTH * 2 + PAD * 4) {
parent.style.display = 'flex'; parent.style.display = 'flex';
if (R.handle != null) { parent.resizeHandle.style.display = "none";
R.handle.style.opacity = '0';
}
return false; return false;
} else { } else {
parent.style.display = 'grid'; parent.style.display = 'grid';
if (R.handle != null) { parent.resizeHandle.style.display = "block";
R.handle.style.opacity = '100';
}
return true; return true;
} }
} }
function afterResize(parent) { function afterResize(parent) {
if (displayResizeHandle(parent) && parent.style.gridTemplateColumns != GRID_TEMPLATE_COLUMNS) { if (displayResizeHandle(parent) && parent.style.gridTemplateColumns != parent.style.originalGridTemplateColumns) {
const oldParentWidth = R.parentWidth; const oldParentWidth = R.parentWidth;
const newParentWidth = parent.offsetWidth; const newParentWidth = parent.offsetWidth;
const widthL = parseInt(parent.style.gridTemplateColumns.split(' ')[0]); const widthL = parseInt(parent.style.gridTemplateColumns.split(' ')[0]);
@@ -52,6 +49,14 @@
} }
function setup(parent) { function setup(parent) {
function onDoubleClick(evt) {
evt.preventDefault();
evt.stopPropagation();
parent.style.gridTemplateColumns = parent.style.originalGridTemplateColumns;
}
const leftCol = parent.firstElementChild; const leftCol = parent.firstElementChild;
const rightCol = parent.lastElementChild; const rightCol = parent.lastElementChild;
@@ -59,14 +64,30 @@
parent.style.display = 'grid'; parent.style.display = 'grid';
parent.style.gap = '0'; parent.style.gap = '0';
parent.style.gridTemplateColumns = GRID_TEMPLATE_COLUMNS; const gridTemplateColumns = `${parent.children[0].style.flexGrow}fr ${PAD}px ${parent.children[1].style.flexGrow}fr`;
parent.style.gridTemplateColumns = gridTemplateColumns;
parent.style.originalGridTemplateColumns = gridTemplateColumns;
const resizeHandle = document.createElement('div'); const resizeHandle = document.createElement('div');
resizeHandle.classList.add('resize-handle'); resizeHandle.classList.add('resize-handle');
parent.insertBefore(resizeHandle, rightCol); parent.insertBefore(resizeHandle, rightCol);
parent.resizeHandle = resizeHandle;
resizeHandle.addEventListener('mousedown', (evt) => { ['mousedown', 'touchstart'].forEach((eventType) => {
resizeHandle.addEventListener(eventType, (evt) => {
if (eventType.startsWith('mouse')) {
if (evt.button !== 0) return; if (evt.button !== 0) return;
} else {
if (evt.changedTouches.length !== 1) return;
const currentTime = new Date().getTime();
if (R.lastTapTime && currentTime - R.lastTapTime <= DOUBLE_TAP_DELAY) {
onDoubleClick(evt);
return;
}
R.lastTapTime = currentTime;
}
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
@@ -76,37 +97,54 @@
R.tracking = true; R.tracking = true;
R.parent = parent; R.parent = parent;
R.parentWidth = parent.offsetWidth; R.parentWidth = parent.offsetWidth;
R.handle = resizeHandle;
R.leftCol = leftCol; R.leftCol = leftCol;
R.leftColStartWidth = leftCol.offsetWidth; R.leftColStartWidth = leftCol.offsetWidth;
if (eventType.startsWith('mouse')) {
R.screenX = evt.screenX; R.screenX = evt.screenX;
} else {
R.screenX = evt.changedTouches[0].screenX;
}
});
}); });
resizeHandle.addEventListener('dblclick', (evt) => { resizeHandle.addEventListener('dblclick', onDoubleClick);
evt.preventDefault();
evt.stopPropagation();
parent.style.gridTemplateColumns = GRID_TEMPLATE_COLUMNS;
});
afterResize(parent); afterResize(parent);
} }
window.addEventListener('mousemove', (evt) => { ['mousemove', 'touchmove'].forEach((eventType) => {
window.addEventListener(eventType, (evt) => {
if (eventType.startsWith('mouse')) {
if (evt.button !== 0) return; if (evt.button !== 0) return;
} else {
if (evt.changedTouches.length !== 1) return;
}
if (R.tracking) { if (R.tracking) {
if (eventType.startsWith('mouse')) {
evt.preventDefault(); evt.preventDefault();
}
evt.stopPropagation(); evt.stopPropagation();
const delta = R.screenX - evt.screenX; let delta = 0;
if (eventType.startsWith('mouse')) {
delta = R.screenX - evt.screenX;
} else {
delta = R.screenX - evt.changedTouches[0].screenX;
}
const leftColWidth = Math.max(Math.min(R.leftColStartWidth - delta, R.parent.offsetWidth - GRADIO_MIN_WIDTH - PAD), GRADIO_MIN_WIDTH); const leftColWidth = Math.max(Math.min(R.leftColStartWidth - delta, R.parent.offsetWidth - GRADIO_MIN_WIDTH - PAD), GRADIO_MIN_WIDTH);
setLeftColGridTemplate(R.parent, leftColWidth); setLeftColGridTemplate(R.parent, leftColWidth);
} }
}); });
});
window.addEventListener('mouseup', (evt) => { ['mouseup', 'touchend'].forEach((eventType) => {
window.addEventListener(eventType, (evt) => {
if (eventType.startsWith('mouse')) {
if (evt.button !== 0) return; if (evt.button !== 0) return;
} else {
if (evt.changedTouches.length !== 1) return;
}
if (R.tracking) { if (R.tracking) {
evt.preventDefault(); evt.preventDefault();
@@ -117,6 +155,7 @@
document.body.classList.remove('resizing'); document.body.classList.remove('resizing');
} }
}); });
});
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
+2 -2
View File
@@ -55,8 +55,8 @@ onOptionsChanged(function() {
}); });
opts._categories.forEach(function(x) { opts._categories.forEach(function(x) {
var section = x[0]; var section = localization[x[0]] ?? x[0];
var category = x[1]; var category = localization[x[1]] ?? x[1];
var span = document.createElement('SPAN'); var span = document.createElement('SPAN');
span.textContent = category; span.textContent = category;
+22 -10
View File
@@ -48,11 +48,6 @@ function setupTokenCounting(id, id_counter, id_button) {
var counter = gradioApp().getElementById(id_counter); var counter = gradioApp().getElementById(id_counter);
var textarea = gradioApp().querySelector(`#${id} > label > textarea`); var textarea = gradioApp().querySelector(`#${id} > label > textarea`);
if (opts.disable_token_counters) {
counter.style.display = "none";
return;
}
if (counter.parentElement == prompt.parentElement) { if (counter.parentElement == prompt.parentElement) {
return; return;
} }
@@ -61,15 +56,32 @@ function setupTokenCounting(id, id_counter, id_button) {
prompt.parentElement.style.position = "relative"; prompt.parentElement.style.position = "relative";
var func = onEdit(id, textarea, 800, function() { var func = onEdit(id, textarea, 800, function() {
if (counter.classList.contains("token-counter-visible")) {
gradioApp().getElementById(id_button)?.click(); gradioApp().getElementById(id_button)?.click();
}
}); });
promptTokenCountUpdateFunctions[id] = func; promptTokenCountUpdateFunctions[id] = func;
promptTokenCountUpdateFunctions[id_button] = func; promptTokenCountUpdateFunctions[id_button] = func;
} }
function setupTokenCounters() { function toggleTokenCountingVisibility(id, id_counter, id_button) {
setupTokenCounting('txt2img_prompt', 'txt2img_token_counter', 'txt2img_token_button'); var counter = gradioApp().getElementById(id_counter);
setupTokenCounting('txt2img_neg_prompt', 'txt2img_negative_token_counter', 'txt2img_negative_token_button');
setupTokenCounting('img2img_prompt', 'img2img_token_counter', 'img2img_token_button'); counter.style.display = opts.disable_token_counters ? "none" : "block";
setupTokenCounting('img2img_neg_prompt', 'img2img_negative_token_counter', 'img2img_negative_token_button'); counter.classList.toggle("token-counter-visible", !opts.disable_token_counters);
} }
function runCodeForTokenCounters(fun) {
fun('txt2img_prompt', 'txt2img_token_counter', 'txt2img_token_button');
fun('txt2img_neg_prompt', 'txt2img_negative_token_counter', 'txt2img_negative_token_button');
fun('img2img_prompt', 'img2img_token_counter', 'img2img_token_button');
fun('img2img_neg_prompt', 'img2img_negative_token_counter', 'img2img_negative_token_button');
}
onUiLoaded(function() {
runCodeForTokenCounters(setupTokenCounting);
});
onOptionsChanged(function() {
runCodeForTokenCounters(toggleTokenCountingVisibility);
});
+11 -4
View File
@@ -119,9 +119,18 @@ function create_submit_args(args) {
return res; return res;
} }
function setSubmitButtonsVisibility(tabname, showInterrupt, showSkip, showInterrupting) {
gradioApp().getElementById(tabname + '_interrupt').style.display = showInterrupt ? "block" : "none";
gradioApp().getElementById(tabname + '_skip').style.display = showSkip ? "block" : "none";
gradioApp().getElementById(tabname + '_interrupting').style.display = showInterrupting ? "block" : "none";
}
function showSubmitButtons(tabname, show) { function showSubmitButtons(tabname, show) {
gradioApp().getElementById(tabname + '_interrupt').style.display = show ? "none" : "block"; setSubmitButtonsVisibility(tabname, !show, !show, false);
gradioApp().getElementById(tabname + '_skip').style.display = show ? "none" : "block"; }
function showSubmitInterruptingPlaceholder(tabname) {
setSubmitButtonsVisibility(tabname, false, true, true);
} }
function showRestoreProgressButton(tabname, show) { function showRestoreProgressButton(tabname, show) {
@@ -310,8 +319,6 @@ onAfterUiUpdate(function() {
}); });
json_elem.parentElement.style.display = "none"; json_elem.parentElement.style.display = "none";
setupTokenCounters();
}); });
onOptionsChanged(function() { onOptionsChanged(function() {
+5
View File
@@ -230,6 +230,7 @@ class Api:
self.add_api_route("/sdapi/v1/realesrgan-models", self.get_realesrgan_models, methods=["GET"], response_model=list[models.RealesrganItem]) self.add_api_route("/sdapi/v1/realesrgan-models", self.get_realesrgan_models, methods=["GET"], response_model=list[models.RealesrganItem])
self.add_api_route("/sdapi/v1/prompt-styles", self.get_prompt_styles, methods=["GET"], response_model=list[models.PromptStyleItem]) self.add_api_route("/sdapi/v1/prompt-styles", self.get_prompt_styles, methods=["GET"], response_model=list[models.PromptStyleItem])
self.add_api_route("/sdapi/v1/embeddings", self.get_embeddings, methods=["GET"], response_model=models.EmbeddingsResponse) self.add_api_route("/sdapi/v1/embeddings", self.get_embeddings, methods=["GET"], response_model=models.EmbeddingsResponse)
self.add_api_route("/sdapi/v1/refresh-embeddings", self.refresh_embeddings, methods=["POST"])
self.add_api_route("/sdapi/v1/refresh-checkpoints", self.refresh_checkpoints, methods=["POST"]) self.add_api_route("/sdapi/v1/refresh-checkpoints", self.refresh_checkpoints, methods=["POST"])
self.add_api_route("/sdapi/v1/refresh-vae", self.refresh_vae, methods=["POST"]) self.add_api_route("/sdapi/v1/refresh-vae", self.refresh_vae, methods=["POST"])
self.add_api_route("/sdapi/v1/create/embedding", self.create_embedding, methods=["POST"], response_model=models.CreateResponse) self.add_api_route("/sdapi/v1/create/embedding", self.create_embedding, methods=["POST"], response_model=models.CreateResponse)
@@ -747,6 +748,10 @@ class Api:
"skipped": convert_embeddings(db.skipped_embeddings), "skipped": convert_embeddings(db.skipped_embeddings),
} }
def refresh_embeddings(self):
with self.queue_lock:
sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings(force_reload=True)
def refresh_checkpoints(self): def refresh_checkpoints(self):
with self.queue_lock: with self.queue_lock:
shared.refresh_checkpoints() shared.refresh_checkpoints()
+22 -21
View File
@@ -1,7 +1,7 @@
import argparse import argparse
import json import json
import os import os
from modules.paths_internal import models_path, script_path, data_path, extensions_dir, extensions_builtin_dir, sd_default_config, sd_model_file # noqa: F401 from modules.paths_internal import normalized_filepath, models_path, script_path, data_path, extensions_dir, extensions_builtin_dir, sd_default_config, sd_model_file # noqa: F401
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
@@ -19,21 +19,21 @@ parser.add_argument("--skip-install", action='store_true', help="launch.py argum
parser.add_argument("--dump-sysinfo", action='store_true', help="launch.py argument: dump limited sysinfo file (without information about extensions, options) to disk and quit") parser.add_argument("--dump-sysinfo", action='store_true', help="launch.py argument: dump limited sysinfo file (without information about extensions, options) to disk and quit")
parser.add_argument("--loglevel", type=str, help="log level; one of: CRITICAL, ERROR, WARNING, INFO, DEBUG", default=None) parser.add_argument("--loglevel", type=str, help="log level; one of: CRITICAL, ERROR, WARNING, INFO, DEBUG", default=None)
parser.add_argument("--do-not-download-clip", action='store_true', help="do not download CLIP model even if it's not included in the checkpoint") parser.add_argument("--do-not-download-clip", action='store_true', help="do not download CLIP model even if it's not included in the checkpoint")
parser.add_argument("--data-dir", type=str, default=os.path.dirname(os.path.dirname(os.path.realpath(__file__))), help="base path where all user data is stored") parser.add_argument("--data-dir", type=normalized_filepath, default=os.path.dirname(os.path.dirname(os.path.realpath(__file__))), help="base path where all user data is stored")
parser.add_argument("--config", type=str, default=sd_default_config, help="path to config which constructs model",) parser.add_argument("--config", type=normalized_filepath, default=sd_default_config, help="path to config which constructs model",)
parser.add_argument("--ckpt", type=str, default=sd_model_file, help="path to checkpoint of stable diffusion model; if specified, this checkpoint will be added to the list of checkpoints and loaded",) parser.add_argument("--ckpt", type=normalized_filepath, default=sd_model_file, help="path to checkpoint of stable diffusion model; if specified, this checkpoint will be added to the list of checkpoints and loaded",)
parser.add_argument("--ckpt-dir", type=str, default=None, help="Path to directory with stable diffusion checkpoints") parser.add_argument("--ckpt-dir", type=normalized_filepath, default=None, help="Path to directory with stable diffusion checkpoints")
parser.add_argument("--vae-dir", type=str, default=None, help="Path to directory with VAE files") parser.add_argument("--vae-dir", type=normalized_filepath, default=None, help="Path to directory with VAE files")
parser.add_argument("--gfpgan-dir", type=str, help="GFPGAN directory", default=('./src/gfpgan' if os.path.exists('./src/gfpgan') else './GFPGAN')) parser.add_argument("--gfpgan-dir", type=normalized_filepath, help="GFPGAN directory", default=('./src/gfpgan' if os.path.exists('./src/gfpgan') else './GFPGAN'))
parser.add_argument("--gfpgan-model", type=str, help="GFPGAN model file name", default=None) parser.add_argument("--gfpgan-model", type=normalized_filepath, help="GFPGAN model file name", default=None)
parser.add_argument("--no-half", action='store_true', help="do not switch the model to 16-bit floats") parser.add_argument("--no-half", action='store_true', help="do not switch the model to 16-bit floats")
parser.add_argument("--no-half-vae", action='store_true', help="do not switch the VAE model to 16-bit floats") parser.add_argument("--no-half-vae", action='store_true', help="do not switch the VAE model to 16-bit floats")
parser.add_argument("--no-progressbar-hiding", action='store_true', help="do not hide progressbar in gradio UI (we hide it because it slows down ML if you have hardware acceleration in browser)") parser.add_argument("--no-progressbar-hiding", action='store_true', help="do not hide progressbar in gradio UI (we hide it because it slows down ML if you have hardware acceleration in browser)")
parser.add_argument("--max-batch-count", type=int, default=16, help="maximum batch count value for the UI") parser.add_argument("--max-batch-count", type=int, default=16, help="maximum batch count value for the UI")
parser.add_argument("--embeddings-dir", type=str, default=os.path.join(data_path, 'embeddings'), help="embeddings directory for textual inversion (default: embeddings)") parser.add_argument("--embeddings-dir", type=normalized_filepath, default=os.path.join(data_path, 'embeddings'), help="embeddings directory for textual inversion (default: embeddings)")
parser.add_argument("--textual-inversion-templates-dir", type=str, default=os.path.join(script_path, 'textual_inversion_templates'), help="directory with textual inversion templates") parser.add_argument("--textual-inversion-templates-dir", type=normalized_filepath, default=os.path.join(script_path, 'textual_inversion_templates'), help="directory with textual inversion templates")
parser.add_argument("--hypernetwork-dir", type=str, default=os.path.join(models_path, 'hypernetworks'), help="hypernetwork directory") parser.add_argument("--hypernetwork-dir", type=normalized_filepath, default=os.path.join(models_path, 'hypernetworks'), help="hypernetwork directory")
parser.add_argument("--localizations-dir", type=str, default=os.path.join(script_path, 'localizations'), help="localizations directory") parser.add_argument("--localizations-dir", type=normalized_filepath, default=os.path.join(script_path, 'localizations'), help="localizations directory")
parser.add_argument("--allow-code", action='store_true', help="allow custom script execution from webui") parser.add_argument("--allow-code", action='store_true', help="allow custom script execution from webui")
parser.add_argument("--medvram", action='store_true', help="enable stable diffusion model optimizations for sacrificing a little speed for low VRM usage") parser.add_argument("--medvram", action='store_true', help="enable stable diffusion model optimizations for sacrificing a little speed for low VRM usage")
parser.add_argument("--medvram-sdxl", action='store_true', help="enable --medvram optimization just for SDXL models") parser.add_argument("--medvram-sdxl", action='store_true', help="enable --medvram optimization just for SDXL models")
@@ -48,12 +48,13 @@ parser.add_argument("--ngrok", type=str, help="ngrok authtoken, alternative to g
parser.add_argument("--ngrok-region", type=str, help="does not do anything.", default="") parser.add_argument("--ngrok-region", type=str, help="does not do anything.", default="")
parser.add_argument("--ngrok-options", type=json.loads, help='The options to pass to ngrok in JSON format, e.g.: \'{"authtoken_from_env":true, "basic_auth":"user:password", "oauth_provider":"google", "oauth_allow_emails":"user@asdf.com"}\'', default=dict()) parser.add_argument("--ngrok-options", type=json.loads, help='The options to pass to ngrok in JSON format, e.g.: \'{"authtoken_from_env":true, "basic_auth":"user:password", "oauth_provider":"google", "oauth_allow_emails":"user@asdf.com"}\'', default=dict())
parser.add_argument("--enable-insecure-extension-access", action='store_true', help="enable extensions tab regardless of other options") parser.add_argument("--enable-insecure-extension-access", action='store_true', help="enable extensions tab regardless of other options")
parser.add_argument("--codeformer-models-path", type=str, help="Path to directory with codeformer model file(s).", default=os.path.join(models_path, 'Codeformer')) parser.add_argument("--codeformer-models-path", type=normalized_filepath, help="Path to directory with codeformer model file(s).", default=os.path.join(models_path, 'Codeformer'))
parser.add_argument("--gfpgan-models-path", type=str, help="Path to directory with GFPGAN model file(s).", default=os.path.join(models_path, 'GFPGAN')) parser.add_argument("--gfpgan-models-path", type=normalized_filepath, help="Path to directory with GFPGAN model file(s).", default=os.path.join(models_path, 'GFPGAN'))
parser.add_argument("--esrgan-models-path", type=str, help="Path to directory with ESRGAN model file(s).", default=os.path.join(models_path, 'ESRGAN')) parser.add_argument("--esrgan-models-path", type=normalized_filepath, help="Path to directory with ESRGAN model file(s).", default=os.path.join(models_path, 'ESRGAN'))
parser.add_argument("--bsrgan-models-path", type=str, help="Path to directory with BSRGAN model file(s).", default=os.path.join(models_path, 'BSRGAN')) parser.add_argument("--bsrgan-models-path", type=normalized_filepath, help="Path to directory with BSRGAN model file(s).", default=os.path.join(models_path, 'BSRGAN'))
parser.add_argument("--realesrgan-models-path", type=str, help="Path to directory with RealESRGAN model file(s).", default=os.path.join(models_path, 'RealESRGAN')) parser.add_argument("--realesrgan-models-path", type=normalized_filepath, help="Path to directory with RealESRGAN model file(s).", default=os.path.join(models_path, 'RealESRGAN'))
parser.add_argument("--clip-models-path", type=str, help="Path to directory with CLIP model file(s).", default=None) parser.add_argument("--dat-models-path", type=normalized_filepath, help="Path to directory with DAT model file(s).", default=os.path.join(models_path, 'DAT'))
parser.add_argument("--clip-models-path", type=normalized_filepath, help="Path to directory with CLIP model file(s).", default=None)
parser.add_argument("--xformers", action='store_true', help="enable xformers for cross attention layers") parser.add_argument("--xformers", action='store_true', help="enable xformers for cross attention layers")
parser.add_argument("--force-enable-xformers", action='store_true', help="enable xformers for cross attention layers regardless of whether the checking code thinks you can run it; do not make bug reports if this fails to work") parser.add_argument("--force-enable-xformers", action='store_true', help="enable xformers for cross attention layers regardless of whether the checking code thinks you can run it; do not make bug reports if this fails to work")
parser.add_argument("--xformers-flash-attention", action='store_true', help="enable xformers with Flash Attention to improve reproducibility (supported for SD2.x or variant only)") parser.add_argument("--xformers-flash-attention", action='store_true', help="enable xformers with Flash Attention to improve reproducibility (supported for SD2.x or variant only)")
@@ -83,18 +84,18 @@ parser.add_argument("--freeze-specific-settings", type=str, help='disable editin
parser.add_argument("--ui-settings-file", type=str, help="filename to use for ui settings", default=os.path.join(data_path, 'config.json')) parser.add_argument("--ui-settings-file", type=str, help="filename to use for ui settings", default=os.path.join(data_path, 'config.json'))
parser.add_argument("--gradio-debug", action='store_true', help="launch gradio with --debug option") parser.add_argument("--gradio-debug", action='store_true', help="launch gradio with --debug option")
parser.add_argument("--gradio-auth", type=str, help='set gradio authentication like "username:password"; or comma-delimit multiple like "u1:p1,u2:p2,u3:p3"', default=None) parser.add_argument("--gradio-auth", type=str, help='set gradio authentication like "username:password"; or comma-delimit multiple like "u1:p1,u2:p2,u3:p3"', default=None)
parser.add_argument("--gradio-auth-path", type=str, help='set gradio authentication file path ex. "/path/to/auth/file" same auth format as --gradio-auth', default=None) parser.add_argument("--gradio-auth-path", type=normalized_filepath, help='set gradio authentication file path ex. "/path/to/auth/file" same auth format as --gradio-auth', default=None)
parser.add_argument("--gradio-img2img-tool", type=str, help='does not do anything') parser.add_argument("--gradio-img2img-tool", type=str, help='does not do anything')
parser.add_argument("--gradio-inpaint-tool", type=str, help="does not do anything") parser.add_argument("--gradio-inpaint-tool", type=str, help="does not do anything")
parser.add_argument("--gradio-allowed-path", action='append', help="add path to gradio's allowed_paths, make it possible to serve files from it", default=[data_path]) parser.add_argument("--gradio-allowed-path", action='append', help="add path to gradio's allowed_paths, make it possible to serve files from it", default=[data_path])
parser.add_argument("--opt-channelslast", action='store_true', help="change memory type for stable diffusion to channels last") parser.add_argument("--opt-channelslast", action='store_true', help="change memory type for stable diffusion to channels last")
parser.add_argument("--styles-file", type=str, help="filename to use for styles", default=os.path.join(data_path, 'styles.csv')) parser.add_argument("--styles-file", type=str, action='append', help="path or wildcard path of styles files, allow multiple entries.", default=[])
parser.add_argument("--autolaunch", action='store_true', help="open the webui URL in the system's default browser upon launch", default=False) parser.add_argument("--autolaunch", action='store_true', help="open the webui URL in the system's default browser upon launch", default=False)
parser.add_argument("--theme", type=str, help="launches the UI with light or dark theme", default=None) parser.add_argument("--theme", type=str, help="launches the UI with light or dark theme", default=None)
parser.add_argument("--use-textbox-seed", action='store_true', help="use textbox for seeds in UI (no up/down, but possible to input long seeds)", default=False) parser.add_argument("--use-textbox-seed", action='store_true', help="use textbox for seeds in UI (no up/down, but possible to input long seeds)", default=False)
parser.add_argument("--disable-console-progressbars", action='store_true', help="do not output progressbars to console", default=False) parser.add_argument("--disable-console-progressbars", action='store_true', help="do not output progressbars to console", default=False)
parser.add_argument("--enable-console-prompts", action='store_true', help="does not do anything", default=False) # Legacy compatibility, use as default value shared.opts.enable_console_prompts parser.add_argument("--enable-console-prompts", action='store_true', help="does not do anything", default=False) # Legacy compatibility, use as default value shared.opts.enable_console_prompts
parser.add_argument('--vae-path', type=str, help='Checkpoint to use as VAE; setting this argument disables all settings related to VAE', default=None) parser.add_argument('--vae-path', type=normalized_filepath, help='Checkpoint to use as VAE; setting this argument disables all settings related to VAE', default=None)
parser.add_argument("--disable-safe-unpickle", action='store_true', help="disable checking pytorch models for malicious code", default=False) parser.add_argument("--disable-safe-unpickle", action='store_true', help="disable checking pytorch models for malicious code", default=False)
parser.add_argument("--api", action='store_true', help="use api=True to launch the API together with the webui (use --nowebui instead for only the API)") parser.add_argument("--api", action='store_true', help="use api=True to launch the API together with the webui (use --nowebui instead for only the API)")
parser.add_argument("--api-auth", type=str, help='Set authentication for API like "username:password"; or comma-delimit multiple like "u1:p1,u2:p2,u3:p3"', default=None) parser.add_argument("--api-auth", type=str, help='Set authentication for API like "username:password"; or comma-delimit multiple like "u1:p1,u2:p2,u3:p3"', default=None)
+21 -5
View File
@@ -3,8 +3,7 @@ import contextlib
from functools import lru_cache from functools import lru_cache
import torch import torch
from modules import errors, shared from modules import errors, shared, npu_specific
from modules import torch_utils
if sys.platform == "darwin": if sys.platform == "darwin":
from modules import mac_specific from modules import mac_specific
@@ -58,6 +57,9 @@ def get_optimal_device_name():
if has_xpu(): if has_xpu():
return xpu_specific.get_xpu_device_string() return xpu_specific.get_xpu_device_string()
if npu_specific.has_npu:
return npu_specific.get_npu_device_string()
return "cpu" return "cpu"
@@ -85,6 +87,16 @@ def torch_gc():
if has_xpu(): if has_xpu():
xpu_specific.torch_xpu_gc() xpu_specific.torch_xpu_gc()
if npu_specific.has_npu:
torch_npu_set_device()
npu_specific.torch_npu_gc()
def torch_npu_set_device():
# Work around due to bug in torch_npu, revert me after fixed, @see https://gitee.com/ascend/pytorch/issues/I8KECW?from=project-issue
if npu_specific.has_npu:
torch.npu.set_device(0)
def enable_tf32(): def enable_tf32():
if torch.cuda.is_available(): if torch.cuda.is_available():
@@ -141,7 +153,12 @@ def manual_cast_forward(target_dtype):
args = [arg.to(target_dtype) if isinstance(arg, torch.Tensor) else arg for arg in args] args = [arg.to(target_dtype) if isinstance(arg, torch.Tensor) else arg for arg in args]
kwargs = {k: v.to(target_dtype) if isinstance(v, torch.Tensor) else v for k, v in kwargs.items()} kwargs = {k: v.to(target_dtype) if isinstance(v, torch.Tensor) else v for k, v in kwargs.items()}
org_dtype = torch_utils.get_param(self).dtype org_dtype = target_dtype
for param in self.parameters():
if param.dtype != target_dtype:
org_dtype = param.dtype
break
if org_dtype != target_dtype: if org_dtype != target_dtype:
self.to(target_dtype) self.to(target_dtype)
result = self.org_forward(*args, **kwargs) result = self.org_forward(*args, **kwargs)
@@ -170,7 +187,7 @@ def manual_cast(target_dtype):
continue continue
applied = True applied = True
org_forward = module_type.forward org_forward = module_type.forward
if module_type == torch.nn.MultiheadAttention and has_xpu(): if module_type == torch.nn.MultiheadAttention:
module_type.forward = manual_cast_forward(torch.float32) module_type.forward = manual_cast_forward(torch.float32)
else: else:
module_type.forward = manual_cast_forward(target_dtype) module_type.forward = manual_cast_forward(target_dtype)
@@ -252,4 +269,3 @@ def first_time_calculation():
x = torch.zeros((1, 1, 3, 3)).to(device, dtype) x = torch.zeros((1, 1, 3, 3)).to(device, dtype)
conv2d = torch.nn.Conv2d(1, 1, (3, 3)).to(device, dtype) conv2d = torch.nn.Conv2d(1, 1, (3, 3)).to(device, dtype)
conv2d(x) conv2d(x)
+3
View File
@@ -21,7 +21,10 @@ def calculate_sha256(filename):
def sha256_from_cache(filename, title, use_addnet_hash=False): def sha256_from_cache(filename, title, use_addnet_hash=False):
hashes = cache("hashes-addnet") if use_addnet_hash else cache("hashes") hashes = cache("hashes-addnet") if use_addnet_hash else cache("hashes")
try:
ondisk_mtime = os.path.getmtime(filename) ondisk_mtime = os.path.getmtime(filename)
except FileNotFoundError:
return None
if title not in hashes: if title not in hashes:
return None return None
+3
View File
@@ -356,6 +356,9 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
if "Cache FP16 weight for LoRA" not in res and res["FP8 weight"] != "Disable": if "Cache FP16 weight for LoRA" not in res and res["FP8 weight"] != "Disable":
res["Cache FP16 weight for LoRA"] = False res["Cache FP16 weight for LoRA"] = False
if "Emphasis" not in res:
res["Emphasis"] = "Original"
infotext_versions.backcompat(res) infotext_versions.backcompat(res)
for key in skip_fields: for key in skip_fields:
+4 -1
View File
@@ -31,9 +31,12 @@ def backcompat(d):
if ver is None: if ver is None:
return return
if ver < v160: if ver < v160 and '[' in d.get('Prompt', ''):
d["Old prompt editing timelines"] = True d["Old prompt editing timelines"] = True
if ver < v160 and d.get('Sampler', '') in ('DDIM', 'PLMS'):
d["Pad conds v0"] = True
if ver < v170_tsnr: if ver < v170_tsnr:
d["Downcast alphas_cumprod"] = True d["Downcast alphas_cumprod"] = True
+2 -1
View File
@@ -142,13 +142,14 @@ def initialize_rest(*, reload_script_modules=False):
its optimization may be None because the list of optimizaers has neet been filled its optimization may be None because the list of optimizaers has neet been filled
by that time, so we apply optimization again. by that time, so we apply optimization again.
""" """
from modules import devices
devices.torch_npu_set_device()
shared.sd_model # noqa: B018 shared.sd_model # noqa: B018
if sd_hijack.current_optimizer is None: if sd_hijack.current_optimizer is None:
sd_hijack.apply_optimizations() sd_hijack.apply_optimizations()
from modules import devices
devices.first_time_calculation() devices.first_time_calculation()
if not shared.cmd_opts.skip_load_model_at_start: if not shared.cmd_opts.skip_load_model_at_start:
Thread(target=load_model).start() Thread(target=load_model).start()
+1 -1
View File
@@ -17,7 +17,7 @@ clip_model_name = 'ViT-L/14'
Category = namedtuple("Category", ["name", "topn", "items"]) Category = namedtuple("Category", ["name", "topn", "items"])
re_topn = re.compile(r"\.top(\d+)\.") re_topn = re.compile(r"\.top(\d+)$")
def category_types(): def category_types():
return [f.stem for f in Path(shared.interrogator.content_dir).glob('*.txt')] return [f.stem for f in Path(shared.interrogator.content_dir).glob('*.txt')]
+9 -2
View File
@@ -188,7 +188,7 @@ def git_clone(url, dir, name, commithash=None):
return return
try: try:
run(f'"{git}" clone "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}", live=True) run(f'"{git}" clone --config core.filemode=false "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}", live=True)
except RuntimeError: except RuntimeError:
shutil.rmtree(dir, ignore_errors=True) shutil.rmtree(dir, ignore_errors=True)
raise raise
@@ -251,7 +251,6 @@ def list_extensions(settings_file):
except Exception: except Exception:
errors.report(f'\nCould not load settings\nThe config file "{settings_file}" is likely corrupted\nIt has been moved to the "tmp/config.json"\nReverting config to default\n\n''', exc_info=True) errors.report(f'\nCould not load settings\nThe config file "{settings_file}" is likely corrupted\nIt has been moved to the "tmp/config.json"\nReverting config to default\n\n''', exc_info=True)
os.replace(settings_file, os.path.join(script_path, "tmp", "config.json")) os.replace(settings_file, os.path.join(script_path, "tmp", "config.json"))
settings = {}
disabled_extensions = set(settings.get('disabled_extensions', [])) disabled_extensions = set(settings.get('disabled_extensions', []))
disable_all_extensions = settings.get('disable_all_extensions', 'none') disable_all_extensions = settings.get('disable_all_extensions', 'none')
@@ -339,6 +338,7 @@ def prepare_environment():
torch_index_url = os.environ.get('TORCH_INDEX_URL', "https://pytorch-extension.intel.com/release-whl/stable/xpu/us/") torch_index_url = os.environ.get('TORCH_INDEX_URL', "https://pytorch-extension.intel.com/release-whl/stable/xpu/us/")
torch_command = os.environ.get('TORCH_COMMAND', f"pip install torch==2.0.0a0 intel-extension-for-pytorch==2.0.110+gitba7f6c1 --extra-index-url {torch_index_url}") torch_command = os.environ.get('TORCH_COMMAND', f"pip install torch==2.0.0a0 intel-extension-for-pytorch==2.0.110+gitba7f6c1 --extra-index-url {torch_index_url}")
requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt") requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")
requirements_file_for_npu = os.environ.get('REQS_FILE_FOR_NPU', "requirements_npu.txt")
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.23.post1') xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.23.post1')
clip_package = os.environ.get('CLIP_PACKAGE', "https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip") clip_package = os.environ.get('CLIP_PACKAGE', "https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip")
@@ -422,6 +422,13 @@ def prepare_environment():
run_pip(f"install -r \"{requirements_file}\"", "requirements") run_pip(f"install -r \"{requirements_file}\"", "requirements")
startup_timer.record("install requirements") startup_timer.record("install requirements")
if not os.path.isfile(requirements_file_for_npu):
requirements_file_for_npu = os.path.join(script_path, requirements_file_for_npu)
if "torch_npu" in torch_command and not requirements_met(requirements_file_for_npu):
run_pip(f"install -r \"{requirements_file_for_npu}\"", "requirements_for_npu")
startup_timer.record("install requirements_for_npu")
if not args.skip_install: if not args.skip_install:
run_extensions_installers(settings_file=args.ui_settings_file) run_extensions_installers(settings_file=args.ui_settings_file)
+31
View File
@@ -0,0 +1,31 @@
import importlib
import torch
from modules import shared
def check_for_npu():
if importlib.util.find_spec("torch_npu") is None:
return False
import torch_npu
try:
# Will raise a RuntimeError if no NPU is found
_ = torch_npu.npu.device_count()
return torch.npu.is_available()
except RuntimeError:
return False
def get_npu_device_string():
if shared.cmd_opts.device_id is not None:
return f"npu:{shared.cmd_opts.device_id}"
return "npu:0"
def torch_npu_gc():
with torch.npu.device(get_npu_device_string()):
torch.npu.empty_cache()
has_npu = check_for_npu()
+2
View File
@@ -198,6 +198,8 @@ class Options:
try: try:
with open(filename, "r", encoding="utf8") as file: with open(filename, "r", encoding="utf8") as file:
self.data = json.load(file) self.data = json.load(file)
except FileNotFoundError:
self.data = {}
except Exception: except Exception:
errors.report(f'\nCould not load settings\nThe config file "{filename}" is likely corrupted\nIt has been moved to the "tmp/config.json"\nReverting config to default\n\n''', exc_info=True) errors.report(f'\nCould not load settings\nThe config file "{filename}" is likely corrupted\nIt has been moved to the "tmp/config.json"\nReverting config to default\n\n''', exc_info=True)
os.replace(filename, os.path.join(script_path, "tmp", "config.json")) os.replace(filename, os.path.join(script_path, "tmp", "config.json"))
+4
View File
@@ -4,6 +4,10 @@ import argparse
import os import os
import sys import sys
import shlex import shlex
from pathlib import Path
normalized_filepath = lambda filepath: str(Path(filepath).absolute())
commandline_args = os.environ.get('COMMANDLINE_ARGS', "") commandline_args = os.environ.get('COMMANDLINE_ARGS', "")
sys.argv += shlex.split(commandline_args) sys.argv += shlex.split(commandline_args)
+15 -37
View File
@@ -74,16 +74,18 @@ def uncrop(image, dest_size, paste_loc):
def apply_overlay(image, paste_loc, overlay): def apply_overlay(image, paste_loc, overlay):
if overlay is None: if overlay is None:
return image return image, image.copy()
if paste_loc is not None: if paste_loc is not None:
image = uncrop(image, (overlay.width, overlay.height), paste_loc) image = uncrop(image, (overlay.width, overlay.height), paste_loc)
original_denoised_image = image.copy()
image = image.convert('RGBA') image = image.convert('RGBA')
image.alpha_composite(overlay) image.alpha_composite(overlay)
image = image.convert('RGB') image = image.convert('RGB')
return image return image, original_denoised_image
def create_binary_mask(image, round=True): def create_binary_mask(image, round=True):
if image.mode == 'RGBA' and image.getextrema()[-1] != (255, 255): if image.mode == 'RGBA' and image.getextrema()[-1] != (255, 255):
@@ -455,6 +457,7 @@ class StableDiffusionProcessing:
self.height, self.height,
opts.fp8_storage, opts.fp8_storage,
opts.cache_fp16_weight, opts.cache_fp16_weight,
opts.emphasis,
) )
def get_conds_with_caching(self, function, required_prompts, steps, caches, extra_network_data, hires_steps=None): def get_conds_with_caching(self, function, required_prompts, steps, caches, extra_network_data, hires_steps=None):
@@ -912,33 +915,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
if p.n_iter > 1: if p.n_iter > 1:
shared.state.job = f"Batch {n+1} out of {p.n_iter}" shared.state.job = f"Batch {n+1} out of {p.n_iter}"
def rescale_zero_terminal_snr_abar(alphas_cumprod): sd_models.apply_alpha_schedule_override(p.sd_model, p)
alphas_bar_sqrt = alphas_cumprod.sqrt()
# Store old values.
alphas_bar_sqrt_0 = alphas_bar_sqrt[0].clone()
alphas_bar_sqrt_T = alphas_bar_sqrt[-1].clone()
# Shift so the last timestep is zero.
alphas_bar_sqrt -= (alphas_bar_sqrt_T)
# Scale so the first timestep is back to the old value.
alphas_bar_sqrt *= alphas_bar_sqrt_0 / (alphas_bar_sqrt_0 - alphas_bar_sqrt_T)
# Convert alphas_bar_sqrt to betas
alphas_bar = alphas_bar_sqrt**2 # Revert sqrt
alphas_bar[-1] = 4.8973451890853435e-08
return alphas_bar
if hasattr(p.sd_model, 'alphas_cumprod') and hasattr(p.sd_model, 'alphas_cumprod_original'):
p.sd_model.alphas_cumprod = p.sd_model.alphas_cumprod_original.to(shared.device)
if opts.use_downcasted_alpha_bar:
p.extra_generation_params['Downcast alphas_cumprod'] = opts.use_downcasted_alpha_bar
p.sd_model.alphas_cumprod = p.sd_model.alphas_cumprod.half().to(shared.device)
if opts.sd_noise_schedule == "Zero Terminal SNR":
p.extra_generation_params['Noise Schedule'] = opts.sd_noise_schedule
p.sd_model.alphas_cumprod = rescale_zero_terminal_snr_abar(p.sd_model.alphas_cumprod).to(shared.device)
with devices.without_autocast() if devices.unet_needs_upcast else devices.autocast(): with devices.without_autocast() if devices.unet_needs_upcast else devices.autocast():
samples_ddim = p.sample(conditioning=p.c, unconditional_conditioning=p.uc, seeds=p.seeds, subseeds=p.subseeds, subseed_strength=p.subseed_strength, prompts=p.prompts) samples_ddim = p.sample(conditioning=p.c, unconditional_conditioning=p.uc, seeds=p.seeds, subseeds=p.subseeds, subseed_strength=p.subseed_strength, prompts=p.prompts)
@@ -1005,7 +982,13 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
image = pp.image image = pp.image
mask_for_overlay = getattr(p, "mask_for_overlay", None) mask_for_overlay = getattr(p, "mask_for_overlay", None)
overlay_image = p.overlay_images[i] if getattr(p, "overlay_images", None) is not None and i < len(p.overlay_images) else None
if not shared.opts.overlay_inpaint:
overlay_image = None
elif getattr(p, "overlay_images", None) is not None and i < len(p.overlay_images):
overlay_image = p.overlay_images[i]
else:
overlay_image = None
if p.scripts is not None: if p.scripts is not None:
ppmo = scripts.PostProcessMaskOverlayArgs(i, mask_for_overlay, overlay_image) ppmo = scripts.PostProcessMaskOverlayArgs(i, mask_for_overlay, overlay_image)
@@ -1014,7 +997,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
if p.color_corrections is not None and i < len(p.color_corrections): if p.color_corrections is not None and i < len(p.color_corrections):
if save_samples and opts.save_images_before_color_correction: if save_samples and opts.save_images_before_color_correction:
image_without_cc = apply_overlay(image, p.paste_to, overlay_image) image_without_cc, _ = apply_overlay(image, p.paste_to, overlay_image)
images.save_image(image_without_cc, p.outpath_samples, "", p.seeds[i], p.prompts[i], opts.samples_format, info=infotext(i), p=p, suffix="-before-color-correction") images.save_image(image_without_cc, p.outpath_samples, "", p.seeds[i], p.prompts[i], opts.samples_format, info=infotext(i), p=p, suffix="-before-color-correction")
image = apply_color_correction(p.color_corrections[i], image) image = apply_color_correction(p.color_corrections[i], image)
@@ -1022,12 +1005,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
# that is being composited over the original image, # that is being composited over the original image,
# we need to keep the original image around # we need to keep the original image around
# and use it in the composite step. # and use it in the composite step.
original_denoised_image = image.copy() image, original_denoised_image = apply_overlay(image, p.paste_to, overlay_image)
if p.paste_to is not None:
original_denoised_image = uncrop(original_denoised_image, (overlay_image.width, overlay_image.height), p.paste_to)
image = apply_overlay(image, p.paste_to, overlay_image)
if p.scripts is not None: if p.scripts is not None:
pp = scripts.PostprocessImageArgs(image) pp = scripts.PostprocessImageArgs(image)
+42
View File
@@ -0,0 +1,42 @@
from modules import scripts, shared, script_callbacks
import re
def strip_comments(text):
text = re.sub('(^|\n)#[^\n]*(\n|$)', '\n', text) # while line comment
text = re.sub('#[^\n]*(\n|$)', '\n', text) # in the middle of the line comment
return text
class ScriptStripComments(scripts.Script):
def title(self):
return "Comments"
def show(self, is_img2img):
return scripts.AlwaysVisible
def process(self, p, *args):
if not shared.opts.enable_prompt_comments:
return
p.all_prompts = [strip_comments(x) for x in p.all_prompts]
p.all_negative_prompts = [strip_comments(x) for x in p.all_negative_prompts]
p.main_prompt = strip_comments(p.main_prompt)
p.main_negative_prompt = strip_comments(p.main_negative_prompt)
def before_token_counter(params: script_callbacks.BeforeTokenCounterParams):
if not shared.opts.enable_prompt_comments:
return
params.prompt = strip_comments(params.prompt)
script_callbacks.on_before_token_counter(before_token_counter)
shared.options_templates.update(shared.options_section(('sd', "Stable Diffusion", "sd"), {
"enable_prompt_comments": shared.OptionInfo(True, "Enable comments").info("Use # anywhere in the prompt to hide the text between # and the end of the line from the generation."),
}))
+26
View File
@@ -1,3 +1,4 @@
import dataclasses
import inspect import inspect
import os import os
from collections import namedtuple from collections import namedtuple
@@ -106,6 +107,15 @@ class ImageGridLoopParams:
self.rows = rows self.rows = rows
@dataclasses.dataclass
class BeforeTokenCounterParams:
prompt: str
steps: int
styles: list
is_positive: bool = True
ScriptCallback = namedtuple("ScriptCallback", ["script", "callback"]) ScriptCallback = namedtuple("ScriptCallback", ["script", "callback"])
callback_map = dict( callback_map = dict(
callbacks_app_started=[], callbacks_app_started=[],
@@ -128,6 +138,7 @@ callback_map = dict(
callbacks_on_reload=[], callbacks_on_reload=[],
callbacks_list_optimizers=[], callbacks_list_optimizers=[],
callbacks_list_unets=[], callbacks_list_unets=[],
callbacks_before_token_counter=[],
) )
@@ -309,6 +320,14 @@ def list_unets_callback():
return res return res
def before_token_counter_callback(params: BeforeTokenCounterParams):
for c in callback_map['callbacks_before_token_counter']:
try:
c.callback(params)
except Exception:
report_exception(c, 'before_token_counter')
def add_callback(callbacks, fun): def add_callback(callbacks, fun):
stack = [x for x in inspect.stack() if x.filename != __file__] stack = [x for x in inspect.stack() if x.filename != __file__]
filename = stack[0].filename if stack else 'unknown file' filename = stack[0].filename if stack else 'unknown file'
@@ -483,3 +502,10 @@ def on_list_unets(callback):
The function will be called with one argument, a list, and shall add objects of type modules.sd_unet.SdUnetOption to it.""" The function will be called with one argument, a list, and shall add objects of type modules.sd_unet.SdUnetOption to it."""
add_callback(callback_map['callbacks_list_unets'], callback) add_callback(callback_map['callbacks_list_unets'], callback)
def on_before_token_counter(callback):
"""register a function to be called when UI is counting tokens for a prompt.
The function will be called with one argument of type BeforeTokenCounterParams, and should modify its fields if necessary."""
add_callback(callback_map['callbacks_before_token_counter'], callback)
+20 -8
View File
@@ -939,22 +939,34 @@ class ScriptRunner:
except Exception: except Exception:
errors.report(f"Error running setup: {script.filename}", exc_info=True) errors.report(f"Error running setup: {script.filename}", exc_info=True)
def set_named_arg(self, args, script_type, arg_elem_id, value): def set_named_arg(self, args, script_name, arg_elem_id, value, fuzzy=False):
script = next((x for x in self.scripts if type(x).__name__ == script_type), None) """Locate an arg of a specific script in script_args and set its value
Args:
args: all script args of process p, p.script_args
script_name: the name target script name to
arg_elem_id: the elem_id of the target arg
value: the value to set
fuzzy: if True, arg_elem_id can be a substring of the control.elem_id else exact match
Returns:
Updated script args
when script_name in not found or arg_elem_id is not found in script controls, raise RuntimeError
"""
script = next((x for x in self.scripts if x.name == script_name), None)
if script is None: if script is None:
return raise RuntimeError(f"script {script_name} not found")
for i, control in enumerate(script.controls): for i, control in enumerate(script.controls):
if arg_elem_id in control.elem_id: if arg_elem_id in control.elem_id if fuzzy else arg_elem_id == control.elem_id:
index = script.args_from + i index = script.args_from + i
if isinstance(args, list): if isinstance(args, tuple):
return args[:index] + (value,) + args[index + 1:]
elif isinstance(args, list):
args[index] = value args[index] = value
return args return args
elif isinstance(args, tuple):
return args[:index] + (value,) + args[index+1:]
else: else:
return None raise RuntimeError(f"args is not a list or tuple, but {type(args)}")
raise RuntimeError(f"arg_elem_id {arg_elem_id} not found in script {script_name}")
scripts_txt2img: ScriptRunner = None scripts_txt2img: ScriptRunner = None
+70
View File
@@ -0,0 +1,70 @@
from __future__ import annotations
import torch
class Emphasis:
"""Emphasis class decides how to death with (emphasized:1.1) text in prompts"""
name: str = "Base"
description: str = ""
tokens: list[list[int]]
"""tokens from the chunk of the prompt"""
multipliers: torch.Tensor
"""tensor with multipliers, once for each token"""
z: torch.Tensor
"""output of cond transformers network (CLIP)"""
def after_transformers(self):
"""Called after cond transformers network has processed the chunk of the prompt; this function should modify self.z to apply the emphasis"""
pass
class EmphasisNone(Emphasis):
name = "None"
description = "disable the mechanism entirely and treat (:.1.1) as literal characters"
class EmphasisIgnore(Emphasis):
name = "Ignore"
description = "treat all empasised words as if they have no emphasis"
class EmphasisOriginal(Emphasis):
name = "Original"
description = "the orginal emphasis implementation"
def after_transformers(self):
original_mean = self.z.mean()
self.z = self.z * self.multipliers.reshape(self.multipliers.shape + (1,)).expand(self.z.shape)
# restoring original mean is likely not correct, but it seems to work well to prevent artifacts that happen otherwise
new_mean = self.z.mean()
self.z = self.z * (original_mean / new_mean)
class EmphasisOriginalNoNorm(EmphasisOriginal):
name = "No norm"
description = "same as orginal, but without normalization (seems to work better for SDXL)"
def after_transformers(self):
self.z = self.z * self.multipliers.reshape(self.multipliers.shape + (1,)).expand(self.z.shape)
def get_current_option(emphasis_option_name):
return next(iter([x for x in options if x.name == emphasis_option_name]), EmphasisOriginal)
def get_options_descriptions():
return ", ".join(f"{x.name}: {x.description}" for x in options)
options = [
EmphasisNone,
EmphasisIgnore,
EmphasisOriginal,
EmphasisOriginalNoNorm,
]
+13 -8
View File
@@ -3,7 +3,7 @@ from collections import namedtuple
import torch import torch
from modules import prompt_parser, devices, sd_hijack from modules import prompt_parser, devices, sd_hijack, sd_emphasis
from modules.shared import opts from modules.shared import opts
@@ -88,7 +88,7 @@ class FrozenCLIPEmbedderWithCustomWordsBase(torch.nn.Module):
Returns the list and the total number of tokens in the prompt. Returns the list and the total number of tokens in the prompt.
""" """
if opts.enable_emphasis: if opts.emphasis != "None":
parsed = prompt_parser.parse_prompt_attention(line) parsed = prompt_parser.parse_prompt_attention(line)
else: else:
parsed = [[line, 1.0]] parsed = [[line, 1.0]]
@@ -249,6 +249,9 @@ class FrozenCLIPEmbedderWithCustomWordsBase(torch.nn.Module):
hashes.append(self.hijack.extra_generation_params.get("TI hashes")) hashes.append(self.hijack.extra_generation_params.get("TI hashes"))
self.hijack.extra_generation_params["TI hashes"] = ", ".join(hashes) self.hijack.extra_generation_params["TI hashes"] = ", ".join(hashes)
if any(x for x in texts if "(" in x or "[" in x) and opts.emphasis != "Original":
self.hijack.extra_generation_params["Emphasis"] = opts.emphasis
if getattr(self.wrapped, 'return_pooled', False): if getattr(self.wrapped, 'return_pooled', False):
return torch.hstack(zs), zs[0].pooled return torch.hstack(zs), zs[0].pooled
else: else:
@@ -274,12 +277,14 @@ class FrozenCLIPEmbedderWithCustomWordsBase(torch.nn.Module):
pooled = getattr(z, 'pooled', None) pooled = getattr(z, 'pooled', None)
# restoring original mean is likely not correct, but it seems to work well to prevent artifacts that happen otherwise emphasis = sd_emphasis.get_current_option(opts.emphasis)()
batch_multipliers = torch.asarray(batch_multipliers).to(devices.device) emphasis.tokens = remade_batch_tokens
original_mean = z.mean() emphasis.multipliers = torch.asarray(batch_multipliers).to(devices.device)
z = z * batch_multipliers.reshape(batch_multipliers.shape + (1,)).expand(z.shape) emphasis.z = z
new_mean = z.mean()
z = z * (original_mean / new_mean) emphasis.after_transformers()
z = emphasis.z
if pooled is not None: if pooled is not None:
z.pooled = pooled z.pooled = pooled
+1 -1
View File
@@ -32,7 +32,7 @@ def process_text_old(self: sd_hijack_clip.FrozenCLIPEmbedderWithCustomWordsBase,
embedding, embedding_length_in_tokens = self.hijack.embedding_db.find_embedding_at_position(tokens, i) embedding, embedding_length_in_tokens = self.hijack.embedding_db.find_embedding_at_position(tokens, i)
mult_change = self.token_mults.get(token) if shared.opts.enable_emphasis else None mult_change = self.token_mults.get(token) if shared.opts.emphasis != "None" else None
if mult_change is not None: if mult_change is not None:
mult *= mult_change mult *= mult_change
i += 1 i += 1
+45
View File
@@ -15,6 +15,7 @@ from ldm.util import instantiate_from_config
from modules import paths, shared, modelloader, devices, script_callbacks, sd_vae, sd_disable_initialization, errors, hashes, sd_models_config, sd_unet, sd_models_xl, cache, extra_networks, processing, lowvram, sd_hijack, patches from modules import paths, shared, modelloader, devices, script_callbacks, sd_vae, sd_disable_initialization, errors, hashes, sd_models_config, sd_unet, sd_models_xl, cache, extra_networks, processing, lowvram, sd_hijack, patches
from modules.timer import Timer from modules.timer import Timer
from modules.shared import opts
import tomesd import tomesd
import numpy as np import numpy as np
@@ -427,6 +428,8 @@ def load_model_weights(model, checkpoint_info: CheckpointInfo, state_dict, timer
devices.dtype_unet = torch.float16 devices.dtype_unet = torch.float16
timer.record("apply half()") timer.record("apply half()")
apply_alpha_schedule_override(model)
for module in model.modules(): for module in model.modules():
if hasattr(module, 'fp16_weight'): if hasattr(module, 'fp16_weight'):
del module.fp16_weight del module.fp16_weight
@@ -550,6 +553,48 @@ def repair_config(sd_config):
sd_config.model.params.noise_aug_config.params.clip_stats_path = sd_config.model.params.noise_aug_config.params.clip_stats_path.replace("checkpoints/karlo_models", karlo_path) sd_config.model.params.noise_aug_config.params.clip_stats_path = sd_config.model.params.noise_aug_config.params.clip_stats_path.replace("checkpoints/karlo_models", karlo_path)
def rescale_zero_terminal_snr_abar(alphas_cumprod):
alphas_bar_sqrt = alphas_cumprod.sqrt()
# Store old values.
alphas_bar_sqrt_0 = alphas_bar_sqrt[0].clone()
alphas_bar_sqrt_T = alphas_bar_sqrt[-1].clone()
# Shift so the last timestep is zero.
alphas_bar_sqrt -= (alphas_bar_sqrt_T)
# Scale so the first timestep is back to the old value.
alphas_bar_sqrt *= alphas_bar_sqrt_0 / (alphas_bar_sqrt_0 - alphas_bar_sqrt_T)
# Convert alphas_bar_sqrt to betas
alphas_bar = alphas_bar_sqrt ** 2 # Revert sqrt
alphas_bar[-1] = 4.8973451890853435e-08
return alphas_bar
def apply_alpha_schedule_override(sd_model, p=None):
"""
Applies an override to the alpha schedule of the model according to settings.
- downcasts the alpha schedule to half precision
- rescales the alpha schedule to have zero terminal SNR
"""
if not hasattr(sd_model, 'alphas_cumprod') or not hasattr(sd_model, 'alphas_cumprod_original'):
return
sd_model.alphas_cumprod = sd_model.alphas_cumprod_original.to(shared.device)
if opts.use_downcasted_alpha_bar:
if p is not None:
p.extra_generation_params['Downcast alphas_cumprod'] = opts.use_downcasted_alpha_bar
sd_model.alphas_cumprod = sd_model.alphas_cumprod.half().to(shared.device)
if opts.sd_noise_schedule == "Zero Terminal SNR":
if p is not None:
p.extra_generation_params['Noise Schedule'] = opts.sd_noise_schedule
sd_model.alphas_cumprod = rescale_zero_terminal_snr_abar(sd_model.alphas_cumprod).to(shared.device)
sd1_clip_weight = 'cond_stage_model.transformer.text_model.embeddings.token_embedding.weight' sd1_clip_weight = 'cond_stage_model.transformer.text_model.embeddings.token_embedding.weight'
sd2_clip_weight = 'cond_stage_model.model.transformer.resblocks.0.attn.in_proj_weight' sd2_clip_weight = 'cond_stage_model.model.transformer.resblocks.0.attn.in_proj_weight'
sdxl_clip_weight = 'conditioner.embedders.1.model.ln_final.weight' sdxl_clip_weight = 'conditioner.embedders.1.model.ln_final.weight'
+62 -10
View File
@@ -53,6 +53,7 @@ class CFGDenoiser(torch.nn.Module):
self.step = 0 self.step = 0
self.image_cfg_scale = None self.image_cfg_scale = None
self.padded_cond_uncond = False self.padded_cond_uncond = False
self.padded_cond_uncond_v0 = False
self.sampler = sampler self.sampler = sampler
self.model_wrap = None self.model_wrap = None
self.p = None self.p = None
@@ -91,6 +92,62 @@ class CFGDenoiser(torch.nn.Module):
self.sampler.sampler_extra_args['cond'] = c self.sampler.sampler_extra_args['cond'] = c
self.sampler.sampler_extra_args['uncond'] = uc self.sampler.sampler_extra_args['uncond'] = uc
def pad_cond_uncond(self, cond, uncond):
empty = shared.sd_model.cond_stage_model_empty_prompt
num_repeats = (cond.shape[1] - uncond.shape[1]) // empty.shape[1]
if num_repeats < 0:
cond = pad_cond(cond, -num_repeats, empty)
self.padded_cond_uncond = True
elif num_repeats > 0:
uncond = pad_cond(uncond, num_repeats, empty)
self.padded_cond_uncond = True
return cond, uncond
def pad_cond_uncond_v0(self, cond, uncond):
"""
Pads the 'uncond' tensor to match the shape of the 'cond' tensor.
If 'uncond' is a dictionary, it is assumed that the 'crossattn' key holds the tensor to be padded.
If 'uncond' is a tensor, it is padded directly.
If the number of columns in 'uncond' is less than the number of columns in 'cond', the last column of 'uncond'
is repeated to match the number of columns in 'cond'.
If the number of columns in 'uncond' is greater than the number of columns in 'cond', 'uncond' is truncated
to match the number of columns in 'cond'.
Args:
cond (torch.Tensor or DictWithShape): The condition tensor to match the shape of 'uncond'.
uncond (torch.Tensor or DictWithShape): The tensor to be padded, or a dictionary containing the tensor to be padded.
Returns:
tuple: A tuple containing the 'cond' tensor and the padded 'uncond' tensor.
Note:
This is the padding that was always used in DDIM before version 1.6.0
"""
is_dict_cond = isinstance(uncond, dict)
uncond_vec = uncond['crossattn'] if is_dict_cond else uncond
if uncond_vec.shape[1] < cond.shape[1]:
last_vector = uncond_vec[:, -1:]
last_vector_repeated = last_vector.repeat([1, cond.shape[1] - uncond_vec.shape[1], 1])
uncond_vec = torch.hstack([uncond_vec, last_vector_repeated])
self.padded_cond_uncond_v0 = True
elif uncond_vec.shape[1] > cond.shape[1]:
uncond_vec = uncond_vec[:, :cond.shape[1]]
self.padded_cond_uncond_v0 = True
if is_dict_cond:
uncond['crossattn'] = uncond_vec
else:
uncond = uncond_vec
return cond, uncond
def forward(self, x, sigma, uncond, cond, cond_scale, s_min_uncond, image_cond): def forward(self, x, sigma, uncond, cond, cond_scale, s_min_uncond, image_cond):
if state.interrupted or state.skipped: if state.interrupted or state.skipped:
raise sd_samplers_common.InterruptedException raise sd_samplers_common.InterruptedException
@@ -162,16 +219,11 @@ class CFGDenoiser(torch.nn.Module):
sigma_in = sigma_in[:-batch_size] sigma_in = sigma_in[:-batch_size]
self.padded_cond_uncond = False self.padded_cond_uncond = False
if shared.opts.pad_cond_uncond and tensor.shape[1] != uncond.shape[1]: self.padded_cond_uncond_v0 = False
empty = shared.sd_model.cond_stage_model_empty_prompt if shared.opts.pad_cond_uncond_v0 and tensor.shape[1] != uncond.shape[1]:
num_repeats = (tensor.shape[1] - uncond.shape[1]) // empty.shape[1] tensor, uncond = self.pad_cond_uncond_v0(tensor, uncond)
elif shared.opts.pad_cond_uncond and tensor.shape[1] != uncond.shape[1]:
if num_repeats < 0: tensor, uncond = self.pad_cond_uncond(tensor, uncond)
tensor = pad_cond(tensor, -num_repeats, empty)
self.padded_cond_uncond = True
elif num_repeats > 0:
uncond = pad_cond(uncond, num_repeats, empty)
self.padded_cond_uncond = True
if tensor.shape[1] == uncond.shape[1] or skip_uncond: if tensor.shape[1] == uncond.shape[1] or skip_uncond:
if is_edit_model: if is_edit_model:
+7
View File
@@ -335,3 +335,10 @@ class Sampler:
def sample_img2img(self, p, x, noise, conditioning, unconditional_conditioning, steps=None, image_conditioning=None): def sample_img2img(self, p, x, noise, conditioning, unconditional_conditioning, steps=None, image_conditioning=None):
raise NotImplementedError() raise NotImplementedError()
def add_infotext(self, p):
if self.model_wrap_cfg.padded_cond_uncond:
p.extra_generation_params["Pad conds"] = True
if self.model_wrap_cfg.padded_cond_uncond_v0:
p.extra_generation_params["Pad conds v0"] = True
+2 -4
View File
@@ -187,8 +187,7 @@ class KDiffusionSampler(sd_samplers_common.Sampler):
samples = self.launch_sampling(t_enc + 1, lambda: self.func(self.model_wrap_cfg, xi, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs)) samples = self.launch_sampling(t_enc + 1, lambda: self.func(self.model_wrap_cfg, xi, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs))
if self.model_wrap_cfg.padded_cond_uncond: self.add_infotext(p)
p.extra_generation_params["Pad conds"] = True
return samples return samples
@@ -234,8 +233,7 @@ class KDiffusionSampler(sd_samplers_common.Sampler):
samples = self.launch_sampling(steps, lambda: self.func(self.model_wrap_cfg, x, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs)) samples = self.launch_sampling(steps, lambda: self.func(self.model_wrap_cfg, x, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs))
if self.model_wrap_cfg.padded_cond_uncond: self.add_infotext(p)
p.extra_generation_params["Pad conds"] = True
return samples return samples
+2 -4
View File
@@ -133,8 +133,7 @@ class CompVisSampler(sd_samplers_common.Sampler):
samples = self.launch_sampling(t_enc + 1, lambda: self.func(self.model_wrap_cfg, xi, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs)) samples = self.launch_sampling(t_enc + 1, lambda: self.func(self.model_wrap_cfg, xi, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs))
if self.model_wrap_cfg.padded_cond_uncond: self.add_infotext(p)
p.extra_generation_params["Pad conds"] = True
return samples return samples
@@ -158,8 +157,7 @@ class CompVisSampler(sd_samplers_common.Sampler):
} }
samples = self.launch_sampling(steps, lambda: self.func(self.model_wrap_cfg, x, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs)) samples = self.launch_sampling(steps, lambda: self.func(self.model_wrap_cfg, x, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs))
if self.model_wrap_cfg.padded_cond_uncond: self.add_infotext(p)
p.extra_generation_params["Pad conds"] = True
return samples return samples
+2 -1
View File
@@ -1,3 +1,4 @@
import os
import sys import sys
import gradio as gr import gradio as gr
@@ -11,7 +12,7 @@ parser = shared_cmd_options.parser
batch_cond_uncond = True # old field, unused now in favor of shared.opts.batch_cond_uncond batch_cond_uncond = True # old field, unused now in favor of shared.opts.batch_cond_uncond
parallel_processing_allowed = True parallel_processing_allowed = True
styles_filename = cmd_opts.styles_file styles_filename = cmd_opts.styles_file = cmd_opts.styles_file if len(cmd_opts.styles_file) > 0 else [os.path.join(data_path, 'styles.csv')]
config_filename = cmd_opts.ui_settings_file config_filename = cmd_opts.ui_settings_file
hide_dirs = {"visible": not cmd_opts.hide_ui_dir_config} hide_dirs = {"visible": not cmd_opts.hide_ui_dir_config}
+10 -4
View File
@@ -1,7 +1,7 @@
import os import os
import gradio as gr import gradio as gr
from modules import localization, ui_components, shared_items, shared, interrogate, shared_gradio_themes, util from modules import localization, ui_components, shared_items, shared, interrogate, shared_gradio_themes, util, sd_emphasis
from modules.paths_internal import models_path, script_path, data_path, sd_configs_path, sd_default_config, sd_model_file, default_sd_model_file, extensions_dir, extensions_builtin_dir, default_output_dir # noqa: F401 from modules.paths_internal import models_path, script_path, data_path, sd_configs_path, sd_default_config, sd_model_file, default_sd_model_file, extensions_dir, extensions_builtin_dir, default_output_dir # noqa: F401
from modules.shared_cmd_options import cmd_opts from modules.shared_cmd_options import cmd_opts
from modules.options import options_section, OptionInfo, OptionHTML, categories from modules.options import options_section, OptionInfo, OptionHTML, categories
@@ -154,7 +154,7 @@ options_templates.update(options_section(('sd', "Stable Diffusion", "sd"), {
"sd_checkpoint_cache": OptionInfo(0, "Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}).info("obsolete; set to 0 and use the two settings above instead"), "sd_checkpoint_cache": OptionInfo(0, "Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}).info("obsolete; set to 0 and use the two settings above instead"),
"sd_unet": OptionInfo("Automatic", "SD Unet", gr.Dropdown, lambda: {"choices": shared_items.sd_unet_items()}, refresh=shared_items.refresh_unet_list).info("choose Unet model: Automatic = use one with same filename as checkpoint; None = use Unet from checkpoint"), "sd_unet": OptionInfo("Automatic", "SD Unet", gr.Dropdown, lambda: {"choices": shared_items.sd_unet_items()}, refresh=shared_items.refresh_unet_list).info("choose Unet model: Automatic = use one with same filename as checkpoint; None = use Unet from checkpoint"),
"enable_quantization": OptionInfo(False, "Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds").needs_reload_ui(), "enable_quantization": OptionInfo(False, "Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds").needs_reload_ui(),
"enable_emphasis": OptionInfo(True, "Enable emphasis").info("use (text) to make model pay more attention to text and [text] to make it pay less attention"), "emphasis": OptionInfo("Original", "Emphasis mode", gr.Radio, lambda: {"choices": [x.name for x in sd_emphasis.options]}, infotext="Emphasis").info("makes it possible to make model to pay (more:1.1) or (less:0.9) attention to text when you use the syntax in prompt; " + sd_emphasis.get_options_descriptions()),
"enable_batch_seeds": OptionInfo(True, "Make K-diffusion samplers produce same images in a batch as when making a single image"), "enable_batch_seeds": OptionInfo(True, "Make K-diffusion samplers produce same images in a batch as when making a single image"),
"comma_padding_backtrack": OptionInfo(20, "Prompt word wrap length limit", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1}).info("in tokens - for texts shorter than specified, if they don't fit into 75 token limit, move them to the next 75 token chunk"), "comma_padding_backtrack": OptionInfo(20, "Prompt word wrap length limit", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1}).info("in tokens - for texts shorter than specified, if they don't fit into 75 token limit, move them to the next 75 token chunk"),
"CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1}, infotext="Clip skip").link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#clip-skip").info("ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer"), "CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1}, infotext="Clip skip").link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#clip-skip").info("ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer"),
@@ -201,6 +201,7 @@ options_templates.update(options_section(('img2img', "img2img", "sd"), {
"return_mask": OptionInfo(False, "For inpainting, include the greyscale mask in results for web"), "return_mask": OptionInfo(False, "For inpainting, include the greyscale mask in results for web"),
"return_mask_composite": OptionInfo(False, "For inpainting, include masked composite in results for web"), "return_mask_composite": OptionInfo(False, "For inpainting, include masked composite in results for web"),
"img2img_batch_show_results_limit": OptionInfo(32, "Show the first N batch img2img results in UI", gr.Slider, {"minimum": -1, "maximum": 1000, "step": 1}).info('0: disable, -1: show all images. Too many images can cause lag'), "img2img_batch_show_results_limit": OptionInfo(32, "Show the first N batch img2img results in UI", gr.Slider, {"minimum": -1, "maximum": 1000, "step": 1}).info('0: disable, -1: show all images. Too many images can cause lag'),
"overlay_inpaint": OptionInfo(True, "Overlay original for inpaint").info("when inpainting, overlay the original image over the areas that weren't inpainted."),
})) }))
options_templates.update(options_section(('optimizations', "Optimizations", "sd"), { options_templates.update(options_section(('optimizations', "Optimizations", "sd"), {
@@ -209,7 +210,8 @@ options_templates.update(options_section(('optimizations', "Optimizations", "sd"
"token_merging_ratio": OptionInfo(0.0, "Token merging ratio", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}, infotext='Token merging ratio').link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/9256").info("0=disable, higher=faster"), "token_merging_ratio": OptionInfo(0.0, "Token merging ratio", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}, infotext='Token merging ratio').link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/9256").info("0=disable, higher=faster"),
"token_merging_ratio_img2img": OptionInfo(0.0, "Token merging ratio for img2img", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}).info("only applies if non-zero and overrides above"), "token_merging_ratio_img2img": OptionInfo(0.0, "Token merging ratio for img2img", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}).info("only applies if non-zero and overrides above"),
"token_merging_ratio_hr": OptionInfo(0.0, "Token merging ratio for high-res pass", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}, infotext='Token merging ratio hr').info("only applies if non-zero and overrides above"), "token_merging_ratio_hr": OptionInfo(0.0, "Token merging ratio for high-res pass", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}, infotext='Token merging ratio hr').info("only applies if non-zero and overrides above"),
"pad_cond_uncond": OptionInfo(False, "Pad prompt/negative prompt to be same length", infotext='Pad conds').info("improves performance when prompt and negative prompt have different lengths; changes seeds"), "pad_cond_uncond": OptionInfo(False, "Pad prompt/negative prompt", infotext='Pad conds').info("improves performance when prompt and negative prompt have different lengths; changes seeds"),
"pad_cond_uncond_v0": OptionInfo(False, "Pad prompt/negative prompt (v0)", infotext='Pad conds v0').info("alternative implementation for the above; used prior to 1.6.0 for DDIM sampler; overrides the above if set; WARNING: truncates negative prompt if it's too long; changes seeds"),
"persistent_cond_cache": OptionInfo(True, "Persistent cond cache").info("do not recalculate conds from prompts if prompts have not changed since previous calculation"), "persistent_cond_cache": OptionInfo(True, "Persistent cond cache").info("do not recalculate conds from prompts if prompts have not changed since previous calculation"),
"batch_cond_uncond": OptionInfo(True, "Batch cond/uncond").info("do both conditional and unconditional denoising in one batch; uses a bit more VRAM during sampling, but improves speed; previously this was controlled by --always-batch-cond-uncond comandline argument"), "batch_cond_uncond": OptionInfo(True, "Batch cond/uncond").info("do both conditional and unconditional denoising in one batch; uses a bit more VRAM during sampling, but improves speed; previously this was controlled by --always-batch-cond-uncond comandline argument"),
"fp8_storage": OptionInfo("Disable", "FP8 weight", gr.Radio, {"choices": ["Disable", "Enable for SDXL", "Enable"]}).info("Use FP8 to store Linear/Conv layers' weight. Require pytorch>=2.1.0."), "fp8_storage": OptionInfo("Disable", "FP8 weight", gr.Radio, {"choices": ["Disable", "Enable for SDXL", "Enable"]}).info("Use FP8 to store Linear/Conv layers' weight. Require pytorch>=2.1.0."),
@@ -252,8 +254,10 @@ options_templates.update(options_section(('extra_networks', "Extra Networks", "s
"extra_networks_card_height": OptionInfo(0, "Card height for Extra Networks").info("in pixels"), "extra_networks_card_height": OptionInfo(0, "Card height for Extra Networks").info("in pixels"),
"extra_networks_card_text_scale": OptionInfo(1.0, "Card text scale", gr.Slider, {"minimum": 0.0, "maximum": 2.0, "step": 0.01}).info("1 = original size"), "extra_networks_card_text_scale": OptionInfo(1.0, "Card text scale", gr.Slider, {"minimum": 0.0, "maximum": 2.0, "step": 0.01}).info("1 = original size"),
"extra_networks_card_show_desc": OptionInfo(True, "Show description on card"), "extra_networks_card_show_desc": OptionInfo(True, "Show description on card"),
"extra_networks_card_description_is_html": OptionInfo(False, "Treat card description as HTML"),
"extra_networks_card_order_field": OptionInfo("Path", "Default order field for Extra Networks cards", gr.Dropdown, {"choices": ['Path', 'Name', 'Date Created', 'Date Modified']}).needs_reload_ui(), "extra_networks_card_order_field": OptionInfo("Path", "Default order field for Extra Networks cards", gr.Dropdown, {"choices": ['Path', 'Name', 'Date Created', 'Date Modified']}).needs_reload_ui(),
"extra_networks_card_order": OptionInfo("Ascending", "Default order for Extra Networks cards", gr.Dropdown, {"choices": ['Ascending', 'Descending']}).needs_reload_ui(), "extra_networks_card_order": OptionInfo("Ascending", "Default order for Extra Networks cards", gr.Dropdown, {"choices": ['Ascending', 'Descending']}).needs_reload_ui(),
"extra_networks_tree_view_default_enabled": OptionInfo(False, "Enables the Extra Networks directory tree view by default").needs_reload_ui(),
"extra_networks_add_text_separator": OptionInfo(" ", "Extra networks separator").info("extra text to add before <...> when adding extra network to prompt"), "extra_networks_add_text_separator": OptionInfo(" ", "Extra networks separator").info("extra text to add before <...> when adding extra network to prompt"),
"ui_extra_networks_tab_reorder": OptionInfo("", "Extra networks tab order").needs_reload_ui(), "ui_extra_networks_tab_reorder": OptionInfo("", "Extra networks tab order").needs_reload_ui(),
"textual_inversion_print_at_load": OptionInfo(False, "Print a list of Textual Inversion embeddings when loading model"), "textual_inversion_print_at_load": OptionInfo(False, "Print a list of Textual Inversion embeddings when loading model"),
@@ -267,7 +271,8 @@ options_templates.update(options_section(('ui_prompt_editing', "Prompt editing",
"keyedit_delimiters": OptionInfo(r".,\/!?%^*;:{}=`~() ", "Word delimiters when editing the prompt with Ctrl+up/down"), "keyedit_delimiters": OptionInfo(r".,\/!?%^*;:{}=`~() ", "Word delimiters when editing the prompt with Ctrl+up/down"),
"keyedit_delimiters_whitespace": OptionInfo(["Tab", "Carriage Return", "Line Feed"], "Ctrl+up/down whitespace delimiters", gr.CheckboxGroup, lambda: {"choices": ["Tab", "Carriage Return", "Line Feed"]}), "keyedit_delimiters_whitespace": OptionInfo(["Tab", "Carriage Return", "Line Feed"], "Ctrl+up/down whitespace delimiters", gr.CheckboxGroup, lambda: {"choices": ["Tab", "Carriage Return", "Line Feed"]}),
"keyedit_move": OptionInfo(True, "Alt+left/right moves prompt elements"), "keyedit_move": OptionInfo(True, "Alt+left/right moves prompt elements"),
"disable_token_counters": OptionInfo(False, "Disable prompt token counters").needs_reload_ui(), "disable_token_counters": OptionInfo(False, "Disable prompt token counters"),
"include_styles_into_token_counters": OptionInfo(True, "Count tokens of enabled styles").info("When calculating how many tokens the prompt has, also consider tokens added by enabled styles."),
})) }))
options_templates.update(options_section(('ui_gallery', "Gallery", "ui"), { options_templates.update(options_section(('ui_gallery', "Gallery", "ui"), {
@@ -280,6 +285,7 @@ options_templates.update(options_section(('ui_gallery', "Gallery", "ui"), {
"sd_webui_modal_lightbox_icon_opacity": OptionInfo(1, "Full page image viewer: control icon unfocused opacity", gr.Slider, {"minimum": 0.0, "maximum": 1, "step": 0.01}, onchange=shared.reload_gradio_theme).info('for mouse only').needs_reload_ui(), "sd_webui_modal_lightbox_icon_opacity": OptionInfo(1, "Full page image viewer: control icon unfocused opacity", gr.Slider, {"minimum": 0.0, "maximum": 1, "step": 0.01}, onchange=shared.reload_gradio_theme).info('for mouse only').needs_reload_ui(),
"sd_webui_modal_lightbox_toolbar_opacity": OptionInfo(0.9, "Full page image viewer: tool bar opacity", gr.Slider, {"minimum": 0.0, "maximum": 1, "step": 0.01}, onchange=shared.reload_gradio_theme).info('for mouse only').needs_reload_ui(), "sd_webui_modal_lightbox_toolbar_opacity": OptionInfo(0.9, "Full page image viewer: tool bar opacity", gr.Slider, {"minimum": 0.0, "maximum": 1, "step": 0.01}, onchange=shared.reload_gradio_theme).info('for mouse only').needs_reload_ui(),
"gallery_height": OptionInfo("", "Gallery height", gr.Textbox).info("can be any valid CSS value, for example 768px or 20em").needs_reload_ui(), "gallery_height": OptionInfo("", "Gallery height", gr.Textbox).info("can be any valid CSS value, for example 768px or 20em").needs_reload_ui(),
"open_dir_button_choice": OptionInfo("Subdirectory", "What directory the [📂] button opens", gr.Radio, {"choices": ["Output Root", "Subdirectory", "Subdirectory (even temp dir)"]}),
})) }))
options_templates.update(options_section(('ui_alternatives', "UI alternatives", "ui"), { options_templates.update(options_section(('ui_alternatives', "UI alternatives", "ui"), {
+44 -39
View File
@@ -1,16 +1,16 @@
from pathlib import Path
from modules import errors
import csv import csv
import fnmatch
import os import os
import os.path
import typing import typing
import shutil import shutil
class PromptStyle(typing.NamedTuple): class PromptStyle(typing.NamedTuple):
name: str name: str
prompt: str prompt: str | None
negative_prompt: str negative_prompt: str | None
path: str = None path: str | None = None
def merge_prompts(style_prompt: str, prompt: str) -> str: def merge_prompts(style_prompt: str, prompt: str) -> str:
@@ -79,14 +79,19 @@ def extract_original_prompts(style: PromptStyle, prompt, negative_prompt):
class StyleDatabase: class StyleDatabase:
def __init__(self, path: str): def __init__(self, paths: list[str | Path]):
self.no_style = PromptStyle("None", "", "", None) self.no_style = PromptStyle("None", "", "", None)
self.styles = {} self.styles = {}
self.path = path self.paths = paths
self.all_styles_files: list[Path] = []
folder, file = os.path.split(self.path) folder, file = os.path.split(self.paths[0])
filename, _, ext = file.partition('*') if '*' in file or '?' in file:
self.default_path = os.path.join(folder, filename + ext) # if the first path is a wildcard pattern, find the first match else use "folder/styles.csv" as the default path
self.default_path = next(Path(folder).glob(file), Path(os.path.join(folder, 'styles.csv')))
self.paths.insert(0, self.default_path)
else:
self.default_path = Path(self.paths[0])
self.prompt_fields = [field for field in PromptStyle._fields if field != "path"] self.prompt_fields = [field for field in PromptStyle._fields if field != "path"]
@@ -99,33 +104,32 @@ class StyleDatabase:
""" """
self.styles.clear() self.styles.clear()
path, filename = os.path.split(self.path) # scans for all styles files
all_styles_files = []
if "*" in filename: for pattern in self.paths:
fileglob = filename.split("*")[0] + "*.csv" folder, file = os.path.split(pattern)
filelist = [] if '*' in file or '?' in file:
for file in os.listdir(path): found_files = Path(folder).glob(file)
if fnmatch.fnmatch(file, fileglob): [all_styles_files.append(file) for file in found_files]
filelist.append(file)
# Add a visible divider to the style list
half_len = round(len(file) / 2)
divider = f"{'-' * (20 - half_len)} {file.upper()}"
divider = f"{divider} {'-' * (40 - len(divider))}"
self.styles[divider] = PromptStyle(
f"{divider}", None, None, "do_not_save"
)
# Add styles from this CSV file
self.load_from_csv(os.path.join(path, file))
if len(filelist) == 0:
print(f"No styles found in {path} matching {fileglob}")
return
elif not os.path.exists(self.path):
print(f"Style database not found: {self.path}")
return
else: else:
self.load_from_csv(self.path) # if os.path.exists(pattern):
all_styles_files.append(Path(pattern))
def load_from_csv(self, path: str): # Remove any duplicate entries
seen = set()
self.all_styles_files = [s for s in all_styles_files if not (s in seen or seen.add(s))]
for styles_file in self.all_styles_files:
if len(all_styles_files) > 1:
# add divider when more than styles file
# '---------------- STYLES ----------------'
divider = f' {styles_file.stem.upper()} '.center(40, '-')
self.styles[divider] = PromptStyle(f"{divider}", None, None, "do_not_save")
if styles_file.is_file():
self.load_from_csv(styles_file)
def load_from_csv(self, path: str | Path):
try:
with open(path, "r", encoding="utf-8-sig", newline="") as file: with open(path, "r", encoding="utf-8-sig", newline="") as file:
reader = csv.DictReader(file, skipinitialspace=True) reader = csv.DictReader(file, skipinitialspace=True)
for row in reader: for row in reader:
@@ -137,19 +141,21 @@ class StyleDatabase:
negative_prompt = row.get("negative_prompt", "") negative_prompt = row.get("negative_prompt", "")
# Add style to database # Add style to database
self.styles[row["name"]] = PromptStyle( self.styles[row["name"]] = PromptStyle(
row["name"], prompt, negative_prompt, path row["name"], prompt, negative_prompt, str(path)
) )
except Exception:
errors.report(f'Error loading styles from {path}: ', exc_info=True)
def get_style_paths(self) -> set: def get_style_paths(self) -> set:
"""Returns a set of all distinct paths of files that styles are loaded from.""" """Returns a set of all distinct paths of files that styles are loaded from."""
# Update any styles without a path to the default path # Update any styles without a path to the default path
for style in list(self.styles.values()): for style in list(self.styles.values()):
if not style.path: if not style.path:
self.styles[style.name] = style._replace(path=self.default_path) self.styles[style.name] = style._replace(path=str(self.default_path))
# Create a list of all distinct paths, including the default path # Create a list of all distinct paths, including the default path
style_paths = set() style_paths = set()
style_paths.add(self.default_path) style_paths.add(str(self.default_path))
for _, style in self.styles.items(): for _, style in self.styles.items():
if style.path: if style.path:
style_paths.add(style.path) style_paths.add(style.path)
@@ -177,7 +183,6 @@ class StyleDatabase:
def save_styles(self, path: str = None) -> None: def save_styles(self, path: str = None) -> None:
# The path argument is deprecated, but kept for backwards compatibility # The path argument is deprecated, but kept for backwards compatibility
_ = path
style_paths = self.get_style_paths() style_paths = self.get_style_paths()
@@ -150,6 +150,7 @@ class EmbeddingDatabase:
return embedding return embedding
def get_expected_shape(self): def get_expected_shape(self):
devices.torch_npu_set_device()
vec = shared.sd_model.cond_stage_model.encode_embedding_init_text(",", 1) vec = shared.sd_model.cond_stage_model.encode_embedding_init_text(",", 1)
return vec.shape[1] return vec.shape[1]
+2 -2
View File
@@ -60,10 +60,10 @@ def txt2img_upscale(id_task: str, request: gr.Request, gallery, gallery_index, g
assert len(gallery) > 0, 'No image to upscale' assert len(gallery) > 0, 'No image to upscale'
assert 0 <= gallery_index < len(gallery), f'Bad image index: {gallery_index}' assert 0 <= gallery_index < len(gallery), f'Bad image index: {gallery_index}'
p = txt2img_create_processing(id_task, request, *args) p = txt2img_create_processing(id_task, request, *args, force_enable_hr=True)
p.enable_hr = True
p.batch_size = 1 p.batch_size = 1
p.n_iter = 1 p.n_iter = 1
# txt2img_upscale attribute that signifies this is called by txt2img_upscale
p.txt2img_upscale = True p.txt2img_upscale = True
geninfo = json.loads(generation_info) geninfo = json.loads(generation_info)
+26 -11
View File
@@ -151,7 +151,18 @@ def connect_clear_prompt(button):
) )
def update_token_counter(text, steps, *, is_positive=True): def update_token_counter(text, steps, styles, *, is_positive=True):
params = script_callbacks.BeforeTokenCounterParams(text, steps, styles, is_positive=is_positive)
script_callbacks.before_token_counter_callback(params)
text = params.prompt
steps = params.steps
styles = params.styles
is_positive = params.is_positive
if shared.opts.include_styles_into_token_counters:
apply_styles = shared.prompt_styles.apply_styles_to_prompt if is_positive else shared.prompt_styles.apply_negative_styles_to_prompt
text = apply_styles(text, styles)
try: try:
text, _ = extra_networks.parse_prompt(text) text, _ = extra_networks.parse_prompt(text)
@@ -173,8 +184,8 @@ def update_token_counter(text, steps, *, is_positive=True):
return f"<span class='gr-box gr-text-input'>{token_count}/{max_length}</span>" return f"<span class='gr-box gr-text-input'>{token_count}/{max_length}</span>"
def update_negative_prompt_token_counter(text, steps): def update_negative_prompt_token_counter(*args):
return update_token_counter(text, steps, is_positive=False) return update_token_counter(*args, is_positive=False)
def setup_progressbar(*args, **kwargs): def setup_progressbar(*args, **kwargs):
@@ -266,7 +277,7 @@ def create_ui():
dummy_component = gr.Label(visible=False) dummy_component = gr.Label(visible=False)
extra_tabs = gr.Tabs(elem_id="txt2img_extra_tabs") extra_tabs = gr.Tabs(elem_id="txt2img_extra_tabs", elem_classes=["extra-networks"])
extra_tabs.__enter__() extra_tabs.__enter__()
with gr.Tab("Generation", id="txt2img_generation") as txt2img_generation_tab, ResizeHandleRow(equal_height=False): with gr.Tab("Generation", id="txt2img_generation") as txt2img_generation_tab, ResizeHandleRow(equal_height=False):
@@ -485,8 +496,10 @@ def create_ui():
height, height,
] ]
toprow.token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[toprow.prompt, steps], outputs=[toprow.token_counter]) toprow.ui_styles.dropdown.change(fn=wrap_queued_call(update_token_counter), inputs=[toprow.prompt, steps, toprow.ui_styles.dropdown], outputs=[toprow.token_counter])
toprow.negative_token_button.click(fn=wrap_queued_call(update_negative_prompt_token_counter), inputs=[toprow.negative_prompt, steps], outputs=[toprow.negative_token_counter]) toprow.ui_styles.dropdown.change(fn=wrap_queued_call(update_negative_prompt_token_counter), inputs=[toprow.negative_prompt, steps, toprow.ui_styles.dropdown], outputs=[toprow.negative_token_counter])
toprow.token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[toprow.prompt, steps, toprow.ui_styles.dropdown], outputs=[toprow.token_counter])
toprow.negative_token_button.click(fn=wrap_queued_call(update_negative_prompt_token_counter), inputs=[toprow.negative_prompt, steps, toprow.ui_styles.dropdown], outputs=[toprow.negative_token_counter])
extra_networks_ui = ui_extra_networks.create_ui(txt2img_interface, [txt2img_generation_tab], 'txt2img') extra_networks_ui = ui_extra_networks.create_ui(txt2img_interface, [txt2img_generation_tab], 'txt2img')
ui_extra_networks.setup_ui(extra_networks_ui, output_panel.gallery) ui_extra_networks.setup_ui(extra_networks_ui, output_panel.gallery)
@@ -499,7 +512,7 @@ def create_ui():
with gr.Blocks(analytics_enabled=False) as img2img_interface: with gr.Blocks(analytics_enabled=False) as img2img_interface:
toprow = ui_toprow.Toprow(is_img2img=True, is_compact=shared.opts.compact_prompt_box) toprow = ui_toprow.Toprow(is_img2img=True, is_compact=shared.opts.compact_prompt_box)
extra_tabs = gr.Tabs(elem_id="img2img_extra_tabs") extra_tabs = gr.Tabs(elem_id="img2img_extra_tabs", elem_classes=["extra-networks"])
extra_tabs.__enter__() extra_tabs.__enter__()
with gr.Tab("Generation", id="img2img_generation") as img2img_generation_tab, ResizeHandleRow(equal_height=False): with gr.Tab("Generation", id="img2img_generation") as img2img_generation_tab, ResizeHandleRow(equal_height=False):
@@ -824,8 +837,10 @@ def create_ui():
**interrogate_args, **interrogate_args,
) )
toprow.token_button.click(fn=update_token_counter, inputs=[toprow.prompt, steps], outputs=[toprow.token_counter]) toprow.ui_styles.dropdown.change(fn=wrap_queued_call(update_token_counter), inputs=[toprow.prompt, steps, toprow.ui_styles.dropdown], outputs=[toprow.token_counter])
toprow.negative_token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[toprow.negative_prompt, steps], outputs=[toprow.negative_token_counter]) toprow.ui_styles.dropdown.change(fn=wrap_queued_call(update_negative_prompt_token_counter), inputs=[toprow.negative_prompt, steps, toprow.ui_styles.dropdown], outputs=[toprow.negative_token_counter])
toprow.token_button.click(fn=update_token_counter, inputs=[toprow.prompt, steps, toprow.ui_styles.dropdown], outputs=[toprow.token_counter])
toprow.negative_token_button.click(fn=wrap_queued_call(update_negative_prompt_token_counter), inputs=[toprow.negative_prompt, steps, toprow.ui_styles.dropdown], outputs=[toprow.negative_token_counter])
img2img_paste_fields = [ img2img_paste_fields = [
(toprow.prompt, "Prompt"), (toprow.prompt, "Prompt"),
@@ -863,7 +878,7 @@ def create_ui():
ui_postprocessing.create_ui() ui_postprocessing.create_ui()
with gr.Blocks(analytics_enabled=False) as pnginfo_interface: with gr.Blocks(analytics_enabled=False) as pnginfo_interface:
with gr.Row(equal_height=False): with ResizeHandleRow(equal_height=False):
with gr.Column(variant='panel'): with gr.Column(variant='panel'):
image = gr.Image(elem_id="pnginfo_image", label="Source", source="upload", interactive=True, type="pil") image = gr.Image(elem_id="pnginfo_image", label="Source", source="upload", interactive=True, type="pil")
@@ -891,7 +906,7 @@ def create_ui():
with gr.Row(equal_height=False): with gr.Row(equal_height=False):
gr.HTML(value="<p style='margin-bottom: 0.7em'>See <b><a href=\"https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Textual-Inversion\">wiki</a></b> for detailed explanation.</p>") gr.HTML(value="<p style='margin-bottom: 0.7em'>See <b><a href=\"https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Textual-Inversion\">wiki</a></b> for detailed explanation.</p>")
with gr.Row(variant="compact", equal_height=False): with ResizeHandleRow(variant="compact", equal_height=False):
with gr.Tabs(elem_id="train_tabs"): with gr.Tabs(elem_id="train_tabs"):
with gr.Tab(label="Create embedding", id="create_embedding"): with gr.Tab(label="Create embedding", id="create_embedding"):
+26 -8
View File
@@ -9,7 +9,7 @@ import sys
import gradio as gr import gradio as gr
import subprocess as sp import subprocess as sp
from modules import call_queue, shared from modules import call_queue, shared, ui_tempdir
from modules.infotext_utils import image_from_url_text from modules.infotext_utils import image_from_url_text
import modules.images import modules.images
from modules.ui_components import ToolButton from modules.ui_components import ToolButton
@@ -164,20 +164,34 @@ class OutputPanel:
def create_output_panel(tabname, outdir, toprow=None): def create_output_panel(tabname, outdir, toprow=None):
res = OutputPanel() res = OutputPanel()
def open_folder(f): def open_folder(f, images=None, index=None):
if shared.cmd_opts.hide_ui_dir_config:
return
try:
if 'Sub' in shared.opts.open_dir_button_choice:
image_dir = os.path.split(images[index]["name"].rsplit('?', 1)[0])[0]
if 'temp' in shared.opts.open_dir_button_choice or not ui_tempdir.is_gradio_temp_path(image_dir):
f = image_dir
except Exception:
pass
if not os.path.exists(f): if not os.path.exists(f):
print(f'Folder "{f}" does not exist. After you create an image, the folder will be created.') msg = f'Folder "{f}" does not exist. After you create an image, the folder will be created.'
print(msg)
gr.Info(msg)
return return
elif not os.path.isdir(f): elif not os.path.isdir(f):
print(f""" msg = f"""
WARNING WARNING
An open_folder request was made with an argument that is not a folder. An open_folder request was made with an argument that is not a folder.
This could be an error or a malicious attempt to run code on your computer. This could be an error or a malicious attempt to run code on your computer.
Requested path was: {f} Requested path was: {f}
""", file=sys.stderr) """
print(msg, file=sys.stderr)
gr.Warning(msg)
return return
if not shared.cmd_opts.hide_ui_dir_config:
path = os.path.normpath(f) path = os.path.normpath(f)
if platform.system() == "Windows": if platform.system() == "Windows":
os.startfile(path) os.startfile(path)
@@ -213,8 +227,12 @@ Requested path was: {f}
res.button_upscale = ToolButton('', elem_id=f'{tabname}_upscale', tooltip="Create an upscaled version of the current image using hires fix settings.") res.button_upscale = ToolButton('', elem_id=f'{tabname}_upscale', tooltip="Create an upscaled version of the current image using hires fix settings.")
open_folder_button.click( open_folder_button.click(
fn=lambda: open_folder(shared.opts.outdir_samples or outdir), fn=lambda images, index: open_folder(shared.opts.outdir_samples or outdir, images, index),
inputs=[], _js="(y, w) => [y, selected_gallery_index()]",
inputs=[
res.gallery,
open_folder_button, # placeholder for index
],
outputs=[], outputs=[],
) )
+3 -1
View File
@@ -548,6 +548,7 @@ def create_ui():
extensions_disable_all = gr.Radio(label="Disable all extensions", choices=["none", "extra", "all"], value=shared.opts.disable_all_extensions, elem_id="extensions_disable_all") extensions_disable_all = gr.Radio(label="Disable all extensions", choices=["none", "extra", "all"], value=shared.opts.disable_all_extensions, elem_id="extensions_disable_all")
extensions_disabled_list = gr.Text(elem_id="extensions_disabled_list", visible=False, container=False) extensions_disabled_list = gr.Text(elem_id="extensions_disabled_list", visible=False, container=False)
extensions_update_list = gr.Text(elem_id="extensions_update_list", visible=False, container=False) extensions_update_list = gr.Text(elem_id="extensions_update_list", visible=False, container=False)
refresh = gr.Button(value='Refresh', variant="compact")
html = "" html = ""
@@ -566,7 +567,8 @@ def create_ui():
with gr.Row(elem_classes="progress-container"): with gr.Row(elem_classes="progress-container"):
extensions_table = gr.HTML('Loading...', elem_id="extensions_installed_html") extensions_table = gr.HTML('Loading...', elem_id="extensions_installed_html")
ui.load(fn=extension_table, inputs=[], outputs=[extensions_table]) ui.load(fn=extension_table, inputs=[], outputs=[extensions_table], show_progress=False)
refresh.click(fn=extension_table, inputs=[], outputs=[extensions_table], show_progress=False)
apply.click( apply.click(
fn=apply_and_restart, fn=apply_and_restart,
+419 -156
View File
@@ -2,6 +2,8 @@ import functools
import os.path import os.path
import urllib.parse import urllib.parse
from pathlib import Path from pathlib import Path
from typing import Optional, Union
from dataclasses import dataclass
from modules import shared, ui_extra_networks_user_metadata, errors, extra_networks, util from modules import shared, ui_extra_networks_user_metadata, errors, extra_networks, util
from modules.images import read_info_from_image, save_image_with_geninfo from modules.images import read_info_from_image, save_image_with_geninfo
@@ -11,14 +13,11 @@ import html
from fastapi.exceptions import HTTPException from fastapi.exceptions import HTTPException
from modules.infotext_utils import image_from_url_text from modules.infotext_utils import image_from_url_text
from modules.ui_components import ToolButton
extra_pages = [] extra_pages = []
allowed_dirs = set() allowed_dirs = set()
default_allowed_preview_extensions = ["png", "jpg", "jpeg", "webp", "gif"] default_allowed_preview_extensions = ["png", "jpg", "jpeg", "webp", "gif"]
@functools.cache @functools.cache
def allowed_preview_extensions_with_extra(extra_extensions=None): def allowed_preview_extensions_with_extra(extra_extensions=None):
return set(default_allowed_preview_extensions) | set(extra_extensions or []) return set(default_allowed_preview_extensions) | set(extra_extensions or [])
@@ -28,6 +27,62 @@ def allowed_preview_extensions():
return allowed_preview_extensions_with_extra((shared.opts.samples_format, )) return allowed_preview_extensions_with_extra((shared.opts.samples_format, ))
@dataclass
class ExtraNetworksItem:
"""Wrapper for dictionaries representing ExtraNetworks items."""
item: dict
def get_tree(paths: Union[str, list[str]], items: dict[str, ExtraNetworksItem]) -> dict:
"""Recursively builds a directory tree.
Args:
paths: Path or list of paths to directories. These paths are treated as roots from which
the tree will be built.
items: A dictionary associating filepaths to an ExtraNetworksItem instance.
Returns:
The result directory tree.
"""
if isinstance(paths, (str,)):
paths = [paths]
def _get_tree(_paths: list[str], _root: str):
_res = {}
for path in _paths:
relpath = os.path.relpath(path, _root)
if os.path.isdir(path):
dir_items = os.listdir(path)
# Ignore empty directories.
if not dir_items:
continue
dir_tree = _get_tree([os.path.join(path, x) for x in dir_items], _root)
# We only want to store non-empty folders in the tree.
if dir_tree:
_res[relpath] = dir_tree
else:
if path not in items:
continue
# Add the ExtraNetworksItem to the result.
_res[relpath] = items[path]
return _res
res = {}
# Handle each root directory separately.
# Each root WILL have a key/value at the root of the result dict though
# the value can be an empty dict if the directory is empty. We want these
# placeholders for empty dirs so we can inform the user later.
for path in paths:
root = os.path.dirname(path)
relpath = os.path.relpath(path, root)
# Wrap the path in a list since that is what the `_get_tree` expects.
res[relpath] = _get_tree([path], root)
if res[relpath]:
# We need to pull the inner path out one for these root dirs.
res[relpath] = res[relpath][relpath]
return res
def register_page(page): def register_page(page):
"""registers extra networks page for the UI; recommend doing it in on_before_ui() callback for extensions""" """registers extra networks page for the UI; recommend doing it in on_before_ui() callback for extensions"""
@@ -79,8 +134,8 @@ def get_single_card(page: str = "", tabname: str = "", name: str = ""):
errors.display(e, "creating item for extra network") errors.display(e, "creating item for extra network")
item = page.items.get(name) item = page.items.get(name)
page.read_user_metadata(item) page.read_user_metadata(item, use_cache=False)
item_html = page.create_html_for_item(item, tabname) item_html = page.create_item_html(tabname, item, shared.html("extra-networks-card.html"))
return JSONResponse({"html": item_html}) return JSONResponse({"html": item_html})
@@ -96,25 +151,31 @@ def quote_js(s):
s = s.replace('"', '\\"') s = s.replace('"', '\\"')
return f'"{s}"' return f'"{s}"'
class ExtraNetworksPage: class ExtraNetworksPage:
def __init__(self, title): def __init__(self, title):
self.title = title self.title = title
self.name = title.lower() self.name = title.lower()
self.id_page = self.name.replace(" ", "_") # This is the actual name of the extra networks tab (not txt2img/img2img).
self.card_page = shared.html("extra-networks-card.html") self.extra_networks_tabname = self.name.replace(" ", "_")
self.allow_prompt = True self.allow_prompt = True
self.allow_negative_prompt = False self.allow_negative_prompt = False
self.metadata = {} self.metadata = {}
self.items = {} self.items = {}
self.lister = util.MassFileLister() self.lister = util.MassFileLister()
# HTML Templates
self.pane_tpl = shared.html("extra-networks-pane.html")
self.card_tpl = shared.html("extra-networks-card.html")
self.btn_tree_tpl = shared.html("extra-networks-tree-button.html")
self.btn_copy_path_tpl = shared.html("extra-networks-copy-path-button.html")
self.btn_metadata_tpl = shared.html("extra-networks-metadata-button.html")
self.btn_edit_item_tpl = shared.html("extra-networks-edit-item-button.html")
def refresh(self): def refresh(self):
pass pass
def read_user_metadata(self, item): def read_user_metadata(self, item, use_cache=True):
filename = item.get("filename", None) filename = item.get("filename", None)
metadata = extra_networks.get_user_metadata(filename, lister=self.lister) metadata = extra_networks.get_user_metadata(filename, lister=self.lister if use_cache else None)
desc = metadata.get("description", None) desc = metadata.get("description", None)
if desc is not None: if desc is not None:
@@ -129,117 +190,69 @@ class ExtraNetworksPage:
def search_terms_from_path(self, filename, possible_directories=None): def search_terms_from_path(self, filename, possible_directories=None):
abspath = os.path.abspath(filename) abspath = os.path.abspath(filename)
for parentdir in (possible_directories if possible_directories is not None else self.allowed_directories_for_previews()): for parentdir in (possible_directories if possible_directories is not None else self.allowed_directories_for_previews()):
parentdir = os.path.abspath(parentdir) parentdir = os.path.dirname(os.path.abspath(parentdir))
if abspath.startswith(parentdir): if abspath.startswith(parentdir):
return abspath[len(parentdir):].replace('\\', '/') return os.path.relpath(abspath, parentdir)
return "" return ""
def create_html(self, tabname): def create_item_html(
self.lister.reset() self,
tabname: str,
item: dict,
template: Optional[str] = None,
) -> Union[str, dict]:
"""Generates HTML for a single ExtraNetworks Item.
items_html = '' Args:
tabname: The name of the active tab.
item: Dictionary containing item information.
template: Optional template string to use.
self.metadata = {} Returns:
If a template is passed: HTML string generated for this item.
subdirs = {} Can be empty if the item is not meant to be shown.
for parentdir in [os.path.abspath(x) for x in self.allowed_directories_for_previews()]: If no template is passed: A dictionary containing the generated item's attributes.
for root, dirs, _ in sorted(os.walk(parentdir, followlinks=True), key=lambda x: shared.natural_sort_key(x[0])):
for dirname in sorted(dirs, key=shared.natural_sort_key):
x = os.path.join(root, dirname)
if not os.path.isdir(x):
continue
subdir = os.path.abspath(x)[len(parentdir):].replace("\\", "/")
if shared.opts.extra_networks_dir_button_function:
if not subdir.startswith("/"):
subdir = "/" + subdir
else:
while subdir.startswith("/"):
subdir = subdir[1:]
is_empty = len(os.listdir(x)) == 0
if not is_empty and not subdir.endswith("/"):
subdir = subdir + "/"
if ("/." in subdir or subdir.startswith(".")) and not shared.opts.extra_networks_show_hidden_directories:
continue
subdirs[subdir] = 1
if subdirs:
subdirs = {"": 1, **subdirs}
subdirs_html = "".join([f"""
<button class='lg secondary gradio-button custom-button{" search-all" if subdir=="" else ""}' onclick='extraNetworksSearchButton("{tabname}_extra_search", event)'>
{html.escape(subdir if subdir!="" else "all")}
</button>
""" for subdir in subdirs])
self.items = {x["name"]: x for x in self.list_items()}
for item in self.items.values():
metadata = item.get("metadata")
if metadata:
self.metadata[item["name"]] = metadata
if "user_metadata" not in item:
self.read_user_metadata(item)
items_html += self.create_html_for_item(item, tabname)
if items_html == '':
dirs = "".join([f"<li>{x}</li>" for x in self.allowed_directories_for_previews()])
items_html = shared.html("extra-networks-no-cards.html").format(dirs=dirs)
self_name_id = self.name.replace(" ", "_")
res = f"""
<div id='{tabname}_{self_name_id}_subdirs' class='extra-network-subdirs extra-network-subdirs-cards'>
{subdirs_html}
</div>
<div id='{tabname}_{self_name_id}_cards' class='extra-network-cards'>
{items_html}
</div>
"""
return res
def create_item(self, name, index=None):
raise NotImplementedError()
def list_items(self):
raise NotImplementedError()
def allowed_directories_for_previews(self):
return []
def create_html_for_item(self, item, tabname):
""" """
Create HTML for card item in tab tabname; can return empty string if the item is not meant to be shown.
"""
preview = item.get("preview", None) preview = item.get("preview", None)
style_height = f"height: {shared.opts.extra_networks_card_height}px;" if shared.opts.extra_networks_card_height else ''
style_width = f"width: {shared.opts.extra_networks_card_width}px;" if shared.opts.extra_networks_card_width else ''
style_font_size = f"font-size: {shared.opts.extra_networks_card_text_scale*100}%;"
card_style = style_height + style_width + style_font_size
background_image = f'<img src="{html.escape(preview)}" class="preview" loading="lazy">' if preview else ''
onclick = item.get("onclick", None) onclick = item.get("onclick", None)
if onclick is None: if onclick is None:
if "negative_prompt" in item: # Don't quote prompt/neg_prompt since they are stored as js strings already.
onclick = '"' + html.escape(f"""return cardClicked({quote_js(tabname)}, {item["prompt"]}, {item["negative_prompt"]}, {"true" if self.allow_negative_prompt else "false"})""") + '"' onclick_js_tpl = "cardClicked('{tabname}', {prompt}, {neg_prompt}, {allow_neg});"
else: onclick = onclick_js_tpl.format(
onclick = '"' + html.escape(f"""return cardClicked({quote_js(tabname)}, {item["prompt"]}, {'""'}, {"true" if self.allow_negative_prompt else "false"})""") + '"' **{
"tabname": tabname,
"prompt": item["prompt"],
"neg_prompt": item.get("negative_prompt", "''"),
"allow_neg": str(self.allow_negative_prompt).lower(),
}
)
onclick = html.escape(onclick)
height = f"height: {shared.opts.extra_networks_card_height}px;" if shared.opts.extra_networks_card_height else '' btn_copy_path = self.btn_copy_path_tpl.format(**{"filename": item["filename"]})
width = f"width: {shared.opts.extra_networks_card_width}px;" if shared.opts.extra_networks_card_width else '' btn_metadata = ""
background_image = f'<img src="{html.escape(preview)}" class="preview" loading="lazy">' if preview else ''
metadata_button = ""
metadata = item.get("metadata") metadata = item.get("metadata")
if metadata: if metadata:
metadata_button = f"<div class='metadata-button card-button' title='Show internal metadata' onclick='extraNetworksRequestMetadata(event, {quote_js(self.name)}, {quote_js(html.escape(item['name']))})'></div>" btn_metadata = self.btn_metadata_tpl.format(
**{
edit_button = f"<div class='edit-button card-button' title='Edit metadata' onclick='extraNetworksEditUserMetadata(event, {quote_js(tabname)}, {quote_js(self.id_page)}, {quote_js(html.escape(item['name']))})'></div>" "extra_networks_tabname": self.extra_networks_tabname,
"name": html.escape(item["name"]),
}
)
btn_edit_item = self.btn_edit_item_tpl.format(
**{
"tabname": tabname,
"extra_networks_tabname": self.extra_networks_tabname,
"name": html.escape(item["name"]),
}
)
local_path = "" local_path = ""
filename = item.get("filename", "") filename = item.get("filename", "")
@@ -259,26 +272,291 @@ class ExtraNetworksPage:
if search_only and shared.opts.extra_networks_hidden_models == "Never": if search_only and shared.opts.extra_networks_hidden_models == "Never":
return "" return ""
sort_keys = " ".join([f'data-sort-{k}="{html.escape(str(v))}"' for k, v in item.get("sort_keys", {}).items()]).strip() sort_keys = " ".join(
[
f'data-sort-{k}="{html.escape(str(v))}"'
for k, v in item.get("sort_keys", {}).items()
]
).strip()
search_terms_html = ""
search_term_template = "<span class='hidden {class}'>{search_term}</span>"
for search_term in item.get("search_terms", []):
search_terms_html += search_term_template.format(
**{
"class": f"search_terms{' search_only' if search_only else ''}",
"search_term": search_term,
}
)
description = (item.get("description", "") or "" if shared.opts.extra_networks_card_show_desc else "")
if not shared.opts.extra_networks_card_description_is_html:
description = html.escape(description)
# Some items here might not be used depending on HTML template used.
args = { args = {
"background_image": background_image, "background_image": background_image,
"style": f"'display: none; {height}{width}; font-size: {shared.opts.extra_networks_card_text_scale*100}%'",
"prompt": item.get("prompt", None),
"tabname": quote_js(tabname),
"local_preview": quote_js(item["local_preview"]),
"name": html.escape(item["name"]),
"description": (item.get("description") or "" if shared.opts.extra_networks_card_show_desc else ""),
"card_clicked": onclick, "card_clicked": onclick,
"save_card_preview": '"' + html.escape(f"""return saveCardPreview(event, {quote_js(tabname)}, {quote_js(item["local_preview"])})""") + '"', "copy_path_button": btn_copy_path,
"search_term": item.get("search_term", ""), "description": description,
"metadata_button": metadata_button, "edit_button": btn_edit_item,
"edit_button": edit_button, "local_preview": quote_js(item["local_preview"]),
"metadata_button": btn_metadata,
"name": html.escape(item["name"]),
"prompt": item.get("prompt", None),
"save_card_preview": html.escape(f"return saveCardPreview(event, '{tabname}', '{item['local_preview']}');"),
"search_only": " search_only" if search_only else "", "search_only": " search_only" if search_only else "",
"search_terms": search_terms_html,
"sort_keys": sort_keys, "sort_keys": sort_keys,
"style": card_style,
"tabname": tabname,
"extra_networks_tabname": self.extra_networks_tabname,
} }
return self.card_page.format(**args) if template:
return template.format(**args)
else:
return args
def create_tree_dir_item_html(
self,
tabname: str,
dir_path: str,
content: Optional[str] = None,
) -> Optional[str]:
"""Generates HTML for a directory item in the tree.
The generated HTML is of the format:
```html
<li class="tree-list-item tree-list-item--has-subitem">
<div class="tree-list-content tree-list-content-dir"></div>
<ul class="tree-list tree-list--subgroup">
{content}
</ul>
</li>
```
Args:
tabname: The name of the active tab.
dir_path: Path to the directory for this item.
content: Optional HTML string that will be wrapped by this <ul>.
Returns:
HTML formatted string.
"""
if not content:
return None
btn = self.btn_tree_tpl.format(
**{
"search_terms": "",
"subclass": "tree-list-content-dir",
"tabname": tabname,
"extra_networks_tabname": self.extra_networks_tabname,
"onclick_extra": "",
"data_path": dir_path,
"data_hash": "",
"action_list_item_action_leading": "<i class='tree-list-item-action-chevron'></i>",
"action_list_item_visual_leading": "🗀",
"action_list_item_label": os.path.basename(dir_path),
"action_list_item_visual_trailing": "",
"action_list_item_action_trailing": "",
}
)
ul = f"<ul class='tree-list tree-list--subgroup' hidden>{content}</ul>"
return (
"<li class='tree-list-item tree-list-item--has-subitem' data-tree-entry-type='dir'>"
f"{btn}{ul}"
"</li>"
)
def create_tree_file_item_html(self, tabname: str, file_path: str, item: dict) -> str:
"""Generates HTML for a file item in the tree.
The generated HTML is of the format:
```html
<li class="tree-list-item tree-list-item--subitem">
<span data-filterable-item-text hidden></span>
<div class="tree-list-content tree-list-content-file"></div>
</li>
```
Args:
tabname: The name of the active tab.
file_path: The path to the file for this item.
item: Dictionary containing the item information.
Returns:
HTML formatted string.
"""
item_html_args = self.create_item_html(tabname, item)
action_buttons = "".join(
[
item_html_args["copy_path_button"],
item_html_args["metadata_button"],
item_html_args["edit_button"],
]
)
action_buttons = f"<div class=\"button-row\">{action_buttons}</div>"
btn = self.btn_tree_tpl.format(
**{
"search_terms": "",
"subclass": "tree-list-content-file",
"tabname": tabname,
"extra_networks_tabname": self.extra_networks_tabname,
"onclick_extra": item_html_args["card_clicked"],
"data_path": file_path,
"data_hash": item["shorthash"],
"action_list_item_action_leading": "<i class='tree-list-item-action-chevron'></i>",
"action_list_item_visual_leading": "🗎",
"action_list_item_label": item["name"],
"action_list_item_visual_trailing": "",
"action_list_item_action_trailing": action_buttons,
}
)
return (
"<li class='tree-list-item tree-list-item--subitem' data-tree-entry-type='file'>"
f"{btn}"
"</li>"
)
def create_tree_view_html(self, tabname: str) -> str:
"""Generates HTML for displaying folders in a tree view.
Args:
tabname: The name of the active tab.
Returns:
HTML string generated for this tree view.
"""
res = ""
# Setup the tree dictionary.
roots = self.allowed_directories_for_previews()
tree_items = {v["filename"]: ExtraNetworksItem(v) for v in self.items.values()}
tree = get_tree([os.path.abspath(x) for x in roots], items=tree_items)
if not tree:
return res
def _build_tree(data: Optional[dict[str, ExtraNetworksItem]] = None) -> Optional[str]:
"""Recursively builds HTML for a tree.
Args:
data: Dictionary representing a directory tree. Can be NoneType.
Data keys should be absolute paths from the root and values
should be subdirectory trees or an ExtraNetworksItem.
Returns:
If data is not None: HTML string
Else: None
"""
if not data:
return None
# Lists for storing <li> items html for directories and files separately.
_dir_li = []
_file_li = []
for k, v in sorted(data.items(), key=lambda x: shared.natural_sort_key(x[0])):
if isinstance(v, (ExtraNetworksItem,)):
_file_li.append(self.create_tree_file_item_html(tabname, k, v.item))
else:
_dir_li.append(self.create_tree_dir_item_html(tabname, k, _build_tree(v)))
# Directories should always be displayed before files so we order them here.
return "".join(_dir_li) + "".join(_file_li)
# Add each root directory to the tree.
for k, v in sorted(tree.items(), key=lambda x: shared.natural_sort_key(x[0])):
item_html = self.create_tree_dir_item_html(tabname, k, _build_tree(v))
# Only add non-empty entries to the tree.
if item_html is not None:
res += item_html
return f"<ul class='tree-list tree-list--tree'>{res}</ul>"
def create_card_view_html(self, tabname: str, *, none_message) -> str:
"""Generates HTML for the network Card View section for a tab.
This HTML goes into the `extra-networks-pane.html` <div> with
`id='{tabname}_{extra_networks_tabname}_cards`.
Args:
tabname: The name of the active tab.
none_message: HTML text to show when there are no cards.
Returns:
HTML formatted string.
"""
res = ""
for item in self.items.values():
res += self.create_item_html(tabname, item, self.card_tpl)
if res == "":
dirs = "".join([f"<li>{x}</li>" for x in self.allowed_directories_for_previews()])
res = none_message or shared.html("extra-networks-no-cards.html").format(dirs=dirs)
return res
def create_html(self, tabname, *, empty=False):
"""Generates an HTML string for the current pane.
The generated HTML uses `extra-networks-pane.html` as a template.
Args:
tabname: The name of the active tab.
empty: create an empty HTML page with no items
Returns:
HTML formatted string.
"""
self.lister.reset()
self.metadata = {}
items_list = [] if empty else self.list_items()
self.items = {x["name"]: x for x in items_list}
# Populate the instance metadata for each item.
for item in self.items.values():
metadata = item.get("metadata")
if metadata:
self.metadata[item["name"]] = metadata
if "user_metadata" not in item:
self.read_user_metadata(item)
data_sortdir = shared.opts.extra_networks_card_order
data_sortmode = shared.opts.extra_networks_card_order_field.lower().replace("sort", "").replace(" ", "_").rstrip("_").strip()
data_sortkey = f"{data_sortmode}-{data_sortdir}-{len(self.items)}"
tree_view_btn_extra_class = ""
tree_view_div_extra_class = "hidden"
if shared.opts.extra_networks_tree_view_default_enabled:
tree_view_btn_extra_class = "extra-network-control--enabled"
tree_view_div_extra_class = ""
return self.pane_tpl.format(
**{
"tabname": tabname,
"extra_networks_tabname": self.extra_networks_tabname,
"data_sortmode": data_sortmode,
"data_sortkey": data_sortkey,
"data_sortdir": data_sortdir,
"tree_view_btn_extra_class": tree_view_btn_extra_class,
"tree_view_div_extra_class": tree_view_div_extra_class,
"tree_html": self.create_tree_view_html(tabname),
"items_html": self.create_card_view_html(tabname, none_message="Loading..." if empty else None),
}
)
def create_item(self, name, index=None):
raise NotImplementedError()
def list_items(self):
raise NotImplementedError()
def allowed_directories_for_previews(self):
return []
def get_sort_keys(self, path): def get_sort_keys(self, path):
""" """
@@ -290,7 +568,7 @@ class ExtraNetworksPage:
"date_created": int(mtime), "date_created": int(mtime),
"date_modified": int(ctime), "date_modified": int(ctime),
"name": pth.name.lower(), "name": pth.name.lower(),
"path": str(pth.parent).lower(), "path": str(pth).lower(),
} }
def find_preview(self, path): def find_preview(self, path):
@@ -298,7 +576,7 @@ class ExtraNetworksPage:
Find a preview PNG for a given path (without extension) and call link_preview on it. Find a preview PNG for a given path (without extension) and call link_preview on it.
""" """
potential_files = sum([[path + "." + ext, path + ".preview." + ext] for ext in allowed_preview_extensions()], []) potential_files = sum([[f"{path}.{ext}", f"{path}.preview.{ext}"] for ext in allowed_preview_extensions()], [])
for file in potential_files: for file in potential_files:
if self.lister.exists(file): if self.lister.exists(file):
@@ -371,8 +649,6 @@ def pages_in_preferred_order(pages):
def create_ui(interface: gr.Blocks, unrelated_tabs, tabname): def create_ui(interface: gr.Blocks, unrelated_tabs, tabname):
from modules.ui import switch_values_symbol
ui = ExtraNetworksUi() ui = ExtraNetworksUi()
ui.pages = [] ui.pages = []
ui.pages_contents = [] ui.pages_contents = []
@@ -383,45 +659,41 @@ def create_ui(interface: gr.Blocks, unrelated_tabs, tabname):
related_tabs = [] related_tabs = []
for page in ui.stored_extra_pages: for page in ui.stored_extra_pages:
with gr.Tab(page.title, elem_id=f"{tabname}_{page.id_page}", elem_classes=["extra-page"]) as tab: with gr.Tab(page.title, elem_id=f"{tabname}_{page.extra_networks_tabname}", elem_classes=["extra-page"]) as tab:
with gr.Column(elem_id=f"{tabname}_{page.id_page}_prompts", elem_classes=["extra-page-prompts"]): with gr.Column(elem_id=f"{tabname}_{page.extra_networks_tabname}_prompts", elem_classes=["extra-page-prompts"]):
pass pass
elem_id = f"{tabname}_{page.id_page}_cards_html" elem_id = f"{tabname}_{page.extra_networks_tabname}_cards_html"
page_elem = gr.HTML('Loading...', elem_id=elem_id) page_elem = gr.HTML(page.create_html(tabname, empty=True), elem_id=elem_id)
ui.pages.append(page_elem) ui.pages.append(page_elem)
page_elem.change(fn=lambda: None, _js='function(){applyExtraNetworkFilter(' + quote_js(tabname) + '); return []}', inputs=[], outputs=[])
editor = page.create_user_metadata_editor(ui, tabname) editor = page.create_user_metadata_editor(ui, tabname)
editor.create_ui() editor.create_ui()
ui.user_metadata_editors.append(editor) ui.user_metadata_editors.append(editor)
related_tabs.append(tab) related_tabs.append(tab)
edit_search = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", elem_classes="search", placeholder="Search...", visible=False, interactive=True) ui.button_save_preview = gr.Button('Save preview', elem_id=f"{tabname}_save_preview", visible=False)
dropdown_sort = gr.Dropdown(choices=['Path', 'Name', 'Date Created', 'Date Modified', ], value=shared.opts.extra_networks_card_order_field, elem_id=tabname+"_extra_sort", elem_classes="sort", multiselect=False, visible=False, show_label=False, interactive=True, label=tabname+"_extra_sort_order") ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=f"{tabname}_preview_filename", visible=False)
button_sortorder = ToolButton(switch_values_symbol, elem_id=tabname+"_extra_sortorder", elem_classes=["sortorder"] + ([] if shared.opts.extra_networks_card_order == "Ascending" else ["sortReverse"]), visible=False, tooltip="Invert sort order")
button_refresh = gr.Button('Refresh', elem_id=tabname+"_extra_refresh", visible=False)
checkbox_show_dirs = gr.Checkbox(True, label='Show dirs', elem_id=tabname+"_extra_show_dirs", elem_classes="show-dirs", visible=False)
ui.button_save_preview = gr.Button('Save preview', elem_id=tabname+"_save_preview", visible=False)
ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=tabname+"_preview_filename", visible=False)
tab_controls = [edit_search, dropdown_sort, button_sortorder, button_refresh, checkbox_show_dirs]
for tab in unrelated_tabs: for tab in unrelated_tabs:
tab.select(fn=lambda: [gr.update(visible=False) for _ in tab_controls], _js='function(){ extraNetworksUrelatedTabSelected("' + tabname + '"); }', inputs=[], outputs=tab_controls, show_progress=False) tab.select(fn=None, _js=f"function(){{extraNetworksUnrelatedTabSelected('{tabname}');}}", inputs=[], outputs=[], show_progress=False)
for page, tab in zip(ui.stored_extra_pages, related_tabs): for page, tab in zip(ui.stored_extra_pages, related_tabs):
allow_prompt = "true" if page.allow_prompt else "false" jscode = (
allow_negative_prompt = "true" if page.allow_negative_prompt else "false" "function(){{"
f"extraNetworksTabSelected('{tabname}', '{tabname}_{page.extra_networks_tabname}_prompts', {str(page.allow_prompt).lower()}, {str(page.allow_negative_prompt).lower()}, '{tabname}_{page.extra_networks_tabname}');"
f"applyExtraNetworkFilter('{tabname}_{page.extra_networks_tabname}');"
"}}"
)
tab.select(fn=None, _js=jscode, inputs=[], outputs=[], show_progress=False)
jscode = 'extraNetworksTabSelected("' + tabname + '", "' + f"{tabname}_{page.id_page}_prompts" + '", ' + allow_prompt + ', ' + allow_negative_prompt + ');' def refresh():
for pg in ui.stored_extra_pages:
pg.refresh()
create_html()
return ui.pages_contents
tab.select(fn=lambda: [gr.update(visible=True) for _ in tab_controls], _js='function(){ ' + jscode + ' }', inputs=[], outputs=tab_controls, show_progress=False) button_refresh = gr.Button("Refresh", elem_id=f"{tabname}_{page.extra_networks_tabname}_extra_refresh_internal", visible=False)
button_refresh.click(fn=refresh, inputs=[], outputs=ui.pages).then(fn=lambda: None, _js="function(){ " + f"applyExtraNetworkFilter('{tabname}_{page.extra_networks_tabname}');" + " }")
dropdown_sort.change(fn=lambda: None, _js="function(){ applyExtraNetworkSort('" + tabname + "'); }")
def create_html(): def create_html():
ui.pages_contents = [pg.create_html(ui.tabname) for pg in ui.stored_extra_pages] ui.pages_contents = [pg.create_html(ui.tabname) for pg in ui.stored_extra_pages]
@@ -431,14 +703,7 @@ def create_ui(interface: gr.Blocks, unrelated_tabs, tabname):
create_html() create_html()
return ui.pages_contents return ui.pages_contents
def refresh():
for pg in ui.stored_extra_pages:
pg.refresh()
create_html()
return ui.pages_contents
interface.load(fn=pages_html, inputs=[], outputs=ui.pages) interface.load(fn=pages_html, inputs=[], outputs=ui.pages)
button_refresh.click(fn=refresh, inputs=[], outputs=ui.pages)
return ui return ui
@@ -487,5 +752,3 @@ def setup_ui(ui, gallery):
for editor in ui.user_metadata_editors: for editor in ui.user_metadata_editors:
editor.setup_ui(gallery) editor.setup_ui(gallery)
+5 -3
View File
@@ -2,7 +2,6 @@ import html
import os import os
from modules import shared, ui_extra_networks, sd_models from modules import shared, ui_extra_networks, sd_models
from modules.ui_extra_networks import quote_js
from modules.ui_extra_networks_checkpoints_user_metadata import CheckpointUserMetadataEditor from modules.ui_extra_networks_checkpoints_user_metadata import CheckpointUserMetadataEditor
@@ -21,14 +20,17 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
return return
path, ext = os.path.splitext(checkpoint.filename) path, ext = os.path.splitext(checkpoint.filename)
search_terms = [self.search_terms_from_path(checkpoint.filename)]
if checkpoint.sha256:
search_terms.append(checkpoint.sha256)
return { return {
"name": checkpoint.name_for_extra, "name": checkpoint.name_for_extra,
"filename": checkpoint.filename, "filename": checkpoint.filename,
"shorthash": checkpoint.shorthash, "shorthash": checkpoint.shorthash,
"preview": self.find_preview(path), "preview": self.find_preview(path),
"description": self.find_description(path), "description": self.find_description(path),
"search_term": self.search_terms_from_path(checkpoint.filename) + " " + (checkpoint.sha256 or ""), "search_terms": search_terms,
"onclick": '"' + html.escape(f"""return selectCheckpoint({quote_js(name)})""") + '"', "onclick": html.escape(f"return selectCheckpoint({ui_extra_networks.quote_js(name)})"),
"local_preview": f"{path}.{shared.opts.samples_format}", "local_preview": f"{path}.{shared.opts.samples_format}",
"metadata": checkpoint.metadata, "metadata": checkpoint.metadata,
"sort_keys": {'default': index, **self.get_sort_keys(checkpoint.filename)}, "sort_keys": {'default': index, **self.get_sort_keys(checkpoint.filename)},
+4 -2
View File
@@ -20,14 +20,16 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage):
path, ext = os.path.splitext(full_path) path, ext = os.path.splitext(full_path)
sha256 = sha256_from_cache(full_path, f'hypernet/{name}') sha256 = sha256_from_cache(full_path, f'hypernet/{name}')
shorthash = sha256[0:10] if sha256 else None shorthash = sha256[0:10] if sha256 else None
search_terms = [self.search_terms_from_path(path)]
if sha256:
search_terms.append(sha256)
return { return {
"name": name, "name": name,
"filename": full_path, "filename": full_path,
"shorthash": shorthash, "shorthash": shorthash,
"preview": self.find_preview(path), "preview": self.find_preview(path),
"description": self.find_description(path), "description": self.find_description(path),
"search_term": self.search_terms_from_path(path) + " " + (sha256 or ""), "search_terms": search_terms,
"prompt": quote_js(f"<hypernet:{name}:") + " + opts.extra_networks_default_multiplier + " + quote_js(">"), "prompt": quote_js(f"<hypernet:{name}:") + " + opts.extra_networks_default_multiplier + " + quote_js(">"),
"local_preview": f"{path}.preview.{shared.opts.samples_format}", "local_preview": f"{path}.preview.{shared.opts.samples_format}",
"sort_keys": {'default': index, **self.get_sort_keys(path + ext)}, "sort_keys": {'default': index, **self.get_sort_keys(path + ext)},
@@ -18,13 +18,16 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage):
return return
path, ext = os.path.splitext(embedding.filename) path, ext = os.path.splitext(embedding.filename)
search_terms = [self.search_terms_from_path(embedding.filename)]
if embedding.hash:
search_terms.append(embedding.hash)
return { return {
"name": name, "name": name,
"filename": embedding.filename, "filename": embedding.filename,
"shorthash": embedding.shorthash, "shorthash": embedding.shorthash,
"preview": self.find_preview(path), "preview": self.find_preview(path),
"description": self.find_description(path), "description": self.find_description(path),
"search_term": self.search_terms_from_path(embedding.filename) + " " + (embedding.hash or ""), "search_terms": search_terms,
"prompt": quote_js(embedding.name), "prompt": quote_js(embedding.name),
"local_preview": f"{path}.preview.{shared.opts.samples_format}", "local_preview": f"{path}.preview.{shared.opts.samples_format}",
"sort_keys": {'default': index, **self.get_sort_keys(embedding.filename)}, "sort_keys": {'default': index, **self.get_sort_keys(embedding.filename)},
+1 -1
View File
@@ -14,7 +14,7 @@ class UserMetadataEditor:
self.ui = ui self.ui = ui
self.tabname = tabname self.tabname = tabname
self.page = page self.page = page
self.id_part = f"{self.tabname}_{self.page.id_page}_edit_user_metadata" self.id_part = f"{self.tabname}_{self.page.extra_networks_tabname}_edit_user_metadata"
self.box = None self.box = None
+2 -1
View File
@@ -1,13 +1,14 @@
import gradio as gr import gradio as gr
from modules import scripts, shared, ui_common, postprocessing, call_queue, ui_toprow from modules import scripts, shared, ui_common, postprocessing, call_queue, ui_toprow
import modules.infotext_utils as parameters_copypaste import modules.infotext_utils as parameters_copypaste
from modules.ui_components import ResizeHandleRow
def create_ui(): def create_ui():
dummy_component = gr.Label(visible=False) dummy_component = gr.Label(visible=False)
tab_index = gr.Number(value=0, visible=False) tab_index = gr.Number(value=0, visible=False)
with gr.Row(equal_height=False, variant='compact'): with ResizeHandleRow(equal_height=False, variant='compact'):
with gr.Column(variant='compact'): with gr.Column(variant='compact'):
with gr.Tabs(elem_id="mode_extras"): with gr.Tabs(elem_id="mode_extras"):
with gr.TabItem('Single Image', id="single_image", elem_id="extras_single_tab") as tab_single: with gr.TabItem('Single Image', id="single_image", elem_id="extras_single_tab") as tab_single:
+6 -3
View File
@@ -22,9 +22,12 @@ def save_style(name, prompt, negative_prompt):
if not name: if not name:
return gr.update(visible=False) return gr.update(visible=False)
style = styles.PromptStyle(name, prompt, negative_prompt) existing_style = shared.prompt_styles.styles.get(name)
path = existing_style.path if existing_style is not None else None
style = styles.PromptStyle(name, prompt, negative_prompt, path)
shared.prompt_styles.styles[style.name] = style shared.prompt_styles.styles[style.name] = style
shared.prompt_styles.save_styles(shared.styles_filename) shared.prompt_styles.save_styles()
return gr.update(visible=True) return gr.update(visible=True)
@@ -34,7 +37,7 @@ def delete_style(name):
return return
shared.prompt_styles.styles.pop(name, None) shared.prompt_styles.styles.pop(name, None)
shared.prompt_styles.save_styles(shared.styles_filename) shared.prompt_styles.save_styles()
return '', '', '' return '', '', ''
+18 -6
View File
@@ -35,12 +35,9 @@ def save_pil_to_file(self, pil_image, dir=None, format="png"):
already_saved_as = getattr(pil_image, 'already_saved_as', None) already_saved_as = getattr(pil_image, 'already_saved_as', None)
if already_saved_as and os.path.isfile(already_saved_as): if already_saved_as and os.path.isfile(already_saved_as):
register_tmp_file(shared.demo, already_saved_as) register_tmp_file(shared.demo, already_saved_as)
filename = already_saved_as filename_with_mtime = f'{already_saved_as}?{os.path.getmtime(already_saved_as)}'
register_tmp_file(shared.demo, filename_with_mtime)
if not shared.opts.save_images_add_number: return filename_with_mtime
filename += f'?{os.path.getmtime(already_saved_as)}'
return filename
if shared.opts.temp_dir != "": if shared.opts.temp_dir != "":
dir = shared.opts.temp_dir dir = shared.opts.temp_dir
@@ -86,3 +83,18 @@ def cleanup_tmpdr():
filename = os.path.join(root, name) filename = os.path.join(root, name)
os.remove(filename) os.remove(filename)
def is_gradio_temp_path(path):
"""
Check if the path is a temp dir used by gradio
"""
path = Path(path)
if shared.opts.temp_dir and path.is_relative_to(shared.opts.temp_dir):
return True
if gradio_temp_dir := os.environ.get("GRADIO_TEMP_DIR"):
if path.is_relative_to(gradio_temp_dir):
return True
if path.is_relative_to(Path(tempfile.gettempdir()) / "gradio"):
return True
return False
+10 -16
View File
@@ -17,6 +17,7 @@ class Toprow:
button_deepbooru = None button_deepbooru = None
interrupt = None interrupt = None
interrupting = None
skip = None skip = None
submit = None submit = None
@@ -96,15 +97,10 @@ class Toprow:
with gr.Row(elem_id=f"{self.id_part}_generate_box", elem_classes=["generate-box"] + (["generate-box-compact"] if self.is_compact else []), render=not self.is_compact) as submit_box: with gr.Row(elem_id=f"{self.id_part}_generate_box", elem_classes=["generate-box"] + (["generate-box-compact"] if self.is_compact else []), render=not self.is_compact) as submit_box:
self.submit_box = submit_box self.submit_box = submit_box
self.interrupt = gr.Button('Interrupt', elem_id=f"{self.id_part}_interrupt", elem_classes="generate-box-interrupt") self.interrupt = gr.Button('Interrupt', elem_id=f"{self.id_part}_interrupt", elem_classes="generate-box-interrupt", tooltip="End generation immediately or after completing current batch")
self.skip = gr.Button('Skip', elem_id=f"{self.id_part}_skip", elem_classes="generate-box-skip") self.skip = gr.Button('Skip', elem_id=f"{self.id_part}_skip", elem_classes="generate-box-skip", tooltip="Stop generation of current batch and continues onto next batch")
self.submit = gr.Button('Generate', elem_id=f"{self.id_part}_generate", variant='primary') self.interrupting = gr.Button('Interrupting...', elem_id=f"{self.id_part}_interrupting", elem_classes="generate-box-interrupting", tooltip="Interrupting generation...")
self.submit = gr.Button('Generate', elem_id=f"{self.id_part}_generate", variant='primary', tooltip="Right click generate forever menu")
self.skip.click(
fn=lambda: shared.state.skip(),
inputs=[],
outputs=[],
)
def interrupt_function(): def interrupt_function():
if not shared.state.stopping_generation and shared.state.job_count > 1 and shared.opts.interrupt_after_current: if not shared.state.stopping_generation and shared.state.job_count > 1 and shared.opts.interrupt_after_current:
@@ -113,11 +109,9 @@ class Toprow:
else: else:
shared.state.interrupt() shared.state.interrupt()
self.interrupt.click( self.skip.click(fn=shared.state.skip)
fn=interrupt_function, self.interrupt.click(fn=interrupt_function, _js='function(){ showSubmitInterruptingPlaceholder("' + self.id_part + '"); }')
inputs=[], self.interrupting.click(fn=interrupt_function)
outputs=[],
)
def create_tools_row(self): def create_tools_row(self):
with gr.Row(elem_id=f"{self.id_part}_tools"): with gr.Row(elem_id=f"{self.id_part}_tools"):
@@ -133,9 +127,9 @@ class Toprow:
self.restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{self.id_part}_restore_progress", visible=False, tooltip="Restore progress") self.restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{self.id_part}_restore_progress", visible=False, tooltip="Restore progress")
self.token_counter = gr.HTML(value="<span>0/75</span>", elem_id=f"{self.id_part}_token_counter", elem_classes=["token-counter"]) self.token_counter = gr.HTML(value="<span>0/75</span>", elem_id=f"{self.id_part}_token_counter", elem_classes=["token-counter"], visible=False)
self.token_button = gr.Button(visible=False, elem_id=f"{self.id_part}_token_button") self.token_button = gr.Button(visible=False, elem_id=f"{self.id_part}_token_button")
self.negative_token_counter = gr.HTML(value="<span>0/75</span>", elem_id=f"{self.id_part}_negative_token_counter", elem_classes=["token-counter"]) self.negative_token_counter = gr.HTML(value="<span>0/75</span>", elem_id=f"{self.id_part}_negative_token_counter", elem_classes=["token-counter"], visible=False)
self.negative_token_button = gr.Button(visible=False, elem_id=f"{self.id_part}_negative_token_button") self.negative_token_button = gr.Button(visible=False, elem_id=f"{self.id_part}_negative_token_button")
self.clear_prompt_button.click( self.clear_prompt_button.click(
+2 -1
View File
@@ -6,7 +6,7 @@ import torch
import tqdm import tqdm
from PIL import Image from PIL import Image
from modules import images, shared, torch_utils from modules import devices, images, shared, torch_utils
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -44,6 +44,7 @@ def upscale_pil_patch(model, img: Image.Image) -> Image.Image:
with torch.no_grad(): with torch.no_grad():
tensor = pil_image_to_torch_bgr(img).unsqueeze(0) # add batch dimension tensor = pil_image_to_torch_bgr(img).unsqueeze(0) # add batch dimension
tensor = tensor.to(device=param.device, dtype=param.dtype) tensor = tensor.to(device=param.device, dtype=param.dtype)
with devices.without_autocast():
return torch_bgr_to_pil_image(model(tensor)) return torch_bgr_to_pil_image(model(tensor))
+1 -1
View File
@@ -42,7 +42,7 @@ def walk_files(path, allowed_extensions=None):
for filename in sorted(files, key=natural_sort_key): for filename in sorted(files, key=natural_sort_key):
if allowed_extensions is not None: if allowed_extensions is not None:
_, ext = os.path.splitext(filename) _, ext = os.path.splitext(filename)
if ext not in allowed_extensions: if ext.lower() not in allowed_extensions:
continue continue
if not shared.opts.list_hidden_files and ("/." in root or "\\." in root): if not shared.opts.list_hidden_files and ("/." in root or "\\." in root):
+4
View File
@@ -0,0 +1,4 @@
cloudpickle
decorator
synr==0.5.0
tornado
+1 -1
View File
@@ -19,7 +19,7 @@ piexif==1.1.3
psutil==5.9.5 psutil==5.9.5
pytorch_lightning==1.9.4 pytorch_lightning==1.9.4
resize-right==0.0.2 resize-right==0.0.2
safetensors==0.3.1 safetensors==0.4.2
scikit-image==0.21.0 scikit-image==0.21.0
spandrel==0.1.6 spandrel==0.1.6
tomesd==0.1.3 tomesd==0.1.3
+2
View File
@@ -167,10 +167,12 @@ document.addEventListener('keydown', function(e) {
const lightboxModal = document.querySelector('#lightboxModal'); const lightboxModal = document.querySelector('#lightboxModal');
if (!globalPopup || globalPopup.style.display === 'none') { if (!globalPopup || globalPopup.style.display === 'none') {
if (document.activeElement === lightboxModal) return; if (document.activeElement === lightboxModal) return;
if (interruptButton.style.display === 'block') {
interruptButton.click(); interruptButton.click();
e.preventDefault(); e.preventDefault();
} }
} }
}
}); });
/** /**
+1 -1
View File
@@ -61,7 +61,7 @@ class ScriptPostprocessingSplitOversized(scripts_postprocessing.ScriptPostproces
ratio = (pp.image.height * width) / (pp.image.width * height) ratio = (pp.image.height * width) / (pp.image.width * height)
inverse_xy = True inverse_xy = True
if ratio >= 1.0 and ratio > split_threshold: if ratio >= 1.0 or ratio > split_threshold:
return return
result, *others = split_pic(pp.image, inverse_xy, width, height, overlap_ratio) result, *others = split_pic(pp.image, inverse_xy, width, height, overlap_ratio)
+4
View File
@@ -554,6 +554,8 @@ class Script(scripts.Script):
valslist_ext = [] valslist_ext = []
for val in valslist: for val in valslist:
if val.strip() == '':
continue
m = re_range.fullmatch(val) m = re_range.fullmatch(val)
mc = re_range_count.fullmatch(val) mc = re_range_count.fullmatch(val)
if m is not None: if m is not None:
@@ -576,6 +578,8 @@ class Script(scripts.Script):
valslist_ext = [] valslist_ext = []
for val in valslist: for val in valslist:
if val.strip() == '':
continue
m = re_range_float.fullmatch(val) m = re_range_float.fullmatch(val)
mc = re_range_count_float.fullmatch(val) mc = re_range_count_float.fullmatch(val)
if m is not None: if m is not None:
+502 -54
View File
@@ -28,7 +28,7 @@ div.gradio-container{
} }
.hidden{ .hidden{
display: none; display: none !important;
} }
.compact{ .compact{
@@ -222,6 +222,10 @@ input[type="checkbox"].input-accordion-checkbox{
top: -0.75em; top: -0.75em;
} }
.block.token-counter-visible{
display: block !important;
}
.block.token-counter span{ .block.token-counter span{
background: var(--input-background-fill) !important; background: var(--input-background-fill) !important;
box-shadow: 0 0 0.0 0.3em rgba(192,192,192,0.15), inset 0 0 0.6em rgba(192,192,192,0.075); box-shadow: 0 0 0.0 0.3em rgba(192,192,192,0.15), inset 0 0 0.6em rgba(192,192,192,0.075);
@@ -331,17 +335,17 @@ input[type="checkbox"].input-accordion-checkbox{
.generate-box{ .generate-box{
position: relative; position: relative;
} }
.gradio-button.generate-box-skip, .gradio-button.generate-box-interrupt{ .gradio-button.generate-box-skip, .gradio-button.generate-box-interrupt, .gradio-button.generate-box-interrupting{
position: absolute; position: absolute;
width: 50%; width: 50%;
height: 100%; height: 100%;
display: none; display: none;
background: #b4c0cc; background: #b4c0cc;
} }
.gradio-button.generate-box-skip:hover, .gradio-button.generate-box-interrupt:hover{ .gradio-button.generate-box-skip:hover, .gradio-button.generate-box-interrupt:hover, .gradio-button.generate-box-interrupting:hover{
background: #c2cfdb; background: #c2cfdb;
} }
.gradio-button.generate-box-interrupt{ .gradio-button.generate-box-interrupt, .gradio-button.generate-box-interrupting{
left: 0; left: 0;
border-radius: 0.5rem 0 0 0.5rem; border-radius: 0.5rem 0 0 0.5rem;
} }
@@ -846,6 +850,20 @@ table.popup-table .link{
display: inline-block; display: inline-block;
} }
/* extensions tab table row hover highlight */
#extensions tr:hover td,
#config_state_extensions tr:hover td,
#available_extensions tr:hover td {
background: rgba(0, 0, 0, 0.15);
}
.dark #extensions tr:hover td ,
.dark #config_state_extensions tr:hover td ,
.dark #available_extensions tr:hover td {
background: rgba(255, 255, 255, 0.15);
}
/* replace original footer with ours */ /* replace original footer with ours */
footer { footer {
@@ -879,31 +897,21 @@ footer {
margin-bottom: 1em; margin-bottom: 1em;
} }
.extra-network-cards{ .extra-networks > div.tab-nav{
height: calc(100vh - 24rem); min-height: 2.7rem;
overflow: clip scroll;
resize: vertical;
min-height: 52rem;
} }
.extra-networks > div.tab-nav{ .extra-networks-controls-div{
min-height: 3.4rem; align-self: center;
margin-left: auto;
} }
.extra-networks > div > [id *= '_extra_']{ .extra-networks > div > [id *= '_extra_']{
margin: 0.3em; margin: 0.3em;
} }
.extra-network-subdirs{
padding: 0.2em 0.35em;
}
.extra-network-subdirs button{
margin: 0 0.15em;
}
.extra-networks .tab-nav .search, .extra-networks .tab-nav .search,
.extra-networks .tab-nav .sort, .extra-networks .tab-nav .sort
.extra-networks .tab-nav .show-dirs
{ {
margin: 0.3em; margin: 0.3em;
align-self: center; align-self: center;
@@ -924,53 +932,69 @@ footer {
width: auto; width: auto;
} }
.extra-network-cards .nocards{ .extra-network-pane .nocards{
margin: 1.25em 0.5em 0.5em 0.5em; margin: 1.25em 0.5em 0.5em 0.5em;
} }
.extra-network-cards .nocards h1{ .extra-network-pane .nocards h1{
font-size: 1.5em; font-size: 1.5em;
margin-bottom: 1em; margin-bottom: 1em;
} }
.extra-network-cards .nocards li{ .extra-network-pane .nocards li{
margin-left: 0.5em; margin-left: 0.5em;
} }
.extra-network-pane .card .button-row{
display: inline-flex;
visibility: hidden;
color: white;
}
.extra-network-cards .card .button-row{ .extra-network-pane .card .button-row {
display: none;
position: absolute; position: absolute;
color: white;
right: 0; right: 0;
z-index: 1 z-index: 1;
}
.extra-network-cards .card:hover .button-row{
display: flex;
} }
.extra-network-cards .card .card-button{ .extra-network-pane .card:hover .button-row{
visibility: visible;
}
.extra-network-pane .card-button{
color: white; color: white;
} }
.extra-network-cards .card .metadata-button:before{ .extra-network-pane .copy-path-button::before {
content: "⎘";
}
.extra-network-pane .metadata-button::before{
content: "🛈"; content: "🛈";
} }
.extra-network-cards .card .edit-button:before{ .extra-network-pane .edit-button::before{
content: "🛠"; content: "🛠";
} }
.extra-network-cards .card .card-button { .extra-network-pane .card-button {
text-shadow: 2px 2px 3px black;
padding: 0.25em 0.1em;
font-size: 200%;
width: 1.5em; width: 1.5em;
text-shadow: 2px 2px 3px black;
color: white;
padding: 0.25em 0.1em;
} }
.extra-network-cards .card .card-button:hover{
.extra-network-pane .card-button:hover{
color: red; color: red;
} }
.extra-network-pane .card .card-button {
font-size: 2rem;
}
.extra-network-pane .card-minimal .card-button {
font-size: 1rem;
}
.standalone-card-preview.card .preview{ .standalone-card-preview.card .preview{
position: absolute; position: absolute;
@@ -979,7 +1003,7 @@ footer {
height:100%; height:100%;
} }
.extra-network-cards .card, .standalone-card-preview.card{ .extra-network-pane .card, .standalone-card-preview.card{
display: inline-block; display: inline-block;
margin: 0.5rem; margin: 0.5rem;
width: 16rem; width: 16rem;
@@ -996,15 +1020,15 @@ footer {
background-image: url('./file=html/card-no-preview.png') background-image: url('./file=html/card-no-preview.png')
} }
.extra-network-cards .card:hover{ .extra-network-pane .card:hover{
box-shadow: 0 0 2px 0.3em rgba(0, 128, 255, 0.35); box-shadow: 0 0 2px 0.3em rgba(0, 128, 255, 0.35);
} }
.extra-network-cards .card .actions .additional{ .extra-network-pane .card .actions .additional{
display: none; display: none;
} }
.extra-network-cards .card .actions{ .extra-network-pane .card .actions{
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
@@ -1015,45 +1039,45 @@ footer {
text-shadow: 0 0 0.2em black; text-shadow: 0 0 0.2em black;
} }
.extra-network-cards .card .actions *{ .extra-network-pane .card .actions *{
color: white; color: white;
} }
.extra-network-cards .card .actions .name{ .extra-network-pane .card .actions .name{
font-size: 1.7em; font-size: 1.7em;
font-weight: bold; font-weight: bold;
line-break: anywhere; line-break: anywhere;
} }
.extra-network-cards .card .actions .description { .extra-network-pane .card .actions .description {
display: block; display: block;
max-height: 3em; max-height: 3em;
white-space: pre-wrap; white-space: pre-wrap;
line-height: 1.1; line-height: 1.1;
} }
.extra-network-cards .card .actions .description:hover { .extra-network-pane .card .actions .description:hover {
max-height: none; max-height: none;
} }
.extra-network-cards .card .actions:hover .additional{ .extra-network-pane .card .actions:hover .additional{
display: block; display: block;
} }
.extra-network-cards .card ul{ .extra-network-pane .card ul{
margin: 0.25em 0 0.75em 0.25em; margin: 0.25em 0 0.75em 0.25em;
cursor: unset; cursor: unset;
} }
.extra-network-cards .card ul a{ .extra-network-pane .card ul a{
cursor: pointer; cursor: pointer;
} }
.extra-network-cards .card ul a:hover{ .extra-network-pane .card ul a:hover{
color: red; color: red;
} }
.extra-network-cards .card .preview{ .extra-network-pane .card .preview{
position: absolute; position: absolute;
object-fit: cover; object-fit: cover;
width: 100%; width: 100%;
@@ -1096,9 +1120,6 @@ div.block.gradio-box.edit-user-metadata {
margin-top: 1.5em; margin-top: 1.5em;
} }
div.block.gradio-box.popup-dialog, .popup-dialog { div.block.gradio-box.popup-dialog, .popup-dialog {
width: 56em; width: 56em;
background: var(--body-background-fill); background: var(--body-background-fill);
@@ -1173,3 +1194,430 @@ body.resizing .resize-handle {
left: 7.5px; left: 7.5px;
border-left: 1px dashed var(--border-color-primary); border-left: 1px dashed var(--border-color-primary);
} }
/* ========================= */
.extra-network-pane {
display: flex;
height: calc(100vh - 24rem);
resize: vertical;
min-height: 52rem;
flex-direction: column;
overflow: hidden;
}
.extra-network-pane .extra-network-pane-content {
display: flex;
flex: 1;
overflow: hidden;
}
.extra-network-pane .extra-network-tree {
flex: 1;
font-size: 1rem;
border: 1px solid var(--block-border-color);
overflow: clip auto !important;
}
.extra-network-pane .extra-network-cards {
flex: 3;
overflow: clip auto !important;
border: 1px solid var(--block-border-color);
}
.extra-network-pane .extra-network-tree .tree-list {
flex: 1;
display: flex;
flex-direction: column;
padding: 0;
width: 100%;
overflow: hidden;
}
.extra-network-pane .extra-network-cards::-webkit-scrollbar,
.extra-network-pane .extra-network-tree::-webkit-scrollbar {
background-color: transparent;
width: 16px;
}
.extra-network-pane .extra-network-cards::-webkit-scrollbar-track,
.extra-network-pane .extra-network-tree::-webkit-scrollbar-track {
background-color: transparent;
background-clip: content-box;
}
.extra-network-pane .extra-network-cards::-webkit-scrollbar-thumb,
.extra-network-pane .extra-network-tree::-webkit-scrollbar-thumb {
background-color: var(--border-color-primary);
border-radius: 16px;
border: 4px solid var(--background-fill-primary);
}
.extra-network-pane .extra-network-cards::-webkit-scrollbar-button,
.extra-network-pane .extra-network-tree::-webkit-scrollbar-button {
display: none;
}
.extra-network-control {
position: relative;
display: grid;
width: 100%;
padding: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
font-size: 1rem;
text-align: left;
user-select: none;
background-color: transparent;
border: none;
transition: background 33.333ms linear;
grid-template-rows: min-content;
grid-template-columns: minmax(0, auto) repeat(4, min-content);
grid-gap: 0.1rem;
align-items: start;
}
.extra-network-tree .tree-list--tree {}
/* Remove auto indentation from tree. Will be overridden later. */
.extra-network-tree .tree-list--subgroup {
margin: 0 !important;
padding: 0 !important;
box-shadow: 0.5rem 0 0 var(--body-background-fill) inset,
0.7rem 0 0 var(--neutral-800) inset;
}
/* Set indentation for each depth of tree. */
.extra-network-tree .tree-list--subgroup > .tree-list-item {
margin-left: 0.4rem !important;
padding-left: 0.4rem !important;
}
/* Styles for tree <li> elements. */
.extra-network-tree .tree-list-item {
list-style: none;
position: relative;
background-color: transparent;
}
/* Directory <ul> visibility based on data-expanded attribute. */
.extra-network-tree .tree-list-content+.tree-list--subgroup {
height: 0;
visibility: hidden;
opacity: 0;
}
.extra-network-tree .tree-list-content[data-expanded]+.tree-list--subgroup {
height: auto;
visibility: visible;
opacity: 1;
}
/* File <li> */
.extra-network-tree .tree-list-item--subitem {
padding-top: 0 !important;
padding-bottom: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
/* <li> containing <ul> */
.extra-network-tree .tree-list-item--has-subitem {}
/* BUTTON ELEMENTS */
/* <button> */
.extra-network-tree .tree-list-content {
position: relative;
display: grid;
width: 100%;
padding: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
font-size: 1rem;
text-align: left;
user-select: none;
background-color: transparent;
border: none;
transition: background 33.333ms linear;
grid-template-rows: min-content;
grid-template-areas: "leading-action leading-visual label trailing-visual trailing-action";
grid-template-columns: min-content min-content minmax(0, auto) min-content min-content;
grid-gap: 0.1rem;
align-items: start;
flex-grow: 1;
flex-basis: 100%;
}
/* Buttons for directories. */
.extra-network-tree .tree-list-content-dir {}
/* Buttons for files. */
.extra-network-tree .tree-list-item--has-subitem .tree-list--subgroup > li:first-child {
padding-top: 0.5rem !important;
}
.dark .extra-network-tree div.tree-list-content:hover {
-webkit-transition: all 0.05s ease-in-out;
transition: all 0.05s ease-in-out;
background-color: var(--neutral-800);
}
.dark .extra-network-tree div.tree-list-content[data-selected] {
background-color: var(--neutral-700);
}
.extra-network-tree div.tree-list-content[data-selected] {
background-color: var(--neutral-300);
}
.extra-network-tree div.tree-list-content:hover {
-webkit-transition: all 0.05s ease-in-out;
transition: all 0.05s ease-in-out;
background-color: var(--neutral-200);
}
/* ==== CHEVRON ICON ACTIONS ==== */
/* Define the animation for the arrow when it is clicked. */
.extra-network-tree .tree-list-content-dir .tree-list-item-action-chevron {
-ms-transform: rotate(135deg);
-webkit-transform: rotate(135deg);
transform: rotate(135deg);
transition: transform 0.2s;
}
.extra-network-tree .tree-list-content-dir[data-expanded] .tree-list-item-action-chevron {
-ms-transform: rotate(225deg);
-webkit-transform: rotate(225deg);
transform: rotate(225deg);
transition: transform 0.2s;
}
.tree-list-item-action-chevron {
display: inline-flex;
/* Uses box shadow to generate a pseudo chevron `>` icon. */
padding: 0.3rem;
box-shadow: 0.1rem 0.1rem 0 0 var(--neutral-200) inset;
transform: rotate(135deg);
}
/* ==== SEARCH INPUT ACTIONS ==== */
/* Add icon to left side of <input> */
.extra-network-control .extra-network-control--search::before {
content: "🔎︎";
position: absolute;
margin: 0.5rem;
font-size: 1rem;
color: var(--input-placeholder-color);
}
.extra-network-control .extra-network-control--search {
display: inline-flex;
position: relative;
}
.extra-network-control .extra-network-control--search .extra-network-control--search-text {
border: 1px solid var(--button-secondary-border-color);
border-radius: 0.5rem;
color: var(--button-secondary-text-color);
background-color: transparent;
width: 100%;
padding-left: 2rem;
line-height: 1rem;
}
/* <input> clear button (x on right side) styling */
.extra-network-control .extra-network-control--search .extra-network-control--search-text::-webkit-search-cancel-button {
-webkit-appearance: none;
appearance: none;
cursor: pointer;
height: 1rem;
width: 1rem;
mask-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>');
mask-repeat: no-repeat;
mask-position: center center;
mask-size: 100%;
background-color: var(--input-placeholder-color);
}
/* ==== SORT ICON ACTIONS ==== */
.extra-network-control .extra-network-control--sort {
padding: 0.25rem;
display: inline-flex;
cursor: pointer;
justify-self: center;
align-self: center;
}
.extra-network-control .extra-network-control--sort .extra-network-control--sort-icon {
height: 1.5rem;
width: 1.5rem;
mask-repeat: no-repeat;
mask-position: center center;
mask-size: 100%;
background-color: var(--input-placeholder-color);
}
.extra-network-control .extra-network-control--sort[data-sortmode="path"] .extra-network-control--sort-icon {
mask-image: url('data:image/svg+xml,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill-rule="evenodd" clip-rule="evenodd" d="M1 5C1 3.34315 2.34315 2 4 2H8.43845C9.81505 2 11.015 2.93689 11.3489 4.27239L11.7808 6H13.5H20C21.6569 6 23 7.34315 23 9V11C23 11.5523 22.5523 12 22 12C21.4477 12 21 11.5523 21 11V9C21 8.44772 20.5523 8 20 8H13.5H11.7808H4C3.44772 8 3 8.44772 3 9V10V19C3 19.5523 3.44772 20 4 20H9C9.55228 20 10 20.4477 10 21C10 21.5523 9.55228 22 9 22H4C2.34315 22 1 20.6569 1 19V10V9V5ZM3 6.17071C3.31278 6.06015 3.64936 6 4 6H9.71922L9.40859 4.75746C9.2973 4.3123 8.89732 4 8.43845 4H4C3.44772 4 3 4.44772 3 5V6.17071ZM20.1716 18.7574C20.6951 17.967 21 17.0191 21 16C21 13.2386 18.7614 11 16 11C13.2386 11 11 13.2386 11 16C11 18.7614 13.2386 21 16 21C17.0191 21 17.967 20.6951 18.7574 20.1716L21.2929 22.7071C21.6834 23.0976 22.3166 23.0976 22.7071 22.7071C23.0976 22.3166 23.0976 21.6834 22.7071 21.2929L20.1716 18.7574ZM13 16C13 14.3431 14.3431 13 16 13C17.6569 13 19 14.3431 19 16C19 17.6569 17.6569 19 16 19C14.3431 19 13 17.6569 13 16Z" fill="%23000000"></path></g></svg>');
}
.extra-network-control .extra-network-control--sort[data-sortmode="name"] .extra-network-control--sort-icon {
mask-image: url('data:image/svg+xml,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill-rule="evenodd" clip-rule="evenodd" d="M17.1841 6.69223C17.063 6.42309 16.7953 6.25 16.5002 6.25C16.2051 6.25 15.9374 6.42309 15.8162 6.69223L11.3162 16.6922C11.1463 17.07 11.3147 17.514 11.6924 17.6839C12.0701 17.8539 12.5141 17.6855 12.6841 17.3078L14.1215 14.1136H18.8789L20.3162 17.3078C20.4862 17.6855 20.9302 17.8539 21.308 17.6839C21.6857 17.514 21.8541 17.07 21.6841 16.6922L17.1841 6.69223ZM16.5002 8.82764L14.7965 12.6136H18.2039L16.5002 8.82764Z" fill="%231C274C"></path><path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M2.25 7C2.25 6.58579 2.58579 6.25 3 6.25H13C13.4142 6.25 13.75 6.58579 13.75 7C13.75 7.41421 13.4142 7.75 13 7.75H3C2.58579 7.75 2.25 7.41421 2.25 7Z" fill="%231C274C"></path><path opacity="0.5" d="M2.25 12C2.25 11.5858 2.58579 11.25 3 11.25H10C10.4142 11.25 10.75 11.5858 10.75 12C10.75 12.4142 10.4142 12.75 10 12.75H3C2.58579 12.75 2.25 12.4142 2.25 12Z" fill="%231C274C"></path><path opacity="0.5" d="M2.25 17C2.25 16.5858 2.58579 16.25 3 16.25H8C8.41421 16.25 8.75 16.5858 8.75 17C8.75 17.4142 8.41421 17.75 8 17.75H3C2.58579 17.75 2.25 17.4142 2.25 17Z" fill="%231C274C"></path></g></svg>');
}
.extra-network-control .extra-network-control--sort[data-sortmode="date_created"] .extra-network-control--sort-icon {
mask-image: url('data:image/svg+xml,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M17 11C14.2386 11 12 13.2386 12 16C12 18.7614 14.2386 21 17 21C19.7614 21 22 18.7614 22 16C22 13.2386 19.7614 11 17 11ZM17 11V9M2 9V15.8C2 16.9201 2 17.4802 2.21799 17.908C2.40973 18.2843 2.71569 18.5903 3.09202 18.782C3.51984 19 4.0799 19 5.2 19H13M2 9V8.2C2 7.0799 2 6.51984 2.21799 6.09202C2.40973 5.71569 2.71569 5.40973 3.09202 5.21799C3.51984 5 4.0799 5 5.2 5H13.8C14.9201 5 15.4802 5 15.908 5.21799C16.2843 5.40973 16.5903 5.71569 16.782 6.09202C17 6.51984 17 7.0799 17 8.2V9M2 9H17M5 3V5M14 3V5M15 16H17M17 16H19M17 16V14M17 16V18" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>');
}
.extra-network-control .extra-network-control--sort[data-sortmode="date_modified"] .extra-network-control--sort-icon {
mask-image: url('data:image/svg+xml,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M10 21H6.2C5.0799 21 4.51984 21 4.09202 20.782C3.71569 20.5903 3.40973 20.2843 3.21799 19.908C3 19.4802 3 18.9201 3 17.8V8.2C3 7.0799 3 6.51984 3.21799 6.09202C3.40973 5.71569 3.71569 5.40973 4.09202 5.21799C4.51984 5 5.0799 5 6.2 5H17.8C18.9201 5 19.4802 5 19.908 5.21799C20.2843 5.40973 20.5903 5.71569 20.782 6.09202C21 6.51984 21 7.0799 21 8.2V10M7 3V5M17 3V5M3 9H21M13.5 13.0001L7 13M10 17.0001L7 17M14 21L16.025 20.595C16.2015 20.5597 16.2898 20.542 16.3721 20.5097C16.4452 20.4811 16.5147 20.4439 16.579 20.399C16.6516 20.3484 16.7152 20.2848 16.8426 20.1574L21 16C21.5523 15.4477 21.5523 14.5523 21 14C20.4477 13.4477 19.5523 13.4477 19 14L14.8426 18.1574C14.7152 18.2848 14.6516 18.3484 14.601 18.421C14.5561 18.4853 14.5189 18.5548 14.4903 18.6279C14.458 18.7102 14.4403 18.7985 14.405 18.975L14 21Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>');
}
/* ==== SORT DIRECTION ICON ACTIONS ==== */
.extra-network-control .extra-network-control--sort-dir {
padding: 0.25rem;
display: inline-flex;
cursor: pointer;
justify-self: center;
align-self: center;
}
.extra-network-control .extra-network-control--sort-dir .extra-network-control--sort-dir-icon {
height: 1.5rem;
width: 1.5rem;
mask-repeat: no-repeat;
mask-position: center center;
mask-size: 100%;
background-color: var(--input-placeholder-color);
}
.extra-network-control .extra-network-control--sort-dir[data-sortdir="Ascending"] .extra-network-control--sort-dir-icon {
mask-image: url('data:image/svg+xml,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M13 12H21M13 8H21M13 16H21M6 7V17M6 7L3 10M6 7L9 10" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>');
}
.extra-network-control .extra-network-control--sort-dir[data-sortdir="Descending"] .extra-network-control--sort-dir-icon {
mask-image: url('data:image/svg+xml,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M13 12H21M13 8H21M13 16H21M6 7V17M6 17L3 14M6 17L9 14" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>');
}
/* ==== TREE VIEW ICON ACTIONS ==== */
.extra-network-control .extra-network-control--tree-view {
padding: 0.25rem;
display: inline-flex;
cursor: pointer;
justify-self: center;
align-self: center;
}
.extra-network-control .extra-network-control--tree-view .extra-network-control--tree-view-icon {
height: 1.5rem;
width: 1.5rem;
mask-image: url('data:image/svg+xml,<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="black"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill="black" d="M16 10v-4h-11v1h-2v-3h9v-4h-12v4h2v10h3v2h11v-4h-11v1h-2v-5h2v2z"></path></g></svg>');
mask-repeat: no-repeat;
mask-position: center center;
mask-size: 100%;
background-color: var(--input-placeholder-color);
}
.extra-network-control .extra-network-control--enabled {
background-color: rgba(0, 0, 0, 0.15);
}
.dark .extra-network-control .extra-network-control--enabled {
background-color: rgba(255, 255, 255, 0.15);
}
/* ==== REFRESH ICON ACTIONS ==== */
.extra-network-control .extra-network-control--refresh {
padding: 0.25rem;
display: inline-flex;
cursor: pointer;
justify-self: center;
align-self: center;
}
.extra-network-control .extra-network-control--refresh .extra-network-control--refresh-icon {
height: 1.5rem;
width: 1.5rem;
mask-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="bevel"><path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38"/></svg>');
mask-repeat: no-repeat;
mask-position: center center;
mask-size: 100%;
background-color: var(--input-placeholder-color);
}
.extra-network-control .extra-network-control--refresh-icon:active {
-ms-transform: rotate(180deg);
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
transition: transform 0.2s;
}
/* ==== TREE GRID CONFIG ==== */
/* Text for button. */
.extra-network-tree .tree-list-item-label {
position: relative;
line-height: 1.25rem;
color: var(--button-secondary-text-color);
grid-area: label;
padding-left: 0.5rem;
}
/* Text for button truncated. */
.extra-network-tree .tree-list-item-label--truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Icon for button. */
.extra-network-tree .tree-list-item-visual {
min-height: 1rem;
color: var(--button-secondary-text-color);
pointer-events: none;
align-items: right;
}
/* Icon for button when it is before label. */
.extra-network-tree .tree-list-item-visual--leading {
grid-area: leading-visual;
width: 1rem;
text-align: right;
}
/* Icon for button when it is after label. */
.extra-network-tree .tree-list-item-visual--trailing {
grid-area: trailing-visual;
width: 1rem;
text-align: right;
}
/* Dropdown arrow for button. */
.extra-network-tree .tree-list-item-action--leading {
margin-right: 0.5rem;
margin-left: 0.2rem;
}
.extra-network-tree .tree-list-content-file .tree-list-item-action--leading {
visibility: hidden;
}
.extra-network-tree .tree-list-item-action--leading {
grid-area: leading-action;
}
.extra-network-tree .tree-list-item-action--trailing {
grid-area: trailing-action;
display: inline-flex;
}
.extra-network-tree .tree-list-content .button-row {
display: inline-flex;
visibility: hidden;
color: var(--button-secondary-text-color);
}
.extra-network-tree .tree-list-content:hover .button-row {
visibility: visible;
}
+29 -9
View File
@@ -158,6 +158,10 @@ then
if echo "$gpu_info" | grep -q "AMD" && [[ -z "${TORCH_COMMAND}" ]] if echo "$gpu_info" | grep -q "AMD" && [[ -z "${TORCH_COMMAND}" ]]
then then
export TORCH_COMMAND="pip install torch==2.0.1+rocm5.4.2 torchvision==0.15.2+rocm5.4.2 --index-url https://download.pytorch.org/whl/rocm5.4.2" export TORCH_COMMAND="pip install torch==2.0.1+rocm5.4.2 torchvision==0.15.2+rocm5.4.2 --index-url https://download.pytorch.org/whl/rocm5.4.2"
elif echo "$gpu_info" | grep -q "Huawei" && [[ -z "${TORCH_COMMAND}" ]]
then
export TORCH_COMMAND="pip install torch==2.1.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu; pip install torch_npu"
fi fi
fi fi
@@ -222,30 +226,46 @@ fi
# Try using TCMalloc on Linux # Try using TCMalloc on Linux
prepare_tcmalloc() { prepare_tcmalloc() {
if [[ "${OSTYPE}" == "linux"* ]] && [[ -z "${NO_TCMALLOC}" ]] && [[ -z "${LD_PRELOAD}" ]]; then if [[ "${OSTYPE}" == "linux"* ]] && [[ -z "${NO_TCMALLOC}" ]] && [[ -z "${LD_PRELOAD}" ]]; then
# check glibc version
LIBC_VER=$(echo $(ldd --version | awk 'NR==1 {print $NF}') | grep -oP '\d+\.\d+')
echo "glibc version is $LIBC_VER"
libc_vernum=$(expr $LIBC_VER)
# Since 2.34 libpthread is integrated into libc.so
libc_v234=2.34
# Define Tcmalloc Libs arrays # Define Tcmalloc Libs arrays
TCMALLOC_LIBS=("libtcmalloc(_minimal|)\.so\.\d" "libtcmalloc\.so\.\d") TCMALLOC_LIBS=("libtcmalloc(_minimal|)\.so\.\d" "libtcmalloc\.so\.\d")
# Traversal array # Traversal array
for lib in "${TCMALLOC_LIBS[@]}" for lib in "${TCMALLOC_LIBS[@]}"
do do
#Determine which type of tcmalloc library the library supports # Determine which type of tcmalloc library the library supports
TCMALLOC="$(PATH=/usr/sbin:$PATH ldconfig -p | grep -P $lib | head -n 1)" TCMALLOC="$(PATH=/usr/sbin:$PATH ldconfig -p | grep -P $lib | head -n 1)"
TC_INFO=(${TCMALLOC//=>/}) TC_INFO=(${TCMALLOC//=>/})
if [[ ! -z "${TC_INFO}" ]]; then if [[ ! -z "${TC_INFO}" ]]; then
echo "Using TCMalloc: ${TC_INFO}" echo "Check TCMalloc: ${TC_INFO}"
#Determine if the library is linked to libptthread and resolve undefined symbol: ptthread_Key_Create # Determine if the library is linked to libpthread and resolve undefined symbol: pthread_key_create
if [ $(echo "$libc_vernum < $libc_v234" | bc) -eq 1 ]; then
# glibc < 2.34 pthread_key_create into libpthread.so. check linking libpthread.so...
if ldd ${TC_INFO[2]} | grep -q 'libpthread'; then if ldd ${TC_INFO[2]} | grep -q 'libpthread'; then
echo "$TC_INFO is linked with libpthread,execute LD_PRELOAD=${TC_INFO}" echo "$TC_INFO is linked with libpthread,execute LD_PRELOAD=${TC_INFO[2]}"
export LD_PRELOAD="${TC_INFO}" # set fullpath LD_PRELOAD (To be on the safe side)
export LD_PRELOAD="${TC_INFO[2]}"
break break
else else
echo "$TC_INFO is not linked with libpthreadand will trigger undefined symbol: ptthread_Key_Create error" echo "$TC_INFO is not linked with libpthread will trigger undefined symbol: pthread_Key_create error"
fi fi
else else
printf "\e[1m\e[31mCannot locate TCMalloc (improves CPU memory usage)\e[0m\n" # Version 2.34 of libc.so (glibc) includes the pthread library IN GLIBC. (USE ubuntu 22.04 and modern linux system and WSL)
# libc.so(glibc) is linked with a library that works in ALMOST ALL Linux userlands. SO NO CHECK!
echo "$TC_INFO is linked with libc.so,execute LD_PRELOAD=${TC_INFO[2]}"
# set fullpath LD_PRELOAD (To be on the safe side)
export LD_PRELOAD="${TC_INFO[2]}"
break
fi
fi fi
done done
if [[ -z "${LD_PRELOAD}" ]]; then
printf "\e[1m\e[31mCannot locate TCMalloc. Do you have tcmalloc or google-perftool installed on your system? (improves CPU memory usage)\e[0m\n"
fi
fi fi
} }