Add search options panel and functionality for filename, model name, and tags

This commit is contained in:
Will Miao
2025-03-10 19:33:45 +08:00
parent cbb76580e4
commit 2783782669
6 changed files with 421 additions and 48 deletions

View File

@@ -7,11 +7,12 @@ export class SearchManager {
constructor() {
// Initialize search manager
this.searchInput = document.getElementById('searchInput');
this.searchModeToggle = document.getElementById('searchModeToggle');
this.searchOptionsToggle = document.getElementById('searchOptionsToggle');
this.searchOptionsPanel = document.getElementById('searchOptionsPanel');
this.recursiveSearchToggle = document.getElementById('recursiveSearchToggle');
this.searchDebounceTimeout = null;
this.currentSearchTerm = '';
this.isSearching = false;
this.isRecursiveSearch = false;
// Add clear button
this.createClearButton();
@@ -27,15 +28,19 @@ export class SearchManager {
});
}
if (this.searchModeToggle) {
// Initialize toggle state from localStorage or default to false
this.isRecursiveSearch = localStorage.getItem('recursiveSearch') === 'true';
this.updateToggleUI();
// Initialize search options
this.initSearchOptions();
}
this.searchModeToggle.addEventListener('click', () => {
this.isRecursiveSearch = !this.isRecursiveSearch;
localStorage.setItem('recursiveSearch', this.isRecursiveSearch);
this.updateToggleUI();
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) {
@@ -43,6 +48,108 @@ export class SearchManager {
}
});
}
// 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();
// Close panel when clicking outside
document.addEventListener('click', (e) => {
if (this.searchOptionsPanel &&
!this.searchOptionsPanel.contains(e.target) &&
e.target !== this.searchOptionsToggle &&
!this.searchOptionsToggle.contains(e.target)) {
this.closeSearchOptionsPanel();
}
});
}
// 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) {
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() {
@@ -74,21 +181,6 @@ export class SearchManager {
}
}
updateToggleUI() {
if (this.searchModeToggle) {
this.searchModeToggle.classList.toggle('active', this.isRecursiveSearch);
this.searchModeToggle.title = this.isRecursiveSearch
? 'Recursive folder search (including subfolders)'
: 'Current folder search only';
// Update the icon to indicate the mode
const icon = this.searchModeToggle.querySelector('i');
if (icon) {
icon.className = this.isRecursiveSearch ? 'fas fa-folder-tree' : 'fas fa-folder';
}
}
}
handleSearch(event) {
if (this.searchDebounceTimeout) {
clearTimeout(this.searchDebounceTimeout);
@@ -126,12 +218,17 @@ export class SearchManager {
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', this.isRecursiveSearch.toString());
url.searchParams.set('recursive', state.searchOptions.recursive.toString());
}
const response = await fetch(url);