(() => {
const input = document.getElementById('search-input');
const resultsBox = document.getElementById('search-results');
if (!input || !resultsBox) return;
let index = [];
let loaded = false;
const render = (items) => {
if (!items.length) {
resultsBox.innerHTML = '
No matches.
';
resultsBox.classList.add('active');
return;
}
const list = items
.slice(0, 12)
.map(item => `
${item.title}
${item.section}
${item.summary}
`).join('');
resultsBox.innerHTML = ``;
resultsBox.classList.add('active');
};
const filter = (query) => {
if (!query.trim()) {
resultsBox.classList.remove('active');
return;
}
const q = query.toLowerCase();
const items = index.filter(item => {
return (
item.title.toLowerCase().includes(q) ||
item.section.toLowerCase().includes(q) ||
item.summary.toLowerCase().includes(q) ||
item.tags.some(tag => tag.toLowerCase().includes(q))
);
});
render(items);
};
const loadIndex = async () => {
if (loaded) return index;
try {
const res = await fetch('/search-index.json');
index = await res.json();
loaded = true;
} catch (err) {
console.error('Search index failed to load', err);
}
return index;
};
input.addEventListener('input', async (e) => {
await loadIndex();
filter(e.target.value);
});
document.addEventListener('click', (e) => {
if (!resultsBox.contains(e.target) && e.target !== input) {
resultsBox.classList.remove('active');
}
});
})();