Refactor out custom input directory dropdown auto-suggest

This commit is contained in:
Christian Bastian
2024-01-27 05:26:13 -05:00
parent 9712d2a3bb
commit f76c28e832

View File

@@ -49,30 +49,127 @@ const modelNodeType = {
"vae_approx": undefined, "vae_approx": undefined,
}; };
const dropdownSelectClass = "search-dropdown-selected"; const DROPDOWN_DIRECTORY_SELECTION_CLASS = "search-dropdown-selected";
/** /**
* @param {HTMLDivElement} dropdown * @typedef {Object} DirectoryItem
* @param {Array.<{name: string, childCount: ?int, childIndex: ?int}>} directories * @param {string} name
* @param {string} modelType * @param {number | undefined} childCount
* @param {string} filter * @param {number | undefined} childIndex
* @param {string} sep
* @param {HTMLInputElement} inputElement
* @param {Function} updateModelDropdown
* @param {Function} updateModelGrid
*/ */
function updateDirectorySuggestionDropdown(
dropdown, class DirectoryDropdown {
directories, /** @type {HTMLDivElement} */
modelType, element = undefined;
filter,
sep, /** @type {HTMLInputElement} */
inputElement, #input = undefined;
updateModelDropdown,
updateModelGrid /** @type {Function} */
) { #submitSearch = null;
let options = [];
if (filter[0] === sep) { /**
* @param {HTMLInputElement} input
* @param {Function} updateDropdown
* @param {Function} submitSearch
*/
constructor(input, updateDropdown, submitSearch) {
/** @type {HTMLDivElement} */
const dropdown = $el("div.search-dropdown", { // TODO: change to `search-directory-dropdown`
style: { display: "none" },
});
this.element = dropdown;
this.#input = input;
this.#submitSearch = submitSearch;
input.addEventListener("input", () => updateDropdown());
input.addEventListener("focus", () => updateDropdown());
input.addEventListener("blur", () => { dropdown.style.display = "none"; });
input.addEventListener(
"keydown",
(e) => {
const children = dropdown.children;
let iChild;
for (iChild = 0; iChild < children.length; iChild++) {
const child = children[iChild];
if (child.classList.contains(DROPDOWN_DIRECTORY_SELECTION_CLASS)) {
break;
}
}
if (e.key === "Escape") {
e.stopPropagation();
if (iChild < children.length) {
// remove select
const child = children[iChild];
child.classList.remove(DROPDOWN_DIRECTORY_SELECTION_CLASS);
}
else {
e.target.blur();
}
}
else if (e.key === "Enter") {
e.stopPropagation();
submitSearch(e.target, children[iChild]);
}
else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
e.stopPropagation();
let iNext = children.length;
if (iChild < children.length) {
const child = children[iChild];
child.classList.remove(DROPDOWN_DIRECTORY_SELECTION_CLASS);
const delta = e.key === "ArrowDown" ? 1 : -1;
iNext = iChild + delta;
if (0 <= iNext && iNext < children.length) {
const nextChild = children[iNext];
nextChild.classList.add(DROPDOWN_DIRECTORY_SELECTION_CLASS);
}
}
else if (iChild === children.length) {
iNext = e.key === "ArrowDown" ? 0 : children.length-1;
const nextChild = children[iNext]
nextChild.classList.add(DROPDOWN_DIRECTORY_SELECTION_CLASS);
}
if (0 <= iNext && iNext < children.length) {
let scrollTop = dropdown.scrollTop;
const dropdownHeight = dropdown.offsetHeight;
const child = children[iNext];
const childHeight = child.offsetHeight;
const childTop = child.offsetTop;
scrollTop = Math.max(scrollTop, childTop - dropdownHeight + childHeight);
scrollTop = Math.min(scrollTop, childTop);
dropdown.scrollTop = scrollTop;
}
else {
dropdown.scrollTop = 0;
const children = dropdown.children;
for (iChild = 0; iChild < children.length; iChild++) {
const child = children[iChild];
if (child.classList.contains(DROPDOWN_DIRECTORY_SELECTION_CLASS)) {
child.classList.remove(DROPDOWN_DIRECTORY_SELECTION_CLASS);
}
}
}
}
},
);
}
/**
* @param {DirectoryItem[]} directories
* @param {string} modelType
* @param {string} sep
*/
update(directories, modelType, sep) {
const dropdown = this.element;
const input = this.#input;
const submitSearch = this.#submitSearch;
const filter = input.value;
if (filter[0] !== sep) {
dropdown.style.display = "none";
return;
}
let cwd = null; let cwd = null;
const root = directories[0]; const root = directories[0];
const rootChildIndex = root["childIndex"]; const rootChildIndex = root["childIndex"];
@@ -85,22 +182,21 @@ function updateDirectorySuggestionDropdown(
} }
} }
// TODO: directories === undefined // TODO: directories === undefined?
let filterIndex0 = 1; let indexLastWord = 1;
while (true) { while (true) {
const filterIndex1 = filter.indexOf(sep, filterIndex0); const indexNextWord = filter.indexOf(sep, indexLastWord);
if (filterIndex1 === -1) { if (indexNextWord === -1) {
// end of filter // end of filter
break; break;
} }
const item = directories[cwd]; const item = directories[cwd];
if (item["childCount"] === undefined) { const childCount = item["childCount"];
if (childCount === undefined) {
// file // file
break; break;
} }
const childCount = item["childCount"];
if (childCount === 0) { if (childCount === 0) {
// directory is empty // directory is empty
break; break;
@@ -108,7 +204,7 @@ function updateDirectorySuggestionDropdown(
const childIndex = item["childIndex"]; const childIndex = item["childIndex"];
const items = directories.slice(childIndex, childIndex + childCount); const items = directories.slice(childIndex, childIndex + childCount);
const word = filter.substring(filterIndex0, filterIndex1); const word = filter.substring(indexLastWord, indexNextWord);
cwd = null; cwd = null;
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
const itemName = items[itemIndex]["name"]; const itemName = items[itemIndex]["name"];
@@ -122,82 +218,69 @@ function updateDirectorySuggestionDropdown(
// directory does not exist // directory does not exist
break; break;
} }
filterIndex0 = filterIndex1 + 1; indexLastWord = indexNextWord + 1;
}
if (cwd === null) {
dropdown.style.display = "none";
return;
} }
if (cwd !== null) { let options = [];
const lastWord = filter.substring(filterIndex0); const lastWord = filter.substring(indexLastWord);
const item = directories[cwd]; const item = directories[cwd];
if (item["childIndex"] !== undefined) { if (item["childIndex"] !== undefined) {
const childIndex = item["childIndex"]; const childIndex = item["childIndex"];
const childCount = item["childCount"]; const childCount = item["childCount"];
const items = directories.slice(childIndex, childIndex + childCount); const items = directories.slice(childIndex, childIndex + childCount);
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
const itemName = items[i]["name"]; const itemName = items[i]["name"];
if (itemName.startsWith(lastWord)) { if (itemName.startsWith(lastWord)) {
options.push(itemName); options.push(itemName);
}
}
}
else {
const filename = item["name"];
if (filename.startsWith(lastWord)) {
options.push(filename);
} }
} }
} }
else {
const setActiveMouseOver = (e) => { const filename = item["name"];
const children = dropdown.children; if (filename.startsWith(lastWord)) {
let iChild; options.push(filename);
for (iChild = 0; iChild < children.length; iChild++) {
const child = children[iChild];
if (child.classList.contains(dropdownSelectClass)) {
child.classList.remove(dropdownSelectClass);
}
} }
e.target.classList.add(dropdownSelectClass); }
}; if (options.length === 0) {
dropdown.style.display = "none";
const onMouseEnter = (e) => { return;
e.stopPropagation(); }
setActiveMouseOver(e);
};
const onMouseMove = (e) => { const selection_select = (e) => {
if (!e.target.classList.contains(dropdownSelectClass)) { const selection = e.target;
if (!selection.classList.contains(DROPDOWN_DIRECTORY_SELECTION_CLASS)) {
// assumes only one will ever selected at a time // assumes only one will ever selected at a time
e.stopPropagation(); e.stopPropagation();
setActiveMouseOver(e); const children = dropdown.children;
let iChild;
for (iChild = 0; iChild < children.length; iChild++) {
const child = children[iChild];
child.classList.remove(DROPDOWN_DIRECTORY_SELECTION_CLASS);
}
selection.classList.add(DROPDOWN_DIRECTORY_SELECTION_CLASS);
} }
}; };
const selection_deselect = (e) => {
const onMouseLeave = (e) => {
e.stopPropagation(); e.stopPropagation();
e.target.classList.remove(dropdownSelectClass); e.target.classList.remove(DROPDOWN_DIRECTORY_SELECTION_CLASS);
}; };
const selection_submit = (e) => {
const onMouseDown = (e) => {
e.stopPropagation(); e.stopPropagation();
//e.target.classList.remove(dropdownSelectClass); submitSearch(input, e.target);
appendDropdownSelectionToInput(
inputElement,
e.target,
sep,
updateModelDropdown,
updateModelGrid
);
}; };
const innerHtml = options.map((text) => { const innerHtml = options.map((text) => {
/** @type {HTMLParagraphElement} */ /** @type {HTMLParagraphElement} */
const p = $el( const p = $el(
"p", "p",
{ {
onmouseenter: (e) => onMouseEnter(e), onmouseenter: (e) => selection_select(e),
onmousemove: (e) => onMouseMove(e), onmousemove: (e) => selection_select(e),
onmouseleave: (e) => onMouseLeave(e), onmouseleave: (e) => selection_deselect(e),
onmousedown: (e) => onMouseDown(e), onmousedown: (e) => selection_submit(e),
}, },
[ [
text text
@@ -207,80 +290,8 @@ function updateDirectorySuggestionDropdown(
}); });
dropdown.innerHTML = ""; dropdown.innerHTML = "";
dropdown.append.apply(dropdown, innerHtml); dropdown.append.apply(dropdown, innerHtml);
dropdown.style.display = options.length === 0 ? "none" : "block"; dropdown.style.display = "block";
} }
else {
dropdown.style.display = "none";
}
}
/**
* @param {HTMLInputElement} inputElement
* @param {HTMLParagraphElement} child
* @param {string} sep
* @param {Function} updateModelDropdown
* @param {Function} updateModelGrid
*/
function appendDropdownSelectionToInput(inputElement, child, sep, updateModelDropdown, updateModelGrid) {
if (child !== undefined && child !== null) {
child.classList.remove(dropdownSelectClass);
const selectedText = child.innerText;
const oldFilterText = inputElement.value;
const iSep = oldFilterText.lastIndexOf(sep);
const previousPath = oldFilterText.substring(0, iSep + 1);
const newFilterText = previousPath + selectedText;
inputElement.value = newFilterText;
updateModelDropdown(); // TODO: should this be here?
}
updateModelGrid(); // TODO: GENERALIZATION -> this shouldn't be here
inputElement.blur();
}
/**
* @param {HTMLDivElement} modelGrid
* @param {Object} models
* @param {HTMLSelectElement} modelSelect
* @param {Object.<{value: string}>} previousModelType
* @param {Object} settings
* @param {Array} previousModelFilters
* @param {HTMLInputElement} modelFilter
*/
function updateModelGrid(modelGrid, models, modelSelect, previousModelType, settings, previousModelFilters, modelFilter) {
let modelType = modelSelect.value;
if (models[modelType] === undefined) {
modelType = "checkpoints"; // TODO: magic value
}
if (modelType !== previousModelType.value) {
if (settings["model-persistent-search"].checked) {
previousModelFilters.splice(0, previousModelFilters.length); // TODO: make sure this actually worked!
}
else {
// cache previous filter text
previousModelFilters[previousModelType.value] = modelFilter.value;
// read cached filter text
modelFilter.value = previousModelFilters[modelType] ?? "";
}
previousModelType.value = modelType;
}
let modelTypeOptions = [];
for (const [key, value] of Object.entries(models)) {
const el = $el("option", [key]);
modelTypeOptions.push(el);
}
modelSelect.innerHTML = "";
modelTypeOptions.forEach(option => modelSelect.add(option));
modelSelect.value = modelType;
const searchAppend = settings["model-search-always-append"].value;
const searchText = modelFilter.value + " " + searchAppend;
const modelList = ModelGrid.filter(models[modelType], searchText);
modelGrid.innerHTML = "";
const modelGridModels = ModelGrid.generateInnerHtml(modelList, modelType, settings);
modelGrid.append.apply(modelGrid, modelGridModels);
} }
/** /**
@@ -563,7 +574,7 @@ class ModelGrid {
* @param {string} searchString * @param {string} searchString
* @returns {Array} * @returns {Array}
*/ */
static filter(list, searchString) { static #filter(list, searchString) {
/** @type {string[]} */ /** @type {string[]} */
const keywords = searchString const keywords = searchString
.replace("*", " ") .replace("*", " ")
@@ -739,7 +750,7 @@ class ModelGrid {
* @param {Object.<HTMLInputElement>} settingsElements * @param {Object.<HTMLInputElement>} settingsElements
* @returns {HTMLElement[]} * @returns {HTMLElement[]}
*/ */
static generateInnerHtml(models, modelType, settingsElements) { static #generateInnerHtml(models, modelType, settingsElements) {
// TODO: seperate text and model logic; getting too messy // TODO: seperate text and model logic; getting too messy
// TODO: fallback on button failure to copy text? // TODO: fallback on button failure to copy text?
const canShowButtons = modelNodeType[modelType] !== undefined; const canShowButtons = modelNodeType[modelType] !== undefined;
@@ -802,6 +813,52 @@ class ModelGrid {
return [$el("h2", ["No Models"])]; return [$el("h2", ["No Models"])];
} }
} }
/**
* @param {HTMLDivElement} modelGrid
* @param {Object} models
* @param {HTMLSelectElement} modelSelect
* @param {Object.<{value: string}>} previousModelType
* @param {Object} settings
* @param {Array} previousModelFilters
* @param {HTMLInputElement} modelFilter
*/
static update(modelGrid, models, modelSelect, previousModelType, settings, previousModelFilters, modelFilter) {
let modelType = modelSelect.value;
if (models[modelType] === undefined) {
modelType = "checkpoints"; // TODO: magic value
}
if (modelType !== previousModelType.value) {
if (settings["model-persistent-search"].checked) {
previousModelFilters.splice(0, previousModelFilters.length); // TODO: make sure this actually worked!
}
else {
// cache previous filter text
previousModelFilters[previousModelType.value] = modelFilter.value;
// read cached filter text
modelFilter.value = previousModelFilters[modelType] ?? "";
}
previousModelType.value = modelType;
}
let modelTypeOptions = [];
for (const [key, value] of Object.entries(models)) {
const el = $el("option", [key]);
modelTypeOptions.push(el);
}
modelSelect.innerHTML = "";
modelTypeOptions.forEach(option => modelSelect.add(option));
modelSelect.value = modelType;
const searchAppend = settings["model-search-always-append"].value;
const searchText = modelFilter.value + " " + searchAppend;
const modelList = ModelGrid.#filter(models[modelType], searchText);
modelGrid.innerHTML = "";
const modelGridModels = ModelGrid.#generateInnerHtml(modelList, modelType, settings);
modelGrid.append.apply(modelGrid, modelGridModels);
}
} }
/** /**
@@ -854,14 +911,14 @@ class ModelManager extends ComfyDialog {
/** @type {HTMLDivElement} */ modelGrid: null, /** @type {HTMLDivElement} */ modelGrid: null,
/** @type {HTMLSelectElement} */ modelTypeSelect: null, /** @type {HTMLSelectElement} */ modelTypeSelect: null,
/** @type {HTMLDivElement} */ modelDirectorySearchOptions: null, /** @type {HTMLDivElement} */ searchDirectoryDropdown: null,
/** @type {HTMLInputElement} */ modelContentFilter: null, /** @type {HTMLInputElement} */ modelContentFilter: null,
/** @type {HTMLDivElement} */ sidebarButtons: null, /** @type {HTMLDivElement} */ sidebarButtons: null,
/** @type {HTMLDivElement} */ settingsTab: null, /** @type {HTMLDivElement} */ settingsTab: null,
/** @type {HTMLButtonElement} */ reloadSettingsBtn: null, /** @type {HTMLButtonElement} */ settings_reloadBtn: null,
/** @type {HTMLButtonElement} */ saveSettingsBtn: null, /** @type {HTMLButtonElement} */ settings_saveBtn: null,
settings: { settings: {
//"sidebar-default-height": null, //"sidebar-default-height": null,
//"sidebar-default-width": null, //"sidebar-default-width": null,
@@ -879,13 +936,13 @@ class ModelManager extends ComfyDialog {
#data = { #data = {
/** @type {Array} */ sources: [], /** @type {Array} */ sources: [],
/** @type {Object} */ models: {}, /** @type {Object} */ models: {},
/** @type {{name: string, childCount: ?int, childIndex: ?int}[]} */ modelDirectories: null, /** @type {DirectoryItem[]} */ modelDirectories: [],
/** @type {Array} */ previousModelFilters: [], /** @type {Array} */ previousModelFilters: [],
/** @type {Object.<{value: string}>} */ previousModelType: { value: undefined }, /** @type {Object.<{value: string}>} */ previousModelType: { value: undefined },
}; };
/** @type {string} */ /** @type {string} */
sep = "/"; #sep = "/";
/** @type {SourceList} */ /** @type {SourceList} */
#sourceList = null; #sourceList = null;
@@ -931,8 +988,8 @@ class ModelManager extends ComfyDialog {
), ),
$tabs([ $tabs([
$tab("Install", this.#createSourceInstall()), $tab("Install", this.#createSourceInstall()),
$tab("Models", this.#createModelTabHtml()), $tab("Models", this.#modelTab_new()),
$tab("Settings", this.#createSettingsTabHtml()), $tab("Settings", [this.#settingsTab_new()]),
]), ]),
]), ]),
] ]
@@ -942,9 +999,9 @@ class ModelManager extends ComfyDialog {
} }
#init() { #init() {
this.#reloadSettings(false); this.#settingsTab_reload(false);
this.#refreshSourceList(); this.#refreshSourceList();
this.#modelGridRefresh(); this.#modelTab_updateModels();
} }
/** /**
@@ -1077,118 +1134,52 @@ class ModelManager extends ComfyDialog {
); );
} }
/** @type {DirectoryDropdown} */
#modelContentFilterDirectoryDropdown = null;
/** /**
* @returns {HTMLElement[]} * @returns {HTMLElement[]}
*/ */
#createModelTabHtml() { #modelTab_new() {
/** @type {HTMLDivElement} */ /** @type {HTMLDivElement} */
const modelGrid = $el("div.comfy-grid"); const modelGrid = $el("div.comfy-grid");
this.#el.modelGrid = modelGrid; this.#el.modelGrid = modelGrid;
/** @type {HTMLDivElement} */ const searchInput = $el("input.search-text-area", {
const searchDropdown = $el("div.search-dropdown", { $: (el) => (this.#el.modelContentFilter = el),
$: (el) => (this.#el.modelDirectorySearchOptions = el), placeholder: "example: /0/1.5/styles/clothing -.pt",
style: { display: "none" },
}); });
const searchDropdown = new DirectoryDropdown(
searchInput,
this.#modelTab_updateDirectoryDropdown,
this.#modelTab_submitSearch
);
this.#modelContentFilterDirectoryDropdown = searchDropdown;
return [ return [
$el("div.row.tab-header", [ $el("div.row.tab-header", [
$el("div.row.tab-header-flex-block", [ $el("div.row.tab-header-flex-block", [
$el("button.icon-button", { $el("button.icon-button", {
type: "button", type: "button",
textContent: "⟳", textContent: "⟳",
onclick: () => this.#modelGridRefresh(), onclick: () => this.#modelTab_updateModels(),
}), }),
$el("select.model-type-dropdown", { $el("select.model-type-dropdown", {
$: (el) => (this.#el.modelTypeSelect = el), $: (el) => (this.#el.modelTypeSelect = el),
name: "model-type", name: "model-type",
onchange: () => this.#modelGridUpdate(), onchange: () => this.#modelTab_updateModelGrid(),
}), }),
]), ]),
$el("div.row.tab-header-flex-block", [ $el("div.row.tab-header-flex-block", [
$el("div.search-models", [ $el("div.search-models", [
$el("input.search-text-area", { searchInput,
$: (el) => (this.#el.modelContentFilter = el), searchDropdown.element,
placeholder: "example: /0/1.5/styles/clothing -.pt",
onkeydown: (e) => {
const children = searchDropdown.children;
let iChild;
for (iChild = 0; iChild < children.length; iChild++) {
const child = children[iChild];
if (child.classList.contains(dropdownSelectClass)) {
break;
}
}
if (e.key === "Escape") {
e.stopPropagation();
if (iChild < children.length) {
const child = children[iChild];
child.classList.remove(dropdownSelectClass);
}
else {
e.target.blur();
}
}
else if (e.key === "Enter") {
e.stopPropagation();
appendDropdownSelectionToInput(
e.target,
children[iChild],
this.sep,
this.#modelUpdateFilterDropdown,
this.#modelGridUpdate
);
}
else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
e.stopPropagation();
let iNext = children.length;
if (iChild < children.length) {
const child = children[iChild];
child.classList.remove(dropdownSelectClass);
const delta = e.key === "ArrowDown" ? 1 : -1;
iNext = iChild + delta;
if (0 <= iNext && iNext < children.length) {
const nextChild = children[iNext];
nextChild.classList.add(dropdownSelectClass);
}
}
else if (iChild === children.length) {
iNext = e.key === "ArrowDown" ? 0 : children.length-1;
const nextChild = children[iNext]
nextChild.classList.add(dropdownSelectClass);
}
if (0 <= iNext && iNext < children.length) {
let scrollTop = searchDropdown.scrollTop;
const dropdownHeight = searchDropdown.offsetHeight;
const child = children[iNext];
const childHeight = child.offsetHeight;
const childTop = child.offsetTop;
scrollTop = Math.max(scrollTop, childTop - dropdownHeight + childHeight);
scrollTop = Math.min(scrollTop, childTop);
searchDropdown.scrollTop = scrollTop;
}
else {
searchDropdown.scrollTop = 0;
const children = searchDropdown.children;
for (iChild = 0; iChild < children.length; iChild++) {
const child = children[iChild];
if (child.classList.contains(dropdownSelectClass)) {
child.classList.remove(dropdownSelectClass);
}
}
}
}
},
oninput: () => this.#modelUpdateFilterDropdown(),
onfocus: () => this.#modelUpdateFilterDropdown(),
onblur: () => { searchDropdown.style.display = "none"; },
}),
searchDropdown,
]), ]),
$el("button.icon-button", { $el("button.icon-button", {
type: "button", type: "button",
textContent: "🔍︎", textContent: "🔍︎",
onclick: () => this.#modelGridUpdate(), onclick: () => this.#modelTab_updateModelGrid(),
}), }),
]), ]),
]), ]),
@@ -1196,7 +1187,7 @@ class ModelManager extends ComfyDialog {
]; ];
} }
#modelGridUpdate = () => updateModelGrid( #modelTab_updateModelGrid = () => ModelGrid.update(
this.#el.modelGrid, this.#el.modelGrid,
this.#data.models, this.#data.models,
this.#el.modelTypeSelect, this.#el.modelTypeSelect,
@@ -1206,60 +1197,40 @@ class ModelManager extends ComfyDialog {
this.#el.modelContentFilter this.#el.modelContentFilter
); );
async #modelGridRefresh() { async #modelTab_updateModels() {
this.#data.models = await request("/model-manager/models"); this.#data.models = await request("/model-manager/models");
this.#data.modelDirectories = await request("/model-manager/model-directory-list"); this.#data.modelDirectories = await request("/model-manager/model-directory-list");
this.#modelGridUpdate(); this.#modelTab_updateModelGrid();
} }
#modelUpdateFilterDropdown = () => { #modelTab_updateDirectoryDropdown = () => {
const modelType = this.#el.modelTypeSelect.value; const modelType = this.#el.modelTypeSelect.value;
const filter = this.#el.modelContentFilter.value; this.#modelContentFilterDirectoryDropdown.update(
updateDirectorySuggestionDropdown(
this.#el.modelDirectorySearchOptions,
this.#data.modelDirectories, this.#data.modelDirectories,
modelType, modelType,
filter, this.#sep,
this.sep,
this.#el.modelContentFilter,
this.#modelUpdateFilterDropdown,
this.#modelGridUpdate,
); );
this.#data.previousModelFilters[modelType] = filter; const value = this.#el.modelContentFilter.value;
this.#data.previousModelFilters[modelType] = value;
} }
/** /**
* @param {Event} event * @param {HTMLInputElement} input
* @param {HTMLParagraphElement} selection
*/ */
#setSidebar(event) { #modelTab_submitSearch = (input, selection) => {
// TODO: settings["sidebar-default-width"] if (selection !== undefined && selection !== null) {
// TODO: settings["sidebar-default-height"] selection.classList.remove(DROPDOWN_DIRECTORY_SELECTION_CLASS);
// TODO: draggable resize? const selectedText = selection.innerText;
const button = event.target; const oldFilterText = input.value;
const modelManager = this.element; const iSep = oldFilterText.lastIndexOf(this.#sep);
const sidebarButtons = this.#el.sidebarButtons.children; const previousPath = oldFilterText.substring(0, iSep + 1);
const newFilterText = previousPath + selectedText;
let buttonIndex; input.value = newFilterText;
for (buttonIndex = 0; buttonIndex < sidebarButtons.length; buttonIndex++) { this.#modelTab_updateDirectoryDropdown();
if (sidebarButtons[buttonIndex] === button) {
break;
}
}
const sidebarStates = ["sidebar-left", "sidebar-top", "sidebar-bottom", "sidebar-right"];
let stateIndex;
for (stateIndex = 0; stateIndex < sidebarStates.length; stateIndex++) {
const state = sidebarStates[stateIndex];
if (modelManager.classList.contains(state)) {
modelManager.classList.remove(state);
break;
}
}
if (stateIndex != buttonIndex) {
const newSidebarState = sidebarStates[buttonIndex];
modelManager.classList.add(newSidebarState);
} }
input.blur();
this.#modelTab_updateModelGrid();
} }
/** /**
@@ -1285,21 +1256,21 @@ class ModelManager extends ComfyDialog {
if (reloadData) { if (reloadData) {
// Is this slow? // Is this slow?
this.#refreshSourceList(); this.#refreshSourceList();
this.#modelGridRefresh(); this.#modelTab_updateModels();
} }
} }
/** /**
* @param {boolean} reloadData * @param {boolean} reloadData
*/ */
async #reloadSettings(reloadData) { async #settingsTab_reload(reloadData) {
const data = await request("/model-manager/settings/load"); const data = await request("/model-manager/settings/load");
const settings = data["settings"]; const settings = data["settings"];
this.#setSettings(settings, reloadData); this.#setSettings(settings, reloadData);
buttonAlert(this.#el.reloadSettingsBtn, true); buttonAlert(this.#el.settings_reloadBtn, true);
} }
async #saveSettings() { async #settingsTab_save() {
let settings = {}; let settings = {};
for (const [setting, el] of Object.entries(this.#el.settings)) { for (const [setting, el] of Object.entries(this.#el.settings)) {
if (!el) { continue; } // hack if (!el) { continue; } // hack
@@ -1327,27 +1298,27 @@ class ModelManager extends ComfyDialog {
const settings = data["settings"]; const settings = data["settings"];
this.#setSettings(settings, true); this.#setSettings(settings, true);
} }
buttonAlert(this.#el.saveSettingsBtn, success); buttonAlert(this.#el.settings_saveBtn, success);
} }
/** /**
* @returns {HTMLElement[]} * @returns {HTMLElement}
*/ */
#createSettingsTabHtml() { #settingsTab_new() {
const settingsTab = $el("div.model-manager-settings", [ const settingsTab = $el("div.model-manager-settings", [
$el("h1", ["Settings"]), $el("h1", ["Settings"]),
$el("div", [ $el("div", [
$el("button", { $el("button", {
$: (el) => (this.#el.reloadSettingsBtn = el), $: (el) => (this.#el.settings_reloadBtn = el),
type: "button", type: "button",
textContent: "Reload", // ⟳ textContent: "Reload", // ⟳
onclick: () => this.#reloadSettings(true), onclick: () => this.#settingsTab_reload(true),
}), }),
$el("button", { $el("button", {
$: (el) => (this.#el.saveSettingsBtn = el), $: (el) => (this.#el.settings_saveBtn = el),
type: "button", type: "button",
textContent: "Save", // 💾︎ textContent: "Save", // 💾︎
onclick: () => this.#saveSettings(), onclick: () => this.#settingsTab_save(),
}), }),
]), ]),
/* /*
@@ -1441,6 +1412,40 @@ class ModelManager extends ComfyDialog {
this.#el.settingsTab = settingsTab; this.#el.settingsTab = settingsTab;
return [settingsTab]; return [settingsTab];
} }
/**
* @param {Event} e
*/
#setSidebar(e) {
// TODO: settings["sidebar-default-width"]
// TODO: settings["sidebar-default-height"]
// TODO: draggable resize?
const button = e.target;
const modelManager = this.element;
const sidebarButtons = this.#el.sidebarButtons.children;
let buttonIndex;
for (buttonIndex = 0; buttonIndex < sidebarButtons.length; buttonIndex++) {
if (sidebarButtons[buttonIndex] === button) {
break;
}
}
const sidebarStates = ["sidebar-left", "sidebar-top", "sidebar-bottom", "sidebar-right"];
let stateIndex;
for (stateIndex = 0; stateIndex < sidebarStates.length; stateIndex++) {
const state = sidebarStates[stateIndex];
if (modelManager.classList.contains(state)) {
modelManager.classList.remove(state);
break;
}
}
if (stateIndex != buttonIndex) {
const newSidebarState = sidebarStates[buttonIndex];
modelManager.classList.add(newSidebarState);
}
}
} }
let instance; let instance;