From 641fa8a3d93f212eebc4162c20155ff6a23e4bc0 Mon Sep 17 00:00:00 2001 From: Will Miao <13051207myq@gmail.com> Date: Wed, 4 Jun 2025 16:46:57 +0800 Subject: [PATCH] Enhance duplicates mode functionality: add toggle for entering/exiting mode, improve exit button styling, and manage control button states during duplicates mode. --- static/css/components/duplicates.css | 48 ++++++++++++- .../js/components/ModelDuplicatesManager.js | 71 +++++++++++++++++++ static/js/components/controls/PageControls.js | 3 +- static/js/components/controls/index.js | 14 ---- templates/checkpoints.html | 4 +- templates/loras.html | 4 +- 6 files changed, 124 insertions(+), 20 deletions(-) diff --git a/static/css/components/duplicates.css b/static/css/components/duplicates.css index 54fd6588..2372ed64 100644 --- a/static/css/components/duplicates.css +++ b/static/css/components/duplicates.css @@ -50,6 +50,29 @@ align-items: center; } +/* Improved exit button in banner */ +.duplicates-banner button.btn-exit-mode { + min-width: 120px; + background-color: var(--card-bg); + color: var(--text-color); + border: 1px solid var(--border-color); + padding: 6px 12px; + border-radius: var(--border-radius-xs); + font-size: 0.85em; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + transition: all 0.2s ease; +} + +.duplicates-banner button.btn-exit-mode:hover { + background-color: var(--bg-color); + border-color: var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h); + transform: translateY(-1px); +} + .duplicates-banner button { min-width: 100px; display: flex; @@ -194,7 +217,7 @@ } .group-toggle-btn:hover { - border-color: var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h); + border-color: var(--lora-accent-l) var(--lora-accent-c) var (--lora-accent-h); transform: translateY(-1px); box-shadow: 0 3px 5px rgba(0, 0, 0, 0.08); } @@ -363,3 +386,26 @@ html[data-theme="dark"] .duplicates-banner { html[data-theme="dark"] .duplicate-group { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25); /* Stronger shadow in dark mode */ } + +/* Styles for disabled controls during duplicates mode */ +.disabled-during-duplicates { + opacity: 0.5 !important; + pointer-events: none !important; + cursor: not-allowed !important; + user-select: none !important; + filter: grayscale(50%) !important; +} + +/* Make the active duplicates button more prominent */ +#findDuplicatesBtn.active { + background: var(--lora-accent); + color: white; + border-color: var(--lora-accent); + box-shadow: 0 0 0 2px oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.25); + position: relative; + z-index: 5; +} + +#findDuplicatesBtn.active:hover { + background: oklch(calc(var(--lora-accent-l) - 5%) var(--lora-accent-c) var(--lora-accent-h)); +} diff --git a/static/js/components/ModelDuplicatesManager.js b/static/js/components/ModelDuplicatesManager.js index c003f691..f0a9b2e1 100644 --- a/static/js/components/ModelDuplicatesManager.js +++ b/static/js/components/ModelDuplicatesManager.js @@ -16,6 +16,9 @@ export class ModelDuplicatesManager { this.renderTooltip = this.renderTooltip.bind(this); this.checkDuplicatesCount = this.checkDuplicatesCount.bind(this); + // Keep track of which controls need to be re-enabled + this.disabledControls = []; + // Check for duplicates on load if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', this.checkDuplicatesCount); @@ -62,6 +65,15 @@ export class ModelDuplicatesManager { } } + // Toggle method to enter/exit duplicates mode + toggleDuplicateMode() { + if (this.inDuplicateMode) { + this.exitDuplicateMode(); + } else { + this.findDuplicates(); + } + } + async findDuplicates() { try { // Determine API endpoint based on model type @@ -126,6 +138,18 @@ export class ModelDuplicatesManager { // Update selected count this.updateSelectedCount(); + + // Update Duplicates button to show active state + const duplicatesBtn = document.getElementById('findDuplicatesBtn'); + if (duplicatesBtn) { + duplicatesBtn.classList.add('active'); + duplicatesBtn.title = 'Exit Duplicates Mode'; + // Change icon and text to indicate it's now an exit button + duplicatesBtn.innerHTML = ' Exit Duplicates'; + } + + // Disable all control buttons except the duplicates button + this.disableControlButtons(); } exitDuplicateMode() { @@ -153,6 +177,53 @@ export class ModelDuplicatesManager { // Re-enable virtual scrolling state.virtualScroller.enable(); + + // Restore Duplicates button to its original state + const duplicatesBtn = document.getElementById('findDuplicatesBtn'); + if (duplicatesBtn) { + duplicatesBtn.classList.remove('active'); + duplicatesBtn.title = 'Find duplicate models'; + duplicatesBtn.innerHTML = ' Duplicates '; + + // Restore badge + const newBadge = duplicatesBtn.querySelector('#duplicatesBadge'); + const oldBadge = document.getElementById('duplicatesBadge'); + if (oldBadge && oldBadge.textContent) { + newBadge.textContent = oldBadge.textContent; + newBadge.classList.add('pulse'); + } + } + + // Re-enable all control buttons + this.enableControlButtons(); + + this.checkDuplicatesCount(); + } + + // Disable all control buttons except the duplicates button + disableControlButtons() { + this.disabledControls = []; + + // Select all control buttons except the duplicates button + const controlButtons = document.querySelectorAll('.control-group button:not(#findDuplicatesBtn), .dropdown-group, .toggle-folders-btn, #favoriteFilterBtn'); + + controlButtons.forEach(button => { + // Only disable enabled buttons (don't disable already disabled buttons) + if (!button.disabled && !button.classList.contains('disabled')) { + this.disabledControls.push(button); + button.disabled = true; + button.classList.add('disabled-during-duplicates'); + } + }); + } + + // Re-enable all previously disabled control buttons + enableControlButtons() { + this.disabledControls.forEach(button => { + button.disabled = false; + button.classList.remove('disabled-during-duplicates'); + }); + this.disabledControls = []; } renderDuplicateGroups() { diff --git a/static/js/components/controls/PageControls.js b/static/js/components/controls/PageControls.js index 3aab5a4c..42ad0062 100644 --- a/static/js/components/controls/PageControls.js +++ b/static/js/components/controls/PageControls.js @@ -516,7 +516,8 @@ export class PageControls { */ findDuplicates() { if (window.modelDuplicatesManager) { - window.modelDuplicatesManager.findDuplicates(); + // Change to toggle functionality + window.modelDuplicatesManager.toggleDuplicateMode(); } else { console.error('Model duplicates manager not available'); } diff --git a/static/js/components/controls/index.js b/static/js/components/controls/index.js index e139fd5d..c767c62f 100644 --- a/static/js/components/controls/index.js +++ b/static/js/components/controls/index.js @@ -2,7 +2,6 @@ import { PageControls } from './PageControls.js'; import { LorasControls } from './LorasControls.js'; import { CheckpointsControls } from './CheckpointsControls.js'; -import { refreshVirtualScroll } from '../../utils/infiniteScroll.js'; // Export the classes export { PageControls, LorasControls, CheckpointsControls }; @@ -21,17 +20,4 @@ export function createPageControls(pageType) { console.error(`Unknown page type: ${pageType}`); return null; } -} - -// Example for a filter method: -function applyFilter(filterType, value) { - // ...existing filter logic... - - // After filters are applied, refresh the virtual scroll if it exists - if (state.virtualScroller) { - refreshVirtualScroll(); - } else { - // Fall back to existing reset and reload logic - resetAndReload(true); - } } \ No newline at end of file diff --git a/templates/checkpoints.html b/templates/checkpoints.html index 024e4f9f..702b6b95 100644 --- a/templates/checkpoints.html +++ b/templates/checkpoints.html @@ -41,8 +41,8 @@ - diff --git a/templates/loras.html b/templates/loras.html index fab86fbd..025b58d2 100644 --- a/templates/loras.html +++ b/templates/loras.html @@ -30,8 +30,8 @@ -