From f614dbf7003296cdbd60f31d2de5203c1292ede5 Mon Sep 17 00:00:00 2001
From: Will Miao <13051207myq@gmail.com>
Date: Wed, 5 Mar 2025 19:53:52 +0800
Subject: [PATCH] Add filters - base model only for now
---
py/routes/api_routes.py | 9 +++-
py/services/lora_scanner.py | 10 +++-
py/utils/model_utils.py | 8 +--
static/css/layout.css | 34 +++++--------
static/js/api/loraApi.js | 6 +++
static/js/main.js | 6 +++
static/js/managers/FilterManager.js | 32 ++++++++----
static/js/utils/constants.js | 78 ++++++++++++++++++++++++++---
templates/components/controls.html | 3 --
9 files changed, 139 insertions(+), 47 deletions(-)
diff --git a/py/routes/api_routes.py b/py/routes/api_routes.py
index e991bcbb..cd66783d 100644
--- a/py/routes/api_routes.py
+++ b/py/routes/api_routes.py
@@ -121,6 +121,10 @@ class ApiRoutes:
fuzzy = request.query.get('fuzzy', 'false').lower() == 'true'
recursive = request.query.get('recursive', 'false').lower() == 'true'
+ # Parse base models filter parameter
+ base_models = request.query.get('base_models', '').split(',')
+ base_models = [model.strip() for model in base_models if model.strip()]
+
# Validate parameters
if page < 1 or page_size < 1 or page_size > 100:
return web.json_response({
@@ -132,7 +136,7 @@ class ApiRoutes:
'error': 'Invalid sort parameter'
}, status=400)
- # Get paginated data with search
+ # Get paginated data with search and filters
result = await self.scanner.get_paginated_data(
page=page,
page_size=page_size,
@@ -140,7 +144,8 @@ class ApiRoutes:
folder=folder,
search=search,
fuzzy=fuzzy,
- recursive=recursive # 添加递归参数
+ recursive=recursive,
+ base_models=base_models # Pass base models filter
)
# Format the response data
diff --git a/py/services/lora_scanner.py b/py/services/lora_scanner.py
index 53f6d4b3..5d3fbdf4 100644
--- a/py/services/lora_scanner.py
+++ b/py/services/lora_scanner.py
@@ -148,7 +148,7 @@ class LoraScanner:
async def get_paginated_data(self, page: int, page_size: int, sort_by: str = 'name',
folder: str = None, search: str = None, fuzzy: bool = False,
- recursive: bool = False):
+ recursive: bool = False, base_models: list = None):
"""Get paginated and filtered lora data
Args:
@@ -159,6 +159,7 @@ class LoraScanner:
search: Search term
fuzzy: Use fuzzy matching for search
recursive: Include subfolders when folder filter is applied
+ base_models: List of base models to filter by
"""
cache = await self.get_cached_data()
@@ -180,6 +181,13 @@ class LoraScanner:
if item['folder'] == folder
]
+ # Apply base model filtering
+ if base_models and len(base_models) > 0:
+ filtered_data = [
+ item for item in filtered_data
+ if item.get('base_model') in base_models
+ ]
+
# 应用搜索过滤
if search:
if fuzzy:
diff --git a/py/utils/model_utils.py b/py/utils/model_utils.py
index c1d5581b..9c09e00b 100644
--- a/py/utils/model_utils.py
+++ b/py/utils/model_utils.py
@@ -2,10 +2,10 @@ from typing import Optional
# Base model mapping based on version string
BASE_MODEL_MAPPING = {
- "sd-v1-5": "SD1.5",
- "sd-v2-1": "SD2.1",
- "sdxl": "SDXL",
- "sd-v2": "SD2.0",
+ "sd-v1-5": "SD 1.5",
+ "sd-v2-1": "SD 2.1",
+ "sdxl": "SDXL 1.0",
+ "sd-v2": "SD 2.0",
"flux1": "Flux.1 D",
"flux.1 d": "Flux.1 D",
"illustrious": "IL",
diff --git a/static/css/layout.css b/static/css/layout.css
index 72e364bf..c9214d43 100644
--- a/static/css/layout.css
+++ b/static/css/layout.css
@@ -438,6 +438,10 @@
font-size: 14px;
cursor: pointer;
transition: all 0.2s ease;
+ user-select: none; /* Prevent text selection */
+ -webkit-user-select: none; /* For Safari */
+ -moz-user-select: none; /* For Firefox */
+ -ms-user-select: none; /* For IE/Edge */
}
.filter-tag:hover {
@@ -467,42 +471,28 @@
/* Filter actions */
.filter-actions {
display: flex;
- justify-content: space-between;
+ justify-content: center;
margin-top: 16px;
gap: 8px;
}
-.clear-filters-btn,
-.apply-filters-btn {
- padding: 6px 12px;
- border-radius: var(--border-radius-sm);
- font-size: 14px;
- cursor: pointer;
- transition: background-color 0.2s ease;
-}
-
.clear-filters-btn {
background-color: transparent;
color: var(--text-color);
border: 1px solid var(--border-color);
- flex: 1;
+ padding: 6px 12px;
+ border-radius: var(--border-radius-sm);
+ font-size: 14px;
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+ width: 100%;
}
.clear-filters-btn:hover {
background-color: var(--lora-surface-hover);
}
-.apply-filters-btn {
- background-color: var(--lora-accent);
- color: white;
- border: 1px solid var(--lora-accent);
- flex: 1;
-}
-
-.apply-filters-btn:hover {
- background-color: var(--lora-accent-hover, var(--lora-accent));
- filter: brightness(1.1);
-}
+/* Remove apply-filters-btn styles since we no longer need it */
@media (max-width: 768px) {
.actions {
diff --git a/static/js/api/loraApi.js b/static/js/api/loraApi.js
index d4357d41..1ac6f012 100644
--- a/static/js/api/loraApi.js
+++ b/static/js/api/loraApi.js
@@ -30,6 +30,12 @@ export async function loadMoreLoras(boolUpdateFolders = false) {
params.append('search', searchInput.value.trim());
params.append('fuzzy', 'true');
}
+
+ // Add filter parameters if active
+ if (state.filters && state.filters.baseModel && state.filters.baseModel.length > 0) {
+ // Convert the array of base models to a comma-separated string
+ params.append('base_models', state.filters.baseModel.join(','));
+ }
console.log('Loading loras with params:', params.toString());
diff --git a/static/js/main.js b/static/js/main.js
index d868e449..7c767553 100644
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -55,6 +55,12 @@ document.addEventListener('DOMContentLoaded', () => {
modalManager.initialize(); // Initialize modalManager after DOM is loaded
window.downloadManager = new DownloadManager(); // Move this after modalManager initialization
window.filterManager = new FilterManager(); // Initialize filter manager
+
+ // Initialize state filters from filterManager if available
+ if (window.filterManager && window.filterManager.filters) {
+ state.filters = { ...window.filterManager.filters };
+ }
+
initializeInfiniteScroll();
initializeEventListeners();
lazyLoadImages();
diff --git a/static/js/managers/FilterManager.js b/static/js/managers/FilterManager.js
index 30620937..a5d8ae03 100644
--- a/static/js/managers/FilterManager.js
+++ b/static/js/managers/FilterManager.js
@@ -1,6 +1,7 @@
import { BASE_MODELS, BASE_MODEL_CLASSES } from '../utils/constants.js';
import { state } from '../state/index.js';
import { showToast } from '../utils/uiHelpers.js';
+import { resetAndReload } from '../api/loraApi.js';
export class FilterManager {
constructor() {
@@ -45,8 +46,8 @@ export class FilterManager {
tag.dataset.baseModel = value;
tag.innerHTML = value;
- // Add click handler to toggle selection
- tag.addEventListener('click', () => {
+ // Add click handler to toggle selection and automatically apply
+ tag.addEventListener('click', async () => {
tag.classList.toggle('active');
if (tag.classList.contains('active')) {
@@ -58,6 +59,9 @@ export class FilterManager {
}
this.updateActiveFiltersCount();
+
+ // Auto-apply filter when tag is clicked
+ await this.applyFilters(false);
});
baseModelTagsContainer.appendChild(tag);
@@ -101,30 +105,39 @@ export class FilterManager {
}
}
- applyFilters() {
+ async applyFilters(showToastNotification = true) {
// Save filters to localStorage
localStorage.setItem('loraFilters', JSON.stringify(this.filters));
- // Apply filters to cards (will be implemented later)
- showToast('Filters applied', 'success');
+ // Update state with current filters
+ state.filters = { ...this.filters };
- // Close the filter panel
- this.closeFilterPanel();
+ // Reload loras with filters applied
+ await resetAndReload();
// Update filter button to show active state
if (this.hasActiveFilters()) {
this.filterButton.classList.add('active');
+ if (showToastNotification) {
+ showToast(`Filtering by ${this.filters.baseModel.length} base models`, 'success');
+ }
} else {
this.filterButton.classList.remove('active');
+ if (showToastNotification) {
+ showToast('Filters cleared', 'info');
+ }
}
}
- clearFilters() {
+ async clearFilters() {
// Clear all filters
this.filters = {
baseModel: []
};
+ // Update state
+ state.filters = { ...this.filters };
+
// Update UI
this.updateTagSelections();
this.updateActiveFiltersCount();
@@ -132,8 +145,9 @@ export class FilterManager {
// Remove from localStorage
localStorage.removeItem('loraFilters');
+ // Update UI and reload data
this.filterButton.classList.remove('active');
- showToast('All filters cleared', 'info');
+ await resetAndReload();
}
loadFiltersFromStorage() {
diff --git a/static/js/utils/constants.js b/static/js/utils/constants.js
index f49bfefe..6be4c2de 100644
--- a/static/js/utils/constants.js
+++ b/static/js/utils/constants.js
@@ -1,24 +1,90 @@
export const BASE_MODELS = {
- SD_1_5: "SD1.5",
- SD_2_0: "SD2.0",
- SD_2_1: "SD2.1",
- SDXL: "SDXL",
+ // Stable Diffusion 1.x models
+ SD_1_4: "SD 1.4",
+ SD_1_5: "SD 1.5",
+ SD_1_5_LCM: "SD 1.5 LCM",
+ SD_1_5_HYPER: "SD 1.5 Hyper",
+
+ // Stable Diffusion 2.x models
+ SD_2_0: "SD 2.0",
+ SD_2_1: "SD 2.1",
+
+ // Stable Diffusion 3.x models
+ SD_3: "SD 3",
+ SD_3_5: "SD 3.5",
+ SD_3_5_MEDIUM: "SD 3.5 Medium",
+ SD_3_5_LARGE: "SD 3.5 Large",
+ SD_3_5_LARGE_TURBO: "SD 3.5 Large Turbo",
+
+ // SDXL models
+ SDXL: "SDXL 1.0",
+ SDXL_LIGHTNING: "SDXL Lightning",
+ SDXL_HYPER: "SDXL Hyper",
+
+ // Other models
FLUX_1_D: "Flux.1 D",
+ FLUX_1_S: "Flux.1 S",
+ AURAFLOW: "AuraFlow",
+ PIXART_A: "PixArt a",
+ PIXART_E: "PixArt E",
+ HUNYUAN_1: "Hunyuan 1",
+ LUMINA: "Lumina",
+ KOLORS: "Kolors",
+ NOOBAI: "NoobAI",
IL: "IL",
PONY: "Pony",
+
+ // Video models
+ SVD: "SVD",
+ WAN_VIDEO: "Wan Video",
HUNYUAN_VIDEO: "Hunyuan Video",
+
+ // Default
UNKNOWN: "Unknown"
};
// Base model display names and their corresponding class names (for styling)
export const BASE_MODEL_CLASSES = {
+ // Stable Diffusion 1.x models
+ [BASE_MODELS.SD_1_4]: "sd-1-4",
[BASE_MODELS.SD_1_5]: "sd-1-5",
+ [BASE_MODELS.SD_1_5_LCM]: "sd-1-5-lcm",
+ [BASE_MODELS.SD_1_5_HYPER]: "sd-1-5-hyper",
+
+ // Stable Diffusion 2.x models
[BASE_MODELS.SD_2_0]: "sd-2-0",
[BASE_MODELS.SD_2_1]: "sd-2-1",
+
+ // Stable Diffusion 3.x models
+ [BASE_MODELS.SD_3]: "sd-3",
+ [BASE_MODELS.SD_3_5]: "sd-3-5",
+ [BASE_MODELS.SD_3_5_MEDIUM]: "sd-3-5-medium",
+ [BASE_MODELS.SD_3_5_LARGE]: "sd-3-5-large",
+ [BASE_MODELS.SD_3_5_LARGE_TURBO]: "sd-3-5-large-turbo",
+
+ // SDXL models
[BASE_MODELS.SDXL]: "sdxl",
- [BASE_MODELS.FLUX_1_D]: "flux",
+ [BASE_MODELS.SDXL_LIGHTNING]: "sdxl-lightning",
+ [BASE_MODELS.SDXL_HYPER]: "sdxl-hyper",
+
+ // Video models
+ [BASE_MODELS.SVD]: "svd",
+ [BASE_MODELS.WAN_VIDEO]: "wan-video",
+ [BASE_MODELS.HUNYUAN_VIDEO]: "hunyuan-video",
+
+ // Other models
+ [BASE_MODELS.FLUX_1_D]: "flux-d",
+ [BASE_MODELS.FLUX_1_S]: "flux-s",
+ [BASE_MODELS.AURAFLOW]: "auraflow",
+ [BASE_MODELS.PIXART_A]: "pixart-a",
+ [BASE_MODELS.PIXART_E]: "pixart-e",
+ [BASE_MODELS.HUNYUAN_1]: "hunyuan-1",
+ [BASE_MODELS.LUMINA]: "lumina",
+ [BASE_MODELS.KOLORS]: "kolors",
+ [BASE_MODELS.NOOBAI]: "noobai",
[BASE_MODELS.IL]: "il",
[BASE_MODELS.PONY]: "pony",
- [BASE_MODELS.HUNYUAN_VIDEO]: "hunyuan",
+
+ // Default
[BASE_MODELS.UNKNOWN]: "unknown"
};
\ No newline at end of file
diff --git a/templates/components/controls.html b/templates/components/controls.html
index ef1d0332..bd79d1cd 100644
--- a/templates/components/controls.html
+++ b/templates/components/controls.html
@@ -61,8 +61,5 @@
-
\ No newline at end of file