/* ============================================================ DASHBOARD — Luxsin X9 Controller Design: Main overview with volume knob, input/output, BT status ============================================================ */ import Layout from "@/components/Layout"; import GlassToggle from "@/components/GlassToggle"; import { useDevice } from "@/contexts/DeviceContext"; import { INPUT_LABELS, OUTPUT_LABELS, } from "@/lib/luxsinApi"; import { cn } from "@/lib/utils"; import { Activity, Bluetooth, ChevronDown, ChevronUp, Cpu, Headphones, Music, SkipBack, SkipForward, Volume2, VolumeX, } from "lucide-react"; import { useCallback, useRef, useState } from "react"; // ---- Volume Knob ---- function VolumeKnob() { const { deviceState, setVolume } = useDevice(); const volume = deviceState?.volume ?? 0; const percent = volume / 200; const isDragging = useRef(false); const startY = useRef(0); const startVol = useRef(0); const handleMouseDown = useCallback((e: React.MouseEvent) => { isDragging.current = true; startY.current = e.clientY; startVol.current = volume; document.addEventListener("mousemove", handleMouseMove as EventListener); document.addEventListener("mouseup", handleMouseUp); }, [volume]); const handleMouseMove = useCallback((e: MouseEvent) => { if (!isDragging.current) return; const delta = (startY.current - e.clientY) * 1.5; const newVol = Math.max(0, Math.min(200, Math.round(startVol.current + delta))); setVolume(newVol); }, [setVolume]); const handleMouseUp = useCallback(() => { isDragging.current = false; document.removeEventListener("mousemove", handleMouseMove as EventListener); document.removeEventListener("mouseup", handleMouseUp); }, [handleMouseMove]); const handleWheel = useCallback((e: React.WheelEvent) => { e.preventDefault(); const delta = e.deltaY > 0 ? -2 : 2; const newVol = Math.max(0, Math.min(200, volume + delta)); setVolume(newVol); }, [volume, setVolume]); // SVG arc calculation const size = 160; const cx = size / 2; const cy = size / 2; const r = 60; const startAngle = 135; const endAngle = 405; // 270 degree sweep const sweepAngle = (endAngle - startAngle) * percent; const polarToXY = (angle: number, radius: number) => { const rad = ((angle - 90) * Math.PI) / 180; return { x: cx + radius * Math.cos(rad), y: cy + radius * Math.sin(rad) }; }; const arcPath = (start: number, end: number, radius: number) => { const s = polarToXY(start, radius); const e = polarToXY(end, radius); const large = end - start > 180 ? 1 : 0; return `M ${s.x} ${s.y} A ${radius} ${radius} 0 ${large} 1 ${e.x} ${e.y}`; }; const thumbPos = polarToXY(startAngle + sweepAngle, r); return (
{/* Track */} {/* Progress */} {percent > 0 && ( )} {/* Thumb dot */} {/* Center display */}
{volume} VOL
{/* Volume controls */}
{Math.round(percent * 100)}%
); } // ---- Input Selector ---- function InputSelector() { const { deviceState, setInput } = useDevice(); const current = deviceState?.input ?? 0; const inputIcons: Record = { 0: USB, 1: C, 2: CO, 3: OPT, 4: , 5: ARC, 6: RCA, }; return (

Input Source

{INPUT_LABELS.map((label, i) => ( ))}
); } // ---- Output Selector ---- function OutputSelector() { const { deviceState, setOutput } = useDevice(); const current = deviceState?.output ?? 0; const outputIcons = [ XLR, RCA, , BOTH, ]; return (

Output

{OUTPUT_LABELS.map((label, i) => ( ))}
); } // ---- BT Player ---- function BluetoothPlayer() { const { deviceState, updateSetting } = useDevice(); if (!deviceState) return null; const { bt_status, bt_title, bt_artist, bt_srcname } = deviceState; const isPlaying = bt_status === 1; const isConnected = bt_status > 0; return (
Bluetooth {isConnected &&
}
{isConnected ? ( <>

{bt_title || "Unknown Track"}

{bt_artist || bt_srcname}

) : (

Not connected

)}
); } // ---- Status Cards ---- function StatusCards() { const { deviceState, updateSetting } = useDevice(); if (!deviceState) return null; const cards = [ { label: "DSP", value: deviceState.dsp_enable === 0 ? "Enabled" : "Disabled", active: deviceState.dsp_enable === 0, icon: , toggle: () => updateSetting({ dsp_enable: deviceState.dsp_enable === 0 ? 1 : 0 }), }, { label: "Audio Out", value: deviceState.audio_enable === 1 ? "Enabled" : "Muted", active: deviceState.audio_enable === 1, icon: deviceState.audio_enable === 1 ? : , toggle: () => updateSetting({ audio_enable: deviceState.audio_enable === 1 ? 0 : 1 }), }, { label: "PEQ", value: deviceState.peqEnable === 1 ? "Enabled" : "Disabled", active: deviceState.peqEnable === 1, icon: , toggle: () => updateSetting({ peqEnable: deviceState.peqEnable === 1 ? 0 : 1 }), }, ]; return (
{cards.map((card) => ( ))}
); } // ---- VU Meter Visual ---- function VuMeterDisplay() { const { deviceState, updateSetting } = useDevice(); if (!deviceState) return null; const { vu, vu_count } = deviceState; const bars = 12; return (
VU Meter Style
{Array.from({ length: vu_count || 5 }, (_, i) => ( ))}
{/* Animated VU bars */}
{Array.from({ length: bars }, (_, i) => { const height = Math.random() * 100; const isHot = i >= bars - 3; return (
= bars - 5 ? "rgba(255,200,0,0.7)" : "rgba(0,255,246,0.6)", boxShadow: isHot ? "0 0 4px rgba(255,80,80,0.5)" : "0 0 4px rgba(0,255,246,0.3)", animation: `vu-pulse ${0.3 + Math.random() * 0.4}s ease-in-out infinite alternate`, animationDelay: `${i * 0.05}s`, }} /> ); })}
); } // ---- Balance Slider ---- function BalanceControl() { const { deviceState, setBalance } = useDevice(); const balance = deviceState?.balance ?? 0; return (
L/R Balance {balance === 0 ? "Center" : balance > 0 ? `R+${balance}` : `L${balance}`}
L setBalance(parseInt(e.target.value))} className="flex-1" style={{ background: `linear-gradient(to right, rgba(0,255,246,0.3) 0%, rgba(0,255,246,0.3) ${((balance + 10) / 20) * 100}%, rgba(255,255,255,0.08) ${((balance + 10) / 20) * 100}%, rgba(255,255,255,0.08) 100%)` }} /> R
); } // ---- Device Info ---- function DeviceInfo() { const { deviceState } = useDevice(); if (!deviceState) return null; return (

Device Info

{[ { label: "Model", value: deviceState.device }, { label: "Firmware", value: deviceState.version }, { label: "MAC", value: deviceState.mac }, { label: "Format", value: deviceState.audioFormat }, ].map(({ label, value }) => (
{label} {value || "—"}
))}
); } // ---- Main Dashboard ---- export default function Dashboard() { const { deviceState } = useDevice(); return (
{/* Hero row: Volume + Input/Output */}
{/* Volume Knob Card */}

Master Volume

{/* Input / Output */}
{/* Status + BT row */}
{/* VU + Device Info */}
); }