Spaces:
Sleeping
Sleeping
| import { createSmoothRenderer } from "./shared.js"; | |
| const socket = io(); | |
| const display = document.getElementById("display"); | |
| const renderer = createSmoothRenderer({ | |
| setText: (t) => (display.textContent = t), | |
| }); | |
| // Identify to server (for admin client list) | |
| socket.on("connect", () => { | |
| socket.emit("client:hello", { | |
| ua: navigator.userAgent, | |
| url: location.href, | |
| tz: Intl.DateTimeFormat().resolvedOptions().timeZone, | |
| lang: navigator.language, | |
| screen: `${window.screen.width}x${window.screen.height}` | |
| }); | |
| }); | |
| // Stopwatch state from server (includes color & blackout) | |
| socket.on("state", (s) => { | |
| renderer.applyServerState(s); | |
| if (s.color) display.style.color = s.color; | |
| // Blackout | |
| const overlay = document.getElementById("blackOverlay"); | |
| if (overlay) { | |
| if (s.blackout) overlay.classList.add("active"); | |
| else overlay.classList.remove("active"); | |
| } | |
| }); | |
| // Latency ping/pong for admin stats | |
| socket.on("srv:ping", ({ sentTs }) => { | |
| socket.emit("srv:pong", { sentTs }); | |
| }); | |
| // 🚀 Wake command from admin — exit blackout and flash brightness | |
| socket.on("wake", () => { | |
| const overlay = document.getElementById("blackOverlay"); | |
| overlay?.classList.remove("active"); | |
| display.style.transition = "filter 0.3s ease"; | |
| display.style.filter = "brightness(1.8)"; | |
| setTimeout(() => { | |
| display.style.filter = "brightness(1)"; | |
| }, 600); | |
| }); | |
| /* -------- Fullscreen helpers -------- */ | |
| function isFullscreen() { | |
| return document.fullscreenElement || document.webkitFullscreenElement; | |
| } | |
| async function enterFullscreen() { | |
| const el = document.documentElement; | |
| try { | |
| if (el.requestFullscreen) await el.requestFullscreen(); | |
| else if (el.webkitRequestFullscreen) await el.webkitRequestFullscreen(); | |
| } catch (e) {} | |
| } | |
| async function exitFullscreen() { | |
| try { | |
| if (document.exitFullscreen) await document.exitFullscreen(); | |
| else if (document.webkitExitFullscreen) await document.webkitExitFullscreen(); | |
| } catch {} | |
| } | |
| async function toggleFullscreen() { | |
| if (isFullscreen()) await exitFullscreen(); | |
| else await enterFullscreen(); | |
| } | |
| /* UI controls */ | |
| const fsBtn = document.getElementById("fsBtn"); | |
| const hideBtn = document.getElementById("hideBtn"); | |
| fsBtn?.addEventListener("click", () => toggleFullscreen()); | |
| hideBtn?.addEventListener("click", () => { | |
| const controls = document.querySelector(".controls"); | |
| controls?.remove(); // Hide for kiosk mode | |
| }); | |
| // Double-click or press 'F' to toggle fullscreen | |
| document.addEventListener("dblclick", toggleFullscreen); | |
| document.addEventListener("keydown", (e) => { | |
| if (e.key.toLowerCase() === "f") toggleFullscreen(); | |
| }); | |