(() => { // Shared with the console (useTheme.ts, index.html) so one choice applies everywhere. const THEME_KEY = 'floci-theme'; const root = document.documentElement; const buttons = document.querySelectorAll('.tg button'); const reducedMotion = matchMedia('(prefers-reduced-motion: reduce)').matches; function setTheme(theme) { root.setAttribute('data-theme', theme); try { localStorage.setItem(THEME_KEY, theme); } catch (ignored) { // Theme persistence is optional when browser storage is unavailable. } buttons.forEach((button) => button.classList.toggle('on', button.dataset.t === theme)); } let initialTheme = matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'; try { const savedTheme = localStorage.getItem(THEME_KEY) ?? localStorage.getItem('thecloud-theme'); if (savedTheme === 'dark' || savedTheme === 'light' || savedTheme === 'cyber') { initialTheme = savedTheme; } } catch (ignored) { // The OS preference remains the fallback when browser storage is unavailable. } setTheme(initialTheme); buttons.forEach((button) => button.addEventListener('click', () => setTheme(button.dataset.t))); document.addEventListener('keydown', (event) => { if ((event.key === 't' || event.key === 'T') && !event.metaKey && !event.ctrlKey && !event.altKey && !/INPUT|TEXTAREA/.test(event.target.tagName)) { const themes = ['dark', 'light', 'cyber']; setTheme(themes[(themes.indexOf(root.getAttribute('data-theme')) + 1) % themes.length]); } }); const lines = ['hello, engineer.', 'aws s3 mb s3://my-bucket', 'your own AWS, on your own hardware.', 'sfn start-execution --local', 'welcome to TheCloud.']; const typewriter = document.getElementById('tw'); let line = 0; let count = 0; let deleting = false; if (reducedMotion) { typewriter.textContent = lines[2]; } else { function type() { const word = lines[line]; if (deleting) { count--; typewriter.textContent = word.slice(0, count); if (count === 0) { deleting = false; line = (line + 1) % lines.length; setTimeout(type, 400); return; } setTimeout(type, 35); return; } count++; typewriter.textContent = word.slice(0, count); if (count === word.length) { deleting = true; setTimeout(type, 2200); return; } setTimeout(type, 75 + Math.random() * 50); } type(); } const hero = document.getElementById('top'); const glow = document.getElementById('grid-glow'); if (!reducedMotion) { hero.addEventListener('mousemove', (event) => { const rect = hero.getBoundingClientRect(); glow.style.left = `${event.clientX - rect.left}px`; glow.style.top = `${event.clientY - rect.top}px`; glow.style.opacity = '1'; }); hero.addEventListener('mouseleave', () => { glow.style.opacity = '0'; }); } const progress = document.getElementById('bar'); addEventListener('scroll', () => { const height = document.documentElement.scrollHeight - innerHeight; progress.style.width = `${height > 0 ? scrollY / height * 100 : 0}%`; }, {passive: true}); const form = document.querySelector('[data-sso-form]'); if (form) { form.addEventListener('submit', () => { const button = form.querySelector('.btn'); button.classList.add('is-loading'); button.disabled = true; document.getElementById('label').textContent = 'Redirecting...'; }); } const copy = document.getElementById('cp'); if (copy) { copy.addEventListener('click', () => { navigator.clipboard.writeText(document.getElementById('code').innerText).then(() => { copy.textContent = 'copied'; copy.classList.add('ok'); setTimeout(() => { copy.textContent = 'copy'; copy.classList.remove('ok'); }, 1800); }); }); } })();