(function() {
'use strict';
const theme = {
get() {
return document.documentElement.getAttribute('data-theme') || 'dark';
},
set(themeName) {
document.documentElement.setAttribute('data-theme', themeName);
localStorage.setItem('theme', themeName);
},
toggle() {
const current = this.get();
const next = current === 'dark' ? 'light' : 'dark';
document.body.classList.add('theme-transition');
this.set(next);
setTimeout(() => {
document.body.classList.remove('theme-transition');
}, 300);
},
init() {
const saved = localStorage.getItem('theme');
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const defaultTheme = systemPrefersDark ? 'dark' : 'light';
this.set(saved || defaultTheme);
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
this.set(e.matches ? 'dark' : 'light');
}
});
}
};
theme.init();
const setupThemeToggle = () => {
const toggleBtn = document.querySelector('.theme-toggle');
if (toggleBtn) {
toggleBtn.addEventListener('click', () => theme.toggle());
}
};
const setupMobileNav = () => {
const toggle = document.querySelector('.nav-toggle');
const menu = document.querySelector('.nav-menu');
if (!toggle || !menu) return;
toggle.addEventListener('click', () => {
const isOpen = toggle.getAttribute('aria-expanded') === 'true';
toggle.setAttribute('aria-expanded', !isOpen);
menu.classList.toggle('is-open');
});
document.addEventListener('click', (e) => {
if (!toggle.contains(e.target) && !menu.contains(e.target)) {
toggle.setAttribute('aria-expanded', 'false');
menu.classList.remove('is-open');
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && menu.classList.contains('is-open')) {
toggle.setAttribute('aria-expanded', 'false');
menu.classList.remove('is-open');
toggle.focus();
}
});
};
const setupSmoothScroll = () => {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', (e) => {
e.preventDefault();
const target = document.querySelector(anchor.getAttribute('href'));
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
});
};
const setupBackToTop = () => {
const btn = document.querySelector('.back-to-top');
if (!btn) return;
let lastScroll = 0;
window.addEventListener('scroll', () => {
const currentScroll = window.pageYOffset;
if (currentScroll > 300) {
btn.classList.add('visible');
} else {
btn.classList.remove('visible');
}
lastScroll = currentScroll;
}, { passive: true });
btn.addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
};
const setupReadingProgress = () => {
const progress = document.querySelector('.reading-progress');
if (!progress) return;
window.addEventListener('scroll', () => {
const winScroll = document.body.scrollTop || document.documentElement.scrollTop;
const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
const scrolled = (winScroll / height) * 100;
progress.style.width = scrolled + '%';
}, { passive: true });
};
const setupCodeCopy = () => {
document.querySelectorAll('pre').forEach(pre => {
const btn = document.createElement('button');
btn.className = 'code-copy';
btn.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>';
btn.setAttribute('aria-label', 'Copy code');
btn.addEventListener('click', async () => {
const code = pre.textContent;
try {
await navigator.clipboard.writeText(code);
btn.classList.add('copied');
setTimeout(() => btn.classList.remove('copied'), 2000);
} catch (err) {
console.error('Failed to copy:', err);
}
});
pre.style.position = 'relative';
pre.appendChild(btn);
});
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
setupThemeToggle();
setupMobileNav();
setupSmoothScroll();
setupBackToTop();
setupReadingProgress();
setupCodeCopy();
});
} else {
setupThemeToggle();
setupMobileNav();
setupSmoothScroll();
setupBackToTop();
setupReadingProgress();
setupCodeCopy();
}
})();