import { Component, createSignal, createEffect, For, onCleanup, onMount, Show } from 'solid-js' import { Link, useNavigate } from '@tanstack/solid-router' import { ArrowRight, Cloud, Hash, MonitorPlay, RadioTower, Server, ShieldCheck, Video, } from 'lucide-solid' import { api } from '../../lib/api' import { permissions } from '../../lib/permissions' import { Badge, Button, Corners, LiveDot, Marquee, Meter, Reveal, } from '../../components/ui' import type { BackendInfo, Room } from '../../lib/types' const PROTOCOLS = ['WHIP', 'WHEP', 'HLS', 'FLV'] function backendIcon(kind: string) { return kind === 'BACKEND_KIND_CLOUDFLARE' ? Cloud : Server } function targetsOf(r: Room) { return r.targets?.length ?? 0 } function EmptyRooms(props: { authed: boolean }) { return (

{props.authed ? '暂无在线房间' : '房间列表需登录'}

{props.authed ? '创建一个房间后,分发拓扑会出现在这里。' : '登录后可查看与管理房间分发拓扑。'}

去登录
) } export const Home: Component = () => { const navigate = useNavigate() const [backends, setBackends] = createSignal([]) const [rooms, setRooms] = createSignal([]) const [room, setRoom] = createSignal('demo') let heroEl: HTMLElement | undefined onMount(() => { api .getConfig() .then((c) => setBackends(c.backends || [])) .catch(() => {}) const el = heroEl if (el && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) { // 鼠标视差:各层按 data-parallax 深度位移,rAF + lerp 平滑;聚光灯同步跟随 const layers = Array.from(el.querySelectorAll('[data-parallax]')) let tx = 0 let ty = 0 let cx = 0 let cy = 0 let raf = 0 const onMove = (e: MouseEvent) => { const r = el.getBoundingClientRect() tx = (e.clientX - r.left) / r.width - 0.5 ty = (e.clientY - r.top) / r.height - 0.5 el.style.setProperty('--mx', `${e.clientX - r.left}px`) el.style.setProperty('--my', `${e.clientY - r.top}px`) } const onLeave = () => { tx = 0 ty = 0 } const tick = () => { cx += (tx - cx) * 0.08 cy += (ty - cy) * 0.08 for (const layer of layers) { const d = Number(layer.dataset.parallax || 0) layer.style.transform = `translate3d(${(cx * d).toFixed(2)}px, ${(cy * d).toFixed(2)}px, 0)` } raf = requestAnimationFrame(tick) } el.addEventListener('mousemove', onMove) el.addEventListener('mouseleave', onLeave) raf = requestAnimationFrame(tick) onCleanup(() => { el.removeEventListener('mousemove', onMove) el.removeEventListener('mouseleave', onLeave) cancelAnimationFrame(raf) }) } }) // 登录态就绪后再拉房间(整页刷新时鉴权接口晚于组件挂载,需响应式等待) createEffect(() => { if (!permissions.check()) return if (!permissions.isAuthed()) return api .listRooms() .then((r) => setRooms(r.rooms || [])) .catch(() => {}) }) const go = (to: string) => navigate({ to, search: { room: room() } }) return (
{/* ===== HERO ===== */}
(heroEl = n)} class="relative overflow-hidden">
{/* ===== 在线房间 + 信号栈 ===== */}

online rooms

在线房间

0} fallback={}>
{(r, i) => (
0} />

{r.name}

{targetsOf(r)} targets · {targetsOf(r) > 0 ? 'live' : 'idle'}

)}
) }