| |
| |
| |
| |
|
|
| (function () { |
| 'use strict'; |
|
|
| const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; |
|
|
| |
| function spawnBubbles() { |
| const container = document.querySelector('.layer-bubbles'); |
| if (!container) return; |
|
|
| |
| const isMobile = window.matchMedia('(max-width: 640px)').matches; |
| const count = isMobile ? 12 : 26; |
|
|
| for (let i = 0; i < count; i++) { |
| const b = document.createElement('span'); |
| b.className = 'bubble'; |
|
|
| const size = rand(16, 82); |
| b.style.width = size + 'px'; |
| b.style.height = size + 'px'; |
| |
| const leftPct = Math.max(0, Math.min(100, (i / count) * 100 + rand(-3.5, 3.5))); |
| b.style.left = leftPct + '%'; |
| const dur = rand(12, 30); |
| b.style.animationDuration = dur + 's'; |
| |
| b.style.animationDelay = '-' + rand(0, dur) + 's'; |
| b.style.setProperty('--sway', rand(-30, 30) + 'px'); |
| b.style.setProperty('--spin', rand(-60, 60) + 'deg'); |
|
|
| container.appendChild(b); |
| } |
| } |
|
|
| |
| function initMoon() { |
| const moon = document.querySelector('.moon'); |
| if (!moon) return; |
|
|
| const reveal = () => { |
| if (window.scrollY > window.innerHeight * 0.15) { |
| moon.classList.add('is-visible'); |
| } else { |
| moon.classList.remove('is-visible'); |
| } |
| }; |
| reveal(); |
| window.addEventListener('scroll', reveal, { passive: true }); |
| } |
|
|
| |
|
|
| |
| function initCopy() { |
| const ENDPOINT = 'https://apiarium-labs.hf.space/v1/chat/completions'; |
| const toast = document.querySelector('.toast'); |
|
|
| |
| document.querySelectorAll('.endpoint code, .endpoint-box code').forEach((el) => { |
| el.textContent = ENDPOINT; |
| }); |
|
|
| async function doCopy(btn) { |
| try { |
| await navigator.clipboard.writeText(ENDPOINT); |
| } catch (e) { |
| |
| const ta = document.createElement('textarea'); |
| ta.value = ENDPOINT; |
| document.body.appendChild(ta); |
| ta.select(); |
| document.execCommand('copy'); |
| document.body.removeChild(ta); |
| } |
| const orig = btn.dataset.orig || btn.textContent; |
| btn.dataset.orig = orig; |
| btn.textContent = 'Copied'; |
| btn.classList.add('copied'); |
| if (toast) { |
| toast.classList.add('show'); |
| setTimeout(() => toast.classList.remove('show'), 1800); |
| } |
| setTimeout(() => { |
| btn.textContent = orig; |
| btn.classList.remove('copied'); |
| }, 1800); |
| } |
|
|
| |
| document.querySelectorAll('.copy-btn').forEach((btn) => { |
| btn.addEventListener('click', () => doCopy(btn)); |
| }); |
| } |
|
|
| |
| function initCountUp() { |
| const nums = document.querySelectorAll('.num[data-count]'); |
| if (reduceMotion || !('IntersectionObserver' in window)) { |
| nums.forEach((el) => { el.textContent = formatNum(+el.dataset.count); }); |
| return; |
| } |
| const io = new IntersectionObserver((entries) => { |
| entries.forEach((entry) => { |
| if (!entry.isIntersecting) return; |
| const el = entry.target; |
| const target = +el.dataset.count; |
| const dur = 1400; |
| const startT = performance.now(); |
| function tick(now) { |
| const p = Math.min(1, (now - startT) / dur); |
| const eased = 1 - Math.pow(1 - p, 3); |
| el.textContent = formatNum(Math.round(target * eased)); |
| if (p < 1) requestAnimationFrame(tick); |
| } |
| requestAnimationFrame(tick); |
| io.unobserve(el); |
| }); |
| }, { threshold: 0.5 }); |
| nums.forEach((el) => io.observe(el)); |
| } |
|
|
| |
| function rand(min, max) { |
| return Math.random() * (max - min) + min; |
| } |
| function formatNum(n) { |
| return n.toLocaleString('en-US'); |
| } |
|
|
| |
| function init() { |
| spawnBubbles(); |
| initMoon(); |
| initCopy(); |
| initCountUp(); |
| } |
|
|
| if (document.readyState === 'loading') { |
| document.addEventListener('DOMContentLoaded', init); |
| } else { |
| init(); |
| } |
| })(); |
|
|