From b1db708af1654ccb5e9d9f834d325a161ddcb30a Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Sun, 12 Feb 2023 22:31:40 -0800 Subject: [PATCH 1/2] Model search bug fixes First batch of bug fixes for model search: - fix navigation issues with arrow keys when filtering models - fix the issue with arrow keys jumping several entries after model reloading - disable autocomplete in search box --- ui/index.html | 6 ++-- ui/media/js/searchable-models.js | 62 +++++++++++++++++++++++++------- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/ui/index.html b/ui/index.html index 734bedf9..a5c2687e 100644 --- a/ui/index.html +++ b/ui/index.html @@ -127,7 +127,7 @@ - + Click to learn more about custom models @@ -136,7 +136,7 @@ --> - + Click to learn more about VAEs @@ -208,7 +208,7 @@
- + diff --git a/ui/media/js/searchable-models.js b/ui/media/js/searchable-models.js index 7ffb4a5a..bdd81117 100644 --- a/ui/media/js/searchable-models.js +++ b/ui/media/js/searchable-models.js @@ -12,7 +12,7 @@ Merely calling getModels() makes all the magic happen behind the scene to refres HOW TO CREATE A MODEL DROPDOWN: 1) Create an input element. Make sure to add a data-path property, as this is how model dropdowns are identified in auto-save.js. - + 2) Just declare one of these for your own dropdown (remember to change the element id, e.g. #stable_diffusion_models to your own input's id). let stableDiffusionModelField = new ModelDropdown(document.querySelector('#stable_diffusion_model'), 'stable-diffusion') @@ -37,6 +37,7 @@ class ModelDropdown modelKey //= undefined flatModelList //= [] noneEntry //= '' + modelFilterInitialized //= undefined /* MIMIC A REGULAR INPUT FIELD */ get parentElement() { @@ -105,9 +106,24 @@ class ModelDropdown } } + getPreviousVisibleSibling(elem) { + let prevSibling = elem.previousElementSibling + while (prevSibling && prevSibling.classList.contains('model-file')) { + if (prevSibling.style.display == 'list-item') return prevSibling + prevSibling = prevSibling.previousElementSibling + } + if (prevSibling && prevSibling.style.display == 'list-item') return prevSibling + } + + getLastVisibleChild(elem) { + let lastElementChild = elem.lastElementChild + if (lastElementChild.style.display == 'list-item') return lastElementChild + return this.getPreviousVisibleSibling(lastElementChild) + } + findPreviousSibling(elem) { // is there an immediate sibling? - let prevSibling = elem.previousElementSibling + let prevSibling = this.getPreviousVisibleSibling(elem) if (prevSibling) { // if the previous sibling is a model file, just select it if (prevSibling.classList.contains('model-file')) return prevSibling @@ -117,19 +133,34 @@ class ModelDropdown } // no sibling model file and no sibling model folder. look for siblings around the parent element. - prevSibling = elem.parentElement.parentElement.previousElementSibling + prevSibling = this.getPreviousVisibleSibling(elem.parentElement.parentElement) if (prevSibling) { // if the previous entry is a model file, select it if (prevSibling.classList.contains('model-file')) return prevSibling // is there another model folder to jump to before the current one? - if (prevSibling.classList.contains('model-folder')) return prevSibling.firstElementChild.lastElementChild + if (prevSibling.classList.contains('model-folder')) return this.getLastVisibleChild(prevSibling.firstElementChild) } } + getNextVisibleSibling(elem) { + let nextSibling = elem.nextElementSibling + while (nextSibling && nextSibling.classList.contains('model-file')) { + if (nextSibling.style.display == 'list-item') return nextSibling + nextSibling = nextSibling.nextElementSibling + } + if (nextSibling && nextSibling.style.display == 'list-item') return nextSibling + } + + getFirstVisibleChild(elem) { + let firstElementChild = elem.firstElementChild + if (firstElementChild.style.display == 'list-item') return firstElementChild + return this.getNextVisibleSibling(firstElementChild) + } + findNextSibling(elem) { // is there an immediate sibling? - let nextSibling = elem.nextElementSibling + let nextSibling = this.getNextVisibleSibling(elem) if (nextSibling) { // if the next sibling is a model file, just select it if (nextSibling.classList.contains('model-file')) return nextSibling @@ -139,13 +170,13 @@ class ModelDropdown } // no sibling model file and no sibling model folder. look for siblings around the parent element. - nextSibling = elem.parentElement.parentElement.nextElementSibling + nextSibling = this.getNextVisibleSibling(elem.parentElement.parentElement) if (nextSibling) { // if the next entry is a model file, select it if (nextSibling.classList.contains('model-file')) return nextSibling // is there another model folder to jump to after the current one? - if (nextSibling.classList.contains('model-folder')) return nextSibling.firstElementChild.firstElementChild + if (nextSibling.classList.contains('model-folder')) return this.getFirstVisibleChild(nextSibling.firstElementChild) } } @@ -219,6 +250,7 @@ class ModelDropdown { this.saveCurrentSelection(this.highlightedModelEntry, this.highlightedModelEntry.innerText, this.highlightedModelEntry.dataset.path) this.hideModelList() + this.showAllEntries() } this.modelFilter.focus() } @@ -493,12 +525,16 @@ class ModelDropdown this.modelFilter.style.width = this.modelList.offsetWidth + 'px' this.modelFilterArrow.style.height = this.modelFilter.offsetHeight + 'px' this.modelList.style.display = 'none' - - this.modelFilter.addEventListener('input', this.bind(this.filterList, this)) - this.modelFilter.addEventListener('focus', this.bind(this.modelListFocus, this)) - this.modelFilter.addEventListener('blur', this.bind(this.hideModelList, this)) - this.modelFilter.addEventListener('click', this.bind(this.showModelList, this)) - this.modelFilter.addEventListener('keydown', this.bind(this.processKey, this)) + + if (this.modelFilterInitialized !== true) { + this.modelFilter.addEventListener('input', this.bind(this.filterList, this)) + this.modelFilter.addEventListener('focus', this.bind(this.modelListFocus, this)) + this.modelFilter.addEventListener('blur', this.bind(this.hideModelList, this)) + this.modelFilter.addEventListener('click', this.bind(this.showModelList, this)) + this.modelFilter.addEventListener('keydown', this.bind(this.processKey, this)) + + this.modelFilterInitialized = true + } this.modelFilterArrow.addEventListener('mousedown', this.bind(this.toggleModelList, this)) this.modelList.addEventListener('mousemove', this.bind(this.highlightModelAtPosition, this)) this.modelList.addEventListener('mousedown', this.bind(this.processClick, this)) From 074a14f05677be2840a608c57588f0914b4019bf Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Mon, 13 Feb 2023 01:37:00 -0800 Subject: [PATCH 2/2] Second batch of fixes for search models Addresses the issues reported by JeLuf: - - gfpgan: the list with models doesn't appear under the box - merge models: As long as no models are selected, the box is very short. - When searching for models by name, the width of the model list shrinks and is smaller than the element. --- ui/media/js/searchable-models.js | 54 +++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/ui/media/js/searchable-models.js b/ui/media/js/searchable-models.js index bdd81117..d28bbcdf 100644 --- a/ui/media/js/searchable-models.js +++ b/ui/media/js/searchable-models.js @@ -85,6 +85,14 @@ class ModelDropdown this.inputModels = modelsOptions[this.modelKey] this.populateModels() }, this)) + document.addEventListener("collapsibleClick", this.bind(function(e) { + // update the input size when the element becomes visible + this.updateInputSize() + }, this)) + document.addEventListener("tabClick", this.bind(function(e) { + // update the input size when the tab changes + this.updateInputSize() + }, this)) } saveCurrentSelection(elem, value, path) { @@ -455,6 +463,47 @@ class ModelDropdown } /* MODEL LOADER */ + getElementDimensions(element) { + // Clone the element + const clone = element.cloneNode(true) + + // Copy the styles of the original element to the cloned element + const originalStyles = window.getComputedStyle(element) + for (let i = 0; i < originalStyles.length; i++) { + const property = originalStyles[i] + clone.style[property] = originalStyles.getPropertyValue(property) + } + + // Set its visibility to hidden and display to inline-block + clone.style.visibility = "hidden" + clone.style.display = "inline-block" + + // Put the cloned element next to the original element + element.parentNode.insertBefore(clone, element.nextSibling) + + // Get its width and height + const width = clone.offsetWidth + const height = clone.offsetHeight + + // Remove it from the DOM + clone.remove() + + // Return its width and height + return { width, height } + } + + updateInputSize() { + if (this.modelList !== undefined) { + const dimensions = this.getElementDimensions(this.modelList) + this.modelFilter.style.width = dimensions.width + 'px' + this.modelList.style.width = dimensions.width + 'px' + this.modelFilterArrow.style.height = dimensions.height + 'px' + if (this.modelFilter.offsetLeft > 0) { + this.modelList.style.left = this.modelFilter.offsetLeft + 'px' + } + } + } + flattenModelList(models, path) { models.forEach(entry => { if (Array.isArray(entry)) { @@ -521,10 +570,7 @@ class ModelDropdown this.modelList = document.querySelector(`#${this.modelFilter.id}-model-list`) this.modelResult = document.querySelector(`#${this.modelFilter.id}-model-result`) this.modelNoResult = document.querySelector(`#${this.modelFilter.id}-model-no-result`) - this.modelList.style.display = 'block' - this.modelFilter.style.width = this.modelList.offsetWidth + 'px' - this.modelFilterArrow.style.height = this.modelFilter.offsetHeight + 'px' - this.modelList.style.display = 'none' + this.updateInputSize() if (this.modelFilterInitialized !== true) { this.modelFilter.addEventListener('input', this.bind(this.filterList, this))