/** * KHAELOR * File: website/public/site.js * Description: Minimal site behavior — theme toggle, active-nav highlighting, copy-to-clipboard on code blocks. * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ (function () { "use strict"; // ── theme ────────────────────────────────────────────────────────── // The initial theme is applied by the inline
snippet (no FOUC); // this wires the toggle button. var root = document.documentElement; function currentTheme() { return root.getAttribute("data-theme") === "light" ? "light" : "dark"; } function applyTheme(theme) { if (theme === "light") { root.setAttribute("data-theme", "light"); } else { root.removeAttribute("data-theme"); } var btn = document.getElementById("theme-toggle"); if (btn) btn.textContent = theme === "light" ? "dark" : "light"; } document.addEventListener("DOMContentLoaded", function () { applyTheme(currentTheme()); var btn = document.getElementById("theme-toggle"); if (btn) { btn.addEventListener("click", function () { var next = currentTheme() === "light" ? "dark" : "light"; try { localStorage.setItem("khaelor-theme", next); } catch (e) { /* private mode — theme just won't persist */ } applyTheme(next); }); } highlightNav(); addCopyButtons(); }); // ── active nav highlighting ──────────────────────────────────────── function highlightNav() { var path = location.pathname.replace(/\/+$/, ""); if (path === "") path = "/index.html"; var links = document.querySelectorAll(".sidebar a, .site-header nav a"); links.forEach(function (a) { var href = a.getAttribute("href"); if (!href || href.charAt(0) === "#") return; var target = new URL(href, location.href).pathname.replace(/\/+$/, ""); if (target === "") target = "/index.html"; if (target === path) { a.classList.add("active"); } else if ( a.closest(".site-header") && target.indexOf("/docs/") !== -1 && path.indexOf("/docs/") === 0 ) { // header "Docs" link stays lit anywhere under /docs/ a.classList.add("active"); } }); } // ── copy-to-clipboard on code blocks ─────────────────────────────── function copyText(text, btn) { function done(ok) { btn.textContent = ok ? "copied" : "failed"; btn.classList.toggle("copied", ok); setTimeout(function () { btn.textContent = "copy"; btn.classList.remove("copied"); }, 1600); } if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then( function () { done(true); }, function () { done(false); } ); } else { done(false); } } function addCopyButtons() { // blocks get a copy button; the hero install line has its own.
document.querySelectorAll("pre").forEach(function (pre) {
if (pre.hasAttribute("data-no-copy")) return;
var code = pre.querySelector("code");
if (!code) return;
var btn = document.createElement("button");
btn.type = "button";
btn.className = "copy-btn";
btn.textContent = "copy";
btn.setAttribute("aria-label", "Copy code to clipboard");
btn.addEventListener("click", function () {
copyText(code.innerText.replace(/\n$/, ""), btn);
});
pre.appendChild(btn);
});
document.querySelectorAll(".install-line").forEach(function (line) {
var code = line.querySelector("code");
var btn = line.querySelector("button");
if (!code || !btn) return;
btn.addEventListener("click", function () {
copyText(code.innerText.trim(), btn);
});
});
}
})();