diff --git a/CHANGELOG.md b/CHANGELOG.md index d2ac998f8..4c989db14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ Also unlike most traditional methods, its also applicable to nearly all model ty - `INT8` -> `uint8` - `INT4_SYM` -> `int4` - `INT4` -> `uint4` - - Add `float8_e4m3fn`, `float8_e5m2`, `uint2` and `uint1` support + - Add `float8_e4m3fn`, `float8_e5m2`, `uint2` and `uint1` support - Add quantized matmul support for `float8_e4m3fn` - Set the default quant mode to `pre` - Use per token input quant with int8 and fp8 quantized matmul @@ -34,12 +34,14 @@ Also unlike most traditional methods, its also applicable to nearly all model ty - Fix scale and zero_point not being offloaded - **IPEX** - Disabe Dynamic Attention by default on PyTorch 2.7 - - Remove GradScaler hijack and use torch.amp.GradScaler instead + - Remove GradScaler hijack and use `torch.amp.GradScaler` instead - **Feature** - TeaCache support for HiDream I1 -- **Changes** +- **Changes** - Set the default attention optimizer to Scaled-Dot-Product on all backends - Enable Dynamic attention for Scaled-Dot-Product with ROCm, DirectML, MPS and CPU backends +- **Fixes** + - Gallery duplicate entries ## Update for 2025-05-17 diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 83f97ba57..aa51e20c1 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -12,11 +12,11 @@ const getENActiveTab = () => { if (gradioApp().getElementById('video_prompt')?.checkVisibility()) return 'video'; if (gradioApp().getElementById('framepack_prompt_row')?.checkVisibility()) return 'framepack'; // legacy method - if (gradioApp().getElementById('tab_txt2img').style.display === 'block') tabName = 'txt2img'; - else if (gradioApp().getElementById('tab_img2img').style.display === 'block') tabName = 'img2img'; - else if (gradioApp().getElementById('tab_control').style.display === 'block') tabName = 'control'; - else if (gradioApp().getElementById('tab_video').style.display === 'block') tabName = 'video'; - else if (gradioApp().getElementById('tab_framepack_tab').style.display === 'block') tabName = 'framepack'; + if (gradioApp().getElementById('tab_txt2img')?.style.display === 'block') tabName = 'txt2img'; + else if (gradioApp().getElementById('tab_img2img')?.style.display === 'block') tabName = 'img2img'; + else if (gradioApp().getElementById('tab_control')?.style.display === 'block') tabName = 'control'; + else if (gradioApp().getElementById('tab_video')?.style.display === 'block') tabName = 'video'; + else if (gradioApp().getElementById('tab_framepack_tab')?.style.display === 'block') tabName = 'framepack'; // log('getENActiveTab', tabName); return tabName; }; diff --git a/javascript/gallery.js b/javascript/gallery.js index cea21fa12..32ae0bde4 100644 --- a/javascript/gallery.js +++ b/javascript/gallery.js @@ -5,6 +5,8 @@ let url; let currentImage; let pruneImagesTimer; let outstanding = 0; +let lastSort = 0; +let lastSortName = 'none'; const el = { folders: undefined, files: undefined, @@ -124,9 +126,14 @@ class GalleryFile extends HTMLElement { } async connectedCallback() { - if (this.shadow.children.length > 0) return; + if (this.shadow.children.length > 0) { + return; + } const ext = this.name.split('.').pop().toLowerCase(); - if (!['jpg', 'jpeg', 'png', 'gif', 'webp', 'jxl', 'svg', 'mp4'].includes(ext)) return; + if (!['jpg', 'jpeg', 'png', 'gif', 'webp', 'jxl', 'svg', 'mp4'].includes(ext)) { + console.error(`gallery: type=${ext} file=${this.name} unsupported`); + return; + } this.hash = await getHash(`${this.folder}/${this.name}/${this.size}/${this.mtime}`); // eslint-disable-line no-use-before-define const style = document.createElement('style'); const width = opts.browser_fixed_width ? `${opts.extra_networks_card_size}px` : 'unset'; @@ -143,7 +150,6 @@ class GalleryFile extends HTMLElement { `; const cache = (this.hash && opts.browser_cache) ? await idbGet(this.hash) : undefined; - this.shadow.appendChild(style); const img = document.createElement('img'); img.className = 'gallery-file'; img.loading = 'lazy'; @@ -196,14 +202,20 @@ class GalleryFile extends HTMLElement { img.src = `file=${this.src}`; } } - if (!ok) return; + if (!ok) { + return; + } img.onclick = () => { currentImage = this.src; el.btnSend.click(); }; img.title = `Folder: ${this.folder}\nFile: ${this.name}\nSize: ${this.size.toLocaleString()} bytes\nModified: ${this.mtime.toLocaleString()}`; + if (this.shadow.children.length > 0) { + return; // avoid double-adding + } this.title = img.title; this.style.display = this.title.toLowerCase().includes(el.search.value.toLowerCase()) ? 'unset' : 'none'; + this.shadow.appendChild(style); this.shadow.appendChild(img); } } @@ -272,49 +284,68 @@ async function gallerySearch(evt) { }, 250); } +const findDuplicates = (arr, key) => { + const map = new Map(); + return arr.filter(item => { + const value = item[key]; + if (map.has(value)) return true; + map.set(value, true); + return false; + }); +}; + async function gallerySort(btn) { const t0 = performance.now(); const arr = Array.from(el.files.children).filter((node) => node.name); // filter out separators + if (arr.length === 0) return; // no files to sort + if (btn) lastSort = btn.charCodeAt(0); + lastSortName = 'none'; const fragment = document.createDocumentFragment(); - el.files.innerHTML = ''; - log('gallerySort', btn.charCodeAt(0)); - switch (btn.charCodeAt(0)) { + switch (lastSort) { case 61789: // name asc + lastSortName = 'name asc'; arr .sort((a, b) => a.name.localeCompare(b.name)) .forEach((node) => fragment.appendChild(node)); break; case 61790: // name dsc + lastSortName = 'name dsc'; arr .sort((b, a) => a.name.localeCompare(b.name)) .forEach((node) => fragment.appendChild(node)); break; case 61792: // size asc + lastSortName = 'size asc'; arr .sort((a, b) => a.size - b.size) .forEach((node) => fragment.appendChild(node)); break; case 61793: // size dsc + lastSortName = 'size dsc'; arr .sort((b, a) => a.size - b.size) .forEach((node) => fragment.appendChild(node)); break; case 61794: // resolution asc + lastSortName = 'resolution asc'; arr .sort((a, b) => a.width * a.height - b.width * b.height) .forEach((node) => fragment.appendChild(node)); break; case 61795: // resolution dsc + lastSortName = 'resolution dsc'; arr .sort((b, a) => a.width * a.height - b.width * b.height) .forEach((node) => fragment.appendChild(node)); break; case 61662: + lastSortName = 'modified asc'; arr .sort((a, b) => a.mtime - b.mtime) .forEach((node) => fragment.appendChild(node)); break; case 61661: + lastSortName = 'modified dsc'; arr .sort((b, a) => a.mtime - b.mtime) .forEach((node) => fragment.appendChild(node)); @@ -322,14 +353,16 @@ async function gallerySort(btn) { default: break; } + if (fragment.children.length === 0) return; + el.files.innerHTML = ''; el.files.appendChild(fragment); addSeparators(); const t1 = performance.now(); - el.status.innerText = `Sort | ${arr.length.toLocaleString()} images | ${Math.floor(t1 - t0).toLocaleString()}ms`; + log(`gallerySort: char=${lastSort} len=${arr.length} time=${Math.floor(t1 - t0)} sort=${lastSortName}`); + el.status.innerText = `Sort | ${lastSortName} | ${arr.length.toLocaleString()} images | ${Math.floor(t1 - t0).toLocaleString()}ms`; } async function fetchFilesHT(evt) { - el.status.innerText = `Folder | ${evt.target.name}`; const t0 = performance.now(); const fragment = document.createDocumentFragment(); el.status.innerText = `Folder | ${evt.target.name} | in-progress`; @@ -389,7 +422,7 @@ async function fetchFilesWS(evt) { // fetch file-by-file list over websockets const file = new GalleryFile(data[0], data[1]); fragment.appendChild(file); if (numFiles % 100 === 0) { - el.status.innerText = `Folder | ${evt.target.name} | ${numFiles.toLocaleString()} images | ${Math.floor(t1 - t0).toLocaleString()}ms`; + el.status.innerText = `Folder | ${evt.target.name} | ${numFiles.toLocaleString()} images | in-progress | ${Math.floor(t1 - t0).toLocaleString()}ms`; el.files.appendChild(fragment); fragment = document.createDocumentFragment(); } @@ -397,6 +430,7 @@ async function fetchFilesWS(evt) { // fetch file-by-file list over websockets }; ws.onclose = (event) => { el.files.appendChild(fragment); + // gallerySort(); log(`gallery: folder=${evt.target.name} num=${numFiles} time=${Math.floor(t1 - t0)}ms`); el.status.innerText = `Folder | ${evt.target.name} | ${numFiles.toLocaleString()} images | ${Math.floor(t1 - t0).toLocaleString()}ms`; addSeparators(); diff --git a/modules/ui_loadsave.py b/modules/ui_loadsave.py index 79efbff62..a8e3dce9b 100644 --- a/modules/ui_loadsave.py +++ b/modules/ui_loadsave.py @@ -48,7 +48,7 @@ class UiLoadsave: if debug_ui and key in self.component_mapping and not key.startswith('customscript'): errors.log.warning(f'UI duplicate: key="{key}" id={getattr(obj, "elem_id", None)} class={getattr(obj, "elem_classes", None)}') if hasattr(obj, 'skip'): - print('HERE', key) + pass if (field == 'value') and (key not in self.component_mapping): self.component_mapping[key] = x if field == 'open' and key not in self.component_mapping: