homelab-server/website/script.js

109 lines
3.3 KiB
JavaScript

// =========================================================================
// Rapture Systems Telemetry & Dynamic Dossier Engine (Simplified)
// =========================================================================
// Audio Context
let audioCtx;
function initAudio() {
if (audioCtx) return;
const AudioContext = window.AudioContext || window.webkitAudioContext;
audioCtx = new AudioContext();
}
// Synthesizes a polished brass mechanical click for hover events
function playClickSound() {
initAudio();
if (!audioCtx || audioCtx.state === 'suspended') return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(1600, audioCtx.currentTime);
osc.frequency.exponentialRampToValueAtTime(600, audioCtx.currentTime + 0.02);
gain.gain.setValueAtTime(0.025, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.02);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + 0.02);
}
// -------------------------------------------------------------------------
// Interactive Systems Initialized
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// FEATURE: Page-Wide Dynamic Bubble Trail (Following Cursor)
// -------------------------------------------------------------------------
let lastBubbleTime = 0;
function createHoverBubble(e) {
const now = Date.now();
if (now - lastBubbleTime < 45) return; // High frequency trail
lastBubbleTime = now;
const bubble = document.createElement('span');
// Alternate between sparkling golden and cyan bubbles
const isGold = Math.random() > 0.5;
bubble.className = isGold ? 'hover-bubble bubble-gold-sparkle' : 'hover-bubble bubble-cyan-sparkle';
// Spawn coordinates centered on cursor
const size = Math.random() * 6 + 3; // 3px to 9px
bubble.style.width = `${size}px`;
bubble.style.height = `${size}px`;
bubble.style.left = `${e.pageX}px`;
bubble.style.top = `${e.pageY}px`;
const drift = (Math.random() - 0.5) * 60; // Sway range
bubble.style.setProperty('--drift', `${drift}px`);
document.body.appendChild(bubble);
// Remove bubble after animation ends
setTimeout(() => {
bubble.remove();
}, 1200);
}
// Bind bubble trail to the entire page body
document.addEventListener('mousemove', createHoverBubble);
// Audio Context unlock triggers on first mouse click
document.body.addEventListener('click', () => {
initAudio();
}, { once: true });
// Mechanical hover sound bindings for all links and cards
document.querySelectorAll('a, button, .tag, .card, .ryan-quote').forEach(el => {
el.addEventListener('mouseenter', () => {
playClickSound();
});
});
// Fetch active VM count dynamically from status.json
function fetchActiveVMs() {
fetch('status.json')
.then(res => {
if (!res.ok) throw new Error();
return res.json();
})
.then(data => {
if (data.active_vms !== undefined) {
const el = document.getElementById('active-vm-count');
if (el) {
el.textContent = `${data.active_vms} Virtual Machines`;
}
}
})
.catch(() => {
// Quietly fall back to default HTML value if not available locally
});
}
fetchActiveVMs();