145 lines
No EOL
3.3 KiB
Text
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> |