From 14a88e2cfa034ef0b3785a397bc0d27e78a9088b Mon Sep 17 00:00:00 2001 From: Will Miao <13051207myq@gmail.com> Date: Mon, 17 Mar 2025 16:55:19 +0800 Subject: [PATCH] update --- static/js/recipes.js | 13 -- static/js/utils/search.js | 328 -------------------------------------- 2 files changed, 341 deletions(-) delete mode 100644 static/js/utils/search.js diff --git a/static/js/recipes.js b/static/js/recipes.js index c7b35ebf..a4f69589 100644 --- a/static/js/recipes.js +++ b/static/js/recipes.js @@ -5,7 +5,6 @@ import { initializeCommonComponents } from './common.js'; import { ImportManager } from './managers/ImportManager.js'; import { RecipeCard } from './components/RecipeCard.js'; import { RecipeModal } from './components/RecipeModal.js'; -import { SearchManager } from './managers/SearchManager.js'; import { HeaderManager } from './components/Header.js'; class RecipeManager { @@ -30,18 +29,6 @@ class RecipeManager { // Load initial set of recipes this.loadRecipes(); - - // Initialize search manager with Recipe-specific options - const recipeSearchManager = new SearchManager({ - searchCallback: (query, options, recursive) => { - // Recipe-specific search implementation - fetchRecipes({ - search: query, - search_options: options, - recursive: recursive - }); - } - }); // Set the current page for proper context document.body.dataset.page = 'recipes'; diff --git a/static/js/utils/search.js b/static/js/utils/search.js deleted file mode 100644 index c3def59a..00000000 --- a/static/js/utils/search.js +++ /dev/null @@ -1,328 +0,0 @@ -import { appendLoraCards } from '../api/loraApi.js'; -import { state } from '../state/index.js'; -import { resetAndReload } from '../api/loraApi.js'; -import { showToast } from './uiHelpers.js'; - -export class SearchManager { - constructor() { - // Initialize search manager - this.searchInput = document.getElementById('searchInput'); - this.searchOptionsToggle = document.getElementById('searchOptionsToggle'); - this.searchOptionsPanel = document.getElementById('searchOptionsPanel'); - this.recursiveSearchToggle = document.getElementById('recursiveSearchToggle'); - this.searchDebounceTimeout = null; - this.currentSearchTerm = ''; - this.isSearching = false; - - // Add clear button - this.createClearButton(); - - // Add this instance to state - state.searchManager = this; - - if (this.searchInput) { - this.searchInput.addEventListener('input', this.handleSearch.bind(this)); - // Update clear button visibility on input - this.searchInput.addEventListener('input', () => { - this.updateClearButtonVisibility(); - }); - } - - // Initialize search options - this.initSearchOptions(); - - // Add global click handler to close panels when clicking outside - document.addEventListener('click', (e) => { - // Close search options panel when clicking outside - if (this.searchOptionsPanel && - !this.searchOptionsPanel.contains(e.target) && - e.target !== this.searchOptionsToggle && - !this.searchOptionsToggle.contains(e.target)) { - this.closeSearchOptionsPanel(); - } - - // Close filter panel when clicking outside (if filterManager exists) - const filterPanel = document.getElementById('filterPanel'); - const filterButton = document.getElementById('filterButton'); - if (filterPanel && - !filterPanel.contains(e.target) && - e.target !== filterButton && - !filterButton.contains(e.target) && - window.filterManager) { - window.filterManager.closeFilterPanel(); - } - }); - - // Initialize panel positions - this.updatePanelPositions(); - - // Add resize listener - window.addEventListener('resize', this.updatePanelPositions.bind(this)); - } - - initSearchOptions() { - // Load recursive search state from localStorage - state.searchOptions.recursive = localStorage.getItem('recursiveSearch') === 'true'; - - if (this.recursiveSearchToggle) { - this.recursiveSearchToggle.checked = state.searchOptions.recursive; - this.recursiveSearchToggle.addEventListener('change', (e) => { - state.searchOptions.recursive = e.target.checked; - localStorage.setItem('recursiveSearch', state.searchOptions.recursive); - - // Rerun search if there's an active search term - if (this.currentSearchTerm) { - this.performSearch(this.currentSearchTerm); - } - }); - } - - // Setup search options toggle - if (this.searchOptionsToggle) { - this.searchOptionsToggle.addEventListener('click', () => { - this.toggleSearchOptionsPanel(); - }); - } - - // Close button for search options panel - const closeButton = document.getElementById('closeSearchOptions'); - if (closeButton) { - closeButton.addEventListener('click', () => { - this.closeSearchOptionsPanel(); - }); - } - - // Setup search option tags - const optionTags = document.querySelectorAll('.search-option-tag'); - optionTags.forEach(tag => { - const option = tag.dataset.option; - - // Initialize tag state from state - tag.classList.toggle('active', state.searchOptions[option]); - - tag.addEventListener('click', () => { - // Check if clicking would deselect the last active option - const activeOptions = document.querySelectorAll('.search-option-tag.active'); - if (activeOptions.length === 1 && activeOptions[0] === tag) { - // Don't allow deselecting the last option and show toast - showToast('At least one search option must be selected', 'info'); - return; - } - - tag.classList.toggle('active'); - state.searchOptions[option] = tag.classList.contains('active'); - - // Save to localStorage - localStorage.setItem(`searchOption_${option}`, state.searchOptions[option]); - - // Rerun search if there's an active search term - if (this.currentSearchTerm) { - this.performSearch(this.currentSearchTerm); - } - }); - - // Load option state from localStorage or use default - const savedState = localStorage.getItem(`searchOption_${option}`); - if (savedState !== null) { - state.searchOptions[option] = savedState === 'true'; - tag.classList.toggle('active', state.searchOptions[option]); - } - }); - - // Ensure at least one search option is selected - this.validateSearchOptions(); - } - - // Add method to validate search options - validateSearchOptions() { - const hasActiveOption = Object.values(state.searchOptions) - .some(value => value === true && value !== state.searchOptions.recursive); - - // If no search options are active, activate at least one default option - if (!hasActiveOption) { - state.searchOptions.filename = true; - localStorage.setItem('searchOption_filename', 'true'); - - // Update UI to match - const fileNameTag = document.querySelector('.search-option-tag[data-option="filename"]'); - if (fileNameTag) { - fileNameTag.classList.add('active'); - } - } - } - - toggleSearchOptionsPanel() { - if (this.searchOptionsPanel) { - const isHidden = this.searchOptionsPanel.classList.contains('hidden'); - if (isHidden) { - // Update position before showing - this.updatePanelPositions(); - this.searchOptionsPanel.classList.remove('hidden'); - this.searchOptionsToggle.classList.add('active'); - } else { - this.closeSearchOptionsPanel(); - } - } - } - - closeSearchOptionsPanel() { - if (this.searchOptionsPanel) { - this.searchOptionsPanel.classList.add('hidden'); - this.searchOptionsToggle.classList.remove('active'); - } - } - - createClearButton() { - // Create clear button - const clearButton = document.createElement('button'); - clearButton.className = 'search-clear'; - clearButton.innerHTML = ''; - clearButton.title = 'Clear search'; - - // Add click handler - clearButton.addEventListener('click', () => { - this.searchInput.value = ''; - this.currentSearchTerm = ''; - this.updateClearButtonVisibility(); - resetAndReload(); - }); - - // Insert after search input - this.searchInput.parentNode.appendChild(clearButton); - this.clearButton = clearButton; - - // Set initial visibility - this.updateClearButtonVisibility(); - } - - updateClearButtonVisibility() { - if (this.clearButton) { - this.clearButton.classList.toggle('visible', this.searchInput.value.length > 0); - } - } - - handleSearch(event) { - if (this.searchDebounceTimeout) { - clearTimeout(this.searchDebounceTimeout); - } - - this.searchDebounceTimeout = setTimeout(async () => { - const searchTerm = event.target.value.trim().toLowerCase(); - - if (searchTerm !== this.currentSearchTerm && !this.isSearching) { - this.currentSearchTerm = searchTerm; - await this.performSearch(searchTerm); - } - }, 250); - } - - async performSearch(searchTerm) { - const grid = document.getElementById('loraGrid'); - - if (!searchTerm) { - state.currentPage = 1; - await resetAndReload(); - return; - } - - try { - this.isSearching = true; - state.loadingManager.showSimpleLoading('Searching...'); - - // Store current scroll position - const scrollPosition = window.pageYOffset || document.documentElement.scrollTop; - - state.currentPage = 1; - state.hasMore = true; - - const url = new URL('/api/loras', window.location.origin); - url.searchParams.set('page', '1'); - url.searchParams.set('page_size', '20'); - url.searchParams.set('sort_by', state.sortBy); - url.searchParams.set('search', searchTerm); - url.searchParams.set('fuzzy', 'true'); - - // Add search options - url.searchParams.set('search_filename', state.searchOptions.filename.toString()); - url.searchParams.set('search_modelname', state.searchOptions.modelname.toString()); - url.searchParams.set('search_tags', state.searchOptions.tags.toString()); - - // Always send folder parameter if there is an active folder - if (state.activeFolder) { - url.searchParams.set('folder', state.activeFolder); - // Add recursive parameter when recursive search is enabled - url.searchParams.set('recursive', state.searchOptions.recursive.toString()); - } - - const response = await fetch(url); - - if (!response.ok) { - throw new Error('Search failed'); - } - - const data = await response.json(); - - if (searchTerm === this.currentSearchTerm) { - grid.innerHTML = ''; - - if (data.items.length === 0) { - grid.innerHTML = '
No matching loras found
'; - state.hasMore = false; - } else { - appendLoraCards(data.items); - state.hasMore = state.currentPage < data.total_pages; - state.currentPage++; - } - - // Restore scroll position after content is loaded - setTimeout(() => { - window.scrollTo({ - top: scrollPosition, - behavior: 'instant' // Use 'instant' to prevent animation - }); - }, 10); - } - } catch (error) { - console.error('Search error:', error); - showToast('Search failed', 'error'); - } finally { - this.isSearching = false; - state.loadingManager.hide(); - } - } - - updatePanelPositions() { - const searchOptionsPanel = document.getElementById('searchOptionsPanel'); - const filterPanel = document.getElementById('filterPanel'); - - if (!searchOptionsPanel || !filterPanel) return; - - // Get the header element - const header = document.querySelector('.app-header'); - if (!header) return; - - // Calculate the position based on the bottom of the header - const headerRect = header.getBoundingClientRect(); - const topPosition = headerRect.bottom + 5; // Add 5px padding - - // Set the positions - searchOptionsPanel.style.top = `${topPosition}px`; - filterPanel.style.top = `${topPosition}px`; - - // Adjust panel horizontal position based on the search container - const searchContainer = document.querySelector('.header-search'); - if (searchContainer) { - const searchRect = searchContainer.getBoundingClientRect(); - - // Position the search options panel aligned with the search container - searchOptionsPanel.style.right = `${window.innerWidth - searchRect.right}px`; - - // Position the filter panel aligned with the filter button - const filterButton = document.getElementById('filterButton'); - if (filterButton) { - const filterRect = filterButton.getBoundingClientRect(); - filterPanel.style.right = `${window.innerWidth - filterRect.right}px`; - } - } - } -} \ No newline at end of file