const $ = (selector, root = document) => root.querySelector(selector); const $$ = (selector, root = document) => [...root.querySelectorAll(selector)]; const THEME_KEY = "obyte-theme"; const themeToggle = $("#themeToggle"); const toast = $("#toast"); let toastTimer = null; function showToast(message) { if (!toast) return; toast.textContent = message; toast.classList.add("is-on"); clearTimeout(toastTimer); toastTimer = setTimeout(() => toast.classList.remove("is-on"), 1400); } async function copyText(value) { try { await navigator.clipboard.writeText(value); showToast("Kopiert: " + value); } catch { const fallback = document.createElement("textarea"); fallback.value = value; fallback.style.position = "fixed"; fallback.style.left = "-9999px"; document.body.appendChild(fallback); fallback.select(); document.execCommand("copy"); fallback.remove(); showToast("Kopiert: " + value); } } document.addEventListener("click", (event) => { const trigger = event.target.closest("[data-copy-value],[data-copy-block]"); if (!trigger) return; const directValue = trigger.getAttribute("data-copy-value"); const blockSelector = trigger.getAttribute("data-copy-block"); if (directValue) { copyText(directValue); return; } if (blockSelector) { const block = $(blockSelector); if (block) { copyText(block.innerText.trim()); } } }); function applyTheme(theme) { const isDark = theme === "dark"; document.documentElement.setAttribute("data-theme", isDark ? "dark" : "light"); if (themeToggle) { themeToggle.setAttribute("aria-pressed", String(isDark)); themeToggle.textContent = isDark ? "Hell" : "Dunkel"; } } function loadInitialTheme() { let savedTheme = null; try { savedTheme = localStorage.getItem(THEME_KEY); } catch { savedTheme = null; } if (savedTheme === "light" || savedTheme === "dark") { applyTheme(savedTheme); return; } const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; applyTheme(prefersDark ? "dark" : "light"); } themeToggle?.addEventListener("click", () => { const currentTheme = document.documentElement.getAttribute("data-theme"); const nextTheme = currentTheme === "dark" ? "light" : "dark"; applyTheme(nextTheme); try { localStorage.setItem(THEME_KEY, nextTheme); } catch { // Ignore storage errors in restrictive browser contexts. } }); loadInitialTheme(); const navToggle = $("#navToggle"); const navLinks = $("#navLinks"); navToggle?.addEventListener("click", () => { const open = navLinks?.classList.toggle("is-open"); navToggle.setAttribute("aria-expanded", String(open)); }); $$(".navlink").forEach((link) => { link.addEventListener("click", () => { if (window.matchMedia("(max-width: 980px)").matches) { navLinks?.classList.remove("is-open"); navToggle?.setAttribute("aria-expanded", "false"); } }); }); const sections = $$(".section"); const links = $$(".navlink"); function setActive(id) { links.forEach((link) => { link.classList.toggle("is-active", link.getAttribute("href") === "#" + id); }); } const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting && entry.target.id) { setActive(entry.target.id); } }); }, { threshold: 0.28 } ); sections.forEach((section) => observer.observe(section)); if (location.hash) { setActive(location.hash.replace("#", "")); }