updated the makefile, added new org files (2026), and started working on adding a search feature for the website.

This commit is contained in:
2025-12-30 22:43:25 +00:00
parent 78cfe20d64
commit c15fcda8c6
20 changed files with 4007 additions and 33 deletions

210
assets/scripts/search.js Normal file
View File

@@ -0,0 +1,210 @@
let lunrIndex;
let documents = [];
/* ------------------------------
JSON → documents
-------------------------------- */
function extractDocuments(node, currentPath = "") {
if (node.type === "folder" && node.children) {
const nextPath = currentPath
? `${currentPath}/${node.name}`
: node.name;
node.children.forEach(child => {
extractDocuments(child, nextPath);
});
}
if (node.type === "file") {
const filePath = currentPath
? `${currentPath}/${node.name}`
: node.name;
documents.push({
id: documents.length.toString(),
title: node.name,
content: node.content || "",
path: "/" + filePath.replace(/^output\//, "")
});
}
}
/* ------------------------------
Lunr index
-------------------------------- */
function buildIndex() {
lunrIndex = lunr(function () {
this.ref("id");
this.field("title", { boost: 10 });
this.field("content");
documents.forEach(doc => this.add(doc));
});
console.log("Lunr index built with", documents.length, "documents");
}
async function initSearch() {
const response = await fetch("test.json");
const json = await response.json();
extractDocuments(json);
buildIndex();
}
initSearch();
/* ------------------------------
Fuzzy search
-------------------------------- */
function buildFuzzyQuery(query) {
return query
.split(/\s+/)
.map(term => `${term}~1`)
.join(" ");
}
function search(query) {
if (!lunrIndex) return [];
const results = lunrIndex.search(buildFuzzyQuery(query));
return results.map(r => {
const doc = documents.find(d => d.id === r.ref);
return {
title: doc.title,
path: doc.path,
score: r.score,
preview: doc.content.slice(0, 200) + "…"
};
});
}
/* ------------------------------
DOM elements
-------------------------------- */
const searchInput = document.getElementById("search-input");
const searchBtn = document.getElementById("search-btn");
/* ------------------------------
Live dropdown results
-------------------------------- */
const resultsBox = document.createElement("div");
resultsBox.id = "search-results";
document.body.appendChild(resultsBox);
function showResults(results) {
resultsBox.innerHTML = "";
if (results.length === 0) {
resultsBox.style.display = "none";
return;
}
const rect = searchInput.getBoundingClientRect();
resultsBox.style.top = `${rect.bottom + window.scrollY}px`;
resultsBox.style.left = `${rect.left + window.scrollX}px`;
resultsBox.style.width = `${rect.width}px`;
results.slice(0, 5).forEach(result => {
const item = document.createElement("div");
item.className = "search-result";
item.innerHTML = `
<div class="search-result-title">${result.title}</div>
<div class="search-result-preview">${result.preview}</div>
`;
item.onclick = () => window.location.href = result.path;
resultsBox.appendChild(item);
});
resultsBox.style.display = "block";
}
searchInput.addEventListener("input", () => {
const q = searchInput.value.trim();
if (!q) {
resultsBox.style.display = "none";
return;
}
showResults(search(q));
});
/* ------------------------------
Modal popup
-------------------------------- */
const modal = document.createElement("div");
modal.id = "search-modal";
modal.innerHTML = `
<div class="search-modal-backdrop"></div>
<div class="search-modal-content">
<h2>Search results</h2>
<div id="search-modal-results"></div>
</div>
`;
document.body.appendChild(modal);
const modalResults = modal.querySelector("#search-modal-results");
function openSearchModal(results) {
modalResults.innerHTML = "";
if (results.length === 0) {
modalResults.innerHTML = "<p>No results found.</p>";
}
results.forEach(result => {
const item = document.createElement("div");
item.className = "search-modal-result";
item.innerHTML = `
<div class="search-modal-title">${result.title}</div>
<div class="search-modal-preview">${result.preview}</div>
`;
item.onclick = () => window.location.href = result.path;
modalResults.appendChild(item);
});
modal.style.display = "block";
}
/* ------------------------------
Submit search (Enter / 🔍)
-------------------------------- */
function submitSearch() {
const q = searchInput.value.trim();
if (!q) return;
openSearchModal(search(q));
}
searchInput.addEventListener("keydown", e => {
if (e.key === "Enter") {
e.preventDefault();
submitSearch();
}
});
searchBtn.addEventListener("click", submitSearch);
/* ------------------------------
Close behaviours
-------------------------------- */
document.addEventListener("click", e => {
if (
!resultsBox.contains(e.target) &&
e.target !== searchInput &&
e.target !== searchBtn
) {
resultsBox.style.display = "none";
}
});
modal.addEventListener("click", e => {
if (e.target.classList.contains("search-modal-backdrop")) {
modal.style.display = "none";
}
});
document.addEventListener("keydown", e => {
if (e.key === "Escape") {
modal.style.display = "none";
}
});