spb/khaelor Public
KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.
TypeScript 82.9%
HTML 14.9%
CSS 1.1%
JavaScript 0.7%
1/**2 * KHAELOR3 * File: website/public/site.js4 * Description: Minimal site behavior — theme toggle, active-nav highlighting, copy-to-clipboard on code blocks.5 *6 * Author: Simon-Pierre Boucher7 * Contact: contact@spboucher.ai8 */910(function () {11 "use strict";1213 // ── theme ──────────────────────────────────────────────────────────14 // The initial theme is applied by the inline <head> snippet (no FOUC);15 // this wires the toggle button.16 var root = document.documentElement;1718 function currentTheme() {19 return root.getAttribute("data-theme") === "light" ? "light" : "dark";20 }2122 function applyTheme(theme) {23 if (theme === "light") {24 root.setAttribute("data-theme", "light");25 } else {26 root.removeAttribute("data-theme");27 }28 var btn = document.getElementById("theme-toggle");29 if (btn) btn.textContent = theme === "light" ? "dark" : "light";30 }3132 document.addEventListener("DOMContentLoaded", function () {33 applyTheme(currentTheme());34 var btn = document.getElementById("theme-toggle");35 if (btn) {36 btn.addEventListener("click", function () {37 var next = currentTheme() === "light" ? "dark" : "light";38 try {39 localStorage.setItem("khaelor-theme", next);40 } catch (e) {41 /* private mode — theme just won't persist */42 }43 applyTheme(next);44 });45 }4647 highlightNav();48 addCopyButtons();49 });5051 // ── active nav highlighting ────────────────────────────────────────52 function highlightNav() {53 var path = location.pathname.replace(/\/+$/, "");54 if (path === "") path = "/index.html";55 var links = document.querySelectorAll(".sidebar a, .site-header nav a");56 links.forEach(function (a) {57 var href = a.getAttribute("href");58 if (!href || href.charAt(0) === "#") return;59 var target = new URL(href, location.href).pathname.replace(/\/+$/, "");60 if (target === "") target = "/index.html";61 if (target === path) {62 a.classList.add("active");63 } else if (64 a.closest(".site-header") &&65 target.indexOf("/docs/") !== -1 &&66 path.indexOf("/docs/") === 067 ) {68 // header "Docs" link stays lit anywhere under /docs/69 a.classList.add("active");70 }71 });72 }7374 // ── copy-to-clipboard on code blocks ───────────────────────────────75 function copyText(text, btn) {76 function done(ok) {77 btn.textContent = ok ? "copied" : "failed";78 btn.classList.toggle("copied", ok);79 setTimeout(function () {80 btn.textContent = "copy";81 btn.classList.remove("copied");82 }, 1600);83 }84 if (navigator.clipboard && navigator.clipboard.writeText) {85 navigator.clipboard.writeText(text).then(86 function () { done(true); },87 function () { done(false); }88 );89 } else {90 done(false);91 }92 }9394 function addCopyButtons() {95 // <pre data-copy> blocks get a copy button; the hero install line has its own.96 document.querySelectorAll("pre").forEach(function (pre) {97 if (pre.hasAttribute("data-no-copy")) return;98 var code = pre.querySelector("code");99 if (!code) return;100 var btn = document.createElement("button");101 btn.type = "button";102 btn.className = "copy-btn";103 btn.textContent = "copy";104 btn.setAttribute("aria-label", "Copy code to clipboard");105 btn.addEventListener("click", function () {106 copyText(code.innerText.replace(/\n$/, ""), btn);107 });108 pre.appendChild(btn);109 });110111 document.querySelectorAll(".install-line").forEach(function (line) {112 var code = line.querySelector("code");113 var btn = line.querySelector("button");114 if (!code || !btn) return;115 btn.addEventListener("click", function () {116 copyText(code.innerText.trim(), btn);117 });118 });119 }120})();121