From 4e372591683b7a58711711d4665146639582e89b Mon Sep 17 00:00:00 2001 From: Christian Bastian <80225746+cdb-boop@users.noreply.github.com> Date: Fri, 19 Jul 2024 01:45:19 -0400 Subject: [PATCH] Reorganized model info into tabs. - Changed save button to floppy disk emoji. - Fixed search bug where scroll was not resetting to the top. --- __init__.py | 52 +---- web/model-manager.css | 40 +++- web/model-manager.js | 502 +++++++++++++++++++++++++++--------------- 3 files changed, 368 insertions(+), 226 deletions(-) diff --git a/__init__.py b/__init__.py index feb6ebf..313c988 100644 --- a/__init__.py +++ b/__init__.py @@ -755,8 +755,8 @@ async def get_model_info(request): stats = pathlib.Path(abs_path).stat() date_format = "%Y-%m-%d %H:%M:%S" date_modified = datetime.fromtimestamp(stats.st_mtime).strftime(date_format) - info["Date Modified"] = date_modified - info["Date Created"] = datetime.fromtimestamp(stats.st_ctime).strftime(date_format) + #info["Date Modified"] = date_modified + #info["Date Created"] = datetime.fromtimestamp(stats.st_ctime).strftime(date_format) model_extensions = folder_paths_get_supported_pt_extensions(model_type) abs_name , _ = split_valid_ext(abs_path, model_extensions) @@ -774,8 +774,6 @@ async def get_model_info(request): header = get_safetensor_header(abs_path) metadata = header.get("__metadata__", None) - #json.dump(metadata, sys.stdout, indent=4) - #print() if metadata is not None and info.get("Preview", None) is None: thumbnail = metadata.get("modelspec.thumbnail") @@ -790,41 +788,10 @@ async def get_model_info(request): } if metadata is not None: - train_end = metadata.get("modelspec.date", "").replace("T", " ") - train_start = metadata.get("ss_training_started_at", "") - if train_start != "": - try: - train_start = float(train_start) - train_start = datetime.fromtimestamp(train_start).strftime(date_format) - except: - train_start = "" - info["Date Trained"] = ( - train_start + - (" ... " if train_start != "" and train_end != "" else "") + - train_end - ) - info["Base Training Model"] = metadata.get("ss_sd_model_name", "") - info["Base Model"] = metadata.get("ss_base_model_version", "") - info["Architecture"] = metadata.get("modelspec.architecture", "") - info["Network Dimension"] = metadata.get("ss_network_dim", "") # features trained - info["Network Alpha"] = metadata.get("ss_network_alpha", "") # trained features applied - info["Model Sampling Type"] = metadata.get("modelspec.prediction_type", "") - clip_skip = metadata.get("ss_clip_skip", "") - if clip_skip == "None" or clip_skip == "1": # assume 1 means no clip skip - clip_skip = "" - info["Clip Skip"] = clip_skip - - # it is unclear what these are - #info["Hash SHA256"] = metadata.get("modelspec.hash_sha256", "") - #info["SSHS Model Hash"] = metadata.get("sshs_model_hash", "") - #info["SSHS Legacy Hash"] = metadata.get("sshs_legacy_hash", "") - #info["New SD Model Hash"] = metadata.get("ss_new_sd_model_hash", "") - - #info["Output Name"] = metadata.get("ss_output_name", "") - #info["Title"] = metadata.get("modelspec.title", "") - info["Author"] = metadata.get("modelspec.author", "") - info["License"] = metadata.get("modelspec.license", "") + info["Base Model Version"] = metadata.get("ss_base_model_version", "") + info["Network Dimension"] = metadata.get("ss_network_dim", "") + info["Network Alpha"] = metadata.get("ss_network_alpha", "") if metadata is not None: training_comment = metadata.get("ss_training_comment", "") @@ -841,7 +808,6 @@ async def get_model_info(request): if os.path.isfile(info_text_file): with open(info_text_file, 'r', encoding="utf-8") as f: notes = f.read() - info["Notes"] = notes if metadata is not None: img_buckets = metadata.get("ss_bucket_info", "{}") @@ -859,6 +825,8 @@ async def get_model_info(request): resolutions.sort(key=lambda x: x[1], reverse=True) info["Bucket Resolutions"] = resolutions + tags = None + if metadata is not None: dir_tags = metadata.get("ss_tag_frequency", "{}") if type(dir_tags) is str: dir_tags = json.loads(dir_tags) @@ -868,10 +836,14 @@ async def get_model_info(request): tags[tag] = tags.get(tag, 0) + count tags = list(tags.items()) tags.sort(key=lambda x: x[1], reverse=True) - info["Tags"] = tags result["success"] = True result["info"] = info + if metadata is not None: + result["metadata"] = metadata + if tags is not None: + result["tags"] = tags + result["notes"] = notes return web.json_response(result) diff --git a/web/model-manager.css b/web/model-manager.css index 6c94a69..240be9a 100644 --- a/web/model-manager.css +++ b/web/model-manager.css @@ -72,7 +72,12 @@ width: 100%; } -.model-manager button, +.model-manager button { + margin: 0; + border: 2px solid var(--border-color); +} + +.model-manager button:not(.icon-button), .model-manager select, .model-manager input { padding: 4px 8px; @@ -121,6 +126,7 @@ } .model-manager .icon-button { + padding: 0; height: 40px; width: 40px; line-height: 1.15; @@ -174,13 +180,13 @@ color: var(--fg-color); } -.model-manager .model-manager-tabs { +.model-manager .model-tab-group { display: flex; gap: 4px; height: 40px; } -.model-manager .model-manager-tabs .head-item { +.model-manager .model-tab-group .tab-button { background-color: var(--comfy-menu-bg); border: 2px solid var(--border-color); border-bottom: none; @@ -192,7 +198,7 @@ z-index: 1; } -.model-manager .model-manager-tabs .head-item.active { +.model-manager .model-tab-group .tab-button.active { background-color: var(--bg-color); cursor: default; position: relative; @@ -233,7 +239,6 @@ height: 100%; overflow-wrap: break-word; overflow-y: auto; - padding: 20px; position: relative; } @@ -244,6 +249,31 @@ width: auto; } +.model-manager .model-metadata { + table-layout: fixed; + text-align: left; + width: 100%; +} + +.model-manager .model-metadata-key { + overflow-wrap: break-word; + width: 20%; +} + +.model-manager .model-metadata-value { + overflow-wrap: anywhere; + width: 80%; +} + +.model-manager table { + border-collapse: collapse; +} + +.model-manager th { + border: 1px solid; + padding: 4px 8px; +} + /* download tab */ .model-manager .download-model-infos { diff --git a/web/model-manager.js b/web/model-manager.js index 5009b0c..09f0f56 100644 --- a/web/model-manager.js +++ b/web/model-manager.js @@ -269,6 +269,98 @@ function $radioGroup(attr) { return $el("div.comfy-radio-group", radioGroup); } +/** + * @param {{name: string, icon: string, tabContent: HTMLDivElement}[]} tabData + * @returns {[Record[], Record[]]} + */ +function GenerateTabGroup(tabData) { + /** @type {HTMLDivElement[]} */ + const tabContents = tabData.map((data) => { + return $el("div", { + dataset: { + name: data.name, + icon: data.icon, // TODO: remove this; not needed + } + }, [ + data.tabContent + ]); + }); + + /** @type {Record} */ + const tabButton = {}; + + /** @type {Record} */ + const tabContent = {}; + + const ACTIVE_TAB_CLASS = "active"; + + /** @type {HTMLDivElement[]} */ + const tabButtons = tabContents.map((content) => { + const name = content.getAttribute("data-name"); + const icon = content.getAttribute("data-icon"); + /** @type {HTMLDivElement} */ + const tab = $el("div.tab-button", { + dataset: { name: name, icon: icon }, + onclick: () => { + Object.keys(tabButton).forEach((key) => { + if (name === key) { + tabButton[key].classList.add(ACTIVE_TAB_CLASS); + tabContent[key].style.display = ""; + } else { + tabButton[key].classList.remove(ACTIVE_TAB_CLASS); + tabContent[key].style.display = "none"; + } + }); + }, + }, + [name], + ); + tabButton[name] = tab; + tabContent[name] = content; + return tab; + }); + + return [tabButtons, tabContents]; +} + +/** + * @param {HTMLDivElement} element + * @param {Record[]} tabButtons + */ +function GenerateDynamicTabTextCallback(element, tabButtons, minWidth) { + return () => { + if (element.style.display === "none") { + return; + } + const managerRect = element.getBoundingClientRect(); + const isNarrow = managerRect.width < minWidth; // TODO: `minWidth` is a magic value + tabButtons.forEach((tabButton) => { + const attribute = isNarrow ? "data-icon" : "data-name"; + tabButton.innerText = tabButton.getAttribute(attribute); + }); + }; +} + +/** + * + * @param {[String, int][]} map + * @returns {String} + */ +function TagCountMapToParagraph(map) { + let text = "

"; + for (let i = 0; i < map.length; i++) { + const v = map[i]; + const tag = v[0]; + const count = v[1]; + text += tag + " (" + count + ")"; + if (i !== map.length - 1) { + text += ", "; + } + } + text += "

"; + return text; +} + class ImageSelect { /** @constant {string} */ #PREVIEW_DEFAULT = "Default"; /** @constant {string} */ #PREVIEW_UPLOAD = "Upload"; @@ -1703,11 +1795,13 @@ class ModelGrid { } } -class ModelInfoView { +class ModelInfo { /** @type {HTMLDivElement} */ element = null; elements = { + /** @type {Record[]} */ tabButtons: null, + /** @type {Record[]} */ tabContents: null, /** @type {HTMLDivElement} */ info: null, /** @type {HTMLTextAreaElement} */ notes: null, /** @type {HTMLButtonElement} */ setPreviewButton: null, @@ -1916,6 +2010,13 @@ class ModelInfoView { "data-path": "", }), ]); + + [this.elements.tabButtons, this.elements.tabContents] = GenerateTabGroup([ + { name: "Overview", icon: "ⓘ", tabContent: this.element }, + { name: "Metadata", icon: "📄", tabContent: $el("div", ["Metadata"]) }, + { name: "Tags", icon: "🏷️", tabContent: $el("div", ["Tags"]) }, + { name: "Notes", icon: "✏️", tabContent: $el("div", ["Notes"]) }, + ]); } /** @returns {void} */ @@ -1985,22 +2086,28 @@ class ModelInfoView { */ async update(searchPath, updateModels, searchSeparator) { const path = encodeURIComponent(searchPath); - const info = await request(`/model-manager/model/info?path=${path}`) - .then((result) => { - const success = result["success"]; - const message = result["alert"]; - if (message !== undefined) { - window.alert(message); - } - if (!success) { + const [info, metadata, tags, noteText] = await request(`/model-manager/model/info?path=${path}`) + .then((result) => { + const success = result["success"]; + const message = result["alert"]; + if (message !== undefined) { + window.alert(message); + } + if (!success) { + return undefined; + } + return [ + result["info"], + result["metadata"], + result["tags"], + result["notes"] + ]; + }) + .catch((err) => { + console.log(err); return undefined; } - return result["info"]; - }) - .catch((err) => { - console.log(err); - return undefined; - }); + ); if (info === undefined || info === null) { return; } @@ -2107,7 +2214,7 @@ class ModelInfoView { setPreviewButton, ]), ]), - $el("h2", ["Details:"]), + $el("h2", ["File Info:"]), $el("div", (() => { const elements = []; @@ -2117,45 +2224,17 @@ class ModelInfoView { } if (Array.isArray(value)) { + // currently only used for "Bucket Resolutions" if (value.length > 0) { elements.push($el("h2", [key + ":"])); - - let text = "

"; - for (let i = 0; i < value.length; i++) { - const v = value[i]; - const tag = v[0]; - const count = v[1]; - text += tag + " (" + count + ")"; - if (i !== value.length - 1) { - text += ", "; - } - } - text += "

"; + const text = TagCountMapToParagraph(value); const div = $el("div"); div.innerHTML = text; elements.push(div); } } else { - if (key === "Notes") { - elements.push($el("h2", [key + ":"])); - const notes = $el("textarea.comfy-multiline-input", { - name: "model notes", - value: value, - rows: 12, - }); - this.elements.notes = notes; - this.#savedNotesValue = value; - elements.push($el("button", { - textContent: "Save Notes", - onclick: async (e) => { - const saved = await this.trySave(false); - buttonAlert(e.target, saved); - }, - })); - elements.push(notes); - } - else if (key === "Description") { + if (key === "Description") { if (value !== "") { elements.push($el("h2", [key + ":"])); elements.push($el("p", [value])); @@ -2177,6 +2256,86 @@ class ModelInfoView { ])); infoHtml.append.apply(infoHtml, innerHtml); // TODO: set default value of dropdown and value to model type? + + /** @type {HTMLDivElement} */ + const metadataElement = this.elements.tabContents[1]; // TODO: remove magic value + const isMetadata = typeof metadata === 'object' && metadata !== null && Object.keys(metadata).length > 0; + metadataElement.innerHTML = ""; + metadataElement.append.apply(metadataElement, [ + $el("h1", ["Metadata"]), + $el("div", (() => { + const tableRows = []; + if (isMetadata) { + for (const [key, value] of Object.entries(metadata)) { + if (value === undefined || value === null) { + continue; + } + if (value !== "") { + tableRows.push($el("tr", [ + $el("th.model-metadata-key", [key]), + $el("th.model-metadata-value", [value]), + ])); + } + } + } + return $el("table.model-metadata", tableRows); + })(), + ), + ]); + const metadataButton = this.elements.tabButtons[1]; // TODO: remove magic value + metadataButton.style.display = isMetadata ? "" : "none"; + + /** @type {HTMLDivElement} */ + const tagsElement = this.elements.tabContents[2]; // TODO: remove magic value + const isTags = Array.isArray(tags) && tags.length > 0; + tagsElement.innerHTML = ""; + tagsElement.append.apply(tagsElement, [ + $el("h1", ["Tags"]), + $el("div", (() => { + const elements = []; + if (isTags) { + let text = TagCountMapToParagraph(tags); + const div = $el("div"); + div.innerHTML = text; + elements.push(div); + } + return elements; + })(), + ), + ]); + const tagButton = this.elements.tabButtons[2]; // TODO: remove magic value + tagButton.style.display = isTags ? "" : "none"; + + /** @type {HTMLDivElement} */ + const notesElement = this.elements.tabContents[3]; // TODO: remove magic value + notesElement.innerHTML = ""; + notesElement.append.apply(notesElement, [ + $el("div", (() => { + const notes = $el("textarea.comfy-multiline-input", { + name: "model notes", + value: noteText, + rows: 14, + }); + this.elements.notes = notes; + this.#savedNotesValue = noteText; + return [ + $el("div.row", { + style: { margin: "8px 0px 16px" }, + }, [ + $el("h1", { style: { margin: "0px" } }, ["Notes"]), + $el("button.icon-button", { + textContent: "💾", + onclick: async (e) => { + const saved = await this.trySave(false); + buttonAlert(e.target, saved); + }, + }), + ]), + notes, + ]; + })(), + ), + ]); } } @@ -2585,7 +2744,7 @@ async function getModelInfos(urlText) { return { "images": [], "fileName": file["name"], - "modelType": DownloadTab.modelTypeToComfyUiDirectory(file["type"], "") ?? "", + "modelType": DownloadView.modelTypeToComfyUiDirectory(file["type"], "") ?? "", "downloadUrl": file["download"], "downloadFilePath": "", "description": file["description"], @@ -2598,7 +2757,7 @@ async function getModelInfos(urlText) { })(); } -class DownloadTab { +class DownloadView { /** @type {HTMLDivElement} */ element = null; @@ -2709,8 +2868,8 @@ class DownloadTab { ); const comfyUIModelType = ( - DownloadTab.modelTypeToComfyUiDirectory(info["details"]["fileType"]) ?? - DownloadTab.modelTypeToComfyUiDirectory(info["modelType"]) ?? + DownloadView.modelTypeToComfyUiDirectory(info["details"]["fileType"]) ?? + DownloadView.modelTypeToComfyUiDirectory(info["modelType"]) ?? "" ); const searchSeparator = modelData.searchSeparator; @@ -2870,7 +3029,7 @@ class DownloadTab { } } -class ModelTab { +class BrowseView { /** @type {HTMLDivElement} */ element = null; @@ -2906,9 +3065,10 @@ class ModelTab { * @param {() => Promise} updateModels * @param {ModelData} modelData * @param {(searchPath: string) => Promise} showModelInfo + * @param {() => void} updateModelGridCallback * @param {any} settingsElements */ - constructor(updateModels, modelData, showModelInfo, settingsElements) { + constructor(updateModels, modelData, showModelInfo, updateModelGridCallback, settingsElements) { /** @type {HTMLDivElement} */ const modelGrid = $el("div.comfy-grid"); this.elements.modelGrid = modelGrid; @@ -2947,7 +3107,7 @@ class ModelTab { this.elements.modelContentFilter, showModelInfo, ); - this.element.parentElement.scrollTop = 0; + updateModelGridCallback(); } this.updateModelGrid = updateModelGrid; @@ -3009,7 +3169,7 @@ class ModelTab { } } -class SettingsTab { +class SettingsView { /** @type {HTMLDivElement} */ element = null; @@ -3285,7 +3445,6 @@ class SidebarButtons { $: (el) => (this.element = el), }, [ - $el("button.icon-button", { textContent: "◨", onclick: (event) => this.#setSidebar(event), @@ -3313,23 +3472,29 @@ class ModelManager extends ComfyDialog { /** @type {ModelData} */ #modelData = null; - /** @type {ModelInfoView} */ - #modelInfoView = null; + /** @type {ModelInfo} */ + #modelInfo = null; - /** @type {DownloadTab} */ - #downloadTab = null; + /** @type {DownloadView} */ + #downloadView = null; - /** @type {ModelTab} */ - #modelTab = null; + /** @type {BrowseView} */ + #browseView = null; - /** @type {SettingsTab} */ - #settingsTab = null; + /** @type {SettingsView} */ + #settingsView = null; /** @type {HTMLDivElement} */ - #tabs = null; + #tabManagerButtons = null; /** @type {HTMLDivElement} */ - #tabContents = null; + #tabManagerContents = null; + + /** @type {HTMLDivElement} */ + #tabInfoButtons = null; + + /** @type {HTMLDivElement} */ + #tabInfoContents = null; /** @type {HTMLButtonElement} */ #closeModelInfoButton = null; @@ -3339,106 +3504,38 @@ class ModelManager extends ComfyDialog { this.#modelData = new ModelData(); - const modelInfoView = new ModelInfoView( + this.#settingsView = new SettingsView( + this.#refreshModels, + ); + + this.#modelInfo = new ModelInfo( this.#modelData, this.#refreshModels, ); - this.#modelInfoView = modelInfoView; - const settingsTab = new SettingsTab( - this.#refreshModels, - ); - this.#settingsTab = settingsTab; - - const ACTIVE_TAB_CLASS = "active"; - - /** - * @param {searchPath: string} - * @return {Promise} - */ - const showModelInfo = async(searchPath) => { - await this.#modelInfoView.update( - searchPath, - this.#refreshModels, - this.#modelData.searchSeparator - ).then(() => { - this.#tabs.style.display = "none"; - this.#tabContents.style.display = "none"; - this.#closeModelInfoButton.style.display = ""; - this.#modelInfoView.show(); - }); - } - - const modelTab = new ModelTab( + this.#browseView = new BrowseView( this.#refreshModels, this.#modelData, - showModelInfo, - this.#settingsTab.elements.settings, // TODO: decouple settingsData from elements? + this.#showModelInfo, + this.#resetManagerContentsScroll, + this.#settingsView.elements.settings, // TODO: decouple settingsData from elements? ); - this.#modelTab = modelTab; - const downloadTab = new DownloadTab( + this.#downloadView = new DownloadView( this.#modelData, - this.#settingsTab.elements.settings, + this.#settingsView.elements.settings, this.#refreshModels, ); - this.#downloadTab = downloadTab; - const sidebarButtons = new SidebarButtons(this); + const [tabManagerButtons, tabManagerContents] = GenerateTabGroup([ + { name: "Download", icon: "⬇️", tabContent: this.#downloadView.element }, + { name: "Models", icon: "📁", tabContent: this.#browseView.element }, + { name: "Settings", icon: "⚙️", tabContent: this.#settingsView.element }, + ]); + tabManagerButtons[0]?.click(); - /** @type {Record} */ - const head = {}; - - /** @type {Record} */ - const body = {}; - - /** @type {HTMLDivElement[]} */ - const contents = [ - $el("div", { dataset: { name: "Download" } }, [downloadTab.element]), - $el("div", { dataset: { name: "Models" } }, [modelTab.element]), - $el("div", { dataset: { name: "Settings" } }, [settingsTab.element]), - ]; - - const tabs = contents.map((content) => { - const name = content.getAttribute("data-name"); - /** @type {HTMLDivElement} */ - const tab = $el("div.head-item", { - onclick: () => { - Object.keys(head).forEach((key) => { - if (name === key) { - head[key].classList.add(ACTIVE_TAB_CLASS); - body[key].style.display = ""; - } else { - head[key].classList.remove(ACTIVE_TAB_CLASS); - body[key].style.display = "none"; - } - }); - }, - }, - [name], - ); - head[name] = tab; - body[name] = content; - return tab; - }); - tabs[0]?.click(); - - const closeManagerButton = $el("button.icon-button", { - textContent: "✖", - onclick: async() => { - const saved = await modelInfoView.trySave(true); - if (saved) { - this.close(); - } - } - }); - - const closeModelInfoButton = $el("button.icon-button", { - $: (el) => (this.#closeModelInfoButton = el), - style: { display: "none" }, - textContent: "⬅", - onclick: async() => { await this.#tryHideModelInfo(true); }, - }); + const tabInfoButtons = this.#modelInfo.elements.tabButtons; + const tabInfoContents = this.#modelInfo.elements.tabContents; const modelManager = $el( "div.comfy-modal.model-manager", @@ -3451,48 +3548,64 @@ class ModelManager extends ComfyDialog { $el("div.model-manager-panel", [ $el("div.model-manager-head", [ $el("div.topbar-right", [ - closeManagerButton, - closeModelInfoButton, - sidebarButtons.element, + $el("button.icon-button", { + textContent: "✖", + onclick: async() => { + const saved = await this.#modelInfo.trySave(true); + if (saved) { + this.close(); + } + } + }), + $el("button.icon-button", { + $: (el) => (this.#closeModelInfoButton = el), + style: { display: "none" }, + textContent: "⬅", + onclick: async() => { await this.#tryHideModelInfo(true); }, + }), + (new SidebarButtons(this)).element, ]), $el("div.topbar-left", [ - $el("div.model-manager-tabs", { - $: (el) => (this.#tabs = el), - }, tabs), + $el("div", [ + $el("div.model-tab-group", { + $: (el) => (this.#tabManagerButtons = el), + }, tabManagerButtons), + $el("div.model-tab-group", { + $: (el) => (this.#tabInfoButtons = el), + style: { display: "none"}, + }, tabInfoButtons), + ]), ]), ]), $el("div.model-manager-body", [ - $el("div.model-manager-tab-contents", { - $: (el) => (this.#tabContents = el), - }, contents), - modelInfoView.element, + $el("div", { + $: (el) => (this.#tabManagerContents = el) + }, tabManagerContents), + $el("div", { + $: (el) => (this.#tabInfoContents = el), + style: { display: "none"}, + }, tabInfoContents), ]), ]), ]), ] ); - new ResizeObserver(() => { - if (modelManager.style.display === "none") { - return; - } - const minWidth = 768; // magic value (could easily break) - const managerRect = modelManager.getBoundingClientRect(); - const isNarrow = managerRect.width < minWidth; - let texts = isNarrow ? ["⬇️", "📁", "⚙️"] : ["Download", "Models", "Settings"]; // magic values - texts.forEach((text, i) => { - tabs[i].innerText = text; - }); - }).observe(modelManager); + new ResizeObserver(GenerateDynamicTabTextCallback(modelManager, tabManagerButtons, 768)).observe(modelManager); + new ResizeObserver(GenerateDynamicTabTextCallback(modelManager, tabInfoButtons, 768)).observe(modelManager); this.#init(); } #init() { - this.#settingsTab.reload(false); + this.#settingsView.reload(false); this.#refreshModels(); } + #resetManagerContentsScroll = () => { + this.#tabManagerContents.scrollTop = 0; + } + #refreshModels = async() => { const modelData = this.#modelData; modelData.systemSeparator = await request("/model-manager/system-separator"); @@ -3501,24 +3614,51 @@ class ModelManager extends ComfyDialog { const newModelDirectories = await request("/model-manager/models/directory-list"); modelData.directories.data.splice(0, Infinity, ...newModelDirectories); // NOTE: do NOT create a new array - this.#modelTab.updateModelGrid(); + this.#browseView.updateModelGrid(); await this.#tryHideModelInfo(false); document.getElementById("comfy-refresh-button")?.click(); } + /** + * @param {searchPath: string} + * @return {Promise} + */ + #showModelInfo = async(searchPath) => { + await this.#modelInfo.update( + searchPath, + this.#refreshModels, + this.#modelData.searchSeparator, + ).then(() => { + this.#tabManagerButtons.style.display = "none"; + this.#tabManagerContents.style.display = "none"; + + this.#closeModelInfoButton.style.display = ""; + this.#tabInfoButtons.style.display = ""; + this.#tabInfoContents.style.display = ""; + + this.#tabInfoButtons.children[0]?.click(); + this.#modelInfo.show(); + this.#tabInfoContents.scrollTop = 0; + }); + } + /** * @param {boolean} promptSave * @returns {Promise} */ #tryHideModelInfo = async(promptSave) => { - if (this.#tabContents.style.display === "none") { - if (!await this.#modelInfoView.tryHide(promptSave)) { + if (this.#tabInfoContents.style.display !== "none") { + if (!await this.#modelInfo.tryHide(promptSave)) { return false; } + this.#closeModelInfoButton.style.display = "none"; - this.#tabs.style.display = ""; - this.#tabContents.style.display = ""; + this.#tabInfoButtons.style.display = "none"; + this.#tabInfoContents.style.display = "none"; + + this.#tabManagerButtons.style.display = ""; + this.#tabManagerContents.style.display = ""; } return true; }