diff --git a/server/public/assets/index-BjEIiAR_.js b/server/public/assets/index-CR--WtYL.js similarity index 94% rename from server/public/assets/index-BjEIiAR_.js rename to server/public/assets/index-CR--WtYL.js index e0b59f3c..4c024478 100644 --- a/server/public/assets/index-BjEIiAR_.js +++ b/server/public/assets/index-CR--WtYL.js @@ -15,7 +15,7 @@ Please change the parent to i&&i>o){const f=fi/100,d=1-f;if(a<=o)return 0;if(a<=i){const p=(a-o)/(i-o);return Math.round(Math.max(0,Math.min(1,p))*fi)}if(a>=l)return 100;const h=(a-i)/(l-i);return Math.round((f+Math.max(0,Math.min(1,h))*d)*100)}return null}const Aj=1600,jj=800;function pn(...a){return a.filter(Boolean).join(" ")}function Ef({visual:a,orientation:l="horizontal",variant:i="inline",compact:o=!1,showLabel:f,className:d=""}){const h=i==="background",p=l==="vertical",x=f??!h;if(!a?.available){if(h){const ae=fi,ue=pn("absolute z-10",p?"left-0 right-0 h-[2px]":"top-0 bottom-0 w-[2px]","bg-black/70",M&&!j&&"battery-tick-warn"),re=p?{bottom:`${ae}%`}:{left:`${ae}%`},F=pn("absolute inset-0 pointer-events-none",d);return r.jsxs("div",{className:F,children:[r.jsx("div",{className:"absolute inset-0 bg-slate-900/35"}),r.jsx("div",{className:ue,style:re})]})}return p?null:r.jsx("p",{className:"text-xs text-slate-500",children:"Battery telemetry unavailable"})}const g=a.percentDisplay??0,b=Math.max(0,Math.min(100,g)),y=a.warnDisplayPercent??fi,j=!!a.urgentActive,M=!!a.warnActive,C=j?`${jj}ms`:M?`${Aj}ms`:void 0,N=h?j||M?"bg-red-500/45":"bg-emerald-500/45":j||M?"bg-red-500":"bg-emerald-500",q=j||M?"bg-red-900/35":"bg-slate-900/35",A=j||M?"bg-red-900/45":"bg-slate-900/35",E=pn(h?"absolute inset-0 pointer-events-none":"relative pointer-events-auto",p&&!h?"flex flex-col items-center justify-center":"w-full",d);if(h){const ae=p?{height:`${b}%`}:{width:`${b}%`},ue=pn("absolute",p?"bottom-0 left-0 right-0":"left-0 top-0 bottom-0",N,"transition-[width,height]",j&&"battery-urgent-flash"),re=pn("absolute z-10",p?"left-0 right-0 h-[2px]":"top-0 bottom-0 w-[2px]","bg-black/70",M&&!j&&"battery-tick-warn"),F=p?{bottom:`${y}%`}:{left:`${y}%`};return r.jsxs("div",{className:E,children:[r.jsx("div",{className:pn("absolute inset-0",q,j&&"battery-urgent-flash")}),r.jsx("div",{className:ue,style:{...ae,animationDuration:C}}),r.jsx("div",{className:re,style:F})]})}const T=pn("relative overflow-hidden rounded-sm border border-slate-800",j||M?A:"bg-zinc-900/90",j&&"battery-urgent-flash",p?"w-full flex-1":o?"h-4":"h-5"),B=p?{height:`${b}%`}:{width:`${b}%`},U=pn("absolute",p?"bottom-0 left-0 right-0":"left-0 top-0 bottom-0",N,"transition-[width,height]",j&&"battery-urgent-flash"),O=pn("absolute z-10",p?"left-0 right-0 h-[2px]":"top-0 bottom-0 w-[2px]","bg-black/70",M&&!j&&"battery-tick-warn"),Q=p?{bottom:`${y}%`}:{left:`${y}%`},L=`Battery ${g}%`,G=pn("pointer-events-none absolute inset-0 flex items-center justify-center text-center font-semibold",p?o?"text-[0.55rem]":"text-[0.65rem]":"text-xs"),Z=pn("rounded px-1.5 py-0.5",h?"bg-black/30 text-slate-100":"bg-black/50 text-slate-100");return r.jsxs("div",{className:E,children:[r.jsxs("div",{className:T,children:[r.jsx("div",{className:U,style:{...B,animationDuration:C}}),r.jsx("div",{className:O,style:Q})]}),x&&!p?r.jsx("div",{className:G,children:r.jsx("span",{className:Z,children:L})}):null,x&&p?r.jsx("div",{className:"mt-0.5 text-[0.65rem] font-semibold text-slate-100",children:r.jsxs("span",{className:Z,children:[g,"%"]})}):null]})}const $p=2e3,Nj=3e3,wj=3e3,Cj=40,Ej=1e3;function di({sessionInfo:a,audioSessionInfo:l,videoMode:i="whep",snapshotFeed:o=null,qualityNotice:f=null,label:d,forceMute:h=!1,telemetryFrame:p,batteryConfig:x,layoutFormat:g="desktop",hudVariant:b="default",driverLabel:y=null,hudForceMap:j=!1,hudMapPosition:M="top-center",hudLabelScale:C=1,fitParent:w=!1,overcurrentLimiter:N=null,showTurnCue:q=!1,turnTimerText:A=null,turnSeconds:E=null,isActiveDriver:T=!1,idleSkipSeconds:B=null}){const{session:U}=_e(),O=m.useRef(null),Q=m.useRef(null),L=m.useRef(null),G=m.useRef(null),Z=m.useRef(null),ae=m.useRef(null),ue=m.useRef(null),re=m.useRef(null),[F,R]=m.useState("idle"),[X,te]=m.useState(null),[se,me]=m.useState("idle"),[k,Y]=m.useState(null),[ne,le]=m.useState(0),[pe,ie]=m.useState(0),[fe,ce]=m.useState(!0),oe=!!l?.url,de=i==="snapshot",Ae=p?.sensors,{value:H}=Pe("audio",wt),P=Number.isFinite(H?.masterVolume)?H.masterVolume:wt.masterVolume,he=Number.isFinite(H?.roverVolume)?H.roverVolume:wt.roverVolume,Se=typeof H?.mainBrushDuckEnabled=="boolean"?H.mainBrushDuckEnabled:typeof H?.autoLevelEnabled=="boolean"?H.autoLevelEnabled:wt.mainBrushDuckEnabled,De=Number.isFinite(H?.mainBrushDuckAmount)?Math.max(0,Math.min(1,H.mainBrushDuckAmount)):wt.mainBrushDuckAmount,ze=Math.max(0,Math.min(1,P*he)),yt=Ae?.batteryChargeMah??null,Mt=g==="desktop",pt=!Mt,Et=pt?"top-right":M,[qn]=hg(),yo=j||pt?!0:qn,mt=pg({charge:yt,config:x}),el=Ae?.wheelOvercurrents||null,tl=el==null?[]:Object.entries(el).filter(([,ye])=>!!ye).map(([ye])=>ye),un=N?.caps||null,Ea=N?.overcurrent?.groups||null,Si=typeof window<"u"&&new URLSearchParams(window.location.search).has("debugAudio"),nl=typeof window<"u"&&new URLSearchParams(window.location.search).has("debugHud"),bo=m.useMemo(()=>{if(!un)return null;const ye=Number.isFinite(un?.drive?.cap)?un.drive.cap:1,Xe=Number.isFinite(un?.aux?.cap)?un.aux.cap:1;return Math.max(0,Math.min(1,1-Math.min(ye,Xe)))},[un]),as=!!N?.isActive,fn=tl.length?tl:as?["limiter"]:[],xt=bo??(tl.length?1:0),Kt=!!fn.length,rt=!!((Number(Ae?.mainBrushCurrentMa)||0)>Cj||Ae?.wheelOvercurrents?.mainBrush),So=Se&&rt?1-De:1,al=Math.max(0,Math.min(1,ze*So)),Ao=Se&&rt&&De>0?`Volume decreased ${Math.round(De*1e3)/10}%`:null,ka=m.useRef({hasDedicatedAudio:!1,audioUrl:null,mainBrushDuckEnabled:!1,mainBrushDuckAmount:0,mainBrushActive:!1,baseRoverGain:0,effectiveRoverGain:0});m.useEffect(()=>{ka.current={hasDedicatedAudio:oe,audioUrl:l?.url||null,mainBrushDuckEnabled:Se,mainBrushDuckAmount:De,mainBrushActive:rt,baseRoverGain:ze,effectiveRoverGain:al}},[oe,l?.url,Se,De,rt,ze,al]);const Ze=m.useCallback((ye,Xe={})=>{if(!Si)return;const Qe=Q.current,Ce=ka.current,He={event:ye,ts:Date.now(),roverLabel:d||null,hasDedicatedAudio:Ce.hasDedicatedAudio,audioUrl:Ce.audioUrl,mainBrushDuckEnabled:Ce.mainBrushDuckEnabled,mainBrushDuckAmount:Ce.mainBrushDuckAmount,mainBrushActive:Ce.mainBrushActive,baseRoverGain:Ce.baseRoverGain,effectiveRoverGain:Ce.effectiveRoverGain,element:Qe?{muted:Qe.muted,volume:Qe.volume,paused:Qe.paused,ended:Qe.ended,readyState:Qe.readyState,networkState:Qe.networkState}:null,...Xe};try{console.log(`[AudioDebug] ${ye} ${JSON.stringify(He)}`)}catch{console.log("[AudioDebug]",ye,He)}},[Si,d]),ll=U?.socials?.find(ye=>String(ye?.id||ye?.label||"").toLowerCase()==="discord")?.url||U?.discord?.invite||null;m.useEffect(()=>{nl&&console.log("[OvercurrentHUD]",{overlayVisible:Kt,overlayMotors:fn,overlayFill:xt,limiterActive:as,limiterCaps:un,limiterGroups:Ea,wheelOvercurrents:el})},[nl,xt,fn,Kt,as,un,Ea,el]),m.useEffect(()=>{Ze("settings/update")},[Ze]);const zt=m.useCallback(()=>{clearTimeout(L.current),L.current=setTimeout(()=>le(Date.now()),$p)},[]),ta=m.useCallback(()=>{clearTimeout(G.current),G.current=setTimeout(()=>ie(Date.now()),$p)},[]),vn=m.useCallback(async()=>{const ye=O.current;if(ye)try{ye.muted=!0,await ye.play()}catch{}},[]),Ai=m.useCallback((ye=0)=>{if(h||oe)return;clearTimeout(ae.current);const Xe=()=>{clearTimeout(ae.current),ae.current=setTimeout(()=>{h||Qe()},Nj)},Qe=async()=>{const Ce=O.current;if(Ce)try{await vn(),Ce.muted=!1,await Ce.play(),ce(!1)}catch{Ce.muted=!0,ce(!0),Xe()}};ae.current=setTimeout(Qe,ye)},[vn,h,oe]);m.useEffect(()=>()=>{clearTimeout(L.current),clearTimeout(G.current),clearTimeout(ae.current),clearInterval(Z.current),ue.current&&(cancelAnimationFrame(ue.current),ue.current=null)},[]),m.useEffect(()=>{F==="playing"&&Ai(0)},[F,Ai]),m.useEffect(()=>{de&&(R("snapshot"),te(null))},[de]),m.useEffect(()=>{if(de||!a?.url||!O.current)return;let ye=!0,Xe;const Qe=setTimeout(()=>ce(!0),0),Ce=(He,bt)=>{ye&&(Ze("video/status",{nextStatus:He,info:bt||null}),R(He),te(bt||null),He==="playing"&&vn(),["error","failed","disconnected","closed"].includes(He)&&zt())};return Xe=new Hp({url:a.url,token:a.token,video:O.current,receiveAudio:!oe,onStatus:Ce}),Xe.start().catch(He=>{ye&&(R("error"),te(He.message),zt())}),()=>{ye=!1,clearTimeout(Qe),Xe?.stop()}},[de,a?.url,a?.token,ne,zt,vn,oe,Ze]),m.useEffect(()=>{F==="stopped"&&a?.url&&zt()},[F,a?.url,zt]),m.useEffect(()=>{const ye=Q.current;if(!ye||!l?.url){Ze("route/no-audio-url"),re.current=null;return}const Xe=Math.max(0,Math.min(1,al)),Qe=re.current==null?Math.max(0,Math.min(1,ye.volume)):re.current;if(ue.current&&(cancelAnimationFrame(ue.current),ue.current=null),Xe>Qe){const Ce=performance.now(),He=Qe,bt=Xe-He,Rt=tt=>{const Oe=Math.min(1,(tt-Ce)/Ej),It=He+bt*Oe;ye.volume=It,re.current=It,Oe<1?ue.current=requestAnimationFrame(Rt):ue.current=null};ue.current=requestAnimationFrame(Rt)}else ye.volume=Xe,re.current=Xe;Ze("route/direct",{elementVolume:Xe,duckEnabled:Se,mainBrushActive:rt,duckAmount:De})},[l?.url,al,Se,rt,De,Ze]),m.useEffect(()=>{if(!l?.url||!Q.current)return;let ye=!0,Xe;const Qe=(Ce,He)=>{ye&&(Ze("audio/status",{nextStatus:Ce,info:He||null}),Y(He||(Ce==="connected"?"connected":null)),me(bt=>Ce==="connected"&&(bt==="playing"||bt==="connecting")||Ce==="new"?bt:Ce),["error","failed"].includes(Ce)&&ta())};return Xe=new Hp({url:l.url,token:l.token,video:Q.current,audioOnly:!0,onStatus:Qe}),Xe.start().catch(Ce=>{ye&&(me("error"),Y(Ce.message),ta())}),()=>{ye=!1,Xe?.stop()}},[l?.url,l?.token,pe,ta,Ze]),m.useEffect(()=>{const ye=Q.current;if(!l?.url||!ye){clearInterval(Z.current);return}if(!["connecting","connected","playing","paused"].includes(se)){clearInterval(Z.current);return}const Qe=async()=>{const Ce=Q.current;Ce&&(Ce.muted=!1,!(!Ce.paused&&!Ce.ended)&&(Ze("retry/play-attempt"),Ce.play().then(()=>{Ze("retry/play-ok"),me(He=>He==="connected"?"playing":He),Y(He=>He==="paused"?null:He)}).catch(He=>{Ze("retry/play-fail",{error:He?.message}),Y(bt=>bt||He?.message||"autoplay blocked")})))};return Qe(),Z.current=setInterval(Qe,wj),()=>clearInterval(Z.current)},[l?.url,se,Ze]),m.useEffect(()=>{const ye=Q.current;if(!ye)return;const Xe=()=>{Ze("element/play"),me(Oe=>Oe==="error"?Oe:"playing"),Y(null)},Qe=()=>{Ze("element/pause"),me(Oe=>["error","failed","disconnected","closed","stopped"].includes(Oe)?Oe:"paused"),Y(Oe=>Oe||"paused")},Ce=()=>{Ze("element/ended"),me(Oe=>Oe==="error"?Oe:"stopped"),Y(Oe=>Oe||"ended")},He=()=>{const{error:Oe}=ye,It=Oe?.message||"audio error";Ze("element/error",{error:It}),me("error"),Y(It)},bt=()=>Ze("element/waiting"),Rt=()=>Ze("element/canplay"),tt=()=>Ze("element/stalled");return ye.addEventListener("play",Xe),ye.addEventListener("pause",Qe),ye.addEventListener("ended",Ce),ye.addEventListener("error",He),ye.addEventListener("waiting",bt),ye.addEventListener("canplay",Rt),ye.addEventListener("stalled",tt),()=>{ye.removeEventListener("play",Xe),ye.removeEventListener("pause",Qe),ye.removeEventListener("ended",Ce),ye.removeEventListener("error",He),ye.removeEventListener("waiting",bt),ye.removeEventListener("canplay",Rt),ye.removeEventListener("stalled",tt)}},[l?.url,Ze]);const ji=o?.error?`Error: ${o.error}`:o?.objectUrl?"snapshot":o?.status||"waiting",ls=de?ji:a?.url?F==="error"?`Error: ${X||"unknown"}`:X?`${F} (${X})`:F:"waiting",qa=l?.error?`Error: ${l.error}`:l?.url?se==="error"?`Error: ${k||"unknown"}`:k?`${se} (${k})`:se:null,Ni=b==="spectator",Zt=b==="none";return r.jsxs("div",{className:`flex flex-col gap-0.5 ${w?"h-full":""}`,children:[r.jsxs("div",{className:`relative w-full overflow-hidden bg-black ${w?"h-full flex-1":"aspect-[4/3]"}`,children:[de?o?.objectUrl?r.jsx("img",{src:o.objectUrl,alt:d,className:"h-full w-full object-contain",draggable:!1}):r.jsx("div",{className:"flex h-full w-full items-center justify-center text-sm text-slate-300",children:"Waiting for frame…"}):r.jsx("video",{ref:O,muted:h||fe||oe,playsInline:!0,autoPlay:!0,controls:!1,className:"h-full w-full object-contain"}),r.jsx("audio",{ref:Q,autoPlay:!0,hidden:!0}),!Zt&&q?r.jsx(Mj,{mobileHud:pt,turnSeconds:E,isActiveDriver:T,idleSkipSeconds:B}):null,Zt?null:r.jsx(qj,{frame:p,sensors:Ae,label:d,status:ls,audioStatus:qa,levelStatus:Ao,desktopLayout:Mt,layoutFormat:g,variant:b,driverLabel:y,battery:mt,showTopDown:yo,mobileHud:pt,mapPosition:Et,turnTimerText:A,labelScale:C}),Zt?null:r.jsx(_j,{compact:pt}),!Zt&&nl?r.jsx("div",{className:"pointer-events-none absolute left-1 top-1 z-40 rounded bg-black/80 px-1 py-0.5 text-[0.6rem] text-lime-200",children:`OC vis:${Kt?1:0} motors:${fn.length} fill:${Math.round(xt*100)}%`}):null,Zt?null:r.jsx(Tj,{motors:fn,fill:xt,compact:pt}),Zt?null:r.jsx(Dj,{battery:mt,compact:pt}),!Zt&&Ni&&mt.available?r.jsx("div",{className:"pointer-events-none absolute right-1 top-1/2 flex h-[70%] -translate-y-1/2 flex-col items-center justify-center rounded bg-black/60 px-0.5 pb-1 pt-1",children:r.jsx(Ef,{visual:mt,orientation:"vertical",variant:"inline",compact:pt,className:"h-full w-4"})}):null,!Zt&&f?r.jsx("div",{className:"pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2",children:r.jsxs("div",{className:`mx-auto w-fit rounded border border-amber-300/80 bg-black/75 text-amber-200 ${pt?"px-2 py-1 text-[0.6rem]":"px-3 py-1.5 text-sm"}`,children:[r.jsx("div",{className:"text-center",children:f}),r.jsx("div",{className:"pointer-events-auto mt-0",children:r.jsx(ed,{id:"discord",label:"Join our Discord server while you wait!",url:ll})})]})}):null]}),!Zt&&!Ni&&r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(kj,{sensors:Ae}),r.jsx("div",{className:"panel-section space-y-0.5 text-sm",children:r.jsx(Ef,{visual:mt,compact:pt})})]})]})}function kj({sensors:a}){const l=[a?.lightBumpLeftSignal,a?.lightBumpFrontLeftSignal,a?.lightBumpCenterLeftSignal,a?.lightBumpCenterRightSignal,a?.lightBumpFrontRightSignal,a?.lightBumpRightSignal],i=l.filter(p=>p!=null).reduce((p,x)=>Math.max(p,x),1200),o=p=>Math.pow(Math.max(0,Math.min(1,(p??0)/i)),.35),f=p=>p==null||p<=0?"hsl(200 60% 18%)":`hsl(${(200+o(p)*360)%360} 100% 60%)`,d=6,h=12;return r.jsx("div",{className:"flex w-full items-center justify-center gap-0.5",children:l.map((p,x)=>{const g=o(p),b=xDate.now());m.useEffect(()=>{const te=setInterval(()=>q(Date.now()),150);return()=>clearInterval(te)},[]),a?.receivedAt&&N-a.receivedAt<200;const A=j,E=p==="mobile-portrait",T=A?"text-[0.45rem]":"text-[0.65rem]",B=A?"px-0.25 py-0.25":"px-1 py-0.5",U=A?"px-0.25 py-0.25":"px-0.5 py-0.5",O=A?"text-[0.55rem]":"text-[0.8rem]",Q=A?"left-0.5 top-0.5":"left-1 top-1",L=A?"text-[0.5rem]":"text-[0.7rem]",G=A?"px-0.5 py-0.25":"px-1 py-0.5",Z=A?"left-0.5 top-1/2":"left-1 top-1/2",ae="bottom-0.5",ue={transform:`translateX(-50%) scale(${w})`,transformOrigin:"center bottom"},re="240px",F=E?.3:A?.33:.7,X={width:re,height:re,opacity:A?.6:.7,transform:M==="top-center"?`translateX(-50%) scale(${F})`:`scale(${F})`,transformOrigin:M==="bottom-left"?"bottom left":M==="top-center"?"top center":"top right",...M==="bottom-left"?{left:"0.25rem",bottom:"0.25rem"}:M==="top-center"?{left:"50%",top:"0.25rem"}:{right:"0.25rem",top:"0.25rem"}};if(x==="none")return null;if(x==="spectator"){const te=[["Voltage",l?.voltageMv!=null?`${(l.voltageMv/1e3).toFixed(2)} V`:"--"],["Current",l?.currentMa!=null?`${l.currentMa} mA`:"--"],["Charge",l?.batteryChargeMah!=null?`${l.batteryChargeMah}`:"--"],["OI",l?.oiMode?.label||"--"]],se=!!l?.chargingSources?.homeBase,me=l?.chargingState?.label||"",k=!!(me&&me.toLowerCase()!=="not charging"),Y=l?.oiMode?.label||"Unknown",ne=Y.toLowerCase(),le=ne==="full"?"bg-emerald-500/80 text-emerald-50":ne==="safe"?"bg-amber-400/80 text-amber-950":ne==="passive"?"bg-slate-700/80 text-slate-100":"bg-slate-700/60 text-slate-200",pe=se?"bg-emerald-500/80 text-emerald-50":"bg-slate-700/70 text-slate-200",ie=k?"bg-emerald-500/80 text-emerald-50":se?"bg-amber-400/80 text-amber-950":"bg-slate-700/70 text-slate-200";return r.jsxs(r.Fragment,{children:[r.jsxs("div",{className:"pointer-events-none absolute inset-0 flex items-center justify-center",children:[r.jsx("div",{className:`absolute ${Q} font-medium text-slate-100 ${T}`,children:r.jsxs("div",{className:"flex flex-col gap-0.5 leading-none",children:[r.jsxs("span",{children:["Status: ",o]}),f?r.jsxs("span",{children:["Audio: ",f]}):null,d?r.jsx("span",{className:"text-cyan-300",children:d}):null]})}),r.jsx("div",{className:`absolute ${Z} flex -translate-y-1/2 flex-col gap-0.5 bg-black/70 text-slate-100 ${T} ${B}`,children:r.jsxs("div",{className:"space-y-0.5 leading-tight",children:[r.jsxs("div",{className:"flex flex-col gap-0.5 text-[0.75rem] font-semibold uppercase tracking-wide",children:[r.jsx("span",{className:`rounded px-1.5 py-0.5 ${pe}`,children:se?"Docked":"Undocked"}),r.jsx("span",{className:`rounded px-1.5 py-0.5 ${ie}`,children:k?"Charging":"Not charging"}),r.jsxs("span",{className:`rounded px-1.5 py-0.5 ${le}`,children:["OI: ",Y]})]}),te.map(([fe,ce])=>r.jsxs("span",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{className:"text-slate-400",children:fe}),r.jsx("span",{className:"font-semibold text-white",children:ce})]},fe))]})}),r.jsx("div",{className:`absolute ${ae} left-1/2`,style:ue,children:r.jsxs("div",{className:`flex items-center gap-0.5 bg-black/80 text-slate-100 ${U} ${O}`,children:[r.jsx("span",{className:"font-semibold text-white",children:i||"Unnamed Rover"}),g?r.jsxs("span",{className:"text-slate-300",children:["• ",g]}):null]})})]}),y?r.jsx("div",{className:"pointer-events-none absolute rounded",style:{...X},children:r.jsx(Cf,{sensors:l,size:240,overlay:!0})}):null]})}return r.jsxs("div",{className:"pointer-events-none absolute inset-0 flex items-center justify-center",children:[r.jsx("div",{className:`absolute ${Q} font-medium text-slate-100 ${T}`,children:r.jsxs("div",{className:"flex flex-col gap-0.5 leading-none",children:[r.jsxs("span",{children:["Status: ",o]}),f?r.jsxs("span",{children:["Audio: ",f]}):null,d?r.jsx("span",{className:"text-cyan-300",children:d}):null]})}),C?r.jsx("div",{className:`absolute left-1/2 top-0.5 -translate-x-1/2 rounded bg-black/70 text-slate-100 ${G} ${L}`,children:C}):null,r.jsx("div",{className:`absolute ${ae} left-1/2`,style:ue,children:r.jsx("div",{className:`flex gap-0.5 bg-black/80 text-slate-100 ${U} ${O}`,children:r.jsxs("span",{children:['Rover: "',i||"Unnamed Rover",'"']})})}),y&&x!=="spectator"?r.jsx("div",{className:"pointer-events-none absolute rounded",style:{...X},children:r.jsx(Cf,{sensors:l,size:240,overlay:!0})}):null]})}function Mj({mobileHud:a=!1,turnSeconds:l=null,isActiveDriver:i=!1,idleSkipSeconds:o=null}){const f=a?"text-3xl":"text-5xl",d=a?"text-xs":"text-sm",h=a?"text-[0.55rem]":"text-[0.75rem]",p=a?"px-4 py-3":"px-6 py-4",x=i&&typeof o=="number";return r.jsx("div",{className:"pointer-events-none absolute inset-0 z-30 flex items-center justify-center bg-black/55",children:r.jsxs("div",{className:`flex flex-col items-center gap-0.5 rounded border border-amber-300/80 bg-black/70 ${p}`,children:[r.jsx("div",{className:`font-semibold text-amber-200 ${f}`,children:"IT IS YOUR TURN!"}),r.jsx("div",{className:`text-amber-200/80 ${d}`,children:"Start driving!"}),x?r.jsxs("div",{className:`text-red-100/90 ${h}`,children:["Idle skip in ",o,"s"]}):null]})})}const Rj={leftWheel:"Left wheel",rightWheel:"Right wheel",mainBrush:"Main brush",sideBrush:"Side brush",limiter:"Overcurrent limit"};function Tj({motors:a,fill:l=0,compact:i=!1}){if(!a?.length)return null;const o=a.map(b=>Rj[b]||b),f=i?"w-[12rem] h-[3.5rem]":"w-[20rem] h-[7rem]",d=i?"px-2 py-1":"px-4 py-2",h=i?"text-lg":"text-4xl",p=i?"text-xs":"text-xl",x=Math.max(0,Math.min(1,l)),g=`${Math.round(x*100)}%`;return r.jsx("div",{className:`pointer-events-none absolute flex items-center justify-center bg-red-900/50 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 ${f}`,children:r.jsxs("div",{className:"relative h-full w-full",children:[r.jsx("div",{className:"absolute inset-0 overflow-hidden",children:r.jsx("div",{className:"h-full bg-red-700/60",style:{width:g}})}),r.jsxs("div",{className:`relative z-10 flex h-full w-full flex-col items-center justify-center text-center font-semibold text-white animate-pulse ${h} ${d}`,children:[r.jsx("div",{children:"OVERCURRENT"}),r.jsx("div",{className:`mt-0 font-medium text-white ${p}`,children:o.join(", ")})]})]})})}function Dj({battery:a,compact:l=!1}){if(!a?.available||!a.warnActive&&!a.urgentActive)return null;const i=a.urgentActive?"BATTERY VERY LOW, DOCK THE ROVER AND CHARGE IMMEDIATELY!!":"Battery low! please dock and charge the rover soon.",o=l?"p-2 top-6":"p-4 top-10",f=l?"text-sm":"text-2xl";return r.jsx("div",{className:`pointer-events-none absolute flex items-center justify-center bg-amber-900/60 left-1/2 -translate-x-1/2 ${o}`,children:r.jsx("div",{className:`text-center font-semibold text-white animate-pulse ${f}`,children:r.jsx("div",{children:i})})})}function _j({compact:a=!1}){const{session:l}=_e(),{sendMessage:i,onInputFocus:o,onInputBlur:f,blurChat:d,registerInputRef:h,setTypingActive:p}=Qf(),{value:x}=Pe("tts",{engine:"flite",voice:"rms",pitch:50}),[g,b]=m.useState(""),[y,j]=m.useState(!1),M=l?.role!=="spectator",C=l?.role==="spectator",w=l?.assignment?.roverId||null,q=!!m.useMemo(()=>l?.roster?.find(O=>String(O.id)===String(w))||null,[w,l?.roster])?.audio?.ttsEnabled,A=m.useMemo(()=>{if(!q)return null;const O=x?.engine==="espeak"?"espeak":"flite";if(O==="espeak"){let L=Number.isFinite(x?.pitch)?Math.round(x.pitch):void 0;return typeof L=="number"&&(L=Math.max(0,Math.min(99,L))),{speak:!0,engine:O,pitch:L}}const Q=typeof x?.voice=="string"?x.voice:void 0;return{speak:!0,engine:O,voice:Q}},[x?.engine,x?.pitch,x?.voice,q]),E=a?"pointer-events-auto absolute bottom-0.5 right-0.5 flex w-[9rem] max-w-[70vw] items-center gap-0.5 rounded bg-black/70 px-0.4 py-0.2":"pointer-events-auto absolute bottom-1 right-1 flex w-[12rem] max-w-[70vw] items-center gap-0.5 rounded bg-black/70 px-0.5 py-0.25",T=a?"min-w-0 flex-1 bg-transparent text-[0.55rem] text-slate-100 placeholder:text-slate-400 focus:outline-none":"min-w-0 flex-1 bg-transparent text-[0.7rem] text-slate-100 placeholder:text-slate-400 focus:outline-none",B=a?"rounded bg-cyan-500/80 px-0.35 py-0.2 text-[0.55rem] font-semibold text-black disabled:opacity-50":"rounded bg-cyan-500/80 px-0.5 py-0.25 text-[0.7rem] font-semibold text-black disabled:opacity-50";async function U(O){if(O.preventDefault(),!M)return;const Q=g.trim();if(Q){j(!0);try{await i(Q,A),b(""),d(),p(!1)}catch(L){alert(L.message)}finally{j(!1)}}}return C?null:r.jsxs("form",{onSubmit:U,className:E,children:[r.jsx("input",{className:T,value:g,onChange:O=>{const Q=O.target.value;b(Q),p(!!Q.trim())},onFocus:O=>{o(O),p(!!g.trim())},onBlur:O=>{f(O),p(!1)},onKeyDown:O=>{O.key==="Enter"&&!g.trim()&&(O.preventDefault(),d(),p(!1))},ref:O=>h(O,{target:"hud"}),placeholder:M?"Chat (TTS)":"Spectator",disabled:!M}),r.jsx("button",{type:"submit",disabled:!M||y,className:B,children:"Speak"})]})}function td({layoutFormat:a="desktop"}){const{session:l}=_e(),{state:{song:i,lastControlIntentAt:o},overcurrentLimiter:f}=qt(),[d,h]=m.useState(()=>Date.now()),[p,x]=m.useState(!1),[g,b]=m.useState(null),y=m.useRef({active:!1,roverId:null});m.useEffect(()=>{if(l?.mode!=="turns")return;const le=setInterval(()=>h(Date.now()),250);return()=>clearInterval(le)},[l?.mode]);const j=l?.assignment?.roverId,C=!!(j&&l?.roster?l.roster.find(le=>String(le.id)===String(j)):null)?.media?.audioPublishUrl,w=j?l?.turnQueues?.[j]:null,N=l?.socketId||null,q=j?l?.activeDrivers?.[j]:null,A=!!(N&&q===N),E=m.useMemo(()=>{const le=w?.queue||[];if(!le.length||!w?.current||le.length<=1)return null;const pe=le.findIndex(ie=>ie===w.current);return pe===-1?le[0]||null:le[(pe+1)%le.length]||null},[w?.queue,w?.current]),T=!!(N&&E===N),B=w?.deadline||null,U=w?.idleDeadline||null,O=B?B-d:null,Q=U?U-d:null,L=l?.mode==="turns"&&T&&O!=null&&O<=5e3&&O>0,G=l?.mode!=="turns"||A||L,Z=O!=null&&Number.isFinite(O)?Math.max(0,Math.ceil(O/1e3)):null,ae=Q!=null&&Number.isFinite(Q)?Math.max(0,Math.ceil(Q/1e3)):null,ue=A?Z!=null?`${Z}s left`:null:T&&Z!=null?`Your turn in ${Z}s`:null,re=j?[...G?[{type:"rover",id:j,key:j}]:[],...C?[{type:"rover",id:`${j}-audio`,key:`${j}-audio`}]:[]]:[],F=ui(re),R=j&&G?F[j]:null,X=j&&C?F[`${j}-audio`]:null,te=Wf(j?[j]:[],{enabled:!!j,version:l?.mode}),se=j&&te[j]||null,me=ns(j),k=j&&l?.roster?l.roster.find(le=>String(le.id)===String(j)):null,Y=k?.battery??null,ne=k?.name||(j?`Rover ${j}`:"");return m.useEffect(()=>{if(l?.mode!=="turns"){x(!1),b(null),y.current={active:!1,roverId:null};return}const le=y.current,pe=A&&!le.active,ie=A&&j&&j!==le.roverId;pe||ie?(x(!0),b(Date.now())):!A&&le.active&&(x(!1),b(null)),y.current={active:A,roverId:j}},[A,j,l?.mode]),m.useEffect(()=>{!p||!g||o>g&&x(!1)},[o,g,p]),r.jsx("section",{className:"panel",children:j?r.jsx(di,{sessionInfo:R,videoMode:G?"whep":"snapshot",snapshotFeed:se,audioSessionInfo:X,label:ne,telemetryFrame:me,batteryConfig:Y,layoutFormat:a,overcurrentLimiter:f,songNote:i?.note,qualityNotice:G?null:"Preview feed (low FPS) until your turn.",showTurnCue:p,turnTimerText:ue,turnSeconds:Z,isActiveDriver:A,idleSkipSeconds:ae}):r.jsxs("div",{className:"panel-muted content-center text-center text-sm text-slate-400 aspect-[4/3]",children:[r.jsx("p",{children:"You are not assigned to a rover."}),r.jsx("p",{className:"mt-0",children:r.jsx("a",{href:"/spectate",className:"text-blue-400 underline hover:text-blue-500",children:"Click here to visit the spectator page."})})]})})}function kf({label:a,tone:l="muted"}){const i=l==="success"?"bg-emerald-900 text-emerald-100":l==="warn"?"bg-amber-900 text-amber-100":"bg-slate-800 text-slate-200";return r.jsx("span",{className:`rounded px-1 py-0.5 text-xs font-semibold leading-none ${i}`,children:a})}function ho(a){if(!Number.isFinite(a))return 0;const l=a%360;return l<0?l+360:l}function Oj(a){if(!Array.isArray(a)||a.length<3)return 0;const[l,i,o]=a,f=Math.max(0,Math.min(255,Number(l)))/255,d=Math.max(0,Math.min(255,Number(i)))/255,h=Math.max(0,Math.min(255,Number(o)))/255,p=Math.max(f,d,h),x=Math.min(f,d,h),g=p-x;if(g===0)return 0;let b=0;return p===f?b=(d-h)/g%6:p===d?b=(h-f)/g+2:b=(f-d)/g+4,ho(Math.round(b*60))}function Gp(a){const l=ho(a),i=1,o=1-Math.abs(l/60%2-1);let f=0,d=0,h=0;return l<60?(f=i,d=o):l<120?(f=o,d=i):l<180?(d=i,h=o):l<240?(d=o,h=i):l<300?(f=o,h=i):(f=i,h=o),[Math.round(f*255),Math.round(d*255),Math.round(h*255)]}function Yp(a){return a?Array.isArray(a.hsColor)?ho(Number(a.hsColor[0])):Array.isArray(a.rgbColor)?Oj(a.rgbColor):0:0}function Bj({entity:a,connected:l,onToggle:i,onSetColor:o}){const f=a.state==="unavailable"||!a.available,d=a.state==="on",h=a.type==="light"&&a.supportsColor,p=f?"warn":d?"success":"muted",x=f?"Unavailable":d?"On":"Off",g=!l||f,b=g||!h,[y,j]=m.useState(()=>Yp(a)),M=m.useRef(y),C=m.useRef(!1),w=f?"border-slate-800 bg-slate-900 text-slate-400 cursor-not-allowed":d?"border-emerald-700 bg-emerald-900/80 text-emerald-50 hover:bg-emerald-800":"border-rose-800 bg-rose-900/80 text-rose-50 hover:bg-rose-800";m.useEffect(()=>{if(!h||C.current)return;const B=Yp(a);M.current=B,j(B)},[a.rgbColor,a.hsColor,h]);const N=B=>{const U=ho(Number(B.target.value));M.current=U,j(U)},q=()=>{b||!o||o(a.id,Gp(M.current))},A=B=>{B.stopPropagation()},T=`rgb(${(h?Gp(M.current):[255,255,255]).join(",")})`;return r.jsxs("button",{type:"button",onClick:()=>i(a.id),disabled:g,className:`relative flex min-w-[12rem] flex-[1_1_12rem] items-start justify-between gap-0.5 rounded px-1 py-0.5 text-left transition-colors ${w} disabled:opacity-60 disabled:hover:bg-inherit`,children:[r.jsxs("div",{className:"min-w-0 flex-1",children:[r.jsxs("div",{className:"flex items-center gap-0.5 text-sm leading-normal",children:[r.jsx("span",{className:"truncate font-semibold text-white",children:a.name||a.id}),h&&r.jsxs(r.Fragment,{children:[r.jsx(kf,{label:x,tone:p}),!l&&r.jsx("span",{className:"text-xs text-amber-200",children:"Offline"})]})]}),h?r.jsx("div",{className:"-mt-0.5 w-full",onClick:A,onPointerDown:B=>{A(B),C.current=!0},onPointerUp:B=>{A(B),C.current=!1,q()},onPointerCancel:B=>{A(B),C.current=!1,q()},onKeyUp:B=>{A(B),q()},onBlur:q,children:r.jsx("div",{className:"relative w-full",children:r.jsx("input",{type:"range",min:"0",max:"360",step:"1",value:y,onChange:N,disabled:b,"aria-label":`Set color for ${a.name||a.id}`,className:"ha-hue-slider w-full"})})}):r.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-400",children:[r.jsx(kf,{label:x,tone:p}),!l&&r.jsx("span",{className:"text-amber-200",children:" · Offline"})]})]}),h&&r.jsx("span",{className:"pointer-events-none absolute right-1 top-1 h-2.5 w-2.5 rounded-full border border-white/60",style:{backgroundColor:T}}),!h&&r.jsx("div",{className:"self-center text-xs font-semibold text-white/90",children:d?"Turn off":"Turn on"})]})}function xg(){const{state:{keymap:a}}=qt(),{session:l,homeAssistantToggle:i,homeAssistantSetLightColor:o}=_e(),f=l?.homeAssistant,d=m.useMemo(()=>f?.entities||[],[f?.entities]),h=Nn(a?.homeAssistantOn?.[0]),p=Nn(a?.homeAssistantOff?.[0]);if(!f?.enabled)return r.jsxs("section",{className:"panel-section space-y-0.5 text-sm text-slate-400",children:[r.jsx("p",{className:"text-slate-300",children:"Room Controls"}),r.jsx("p",{className:"text-slate-500",children:"Not configured on the server."})]});if(d.length===0)return r.jsxs("section",{className:"panel-section space-y-0.5 text-sm text-slate-400",children:[r.jsx("p",{className:"text-slate-300",children:"Room Controls"}),r.jsx("p",{className:"text-slate-500",children:"No lights or switches configured."})]});const x=!!f?.connected;return r.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[r.jsxs("header",{className:"flex items-center justify-between gap-0.5 text-sm text-slate-400",children:[r.jsxs("div",{className:"flex items-center gap-0.5",children:[r.jsx("p",{children:"Room Controls"}),r.jsx("span",{className:"text-xs text-slate-500",children:d.length}),r.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-300 background-black",children:[r.jsxs("span",{className:"flex items-center gap-0.5",children:[r.jsx("span",{children:"On"}),h?r.jsx(Fp,{label:h}):null]}),r.jsxs("span",{className:"flex items-center gap-0.5",children:[r.jsx("span",{children:"Off"}),p?r.jsx(Fp,{label:p}):null]})]})]}),r.jsx(kf,{label:x?"Connected":"Offline",tone:x?"success":"warn"})]}),r.jsx("div",{className:"flex flex-wrap gap-0.5",children:d.map(g=>r.jsx(Bj,{entity:g,connected:x,onToggle:i,onSetColor:o},g.id))})]})}function Fp({label:a}){return a?r.jsx("span",{className:"rounded border border-white/40 px-1 text-[0.7rem] text-white",children:a}):null}function gg(){const{login:a,setRole:l}=_e(),[i,o]=m.useState(""),[f,d]=m.useState(""),[h,p]=m.useState(!1);async function x(g){g.preventDefault(),p(!0);try{await a(i,f),o(""),d("")}catch(b){alert(b.message)}finally{p(!1)}}return r.jsxs("div",{className:"panel-section space-y-0.5 text-base",children:[r.jsx("p",{className:"text-sm text-slate-400",children:"Admin login"}),r.jsxs("form",{className:"flex flex-col gap-0.5",onSubmit:x,children:[r.jsx("input",{className:"field-input",placeholder:"Username",value:i,onChange:g=>o(g.target.value)}),r.jsx("input",{className:"field-input",placeholder:"Password",type:"password",value:f,onChange:g=>d(g.target.value)}),r.jsx("button",{type:"submit",disabled:h,className:"button-dark w-full disabled:opacity-50",children:h?"Logging in…":"Login"})]})]})}function Xp(...a){return a.filter(Boolean).join(" ")}function Lj(a){const l=a?.batteryState?.percentDisplay;return l==null?"--":`${l}%`}function zj(a){return a?.batteryState?a.batteryState.urgentActive?"text-red-400":a.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function Uj({title:a,roster:l=[],emptyText:i="No roster data.",renderActions:o=null}){return r.jsxs("div",{className:"space-y-0.5 text-sm",children:[a&&r.jsx("p",{className:"text-sm text-slate-400",children:a}),l.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:i}):r.jsx("ul",{className:"space-y-0.5 text-sm",children:l.map(f=>r.jsxs("li",{className:Xp("surface flex flex-wrap items-center justify-between gap-0.5",f.locked&&"bg-red-900/40"),children:[r.jsxs("div",{children:[r.jsx("p",{className:"text-slate-200",children:f.name}),r.jsxs("p",{className:"text-xs text-slate-500 flex flex-wrap items-center gap-0.5",children:[r.jsx("span",{children:f.locked?"locked":"free"}),f.lockReason&&r.jsx("span",{className:"rounded bg-black/30 px-1",children:f.lockReason}),r.jsxs("span",{className:Xp("font-semibold",zj(f)),children:["Battery ",Lj(f)]})]})]}),o?o(f):null]},f.id))})]})}const Hj=[{key:"open",label:"Open"},{key:"turns",label:"Turns"},{key:"admin",label:"Admin"},{key:"lockdown",label:"Lockdown"}];function Vj(){const{session:a,lockRover:l,setMode:i,requestControl:o,setCommunityGoal:f,setAdminReason:d,rebootRover:h,rebootServer:p,setAudioLevels:x,llmControl:g,adminLogs:b,llmCommentaryState:y}=_e(),j=m.useMemo(()=>a?.roster??[],[a?.roster]),[M,C]=m.useState({}),[w,N]=m.useState({}),[q,A]=m.useState(!1),[E,T]=m.useState(!1),B=a?.health||null,U=a?.communityGoal?.text||"",O=a?.communityGoal?.updatedAt||null,[Q,L]=m.useState(U),G=a?.adminReason?.text||"",Z=a?.adminReason?.updatedAt||null,[ae,ue]=m.useState(G),re=a?.audioLevels||{},[F,R]=m.useState({hornGain:Number.isFinite(re.hornGain)?re.hornGain:1,ttsGain:Number.isFinite(re.ttsGain)?re.ttsGain:1,forwardGain:Number.isFinite(re.forwardGain)?re.forwardGain:1}),X=a?.role==="admin"||a?.role==="lockdown"||a?.role==="lockdown-admin",te=a?.mode??"open",se=async(H,P)=>{try{await l(H,P),C(he=>({...he,[H]:P}))}catch(he){alert(he.message)}},me=async H=>{const P=H.target.value;try{await i(P)}catch(he){alert(he.message)}},k=async H=>{try{await o(H,{force:!0})}catch(P){alert(P.message)}},Y=async H=>{if(!(!H?.id||!window.confirm(`Reboot rover "${H.name||H.id}" now?`))){N(he=>({...he,[H.id]:!0}));try{await h(H.id)}catch(he){alert(he.message)}finally{N(he=>({...he,[H.id]:!1}))}}},ne=async()=>{if(window.confirm("Reboot the server host now? This will disconnect all users.")){A(!0);try{await p()}catch(P){alert(P.message),A(!1)}}},le=async()=>{if(window.confirm("Clear LLM commentary history now? This resets chat context, bot memory, and rover activity metrics for narration.")){T(!0);try{await g("clearHistory")}catch(P){alert(P.message)}finally{T(!1)}}},pe=async()=>{try{await f(Q)}catch(H){alert(H.message)}},ie=async()=>{try{await f(null)}catch(H){alert(H.message)}},fe=async()=>{try{await d(ae)}catch(H){alert(H.message)}},ce=async()=>{try{await d(null)}catch(H){alert(H.message)}},oe=H=>P=>{const he=Number(P.target.value);R(Se=>({...Se||{},[H]:Number.isFinite(he)?he:1}))},de=async()=>{try{await x(F)}catch(H){alert(H.message)}};m.useEffect(()=>{L(U)},[U]),m.useEffect(()=>{ue(G)},[G]),m.useEffect(()=>{R({hornGain:Number.isFinite(re.hornGain)?re.hornGain:1,ttsGain:Number.isFinite(re.ttsGain)?re.ttsGain:1,forwardGain:Number.isFinite(re.forwardGain)?re.forwardGain:1})},[re.forwardGain,re.hornGain,re.ttsGain]);const Ae=m.useMemo(()=>{const H={};return j.forEach(P=>{H[P.id]=M[P.id]??P.locked}),H},[j,M]);return X?r.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5 text-sm",children:[r.jsx("span",{children:"Admin controls"}),r.jsx("select",{value:te,onChange:me,className:"field-input text-sm",children:Hj.map(H=>r.jsx("option",{value:H.key,children:H.label},H.key))})]}),r.jsx("div",{className:"flex gap-0.5 text-xs",children:r.jsx("button",{type:"button",onClick:ne,disabled:q,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:q?"Server rebooting...":"Reboot Server"})}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Global audio levels"}),a?.audioLevels?.updatedAt?r.jsxs("span",{children:["Updated ",new Date(a.audioLevels.updatedAt).toLocaleString()]}):null]}),r.jsxs("label",{className:"grid gap-0.5 text-xs text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Horn gain"}),r.jsxs("span",{children:[F.hornGain.toFixed(2),"x"]})]}),r.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:F.hornGain,onChange:oe("hornGain"),className:"w-full accent-emerald-500"})]}),r.jsxs("label",{className:"grid gap-0.5 text-xs text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"TTS gain"}),r.jsxs("span",{children:[F.ttsGain.toFixed(2),"x"]})]}),r.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:F.ttsGain,onChange:oe("ttsGain"),className:"w-full accent-emerald-500"})]}),r.jsxs("label",{className:"grid gap-0.5 text-xs text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Forward gain"}),r.jsxs("span",{children:[F.forwardGain.toFixed(2),"x"]})]}),r.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:F.forwardGain,onChange:oe("forwardGain"),className:"w-full accent-emerald-500"})]}),r.jsx("div",{className:"flex gap-0.5 text-xs",children:r.jsx("button",{type:"button",onClick:de,className:"button-dark",children:"Apply audio levels"})})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Community goal"}),O?r.jsxs("span",{children:["Updated ",new Date(O).toLocaleString()]}):null]}),r.jsx("input",{type:"text",value:Q,onChange:H=>L(H.target.value),placeholder:"Set a community goal",className:"field-input text-sm"}),r.jsxs("div",{className:"flex gap-0.5 text-xs",children:[r.jsx("button",{type:"button",onClick:pe,className:"button-dark",children:"Set goal"}),r.jsx("button",{type:"button",onClick:ie,className:"button-danger",children:"Clear"})]})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Admin mode reason"}),Z?r.jsxs("span",{children:["Updated ",new Date(Z).toLocaleString()]}):null]}),r.jsx("textarea",{value:ae,onChange:H=>ue(H.target.value),placeholder:"Set an admin mode reason",className:"field-input text-sm min-h-[3.5rem]"}),r.jsxs("div",{className:"flex gap-0.5 text-xs",children:[r.jsx("button",{type:"button",onClick:fe,className:"button-dark",children:"Set reason"}),r.jsx("button",{type:"button",onClick:ce,className:"button-danger",children:"Clear"})]})]}),r.jsx(Uj,{roster:j,renderActions:H=>r.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 text-xs",children:[r.jsx("button",{type:"button",onClick:()=>se(H.id,!Ae[H.id]),className:"button-dark",children:Ae[H.id]?"Unlock":"Lock"}),r.jsx("button",{type:"button",onClick:()=>k(H.id),className:"button-dark",children:"Force"}),r.jsx("button",{type:"button",onClick:()=>Y(H),disabled:!!w[H.id],className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:w[H.id]?"Rebooting...":"Reboot"}),r.jsxs("span",{className:"surface-muted",children:["audio: ",a?.audioForward?.[H.id]?.state||"idle"]}),a?.audioForward?.[H.id]?.error?r.jsx("span",{className:"surface-muted text-rose-300",children:String(a.audioForward[H.id].error)}):null]})}),r.jsx(Fj,{health:B}),r.jsx($j,{state:y,onClearHistory:le,clearingHistory:E}),r.jsx(Xj,{entries:b})]}):null}function $j({state:a,onClearHistory:l,clearingHistory:i}){const[o,f]=m.useState(null);if(!a)return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"LLM Commentary"}),r.jsx("div",{className:"surface text-xs text-slate-300",children:"No status received yet."})]});const d=a.runtime||{},h=a.counters||{},p=a.timings||{},x=a.input||{},g=a.output||{},b=a.errors||{},y=Array.isArray(a.history)?a.history:[],j=y.find(N=>N.runId===o)||(y.length?y[y.length-1]:null),M=Gj(a),C=Yj(x.modelMessages,g.raw),w=[{label:"running",value:d.running?"yes":"no"},{label:"in flight",value:d.inFlight?"yes":"no"},{label:"phase",value:d.phase||"--"},{label:"run id",value:d.currentRunId??"--"},{label:"tick count",value:d.tickCount??0},{label:"last tick",value:d.lastTickAt?new Date(d.lastTickAt).toLocaleString():"never"},{label:"next run",value:d.nextRunAt?new Date(d.nextRunAt).toLocaleString():"n/a"},{label:"outcome",value:d.outcome||"--"},{label:"reason",value:d.reason||"--"},{label:"skip streak",value:h.skipStreak??0},{label:"clear count",value:h.clearCount??0},{label:"last gen",value:p.lastGenerationMs!=null?`${p.lastGenerationMs} ms`:"--"},{label:"avg gen",value:p.avgGenerationMs!=null?`${p.avgGenerationMs} ms`:"--"},{label:"gen count",value:p.generationCount??0},{label:"prompt chars",value:h.promptChars??0},{label:"active drivers",value:h.snapshotSummary?.activeDrivers??0},{label:"snapshot rovers",value:h.snapshotSummary?.rovers??0},{label:"snapshot chat",value:h.snapshotSummary?.chatMessages??0}];return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"LLM Commentary"}),r.jsx("div",{className:"flex gap-0.5 text-xs",children:r.jsx("button",{type:"button",onClick:l,disabled:!!i,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:i?"Clearing...":"Clear LLM History"})}),r.jsxs("div",{className:`surface border text-center ${M.className}`,children:[r.jsx("div",{className:"text-[1.1rem] font-bold tracking-wide",children:M.label}),r.jsx("div",{className:"text-xs text-slate-200",children:M.detail})]}),r.jsx("div",{className:"surface flex flex-wrap gap-0.5 text-xs",children:w.map(N=>r.jsxs("span",{className:"rounded border border-slate-600/60 bg-slate-800/70 px-0.5 py-0.25 text-[0.72rem] leading-tight text-slate-200",children:[N.label,": ",N.value]},N.label))}),b.message?r.jsxs("div",{className:"surface text-xs text-red-300 break-words",children:["Error: ",b.message]}):null,b.details?r.jsxs("details",{className:"surface text-xs text-red-200",children:[r.jsx("summary",{className:"cursor-pointer select-none text-red-300",children:"Failure details"}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-red-200",children:JSON.stringify(b.details,null,2)})]}):null,g.generated?r.jsxs("div",{className:"surface text-xs text-slate-200 break-words",children:["Generated: ",g.generated]}):null,g.posted?r.jsxs("div",{className:"surface text-xs text-emerald-200 break-words",children:["Posted: ",g.posted]}):null,r.jsxs("div",{className:"grid gap-0.5 md:grid-cols-2",children:[r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"Live Input Conversation"}),r.jsx("div",{className:"surface max-h-72 space-y-0.5 overflow-y-auto",children:C.length?C.map(N=>r.jsx(Yf,{message:N.message},N.id)):r.jsx("div",{className:"text-xs text-slate-300",children:"No model conversation captured yet."})})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"Output + Error"}),r.jsxs("div",{className:"surface space-y-0.5 text-xs text-slate-200",children:[r.jsxs("div",{children:["Raw output: ",g.raw?.trim()?g.raw:""]}),r.jsxs("div",{children:["Posted: ",g.posted||""]}),r.jsxs("div",{children:["Output at:"," ",g.modelOutputAt?new Date(g.modelOutputAt).toLocaleString():"n/a"]}),r.jsxs("div",{children:["Failed at: ",b.failedAt?new Date(b.failedAt).toLocaleString():"n/a"]})]})]})]}),r.jsxs("details",{className:"surface text-xs text-slate-200",children:[r.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Full Monitor Payload"}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify(a,null,2)})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"Recent Runs"}),r.jsx("div",{className:"surface max-h-52 space-y-0.5 overflow-y-auto text-xs",children:y.length?y.slice().reverse().map(N=>r.jsxs("button",{type:"button",onClick:()=>f(N.runId),className:`w-full text-left surface ${j?.runId===N.runId?"border border-sky-400/50":""}`,children:[r.jsxs("span",{className:"text-slate-300",children:["#",N.runId]})," ",r.jsx("span",{className:"text-slate-200",children:N.outcome||N.phase||"--"})," ",r.jsx("span",{className:"text-slate-400",children:N.durationMs!=null?`${N.durationMs}ms`:"--"})," ",r.jsx("span",{className:"text-slate-500",children:N.reason||""})]},N.runId)):r.jsx("div",{className:"text-slate-300",children:"No runs recorded yet."})})]}),j?r.jsxs("details",{className:"surface text-xs text-slate-200",open:!0,children:[r.jsxs("summary",{className:"cursor-pointer select-none text-slate-300",children:["Run #",j.runId," details"]}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify(j,null,2)})]}):null]})}function Gj(a){const l=a?.runtime||{},i=a?.output||{},o=a?.errors||{};return l.inFlight?{label:"IN FLIGHT",detail:l.reason||"Generating commentary now",className:"border-amber-400/60 bg-amber-700/20 text-amber-200"}:l.outcome==="posted"?{label:"POSTED",detail:i.posted?`Last: ${i.posted}`:"Commentary posted",className:"border-emerald-400/60 bg-emerald-700/20 text-emerald-200"}:l.outcome==="skipped"?{label:"SKIPPED",detail:l.reason||"Model chose to skip",className:"border-slate-400/60 bg-slate-700/30 text-slate-200"}:l.outcome==="failed"?{label:"FAILED",detail:o.message||l.reason||"Tick failed",className:"border-red-400/60 bg-red-700/20 text-red-200"}:{label:l.running?"IDLE":"STOPPED",detail:l.reason||"Waiting for next tick",className:"border-sky-400/50 bg-sky-700/20 text-sky-200"}}function Yj(a,l){const i=Date.now(),f=(Array.isArray(a)?a:[]).map((d,h)=>{const p=String(d?.role||"").toLowerCase(),x=typeof d?.content=="string"?d.content:JSON.stringify(d?.content??null,null,2),g=p==="system"?"LLM System":p==="assistant"?"LLM Context":p==="user"?"LLM Input":"LLM Message";return{id:`llm-msg-${h}`,message:{ts:i+h,nickname:g,text:x,role:"spectator",system:p==="system"}}});if(l!=null){const d=String(l);f.push({id:"llm-output",message:{ts:i+f.length+1,nickname:"LLM Output",text:d.trim()?d:"",role:"spectator",system:!0}})}return f}function Fj({health:a}){if(!a)return null;const l=a.replay||{sources:[],readyCount:0,totalCount:0},i=a.snapshots||{rovers:[],rooms:[]},o=`${l.readyCount}/${l.totalCount} sources ready`,f=i.rovers.filter(h=>h.stale).length,d=i.rooms.filter(h=>h.stale).length;return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"Health"}),r.jsxs("div",{className:"surface space-y-0.5 text-xs text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:"Replay segments"}),r.jsx("span",{className:"text-slate-400",children:o})]}),r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:"Rover snapshots"}),r.jsxs("span",{className:f?"text-amber-300":"text-emerald-300",children:[i.rovers.length-f,"/",i.rovers.length," ok"]})]}),r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:"Room cameras"}),r.jsxs("span",{className:d?"text-amber-300":"text-emerald-300",children:[i.rooms.length-d,"/",i.rooms.length," ok"]})]})]}),r.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:l.sources.map(h=>r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:h.label}),r.jsxs("span",{className:h.ready?"text-emerald-300":"text-amber-300",children:[h.recentCount,"/",h.neededCount]})]},`${h.type}:${h.id}`))}),r.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:i.rovers.map(h=>r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:h.name}),r.jsx("span",{className:h.stale?"text-amber-300":"text-emerald-300",children:h.stale?"stale":"ok"})]},`rover:${h.id}`))}),r.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:i.rooms.map(h=>r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:h.name}),r.jsx("span",{className:h.stale?"text-amber-300":"text-emerald-300",children:h.error?"error":h.stale?"stale":"ok"})]},`room:${h.id}`))})]})}function Xj({entries:a}){const l=a||[];return r.jsxs("div",{className:"panel-section space-y-0.5 text-base",children:[r.jsxs("div",{className:"flex items-center justify-between text-sm text-slate-400",children:[r.jsx("span",{children:"Admin IP log"}),r.jsx("span",{children:l.length})]}),r.jsx("div",{className:"surface h-64 overflow-y-auto font-mono text-xs",children:l.length===0?r.jsx("p",{children:"No admin log entries yet."}):l.slice().reverse().map(i=>r.jsxs("div",{className:"surface",children:[r.jsx("span",{className:"text-amber-400",children:i.ts?new Date(i.ts).toLocaleTimeString():"--"})," ",i.label&&r.jsxs("span",{className:"text-teal-400",children:["[",i.label,"]"]})," ",r.jsx("span",{className:"text-slate-200",children:i.message})," ",i.ip&&r.jsx("span",{className:"text-cyan-300",children:i.ip})," ",i.meta&&r.jsx("span",{className:"text-slate-500",children:JSON.stringify(i.meta)})]},i.id))}),r.jsx("p",{className:"text-xs text-slate-500",children:"Admin-only log stream; IPs never appear in user data."})]})}const Qj=[{id:"driveForward",label:"Drive Forward",group:"Driving"},{id:"driveBackward",label:"Drive Backward",group:"Driving"},{id:"driveLeft",label:"Turn Left",group:"Driving"},{id:"driveRight",label:"Turn Right",group:"Driving"},{id:"boostModifier",label:"Boost Modifier",group:"Driving"},{id:"slowModifier",label:"Slow Modifier",group:"Driving"},{id:"auxMainForward",label:"Main Brush Forward",group:"Aux Motors"},{id:"auxMainReverse",label:"Main Brush Reverse",group:"Aux Motors"},{id:"auxSideForward",label:"Side Brush Forward",group:"Aux Motors"},{id:"auxSideReverse",label:"Side Brush Reverse",group:"Aux Motors"},{id:"auxVacuumFast",label:"Vacuum Max",group:"Aux Motors"},{id:"auxVacuumSlow",label:"Vacuum Low",group:"Aux Motors"},{id:"auxAllForward",label:"All Aux Forward",group:"Aux Motors"},{id:"cameraUp",label:"Camera Up",group:"Camera"},{id:"cameraDown",label:"Camera Down",group:"Camera"},{id:"nightVisionToggle",label:"Toggle Night Vision",group:"Camera"},{id:"hornHonk",label:"Horn (Hold)",group:"Audio"},{id:"micPtt",label:"Mic Push To Talk",group:"Audio"},{id:"driveMacro",label:"Drive Macro",group:"Macros"},{id:"dockMacro",label:"Dock Macro",group:"Macros"},{id:"chatFocus",label:"Toggle Chat",group:"Chat"},{id:"songNoteUp",label:"Song Note Up",group:"Audio"},{id:"songNoteDown",label:"Song Note Down",group:"Audio"},{id:"homeAssistantOn",label:"Room Controls On (Cycle)",group:"Room Controls"},{id:"homeAssistantOff",label:"Room Controls Off (Cycle)",group:"Room Controls"}];function Kj(a){return a.reduce((l,i)=>((l[i.group]||(l[i.group]=[])).push(i),l),{})}function Zj(a,l){const i=Number(a);return Number.isFinite(i)?Math.max(0,Math.min(500,i)):l}const nd=5,mi=500,hi=1,ad=100;function Ij(a,l){const i=Number(a);return Number.isFinite(i)?Math.max(nd,Math.min(mi,i)):l}function Pr(a,l){const i=Number(a);return Number.isFinite(i)?Math.max(hi,Math.min(ad,i)):l}function Jj(a){const i=(Pr(a,a)-hi)/(ad-hi),o=mi-i*(mi-nd);return Math.round(o)}function Wj(a){const l=Ij(a,a),i=(mi-l)/(mi-nd),o=hi+i*(ad-hi);return Math.round(o)}function Pj(a){const[l,i]=m.useState(null),o=m.useCallback(h=>{Xu(!0),i(h)},[]),f=m.useCallback(()=>{Xu(!1),i(null)},[]),d=m.useCallback((h,p)=>{if(p.preventDefault(),p.stopPropagation(),p.key==="Escape"){f();return}const x=bi(p.key??"");x&&(a(h,x,p),f())},[f,a]);return m.useEffect(()=>()=>Xu(!1),[]),{active:l,startCapture:o,cancel:f,captureFromEvent:d}}function $r({label:a,description:l,value:i,onChange:o,min:f=0,max:d=500,step:h=5}){return r.jsxs("label",{className:"surface-muted block p-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[r.jsx("span",{className:"font-semibold text-slate-100",children:a}),r.jsx("input",{type:"number",min:f,max:d,value:i,onChange:p=>o(p.target.value),className:"w-16 rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-right text-[0.75rem] font-mono text-slate-100"})]}),l&&r.jsx("p",{className:"text-[0.65rem] text-slate-500",children:l}),r.jsxs("div",{className:"mt-0 flex items-center gap-0.5",children:[r.jsx("input",{type:"range",min:f,max:d,step:h,value:i,onChange:p=>o(p.target.value),className:"h-2 flex-1 accent-emerald-400"}),r.jsx("span",{className:"w-12 text-right text-[0.75rem] font-mono text-slate-400",children:i})]})]})}function eN(){const{state:{keymap:a},actions:{updateKeyBinding:l,resetKeyBindings:i}}=qt(),{value:o,save:f}=Pe("inputs",jn),d={...jn.keyboard,...o.keyboard??{}},h=m.useMemo(()=>typeof d.tiltSpeed=="number"?Pr(d.tiltSpeed,jn.keyboard.tiltSpeed):typeof d.tiltIntervalMs=="number"?Pr(Wj(d.tiltIntervalMs),jn.keyboard.tiltSpeed):jn.keyboard.tiltSpeed,[d.tiltIntervalMs,d.tiltSpeed]),p=m.useMemo(()=>Kj(Qj),[]),{active:x,startCapture:g,cancel:b,captureFromEvent:y}=Pj((w,N)=>{l(w,N)}),j=m.useCallback(w=>a?.[w]?.[0]||li[w]?.[0]||"",[a]),M=m.useCallback((w,N)=>{const q=jn.keyboard,A=Zj(N,q[w]);f(E=>({...E??{},keyboard:{...E?.keyboard??q,[w]:A}}))},[f]),C=m.useCallback(w=>{const N=jn.keyboard,q=Pr(w,N.tiltSpeed),A=Jj(q);f(E=>({...E??{},keyboard:{...E?.keyboard??N,tiltSpeed:q,tiltIntervalMs:A}}))},[f]);return r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsxs("div",{children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Keyboard layout"}),r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Per-browser · click to change a binding"})]}),r.jsx("button",{type:"button",onClick:()=>i(),className:"button-dark text-xs",children:"Reset defaults"})]}),r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Keyboard speeds"}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsx($r,{label:"Base speed",description:"Normal driving speed",value:d.baseSpeed,onChange:w=>M("baseSpeed",w)}),r.jsx($r,{label:"Turbo speed",description:"Used when holding the boost modifier",value:d.turboSpeed,onChange:w=>M("turboSpeed",w)}),r.jsx($r,{label:"Precision speed",description:"Used when holding the precision modifier",value:d.precisionSpeed,onChange:w=>M("precisionSpeed",w)})]})]}),r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Camera tilt"}),r.jsx($r,{label:"Tilt speed",description:"Higher = faster when holding tilt keys",value:h,onChange:C,min:1,max:100,step:1})]}),r.jsx("div",{className:"space-y-0.5",children:Object.entries(p).map(([w,N])=>r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.75rem] text-slate-400",children:w}),r.jsx("div",{className:"space-y-0.5",children:N.map(q=>{const A=j(q.id),E=x===q.id;return r.jsxs("div",{className:"surface-muted flex items-center justify-between text-xs",children:[r.jsxs("div",{children:[r.jsx("p",{className:"font-semibold text-slate-100",children:q.label}),r.jsx("p",{className:"text-[0.65rem] text-slate-400",children:Nn(A)})]}),r.jsxs("div",{className:"flex items-center gap-0.5",children:[E&&r.jsx("button",{type:"button",onClick:()=>b(),className:"button-danger text-[0.7rem]",children:"Cancel"}),r.jsx("button",{type:"button",onClick:()=>g(q.id),onKeyDown:E?T=>y(q.id,T):void 0,autoFocus:E,className:`${E?"px-0.5 py-0.5 bg-emerald-500 text-emerald-950 hover:bg-emerald-400":"button-dark"} text-[0.7rem] font-medium transition-colors`,children:E?"Press a key…":"Change"})]})]},q.id)})})]},w))})]})}const In=new Intl.NumberFormat(void 0,{maximumFractionDigits:2}),tN=[{id:"drive",label:"Drive stick",kind:"axisPair",section:"Driving",invertDefaults:{invertX:!1,invertY:!0}},{id:"cameraTilt",label:"Camera tilt",kind:"axis",section:"Camera",invertDefaults:{invert:!0}},{id:"mainBrush",label:"Main brush",kind:"axis",section:"Brushes",invertDefaults:{invert:!1}},{id:"sideBrush",label:"Side brush",kind:"axis",section:"Brushes",invertDefaults:{invert:!1}},{id:"vacuum",label:"Vacuum",kind:"button",section:"Aux buttons"},{id:"allAux",label:"All aux",kind:"button",section:"Aux buttons"},{id:"mainReverse",label:"Main reverse toggle",kind:"button",section:"Brush toggles"},{id:"sideReverse",label:"Side reverse toggle",kind:"button",section:"Brush toggles"},{id:"driveMacro",label:"Drive macro",kind:"button",section:"Mode macros"},{id:"dockMacro",label:"Dock macro",kind:"button",section:"Mode macros"},{id:"nightVisionToggle",label:"Night vision toggle",kind:"button",section:"Camera"}],Qp=.45,nN=.6;function ti({label:a,description:l,min:i,max:o,step:f,value:d,onChange:h}){return r.jsxs("label",{className:"surface-muted block p-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[r.jsx("span",{className:"font-semibold text-slate-100",children:a}),r.jsx("span",{className:"font-mono text-slate-400",children:In.format(d)})]}),l&&r.jsx("p",{className:"text-[0.65rem] text-slate-500",children:l}),r.jsx("input",{type:"range",min:i,max:o,step:f,value:d,onChange:p=>h(Number(p.target.value)),className:"mt-0 w-full accent-emerald-400"})]})}function aN(a){if(!a)return"Unassigned";if(a.kind==="axisPair"){const l=`${a.invertX?"X inv ":""}${a.invertY?"Y inv":""}`.trim();return`Axes ${a.x}/${a.y}${l?` (${l})`:""}`}if(a.kind==="axis")return`Axis ${a.index}${a.invert?" (inv)":""}`;if(a.kind==="button")return`Button ${a.index}`;if(a.kind==="buttonAxis")return`Button ${a.index} (analog)`;if(a.kind==="axisButton"){const l=a.direction<0?"<":">";return`Axis ${a.index} ${l} ${In.format(a.threshold??.6)}`}return"Unassigned"}function lN(a){return a.reduce((l,i)=>((l[i.section]||(l[i.section]=[])).push(i),l),{})}function sN(a,l){if(!a||a.length===0)return null;if(l){const i=a.find(o=>o.signature===l);if(i)return i}return a[0]}function iN(a){return{axes:Array.from(a.axes??[]),buttons:(a.buttons??[]).map(l=>({pressed:!!l?.pressed,value:typeof l?.value=="number"?l.value:l?.pressed?1:0}))}}function rN(a,l,i){const o=(a.axes??[]).map((d,h)=>({index:h,delta:Math.abs((d??0)-(l.axes?.[h]??0)),value:d}));if(o.sort((d,h)=>h.delta-d.delta),i.kind==="axisPair"){const d=o.filter(h=>h.delta>Qp).slice(0,2);return d.length<2?null:{kind:"axisPair",x:d[0].index,y:d[1].index,...i.invertDefaults??{}}}const f=o.find(d=>d.delta>Qp);return f?{kind:"axis",index:f.index,...i.invertDefaults??{}}:null}function oN(a,l,i){const o=a.buttons??[];for(let d=0;dnN)return i.kind==="axis"?{kind:"buttonAxis",index:d}:{kind:"button",index:d}}const f=a.axes??[];for(let d=0;d.7&&p>.5)return i.kind==="axis"?{kind:"axis",index:d,...i.invertDefaults??{}}:{kind:"axisButton",index:d,direction:h>=0?1:-1,threshold:.6}}return null}function cN(a,l,i){if(!a)return null;if(i.kind==="axis"||i.kind==="axisPair"){const o=rN(a,l,i);if(o)return o}return oN(a,l,i)}function uN(){const a=UA(),{value:l,save:i}=Pe("gamepad",wa),[o,f]=m.useState(null),d=m.useRef(null),h=m.useMemo(()=>lN(tN),[]),p=m.useMemo(()=>sN(a.pads,l.activeSignature),[a.pads,l.activeSignature]),x=p?.signature??null,g=m.useMemo(()=>x?l?.profiles?.[x]??l?.defaults?.profile??Jn:l?.defaults?.profile??Jn,[x,l?.defaults?.profile,l?.profiles]);m.useEffect(()=>{!p||!x||l?.profiles?.[x]||i(N=>{const q=N??wa;if(q.profiles?.[x])return q;const A=q?.defaults?.profile??Jn,E=eg(p,A);return{...q,profiles:{...q.profiles??{},[x]:E}}})},[p,x,l?.profiles,i]),m.useEffect(()=>{d.current=null},[o,x]),m.useEffect(()=>{if(!o||!p)return;if(!d.current){d.current=iN(p);return}const N=cN(p,d.current,o);N&&(i(q=>{const A=q??wa,E=A.profiles?.[x]??A?.defaults?.profile??Jn,T={...E,bindings:{...E.bindings??{},[o.id]:{...E.bindings?.[o.id]??{},kind:o.kind,sources:[N]}}};return{...A,profiles:{...A.profiles??{},[x]:T}}}),f(null))},[p,x,o,i]);const b=m.useCallback(N=>{i(q=>({...q??wa,activeSignature:N||null}))},[i]),y=m.useCallback(N=>{i(q=>{const A=q??wa,E=(x&&A.profiles?.[x])??A?.defaults?.profile??Jn,T={...E,calibration:{...E.calibration??{},...N}};return x?{...A,profiles:{...A.profiles??{},[x]:T}}:{...A,defaults:{...A.defaults??{},profile:T}}})},[x,i]),j=m.useCallback((N,q)=>{i(A=>{const E=A??wa,T=(x&&E.profiles?.[x])??E?.defaults?.profile??Jn,B=q(T.bindings?.[N]??{}),U={...T,bindings:{...T.bindings??{},[N]:B}};return x?{...E,profiles:{...E.profiles??{},[x]:U}}:{...E,defaults:{...E.defaults??{},profile:U}}})},[x,i]),M=m.useCallback(N=>{j(N.id,q=>({...q,sources:[]}))},[j]),C=m.useCallback((N,q)=>{j(N.id,A=>{const E=Array.isArray(A.sources)?[...A.sources]:[];if(!E[0])return A;const T={...E[0]};return q==="invertX"?T.invertX=!T.invertX:q==="invertY"?T.invertY=!T.invertY:T.invert=!T.invert,E[0]=T,{...A,sources:E}})},[j]),w=m.useMemo(()=>p?{outputs:tg(p,g)}:null,[p,g]);return r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("div",{className:"flex items-center justify-between",children:r.jsxs("div",{children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Controller"}),r.jsx("p",{className:"text-[0.65rem] text-slate-500",children:p?"Move sticks or press buttons to bind":"Connect a controller to configure."}),o&&r.jsxs("p",{className:"mt-0 text-[0.7rem] text-emerald-400",children:["Capturing ",o.label,"…"]})]})}),r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Connected controller"}),a.pads.length===0?r.jsx("p",{className:"text-[0.7rem] text-slate-400",children:"No controller detected."}):r.jsxs("div",{className:"flex items-center justify-between gap-0.5 text-xs",children:[r.jsx("select",{value:x??"",onChange:N=>b(N.target.value),className:"flex-1 rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:a.pads.map(N=>r.jsx("option",{value:N.signature,children:N.id||"Unknown controller"},N.signature))}),r.jsx("span",{className:"text-[0.7rem] text-slate-400",children:p?.mapping??"unknown"})]})]}),r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Calibration"}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(ti,{label:"Drive deadzone",description:"Ignore small drive stick drift",min:0,max:.6,step:.01,value:g.calibration?.driveDeadzone??.18,onChange:N=>y({driveDeadzone:N})}),r.jsx(ti,{label:"Camera deadzone",description:"Ignore small camera tilt drift",min:0,max:.4,step:.01,value:g.calibration?.cameraDeadzone??.08,onChange:N=>y({cameraDeadzone:N})}),r.jsx(ti,{label:"Aux deadzone",description:"Ignore small trigger noise",min:0,max:.4,step:.01,value:g.calibration?.auxDeadzone??.05,onChange:N=>y({auxDeadzone:N})}),r.jsx(ti,{label:"Side brush scale",description:"Scale side brush output",min:.3,max:1,step:.05,value:g.calibration?.auxSideScale??.55,onChange:N=>y({auxSideScale:N})}),r.jsxs("label",{className:"surface-muted block p-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[r.jsx("span",{className:"font-semibold text-slate-100",children:"Camera mode"}),r.jsxs("select",{value:g.calibration?.cameraMode??"absolute",onChange:N=>y({cameraMode:N.target.value}),className:"rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:[r.jsx("option",{value:"absolute",children:"Absolute"}),r.jsx("option",{value:"velocity",children:"Velocity"})]})]}),r.jsx("p",{className:"text-[0.65rem] text-slate-500",children:"Absolute maps stick to angle; velocity moves over time."})]}),r.jsx(ti,{label:"Camera sensitivity",description:"Velocity mode degrees per second",min:10,max:180,step:5,value:g.calibration?.cameraSensitivity??60,onChange:N=>y({cameraSensitivity:N})})]})]}),r.jsx("div",{className:"space-y-0.5",children:Object.entries(h).map(([N,q])=>r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:N}),r.jsx("div",{className:"space-y-0.5",children:q.map(A=>{const T=g.bindings?.[A.id]?.sources?.[0]??null;return r.jsxs("div",{className:"surface-muted flex items-center justify-between text-xs",children:[r.jsxs("div",{children:[r.jsx("p",{className:"font-semibold text-slate-100",children:A.label}),r.jsx("p",{className:"text-[0.65rem] text-slate-400",children:aN(T)})]}),r.jsxs("div",{className:"flex items-center gap-0.5",children:[A.kind==="axisPair"&&r.jsxs(r.Fragment,{children:[r.jsx("button",{type:"button",disabled:!T,onClick:()=>C(A,"invertX"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert X"}),r.jsx("button",{type:"button",disabled:!T,onClick:()=>C(A,"invertY"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert Y"})]}),A.kind==="axis"&&r.jsx("button",{type:"button",disabled:!T,onClick:()=>C(A),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert"}),r.jsx("button",{type:"button",onClick:()=>M(A),className:"button-dark text-[0.7rem]",children:"Clear"}),r.jsx("button",{type:"button",onClick:()=>f(A),className:`${o?.id===A.id?"px-0.5 py-0.5 bg-emerald-500 text-emerald-950 hover:bg-emerald-400":"button-dark"} text-[0.7rem] font-medium`,children:o?.id===A.id?"Waiting…":"Capture"})]})]},A.id)})})]},N))}),r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Diagnostics"}),p?r.jsxs("div",{className:"space-y-0.5 text-[0.7rem] text-slate-300",children:[r.jsx("p",{className:"text-slate-400",children:"Raw axes"}),r.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:p.axes.map((N,q)=>r.jsxs("span",{className:"font-mono text-slate-300",children:["A",q,": ",In.format(N)]},`axis-${q}`))}),r.jsx("p",{className:"text-slate-400",children:"Raw buttons"}),r.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:p.buttons.map((N,q)=>r.jsxs("span",{className:"font-mono text-slate-300",children:["B",q,": ",In.format(N.value)," ",N.pressed?"●":""]},`btn-${q}`))}),w?.outputs&&r.jsxs(r.Fragment,{children:[r.jsx("p",{className:"text-slate-400",children:"Mapped outputs"}),r.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[r.jsxs("span",{className:"font-mono text-slate-300",children:["Drive: ",In.format(w.outputs.driveVector.x),","," ",In.format(w.outputs.driveVector.y)]}),r.jsxs("span",{className:"font-mono text-slate-300",children:["Camera: ",In.format(w.outputs.cameraAxis)]}),r.jsxs("span",{className:"font-mono text-slate-300",children:["Main: ",In.format(w.outputs.auxAxis.main)]}),r.jsxs("span",{className:"font-mono text-slate-300",children:["Side: ",In.format(w.outputs.auxAxis.side)]})]})]})]}):r.jsx("p",{className:"text-[0.7rem] text-slate-400",children:"No controller detected."})]})]})}const fN={drive:"Drive wheels",aux:"Aux motors"};function Kp(a){return Number.isFinite(a)?`${Math.round(a*100)}%`:"--"}function dN({value:a,color:l="bg-emerald-500"}){const i=`${Math.round(Math.max(0,Math.min(1,a))*100)}%`;return r.jsx("div",{className:"h-2 w-full overflow-hidden rounded bg-slate-800",children:r.jsx("div",{className:`h-full ${l}`,style:{width:i}})})}function mN(){const{state:{roverId:a},overcurrentLimiter:l}=qt(),i=m.useMemo(()=>Ql.map(o=>o.key),[]);return r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Overcurrent limiter"}),r.jsx("span",{children:l?.adminImmune?"Admin immune":"Active"})]}),a?r.jsxs("div",{className:"space-y-0.5",children:[i.map(o=>{const f=l?.caps?.[o]?.cap??0,d=l?.overcurrent?.groups?.[o]??!1,h=l?.scales?.perGroup?.[o]??1;return r.jsxs("div",{className:"surface space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs",children:[r.jsx("span",{className:"text-slate-200",children:fN[o]||o}),r.jsx("span",{className:d?"text-red-300":"text-slate-400",children:d?"overcurrent":"ok"})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-[0.7rem] text-slate-400",children:[r.jsx("span",{children:"Cap"}),r.jsx("span",{children:Kp(f)})]}),r.jsx(dN,{value:f,color:"bg-amber-500"}),r.jsxs("div",{className:"flex items-center justify-between text-[0.7rem] text-slate-400",children:[r.jsx("span",{children:"Scale"}),r.jsx("span",{children:Kp(h)})]})]})]},o)}),r.jsxs("div",{className:"surface text-[0.7rem] text-slate-400",children:[r.jsx("div",{children:`Down rate ${l?.config?.downRatePerSec}/s · Up rate ${l?.config?.upRatePerSec}/s`}),r.jsx("div",{children:`Release delay ${l?.config?.releaseDelaySec}s`}),r.jsx("div",{children:`Output rate ${l?.config?.outputRateMs}ms`})]})]}):r.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to view limiter status."})]})}const vg=m.createContext(null);function yg(){const a=m.useContext(vg);if(!a)throw new Error("Tabs components must be used within a provider.");return a}const Zp={primary:{base:"flex-1 px-0.5 py-0.5 text-sm font-medium transition-colors focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-slate-500 rounded-md border border-slate-800",active:"bg-sky-600 text-white border-white",inactive:"bg-zinc-900 text-slate-300 hover:bg-sky-500 hover:text-white"}},bg="primary";function ld(...a){return a.filter(Boolean).join(" ")}function sd({children:a,defaultTab:l,currentTab:i,onTabChange:o,variant:f=bg}){const[d,h]=m.useState(l??null),[p,x]=m.useState([]),g=m.useCallback(M=>M?(x(C=>C.includes(M)?C:[...C,M]),()=>x(C=>C.includes(M)?C.filter(w=>w!==M):C)):()=>{},[]),b=i??d??p[0]??null;m.useEffect(()=>{l&&i===void 0&&h(l)},[l,i]);const y=m.useCallback(M=>{M&&(i===void 0&&h(M),o&&o(M))},[i,o]),j=m.useMemo(()=>({activeTab:b,setActiveTab:y,registerTab:g,variant:f}),[b,y,g,f]);return r.jsx(vg.Provider,{value:j,children:a})}function id({children:a,className:l=""}){return r.jsx("div",{className:ld("flex gap-0.5",l),children:a})}function Xt({id:a,children:l,className:i="",disabled:o=!1}){const{activeTab:f,setActiveTab:d,registerTab:h,variant:p}=yg();m.useEffect(()=>h(a),[a,h]);const x=Zp[p]??Zp[bg],g=f===a,b=ld(x.base,g?x.active:x.inactive,o?"cursor-not-allowed opacity-50":"",i);return r.jsx("button",{type:"button",className:b,onClick:()=>!o&&d(a),"aria-pressed":g,"aria-disabled":o,children:l})}function rd({children:a,className:l=""}){return r.jsx("div",{className:ld("mt-0 space-y-0.5",l),children:a})}function Qt({id:a,children:l,keepMounted:i=!1}){const{activeTab:o}=yg(),f=o===a;return!f&&!i?null:!f&&i?r.jsx("div",{hidden:!0,"aria-hidden":"true",children:l}):r.jsx(r.Fragment,{children:l})}function hN(){const{session:a}=_e(),l=m.useMemo(()=>JSON.stringify(a??{},null,2),[a]);return r.jsxs("div",{className:"panel-section flex min-h-0 flex-col gap-0.5 text-xs",children:[r.jsx("p",{className:"text-sm text-slate-400",children:"Session snapshot"}),r.jsx("pre",{className:"surface min-h-0 flex-1 overflow-y-auto font-mono text-[0.7rem] text-lime-300",children:l})]})}const Ip=300,Jp=240;function pN(a,l){if(a===void 0)return"undefined";if(a===null)return"null";if(typeof a=="string")return a;if(typeof a=="number"||typeof a=="boolean")return String(a);if(typeof a=="function")return"[Function]";if(a instanceof Error)return`${a.name}: ${a.message}`;if(a instanceof Date)return a.toISOString();if(l||(l=new WeakSet),typeof a=="object"){if(l.has(a))return"[Circular]";l.add(a)}try{return JSON.stringify(a,(i,o)=>typeof o=="function"?"[Function]":o instanceof Error?`${o.name}: ${o.message}`:o,0)}catch{return String(a)}}function xN(a){const i=a.map(o=>pN(o)).join(" ");return i.length>Jp?`${i.slice(0,Jp)}…`:i}function gN(){const a=kn(),[l,i]=m.useState([]),o=m.useMemo(()=>l.reduce((f,d)=>(d.direction==="in"&&(f.in+=1),d.direction==="out"&&(f.out+=1),f),{in:0,out:0}),[l]);return m.useEffect(()=>{const f=(x,g,b)=>{i(y=>{const j=[...y,{id:`${Date.now()}-${Math.random().toString(16).slice(2)}`,direction:x,event:g,payload:xN(b),timestamp:new Date().toLocaleTimeString()}];return j.length>Ip?j.slice(-Ip):j})},d=(x,...g)=>f("in",x,g),h=(x,...g)=>f("out",x,g);a.onAny(d);let p=null;if(typeof a.onAnyOutgoing=="function")a.onAnyOutgoing(h),p=()=>a.offAnyOutgoing(h);else{const x=a.emit.bind(a);a.emit=(g,...b)=>(h(g,...b),x(g,...b)),p=()=>{a.emit=x}}return()=>{a.offAny(d),p&&p()}},[a]),r.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[r.jsxs("div",{className:"flex items-center justify-between text-sm text-slate-400",children:[r.jsx("span",{children:"Socket activity"}),r.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-400",children:[r.jsxs("span",{className:"surface-muted",children:["in ",o.in]}),r.jsxs("span",{className:"surface-muted",children:["out ",o.out]}),r.jsx("button",{type:"button",className:"button-dark text-xs",onClick:()=>i([]),children:"Clear"})]})]}),r.jsx("div",{className:"surface h-64 overflow-y-auto font-mono text-xs",children:l.length===0?r.jsx("p",{children:"No socket activity yet."}):l.slice().reverse().map(f=>r.jsxs("div",{className:"surface",children:[r.jsx("span",{className:"text-slate-400",children:f.timestamp})," ",r.jsxs("span",{className:f.direction==="in"?"text-emerald-400":"text-amber-400",children:["[",f.direction.toUpperCase(),"]"]})," ",r.jsx("span",{className:"text-cyan-300",children:f.event})," ",r.jsx("span",{className:"text-slate-200",children:f.payload})]},f.id))}),r.jsx("p",{className:"text-xs text-slate-500",children:"Logs are collected only while this panel is open."})]})}const vN=[{key:"start",label:"Start OI"},{key:"safe",label:"Safe"},{key:"full",label:"Full"},{key:"passive",label:"Passive"},{key:"dock",label:"Dock"}];function Sg(){const{state:{roverId:a},actions:{sendOiCommand:l,setSensorStream:i}}=qt(),o=!!a,[f,d]=hg(),h=kn(),{value:p,save:x}=Pe("page",{hudMapDesktop:!1,connectionTransport:"websocket"}),{value:g,save:b}=Pe("audio",wt),y=p?.connectionTransport||"websocket",j=Number.isFinite(g?.masterVolume)?g.masterVolume:wt.masterVolume,M=Number.isFinite(g?.alertVolume)?g.alertVolume:wt.alertVolume,C=Number.isFinite(g?.roverVolume)?g.roverVolume:wt.roverVolume,w=typeof g?.mainBrushDuckEnabled=="boolean"?g.mainBrushDuckEnabled:typeof g?.autoLevelEnabled=="boolean"?g.autoLevelEnabled:wt.mainBrushDuckEnabled,N=Number.isFinite(g?.mainBrushDuckAmount)?Math.max(0,Math.min(1,g.mainBrushDuckAmount)):wt.mainBrushDuckAmount,q=m.useMemo(()=>[{key:"start",label:"Enable stream",enable:!0},{key:"stop",label:"Disable stream",enable:!1}],[]),A=U=>{a&&i(U)},E=U=>{const O=U.target.value;x(Q=>({...Q??{},connectionTransport:O})),h?.io?.opts&&(h.io.opts.transports=O==="polling"?["polling"]:["websocket","polling"],h.disconnect(),h.connect())},T=U=>O=>{const Q=Number(O.target.value),L=Number.isFinite(Q)?Math.max(0,Math.min(1,Q)):0;b(G=>({...G??{},[U]:L}))},B=U=>{const O=!!U.target.checked;b(Q=>({...Q??{},mainBrushDuckEnabled:O}))};return r.jsxs(sd,{defaultTab:"keybindings",children:[r.jsxs(id,{children:[r.jsx(Xt,{id:"keybindings",children:"Keybindings"}),r.jsx(Xt,{id:"controller",children:"Controller"}),r.jsx(Xt,{id:"page",children:"Page settings"}),r.jsx(Xt,{id:"admin",children:"Admin"})]}),r.jsxs(rd,{children:[r.jsx(Qt,{id:"keybindings",children:r.jsx("div",{className:"space-y-0.5",children:r.jsx(eN,{})})}),r.jsx(Qt,{id:"controller",children:r.jsx("div",{className:"space-y-0.5",children:r.jsx(uN,{})})}),r.jsx(Qt,{id:"page",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("p",{className:"text-slate-400",children:"HUD"}),r.jsxs("label",{className:"flex items-center gap-0.5 text-slate-200",children:[r.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:f,onChange:U=>d(U.target.checked)}),r.jsx("span",{children:"Show top-down map in HUD (desktop)"})]}),r.jsx("p",{className:"text-xs text-slate-500",children:"Mobile HUD keeps the map on by default."})]}),r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("p",{className:"text-slate-400",children:"Audio"}),r.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Master volume"}),r.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(j*100),"%"]})]}),r.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:j,onChange:T("masterVolume"),className:"w-full accent-emerald-500"})]}),r.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Alert/page sounds"}),r.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(M*100),"%"]})]}),r.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:M,onChange:T("alertVolume"),className:"w-full accent-emerald-500"})]}),r.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Rover audio"}),r.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(C*100),"%"]})]}),r.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:C,onChange:T("roverVolume"),className:"w-full accent-emerald-500"})]}),r.jsxs("label",{className:"flex items-center justify-between gap-0.5 text-slate-200",children:[r.jsx("span",{children:"Main brush ducking"}),r.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:w,onChange:B})]}),r.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Main brush duck amount"}),r.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(N*100),"%"]})]}),r.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:N,onChange:T("mainBrushDuckAmount"),className:"w-full accent-emerald-500",disabled:!w})]}),r.jsx("p",{className:"text-xs text-slate-500",children:"Lowers rover audio only while the main brush is running."})]}),r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("p",{className:"text-slate-400",children:"Connection"}),r.jsxs("label",{className:"flex items-center justify-between gap-0.5 text-slate-200",children:[r.jsx("span",{children:"Transport"}),r.jsxs("select",{value:y,onChange:E,className:"field-input text-sm",children:[r.jsx("option",{value:"websocket",children:"WebSocket"}),r.jsx("option",{value:"polling",children:"Polling"})]})]}),r.jsx("p",{className:"text-xs text-slate-500",children:"Switching reconnects your session."})]})]})}),r.jsx(Qt,{id:"admin",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("p",{className:"text-slate-400",children:"Manual OI commands"}),r.jsx("div",{className:"surface flex flex-wrap gap-0.5",children:vN.map(U=>r.jsx("button",{type:"button",onClick:()=>l(U.key),disabled:!o,className:"button-dark text-xs disabled:opacity-30",children:U.label},U.key))})]}),r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("p",{className:"text-slate-400",children:"Sensor stream"}),r.jsx("div",{className:"surface flex gap-0.5",children:q.map(U=>r.jsx("button",{type:"button",onClick:()=>A(U.enable),disabled:!o,className:"flex-1 button-dark text-xs disabled:opacity-30",children:U.label},U.key))}),!o&&r.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to toggle streams."})]}),r.jsx(gg,{}),r.jsx(mN,{}),r.jsx(Vj,{}),r.jsx(hN,{}),r.jsx(gN,{})]})})]})]})}const Wp={desktop:{main:[{type:"list",title:"Chat and nicknames",items:["Set a nickname in the user list panel, on the bottom left of the page below the rover video.",{segments:["Toggle chat focus with ",{action:"chatFocus"},". Press ",{action:"chatFocus"}," again to send."]}]},{type:"list",title:"Driving the rover",items:[{segments:['Press the "Start Driving" button onscreen, or press ',{action:"driveMacro"}," on your keyboard to put the rover into driving mode."]},"Refer to the controls for the controls for the rover."]},{type:"list",title:"Docking the rover",items:["Line up the rover to the dock, about a foot away, then:",{segments:['Press the "Dock and Charge" button onscreen, or press ',{action:"dockMacro"}," on your keyboard to start docking."]},"Wait for the rover to confirm it is docked and charging before leaving it unattended."]}],aside:[{type:"keyboard",title:"Keyboard controls",footnote:"Per-browser; adjust bindings and speeds in Settings → Keybindings.",groups:[{id:"movement",title:"Movement",items:[{action:"driveForward",label:"Forward"},{action:"driveBackward",label:"Backward"},{action:"driveLeft",label:"Turn left"},{action:"driveRight",label:"Turn right"},{action:"boostModifier",label:"Boost speed"},{action:"slowModifier",label:"Precision speed"}]},{id:"macros",title:"Rover modes & chat",items:[{action:"driveMacro",label:"Drive macro"},{action:"dockMacro",label:"Dock macro"},{action:"chatFocus",label:"Chat focus"}]},{id:"camera",title:"Camera",items:[{action:"cameraUp",label:"Tilt up"},{action:"cameraDown",label:"Tilt down"},{action:"nightVisionToggle",label:"Toggle night vision"}]},{id:"motors",title:"Rover Aux Motors",items:[{action:"auxMainForward",label:"Main brush forward"},{action:"auxMainReverse",label:"Main brush reverse"},{action:"auxSideForward",label:"Side brush forward"},{action:"auxSideReverse",label:"Side brush reverse"},{action:"auxVacuumFast",label:"Vacuum max"},{action:"auxVacuumSlow",label:"Vacuum low"},{action:"auxAllForward",label:"All motors forward"}]},{id:"audio",title:"Audio / Song",items:[{action:"songNoteUp",label:"Song note up"},{action:"songNoteDown",label:"Song note down"}]},{id:"room-controls",title:"Room Controls",items:[{action:"homeAssistantOn",label:"Next room control on"},{action:"homeAssistantOff",label:"Next room control off (reverse)"}]}]},{type:"gamepad",title:"Gamepad / joystick",items:["Gamepad controls are fully configurable to handle inconsistent browser mappings.","Use Settings → Controller to bind inputs, calibrate deadzones, and view diagnostics.","If inputs behave oddly, the diagnostics panel will show raw axis/button values."]}]},"mobile-portrait":{main:[{type:"list",title:"Chat and nicknames",items:["Set a nickname in the user list panel below.","Tap in the chat box to send messages in the chat."]},{type:"list",title:"Driving the rover",items:[{segments:['Press the "Start Driving" button onscreen to put the rover into driving mode.']},"Look below the rover video. Use the joystick on the right to move the rover, and hold the buttons on the left to run the aux motors."]},{type:"list",title:"Docking the rover",items:["Line up the rover to the dock, about a foot away, then:",{segments:['Press the "Dock and Charge" button onscreen to start docking.']},"Wait for the rover to confirm it is docked and charging before leaving it unattended."]}]},"mobile-landscape":{main:[{type:"list",title:"Chat and nicknames",items:["Scroll down to see more of the page.","Set a nickname in the user list panel below.","Tap in the chat box to send messages in the chat."]},{type:"list",title:"Driving the rover",items:[{segments:['Press the "Start Driving" button, or the "Drive" button to put the rover into driving mode.']},"Use the joystick to the right of the video feed to move the rover, and hold the buttons on the left to run the aux motors."]},{type:"list",title:"Docking the rover",items:["Line up the rover to the dock, about a foot away, then:",{segments:['Press the "Dock and Charge" or the "Dock" button onscreen to start docking.']},"Wait for the rover to confirm it is docked and charging before leaving it unattended."]}]}};function yN(a){return Wp[a]||Wp.desktop}function Ag({actionId:a,keymap:l}){const i=l?.[a]?.[0]??"";return r.jsx("span",{className:"rounded border border-slate-600 bg-slate-900/40 px-1 text-[0.7rem] text-slate-200",children:Nn(i)})}function Pp(a,l){return a.map((i,o)=>typeof i=="string"?r.jsx("span",{children:i},`text-${o}`):i?.action?r.jsx(Ag,{actionId:i.action,keymap:l},`pill-${i.action}-${o}`):i?.text?r.jsx("span",{children:i.text},`span-${o}`):null)}function po(a,l,i){if(typeof a=="string")return a;if(Array.isArray(a?.segments))return Pp(a.segments,l);if(a&&typeof a=="object"){if(typeof a.text=="string")return a.text;if(Array.isArray(a))return Pp(a,l)}return String(a??"")}function bN({hero:a,keymap:l}){return a?r.jsxs("div",{className:"surface space-y-0.5 px-0.5 py-0.5",children:[r.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-0.5",children:[r.jsxs("div",{children:[r.jsx("p",{className:"text-sm font-semibold text-white",children:a.title}),a.subtitle&&r.jsx("p",{className:"text-xs text-slate-300",children:a.subtitle})]}),a.chips&&r.jsx("div",{className:"flex flex-wrap gap-0.5 text-[0.7rem] text-slate-200",children:a.chips.map(i=>r.jsx("span",{className:"rounded border border-slate-700 px-1 py-[2px]",children:i},i))})]}),a.bullets&&r.jsx("ul",{className:"space-y-0.5 text-[0.85rem] text-slate-200",children:a.bullets.map((i,o)=>{const f=Array.isArray(i?.segments)?`hero-${o}`:`hero-${o}`;return r.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:po(i,l)},f)})})]}):null}function SN({block:a,keymap:l}){return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs font-semibold text-slate-200",children:a.title}),r.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:a.items.map((i,o)=>{const f=`item-${o}`;return r.jsx("li",{className:"surface-muted flex flex-wrap items-center gap-0.5 px-0.5 py-0.25",children:po(i,l)},f)})})]})}function AN({block:a}){const l=a.tone==="info"?"border-cyan-500/40":"border-slate-700";return r.jsxs("div",{className:`surface space-y-0.5 border ${l} px-0.5 py-0.5`,children:[r.jsx("p",{className:"text-xs font-semibold text-slate-100",children:a.title}),a.body&&r.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:a.body.map((i,o)=>r.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:po(i,{})},`callout-${o}`))})]})}function jN({group:a,keymap:l}){return r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"px-0.5 py-0.25 text-[0.75rem] font-semibold text-slate-200",children:a.title}),r.jsx("div",{className:"space-y-0.5 px-0.5 pb-0.25",children:a.items.map(i=>r.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-0.5 py-0.25 text-[0.8rem]",children:[r.jsx("span",{className:"text-slate-200",children:i.label}),r.jsx(Ag,{actionId:i.action,keymap:l})]},i.action))})]})}function NN({block:a,keymap:l}){return a?r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-200",children:[r.jsx("span",{className:"font-semibold",children:a.title}),a.footnote&&r.jsx("span",{className:"text-[0.7rem] text-slate-400",children:a.footnote})]}),r.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-0.5",children:a.groups?.map(i=>r.jsx(jN,{group:i,keymap:l},i.id))})]}):null}function wN({block:a}){return a?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs font-semibold text-slate-200",children:a.title}),r.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:a.items?.map((l,i)=>r.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:po(l,{})},`gamepad-${i}`))})]}):null}function ex({block:a,keymap:l}){if(!a)return null;switch(a.type){case"list":return r.jsx(SN,{block:a,keymap:l});case"callout":return r.jsx(AN,{block:a});case"keyboard":return r.jsx(NN,{block:a,keymap:l});case"gamepad":return r.jsx(wN,{block:a});default:return null}}function jg({layout:a,keymap:l}){const i=m.useMemo(()=>yN(a),[a]),o=l||{},f=i.main||[],d=i.aside||[];return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(bN,{hero:i.hero,keymap:o}),r.jsxs("div",{className:"grid gap-0.5",style:{gridTemplateColumns:"repeat(auto-fit, minmax(360px, 1fr))"},children:[r.jsx("div",{className:"space-y-0.5",children:f.map((h,p)=>r.jsx(ex,{block:h,keymap:o},h.title||p))}),r.jsx("div",{className:"space-y-0.5",children:d.map((h,p)=>r.jsx(ex,{block:h,keymap:o},h.title||p))})]})]})}function Ng({layout:a,onOpenOverlay:l}){const{state:i}=qt();return r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Help"}),r.jsx("button",{type:"button",onClick:l,className:"button-dark px-1 py-0.25 text-[0.75rem]",children:"Open full help"})]}),r.jsx(jg,{layout:a,keymap:i?.keymap||{}})]})}const CN=["kal","rms","slt","ksp","bdl"],EN=Array.from({length:10},(a,l)=>l*10);function xo({hideInput:a=!1,hideSpectatorNotice:l=!1,fillHeight:i=!1,allowSpectatorInput:o=!1}){const{session:f}=_e(),{messages:d,typing:h,sendMessage:p,registerInputRef:x,onInputFocus:g,onInputBlur:b,blurChat:y,setTypingActive:j}=Qf(),{value:M,save:C}=Pe("tts",{engine:"flite",voice:"rms",pitch:50}),[w,N]=m.useState(""),[q,A]=m.useState(!1),[E,T]=m.useState(!1),[B,U]=m.useState(()=>M?.engine||"flite"),[O,Q]=m.useState(()=>M?.voice||"rms"),[L,G]=m.useState(()=>Number.isFinite(M?.pitch)?M.pitch:50),Z=f?.role!=="spectator"||o,ae=m.useRef(null),ue=f?.assignment?.roverId||null,F=!!m.useMemo(()=>f?.roster?.find(k=>String(k.id)===String(ue))||null,[ue,f?.roster])?.audio?.ttsEnabled,R=m.useMemo(()=>d.slice(-200),[d]),X=m.useMemo(()=>h||[],[h]);m.useEffect(()=>{ae.current&&(ae.current.scrollTop=ae.current.scrollHeight)},[R,X]),m.useEffect(()=>{const k=M?.engine||"flite",Y=M?.voice||"rms",ne=Number.isFinite(M?.pitch)?M.pitch:50;B!==k&&U(k),O!==Y&&Q(Y),L!==ne&&G(ne)},[B,L,M?.engine,M?.pitch,M?.voice,O]),m.useEffect(()=>{T(!!F)},[F]);const te=m.useMemo(()=>!F||!E?null:B==="espeak"?{speak:!0,engine:B,pitch:L}:{speak:!0,engine:B,voice:O},[B,L,E,F,O]);async function se(k){if(k.preventDefault(),!Z||a)return;const ne=w.replace(/\\n/g,` -`).trim();if(ne){A(!0);try{await p(ne,te),N(""),y(),j(!1)}catch(le){alert(le.message)}finally{A(!1)}}}const me=i?"flex-1 min-h-0 overflow-y-auto":"h-48 overflow-y-auto";return r.jsxs("section",{className:`panel-section space-y-0.5 text-base ${i?"flex h-full flex-col overflow-hidden":""}`,children:[r.jsxs("div",{className:`surface overflow-y-auto space-y-0.5 px-0 ${me}`,ref:ae,children:[R.length===0&&X.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"No messages yet."}):R.map(k=>r.jsx(Yf,{message:k},k.id)),X.map(k=>r.jsx(Fx,{message:k},`typing-${k.typingId||k.id}`))]}),!a&&r.jsxs("form",{className:"flex flex-wrap items-stretch gap-0.5",onSubmit:se,children:[r.jsx("input",{className:"field-input flex-1 min-w-[10rem]",value:w,onChange:k=>{const Y=k.target.value;N(Y),j(!!Y.trim())},onFocus:k=>{g(k),j(!!w.trim())},onBlur:k=>{b(k),j(!1)},onKeyDown:k=>{k.key==="Enter"&&!w.trim()&&(k.preventDefault(),y(),j(!1))},ref:k=>x(k,{target:"panel"}),placeholder:Z?"Type a message…":l?"":"Spectators cannot chat",disabled:!Z}),F&&r.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 basis-full sm:basis-auto",children:[r.jsxs("label",{className:"flex items-center gap-0.5 text-xs text-slate-300",children:[r.jsx("input",{type:"checkbox",checked:E,onChange:k=>T(k.target.checked),className:"accent-cyan-500"}),r.jsx("span",{children:"Speak"})]}),r.jsxs("select",{value:B,onChange:k=>{const Y=k.target.value;U(Y),C(ne=>({...ne||{},engine:Y}))},className:"field-input text-xs",children:[r.jsx("option",{value:"flite",children:"flite"}),r.jsx("option",{value:"espeak",children:"espeak"})]}),B==="flite"?r.jsx("select",{value:O,onChange:k=>{const Y=k.target.value;Q(Y),C(ne=>({...ne||{},voice:Y}))},className:"field-input text-xs",children:CN.map(k=>r.jsx("option",{value:k,children:k},k))}):r.jsx("select",{value:L,onChange:k=>{const Y=Number(k.target.value);G(Y),C(ne=>({...ne||{},pitch:Y}))},className:"field-input text-xs",children:EN.map(k=>r.jsxs("option",{value:k,children:["pitch ",k]},k))})]}),r.jsx("button",{type:"submit",disabled:!Z||q,className:"button-dark h-full disabled:opacity-50 self-stretch",children:q?"...":"Send"})]})]})}function go({compact:a=!1}){const{session:l,setNickname:i}=_e(),{value:o,save:f}=Pe("profile",{nickname:""}),[d,h]=m.useState(o.nickname||""),[p,x]=m.useState(!1),g=l?.role!=="spectator";m.useEffect(()=>{h(o.nickname||"")},[o.nickname]);async function b(y){if(y.preventDefault(),!g)return;const j=(d||"").trim().slice(0,32);if(j){x(!0);try{await i(j),f({nickname:j})}catch(M){alert(M.message)}finally{x(!1)}}}return r.jsxs("form",{className:"grid w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-stretch gap-0.5",onSubmit:b,children:[r.jsx("input",{className:"field-input flex-1 min-w-0",value:d,onChange:y=>h(y.target.value),maxLength:32,placeholder:"Enter a nickname",disabled:!g}),r.jsx("button",{type:"submit",disabled:!g||p,className:"button-dark h-full shrink-0 whitespace-nowrap px-0.5 py-0 disabled:opacity-50",children:p?"Saving…":a?"Set":"Save"})]})}function kN(a){const l=Array.isArray(a?.socials)?a.socials:null;if(l&&l.length)return l;const i=[];return a?.discord?.invite&&i.push({id:"discord",label:"Discord",url:a.discord.invite}),a?.kofi?.link&&i.push({id:"kofi",label:"Ko-fi",url:a.kofi.link}),i}function qN(a){if(!a||typeof a!="object")return null;const l=a.id||a.service||a.key||a.name||null,i=a.label||a.title||a.name||l||"Link",o=a.url||a.link||a.href||null;return o?{id:l,label:i,url:o}:null}function wg({className:a=""}){const{session:l}=_e(),i=kN(l).map(qN).filter(Boolean).slice(0,4);return i.length?r.jsx("div",{className:`grid grid-cols-2 grid-rows-2 gap-0.5 ${a}`,children:i.map(o=>r.jsx(ed,{...o},`${o.id||o.label}-${o.url}`))}):null}function MN({compact:a=!1}){return r.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:r.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:r.jsx(go,{compact:a})})})}function RN(){return r.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:r.jsx(wg,{className:"flex-1 min-h-0"})})}function rf(...a){return a.filter(Boolean).join(" ")}function TN(a){const l=a?.batteryState?.percentDisplay;return l==null?"--":`${l}%`}function DN(a){return a?.batteryState?a.batteryState.urgentActive?"text-red-400":a.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function _N(a){switch(a){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function ON(a,l){if(!a)return"";const i=a.nickname||a.socketId?.slice(0,6)||"unknown";return a.socketId&&a.socketId===l?`${i} (you)`:i}function vo({title:a="Rovers"}){const{session:l,requestControl:i}=_e(),[o,f]=m.useState({}),[d,h]=m.useState(()=>Date.now()),p=m.useMemo(()=>l?.role&&l.role!=="spectator",[l?.role]),x=l?.roster??[],g=l?.turnQueues??{},b=l?.users??[],y=l?.socketId||null,j=m.useMemo(()=>Object.values(g||{}).some(N=>N?.deadline||N?.idleDeadline),[g]);m.useEffect(()=>{if(!j)return;const N=setInterval(()=>{h(Date.now())},1e3);return()=>clearInterval(N)},[j]);const M=m.useMemo(()=>{const N=new Set(x.map(A=>String(A.id))),q=Object.keys(g||{}).filter(A=>!N.has(String(A))).map(A=>({id:A,name:A,locked:!1,batteryState:null}));return[...x,...q]},[x,g]);async function C(N){if(N){f(q=>({...q,[N]:!0}));try{await i(N)}catch(q){alert(q.message)}finally{f(q=>({...q,[N]:!1}))}}}const w=N=>b.find(q=>q.socketId===N)||{socketId:N,nickname:null,role:null};return r.jsxs("div",{className:"space-y-0.5 text-sm",children:[a&&r.jsx("p",{className:"text-sm text-slate-400",children:a}),M.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"No rovers registered."}):r.jsx("ul",{className:"space-y-0.5 text-sm",children:M.map(N=>{const q=String(N.id),A=g?.[q]||null,E=A?.queue||[],T=A?.idleDeadline||A?.deadline||null,B=T&&T>d?Math.ceil((T-d)/1e3):T?0:null,U=A?.current||null,O=U?E.findIndex(R=>R===U):-1,Q=E.length>1?O>=0?E[(O+1)%E.length]:E[0]:null,L=!!(y&&U&&U===y),G=!!(y&&Q&&Q===y),Z=B!=null&&(L||G),ae=!!N.locked,ue=N.lockReason?`locked: ${N.lockReason}`:"locked",re=ae?ue:o[q]?"...":"request",F=p&&!ae&&!o[q];return r.jsxs("li",{className:rf("surface flex flex-wrap items-start justify-between gap-0.5",F&&"cursor-pointer",ae&&"bg-red-900/40"),onClick:()=>{F&&C(N.id)},children:[r.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsxs("div",{className:"flex items-center gap-0.5",children:[r.jsx("p",{className:"text-slate-200",children:N.name}),Z?r.jsx("span",{className:"rounded bg-slate-800 px-1 text-[0.7rem] text-slate-200",children:L?`${B}s left`:`Your turn in ${B}s`}):null]}),r.jsx("span",{className:rf("text-[0.75rem] font-semibold",DN(N)),children:TN(N)})]}),E.length===0?r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"No queue."}):r.jsx("div",{className:"flex flex-wrap items-center gap-0.5",children:E.map((R,X)=>{const te=w(R),se=R===U,me=!!(Q&&R===Q&&!se),k=se?"bg-sky-600 text-white ring-2 ring-amber-300":me?"bg-emerald-700/60 text-emerald-100 ring-1 ring-emerald-300/70":"bg-slate-800 text-slate-200";return r.jsxs("span",{className:`flex items-center gap-0.5 rounded px-1 text-[0.7rem] ${k}`,children:[r.jsx("span",{className:`${_N(te.role)} font-semibold`,children:ON(te,y)}),se&&r.jsx("span",{className:"text-[0.65rem] text-slate-200",children:"now"}),me&&r.jsx("span",{className:"text-[0.65rem] text-emerald-100",children:"next"})]},`${q}-${R}-${X}`)})})]}),p?r.jsx("button",{type:"button",onClick:R=>{R.stopPropagation(),C(N.id)},disabled:o[q]||ae,className:rf("button-dark disabled:opacity-40",ae&&"bg-red-600/70 text-white hover:bg-red-600"),children:re}):null]},N.id)})})]})}function BN(a){switch(a){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function LN(a,l){if(!a)return"";const i=a.nickname||a.socketId?.slice(0,6)||"unknown";return a.socketId&&a.socketId===l?`${i} (you)`:i}function so({hideNicknameForm:a=!1,hideHeader:l=!1,className:i="",fillHeight:o=!1,compact:f=!1}){const{session:d}=_e(),h=d?.role!=="spectator",p=d?.users??[],x=d?.socketId||null,g=m.useMemo(()=>[...p].sort((y,j)=>y.socketId===x?-1:j.socketId===x?1:(y.nickname||"").localeCompare(j.nickname||"")),[x,p]),b=o?"flex-1 min-h-0 overflow-y-auto":f?"h-28 overflow-y-auto":"h-48 overflow-y-auto";return r.jsxs("section",{className:`panel-section space-y-0.5 text-base ${o?"flex h-full min-h-0 flex-col overflow-hidden":""} ${i}`,children:[!a&&r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"grid gap-0.5 md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]",children:[r.jsx("div",{className:"min-w-0",children:r.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:r.jsx(go,{compact:f})})}),r.jsx(wg,{})]}),!h&&r.jsx("p",{className:"text-xs text-slate-500",children:"Spectators cannot set nicknames."})]}),r.jsxs("div",{className:`space-y-0.5 ${o?"flex flex-1 min-h-0 flex-col":""}`,children:[!l&&r.jsxs("div",{className:`flex items-center justify-between text-sm text-slate-400 ${f?"text-xs":""}`,children:[r.jsx("span",{children:"Users"}),r.jsx("span",{className:"text-xs text-slate-500",children:g.length})]}),r.jsx("div",{className:`surface flex flex-wrap content-start items-start gap-0.5 px-0 pb-0 ${b} ${f?"text-[0.8rem]":""}`,children:g.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"Waiting for users…"}):g.map(y=>r.jsx("span",{className:`rounded bg-slate-800/80 px-1 py-0.25 text-[0.7rem] ${BN(y.role)}`,children:LN(y,x)},y.socketId))})]})]})}const zN=/^cu_[a-f0-9]{32}$/,tx=8*1024*1024,pi="field-input w-full max-w-sm text-left focus:ring-emerald-500",Zl="mx-auto w-full max-w-xl flex justify-center",ii="mx-auto flex w-full max-w-md flex-col items-center space-y-0.5 text-center";function UN(a){const l=String(a||"").trim();return l?l.length<=10?`${l.slice(0,2)}***${l.slice(-2)}`:`${l.slice(0,6)}...${l.slice(-6)}`:""}function HN(a){let l="";for(let o=0;o{if(!a||a.iceGatheringState==="complete"){i();return}const o=setTimeout(()=>{a.removeEventListener("icegatheringstatechange",f),i()},l);function f(){a.iceGatheringState==="complete"&&(clearTimeout(o),a.removeEventListener("icegatheringstatechange",f),i())}a.addEventListener("icegatheringstatechange",f)})}function XN(a,l){if(!a||!a.length)return new Float32Array(0);if(l===io||!Number.isFinite(l)||l<=0)return a;const i=l/io,o=Math.max(1,Math.round(a.length/i)),f=new Float32Array(o);for(let d=0;dse&&i?.[se]||null,[i,se]),Y=async()=>{const ce=se;if(!ce){B("Take control of a rover first.");return}if(!j){B("Select an audio file first.");return}if(j.size>tx){B(`File too large (max ${tx} bytes).`);return}w(!0),B("");try{const oe=await j.arrayBuffer(),de=HN(new Uint8Array(oe));await o?.({roverId:ce,name:j.name,mime:j.type||"",dataBase64:de}),B(`Playing upload on ${ce}.`)}catch(oe){B(oe.message||"Failed to play upload.")}finally{w(!1)}},ne=async()=>{const ce=se;if(!ce){B("Take control of a rover first.");return}w(!0),B("");try{await f?.(ce),B(`Stopped upload on ${ce}.`)}catch(oe){B(oe.message||"Failed to stop upload.")}finally{w(!1)}},le=m.useCallback(async ce=>{const oe=String(ce||X.current||"").trim();R.current=!1,E("idle");try{L.current&&Q.current&&Q.current.disconnect(L.current)}catch{}try{L.current&&G.current&&L.current.disconnect(G.current)}catch{}try{G.current&&O.current?.destination&&G.current.disconnect(O.current.destination)}catch{}if(O.current)try{await O.current.close()}catch{}if(ue.current){try{ue.current.getSenders().forEach(de=>de.track?.stop())}catch{}try{ue.current.close()}catch{}}if(ue.current=null,L.current=null,Q.current=null,G.current=null,O.current=null,Z.current=[],ae.current=0,F.current=!1,re.current="none",U.current)try{U.current.getTracks().forEach(de=>de.stop())}catch{}if(U.current=null,oe){try{await b?.(oe)}catch{}try{await h?.(oe)}catch{}}X.current=""},[h,b]),pe=m.useCallback(async(ce,oe)=>{const de=window.AudioContext||window.webkitAudioContext;if(!de)throw new Error("Web Audio API is not supported in this browser.");await d?.(ce);const Ae=new de({latencyHint:"interactive"});O.current=Ae;const H=Ae.createMediaStreamSource(oe);Q.current=H;const P=Ae.createScriptProcessor(1024,1,1);L.current=P;const he=Ae.createGain();he.gain.value=0,G.current=he,Z.current=[],ae.current=0,re.current="socket",P.onaudioprocess=Se=>{if(!R.current||re.current!=="socket")return;const De=Se.inputBuffer?.getChannelData(0);if(!De||De.length===0)return;const ze=XN(De,Ae.sampleRate);if(!ze.length)return;const yt=QN(ze);for(Z.current.push(yt),ae.current+=yt.length;ae.current>=of;){const Mt=KN(Z.current,ae.current),pt=Mt.slice(0,of),Et=Mt.slice(of);Z.current=Et.length?[Et]:[],ae.current=Et.length,p?.({roverId:ce,data:pt})}},H.connect(P),P.connect(he),he.connect(Ae.destination),Ae.state==="suspended"&&await Ae.resume(),E("live")},[p,d]),ie=m.useCallback(async(ce,oe)=>{const de=await x?.(ce),Ae=String(de?.whipUrl||"").trim(),H=String(de?.token||"").trim();if(!Ae||!H)throw new Error("WHIP endpoint unavailable");const P=new RTCPeerConnection($N);ue.current=P,re.current="whip",F.current=!1;try{oe.getAudioTracks().forEach(ze=>P.addTrack(ze,oe)),P.onconnectionstatechange=()=>{const ze=P.connectionState;if(R.current){if(ze==="connected"){E("live");return}if((ze==="failed"||ze==="disconnected")&&!F.current){F.current=!0;const yt=X.current;if(!yt||!U.current)return;(async()=>{try{await b?.(yt)}catch{}if(!(!R.current||re.current!=="whip"))try{await pe(yt,U.current)}catch(Mt){E("error"),B(Mt?.message||"Mic fallback failed.")}})()}}};const he=await P.createOffer({offerToReceiveAudio:!1,offerToReceiveVideo:!1});await P.setLocalDescription(he),await FN(P,1800);const Se=await fetch(Ae,{method:"POST",headers:{"Content-Type":"application/sdp",...YN(H)},body:P.localDescription?.sdp||he.sdp});if(!Se.ok)throw new Error(`WHIP request failed: ${Se.status}`);const De=await Se.text();await P.setRemoteDescription({type:"answer",sdp:De}),await g?.(ce),E("live")}catch(he){try{P.close()}catch{}throw ue.current===P&&(ue.current=null),re.current="none",he}},[g,x,pe,b]),fe=m.useCallback(async ce=>{const oe=String(ce||"").trim();if(!oe)throw new Error("Take control of a rover first.");if(!navigator.mediaDevices?.getUserMedia)throw new Error("Microphone capture is not supported in this browser.");await le(oe),E("starting");let de=null;try{de=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:io,echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0}}),U.current=de,R.current=!0,X.current=oe;let Ae=null;try{await ie(oe,de);return}catch(H){Ae=H;try{await b?.(oe)}catch{}}await pe(oe,de),Ae&&B(`WHIP unavailable, using socket fallback: ${Ae.message||"unknown error"}`)}catch(Ae){if(de)try{de.getTracks().forEach(H=>H.stop())}catch{}throw U.current=null,O.current=null,Q.current=null,L.current=null,G.current=null,ue.current=null,re.current="none",Ae}},[pe,ie,le,b]);return m.useEffect(()=>{const ce=!!(N||me),oe=se;let de=!1;async function Ae(){if(!oe||!ce){await le(X.current||oe);return}if(!(R.current&&X.current===oe))try{await fe(oe)}catch(H){de||(E("error"),B(H?.message||"Failed to start microphone forwarding."))}}return Ae(),()=>{de=!0}},[N,me,fe,le,se]),m.useEffect(()=>()=>{le(X.current)},[le]),r.jsx("section",{className:`surface ${Zl}`,children:r.jsxs("div",{className:ii,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"VIP Audio Forwarding"}),r.jsxs("label",{className:"grid w-full gap-0.5 text-xs text-slate-300",children:[r.jsx("span",{children:"Audio file (mp3 / wav / ogg)"}),r.jsx("input",{className:pi,type:"file",accept:".mp3,.wav,.ogg,audio/mpeg,audio/wav,audio/ogg",disabled:C||!se,onChange:ce=>M(ce.target.files?.[0]||null)})]}),j?r.jsxs("div",{className:"surface-muted mx-auto w-full max-w-sm text-xs text-slate-300 text-center",children:[j.name," (",j.size," bytes)"]}):null,r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:C||!se,onClick:Y,children:C?"Working...":"Play Upload"}),r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:C||!se,onClick:ne,children:"Stop"})]}),r.jsxs("div",{className:"surface-muted mx-auto flex w-full max-w-sm flex-col gap-0.5 p-0.5 text-xs text-slate-300 text-center",children:[r.jsx("p",{className:"text-slate-200",children:"Microphone Forwarding"}),r.jsxs("label",{className:"flex items-center justify-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:N,disabled:!se,onChange:ce=>q(!!ce.target.checked)}),r.jsx("span",{children:"Open mic"})]}),r.jsxs("p",{className:"text-slate-400",children:["PTT key: ",y?.keymap?.micPtt?.[0]||"v"," (hold)"]}),r.jsxs("p",{className:"text-slate-400",children:["mic: ",A]}),r.jsxs("p",{className:"text-slate-500",children:["transport: ",re.current==="none"?"idle":re.current]})]}),k?r.jsxs("div",{className:"surface-muted mx-auto w-full max-w-sm text-xs text-slate-300 text-center",children:["state: ",k.state||"idle",k.error?` | error: ${k.error}`:""]}):null,T?r.jsx("div",{className:"text-xs text-slate-400 text-center",children:T}):null]})})}function IN({pendingRequestId:a,currentStoredKey:l,nickname:i,requestVerification:o,applyIdentityKey:f,onMessage:d}){const[h,p]=m.useState(0),[x,g]=m.useState(""),[b,y]=m.useState(!1),[j,M]=m.useState(!1),C=()=>{p(1),g(l),y(!1),d?.("")},w=()=>{p(0),y(!1)},N=async q=>{if(q.preventDefault(),!b){d?.("Please confirm your nickname agreement before sending.");return}if(h<3){d?.("Complete all request steps before sending.");return}M(!0),d?.("");try{const A=await f(x);await o?.(),g(A),p(0),y(!1),d?.("Verification request sent to lockdown admins.")}catch(A){d?.(A.message||"Failed to submit request.")}finally{M(!1)}};return a?r.jsx("section",{className:`surface text-sm text-slate-300 ${Zl}`,children:r.jsxs("div",{className:ii,children:["Verification request pending: ",a]})}):h===0?r.jsx("section",{className:`surface ${Zl}`,children:r.jsxs("div",{className:ii,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Verification"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:C,disabled:j,children:"Request Verification"})]})}):r.jsx("form",{className:`surface ${Zl}`,onSubmit:N,children:r.jsxs("div",{className:ii,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Request verification"}),r.jsxs("p",{className:"text-xs text-slate-500",children:["Step ",h," of 3"]}),h===1?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Confirm your nickname, which is used as part of the verification process. You can edit it here before requesting."}),r.jsx("div",{className:"mx-auto w-full max-w-sm",children:r.jsx(go,{compact:!0})}),r.jsxs("div",{className:"surface-muted mx-auto w-full max-w-sm text-xs text-slate-300 text-center",children:["Current nickname: ",r.jsx("span",{className:"font-semibold",children:i||"(not set)"})]}),r.jsxs("label",{className:"flex items-center justify-center gap-0.5 text-xs text-slate-300 text-center",children:[r.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:b,onChange:q=>y(q.target.checked)}),r.jsx("span",{children:"I understand this nickname is tied to my verification."})]}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!i||!b,onClick:()=>p(2),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:w,children:"Cancel"})]})]}):null,h===2?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Save your identity key in a safe place. You can use it to restore your identity in another browser."}),r.jsx("input",{className:pi,type:"password",name:"identity_key_request",autoComplete:"current-password",maxLength:35,value:x,onChange:q=>g(q.target.value.toLowerCase()),placeholder:"Identity key for this request"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!x,onClick:()=>navigator.clipboard?.writeText(x),children:"Copy Key"}),r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!x,onClick:()=>p(3),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>p(1),children:"Back"})]})]}):null,h===3?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Final step: confirm and send your verification request."}),r.jsx("input",{className:pi,type:"password",name:"identity_key_request_final",autoComplete:"current-password",maxLength:35,value:x,onChange:q=>g(q.target.value.toLowerCase()),placeholder:"Identity key for this request"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:j||!x,children:"Confirm Request"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>p(2),children:"Back"})]})]}):null]})})}function JN({currentStoredKey:a,applyIdentityKey:l,onMessage:i}){const[o,f]=m.useState(0),[d,h]=m.useState(""),[p,x]=m.useState(!1),g=()=>{f(1),h(""),i?.("")},b=()=>{f(0),h("")},y=async j=>{if(j.preventDefault(),!(o<2)){x(!0),i?.("");try{await l(d),f(0),h(""),i?.("Identity key restored.")}catch(M){i?.(M.message||"Failed to restore key.")}finally{x(!1)}}};return r.jsx("section",{className:`surface ${Zl}`,children:r.jsxs("div",{className:ii,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Identity key"}),r.jsxs("p",{className:"text-xs text-slate-500",children:["Current: ",UN(a)||"not set yet"]}),r.jsx("input",{className:pi,type:"password",name:"identity_key_current",autoComplete:"current-password",maxLength:35,value:a,readOnly:!0,placeholder:"Identity key"}),o===0?r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!a,onClick:()=>navigator.clipboard?.writeText(a),children:"Copy Key"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:g,disabled:p,children:"Restore Key"})]}):null,o===1?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Restoring your key should only be done when needed. Use this to move your identity to another browser."}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(2),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:b,children:"Cancel"})]})]}):null,o===2?r.jsxs("form",{className:"space-y-0.5",onSubmit:y,children:[r.jsx("input",{className:pi,type:"password",name:"identity_key_restore",autoComplete:"current-password",maxLength:35,value:d,onChange:j=>h(j.target.value.toLowerCase()),placeholder:"Paste key to restore"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:p||!d,children:"Confirm Restore"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(1),children:"Back"})]})]}):null]})})}function Cg(){const{session:a,identifySession:l,requestVerification:i,playUploadedAudio:o,stopUploadedAudio:f,startMicForward:d,stopMicForward:h,sendMicChunk:p,startMicWhip:x,readyMicWhip:g,stopMicWhip:b}=_e(),{value:y,save:j}=Pe("identity",{cookieUserId:""}),{value:M}=Pe("profile",{nickname:""}),C=m.useMemo(()=>(y?.cookieUserId||"").trim(),[y?.cookieUserId]),w=(M?.nickname||"").trim(),N=!!a?.isVerified,q=a?.verification?.pendingRequestId||null,A=m.useMemo(()=>a?.roster??[],[a?.roster]),E=String(a?.assignment?.roverId||"").trim(),T=m.useMemo(()=>E?A.filter(Q=>Q.id===E):[],[E,A]),[B,U]=m.useState(""),O=async Q=>{const L=String(Q||"").trim().toLowerCase();if(!L)throw new Error("Identity key required.");if(!zN.test(L))throw new Error("Identity key must match format: cu_ + 32 lowercase hex chars.");return j(G=>({...G||{},cookieUserId:L})),await l({cookieUserId:L,nickname:w}),L};return r.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[N?r.jsx(ZN,{roster:T,ownRoverId:E,audioForwardByRover:a?.audioForward||{},playUploadedAudio:o,stopUploadedAudio:f,startMicForward:d,stopMicForward:h,sendMicChunk:p,startMicWhip:x,readyMicWhip:g,stopMicWhip:b}):r.jsx(IN,{pendingRequestId:q,currentStoredKey:C,nickname:w,requestVerification:i,applyIdentityKey:O,onMessage:U}),r.jsx(JN,{currentStoredKey:C,applyIdentityKey:O,onMessage:U}),B?r.jsx("div",{className:Zl,children:r.jsx("p",{className:"text-xs text-slate-400 text-center",children:B})}):null]})}function WN(){const{state:{roverId:a}}=qt(),i=ns(a)?.sensors||{};return r.jsx("section",{className:"panel-section",children:r.jsx("div",{className:"aspect-square w-full",children:r.jsx(Cf,{sensors:i})})})}function PN(){const{state:{roverId:a,keymap:l,camera:i,horn:o},pipeline:f,actions:{setServoAngle:d,setNightVision:h,startHorn:p,stopHorn:x}}=qt(),g=sg(a),b=g.docked&&!g.driving,y=i?.config,j=!!(a&&i?.enabled&&y),M=!!(a&&f?.nightVision),C=f?.nightVisionState,w=!!(a&&f?.horn),N=o?.overheated,q=typeof y?.minAngle=="number"?y.minAngle:-30,A=typeof y?.maxAngle=="number"?y.maxAngle:30,E=typeof i?.angle=="number"?i.angle:typeof y?.homeAngle=="number"?y.homeAngle:(q+A)/2,T=Nn(l?.nightVisionToggle?.[0]),B=Nn(l?.hornHonk?.[0]),U=Nn(l?.cameraUp?.[0]),O=Nn(l?.cameraDown?.[0]);return r.jsxs("section",{className:"panel-section grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] gap-0.5",children:[r.jsx(ig,{layout:"desktop",expand:!0,driveDockState:g}),b?null:r.jsxs("div",{className:"surface space-y-0.5 p-0 text-sm text-slate-200",children:[M&&r.jsx(rg,{nightVisionOn:C?.nightVisionOn,disabled:!a,onToggle:h,keyLabel:T}),w&&r.jsx(og,{disabled:!a||N,onStart:p,onStop:x,keyLabel:B,active:o?.active,heat:o?.heat}),j&&r.jsx(cg,{value:E,min:q,max:A,onChange:d,keyDownLabel:O,keyUpLabel:U,className:"space-y-0.5 px-1 py-1",labelRowClass:"text-xs text-slate-300",labelClass:"",valueClass:"font-mono text-slate-100",sliderClass:"w-full",accentClass:"accent-emerald-400",endpointClass:"text-[0.7rem] text-slate-400"})]})]})}function ew({layout:a,onOpenHelpOverlay:l}){const{session:i}=_e(),o=i?.isVerified?"bg-emerald-400":"bg-red-600";return r.jsx("section",{className:"panel text-base",children:r.jsxs(sd,{defaultTab:"telemetry",children:[r.jsxs(id,{children:[r.jsx(Xt,{id:"telemetry",children:"Controls"}),r.jsx(Xt,{id:"vip",children:r.jsxs("span",{className:"inline-flex items-center gap-2",children:[r.jsx("span",{children:"VIP"}),r.jsx("span",{className:`inline-block h-3 w-3 rounded-full ${o}`,"aria-hidden":"true",title:i?.isVerified?"Verified":"Not verified"})]})}),r.jsx(Xt,{id:"help",children:"Help"}),r.jsx(Xt,{id:"settings",children:"Settings"})]}),r.jsxs(rd,{children:[r.jsx(Qt,{id:"telemetry",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]",children:[r.jsx(WN,{}),r.jsx(PN,{})]}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]",children:[r.jsx(vo,{}),r.jsx($f,{panelId:"replay-sources-desktop",fillHeight:!0}),r.jsx(RN,{})]}),r.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)] h-[14rem]",children:[r.jsx(xo,{fillHeight:!0}),r.jsxs("div",{className:"grid min-h-0 gap-0.5 grid-rows-[minmax(0,1fr)_auto]",children:[r.jsx(so,{compact:!0,hideNicknameForm:!0,fillHeight:!0}),r.jsx(MN,{compact:!0})]})]}),r.jsx(xg,{}),r.jsx(If,{defaultOrientation:"horizontal",panelId:"rightpane-telemetry"})]})}),r.jsx(Qt,{id:"vip",children:r.jsx(Cg,{})}),r.jsx(Qt,{id:"help",children:r.jsx(Ng,{layout:a,onOpenOverlay:l})}),r.jsx(Qt,{id:"settings",children:r.jsx(Sg,{})})]})]})})}const tw=new Set(["admin","lockdown","lockdown-admin"]),nw=new Set(["lockdown","lockdown-admin"]),aw=new Set(["admin","lockdown"]);function lw(a="admin"){return a==="lockdown"?{title:"Lockdown mode active",description:"Only the server owners can access the interface at this time."}:{title:"Admin mode active"}}function sw(){const{session:a}=_e(),l=a?.mode,i=a?.role,o=a?.adminReason?.text||"";a?.adminReason?.updatedAt;const f=a?.timezone||"UTC",d=a?.socials?.find(j=>String(j?.id||j?.label||"").toLowerCase()==="discord")?.url||a?.discord?.invite||null,h=aw.has(l),p=l==="lockdown"?nw.has(i):tw.has(i),[x,g]=m.useState(()=>new Date),b=m.useMemo(()=>{try{return new Intl.DateTimeFormat("en-US",{timeZone:f,hour:"numeric",minute:"2-digit",second:"2-digit"}).format(x)}catch{return x.toLocaleTimeString()}},[x,f]);if(m.useEffect(()=>{const j=setInterval(()=>g(new Date),1e3);return()=>clearInterval(j)},[]),!h||p)return null;const y=lw(l);return r.jsx("div",{className:"pointer-events-auto fixed inset-0 z-50 flex items-center justify-center bg-black/85 px-0.5 py-0.5",children:r.jsxs("div",{className:"surface w-full max-w-md space-y-0.5 text-slate-100 shadow-2xl",children:[r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-lg font-semibold",children:y.title}),r.jsx("p",{className:"text-sm text-slate-300",children:y.description})]}),r.jsxs("div",{className:"surface-muted space-y-0.5",children:[r.jsx("p",{className:"text-[0.7rem] tracking-wide text-slate-400",children:"Reason for locking:"}),r.jsx("p",{className:"text-lg font-semibold text-slate-100",children:o||"No reason set."}),r.jsxs("p",{className:"text-center text-sm text-slate-300",children:["Server time: ",b]})]}),r.jsx("div",{className:"surface-muted",children:r.jsx(gg,{})}),r.jsx("div",{className:"w-full justify-center items-center",children:r.jsx(ed,{id:"discord",label:"Join our Discord server for updates!",url:d})}),"You can still use the chat while the server is locked:",r.jsxs("div",{className:"max-h-80 overflow-y-auto",children:[r.jsx(xo,{}),r.jsx(go,{})]})]})})}const iw="/assets/turn_alert-DVO8EZJa.mp3";function rw(a,l=1){const i=m.useRef(null);return m.useEffect(()=>{i.current=new Audio(a),i.current.volume=l,i.current.load()},[a,l]),()=>{i.current&&(i.current.volume=l,i.current.currentTime=0,i.current.play().catch(()=>{}))}}function ow(){const{session:a,pushAlert:l}=_e(),{value:i}=Pe("audio",wt),o=Number.isFinite(i?.masterVolume)?i.masterVolume:wt.masterVolume,f=Number.isFinite(i?.alertVolume)?i.alertVolume:wt.alertVolume,d=Math.max(0,Math.min(1,o*f)),h=rw(iw,d),p=m.useRef(new Set),x=m.useMemo(()=>a?.turnQueues||{},[a?.turnQueues]),g=a?.socketId||null;return m.useEffect(()=>{if(!g)return;const b=[];Object.entries(x).forEach(([y,j])=>{!j||!j.current||j.current!==g||p.current.has(y)||(b.push(y),p.current.add(y))}),Object.keys(x).forEach(y=>{(!x[y]||x[y].current!==g)&&p.current.delete(y)}),b.length!==0&&(b.forEach(y=>{const j=a?.roster?.find(M=>String(M.id)===String(y))?.name||y;l({title:"Your turn!",message:`You now control ${j}.`,color:"#ff5722"})}),h())},[x,h,l,a?.roster,g]),null}function cw({visible:a,mode:l,onEnterFullscreen:i,onDismiss:o}){if(!a)return null;const f=l==="pwa-hint";return r.jsx("div",{className:"fixed inset-0 z-40 flex items-end justify-center p-2 pointer-events-none sm:items-center",children:r.jsx("div",{className:"pointer-events-auto w-full max-w-sm rounded-lg border border-cyan-500/40 bg-zinc-950/95 shadow-xl",children:r.jsxs("div",{className:"space-y-0.5 p-4 text-sm text-slate-100",children:[r.jsx("h2",{className:"text-base font-semibold text-white",children:"Better in fullscreen"}),f?r.jsxs("p",{className:"text-slate-300",children:["For fullscreen on iOS, open Safari's share menu and pick ",r.jsx("strong",{children:"Add to Home Screen"}),". Launching from the home screen removes the browser chrome."]}):r.jsx("p",{className:"text-slate-300",children:"Enable fullscreen to free up more space for the video feed and controls. You can exit fullscreen at any time via the system back or home gesture."}),r.jsxs("div",{className:"flex justify-end gap-0.5 pt-1 text-sm",children:[r.jsx("button",{type:"button",className:"rounded border border-slate-600 px-3 py-1 text-slate-200",onClick:o,children:f?"Got it":"Not now"}),!f&&r.jsx("button",{type:"button",className:"rounded bg-cyan-500 px-3 py-1 font-semibold text-black hover:bg-cyan-400",onClick:i,children:"Enter fullscreen"})]})]})})})}const Eg="fullscreenPromptDismissed",uw=new Set(["mobile-portrait","mobile-landscape"]),kg=()=>{if(typeof window>"u")return null;try{return window.sessionStorage}catch{return null}},fw=()=>{const a=kg();if(!a)return!1;try{return a.getItem(Eg)==="1"}catch{return!1}},nx=()=>{const a=kg();if(a)try{a.setItem(Eg,"1")}catch{}},dw=()=>{if(typeof navigator>"u")return!1;const a=navigator.userAgent||navigator.platform||"";return/iphone|ipad|ipod/i.test(a)},mw=()=>{if(typeof document>"u")return!1;const a=document;return!!(a.fullscreenEnabled||a.webkitFullscreenEnabled||a.mozFullScreenEnabled||a.msFullscreenEnabled)};function hw(a){const l=uw.has(a),[i,o]=m.useState(!1),[f,d]=m.useState("native"),h=m.useMemo(()=>dw(),[]),p=m.useCallback(()=>{if(!l){o(!1);return}if(fw()){o(!1);return}if(h){d("pwa-hint"),o(!0);return}if(!mw()){o(!1);return}if(typeof document<"u"&&document.fullscreenElement){o(!1);return}d("native"),o(!0)},[h,l]);m.useEffect(()=>{p()},[p]),m.useEffect(()=>{if(typeof document>"u")return;const b=()=>{if(document.fullscreenElement){o(!1);return}p()};return document.addEventListener("fullscreenchange",b),document.addEventListener("webkitfullscreenchange",b),()=>{document.removeEventListener("fullscreenchange",b),document.removeEventListener("webkitfullscreenchange",b)}},[p]);const x=m.useCallback(()=>{nx(),o(!1)},[]),g=m.useCallback(async()=>{if(!l||h||typeof document>"u")return!1;const b=document.documentElement;if(!b)return!1;const y=b.requestFullscreen||b.webkitRequestFullscreen||b.mozRequestFullScreen||b.msRequestFullscreen;if(!y)return!1;try{const j=y.call(b);return j&&typeof j.then=="function"&&await j,nx(),o(!1),!0}catch(j){return console.warn("Failed to enter fullscreen",j),!1}},[h,l]);return m.useEffect(()=>{l||o(!1)},[l]),{visible:i,mode:f,enterFullscreen:g,dismiss:x}}function pw({visible:a,layout:l,onClose:i,showOnLoad:o,onToggleShowOnLoad:f}){if(!a)return null;const{state:d}=qt(),h=p=>{const x=!p.target.checked;f?.(x)};return r.jsx("div",{className:"fixed inset-0 z-30 flex items-center justify-center bg-black/80 px-0.5 py-0.5",children:r.jsxs("div",{className:"pointer-events-auto surface w-full max-w-4xl max-h-[90vh] overflow-hidden shadow-2xl",children:[r.jsxs("div",{className:"flex items-center justify-between border-b border-slate-700 px-0.5 py-0.25 text-sm text-slate-200",children:[r.jsx("span",{className:"font-semibold",children:"Help & controls"}),r.jsxs("div",{className:"flex items-center gap-0.5 text-[0.8rem] text-slate-300",children:[r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!o,onChange:h,className:"accent-cyan-500"}),r.jsx("span",{children:"Don't show again"})]}),r.jsx("button",{type:"button",onClick:i,className:"button-dark px-2 py-0.25 text-[0.8rem]",children:"Close"})]})]}),r.jsx("div",{className:"max-h-[85vh] overflow-y-auto p-0.5",children:r.jsx(jg,{layout:l,keymap:d?.keymap||{}})})]})})}const xw=`aroma +`).trim();if(ne){A(!0);try{await p(ne,te),N(""),y(),j(!1)}catch(le){alert(le.message)}finally{A(!1)}}}const me=i?"flex-1 min-h-0 overflow-y-auto":"h-48 overflow-y-auto";return r.jsxs("section",{className:`panel-section space-y-0.5 text-base ${i?"flex h-full flex-col overflow-hidden":""}`,children:[r.jsxs("div",{className:`surface overflow-y-auto space-y-0.5 px-0 ${me}`,ref:ae,children:[R.length===0&&X.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"No messages yet."}):R.map(k=>r.jsx(Yf,{message:k},k.id)),X.map(k=>r.jsx(Fx,{message:k},`typing-${k.typingId||k.id}`))]}),!a&&r.jsxs("form",{className:"flex flex-wrap items-stretch gap-0.5",onSubmit:se,children:[r.jsx("input",{className:"field-input flex-1 min-w-[10rem]",value:w,onChange:k=>{const Y=k.target.value;N(Y),j(!!Y.trim())},onFocus:k=>{g(k),j(!!w.trim())},onBlur:k=>{b(k),j(!1)},onKeyDown:k=>{k.key==="Enter"&&!w.trim()&&(k.preventDefault(),y(),j(!1))},ref:k=>x(k,{target:"panel"}),placeholder:Z?"Type a message…":l?"":"Spectators cannot chat",disabled:!Z}),F&&r.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 basis-full sm:basis-auto",children:[r.jsxs("label",{className:"flex items-center gap-0.5 text-xs text-slate-300",children:[r.jsx("input",{type:"checkbox",checked:E,onChange:k=>T(k.target.checked),className:"accent-cyan-500"}),r.jsx("span",{children:"Speak"})]}),r.jsxs("select",{value:B,onChange:k=>{const Y=k.target.value;U(Y),C(ne=>({...ne||{},engine:Y}))},className:"field-input text-xs",children:[r.jsx("option",{value:"flite",children:"flite"}),r.jsx("option",{value:"espeak",children:"espeak"})]}),B==="flite"?r.jsx("select",{value:O,onChange:k=>{const Y=k.target.value;Q(Y),C(ne=>({...ne||{},voice:Y}))},className:"field-input text-xs",children:CN.map(k=>r.jsx("option",{value:k,children:k},k))}):r.jsx("select",{value:L,onChange:k=>{const Y=Number(k.target.value);G(Y),C(ne=>({...ne||{},pitch:Y}))},className:"field-input text-xs",children:EN.map(k=>r.jsxs("option",{value:k,children:["pitch ",k]},k))})]}),r.jsx("button",{type:"submit",disabled:!Z||q,className:"button-dark h-full disabled:opacity-50 self-stretch",children:q?"...":"Send"})]})]})}function go({compact:a=!1}){const{session:l,setNickname:i}=_e(),{value:o,save:f}=Pe("profile",{nickname:""}),[d,h]=m.useState(o.nickname||""),[p,x]=m.useState(!1),g=l?.role!=="spectator";m.useEffect(()=>{h(o.nickname||"")},[o.nickname]);async function b(y){if(y.preventDefault(),!g)return;const j=(d||"").trim().slice(0,32);if(j){x(!0);try{await i(j),f({nickname:j})}catch(M){alert(M.message)}finally{x(!1)}}}return r.jsxs("form",{className:"grid w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-stretch gap-0.5",onSubmit:b,children:[r.jsx("input",{className:"field-input flex-1 min-w-0",value:d,onChange:y=>h(y.target.value),maxLength:32,placeholder:"Enter a nickname",disabled:!g}),r.jsx("button",{type:"submit",disabled:!g||p,className:"button-dark h-full shrink-0 whitespace-nowrap px-0.5 py-0 disabled:opacity-50",children:p?"Saving…":a?"Set":"Save"})]})}function kN(a){const l=Array.isArray(a?.socials)?a.socials:null;if(l&&l.length)return l;const i=[];return a?.discord?.invite&&i.push({id:"discord",label:"Discord",url:a.discord.invite}),a?.kofi?.link&&i.push({id:"kofi",label:"Ko-fi",url:a.kofi.link}),i}function qN(a){if(!a||typeof a!="object")return null;const l=a.id||a.service||a.key||a.name||null,i=a.label||a.title||a.name||l||"Link",o=a.url||a.link||a.href||null;return o?{id:l,label:i,url:o}:null}function wg({className:a=""}){const{session:l}=_e(),i=kN(l).map(qN).filter(Boolean).slice(0,4);return i.length?r.jsx("div",{className:`grid grid-cols-2 grid-rows-2 gap-0.5 ${a}`,children:i.map(o=>r.jsx(ed,{...o},`${o.id||o.label}-${o.url}`))}):null}function MN({compact:a=!1}){return r.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:r.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:r.jsx(go,{compact:a})})})}function RN(){return r.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:r.jsx(wg,{className:"flex-1 min-h-0"})})}function rf(...a){return a.filter(Boolean).join(" ")}function TN(a){const l=a?.batteryState?.percentDisplay;return l==null?"--":`${l}%`}function DN(a){return a?.batteryState?a.batteryState.urgentActive?"text-red-400":a.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function _N(a){switch(a){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function ON(a,l){if(!a)return"";const i=a.nickname||a.socketId?.slice(0,6)||"unknown";return a.socketId&&a.socketId===l?`${i} (you)`:i}function vo({title:a="Rovers"}){const{session:l,requestControl:i}=_e(),[o,f]=m.useState({}),[d,h]=m.useState(()=>Date.now()),p=m.useMemo(()=>l?.role&&l.role!=="spectator",[l?.role]),x=l?.roster??[],g=l?.turnQueues??{},b=l?.users??[],y=l?.socketId||null,j=m.useMemo(()=>Object.values(g||{}).some(N=>N?.deadline||N?.idleDeadline),[g]);m.useEffect(()=>{if(!j)return;const N=setInterval(()=>{h(Date.now())},1e3);return()=>clearInterval(N)},[j]);const M=m.useMemo(()=>{const N=new Set(x.map(A=>String(A.id))),q=Object.keys(g||{}).filter(A=>!N.has(String(A))).map(A=>({id:A,name:A,locked:!1,batteryState:null}));return[...x,...q]},[x,g]);async function C(N){if(N){f(q=>({...q,[N]:!0}));try{await i(N)}catch(q){alert(q.message)}finally{f(q=>({...q,[N]:!1}))}}}const w=N=>b.find(q=>q.socketId===N)||{socketId:N,nickname:null,role:null};return r.jsxs("div",{className:"space-y-0.5 text-sm",children:[a&&r.jsx("p",{className:"text-sm text-slate-400",children:a}),M.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"No rovers registered."}):r.jsx("ul",{className:"space-y-0.5 text-sm",children:M.map(N=>{const q=String(N.id),A=g?.[q]||null,E=A?.queue||[],T=A?.idleDeadline||A?.deadline||null,B=T&&T>d?Math.ceil((T-d)/1e3):T?0:null,U=A?.current||null,O=U?E.findIndex(R=>R===U):-1,Q=E.length>1?O>=0?E[(O+1)%E.length]:E[0]:null,L=!!(y&&U&&U===y),G=!!(y&&Q&&Q===y),Z=B!=null&&(L||G),ae=!!N.locked,ue=N.lockReason?`locked: ${N.lockReason}`:"locked",re=ae?ue:o[q]?"...":"request",F=p&&!ae&&!o[q];return r.jsxs("li",{className:rf("surface flex flex-wrap items-start justify-between gap-0.5",F&&"cursor-pointer",ae&&"bg-red-900/40"),onClick:()=>{F&&C(N.id)},children:[r.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsxs("div",{className:"flex items-center gap-0.5",children:[r.jsx("p",{className:"text-slate-200",children:N.name}),Z?r.jsx("span",{className:"rounded bg-slate-800 px-1 text-[0.7rem] text-slate-200",children:L?`${B}s left`:`Your turn in ${B}s`}):null]}),r.jsx("span",{className:rf("text-[0.75rem] font-semibold",DN(N)),children:TN(N)})]}),E.length===0?r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"No queue."}):r.jsx("div",{className:"flex flex-wrap items-center gap-0.5",children:E.map((R,X)=>{const te=w(R),se=R===U,me=!!(Q&&R===Q&&!se),k=se?"bg-sky-600 text-white ring-2 ring-amber-300":me?"bg-emerald-700/60 text-emerald-100 ring-1 ring-emerald-300/70":"bg-slate-800 text-slate-200";return r.jsxs("span",{className:`flex items-center gap-0.5 rounded px-1 text-[0.7rem] ${k}`,children:[r.jsx("span",{className:`${_N(te.role)} font-semibold`,children:ON(te,y)}),se&&r.jsx("span",{className:"text-[0.65rem] text-slate-200",children:"now"}),me&&r.jsx("span",{className:"text-[0.65rem] text-emerald-100",children:"next"})]},`${q}-${R}-${X}`)})})]}),p?r.jsx("button",{type:"button",onClick:R=>{R.stopPropagation(),C(N.id)},disabled:o[q]||ae,className:rf("button-dark disabled:opacity-40",ae&&"bg-red-600/70 text-white hover:bg-red-600"),children:re}):null]},N.id)})})]})}function BN(a){switch(a){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function LN(a,l){if(!a)return"";const i=a.nickname||a.socketId?.slice(0,6)||"unknown";return a.socketId&&a.socketId===l?`${i} (you)`:i}function so({hideNicknameForm:a=!1,hideHeader:l=!1,className:i="",fillHeight:o=!1,compact:f=!1}){const{session:d}=_e(),h=d?.role!=="spectator",p=d?.users??[],x=d?.socketId||null,g=m.useMemo(()=>[...p].sort((y,j)=>y.socketId===x?-1:j.socketId===x?1:(y.nickname||"").localeCompare(j.nickname||"")),[x,p]),b=o?"flex-1 min-h-0 overflow-y-auto":f?"h-28 overflow-y-auto":"h-48 overflow-y-auto";return r.jsxs("section",{className:`panel-section space-y-0.5 text-base ${o?"flex h-full min-h-0 flex-col overflow-hidden":""} ${i}`,children:[!a&&r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"grid gap-0.5 md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]",children:[r.jsx("div",{className:"min-w-0",children:r.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:r.jsx(go,{compact:f})})}),r.jsx(wg,{})]}),!h&&r.jsx("p",{className:"text-xs text-slate-500",children:"Spectators cannot set nicknames."})]}),r.jsxs("div",{className:`space-y-0.5 ${o?"flex flex-1 min-h-0 flex-col":""}`,children:[!l&&r.jsxs("div",{className:`flex items-center justify-between text-sm text-slate-400 ${f?"text-xs":""}`,children:[r.jsx("span",{children:"Users"}),r.jsx("span",{className:"text-xs text-slate-500",children:g.length})]}),r.jsx("div",{className:`surface flex flex-wrap content-start items-start gap-0.5 px-0 pb-0 ${b} ${f?"text-[0.8rem]":""}`,children:g.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"Waiting for users…"}):g.map(y=>r.jsx("span",{className:`rounded bg-slate-800/80 px-1 py-0.25 text-[0.7rem] ${BN(y.role)}`,children:LN(y,x)},y.socketId))})]})]})}const zN=/^cu_[a-f0-9]{32}$/,tx=8*1024*1024,pi="field-input w-full max-w-sm text-left focus:ring-emerald-500",Zl="mx-auto w-full max-w-xl flex justify-center",ii="mx-auto flex w-full max-w-md flex-col items-center space-y-0.5 text-center";function UN(a){const l=String(a||"").trim();return l?l.length<=10?`${l.slice(0,2)}***${l.slice(-2)}`:`${l.slice(0,6)}...${l.slice(-6)}`:""}function HN(a){let l="";for(let o=0;o{if(!a||a.iceGatheringState==="complete"){i();return}const o=setTimeout(()=>{a.removeEventListener("icegatheringstatechange",f),i()},l);function f(){a.iceGatheringState==="complete"&&(clearTimeout(o),a.removeEventListener("icegatheringstatechange",f),i())}a.addEventListener("icegatheringstatechange",f)})}function XN(a,l=4e3){return new Promise((i,o)=>{if(!a){o(new Error("Peer connection missing"));return}const f=a.connectionState;if(f==="connected"){i();return}if(f==="failed"||f==="closed"){o(new Error(`Peer connection ${f}`));return}const d=setTimeout(()=>{p(),o(new Error("Peer connection timeout"))},l),h=()=>{const x=a.connectionState;x==="connected"?(p(),i()):(x==="failed"||x==="closed")&&(p(),o(new Error(`Peer connection ${x}`)))};function p(){clearTimeout(d),a.removeEventListener("connectionstatechange",h)}a.addEventListener("connectionstatechange",h)})}function QN(a,l){if(!a||!a.length)return new Float32Array(0);if(l===io||!Number.isFinite(l)||l<=0)return a;const i=l/io,o=Math.max(1,Math.round(a.length/i)),f=new Float32Array(o);for(let d=0;dse&&i?.[se]||null,[i,se]),Y=async()=>{const ce=se;if(!ce){B("Take control of a rover first.");return}if(!j){B("Select an audio file first.");return}if(j.size>tx){B(`File too large (max ${tx} bytes).`);return}w(!0),B("");try{const oe=await j.arrayBuffer(),de=HN(new Uint8Array(oe));await o?.({roverId:ce,name:j.name,mime:j.type||"",dataBase64:de}),B(`Playing upload on ${ce}.`)}catch(oe){B(oe.message||"Failed to play upload.")}finally{w(!1)}},ne=async()=>{const ce=se;if(!ce){B("Take control of a rover first.");return}w(!0),B("");try{await f?.(ce),B(`Stopped upload on ${ce}.`)}catch(oe){B(oe.message||"Failed to stop upload.")}finally{w(!1)}},le=m.useCallback(async ce=>{const oe=String(ce||X.current||"").trim();R.current=!1,E("idle");try{L.current&&Q.current&&Q.current.disconnect(L.current)}catch{}try{L.current&&G.current&&L.current.disconnect(G.current)}catch{}try{G.current&&O.current?.destination&&G.current.disconnect(O.current.destination)}catch{}if(O.current)try{await O.current.close()}catch{}if(ue.current){try{ue.current.getSenders().forEach(de=>de.track?.stop())}catch{}try{ue.current.close()}catch{}}if(ue.current=null,L.current=null,Q.current=null,G.current=null,O.current=null,Z.current=[],ae.current=0,F.current=!1,re.current="none",U.current)try{U.current.getTracks().forEach(de=>de.stop())}catch{}if(U.current=null,oe){try{await b?.(oe)}catch{}try{await h?.(oe)}catch{}}X.current=""},[h,b]),pe=m.useCallback(async(ce,oe)=>{const de=window.AudioContext||window.webkitAudioContext;if(!de)throw new Error("Web Audio API is not supported in this browser.");await d?.(ce);const Ae=new de({latencyHint:"interactive"});O.current=Ae;const H=Ae.createMediaStreamSource(oe);Q.current=H;const P=Ae.createScriptProcessor(1024,1,1);L.current=P;const he=Ae.createGain();he.gain.value=0,G.current=he,Z.current=[],ae.current=0,re.current="socket",P.onaudioprocess=Se=>{if(!R.current||re.current!=="socket")return;const De=Se.inputBuffer?.getChannelData(0);if(!De||De.length===0)return;const ze=QN(De,Ae.sampleRate);if(!ze.length)return;const yt=KN(ze);for(Z.current.push(yt),ae.current+=yt.length;ae.current>=of;){const Mt=ZN(Z.current,ae.current),pt=Mt.slice(0,of),Et=Mt.slice(of);Z.current=Et.length?[Et]:[],ae.current=Et.length,p?.({roverId:ce,data:pt})}},H.connect(P),P.connect(he),he.connect(Ae.destination),Ae.state==="suspended"&&await Ae.resume(),E("live")},[p,d]),ie=m.useCallback(async(ce,oe)=>{const de=await x?.(ce),Ae=String(de?.whipUrl||"").trim(),H=String(de?.token||"").trim();if(!Ae||!H)throw new Error("WHIP endpoint unavailable");const P=new RTCPeerConnection($N);ue.current=P,re.current="whip",F.current=!1;try{oe.getAudioTracks().forEach(ze=>P.addTrack(ze,oe)),P.onconnectionstatechange=()=>{const ze=P.connectionState;if(R.current){if(ze==="connected"){E("live");return}if((ze==="failed"||ze==="disconnected")&&!F.current){F.current=!0;const yt=X.current;if(!yt||!U.current)return;(async()=>{try{await b?.(yt)}catch{}if(!(!R.current||re.current!=="whip"))try{await pe(yt,U.current)}catch(Mt){E("error"),B(Mt?.message||"Mic fallback failed.")}})()}}};const he=await P.createOffer({offerToReceiveAudio:!1,offerToReceiveVideo:!1});await P.setLocalDescription(he),await FN(P,1800);const Se=await fetch(Ae,{method:"POST",headers:{"Content-Type":"application/sdp",...YN(H)},body:P.localDescription?.sdp||he.sdp});if(!Se.ok)throw new Error(`WHIP request failed: ${Se.status}`);const De=await Se.text();await P.setRemoteDescription({type:"answer",sdp:De}),await XN(P,4500),await g?.(ce),E("live")}catch(he){try{P.close()}catch{}throw ue.current===P&&(ue.current=null),re.current="none",he}},[g,x,pe,b]),fe=m.useCallback(async ce=>{const oe=String(ce||"").trim();if(!oe)throw new Error("Take control of a rover first.");if(!navigator.mediaDevices?.getUserMedia)throw new Error("Microphone capture is not supported in this browser.");await le(oe),E("starting");let de=null;try{de=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:io,echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0}}),U.current=de,R.current=!0,X.current=oe;let Ae=null;try{await ie(oe,de);return}catch(H){Ae=H;try{await b?.(oe)}catch{}}await pe(oe,de),Ae&&B(`WHIP unavailable, using socket fallback: ${Ae.message||"unknown error"}`)}catch(Ae){if(de)try{de.getTracks().forEach(H=>H.stop())}catch{}throw U.current=null,O.current=null,Q.current=null,L.current=null,G.current=null,ue.current=null,re.current="none",Ae}},[pe,ie,le,b]);return m.useEffect(()=>{const ce=!!(N||me),oe=se;let de=!1;async function Ae(){if(!oe||!ce){await le(X.current||oe);return}if(!(R.current&&X.current===oe))try{await fe(oe)}catch(H){de||(E("error"),B(H?.message||"Failed to start microphone forwarding."))}}return Ae(),()=>{de=!0}},[N,me,fe,le,se]),m.useEffect(()=>()=>{le(X.current)},[le]),r.jsx("section",{className:`surface ${Zl}`,children:r.jsxs("div",{className:ii,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"VIP Audio Forwarding"}),r.jsxs("label",{className:"grid w-full gap-0.5 text-xs text-slate-300",children:[r.jsx("span",{children:"Audio file (mp3 / wav / ogg)"}),r.jsx("input",{className:pi,type:"file",accept:".mp3,.wav,.ogg,audio/mpeg,audio/wav,audio/ogg",disabled:C||!se,onChange:ce=>M(ce.target.files?.[0]||null)})]}),j?r.jsxs("div",{className:"surface-muted mx-auto w-full max-w-sm text-xs text-slate-300 text-center",children:[j.name," (",j.size," bytes)"]}):null,r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:C||!se,onClick:Y,children:C?"Working...":"Play Upload"}),r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:C||!se,onClick:ne,children:"Stop"})]}),r.jsxs("div",{className:"surface-muted mx-auto flex w-full max-w-sm flex-col gap-0.5 p-0.5 text-xs text-slate-300 text-center",children:[r.jsx("p",{className:"text-slate-200",children:"Microphone Forwarding"}),r.jsxs("label",{className:"flex items-center justify-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:N,disabled:!se,onChange:ce=>q(!!ce.target.checked)}),r.jsx("span",{children:"Open mic"})]}),r.jsxs("p",{className:"text-slate-400",children:["PTT key: ",y?.keymap?.micPtt?.[0]||"v"," (hold)"]}),r.jsxs("p",{className:"text-slate-400",children:["mic: ",A]}),r.jsxs("p",{className:"text-slate-500",children:["transport: ",re.current==="none"?"idle":re.current]})]}),k?r.jsxs("div",{className:"surface-muted mx-auto w-full max-w-sm text-xs text-slate-300 text-center",children:["state: ",k.state||"idle",k.error?` | error: ${k.error}`:""]}):null,T?r.jsx("div",{className:"text-xs text-slate-400 text-center",children:T}):null]})})}function JN({pendingRequestId:a,currentStoredKey:l,nickname:i,requestVerification:o,applyIdentityKey:f,onMessage:d}){const[h,p]=m.useState(0),[x,g]=m.useState(""),[b,y]=m.useState(!1),[j,M]=m.useState(!1),C=()=>{p(1),g(l),y(!1),d?.("")},w=()=>{p(0),y(!1)},N=async q=>{if(q.preventDefault(),!b){d?.("Please confirm your nickname agreement before sending.");return}if(h<3){d?.("Complete all request steps before sending.");return}M(!0),d?.("");try{const A=await f(x);await o?.(),g(A),p(0),y(!1),d?.("Verification request sent to lockdown admins.")}catch(A){d?.(A.message||"Failed to submit request.")}finally{M(!1)}};return a?r.jsx("section",{className:`surface text-sm text-slate-300 ${Zl}`,children:r.jsxs("div",{className:ii,children:["Verification request pending: ",a]})}):h===0?r.jsx("section",{className:`surface ${Zl}`,children:r.jsxs("div",{className:ii,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Verification"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:C,disabled:j,children:"Request Verification"})]})}):r.jsx("form",{className:`surface ${Zl}`,onSubmit:N,children:r.jsxs("div",{className:ii,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Request verification"}),r.jsxs("p",{className:"text-xs text-slate-500",children:["Step ",h," of 3"]}),h===1?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Confirm your nickname, which is used as part of the verification process. You can edit it here before requesting."}),r.jsx("div",{className:"mx-auto w-full max-w-sm",children:r.jsx(go,{compact:!0})}),r.jsxs("div",{className:"surface-muted mx-auto w-full max-w-sm text-xs text-slate-300 text-center",children:["Current nickname: ",r.jsx("span",{className:"font-semibold",children:i||"(not set)"})]}),r.jsxs("label",{className:"flex items-center justify-center gap-0.5 text-xs text-slate-300 text-center",children:[r.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:b,onChange:q=>y(q.target.checked)}),r.jsx("span",{children:"I understand this nickname is tied to my verification."})]}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!i||!b,onClick:()=>p(2),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:w,children:"Cancel"})]})]}):null,h===2?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Save your identity key in a safe place. You can use it to restore your identity in another browser."}),r.jsx("input",{className:pi,type:"password",name:"identity_key_request",autoComplete:"current-password",maxLength:35,value:x,onChange:q=>g(q.target.value.toLowerCase()),placeholder:"Identity key for this request"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!x,onClick:()=>navigator.clipboard?.writeText(x),children:"Copy Key"}),r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!x,onClick:()=>p(3),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>p(1),children:"Back"})]})]}):null,h===3?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Final step: confirm and send your verification request."}),r.jsx("input",{className:pi,type:"password",name:"identity_key_request_final",autoComplete:"current-password",maxLength:35,value:x,onChange:q=>g(q.target.value.toLowerCase()),placeholder:"Identity key for this request"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:j||!x,children:"Confirm Request"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>p(2),children:"Back"})]})]}):null]})})}function WN({currentStoredKey:a,applyIdentityKey:l,onMessage:i}){const[o,f]=m.useState(0),[d,h]=m.useState(""),[p,x]=m.useState(!1),g=()=>{f(1),h(""),i?.("")},b=()=>{f(0),h("")},y=async j=>{if(j.preventDefault(),!(o<2)){x(!0),i?.("");try{await l(d),f(0),h(""),i?.("Identity key restored.")}catch(M){i?.(M.message||"Failed to restore key.")}finally{x(!1)}}};return r.jsx("section",{className:`surface ${Zl}`,children:r.jsxs("div",{className:ii,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Identity key"}),r.jsxs("p",{className:"text-xs text-slate-500",children:["Current: ",UN(a)||"not set yet"]}),r.jsx("input",{className:pi,type:"password",name:"identity_key_current",autoComplete:"current-password",maxLength:35,value:a,readOnly:!0,placeholder:"Identity key"}),o===0?r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!a,onClick:()=>navigator.clipboard?.writeText(a),children:"Copy Key"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:g,disabled:p,children:"Restore Key"})]}):null,o===1?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Restoring your key should only be done when needed. Use this to move your identity to another browser."}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(2),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:b,children:"Cancel"})]})]}):null,o===2?r.jsxs("form",{className:"space-y-0.5",onSubmit:y,children:[r.jsx("input",{className:pi,type:"password",name:"identity_key_restore",autoComplete:"current-password",maxLength:35,value:d,onChange:j=>h(j.target.value.toLowerCase()),placeholder:"Paste key to restore"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:p||!d,children:"Confirm Restore"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(1),children:"Back"})]})]}):null]})})}function Cg(){const{session:a,identifySession:l,requestVerification:i,playUploadedAudio:o,stopUploadedAudio:f,startMicForward:d,stopMicForward:h,sendMicChunk:p,startMicWhip:x,readyMicWhip:g,stopMicWhip:b}=_e(),{value:y,save:j}=Pe("identity",{cookieUserId:""}),{value:M}=Pe("profile",{nickname:""}),C=m.useMemo(()=>(y?.cookieUserId||"").trim(),[y?.cookieUserId]),w=(M?.nickname||"").trim(),N=!!a?.isVerified,q=a?.verification?.pendingRequestId||null,A=m.useMemo(()=>a?.roster??[],[a?.roster]),E=String(a?.assignment?.roverId||"").trim(),T=m.useMemo(()=>E?A.filter(Q=>Q.id===E):[],[E,A]),[B,U]=m.useState(""),O=async Q=>{const L=String(Q||"").trim().toLowerCase();if(!L)throw new Error("Identity key required.");if(!zN.test(L))throw new Error("Identity key must match format: cu_ + 32 lowercase hex chars.");return j(G=>({...G||{},cookieUserId:L})),await l({cookieUserId:L,nickname:w}),L};return r.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[N?r.jsx(IN,{roster:T,ownRoverId:E,audioForwardByRover:a?.audioForward||{},playUploadedAudio:o,stopUploadedAudio:f,startMicForward:d,stopMicForward:h,sendMicChunk:p,startMicWhip:x,readyMicWhip:g,stopMicWhip:b}):r.jsx(JN,{pendingRequestId:q,currentStoredKey:C,nickname:w,requestVerification:i,applyIdentityKey:O,onMessage:U}),r.jsx(WN,{currentStoredKey:C,applyIdentityKey:O,onMessage:U}),B?r.jsx("div",{className:Zl,children:r.jsx("p",{className:"text-xs text-slate-400 text-center",children:B})}):null]})}function PN(){const{state:{roverId:a}}=qt(),i=ns(a)?.sensors||{};return r.jsx("section",{className:"panel-section",children:r.jsx("div",{className:"aspect-square w-full",children:r.jsx(Cf,{sensors:i})})})}function ew(){const{state:{roverId:a,keymap:l,camera:i,horn:o},pipeline:f,actions:{setServoAngle:d,setNightVision:h,startHorn:p,stopHorn:x}}=qt(),g=sg(a),b=g.docked&&!g.driving,y=i?.config,j=!!(a&&i?.enabled&&y),M=!!(a&&f?.nightVision),C=f?.nightVisionState,w=!!(a&&f?.horn),N=o?.overheated,q=typeof y?.minAngle=="number"?y.minAngle:-30,A=typeof y?.maxAngle=="number"?y.maxAngle:30,E=typeof i?.angle=="number"?i.angle:typeof y?.homeAngle=="number"?y.homeAngle:(q+A)/2,T=Nn(l?.nightVisionToggle?.[0]),B=Nn(l?.hornHonk?.[0]),U=Nn(l?.cameraUp?.[0]),O=Nn(l?.cameraDown?.[0]);return r.jsxs("section",{className:"panel-section grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] gap-0.5",children:[r.jsx(ig,{layout:"desktop",expand:!0,driveDockState:g}),b?null:r.jsxs("div",{className:"surface space-y-0.5 p-0 text-sm text-slate-200",children:[M&&r.jsx(rg,{nightVisionOn:C?.nightVisionOn,disabled:!a,onToggle:h,keyLabel:T}),w&&r.jsx(og,{disabled:!a||N,onStart:p,onStop:x,keyLabel:B,active:o?.active,heat:o?.heat}),j&&r.jsx(cg,{value:E,min:q,max:A,onChange:d,keyDownLabel:O,keyUpLabel:U,className:"space-y-0.5 px-1 py-1",labelRowClass:"text-xs text-slate-300",labelClass:"",valueClass:"font-mono text-slate-100",sliderClass:"w-full",accentClass:"accent-emerald-400",endpointClass:"text-[0.7rem] text-slate-400"})]})]})}function tw({layout:a,onOpenHelpOverlay:l}){const{session:i}=_e(),o=i?.isVerified?"bg-emerald-400":"bg-red-600";return r.jsx("section",{className:"panel text-base",children:r.jsxs(sd,{defaultTab:"telemetry",children:[r.jsxs(id,{children:[r.jsx(Xt,{id:"telemetry",children:"Controls"}),r.jsx(Xt,{id:"vip",children:r.jsxs("span",{className:"inline-flex items-center gap-2",children:[r.jsx("span",{children:"VIP"}),r.jsx("span",{className:`inline-block h-3 w-3 rounded-full ${o}`,"aria-hidden":"true",title:i?.isVerified?"Verified":"Not verified"})]})}),r.jsx(Xt,{id:"help",children:"Help"}),r.jsx(Xt,{id:"settings",children:"Settings"})]}),r.jsxs(rd,{children:[r.jsx(Qt,{id:"telemetry",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]",children:[r.jsx(PN,{}),r.jsx(ew,{})]}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]",children:[r.jsx(vo,{}),r.jsx($f,{panelId:"replay-sources-desktop",fillHeight:!0}),r.jsx(RN,{})]}),r.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)] h-[14rem]",children:[r.jsx(xo,{fillHeight:!0}),r.jsxs("div",{className:"grid min-h-0 gap-0.5 grid-rows-[minmax(0,1fr)_auto]",children:[r.jsx(so,{compact:!0,hideNicknameForm:!0,fillHeight:!0}),r.jsx(MN,{compact:!0})]})]}),r.jsx(xg,{}),r.jsx(If,{defaultOrientation:"horizontal",panelId:"rightpane-telemetry"})]})}),r.jsx(Qt,{id:"vip",children:r.jsx(Cg,{})}),r.jsx(Qt,{id:"help",children:r.jsx(Ng,{layout:a,onOpenOverlay:l})}),r.jsx(Qt,{id:"settings",children:r.jsx(Sg,{})})]})]})})}const nw=new Set(["admin","lockdown","lockdown-admin"]),aw=new Set(["lockdown","lockdown-admin"]),lw=new Set(["admin","lockdown"]);function sw(a="admin"){return a==="lockdown"?{title:"Lockdown mode active",description:"Only the server owners can access the interface at this time."}:{title:"Admin mode active"}}function iw(){const{session:a}=_e(),l=a?.mode,i=a?.role,o=a?.adminReason?.text||"";a?.adminReason?.updatedAt;const f=a?.timezone||"UTC",d=a?.socials?.find(j=>String(j?.id||j?.label||"").toLowerCase()==="discord")?.url||a?.discord?.invite||null,h=lw.has(l),p=l==="lockdown"?aw.has(i):nw.has(i),[x,g]=m.useState(()=>new Date),b=m.useMemo(()=>{try{return new Intl.DateTimeFormat("en-US",{timeZone:f,hour:"numeric",minute:"2-digit",second:"2-digit"}).format(x)}catch{return x.toLocaleTimeString()}},[x,f]);if(m.useEffect(()=>{const j=setInterval(()=>g(new Date),1e3);return()=>clearInterval(j)},[]),!h||p)return null;const y=sw(l);return r.jsx("div",{className:"pointer-events-auto fixed inset-0 z-50 flex items-center justify-center bg-black/85 px-0.5 py-0.5",children:r.jsxs("div",{className:"surface w-full max-w-md space-y-0.5 text-slate-100 shadow-2xl",children:[r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-lg font-semibold",children:y.title}),r.jsx("p",{className:"text-sm text-slate-300",children:y.description})]}),r.jsxs("div",{className:"surface-muted space-y-0.5",children:[r.jsx("p",{className:"text-[0.7rem] tracking-wide text-slate-400",children:"Reason for locking:"}),r.jsx("p",{className:"text-lg font-semibold text-slate-100",children:o||"No reason set."}),r.jsxs("p",{className:"text-center text-sm text-slate-300",children:["Server time: ",b]})]}),r.jsx("div",{className:"surface-muted",children:r.jsx(gg,{})}),r.jsx("div",{className:"w-full justify-center items-center",children:r.jsx(ed,{id:"discord",label:"Join our Discord server for updates!",url:d})}),"You can still use the chat while the server is locked:",r.jsxs("div",{className:"max-h-80 overflow-y-auto",children:[r.jsx(xo,{}),r.jsx(go,{})]})]})})}const rw="/assets/turn_alert-DVO8EZJa.mp3";function ow(a,l=1){const i=m.useRef(null);return m.useEffect(()=>{i.current=new Audio(a),i.current.volume=l,i.current.load()},[a,l]),()=>{i.current&&(i.current.volume=l,i.current.currentTime=0,i.current.play().catch(()=>{}))}}function cw(){const{session:a,pushAlert:l}=_e(),{value:i}=Pe("audio",wt),o=Number.isFinite(i?.masterVolume)?i.masterVolume:wt.masterVolume,f=Number.isFinite(i?.alertVolume)?i.alertVolume:wt.alertVolume,d=Math.max(0,Math.min(1,o*f)),h=ow(rw,d),p=m.useRef(new Set),x=m.useMemo(()=>a?.turnQueues||{},[a?.turnQueues]),g=a?.socketId||null;return m.useEffect(()=>{if(!g)return;const b=[];Object.entries(x).forEach(([y,j])=>{!j||!j.current||j.current!==g||p.current.has(y)||(b.push(y),p.current.add(y))}),Object.keys(x).forEach(y=>{(!x[y]||x[y].current!==g)&&p.current.delete(y)}),b.length!==0&&(b.forEach(y=>{const j=a?.roster?.find(M=>String(M.id)===String(y))?.name||y;l({title:"Your turn!",message:`You now control ${j}.`,color:"#ff5722"})}),h())},[x,h,l,a?.roster,g]),null}function uw({visible:a,mode:l,onEnterFullscreen:i,onDismiss:o}){if(!a)return null;const f=l==="pwa-hint";return r.jsx("div",{className:"fixed inset-0 z-40 flex items-end justify-center p-2 pointer-events-none sm:items-center",children:r.jsx("div",{className:"pointer-events-auto w-full max-w-sm rounded-lg border border-cyan-500/40 bg-zinc-950/95 shadow-xl",children:r.jsxs("div",{className:"space-y-0.5 p-4 text-sm text-slate-100",children:[r.jsx("h2",{className:"text-base font-semibold text-white",children:"Better in fullscreen"}),f?r.jsxs("p",{className:"text-slate-300",children:["For fullscreen on iOS, open Safari's share menu and pick ",r.jsx("strong",{children:"Add to Home Screen"}),". Launching from the home screen removes the browser chrome."]}):r.jsx("p",{className:"text-slate-300",children:"Enable fullscreen to free up more space for the video feed and controls. You can exit fullscreen at any time via the system back or home gesture."}),r.jsxs("div",{className:"flex justify-end gap-0.5 pt-1 text-sm",children:[r.jsx("button",{type:"button",className:"rounded border border-slate-600 px-3 py-1 text-slate-200",onClick:o,children:f?"Got it":"Not now"}),!f&&r.jsx("button",{type:"button",className:"rounded bg-cyan-500 px-3 py-1 font-semibold text-black hover:bg-cyan-400",onClick:i,children:"Enter fullscreen"})]})]})})})}const Eg="fullscreenPromptDismissed",fw=new Set(["mobile-portrait","mobile-landscape"]),kg=()=>{if(typeof window>"u")return null;try{return window.sessionStorage}catch{return null}},dw=()=>{const a=kg();if(!a)return!1;try{return a.getItem(Eg)==="1"}catch{return!1}},nx=()=>{const a=kg();if(a)try{a.setItem(Eg,"1")}catch{}},mw=()=>{if(typeof navigator>"u")return!1;const a=navigator.userAgent||navigator.platform||"";return/iphone|ipad|ipod/i.test(a)},hw=()=>{if(typeof document>"u")return!1;const a=document;return!!(a.fullscreenEnabled||a.webkitFullscreenEnabled||a.mozFullScreenEnabled||a.msFullscreenEnabled)};function pw(a){const l=fw.has(a),[i,o]=m.useState(!1),[f,d]=m.useState("native"),h=m.useMemo(()=>mw(),[]),p=m.useCallback(()=>{if(!l){o(!1);return}if(dw()){o(!1);return}if(h){d("pwa-hint"),o(!0);return}if(!hw()){o(!1);return}if(typeof document<"u"&&document.fullscreenElement){o(!1);return}d("native"),o(!0)},[h,l]);m.useEffect(()=>{p()},[p]),m.useEffect(()=>{if(typeof document>"u")return;const b=()=>{if(document.fullscreenElement){o(!1);return}p()};return document.addEventListener("fullscreenchange",b),document.addEventListener("webkitfullscreenchange",b),()=>{document.removeEventListener("fullscreenchange",b),document.removeEventListener("webkitfullscreenchange",b)}},[p]);const x=m.useCallback(()=>{nx(),o(!1)},[]),g=m.useCallback(async()=>{if(!l||h||typeof document>"u")return!1;const b=document.documentElement;if(!b)return!1;const y=b.requestFullscreen||b.webkitRequestFullscreen||b.mozRequestFullScreen||b.msRequestFullscreen;if(!y)return!1;try{const j=y.call(b);return j&&typeof j.then=="function"&&await j,nx(),o(!1),!0}catch(j){return console.warn("Failed to enter fullscreen",j),!1}},[h,l]);return m.useEffect(()=>{l||o(!1)},[l]),{visible:i,mode:f,enterFullscreen:g,dismiss:x}}function xw({visible:a,layout:l,onClose:i,showOnLoad:o,onToggleShowOnLoad:f}){if(!a)return null;const{state:d}=qt(),h=p=>{const x=!p.target.checked;f?.(x)};return r.jsx("div",{className:"fixed inset-0 z-30 flex items-center justify-center bg-black/80 px-0.5 py-0.5",children:r.jsxs("div",{className:"pointer-events-auto surface w-full max-w-4xl max-h-[90vh] overflow-hidden shadow-2xl",children:[r.jsxs("div",{className:"flex items-center justify-between border-b border-slate-700 px-0.5 py-0.25 text-sm text-slate-200",children:[r.jsx("span",{className:"font-semibold",children:"Help & controls"}),r.jsxs("div",{className:"flex items-center gap-0.5 text-[0.8rem] text-slate-300",children:[r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!o,onChange:h,className:"accent-cyan-500"}),r.jsx("span",{children:"Don't show again"})]}),r.jsx("button",{type:"button",onClick:i,className:"button-dark px-2 py-0.25 text-[0.8rem]",children:"Close"})]})]}),r.jsx("div",{className:"max-h-[85vh] overflow-y-auto p-0.5",children:r.jsx(jg,{layout:l,keymap:d?.keymap||{}})})]})})}const gw=`aroma bagel batter beans @@ -122,4 +122,4 @@ wine wok yeast yogurt -`;function gw(a){return a?a.charAt(0).toUpperCase()+a.slice(1):""}function vw(){const a=String(xw).split(/\r?\n/).map(i=>i.trim()).filter(Boolean);return a.length?(a[Math.floor(Math.random()*a.length)]||"").trim():""}function od(){const{setNickname:a}=_e(),{value:l,status:i,save:o}=Pe("profile",{nickname:""}),f=m.useRef(!1);m.useEffect(()=>{if(i!=="ready"||f.current||(l.nickname||"").trim())return;f.current=!0;const h=gw(vw()).slice(0,32);if(!h)return;(async()=>{try{await a(h)}catch{}finally{o({nickname:h})}})()},[o,a,i,l.nickname])}function yw(){const a=kn(),{connected:l,identifySession:i}=_e(),{value:o,status:f,save:d}=Pe("identity",{cookieUserId:""}),{value:h,status:p}=Pe("profile",{nickname:""}),x=m.useRef(!1),g=m.useRef(null),b=m.useRef(null),y=f==="ready"&&p==="ready",j=(o?.cookieUserId||"").trim(),M=(h?.nickname||"").trim(),C=m.useCallback(()=>{b.current&&(clearTimeout(b.current),b.current=null)},[]),w=m.useCallback(async()=>{if(!(!y||!l||!a?.id||x.current)){x.current=!0;try{const q=((await i({cookieUserId:j,nickname:M}))?.cookieUserId||"").trim();q&&q!==j&&d(A=>({...A||{},cookieUserId:q})),g.current=a.id,C()}catch{C(),b.current=setTimeout(()=>{w()},2e3)}finally{x.current=!1}}},[C,l,j,i,M,y,d,a?.id]);m.useEffect(()=>{!y||!l||!a?.id||g.current!==a.id&&w()},[l,y,w,a?.id]),m.useEffect(()=>{!y||!l||!a?.id||w()},[y,l,a?.id,j,M,w]),m.useEffect(()=>{const N=()=>{a?.connected&&w()},q=()=>{typeof document>"u"||document.visibilityState!=="visible"||a?.connected&&w()};return window.addEventListener("online",N),document.addEventListener("visibilitychange",q),()=>{window.removeEventListener("online",N),document.removeEventListener("visibilitychange",q)}},[w,a?.connected]),m.useEffect(()=>()=>C(),[C])}const cf=1e4,uf=28,bw=14;function ro({layout:a="desktop",className:l="",dismissable:i=!0}){const{session:o}=_e(),f=o?.communityGoal?.text?String(o.communityGoal.text).trim():"",d=a==="mobile-portrait"||a==="mobile-landscape"||a==="mobile",[h,p]=m.useState(!1),[x,g]=m.useState(uf),[b,y]=m.useState(0),j=m.useRef(null),M=m.useRef(null),C=m.useRef(null);m.useEffect(()=>{if(!f){p(!1);return}if(p(!0),!d||!i)return;const A=Date.now();y(cf);const E=setTimeout(()=>p(!1),cf),T=setInterval(()=>{const B=Date.now()-A;y(Math.max(0,cf-B))},250);return()=>{clearTimeout(E),clearInterval(T)}},[f,d]),m.useEffect(()=>{if(!f)return;g(uf);let A=0;const E=()=>{const B=C.current,U=M.current;if(!B||!U)return;const O=B.clientWidth;if(!O)return;const Q=U.scrollWidth;if(!Q)return;const L=Math.min(1,O/Q),G=Math.max(bw,Math.floor(uf*L));g(G)};A=window.requestAnimationFrame(E);const T=new ResizeObserver(()=>{window.cancelAnimationFrame(A),A=window.requestAnimationFrame(E)});return j.current&&T.observe(j.current),()=>{window.cancelAnimationFrame(A),T.disconnect()}},[f]);const w=m.useMemo(()=>["panel-section flex w-full items-center justify-center",d?"rounded-none":"rounded","px-1 py-1 text-center font-semibold tracking-tight",l].filter(Boolean).join(" "),[l,d]);if(!f||!h)return null;const N=d?Math.ceil(b/1e3):null,q=i?{onClick:()=>p(!1),role:"button",tabIndex:0}:{};return r.jsx("div",{ref:j,className:w,style:{fontSize:`${x}px`,lineHeight:1.1},...q,children:r.jsxs("span",{className:"flex w-full items-stretch gap-0.5 whitespace-nowrap rounded-md",children:[r.jsxs("span",{className:"flex flex-col justify-center border-r border-slate-700/60 px-0.5 text-[0.55em] font-semibold leading-tight text-slate-400",children:[r.jsx("span",{children:"Community"}),r.jsx("span",{children:"Goal"})]}),r.jsx("span",{ref:C,className:"flex-1 overflow-hidden text-slate-100",children:r.jsx("span",{ref:M,className:"block",children:f})}),d?r.jsxs("span",{className:"flex items-center border-l border-slate-700/60 px-0.5 text-[0.55em] font-semibold uppercase tracking-wide text-slate-400",children:[N,"s"]}):null]})})}function Sw(){const[a,l]=m.useState(()=>typeof window>"u"||window.innerWidth>=1024?"desktop":window.innerWidth>window.innerHeight?"mobile-landscape":"mobile-portrait");return m.useEffect(()=>{function i(){if(typeof window>"u")return;const{innerWidth:o,innerHeight:f}=window;o>=1024?l("desktop"):o>f?l("mobile-landscape"):l("mobile-portrait")}return i(),window.addEventListener("resize",i),()=>window.removeEventListener("resize",i)},[]),a}function Aw({layout:a,onOpenHelpOverlay:l}){return r.jsxs("div",{className:"flex h-full gap-0.5 overflow-hidden",children:[r.jsxs("div",{className:"flex min-w-0 flex-[1.22] flex-col gap-0.5 overflow-y-auto pr-0",children:[r.jsx(td,{}),r.jsx(kS,{}),r.jsx(Jf,{})]}),r.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-0.5 overflow-y-auto",children:[r.jsx(ro,{layout:a}),r.jsx(ew,{layout:a,onOpenHelpOverlay:l})]})]})}function qg({layout:a,onOpenHelpOverlay:l,roomPanelId:i,showTelemetry:o=!0}){const{session:f}=_e(),d=f?.isVerified?"bg-emerald-400":"bg-amber-400";return r.jsx("section",{className:"panel text-base",children:r.jsxs(sd,{defaultTab:"chat",children:[r.jsxs(id,{children:[r.jsx(Xt,{id:"chat",children:"Chat"}),r.jsx(Xt,{id:"vip",children:r.jsxs("span",{className:"inline-flex items-center gap-0.5",children:[r.jsx("span",{children:"VIP"}),r.jsx("span",{className:`inline-block h-1.5 w-1.5 rounded-full ${d}`,"aria-hidden":"true",title:f?.isVerified?"Verified":"Not verified"})]})}),r.jsx(Xt,{id:"roomcontrols",children:"Room Controls"}),r.jsx(Xt,{id:"help",children:"Help"}),r.jsx(Xt,{id:"settings",children:"Settings"})]}),r.jsxs(rd,{children:[r.jsx(Qt,{id:"chat",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(xo,{}),r.jsx(so,{})]})}),r.jsx(Qt,{id:"vip",children:r.jsx(Cg,{})}),r.jsx(Qt,{id:"roomcontrols",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(xg,{}),r.jsx(If,{panelId:i})]})}),r.jsx(Qt,{id:"help",children:r.jsx(Ng,{layout:a,onOpenOverlay:l})}),r.jsx(Qt,{id:"settings",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(Sg,{}),r.jsx(Jf,{})]})})]})]})})}function jw({onOpenHelpOverlay:a}){return r.jsxs("div",{className:"flex flex-col gap-0.5",children:[r.jsx(td,{layoutFormat:"mobile-portrait"}),r.jsx(tj,{}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]",children:[r.jsx($f,{panelId:"replay-sources-mobile-portrait"}),r.jsx(vo,{})]}),r.jsx(qg,{layout:"mobile-portrait",onOpenHelpOverlay:a,roomPanelId:"mobile-portrait-room"})]})}function Nw({onOpenHelpOverlay:a}){const l="self-start h-[min(100svh,32rem)]";return r.jsxs("div",{className:"flex flex-col gap-0.5",children:[r.jsxs("section",{className:"grid min-h-screen grid-cols-[minmax(0,0.7fr)_minmax(0,2.1fr)_minmax(0,0.7fr)] gap-0.5",children:[r.jsx(ug,{layout:"landscape",className:l}),r.jsxs("div",{children:[r.jsx(td,{layoutFormat:"mobile-landscape"}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]",children:[r.jsx($f,{panelId:"replay-sources-mobile-landscape"}),r.jsx(vo,{})]})]}),r.jsx(fg,{layout:"landscape",className:l})]}),r.jsx("div",{className:"flex flex-col gap-0.5 pb-0",children:r.jsx(qg,{layout:"mobile-landscape",onOpenHelpOverlay:a,roomPanelId:"mobile-landscape-room",showTelemetry:!1})})]})}function ww(){const a=Sw(),l=a==="desktop",i=hw(a);return r.jsx("div",{className:`bg-black text-slate-100 ${l?"h-screen overflow-hidden":"min-h-screen"}`,children:r.jsx(Cw,{layout:a,isDesktop:l,fullscreen:i})})}function Cw({layout:a,isDesktop:l,fullscreen:i}){od(),yw();const{visible:o,mode:f,enterFullscreen:d,dismiss:h}=i,{value:p,status:x,save:g}=Pe("help",{showOnLoad:!0}),[b,y]=m.useState(!1);m.useEffect(()=>{x==="ready"&&y(p?.showOnLoad!==!1)},[x,p?.showOnLoad]);const j=m.useCallback(()=>y(!0),[]),M=m.useCallback(()=>y(!1),[]),C=m.useCallback(N=>{const q=!!N;g(A=>({...A??{},showOnLoad:q})),q||y(!1)},[g]),w=m.useMemo(()=>l?r.jsx(Aw,{layout:a,onOpenHelpOverlay:j}):a==="mobile-landscape"?r.jsx(Nw,{onOpenHelpOverlay:j}):r.jsx(jw,{onOpenHelpOverlay:j}),[l,a,j]);return r.jsxs(vA,{children:[r.jsx(qA,{}),r.jsx(FA,{}),r.jsxs("main",{className:`flex w-full flex-col gap-0.5 text-base ${l?"h-full overflow-hidden":""}`,children:[l?null:r.jsx(ro,{layout:a}),w]}),r.jsx(Ff,{}),r.jsx(ow,{}),r.jsx(sw,{}),r.jsx(pw,{visible:b,layout:a,onClose:M,showOnLoad:p?.showOnLoad!==!1,onToggleShowOnLoad:C}),r.jsx(cw,{visible:o,mode:f,onEnterFullscreen:d,onDismiss:h})]})}function Mg(){const{session:a,setRole:l,subscribeAll:i,connected:o}=_e(),[f,d]=m.useState(!1);return m.useEffect(()=>{let h=!1;async function p(){if(a?.mode==="lockdown"){d(!1);return}try{a?.role!=="spectator"&&await l("spectator"),await i(),h||d(!0)}catch(x){console.error("Failed to enter spectator mode",x),h||d(!1)}}return p(),()=>{h=!0}},[o,a?.mode,a?.role,l,i]),f}function Ew(){const[a,l]=m.useState(()=>typeof window>"u"?!1:window.matchMedia("(max-aspect-ratio: 4/3)").matches);return m.useEffect(()=>{if(typeof window>"u")return;const i=window.matchMedia("(max-aspect-ratio: 4/3)"),o=f=>l(f.matches);return i.addEventListener?i.addEventListener("change",o):i.addListener(o),()=>{i.removeEventListener?i.removeEventListener("change",o):i.removeListener(o)}},[]),a}function kw({roverId:a,session:l}){const i=l?.activeDrivers?.[a]||null,f=(l?.users||[]).find(x=>x.socketId===i)?.nickname||(i?i.slice(0,6):"No driver"),d=l?.mode,h=l?.turnQueues?.[a];return d==="turns"&&h?.current?`${f} (turns)`:f}function qw({rover:a,frame:l,sessionInfo:i,videoMode:o,snapshotFeed:f,audioInfo:d,session:h}){const p=kw({roverId:a.id,session:h});return r.jsx("article",{className:"min-h-[16rem] rounded bg-zinc-900 p-0 sm:min-h-[18rem]",children:r.jsx("div",{className:"min-h-0 overflow-hidden rounded bg-black/20",children:r.jsx(di,{sessionInfo:i,videoMode:o,snapshotFeed:f,audioSessionInfo:d,label:a.name,telemetryFrame:l,batteryConfig:a.battery,hudVariant:"spectator",driverLabel:p,hudForceMap:!0,hudMapPosition:"top-center"})})})}function Mw({roster:a,frames:l,videoSources:i,snapshotFeeds:o,audioSources:f,session:d,canSpectateVideo:h}){return a.length===0?r.jsx("p",{className:"col-span-full text-slate-400",children:"No rovers registered."}):r.jsx("section",{className:"grid grid-cols-1 gap-0.5 md:grid-cols-2",children:a.map(p=>r.jsx(qw,{rover:p,frame:l[p.id],sessionInfo:h&&i[p.id]||null,videoMode:h?"whep":"snapshot",snapshotFeed:h?null:o[p.id],audioInfo:f[`${p.id}-audio`],session:d,showHudMap:!0,hudMapPosition:"bottom-left"},p.id))})}function Rw(){return r.jsx("section",{className:"min-h-0",children:r.jsx("div",{className:"surface min-h-[14rem] overflow-hidden",children:r.jsx(If,{defaultOrientation:"horizontal",hideLayoutToggle:!0,hideHeader:!0,panelId:"spectator-secondary"})})})}function Tw({className:a=""}){return r.jsx("div",{className:`panel ${a}`,children:r.jsx(Jf,{})})}function Dw(){const{session:a}=_e(),l=a?.mode==="lockdown",i=!!a?.isLocalNetwork;od(),Mg();const o=Ew(),f=Vf(),d=a?.roster??[],h=Wf(d.map(N=>N.id),{enabled:!l&&!i,version:a?.mode}),p=i?d.map(N=>({type:"rover",id:N.id,key:N.id})):[],x=ui(p,{enabled:!l&&i,version:a?.mode}),g=d.flatMap(N=>N.media?.audioPublishUrl?[{type:"rover",id:`${N.id}-audio`,key:`${N.id}-audio`}]:[]),b=ui(g,{enabled:!l,version:a?.mode});if(l)return r.jsx("div",{className:"flex min-h-screen items-center justify-center bg-black text-slate-200",children:r.jsxs("div",{className:"surface max-w-md space-y-0.5 p-1 text-center text-sm",children:[r.jsx("p",{className:"text-lg font-semibold text-white",children:"Spectate disabled during lockdown."}),r.jsx("p",{className:"text-slate-300",children:"Please wait until the server leaves lockdown to view streams."})]})});const y=o?"flex min-h-screen flex-col bg-black text-slate-100 md:h-screen md:overflow-hidden":"grid min-h-screen grid-cols-1 gap-0.5 bg-black text-slate-100 md:h-full md:min-h-0 md:grid-cols-[minmax(0,1fr)_18rem] lg:grid-cols-[minmax(0,1fr)_20rem]",j=o?"order-2 flex min-h-0 min-w-0 flex-1 flex-col gap-0.5 overflow-y-auto":"order-1 flex min-h-0 min-w-0 flex-col gap-0.5 md:overflow-y-auto",M=o?"order-1 grid w-full min-h-0 items-stretch gap-0.5 border-b border-slate-800/60 bg-slate-950/90 p-0.5 grid-cols-[minmax(0,1fr)_minmax(0,0.7fr)_minmax(0,1.9fr)_minmax(0,0.7fr)]":"order-2 flex min-h-0 min-w-0 flex-col gap-0.5 border-l border-slate-800/60 bg-slate-950/90 md:h-full md:overflow-y-auto",C="",w=o?"h-64":"";return r.jsxs("div",{className:"min-h-screen bg-black text-slate-100 md:h-screen md:overflow-hidden",children:[r.jsxs("main",{className:y,children:[r.jsxs("section",{className:M,children:[o?r.jsxs("div",{className:`${C} ${w} flex flex-col gap-0.5`,children:[r.jsx(ro,{layout:"desktop",dismissable:!1,className:"text-sm"}),r.jsx("div",{className:"min-h-0 flex-1",children:r.jsx(so,{hideNicknameForm:!0,hideHeader:!0,compact:!0,fillHeight:!0,className:"h-full"})})]}):r.jsx("div",{className:C,children:r.jsx(ro,{layout:"desktop",dismissable:!1,className:"text-sm"})}),r.jsx("div",{className:`${C} ${o?`${w} overflow-y-auto`:""}`,children:r.jsx("div",{className:"panel h-full",children:r.jsx(vo,{title:"Rovers"})})}),o?null:r.jsx("div",{className:`${C} flex-1 min-h-0`,children:r.jsx(so,{hideNicknameForm:!0,hideHeader:!0,compact:!0,fillHeight:!0,className:"h-full"})}),r.jsx("div",{className:`${C} ${o?w:"flex-[1.1] min-h-0"}`,children:r.jsx(xo,{allowSpectatorInput:!0,hideSpectatorNotice:!0,fillHeight:!0})}),r.jsx("div",{className:`${C} ${o?w:""}`,children:r.jsx(Tw,{className:`${o?"h-full":"h-40"} overflow-hidden`})})]}),r.jsxs("section",{className:j,children:[r.jsx(Mw,{roster:d,frames:f,videoSources:x,snapshotFeeds:h,audioSources:b,session:a,canSpectateVideo:i}),r.jsx(Rw,{})]})]}),r.jsx(Ff,{})]})}function _w(){return r.jsx(Dw,{})}const Ow=2e4,Bw=3600*1e3;function Lw({roverId:a,session:l}){const i=l?.activeDrivers?.[a]||null,f=(l?.users||[]).find(p=>p.socketId===i)?.nickname||(i?i.slice(0,6):"No driver"),d=l?.mode,h=l?.turnQueues?.[a];return d==="turns"&&h?.current?`${f}`:f}function zw({rover:a,frame:l}){const i=l?.sensors?.batteryChargeMah??null,o=a?.battery??null;return pg({batteryState:a?.batteryState??null,charge:i,config:o})}function ax({rover:a,frame:l,driverLabel:i,sessionInfo:o,videoMode:f="snapshot",snapshotFeed:d,withDivider:h=!1,showPreview:p=!0,variant:x="stacked"}){const g=zw({rover:a,frame:l}),b=g?.available?g.percentDisplay:null,y=x==="active";return r.jsxs("div",{className:`relative flex min-w-0 flex-1 flex-col gap-4 overflow-hidden bg-black px-0 py-0 ${h?"border-r border-slate-700/60":""}`,children:[r.jsx(Ef,{visual:g,orientation:y?"vertical":"horizontal",variant:"background"}),y?r.jsxs("div",{className:"relative z-10 flex min-w-0 flex-1 flex-col justify-between text-center",children:[r.jsx("div",{className:"min-w-0 bg-transparent px-0 py-0 leading-none",children:r.jsx(Xl,{className:"font-semibold leading-none text-white",maxSize:1e3,minSize:18,children:a.name||a.id})}),i?r.jsx("div",{className:"min-w-0 bg-transparent px-0 py-0 leading-none",children:r.jsx(Xl,{className:"font-semibold leading-none text-white",maxSize:1e3,minSize:16,children:i})}):r.jsx("div",{}),r.jsx("div",{className:"relative min-w-0 overflow-hidden bg-transparent px-0 py-0 leading-none",children:r.jsx("div",{className:"relative",children:r.jsx(Xl,{className:"font-semibold leading-none text-white",maxSize:1e3,minSize:16,children:b==null?"--%":`${b}%`})})})]}):r.jsxs(r.Fragment,{children:[r.jsxs("div",{className:"relative z-10 flex min-w-0 flex-col gap-1 text-center",children:[r.jsx("div",{className:"min-w-0 bg-transparent px-0 py-0 leading-none",children:r.jsx(Xl,{className:"font-semibold leading-none text-white",maxSize:1e3,minSize:18,children:a.name||a.id})}),i?r.jsx("div",{className:"min-w-0 bg-transparent px-0 py-0 leading-none",children:r.jsx(Xl,{className:"font-semibold leading-none text-white",maxSize:1e3,minSize:16,children:i})}):null,r.jsx("div",{className:"relative min-w-0 overflow-hidden bg-transparent px-0 py-0 leading-none",children:r.jsx("div",{className:"relative",children:r.jsx(Xl,{className:"font-semibold leading-none text-white",maxSize:80,minSize:16,children:b==null?"--%":`${b}%`})})})]}),r.jsx("div",{className:"relative z-10 flex min-w-0 flex-1 flex-col gap-4",children:r.jsx("div",{className:"flex w-full min-w-0 flex-1 flex-col items-center gap-4",children:p?r.jsx("div",{className:"mt-auto w-full",children:r.jsx("div",{className:"w-full aspect-[4/3]",children:r.jsx(di,{sessionInfo:o,videoMode:f,snapshotFeed:d,audioSessionInfo:null,label:a.name||a.id,telemetryFrame:l,batteryConfig:a.battery,layoutFormat:"mobile",hudVariant:"none",fitParent:!0})})}):null})})]})]})}function Xl({children:a,className:l="",maxSize:i=1e3,minSize:o=14}){const f=m.useRef(null),d=m.useRef(null),[h,p]=m.useState(i);return m.useLayoutEffect(()=>{const x=f.current,g=d.current;if(!x||!g)return;let b=null;const y=()=>{const C=x.clientWidth;if(!C){j();return}let w=o,N=i,q=o;for(;w<=N;){const A=Math.floor((w+N)/2);g.style.fontSize=`${A}px`,g.scrollWidth<=C?(q=A,w=A+1):N=A-1}p(q)},j=()=>{b&&cancelAnimationFrame(b),b=requestAnimationFrame(y)};j();const M=new ResizeObserver(j);return M.observe(x),()=>{b&&cancelAnimationFrame(b),M.disconnect()}},[a,i,o]),r.jsx("div",{ref:f,className:"w-full min-w-0",children:r.jsx("div",{ref:d,className:`whitespace-nowrap ${l}`,style:{fontSize:`${h}px`,lineHeight:1.1},children:a})})}function Uw(){const{session:a}=_e(),l=Mg();od();const i=a?.mode==="lockdown",o=!!a?.isLocalNetwork,f=Vf(),d=a?.roster??[],[h,p]=m.useState(0),x=a?.activeDrivers||{},g=m.useMemo(()=>d.filter(G=>x[G.id]),[d,x]),b=g.length?g:d,y=Wf(b.map(G=>G.id),{enabled:!i&&!o,version:a?.mode}),j=m.useMemo(()=>o?b.map(G=>({type:"rover",id:G.id,key:G.id})):[],[o,b]),M=ui(j,{enabled:!i&&o,version:a?.mode}),C=m.useMemo(()=>g.flatMap(G=>{if(!G?.id||!G.media?.audioPublishUrl)return[];const Z=String(G.id);return[{type:"rover",id:`${Z}-audio`,key:`${Z}-audio`}]}),[g]),w=ui(C,{enabled:!i,version:a?.mode}),N=m.useMemo(()=>{if(!g.length)return[];if(!o){const Z=g.filter(ae=>y[ae.id]?.objectUrl);return Z.length?Z:g}const G=g.filter(Z=>{const ae=M[Z.id];return ae?.url&&!ae?.error});return G.length?G:g},[g,y,M,o]),q=m.useMemo(()=>N.map(G=>({type:"rover",rover:G})),[N]),A=m.useMemo(()=>q.map(G=>`r:${G.rover.id}`).join("|"),[q]);m.useEffect(()=>{p(0)},[A]),m.useEffect(()=>{if(!q.length)return;const G=setInterval(()=>{p(Z=>(Z+1)%q.length)},Ow);return()=>clearInterval(G)},[q.length,A]);const E=q.length?q[h%q.length]:null,T=E?.type==="rover"?E.rover:null,B=!o&&T&&y[T.id]||null,U=o&&T&&M[T.id]||null,O=T&&w[`${T.id}-audio`]||null,Q=T&&f[T.id]||null,L=T?Lw({roverId:T.id,session:a}):null;return i?r.jsx("div",{className:"relative flex h-screen w-screen items-center justify-center bg-black text-slate-200",children:r.jsxs("div",{className:"surface max-w-sm space-y-0.5 p-1 text-center text-sm",children:[r.jsx("p",{className:"text-lg font-semibold text-white",children:"Mini spectator is disabled in lockdown."}),r.jsx("p",{className:"text-slate-300",children:"It will automatically resume once lockdown ends."})]})}):g.length?r.jsxs("div",{className:"relative flex h-screen w-screen overflow-hidden bg-black text-slate-100",children:[r.jsx("section",{className:"relative flex h-full shrink-0 items-center justify-start overflow-hidden bg-black",style:{width:"min(100vw, calc(100vh * 4 / 3))"},children:l?T?r.jsx(Vw,{children:o?r.jsx("div",{className:"relative h-full w-full",children:q.map(G=>{const Z=G.rover,ae=T?.id===Z.id;return r.jsx("div",{className:`absolute inset-0 ${ae?"opacity-100":"opacity-0 pointer-events-none"}`,children:r.jsx(di,{sessionInfo:M[Z.id]||null,videoMode:"whep",snapshotFeed:null,audioSessionInfo:ae?O:null,forceMute:!ae,label:Z.name||Z.id,telemetryFrame:f[Z.id]||null,batteryConfig:Z.battery,layoutFormat:"mobile",hudVariant:"none",fitParent:!0})},Z.id)})}):r.jsx(di,{sessionInfo:null,videoMode:"snapshot",snapshotFeed:B,audioSessionInfo:O,label:T.name||T.id,telemetryFrame:Q,batteryConfig:T.battery,layoutFormat:"mobile",hudVariant:"none",fitParent:!0})}):r.jsx("div",{className:"flex h-full w-full items-center justify-center text-sm text-slate-500",children:g.length?"No sources available.":"No active drivers."}):r.jsx("div",{className:"flex h-full w-full items-center justify-center text-sm text-slate-500",children:"Switching to spectator…"})}),r.jsx("aside",{className:"flex h-full min-w-0 flex-1 flex-col border-l border-slate-800/60 bg-black",children:T?r.jsx(ax,{rover:T,frame:Q,driverLabel:L,sessionInfo:o?U:null,videoMode:o?"whep":"snapshot",snapshotFeed:o?null:y[T.id]||null,showPreview:!1,variant:"active"}):r.jsx("div",{className:"flex h-full w-full items-center justify-center text-sm text-slate-500",children:"No active rover."})})]}):r.jsx("div",{className:"relative flex h-screen w-screen overflow-hidden bg-black text-slate-100",children:r.jsx("section",{className:"flex h-full w-full gap-x-3 bg-slate-900 px-0",children:d.length?d.map((G,Z)=>r.jsx(ax,{rover:G,frame:f[G.id]||null,driverLabel:null,sessionInfo:o&&M[G.id]||null,videoMode:o?"whep":"snapshot",snapshotFeed:o?null:y[G.id]||null,showPreview:!0,withDivider:!1},G.id)):r.jsx("div",{className:"flex h-full w-full items-center justify-center text-sm text-slate-500",children:"No rovers available."})})})}function Hw(){return m.useEffect(()=>{if(typeof window>"u")return;const a=setTimeout(()=>{const l=new URL(window.location.href);l.searchParams.set("refresh",Date.now().toString()),window.location.replace(l.toString())},Bw);return()=>clearTimeout(a)},[]),r.jsxs(r.Fragment,{children:[r.jsx(Uw,{}),r.jsx(Ff,{scale:3})]})}function Vw({children:a}){return r.jsx("div",{className:"flex h-full w-full items-center justify-start overflow-hidden bg-black",children:r.jsx("div",{className:"relative flex items-center justify-center overflow-hidden bg-black",style:{width:"min(100%, calc(100vh * 4 / 3))",height:"min(100%, calc(100vw * 3 / 4))",maxWidth:"100%",maxHeight:"100%",aspectRatio:"4 / 3"},children:r.jsx("div",{className:"flex h-full w-full items-center justify-center overflow-hidden",children:a})})})}Wy.createRoot(document.getElementById("root")).render(r.jsx(m.StrictMode,{children:r.jsx(SS,{children:r.jsx(jS,{children:r.jsx(NS,{children:r.jsx(DS,{children:r.jsx(bA,{children:r.jsx(m1,{children:r.jsxs(Yb,{children:[r.jsx(Gr,{path:"/",element:r.jsx(ww,{})}),r.jsx(Gr,{path:"/spectate",element:r.jsx(_w,{})}),r.jsx(Gr,{path:"/mini",element:r.jsx(Hw,{})})]})})})})})})})})); +`;function vw(a){return a?a.charAt(0).toUpperCase()+a.slice(1):""}function yw(){const a=String(gw).split(/\r?\n/).map(i=>i.trim()).filter(Boolean);return a.length?(a[Math.floor(Math.random()*a.length)]||"").trim():""}function od(){const{setNickname:a}=_e(),{value:l,status:i,save:o}=Pe("profile",{nickname:""}),f=m.useRef(!1);m.useEffect(()=>{if(i!=="ready"||f.current||(l.nickname||"").trim())return;f.current=!0;const h=vw(yw()).slice(0,32);if(!h)return;(async()=>{try{await a(h)}catch{}finally{o({nickname:h})}})()},[o,a,i,l.nickname])}function bw(){const a=kn(),{connected:l,identifySession:i}=_e(),{value:o,status:f,save:d}=Pe("identity",{cookieUserId:""}),{value:h,status:p}=Pe("profile",{nickname:""}),x=m.useRef(!1),g=m.useRef(null),b=m.useRef(null),y=f==="ready"&&p==="ready",j=(o?.cookieUserId||"").trim(),M=(h?.nickname||"").trim(),C=m.useCallback(()=>{b.current&&(clearTimeout(b.current),b.current=null)},[]),w=m.useCallback(async()=>{if(!(!y||!l||!a?.id||x.current)){x.current=!0;try{const q=((await i({cookieUserId:j,nickname:M}))?.cookieUserId||"").trim();q&&q!==j&&d(A=>({...A||{},cookieUserId:q})),g.current=a.id,C()}catch{C(),b.current=setTimeout(()=>{w()},2e3)}finally{x.current=!1}}},[C,l,j,i,M,y,d,a?.id]);m.useEffect(()=>{!y||!l||!a?.id||g.current!==a.id&&w()},[l,y,w,a?.id]),m.useEffect(()=>{!y||!l||!a?.id||w()},[y,l,a?.id,j,M,w]),m.useEffect(()=>{const N=()=>{a?.connected&&w()},q=()=>{typeof document>"u"||document.visibilityState!=="visible"||a?.connected&&w()};return window.addEventListener("online",N),document.addEventListener("visibilitychange",q),()=>{window.removeEventListener("online",N),document.removeEventListener("visibilitychange",q)}},[w,a?.connected]),m.useEffect(()=>()=>C(),[C])}const cf=1e4,uf=28,Sw=14;function ro({layout:a="desktop",className:l="",dismissable:i=!0}){const{session:o}=_e(),f=o?.communityGoal?.text?String(o.communityGoal.text).trim():"",d=a==="mobile-portrait"||a==="mobile-landscape"||a==="mobile",[h,p]=m.useState(!1),[x,g]=m.useState(uf),[b,y]=m.useState(0),j=m.useRef(null),M=m.useRef(null),C=m.useRef(null);m.useEffect(()=>{if(!f){p(!1);return}if(p(!0),!d||!i)return;const A=Date.now();y(cf);const E=setTimeout(()=>p(!1),cf),T=setInterval(()=>{const B=Date.now()-A;y(Math.max(0,cf-B))},250);return()=>{clearTimeout(E),clearInterval(T)}},[f,d]),m.useEffect(()=>{if(!f)return;g(uf);let A=0;const E=()=>{const B=C.current,U=M.current;if(!B||!U)return;const O=B.clientWidth;if(!O)return;const Q=U.scrollWidth;if(!Q)return;const L=Math.min(1,O/Q),G=Math.max(Sw,Math.floor(uf*L));g(G)};A=window.requestAnimationFrame(E);const T=new ResizeObserver(()=>{window.cancelAnimationFrame(A),A=window.requestAnimationFrame(E)});return j.current&&T.observe(j.current),()=>{window.cancelAnimationFrame(A),T.disconnect()}},[f]);const w=m.useMemo(()=>["panel-section flex w-full items-center justify-center",d?"rounded-none":"rounded","px-1 py-1 text-center font-semibold tracking-tight",l].filter(Boolean).join(" "),[l,d]);if(!f||!h)return null;const N=d?Math.ceil(b/1e3):null,q=i?{onClick:()=>p(!1),role:"button",tabIndex:0}:{};return r.jsx("div",{ref:j,className:w,style:{fontSize:`${x}px`,lineHeight:1.1},...q,children:r.jsxs("span",{className:"flex w-full items-stretch gap-0.5 whitespace-nowrap rounded-md",children:[r.jsxs("span",{className:"flex flex-col justify-center border-r border-slate-700/60 px-0.5 text-[0.55em] font-semibold leading-tight text-slate-400",children:[r.jsx("span",{children:"Community"}),r.jsx("span",{children:"Goal"})]}),r.jsx("span",{ref:C,className:"flex-1 overflow-hidden text-slate-100",children:r.jsx("span",{ref:M,className:"block",children:f})}),d?r.jsxs("span",{className:"flex items-center border-l border-slate-700/60 px-0.5 text-[0.55em] font-semibold uppercase tracking-wide text-slate-400",children:[N,"s"]}):null]})})}function Aw(){const[a,l]=m.useState(()=>typeof window>"u"||window.innerWidth>=1024?"desktop":window.innerWidth>window.innerHeight?"mobile-landscape":"mobile-portrait");return m.useEffect(()=>{function i(){if(typeof window>"u")return;const{innerWidth:o,innerHeight:f}=window;o>=1024?l("desktop"):o>f?l("mobile-landscape"):l("mobile-portrait")}return i(),window.addEventListener("resize",i),()=>window.removeEventListener("resize",i)},[]),a}function jw({layout:a,onOpenHelpOverlay:l}){return r.jsxs("div",{className:"flex h-full gap-0.5 overflow-hidden",children:[r.jsxs("div",{className:"flex min-w-0 flex-[1.22] flex-col gap-0.5 overflow-y-auto pr-0",children:[r.jsx(td,{}),r.jsx(kS,{}),r.jsx(Jf,{})]}),r.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-0.5 overflow-y-auto",children:[r.jsx(ro,{layout:a}),r.jsx(tw,{layout:a,onOpenHelpOverlay:l})]})]})}function qg({layout:a,onOpenHelpOverlay:l,roomPanelId:i,showTelemetry:o=!0}){const{session:f}=_e(),d=f?.isVerified?"bg-emerald-400":"bg-amber-400";return r.jsx("section",{className:"panel text-base",children:r.jsxs(sd,{defaultTab:"chat",children:[r.jsxs(id,{children:[r.jsx(Xt,{id:"chat",children:"Chat"}),r.jsx(Xt,{id:"vip",children:r.jsxs("span",{className:"inline-flex items-center gap-0.5",children:[r.jsx("span",{children:"VIP"}),r.jsx("span",{className:`inline-block h-1.5 w-1.5 rounded-full ${d}`,"aria-hidden":"true",title:f?.isVerified?"Verified":"Not verified"})]})}),r.jsx(Xt,{id:"roomcontrols",children:"Room Controls"}),r.jsx(Xt,{id:"help",children:"Help"}),r.jsx(Xt,{id:"settings",children:"Settings"})]}),r.jsxs(rd,{children:[r.jsx(Qt,{id:"chat",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(xo,{}),r.jsx(so,{})]})}),r.jsx(Qt,{id:"vip",children:r.jsx(Cg,{})}),r.jsx(Qt,{id:"roomcontrols",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(xg,{}),r.jsx(If,{panelId:i})]})}),r.jsx(Qt,{id:"help",children:r.jsx(Ng,{layout:a,onOpenOverlay:l})}),r.jsx(Qt,{id:"settings",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(Sg,{}),r.jsx(Jf,{})]})})]})]})})}function Nw({onOpenHelpOverlay:a}){return r.jsxs("div",{className:"flex flex-col gap-0.5",children:[r.jsx(td,{layoutFormat:"mobile-portrait"}),r.jsx(tj,{}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]",children:[r.jsx($f,{panelId:"replay-sources-mobile-portrait"}),r.jsx(vo,{})]}),r.jsx(qg,{layout:"mobile-portrait",onOpenHelpOverlay:a,roomPanelId:"mobile-portrait-room"})]})}function ww({onOpenHelpOverlay:a}){const l="self-start h-[min(100svh,32rem)]";return r.jsxs("div",{className:"flex flex-col gap-0.5",children:[r.jsxs("section",{className:"grid min-h-screen grid-cols-[minmax(0,0.7fr)_minmax(0,2.1fr)_minmax(0,0.7fr)] gap-0.5",children:[r.jsx(ug,{layout:"landscape",className:l}),r.jsxs("div",{children:[r.jsx(td,{layoutFormat:"mobile-landscape"}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]",children:[r.jsx($f,{panelId:"replay-sources-mobile-landscape"}),r.jsx(vo,{})]})]}),r.jsx(fg,{layout:"landscape",className:l})]}),r.jsx("div",{className:"flex flex-col gap-0.5 pb-0",children:r.jsx(qg,{layout:"mobile-landscape",onOpenHelpOverlay:a,roomPanelId:"mobile-landscape-room",showTelemetry:!1})})]})}function Cw(){const a=Aw(),l=a==="desktop",i=pw(a);return r.jsx("div",{className:`bg-black text-slate-100 ${l?"h-screen overflow-hidden":"min-h-screen"}`,children:r.jsx(Ew,{layout:a,isDesktop:l,fullscreen:i})})}function Ew({layout:a,isDesktop:l,fullscreen:i}){od(),bw();const{visible:o,mode:f,enterFullscreen:d,dismiss:h}=i,{value:p,status:x,save:g}=Pe("help",{showOnLoad:!0}),[b,y]=m.useState(!1);m.useEffect(()=>{x==="ready"&&y(p?.showOnLoad!==!1)},[x,p?.showOnLoad]);const j=m.useCallback(()=>y(!0),[]),M=m.useCallback(()=>y(!1),[]),C=m.useCallback(N=>{const q=!!N;g(A=>({...A??{},showOnLoad:q})),q||y(!1)},[g]),w=m.useMemo(()=>l?r.jsx(jw,{layout:a,onOpenHelpOverlay:j}):a==="mobile-landscape"?r.jsx(ww,{onOpenHelpOverlay:j}):r.jsx(Nw,{onOpenHelpOverlay:j}),[l,a,j]);return r.jsxs(vA,{children:[r.jsx(qA,{}),r.jsx(FA,{}),r.jsxs("main",{className:`flex w-full flex-col gap-0.5 text-base ${l?"h-full overflow-hidden":""}`,children:[l?null:r.jsx(ro,{layout:a}),w]}),r.jsx(Ff,{}),r.jsx(cw,{}),r.jsx(iw,{}),r.jsx(xw,{visible:b,layout:a,onClose:M,showOnLoad:p?.showOnLoad!==!1,onToggleShowOnLoad:C}),r.jsx(uw,{visible:o,mode:f,onEnterFullscreen:d,onDismiss:h})]})}function Mg(){const{session:a,setRole:l,subscribeAll:i,connected:o}=_e(),[f,d]=m.useState(!1);return m.useEffect(()=>{let h=!1;async function p(){if(a?.mode==="lockdown"){d(!1);return}try{a?.role!=="spectator"&&await l("spectator"),await i(),h||d(!0)}catch(x){console.error("Failed to enter spectator mode",x),h||d(!1)}}return p(),()=>{h=!0}},[o,a?.mode,a?.role,l,i]),f}function kw(){const[a,l]=m.useState(()=>typeof window>"u"?!1:window.matchMedia("(max-aspect-ratio: 4/3)").matches);return m.useEffect(()=>{if(typeof window>"u")return;const i=window.matchMedia("(max-aspect-ratio: 4/3)"),o=f=>l(f.matches);return i.addEventListener?i.addEventListener("change",o):i.addListener(o),()=>{i.removeEventListener?i.removeEventListener("change",o):i.removeListener(o)}},[]),a}function qw({roverId:a,session:l}){const i=l?.activeDrivers?.[a]||null,f=(l?.users||[]).find(x=>x.socketId===i)?.nickname||(i?i.slice(0,6):"No driver"),d=l?.mode,h=l?.turnQueues?.[a];return d==="turns"&&h?.current?`${f} (turns)`:f}function Mw({rover:a,frame:l,sessionInfo:i,videoMode:o,snapshotFeed:f,audioInfo:d,session:h}){const p=qw({roverId:a.id,session:h});return r.jsx("article",{className:"min-h-[16rem] rounded bg-zinc-900 p-0 sm:min-h-[18rem]",children:r.jsx("div",{className:"min-h-0 overflow-hidden rounded bg-black/20",children:r.jsx(di,{sessionInfo:i,videoMode:o,snapshotFeed:f,audioSessionInfo:d,label:a.name,telemetryFrame:l,batteryConfig:a.battery,hudVariant:"spectator",driverLabel:p,hudForceMap:!0,hudMapPosition:"top-center"})})})}function Rw({roster:a,frames:l,videoSources:i,snapshotFeeds:o,audioSources:f,session:d,canSpectateVideo:h}){return a.length===0?r.jsx("p",{className:"col-span-full text-slate-400",children:"No rovers registered."}):r.jsx("section",{className:"grid grid-cols-1 gap-0.5 md:grid-cols-2",children:a.map(p=>r.jsx(Mw,{rover:p,frame:l[p.id],sessionInfo:h&&i[p.id]||null,videoMode:h?"whep":"snapshot",snapshotFeed:h?null:o[p.id],audioInfo:f[`${p.id}-audio`],session:d,showHudMap:!0,hudMapPosition:"bottom-left"},p.id))})}function Tw(){return r.jsx("section",{className:"min-h-0",children:r.jsx("div",{className:"surface min-h-[14rem] overflow-hidden",children:r.jsx(If,{defaultOrientation:"horizontal",hideLayoutToggle:!0,hideHeader:!0,panelId:"spectator-secondary"})})})}function Dw({className:a=""}){return r.jsx("div",{className:`panel ${a}`,children:r.jsx(Jf,{})})}function _w(){const{session:a}=_e(),l=a?.mode==="lockdown",i=!!a?.isLocalNetwork;od(),Mg();const o=kw(),f=Vf(),d=a?.roster??[],h=Wf(d.map(N=>N.id),{enabled:!l&&!i,version:a?.mode}),p=i?d.map(N=>({type:"rover",id:N.id,key:N.id})):[],x=ui(p,{enabled:!l&&i,version:a?.mode}),g=d.flatMap(N=>N.media?.audioPublishUrl?[{type:"rover",id:`${N.id}-audio`,key:`${N.id}-audio`}]:[]),b=ui(g,{enabled:!l,version:a?.mode});if(l)return r.jsx("div",{className:"flex min-h-screen items-center justify-center bg-black text-slate-200",children:r.jsxs("div",{className:"surface max-w-md space-y-0.5 p-1 text-center text-sm",children:[r.jsx("p",{className:"text-lg font-semibold text-white",children:"Spectate disabled during lockdown."}),r.jsx("p",{className:"text-slate-300",children:"Please wait until the server leaves lockdown to view streams."})]})});const y=o?"flex min-h-screen flex-col bg-black text-slate-100 md:h-screen md:overflow-hidden":"grid min-h-screen grid-cols-1 gap-0.5 bg-black text-slate-100 md:h-full md:min-h-0 md:grid-cols-[minmax(0,1fr)_18rem] lg:grid-cols-[minmax(0,1fr)_20rem]",j=o?"order-2 flex min-h-0 min-w-0 flex-1 flex-col gap-0.5 overflow-y-auto":"order-1 flex min-h-0 min-w-0 flex-col gap-0.5 md:overflow-y-auto",M=o?"order-1 grid w-full min-h-0 items-stretch gap-0.5 border-b border-slate-800/60 bg-slate-950/90 p-0.5 grid-cols-[minmax(0,1fr)_minmax(0,0.7fr)_minmax(0,1.9fr)_minmax(0,0.7fr)]":"order-2 flex min-h-0 min-w-0 flex-col gap-0.5 border-l border-slate-800/60 bg-slate-950/90 md:h-full md:overflow-y-auto",C="",w=o?"h-64":"";return r.jsxs("div",{className:"min-h-screen bg-black text-slate-100 md:h-screen md:overflow-hidden",children:[r.jsxs("main",{className:y,children:[r.jsxs("section",{className:M,children:[o?r.jsxs("div",{className:`${C} ${w} flex flex-col gap-0.5`,children:[r.jsx(ro,{layout:"desktop",dismissable:!1,className:"text-sm"}),r.jsx("div",{className:"min-h-0 flex-1",children:r.jsx(so,{hideNicknameForm:!0,hideHeader:!0,compact:!0,fillHeight:!0,className:"h-full"})})]}):r.jsx("div",{className:C,children:r.jsx(ro,{layout:"desktop",dismissable:!1,className:"text-sm"})}),r.jsx("div",{className:`${C} ${o?`${w} overflow-y-auto`:""}`,children:r.jsx("div",{className:"panel h-full",children:r.jsx(vo,{title:"Rovers"})})}),o?null:r.jsx("div",{className:`${C} flex-1 min-h-0`,children:r.jsx(so,{hideNicknameForm:!0,hideHeader:!0,compact:!0,fillHeight:!0,className:"h-full"})}),r.jsx("div",{className:`${C} ${o?w:"flex-[1.1] min-h-0"}`,children:r.jsx(xo,{allowSpectatorInput:!0,hideSpectatorNotice:!0,fillHeight:!0})}),r.jsx("div",{className:`${C} ${o?w:""}`,children:r.jsx(Dw,{className:`${o?"h-full":"h-40"} overflow-hidden`})})]}),r.jsxs("section",{className:j,children:[r.jsx(Rw,{roster:d,frames:f,videoSources:x,snapshotFeeds:h,audioSources:b,session:a,canSpectateVideo:i}),r.jsx(Tw,{})]})]}),r.jsx(Ff,{})]})}function Ow(){return r.jsx(_w,{})}const Bw=2e4,Lw=3600*1e3;function zw({roverId:a,session:l}){const i=l?.activeDrivers?.[a]||null,f=(l?.users||[]).find(p=>p.socketId===i)?.nickname||(i?i.slice(0,6):"No driver"),d=l?.mode,h=l?.turnQueues?.[a];return d==="turns"&&h?.current?`${f}`:f}function Uw({rover:a,frame:l}){const i=l?.sensors?.batteryChargeMah??null,o=a?.battery??null;return pg({batteryState:a?.batteryState??null,charge:i,config:o})}function ax({rover:a,frame:l,driverLabel:i,sessionInfo:o,videoMode:f="snapshot",snapshotFeed:d,withDivider:h=!1,showPreview:p=!0,variant:x="stacked"}){const g=Uw({rover:a,frame:l}),b=g?.available?g.percentDisplay:null,y=x==="active";return r.jsxs("div",{className:`relative flex min-w-0 flex-1 flex-col gap-4 overflow-hidden bg-black px-0 py-0 ${h?"border-r border-slate-700/60":""}`,children:[r.jsx(Ef,{visual:g,orientation:y?"vertical":"horizontal",variant:"background"}),y?r.jsxs("div",{className:"relative z-10 flex min-w-0 flex-1 flex-col justify-between text-center",children:[r.jsx("div",{className:"min-w-0 bg-transparent px-0 py-0 leading-none",children:r.jsx(Xl,{className:"font-semibold leading-none text-white",maxSize:1e3,minSize:18,children:a.name||a.id})}),i?r.jsx("div",{className:"min-w-0 bg-transparent px-0 py-0 leading-none",children:r.jsx(Xl,{className:"font-semibold leading-none text-white",maxSize:1e3,minSize:16,children:i})}):r.jsx("div",{}),r.jsx("div",{className:"relative min-w-0 overflow-hidden bg-transparent px-0 py-0 leading-none",children:r.jsx("div",{className:"relative",children:r.jsx(Xl,{className:"font-semibold leading-none text-white",maxSize:1e3,minSize:16,children:b==null?"--%":`${b}%`})})})]}):r.jsxs(r.Fragment,{children:[r.jsxs("div",{className:"relative z-10 flex min-w-0 flex-col gap-1 text-center",children:[r.jsx("div",{className:"min-w-0 bg-transparent px-0 py-0 leading-none",children:r.jsx(Xl,{className:"font-semibold leading-none text-white",maxSize:1e3,minSize:18,children:a.name||a.id})}),i?r.jsx("div",{className:"min-w-0 bg-transparent px-0 py-0 leading-none",children:r.jsx(Xl,{className:"font-semibold leading-none text-white",maxSize:1e3,minSize:16,children:i})}):null,r.jsx("div",{className:"relative min-w-0 overflow-hidden bg-transparent px-0 py-0 leading-none",children:r.jsx("div",{className:"relative",children:r.jsx(Xl,{className:"font-semibold leading-none text-white",maxSize:80,minSize:16,children:b==null?"--%":`${b}%`})})})]}),r.jsx("div",{className:"relative z-10 flex min-w-0 flex-1 flex-col gap-4",children:r.jsx("div",{className:"flex w-full min-w-0 flex-1 flex-col items-center gap-4",children:p?r.jsx("div",{className:"mt-auto w-full",children:r.jsx("div",{className:"w-full aspect-[4/3]",children:r.jsx(di,{sessionInfo:o,videoMode:f,snapshotFeed:d,audioSessionInfo:null,label:a.name||a.id,telemetryFrame:l,batteryConfig:a.battery,layoutFormat:"mobile",hudVariant:"none",fitParent:!0})})}):null})})]})]})}function Xl({children:a,className:l="",maxSize:i=1e3,minSize:o=14}){const f=m.useRef(null),d=m.useRef(null),[h,p]=m.useState(i);return m.useLayoutEffect(()=>{const x=f.current,g=d.current;if(!x||!g)return;let b=null;const y=()=>{const C=x.clientWidth;if(!C){j();return}let w=o,N=i,q=o;for(;w<=N;){const A=Math.floor((w+N)/2);g.style.fontSize=`${A}px`,g.scrollWidth<=C?(q=A,w=A+1):N=A-1}p(q)},j=()=>{b&&cancelAnimationFrame(b),b=requestAnimationFrame(y)};j();const M=new ResizeObserver(j);return M.observe(x),()=>{b&&cancelAnimationFrame(b),M.disconnect()}},[a,i,o]),r.jsx("div",{ref:f,className:"w-full min-w-0",children:r.jsx("div",{ref:d,className:`whitespace-nowrap ${l}`,style:{fontSize:`${h}px`,lineHeight:1.1},children:a})})}function Hw(){const{session:a}=_e(),l=Mg();od();const i=a?.mode==="lockdown",o=!!a?.isLocalNetwork,f=Vf(),d=a?.roster??[],[h,p]=m.useState(0),x=a?.activeDrivers||{},g=m.useMemo(()=>d.filter(G=>x[G.id]),[d,x]),b=g.length?g:d,y=Wf(b.map(G=>G.id),{enabled:!i&&!o,version:a?.mode}),j=m.useMemo(()=>o?b.map(G=>({type:"rover",id:G.id,key:G.id})):[],[o,b]),M=ui(j,{enabled:!i&&o,version:a?.mode}),C=m.useMemo(()=>g.flatMap(G=>{if(!G?.id||!G.media?.audioPublishUrl)return[];const Z=String(G.id);return[{type:"rover",id:`${Z}-audio`,key:`${Z}-audio`}]}),[g]),w=ui(C,{enabled:!i,version:a?.mode}),N=m.useMemo(()=>{if(!g.length)return[];if(!o){const Z=g.filter(ae=>y[ae.id]?.objectUrl);return Z.length?Z:g}const G=g.filter(Z=>{const ae=M[Z.id];return ae?.url&&!ae?.error});return G.length?G:g},[g,y,M,o]),q=m.useMemo(()=>N.map(G=>({type:"rover",rover:G})),[N]),A=m.useMemo(()=>q.map(G=>`r:${G.rover.id}`).join("|"),[q]);m.useEffect(()=>{p(0)},[A]),m.useEffect(()=>{if(!q.length)return;const G=setInterval(()=>{p(Z=>(Z+1)%q.length)},Bw);return()=>clearInterval(G)},[q.length,A]);const E=q.length?q[h%q.length]:null,T=E?.type==="rover"?E.rover:null,B=!o&&T&&y[T.id]||null,U=o&&T&&M[T.id]||null,O=T&&w[`${T.id}-audio`]||null,Q=T&&f[T.id]||null,L=T?zw({roverId:T.id,session:a}):null;return i?r.jsx("div",{className:"relative flex h-screen w-screen items-center justify-center bg-black text-slate-200",children:r.jsxs("div",{className:"surface max-w-sm space-y-0.5 p-1 text-center text-sm",children:[r.jsx("p",{className:"text-lg font-semibold text-white",children:"Mini spectator is disabled in lockdown."}),r.jsx("p",{className:"text-slate-300",children:"It will automatically resume once lockdown ends."})]})}):g.length?r.jsxs("div",{className:"relative flex h-screen w-screen overflow-hidden bg-black text-slate-100",children:[r.jsx("section",{className:"relative flex h-full shrink-0 items-center justify-start overflow-hidden bg-black",style:{width:"min(100vw, calc(100vh * 4 / 3))"},children:l?T?r.jsx($w,{children:o?r.jsx("div",{className:"relative h-full w-full",children:q.map(G=>{const Z=G.rover,ae=T?.id===Z.id;return r.jsx("div",{className:`absolute inset-0 ${ae?"opacity-100":"opacity-0 pointer-events-none"}`,children:r.jsx(di,{sessionInfo:M[Z.id]||null,videoMode:"whep",snapshotFeed:null,audioSessionInfo:ae?O:null,forceMute:!ae,label:Z.name||Z.id,telemetryFrame:f[Z.id]||null,batteryConfig:Z.battery,layoutFormat:"mobile",hudVariant:"none",fitParent:!0})},Z.id)})}):r.jsx(di,{sessionInfo:null,videoMode:"snapshot",snapshotFeed:B,audioSessionInfo:O,label:T.name||T.id,telemetryFrame:Q,batteryConfig:T.battery,layoutFormat:"mobile",hudVariant:"none",fitParent:!0})}):r.jsx("div",{className:"flex h-full w-full items-center justify-center text-sm text-slate-500",children:g.length?"No sources available.":"No active drivers."}):r.jsx("div",{className:"flex h-full w-full items-center justify-center text-sm text-slate-500",children:"Switching to spectator…"})}),r.jsx("aside",{className:"flex h-full min-w-0 flex-1 flex-col border-l border-slate-800/60 bg-black",children:T?r.jsx(ax,{rover:T,frame:Q,driverLabel:L,sessionInfo:o?U:null,videoMode:o?"whep":"snapshot",snapshotFeed:o?null:y[T.id]||null,showPreview:!1,variant:"active"}):r.jsx("div",{className:"flex h-full w-full items-center justify-center text-sm text-slate-500",children:"No active rover."})})]}):r.jsx("div",{className:"relative flex h-screen w-screen overflow-hidden bg-black text-slate-100",children:r.jsx("section",{className:"flex h-full w-full gap-x-3 bg-slate-900 px-0",children:d.length?d.map((G,Z)=>r.jsx(ax,{rover:G,frame:f[G.id]||null,driverLabel:null,sessionInfo:o&&M[G.id]||null,videoMode:o?"whep":"snapshot",snapshotFeed:o?null:y[G.id]||null,showPreview:!0,withDivider:!1},G.id)):r.jsx("div",{className:"flex h-full w-full items-center justify-center text-sm text-slate-500",children:"No rovers available."})})})}function Vw(){return m.useEffect(()=>{if(typeof window>"u")return;const a=setTimeout(()=>{const l=new URL(window.location.href);l.searchParams.set("refresh",Date.now().toString()),window.location.replace(l.toString())},Lw);return()=>clearTimeout(a)},[]),r.jsxs(r.Fragment,{children:[r.jsx(Hw,{}),r.jsx(Ff,{scale:3})]})}function $w({children:a}){return r.jsx("div",{className:"flex h-full w-full items-center justify-start overflow-hidden bg-black",children:r.jsx("div",{className:"relative flex items-center justify-center overflow-hidden bg-black",style:{width:"min(100%, calc(100vh * 4 / 3))",height:"min(100%, calc(100vw * 3 / 4))",maxWidth:"100%",maxHeight:"100%",aspectRatio:"4 / 3"},children:r.jsx("div",{className:"flex h-full w-full items-center justify-center overflow-hidden",children:a})})})}Wy.createRoot(document.getElementById("root")).render(r.jsx(m.StrictMode,{children:r.jsx(SS,{children:r.jsx(jS,{children:r.jsx(NS,{children:r.jsx(DS,{children:r.jsx(bA,{children:r.jsx(m1,{children:r.jsxs(Yb,{children:[r.jsx(Gr,{path:"/",element:r.jsx(Cw,{})}),r.jsx(Gr,{path:"/spectate",element:r.jsx(Ow,{})}),r.jsx(Gr,{path:"/mini",element:r.jsx(Vw,{})})]})})})})})})})})); diff --git a/server/public/index.html b/server/public/index.html index 1f31b0ad..c4846295 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -11,7 +11,7 @@ Multi Roomba Rover - + diff --git a/webui/src/components/vip/VipAudioForwardingCard.jsx b/webui/src/components/vip/VipAudioForwardingCard.jsx index 27a34166..c73f85d1 100644 --- a/webui/src/components/vip/VipAudioForwardingCard.jsx +++ b/webui/src/components/vip/VipAudioForwardingCard.jsx @@ -49,6 +49,43 @@ function waitForIceGatheringComplete(pc, timeoutMs = 1500) { }); } +function waitForPeerConnected(pc, timeoutMs = 4000) { + return new Promise((resolve, reject) => { + if (!pc) { + reject(new Error('Peer connection missing')); + return; + } + const state = pc.connectionState; + if (state === 'connected') { + resolve(); + return; + } + if (state === 'failed' || state === 'closed') { + reject(new Error(`Peer connection ${state}`)); + return; + } + const timer = setTimeout(() => { + cleanup(); + reject(new Error('Peer connection timeout')); + }, timeoutMs); + const onState = () => { + const next = pc.connectionState; + if (next === 'connected') { + cleanup(); + resolve(); + } else if (next === 'failed' || next === 'closed') { + cleanup(); + reject(new Error(`Peer connection ${next}`)); + } + }; + function cleanup() { + clearTimeout(timer); + pc.removeEventListener('connectionstatechange', onState); + } + pc.addEventListener('connectionstatechange', onState); + }); +} + function resampleTo16k(input, sampleRate) { if (!input || !input.length) return new Float32Array(0); if (sampleRate === TARGET_SAMPLE_RATE) return input; @@ -368,6 +405,7 @@ export default function VipAudioForwardingCard({ } const answerSdp = await response.text(); await pc.setRemoteDescription({ type: 'answer', sdp: answerSdp }); + await waitForPeerConnected(pc, 4500); await readyMicWhip?.(target); setMicState('live'); } catch (err) {