let searchInputTimeout = null; const SEARCH_DEBOUNCE_MS = 300; const SEARCH_MIN_CHARS = 2; function initializeSearch() { const searchInput = document.getElementById("header-search"); if (!searchInput) { console.warn("Search input not found"); return; } searchInput.addEventListener("input", handleSearchInput); searchInput.addEventListener("keydown", handleSearchKeydown); searchInput.addEventListener("focus", () => { if (searchInput.value.length >= SEARCH_MIN_CHARS) { performSearch(searchInput.value); } }); document.addEventListener("click", (e) => { const searchResults = document.getElementById("search-results"); const searchInputEl = document.getElementById("header-search"); if (searchResults && !searchResults.contains(e.target) && e.target !== searchInputEl) { hideSearchResults(); } }); } function handleSearchInput(e) { const target = e.target; const query = target.value.trim(); if (searchInputTimeout) { clearTimeout(searchInputTimeout); } if (query.length < SEARCH_MIN_CHARS) { hideSearchResults(); return; } searchInputTimeout = setTimeout(() => { performSearch(query); }, SEARCH_DEBOUNCE_MS); } function handleSearchKeydown(e) { const searchResults = document.getElementById("search-results"); if (!searchResults || searchResults.classList.contains("hidden")) { return; } const items = searchResults.querySelectorAll(".search-result-item"); const currentIndex = parseInt(searchResults.dataset.selectedIndex || "-1"); if (e.key === "ArrowDown") { e.preventDefault(); const nextIndex = Math.min(currentIndex + 1, items.length - 1); selectSearchResult(items, nextIndex); } else if (e.key === "ArrowUp") { e.preventDefault(); const prevIndex = Math.max(currentIndex - 1, -1); selectSearchResult(items, prevIndex); } else if (e.key === "Enter") { e.preventDefault(); if (currentIndex >= 0 && items[currentIndex]) { const link = items[currentIndex].querySelector("a"); if (link) link.click(); } } else if (e.key === "Escape") { hideSearchResults(); } } function selectSearchResult(items, index) { items.forEach((item, i) => { if (i === index) { item.classList.add("bg-opacity-80"); } else { item.classList.remove("bg-opacity-80"); } }); const searchResults = document.getElementById("search-results"); if (searchResults) { searchResults.dataset.selectedIndex = index.toString(); } } function performSearch(query) { const token = localStorage.getItem("token"); if (!token) { console.warn("No authentication token found"); return; } showSearchLoading(); fetch(`/api/media-items/search?q=${encodeURIComponent(query)}`, { headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, }) .then((response) => { if (response.status === 404) { return { error: "no results found", results: [] }; } return response.json(); }) .then((data) => { hideSearchLoading(); if (data && "error" in data && data.error === "no results found") { showNoResults(query); } else if (Array.isArray(data) && data.length > 0) { showSearchResults(data, query); } else if (Array.isArray(data)) { showNoResults(query); } else { showNoResults(query); } }) .catch((error) => { hideSearchLoading(); console.error("Search error:", error); showSearchError(); }); } function showSearchLoading() { createSearchResultsContainer(); const searchResults = document.getElementById("search-results"); if (!searchResults) return; searchResults.innerHTML = `
Searching...
${results.length} result${results.length !== 1 ? "s" : ""} for "${searchEscapeHtml(query)}"
Press ↑↓ to navigate, Enter to select
No results found for "${searchEscapeHtml(query)}"
Try different keywords
Search error
Please try again