mercury/src/components/Search.astro
2025-05-18 12:58:33 +08:00

145 lines
No EOL
3.3 KiB
Text

---
---
<div class="search-container">
<div class="command-prompt">
<span class="command">search</span>
<input
type="text"
id="search-input"
class="search-input"
placeholder="Type to search..."
autocomplete="off"
/>
</div>
<div id="search-results" class="search-results"></div>
</div>
<script>
import Fuse from 'fuse.js';
let fuse;
let posts = [];
let searchInitialized = false;
async function initializeSearch() {
// Prevent multiple initializations
if (searchInitialized) return;
try {
// Fetch the search index
const response = await fetch('/search-index.json');
posts = await response.json();
fuse = new Fuse(posts, {
keys: ['title', 'description', 'content'],
threshold: 0.3,
includeMatches: true
});
searchInitialized = true;
document.getElementById('search-results').innerHTML = '';
} catch (error) {
console.error('Error fetching search index:', error);
}
}
function performSearch(query) {
if (!query) {
document.getElementById('search-results').innerHTML = '';
return;
}
if (!searchInitialized) return;
const results = fuse.search(query);
const resultsElement = document.getElementById('search-results');
if (results.length === 0) {
resultsElement.innerHTML = '<p>No results found.</p>';
return;
}
const html = results
.map(result => `
<div class="search-result">
<a href="/post/${result.item.slug}">
<span class="result-title">${result.item.title}</span>
<span class="result-date">${new Date(result.item.pubDate).toISOString().split('T')[0]}</span>
</a>
</div>
`)
.join('');
resultsElement.innerHTML = html;
}
// Add event listener for search input
const searchInput = document.getElementById('search-input');
// Initialize search only when the input is focused or clicked
searchInput.addEventListener('focus', initializeSearch);
searchInput.addEventListener('click', initializeSearch);
searchInput.addEventListener('input', (e) => {
if (!searchInitialized) {
initializeSearch().then(() => {
performSearch(e.target.value);
});
} else {
performSearch(e.target.value);
}
});
searchInput.addEventListener('focus', () => {
if (!searchInitialized) {
document.getElementById('search-results').innerHTML = '<p>Loading search index...</p>';
}
});
</script>
<style>
.search-container {
margin: 2rem 0;
}
.command-prompt {
display: flex;
align-items: center;
gap: 0.5rem;
}
.search-input {
background: transparent;
border: none;
color: var(--text-color);
font-family: var(--font-mono);
font-size: 1rem;
padding: 0.5rem;
outline: none;
}
.search-results {
margin-top: 1rem;
margin-left: 1rem;
}
.search-result {
margin: 0.5rem 0;
}
.search-result a {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem;
text-decoration: none;
color: var(--text-color);
border-radius: 4px;
}
.search-result a:hover {
background: var(--border-color);
}
.result-date {
color: var(--terminal-yellow);
font-size: 0.9rem;
}
</style>