From 766f6e3eca38996bb291c6e891ce457671abc383 Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:30:36 -0500 Subject: [PATCH 01/57] edit-attention: deselect surrounding whitespace --- javascript/edit-attention.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 688c2f112..01069449f 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -64,6 +64,14 @@ function keyupEditAttention(event) { selectionEnd++; } + // deselect surrounding whitespace + while (target.value.slice(selectionStart, selectionStart + 1) == " " && selectionStart < selectionEnd) { + selectionStart++; + } + while (target.value.slice(selectionEnd - 1, selectionEnd) == " " && selectionEnd > selectionStart) { + selectionEnd--; + } + target.setSelectionRange(selectionStart, selectionEnd); return true; } From 5ab5405b6f50ad0eae0cab32772cb32c5b4a4781 Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Thu, 7 Mar 2024 21:30:05 -0500 Subject: [PATCH 02/57] Simpler comparison Co-authored-by: missionfloyd --- javascript/edit-attention.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 01069449f..b07ba97cb 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -65,10 +65,10 @@ function keyupEditAttention(event) { } // deselect surrounding whitespace - while (target.value.slice(selectionStart, selectionStart + 1) == " " && selectionStart < selectionEnd) { + while (text[selectionStart] == " " && selectionStart < selectionEnd) { selectionStart++; } - while (target.value.slice(selectionEnd - 1, selectionEnd) == " " && selectionEnd > selectionStart) { + while (text[selectionEnd - 1] == " " && selectionEnd > selectionStart) { selectionEnd--; } From e0c9361b7dd673cf28dfe8888cbd463eddbb8431 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Fri, 8 Mar 2024 07:51:14 +0300 Subject: [PATCH 03/57] performance optimization for extra networks --- modules/ui_extra_networks.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index ad2c23054..2cf91d36b 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -489,15 +489,15 @@ class ExtraNetworksPage: Returns: HTML formatted string. """ - res = "" + res = [] for item in self.items.values(): - res += self.create_item_html(tabname, item, self.card_tpl) + res.append(self.create_item_html(tabname, item, self.card_tpl)) - if res == "": + if not res: dirs = "".join([f"
  • {x}
  • " for x in self.allowed_directories_for_previews()]) - res = none_message or shared.html("extra-networks-no-cards.html").format(dirs=dirs) + res = [none_message or shared.html("extra-networks-no-cards.html").format(dirs=dirs)] - return res + return "".join(res) def create_html(self, tabname, *, empty=False): """Generates an HTML string for the current pane. From a43ce7eabbdd58eb6ec1c75bdd238efc3a60fdd4 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Fri, 8 Mar 2024 08:13:02 +0300 Subject: [PATCH 04/57] fix broken resize handle on the train tab --- javascript/resizeHandle.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/javascript/resizeHandle.js b/javascript/resizeHandle.js index 50251ffc1..4aeb14b41 100644 --- a/javascript/resizeHandle.js +++ b/javascript/resizeHandle.js @@ -79,6 +79,11 @@ parent.minRightColWidth = 0; parent.needHideOnMoblie = false; } + + if (!leftColTemplate) { + leftColTemplate = '1fr'; + } + const gridTemplateColumns = `${leftColTemplate} ${PAD}px ${parent.children[1].style.flexGrow}fr`; parent.style.gridTemplateColumns = gridTemplateColumns; parent.style.originalGridTemplateColumns = gridTemplateColumns; From a551a43164b8baf1b5652a9ee73081cca54c612b Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Fri, 8 Mar 2024 09:52:25 +0300 Subject: [PATCH 05/57] add an option to have old-style directory view instead of tree view --- html/extra-networks-pane-dirs.html | 8 +++ html/extra-networks-pane-tree.html | 8 +++ html/extra-networks-pane.html | 13 +---- javascript/extraNetworks.js | 34 +++++------- modules/shared_options.py | 3 +- modules/ui_extra_networks.py | 88 +++++++++++++++++++++--------- style.css | 14 ++++- 7 files changed, 111 insertions(+), 57 deletions(-) create mode 100644 html/extra-networks-pane-dirs.html create mode 100644 html/extra-networks-pane-tree.html diff --git a/html/extra-networks-pane-dirs.html b/html/extra-networks-pane-dirs.html new file mode 100644 index 000000000..5ce04289a --- /dev/null +++ b/html/extra-networks-pane-dirs.html @@ -0,0 +1,8 @@ +
    +
    + {dirs_html} +
    +
    + {items_html} +
    +
    diff --git a/html/extra-networks-pane-tree.html b/html/extra-networks-pane-tree.html new file mode 100644 index 000000000..88561fcdc --- /dev/null +++ b/html/extra-networks-pane-tree.html @@ -0,0 +1,8 @@ +
    +
    + {tree_html} +
    +
    + {items_html} +
    +
    \ No newline at end of file diff --git a/html/extra-networks-pane.html b/html/extra-networks-pane.html index 02a871086..ff8a73ad2 100644 --- a/html/extra-networks-pane.html +++ b/html/extra-networks-pane.html @@ -1,4 +1,4 @@ -
    +
    -
    -
    - {tree_html} -
    -
    - {items_html} -
    -
    -
    \ No newline at end of file + {pane_content} +
    diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 584fd6c75..6adf9ec0d 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -272,6 +272,15 @@ function saveCardPreview(event, tabname, filename) { event.preventDefault(); } +function extraNetworksSearchButton(tabname, extra_networks_tabname, event) { + var searchTextarea = gradioApp().querySelector("#" + tabname + "_" + extra_networks_tabname + "_extra_search"); + var button = event.target; + var text = button.classList.contains("search-all") ? "" : button.textContent.trim(); + + searchTextarea.value = text; + updateInput(searchTextarea); +} + function extraNetworksTreeProcessFileClick(event, btn, tabname, extra_networks_tabname) { /** * Processes `onclick` events when user clicks on files in tree. @@ -447,27 +456,12 @@ function extraNetworksControlTreeViewOnClick(event, tabname, extra_networks_tabn * @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. */ - const tree = gradioApp().getElementById(tabname + "_" + extra_networks_tabname + "_tree"); - const parent = tree.parentElement; - let resizeHandle = parent.querySelector('.resize-handle'); - tree.classList.toggle("hidden"); + var button = event.currentTarget; + button.classList.toggle("extra-network-control--enabled"); + var show = ! button.classList.contains("extra-network-control--enabled"); - if (tree.classList.contains("hidden")) { - tree.style.display = 'none'; - parent.style.display = 'flex'; - if (resizeHandle) { - resizeHandle.style.display = 'none'; - } - } else { - tree.style.display = 'block'; - parent.style.display = 'grid'; - if (!resizeHandle) { - setupResizeHandle(parent); - resizeHandle = parent.querySelector('.resize-handle'); - } - resizeHandle.style.display = 'block'; - } - event.currentTarget.classList.toggle("extra-network-control--enabled"); + var pane = gradioApp().getElementById(tabname + "_" + extra_networks_tabname + "_pane"); + pane.classList.toggle("extra-network-dirs-hidden", show); } function extraNetworksControlRefreshOnClick(event, tabname, extra_networks_tabname) { diff --git a/modules/shared_options.py b/modules/shared_options.py index 536766dbe..21643afe0 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -258,7 +258,8 @@ options_templates.update(options_section(('extra_networks', "Extra Networks", "s "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": 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_tree_view_style": OptionInfo("Dirs", "Extra Networks directory view style", gr.Radio, {"choices": ["Tree", "Dirs"]}).needs_reload_ui(), + "extra_networks_tree_view_default_enabled": OptionInfo(True, "Show the Extra Networks directory view by default").needs_reload_ui(), "extra_networks_tree_view_default_width": OptionInfo(180, "Default width for the Extra Networks directory tree view", gr.Number).needs_reload_ui(), "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(), diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 2cf91d36b..9a1cf913f 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -164,6 +164,8 @@ class ExtraNetworksPage: self.lister = util.MassFileLister() # HTML Templates self.pane_tpl = shared.html("extra-networks-pane.html") + self.pane_content_tree_tpl = shared.html("extra-networks-pane-tree.html") + self.pane_content_dirs_tpl = shared.html("extra-networks-pane-dirs.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") @@ -476,6 +478,47 @@ class ExtraNetworksPage: return f"" + def create_dirs_view_html(self, tabname: str) -> str: + """Generates HTML for displaying folders.""" + + subdirs = {} + for parentdir in [os.path.abspath(x) for x in self.allowed_directories_for_previews()]: + 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):] + + if shared.opts.extra_networks_dir_button_function: + if not subdir.startswith(os.path.sep): + subdir = os.path.sep + subdir + else: + while subdir.startswith(os.path.sep): + subdir = subdir[1:] + + is_empty = len(os.listdir(x)) == 0 + if not is_empty and not subdir.endswith(os.path.sep): + subdir = subdir + os.path.sep + + if (os.path.sep + "." 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""" + + """ for subdir in subdirs]) + + return subdirs_html + def create_card_view_html(self, tabname: str, *, none_message) -> str: """Generates HTML for the network Card View section for a tab. @@ -529,32 +572,27 @@ class ExtraNetworksPage: 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" - tree_view_div_default_display = "none" - extra_network_pane_content_default_display = "flex" - if shared.opts.extra_networks_tree_view_default_enabled: - tree_view_btn_extra_class = "extra-network-control--enabled" - tree_view_div_extra_class = "" - tree_view_div_default_display = "block" - extra_network_pane_content_default_display = "grid" - 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), - "extra_networks_tree_view_default_width": shared.opts.extra_networks_tree_view_default_width, - "tree_view_div_default_display": tree_view_div_default_display, - "extra_network_pane_content_default_display": extra_network_pane_content_default_display, - } - ) + show_tree = shared.opts.extra_networks_tree_view_default_enabled + + page_params = { + "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": "extra-network-control--enabled" if show_tree else "", + "items_html": self.create_card_view_html(tabname, none_message="Loading..." if empty else None), + "extra_networks_tree_view_default_width": shared.opts.extra_networks_tree_view_default_width, + "tree_view_div_default_display_class": "" if show_tree else "extra-network-dirs-hidden", + } + + if shared.opts.extra_networks_tree_view_style == "Tree": + pane_content = self.pane_content_tree_tpl.format(**page_params, tree_html=self.create_tree_view_html(tabname)) + else: + pane_content = self.pane_content_dirs_tpl.format(**page_params, dirs_html=self.create_dirs_view_html(tabname)) + + return self.pane_tpl.format(**page_params, pane_content=pane_content) def create_item(self, name, index=None): raise NotImplementedError() diff --git a/style.css b/style.css index 004038f89..49978a771 100644 --- a/style.css +++ b/style.css @@ -1205,12 +1205,24 @@ body.resizing .resize-handle { overflow: hidden; } -.extra-network-pane .extra-network-pane-content { +.extra-network-pane .extra-network-pane-content-dirs { + display: flex; + flex: 1; + flex-direction: column; + overflow: hidden; +} + +.extra-network-pane .extra-network-pane-content-tree { display: flex; flex: 1; overflow: hidden; } +.extra-network-dirs-hidden .extra-network-dirs{ display: none; } +.extra-network-dirs-hidden .extra-network-tree{ display: none; } +.extra-network-dirs-hidden .resize-handle { display: none; } +.extra-network-dirs-hidden .resize-handle-row { display: flex !important; } + .extra-network-pane .extra-network-tree { flex: 1; font-size: 1rem; From 01f531e9b162be94601b9f3a451cef9a9dd7dd53 Mon Sep 17 00:00:00 2001 From: SunChaser Date: Fri, 8 Mar 2024 17:25:28 +0800 Subject: [PATCH 06/57] fix: fix syntax errors --- modules/upscaler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/upscaler.py b/modules/upscaler.py index 3aee69db8..cc662fc9c 100644 --- a/modules/upscaler.py +++ b/modules/upscaler.py @@ -20,7 +20,7 @@ class Upscaler: filter = None model = None user_path = None - scalers: [] + scalers = [] tile = True def __init__(self, create_dirs=False): From 3bd75adb1c5a704fcce60a44138cb42c0301d699 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Fri, 8 Mar 2024 16:54:39 +0300 Subject: [PATCH 07/57] optimization for extra networks filtering --- javascript/extraNetworks.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 6adf9ec0d..ec6da69d8 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -106,7 +106,9 @@ function setupExtraNetworksForTab(tabname) { }); }; - search.addEventListener("input", applyFilter); + search.addEventListener("input", function() { + applyFilter(); + }); applySort(); applyFilter(); extraNetworksApplySort[tabname_full] = applySort; @@ -458,7 +460,7 @@ function extraNetworksControlTreeViewOnClick(event, tabname, extra_networks_tabn */ var button = event.currentTarget; button.classList.toggle("extra-network-control--enabled"); - var show = ! button.classList.contains("extra-network-control--enabled"); + var show = !button.classList.contains("extra-network-control--enabled"); var pane = gradioApp().getElementById(tabname + "_" + extra_networks_tabname + "_pane"); pane.classList.toggle("extra-network-dirs-hidden", show); From 530fea2bc4a2ab3412c76521961dd256b005a38b Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Fri, 8 Mar 2024 17:09:11 +0300 Subject: [PATCH 08/57] optimization for extra networks sorting --- extensions-builtin/Lora/network.py | 3 ++- javascript/extraNetworks.js | 17 +++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/extensions-builtin/Lora/network.py b/extensions-builtin/Lora/network.py index 2268b0f7e..b1426c6f7 100644 --- a/extensions-builtin/Lora/network.py +++ b/extensions-builtin/Lora/network.py @@ -37,7 +37,8 @@ class NetworkOnDisk: try: self.metadata = cache.cached_data_for_file('safetensors-metadata', "lora/" + self.name, filename, read_metadata) except Exception as e: - errors.display(e, f"reading lora {filename}") + #errors.display(e, f"reading lora {filename}") + pass if self.metadata: m = {} diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index ec6da69d8..b557d20d7 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -71,7 +71,8 @@ function setupExtraNetworksForTab(tabname) { }; var applySort = function(force) { - var cards = gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card'); + var cards = gradioApp().querySelectorAll('#' + tabname_full + ' div.card'); + var parent = gradioApp().querySelector('#' + tabname_full + "_cards" ); var reverse = sort_dir.dataset.sortdir == "Descending"; var sortKey = sort_mode.dataset.sortmode.toLowerCase().replace("sort", "").replaceAll(" ", "_").replace(/_+$/, "").trim() || "name"; sortKey = "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1); @@ -82,9 +83,6 @@ function setupExtraNetworksForTab(tabname) { } sort_mode.dataset.sortkey = sortKeyStore; - cards.forEach(function(card) { - card.originalParentElement = card.parentElement; - }); var sortedCards = Array.from(cards); sortedCards.sort(function(cardA, cardB) { var a = cardA.dataset[sortKey]; @@ -95,15 +93,18 @@ function setupExtraNetworksForTab(tabname) { return (a < b ? -1 : (a > b ? 1 : 0)); }); + if (reverse) { sortedCards.reverse(); } - cards.forEach(function(card) { - card.remove(); - }); + + parent.innerHTML = ''; + + var frag = document.createDocumentFragment(); sortedCards.forEach(function(card) { - card.originalParentElement.appendChild(card); + frag.appendChild(card); }); + parent.appendChild(frag); }; search.addEventListener("input", function() { From 758e8d7b4157c73065a20ff23194f61a9fd0d3cb Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Fri, 8 Mar 2024 17:11:42 +0300 Subject: [PATCH 09/57] undo unwanted change for extra networks --- extensions-builtin/Lora/network.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions-builtin/Lora/network.py b/extensions-builtin/Lora/network.py index b1426c6f7..2268b0f7e 100644 --- a/extensions-builtin/Lora/network.py +++ b/extensions-builtin/Lora/network.py @@ -37,8 +37,7 @@ class NetworkOnDisk: try: self.metadata = cache.cached_data_for_file('safetensors-metadata', "lora/" + self.name, filename, read_metadata) except Exception as e: - #errors.display(e, f"reading lora {filename}") - pass + errors.display(e, f"reading lora {filename}") if self.metadata: m = {} From 7d1368c51ca39c23f65a3a7431211cd0235bddbe Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Fri, 8 Mar 2024 17:11:56 +0300 Subject: [PATCH 10/57] lint --- javascript/extraNetworks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index b557d20d7..4d891b245 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -72,7 +72,7 @@ function setupExtraNetworksForTab(tabname) { var applySort = function(force) { var cards = gradioApp().querySelectorAll('#' + tabname_full + ' div.card'); - var parent = gradioApp().querySelector('#' + tabname_full + "_cards" ); + var parent = gradioApp().querySelector('#' + tabname_full + "_cards"); var reverse = sort_dir.dataset.sortdir == "Descending"; var sortKey = sort_mode.dataset.sortmode.toLowerCase().replace("sort", "").replaceAll(" ", "_").replace(/_+$/, "").trim() || "name"; sortKey = "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1); From 02a4ceabddeab10be9d5a3afbb4ef66fee81fe4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E9=88=9E?= Date: Sat, 9 Mar 2024 02:07:42 +0800 Subject: [PATCH 11/57] chore: fix font not loaded fix #15182 --- style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style.css b/style.css index 49978a771..fe74ec41f 100644 --- a/style.css +++ b/style.css @@ -1,6 +1,6 @@ /* temporary fix to load default gradio font in frontend instead of backend */ -@import url('webui-assets/css/sourcesanspro.css'); +@import url('/webui-assets/css/sourcesanspro.css'); /* temporary fix to hide gradio crop tool until it's fixed https://github.com/gradio-app/gradio/issues/3810 */ From c50b7e4eff88f52b22cd6881b47f3abf3bff00de Mon Sep 17 00:00:00 2001 From: 10sa Date: Sat, 9 Mar 2024 11:33:45 +0900 Subject: [PATCH 12/57] Add '--no-prompt-history' cmd args for disable last generation prompt history --- modules/cmd_args.py | 1 + modules/infotext_utils.py | 2 +- modules/processing.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/cmd_args.py b/modules/cmd_args.py index bf3553031..016a33d10 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -124,3 +124,4 @@ parser.add_argument("--disable-extra-extensions", action='store_true', help="pre parser.add_argument("--skip-load-model-at-start", action='store_true', help="if load a model at web start, only take effect when --nowebui") parser.add_argument("--unix-filenames-sanitization", action='store_true', help="allow any symbols except '/' in filenames. May conflict with your browser and file system") parser.add_argument("--filenames-max-length", type=int, default=128, help='maximal length of filenames of saved images. If you override it, it can conflict with your file system') +parser.add_argument("--no-prompt-history", action='store_true', help="disable read prompt from last generation feature; settings this argument will not create '--data_path/params.txt' file") diff --git a/modules/infotext_utils.py b/modules/infotext_utils.py index db1866449..a1cbfb17d 100644 --- a/modules/infotext_utils.py +++ b/modules/infotext_utils.py @@ -462,7 +462,7 @@ def get_override_settings(params, *, skip_fields=None): def connect_paste(button, paste_fields, input_comp, override_settings_component, tabname): def paste_func(prompt): - if not prompt and not shared.cmd_opts.hide_ui_dir_config: + if not prompt and not shared.cmd_opts.hide_ui_dir_config and not shared.cmd_opts.no_prompt_history: filename = os.path.join(data_path, "params.txt") try: with open(filename, "r", encoding="utf8") as file: diff --git a/modules/processing.py b/modules/processing.py index 93493f80e..86194b057 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -904,7 +904,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: # infotext could be modified by that callback # Example: a wildcard processed by process_batch sets an extra model # strength, which is saved as "Model Strength: 1.0" in the infotext - if n == 0: + if n == 0 and not cmd_opts.no_prompt_history: with open(os.path.join(paths.data_path, "params.txt"), "w", encoding="utf8") as file: processed = Processed(p, []) file.write(processed.infotext(p, 0)) From 5251733c0d6939c8d5ba71168e124634872c8dcd Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 9 Mar 2024 07:24:25 +0300 Subject: [PATCH 13/57] use natural sort in extra networks when ordering by path --- javascript/extraNetworks.js | 12 ++++-------- style.css | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 4d891b245..a816f4981 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -406,25 +406,21 @@ function extraNetworksControlSortOnClick(event, tabname, extra_networks_tabname) * @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") { + + if (curr_mode == "default") { 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.dataset.sortmode = "default"; event.currentTarget.setAttribute("title", "Sort by path"); } + applyExtraNetworkSort(tabname + "_" + extra_networks_tabname); } diff --git a/style.css b/style.css index fe74ec41f..c2637ec89 100644 --- a/style.css +++ b/style.css @@ -1468,7 +1468,7 @@ body.resizing .resize-handle { background-color: var(--input-placeholder-color); } -.extra-network-control .extra-network-control--sort[data-sortmode="path"] .extra-network-control--sort-icon { +.extra-network-control .extra-network-control--sort[data-sortmode="default"] .extra-network-control--sort-icon { mask-image: url('data:image/svg+xml,'); } From 18d801a13d71b9a9e66722dead8b2e4a7a5612a9 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 9 Mar 2024 08:25:01 +0300 Subject: [PATCH 14/57] stylistic changes for extra network sorting/search controls --- html/extra-networks-pane.html | 51 ++++++++++++++++++++++++++++------ javascript/extraNetworks.js | 52 +++++++++++++---------------------- modules/ui_extra_networks.py | 12 ++++---- style.css | 29 +++++++++++++++---- 4 files changed, 89 insertions(+), 55 deletions(-) diff --git a/html/extra-networks-pane.html b/html/extra-networks-pane.html index ff8a73ad2..9a67baea9 100644 --- a/html/extra-networks-pane.html +++ b/html/extra-networks-pane.html @@ -5,19 +5,49 @@ id="{tabname}_{extra_networks_tabname}_extra_search" class="extra-network-control--search-text" type="search" - placeholder="Filter files" + placeholder="Search" > + + Sort:
    - +
    +
    + +
    +
    + +
    +
    + +
    + +
    - +
    + + +
    - +
    - +
    {pane_content} diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index a816f4981..8c390ab89 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -39,12 +39,12 @@ function setupExtraNetworksForTab(tabname) { // 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"); + var currentSort = ''; // 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) { + if (!search || !sort_dir || !refresh) { return; // `return` is equivalent of `continue` but for forEach loops. } @@ -74,19 +74,20 @@ function setupExtraNetworksForTab(tabname) { var cards = gradioApp().querySelectorAll('#' + tabname_full + ' div.card'); var parent = gradioApp().querySelector('#' + tabname_full + "_cards"); var reverse = sort_dir.dataset.sortdir == "Descending"; - var sortKey = sort_mode.dataset.sortmode.toLowerCase().replace("sort", "").replaceAll(" ", "_").replace(/_+$/, "").trim() || "name"; - sortKey = "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1); - var sortKeyStore = sortKey + "-" + (reverse ? "Descending" : "Ascending") + "-" + cards.length; + var activeSearchElem = gradioApp().querySelector('#' + tabname_full + "_controls .extra-network-control--sort.extra-network-control--enabled"); + var sortKey = activeSearchElem ? activeSearchElem.dataset.sortkey : "default"; + var sortKeyDataField = "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1); + var sortKeyStore = sortKey + "-" + sort_dir.dataset.sortdir + "-" + cards.length; - if (sortKeyStore == sort_mode.dataset.sortkey && !force) { + if (sortKeyStore == currentSort && !force) { return; } - sort_mode.dataset.sortkey = sortKeyStore; + currentSort = sortKeyStore; var sortedCards = Array.from(cards); sortedCards.sort(function(cardA, cardB) { - var a = cardA.dataset[sortKey]; - var b = cardB.dataset[sortKey]; + var a = cardA.dataset[sortKeyDataField]; + var b = cardB.dataset[sortKeyDataField]; if (!isNaN(a) && !isNaN(b)) { return parseInt(a) - parseInt(b); } @@ -395,31 +396,16 @@ function extraNetworksTreeOnClick(event, 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; + /** Handles `onclick` events for Sort Mode buttons. */ - if (curr_mode == "default") { - event.currentTarget.dataset.sortmode = "name"; - event.currentTarget.setAttribute("title", "Sort by filename"); - } else if (curr_mode == "name") { - event.currentTarget.dataset.sortmode = "date_created"; - event.currentTarget.setAttribute("title", "Sort by date created"); - } else if (curr_mode == "date_created") { - event.currentTarget.dataset.sortmode = "date_modified"; - event.currentTarget.setAttribute("title", "Sort by date modified"); - } else { - event.currentTarget.dataset.sortmode = "default"; - event.currentTarget.setAttribute("title", "Sort by path"); - } + var self = event.currentTarget; + var parent = event.currentTarget.parentElement; + + parent.querySelectorAll('.extra-network-control--sort').forEach(function(x){ + x.classList.remove('extra-network-control--enabled'); + }); + + self.classList.add('extra-network-control--enabled'); applyExtraNetworkSort(tabname + "_" + extra_networks_tabname); } diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 9a1cf913f..f4627ce8d 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -569,18 +569,16 @@ class ExtraNetworksPage: 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)}" - show_tree = shared.opts.extra_networks_tree_view_default_enabled page_params = { "tabname": tabname, "extra_networks_tabname": self.extra_networks_tabname, - "data_sortmode": data_sortmode, - "data_sortkey": data_sortkey, - "data_sortdir": data_sortdir, + "data_sortdir": shared.opts.extra_networks_card_order, + "sort_path_active": ' extra-network-control--enabled' if shared.opts.extra_networks_card_order_field == 'Path' else '', + "sort_name_active": ' extra-network-control--enabled' if shared.opts.extra_networks_card_order_field == 'Name' else '', + "sort_date_created_active": ' extra-network-control--enabled' if shared.opts.extra_networks_card_order_field == 'Date Created' else '', + "sort_date_modified_active": ' extra-network-control--enabled' if shared.opts.extra_networks_card_order_field == 'Date Modified' else '', "tree_view_btn_extra_class": "extra-network-control--enabled" if show_tree else "", "items_html": self.create_card_view_html(tabname, none_message="Loading..." if empty else None), "extra_networks_tree_view_default_width": shared.opts.extra_networks_tree_view_default_width, diff --git a/style.css b/style.css index c2637ec89..29eae4127 100644 --- a/style.css +++ b/style.css @@ -1272,7 +1272,7 @@ body.resizing .resize-handle { .extra-network-control { position: relative; - display: grid; + display: flex; width: 100%; padding: 0 !important; margin-top: 0 !important; @@ -1289,6 +1289,12 @@ body.resizing .resize-handle { align-items: start; } +.extra-network-control small{ + color: var(--input-placeholder-color); + line-height: 2.2rem; + margin: 0 0.5rem 0 0.75rem; +} + .extra-network-tree .tree-list--tree {} /* Remove auto indentation from tree. Will be overridden later. */ @@ -1436,6 +1442,12 @@ body.resizing .resize-handle { line-height: 1rem; } + +.extra-network-control .extra-network-control--search .extra-network-control--search-text::placeholder { + color: var(--input-placeholder-color); +} + + /* 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; @@ -1468,19 +1480,19 @@ body.resizing .resize-handle { background-color: var(--input-placeholder-color); } -.extra-network-control .extra-network-control--sort[data-sortmode="default"] .extra-network-control--sort-icon { +.extra-network-control .extra-network-control--sort[data-sortkey="default"] .extra-network-control--sort-icon { mask-image: url('data:image/svg+xml,'); } -.extra-network-control .extra-network-control--sort[data-sortmode="name"] .extra-network-control--sort-icon { +.extra-network-control .extra-network-control--sort[data-sortkey="name"] .extra-network-control--sort-icon { mask-image: url('data:image/svg+xml,'); } -.extra-network-control .extra-network-control--sort[data-sortmode="date_created"] .extra-network-control--sort-icon { +.extra-network-control .extra-network-control--sort[data-sortkey="date_created"] .extra-network-control--sort-icon { mask-image: url('data:image/svg+xml,'); } -.extra-network-control .extra-network-control--sort[data-sortmode="date_modified"] .extra-network-control--sort-icon { +.extra-network-control .extra-network-control--sort[data-sortkey="date_modified"] .extra-network-control--sort-icon { mask-image: url('data:image/svg+xml,'); } @@ -1530,13 +1542,18 @@ body.resizing .resize-handle { } .extra-network-control .extra-network-control--enabled { - background-color: rgba(0, 0, 0, 0.15); + background-color: rgba(0, 0, 0, 0.1); + border-radius: 0.25rem; } .dark .extra-network-control .extra-network-control--enabled { background-color: rgba(255, 255, 255, 0.15); } +.extra-network-control .extra-network-control--enabled .extra-network-control--icon{ + background-color: var(--button-secondary-text-color); +} + /* ==== REFRESH ICON ACTIONS ==== */ .extra-network-control .extra-network-control--refresh { padding: 0.25rem; From 0dc179ee7256690db9e63864bd330f235911e5d1 Mon Sep 17 00:00:00 2001 From: Kohaku-Blueleaf <59680068+KohakuBlueleaf@users.noreply.github.com> Date: Sat, 9 Mar 2024 17:12:54 +0800 Subject: [PATCH 15/57] Avoid error from None --- modules/sd_models_xl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/sd_models_xl.py b/modules/sd_models_xl.py index 0de17af3d..94ff973fb 100644 --- a/modules/sd_models_xl.py +++ b/modules/sd_models_xl.py @@ -13,8 +13,8 @@ def get_learned_conditioning(self: sgm.models.diffusion.DiffusionEngine, batch: for embedder in self.conditioner.embedders: embedder.ucg_rate = 0.0 - width = getattr(batch, 'width', 1024) - height = getattr(batch, 'height', 1024) + width = getattr(batch, 'width', 1024) or 1024 + height = getattr(batch, 'height', 1024) or 1024 is_negative_prompt = getattr(batch, 'is_negative_prompt', False) aesthetic_score = shared.opts.sdxl_refiner_low_aesthetic_score if is_negative_prompt else shared.opts.sdxl_refiner_high_aesthetic_score From 6136db1409b1d9d4a01358a558602fec40562488 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 9 Mar 2024 12:21:46 +0300 Subject: [PATCH 16/57] linter --- javascript/extraNetworks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 8c390ab89..c0c53cc35 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -401,7 +401,7 @@ function extraNetworksControlSortOnClick(event, tabname, extra_networks_tabname) var self = event.currentTarget; var parent = event.currentTarget.parentElement; - parent.querySelectorAll('.extra-network-control--sort').forEach(function(x){ + parent.querySelectorAll('.extra-network-control--sort').forEach(function(x) { x.classList.remove('extra-network-control--enabled'); }); From 0085e719a91a480ad1297423f63554be23f2418f Mon Sep 17 00:00:00 2001 From: Alexandre Macabies Date: Sat, 9 Mar 2024 21:53:38 +0100 Subject: [PATCH 17/57] Add model description to searched terms. This adds the model description to the searchable terms. This is particularly useful since the description can be used to store arbitrary tags, independently from the filename, which is imposed by the model publisher. --- javascript/extraNetworks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index c0c53cc35..be5f0f304 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -52,7 +52,7 @@ function setupExtraNetworksForTab(tabname) { var searchTerm = search.value.toLowerCase(); gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card').forEach(function(elem) { var searchOnly = elem.querySelector('.search_only'); - var text = Array.prototype.map.call(elem.querySelectorAll('.search_terms'), function(t) { + var text = Array.prototype.map.call(elem.querySelectorAll('.search_terms, .description'), function(t) { return t.textContent.toLowerCase(); }).join(" "); From fb62f1fb4090b900eba07c27cf7d394a770e7efd Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sun, 10 Mar 2024 06:07:16 +0900 Subject: [PATCH 18/57] add entry to MassFileLister after writing metadata fix #15184 --- modules/ui_extra_networks_user_metadata.py | 7 +++---- modules/util.py | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/modules/ui_extra_networks_user_metadata.py b/modules/ui_extra_networks_user_metadata.py index 2ca937fd1..6bc25a4d2 100644 --- a/modules/ui_extra_networks_user_metadata.py +++ b/modules/ui_extra_networks_user_metadata.py @@ -133,8 +133,10 @@ class UserMetadataEditor: filename = item.get("filename", None) basename, ext = os.path.splitext(filename) - with open(basename + '.json', "w", encoding="utf8") as file: + metadata_path = basename + '.json' + with open(metadata_path, "w", encoding="utf8") as file: json.dump(metadata, file, indent=4, ensure_ascii=False) + self.page.lister.update_file_entry(metadata_path) def save_user_metadata(self, name, desc, notes): user_metadata = self.get_user_metadata(name) @@ -200,6 +202,3 @@ class UserMetadataEditor: inputs=[self.edit_name_input], outputs=[] ) - - - diff --git a/modules/util.py b/modules/util.py index 8d1aea44f..cb690e734 100644 --- a/modules/util.py +++ b/modules/util.py @@ -81,6 +81,17 @@ class MassFileListerCachedDir: self.files = {x[0].lower(): x for x in files} self.files_cased = {x[0]: x for x in files} + def update_entry(self, filename): + """Add a file to the cache""" + file_path = os.path.join(self.dirname, filename) + try: + stat = os.stat(file_path) + entry = (filename, stat.st_mtime, stat.st_ctime) + self.files[filename.lower()] = entry + self.files_cased[filename] = entry + except FileNotFoundError as e: + print(f'MassFileListerCachedDir.add_entry: "{file_path}" {e}') + class MassFileLister: """A class that provides a way to check for the existence and mtime/ctile of files without doing more than one stat call per file.""" @@ -136,3 +147,9 @@ class MassFileLister: def reset(self): """Clear the cache of all directories.""" self.cached_dirs.clear() + + def update_file_entry(self, path): + """Update the cache for a specific directory.""" + dirname, filename = os.path.split(path) + if cached_dir := self.cached_dirs.get(dirname): + cached_dir.update_entry(filename) From 0411eced89688f55aec356eea0c7d29377d37bc8 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 10 Mar 2024 07:52:57 +0300 Subject: [PATCH 19/57] add names to callbacks --- modules/extensions.py | 17 +++++ modules/script_callbacks.py | 126 +++++++++++++++++++++--------------- 2 files changed, 91 insertions(+), 52 deletions(-) diff --git a/modules/extensions.py b/modules/extensions.py index 04bda297e..ab835d3f2 100644 --- a/modules/extensions.py +++ b/modules/extensions.py @@ -186,6 +186,7 @@ class Extension: def list_extensions(): extensions.clear() + extension_paths.clear() if shared.cmd_opts.disable_all_extensions: print("*** \"--disable-all-extensions\" arg was used, will not load any extensions ***") @@ -220,6 +221,7 @@ def list_extensions(): is_builtin = dirname == extensions_builtin_dir extension = Extension(name=extension_dirname, path=path, enabled=extension_dirname not in shared.opts.disabled_extensions, is_builtin=is_builtin, metadata=metadata) extensions.append(extension) + extension_paths[extension.path] = extension loaded_extensions[canonical_name] = extension # check for requirements @@ -238,4 +240,19 @@ def list_extensions(): continue +def find_extension(filename): + parentdir = os.path.dirname(os.path.realpath(filename)) + + while parentdir != filename: + extension = extension_paths.get(parentdir) + if extension is not None: + return extension + + filename = parentdir + parentdir = os.path.dirname(filename) + + return None + + extensions: list[Extension] = [] +extension_paths: dict[str, Extension] = {} diff --git a/modules/script_callbacks.py b/modules/script_callbacks.py index 08bc52564..98952ec78 100644 --- a/modules/script_callbacks.py +++ b/modules/script_callbacks.py @@ -1,13 +1,14 @@ +from __future__ import annotations + import dataclasses import inspect import os -from collections import namedtuple from typing import Optional, Any from fastapi import FastAPI from gradio import Blocks -from modules import errors, timer +from modules import errors, timer, extensions def report_exception(c, job): @@ -116,7 +117,35 @@ class BeforeTokenCounterParams: is_positive: bool = True -ScriptCallback = namedtuple("ScriptCallback", ["script", "callback"]) +@dataclasses.dataclass +class ScriptCallback: + script: str + callback: '' + name: str = None + + +def add_callback(callbacks, fun, *, name=None, category='unknown'): + stack = [x for x in inspect.stack() if x.filename != __file__] + filename = stack[0].filename if stack else 'unknown file' + + extension = extensions.find_extension(filename) + extension_name = extension.canonical_name if extension else 'base' + + callback_name = f"{extension_name}/{os.path.basename(filename)}/{category}" + if name is not None: + callback_name += f'/{name}' + + unique_callback_name = callback_name + for index in range(1000): + existing = any(x.name == unique_callback_name for x in callbacks) + if not existing: + break + + unique_callback_name = f'{callback_name}-{index+1}' + + callbacks.append(ScriptCallback(filename, fun, unique_callback_name)) + + callback_map = dict( callbacks_app_started=[], callbacks_model_loaded=[], @@ -328,13 +357,6 @@ def before_token_counter_callback(params: BeforeTokenCounterParams): report_exception(c, 'before_token_counter') -def add_callback(callbacks, fun): - stack = [x for x in inspect.stack() if x.filename != __file__] - filename = stack[0].filename if stack else 'unknown file' - - callbacks.append(ScriptCallback(filename, fun)) - - def remove_current_script_callbacks(): stack = [x for x in inspect.stack() if x.filename != __file__] filename = stack[0].filename if stack else 'unknown file' @@ -351,24 +373,24 @@ def remove_callbacks_for_function(callback_func): callback_list.remove(callback_to_remove) -def on_app_started(callback): +def on_app_started(callback, *, name=None): """register a function to be called when the webui started, the gradio `Block` component and fastapi `FastAPI` object are passed as the arguments""" - add_callback(callback_map['callbacks_app_started'], callback) + add_callback(callback_map['callbacks_app_started'], callback, name=name, category='app_started') -def on_before_reload(callback): +def on_before_reload(callback, *, name=None): """register a function to be called just before the server reloads.""" - add_callback(callback_map['callbacks_on_reload'], callback) + add_callback(callback_map['callbacks_on_reload'], callback, name=name, category='on_reload') -def on_model_loaded(callback): +def on_model_loaded(callback, *, name=None): """register a function to be called when the stable diffusion model is created; the model is passed as an argument; this function is also called when the script is reloaded. """ - add_callback(callback_map['callbacks_model_loaded'], callback) + add_callback(callback_map['callbacks_model_loaded'], callback, name=name, category='model_loaded') -def on_ui_tabs(callback): +def on_ui_tabs(callback, *, name=None): """register a function to be called when the UI is creating new tabs. The function must either return a None, which means no new tabs to be added, or a list, where each element is a tuple: @@ -378,71 +400,71 @@ def on_ui_tabs(callback): title is tab text displayed to user in the UI elem_id is HTML id for the tab """ - add_callback(callback_map['callbacks_ui_tabs'], callback) + add_callback(callback_map['callbacks_ui_tabs'], callback, name=name, category='ui_tabs') -def on_ui_train_tabs(callback): +def on_ui_train_tabs(callback, *, name=None): """register a function to be called when the UI is creating new tabs for the train tab. Create your new tabs with gr.Tab. """ - add_callback(callback_map['callbacks_ui_train_tabs'], callback) + add_callback(callback_map['callbacks_ui_train_tabs'], callback, name=name, category='ui_train_tabs') -def on_ui_settings(callback): +def on_ui_settings(callback, *, name=None): """register a function to be called before UI settings are populated; add your settings by using shared.opts.add_option(shared.OptionInfo(...)) """ - add_callback(callback_map['callbacks_ui_settings'], callback) + add_callback(callback_map['callbacks_ui_settings'], callback, name=name, category='ui_settings') -def on_before_image_saved(callback): +def on_before_image_saved(callback, *, name=None): """register a function to be called before an image is saved to a file. The callback is called with one argument: - params: ImageSaveParams - parameters the image is to be saved with. You can change fields in this object. """ - add_callback(callback_map['callbacks_before_image_saved'], callback) + add_callback(callback_map['callbacks_before_image_saved'], callback, name=name, category='before_image_saved') -def on_image_saved(callback): +def on_image_saved(callback, *, name=None): """register a function to be called after an image is saved to a file. The callback is called with one argument: - params: ImageSaveParams - parameters the image was saved with. Changing fields in this object does nothing. """ - add_callback(callback_map['callbacks_image_saved'], callback) + add_callback(callback_map['callbacks_image_saved'], callback, name=name, category='image_saved') -def on_extra_noise(callback): +def on_extra_noise(callback, *, name=None): """register a function to be called before adding extra noise in img2img or hires fix; The callback is called with one argument: - params: ExtraNoiseParams - contains noise determined by seed and latent representation of image """ - add_callback(callback_map['callbacks_extra_noise'], callback) + add_callback(callback_map['callbacks_extra_noise'], callback, name=name, category='extra_noise') -def on_cfg_denoiser(callback): +def on_cfg_denoiser(callback, *, name=None): """register a function to be called in the kdiffussion cfg_denoiser method after building the inner model inputs. The callback is called with one argument: - params: CFGDenoiserParams - parameters to be passed to the inner model and sampling state details. """ - add_callback(callback_map['callbacks_cfg_denoiser'], callback) + add_callback(callback_map['callbacks_cfg_denoiser'], callback, name=name, category='cfg_denoiser') -def on_cfg_denoised(callback): +def on_cfg_denoised(callback, *, name=None): """register a function to be called in the kdiffussion cfg_denoiser method after building the inner model inputs. The callback is called with one argument: - params: CFGDenoisedParams - parameters to be passed to the inner model and sampling state details. """ - add_callback(callback_map['callbacks_cfg_denoised'], callback) + add_callback(callback_map['callbacks_cfg_denoised'], callback, name=name, category='cfg_denoised') -def on_cfg_after_cfg(callback): +def on_cfg_after_cfg(callback, *, name=None): """register a function to be called in the kdiffussion cfg_denoiser method after cfg calculations are completed. The callback is called with one argument: - params: AfterCFGCallbackParams - parameters to be passed to the script for post-processing after cfg calculation. """ - add_callback(callback_map['callbacks_cfg_after_cfg'], callback) + add_callback(callback_map['callbacks_cfg_after_cfg'], callback, name=name, category='cfg_after_cfg') -def on_before_component(callback): +def on_before_component(callback, *, name=None): """register a function to be called before a component is created. The callback is called with arguments: - component - gradio component that is about to be created. @@ -451,61 +473,61 @@ def on_before_component(callback): Use elem_id/label fields of kwargs to figure out which component it is. This can be useful to inject your own components somewhere in the middle of vanilla UI. """ - add_callback(callback_map['callbacks_before_component'], callback) + add_callback(callback_map['callbacks_before_component'], callback, name=name, category='before_component') -def on_after_component(callback): +def on_after_component(callback, *, name=None): """register a function to be called after a component is created. See on_before_component for more.""" - add_callback(callback_map['callbacks_after_component'], callback) + add_callback(callback_map['callbacks_after_component'], callback, name=name, category='after_component') -def on_image_grid(callback): +def on_image_grid(callback, *, name=None): """register a function to be called before making an image grid. The callback is called with one argument: - params: ImageGridLoopParams - parameters to be used for grid creation. Can be modified. """ - add_callback(callback_map['callbacks_image_grid'], callback) + add_callback(callback_map['callbacks_image_grid'], callback, name=name, category='image_grid') -def on_infotext_pasted(callback): +def on_infotext_pasted(callback, *, name=None): """register a function to be called before applying an infotext. The callback is called with two arguments: - infotext: str - raw infotext. - result: dict[str, any] - parsed infotext parameters. """ - add_callback(callback_map['callbacks_infotext_pasted'], callback) + add_callback(callback_map['callbacks_infotext_pasted'], callback, name=name, category='infotext_pasted') -def on_script_unloaded(callback): +def on_script_unloaded(callback, *, name=None): """register a function to be called before the script is unloaded. Any hooks/hijacks/monkeying about that the script did should be reverted here""" - add_callback(callback_map['callbacks_script_unloaded'], callback) + add_callback(callback_map['callbacks_script_unloaded'], callback, name=name, category='script_unloaded') -def on_before_ui(callback): +def on_before_ui(callback, *, name=None): """register a function to be called before the UI is created.""" - add_callback(callback_map['callbacks_before_ui'], callback) + add_callback(callback_map['callbacks_before_ui'], callback, name=name, category='before_ui') -def on_list_optimizers(callback): +def on_list_optimizers(callback, *, name=None): """register a function to be called when UI is making a list of cross attention optimization options. The function will be called with one argument, a list, and shall add objects of type modules.sd_hijack_optimizations.SdOptimization to it.""" - add_callback(callback_map['callbacks_list_optimizers'], callback) + add_callback(callback_map['callbacks_list_optimizers'], callback, name=name, category='list_optimizers') -def on_list_unets(callback): +def on_list_unets(callback, *, name=None): """register a function to be called when UI is making a list of alternative options for unet. 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, name=name, category='list_unets') -def on_before_token_counter(callback): +def on_before_token_counter(callback, *, name=None): """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) + add_callback(callback_map['callbacks_before_token_counter'], callback, name=name, category='before_token_counter') From 9b842e9ec745000248d00c3ebef2d599e8f033fa Mon Sep 17 00:00:00 2001 From: SunChaser Date: Sun, 10 Mar 2024 16:19:59 +0800 Subject: [PATCH 20/57] fix: resolve type annotation warnings --- modules/upscaler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/upscaler.py b/modules/upscaler.py index cc662fc9c..9d13ee993 100644 --- a/modules/upscaler.py +++ b/modules/upscaler.py @@ -20,7 +20,7 @@ class Upscaler: filter = None model = None user_path = None - scalers = [] + scalers: list = [] tile = True def __init__(self, create_dirs=False): From 9fd0cd6a805ee0a0a0651de41d4c6154407a045f Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sun, 10 Mar 2024 18:24:52 +0900 Subject: [PATCH 21/57] update preview on Replace Preview --- modules/ui_extra_networks_user_metadata.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ui_extra_networks_user_metadata.py b/modules/ui_extra_networks_user_metadata.py index 6bc25a4d2..fde093700 100644 --- a/modules/ui_extra_networks_user_metadata.py +++ b/modules/ui_extra_networks_user_metadata.py @@ -187,7 +187,8 @@ class UserMetadataEditor: geninfo, items = images.read_info_from_image(image) images.save_image_with_geninfo(image, geninfo, item["local_preview"]) - + self.page.lister.update_file_entry(item["local_preview"]) + item['preview'] = self.page.find_preview(item["local_preview"]) return self.get_card_html(name), '' def setup_ui(self, gallery): From 7e5e67330b092a65a8dd9e04a18969c458cb05d1 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 10 Mar 2024 14:07:51 +0300 Subject: [PATCH 22/57] add UI for reordering callbacks --- modules/script_callbacks.py | 92 +++++++++++++++++++++++++++---------- modules/scripts.py | 90 ++++++++++++++++++++++++++++-------- modules/shared_items.py | 42 +++++++++++++++++ modules/ui_settings.py | 8 +++- style.css | 4 ++ 5 files changed, 192 insertions(+), 44 deletions(-) diff --git a/modules/script_callbacks.py b/modules/script_callbacks.py index 98952ec78..a0ecf5a53 100644 --- a/modules/script_callbacks.py +++ b/modules/script_callbacks.py @@ -8,7 +8,7 @@ from typing import Optional, Any from fastapi import FastAPI from gradio import Blocks -from modules import errors, timer, extensions +from modules import errors, timer, extensions, shared def report_exception(c, job): @@ -124,9 +124,10 @@ class ScriptCallback: name: str = None -def add_callback(callbacks, fun, *, name=None, category='unknown'): - stack = [x for x in inspect.stack() if x.filename != __file__] - filename = stack[0].filename if stack else 'unknown file' +def add_callback(callbacks, fun, *, name=None, category='unknown', filename=None): + if filename is None: + stack = [x for x in inspect.stack() if x.filename != __file__] + filename = stack[0].filename if stack else 'unknown file' extension = extensions.find_extension(filename) extension_name = extension.canonical_name if extension else 'base' @@ -146,6 +147,43 @@ def add_callback(callbacks, fun, *, name=None, category='unknown'): callbacks.append(ScriptCallback(filename, fun, unique_callback_name)) +def sort_callbacks(category, unordered_callbacks, *, enable_user_sort=True): + callbacks = unordered_callbacks.copy() + + if enable_user_sort: + for name in reversed(getattr(shared.opts, 'prioritized_callbacks_' + category, [])): + index = next((i for i, callback in enumerate(callbacks) if callback.name == name), None) + if index is not None: + callbacks.insert(0, callbacks.pop(index)) + + return callbacks + + +def ordered_callbacks(category, unordered_callbacks=None, *, enable_user_sort=True): + if unordered_callbacks is None: + unordered_callbacks = callback_map.get('callbacks_' + category, []) + + if not enable_user_sort: + return sort_callbacks(category, unordered_callbacks, enable_user_sort=False) + + callbacks = ordered_callbacks_map.get(category) + if callbacks is not None and len(callbacks) == len(unordered_callbacks): + return callbacks + + callbacks = sort_callbacks(category, unordered_callbacks) + + ordered_callbacks_map[category] = callbacks + return callbacks + + +def enumerate_callbacks(): + for category, callbacks in callback_map.items(): + if category.startswith('callbacks_'): + category = category[10:] + + yield category, callbacks + + callback_map = dict( callbacks_app_started=[], callbacks_model_loaded=[], @@ -170,14 +208,18 @@ callback_map = dict( callbacks_before_token_counter=[], ) +ordered_callbacks_map = {} + def clear_callbacks(): for callback_list in callback_map.values(): callback_list.clear() + ordered_callbacks_map.clear() + def app_started_callback(demo: Optional[Blocks], app: FastAPI): - for c in callback_map['callbacks_app_started']: + for c in ordered_callbacks('app_started'): try: c.callback(demo, app) timer.startup_timer.record(os.path.basename(c.script)) @@ -186,7 +228,7 @@ def app_started_callback(demo: Optional[Blocks], app: FastAPI): def app_reload_callback(): - for c in callback_map['callbacks_on_reload']: + for c in ordered_callbacks('on_reload'): try: c.callback() except Exception: @@ -194,7 +236,7 @@ def app_reload_callback(): def model_loaded_callback(sd_model): - for c in callback_map['callbacks_model_loaded']: + for c in ordered_callbacks('model_loaded'): try: c.callback(sd_model) except Exception: @@ -204,7 +246,7 @@ def model_loaded_callback(sd_model): def ui_tabs_callback(): res = [] - for c in callback_map['callbacks_ui_tabs']: + for c in ordered_callbacks('ui_tabs'): try: res += c.callback() or [] except Exception: @@ -214,7 +256,7 @@ def ui_tabs_callback(): def ui_train_tabs_callback(params: UiTrainTabParams): - for c in callback_map['callbacks_ui_train_tabs']: + for c in ordered_callbacks('ui_train_tabs'): try: c.callback(params) except Exception: @@ -222,7 +264,7 @@ def ui_train_tabs_callback(params: UiTrainTabParams): def ui_settings_callback(): - for c in callback_map['callbacks_ui_settings']: + for c in ordered_callbacks('ui_settings'): try: c.callback() except Exception: @@ -230,7 +272,7 @@ def ui_settings_callback(): def before_image_saved_callback(params: ImageSaveParams): - for c in callback_map['callbacks_before_image_saved']: + for c in ordered_callbacks('before_image_saved'): try: c.callback(params) except Exception: @@ -238,7 +280,7 @@ def before_image_saved_callback(params: ImageSaveParams): def image_saved_callback(params: ImageSaveParams): - for c in callback_map['callbacks_image_saved']: + for c in ordered_callbacks('image_saved'): try: c.callback(params) except Exception: @@ -246,7 +288,7 @@ def image_saved_callback(params: ImageSaveParams): def extra_noise_callback(params: ExtraNoiseParams): - for c in callback_map['callbacks_extra_noise']: + for c in ordered_callbacks('extra_noise'): try: c.callback(params) except Exception: @@ -254,7 +296,7 @@ def extra_noise_callback(params: ExtraNoiseParams): def cfg_denoiser_callback(params: CFGDenoiserParams): - for c in callback_map['callbacks_cfg_denoiser']: + for c in ordered_callbacks('cfg_denoiser'): try: c.callback(params) except Exception: @@ -262,7 +304,7 @@ def cfg_denoiser_callback(params: CFGDenoiserParams): def cfg_denoised_callback(params: CFGDenoisedParams): - for c in callback_map['callbacks_cfg_denoised']: + for c in ordered_callbacks('cfg_denoised'): try: c.callback(params) except Exception: @@ -270,7 +312,7 @@ def cfg_denoised_callback(params: CFGDenoisedParams): def cfg_after_cfg_callback(params: AfterCFGCallbackParams): - for c in callback_map['callbacks_cfg_after_cfg']: + for c in ordered_callbacks('cfg_after_cfg'): try: c.callback(params) except Exception: @@ -278,7 +320,7 @@ def cfg_after_cfg_callback(params: AfterCFGCallbackParams): def before_component_callback(component, **kwargs): - for c in callback_map['callbacks_before_component']: + for c in ordered_callbacks('before_component'): try: c.callback(component, **kwargs) except Exception: @@ -286,7 +328,7 @@ def before_component_callback(component, **kwargs): def after_component_callback(component, **kwargs): - for c in callback_map['callbacks_after_component']: + for c in ordered_callbacks('after_component'): try: c.callback(component, **kwargs) except Exception: @@ -294,7 +336,7 @@ def after_component_callback(component, **kwargs): def image_grid_callback(params: ImageGridLoopParams): - for c in callback_map['callbacks_image_grid']: + for c in ordered_callbacks('image_grid'): try: c.callback(params) except Exception: @@ -302,7 +344,7 @@ def image_grid_callback(params: ImageGridLoopParams): def infotext_pasted_callback(infotext: str, params: dict[str, Any]): - for c in callback_map['callbacks_infotext_pasted']: + for c in ordered_callbacks('infotext_pasted'): try: c.callback(infotext, params) except Exception: @@ -310,7 +352,7 @@ def infotext_pasted_callback(infotext: str, params: dict[str, Any]): def script_unloaded_callback(): - for c in reversed(callback_map['callbacks_script_unloaded']): + for c in reversed(ordered_callbacks('script_unloaded')): try: c.callback() except Exception: @@ -318,7 +360,7 @@ def script_unloaded_callback(): def before_ui_callback(): - for c in reversed(callback_map['callbacks_before_ui']): + for c in reversed(ordered_callbacks('before_ui')): try: c.callback() except Exception: @@ -328,7 +370,7 @@ def before_ui_callback(): def list_optimizers_callback(): res = [] - for c in callback_map['callbacks_list_optimizers']: + for c in ordered_callbacks('list_optimizers'): try: c.callback(res) except Exception: @@ -340,7 +382,7 @@ def list_optimizers_callback(): def list_unets_callback(): res = [] - for c in callback_map['callbacks_list_unets']: + for c in ordered_callbacks('list_unets'): try: c.callback(res) except Exception: @@ -350,7 +392,7 @@ def list_unets_callback(): def before_token_counter_callback(params: BeforeTokenCounterParams): - for c in callback_map['callbacks_before_token_counter']: + for c in ordered_callbacks('before_token_counter'): try: c.callback(params) except Exception: diff --git a/modules/scripts.py b/modules/scripts.py index 77f5e4f3e..e1a435827 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -138,7 +138,6 @@ class Script: """ pass - def before_process(self, p, *args): """ This function is called very early during processing begins for AlwaysVisible scripts. @@ -562,6 +561,25 @@ class ScriptRunner: self.paste_field_names = [] self.inputs = [None] + self.callback_map = {} + self.callback_names = [ + 'before_process', + 'process', + 'before_process_batch', + 'after_extra_networks_activate', + 'process_batch', + 'postprocess', + 'postprocess_batch', + 'postprocess_batch_list', + 'post_sample', + 'on_mask_blend', + 'postprocess_image', + 'postprocess_maskoverlay', + 'postprocess_image_after_composite', + 'before_component', + 'after_component', + ] + self.on_before_component_elem_id = {} """dict of callbacks to be called before an element is created; key=elem_id, value=list of callbacks""" @@ -600,6 +618,8 @@ class ScriptRunner: self.scripts.append(script) self.selectable_scripts.append(script) + self.callback_map.clear() + self.apply_on_before_component_callbacks() def apply_on_before_component_callbacks(self): @@ -769,8 +789,42 @@ class ScriptRunner: return processed + def list_scripts_for_method(self, method_name): + if method_name in ('before_component', 'after_component'): + return self.scripts + else: + return self.alwayson_scripts + + def create_ordered_callbacks_list(self, method_name, *, enable_user_sort=True): + script_list = self.list_scripts_for_method(method_name) + category = f'script_{method_name}' + callbacks = [] + + for script in script_list: + if getattr(script.__class__, method_name, None) == getattr(Script, method_name, None): + continue + + script_callbacks.add_callback(callbacks, script, category=category, name=script.__class__.__name__, filename=script.filename) + + return script_callbacks.sort_callbacks(category, callbacks, enable_user_sort=enable_user_sort) + + def ordered_callbacks(self, method_name, *, enable_user_sort=True): + script_list = self.list_scripts_for_method(method_name) + category = f'script_{method_name}' + + scrpts_len, callbacks = self.callback_map.get(category, (-1, None)) + + if callbacks is None or scrpts_len != len(script_list): + callbacks = self.create_ordered_callbacks_list(method_name, enable_user_sort=enable_user_sort) + self.callback_map[category] = len(script_list), callbacks + + return callbacks + + def ordered_scripts(self, method_name): + return [x.callback for x in self.ordered_callbacks(method_name)] + def before_process(self, p): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('before_process'): try: script_args = p.script_args[script.args_from:script.args_to] script.before_process(p, *script_args) @@ -778,7 +832,7 @@ class ScriptRunner: errors.report(f"Error running before_process: {script.filename}", exc_info=True) def process(self, p): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('process'): try: script_args = p.script_args[script.args_from:script.args_to] script.process(p, *script_args) @@ -786,7 +840,7 @@ class ScriptRunner: errors.report(f"Error running process: {script.filename}", exc_info=True) def before_process_batch(self, p, **kwargs): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('before_process_batch'): try: script_args = p.script_args[script.args_from:script.args_to] script.before_process_batch(p, *script_args, **kwargs) @@ -794,7 +848,7 @@ class ScriptRunner: errors.report(f"Error running before_process_batch: {script.filename}", exc_info=True) def after_extra_networks_activate(self, p, **kwargs): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('after_extra_networks_activate'): try: script_args = p.script_args[script.args_from:script.args_to] script.after_extra_networks_activate(p, *script_args, **kwargs) @@ -802,7 +856,7 @@ class ScriptRunner: errors.report(f"Error running after_extra_networks_activate: {script.filename}", exc_info=True) def process_batch(self, p, **kwargs): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('process_batch'): try: script_args = p.script_args[script.args_from:script.args_to] script.process_batch(p, *script_args, **kwargs) @@ -810,7 +864,7 @@ class ScriptRunner: errors.report(f"Error running process_batch: {script.filename}", exc_info=True) def postprocess(self, p, processed): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('postprocess'): try: script_args = p.script_args[script.args_from:script.args_to] script.postprocess(p, processed, *script_args) @@ -818,7 +872,7 @@ class ScriptRunner: errors.report(f"Error running postprocess: {script.filename}", exc_info=True) def postprocess_batch(self, p, images, **kwargs): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('postprocess_batch'): try: script_args = p.script_args[script.args_from:script.args_to] script.postprocess_batch(p, *script_args, images=images, **kwargs) @@ -826,7 +880,7 @@ class ScriptRunner: errors.report(f"Error running postprocess_batch: {script.filename}", exc_info=True) def postprocess_batch_list(self, p, pp: PostprocessBatchListArgs, **kwargs): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('postprocess_batch_list'): try: script_args = p.script_args[script.args_from:script.args_to] script.postprocess_batch_list(p, pp, *script_args, **kwargs) @@ -834,7 +888,7 @@ class ScriptRunner: errors.report(f"Error running postprocess_batch_list: {script.filename}", exc_info=True) def post_sample(self, p, ps: PostSampleArgs): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('post_sample'): try: script_args = p.script_args[script.args_from:script.args_to] script.post_sample(p, ps, *script_args) @@ -842,7 +896,7 @@ class ScriptRunner: errors.report(f"Error running post_sample: {script.filename}", exc_info=True) def on_mask_blend(self, p, mba: MaskBlendArgs): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('on_mask_blend'): try: script_args = p.script_args[script.args_from:script.args_to] script.on_mask_blend(p, mba, *script_args) @@ -850,7 +904,7 @@ class ScriptRunner: errors.report(f"Error running post_sample: {script.filename}", exc_info=True) def postprocess_image(self, p, pp: PostprocessImageArgs): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('postprocess_image'): try: script_args = p.script_args[script.args_from:script.args_to] script.postprocess_image(p, pp, *script_args) @@ -858,7 +912,7 @@ class ScriptRunner: errors.report(f"Error running postprocess_image: {script.filename}", exc_info=True) def postprocess_maskoverlay(self, p, ppmo: PostProcessMaskOverlayArgs): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('postprocess_maskoverlay'): try: script_args = p.script_args[script.args_from:script.args_to] script.postprocess_maskoverlay(p, ppmo, *script_args) @@ -866,7 +920,7 @@ class ScriptRunner: errors.report(f"Error running postprocess_image: {script.filename}", exc_info=True) def postprocess_image_after_composite(self, p, pp: PostprocessImageArgs): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('postprocess_image_after_composite'): try: script_args = p.script_args[script.args_from:script.args_to] script.postprocess_image_after_composite(p, pp, *script_args) @@ -880,7 +934,7 @@ class ScriptRunner: except Exception: errors.report(f"Error running on_before_component: {script.filename}", exc_info=True) - for script in self.scripts: + for script in self.ordered_scripts('before_component'): try: script.before_component(component, **kwargs) except Exception: @@ -893,7 +947,7 @@ class ScriptRunner: except Exception: errors.report(f"Error running on_after_component: {script.filename}", exc_info=True) - for script in self.scripts: + for script in self.ordered_scripts('after_component'): try: script.after_component(component, **kwargs) except Exception: @@ -921,7 +975,7 @@ class ScriptRunner: self.scripts[si].args_to = args_to def before_hr(self, p): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('before_hr'): try: script_args = p.script_args[script.args_from:script.args_to] script.before_hr(p, *script_args) @@ -929,7 +983,7 @@ class ScriptRunner: errors.report(f"Error running before_hr: {script.filename}", exc_info=True) def setup_scrips(self, p, *, is_ui=True): - for script in self.alwayson_scripts: + for script in self.ordered_scripts('setup'): if not is_ui and script.setup_for_ui_only: continue diff --git a/modules/shared_items.py b/modules/shared_items.py index 88f636452..11f10b3f7 100644 --- a/modules/shared_items.py +++ b/modules/shared_items.py @@ -1,5 +1,8 @@ +import html import sys +from modules import script_callbacks, scripts, ui_components +from modules.options import OptionHTML, OptionInfo from modules.shared_cmd_options import cmd_opts @@ -118,6 +121,45 @@ def ui_reorder_categories(): yield "scripts" +def callbacks_order_settings(): + options = { + "sd_vae_explanation": OptionHTML(""" + For categories below, callbacks added to dropdowns happen before others, in order listed. + """), + + } + + callback_options = {} + + for category, _ in script_callbacks.enumerate_callbacks(): + callback_options[category] = script_callbacks.ordered_callbacks(category, enable_user_sort=False) + + for method_name in scripts.scripts_txt2img.callback_names: + callback_options["script_" + method_name] = scripts.scripts_txt2img.create_ordered_callbacks_list(method_name, enable_user_sort=False) + + for method_name in scripts.scripts_img2img.callback_names: + callbacks = callback_options.get("script_" + method_name, []) + + for addition in scripts.scripts_img2img.create_ordered_callbacks_list(method_name, enable_user_sort=False): + if any(x.name == addition.name for x in callbacks): + continue + + callbacks.append(addition) + + callback_options["script_" + method_name] = callbacks + + for category, callbacks in callback_options.items(): + if not callbacks: + continue + + option_info = OptionInfo([], f"{category} callback priority", ui_components.DropdownMulti, {"choices": [x.name for x in callbacks]}) + option_info.needs_restart() + option_info.html("
    Default order:
      " + "".join(f"
    1. {html.escape(x.name)}
    2. \n" for x in callbacks) + "
    ") + options['prioritized_callbacks_' + category] = option_info + + return options + + class Shared(sys.modules[__name__].__class__): """ this class is here to provide sd_model field as a property, so that it can be created and loaded on demand rather than diff --git a/modules/ui_settings.py b/modules/ui_settings.py index d17ef1d95..087b91f3b 100644 --- a/modules/ui_settings.py +++ b/modules/ui_settings.py @@ -1,7 +1,8 @@ import gradio as gr -from modules import ui_common, shared, script_callbacks, scripts, sd_models, sysinfo, timer +from modules import ui_common, shared, script_callbacks, scripts, sd_models, sysinfo, timer, shared_items from modules.call_queue import wrap_gradio_call +from modules.options import options_section from modules.shared import opts from modules.ui_components import FormRow from modules.ui_gradio_extensions import reload_javascript @@ -108,6 +109,11 @@ class UiSettings: shared.settings_components = self.component_dict + # we add this as late as possible so that scripts have already registered their callbacks + opts.data_labels.update(options_section(('callbacks', "Callbacks", "system"), { + **shared_items.callbacks_order_settings(), + })) + opts.reorder() with gr.Blocks(analytics_enabled=False) as settings_interface: diff --git a/style.css b/style.css index 29eae4127..f6a89b8f9 100644 --- a/style.css +++ b/style.css @@ -528,6 +528,10 @@ table.popup-table .link{ opacity: 0.75; } +.settings-comment .info ol{ + margin: 0.4em 0 0.8em 1em; +} + #sysinfo_download a.sysinfo_big_link{ font-size: 24pt; } From 2f55d669a26ca2c785b92656b8f32b56839f3750 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 10 Mar 2024 15:14:04 +0300 Subject: [PATCH 23/57] add support for specifying callback order in metadata --- modules/extensions.py | 24 ++++++++++++++++++++++++ modules/script_callbacks.py | 34 +++++++++++++++++++++++++++++++++- modules/scripts.py | 27 +++------------------------ modules/util.py | 24 ++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 25 deletions(-) diff --git a/modules/extensions.py b/modules/extensions.py index ab835d3f2..1f620ff16 100644 --- a/modules/extensions.py +++ b/modules/extensions.py @@ -1,6 +1,7 @@ from __future__ import annotations import configparser +import dataclasses import os import threading import re @@ -22,6 +23,13 @@ def active(): return [x for x in extensions if x.enabled] +@dataclasses.dataclass +class CallbackOrderInfo: + name: str + before: list + after: list + + class ExtensionMetadata: filename = "metadata.ini" config: configparser.ConfigParser @@ -65,6 +73,22 @@ class ExtensionMetadata: # both "," and " " are accepted as separator return [x for x in re.split(r"[,\s]+", text.strip()) if x] + def list_callback_order_instructions(self): + for section in self.config.sections(): + if not section.startswith("callbacks/"): + continue + + callback_name = section[10:] + + if not callback_name.startswith(self.canonical_name): + errors.report(f"Callback order section for extension {self.canonical_name} is referencing the wrong extension: {section}") + continue + + before = self.parse_list(self.config.get(section, 'Before', fallback='')) + after = self.parse_list(self.config.get(section, 'After', fallback='')) + + yield CallbackOrderInfo(callback_name, before, after) + class Extension: lock = threading.Lock() diff --git a/modules/script_callbacks.py b/modules/script_callbacks.py index a0ecf5a53..4d80b433b 100644 --- a/modules/script_callbacks.py +++ b/modules/script_callbacks.py @@ -8,7 +8,7 @@ from typing import Optional, Any from fastapi import FastAPI from gradio import Blocks -from modules import errors, timer, extensions, shared +from modules import errors, timer, extensions, shared, util def report_exception(c, job): @@ -149,6 +149,38 @@ def add_callback(callbacks, fun, *, name=None, category='unknown', filename=None def sort_callbacks(category, unordered_callbacks, *, enable_user_sort=True): callbacks = unordered_callbacks.copy() + callback_lookup = {x.name: x for x in callbacks} + dependencies = {} + + order_instructions = {} + for extension in extensions.extensions: + for order_instruction in extension.metadata.list_callback_order_instructions(): + if order_instruction.name in callback_lookup: + if order_instruction.name not in order_instructions: + order_instructions[order_instruction.name] = [] + + order_instructions[order_instruction.name].append(order_instruction) + + if order_instructions: + for callback in callbacks: + dependencies[callback.name] = [] + + for callback in callbacks: + for order_instruction in order_instructions.get(callback.name, []): + for after in order_instruction.after: + if after not in callback_lookup: + continue + + dependencies[callback.name].append(after) + + for before in order_instruction.before: + if before not in callback_lookup: + continue + + dependencies[before].append(callback.name) + + sorted_names = util.topological_sort(dependencies) + callbacks = [callback_lookup[x] for x in sorted_names] if enable_user_sort: for name in reversed(getattr(shared.opts, 'prioritized_callbacks_' + category, [])): diff --git a/modules/scripts.py b/modules/scripts.py index e1a435827..20710b37d 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -7,7 +7,9 @@ from dataclasses import dataclass import gradio as gr -from modules import shared, paths, script_callbacks, extensions, script_loading, scripts_postprocessing, errors, timer +from modules import shared, paths, script_callbacks, extensions, script_loading, scripts_postprocessing, errors, timer, util + +topological_sort = util.topological_sort AlwaysVisible = object() @@ -368,29 +370,6 @@ scripts_data = [] postprocessing_scripts_data = [] ScriptClassData = namedtuple("ScriptClassData", ["script_class", "path", "basedir", "module"]) -def topological_sort(dependencies): - """Accepts a dictionary mapping name to its dependencies, returns a list of names ordered according to dependencies. - Ignores errors relating to missing dependeencies or circular dependencies - """ - - visited = {} - result = [] - - def inner(name): - visited[name] = True - - for dep in dependencies.get(name, []): - if dep in dependencies and dep not in visited: - inner(dep) - - result.append(name) - - for depname in dependencies: - if depname not in visited: - inner(depname) - - return result - @dataclass class ScriptWithDependencies: diff --git a/modules/util.py b/modules/util.py index 8d1aea44f..48268f04c 100644 --- a/modules/util.py +++ b/modules/util.py @@ -136,3 +136,27 @@ class MassFileLister: def reset(self): """Clear the cache of all directories.""" self.cached_dirs.clear() + + +def topological_sort(dependencies): + """Accepts a dictionary mapping name to its dependencies, returns a list of names ordered according to dependencies. + Ignores errors relating to missing dependeencies or circular dependencies + """ + + visited = {} + result = [] + + def inner(name): + visited[name] = True + + for dep in dependencies.get(name, []): + if dep in dependencies and dep not in visited: + inner(dep) + + result.append(name) + + for depname in dependencies: + if depname not in visited: + inner(depname) + + return result From 3670b4f49e070e8b8da20c88f4d6bda9ba18895c Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 10 Mar 2024 15:16:12 +0300 Subject: [PATCH 24/57] lint --- modules/script_callbacks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/script_callbacks.py b/modules/script_callbacks.py index 4d80b433b..2cd65e832 100644 --- a/modules/script_callbacks.py +++ b/modules/script_callbacks.py @@ -120,7 +120,7 @@ class BeforeTokenCounterParams: @dataclasses.dataclass class ScriptCallback: script: str - callback: '' + callback: any name: str = None From 3e0146f9bdd79ed13d1fed729c76b97f7ab91587 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 10 Mar 2024 22:40:35 +0300 Subject: [PATCH 25/57] restore the lost Uncategorized options section --- modules/options.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/options.py b/modules/options.py index 35ccade25..2a78a825e 100644 --- a/modules/options.py +++ b/modules/options.py @@ -240,6 +240,9 @@ class Options: item_categories = {} for item in self.data_labels.values(): + if item.section[0] is None: + continue + category = categories.mapping.get(item.category_id) category = "Uncategorized" if category is None else category.label if category not in item_categories: From eb10da8bb74a0f664c01052f04168837387598b2 Mon Sep 17 00:00:00 2001 From: Andray Date: Mon, 11 Mar 2024 05:15:09 +0400 Subject: [PATCH 26/57] type hinting in shared.py --- modules/shared.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/modules/shared.py b/modules/shared.py index b4ba14ad7..8d1791532 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -7,6 +7,10 @@ from modules import shared_cmd_options, shared_gradio_themes, options, shared_it 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 # noqa: F401 from modules import util +falseVar = False # avoid circular import for type hinting +if falseVar: + from modules import shared_state, styles, interrogate, shared_total_tqdm, memmon + cmd_opts = shared_cmd_options.cmd_opts parser = shared_cmd_options.parser @@ -16,11 +20,11 @@ styles_filename = cmd_opts.styles_file = cmd_opts.styles_file if len(cmd_opts.st config_filename = cmd_opts.ui_settings_file hide_dirs = {"visible": not cmd_opts.hide_ui_dir_config} -demo = None +demo: gr.Blocks = None -device = None +device: str = None -weight_load_location = None +weight_load_location: str = None xformers_available = False @@ -28,21 +32,21 @@ hypernetworks = {} loaded_hypernetworks = [] -state = None +state: 'shared_state.State' = None -prompt_styles = None +prompt_styles: 'styles.StyleDatabase' = None -interrogator = None +interrogator: 'interrogate.InterrogateModels' = None face_restorers = [] -options_templates = None -opts = None -restricted_opts = None +options_templates: dict = None +opts: options.Options = None +restricted_opts: set[str] = None sd_model: sd_models_types.WebuiSdModel = None -settings_components = None +settings_components: dict = None """assigned from ui.py, a mapping on setting names to gradio components repsponsible for those settings""" tab_names = [] @@ -65,9 +69,9 @@ progress_print_out = sys.stdout gradio_theme = gr.themes.Base() -total_tqdm = None +total_tqdm: 'shared_total_tqdm.TotalTQDM' = None -mem_mon = None +mem_mon: 'memmon.MemUsageMonitor' = None options_section = options.options_section OptionInfo = options.OptionInfo From 2d57a2df660ec096969c89eb1ec72ebaa9a34636 Mon Sep 17 00:00:00 2001 From: Andray <33491867+light-and-ray@users.noreply.github.com> Date: Mon, 11 Mar 2024 07:40:15 +0400 Subject: [PATCH 27/57] Update modules/shared.py Co-authored-by: catboxanon <122327233+catboxanon@users.noreply.github.com> --- modules/shared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/shared.py b/modules/shared.py index 8d1791532..4cf7f6a81 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -6,9 +6,9 @@ import gradio as gr from modules import shared_cmd_options, shared_gradio_themes, options, shared_items, sd_models_types 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 # noqa: F401 from modules import util +from typing import TYPE_CHECKING -falseVar = False # avoid circular import for type hinting -if falseVar: +if TYPE_CHECKING: from modules import shared_state, styles, interrogate, shared_total_tqdm, memmon cmd_opts = shared_cmd_options.cmd_opts From 1a1205f601d27e1ca5052e01cf4f877615f6a499 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Tue, 12 Mar 2024 03:26:50 +0900 Subject: [PATCH 28/57] fix Restore progress --- javascript/ui.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/javascript/ui.js b/javascript/ui.js index 1eef6d337..e0f5feebd 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -136,8 +136,7 @@ function showSubmitInterruptingPlaceholder(tabname) { function showRestoreProgressButton(tabname, show) { var button = gradioApp().getElementById(tabname + "_restore_progress"); if (!button) return; - - button.style.display = show ? "flex" : "none"; + button.style.setProperty('display', show ? 'flex' : 'none', 'important'); } function submit() { @@ -209,6 +208,7 @@ function restoreProgressTxt2img() { var id = localGet("txt2img_task_id"); if (id) { + showSubmitInterruptingPlaceholder('txt2img'); requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function() { showSubmitButtons('txt2img', true); }, null, 0); @@ -223,6 +223,7 @@ function restoreProgressImg2img() { var id = localGet("img2img_task_id"); if (id) { + showSubmitInterruptingPlaceholder('img2img'); requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function() { showSubmitButtons('img2img', true); }, null, 0); From 4079b17dd979564bd762b42b31162c7e3e2edb7b Mon Sep 17 00:00:00 2001 From: Andray Date: Tue, 12 Mar 2024 01:47:23 +0400 Subject: [PATCH 29/57] move postprocessing-for-training into builtin extensions --- .../scripts/postprocessing_autosized_crop.py | 0 .../scripts}/postprocessing_caption.py | 0 .../scripts}/postprocessing_create_flipped_copies.py | 0 .../scripts}/postprocessing_focal_crop.py | 0 .../scripts}/postprocessing_split_oversized.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename scripts/processing_autosized_crop.py => extensions-builtin/postprocessing-for-training/scripts/postprocessing_autosized_crop.py (100%) rename {scripts => extensions-builtin/postprocessing-for-training/scripts}/postprocessing_caption.py (100%) rename {scripts => extensions-builtin/postprocessing-for-training/scripts}/postprocessing_create_flipped_copies.py (100%) rename {scripts => extensions-builtin/postprocessing-for-training/scripts}/postprocessing_focal_crop.py (100%) rename {scripts => extensions-builtin/postprocessing-for-training/scripts}/postprocessing_split_oversized.py (100%) diff --git a/scripts/processing_autosized_crop.py b/extensions-builtin/postprocessing-for-training/scripts/postprocessing_autosized_crop.py similarity index 100% rename from scripts/processing_autosized_crop.py rename to extensions-builtin/postprocessing-for-training/scripts/postprocessing_autosized_crop.py diff --git a/scripts/postprocessing_caption.py b/extensions-builtin/postprocessing-for-training/scripts/postprocessing_caption.py similarity index 100% rename from scripts/postprocessing_caption.py rename to extensions-builtin/postprocessing-for-training/scripts/postprocessing_caption.py diff --git a/scripts/postprocessing_create_flipped_copies.py b/extensions-builtin/postprocessing-for-training/scripts/postprocessing_create_flipped_copies.py similarity index 100% rename from scripts/postprocessing_create_flipped_copies.py rename to extensions-builtin/postprocessing-for-training/scripts/postprocessing_create_flipped_copies.py diff --git a/scripts/postprocessing_focal_crop.py b/extensions-builtin/postprocessing-for-training/scripts/postprocessing_focal_crop.py similarity index 100% rename from scripts/postprocessing_focal_crop.py rename to extensions-builtin/postprocessing-for-training/scripts/postprocessing_focal_crop.py diff --git a/scripts/postprocessing_split_oversized.py b/extensions-builtin/postprocessing-for-training/scripts/postprocessing_split_oversized.py similarity index 100% rename from scripts/postprocessing_split_oversized.py rename to extensions-builtin/postprocessing-for-training/scripts/postprocessing_split_oversized.py From 2e3a0f39f6bcbb53837e43341507ffda0bd36eec Mon Sep 17 00:00:00 2001 From: Andray Date: Tue, 12 Mar 2024 02:28:15 +0400 Subject: [PATCH 30/57] move upscale postprocessing under input accordion --- scripts/postprocessing_upscale.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index e269682d0..80692e5c2 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -4,7 +4,7 @@ import numpy as np from modules import scripts_postprocessing, shared import gradio as gr -from modules.ui_components import FormRow, ToolButton +from modules.ui_components import FormRow, ToolButton, InputAccordion from modules.ui import switch_values_symbol upscale_cache = {} @@ -17,7 +17,14 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): def ui(self): selected_tab = gr.Number(value=0, visible=False) - with gr.Column(): + with InputAccordion(True, label="Upscale", elem_id="extras_upscale") as upscale_enabled: + with FormRow(): + extras_upscaler_1 = gr.Dropdown(label='Upscaler 1', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) + + with FormRow(): + extras_upscaler_2 = gr.Dropdown(label='Upscaler 2', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) + extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility") + with FormRow(): with gr.Tabs(elem_id="extras_resize_mode"): with gr.TabItem('Scale by', elem_id="extras_scale_by_tab") as tab_scale_by: @@ -32,18 +39,12 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): upscaling_res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="upscaling_res_switch_btn", tooltip="Switch width/height") upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") - with FormRow(): - extras_upscaler_1 = gr.Dropdown(label='Upscaler 1', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) - - with FormRow(): - extras_upscaler_2 = gr.Dropdown(label='Upscaler 2', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) - extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility") - upscaling_res_switch_btn.click(lambda w, h: (h, w), inputs=[upscaling_resize_w, upscaling_resize_h], outputs=[upscaling_resize_w, upscaling_resize_h], show_progress=False) tab_scale_by.select(fn=lambda: 0, inputs=[], outputs=[selected_tab]) tab_scale_to.select(fn=lambda: 1, inputs=[], outputs=[selected_tab]) return { + "upscale_enabled": upscale_enabled, "upscale_mode": selected_tab, "upscale_by": upscaling_resize, "upscale_to_width": upscaling_resize_w, @@ -81,7 +82,7 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): return image - def process_firstpass(self, pp: scripts_postprocessing.PostprocessedImage, upscale_mode=1, upscale_by=2.0, upscale_to_width=None, upscale_to_height=None, upscale_crop=False, upscaler_1_name=None, upscaler_2_name=None, upscaler_2_visibility=0.0): + def process_firstpass(self, pp: scripts_postprocessing.PostprocessedImage, upscale_enabled=True, upscale_mode=1, upscale_by=2.0, upscale_to_width=None, upscale_to_height=None, upscale_crop=False, upscaler_1_name=None, upscaler_2_name=None, upscaler_2_visibility=0.0): if upscale_mode == 1: pp.shared.target_width = upscale_to_width pp.shared.target_height = upscale_to_height @@ -89,7 +90,10 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): pp.shared.target_width = int(pp.image.width * upscale_by) pp.shared.target_height = int(pp.image.height * upscale_by) - def process(self, pp: scripts_postprocessing.PostprocessedImage, upscale_mode=1, upscale_by=2.0, upscale_to_width=None, upscale_to_height=None, upscale_crop=False, upscaler_1_name=None, upscaler_2_name=None, upscaler_2_visibility=0.0): + def process(self, pp: scripts_postprocessing.PostprocessedImage, upscale_enabled=True, upscale_mode=1, upscale_by=2.0, upscale_to_width=None, upscale_to_height=None, upscale_crop=False, upscaler_1_name=None, upscaler_2_name=None, upscaler_2_visibility=0.0): + if not upscale_enabled: + return + if upscaler_1_name == "None": upscaler_1_name = None From 8262cd71c49e58a109ca8d5e57d6590831b5ead7 Mon Sep 17 00:00:00 2001 From: DGdev91 Date: Tue, 12 Mar 2024 00:09:07 +0100 Subject: [PATCH 31/57] Better workaround for Navi1, removing --pre for Navi3 --- webui.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/webui.sh b/webui.sh index 361255f69..89c52b519 100755 --- a/webui.sh +++ b/webui.sh @@ -129,11 +129,11 @@ case "$gpu_info" in export HSA_OVERRIDE_GFX_VERSION=10.3.0 if [[ -z "${TORCH_COMMAND}" ]] then - pyv="$(${python_cmd} -c 'import sys; print(".".join(map(str, sys.version_info[0:2])))')" - if [[ $(bc <<< "$pyv <= 3.10") -eq 1 ]] + pyv="$(${python_cmd} -c 'import sys; print(float(".".join(map(str, sys.version_info[0:2]))) <= 3.10)')" + if [[ $pyv == "True" ]] then - # Navi users will still use torch 1.13 because 2.0 does not seem to work. - export TORCH_COMMAND="pip install --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm5.6" + # Using an old nightly compiled against rocm 5.2 for Navi1, see https://github.com/pytorch/pytorch/issues/106728#issuecomment-1749511711 + export TORCH_COMMAND="pip install https://download.pytorch.org/whl/nightly/rocm5.2/torch-2.0.0.dev20230209%2Brocm5.2-cp310-cp310-linux_x86_64.whl https://download.pytorch.org/whl/nightly/rocm5.2/torchvision-0.15.0.dev20230209%2Brocm5.2-cp310-cp310-linux_x86_64.whl" else printf "\e[1m\e[31mERROR: RX 5000 series GPUs must be using at max python 3.10, aborting...\e[0m" exit 1 @@ -143,7 +143,7 @@ case "$gpu_info" in *"Navi 2"*) export HSA_OVERRIDE_GFX_VERSION=10.3.0 ;; *"Navi 3"*) [[ -z "${TORCH_COMMAND}" ]] && \ - export TORCH_COMMAND="pip install --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm5.7" + export TORCH_COMMAND="pip install torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm5.7" ;; *"Renoir"*) export HSA_OVERRIDE_GFX_VERSION=9.0.0 printf "\n%s\n" "${delimiter}" From 994e08aac1f80932dbd87a59a75ca6d4411bfe3a Mon Sep 17 00:00:00 2001 From: wangshuai09 <391746016@qq.com> Date: Tue, 12 Mar 2024 18:41:44 +0800 Subject: [PATCH 32/57] ascend npu readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f4cfcf290..bc08e7ad1 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,7 @@ Make sure the required [dependencies](https://github.com/AUTOMATIC1111/stable-di - [NVidia](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-NVidia-GPUs) (recommended) - [AMD](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-AMD-GPUs) GPUs. - [Intel CPUs, Intel GPUs (both integrated and discrete)](https://github.com/openvinotoolkit/stable-diffusion-webui/wiki/Installation-on-Intel-Silicon) (external wiki page) +- [Ascend NPUs](https://github.com/wangshuai09/stable-diffusion-webui/wiki/Install-and-run-on-Ascend-NPUs) (external wiki page) Alternatively, use online services (like Google Colab): From b980c8140ba336b3151cef7b4d1c1d2a3bca9130 Mon Sep 17 00:00:00 2001 From: Andray Date: Tue, 12 Mar 2024 22:21:59 +0400 Subject: [PATCH 33/57] featch only active branch updates for extensions --- modules/extensions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/extensions.py b/modules/extensions.py index 04bda297e..6542cb7ac 100644 --- a/modules/extensions.py +++ b/modules/extensions.py @@ -156,6 +156,8 @@ class Extension: def check_updates(self): repo = Repo(self.path) for fetch in repo.remote().fetch(dry_run=True): + if self.branch and fetch.name != f'{repo.remote().name}/{self.branch}': + continue if fetch.flags != fetch.HEAD_UPTODATE: self.can_update = True self.status = "new commits" From 74e2e5279c6a4147a8a893a137c028fda0479d07 Mon Sep 17 00:00:00 2001 From: DGdev91 Date: Wed, 13 Mar 2024 00:17:24 +0100 Subject: [PATCH 34/57] Workaround for Navi1: pytorch nightly whl for 3.8 and 3.9 --- webui.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/webui.sh b/webui.sh index 89c52b519..0b1d4d094 100755 --- a/webui.sh +++ b/webui.sh @@ -129,13 +129,19 @@ case "$gpu_info" in export HSA_OVERRIDE_GFX_VERSION=10.3.0 if [[ -z "${TORCH_COMMAND}" ]] then - pyv="$(${python_cmd} -c 'import sys; print(float(".".join(map(str, sys.version_info[0:2]))) <= 3.10)')" - if [[ $pyv == "True" ]] + pyv="$(${python_cmd} -c 'import sys; print(".".join(map(str, sys.version_info[0:2])))')" + # Using an old nightly compiled against rocm 5.2 for Navi1, see https://github.com/pytorch/pytorch/issues/106728#issuecomment-1749511711 + if [[ $pyv == "3.8" ]] + then + export TORCH_COMMAND="pip install https://download.pytorch.org/whl/nightly/rocm5.2/torch-2.0.0.dev20230209%2Brocm5.2-cp38-cp38-linux_x86_64.whl https://download.pytorch.org/whl/nightly/rocm5.2/torchvision-0.15.0.dev20230209%2Brocm5.2-cp38-cp38-linux_x86_64.whl" + if [[ $pyv == "3.9" ]] + then + export TORCH_COMMAND="pip install https://download.pytorch.org/whl/nightly/rocm5.2/torch-2.0.0.dev20230209%2Brocm5.2-cp39-cp39-linux_x86_64.whl https://download.pytorch.org/whl/nightly/rocm5.2/torchvision-0.15.0.dev20230209%2Brocm5.2-cp39-cp39-linux_x86_64.whl" + if [[ $pyv == "3.10" ]] then - # Using an old nightly compiled against rocm 5.2 for Navi1, see https://github.com/pytorch/pytorch/issues/106728#issuecomment-1749511711 export TORCH_COMMAND="pip install https://download.pytorch.org/whl/nightly/rocm5.2/torch-2.0.0.dev20230209%2Brocm5.2-cp310-cp310-linux_x86_64.whl https://download.pytorch.org/whl/nightly/rocm5.2/torchvision-0.15.0.dev20230209%2Brocm5.2-cp310-cp310-linux_x86_64.whl" else - printf "\e[1m\e[31mERROR: RX 5000 series GPUs must be using at max python 3.10, aborting...\e[0m" + printf "\e[1m\e[31mERROR: RX 5000 series GPUs python version must be between 3.8 and 3.10, aborting...\e[0m" exit 1 fi fi From 9fbfb8ad324415597765e3e63b1ce4a123f91539 Mon Sep 17 00:00:00 2001 From: DGdev91 Date: Wed, 13 Mar 2024 00:43:01 +0100 Subject: [PATCH 35/57] Better workaround for Navi1 - fix if --- webui.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webui.sh b/webui.sh index 0b1d4d094..b348c387e 100755 --- a/webui.sh +++ b/webui.sh @@ -134,10 +134,10 @@ case "$gpu_info" in if [[ $pyv == "3.8" ]] then export TORCH_COMMAND="pip install https://download.pytorch.org/whl/nightly/rocm5.2/torch-2.0.0.dev20230209%2Brocm5.2-cp38-cp38-linux_x86_64.whl https://download.pytorch.org/whl/nightly/rocm5.2/torchvision-0.15.0.dev20230209%2Brocm5.2-cp38-cp38-linux_x86_64.whl" - if [[ $pyv == "3.9" ]] + elif [[ $pyv == "3.9" ]] then export TORCH_COMMAND="pip install https://download.pytorch.org/whl/nightly/rocm5.2/torch-2.0.0.dev20230209%2Brocm5.2-cp39-cp39-linux_x86_64.whl https://download.pytorch.org/whl/nightly/rocm5.2/torchvision-0.15.0.dev20230209%2Brocm5.2-cp39-cp39-linux_x86_64.whl" - if [[ $pyv == "3.10" ]] + elif [[ $pyv == "3.10" ]] then export TORCH_COMMAND="pip install https://download.pytorch.org/whl/nightly/rocm5.2/torch-2.0.0.dev20230209%2Brocm5.2-cp310-cp310-linux_x86_64.whl https://download.pytorch.org/whl/nightly/rocm5.2/torchvision-0.15.0.dev20230209%2Brocm5.2-cp310-cp310-linux_x86_64.whl" else From 2efc7c1b0535e65274844c2e554bcdfd2b29a1f0 Mon Sep 17 00:00:00 2001 From: DGdev91 Date: Wed, 13 Mar 2024 00:54:32 +0100 Subject: [PATCH 36/57] Better workaround for Navi1, removing --pre for Navi3 --- webui.sh | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/webui.sh b/webui.sh index 361255f69..b348c387e 100755 --- a/webui.sh +++ b/webui.sh @@ -130,12 +130,18 @@ case "$gpu_info" in if [[ -z "${TORCH_COMMAND}" ]] then pyv="$(${python_cmd} -c 'import sys; print(".".join(map(str, sys.version_info[0:2])))')" - if [[ $(bc <<< "$pyv <= 3.10") -eq 1 ]] + # Using an old nightly compiled against rocm 5.2 for Navi1, see https://github.com/pytorch/pytorch/issues/106728#issuecomment-1749511711 + if [[ $pyv == "3.8" ]] then - # Navi users will still use torch 1.13 because 2.0 does not seem to work. - export TORCH_COMMAND="pip install --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm5.6" + export TORCH_COMMAND="pip install https://download.pytorch.org/whl/nightly/rocm5.2/torch-2.0.0.dev20230209%2Brocm5.2-cp38-cp38-linux_x86_64.whl https://download.pytorch.org/whl/nightly/rocm5.2/torchvision-0.15.0.dev20230209%2Brocm5.2-cp38-cp38-linux_x86_64.whl" + elif [[ $pyv == "3.9" ]] + then + export TORCH_COMMAND="pip install https://download.pytorch.org/whl/nightly/rocm5.2/torch-2.0.0.dev20230209%2Brocm5.2-cp39-cp39-linux_x86_64.whl https://download.pytorch.org/whl/nightly/rocm5.2/torchvision-0.15.0.dev20230209%2Brocm5.2-cp39-cp39-linux_x86_64.whl" + elif [[ $pyv == "3.10" ]] + then + export TORCH_COMMAND="pip install https://download.pytorch.org/whl/nightly/rocm5.2/torch-2.0.0.dev20230209%2Brocm5.2-cp310-cp310-linux_x86_64.whl https://download.pytorch.org/whl/nightly/rocm5.2/torchvision-0.15.0.dev20230209%2Brocm5.2-cp310-cp310-linux_x86_64.whl" else - printf "\e[1m\e[31mERROR: RX 5000 series GPUs must be using at max python 3.10, aborting...\e[0m" + printf "\e[1m\e[31mERROR: RX 5000 series GPUs python version must be between 3.8 and 3.10, aborting...\e[0m" exit 1 fi fi @@ -143,7 +149,7 @@ case "$gpu_info" in *"Navi 2"*) export HSA_OVERRIDE_GFX_VERSION=10.3.0 ;; *"Navi 3"*) [[ -z "${TORCH_COMMAND}" ]] && \ - export TORCH_COMMAND="pip install --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm5.7" + export TORCH_COMMAND="pip install torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm5.7" ;; *"Renoir"*) export HSA_OVERRIDE_GFX_VERSION=9.0.0 printf "\n%s\n" "${delimiter}" From 9f2ae1cb85015ee3cc79f1de92641032df154d5b Mon Sep 17 00:00:00 2001 From: KohakuBlueleaf Date: Wed, 13 Mar 2024 11:47:33 +0800 Subject: [PATCH 37/57] Add missing .mean --- extensions-builtin/Lora/network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions-builtin/Lora/network.py b/extensions-builtin/Lora/network.py index 183f8bd7c..473ba29e8 100644 --- a/extensions-builtin/Lora/network.py +++ b/extensions-builtin/Lora/network.py @@ -173,7 +173,7 @@ class NetworkModule: orig_weight = orig_weight.to(updown) merged_scale1 = updown + orig_weight dora_merged = ( - merged_scale1 / merged_scale1(dim=self.dora_mean_dim, keepdim=True) * self.dora_scale + merged_scale1 / merged_scale1.mean(dim=self.dora_mean_dim, keepdim=True) * self.dora_scale ) final_updown = dora_merged - orig_weight return final_updown From d18eb10ecdbf3690571d2d7ce0c0fe548d00d836 Mon Sep 17 00:00:00 2001 From: Haoming Date: Wed, 13 Mar 2024 21:15:52 +0800 Subject: [PATCH 38/57] add hook --- modules/ui_postprocessing.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/modules/ui_postprocessing.py b/modules/ui_postprocessing.py index 7261c2df8..e9d82d46a 100644 --- a/modules/ui_postprocessing.py +++ b/modules/ui_postprocessing.py @@ -4,6 +4,30 @@ import modules.infotext_utils as parameters_copypaste from modules.ui_components import ResizeHandleRow +def hook_scale_update(inputs): + resize = upscaler = None + for script in inputs: + if script.label == "Resize": + resize = script + elif script.label == "Upscaler 1": + upscaler = script + elif resize and upscaler: + break + + def update_scale(upscaler: str, slider: float): + if upscaler[1] in ('x', 'X'): + try: + scale = int(upscaler[0]) + return gr.update(value=scale) + except ValueError: + return gr.update(value=slider) + + return gr.update(value=slider) + + if resize and upscaler: + upscaler.input(update_scale, inputs=[upscaler, resize], outputs=[resize]) + + def create_ui(): dummy_component = gr.Label(visible=False) tab_index = gr.Number(value=0, visible=False) @@ -23,6 +47,7 @@ def create_ui(): show_extras_results = gr.Checkbox(label='Show result images', value=True, elem_id="extras_show_extras_results") script_inputs = scripts.scripts_postproc.setup_ui() + hook_scale_update(script_inputs) with gr.Column(): toprow = ui_toprow.Toprow(is_compact=True, is_img2img=False, id_part="extras") From fd71b761ff6b6636204ffcd71212cdbb7bb5d658 Mon Sep 17 00:00:00 2001 From: Haoming Date: Thu, 14 Mar 2024 09:55:14 +0800 Subject: [PATCH 39/57] use re instead of hardcoding Now supports all natively provided upscaler as well --- modules/ui_postprocessing.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/ui_postprocessing.py b/modules/ui_postprocessing.py index e9d82d46a..7fd889105 100644 --- a/modules/ui_postprocessing.py +++ b/modules/ui_postprocessing.py @@ -5,6 +5,9 @@ from modules.ui_components import ResizeHandleRow def hook_scale_update(inputs): + import re + pattern = r'(\d)[xX]|[xX](\d)' + resize = upscaler = None for script in inputs: if script.label == "Resize": @@ -15,14 +18,17 @@ def hook_scale_update(inputs): break def update_scale(upscaler: str, slider: float): - if upscaler[1] in ('x', 'X'): - try: - scale = int(upscaler[0]) - return gr.update(value=scale) - except ValueError: - return gr.update(value=slider) + match = re.search(pattern, upscaler) - return gr.update(value=slider) + if match: + if match.group(1): + return gr.update(value=int(match.group(1))) + + else: + return gr.update(value=int(match.group(2))) + + else: + return gr.update(value=slider) if resize and upscaler: upscaler.input(update_scale, inputs=[upscaler, resize], outputs=[resize]) From 4e17fc36d87a1d983c542dbfb606a93e70a68e2a Mon Sep 17 00:00:00 2001 From: Haoming Date: Thu, 14 Mar 2024 10:04:09 +0800 Subject: [PATCH 40/57] add user setting Now this is disabled by default --- modules/shared_options.py | 1 + modules/ui_postprocessing.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/shared_options.py b/modules/shared_options.py index 21643afe0..99a051aaf 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -101,6 +101,7 @@ options_templates.update(options_section(('upscaling', "Upscaling", "postprocess "DAT_tile": OptionInfo(192, "Tile size for DAT upscalers.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}).info("0 = no tiling"), "DAT_tile_overlap": OptionInfo(8, "Tile overlap for DAT upscalers.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}).info("Low values = visible seam"), "upscaler_for_img2img": OptionInfo(None, "Upscaler for img2img", gr.Dropdown, lambda: {"choices": [x.name for x in shared.sd_upscalers]}), + "scaleBy_from_upscaler": OptionInfo(False, "Automatically set the Scale by factor based on the name of the selected Upscaler.").info("Will not change the value when no matching pattern is found."), })) options_templates.update(options_section(('face-restoration', "Face restoration", "postprocessing"), { diff --git a/modules/ui_postprocessing.py b/modules/ui_postprocessing.py index 7fd889105..af44f6195 100644 --- a/modules/ui_postprocessing.py +++ b/modules/ui_postprocessing.py @@ -53,7 +53,8 @@ def create_ui(): show_extras_results = gr.Checkbox(label='Show result images', value=True, elem_id="extras_show_extras_results") script_inputs = scripts.scripts_postproc.setup_ui() - hook_scale_update(script_inputs) + if getattr(shared.opts, 'scaleBy_from_upscaler', False): + hook_scale_update(script_inputs) with gr.Column(): toprow = ui_toprow.Toprow(is_compact=True, is_img2img=False, id_part="extras") From c40f33ca0475a39a44576bc32dba9f87b011e9b2 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Fri, 15 Mar 2024 08:22:36 +0900 Subject: [PATCH 41/57] PEP 604 annotations --- modules/styles.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/styles.py b/modules/styles.py index a9d8636a9..25f22d3dd 100644 --- a/modules/styles.py +++ b/modules/styles.py @@ -1,3 +1,4 @@ +from __future__ import annotations from pathlib import Path from modules import errors import csv From 07805cbeee144fb112039fd114dba1479cf4ba63 Mon Sep 17 00:00:00 2001 From: v0xie <28695009+v0xie@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:05:14 -0700 Subject: [PATCH 42/57] fix: AttributeError when attempting to reshape rescale by org_module weight --- extensions-builtin/Lora/network_oft.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/extensions-builtin/Lora/network_oft.py b/extensions-builtin/Lora/network_oft.py index 7821a8a7d..1c515ebb7 100644 --- a/extensions-builtin/Lora/network_oft.py +++ b/extensions-builtin/Lora/network_oft.py @@ -36,13 +36,6 @@ class NetworkModuleOFT(network.NetworkModule): # self.alpha is unused 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_conv = type(self.sd_module) in [torch.nn.Conv2d] is_other_linear = type(self.sd_module) in [torch.nn.MultiheadAttention] # unsupported @@ -54,6 +47,13 @@ class NetworkModuleOFT(network.NetworkModule): elif is_other_linear: self.out_dim = self.sd_module.embed_dim + # 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 and not is_other_linear: + self.rescale = self.rescale.reshape(-1, *[1]*(self.org_module[0].weight.dim() - 1)) + self.num_blocks = self.dim self.block_size = self.out_dim // self.dim self.constraint = (0 if self.alpha is None else self.alpha) * self.out_dim From 76fd487818b1801906830a218c41ac434f2bd43d Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Thu, 14 Mar 2024 21:59:53 -0400 Subject: [PATCH 43/57] Make imageviewer event listeners browser consistent --- javascript/imageviewer.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/javascript/imageviewer.js b/javascript/imageviewer.js index 625c5d148..d4d4f016d 100644 --- a/javascript/imageviewer.js +++ b/javascript/imageviewer.js @@ -131,19 +131,15 @@ function setupImageForLightbox(e) { e.style.cursor = 'pointer'; e.style.userSelect = 'none'; - var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; - - // For Firefox, listening on click first switched to next image then shows the lightbox. - // If you know how to fix this without switching to mousedown event, please. - // For other browsers the event is click to make it possiblr to drag picture. - var event = isFirefox ? 'mousedown' : 'click'; - - e.addEventListener(event, function(evt) { + e.addEventListener('mousedown', function(evt) { if (evt.button == 1) { open(evt.target.src); evt.preventDefault(); return; } + }, true); + + e.addEventListener('click', function(evt) { if (!opts.js_modal_lightbox || evt.button != 0) return; modalZoomSet(gradioApp().getElementById('modalImage'), opts.js_modal_lightbox_initially_zoomed); From 5f4203bf9b622a0eb3b6a1eb2c8ef8dbb56930a9 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Thu, 14 Mar 2024 22:21:08 -0600 Subject: [PATCH 44/57] Strip comments from hires fix prompt --- modules/processing_scripts/comments.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/processing_scripts/comments.py b/modules/processing_scripts/comments.py index 638e39f29..cf81dfd8b 100644 --- a/modules/processing_scripts/comments.py +++ b/modules/processing_scripts/comments.py @@ -26,6 +26,13 @@ class ScriptStripComments(scripts.Script): p.main_prompt = strip_comments(p.main_prompt) p.main_negative_prompt = strip_comments(p.main_negative_prompt) + if getattr(p, 'enable_hr', False): + p.all_hr_prompts = [strip_comments(x) for x in p.all_hr_prompts] + p.all_hr_negative_prompts = [strip_comments(x) for x in p.all_hr_negative_prompts] + + p.hr_prompt = strip_comments(p.hr_prompt) + p.hr_negative_prompt = strip_comments(p.hr_negative_prompt) + def before_token_counter(params: script_callbacks.BeforeTokenCounterParams): if not shared.opts.enable_prompt_comments: From 6f51e0555376cff9f185cb4f1dde8537bfec92af Mon Sep 17 00:00:00 2001 From: Andray Date: Fri, 15 Mar 2024 12:01:43 +0400 Subject: [PATCH 45/57] prevent alt menu for firefox --- .../canvas-zoom-and-pan/javascript/zoom.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index 64e7a638a..aa27ac157 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -966,3 +966,26 @@ onUiLoaded(async() => { // Add integration with Inpaint Anything // applyZoomAndPanIntegration("None", ["#ia_sam_image", "#ia_sel_mask"]); }); + + +onUiLoaded(function() { + let isAltPressed = false; + + function handleAltKeyDown(e) { + if (e.code === "AltLeft" || e.code === "AltRight") { + isAltPressed = true; + } else { + isAltPressed = false; + } + } + + function handleAltKeyUp(e) { + if (isAltPressed) { + e.preventDefault(); + } + isAltPressed = false; + } + + document.addEventListener("keydown", handleAltKeyDown); + document.addEventListener("keyup", handleAltKeyUp); +}); From 887a5122083d27fd819bfeb54524dbdc791961cc Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Fri, 15 Mar 2024 21:06:54 +0900 Subject: [PATCH 46/57] fix issue with Styles when Hires prompt is used --- modules/infotext_utils.py | 31 ++++++++++++++++++++----------- modules/infotext_versions.py | 1 + modules/processing.py | 15 ++++++++------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/modules/infotext_utils.py b/modules/infotext_utils.py index a1cbfb17d..723cb1f82 100644 --- a/modules/infotext_utils.py +++ b/modules/infotext_utils.py @@ -265,17 +265,6 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model else: prompt += ("" if prompt == "" else "\n") + line - if shared.opts.infotext_styles != "Ignore": - found_styles, prompt, negative_prompt = shared.prompt_styles.extract_styles_from_prompt(prompt, negative_prompt) - - if shared.opts.infotext_styles == "Apply": - res["Styles array"] = found_styles - elif shared.opts.infotext_styles == "Apply if any" and found_styles: - res["Styles array"] = found_styles - - res["Prompt"] = prompt - res["Negative prompt"] = negative_prompt - for k, v in re_param.findall(lastline): try: if v[0] == '"' and v[-1] == '"': @@ -290,6 +279,26 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model except Exception: print(f"Error parsing \"{k}: {v}\"") + # Extract styles from prompt + if shared.opts.infotext_styles != "Ignore": + found_styles, prompt_no_styles, negative_prompt_no_styles = shared.prompt_styles.extract_styles_from_prompt(prompt, negative_prompt) + + same_hr_styles = True + if ("Hires prompt" in res or "Hires negative prompt" in res) and (infotext_ver > infotext_versions.v180_hr_styles if (infotext_ver := infotext_versions.parse_version(res.get("Version"))) else True): + hr_prompt, hr_negative_prompt = res.get("Hires prompt", prompt), res.get("Hires negative prompt", negative_prompt) + hr_found_styles, hr_prompt_no_styles, hr_negative_prompt_no_styles = shared.prompt_styles.extract_styles_from_prompt(hr_prompt, hr_negative_prompt) + if same_hr_styles := found_styles == hr_found_styles: + res["Hires prompt"] = '' if hr_prompt_no_styles == prompt_no_styles else hr_prompt_no_styles + res['Hires negative prompt'] = '' if hr_negative_prompt_no_styles == negative_prompt_no_styles else hr_negative_prompt_no_styles + + if same_hr_styles: + prompt, negative_prompt = prompt_no_styles, negative_prompt_no_styles + if (shared.opts.infotext_styles == "Apply if any" and found_styles) or shared.opts.infotext_styles == "Apply": + res['Styles array'] = found_styles + + res["Prompt"] = prompt + res["Negative prompt"] = negative_prompt + # Missing CLIP skip means it was set to 1 (the default) if "Clip skip" not in res: res["Clip skip"] = "1" diff --git a/modules/infotext_versions.py b/modules/infotext_versions.py index b5552a312..0d2d6282a 100644 --- a/modules/infotext_versions.py +++ b/modules/infotext_versions.py @@ -6,6 +6,7 @@ import re v160 = version.parse("1.6.0") v170_tsnr = version.parse("v1.7.0-225") v180 = version.parse("1.8.0") +v180_hr_styles = version.parse("1.8.0-136") # todo: change to the actual version number after merge def parse_version(text): diff --git a/modules/processing.py b/modules/processing.py index 86194b057..d6873a510 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -702,7 +702,7 @@ def program_version(): return res -def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0, use_main_prompt=False, index=None, all_negative_prompts=None): +def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0, use_main_prompt=False, index=None, all_negative_prompts=None, all_hr_prompts=None, all_hr_negative_prompts=None): if index is None: index = position_in_batch + iteration * p.batch_size @@ -745,11 +745,18 @@ def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iter "RNG": opts.randn_source if opts.randn_source != "GPU" else None, "NGMS": None if p.s_min_uncond == 0 else p.s_min_uncond, "Tiling": "True" if p.tiling else None, + "Hires prompt": None, # This is set later, insert here to keep order + "Hires negative prompt": None, # This is set later, insert here to keep order **p.extra_generation_params, "Version": program_version() if opts.add_version_to_infotext else None, "User": p.user if opts.add_user_name_to_info else None, } + if all_hr_prompts := all_hr_prompts or getattr(p, 'all_hr_prompts', None): + generation_params['Hires prompt'] = all_hr_prompts[index] if all_hr_prompts[index] != all_prompts[index] else None + if all_hr_negative_prompts := all_hr_negative_prompts or getattr(p, 'all_hr_negative_prompts', None): + generation_params['Hires negative prompt'] = all_hr_negative_prompts[index] if all_hr_negative_prompts[index] != all_negative_prompts[index] else None + generation_params_text = ", ".join([k if k == v else f'{k}: {infotext_utils.quote(v)}' for k, v in generation_params.items() if v is not None]) prompt_text = p.main_prompt if use_main_prompt else all_prompts[index] @@ -1194,12 +1201,6 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): if self.hr_sampler_name is not None and self.hr_sampler_name != self.sampler_name: self.extra_generation_params["Hires sampler"] = self.hr_sampler_name - if tuple(self.hr_prompt) != tuple(self.prompt): - self.extra_generation_params["Hires prompt"] = self.hr_prompt - - if tuple(self.hr_negative_prompt) != tuple(self.negative_prompt): - self.extra_generation_params["Hires negative prompt"] = self.hr_negative_prompt - self.latent_scale_mode = shared.latent_upscale_modes.get(self.hr_upscaler, None) if self.hr_upscaler is not None else shared.latent_upscale_modes.get(shared.latent_upscale_default_mode, "nearest") if self.enable_hr and self.latent_scale_mode is None: if not any(x.name == self.hr_upscaler for x in shared.sd_upscalers): From a3a648bf6bf49d3ec51de3cee74035ac71f1662d Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sat, 16 Mar 2024 05:57:23 +0900 Subject: [PATCH 47/57] bump action version --- .github/workflows/on_pull_request.yaml | 8 ++++---- .github/workflows/run_tests.yaml | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/on_pull_request.yaml b/.github/workflows/on_pull_request.yaml index 9e44c806a..c595b80aa 100644 --- a/.github/workflows/on_pull_request.yaml +++ b/.github/workflows/on_pull_request.yaml @@ -11,8 +11,8 @@ jobs: if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name steps: - name: Checkout Code - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: 3.11 # NB: there's no cache: pip here since we're not installing anything @@ -29,9 +29,9 @@ jobs: if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18 - run: npm i --ci diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index f42e4758e..0610f4f54 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -11,9 +11,9 @@ jobs: if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python 3.10 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.10.6 cache: pip @@ -22,7 +22,7 @@ jobs: launch.py - name: Cache models id: cache-models - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: models key: "2023-12-30" @@ -68,13 +68,13 @@ jobs: python -m coverage report -i python -m coverage html -i - name: Upload main app output - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: output path: output.txt - name: Upload coverage HTML - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: htmlcov From 63c3c4dbc357e1e400b40fe22521857eeaeb01b4 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 16 Mar 2024 09:04:08 +0300 Subject: [PATCH 48/57] simplify code for #15244 --- modules/shared_options.py | 2 +- modules/ui_postprocessing.py | 32 ------------------------------- scripts/postprocessing_upscale.py | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 33 deletions(-) diff --git a/modules/shared_options.py b/modules/shared_options.py index 99a051aaf..fc9f13d6f 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -101,7 +101,7 @@ options_templates.update(options_section(('upscaling', "Upscaling", "postprocess "DAT_tile": OptionInfo(192, "Tile size for DAT upscalers.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}).info("0 = no tiling"), "DAT_tile_overlap": OptionInfo(8, "Tile overlap for DAT upscalers.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}).info("Low values = visible seam"), "upscaler_for_img2img": OptionInfo(None, "Upscaler for img2img", gr.Dropdown, lambda: {"choices": [x.name for x in shared.sd_upscalers]}), - "scaleBy_from_upscaler": OptionInfo(False, "Automatically set the Scale by factor based on the name of the selected Upscaler.").info("Will not change the value when no matching pattern is found."), + "set_scale_by_when_changing_upscaler": OptionInfo(False, "Automatically set the Scale by factor based on the name of the selected Upscaler."), })) options_templates.update(options_section(('face-restoration', "Face restoration", "postprocessing"), { diff --git a/modules/ui_postprocessing.py b/modules/ui_postprocessing.py index af44f6195..7261c2df8 100644 --- a/modules/ui_postprocessing.py +++ b/modules/ui_postprocessing.py @@ -4,36 +4,6 @@ import modules.infotext_utils as parameters_copypaste from modules.ui_components import ResizeHandleRow -def hook_scale_update(inputs): - import re - pattern = r'(\d)[xX]|[xX](\d)' - - resize = upscaler = None - for script in inputs: - if script.label == "Resize": - resize = script - elif script.label == "Upscaler 1": - upscaler = script - elif resize and upscaler: - break - - def update_scale(upscaler: str, slider: float): - match = re.search(pattern, upscaler) - - if match: - if match.group(1): - return gr.update(value=int(match.group(1))) - - else: - return gr.update(value=int(match.group(2))) - - else: - return gr.update(value=slider) - - if resize and upscaler: - upscaler.input(update_scale, inputs=[upscaler, resize], outputs=[resize]) - - def create_ui(): dummy_component = gr.Label(visible=False) tab_index = gr.Number(value=0, visible=False) @@ -53,8 +23,6 @@ def create_ui(): show_extras_results = gr.Checkbox(label='Show result images', value=True, elem_id="extras_show_extras_results") script_inputs = scripts.scripts_postproc.setup_ui() - if getattr(shared.opts, 'scaleBy_from_upscaler', False): - hook_scale_update(script_inputs) with gr.Column(): toprow = ui_toprow.Toprow(is_compact=True, is_img2img=False, id_part="extras") diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index e269682d0..d8ba70ed8 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -1,3 +1,5 @@ +import re + from PIL import Image import numpy as np @@ -39,10 +41,22 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): extras_upscaler_2 = gr.Dropdown(label='Upscaler 2', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility") + def on_selected_upscale_method(upscale_method): + if not shared.opts.set_scale_by_when_changing_upscaler: + return gr.update() + + match = re.search(r'(\d)[xX]|[xX](\d)', upscale_method) + if not match: + return gr.update() + + return gr.update(value=int(match.group(1) or match.group(2))) + upscaling_res_switch_btn.click(lambda w, h: (h, w), inputs=[upscaling_resize_w, upscaling_resize_h], outputs=[upscaling_resize_w, upscaling_resize_h], show_progress=False) tab_scale_by.select(fn=lambda: 0, inputs=[], outputs=[selected_tab]) tab_scale_to.select(fn=lambda: 1, inputs=[], outputs=[selected_tab]) + extras_upscaler_1.change(on_selected_upscale_method, inputs=[extras_upscaler_1], outputs=[upscaling_resize], show_progress="hidden") + return { "upscale_mode": selected_tab, "upscale_by": upscaling_resize, From 38a7dc54885f9df098a3275d687d3b82fa54de1d Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sat, 16 Mar 2024 17:19:38 +0900 Subject: [PATCH 49/57] v180_hr_styles actual version number --- modules/infotext_versions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/infotext_versions.py b/modules/infotext_versions.py index 0d2d6282a..cea676cda 100644 --- a/modules/infotext_versions.py +++ b/modules/infotext_versions.py @@ -6,7 +6,7 @@ import re v160 = version.parse("1.6.0") v170_tsnr = version.parse("v1.7.0-225") v180 = version.parse("1.8.0") -v180_hr_styles = version.parse("1.8.0-136") # todo: change to the actual version number after merge +v180_hr_styles = version.parse("1.8.0-139") def parse_version(text): From cc8ea32501e09787d73815d2a982071fd3a4686a Mon Sep 17 00:00:00 2001 From: Andray Date: Tue, 12 Mar 2024 21:29:57 +0400 Subject: [PATCH 50/57] fix ui-config for InputAccordion --- modules/ui_loadsave.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/ui_loadsave.py b/modules/ui_loadsave.py index 2555cdb6c..0cc1ab82a 100644 --- a/modules/ui_loadsave.py +++ b/modules/ui_loadsave.py @@ -104,6 +104,8 @@ class UiLoadsave: apply_field(x, 'value', check_dropdown, getattr(x, 'init_field', None)) if type(x) == InputAccordion: + if hasattr(x, 'custom_script_source'): + x.accordion.custom_script_source = x.custom_script_source if x.accordion.visible: apply_field(x.accordion, 'visible') apply_field(x, 'value') From 79514e5b8e112c47128f3da506267e8540219097 Mon Sep 17 00:00:00 2001 From: Andray Date: Sat, 16 Mar 2024 16:06:21 +0400 Subject: [PATCH 51/57] prevent defaults for alt only if mouse inside image --- .../canvas-zoom-and-pan/javascript/zoom.js | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index aa27ac157..63900025f 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -839,6 +839,31 @@ onUiLoaded(async() => { document.addEventListener("keydown", handleMoveKeyDown); document.addEventListener("keyup", handleMoveKeyUp); + // Prevent firefox to open toolbar on pressing alt + if (hotkeysConfig.canvas_hotkey_zoom === "Alt") { + let isAltPressed = false; + + function handleAltKeyDown(e) { + if (!activeElement) return; + if (e.code === "AltLeft" || e.code === "AltRight") { + isAltPressed = true; + } else { + isAltPressed = false; + } + } + + function handleAltKeyUp(e) { + if (isAltPressed) { + e.preventDefault(); + } + isAltPressed = false; + } + + document.addEventListener("keydown", handleAltKeyDown); + document.addEventListener("keyup", handleAltKeyUp); + } + + // Detect zoom level and update the pan speed. function updatePanPosition(movementX, movementY) { let panSpeed = 2; @@ -966,26 +991,3 @@ onUiLoaded(async() => { // Add integration with Inpaint Anything // applyZoomAndPanIntegration("None", ["#ia_sam_image", "#ia_sel_mask"]); }); - - -onUiLoaded(function() { - let isAltPressed = false; - - function handleAltKeyDown(e) { - if (e.code === "AltLeft" || e.code === "AltRight") { - isAltPressed = true; - } else { - isAltPressed = false; - } - } - - function handleAltKeyUp(e) { - if (isAltPressed) { - e.preventDefault(); - } - isAltPressed = false; - } - - document.addEventListener("keydown", handleAltKeyDown); - document.addEventListener("keyup", handleAltKeyUp); -}); From 9142ce8188cc0d7b8847df0c2f11ab47a0b13a50 Mon Sep 17 00:00:00 2001 From: Andray Date: Sat, 16 Mar 2024 16:14:57 +0400 Subject: [PATCH 52/57] fix linter and do not require reload page if option was changed --- .../canvas-zoom-and-pan/javascript/zoom.js | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index 63900025f..a575862df 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -839,30 +839,31 @@ onUiLoaded(async() => { document.addEventListener("keydown", handleMoveKeyDown); document.addEventListener("keyup", handleMoveKeyUp); + // Prevent firefox to open toolbar on pressing alt - if (hotkeysConfig.canvas_hotkey_zoom === "Alt") { - let isAltPressed = false; + let isAltPressed = false; - function handleAltKeyDown(e) { - if (!activeElement) return; - if (e.code === "AltLeft" || e.code === "AltRight") { - isAltPressed = true; - } else { - isAltPressed = false; - } - } - - function handleAltKeyUp(e) { - if (isAltPressed) { - e.preventDefault(); - } + function handleAltKeyDown(e) { + if (!activeElement) return; + if (hotkeysConfig.canvas_hotkey_zoom !== "Alt") return; + if (e.code === "AltLeft" || e.code === "AltRight") { + isAltPressed = true; + } else { isAltPressed = false; } - - document.addEventListener("keydown", handleAltKeyDown); - document.addEventListener("keyup", handleAltKeyUp); } + function handleAltKeyUp(e) { + if (hotkeysConfig.canvas_hotkey_zoom !== "Alt") return; + if (isAltPressed) { + e.preventDefault(); + } + isAltPressed = false; + } + + document.addEventListener("keydown", handleAltKeyDown); + document.addEventListener("keyup", handleAltKeyUp); + // Detect zoom level and update the pan speed. function updatePanPosition(movementX, movementY) { From eb2ea8df1de2aaaab8a1ed069b34ca7385e30af2 Mon Sep 17 00:00:00 2001 From: Andray Date: Sat, 16 Mar 2024 17:42:25 +0400 Subject: [PATCH 53/57] check e.key in up event --- .../canvas-zoom-and-pan/javascript/zoom.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index a575862df..dfd7b17a2 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -841,24 +841,24 @@ onUiLoaded(async() => { // Prevent firefox to open toolbar on pressing alt - let isAltPressed = false; + let wasAltPressed = false; function handleAltKeyDown(e) { if (!activeElement) return; if (hotkeysConfig.canvas_hotkey_zoom !== "Alt") return; if (e.code === "AltLeft" || e.code === "AltRight") { - isAltPressed = true; + wasAltPressed = true; } else { - isAltPressed = false; + wasAltPressed = false; } } function handleAltKeyUp(e) { if (hotkeysConfig.canvas_hotkey_zoom !== "Alt") return; - if (isAltPressed) { + if (wasAltPressed || (activeElement && e.key === "Alt")) { e.preventDefault(); } - isAltPressed = false; + wasAltPressed = false; } document.addEventListener("keydown", handleAltKeyDown); From 7598a92436be66f5ba63a9234ba8cdbcbc3cc662 Mon Sep 17 00:00:00 2001 From: Andray Date: Sat, 16 Mar 2024 17:49:05 +0400 Subject: [PATCH 54/57] use e.key instead of e.code --- extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index dfd7b17a2..c6b463fd4 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -846,7 +846,7 @@ onUiLoaded(async() => { function handleAltKeyDown(e) { if (!activeElement) return; if (hotkeysConfig.canvas_hotkey_zoom !== "Alt") return; - if (e.code === "AltLeft" || e.code === "AltRight") { + if (e.key === "Alt") { wasAltPressed = true; } else { wasAltPressed = false; From c364b607764047cab27887bb78122fc302e2041b Mon Sep 17 00:00:00 2001 From: Andray Date: Fri, 15 Mar 2024 12:07:11 +0400 Subject: [PATCH 55/57] handle 0 wheel deltaX --- extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index 64e7a638a..b0963f4fe 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -793,7 +793,7 @@ onUiLoaded(async() => { targetElement.addEventListener("wheel", e => { // change zoom level - const operation = e.deltaY > 0 ? "-" : "+"; + const operation = (e.deltaY || -e.wheelDelta) > 0 ? "-" : "+"; changeZoomLevel(operation, e); // Handle brush size adjustment with ctrl key pressed From 0283826179cd3aec923053877f0742919c52e166 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 16 Mar 2024 18:44:36 +0300 Subject: [PATCH 56/57] prevent make alt key from opening main menu if it's used for brush size also --- .../canvas-zoom-and-pan/javascript/zoom.js | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index 6e61def21..ed2ef99b0 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -252,6 +252,7 @@ onUiLoaded(async() => { let isMoving = false; let mouseX, mouseY; let activeElement; + let interactedWithAltKey = false; const elements = Object.fromEntries( Object.keys(elementIDs).map(id => [ @@ -508,6 +509,10 @@ onUiLoaded(async() => { if (isModifierKey(e, hotkeysConfig.canvas_hotkey_zoom)) { e.preventDefault(); + if(hotkeysConfig.canvas_hotkey_zoom === "Alt"){ + interactedWithAltKey = true; + } + let zoomPosX, zoomPosY; let delta = 0.2; if (elemData[elemId].zoomLevel > 7) { @@ -800,6 +805,10 @@ onUiLoaded(async() => { if (isModifierKey(e, hotkeysConfig.canvas_hotkey_adjust)) { e.preventDefault(); + if(hotkeysConfig.canvas_hotkey_adjust === "Alt"){ + interactedWithAltKey = true; + } + // Increase or decrease brush size based on scroll direction adjustBrushSize(elemId, e.deltaY); } @@ -840,28 +849,16 @@ onUiLoaded(async() => { document.addEventListener("keyup", handleMoveKeyUp); - // Prevent firefox to open toolbar on pressing alt - let wasAltPressed = false; - - function handleAltKeyDown(e) { - if (!activeElement) return; - if (hotkeysConfig.canvas_hotkey_zoom !== "Alt") return; - if (e.key === "Alt") { - wasAltPressed = true; - } else { - wasAltPressed = false; - } - } - + // Prevent firefox from opening main menu when alt is used as a hotkey for zoom or brush size function handleAltKeyUp(e) { - if (hotkeysConfig.canvas_hotkey_zoom !== "Alt") return; - if (wasAltPressed || (activeElement && e.key === "Alt")) { - e.preventDefault(); + if (e.key !== "Alt" || !interactedWithAltKey) { + return; } - wasAltPressed = false; + + e.preventDefault(); + interactedWithAltKey = false; } - document.addEventListener("keydown", handleAltKeyDown); document.addEventListener("keyup", handleAltKeyUp); From bf35c661834be65d10a1e77f6cecbf7e5e6a687e Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 16 Mar 2024 18:45:19 +0300 Subject: [PATCH 57/57] fix for #15179 --- modules/upscaler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/upscaler.py b/modules/upscaler.py index 9d13ee993..4ffd428c6 100644 --- a/modules/upscaler.py +++ b/modules/upscaler.py @@ -20,7 +20,7 @@ class Upscaler: filter = None model = None user_path = None - scalers: list = [] + scalers: list tile = True def __init__(self, create_dirs=False):