2024-03-26 18:11:01 +00:00
|
|
|
// Prevent eslint errors on functions defined in other files.
|
|
|
|
/*global
|
2024-04-30 18:29:06 +00:00
|
|
|
selectCheckpoint,
|
2024-03-26 18:11:01 +00:00
|
|
|
ExtraNetworksClusterizeTreeList,
|
|
|
|
ExtraNetworksClusterizeCardsList,
|
2024-04-14 18:52:17 +00:00
|
|
|
waitForElement,
|
2024-04-22 22:25:48 +00:00
|
|
|
isString,
|
2024-04-14 18:52:17 +00:00
|
|
|
isElement,
|
|
|
|
isElementThrowError,
|
2024-04-26 18:00:00 +00:00
|
|
|
fetchWithRetryAndBackoff,
|
2024-04-14 18:52:17 +00:00
|
|
|
isElementLogError,
|
|
|
|
isNumber,
|
|
|
|
waitForKeyInObject,
|
|
|
|
isNullOrUndefined,
|
|
|
|
debounce,
|
|
|
|
waitForBool,
|
|
|
|
copyToClipboard,
|
2024-03-26 18:11:01 +00:00
|
|
|
*/
|
|
|
|
/*eslint no-undef: "error"*/
|
|
|
|
|
2024-03-25 14:26:29 +00:00
|
|
|
const SEARCH_INPUT_DEBOUNCE_TIME_MS = 250;
|
2024-04-16 16:43:30 +00:00
|
|
|
const EXTRA_NETWORKS_REFRESH_INTERNAL_DEBOUNCE_TIMEOUT_MS = 200;
|
2024-04-26 18:17:43 +00:00
|
|
|
const EXTRA_NETWORKS_WAIT_FOR_PAGE_READY_TIMEOUT_MS = 60000;
|
|
|
|
const EXTRA_NETWORKS_INIT_DATA_TIMEOUT_MS = 60000;
|
|
|
|
const EXTRA_NETWORKS_FETCH_DATA_TIMEOUT_MS = 60000;
|
2024-03-25 14:26:29 +00:00
|
|
|
|
2024-03-13 21:11:44 +00:00
|
|
|
const re_extranet = /<([^:^>]+:[^:]+):[\d.]+>(.*)/;
|
|
|
|
const re_extranet_g = /<([^:^>]+:[^:]+):[\d.]+>/g;
|
|
|
|
const re_extranet_neg = /\(([^:^>]+:[\d.]+)\)/;
|
|
|
|
const re_extranet_g_neg = /\(([^:^>]+:[\d.]+)\)/g;
|
2024-03-20 18:01:38 +00:00
|
|
|
var globalPopup = null;
|
|
|
|
var globalPopupInner = null;
|
|
|
|
const storedPopupIds = {};
|
|
|
|
const extraPageUserMetadataEditors = {};
|
2024-04-12 16:40:20 +00:00
|
|
|
const extra_networks_tabs = {};
|
2024-04-16 16:43:30 +00:00
|
|
|
var extra_networks_refresh_internal_debounce_timer;
|
|
|
|
|
2024-04-14 18:43:31 +00:00
|
|
|
/** Boolean flags used along with utils.js::waitForBool(). */
|
|
|
|
// Set true when we first load the UI options.
|
2024-04-26 18:16:31 +00:00
|
|
|
const initialUiOptionsLoaded = {state: false};
|
2024-03-13 21:11:44 +00:00
|
|
|
|
2024-04-17 19:24:45 +00:00
|
|
|
const _debounce = (handler, timeout_ms) => {
|
|
|
|
/** Debounces a function call.
|
|
|
|
*
|
|
|
|
* NOTE: This will NOT work if called from within a class.
|
|
|
|
* It will drop `this` from scope.
|
|
|
|
*
|
|
|
|
* Repeated calls to the debounce handler will not call the handler until there are
|
|
|
|
* no new calls to the debounce handler for timeout_ms time.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* function add(x, y) { return x + y; }
|
|
|
|
* let debounce_handler = debounce(add, 5000);
|
|
|
|
* let res;
|
|
|
|
* for (let i = 0; i < 10; i++) {
|
|
|
|
* res = debounce_handler(i, 100);
|
|
|
|
* }
|
|
|
|
* console.log("Result:", res);
|
|
|
|
*
|
|
|
|
* This example will print "Result: 109".
|
|
|
|
*/
|
|
|
|
let timer = null;
|
|
|
|
return (...args) => {
|
|
|
|
clearTimeout(timer);
|
|
|
|
timer = setTimeout(() => handler(...args), timeout_ms);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2024-04-26 18:00:00 +00:00
|
|
|
class ExtraNetworksError extends Error {
|
|
|
|
constructor(...args) {
|
|
|
|
super(...args);
|
|
|
|
this.name = this.constructor.name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class ExtraNetworksPageReadyError extends Error {
|
2024-04-26 18:16:31 +00:00
|
|
|
constructor(...args) {
|
|
|
|
super(...args);
|
|
|
|
}
|
2024-04-26 18:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class ExtraNetworksDataReadyError extends Error {
|
2024-04-26 18:16:31 +00:00
|
|
|
constructor(...args) {
|
|
|
|
super(...args);
|
|
|
|
}
|
2024-04-26 18:00:00 +00:00
|
|
|
}
|
|
|
|
|
2024-04-12 16:40:20 +00:00
|
|
|
class ExtraNetworksTab {
|
2024-04-18 17:09:58 +00:00
|
|
|
tabname;
|
|
|
|
extra_networks_tabname;
|
|
|
|
tabname_full; // {tabname}_{extra_networks_tabname}
|
2024-04-12 16:40:20 +00:00
|
|
|
tree_list;
|
|
|
|
cards_list;
|
|
|
|
container_elem;
|
|
|
|
controls_elem;
|
|
|
|
txt_search_elem;
|
|
|
|
prompt_container_elem;
|
|
|
|
prompts_elem;
|
|
|
|
prompt_row_elem;
|
|
|
|
neg_prompt_row_elem;
|
|
|
|
txt_prompt_elem;
|
|
|
|
txt_neg_prompt_elem;
|
|
|
|
active_prompt_elem;
|
2024-04-14 18:43:31 +00:00
|
|
|
sort_mode_str = "";
|
|
|
|
sort_dir_str = "";
|
|
|
|
filter_str = "";
|
2024-04-22 22:25:48 +00:00
|
|
|
directory_filter_str = "";
|
2024-04-29 21:51:16 +00:00
|
|
|
directory_filter_recurse = false;
|
2024-04-12 16:40:20 +00:00
|
|
|
show_prompt = true;
|
|
|
|
show_neg_prompt = true;
|
|
|
|
compact_prompt_en = false;
|
2024-04-16 16:43:30 +00:00
|
|
|
refresh_in_progress = false;
|
2024-04-29 21:51:16 +00:00
|
|
|
dirs_view_en = false;
|
|
|
|
tree_view_en = false;
|
2024-05-03 17:13:25 +00:00
|
|
|
cards_list_splash_state = null;
|
|
|
|
tree_list_splash_state = null;
|
2024-04-26 18:16:31 +00:00
|
|
|
constructor({tabname, extra_networks_tabname}) {
|
2024-04-12 16:40:20 +00:00
|
|
|
this.tabname = tabname;
|
|
|
|
this.extra_networks_tabname = extra_networks_tabname;
|
|
|
|
this.tabname_full = `${tabname}_${extra_networks_tabname}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
async setup(pane, controls_div) {
|
|
|
|
this.container_elem = pane;
|
|
|
|
|
|
|
|
// get page elements
|
|
|
|
await Promise.all([
|
|
|
|
waitForElement(`#${this.tabname_full}_pane .extra-network-controls`).then(elem => this.controls_elem = elem),
|
|
|
|
waitForElement(`#${this.tabname}_prompt_container`).then(elem => this.prompt_container_elem = elem),
|
|
|
|
waitForElement(`#${this.tabname_full}_prompts`).then(elem => this.prompts_elem = elem),
|
|
|
|
waitForElement(`#${this.tabname}_prompt_row`).then(elem => this.prompt_row_elem = elem),
|
|
|
|
waitForElement(`#${this.tabname}_neg_prompt_row`).then(elem => this.neg_prompt_row_elem = elem),
|
|
|
|
waitForElement(`#${this.tabname_full}_tree_list_scroll_area`),
|
|
|
|
waitForElement(`#${this.tabname_full}_tree_list_content_area`),
|
|
|
|
waitForElement(`#${this.tabname_full}_cards_list_scroll_area`),
|
|
|
|
waitForElement(`#${this.tabname_full}_cards_list_content_area`),
|
|
|
|
]);
|
|
|
|
|
2024-05-03 17:13:25 +00:00
|
|
|
this.updateSplashState({cards_list_state: "loading", tree_list_state: "loading"});
|
|
|
|
|
2024-04-12 16:40:20 +00:00
|
|
|
this.txt_search_elem = this.controls_elem.querySelector(".extra-network-control--search-text");
|
2024-04-29 21:51:16 +00:00
|
|
|
this.dirs_view_en = "selected" in this.controls_elem.querySelector(
|
|
|
|
".extra-network-control--dirs-view"
|
|
|
|
).dataset;
|
|
|
|
this.tree_view_en = "selected" in this.controls_elem.querySelector(
|
|
|
|
".extra-network-control--tree-view"
|
|
|
|
).dataset;
|
2024-04-12 16:40:20 +00:00
|
|
|
|
|
|
|
// determine whether compact prompt mode is enabled.
|
|
|
|
// cannot await this since it may not exist on page depending on user setting.
|
|
|
|
this.compact_prompt_en = isElement(gradioApp().querySelector(".toprow-compact-tools"));
|
|
|
|
|
|
|
|
// setup this tab's controls
|
|
|
|
this.controls_elem.id = `${this.tabname_full}_controls`;
|
|
|
|
controls_div.insertBefore(this.controls_elem, null);
|
|
|
|
|
2024-05-03 18:41:27 +00:00
|
|
|
const error_btn = this.controls_elem.querySelector(
|
|
|
|
".extra-network-control--refresh"
|
|
|
|
).cloneNode(true);
|
|
|
|
error_btn.id = null;
|
|
|
|
error_btn.dataset.tabnameFull = this.tabname_full;
|
|
|
|
this.clusterize_error_html = "<div>Data Error.<br/>" +
|
|
|
|
"Please refresh tab.<br>" +
|
|
|
|
`${error_btn.outerHTML}</div>`;
|
|
|
|
|
2024-04-16 16:43:30 +00:00
|
|
|
await Promise.all([this.setupTreeList(), this.setupCardsList()]);
|
2024-04-12 16:40:20 +00:00
|
|
|
|
2024-05-02 17:27:03 +00:00
|
|
|
const btn_dirs_view = this.controls_elem.querySelector(".extra-network-control--dirs-view");
|
|
|
|
const btn_tree_view = this.controls_elem.querySelector(".extra-network-control--tree-view");
|
|
|
|
const div_dirs = this.container_elem.querySelector(".extra-network-content--dirs-view");
|
|
|
|
// We actually want to select the tree view's column in the resize-handle-row.
|
|
|
|
// This is what we actually show/hide, not the inner elements.
|
|
|
|
const div_tree = this.tree_list.scroll_elem.closest(".resize-handle-col");
|
|
|
|
|
|
|
|
this.dirs_view_en = "selected" in btn_dirs_view.dataset;
|
|
|
|
this.tree_view_en = "selected" in btn_tree_view.dataset;
|
|
|
|
// Remove "hidden" class if button is enabled, otherwise add it.
|
|
|
|
div_dirs.classList.toggle("hidden", !this.dirs_view_en);
|
|
|
|
div_tree.classList.toggle("hidden", !this.tree_view_en);
|
|
|
|
|
|
|
|
// Apply the current resize handle classes.
|
|
|
|
const resize_handle_row = this.tree_list.scroll_elem.closest(".resize-handle-row");
|
|
|
|
resize_handle_row.classList.toggle("resize-handle-hidden", div_tree.classList.contains("hidden"));
|
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
const sort_mode_elem = this.controls_elem.querySelector(".extra-network-control--sort-mode[data-selected]");
|
2024-04-14 18:43:31 +00:00
|
|
|
isElementThrowError(sort_mode_elem);
|
|
|
|
const sort_dir_elem = this.controls_elem.querySelector(".extra-network-control--sort-dir");
|
|
|
|
isElementThrowError(sort_dir_elem);
|
|
|
|
|
|
|
|
this.setSortMode(sort_mode_elem.dataset.sortMode);
|
|
|
|
this.setSortDir(sort_dir_elem.dataset.sortDir);
|
2024-04-22 22:25:48 +00:00
|
|
|
this.applyDirectoryFilter();
|
|
|
|
this.applyFilter();
|
2024-04-14 18:43:31 +00:00
|
|
|
|
2024-04-12 16:40:20 +00:00
|
|
|
this.registerPrompt();
|
|
|
|
|
|
|
|
if (this.container_elem.style.display === "none") {
|
|
|
|
this.hideControls();
|
|
|
|
} else {
|
|
|
|
this.showControls();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-14 18:43:31 +00:00
|
|
|
destroy() {
|
|
|
|
this.unload();
|
|
|
|
this.tree_list.destroy();
|
|
|
|
this.cards_list.destroy();
|
|
|
|
this.tree_list = null;
|
|
|
|
this.cards_list = null;
|
|
|
|
this.container_elem = null;
|
|
|
|
this.controls_elem = null;
|
|
|
|
this.txt_search_elem = null;
|
|
|
|
this.prompt_container_elem = null;
|
|
|
|
this.prompts_elem = null;
|
|
|
|
this.prompt_row_elem = null;
|
|
|
|
this.neg_prompt_row_elem = null;
|
|
|
|
this.txt_prompt_elem = null;
|
|
|
|
this.txt_neg_prompt_elem = null;
|
|
|
|
this.active_prompt_elem = null;
|
2024-04-16 16:43:30 +00:00
|
|
|
this.refresh_in_progress = false;
|
2024-04-29 21:51:16 +00:00
|
|
|
this.tree_view_en = false;
|
|
|
|
this.dirs_view_en = false;
|
2024-04-14 18:43:31 +00:00
|
|
|
}
|
|
|
|
|
2024-04-12 16:40:20 +00:00
|
|
|
async registerPrompt() {
|
|
|
|
await Promise.all([
|
|
|
|
waitForElement(`#${this.tabname}_prompt > label > textarea`).then(elem => this.txt_prompt_elem = elem),
|
|
|
|
waitForElement(`#${this.tabname}_neg_prompt > label > textarea`).then(elem => this.txt_neg_prompt_elem = elem),
|
|
|
|
]);
|
|
|
|
this.active_prompt_elem = this.txt_prompt_elem;
|
|
|
|
this.txt_prompt_elem.addEventListener("focus", () => this.active_prompt_elem = this.txt_prompt_elem);
|
|
|
|
this.txt_neg_prompt_elem.addEventListener("focus", () => this.active_prompt_elem = this.txt_neg_prompt_elem);
|
|
|
|
}
|
|
|
|
|
|
|
|
async setupTreeList() {
|
|
|
|
if (this.tree_list instanceof ExtraNetworksClusterizeTreeList) {
|
|
|
|
this.tree_list.destroy();
|
|
|
|
}
|
|
|
|
this.tree_list = new ExtraNetworksClusterizeTreeList({
|
|
|
|
tabname: this.tabname,
|
|
|
|
extra_networks_tabname: this.extra_networks_tabname,
|
|
|
|
scrollId: `${this.tabname_full}_tree_list_scroll_area`,
|
|
|
|
contentId: `${this.tabname_full}_tree_list_content_area`,
|
|
|
|
tag: "button",
|
2024-05-03 18:41:27 +00:00
|
|
|
error_html: this.clusterize_error_html,
|
2024-04-12 16:40:20 +00:00
|
|
|
callbacks: {
|
2024-04-14 18:43:31 +00:00
|
|
|
initData: this.onInitTreeData.bind(this),
|
|
|
|
fetchData: this.onFetchTreeData.bind(this),
|
2024-04-12 16:40:20 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
await this.tree_list.setup();
|
|
|
|
}
|
|
|
|
|
|
|
|
async setupCardsList() {
|
|
|
|
if (this.cards_list instanceof ExtraNetworksClusterizeCardsList) {
|
|
|
|
this.cards_list.destroy();
|
|
|
|
}
|
|
|
|
this.cards_list = new ExtraNetworksClusterizeCardsList({
|
|
|
|
tabname: this.tabname,
|
|
|
|
extra_networks_tabname: this.extra_networks_tabname,
|
|
|
|
scrollId: `${this.tabname_full}_cards_list_scroll_area`,
|
|
|
|
contentId: `${this.tabname_full}_cards_list_content_area`,
|
|
|
|
tag: "div",
|
2024-05-03 18:41:27 +00:00
|
|
|
no_data_html: "No data matching filter.",
|
|
|
|
error_html: this.clusterize_error_html,
|
2024-04-12 16:40:20 +00:00
|
|
|
callbacks: {
|
2024-04-14 18:43:31 +00:00
|
|
|
initData: this.onInitCardsData.bind(this),
|
|
|
|
fetchData: this.onFetchCardsData.bind(this),
|
2024-04-12 16:40:20 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
await this.cards_list.setup();
|
|
|
|
}
|
|
|
|
|
2024-04-14 18:43:31 +00:00
|
|
|
setSortMode(sort_mode_str) {
|
|
|
|
this.sort_mode_str = sort_mode_str;
|
|
|
|
this.cards_list.setSortMode(this.sort_mode_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
setSortDir(sort_dir_str) {
|
|
|
|
this.sort_dir_str = sort_dir_str;
|
|
|
|
this.cards_list.setSortDir(this.sort_dir_str);
|
|
|
|
}
|
|
|
|
|
2024-04-22 22:25:48 +00:00
|
|
|
setFilterStr(filter_str) {
|
2024-04-14 18:43:31 +00:00
|
|
|
this.filter_str = filter_str;
|
2024-04-22 22:25:48 +00:00
|
|
|
this.cards_list.setFilterStr(this.filter_str);
|
|
|
|
}
|
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
setDirectoryFilterStr(filter_str, recurse) {
|
2024-04-22 22:25:48 +00:00
|
|
|
this.directory_filter_str = filter_str;
|
2024-04-29 21:51:16 +00:00
|
|
|
this.directory_filter_recurse = recurse;
|
|
|
|
this.cards_list.setDirectoryFilterStr(this.directory_filter_str, this.directory_filter_recurse);
|
2024-04-14 18:43:31 +00:00
|
|
|
}
|
|
|
|
|
2024-04-14 18:52:17 +00:00
|
|
|
movePrompt(show_prompt = true, show_neg_prompt = true) {
|
2024-04-12 16:40:20 +00:00
|
|
|
// This function only applies when compact prompt mode is enabled.
|
|
|
|
if (!this.compact_prompt_en) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (show_neg_prompt) {
|
|
|
|
this.prompts_elem.insertBefore(this.neg_prompt_row_elem, this.prompts_elem.firstChild);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (show_prompt) {
|
|
|
|
this.prompts_elem.insertBefore(this.prompt_row_elem, this.prompts_elem.firstChild);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.prompts_elem.classList.toggle("extra-page-prompts-active", show_neg_prompt || show_prompt);
|
|
|
|
}
|
|
|
|
|
2024-04-22 19:02:00 +00:00
|
|
|
refreshSingleCard(elem) {
|
2024-04-14 18:43:31 +00:00
|
|
|
requestGet(
|
2024-04-12 16:40:20 +00:00
|
|
|
"./sd_extra_networks/get-single-card",
|
|
|
|
{
|
|
|
|
tabname: this.tabname,
|
|
|
|
extra_networks_tabname: this.extra_networks_tabname,
|
2024-04-22 19:02:00 +00:00
|
|
|
name: elem.dataset.name,
|
|
|
|
div_id: elem.dataset.divId,
|
2024-04-12 16:40:20 +00:00
|
|
|
},
|
|
|
|
(data) => {
|
|
|
|
if (data && data.html) {
|
2024-04-29 21:51:16 +00:00
|
|
|
this.cards_list.updateHtml(elem, data.html);
|
2024-04-12 16:40:20 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
showControls() {
|
|
|
|
this.controls_elem.classList.remove("hidden");
|
|
|
|
}
|
|
|
|
|
|
|
|
hideControls() {
|
|
|
|
this.controls_elem.classList.add("hidden");
|
|
|
|
}
|
|
|
|
|
2024-05-03 17:13:25 +00:00
|
|
|
updateSplashState({cards_list_state, tree_list_state} = {}) {
|
|
|
|
const _handle_state = (state_str, loading_elem, no_data_elem) => {
|
|
|
|
switch (state_str) {
|
|
|
|
case "loading":
|
|
|
|
no_data_elem.classList.add("hidden");
|
|
|
|
loading_elem.classList.remove("hidden");
|
|
|
|
break;
|
|
|
|
case "no_data":
|
|
|
|
loading_elem.classList.add("hidden");
|
|
|
|
no_data_elem.classList.remove("hidden");
|
|
|
|
break;
|
|
|
|
case "show":
|
|
|
|
no_data_elem.classList.add("hidden");
|
|
|
|
loading_elem.classList.add("hidden");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-05-03 18:41:27 +00:00
|
|
|
let loading_elem;
|
|
|
|
let no_data_elem;
|
|
|
|
|
2024-05-03 17:13:25 +00:00
|
|
|
if (isString(cards_list_state)) {
|
|
|
|
this.cards_list_splash_state = cards_list_state;
|
2024-05-03 18:41:27 +00:00
|
|
|
loading_elem = document.getElementById(`${this.tabname_full}_cards_list_loading_splash`);
|
|
|
|
no_data_elem = document.getElementById(`${this.tabname_full}_cards_list_no_data_splash`);
|
|
|
|
_handle_state(cards_list_state, loading_elem, no_data_elem);
|
2024-05-03 17:13:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (isString(tree_list_state)) {
|
|
|
|
this.tree_list_splash_state = tree_list_state;
|
2024-05-03 18:41:27 +00:00
|
|
|
loading_elem = document.getElementById(`${this.tabname_full}_tree_list_loading_splash`);
|
|
|
|
no_data_elem = document.getElementById(`${this.tabname_full}_tree_list_no_data_splash`);
|
|
|
|
_handle_state(tree_list_state, loading_elem, no_data_elem);
|
2024-05-03 17:13:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-16 16:43:30 +00:00
|
|
|
async #refresh() {
|
2024-05-03 17:13:25 +00:00
|
|
|
this.updateSplashState({cards_list_state: "loading", tree_list_state: "loading"});
|
|
|
|
|
2024-04-26 18:00:00 +00:00
|
|
|
try {
|
|
|
|
await this.waitForServerPageReady();
|
|
|
|
} catch (error) {
|
|
|
|
console.error(`refresh error: ${error.message}`);
|
2024-05-03 17:13:25 +00:00
|
|
|
this.updateSplashState({cards_list_state: "no_data", tree_list_state: "no_data"});
|
2024-04-26 18:00:00 +00:00
|
|
|
return;
|
|
|
|
}
|
2024-04-12 16:40:20 +00:00
|
|
|
const btn_dirs_view = this.controls_elem.querySelector(".extra-network-control--dirs-view");
|
|
|
|
const btn_tree_view = this.controls_elem.querySelector(".extra-network-control--tree-view");
|
|
|
|
const div_dirs = this.container_elem.querySelector(".extra-network-content--dirs-view");
|
2024-04-14 18:43:31 +00:00
|
|
|
// We actually want to select the tree view's column in the resize-handle-row.
|
|
|
|
// This is what we actually show/hide, not the inner elements.
|
2024-04-17 23:23:38 +00:00
|
|
|
const div_tree = this.tree_list.scroll_elem.closest(".resize-handle-col");
|
2024-04-12 16:40:20 +00:00
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
this.dirs_view_en = "selected" in btn_dirs_view.dataset;
|
|
|
|
this.tree_view_en = "selected" in btn_tree_view.dataset;
|
2024-04-12 16:40:20 +00:00
|
|
|
// Remove "hidden" class if button is enabled, otherwise add it.
|
2024-04-29 21:51:16 +00:00
|
|
|
div_dirs.classList.toggle("hidden", !this.dirs_view_en);
|
|
|
|
div_tree.classList.toggle("hidden", !this.tree_view_en);
|
|
|
|
|
2024-04-17 23:23:38 +00:00
|
|
|
// Apply the current resize handle classes.
|
|
|
|
const resize_handle_row = this.tree_list.scroll_elem.closest(".resize-handle-row");
|
|
|
|
resize_handle_row.classList.toggle("resize-handle-hidden", div_tree.classList.contains("hidden"));
|
|
|
|
|
2024-04-12 16:40:20 +00:00
|
|
|
await Promise.all([this.setupTreeList(), this.setupCardsList()]);
|
2024-05-03 17:13:25 +00:00
|
|
|
this.tree_list.enable(this.tree_view_en);
|
2024-04-16 16:43:30 +00:00
|
|
|
this.cards_list.enable(true);
|
2024-04-12 16:40:20 +00:00
|
|
|
await Promise.all([this.tree_list.load(true), this.cards_list.load(true)]);
|
2024-04-14 18:43:31 +00:00
|
|
|
// apply the previous sort/filter options
|
|
|
|
this.setSortMode(this.sort_mode_str);
|
|
|
|
this.setSortDir(this.sort_dir_str);
|
2024-04-29 21:51:16 +00:00
|
|
|
this.applyDirectoryFilter(this.directory_filter_str, this.directory_filter_recurse);
|
2024-04-22 22:25:48 +00:00
|
|
|
this.applyFilter(this.filter_str);
|
2024-04-12 16:40:20 +00:00
|
|
|
}
|
|
|
|
|
2024-04-16 16:43:30 +00:00
|
|
|
async refresh() {
|
|
|
|
if (this.refresh_in_progress) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.refresh_in_progress = true;
|
|
|
|
await this.#refresh();
|
|
|
|
this.refresh_in_progress = false;
|
|
|
|
}
|
|
|
|
|
2024-04-12 16:40:20 +00:00
|
|
|
async load(show_prompt, show_neg_prompt) {
|
2024-04-14 18:52:17 +00:00
|
|
|
this.movePrompt(show_prompt, show_neg_prompt);
|
2024-05-03 17:13:25 +00:00
|
|
|
this.updateSplashState({
|
|
|
|
cards_list_state: this.cards_list_splash_state,
|
|
|
|
tree_list_state: this.tree_list_splash_state,
|
|
|
|
});
|
2024-04-12 16:40:20 +00:00
|
|
|
this.showControls();
|
2024-05-03 17:13:25 +00:00
|
|
|
this.tree_list.enable(this.tree_view_en);
|
2024-04-12 16:40:20 +00:00
|
|
|
this.cards_list.enable(true);
|
|
|
|
await Promise.all([this.tree_list.load(), this.cards_list.load()]);
|
|
|
|
}
|
|
|
|
|
|
|
|
unload() {
|
|
|
|
this.movePrompt(false, false);
|
|
|
|
this.hideControls();
|
|
|
|
this.tree_list.enable(false);
|
|
|
|
this.cards_list.enable(false);
|
|
|
|
}
|
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
applyDirectoryFilter(filter_str, recurse) {
|
|
|
|
filter_str = isString(filter_str) ? filter_str : "";
|
|
|
|
recurse = recurse === true || recurse === false ? recurse : false;
|
|
|
|
this.setDirectoryFilterStr(filter_str, recurse);
|
2024-04-22 22:25:48 +00:00
|
|
|
}
|
2024-04-12 16:40:20 +00:00
|
|
|
|
2024-04-22 22:25:48 +00:00
|
|
|
applyFilter(filter_str) {
|
|
|
|
filter_str = !isString(filter_str) ? this.txt_search_elem.value : filter_str;
|
|
|
|
this.setFilterStr(filter_str);
|
2024-04-12 16:40:20 +00:00
|
|
|
}
|
|
|
|
|
2024-04-26 18:00:00 +00:00
|
|
|
async waitForServerPageReady(timeout_ms) {
|
2024-04-16 15:22:12 +00:00
|
|
|
/** Waits for a page on the server to be ready.
|
|
|
|
*
|
|
|
|
* We need to wait for the page to be ready before we can fetch any data.
|
|
|
|
* It is possible to click on a tab before the server has any data ready for us.
|
|
|
|
* Since clicking on tabs triggers a data request, there will be an error from
|
|
|
|
* the server since the data isn't ready. This function allows us to wait for
|
|
|
|
* the server to tell us that it is ready for data requests.
|
|
|
|
*
|
|
|
|
* Args:
|
2024-04-18 17:09:58 +00:00
|
|
|
* max_attempts [int]: The max number of requests that will be attempted
|
2024-04-16 15:22:12 +00:00
|
|
|
* before giving up. If set to 0, will attempt forever.
|
|
|
|
*/
|
2024-04-26 18:00:00 +00:00
|
|
|
timeout_ms = timeout_ms || EXTRA_NETWORKS_WAIT_FOR_PAGE_READY_TIMEOUT_MS;
|
2024-04-26 18:16:31 +00:00
|
|
|
const response_handler = (response) => new Promise((resolve, reject) => {
|
2024-04-26 18:00:00 +00:00
|
|
|
if (!response.ok) {
|
|
|
|
return reject(response);
|
|
|
|
}
|
2024-04-26 18:16:31 +00:00
|
|
|
|
|
|
|
response.json().then(json => {
|
|
|
|
if (!json.ready) {
|
|
|
|
return reject(`page not ready: ${this.extra_networks_tabname}`);
|
|
|
|
}
|
|
|
|
return resolve(json);
|
|
|
|
});
|
2024-04-16 15:22:12 +00:00
|
|
|
});
|
2024-04-26 18:00:00 +00:00
|
|
|
|
|
|
|
const url = "./sd_extra_networks/page-is-ready";
|
2024-04-26 18:16:31 +00:00
|
|
|
const payload = {extra_networks_tabname: this.extra_networks_tabname};
|
|
|
|
const opts = {timeout_ms: timeout_ms, response_handler: response_handler};
|
2024-04-26 18:00:00 +00:00
|
|
|
return await fetchWithRetryAndBackoff(url, payload, opts);
|
2024-04-14 18:43:31 +00:00
|
|
|
}
|
|
|
|
|
2024-04-12 16:40:20 +00:00
|
|
|
async onInitCardsData() {
|
2024-05-03 17:13:25 +00:00
|
|
|
this.updateSplashState({cards_list_state: "loading"});
|
2024-04-16 15:22:12 +00:00
|
|
|
try {
|
|
|
|
await this.waitForServerPageReady();
|
|
|
|
} catch (error) {
|
2024-04-26 18:00:00 +00:00
|
|
|
console.error(`onInitCardsData error: ${error.message}`);
|
2024-05-03 17:13:25 +00:00
|
|
|
this.updateSplashState({cards_list_state: "no_data"});
|
2024-04-16 15:22:12 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2024-04-26 18:16:31 +00:00
|
|
|
const response_handler = (response) => new Promise((resolve, reject) => {
|
2024-04-26 18:00:00 +00:00
|
|
|
if (!response.ok) {
|
|
|
|
return reject(response);
|
|
|
|
}
|
2024-04-26 18:16:31 +00:00
|
|
|
response.json().then(json => {
|
|
|
|
if (!json.ready) {
|
|
|
|
return reject(`data not ready: ${this.extra_networks_tabname}`);
|
|
|
|
}
|
|
|
|
return resolve(json);
|
|
|
|
});
|
2024-04-26 18:00:00 +00:00
|
|
|
});
|
|
|
|
|
2024-04-16 15:22:12 +00:00
|
|
|
const url = "./sd_extra_networks/init-cards-data";
|
2024-04-26 18:16:31 +00:00
|
|
|
const payload = {tabname: this.tabname, extra_networks_tabname: this.extra_networks_tabname};
|
2024-04-26 18:00:00 +00:00
|
|
|
const timeout_ms = EXTRA_NETWORKS_INIT_DATA_TIMEOUT_MS;
|
2024-04-26 18:16:31 +00:00
|
|
|
const opts = {timeout_ms: timeout_ms, response_handler: response_handler};
|
2024-04-16 15:22:12 +00:00
|
|
|
try {
|
2024-04-26 18:00:00 +00:00
|
|
|
const response = await fetchWithRetryAndBackoff(url, payload, opts);
|
2024-05-03 17:13:25 +00:00
|
|
|
if (Object.keys(response.data).length === 0) {
|
|
|
|
this.updateSplashState({cards_list_state: "no_data"});
|
|
|
|
} else {
|
|
|
|
this.updateSplashState({cards_list_state: "show"});
|
|
|
|
}
|
2024-04-26 18:00:00 +00:00
|
|
|
return response.data;
|
2024-04-16 15:22:12 +00:00
|
|
|
} catch (error) {
|
2024-04-26 18:00:00 +00:00
|
|
|
console.error(`onInitCardsData error: ${error.message}`);
|
2024-05-03 17:13:25 +00:00
|
|
|
this.updateSplashState({cards_list_state: "no_data"});
|
2024-04-16 15:22:12 +00:00
|
|
|
return {};
|
|
|
|
}
|
2024-04-12 16:40:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async onInitTreeData() {
|
2024-05-03 17:13:25 +00:00
|
|
|
this.updateSplashState({tree_list_state: "loading"});
|
2024-04-16 15:22:12 +00:00
|
|
|
try {
|
|
|
|
await this.waitForServerPageReady();
|
|
|
|
} catch (error) {
|
2024-04-26 18:00:00 +00:00
|
|
|
console.error(`onInitTreeData error: ${error.message}`);
|
2024-05-03 17:13:25 +00:00
|
|
|
this.updateSplashState({tree_list_state: "no_data"});
|
2024-04-16 15:22:12 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2024-04-26 18:16:31 +00:00
|
|
|
const response_handler = (response) => new Promise((resolve, reject) => {
|
2024-04-26 18:00:00 +00:00
|
|
|
if (!response.ok) {
|
|
|
|
return reject(response);
|
|
|
|
}
|
2024-04-26 18:16:31 +00:00
|
|
|
response.json().then(json => {
|
|
|
|
if (!json.ready) {
|
|
|
|
return reject(`data not ready: ${this.extra_networks_tabname}`);
|
|
|
|
}
|
|
|
|
return resolve(json);
|
|
|
|
});
|
2024-04-26 18:00:00 +00:00
|
|
|
});
|
|
|
|
|
2024-04-16 15:22:12 +00:00
|
|
|
const url = "./sd_extra_networks/init-tree-data";
|
2024-04-26 18:16:31 +00:00
|
|
|
const payload = {tabname: this.tabname, extra_networks_tabname: this.extra_networks_tabname};
|
2024-04-26 18:00:00 +00:00
|
|
|
const timeout_ms = EXTRA_NETWORKS_INIT_DATA_TIMEOUT_MS;
|
2024-04-26 18:16:31 +00:00
|
|
|
const opts = {timeout_ms: timeout_ms, response_handler: response_handler};
|
2024-04-16 15:22:12 +00:00
|
|
|
try {
|
2024-04-26 18:00:00 +00:00
|
|
|
const response = await fetchWithRetryAndBackoff(url, payload, opts);
|
2024-05-03 17:13:25 +00:00
|
|
|
if (Object.keys(response.data).length === 0) {
|
|
|
|
this.updateSplashState({tree_list_state: "no_data"});
|
|
|
|
} else {
|
|
|
|
this.updateSplashState({tree_list_state: "show"});
|
|
|
|
}
|
2024-04-26 18:00:00 +00:00
|
|
|
return response.data;
|
2024-04-16 15:22:12 +00:00
|
|
|
} catch (error) {
|
2024-04-26 18:00:00 +00:00
|
|
|
console.error(`onInitTreeData error: ${error.message}`);
|
2024-05-03 17:13:25 +00:00
|
|
|
this.updateSplashState({tree_list_state: "no_data"});
|
2024-04-16 15:22:12 +00:00
|
|
|
return {};
|
|
|
|
}
|
2024-04-12 16:40:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async onFetchCardsData(div_ids) {
|
2024-04-16 15:22:12 +00:00
|
|
|
const url = "./sd_extra_networks/fetch-cards-data";
|
2024-04-26 18:16:31 +00:00
|
|
|
const payload = {extra_networks_tabname: this.extra_networks_tabname, div_ids: div_ids};
|
2024-04-26 18:00:00 +00:00
|
|
|
const timeout_ms = EXTRA_NETWORKS_FETCH_DATA_TIMEOUT_MS;
|
2024-04-26 18:16:31 +00:00
|
|
|
const opts = {timeout_ms: timeout_ms};
|
2024-04-16 15:22:12 +00:00
|
|
|
try {
|
2024-04-26 18:00:00 +00:00
|
|
|
const response = await fetchWithRetryAndBackoff(url, payload, opts);
|
|
|
|
if (response.missing_div_ids.length) {
|
|
|
|
console.warn(`Failed to fetch multiple div_ids: ${response.missing_div_ids}`);
|
2024-04-19 20:11:47 +00:00
|
|
|
}
|
2024-05-03 18:41:27 +00:00
|
|
|
if (Object.keys(response.data).length === 0) {
|
|
|
|
this.updateSplashState({cards_list_state: "no_data"});
|
|
|
|
} else {
|
|
|
|
this.updateSplashState({cards_list_state: "show"});
|
|
|
|
}
|
2024-04-26 18:00:00 +00:00
|
|
|
return response.data;
|
2024-04-16 15:22:12 +00:00
|
|
|
} catch (error) {
|
2024-04-26 18:00:00 +00:00
|
|
|
console.error(`onFetchCardsData error: ${error.message}`);
|
2024-05-03 18:41:27 +00:00
|
|
|
this.updateSplashState({cards_list_state: "no_data"});
|
2024-04-16 15:22:12 +00:00
|
|
|
return {};
|
|
|
|
}
|
2024-04-12 16:40:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async onFetchTreeData(div_ids) {
|
2024-04-16 15:22:12 +00:00
|
|
|
const url = "./sd_extra_networks/fetch-tree-data";
|
2024-04-26 18:16:31 +00:00
|
|
|
const payload = {extra_networks_tabname: this.extra_networks_tabname, div_ids: div_ids};
|
2024-04-26 18:00:00 +00:00
|
|
|
const timeout_ms = EXTRA_NETWORKS_FETCH_DATA_TIMEOUT_MS;
|
2024-04-26 18:16:31 +00:00
|
|
|
const opts = {timeout_ms: timeout_ms};
|
2024-04-16 15:22:12 +00:00
|
|
|
try {
|
2024-04-26 18:00:00 +00:00
|
|
|
const response = await fetchWithRetryAndBackoff(url, payload, opts);
|
|
|
|
if (response.missing_div_ids.length) {
|
|
|
|
console.warn(`Failed to fetch multiple div_ids: ${response.missing_div_ids}`);
|
2024-04-19 20:11:47 +00:00
|
|
|
}
|
2024-05-03 18:41:27 +00:00
|
|
|
if (Object.keys(response.data).length === 0) {
|
|
|
|
this.updateSplashState({tree_list_state: "no_data"});
|
|
|
|
} else {
|
|
|
|
this.updateSplashState({tree_list_state: "show"});
|
|
|
|
}
|
2024-04-26 18:00:00 +00:00
|
|
|
return response.data;
|
2024-04-16 15:22:12 +00:00
|
|
|
} catch (error) {
|
2024-04-26 18:00:00 +00:00
|
|
|
console.error(`onFetchTreeData error: ${error.message}`);
|
2024-05-03 18:41:27 +00:00
|
|
|
this.updateSplashState({tree_list_state: "no_data"});
|
2024-04-16 15:22:12 +00:00
|
|
|
return {};
|
|
|
|
}
|
2024-04-12 16:40:20 +00:00
|
|
|
}
|
|
|
|
|
2024-04-22 22:25:48 +00:00
|
|
|
updateSearch(text) {
|
2024-04-12 16:40:20 +00:00
|
|
|
this.txt_search_elem.value = text;
|
|
|
|
updateInput(this.txt_search_elem);
|
2024-04-22 22:25:48 +00:00
|
|
|
this.applyFilter(this.txt_search_elem.value);
|
2024-04-12 16:40:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
autoSetTreeWidth() {
|
|
|
|
const row = this.container_elem.querySelector(".resize-handle-row");
|
|
|
|
if (!isElementLogError(row)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const left_col = row.firstElementChild;
|
|
|
|
if (!isElementLogError(left_col)) {
|
|
|
|
return;
|
|
|
|
}
|
2024-04-14 18:52:17 +00:00
|
|
|
|
2024-04-12 16:40:20 +00:00
|
|
|
// If the left column is hidden then we don't want to do anything.
|
|
|
|
if (left_col.classList.contains("hidden")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const pad = parseFloat(row.style.gridTemplateColumns.split(" ")[1]);
|
|
|
|
const min_left_col_width = parseFloat(left_col.style.flexBasis.slice(0, -2));
|
|
|
|
// We know that the tree list is the left column. That is the only one we want to resize.
|
|
|
|
let max_width = this.tree_list.getMaxRowWidth();
|
|
|
|
if (!isNumber(max_width)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Add the resize handle's padding to the result and default to minLeftColWidth if necessary.
|
|
|
|
max_width = Math.max(max_width + pad, min_left_col_width);
|
|
|
|
|
|
|
|
// Mimicks resizeHandle.js::setLeftColGridTemplate().
|
|
|
|
row.style.gridTemplateColumns = `${max_width}px ${pad}px 1fr`;
|
|
|
|
}
|
2024-04-22 22:25:48 +00:00
|
|
|
|
2024-04-30 18:29:06 +00:00
|
|
|
async setDirectoryButtons({source_elem, source_selector, source_class, reset_all} = {}) {
|
2024-04-29 21:51:16 +00:00
|
|
|
// At least one argument must be specified.
|
2024-04-30 18:29:06 +00:00
|
|
|
if (isNullOrUndefined(source_elem) &&
|
|
|
|
isNullOrUndefined(source_selector) &&
|
|
|
|
isNullOrUndefined(source_class) &&
|
|
|
|
isNullOrUndefined(reset_all)) {
|
|
|
|
console.error("At least one argument must be specified.");
|
2024-04-29 21:51:16 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-04-22 22:25:48 +00:00
|
|
|
// Checks if an element exists and is visible on the page.
|
|
|
|
const _exists = (elem) => {
|
|
|
|
return isElement(elem) && !isNullOrUndefined(elem.offsetParent);
|
|
|
|
};
|
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
// source_elem is specified but invalid.
|
|
|
|
if (!isNullOrUndefined(source_elem) && !_exists(source_elem)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-04-22 22:25:48 +00:00
|
|
|
// Removes `data-selected` attribute from all tree/dirs buttons.
|
2024-04-30 18:29:06 +00:00
|
|
|
const _reset_all_buttons = async({excluded_elems} = {}) => {
|
2024-04-29 21:51:16 +00:00
|
|
|
const elems = this.container_elem.querySelectorAll(
|
|
|
|
".extra-network-dirs-view-button, .tree-list-item"
|
|
|
|
);
|
|
|
|
for (const elem of elems) {
|
|
|
|
if (Array.isArray(excluded_elems) && excluded_elems.includes(elem)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const prev = elem.outerHTML;
|
2024-04-22 22:25:48 +00:00
|
|
|
delete elem.dataset.selected;
|
2024-04-29 21:51:16 +00:00
|
|
|
delete elem.dataset.recurse;
|
|
|
|
elem.classList.remove("short-pressed");
|
|
|
|
elem.classList.remove("long-pressed");
|
|
|
|
if (prev !== elem.outerHTML) {
|
|
|
|
this.tree_list.updateHtml(elem);
|
|
|
|
}
|
2024-04-30 18:29:06 +00:00
|
|
|
}
|
2024-04-29 21:51:16 +00:00
|
|
|
|
|
|
|
this.tree_list.content_elem.querySelectorAll(
|
|
|
|
".tree-list-item-indent [data-selected]"
|
|
|
|
).forEach(elem => {
|
2024-04-22 22:25:48 +00:00
|
|
|
delete elem.dataset.selected;
|
|
|
|
});
|
|
|
|
};
|
2024-04-29 21:51:16 +00:00
|
|
|
_reset_all_buttons.bind(this);
|
2024-04-22 22:25:48 +00:00
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
const _set_recursion_depth = (parent_id, state) => {
|
|
|
|
this.tree_list.content_elem.querySelectorAll(
|
|
|
|
`.tree-list-item-indent [data-parent-id="${parent_id}"]`
|
|
|
|
).forEach(elem => {
|
|
|
|
elem.toggleAttribute("data-selected", state);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
_set_recursion_depth.bind(this);
|
2024-04-22 22:25:48 +00:00
|
|
|
|
2024-04-30 18:27:13 +00:00
|
|
|
if (reset_all === true) {
|
|
|
|
_reset_all_buttons();
|
|
|
|
await this.tree_list.onRowSelected(); // no args deselects all.
|
|
|
|
this.applyDirectoryFilter();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
if (!_exists(source_elem) && isString(source_selector)) {
|
|
|
|
source_elem = this.container_elem.querySelector(source_selector);
|
2024-04-23 14:48:50 +00:00
|
|
|
}
|
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
if (!_exists(source_elem) && isString(source_class)) {
|
|
|
|
source_elem = this.container_elem.querySelector(`${source_class}[data-selected]`);
|
|
|
|
}
|
2024-04-22 22:25:48 +00:00
|
|
|
|
2024-04-30 18:27:13 +00:00
|
|
|
// try to find any selected buttons to use as a source.
|
|
|
|
if (!_exists(source_elem)) {
|
|
|
|
source_elem = this.container_elem.querySelector("[data-selected]");
|
|
|
|
}
|
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
// If we got here with no source elem, then we will take this to mean that
|
|
|
|
// we are deselecting all.
|
|
|
|
if (!_exists(source_elem)) {
|
|
|
|
_reset_all_buttons();
|
|
|
|
await this.tree_list.onRowSelected(); // no args deselects all.
|
2024-04-30 18:27:13 +00:00
|
|
|
this.applyDirectoryFilter();
|
2024-04-29 21:51:16 +00:00
|
|
|
return;
|
|
|
|
}
|
2024-04-22 22:25:48 +00:00
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
const source_is_tree = source_elem.classList.contains("tree-list-item");
|
|
|
|
const other_selector = source_is_tree ? ".extra-network-dirs-view-button" : ".tree-list-item";
|
2024-05-02 17:27:03 +00:00
|
|
|
// NOTE: We only use this escaped path in selectors since html handles these chars
|
|
|
|
// different from JS. We don't need to escape the path for any internal operations.
|
|
|
|
const data_path = String.raw`${source_elem.dataset.path.replaceAll("\\", "\\\\")}`;
|
2024-04-29 21:51:16 +00:00
|
|
|
const other_elem = document.querySelector(`${other_selector}[data-path="${data_path}"]`);
|
|
|
|
if (!_exists(other_elem)) {
|
|
|
|
// Can't reflect attributes since no matching element exists.
|
|
|
|
// This can happen when tree/dirs view is disabled or tree is collapsed.
|
|
|
|
_reset_all_buttons({excluded_elems: [source_elem]});
|
|
|
|
if (source_is_tree) {
|
|
|
|
await this.tree_list.onRowSelected(source_elem);
|
|
|
|
_set_recursion_depth(source_elem.dataset.divId, "recurse" in source_elem.dataset);
|
2024-04-22 22:25:48 +00:00
|
|
|
} else {
|
2024-04-29 21:51:16 +00:00
|
|
|
await this.tree_list.onRowSelected();
|
2024-04-22 22:25:48 +00:00
|
|
|
}
|
2024-05-02 17:27:03 +00:00
|
|
|
// Don't use escaped path here since this is pure javascript beyond this point.
|
2024-04-30 18:27:13 +00:00
|
|
|
this.applyDirectoryFilter(
|
2024-05-02 17:27:03 +00:00
|
|
|
"selected" in source_elem.dataset ? source_elem.dataset.path : null,
|
2024-04-30 18:27:13 +00:00
|
|
|
"recurse" in source_elem.dataset,
|
|
|
|
);
|
2024-04-29 21:51:16 +00:00
|
|
|
return;
|
2024-04-22 22:25:48 +00:00
|
|
|
}
|
2024-04-29 21:51:16 +00:00
|
|
|
|
|
|
|
const data_selected = "selected" in source_elem.dataset;
|
|
|
|
const data_recurse = "recurse" in source_elem.dataset;
|
|
|
|
const short_pressed = source_elem.classList.contains("short-pressed");
|
|
|
|
const long_pressed = source_elem.classList.contains("long-pressed");
|
|
|
|
|
|
|
|
_reset_all_buttons({excluded_elems: [source_elem, other_elem]});
|
|
|
|
other_elem.toggleAttribute("data-selected", data_selected);
|
|
|
|
other_elem.toggleAttribute("data-recurse", data_recurse);
|
|
|
|
other_elem.classList.toggle("short-pressed", short_pressed);
|
|
|
|
other_elem.classList.toggle("long-pressed", long_pressed);
|
|
|
|
|
|
|
|
await this.tree_list.onRowSelected(source_is_tree ? source_elem : other_elem);
|
|
|
|
const div_id = source_is_tree ? source_elem.dataset.divId : other_elem.dataset.divId;
|
|
|
|
_set_recursion_depth(div_id, data_recurse);
|
2024-05-02 17:27:03 +00:00
|
|
|
// Don't use escaped path here since this is pure javascript beyond this point.
|
2024-04-30 18:27:13 +00:00
|
|
|
this.applyDirectoryFilter(
|
2024-05-02 17:27:03 +00:00
|
|
|
"selected" in source_elem.dataset ? source_elem.dataset.path : null,
|
2024-04-30 18:27:13 +00:00
|
|
|
"recurse" in source_elem.dataset,
|
|
|
|
);
|
2024-04-22 22:25:48 +00:00
|
|
|
}
|
2024-04-12 16:40:20 +00:00
|
|
|
}
|
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
function popup(contents) {
|
2024-04-09 11:19:07 +00:00
|
|
|
if (!globalPopup) {
|
|
|
|
globalPopup = document.createElement('div');
|
|
|
|
globalPopup.classList.add('global-popup');
|
|
|
|
|
|
|
|
var close = document.createElement('div');
|
|
|
|
close.classList.add('global-popup-close');
|
|
|
|
close.addEventListener("click", closePopup);
|
|
|
|
close.title = "Close";
|
|
|
|
globalPopup.appendChild(close);
|
|
|
|
|
|
|
|
globalPopupInner = document.createElement('div');
|
|
|
|
globalPopupInner.classList.add('global-popup-inner');
|
|
|
|
globalPopup.appendChild(globalPopupInner);
|
|
|
|
|
|
|
|
gradioApp().querySelector('.main').appendChild(globalPopup);
|
2023-08-05 06:15:18 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
|
|
|
|
globalPopupInner.innerHTML = '';
|
|
|
|
globalPopupInner.appendChild(contents);
|
|
|
|
|
|
|
|
globalPopup.style.display = "flex";
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
function popupId(id) {
|
2024-04-09 11:19:07 +00:00
|
|
|
if (!storedPopupIds[id]) {
|
|
|
|
storedPopupIds[id] = gradioApp().getElementById(id);
|
2023-08-05 06:15:18 +00:00
|
|
|
}
|
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
popup(storedPopupIds[id]);
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-03-19 01:59:41 +00:00
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
function closePopup() {
|
2024-04-09 11:19:07 +00:00
|
|
|
if (!globalPopup) return;
|
|
|
|
globalPopup.style.display = "none";
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-03-19 01:59:41 +00:00
|
|
|
|
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
// ==== GENERAL EXTRA NETWORKS FUNCTIONS ====
|
2024-03-13 21:11:44 +00:00
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
function extraNetworksRemoveFromPrompt(textarea, text, is_neg) {
|
2024-04-09 11:19:07 +00:00
|
|
|
let match = text.match(is_neg ? re_extranet_neg : re_extranet);
|
|
|
|
let replaced = false;
|
|
|
|
let res;
|
2024-04-15 15:18:01 +00:00
|
|
|
let sep = opts.extra_networks_add_text_separator;
|
2024-04-09 11:19:07 +00:00
|
|
|
|
|
|
|
if (match) {
|
|
|
|
const content = match[1];
|
|
|
|
const postfix = match[2];
|
|
|
|
let idx = -1;
|
|
|
|
res = textarea.value.replaceAll(
|
|
|
|
is_neg ? re_extranet_g_neg : re_extranet_g,
|
|
|
|
(found, net, pos) => {
|
|
|
|
match = found.match(is_neg ? re_extranet_neg : re_extranet);
|
|
|
|
if (match[1] === content) {
|
|
|
|
replaced = true;
|
|
|
|
idx = pos;
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
},
|
|
|
|
);
|
|
|
|
if (idx >= 0) {
|
2024-04-15 14:48:28 +00:00
|
|
|
if (postfix && res.slice(idx, idx + postfix.length) === postfix) {
|
2024-04-09 11:19:07 +00:00
|
|
|
res = res.slice(0, idx) + res.slice(idx + postfix.length);
|
|
|
|
}
|
2024-04-15 15:18:01 +00:00
|
|
|
if (res.slice(idx - sep.length, idx) === sep) {
|
|
|
|
res = res.slice(0, idx - sep.length) + res.slice(idx);
|
|
|
|
}
|
|
|
|
// Remove separator if it is at beginning of string.
|
|
|
|
if (res.startsWith(sep)) {
|
|
|
|
res = res.slice(sep.length);
|
2024-04-09 11:19:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2024-04-15 15:18:01 +00:00
|
|
|
res = textarea.value.replaceAll(new RegExp(`((?:${sep})?${text})`, "g"), "");
|
2024-04-09 11:19:07 +00:00
|
|
|
replaced = (res !== textarea.value);
|
|
|
|
}
|
2024-01-22 20:20:30 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
if (replaced) {
|
|
|
|
textarea.value = res;
|
|
|
|
return true;
|
|
|
|
}
|
2024-01-22 20:20:30 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
return false;
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2023-11-05 16:19:55 +00:00
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
function extraNetworksUpdatePrompt(textarea, text, is_neg) {
|
2024-04-09 11:19:07 +00:00
|
|
|
if (!extraNetworksRemoveFromPrompt(textarea, text, is_neg)) {
|
2024-04-15 15:18:01 +00:00
|
|
|
if (!textarea.value) {
|
|
|
|
// if textarea is empty, dont add the separator.
|
|
|
|
textarea.value = text;
|
|
|
|
} else {
|
|
|
|
textarea.value = textarea.value + opts.extra_networks_add_text_separator + text;
|
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
}
|
2024-03-13 21:11:44 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
updateInput(textarea);
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-03-13 21:11:44 +00:00
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
function extraNetworksSaveCardPreview(event, tabname, filename) {
|
2024-04-09 11:19:07 +00:00
|
|
|
const textarea = gradioApp().querySelector(`#${tabname}_preview_filename > label > textarea`);
|
|
|
|
const button = gradioApp().getElementById(`${tabname}_save_preview`);
|
2023-11-05 16:19:55 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
textarea.value = filename;
|
|
|
|
updateInput(textarea);
|
2024-03-22 20:16:45 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
button.click();
|
2024-03-27 20:11:13 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
event.stopPropagation();
|
|
|
|
event.preventDefault();
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
function extraNetworksFlattenMetadata(obj) {
|
2024-04-09 11:19:07 +00:00
|
|
|
const result = {};
|
|
|
|
|
|
|
|
// Convert any stringified JSON objects to actual objects
|
|
|
|
for (const key of Object.keys(obj)) {
|
|
|
|
if (typeof obj[key] === 'string') {
|
|
|
|
try {
|
|
|
|
const parsed = JSON.parse(obj[key]);
|
|
|
|
if (parsed && typeof parsed === 'object') {
|
|
|
|
obj[key] = parsed;
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2024-03-22 20:16:45 +00:00
|
|
|
}
|
2023-05-17 12:46:58 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
// Flatten the object
|
|
|
|
for (const key of Object.keys(obj)) {
|
|
|
|
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
|
|
const nested = extraNetworksFlattenMetadata(obj[key]);
|
|
|
|
for (const nestedKey of Object.keys(nested)) {
|
|
|
|
result[`${key}/${nestedKey}`] = nested[nestedKey];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
result[key] = obj[key];
|
2024-03-21 20:03:27 +00:00
|
|
|
}
|
2024-03-13 21:11:44 +00:00
|
|
|
}
|
2023-05-17 12:46:58 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
// Special case for handling modelspec keys
|
|
|
|
for (const key of Object.keys(result)) {
|
|
|
|
if (key.startsWith("modelspec.")) {
|
|
|
|
result[key.replaceAll(".", "/")] = result[key];
|
|
|
|
delete result[key];
|
2024-03-22 20:16:45 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
}
|
2024-03-26 17:58:21 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
// Add empty keys to designate hierarchy
|
|
|
|
for (const key of Object.keys(result)) {
|
|
|
|
const parts = key.split("/");
|
|
|
|
for (let i = 1; i < parts.length; i++) {
|
|
|
|
const parent = parts.slice(0, i).join("/");
|
|
|
|
if (!result[parent]) {
|
|
|
|
result[parent] = "";
|
2024-03-29 16:21:02 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
function extraNetworksShowMetadata(text) {
|
2024-04-09 11:19:07 +00:00
|
|
|
try {
|
|
|
|
let parsed = JSON.parse(text);
|
|
|
|
if (parsed && typeof parsed === 'object') {
|
|
|
|
parsed = extraNetworksFlattenMetadata(parsed);
|
|
|
|
const table = createVisualizationTable(parsed, 0);
|
|
|
|
popup(table);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
var elem = document.createElement('pre');
|
|
|
|
elem.classList.add('popup-metadata');
|
|
|
|
elem.textContent = text;
|
|
|
|
|
|
|
|
popup(elem);
|
|
|
|
return;
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
function extraNetworksRefreshSingleCard(tabname, extra_networks_tabname, name) {
|
2024-04-14 18:43:31 +00:00
|
|
|
const tab = extra_networks_tabs[`${tabname}_${extra_networks_tabname}`];
|
2024-04-22 19:02:00 +00:00
|
|
|
const elem = tab.cards_list.content_elem.querySelector(`.card[data-name="${name}"]`);
|
|
|
|
isElementThrowError(elem);
|
|
|
|
tab.refreshSingleCard(elem);
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
|
2024-04-16 16:43:30 +00:00
|
|
|
function extraNetworksRefreshTab(tabname_full) {
|
2024-04-09 11:19:07 +00:00
|
|
|
/** called from python when user clicks the extra networks refresh tab button */
|
2024-04-16 16:43:30 +00:00
|
|
|
extra_networks_tabs[tabname_full].refresh();
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2023-05-17 12:46:58 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
// ==== EVENT HANDLING ====
|
2023-12-30 20:52:27 +00:00
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
function extraNetworksFetchMetadata(extra_networks_tabname, card_name) {
|
2024-04-14 18:52:17 +00:00
|
|
|
const _showError = () => {
|
|
|
|
extraNetworksShowMetadata("there was an error getting metadata");
|
|
|
|
};
|
2024-01-13 18:16:39 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
requestGet(
|
|
|
|
"./sd_extra_networks/metadata",
|
2024-04-26 18:16:31 +00:00
|
|
|
{extra_networks_tabname: extra_networks_tabname, item: card_name},
|
|
|
|
function(data) {
|
2024-04-09 11:19:07 +00:00
|
|
|
if (data && data.metadata) {
|
|
|
|
extraNetworksShowMetadata(data.metadata);
|
|
|
|
} else {
|
|
|
|
_showError();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_showError,
|
|
|
|
);
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-01-13 18:16:39 +00:00
|
|
|
|
2024-04-15 15:18:01 +00:00
|
|
|
function extraNetworksUnrelatedTabSelected(tabname) {
|
2024-04-09 11:19:07 +00:00
|
|
|
/** called from python when user selects an unrelated tab (generate) */
|
2024-04-15 15:18:01 +00:00
|
|
|
for (const v of Object.values(extra_networks_tabs)) {
|
2024-04-12 16:40:20 +00:00
|
|
|
v.unload();
|
|
|
|
}
|
2024-04-15 15:18:01 +00:00
|
|
|
|
|
|
|
// Move all prompts into the selected tab.
|
|
|
|
const prompt_container_elem = document.querySelector(`#${tabname}_prompt_container`);
|
|
|
|
const prompt_row_elem = document.querySelector(`#${tabname}_prompt_row`);
|
|
|
|
const neg_prompt_row_elem = document.querySelector(`#${tabname}_neg_prompt_row`);
|
|
|
|
prompt_container_elem.insertBefore(neg_prompt_row_elem, prompt_container_elem.firstChild);
|
|
|
|
prompt_container_elem.insertBefore(prompt_row_elem, prompt_container_elem.firstChild);
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
|
|
|
|
2024-04-12 16:40:20 +00:00
|
|
|
async function extraNetworksTabSelected(tabname_full, show_prompt, show_neg_prompt) {
|
2024-04-09 11:19:07 +00:00
|
|
|
/** called from python when user selects an extra networks tab */
|
2024-04-26 18:16:31 +00:00
|
|
|
await waitForKeyInObject({obj: extra_networks_tabs, k: tabname_full});
|
2024-04-12 16:40:20 +00:00
|
|
|
for (const [k, v] of Object.entries(extra_networks_tabs)) {
|
|
|
|
if (k === tabname_full) {
|
2024-04-16 15:22:12 +00:00
|
|
|
v.load(show_prompt, show_neg_prompt);
|
2024-04-12 16:40:20 +00:00
|
|
|
} else {
|
|
|
|
v.unload();
|
|
|
|
}
|
|
|
|
}
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-03-13 21:11:44 +00:00
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
function extraNetworksBtnDirsViewItemOnLongPress(event) {
|
|
|
|
const btn = event.target.closest(".extra-network-dirs-view-button");
|
|
|
|
const pane = btn.closest(".extra-network-pane");
|
|
|
|
const tab = extra_networks_tabs[pane.dataset.tabnameFull];
|
|
|
|
|
|
|
|
tab.setDirectoryButtons({source_elem: btn});
|
|
|
|
}
|
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
function extraNetworksBtnDirsViewItemOnClick(event) {
|
2024-03-25 17:50:44 +00:00
|
|
|
/** Handles `onclick` events for buttons in the directory view. */
|
2024-04-27 16:37:17 +00:00
|
|
|
const btn = event.target.closest(".extra-network-dirs-view-button");
|
|
|
|
const pane = btn.closest(".extra-network-pane");
|
|
|
|
const tab = extra_networks_tabs[pane.dataset.tabnameFull];
|
2024-03-15 23:11:30 +00:00
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
tab.setDirectoryButtons({source_elem: btn});
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-03-15 18:31:58 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
function extraNetworksControlSearchClearOnClick(event) {
|
2024-03-25 17:50:44 +00:00
|
|
|
/** Dispatches custom event when the `clear` button in a search input is clicked. */
|
2024-04-27 16:37:17 +00:00
|
|
|
const btn = event.target.closest(".extra-network-control--search-clear");
|
|
|
|
const controls = btn.closest(".extra-network-controls");
|
|
|
|
extra_networks_tabs[controls.dataset.tabnameFull].updateSearch("");
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2023-05-17 12:46:58 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
function extraNetworksControlSortModeOnClick(event) {
|
2024-03-09 05:25:01 +00:00
|
|
|
/** Handles `onclick` events for Sort Mode buttons. */
|
2024-04-27 16:37:17 +00:00
|
|
|
const btn = event.target.closest(".extra-network-control--sort-mode");
|
|
|
|
// No operation if button is already selected.
|
|
|
|
if ("selected" in btn.dataset) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const controls = btn.closest(".extra-network-controls");
|
|
|
|
const tab = extra_networks_tabs[controls.dataset.tabnameFull];
|
2024-04-12 16:40:20 +00:00
|
|
|
tab.controls_elem.querySelectorAll(".extra-network-control--sort-mode").forEach(elem => {
|
2024-03-25 20:43:29 +00:00
|
|
|
delete elem.dataset.selected;
|
2024-04-29 21:51:16 +00:00
|
|
|
delete elem.dataset.recurse;
|
2024-03-09 05:25:01 +00:00
|
|
|
});
|
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
btn.dataset.selected = "";
|
2024-03-09 04:24:25 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
const sort_mode_str = btn.dataset.sortMode.toLowerCase();
|
2024-04-12 16:40:20 +00:00
|
|
|
|
2024-04-14 18:43:31 +00:00
|
|
|
tab.setSortMode(sort_mode_str);
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
function extraNetworksControlSortDirOnClick(event) {
|
2024-03-25 17:50:44 +00:00
|
|
|
/** Handles `onclick` events for the Sort Direction button.
|
2024-01-16 18:35:01 +00:00
|
|
|
*
|
|
|
|
* Modifies the data attributes of the Sort Direction button to cycle between
|
|
|
|
* ascending and descending sort directions.
|
|
|
|
*/
|
2024-04-27 16:37:17 +00:00
|
|
|
const btn = event.target.closest(".extra-network-control--sort-dir");
|
|
|
|
const controls = btn.closest(".extra-network-controls");
|
|
|
|
const tab = extra_networks_tabs[controls.dataset.tabnameFull];
|
2024-04-12 16:40:20 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
const curr_sort_dir_str = btn.dataset.sortDir.toLowerCase();
|
2024-04-09 11:19:07 +00:00
|
|
|
if (!["ascending", "descending"].includes(curr_sort_dir_str)) {
|
|
|
|
console.error(`Invalid sort_dir_str: ${curr_sort_dir_str}`);
|
|
|
|
return;
|
2024-01-15 22:34:44 +00:00
|
|
|
}
|
2024-03-14 20:51:52 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
let sort_dir_str = curr_sort_dir_str === "ascending" ? "descending" : "ascending";
|
2024-04-27 16:37:17 +00:00
|
|
|
btn.dataset.sortDir = sort_dir_str;
|
|
|
|
btn.setAttribute("title", `Sort ${sort_dir_str}`);
|
2024-04-09 11:19:07 +00:00
|
|
|
|
2024-04-14 18:43:31 +00:00
|
|
|
tab.setSortDir(sort_dir_str);
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
async function extraNetworksControlTreeViewOnClick(event) {
|
2024-03-25 17:50:44 +00:00
|
|
|
/** Handles `onclick` events for the Tree View button.
|
2024-01-20 16:43:45 +00:00
|
|
|
*
|
|
|
|
* Toggles the tree view in the extra networks pane.
|
|
|
|
*/
|
2024-04-27 16:37:17 +00:00
|
|
|
const btn = event.target.closest(".extra-network-control--tree-view");
|
|
|
|
const controls = btn.closest(".extra-network-controls");
|
|
|
|
const tab = extra_networks_tabs[controls.dataset.tabnameFull];
|
2024-05-02 18:27:36 +00:00
|
|
|
btn.toggleAttribute("data-selected");
|
|
|
|
tab.tree_view_en = "selected" in btn.dataset;
|
2024-04-30 18:29:06 +00:00
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
// If hiding, clear the tree list selections before hiding it.
|
|
|
|
if (!tab.tree_view_en) {
|
|
|
|
await tab.tree_list.onRowSelected();
|
2024-05-02 18:27:36 +00:00
|
|
|
tab.tree_list.content_elem.querySelectorAll(
|
|
|
|
".tree-list-item[data-selected]"
|
|
|
|
).forEach(elem => {
|
|
|
|
delete elem.dataset.selected;
|
|
|
|
delete elem.dataset.recurse;
|
|
|
|
});
|
2024-04-23 14:48:50 +00:00
|
|
|
}
|
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
tab.tree_list.scroll_elem.parentElement.classList.toggle("hidden", !tab.tree_view_en);
|
|
|
|
tab.tree_list.enable(tab.tree_view_en);
|
2024-04-17 17:01:46 +00:00
|
|
|
|
2024-04-17 23:23:38 +00:00
|
|
|
// Apply the resize-handle-hidden class to the resize-handle-row.
|
|
|
|
// NOTE: This can be simplified using only css with the ":has" selector however
|
|
|
|
// this is only recently supported in firefox. So for now we just add a class
|
|
|
|
// to the resize-handle-row instead.
|
|
|
|
const resize_handle_row = tab.tree_list.scroll_elem.closest(".resize-handle-row");
|
2024-04-29 21:51:16 +00:00
|
|
|
resize_handle_row.classList.toggle("resize-handle-hidden", !tab.tree_view_en);
|
2024-04-17 23:23:38 +00:00
|
|
|
|
2024-05-03 17:13:25 +00:00
|
|
|
// If the tree list hasn't loaded yet, we need to force it to load.
|
|
|
|
// This can happen if tree view is disabled by default or before refresh.
|
|
|
|
// Then after refresh, enabling the tree view will require a load.
|
|
|
|
if (tab.tree_view_en && !tab.tree_list.initial_load) {
|
|
|
|
await tab.tree_list.load();
|
|
|
|
}
|
|
|
|
|
2024-05-02 18:27:36 +00:00
|
|
|
if ((tab.tree_view_en && tab.dirs_view_en) || (!tab.tree_view_en && tab.dirs_view_en)) {
|
2024-04-29 21:51:16 +00:00
|
|
|
tab.setDirectoryButtons({source_class: ".extra-network-dirs-view-button"});
|
2024-05-02 18:27:36 +00:00
|
|
|
} else if (tab.tree_view_en) {
|
|
|
|
tab.setDirectoryButtons({source_class: ".tree-list-item"});
|
|
|
|
} else {
|
|
|
|
tab.setDirectoryButtons({reset_all: true});
|
2024-04-29 21:51:16 +00:00
|
|
|
}
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-03-19 01:59:41 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
function extraNetworksControlDirsViewOnClick(event) {
|
2024-03-25 17:50:44 +00:00
|
|
|
/** Handles `onclick` events for the Dirs View button.
|
2024-03-19 01:59:41 +00:00
|
|
|
*
|
|
|
|
* Toggles the directory view in the extra networks pane.
|
|
|
|
*/
|
2024-04-27 16:37:17 +00:00
|
|
|
const btn = event.target.closest(".extra-network-control--dirs-view");
|
|
|
|
const controls = btn.closest(".extra-network-controls");
|
|
|
|
const tab = extra_networks_tabs[controls.dataset.tabnameFull];
|
2024-05-02 18:27:36 +00:00
|
|
|
btn.toggleAttribute("data-selected");
|
|
|
|
tab.dirs_view_en = "selected" in btn.dataset;
|
2024-03-14 20:51:52 +00:00
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
if (!tab.dirs_view_en) {
|
2024-04-23 14:48:50 +00:00
|
|
|
// If hiding, we want to deselect all buttons prior to hiding.
|
|
|
|
tab.container_elem.querySelectorAll(
|
2024-04-29 21:51:16 +00:00
|
|
|
".extra-network-dirs-view-button[data-selected]"
|
2024-04-23 14:48:50 +00:00
|
|
|
).forEach(elem => {
|
|
|
|
delete elem.dataset.selected;
|
2024-04-29 21:51:16 +00:00
|
|
|
delete elem.dataset.recurse;
|
2024-04-23 14:48:50 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-04-12 16:40:20 +00:00
|
|
|
tab.container_elem.querySelector(
|
|
|
|
".extra-network-content--dirs-view"
|
2024-04-29 21:51:16 +00:00
|
|
|
).classList.toggle("hidden", !tab.dirs_view_en);
|
2024-04-17 17:01:46 +00:00
|
|
|
|
2024-05-02 18:27:36 +00:00
|
|
|
if ((tab.tree_view_en && tab.dirs_view_en) || (tab.tree_view_en && !tab.dirs_view_en)) {
|
2024-04-29 21:51:16 +00:00
|
|
|
tab.setDirectoryButtons({source_class: ".tree-list-item"});
|
2024-05-02 18:27:36 +00:00
|
|
|
} else if (tab.dirs_view_en) {
|
|
|
|
tab.setDirectoryButtons({source_class: ".extra-network-dirs-view-button"});
|
|
|
|
} else {
|
|
|
|
tab.setDirectoryButtons({reset_all: true});
|
2024-04-29 21:51:16 +00:00
|
|
|
}
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-01-20 16:43:45 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
function extraNetworksControlRefreshOnClick(event) {
|
2024-03-25 17:50:44 +00:00
|
|
|
/** Handles `onclick` events for the Refresh Page button.
|
2024-01-16 18:35:01 +00:00
|
|
|
*
|
|
|
|
* In order to actually call the python functions in `ui_extra_networks.py`
|
|
|
|
* to refresh the page, we created an empty gradio button in that file with an
|
|
|
|
* event handler that refreshes the page. So what this function here does
|
|
|
|
* is it manually raises a `click` event on that button.
|
|
|
|
*/
|
2024-04-16 16:43:30 +00:00
|
|
|
clearTimeout(extra_networks_refresh_internal_debounce_timer);
|
2024-04-26 18:16:31 +00:00
|
|
|
extra_networks_refresh_internal_debounce_timer = setTimeout(async() => {
|
2024-04-27 16:37:17 +00:00
|
|
|
const btn = event.target.closest(".extra-network-control--refresh");
|
|
|
|
const controls = btn.closest(".extra-network-controls");
|
2024-05-03 18:41:27 +00:00
|
|
|
// Bit of lazy workaround to allow for us to create copies of the refresh
|
|
|
|
// button anywhere we want as long as we include tabnameFull in the dataset.
|
|
|
|
let tabname_full;
|
|
|
|
if (isElement(controls)) {
|
|
|
|
tabname_full = controls.dataset.tabnameFull;
|
|
|
|
} else {
|
|
|
|
tabname_full = btn.dataset.tabnameFull;
|
|
|
|
}
|
|
|
|
const tab = extra_networks_tabs[tabname_full];
|
|
|
|
if (isNullOrUndefined(tab)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-05-03 17:13:25 +00:00
|
|
|
tab.updateSplashState({cards_list_state: "loading", tree_list_state: "loading"});
|
2024-04-17 16:50:22 +00:00
|
|
|
// We want to reset tab lists on refresh click so that the viewing area
|
2024-04-16 16:43:30 +00:00
|
|
|
// shows that it is loading new data.
|
2024-04-24 18:50:21 +00:00
|
|
|
tab.tree_list.clear();
|
|
|
|
tab.cards_list.clear();
|
2024-04-16 16:43:30 +00:00
|
|
|
// Fire an event for this button click.
|
2024-04-27 16:37:17 +00:00
|
|
|
gradioApp().getElementById(
|
2024-05-03 18:41:27 +00:00
|
|
|
`${tabname_full}_extra_refresh_internal`
|
2024-04-27 16:37:17 +00:00
|
|
|
).dispatchEvent(new Event("click"));
|
2024-04-16 16:43:30 +00:00
|
|
|
}, EXTRA_NETWORKS_REFRESH_INTERNAL_DEBOUNCE_TIMEOUT_MS);
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2023-08-27 06:39:37 +00:00
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
function extraNetworksSelectModel({tab, prompt, neg_prompt, allow_neg, checkpoint_name}) {
|
|
|
|
if (checkpoint_name) {
|
|
|
|
selectCheckpoint(checkpoint_name);
|
|
|
|
} else if (neg_prompt) {
|
|
|
|
extraNetworksUpdatePrompt(tab.txt_prompt_elem, prompt);
|
|
|
|
extraNetworksUpdatePrompt(tab.txt_neg_prompt_elem, neg_prompt);
|
|
|
|
} else if (allow_neg) {
|
|
|
|
extraNetworksUpdatePrompt(tab.active_prompt_elem, prompt);
|
2024-04-09 11:19:07 +00:00
|
|
|
} else {
|
2024-04-29 21:51:16 +00:00
|
|
|
extraNetworksUpdatePrompt(tab.txt_prompt_elem, prompt);
|
2024-03-04 23:33:22 +00:00
|
|
|
}
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-03-04 23:33:22 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
function extraNetworksCardOnClick(event) {
|
|
|
|
// Do not select the card if its child button-row is the target of the event.
|
|
|
|
if (event.target.closest(".button-row")) {
|
|
|
|
return;
|
|
|
|
}
|
2024-03-04 23:33:22 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
const btn = event.target.closest(".card");
|
|
|
|
const pane = btn.closest(".extra-network-pane");
|
|
|
|
const tab = extra_networks_tabs[pane.dataset.tabnameFull];
|
|
|
|
|
|
|
|
let checkpoint_name;
|
|
|
|
if ("isCheckpoint" in btn.dataset) {
|
|
|
|
checkpoint_name = btn.dataset.name;
|
2024-04-21 16:07:53 +00:00
|
|
|
}
|
2024-04-29 21:51:16 +00:00
|
|
|
extraNetworksSelectModel({
|
|
|
|
tab: tab,
|
2024-04-27 16:37:17 +00:00
|
|
|
prompt: btn.dataset.prompt,
|
|
|
|
neg_prompt: btn.dataset.negPrompt,
|
|
|
|
allow_neg: btn.dataset.allowNeg,
|
|
|
|
checkpoint_name: checkpoint_name,
|
|
|
|
});
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-03-04 23:33:22 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
function extraNetworksTreeFileOnClick(event) {
|
|
|
|
// Do not select the row if its child button-row is the target of the event.
|
|
|
|
if (event.target.closest(".tree-list-item-action")) {
|
2024-04-09 11:19:07 +00:00
|
|
|
return;
|
2024-03-04 23:33:22 +00:00
|
|
|
}
|
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
const btn = event.target.closest(".tree-list-item");
|
|
|
|
const pane = btn.closest(".extra-network-pane");
|
|
|
|
const tab = extra_networks_tabs[pane.dataset.tabnameFull];
|
2024-04-30 18:29:06 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
|
|
|
|
let checkpoint_name;
|
|
|
|
if ("isCheckpoint" in btn.dataset) {
|
|
|
|
checkpoint_name = btn.dataset.name;
|
2024-03-04 23:46:25 +00:00
|
|
|
}
|
2024-04-29 21:51:16 +00:00
|
|
|
extraNetworksSelectModel({
|
|
|
|
tab: tab,
|
2024-04-27 16:37:17 +00:00
|
|
|
prompt: btn.dataset.prompt,
|
|
|
|
neg_prompt: btn.dataset.negPrompt,
|
|
|
|
allow_neg: btn.dataset.allowNeg,
|
|
|
|
checkpoint_name: checkpoint_name,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-04-29 21:51:16 +00:00
|
|
|
async function extraNetworksTreeDirectoryOnLongPress(event) {
|
|
|
|
// Do not select the row if its child button-row is the target of the event.
|
|
|
|
if (event.target.closest(".tree-list-item-action")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const btn = event.target.closest(".tree-list-item");
|
|
|
|
const pane = btn.closest(".extra-network-pane");
|
|
|
|
const tab = extra_networks_tabs[pane.dataset.tabnameFull];
|
|
|
|
|
|
|
|
tab.setDirectoryButtons({source_elem: btn});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function extraNetworksTreeDirectoryOnDblClick(event) {
|
|
|
|
// stopPropagation so we don't also trigger event on parent since this btn is nested.
|
|
|
|
event.stopPropagation();
|
|
|
|
const btn = event.target.closest(".tree-list-item");
|
2024-04-30 18:27:13 +00:00
|
|
|
const pane = btn.closest(".extra-network-pane");
|
|
|
|
const div_id = btn.dataset.divId;
|
|
|
|
const tab = extra_networks_tabs[pane.dataset.tabnameFull];
|
|
|
|
await tab.tree_list.toggleRowExpanded(div_id);
|
|
|
|
tab.setDirectoryButtons({source_class: ".tree-list-item"});
|
2024-04-29 21:51:16 +00:00
|
|
|
}
|
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
async function extraNetworksTreeDirectoryOnClick(event) {
|
|
|
|
// Do not select the row if its child button-row is the target of the event.
|
|
|
|
if (event.target.closest(".tree-list-item-action")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const btn = event.target.closest(".tree-list-item");
|
|
|
|
const pane = btn.closest(".extra-network-pane");
|
|
|
|
const tab = extra_networks_tabs[pane.dataset.tabnameFull];
|
2024-04-29 21:51:16 +00:00
|
|
|
tab.setDirectoryButtons({source_elem: btn});
|
2024-04-27 16:37:17 +00:00
|
|
|
}
|
|
|
|
|
2024-04-30 18:27:13 +00:00
|
|
|
async function extraNetworksTreeDirectoryChevronOnLongPress(event) {
|
2024-04-27 16:37:17 +00:00
|
|
|
// stopPropagation so we don't also trigger event on parent since this btn is nested.
|
|
|
|
event.stopPropagation();
|
2024-04-30 18:27:13 +00:00
|
|
|
const chevron = event.target.closest(".tree-list-item-action--chevron");
|
2024-04-29 21:51:16 +00:00
|
|
|
const btn = event.target.closest(".tree-list-item");
|
2024-04-27 16:37:17 +00:00
|
|
|
const pane = btn.closest(".extra-network-pane");
|
2024-04-29 21:51:16 +00:00
|
|
|
const div_id = btn.dataset.divId;
|
2024-04-27 16:37:17 +00:00
|
|
|
const tab = extra_networks_tabs[pane.dataset.tabnameFull];
|
2024-04-30 18:27:13 +00:00
|
|
|
if ("expanded" in btn.dataset) {
|
|
|
|
await tab.tree_list.collapseAllRows(div_id);
|
|
|
|
} else {
|
|
|
|
await tab.tree_list.expandAllRows(div_id);
|
|
|
|
}
|
2024-04-29 21:51:16 +00:00
|
|
|
tab.setDirectoryButtons({source_class: ".tree-list-item"});
|
2024-04-27 16:37:17 +00:00
|
|
|
}
|
2024-03-04 23:33:22 +00:00
|
|
|
|
2024-04-30 18:27:13 +00:00
|
|
|
async function extraNetworksBtnTreeViewChevronOnClick(event) {
|
2024-04-27 16:37:17 +00:00
|
|
|
// stopPropagation so we don't also trigger event on parent since this btn is nested.
|
2024-01-08 19:10:03 +00:00
|
|
|
event.stopPropagation();
|
2024-04-29 21:51:16 +00:00
|
|
|
const btn = event.target.closest(".tree-list-item");
|
2024-04-27 16:37:17 +00:00
|
|
|
const pane = btn.closest(".extra-network-pane");
|
2024-04-29 21:51:16 +00:00
|
|
|
const div_id = btn.dataset.divId;
|
2024-04-27 16:37:17 +00:00
|
|
|
const tab = extra_networks_tabs[pane.dataset.tabnameFull];
|
2024-04-30 18:27:13 +00:00
|
|
|
await tab.tree_list.toggleRowExpanded(div_id);
|
2024-04-29 21:51:16 +00:00
|
|
|
tab.setDirectoryButtons({source_class: ".tree-list-item"});
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2023-05-17 12:46:58 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
function extraNetworksBtnShowMetadataOnClick(event) {
|
|
|
|
// stopPropagation so we don't also trigger event on parent since this btn is nested.
|
2023-03-25 09:10:03 +00:00
|
|
|
event.stopPropagation();
|
2024-04-27 16:37:17 +00:00
|
|
|
const btn = event.target.closest(".metadata-button");
|
|
|
|
const pane = btn.closest(".extra-network-pane");
|
|
|
|
let parent = btn.closest(".card");
|
|
|
|
if (!parent) {
|
|
|
|
parent = btn.closest(".tree-list-item");
|
|
|
|
}
|
|
|
|
extraNetworksFetchMetadata(pane.dataset.extraNetworksTabname, parent.dataset.name);
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2023-07-15 17:39:04 +00:00
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
function extraNetworksBtnEditMetadataOnClick(event) {
|
|
|
|
// stopPropagation so we don't also trigger event on parent since this btn is nested.
|
|
|
|
event.stopPropagation();
|
|
|
|
const btn = event.target.closest(".edit-button");
|
|
|
|
const pane = btn.closest(".extra-network-pane");
|
|
|
|
let parent = btn.closest(".card");
|
|
|
|
if (!parent) {
|
|
|
|
parent = btn.closest(".tree-list-item");
|
|
|
|
}
|
|
|
|
const id = `${pane.dataset.tabnameFull}_edit_user_metadata`;
|
2024-03-29 16:21:02 +00:00
|
|
|
let editor = extraPageUserMetadataEditors[id];
|
2024-04-09 11:19:07 +00:00
|
|
|
if (isNullOrUndefined(editor)) {
|
2023-07-15 17:39:04 +00:00
|
|
|
editor = {};
|
|
|
|
editor.page = gradioApp().getElementById(id);
|
2024-03-29 16:21:02 +00:00
|
|
|
editor.nameTextarea = gradioApp().querySelector(`#${id}_name textarea`);
|
|
|
|
editor.button = gradioApp().querySelector(`#${id}_button`);
|
2023-07-15 17:39:04 +00:00
|
|
|
extraPageUserMetadataEditors[id] = editor;
|
|
|
|
}
|
|
|
|
|
2024-04-27 16:37:17 +00:00
|
|
|
editor.nameTextarea.value = parent.dataset.name;
|
2023-07-15 17:39:04 +00:00
|
|
|
updateInput(editor.nameTextarea);
|
|
|
|
|
|
|
|
editor.button.click();
|
|
|
|
|
|
|
|
popup(editor.page);
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2023-07-15 17:39:04 +00:00
|
|
|
|
2024-04-22 13:07:35 +00:00
|
|
|
function extraNetworksBtnCopyPathOnClick(event) {
|
2024-04-27 16:37:17 +00:00
|
|
|
// stopPropagation so we don't also trigger event on parent since this btn is nested.
|
2023-07-15 17:39:04 +00:00
|
|
|
event.stopPropagation();
|
2024-04-27 16:37:17 +00:00
|
|
|
const btn = event.target.closest(".copy-path-button");
|
|
|
|
copyToClipboard(btn.dataset.clipboardText);
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
|
|
|
|
// ==== MAIN SETUP ====
|
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
function extraNetworksSetupEventDelegators() {
|
2024-04-09 11:19:07 +00:00
|
|
|
/** Sets up event delegators for all extraNetworks tabs.
|
|
|
|
*
|
|
|
|
* These event handlers are not tied to any specific elements on the page.
|
|
|
|
* We do this because elements within each tab may be removed and replaced
|
|
|
|
* which would break references to elements in DOM and thus prevent any event
|
|
|
|
* listeners from firing.
|
|
|
|
*/
|
|
|
|
|
|
|
|
window.addEventListener("resizeHandleDblClick", event => {
|
|
|
|
// See resizeHandle.js::onDoubleClick() for event detail.
|
|
|
|
event.stopPropagation();
|
2024-04-12 16:40:20 +00:00
|
|
|
const pane = event.target.closest(".extra-network-pane");
|
|
|
|
extra_networks_tabs[pane.dataset.tabnameFull].autoSetTreeWidth();
|
2024-04-09 11:19:07 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Debounce search text input. This way we only search after user is done typing.
|
2024-04-17 19:24:45 +00:00
|
|
|
const search_input_debounce = _debounce(tabname_full => {
|
2024-04-12 16:40:20 +00:00
|
|
|
extra_networks_tabs[tabname_full].applyFilter();
|
2024-04-09 11:19:07 +00:00
|
|
|
}, SEARCH_INPUT_DEBOUNCE_TIME_MS);
|
2023-07-16 05:38:23 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
window.addEventListener("keyup", event => {
|
|
|
|
const controls = event.target.closest(".extra-network-controls");
|
|
|
|
if (isElement(controls)) {
|
|
|
|
const tabname_full = controls.dataset.tabnameFull;
|
|
|
|
const target = event.target.closest(".extra-network-control--search-text");
|
|
|
|
if (isElement(target)) {
|
|
|
|
search_input_debounce.call(target, tabname_full);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2023-07-16 05:38:23 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
window.addEventListener("keydown", event => {
|
|
|
|
if (event.key === "Escape") {
|
|
|
|
closePopup();
|
2023-07-16 05:38:23 +00:00
|
|
|
}
|
|
|
|
});
|
2024-04-27 16:37:17 +00:00
|
|
|
|
|
|
|
const click_event_map = {
|
|
|
|
".tree-list-item--file": extraNetworksTreeFileOnClick,
|
|
|
|
".card": extraNetworksCardOnClick,
|
|
|
|
".copy-path-button": extraNetworksBtnCopyPathOnClick,
|
|
|
|
".edit-button": extraNetworksBtnEditMetadataOnClick,
|
|
|
|
".metadata-button": extraNetworksBtnShowMetadataOnClick,
|
|
|
|
".extra-network-control--search-clear": extraNetworksControlSearchClearOnClick,
|
|
|
|
".extra-network-control--sort-mode": extraNetworksControlSortModeOnClick,
|
|
|
|
".extra-network-control--sort-dir": extraNetworksControlSortDirOnClick,
|
|
|
|
".extra-network-control--dirs-view": extraNetworksControlDirsViewOnClick,
|
|
|
|
".extra-network-control--tree-view": extraNetworksControlTreeViewOnClick,
|
|
|
|
".extra-network-control--refresh": extraNetworksControlRefreshOnClick,
|
|
|
|
};
|
|
|
|
|
|
|
|
window.addEventListener("click", event => {
|
|
|
|
for (const [selector, handler] of Object.entries(click_event_map)) {
|
|
|
|
if (event.target.closest(selector)) {
|
|
|
|
handler(event);
|
|
|
|
}
|
|
|
|
}
|
2024-04-29 21:51:16 +00:00
|
|
|
});
|
|
|
|
|
2024-04-30 18:27:13 +00:00
|
|
|
// Order in these maps matters since we may have separate events for both a div
|
|
|
|
// and for a child within that div however if the child is clicked then we wouldn't
|
|
|
|
// want to handle clicks for the parent as well. In this case, order the child's event
|
|
|
|
// before the parent and the parent will be ignored.
|
|
|
|
// Can add entries with handler=null to forcefully ignore specific event types.
|
2024-04-29 21:51:16 +00:00
|
|
|
|
2024-04-30 18:27:13 +00:00
|
|
|
const short_press_event_map = [
|
|
|
|
{
|
2024-04-30 18:29:06 +00:00
|
|
|
selector: ".tree-list-item-action--chevron",
|
|
|
|
handler: extraNetworksBtnTreeViewChevronOnClick,
|
2024-04-30 18:27:13 +00:00
|
|
|
},
|
|
|
|
{
|
2024-04-30 18:29:06 +00:00
|
|
|
selector: ".tree-list-item--dir",
|
|
|
|
negative: ".tree-list-item-action",
|
|
|
|
handler: extraNetworksTreeDirectoryOnClick,
|
2024-04-30 18:27:13 +00:00
|
|
|
},
|
|
|
|
{
|
2024-04-30 18:29:06 +00:00
|
|
|
selector: ".extra-network-dirs-view-button",
|
|
|
|
handler: extraNetworksBtnDirsViewItemOnClick,
|
2024-04-30 18:27:13 +00:00
|
|
|
},
|
|
|
|
];
|
2024-04-29 21:51:16 +00:00
|
|
|
|
2024-04-30 18:27:13 +00:00
|
|
|
const long_press_event_map = [
|
|
|
|
{
|
2024-04-30 18:29:06 +00:00
|
|
|
selector: ".tree-list-item-action--chevron",
|
|
|
|
handler: extraNetworksTreeDirectoryChevronOnLongPress,
|
2024-04-30 18:27:13 +00:00
|
|
|
},
|
|
|
|
{
|
2024-04-30 18:29:06 +00:00
|
|
|
selector: ".tree-list-item--dir",
|
|
|
|
negative: ".tree-list-item-action",
|
|
|
|
handler: extraNetworksTreeDirectoryOnLongPress,
|
2024-04-30 18:27:13 +00:00
|
|
|
},
|
|
|
|
{
|
2024-04-30 18:29:06 +00:00
|
|
|
selector: ".extra-network-dirs-view-button",
|
|
|
|
handler: extraNetworksBtnDirsViewItemOnLongPress,
|
2024-04-30 18:27:13 +00:00
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
const dbl_press_event_map = [
|
|
|
|
{
|
2024-04-30 18:29:06 +00:00
|
|
|
selector: ".tree-list-item--dir",
|
|
|
|
negative: ".tree-list-item-action",
|
|
|
|
handler: extraNetworksTreeDirectoryOnDblClick,
|
2024-04-30 18:27:13 +00:00
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
const on_short_press = (event, elem, handler) => {
|
|
|
|
if (!handler) {
|
|
|
|
return;
|
2024-04-29 21:51:16 +00:00
|
|
|
}
|
|
|
|
// Toggle
|
|
|
|
if (elem.classList.contains("long-pressed")) {
|
|
|
|
elem.classList.remove("long-pressed");
|
|
|
|
delete elem.dataset.selected;
|
|
|
|
delete elem.dataset.recurse;
|
|
|
|
} else {
|
|
|
|
elem.classList.toggle("short-pressed");
|
|
|
|
elem.toggleAttribute("data-selected");
|
|
|
|
}
|
|
|
|
|
|
|
|
elem.dispatchEvent(new Event("shortpress", event));
|
2024-04-30 18:27:13 +00:00
|
|
|
handler(event);
|
2024-04-29 21:51:16 +00:00
|
|
|
};
|
|
|
|
|
2024-04-30 18:27:13 +00:00
|
|
|
const on_long_press = (event, elem, handler) => {
|
|
|
|
if (!handler) {
|
|
|
|
return;
|
2024-04-29 21:51:16 +00:00
|
|
|
}
|
|
|
|
// If long pressed, we deselect.
|
|
|
|
// Else we set as long pressed.
|
2024-04-30 19:12:51 +00:00
|
|
|
if (elem.classList.contains("long-pressed")) {
|
|
|
|
elem.classList.remove("long-pressed");
|
|
|
|
delete elem.dataset.recurse;
|
|
|
|
// Don't want to remove selected state if btn was previously short-pressed.
|
|
|
|
if (!elem.classList.contains("short-pressed")) {
|
|
|
|
delete elem.dataset.selected;
|
|
|
|
}
|
2024-04-29 21:51:16 +00:00
|
|
|
} else {
|
|
|
|
elem.classList.toggle("long-pressed");
|
2024-04-30 19:12:51 +00:00
|
|
|
elem.dataset.selected = "";
|
|
|
|
elem.dataset.recurse = "";
|
2024-04-29 21:51:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
elem.dispatchEvent(new Event("longpress", event));
|
2024-04-30 18:27:13 +00:00
|
|
|
handler(event);
|
2024-04-29 21:51:16 +00:00
|
|
|
};
|
|
|
|
|
2024-04-30 18:27:13 +00:00
|
|
|
const on_dbl_press = (event, elem, handler) => {
|
|
|
|
if (!handler) {
|
|
|
|
return;
|
2024-04-29 21:51:16 +00:00
|
|
|
}
|
2024-04-30 18:27:13 +00:00
|
|
|
handler(event);
|
2024-04-30 18:29:06 +00:00
|
|
|
};
|
2024-04-29 21:51:16 +00:00
|
|
|
|
|
|
|
let press_timer;
|
|
|
|
let press_time_ms = 800;
|
|
|
|
|
|
|
|
window.addEventListener("mousedown", event => {
|
2024-04-30 18:27:13 +00:00
|
|
|
for (const obj of short_press_event_map) {
|
|
|
|
const elem = event.target.closest(obj.selector);
|
|
|
|
const neg = obj.negative ? event.target.closest(obj.negative) : null;
|
|
|
|
if (elem && !neg) {
|
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
|
|
|
elem.classList.add("pressed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const obj of long_press_event_map) {
|
|
|
|
const elem = event.target.closest(obj.selector);
|
|
|
|
const neg = obj.negative ? event.target.closest(obj.negative) : null;
|
|
|
|
if (elem && !neg) {
|
2024-04-29 21:51:16 +00:00
|
|
|
event.preventDefault();
|
2024-04-30 18:27:13 +00:00
|
|
|
event.stopPropagation();
|
|
|
|
elem.classList.add("pressed");
|
|
|
|
press_timer = setTimeout(() => {
|
|
|
|
elem.classList.remove("pressed");
|
|
|
|
on_long_press(event, elem, obj.handler);
|
|
|
|
}, press_time_ms);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const obj of dbl_press_event_map) {
|
|
|
|
const elem = event.target.closest(obj.selector);
|
|
|
|
const neg = obj.negative ? event.target.closest(obj.negative) : null;
|
|
|
|
if (elem && !neg) {
|
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
2024-04-29 21:51:16 +00:00
|
|
|
elem.classList.add("pressed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
window.addEventListener("mouseup", event => {
|
2024-04-30 18:27:13 +00:00
|
|
|
for (const obj of short_press_event_map) {
|
|
|
|
const elem = event.target.closest(obj.selector);
|
|
|
|
const neg = obj.negative ? event.target.closest(obj.negative) : null;
|
|
|
|
if (elem && !neg) {
|
2024-04-29 21:51:16 +00:00
|
|
|
event.preventDefault();
|
2024-04-30 18:27:13 +00:00
|
|
|
event.stopPropagation();
|
2024-04-29 21:51:16 +00:00
|
|
|
clearTimeout(press_timer);
|
|
|
|
if (elem.classList.contains("pressed")) {
|
2024-04-30 18:29:06 +00:00
|
|
|
if (event.detail === 1 ||
|
|
|
|
!dbl_press_event_map.map(x => x.selector).includes(obj.selector)
|
2024-04-30 18:27:13 +00:00
|
|
|
) {
|
|
|
|
elem.classList.remove("pressed");
|
|
|
|
on_short_press(event, elem, obj.handler);
|
2024-04-29 21:51:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-04-30 18:27:13 +00:00
|
|
|
|
2024-04-30 18:55:33 +00:00
|
|
|
if (event.detail % 2 === 0) {
|
2024-04-30 18:27:13 +00:00
|
|
|
for (const obj of dbl_press_event_map) {
|
|
|
|
const elem = event.target.closest(obj.selector);
|
|
|
|
const neg = obj.negative ? event.target.closest(obj.negative) : null;
|
|
|
|
if (elem && !neg) {
|
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
|
|
|
clearTimeout(press_timer);
|
|
|
|
if (elem.classList.contains("pressed")) {
|
|
|
|
elem.classList.remove("pressed");
|
|
|
|
on_dbl_press(event, elem, obj.handler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-05-03 18:41:27 +00:00
|
|
|
// NOTE: long_press_event_map is handled by the timer setup in "mousedown" handlers.
|
2024-04-29 21:51:16 +00:00
|
|
|
});
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
async function extraNetworksSetupTab(tabname) {
|
2024-04-09 11:19:07 +00:00
|
|
|
const this_tab = await waitForElement(`#${tabname}_extra_tabs`);
|
|
|
|
const tab_nav = await waitForElement(`#${tabname}_extra_tabs > div.tab-nav`);
|
2024-04-12 16:40:20 +00:00
|
|
|
const controls_div = document.createElement("div");
|
|
|
|
|
|
|
|
controls_div.id = `${tabname}_extra_network_controls_div`;
|
2024-04-09 11:19:07 +00:00
|
|
|
controls_div.classList.add("extra-network-controls-div");
|
|
|
|
tab_nav.appendChild(controls_div);
|
|
|
|
tab_nav.insertBefore(controls_div, null);
|
2024-04-14 18:52:17 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
const panes = this_tab.querySelectorAll(`:scope > .tabitem[id^="${tabname}_"]`);
|
|
|
|
for (const pane of panes) {
|
2024-04-12 16:40:20 +00:00
|
|
|
const tabname_full = pane.id;
|
|
|
|
const extra_networks_tabname = tabname_full.replace(`${tabname}_`, "");
|
|
|
|
extra_networks_tabs[tabname_full] = new ExtraNetworksTab({
|
|
|
|
tabname: tabname,
|
|
|
|
extra_networks_tabname: extra_networks_tabname,
|
|
|
|
});
|
|
|
|
await extra_networks_tabs[tabname_full].setup(pane, controls_div);
|
2024-04-09 11:19:07 +00:00
|
|
|
}
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
|
2024-04-09 21:26:56 +00:00
|
|
|
async function extraNetworksSetup() {
|
2024-04-09 11:19:07 +00:00
|
|
|
await waitForBool(initialUiOptionsLoaded);
|
|
|
|
|
2024-04-12 16:40:20 +00:00
|
|
|
await Promise.all([
|
|
|
|
extraNetworksSetupTab('txt2img'),
|
|
|
|
extraNetworksSetupTab('img2img'),
|
|
|
|
]);
|
2024-04-14 18:43:31 +00:00
|
|
|
|
2024-04-09 11:19:07 +00:00
|
|
|
extraNetworksSetupEventDelegators();
|
2024-04-09 21:26:56 +00:00
|
|
|
}
|
2024-04-09 11:19:07 +00:00
|
|
|
|
|
|
|
onUiLoaded(extraNetworksSetup);
|
2024-03-26 17:58:21 +00:00
|
|
|
onOptionsChanged(() => initialUiOptionsLoaded.state = true);
|