import Hls from "hls.js"; import "./style.css"; // Main application state with Alpine.js declare global { interface Window { app: () => any; switchRoom: (room: string) => void; sendMessage: () => void; togglePrivateDM: () => void; } } let hls: Hls | null = null; let ws: WebSocket | null = null; let currentRoom = "main"; let username = "viewer" + Math.floor(Math.random() * 1000); function resolveWebSocketBaseUrl() { const configuredUrl = import.meta.env.VITE_PUBLIC_WS_URL?.trim(); if (configuredUrl) { return configuredUrl; } const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; return `${protocol}//${window.location.host}/ws`; } function resolveManifestUrl() { const configuredUrl = import.meta.env.VITE_PUBLIC_HLS_URL?.trim(); const cacheBust = `stream=${Date.now()}`; const sameOriginManifestUrl = new URL( "/hls/playlist.m3u8", window.location.origin, ); sameOriginManifestUrl.searchParams.set("stream", cacheBust); const allowCrossOriginManifest = import.meta.env.VITE_PUBLIC_HLS_CROSS_ORIGIN?.trim().toLowerCase() === "true"; if (configuredUrl) { const manifestUrl = new URL(configuredUrl); manifestUrl.searchParams.set("stream", cacheBust); if ( !allowCrossOriginManifest && manifestUrl.origin !== window.location.origin ) { return sameOriginManifestUrl.toString(); } return manifestUrl.toString(); } return sameOriginManifestUrl.toString(); } function connectWebSocket(room: string) { if (ws) ws.close(); const socketUrl = new URL(resolveWebSocketBaseUrl()); socketUrl.searchParams.set("username", username); socketUrl.searchParams.set("room", room); ws = new WebSocket(socketUrl); ws.onopen = () => { console.log(`Connected to chat room: ${room}`); addMessage("system", "Connected to " + room); }; ws.onmessage = (event) => { const msg = JSON.parse(event.data); addMessage(msg.type || "public", msg.text, msg.username, msg.timestamp); }; ws.onclose = () => { console.log("Chat disconnected. Reconnecting in 3s..."); setTimeout(() => connectWebSocket(room), 3000); }; } function addMessage( type: string, text: string, sender = "", timestamp = new Date(), ) { const messages = document.getElementById("messages")!; const div = document.createElement("div"); div.className = `message flex ${type === "system" ? "justify-center" : sender === username ? "justify-end" : "justify-start"}`; if (type === "system") { div.innerHTML = `
${text}
`; } else { div.innerHTML = `
${sender && sender !== username ? `
${sender}
` : ""}
${text}
${new Date(timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}
`; } messages.appendChild(div); messages.scrollTop = messages.scrollHeight; } function sendMessage() { const input = document.getElementById("message-input") as HTMLInputElement; const text = input.value.trim(); if (!text || !ws) return; const msg = { room: currentRoom, username: username, text: text, type: currentRoom.startsWith("dm:") ? "private" : "public", }; ws.send(JSON.stringify(msg)); input.value = ""; } function switchRoom(room: string) { currentRoom = room; document.getElementById("room-name")!.textContent = room === "main" ? "Main Channel" : room.charAt(0).toUpperCase() + room.slice(1) + " Room"; addMessage("system", `Switched to ${room} room`); connectWebSocket(room); } function togglePrivateDM() { const target = prompt("Enter username to start private chat with:"); if (target && target !== username) { const dmRoom = `dm:${[username, target].sort().join("-")}`; switchRoom(dmRoom); } } // Alpine.js app window.app = () => { return { init() { // Initialize Tailwind // @ts-ignore tailwind.config = { content: [], theme: { extend: {} }, }; // Initialize HLS Player const video = document.getElementById("player") as HTMLVideoElement; const manifestUrl = resolveManifestUrl(); if (Hls.isSupported()) { hls = new Hls(); hls.loadSource(manifestUrl); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, () => video.play()); } else if (video.canPlayType("application/vnd.apple.mpegurl")) { video.src = manifestUrl; video.addEventListener("loadedmetadata", () => video.play()); } // Connect to default chat room connectWebSocket("main"); // Simulate now playing setInterval(() => { const titles = [ "Midnight Jazz Lounge", "Synthwave Nights", "Classic Cinema", "Ambient Void", ]; document.getElementById("now-playing")!.textContent = titles[Math.floor(Math.random() * titles.length)]; const overlay = document.getElementById("now-playing-overlay")!; overlay.classList.remove("hidden"); document.getElementById("overlay-title")!.textContent = titles[Math.floor(Math.random() * titles.length)]; }, 15000); console.log( "Linear Void Frontend initialized - HLS Player + Chat Sidebar ready", ); }, }; }; // Make functions global for onclick handlers window.switchRoom = switchRoom; window.sendMessage = sendMessage; window.togglePrivateDM = togglePrivateDM; // Auto initialize when loaded document.addEventListener("DOMContentLoaded", () => { // @ts-ignore window.app().init(); });