function toggleCss(key, css, enable) { let style = document.getElementById(key); if (enable && !style) { style = document.createElement("style"); style.id = key; style.type = "text/css"; document.head.appendChild(style); } if (style && !enable) { document.head.removeChild(style); } if (style) { style.innerHTML == ""; style.appendChild(document.createTextNode(css)); } } function setupExtraNetworksForTab(tabname) { function registerPrompt(tabname, id) { let textarea = gradioApp().querySelector("#" + id + " > label > textarea"); if (!activePromptTextarea[tabname]) { activePromptTextarea[tabname] = textarea; } textarea.addEventListener("focus", function () { activePromptTextarea[tabname] = textarea; }); } let tabnav = gradioApp().querySelector( "#" + tabname + "_extra_tabs > div.tab-nav", ); let controlsDiv = document.createElement("DIV"); controlsDiv.classList.add("extra-networks-controls-div"); tabnav.appendChild(controlsDiv); tabnav.insertBefore(controlsDiv, null); let this_tab = gradioApp().querySelector("#" + tabname + "_extra_tabs"); this_tab .querySelectorAll(":scope > [id^='" + tabname + "_']") .forEach(function (elem) { // tabname_full = {tabname}_{extra_networks_tabname} let tabname_full = elem.id; let search = gradioApp().querySelector( "#" + tabname_full + "_extra_search", ); let sort_dir = gradioApp().querySelector( "#" + tabname_full + "_extra_sort_dir", ); let refresh = gradioApp().querySelector( "#" + tabname_full + "_extra_refresh", ); let currentSort = ""; // If any of the buttons above don't exist, we want to skip this iteration of the loop. if (!search || !sort_dir || !refresh) { return; // `return` is equivalent of `continue` but for forEach loops. } let applyFilter = function (force) { let searchTerm = search.value.toLowerCase(); // get UI preset radioUI = gradioApp().querySelector("#forge_ui_preset"); radioButtons = radioUI.getElementsByTagName("input"); UIresult = 3; // default to 'all' for (i = 0; i < radioButtons.length; i++) { if (radioButtons[i].checked) { UIresult = i; } } gradioApp() .querySelectorAll("#" + tabname + "_extra_tabs div.card") .forEach(function (elem) { let searchOnly = elem.querySelector(".search_only"); let text = Array.prototype.map .call( elem.querySelectorAll(".search_terms, .description"), function (t) { return t.textContent.toLowerCase(); }, ) .join(" "); let visible = true; if (searchOnly && searchTerm.length < 4) visible = false; splitSearch = searchTerm.split(" "); splitSearch.forEach(function (partial) { if (text.indexOf(partial) == -1) visible = false; }); sdversion = elem.getAttribute("data-sort-sdversion"); if (sdversion == null); else if (sdversion == "SdVersion.Unknown"); else if (opts.lora_filter_disabled == True); else if (UIresult == 3); // 'all' else if (UIresult == 0) { // 'sd' if (sdversion != "SdVersion.SD1") visible = false; } else if (UIresult == 1) { // 'xl' if (sdversion != "SdVersion.SDXL") visible = false; } else if (UIresult == 2) { // 'flux' if (sdversion != "SdVersion.Flux") visible = false; } if (visible) { elem.classList.remove("hidden"); } else { elem.classList.add("hidden"); } }); applySort(force); }; let applySort = function (force) { let cards = gradioApp().querySelectorAll( "#" + tabname_full + " div.card", ); let parent = gradioApp().querySelector("#" + tabname_full + "_cards"); let reverse = sort_dir.dataset.sortdir == "Descending"; let activeSearchElem = gradioApp().querySelector( "#" + tabname_full + "_controls .extra-network-control--sort.extra-network-control--enabled", ); let sortKey = activeSearchElem ? activeSearchElem.dataset.sortkey : "default"; let sortKeyDataField = "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1); let sortKeyStore = sortKey + "-" + sort_dir.dataset.sortdir + "-" + cards.length; if (sortKeyStore == currentSort && !force) { return; } currentSort = sortKeyStore; let sortedCards = Array.from(cards); sortedCards.sort(function (cardA, cardB) { let a = cardA.dataset[sortKeyDataField]; let b = cardB.dataset[sortKeyDataField]; if (!isNaN(a) && !isNaN(b)) { return parseInt(a) - parseInt(b); } return a < b ? -1 : a > b ? 1 : 0; }); if (reverse) { sortedCards.reverse(); } parent.innerHTML = ""; let frag = document.createDocumentFragment(); sortedCards.forEach(function (card) { frag.appendChild(card); }); parent.appendChild(frag); }; search.addEventListener("input", function () { applyFilter(); }); applySort(); applyFilter(); extraNetworksApplySort[tabname_full] = applySort; extraNetworksApplyFilter[tabname_full] = applyFilter; let controls = gradioApp().querySelector( "#" + tabname_full + "_controls", ); controlsDiv.insertBefore(controls, null); if (elem.style.display != "none") { extraNetworksShowControlsForPage(tabname, tabname_full); } }); registerPrompt(tabname, tabname + "_prompt"); registerPrompt(tabname, tabname + "_neg_prompt"); } function extraNetworksMovePromptToTab( tabname, id, showPrompt, showNegativePrompt, ) { if (!gradioApp().querySelector(".toprow-compact-tools")) return; // only applicable for compact prompt layout let promptContainer = gradioApp().getElementById( tabname + "_prompt_container", ); let prompt = gradioApp().getElementById(tabname + "_prompt_row"); let negPrompt = gradioApp().getElementById(tabname + "_neg_prompt_row"); let elem = id ? gradioApp().getElementById(id) : null; if (showNegativePrompt && elem) { elem.insertBefore(negPrompt, elem.firstChild); } else { promptContainer.insertBefore(negPrompt, promptContainer.firstChild); } if (showPrompt && elem) { elem.insertBefore(prompt, elem.firstChild); } else { promptContainer.insertBefore(prompt, promptContainer.firstChild); } if (elem) { elem.classList.toggle( "extra-page-prompts-active", showNegativePrompt || showPrompt, ); } } function extraNetworksShowControlsForPage(tabname, tabname_full) { gradioApp() .querySelectorAll( "#" + tabname + "_extra_tabs .extra-networks-controls-div > div", ) .forEach(function (elem) { let targetId = tabname_full + "_controls"; elem.style.display = elem.id == targetId ? "" : "none"; }); } function extraNetworksUnrelatedTabSelected(tabname) { // called from python when user selects an unrelated tab (generate) extraNetworksMovePromptToTab(tabname, "", false, false); extraNetworksShowControlsForPage(tabname, null); } function extraNetworksTabSelected( tabname, id, showPrompt, showNegativePrompt, tabname_full, ) { // called from python when user selects an extra networks tab extraNetworksMovePromptToTab(tabname, id, showPrompt, showNegativePrompt); extraNetworksShowControlsForPage(tabname, tabname_full); } function applyExtraNetworkFilter(tabname_full) { let doFilter = function () { let applyFunction = extraNetworksApplyFilter[tabname_full]; if (applyFunction) { applyFunction(true); } }; setTimeout(doFilter, 1); } function applyExtraNetworkSort(tabname_full) { let doSort = function () { extraNetworksApplySort[tabname_full](true); }; setTimeout(doSort, 1); } let extraNetworksApplyFilter = {}; let extraNetworksApplySort = {}; let activePromptTextarea = {}; function setupExtraNetworks() { setupExtraNetworksForTab("txt2img"); setupExtraNetworksForTab("img2img"); } let re_extranet = /<([^:^>]+:[^:]+):[\d.]+>(.*)/; let re_extranet_g = /<([^:^>]+:[^:]+):[\d.]+>/g; let re_extranet_neg = /\(([^:^>]+:[\d.]+)\)/; let re_extranet_g_neg = /\(([^:^>]+:[\d.]+)\)/g; function tryToRemoveExtraNetworkFromPrompt(textarea, text, isNeg) { let m = text.match(isNeg ? re_extranet_neg : re_extranet); let replaced = false; let newTextareaText; let extraTextBeforeNet = opts.extra_networks_add_text_separator; if (m) { let extraTextAfterNet = m[2]; let partToSearch = m[1]; let foundAtPosition = -1; newTextareaText = textarea.value.replaceAll( isNeg ? re_extranet_g_neg : re_extranet_g, function (found, net, pos) { m = found.match(isNeg ? re_extranet_neg : re_extranet); if (m[1] == partToSearch) { replaced = true; foundAtPosition = pos; return ""; } return found; }, ); if (foundAtPosition >= 0) { if ( extraTextAfterNet && newTextareaText.substr(foundAtPosition, extraTextAfterNet.length) == extraTextAfterNet ) { newTextareaText = newTextareaText.substr(0, foundAtPosition) + newTextareaText.substr(foundAtPosition + extraTextAfterNet.length); } if ( newTextareaText.substr( foundAtPosition - extraTextBeforeNet.length, extraTextBeforeNet.length, ) == extraTextBeforeNet ) { newTextareaText = newTextareaText.substr( 0, foundAtPosition - extraTextBeforeNet.length, ) + newTextareaText.substr(foundAtPosition); } } } else { newTextareaText = textarea.value.replaceAll( new RegExp(`((?:${extraTextBeforeNet})?${text})`, "g"), "", ); replaced = newTextareaText != textarea.value; } if (replaced) { textarea.value = newTextareaText; return true; } return false; } function updatePromptArea(text, textArea, isNeg) { if (!tryToRemoveExtraNetworkFromPrompt(textArea, text, isNeg)) { textArea.value = textArea.value + opts.extra_networks_add_text_separator + text; } updateInput(textArea); } function cardClicked( tabname, textToAdd, textToAddNegative, allowNegativePrompt, ) { if (textToAddNegative.length > 0) { updatePromptArea( textToAdd, gradioApp().querySelector("#" + tabname + "_prompt > label > textarea"), ); updatePromptArea( textToAddNegative, gradioApp().querySelector( "#" + tabname + "_neg_prompt > label > textarea", ), true, ); } else { let textarea = allowNegativePrompt ? activePromptTextarea[tabname] : gradioApp().querySelector("#" + tabname + "_prompt > label > textarea"); updatePromptArea(textToAdd, textarea); } } function saveCardPreview(event, tabname, filename) { let textarea = gradioApp().querySelector( "#" + tabname + "_preview_filename > label > textarea", ); let button = gradioApp().getElementById(tabname + "_save_preview"); textarea.value = filename; updateInput(textarea); button.click(); event.stopPropagation(); event.preventDefault(); } function extraNetworksSearchButton(tabname, extra_networks_tabname, event) { let searchTextarea = gradioApp().querySelector( "#" + tabname + "_" + extra_networks_tabname + "_extra_search", ); let button = event.target; let 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. * * @param event The generated event. * @param btn The clicked `tree-list-item` button. * @param tabname The name of the active tab in the sd webui. Ex: txt2img, img2img, etc. * @param extra_networks_tabname The id of the active extraNetworks tab. Ex: lora, checkpoints, etc. */ // NOTE: Currently unused. return; } function extraNetworksTreeProcessDirectoryClick( event, btn, tabname, extra_networks_tabname, ) { /** * Processes `onclick` events when user clicks on directories in tree. * * Here is how the tree reacts to clicks for various states: * unselected unopened directory: Directory is selected and expanded. * unselected opened directory: Directory is selected. * selected opened directory: Directory is collapsed and deselected. * chevron is clicked: Directory is expanded or collapsed. Selected state unchanged. * * @param event The generated event. * @param btn The clicked `tree-list-item` button. * @param tabname The name of the active tab in the sd webui. Ex: txt2img, img2img, etc. * @param extra_networks_tabname The id of the active extraNetworks tab. Ex: lora, checkpoints, etc. */ let ul = btn.nextElementSibling; // This is the actual target that the user clicked on within the target button. // We use this to detect if the chevron was clicked. let true_targ = event.target; function _expand_or_collapse(_ul, _btn) { // Expands