From 657189e979b398903bf792bf1bfc1422e1427dab Mon Sep 17 00:00:00 2001 From: legop3 Date: Sat, 21 Feb 2026 13:40:02 -0500 Subject: [PATCH] wuh --- server/public/assets/{index-B-zhHWLf.js => index-Bqpg29IV.js} | 2 +- server/public/index.html | 2 +- webui/src/components/VideoTile.jsx | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) rename server/public/assets/{index-B-zhHWLf.js => index-Bqpg29IV.js} (85%) diff --git a/server/public/assets/index-B-zhHWLf.js b/server/public/assets/index-Bqpg29IV.js similarity index 85% rename from server/public/assets/index-B-zhHWLf.js rename to server/public/assets/index-Bqpg29IV.js index 5a396343..9c0055ed 100644 --- a/server/public/assets/index-B-zhHWLf.js +++ b/server/public/assets/index-Bqpg29IV.js @@ -14,7 +14,7 @@ Please change the parent to i&&i>r){const f=gi/100,d=1-f;if(a<=r)return 0;if(a<=i){const p=(a-r)/(i-r);return Math.round(Math.max(0,Math.min(1,p))*gi)}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 dj=1600,mj=800;function xn(...a){return a.filter(Boolean).join(" ")}function jf({visual:a,orientation:l="horizontal",variant:i="inline",compact:r=!1,showLabel:f,className:d=""}){const h=i==="background",p=l==="vertical",x=f??!h;if(!a?.available){if(h){const ne=gi,ue=xn("absolute z-10",p?"left-0 right-0 h-[2px]":"top-0 bottom-0 w-[2px]","bg-black/70",M&&!N&&"battery-tick-warn"),he=p?{bottom:`${ne}%`}:{left:`${ne}%`},V=xn("absolute inset-0 pointer-events-none",d);return o.jsxs("div",{className:V,children:[o.jsx("div",{className:"absolute inset-0 bg-slate-900/35"}),o.jsx("div",{className:ue,style:he})]})}return p?null:o.jsx("p",{className:"text-xs text-slate-500",children:"Battery telemetry unavailable"})}const v=a.percentDisplay??0,y=Math.max(0,Math.min(100,v)),b=a.warnDisplayPercent??gi,N=!!a.urgentActive,M=!!a.warnActive,C=N?`${mj}ms`:M?`${dj}ms`:void 0,j=h?N||M?"bg-red-500/45":"bg-emerald-500/45":N||M?"bg-red-500":"bg-emerald-500",R=N||M?"bg-red-900/35":"bg-slate-900/35",A=N||M?"bg-red-900/45":"bg-slate-900/35",T=xn(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 ne=p?{height:`${y}%`}:{width:`${y}%`},ue=xn("absolute",p?"bottom-0 left-0 right-0":"left-0 top-0 bottom-0",j,"transition-[width,height]",N&&"battery-urgent-flash"),he=xn("absolute z-10",p?"left-0 right-0 h-[2px]":"top-0 bottom-0 w-[2px]","bg-black/70",M&&!N&&"battery-tick-warn"),V=p?{bottom:`${b}%`}:{left:`${b}%`};return o.jsxs("div",{className:T,children:[o.jsx("div",{className:xn("absolute inset-0",R,N&&"battery-urgent-flash")}),o.jsx("div",{className:ue,style:{...ne,animationDuration:C}}),o.jsx("div",{className:he,style:V})]})}const O=xn("relative overflow-hidden rounded-sm border border-slate-800",N||M?A:"bg-zinc-900/90",N&&"battery-urgent-flash",p?"w-full flex-1":r?"h-4":"h-5"),L=p?{height:`${y}%`}:{width:`${y}%`},Z=xn("absolute",p?"bottom-0 left-0 right-0":"left-0 top-0 bottom-0",j,"transition-[width,height]",N&&"battery-urgent-flash"),D=xn("absolute z-10",p?"left-0 right-0 h-[2px]":"top-0 bottom-0 w-[2px]","bg-black/70",M&&!N&&"battery-tick-warn"),$=p?{bottom:`${b}%`}:{left:`${b}%`},Y=`Battery ${v}%`,F=xn("pointer-events-none absolute inset-0 flex items-center justify-center text-center font-semibold",p?r?"text-[0.55rem]":"text-[0.65rem]":"text-xs"),Q=xn("rounded px-1.5 py-0.5",h?"bg-black/30 text-slate-100":"bg-black/50 text-slate-100");return o.jsxs("div",{className:T,children:[o.jsxs("div",{className:O,children:[o.jsx("div",{className:Z,style:{...L,animationDuration:C}}),o.jsx("div",{className:D,style:$})]}),x&&!p?o.jsx("div",{className:F,children:o.jsx("span",{className:Q,children:Y})}):null,x&&p?o.jsx("div",{className:"mt-0.5 text-[0.65rem] font-semibold text-slate-100",children:o.jsxs("span",{className:Q,children:[v,"%"]})}):null]})}const Op=2e3,hj=3e3,pj=3e3,gj=.55,Bp=40,xj=-.75,Cn={threshold:-30,knee:10,ratio:8,attack:.002,release:.18};function xi({sessionInfo:a,audioSessionInfo:l,videoMode:i="whep",snapshotFeed:r=null,qualityNotice:f=null,label:d,forceMute:h=!1,telemetryFrame:p,batteryConfig:x,layoutFormat:v="desktop",hudVariant:y="default",driverLabel:b=null,hudForceMap:N=!1,hudMapPosition:M="top-center",hudLabelScale:C=1,fitParent:E=!1,overcurrentLimiter:j=null,showTurnCue:R=!1,turnTimerText:A=null,turnSeconds:T=null,isActiveDriver:O=!1,idleSkipSeconds:L=null}){const{session:Z}=$e(),D=m.useRef(null),$=m.useRef(null),Y=m.useRef(null),F=m.useRef(null),Q=m.useRef(null),ne=m.useRef(null),ue=m.useRef(null),he=m.useRef(null),V=m.useRef(null),q=m.useRef(null),X=m.useRef(!1),W=m.useRef(null),oe=m.useRef(!1),[fe,w]=m.useState("idle"),[z,P]=m.useState(null),[te,le]=m.useState("idle"),[ie,ce]=m.useState(null),[ve,pe]=m.useState(null),[Le,ae]=m.useState(0),[re,je]=m.useState(0),[ze,_e]=m.useState(!0),He=!!l?.url,qt=i==="snapshot",Mt=p?.sensors,{value:Lt}=rt("audio",Ct),on=Number.isFinite(Lt?.masterVolume)?Lt.masterVolume:Ct.masterVolume,cn=Number.isFinite(Lt?.roverVolume)?Lt.roverVolume:Ct.roverVolume,St=typeof Lt?.autoLevelEnabled=="boolean"?Lt.autoLevelEnabled:Ct.autoLevelEnabled,At=Lt?.autoLevelMode==="duck"?"duck":"compressor",gt=Math.max(0,Math.min(1,on*cn)),bo=Mt?.batteryChargeMah??null,rs=v==="desktop",zt=!rs,ol=zt?"top-right":M,[So]=rx(),wi=N||zt?!0:So,Ba=ox({charge:bo,config:x}),cl=Mt?.wheelOvercurrents||null,Dn=cl==null?[]:Object.entries(cl).filter(([,se])=>!!se).map(([se])=>se),et=j?.caps||null,un=j?.overcurrent?.groups||null,jt=typeof window<"u"&&new URLSearchParams(window.location.search).has("debugAudio"),os=typeof window<"u"&&new URLSearchParams(window.location.search).has("debugHud"),Ao=m.useMemo(()=>{if(!et)return null;const se=Number.isFinite(et?.drive?.cap)?et.drive.cap:1,we=Number.isFinite(et?.aux?.cap)?et.aux.cap:1;return Math.max(0,Math.min(1,1-Math.min(se,we)))},[et]),cs=!!j?.isActive,An=Dn.length?Dn:cs?["limiter"]:[],On=Ao??(Dn.length?1:0),ua=!!An.length,Kt=!!((Number(Mt?.mainBrushCurrentMa)||0)>Bp||(Number(Mt?.sideBrushCurrentMa)||0)>Bp||Mt?.wheelOvercurrents?.mainBrush||Mt?.wheelOvercurrents?.sideBrush),Nt=Math.max(0,Math.min(1,gt*(St&&At==="duck"&&Kt?gj:1))),be=m.useCallback((se,we={})=>{if(!jt)return;const qe=$.current,ye=ue.current,De={event:se,ts:Date.now(),roverLabel:d||null,hasDedicatedAudio:He,audioUrl:l?.url||null,autoLevelEnabled:St,autoLevelMode:At,baseRoverGain:gt,effectiveRoverGain:Nt,element:qe?{muted:qe.muted,volume:qe.volume,paused:qe.paused,ended:qe.ended,readyState:qe.readyState,networkState:qe.networkState}:null,context:ye?{state:ye.state,sampleRate:ye.sampleRate}:null,...we};try{console.log(`[AudioDebug] ${se} ${JSON.stringify(De)}`)}catch{console.log("[AudioDebug]",se,De)}},[jt,d,He,l?.url,St,At,gt,Nt]),Ei=Z?.socials?.find(se=>String(se?.id||se?.label||"").toLowerCase()==="discord")?.url||Z?.discord?.invite||null;m.useEffect(()=>{os&&console.log("[OvercurrentHUD]",{overlayVisible:ua,overlayMotors:An,overlayFill:On,limiterActive:cs,limiterCaps:et,limiterGroups:un,wheelOvercurrents:cl})},[os,On,An,ua,cs,et,un,cl]),m.useEffect(()=>{be("settings/update")},[be]);const Bn=m.useCallback(()=>{clearTimeout(Y.current),Y.current=setTimeout(()=>ae(Date.now()),Op)},[]),Ln=m.useCallback(()=>{clearTimeout(F.current),F.current=setTimeout(()=>je(Date.now()),Op)},[]),ul=m.useCallback(()=>{if(oe.current)return!1;const se=$.current;if(!se||typeof window>"u")return!1;try{const we=window.AudioContext||window.webkitAudioContext;if(typeof we!="function")return!1;ue.current||(ue.current=new we);const qe=ue.current;if(he.current||(he.current=qe.createMediaElementSource(se)),!V.current){const ye=qe.createDynamicsCompressor();ye.threshold.value=Cn.threshold,ye.knee.value=Cn.knee,ye.ratio.value=Cn.ratio,ye.attack.value=Cn.attack,ye.release.value=Cn.release,V.current=ye,be("graph/compressor-created",{threshold:ye.threshold.value,knee:ye.knee.value,ratio:ye.ratio.value,attack:ye.attack.value,release:ye.release.value})}return q.current||(q.current=qe.createGain()),X.current||(he.current.disconnect(),V.current.disconnect(),q.current.disconnect(),he.current.connect(V.current),V.current.connect(q.current),q.current.connect(qe.destination),X.current=!0,be("graph/connected")),!0}catch{return oe.current=!0,be("graph/failed"),!1}},[be]),fn=m.useCallback(async()=>{const se=ue.current;!se||se.state!=="suspended"||(be("context/resume-attempt"),await se.resume().catch(()=>{}),se.state==="running"&&St&&At==="compressor"&&q.current&&(q.current.gain.value=Nt,be("context/gain-restored",{gain:Nt})),be("context/resume-result"))},[be,St,At,Nt]),zn=m.useCallback(async()=>{if(!l?.url||!St||At!=="compressor")return;if(!ul()){be("unlock/no-graph");return}await fn();const we=$.current,qe=q.current;if(qe&&(qe.gain.value=Nt),!we){be("unlock/no-audio-element");return}we.volume=0,we.muted=!1,we.play().then(()=>be("unlock/force-play-ok")).catch(ye=>be("unlock/force-play-fail",{error:ye?.message}))},[l?.url,St,At,ul,fn,Nt,be]);m.useEffect(()=>{if(!l?.url||!St||At!=="compressor")return;const se=()=>{be("unlock/gesture"),zn()};return window.addEventListener("pointerdown",se,{passive:!0,capture:!0}),window.addEventListener("touchstart",se,{passive:!0,capture:!0}),window.addEventListener("click",se,{passive:!0,capture:!0}),window.addEventListener("keydown",se),()=>{window.removeEventListener("pointerdown",se),window.removeEventListener("touchstart",se),window.removeEventListener("click",se),window.removeEventListener("keydown",se)}},[l?.url,St,At,zn,be]),m.useEffect(()=>{if(!l?.url||!St||At!=="compressor")return;const se=setInterval(()=>{const we=ue.current;["connecting","connected","playing","paused"].includes(te)&&(!we||we.state!=="running")&&(be("unlock/interval"),zn())},1e3);return()=>clearInterval(se)},[l?.url,St,At,te,zn,be]);const La=m.useCallback(async()=>{const se=D.current;if(se)try{se.muted=!0,await se.play()}catch{}},[]),fl=m.useCallback((se=0)=>{if(h||He)return;clearTimeout(ne.current);const we=()=>{clearTimeout(ne.current),ne.current=setTimeout(()=>{h||qe()},hj)},qe=async()=>{const ye=D.current;if(ye)try{await La(),ye.muted=!1,await ye.play(),_e(!1)}catch{ye.muted=!0,_e(!0),we()}};ne.current=setTimeout(qe,se)},[La,h,He]);m.useEffect(()=>()=>{clearTimeout(Y.current),clearTimeout(F.current),clearTimeout(ne.current),clearInterval(Q.current),clearInterval(W.current),X.current=!1,he.current?.disconnect?.(),V.current?.disconnect?.(),q.current?.disconnect?.(),ue.current?.state&&ue.current.state!=="closed"&&ue.current.close().catch(()=>{})},[]),m.useEffect(()=>{fe==="playing"&&fl(0)},[fe,fl]),m.useEffect(()=>{qt&&(w("snapshot"),P(null))},[qt]),m.useEffect(()=>{if(qt||!a?.url||!D.current)return;let se=!0,we;const qe=setTimeout(()=>_e(!0),0),ye=(De,tt)=>{se&&(be("video/status",{nextStatus:De,info:tt||null}),w(De),P(tt||null),De==="playing"&&La(),["error","failed","disconnected","closed"].includes(De)&&Bn())};return we=new _p({url:a.url,token:a.token,video:D.current,receiveAudio:!He,onStatus:ye}),we.start().catch(De=>{se&&(w("error"),P(De.message),Bn())}),()=>{se=!1,clearTimeout(qe),we?.stop()}},[qt,a?.url,a?.token,Le,Bn,La,He,be]),m.useEffect(()=>{fe==="stopped"&&a?.url&&Bn()},[fe,a?.url,Bn]),m.useEffect(()=>{const se=$.current;if(clearInterval(W.current),W.current=null,!se||!l?.url){pe(null),be("route/no-audio-url");return}const we=St&&At==="compressor",qe=St&&At==="duck";if(we&&ul()){const De=V.current,tt=q.current,Ut=ue.current?.state==="running";se.volume=0,be("route/graph",{contextRunning:Ut,elementVolume:se.volume}),tt&&(tt.gain.value=Ut?Nt:0),De&&(De.threshold.value=Cn.threshold,De.knee.value=Cn.knee,De.ratio.value=Cn.ratio,De.attack.value=Cn.attack,De.release.value=Cn.release),fn(),W.current=setInterval(()=>{if(ue.current?.state!=="running"){pe(null);return}const mn=De?.reduction;if(typeof mn=="number"&&mn<=xj){const Ze=Math.round(Math.abs(mn)*10)/10,Ft=Number.isInteger(Ze)?Ze.toFixed(0):Ze.toFixed(1);pe(`Level: ${Ft}dB`),be("compressor/reduction",{reduction:mn,formatted:Ft})}else pe(null)},180);return}se.volume=Nt,be("route/direct",{elementVolume:se.volume,duckMode:qe,brushOrVacuumActive:Kt}),V.current&&(V.current.threshold.value=0,V.current.knee.value=0,V.current.ratio.value=1,V.current.attack.value=.003,V.current.release.value=.25),q.current&&(q.current.gain.value=0),pe(qe&&Kt?"Level: ducking":null)},[l?.url,St,At,Kt,Nt,ul,fn,be]),m.useEffect(()=>{if(!l?.url||!$.current)return;let se=!0,we;const qe=(ye,De)=>{if(se){if(be("audio/status",{nextStatus:ye,info:De||null}),ce(De||(ye==="connected"?"connected":null)),le(tt=>ye==="connected"&&(tt==="playing"||tt==="connecting")||ye==="new"?tt:ye),ye==="playing"){fn(),zn();const tt=$.current;tt&&(tt.muted=!1,tt.play().then(()=>be("audio/play-ok")).catch(Ut=>be("audio/play-fail",{error:Ut?.message})))}["error","failed","disconnected","closed"].includes(ye)&&Ln()}};return we=new _p({url:l.url,token:l.token,video:$.current,audioOnly:!0,onStatus:qe}),we.start().catch(ye=>{se&&(le("error"),ce(ye.message),Ln())}),()=>{se=!1,we?.stop()}},[l?.url,l?.token,re,Ln,fn,zn,be]),m.useEffect(()=>{const se=$.current;if(!l?.url||!se){clearInterval(Q.current);return}if(!["connecting","connected","playing","paused"].includes(te)){clearInterval(Q.current);return}const qe=async()=>{const ye=$.current;ye&&(ye.muted=!1,!(!ye.paused&&!ye.ended)&&(be("retry/play-attempt"),await fn(),ye.play().then(()=>{be("retry/play-ok"),le(De=>De==="connected"?"playing":De),ce(De=>De==="paused"?null:De)}).catch(De=>{be("retry/play-fail",{error:De?.message}),ce(tt=>tt||De?.message||"autoplay blocked")})))};return qe(),Q.current=setInterval(qe,pj),()=>clearInterval(Q.current)},[l?.url,te,fn,be]),m.useEffect(()=>{const se=$.current;if(!se)return;const we=()=>{be("element/play"),le(Ze=>Ze==="error"?Ze:"playing"),ce(null)},qe=()=>{be("element/pause"),le(Ze=>["error","failed","disconnected","closed","stopped"].includes(Ze)?Ze:"paused"),ce(Ze=>Ze||"paused")},ye=()=>{be("element/ended"),le(Ze=>Ze==="error"?Ze:"stopped"),ce(Ze=>Ze||"ended")},De=()=>{const{error:Ze}=se,Ft=Ze?.message||"audio error";be("element/error",{error:Ft}),le("error"),ce(Ft)},tt=()=>be("element/waiting"),Ut=()=>be("element/canplay"),mn=()=>be("element/stalled");return se.addEventListener("play",we),se.addEventListener("pause",qe),se.addEventListener("ended",ye),se.addEventListener("error",De),se.addEventListener("waiting",tt),se.addEventListener("canplay",Ut),se.addEventListener("stalled",mn),()=>{se.removeEventListener("play",we),se.removeEventListener("pause",qe),se.removeEventListener("ended",ye),se.removeEventListener("error",De),se.removeEventListener("waiting",tt),se.removeEventListener("canplay",Ut),se.removeEventListener("stalled",mn)}},[l?.url,be]);const fs=r?.error?`Error: ${r.error}`:r?.objectUrl?"snapshot":r?.status||"waiting",Ci=qt?fs:a?.url?fe==="error"?`Error: ${z||"unknown"}`:z?`${fe} (${z})`:fe:"waiting",qi=l?.error?`Error: ${l.error}`:l?.url?te==="error"?`Error: ${ie||"unknown"}`:ie?`${te} (${ie})`:te:null,dn=y==="spectator",Ke=y==="none";return o.jsxs("div",{className:`flex flex-col gap-0.5 ${E?"h-full":""}`,children:[o.jsxs("div",{className:`relative w-full overflow-hidden bg-black ${E?"h-full flex-1":"aspect-[4/3]"}`,children:[qt?r?.objectUrl?o.jsx("img",{src:r.objectUrl,alt:d,className:"h-full w-full object-contain",draggable:!1}):o.jsx("div",{className:"flex h-full w-full items-center justify-center text-sm text-slate-300",children:"Waiting for frame…"}):o.jsx("video",{ref:D,muted:h||ze||He,playsInline:!0,autoPlay:!0,controls:!1,className:"h-full w-full object-contain"}),o.jsx("audio",{ref:$,autoPlay:!0,hidden:!0}),!Ke&&R?o.jsx(bj,{mobileHud:zt,turnSeconds:T,isActiveDriver:O,idleSkipSeconds:L}):null,Ke?null:o.jsx(yj,{frame:p,sensors:Mt,label:d,status:Ci,audioStatus:qi,levelStatus:ve,desktopLayout:rs,layoutFormat:v,variant:y,driverLabel:b,battery:Ba,showTopDown:wi,mobileHud:zt,mapPosition:ol,turnTimerText:A,labelScale:C}),Ke?null:o.jsx(Nj,{compact:zt}),!Ke&&os?o.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:${ua?1:0} motors:${An.length} fill:${Math.round(On*100)}%`}):null,Ke?null:o.jsx(Aj,{motors:An,fill:On,compact:zt}),Ke?null:o.jsx(jj,{battery:Ba,compact:zt}),!Ke&&dn&&Ba.available?o.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:o.jsx(jf,{visual:Ba,orientation:"vertical",variant:"inline",compact:zt,className:"h-full w-4"})}):null,!Ke&&f?o.jsx("div",{className:"pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2",children:o.jsxs("div",{className:`mx-auto w-fit rounded border border-amber-300/80 bg-black/75 text-amber-200 ${zt?"px-2 py-1 text-[0.6rem]":"px-3 py-1.5 text-sm"}`,children:[o.jsx("div",{className:"text-center",children:f}),o.jsx("div",{className:"pointer-events-auto mt-0",children:o.jsx(Ff,{id:"discord",label:"Join our Discord server while you wait!",url:Ei})})]})}):null]}),!Ke&&!dn&&o.jsxs("div",{className:"space-y-0.5",children:[o.jsx(vj,{sensors:Mt}),o.jsx("div",{className:"panel-section space-y-0.5 text-sm",children:o.jsx(jf,{visual:Ba,compact:zt})})]})]})}function vj({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),r=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+r(p)*360)%360} 100% 60%)`,d=6,h=12;return o.jsx("div",{className:"flex w-full items-center justify-center gap-0.5",children:l.map((p,x)=>{const v=r(p),y=xDate.now());m.useEffect(()=>{const W=setInterval(()=>R(Date.now()),150);return()=>clearInterval(W)},[]),a?.receivedAt&&j-a.receivedAt<200;const A=N,T=p==="mobile-portrait",O=A?"text-[0.45rem]":"text-[0.65rem]",L=A?"px-0.25 py-0.25":"px-1 py-0.5",Z=A?"px-0.25 py-0.25":"px-0.5 py-0.5",D=A?"text-[0.55rem]":"text-[0.8rem]",$=A?"left-0.5 top-0.5":"left-1 top-1",Y=A?"text-[0.5rem]":"text-[0.7rem]",F=A?"px-0.5 py-0.25":"px-1 py-0.5",Q=A?"left-0.5 top-1/2":"left-1 top-1/2",ne="bottom-0.5",ue={transform:`translateX(-50%) scale(${E})`,transformOrigin:"center bottom"},he="240px",V=T?.3:A?.33:.7,X={width:he,height:he,opacity:A?.6:.7,transform:M==="top-center"?`translateX(-50%) scale(${V})`:`scale(${V})`,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 W=[["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||"--"]],oe=!!l?.chargingSources?.homeBase,fe=l?.chargingState?.label||"",w=!!(fe&&fe.toLowerCase()!=="not charging"),z=l?.oiMode?.label||"Unknown",P=z.toLowerCase(),te=P==="full"?"bg-emerald-500/80 text-emerald-50":P==="safe"?"bg-amber-400/80 text-amber-950":P==="passive"?"bg-slate-700/80 text-slate-100":"bg-slate-700/60 text-slate-200",le=oe?"bg-emerald-500/80 text-emerald-50":"bg-slate-700/70 text-slate-200",ie=w?"bg-emerald-500/80 text-emerald-50":oe?"bg-amber-400/80 text-amber-950":"bg-slate-700/70 text-slate-200";return o.jsxs(o.Fragment,{children:[o.jsxs("div",{className:"pointer-events-none absolute inset-0 flex items-center justify-center",children:[o.jsx("div",{className:`absolute ${$} font-medium text-slate-100 ${O}`,children:o.jsxs("div",{className:"flex flex-col gap-0.5 leading-none",children:[o.jsxs("span",{children:["Status: ",r]}),f?o.jsxs("span",{children:["Audio: ",f]}):null,d?o.jsx("span",{className:"text-cyan-300",children:d}):null]})}),o.jsx("div",{className:`absolute ${Q} flex -translate-y-1/2 flex-col gap-0.5 bg-black/70 text-slate-100 ${O} ${L}`,children:o.jsxs("div",{className:"space-y-0.5 leading-tight",children:[o.jsxs("div",{className:"flex flex-col gap-0.5 text-[0.75rem] font-semibold uppercase tracking-wide",children:[o.jsx("span",{className:`rounded px-1.5 py-0.5 ${le}`,children:oe?"Docked":"Undocked"}),o.jsx("span",{className:`rounded px-1.5 py-0.5 ${ie}`,children:w?"Charging":"Not charging"}),o.jsxs("span",{className:`rounded px-1.5 py-0.5 ${te}`,children:["OI: ",z]})]}),W.map(([ce,ve])=>o.jsxs("span",{className:"flex items-center justify-between gap-0.5",children:[o.jsx("span",{className:"text-slate-400",children:ce}),o.jsx("span",{className:"font-semibold text-white",children:ve})]},ce))]})}),o.jsx("div",{className:`absolute ${ne} left-1/2`,style:ue,children:o.jsxs("div",{className:`flex items-center gap-0.5 bg-black/80 text-slate-100 ${Z} ${D}`,children:[o.jsx("span",{className:"font-semibold text-white",children:i||"Unnamed Rover"}),v?o.jsxs("span",{className:"text-slate-300",children:["• ",v]}):null]})})]}),b?o.jsx("div",{className:"pointer-events-none absolute rounded",style:{...X},children:o.jsx(Af,{sensors:l,size:240,overlay:!0})}):null]})}return o.jsxs("div",{className:"pointer-events-none absolute inset-0 flex items-center justify-center",children:[o.jsx("div",{className:`absolute ${$} font-medium text-slate-100 ${O}`,children:o.jsxs("div",{className:"flex flex-col gap-0.5 leading-none",children:[o.jsxs("span",{children:["Status: ",r]}),f?o.jsxs("span",{children:["Audio: ",f]}):null,d?o.jsx("span",{className:"text-cyan-300",children:d}):null]})}),C?o.jsx("div",{className:`absolute left-1/2 top-0.5 -translate-x-1/2 rounded bg-black/70 text-slate-100 ${F} ${Y}`,children:C}):null,o.jsx("div",{className:`absolute ${ne} left-1/2`,style:ue,children:o.jsx("div",{className:`flex gap-0.5 bg-black/80 text-slate-100 ${Z} ${D}`,children:o.jsxs("span",{children:['Rover: "',i||"Unnamed Rover",'"']})})}),b&&x!=="spectator"?o.jsx("div",{className:"pointer-events-none absolute rounded",style:{...X},children:o.jsx(Af,{sensors:l,size:240,overlay:!0})}):null]})}function bj({mobileHud:a=!1,turnSeconds:l=null,isActiveDriver:i=!1,idleSkipSeconds:r=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 r=="number";return o.jsx("div",{className:"pointer-events-none absolute inset-0 z-30 flex items-center justify-center bg-black/55",children:o.jsxs("div",{className:`flex flex-col items-center gap-0.5 rounded border border-amber-300/80 bg-black/70 ${p}`,children:[o.jsx("div",{className:`font-semibold text-amber-200 ${f}`,children:"IT IS YOUR TURN!"}),o.jsx("div",{className:`text-amber-200/80 ${d}`,children:"Start driving!"}),x?o.jsxs("div",{className:`text-red-100/90 ${h}`,children:["Idle skip in ",r,"s"]}):null]})})}const Sj={leftWheel:"Left wheel",rightWheel:"Right wheel",mainBrush:"Main brush",sideBrush:"Side brush",limiter:"Overcurrent limit"};function Aj({motors:a,fill:l=0,compact:i=!1}){if(!a?.length)return null;const r=a.map(y=>Sj[y]||y),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)),v=`${Math.round(x*100)}%`;return o.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:o.jsxs("div",{className:"relative h-full w-full",children:[o.jsx("div",{className:"absolute inset-0 overflow-hidden",children:o.jsx("div",{className:"h-full bg-red-700/60",style:{width:v}})}),o.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:[o.jsx("div",{children:"OVERCURRENT"}),o.jsx("div",{className:`mt-0 font-medium text-white ${p}`,children:r.join(", ")})]})]})})}function jj({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.",r=l?"p-2 top-6":"p-4 top-10",f=l?"text-sm":"text-2xl";return o.jsx("div",{className:`pointer-events-none absolute flex items-center justify-center bg-amber-900/60 left-1/2 -translate-x-1/2 ${r}`,children:o.jsx("div",{className:`text-center font-semibold text-white animate-pulse ${f}`,children:o.jsx("div",{children:i})})})}function Nj({compact:a=!1}){const{session:l}=$e(),{sendMessage:i,onInputFocus:r,onInputBlur:f,blurChat:d,registerInputRef:h,setTypingActive:p}=Vf(),{value:x}=rt("tts",{engine:"flite",voice:"rms",pitch:50}),[v,y]=m.useState(""),[b,N]=m.useState(!1),M=l?.role!=="spectator",C=l?.role==="spectator",E=l?.assignment?.roverId||null,R=!!m.useMemo(()=>l?.roster?.find(D=>String(D.id)===String(E))||null,[E,l?.roster])?.audio?.ttsEnabled,A=m.useMemo(()=>{if(!R)return null;const D=x?.engine==="espeak"?"espeak":"flite";if(D==="espeak"){let Y=Number.isFinite(x?.pitch)?Math.round(x.pitch):void 0;return typeof Y=="number"&&(Y=Math.max(0,Math.min(99,Y))),{speak:!0,engine:D,pitch:Y}}const $=typeof x?.voice=="string"?x.voice:void 0;return{speak:!0,engine:D,voice:$}},[x?.engine,x?.pitch,x?.voice,R]),T=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",O=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",L=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 Z(D){if(D.preventDefault(),!M)return;const $=v.trim();if($){N(!0);try{await i($,A),y(""),d(),p(!1)}catch(Y){alert(Y.message)}finally{N(!1)}}}return C?null:o.jsxs("form",{onSubmit:Z,className:T,children:[o.jsx("input",{className:O,value:v,onChange:D=>{const $=D.target.value;y($),p(!!$.trim())},onFocus:D=>{r(D),p(!!v.trim())},onBlur:D=>{f(D),p(!1)},onKeyDown:D=>{D.key==="Enter"&&!v.trim()&&(D.preventDefault(),d(),p(!1))},ref:D=>h(D,{target:"hud"}),placeholder:M?"Chat (TTS)":"Spectator",disabled:!M}),o.jsx("button",{type:"submit",disabled:!M||b,className:L,children:"Speak"})]})}function Zf({layoutFormat:a="desktop"}){const{session:l}=$e(),{state:{song:i,lastControlIntentAt:r},overcurrentLimiter:f}=Bt(),[d,h]=m.useState(()=>Date.now()),[p,x]=m.useState(!1),[v,y]=m.useState(null),b=m.useRef({active:!1,roverId:null});m.useEffect(()=>{if(l?.mode!=="turns")return;const te=setInterval(()=>h(Date.now()),250);return()=>clearInterval(te)},[l?.mode]);const N=l?.assignment?.roverId,C=!!(N&&l?.roster?l.roster.find(te=>String(te.id)===String(N)):null)?.media?.audioPublishUrl,E=N?l?.turnQueues?.[N]:null,j=l?.socketId||null,R=N?l?.activeDrivers?.[N]:null,A=!!(j&&R===j),T=m.useMemo(()=>{const te=E?.queue||[];if(!te.length||!E?.current||te.length<=1)return null;const le=te.findIndex(ie=>ie===E.current);return le===-1?te[0]||null:te[(le+1)%te.length]||null},[E?.queue,E?.current]),O=!!(j&&T===j),L=E?.deadline||null,Z=E?.idleDeadline||null,D=L?L-d:null,$=Z?Z-d:null,Y=l?.mode==="turns"&&O&&D!=null&&D<=5e3&&D>0,F=l?.mode!=="turns"||A||Y,Q=D!=null&&Number.isFinite(D)?Math.max(0,Math.ceil(D/1e3)):null,ne=$!=null&&Number.isFinite($)?Math.max(0,Math.ceil($/1e3)):null,ue=A?Q!=null?`${Q}s left`:null:O&&Q!=null?`Your turn in ${Q}s`:null,he=N?[...F?[{type:"rover",id:N,key:N}]:[],...C?[{type:"rover",id:`${N}-audio`,key:`${N}-audio`}]:[]]:[],V=pi(he),q=N&&F?V[N]:null,X=N&&C?V[`${N}-audio`]:null,W=Qf(N?[N]:[],{enabled:!!N,version:l?.mode}),oe=N&&W[N]||null,fe=is(N),w=N&&l?.roster?l.roster.find(te=>String(te.id)===String(N)):null,z=w?.battery??null,P=w?.name||(N?`Rover ${N}`:"");return m.useEffect(()=>{if(l?.mode!=="turns"){x(!1),y(null),b.current={active:!1,roverId:null};return}const te=b.current,le=A&&!te.active,ie=A&&N&&N!==te.roverId;le||ie?(x(!0),y(Date.now())):!A&&te.active&&(x(!1),y(null)),b.current={active:A,roverId:N}},[A,N,l?.mode]),m.useEffect(()=>{!p||!v||r>v&&x(!1)},[r,v,p]),o.jsx("section",{className:"panel",children:N?o.jsx(xi,{sessionInfo:q,videoMode:F?"whep":"snapshot",snapshotFeed:oe,audioSessionInfo:X,label:P,telemetryFrame:fe,batteryConfig:z,layoutFormat:a,overcurrentLimiter:f,songNote:i?.note,qualityNotice:F?null:"Preview feed (low FPS) until your turn.",showTurnCue:p,turnTimerText:ue,turnSeconds:Q,isActiveDriver:A,idleSkipSeconds:ne}):o.jsxs("div",{className:"panel-muted content-center text-center text-sm text-slate-400 aspect-[4/3]",children:[o.jsx("p",{children:"You are not assigned to a rover."}),o.jsx("p",{className:"mt-0",children:o.jsx("a",{href:"/spectate",className:"text-blue-400 underline hover:text-blue-500",children:"Click here to visit the spectator page."})})]})})}function Nf({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 o.jsx("span",{className:`rounded px-1 py-0.5 text-xs font-semibold leading-none ${i}`,children:a})}function go(a){if(!Number.isFinite(a))return 0;const l=a%360;return l<0?l+360:l}function wj(a){if(!Array.isArray(a)||a.length<3)return 0;const[l,i,r]=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(r)))/255,p=Math.max(f,d,h),x=Math.min(f,d,h),v=p-x;if(v===0)return 0;let y=0;return p===f?y=(d-h)/v%6:p===d?y=(h-f)/v+2:y=(f-d)/v+4,go(Math.round(y*60))}function Lp(a){const l=go(a),i=1,r=1-Math.abs(l/60%2-1);let f=0,d=0,h=0;return l<60?(f=i,d=r):l<120?(f=r,d=i):l<180?(d=i,h=r):l<240?(d=r,h=i):l<300?(f=r,h=i):(f=i,h=r),[Math.round(f*255),Math.round(d*255),Math.round(h*255)]}function zp(a){return a?Array.isArray(a.hsColor)?go(Number(a.hsColor[0])):Array.isArray(a.rgbColor)?wj(a.rgbColor):0:0}function Ej({entity:a,connected:l,onToggle:i,onSetColor:r}){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",v=!l||f,y=v||!h,[b,N]=m.useState(()=>zp(a)),M=m.useRef(b),C=m.useRef(!1),E=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 L=zp(a);M.current=L,N(L)},[a.rgbColor,a.hsColor,h]);const j=L=>{const Z=go(Number(L.target.value));M.current=Z,N(Z)},R=()=>{y||!r||r(a.id,Lp(M.current))},A=L=>{L.stopPropagation()},O=`rgb(${(h?Lp(M.current):[255,255,255]).join(",")})`;return o.jsxs("button",{type:"button",onClick:()=>i(a.id),disabled:v,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 ${E} disabled:opacity-60 disabled:hover:bg-inherit`,children:[o.jsxs("div",{className:"min-w-0 flex-1",children:[o.jsxs("div",{className:"flex items-center gap-0.5 text-sm leading-normal",children:[o.jsx("span",{className:"truncate font-semibold text-white",children:a.name||a.id}),h&&o.jsxs(o.Fragment,{children:[o.jsx(Nf,{label:x,tone:p}),!l&&o.jsx("span",{className:"text-xs text-amber-200",children:"Offline"})]})]}),h?o.jsx("div",{className:"-mt-0.5 w-full",onClick:A,onPointerDown:L=>{A(L),C.current=!0},onPointerUp:L=>{A(L),C.current=!1,R()},onPointerCancel:L=>{A(L),C.current=!1,R()},onKeyUp:L=>{A(L),R()},onBlur:R,children:o.jsx("div",{className:"relative w-full",children:o.jsx("input",{type:"range",min:"0",max:"360",step:"1",value:b,onChange:j,disabled:y,"aria-label":`Set color for ${a.name||a.id}`,className:"ha-hue-slider w-full"})})}):o.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-400",children:[o.jsx(Nf,{label:x,tone:p}),!l&&o.jsx("span",{className:"text-amber-200",children:" · Offline"})]})]}),h&&o.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:O}}),!h&&o.jsx("div",{className:"self-center text-xs font-semibold text-white/90",children:d?"Turn off":"Turn on"})]})}function cx(){const{state:{keymap:a}}=Bt(),{session:l,homeAssistantToggle:i,homeAssistantSetLightColor:r}=$e(),f=l?.homeAssistant,d=m.useMemo(()=>f?.entities||[],[f?.entities]),h=Mn(a?.homeAssistantOn?.[0]),p=Mn(a?.homeAssistantOff?.[0]);if(!f?.enabled)return o.jsxs("section",{className:"panel-section space-y-0.5 text-sm text-slate-400",children:[o.jsx("p",{className:"text-slate-300",children:"Room Controls"}),o.jsx("p",{className:"text-slate-500",children:"Not configured on the server."})]});if(d.length===0)return o.jsxs("section",{className:"panel-section space-y-0.5 text-sm text-slate-400",children:[o.jsx("p",{className:"text-slate-300",children:"Room Controls"}),o.jsx("p",{className:"text-slate-500",children:"No lights or switches configured."})]});const x=!!f?.connected;return o.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[o.jsxs("header",{className:"flex items-center justify-between gap-0.5 text-sm text-slate-400",children:[o.jsxs("div",{className:"flex items-center gap-0.5",children:[o.jsx("p",{children:"Room Controls"}),o.jsx("span",{className:"text-xs text-slate-500",children:d.length}),o.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-300 background-black",children:[o.jsxs("span",{className:"flex items-center gap-0.5",children:[o.jsx("span",{children:"On"}),h?o.jsx(Up,{label:h}):null]}),o.jsxs("span",{className:"flex items-center gap-0.5",children:[o.jsx("span",{children:"Off"}),p?o.jsx(Up,{label:p}):null]})]})]}),o.jsx(Nf,{label:x?"Connected":"Offline",tone:x?"success":"warn"})]}),o.jsx("div",{className:"flex flex-wrap gap-0.5",children:d.map(v=>o.jsx(Ej,{entity:v,connected:x,onToggle:i,onSetColor:r},v.id))})]})}function Up({label:a}){return a?o.jsx("span",{className:"rounded border border-white/40 px-1 text-[0.7rem] text-white",children:a}):null}function ux(){const{login:a,setRole:l}=$e(),[i,r]=m.useState(""),[f,d]=m.useState(""),[h,p]=m.useState(!1);async function x(v){v.preventDefault(),p(!0);try{await a(i,f),r(""),d("")}catch(y){alert(y.message)}finally{p(!1)}}return o.jsxs("div",{className:"panel-section space-y-0.5 text-base",children:[o.jsx("p",{className:"text-sm text-slate-400",children:"Admin login"}),o.jsxs("form",{className:"flex flex-col gap-0.5",onSubmit:x,children:[o.jsx("input",{className:"field-input",placeholder:"Username",value:i,onChange:v=>r(v.target.value)}),o.jsx("input",{className:"field-input",placeholder:"Password",type:"password",value:f,onChange:v=>d(v.target.value)}),o.jsx("button",{type:"submit",disabled:h,className:"button-dark w-full disabled:opacity-50",children:h?"Logging in…":"Login"})]})]})}function Hp(...a){return a.filter(Boolean).join(" ")}function Cj(a){const l=a?.batteryState?.percentDisplay;return l==null?"--":`${l}%`}function qj(a){return a?.batteryState?a.batteryState.urgentActive?"text-red-400":a.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function Mj({title:a,roster:l=[],emptyText:i="No roster data.",renderActions:r=null}){return o.jsxs("div",{className:"space-y-0.5 text-sm",children:[a&&o.jsx("p",{className:"text-sm text-slate-400",children:a}),l.length===0?o.jsx("p",{className:"text-sm text-slate-500",children:i}):o.jsx("ul",{className:"space-y-0.5 text-sm",children:l.map(f=>o.jsxs("li",{className:Hp("surface flex flex-wrap items-center justify-between gap-0.5",f.locked&&"bg-red-900/40"),children:[o.jsxs("div",{children:[o.jsx("p",{className:"text-slate-200",children:f.name}),o.jsxs("p",{className:"text-xs text-slate-500 flex flex-wrap items-center gap-0.5",children:[o.jsx("span",{children:f.locked?"locked":"free"}),f.lockReason&&o.jsx("span",{className:"rounded bg-black/30 px-1",children:f.lockReason}),o.jsxs("span",{className:Hp("font-semibold",qj(f)),children:["Battery ",Cj(f)]})]})]}),r?r(f):null]},f.id))})]})}const Rj=[{key:"open",label:"Open"},{key:"turns",label:"Turns"},{key:"admin",label:"Admin"},{key:"lockdown",label:"Lockdown"}];function kj(){const{session:a,lockRover:l,setMode:i,requestControl:r,setCommunityGoal:f,setAdminReason:d,rebootRover:h,rebootServer:p,adminLogs:x}=$e(),v=m.useMemo(()=>a?.roster??[],[a?.roster]),[y,b]=m.useState({}),[N,M]=m.useState({}),[C,E]=m.useState(!1),j=a?.health||null,R=a?.communityGoal?.text||"",A=a?.communityGoal?.updatedAt||null,[T,O]=m.useState(R),L=a?.adminReason?.text||"",Z=a?.adminReason?.updatedAt||null,[D,$]=m.useState(L),Y=a?.role==="admin"||a?.role==="lockdown"||a?.role==="lockdown-admin",F=a?.mode??"open",Q=async(w,z)=>{try{await l(w,z),b(P=>({...P,[w]:z}))}catch(P){alert(P.message)}},ne=async w=>{const z=w.target.value;try{await i(z)}catch(P){alert(P.message)}},ue=async w=>{try{await r(w,{force:!0})}catch(z){alert(z.message)}},he=async w=>{if(!(!w?.id||!window.confirm(`Reboot rover "${w.name||w.id}" now?`))){M(P=>({...P,[w.id]:!0}));try{await h(w.id)}catch(P){alert(P.message)}finally{M(P=>({...P,[w.id]:!1}))}}},V=async()=>{if(window.confirm("Reboot the server host now? This will disconnect all users.")){E(!0);try{await p()}catch(z){alert(z.message),E(!1)}}},q=async()=>{try{await f(T)}catch(w){alert(w.message)}},X=async()=>{try{await f(null)}catch(w){alert(w.message)}},W=async()=>{try{await d(D)}catch(w){alert(w.message)}},oe=async()=>{try{await d(null)}catch(w){alert(w.message)}};m.useEffect(()=>{O(R)},[R]),m.useEffect(()=>{$(L)},[L]);const fe=m.useMemo(()=>{const w={};return v.forEach(z=>{w[z.id]=y[z.id]??z.locked}),w},[v,y]);return Y?o.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[o.jsxs("div",{className:"flex items-center justify-between gap-0.5 text-sm",children:[o.jsx("span",{children:"Admin controls"}),o.jsx("select",{value:F,onChange:ne,className:"field-input text-sm",children:Rj.map(w=>o.jsx("option",{value:w.key,children:w.label},w.key))})]}),o.jsx("div",{className:"flex gap-0.5 text-xs",children:o.jsx("button",{type:"button",onClick:V,disabled:C,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:C?"Server rebooting...":"Reboot Server"})}),o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[o.jsx("span",{children:"Community goal"}),A?o.jsxs("span",{children:["Updated ",new Date(A).toLocaleString()]}):null]}),o.jsx("input",{type:"text",value:T,onChange:w=>O(w.target.value),placeholder:"Set a community goal",className:"field-input text-sm"}),o.jsxs("div",{className:"flex gap-0.5 text-xs",children:[o.jsx("button",{type:"button",onClick:q,className:"button-dark",children:"Set goal"}),o.jsx("button",{type:"button",onClick:X,className:"button-danger",children:"Clear"})]})]}),o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[o.jsx("span",{children:"Admin mode reason"}),Z?o.jsxs("span",{children:["Updated ",new Date(Z).toLocaleString()]}):null]}),o.jsx("textarea",{value:D,onChange:w=>$(w.target.value),placeholder:"Set an admin mode reason",className:"field-input text-sm min-h-[3.5rem]"}),o.jsxs("div",{className:"flex gap-0.5 text-xs",children:[o.jsx("button",{type:"button",onClick:W,className:"button-dark",children:"Set reason"}),o.jsx("button",{type:"button",onClick:oe,className:"button-danger",children:"Clear"})]})]}),o.jsx(Mj,{roster:v,renderActions:w=>o.jsxs("div",{className:"flex flex-wrap gap-0.5 text-xs",children:[o.jsx("button",{type:"button",onClick:()=>Q(w.id,!fe[w.id]),className:"button-dark",children:fe[w.id]?"Unlock":"Lock"}),o.jsx("button",{type:"button",onClick:()=>ue(w.id),className:"button-dark",children:"Force"}),o.jsx("button",{type:"button",onClick:()=>he(w),disabled:!!N[w.id],className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:N[w.id]?"Rebooting...":"Reboot"})]})}),o.jsx(Tj,{health:j}),o.jsx(_j,{entries:x})]}):null}function Tj({health:a}){if(!a)return null;const l=a.replay||{sources:[],readyCount:0,totalCount:0},i=a.snapshots||{rovers:[],rooms:[]},r=`${l.readyCount}/${l.totalCount} sources ready`,f=i.rovers.filter(h=>h.stale).length,d=i.rooms.filter(h=>h.stale).length;return o.jsxs("div",{className:"space-y-0.5",children:[o.jsx("div",{className:"panel-muted text-xs uppercase",children:"Health"}),o.jsxs("div",{className:"surface space-y-0.5 text-xs text-slate-200",children:[o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsx("span",{children:"Replay segments"}),o.jsx("span",{className:"text-slate-400",children:r})]}),o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsx("span",{children:"Rover snapshots"}),o.jsxs("span",{className:f?"text-amber-300":"text-emerald-300",children:[i.rovers.length-f,"/",i.rovers.length," ok"]})]}),o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsx("span",{children:"Room cameras"}),o.jsxs("span",{className:d?"text-amber-300":"text-emerald-300",children:[i.rooms.length-d,"/",i.rooms.length," ok"]})]})]}),o.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:l.sources.map(h=>o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsx("span",{children:h.label}),o.jsxs("span",{className:h.ready?"text-emerald-300":"text-amber-300",children:[h.recentCount,"/",h.neededCount]})]},`${h.type}:${h.id}`))}),o.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:i.rovers.map(h=>o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsx("span",{children:h.name}),o.jsx("span",{className:h.stale?"text-amber-300":"text-emerald-300",children:h.stale?"stale":"ok"})]},`rover:${h.id}`))}),o.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:i.rooms.map(h=>o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsx("span",{children:h.name}),o.jsx("span",{className:h.stale?"text-amber-300":"text-emerald-300",children:h.error?"error":h.stale?"stale":"ok"})]},`room:${h.id}`))})]})}function _j({entries:a}){const l=a||[];return o.jsxs("div",{className:"panel-section space-y-0.5 text-base",children:[o.jsxs("div",{className:"flex items-center justify-between text-sm text-slate-400",children:[o.jsx("span",{children:"Admin IP log"}),o.jsx("span",{children:l.length})]}),o.jsx("div",{className:"surface h-64 overflow-y-auto font-mono text-xs",children:l.length===0?o.jsx("p",{children:"No admin log entries yet."}):l.slice().reverse().map(i=>o.jsxs("div",{className:"surface",children:[o.jsx("span",{className:"text-amber-400",children:i.ts?new Date(i.ts).toLocaleTimeString():"--"})," ",i.label&&o.jsxs("span",{className:"text-teal-400",children:["[",i.label,"]"]})," ",o.jsx("span",{className:"text-slate-200",children:i.message})," ",i.ip&&o.jsx("span",{className:"text-cyan-300",children:i.ip})," ",i.meta&&o.jsx("span",{className:"text-slate-500",children:JSON.stringify(i.meta)})]},i.id))}),o.jsx("p",{className:"text-xs text-slate-500",children:"Admin-only log stream; IPs never appear in user data."})]})}const Dj=[{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:"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 Oj(a){return a.reduce((l,i)=>((l[i.group]||(l[i.group]=[])).push(i),l),{})}function Bj(a,l){const i=Number(a);return Number.isFinite(i)?Math.max(0,Math.min(500,i)):l}const Jf=5,vi=500,yi=1,If=100;function Lj(a,l){const i=Number(a);return Number.isFinite(i)?Math.max(Jf,Math.min(vi,i)):l}function no(a,l){const i=Number(a);return Number.isFinite(i)?Math.max(yi,Math.min(If,i)):l}function zj(a){const i=(no(a,a)-yi)/(If-yi),r=vi-i*(vi-Jf);return Math.round(r)}function Uj(a){const l=Lj(a,a),i=(vi-l)/(vi-Jf),r=yi+i*(If-yi);return Math.round(r)}function Hj(a){const[l,i]=m.useState(null);return m.useEffect(()=>{if(!l)return;function r(f){if(f.preventDefault(),f.key==="Escape"){i(null);return}const d=Ni(f.key??"");d&&(a(l,d,f),i(null))}return window.addEventListener("keydown",r,{capture:!0}),()=>window.removeEventListener("keydown",r,{capture:!0})},[l,a]),{active:l,startCapture:i,cancel:()=>i(null)}}function Qr({label:a,description:l,value:i,onChange:r,min:f=0,max:d=500,step:h=5}){return o.jsxs("label",{className:"surface-muted block p-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[o.jsx("span",{className:"font-semibold text-slate-100",children:a}),o.jsx("input",{type:"number",min:f,max:d,value:i,onChange:p=>r(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&&o.jsx("p",{className:"text-[0.65rem] text-slate-500",children:l}),o.jsxs("div",{className:"mt-0 flex items-center gap-0.5",children:[o.jsx("input",{type:"range",min:f,max:d,step:h,value:i,onChange:p=>r(p.target.value),className:"h-2 flex-1 accent-emerald-400"}),o.jsx("span",{className:"w-12 text-right text-[0.75rem] font-mono text-slate-400",children:i})]})]})}function Vj(){const{state:{keymap:a},actions:{updateKeyBinding:l,resetKeyBindings:i}}=Bt(),{value:r,save:f}=rt("inputs",qn),d={...qn.keyboard,...r.keyboard??{}},h=m.useMemo(()=>typeof d.tiltSpeed=="number"?no(d.tiltSpeed,qn.keyboard.tiltSpeed):typeof d.tiltIntervalMs=="number"?no(Uj(d.tiltIntervalMs),qn.keyboard.tiltSpeed):qn.keyboard.tiltSpeed,[d.tiltIntervalMs,d.tiltSpeed]),p=m.useMemo(()=>Oj(Dj),[]),{active:x,startCapture:v,cancel:y}=Hj((C,E)=>{l(C,E)}),b=m.useCallback(C=>a?.[C]?.[0]||ui[C]?.[0]||"",[a]),N=m.useCallback((C,E)=>{const j=qn.keyboard,R=Bj(E,j[C]);f(A=>({...A??{},keyboard:{...A?.keyboard??j,[C]:R}}))},[f]),M=m.useCallback(C=>{const E=qn.keyboard,j=no(C,E.tiltSpeed),R=zj(j);f(A=>({...A??{},keyboard:{...A?.keyboard??E,tiltSpeed:j,tiltIntervalMs:R}}))},[f]);return o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsxs("div",{children:[o.jsx("p",{className:"text-xs text-slate-400",children:"Keyboard layout"}),o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Per-browser · click to change a binding"})]}),o.jsx("button",{type:"button",onClick:()=>i(),className:"button-dark text-xs",children:"Reset defaults"})]}),o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Keyboard speeds"}),o.jsxs("div",{className:"space-y-0.5",children:[o.jsx(Qr,{label:"Base speed",description:"Normal driving speed",value:d.baseSpeed,onChange:C=>N("baseSpeed",C)}),o.jsx(Qr,{label:"Turbo speed",description:"Used when holding the boost modifier",value:d.turboSpeed,onChange:C=>N("turboSpeed",C)}),o.jsx(Qr,{label:"Precision speed",description:"Used when holding the precision modifier",value:d.precisionSpeed,onChange:C=>N("precisionSpeed",C)})]})]}),o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Camera tilt"}),o.jsx(Qr,{label:"Tilt speed",description:"Higher = faster when holding tilt keys",value:h,onChange:M,min:1,max:100,step:1})]}),o.jsx("div",{className:"space-y-0.5",children:Object.entries(p).map(([C,E])=>o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.75rem] text-slate-400",children:C}),o.jsx("div",{className:"space-y-0.5",children:E.map(j=>{const R=b(j.id),A=x===j.id;return o.jsxs("div",{className:"surface-muted flex items-center justify-between text-xs",children:[o.jsxs("div",{children:[o.jsx("p",{className:"font-semibold text-slate-100",children:j.label}),o.jsx("p",{className:"text-[0.65rem] text-slate-400",children:Mn(R)})]}),o.jsxs("div",{className:"flex items-center gap-0.5",children:[A&&o.jsx("button",{type:"button",onClick:()=>y(),className:"button-danger text-[0.7rem]",children:"Cancel"}),o.jsx("button",{type:"button",onClick:()=>v(j.id),className:`${A?"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:A?"Press a key…":"Change"})]})]},j.id)})})]},C))})]})}const sa=new Intl.NumberFormat(void 0,{maximumFractionDigits:2}),Yj=[{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"}],Vp=.45,$j=.6;function ri({label:a,description:l,min:i,max:r,step:f,value:d,onChange:h}){return o.jsxs("label",{className:"surface-muted block p-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[o.jsx("span",{className:"font-semibold text-slate-100",children:a}),o.jsx("span",{className:"font-mono text-slate-400",children:sa.format(d)})]}),l&&o.jsx("p",{className:"text-[0.65rem] text-slate-500",children:l}),o.jsx("input",{type:"range",min:i,max:r,step:f,value:d,onChange:p=>h(Number(p.target.value)),className:"mt-0 w-full accent-emerald-400"})]})}function Gj(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} ${sa.format(a.threshold??.6)}`}return"Unassigned"}function Xj(a){return a.reduce((l,i)=>((l[i.section]||(l[i.section]=[])).push(i),l),{})}function Qj(a,l){if(!a||a.length===0)return null;if(l){const i=a.find(r=>r.signature===l);if(i)return i}return a[0]}function Kj(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 Fj(a,l,i){const r=(a.axes??[]).map((d,h)=>({index:h,delta:Math.abs((d??0)-(l.axes?.[h]??0)),value:d}));if(r.sort((d,h)=>h.delta-d.delta),i.kind==="axisPair"){const d=r.filter(h=>h.delta>Vp).slice(0,2);return d.length<2?null:{kind:"axisPair",x:d[0].index,y:d[1].index,...i.invertDefaults??{}}}const f=r.find(d=>d.delta>Vp);return f?{kind:"axis",index:f.index,...i.invertDefaults??{}}:null}function Zj(a,l,i){const r=a.buttons??[];for(let d=0;d$j)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 Jj(a,l,i){if(!a)return null;if(i.kind==="axis"||i.kind==="axisPair"){const r=Fj(a,l,i);if(r)return r}return Zj(a,l,i)}function Ij(){const a=MA(),{value:l,save:i}=rt("gamepad",Da),[r,f]=m.useState(null),d=m.useRef(null),h=m.useMemo(()=>Xj(Yj),[]),p=m.useMemo(()=>Qj(a.pads,l.activeSignature),[a.pads,l.activeSignature]),x=p?.signature??null,v=m.useMemo(()=>x?l?.profiles?.[x]??l?.defaults?.profile??ia:l?.defaults?.profile??ia,[x,l?.defaults?.profile,l?.profiles]);m.useEffect(()=>{!p||!x||l?.profiles?.[x]||i(j=>{const R=j??Da;if(R.profiles?.[x])return R;const A=R?.defaults?.profile??ia,T=Kg(p,A);return{...R,profiles:{...R.profiles??{},[x]:T}}})},[p,x,l?.profiles,i]),m.useEffect(()=>{d.current=null},[r,x]),m.useEffect(()=>{if(!r||!p)return;if(!d.current){d.current=Kj(p);return}const j=Jj(p,d.current,r);j&&(i(R=>{const A=R??Da,T=A.profiles?.[x]??A?.defaults?.profile??ia,O={...T,bindings:{...T.bindings??{},[r.id]:{...T.bindings?.[r.id]??{},kind:r.kind,sources:[j]}}};return{...A,profiles:{...A.profiles??{},[x]:O}}}),f(null))},[p,x,r,i]);const y=m.useCallback(j=>{i(R=>({...R??Da,activeSignature:j||null}))},[i]),b=m.useCallback(j=>{i(R=>{const A=R??Da,T=(x&&A.profiles?.[x])??A?.defaults?.profile??ia,O={...T,calibration:{...T.calibration??{},...j}};return x?{...A,profiles:{...A.profiles??{},[x]:O}}:{...A,defaults:{...A.defaults??{},profile:O}}})},[x,i]),N=m.useCallback((j,R)=>{i(A=>{const T=A??Da,O=(x&&T.profiles?.[x])??T?.defaults?.profile??ia,L=R(O.bindings?.[j]??{}),Z={...O,bindings:{...O.bindings??{},[j]:L}};return x?{...T,profiles:{...T.profiles??{},[x]:Z}}:{...T,defaults:{...T.defaults??{},profile:Z}}})},[x,i]),M=m.useCallback(j=>{N(j.id,R=>({...R,sources:[]}))},[N]),C=m.useCallback((j,R)=>{N(j.id,A=>{const T=Array.isArray(A.sources)?[...A.sources]:[];if(!T[0])return A;const O={...T[0]};return R==="invertX"?O.invertX=!O.invertX:R==="invertY"?O.invertY=!O.invertY:O.invert=!O.invert,T[0]=O,{...A,sources:T}})},[N]),E=m.useMemo(()=>p?{outputs:Fg(p,v)}:null,[p,v]);return o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsx("div",{className:"flex items-center justify-between",children:o.jsxs("div",{children:[o.jsx("p",{className:"text-xs text-slate-400",children:"Controller"}),o.jsx("p",{className:"text-[0.65rem] text-slate-500",children:p?"Move sticks or press buttons to bind":"Connect a controller to configure."}),r&&o.jsxs("p",{className:"mt-0 text-[0.7rem] text-emerald-400",children:["Capturing ",r.label,"…"]})]})}),o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Connected controller"}),a.pads.length===0?o.jsx("p",{className:"text-[0.7rem] text-slate-400",children:"No controller detected."}):o.jsxs("div",{className:"flex items-center justify-between gap-0.5 text-xs",children:[o.jsx("select",{value:x??"",onChange:j=>y(j.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(j=>o.jsx("option",{value:j.signature,children:j.id||"Unknown controller"},j.signature))}),o.jsx("span",{className:"text-[0.7rem] text-slate-400",children:p?.mapping??"unknown"})]})]}),o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Calibration"}),o.jsxs("div",{className:"space-y-0.5",children:[o.jsx(ri,{label:"Drive deadzone",description:"Ignore small drive stick drift",min:0,max:.6,step:.01,value:v.calibration?.driveDeadzone??.18,onChange:j=>b({driveDeadzone:j})}),o.jsx(ri,{label:"Camera deadzone",description:"Ignore small camera tilt drift",min:0,max:.4,step:.01,value:v.calibration?.cameraDeadzone??.08,onChange:j=>b({cameraDeadzone:j})}),o.jsx(ri,{label:"Aux deadzone",description:"Ignore small trigger noise",min:0,max:.4,step:.01,value:v.calibration?.auxDeadzone??.05,onChange:j=>b({auxDeadzone:j})}),o.jsx(ri,{label:"Side brush scale",description:"Scale side brush output",min:.3,max:1,step:.05,value:v.calibration?.auxSideScale??.55,onChange:j=>b({auxSideScale:j})}),o.jsxs("label",{className:"surface-muted block p-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[o.jsx("span",{className:"font-semibold text-slate-100",children:"Camera mode"}),o.jsxs("select",{value:v.calibration?.cameraMode??"absolute",onChange:j=>b({cameraMode:j.target.value}),className:"rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:[o.jsx("option",{value:"absolute",children:"Absolute"}),o.jsx("option",{value:"velocity",children:"Velocity"})]})]}),o.jsx("p",{className:"text-[0.65rem] text-slate-500",children:"Absolute maps stick to angle; velocity moves over time."})]}),o.jsx(ri,{label:"Camera sensitivity",description:"Velocity mode degrees per second",min:10,max:180,step:5,value:v.calibration?.cameraSensitivity??60,onChange:j=>b({cameraSensitivity:j})})]})]}),o.jsx("div",{className:"space-y-0.5",children:Object.entries(h).map(([j,R])=>o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:j}),o.jsx("div",{className:"space-y-0.5",children:R.map(A=>{const O=v.bindings?.[A.id]?.sources?.[0]??null;return o.jsxs("div",{className:"surface-muted flex items-center justify-between text-xs",children:[o.jsxs("div",{children:[o.jsx("p",{className:"font-semibold text-slate-100",children:A.label}),o.jsx("p",{className:"text-[0.65rem] text-slate-400",children:Gj(O)})]}),o.jsxs("div",{className:"flex items-center gap-0.5",children:[A.kind==="axisPair"&&o.jsxs(o.Fragment,{children:[o.jsx("button",{type:"button",disabled:!O,onClick:()=>C(A,"invertX"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert X"}),o.jsx("button",{type:"button",disabled:!O,onClick:()=>C(A,"invertY"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert Y"})]}),A.kind==="axis"&&o.jsx("button",{type:"button",disabled:!O,onClick:()=>C(A),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert"}),o.jsx("button",{type:"button",onClick:()=>M(A),className:"button-dark text-[0.7rem]",children:"Clear"}),o.jsx("button",{type:"button",onClick:()=>f(A),className:`${r?.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:r?.id===A.id?"Waiting…":"Capture"})]})]},A.id)})})]},j))}),o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Diagnostics"}),p?o.jsxs("div",{className:"space-y-0.5 text-[0.7rem] text-slate-300",children:[o.jsx("p",{className:"text-slate-400",children:"Raw axes"}),o.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:p.axes.map((j,R)=>o.jsxs("span",{className:"font-mono text-slate-300",children:["A",R,": ",sa.format(j)]},`axis-${R}`))}),o.jsx("p",{className:"text-slate-400",children:"Raw buttons"}),o.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:p.buttons.map((j,R)=>o.jsxs("span",{className:"font-mono text-slate-300",children:["B",R,": ",sa.format(j.value)," ",j.pressed?"●":""]},`btn-${R}`))}),E?.outputs&&o.jsxs(o.Fragment,{children:[o.jsx("p",{className:"text-slate-400",children:"Mapped outputs"}),o.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[o.jsxs("span",{className:"font-mono text-slate-300",children:["Drive: ",sa.format(E.outputs.driveVector.x),","," ",sa.format(E.outputs.driveVector.y)]}),o.jsxs("span",{className:"font-mono text-slate-300",children:["Camera: ",sa.format(E.outputs.cameraAxis)]}),o.jsxs("span",{className:"font-mono text-slate-300",children:["Main: ",sa.format(E.outputs.auxAxis.main)]}),o.jsxs("span",{className:"font-mono text-slate-300",children:["Side: ",sa.format(E.outputs.auxAxis.side)]})]})]})]}):o.jsx("p",{className:"text-[0.7rem] text-slate-400",children:"No controller detected."})]})]})}const Wj={drive:"Drive wheels",aux:"Aux motors"};function Yp(a){return Number.isFinite(a)?`${Math.round(a*100)}%`:"--"}function Pj({value:a,color:l="bg-emerald-500"}){const i=`${Math.round(Math.max(0,Math.min(1,a))*100)}%`;return o.jsx("div",{className:"h-2 w-full overflow-hidden rounded bg-slate-800",children:o.jsx("div",{className:`h-full ${l}`,style:{width:i}})})}function e2(){const{state:{roverId:a},overcurrentLimiter:l}=Bt(),i=m.useMemo(()=>Wl.map(r=>r.key),[]);return o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[o.jsx("span",{children:"Overcurrent limiter"}),o.jsx("span",{children:l?.adminImmune?"Admin immune":"Active"})]}),a?o.jsxs("div",{className:"space-y-0.5",children:[i.map(r=>{const f=l?.caps?.[r]?.cap??0,d=l?.overcurrent?.groups?.[r]??!1,h=l?.scales?.perGroup?.[r]??1;return o.jsxs("div",{className:"surface space-y-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs",children:[o.jsx("span",{className:"text-slate-200",children:Wj[r]||r}),o.jsx("span",{className:d?"text-red-300":"text-slate-400",children:d?"overcurrent":"ok"})]}),o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-[0.7rem] text-slate-400",children:[o.jsx("span",{children:"Cap"}),o.jsx("span",{children:Yp(f)})]}),o.jsx(Pj,{value:f,color:"bg-amber-500"}),o.jsxs("div",{className:"flex items-center justify-between text-[0.7rem] text-slate-400",children:[o.jsx("span",{children:"Scale"}),o.jsx("span",{children:Yp(h)})]})]})]},r)}),o.jsxs("div",{className:"surface text-[0.7rem] text-slate-400",children:[o.jsx("div",{children:`Down rate ${l?.config?.downRatePerSec}/s · Up rate ${l?.config?.upRatePerSec}/s`}),o.jsx("div",{children:`Release delay ${l?.config?.releaseDelaySec}s`}),o.jsx("div",{children:`Output rate ${l?.config?.outputRateMs}ms`})]})]}):o.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to view limiter status."})]})}const fx=m.createContext(null);function dx(){const a=m.useContext(fx);if(!a)throw new Error("Tabs components must be used within a provider.");return a}const $p={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",active:"bg-sky-600 text-white",inactive:"bg-zinc-900 text-slate-300 hover:bg-sky-500 hover:text-white"}},mx="primary";function Wf(...a){return a.filter(Boolean).join(" ")}function Pf({children:a,defaultTab:l,currentTab:i,onTabChange:r,variant:f=mx}){const[d,h]=m.useState(l??null),[p,x]=m.useState([]),v=m.useCallback(M=>M?(x(C=>C.includes(M)?C:[...C,M]),()=>x(C=>C.includes(M)?C.filter(E=>E!==M):C)):()=>{},[]),y=i??d??p[0]??null;m.useEffect(()=>{l&&i===void 0&&h(l)},[l,i]);const b=m.useCallback(M=>{M&&(i===void 0&&h(M),r&&r(M))},[i,r]),N=m.useMemo(()=>({activeTab:y,setActiveTab:b,registerTab:v,variant:f}),[y,b,v,f]);return o.jsx(fx.Provider,{value:N,children:a})}function ed({children:a,className:l=""}){return o.jsx("div",{className:Wf("flex gap-0.5",l),children:a})}function yn({id:a,children:l,className:i="",disabled:r=!1}){const{activeTab:f,setActiveTab:d,registerTab:h,variant:p}=dx();m.useEffect(()=>h(a),[a,h]);const x=$p[p]??$p[mx],v=f===a,y=Wf(x.base,v?x.active:x.inactive,r?"cursor-not-allowed opacity-50":"",i);return o.jsx("button",{type:"button",className:y,onClick:()=>!r&&d(a),"aria-pressed":v,"aria-disabled":r,children:l})}function td({children:a,className:l=""}){return o.jsx("div",{className:Wf("mt-0 space-y-0.5",l),children:a})}function bn({id:a,children:l,keepMounted:i=!1}){const{activeTab:r}=dx(),f=r===a;return!f&&!i?null:!f&&i?o.jsx("div",{hidden:!0,"aria-hidden":"true",children:l}):o.jsx(o.Fragment,{children:l})}function t2(){const{session:a}=$e(),l=m.useMemo(()=>JSON.stringify(a??{},null,2),[a]);return o.jsxs("div",{className:"panel-section flex min-h-0 flex-col gap-0.5 text-xs",children:[o.jsx("p",{className:"text-sm text-slate-400",children:"Session snapshot"}),o.jsx("pre",{className:"surface min-h-0 flex-1 overflow-y-auto font-mono text-[0.7rem] text-lime-300",children:l})]})}const Gp=300,Xp=240;function n2(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,r)=>typeof r=="function"?"[Function]":r instanceof Error?`${r.name}: ${r.message}`:r,0)}catch{return String(a)}}function a2(a){const i=a.map(r=>n2(r)).join(" ");return i.length>Xp?`${i.slice(0,Xp)}…`:i}function l2(){const a=_n(),[l,i]=m.useState([]),r=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,v,y)=>{i(b=>{const N=[...b,{id:`${Date.now()}-${Math.random().toString(16).slice(2)}`,direction:x,event:v,payload:a2(y),timestamp:new Date().toLocaleTimeString()}];return N.length>Gp?N.slice(-Gp):N})},d=(x,...v)=>f("in",x,v),h=(x,...v)=>f("out",x,v);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=(v,...y)=>(h(v,...y),x(v,...y)),p=()=>{a.emit=x}}return()=>{a.offAny(d),p&&p()}},[a]),o.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[o.jsxs("div",{className:"flex items-center justify-between text-sm text-slate-400",children:[o.jsx("span",{children:"Socket activity"}),o.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-400",children:[o.jsxs("span",{className:"surface-muted",children:["in ",r.in]}),o.jsxs("span",{className:"surface-muted",children:["out ",r.out]}),o.jsx("button",{type:"button",className:"button-dark text-xs",onClick:()=>i([]),children:"Clear"})]})]}),o.jsx("div",{className:"surface h-64 overflow-y-auto font-mono text-xs",children:l.length===0?o.jsx("p",{children:"No socket activity yet."}):l.slice().reverse().map(f=>o.jsxs("div",{className:"surface",children:[o.jsx("span",{className:"text-slate-400",children:f.timestamp})," ",o.jsxs("span",{className:f.direction==="in"?"text-emerald-400":"text-amber-400",children:["[",f.direction.toUpperCase(),"]"]})," ",o.jsx("span",{className:"text-cyan-300",children:f.event})," ",o.jsx("span",{className:"text-slate-200",children:f.payload})]},f.id))}),o.jsx("p",{className:"text-xs text-slate-500",children:"Logs are collected only while this panel is open."})]})}const s2=[{key:"start",label:"Start OI"},{key:"safe",label:"Safe"},{key:"full",label:"Full"},{key:"passive",label:"Passive"},{key:"dock",label:"Dock"}];function hx(){const{state:{roverId:a},actions:{sendOiCommand:l,setSensorStream:i}}=Bt(),r=!!a,[f,d]=rx(),h=_n(),{value:p,save:x}=rt("page",{hudMapDesktop:!1,connectionTransport:"websocket"}),{value:v,save:y}=rt("audio",Ct),b=p?.connectionTransport||"websocket",N=Number.isFinite(v?.masterVolume)?v.masterVolume:Ct.masterVolume,M=Number.isFinite(v?.alertVolume)?v.alertVolume:Ct.alertVolume,C=Number.isFinite(v?.roverVolume)?v.roverVolume:Ct.roverVolume,E=typeof v?.autoLevelEnabled=="boolean"?v.autoLevelEnabled:Ct.autoLevelEnabled,j=v?.autoLevelMode==="duck"?"duck":"compressor",R=m.useMemo(()=>[{key:"start",label:"Enable stream",enable:!0},{key:"stop",label:"Disable stream",enable:!1}],[]),A=D=>{a&&i(D)},T=D=>{const $=D.target.value;x(Y=>({...Y??{},connectionTransport:$})),h?.io?.opts&&(h.io.opts.transports=$==="polling"?["polling"]:["websocket","polling"],h.disconnect(),h.connect())},O=D=>$=>{const Y=Number($.target.value),F=Number.isFinite(Y)?Math.max(0,Math.min(1,Y)):0;y(Q=>({...Q??{},[D]:F}))},L=D=>{const $=!!D.target.checked;y(Y=>({...Y??{},autoLevelEnabled:$}))},Z=D=>{const $=D.target.value==="duck"?"duck":"compressor";y(Y=>({...Y??{},autoLevelMode:$}))};return o.jsxs(Pf,{defaultTab:"keybindings",children:[o.jsxs(ed,{children:[o.jsx(yn,{id:"keybindings",children:"Keybindings"}),o.jsx(yn,{id:"controller",children:"Controller"}),o.jsx(yn,{id:"page",children:"Page settings"}),o.jsx(yn,{id:"admin",children:"Admin"})]}),o.jsxs(td,{children:[o.jsx(bn,{id:"keybindings",children:o.jsx("div",{className:"space-y-0.5",children:o.jsx(Vj,{})})}),o.jsx(bn,{id:"controller",children:o.jsx("div",{className:"space-y-0.5",children:o.jsx(Ij,{})})}),o.jsx(bn,{id:"page",children:o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsx("p",{className:"text-slate-400",children:"HUD"}),o.jsxs("label",{className:"flex items-center gap-0.5 text-slate-200",children:[o.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:f,onChange:D=>d(D.target.checked)}),o.jsx("span",{children:"Show top-down map in HUD (desktop)"})]}),o.jsx("p",{className:"text-xs text-slate-500",children:"Mobile HUD keeps the map on by default."})]}),o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsx("p",{className:"text-slate-400",children:"Audio"}),o.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[o.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[o.jsx("span",{children:"Master volume"}),o.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(N*100),"%"]})]}),o.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:N,onChange:O("masterVolume"),className:"w-full accent-emerald-500"})]}),o.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[o.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[o.jsx("span",{children:"Alert/page sounds"}),o.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(M*100),"%"]})]}),o.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:M,onChange:O("alertVolume"),className:"w-full accent-emerald-500"})]}),o.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[o.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[o.jsx("span",{children:"Rover audio"}),o.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(C*100),"%"]})]}),o.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:C,onChange:O("roverVolume"),className:"w-full accent-emerald-500"})]}),o.jsxs("label",{className:"flex items-center justify-between gap-0.5 text-slate-200",children:[o.jsx("span",{children:"Auto-level rover audio"}),o.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:E,onChange:L})]}),o.jsxs("label",{className:"flex items-center justify-between gap-0.5 text-slate-200",children:[o.jsx("span",{children:"Auto-level mode"}),o.jsxs("select",{value:j,onChange:Z,className:"field-input text-sm",disabled:!E,children:[o.jsx("option",{value:"compressor",children:"Compressor (preferred)"}),o.jsx("option",{value:"duck",children:"Brush/vacuum ducking"})]})]}),o.jsx("p",{className:"text-xs text-slate-500",children:"Compressor evens out incoming audio; ducking lowers volume when cleaning motors are active."})]}),o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsx("p",{className:"text-slate-400",children:"Connection"}),o.jsxs("label",{className:"flex items-center justify-between gap-0.5 text-slate-200",children:[o.jsx("span",{children:"Transport"}),o.jsxs("select",{value:b,onChange:T,className:"field-input text-sm",children:[o.jsx("option",{value:"websocket",children:"WebSocket"}),o.jsx("option",{value:"polling",children:"Polling"})]})]}),o.jsx("p",{className:"text-xs text-slate-500",children:"Switching reconnects your session."})]})]})}),o.jsx(bn,{id:"admin",children:o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsx("p",{className:"text-slate-400",children:"Manual OI commands"}),o.jsx("div",{className:"surface flex flex-wrap gap-0.5",children:s2.map(D=>o.jsx("button",{type:"button",onClick:()=>l(D.key),disabled:!r,className:"button-dark text-xs disabled:opacity-30",children:D.label},D.key))})]}),o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsx("p",{className:"text-slate-400",children:"Sensor stream"}),o.jsx("div",{className:"surface flex gap-0.5",children:R.map(D=>o.jsx("button",{type:"button",onClick:()=>A(D.enable),disabled:!r,className:"flex-1 button-dark text-xs disabled:opacity-30",children:D.label},D.key))}),!r&&o.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to toggle streams."})]}),o.jsx(ux,{}),o.jsx(e2,{}),o.jsx(kj,{}),o.jsx(t2,{}),o.jsx(l2,{})]})})]})]})}const Qp={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 i2(a){return Qp[a]||Qp.desktop}function px({actionId:a,keymap:l}){const i=l?.[a]?.[0]??"";return o.jsx("span",{className:"rounded border border-slate-600 bg-slate-900/40 px-1 text-[0.7rem] text-slate-200",children:Mn(i)})}function Kp(a,l){return a.map((i,r)=>typeof i=="string"?o.jsx("span",{children:i},`text-${r}`):i?.action?o.jsx(px,{actionId:i.action,keymap:l},`pill-${i.action}-${r}`):i?.text?o.jsx("span",{children:i.text},`span-${r}`):null)}function xo(a,l,i){if(typeof a=="string")return a;if(Array.isArray(a?.segments))return Kp(a.segments,l);if(a&&typeof a=="object"){if(typeof a.text=="string")return a.text;if(Array.isArray(a))return Kp(a,l)}return String(a??"")}function r2({hero:a,keymap:l}){return a?o.jsxs("div",{className:"surface space-y-0.5 px-0.5 py-0.5",children:[o.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-0.5",children:[o.jsxs("div",{children:[o.jsx("p",{className:"text-sm font-semibold text-white",children:a.title}),a.subtitle&&o.jsx("p",{className:"text-xs text-slate-300",children:a.subtitle})]}),a.chips&&o.jsx("div",{className:"flex flex-wrap gap-0.5 text-[0.7rem] text-slate-200",children:a.chips.map(i=>o.jsx("span",{className:"rounded border border-slate-700 px-1 py-[2px]",children:i},i))})]}),a.bullets&&o.jsx("ul",{className:"space-y-0.5 text-[0.85rem] text-slate-200",children:a.bullets.map((i,r)=>{const f=Array.isArray(i?.segments)?`hero-${r}`:`hero-${r}`;return o.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:xo(i,l)},f)})})]}):null}function o2({block:a,keymap:l}){return o.jsxs("div",{className:"space-y-0.5",children:[o.jsx("p",{className:"text-xs font-semibold text-slate-200",children:a.title}),o.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:a.items.map((i,r)=>{const f=`item-${r}`;return o.jsx("li",{className:"surface-muted flex flex-wrap items-center gap-0.5 px-0.5 py-0.25",children:xo(i,l)},f)})})]})}function c2({block:a}){const l=a.tone==="info"?"border-cyan-500/40":"border-slate-700";return o.jsxs("div",{className:`surface space-y-0.5 border ${l} px-0.5 py-0.5`,children:[o.jsx("p",{className:"text-xs font-semibold text-slate-100",children:a.title}),a.body&&o.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:a.body.map((i,r)=>o.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:xo(i,{})},`callout-${r}`))})]})}function u2({group:a,keymap:l}){return o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"px-0.5 py-0.25 text-[0.75rem] font-semibold text-slate-200",children:a.title}),o.jsx("div",{className:"space-y-0.5 px-0.5 pb-0.25",children:a.items.map(i=>o.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-0.5 py-0.25 text-[0.8rem]",children:[o.jsx("span",{className:"text-slate-200",children:i.label}),o.jsx(px,{actionId:i.action,keymap:l})]},i.action))})]})}function f2({block:a,keymap:l}){return a?o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-200",children:[o.jsx("span",{className:"font-semibold",children:a.title}),a.footnote&&o.jsx("span",{className:"text-[0.7rem] text-slate-400",children:a.footnote})]}),o.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-0.5",children:a.groups?.map(i=>o.jsx(u2,{group:i,keymap:l},i.id))})]}):null}function d2({block:a}){return a?o.jsxs("div",{className:"space-y-0.5",children:[o.jsx("p",{className:"text-xs font-semibold text-slate-200",children:a.title}),o.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:a.items?.map((l,i)=>o.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:xo(l,{})},`gamepad-${i}`))})]}):null}function Fp({block:a,keymap:l}){if(!a)return null;switch(a.type){case"list":return o.jsx(o2,{block:a,keymap:l});case"callout":return o.jsx(c2,{block:a});case"keyboard":return o.jsx(f2,{block:a,keymap:l});case"gamepad":return o.jsx(d2,{block:a});default:return null}}function gx({layout:a,keymap:l}){const i=m.useMemo(()=>i2(a),[a]),r=l||{},f=i.main||[],d=i.aside||[];return o.jsxs("div",{className:"space-y-0.5",children:[o.jsx(r2,{hero:i.hero,keymap:r}),o.jsxs("div",{className:"grid gap-0.5",style:{gridTemplateColumns:"repeat(auto-fit, minmax(360px, 1fr))"},children:[o.jsx("div",{className:"space-y-0.5",children:f.map((h,p)=>o.jsx(Fp,{block:h,keymap:r},h.title||p))}),o.jsx("div",{className:"space-y-0.5",children:d.map((h,p)=>o.jsx(Fp,{block:h,keymap:r},h.title||p))})]})]})}function xx({layout:a,onOpenOverlay:l}){const{state:i}=Bt();return o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[o.jsx("span",{children:"Help"}),o.jsx("button",{type:"button",onClick:l,className:"button-dark px-1 py-0.25 text-[0.75rem]",children:"Open full help"})]}),o.jsx(gx,{layout:a,keymap:i?.keymap||{}})]})}const m2=["kal","rms","slt","ksp","bdl"],h2=Array.from({length:10},(a,l)=>l*10);function vo({hideInput:a=!1,hideSpectatorNotice:l=!1,fillHeight:i=!1,allowSpectatorInput:r=!1}){const{session:f}=$e(),{messages:d,typing:h,sendMessage:p,registerInputRef:x,onInputFocus:v,onInputBlur:y,blurChat:b,setTypingActive:N}=Vf(),{value:M,save:C}=rt("tts",{engine:"flite",voice:"rms",pitch:50}),[E,j]=m.useState(""),[R,A]=m.useState(!1),[T,O]=m.useState(!1),[L,Z]=m.useState(()=>M?.engine||"flite"),[D,$]=m.useState(()=>M?.voice||"rms"),[Y,F]=m.useState(()=>Number.isFinite(M?.pitch)?M.pitch:50),Q=f?.role!=="spectator"||r,ne=m.useRef(null),ue=f?.assignment?.roverId||null,V=!!m.useMemo(()=>f?.roster?.find(w=>String(w.id)===String(ue))||null,[ue,f?.roster])?.audio?.ttsEnabled,q=m.useMemo(()=>d.slice(-200),[d]),X=m.useMemo(()=>h||[],[h]);m.useEffect(()=>{ne.current&&(ne.current.scrollTop=ne.current.scrollHeight)},[q,X]),m.useEffect(()=>{const w=M?.engine||"flite",z=M?.voice||"rms",P=Number.isFinite(M?.pitch)?M.pitch:50;L!==w&&Z(w),D!==z&&$(z),Y!==P&&F(P)},[L,Y,M?.engine,M?.pitch,M?.voice,D]),m.useEffect(()=>{O(!!V)},[V]);const W=m.useMemo(()=>!V||!T?null:L==="espeak"?{speak:!0,engine:L,pitch:Y}:{speak:!0,engine:L,voice:D},[L,Y,T,V,D]);async function oe(w){if(w.preventDefault(),!Q||a)return;const P=E.replace(/\\n/g,` + `,document.head.appendChild(l)},[])}function uj(a,l){return typeof a=="string"&&a.trim()?a.trim().toLowerCase():typeof l=="string"&&l.trim()?l.trim().toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,""):""}function Ff({id:a,label:l,url:i,className:r=""}){if(!i)return null;cj();const f=uj(a,l),d=rj[f]||OS,h=oj[f]||null,p=l||a||"Link";return o.jsxs("a",{href:i,target:"_blank",rel:"noopener noreferrer","aria-label":p,className:`inline-flex items-center w-full px-0.5 py-0.5 text-sm font-medium text-white transition justify-center gap-1 rounded-md ${h?"":"bg-slate-700 hover:bg-slate-600"} ${r}`,style:h||void 0,children:[o.jsx(d,{className:"mr-0"}),p]})}const gi=10;function ox({batteryState:a=null,charge:l=null,config:i=null}){const r=a?.percentDisplay!=null&&Number.isFinite(a.percentDisplay)?Math.round(a.percentDisplay):null,f=a?.warnActive??null,d=a?.urgentActive??null,h=i?.Full??null,p=i?.Warn??null,x=i?.Urgent??null,v=l??a?.charge??null;let y=r;if(y==null&&(y=fj({charge:v,full:h,warn:p,urgent:x})),y==null)return{available:!1};const b=Math.max(0,Math.min(1,y/100)),N=f??(p!=null&&v!=null&&v<=p),M=d??(x!=null&&v!=null&&v<=x);return{available:!0,percent:b,percentDisplay:y,warnActive:N,urgentActive:M,warnDisplayPercent:gi}}function fj({charge:a,full:l,warn:i,urgent:r}){if(a!=null&&l!=null&&i!=null&&r!=null&&l>i&&i>r){const f=gi/100,d=1-f;if(a<=r)return 0;if(a<=i){const p=(a-r)/(i-r);return Math.round(Math.max(0,Math.min(1,p))*gi)}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 dj=1600,mj=800;function xn(...a){return a.filter(Boolean).join(" ")}function jf({visual:a,orientation:l="horizontal",variant:i="inline",compact:r=!1,showLabel:f,className:d=""}){const h=i==="background",p=l==="vertical",x=f??!h;if(!a?.available){if(h){const ne=gi,ue=xn("absolute z-10",p?"left-0 right-0 h-[2px]":"top-0 bottom-0 w-[2px]","bg-black/70",M&&!N&&"battery-tick-warn"),he=p?{bottom:`${ne}%`}:{left:`${ne}%`},V=xn("absolute inset-0 pointer-events-none",d);return o.jsxs("div",{className:V,children:[o.jsx("div",{className:"absolute inset-0 bg-slate-900/35"}),o.jsx("div",{className:ue,style:he})]})}return p?null:o.jsx("p",{className:"text-xs text-slate-500",children:"Battery telemetry unavailable"})}const v=a.percentDisplay??0,y=Math.max(0,Math.min(100,v)),b=a.warnDisplayPercent??gi,N=!!a.urgentActive,M=!!a.warnActive,C=N?`${mj}ms`:M?`${dj}ms`:void 0,j=h?N||M?"bg-red-500/45":"bg-emerald-500/45":N||M?"bg-red-500":"bg-emerald-500",R=N||M?"bg-red-900/35":"bg-slate-900/35",A=N||M?"bg-red-900/45":"bg-slate-900/35",T=xn(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 ne=p?{height:`${y}%`}:{width:`${y}%`},ue=xn("absolute",p?"bottom-0 left-0 right-0":"left-0 top-0 bottom-0",j,"transition-[width,height]",N&&"battery-urgent-flash"),he=xn("absolute z-10",p?"left-0 right-0 h-[2px]":"top-0 bottom-0 w-[2px]","bg-black/70",M&&!N&&"battery-tick-warn"),V=p?{bottom:`${b}%`}:{left:`${b}%`};return o.jsxs("div",{className:T,children:[o.jsx("div",{className:xn("absolute inset-0",R,N&&"battery-urgent-flash")}),o.jsx("div",{className:ue,style:{...ne,animationDuration:C}}),o.jsx("div",{className:he,style:V})]})}const O=xn("relative overflow-hidden rounded-sm border border-slate-800",N||M?A:"bg-zinc-900/90",N&&"battery-urgent-flash",p?"w-full flex-1":r?"h-4":"h-5"),L=p?{height:`${y}%`}:{width:`${y}%`},Z=xn("absolute",p?"bottom-0 left-0 right-0":"left-0 top-0 bottom-0",j,"transition-[width,height]",N&&"battery-urgent-flash"),D=xn("absolute z-10",p?"left-0 right-0 h-[2px]":"top-0 bottom-0 w-[2px]","bg-black/70",M&&!N&&"battery-tick-warn"),$=p?{bottom:`${b}%`}:{left:`${b}%`},Y=`Battery ${v}%`,F=xn("pointer-events-none absolute inset-0 flex items-center justify-center text-center font-semibold",p?r?"text-[0.55rem]":"text-[0.65rem]":"text-xs"),Q=xn("rounded px-1.5 py-0.5",h?"bg-black/30 text-slate-100":"bg-black/50 text-slate-100");return o.jsxs("div",{className:T,children:[o.jsxs("div",{className:O,children:[o.jsx("div",{className:Z,style:{...L,animationDuration:C}}),o.jsx("div",{className:D,style:$})]}),x&&!p?o.jsx("div",{className:F,children:o.jsx("span",{className:Q,children:Y})}):null,x&&p?o.jsx("div",{className:"mt-0.5 text-[0.65rem] font-semibold text-slate-100",children:o.jsxs("span",{className:Q,children:[v,"%"]})}):null]})}const Op=2e3,hj=3e3,pj=3e3,gj=.55,Bp=40,xj=-.75,Cn={threshold:-30,knee:10,ratio:8,attack:.002,release:.18};function xi({sessionInfo:a,audioSessionInfo:l,videoMode:i="whep",snapshotFeed:r=null,qualityNotice:f=null,label:d,forceMute:h=!1,telemetryFrame:p,batteryConfig:x,layoutFormat:v="desktop",hudVariant:y="default",driverLabel:b=null,hudForceMap:N=!1,hudMapPosition:M="top-center",hudLabelScale:C=1,fitParent:E=!1,overcurrentLimiter:j=null,showTurnCue:R=!1,turnTimerText:A=null,turnSeconds:T=null,isActiveDriver:O=!1,idleSkipSeconds:L=null}){const{session:Z}=$e(),D=m.useRef(null),$=m.useRef(null),Y=m.useRef(null),F=m.useRef(null),Q=m.useRef(null),ne=m.useRef(null),ue=m.useRef(null),he=m.useRef(null),V=m.useRef(null),q=m.useRef(null),X=m.useRef(!1),W=m.useRef(null),oe=m.useRef(!1),[fe,w]=m.useState("idle"),[z,P]=m.useState(null),[te,le]=m.useState("idle"),[ie,ce]=m.useState(null),[ve,pe]=m.useState(null),[Le,ae]=m.useState(0),[re,je]=m.useState(0),[ze,_e]=m.useState(!0),He=!!l?.url,qt=i==="snapshot",Mt=p?.sensors,{value:Lt}=rt("audio",Ct),on=Number.isFinite(Lt?.masterVolume)?Lt.masterVolume:Ct.masterVolume,cn=Number.isFinite(Lt?.roverVolume)?Lt.roverVolume:Ct.roverVolume,St=typeof Lt?.autoLevelEnabled=="boolean"?Lt.autoLevelEnabled:Ct.autoLevelEnabled,At=Lt?.autoLevelMode==="duck"?"duck":"compressor",gt=Math.max(0,Math.min(1,on*cn)),bo=Mt?.batteryChargeMah??null,rs=v==="desktop",zt=!rs,ol=zt?"top-right":M,[So]=rx(),wi=N||zt?!0:So,Ba=ox({charge:bo,config:x}),cl=Mt?.wheelOvercurrents||null,Dn=cl==null?[]:Object.entries(cl).filter(([,se])=>!!se).map(([se])=>se),et=j?.caps||null,un=j?.overcurrent?.groups||null,jt=typeof window<"u"&&new URLSearchParams(window.location.search).has("debugAudio"),os=typeof window<"u"&&new URLSearchParams(window.location.search).has("debugHud"),Ao=m.useMemo(()=>{if(!et)return null;const se=Number.isFinite(et?.drive?.cap)?et.drive.cap:1,we=Number.isFinite(et?.aux?.cap)?et.aux.cap:1;return Math.max(0,Math.min(1,1-Math.min(se,we)))},[et]),cs=!!j?.isActive,An=Dn.length?Dn:cs?["limiter"]:[],On=Ao??(Dn.length?1:0),ua=!!An.length,Kt=!!((Number(Mt?.mainBrushCurrentMa)||0)>Bp||(Number(Mt?.sideBrushCurrentMa)||0)>Bp||Mt?.wheelOvercurrents?.mainBrush||Mt?.wheelOvercurrents?.sideBrush),Nt=Math.max(0,Math.min(1,gt*(St&&At==="duck"&&Kt?gj:1))),be=m.useCallback((se,we={})=>{if(!jt)return;const qe=$.current,ye=ue.current,De={event:se,ts:Date.now(),roverLabel:d||null,hasDedicatedAudio:He,audioUrl:l?.url||null,autoLevelEnabled:St,autoLevelMode:At,baseRoverGain:gt,effectiveRoverGain:Nt,element:qe?{muted:qe.muted,volume:qe.volume,paused:qe.paused,ended:qe.ended,readyState:qe.readyState,networkState:qe.networkState}:null,context:ye?{state:ye.state,sampleRate:ye.sampleRate}:null,...we};try{console.log(`[AudioDebug] ${se} ${JSON.stringify(De)}`)}catch{console.log("[AudioDebug]",se,De)}},[jt,d,He,l?.url,St,At,gt,Nt]),Ei=Z?.socials?.find(se=>String(se?.id||se?.label||"").toLowerCase()==="discord")?.url||Z?.discord?.invite||null;m.useEffect(()=>{os&&console.log("[OvercurrentHUD]",{overlayVisible:ua,overlayMotors:An,overlayFill:On,limiterActive:cs,limiterCaps:et,limiterGroups:un,wheelOvercurrents:cl})},[os,On,An,ua,cs,et,un,cl]),m.useEffect(()=>{be("settings/update")},[be]);const Bn=m.useCallback(()=>{clearTimeout(Y.current),Y.current=setTimeout(()=>ae(Date.now()),Op)},[]),Ln=m.useCallback(()=>{clearTimeout(F.current),F.current=setTimeout(()=>je(Date.now()),Op)},[]),ul=m.useCallback(()=>{if(oe.current)return!1;const se=$.current;if(!se||typeof window>"u")return!1;try{const we=window.AudioContext||window.webkitAudioContext;if(typeof we!="function")return!1;ue.current||(ue.current=new we);const qe=ue.current;if(he.current||(he.current=qe.createMediaElementSource(se)),!V.current){const ye=qe.createDynamicsCompressor();ye.threshold.value=Cn.threshold,ye.knee.value=Cn.knee,ye.ratio.value=Cn.ratio,ye.attack.value=Cn.attack,ye.release.value=Cn.release,V.current=ye,be("graph/compressor-created",{threshold:ye.threshold.value,knee:ye.knee.value,ratio:ye.ratio.value,attack:ye.attack.value,release:ye.release.value})}return q.current||(q.current=qe.createGain()),X.current||(he.current.disconnect(),V.current.disconnect(),q.current.disconnect(),he.current.connect(V.current),V.current.connect(q.current),q.current.connect(qe.destination),X.current=!0,be("graph/connected")),!0}catch{return oe.current=!0,be("graph/failed"),!1}},[be]),fn=m.useCallback(async()=>{const se=ue.current;!se||se.state!=="suspended"||(be("context/resume-attempt"),await se.resume().catch(()=>{}),se.state==="running"&&St&&At==="compressor"&&q.current&&(q.current.gain.value=Nt,be("context/gain-restored",{gain:Nt})),be("context/resume-result"))},[be,St,At,Nt]),zn=m.useCallback(async()=>{if(!l?.url||!St||At!=="compressor")return;if(!ul()){be("unlock/no-graph");return}await fn();const we=$.current,qe=q.current;if(qe&&(qe.gain.value=Nt),!we){be("unlock/no-audio-element");return}we.volume=0,we.muted=!1,we.play().then(()=>be("unlock/force-play-ok")).catch(ye=>be("unlock/force-play-fail",{error:ye?.message}))},[l?.url,St,At,ul,fn,Nt,be]);m.useEffect(()=>{if(!l?.url||!St||At!=="compressor")return;const se=()=>{ue.current?.state!=="running"&&(be("unlock/gesture"),zn())};return window.addEventListener("pointerdown",se,{passive:!0,capture:!0}),window.addEventListener("touchstart",se,{passive:!0,capture:!0}),window.addEventListener("click",se,{passive:!0,capture:!0}),window.addEventListener("keydown",se),()=>{window.removeEventListener("pointerdown",se),window.removeEventListener("touchstart",se),window.removeEventListener("click",se),window.removeEventListener("keydown",se)}},[l?.url,St,At,zn,be]),m.useEffect(()=>{if(!l?.url||!St||At!=="compressor")return;const se=setInterval(()=>{const we=ue.current;["connecting","connected","playing","paused"].includes(te)&&(!we||we.state!=="running")&&(be("unlock/interval"),zn())},1e3);return()=>clearInterval(se)},[l?.url,St,At,te,zn,be]);const La=m.useCallback(async()=>{const se=D.current;if(se)try{se.muted=!0,await se.play()}catch{}},[]),fl=m.useCallback((se=0)=>{if(h||He)return;clearTimeout(ne.current);const we=()=>{clearTimeout(ne.current),ne.current=setTimeout(()=>{h||qe()},hj)},qe=async()=>{const ye=D.current;if(ye)try{await La(),ye.muted=!1,await ye.play(),_e(!1)}catch{ye.muted=!0,_e(!0),we()}};ne.current=setTimeout(qe,se)},[La,h,He]);m.useEffect(()=>()=>{clearTimeout(Y.current),clearTimeout(F.current),clearTimeout(ne.current),clearInterval(Q.current),clearInterval(W.current),X.current=!1,he.current?.disconnect?.(),V.current?.disconnect?.(),q.current?.disconnect?.(),ue.current?.state&&ue.current.state!=="closed"&&ue.current.close().catch(()=>{})},[]),m.useEffect(()=>{fe==="playing"&&fl(0)},[fe,fl]),m.useEffect(()=>{qt&&(w("snapshot"),P(null))},[qt]),m.useEffect(()=>{if(qt||!a?.url||!D.current)return;let se=!0,we;const qe=setTimeout(()=>_e(!0),0),ye=(De,tt)=>{se&&(be("video/status",{nextStatus:De,info:tt||null}),w(De),P(tt||null),De==="playing"&&La(),["error","failed","disconnected","closed"].includes(De)&&Bn())};return we=new _p({url:a.url,token:a.token,video:D.current,receiveAudio:!He,onStatus:ye}),we.start().catch(De=>{se&&(w("error"),P(De.message),Bn())}),()=>{se=!1,clearTimeout(qe),we?.stop()}},[qt,a?.url,a?.token,Le,Bn,La,He,be]),m.useEffect(()=>{fe==="stopped"&&a?.url&&Bn()},[fe,a?.url,Bn]),m.useEffect(()=>{const se=$.current;if(clearInterval(W.current),W.current=null,!se||!l?.url){pe(null),be("route/no-audio-url");return}const we=St&&At==="compressor",qe=St&&At==="duck";if(we&&ul()){const De=V.current,tt=q.current,Ut=ue.current?.state==="running";se.volume=0,be("route/graph",{contextRunning:Ut,elementVolume:se.volume}),tt&&(tt.gain.value=Ut?Nt:0),De&&(De.threshold.value=Cn.threshold,De.knee.value=Cn.knee,De.ratio.value=Cn.ratio,De.attack.value=Cn.attack,De.release.value=Cn.release),fn(),W.current=setInterval(()=>{if(ue.current?.state!=="running"){pe(null);return}const mn=De?.reduction;if(typeof mn=="number"&&mn<=xj){const Ze=Math.round(Math.abs(mn)*10)/10,Ft=Number.isInteger(Ze)?Ze.toFixed(0):Ze.toFixed(1);pe(`Level: ${Ft}dB`),be("compressor/reduction",{reduction:mn,formatted:Ft})}else pe(null)},180);return}se.volume=Nt,be("route/direct",{elementVolume:se.volume,duckMode:qe,brushOrVacuumActive:Kt}),V.current&&(V.current.threshold.value=0,V.current.knee.value=0,V.current.ratio.value=1,V.current.attack.value=.003,V.current.release.value=.25),q.current&&(q.current.gain.value=0),pe(qe&&Kt?"Level: ducking":null)},[l?.url,St,At,Kt,Nt,ul,fn,be]),m.useEffect(()=>{if(!l?.url||!$.current)return;let se=!0,we;const qe=(ye,De)=>{if(se){if(be("audio/status",{nextStatus:ye,info:De||null}),ce(De||(ye==="connected"?"connected":null)),le(tt=>ye==="connected"&&(tt==="playing"||tt==="connecting")||ye==="new"?tt:ye),ye==="playing"){fn(),zn();const tt=$.current;tt&&(tt.muted=!1,tt.play().then(()=>be("audio/play-ok")).catch(Ut=>be("audio/play-fail",{error:Ut?.message})))}["error","failed"].includes(ye)&&Ln()}};return we=new _p({url:l.url,token:l.token,video:$.current,audioOnly:!0,onStatus:qe}),we.start().catch(ye=>{se&&(le("error"),ce(ye.message),Ln())}),()=>{se=!1,we?.stop()}},[l?.url,l?.token,re,Ln,fn,zn,be]),m.useEffect(()=>{const se=$.current;if(!l?.url||!se){clearInterval(Q.current);return}if(!["connecting","connected","playing","paused"].includes(te)){clearInterval(Q.current);return}const qe=async()=>{const ye=$.current;ye&&(ye.muted=!1,!(!ye.paused&&!ye.ended)&&(be("retry/play-attempt"),await fn(),ye.play().then(()=>{be("retry/play-ok"),le(De=>De==="connected"?"playing":De),ce(De=>De==="paused"?null:De)}).catch(De=>{be("retry/play-fail",{error:De?.message}),ce(tt=>tt||De?.message||"autoplay blocked")})))};return qe(),Q.current=setInterval(qe,pj),()=>clearInterval(Q.current)},[l?.url,te,fn,be]),m.useEffect(()=>{const se=$.current;if(!se)return;const we=()=>{be("element/play"),le(Ze=>Ze==="error"?Ze:"playing"),ce(null)},qe=()=>{be("element/pause"),le(Ze=>["error","failed","disconnected","closed","stopped"].includes(Ze)?Ze:"paused"),ce(Ze=>Ze||"paused")},ye=()=>{be("element/ended"),le(Ze=>Ze==="error"?Ze:"stopped"),ce(Ze=>Ze||"ended")},De=()=>{const{error:Ze}=se,Ft=Ze?.message||"audio error";be("element/error",{error:Ft}),le("error"),ce(Ft)},tt=()=>be("element/waiting"),Ut=()=>be("element/canplay"),mn=()=>be("element/stalled");return se.addEventListener("play",we),se.addEventListener("pause",qe),se.addEventListener("ended",ye),se.addEventListener("error",De),se.addEventListener("waiting",tt),se.addEventListener("canplay",Ut),se.addEventListener("stalled",mn),()=>{se.removeEventListener("play",we),se.removeEventListener("pause",qe),se.removeEventListener("ended",ye),se.removeEventListener("error",De),se.removeEventListener("waiting",tt),se.removeEventListener("canplay",Ut),se.removeEventListener("stalled",mn)}},[l?.url,be]);const fs=r?.error?`Error: ${r.error}`:r?.objectUrl?"snapshot":r?.status||"waiting",Ci=qt?fs:a?.url?fe==="error"?`Error: ${z||"unknown"}`:z?`${fe} (${z})`:fe:"waiting",qi=l?.error?`Error: ${l.error}`:l?.url?te==="error"?`Error: ${ie||"unknown"}`:ie?`${te} (${ie})`:te:null,dn=y==="spectator",Ke=y==="none";return o.jsxs("div",{className:`flex flex-col gap-0.5 ${E?"h-full":""}`,children:[o.jsxs("div",{className:`relative w-full overflow-hidden bg-black ${E?"h-full flex-1":"aspect-[4/3]"}`,children:[qt?r?.objectUrl?o.jsx("img",{src:r.objectUrl,alt:d,className:"h-full w-full object-contain",draggable:!1}):o.jsx("div",{className:"flex h-full w-full items-center justify-center text-sm text-slate-300",children:"Waiting for frame…"}):o.jsx("video",{ref:D,muted:h||ze||He,playsInline:!0,autoPlay:!0,controls:!1,className:"h-full w-full object-contain"}),o.jsx("audio",{ref:$,autoPlay:!0,hidden:!0}),!Ke&&R?o.jsx(bj,{mobileHud:zt,turnSeconds:T,isActiveDriver:O,idleSkipSeconds:L}):null,Ke?null:o.jsx(yj,{frame:p,sensors:Mt,label:d,status:Ci,audioStatus:qi,levelStatus:ve,desktopLayout:rs,layoutFormat:v,variant:y,driverLabel:b,battery:Ba,showTopDown:wi,mobileHud:zt,mapPosition:ol,turnTimerText:A,labelScale:C}),Ke?null:o.jsx(Nj,{compact:zt}),!Ke&&os?o.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:${ua?1:0} motors:${An.length} fill:${Math.round(On*100)}%`}):null,Ke?null:o.jsx(Aj,{motors:An,fill:On,compact:zt}),Ke?null:o.jsx(jj,{battery:Ba,compact:zt}),!Ke&&dn&&Ba.available?o.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:o.jsx(jf,{visual:Ba,orientation:"vertical",variant:"inline",compact:zt,className:"h-full w-4"})}):null,!Ke&&f?o.jsx("div",{className:"pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2",children:o.jsxs("div",{className:`mx-auto w-fit rounded border border-amber-300/80 bg-black/75 text-amber-200 ${zt?"px-2 py-1 text-[0.6rem]":"px-3 py-1.5 text-sm"}`,children:[o.jsx("div",{className:"text-center",children:f}),o.jsx("div",{className:"pointer-events-auto mt-0",children:o.jsx(Ff,{id:"discord",label:"Join our Discord server while you wait!",url:Ei})})]})}):null]}),!Ke&&!dn&&o.jsxs("div",{className:"space-y-0.5",children:[o.jsx(vj,{sensors:Mt}),o.jsx("div",{className:"panel-section space-y-0.5 text-sm",children:o.jsx(jf,{visual:Ba,compact:zt})})]})]})}function vj({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),r=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+r(p)*360)%360} 100% 60%)`,d=6,h=12;return o.jsx("div",{className:"flex w-full items-center justify-center gap-0.5",children:l.map((p,x)=>{const v=r(p),y=xDate.now());m.useEffect(()=>{const W=setInterval(()=>R(Date.now()),150);return()=>clearInterval(W)},[]),a?.receivedAt&&j-a.receivedAt<200;const A=N,T=p==="mobile-portrait",O=A?"text-[0.45rem]":"text-[0.65rem]",L=A?"px-0.25 py-0.25":"px-1 py-0.5",Z=A?"px-0.25 py-0.25":"px-0.5 py-0.5",D=A?"text-[0.55rem]":"text-[0.8rem]",$=A?"left-0.5 top-0.5":"left-1 top-1",Y=A?"text-[0.5rem]":"text-[0.7rem]",F=A?"px-0.5 py-0.25":"px-1 py-0.5",Q=A?"left-0.5 top-1/2":"left-1 top-1/2",ne="bottom-0.5",ue={transform:`translateX(-50%) scale(${E})`,transformOrigin:"center bottom"},he="240px",V=T?.3:A?.33:.7,X={width:he,height:he,opacity:A?.6:.7,transform:M==="top-center"?`translateX(-50%) scale(${V})`:`scale(${V})`,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 W=[["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||"--"]],oe=!!l?.chargingSources?.homeBase,fe=l?.chargingState?.label||"",w=!!(fe&&fe.toLowerCase()!=="not charging"),z=l?.oiMode?.label||"Unknown",P=z.toLowerCase(),te=P==="full"?"bg-emerald-500/80 text-emerald-50":P==="safe"?"bg-amber-400/80 text-amber-950":P==="passive"?"bg-slate-700/80 text-slate-100":"bg-slate-700/60 text-slate-200",le=oe?"bg-emerald-500/80 text-emerald-50":"bg-slate-700/70 text-slate-200",ie=w?"bg-emerald-500/80 text-emerald-50":oe?"bg-amber-400/80 text-amber-950":"bg-slate-700/70 text-slate-200";return o.jsxs(o.Fragment,{children:[o.jsxs("div",{className:"pointer-events-none absolute inset-0 flex items-center justify-center",children:[o.jsx("div",{className:`absolute ${$} font-medium text-slate-100 ${O}`,children:o.jsxs("div",{className:"flex flex-col gap-0.5 leading-none",children:[o.jsxs("span",{children:["Status: ",r]}),f?o.jsxs("span",{children:["Audio: ",f]}):null,d?o.jsx("span",{className:"text-cyan-300",children:d}):null]})}),o.jsx("div",{className:`absolute ${Q} flex -translate-y-1/2 flex-col gap-0.5 bg-black/70 text-slate-100 ${O} ${L}`,children:o.jsxs("div",{className:"space-y-0.5 leading-tight",children:[o.jsxs("div",{className:"flex flex-col gap-0.5 text-[0.75rem] font-semibold uppercase tracking-wide",children:[o.jsx("span",{className:`rounded px-1.5 py-0.5 ${le}`,children:oe?"Docked":"Undocked"}),o.jsx("span",{className:`rounded px-1.5 py-0.5 ${ie}`,children:w?"Charging":"Not charging"}),o.jsxs("span",{className:`rounded px-1.5 py-0.5 ${te}`,children:["OI: ",z]})]}),W.map(([ce,ve])=>o.jsxs("span",{className:"flex items-center justify-between gap-0.5",children:[o.jsx("span",{className:"text-slate-400",children:ce}),o.jsx("span",{className:"font-semibold text-white",children:ve})]},ce))]})}),o.jsx("div",{className:`absolute ${ne} left-1/2`,style:ue,children:o.jsxs("div",{className:`flex items-center gap-0.5 bg-black/80 text-slate-100 ${Z} ${D}`,children:[o.jsx("span",{className:"font-semibold text-white",children:i||"Unnamed Rover"}),v?o.jsxs("span",{className:"text-slate-300",children:["• ",v]}):null]})})]}),b?o.jsx("div",{className:"pointer-events-none absolute rounded",style:{...X},children:o.jsx(Af,{sensors:l,size:240,overlay:!0})}):null]})}return o.jsxs("div",{className:"pointer-events-none absolute inset-0 flex items-center justify-center",children:[o.jsx("div",{className:`absolute ${$} font-medium text-slate-100 ${O}`,children:o.jsxs("div",{className:"flex flex-col gap-0.5 leading-none",children:[o.jsxs("span",{children:["Status: ",r]}),f?o.jsxs("span",{children:["Audio: ",f]}):null,d?o.jsx("span",{className:"text-cyan-300",children:d}):null]})}),C?o.jsx("div",{className:`absolute left-1/2 top-0.5 -translate-x-1/2 rounded bg-black/70 text-slate-100 ${F} ${Y}`,children:C}):null,o.jsx("div",{className:`absolute ${ne} left-1/2`,style:ue,children:o.jsx("div",{className:`flex gap-0.5 bg-black/80 text-slate-100 ${Z} ${D}`,children:o.jsxs("span",{children:['Rover: "',i||"Unnamed Rover",'"']})})}),b&&x!=="spectator"?o.jsx("div",{className:"pointer-events-none absolute rounded",style:{...X},children:o.jsx(Af,{sensors:l,size:240,overlay:!0})}):null]})}function bj({mobileHud:a=!1,turnSeconds:l=null,isActiveDriver:i=!1,idleSkipSeconds:r=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 r=="number";return o.jsx("div",{className:"pointer-events-none absolute inset-0 z-30 flex items-center justify-center bg-black/55",children:o.jsxs("div",{className:`flex flex-col items-center gap-0.5 rounded border border-amber-300/80 bg-black/70 ${p}`,children:[o.jsx("div",{className:`font-semibold text-amber-200 ${f}`,children:"IT IS YOUR TURN!"}),o.jsx("div",{className:`text-amber-200/80 ${d}`,children:"Start driving!"}),x?o.jsxs("div",{className:`text-red-100/90 ${h}`,children:["Idle skip in ",r,"s"]}):null]})})}const Sj={leftWheel:"Left wheel",rightWheel:"Right wheel",mainBrush:"Main brush",sideBrush:"Side brush",limiter:"Overcurrent limit"};function Aj({motors:a,fill:l=0,compact:i=!1}){if(!a?.length)return null;const r=a.map(y=>Sj[y]||y),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)),v=`${Math.round(x*100)}%`;return o.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:o.jsxs("div",{className:"relative h-full w-full",children:[o.jsx("div",{className:"absolute inset-0 overflow-hidden",children:o.jsx("div",{className:"h-full bg-red-700/60",style:{width:v}})}),o.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:[o.jsx("div",{children:"OVERCURRENT"}),o.jsx("div",{className:`mt-0 font-medium text-white ${p}`,children:r.join(", ")})]})]})})}function jj({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.",r=l?"p-2 top-6":"p-4 top-10",f=l?"text-sm":"text-2xl";return o.jsx("div",{className:`pointer-events-none absolute flex items-center justify-center bg-amber-900/60 left-1/2 -translate-x-1/2 ${r}`,children:o.jsx("div",{className:`text-center font-semibold text-white animate-pulse ${f}`,children:o.jsx("div",{children:i})})})}function Nj({compact:a=!1}){const{session:l}=$e(),{sendMessage:i,onInputFocus:r,onInputBlur:f,blurChat:d,registerInputRef:h,setTypingActive:p}=Vf(),{value:x}=rt("tts",{engine:"flite",voice:"rms",pitch:50}),[v,y]=m.useState(""),[b,N]=m.useState(!1),M=l?.role!=="spectator",C=l?.role==="spectator",E=l?.assignment?.roverId||null,R=!!m.useMemo(()=>l?.roster?.find(D=>String(D.id)===String(E))||null,[E,l?.roster])?.audio?.ttsEnabled,A=m.useMemo(()=>{if(!R)return null;const D=x?.engine==="espeak"?"espeak":"flite";if(D==="espeak"){let Y=Number.isFinite(x?.pitch)?Math.round(x.pitch):void 0;return typeof Y=="number"&&(Y=Math.max(0,Math.min(99,Y))),{speak:!0,engine:D,pitch:Y}}const $=typeof x?.voice=="string"?x.voice:void 0;return{speak:!0,engine:D,voice:$}},[x?.engine,x?.pitch,x?.voice,R]),T=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",O=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",L=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 Z(D){if(D.preventDefault(),!M)return;const $=v.trim();if($){N(!0);try{await i($,A),y(""),d(),p(!1)}catch(Y){alert(Y.message)}finally{N(!1)}}}return C?null:o.jsxs("form",{onSubmit:Z,className:T,children:[o.jsx("input",{className:O,value:v,onChange:D=>{const $=D.target.value;y($),p(!!$.trim())},onFocus:D=>{r(D),p(!!v.trim())},onBlur:D=>{f(D),p(!1)},onKeyDown:D=>{D.key==="Enter"&&!v.trim()&&(D.preventDefault(),d(),p(!1))},ref:D=>h(D,{target:"hud"}),placeholder:M?"Chat (TTS)":"Spectator",disabled:!M}),o.jsx("button",{type:"submit",disabled:!M||b,className:L,children:"Speak"})]})}function Zf({layoutFormat:a="desktop"}){const{session:l}=$e(),{state:{song:i,lastControlIntentAt:r},overcurrentLimiter:f}=Bt(),[d,h]=m.useState(()=>Date.now()),[p,x]=m.useState(!1),[v,y]=m.useState(null),b=m.useRef({active:!1,roverId:null});m.useEffect(()=>{if(l?.mode!=="turns")return;const te=setInterval(()=>h(Date.now()),250);return()=>clearInterval(te)},[l?.mode]);const N=l?.assignment?.roverId,C=!!(N&&l?.roster?l.roster.find(te=>String(te.id)===String(N)):null)?.media?.audioPublishUrl,E=N?l?.turnQueues?.[N]:null,j=l?.socketId||null,R=N?l?.activeDrivers?.[N]:null,A=!!(j&&R===j),T=m.useMemo(()=>{const te=E?.queue||[];if(!te.length||!E?.current||te.length<=1)return null;const le=te.findIndex(ie=>ie===E.current);return le===-1?te[0]||null:te[(le+1)%te.length]||null},[E?.queue,E?.current]),O=!!(j&&T===j),L=E?.deadline||null,Z=E?.idleDeadline||null,D=L?L-d:null,$=Z?Z-d:null,Y=l?.mode==="turns"&&O&&D!=null&&D<=5e3&&D>0,F=l?.mode!=="turns"||A||Y,Q=D!=null&&Number.isFinite(D)?Math.max(0,Math.ceil(D/1e3)):null,ne=$!=null&&Number.isFinite($)?Math.max(0,Math.ceil($/1e3)):null,ue=A?Q!=null?`${Q}s left`:null:O&&Q!=null?`Your turn in ${Q}s`:null,he=N?[...F?[{type:"rover",id:N,key:N}]:[],...C?[{type:"rover",id:`${N}-audio`,key:`${N}-audio`}]:[]]:[],V=pi(he),q=N&&F?V[N]:null,X=N&&C?V[`${N}-audio`]:null,W=Qf(N?[N]:[],{enabled:!!N,version:l?.mode}),oe=N&&W[N]||null,fe=is(N),w=N&&l?.roster?l.roster.find(te=>String(te.id)===String(N)):null,z=w?.battery??null,P=w?.name||(N?`Rover ${N}`:"");return m.useEffect(()=>{if(l?.mode!=="turns"){x(!1),y(null),b.current={active:!1,roverId:null};return}const te=b.current,le=A&&!te.active,ie=A&&N&&N!==te.roverId;le||ie?(x(!0),y(Date.now())):!A&&te.active&&(x(!1),y(null)),b.current={active:A,roverId:N}},[A,N,l?.mode]),m.useEffect(()=>{!p||!v||r>v&&x(!1)},[r,v,p]),o.jsx("section",{className:"panel",children:N?o.jsx(xi,{sessionInfo:q,videoMode:F?"whep":"snapshot",snapshotFeed:oe,audioSessionInfo:X,label:P,telemetryFrame:fe,batteryConfig:z,layoutFormat:a,overcurrentLimiter:f,songNote:i?.note,qualityNotice:F?null:"Preview feed (low FPS) until your turn.",showTurnCue:p,turnTimerText:ue,turnSeconds:Q,isActiveDriver:A,idleSkipSeconds:ne}):o.jsxs("div",{className:"panel-muted content-center text-center text-sm text-slate-400 aspect-[4/3]",children:[o.jsx("p",{children:"You are not assigned to a rover."}),o.jsx("p",{className:"mt-0",children:o.jsx("a",{href:"/spectate",className:"text-blue-400 underline hover:text-blue-500",children:"Click here to visit the spectator page."})})]})})}function Nf({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 o.jsx("span",{className:`rounded px-1 py-0.5 text-xs font-semibold leading-none ${i}`,children:a})}function go(a){if(!Number.isFinite(a))return 0;const l=a%360;return l<0?l+360:l}function wj(a){if(!Array.isArray(a)||a.length<3)return 0;const[l,i,r]=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(r)))/255,p=Math.max(f,d,h),x=Math.min(f,d,h),v=p-x;if(v===0)return 0;let y=0;return p===f?y=(d-h)/v%6:p===d?y=(h-f)/v+2:y=(f-d)/v+4,go(Math.round(y*60))}function Lp(a){const l=go(a),i=1,r=1-Math.abs(l/60%2-1);let f=0,d=0,h=0;return l<60?(f=i,d=r):l<120?(f=r,d=i):l<180?(d=i,h=r):l<240?(d=r,h=i):l<300?(f=r,h=i):(f=i,h=r),[Math.round(f*255),Math.round(d*255),Math.round(h*255)]}function zp(a){return a?Array.isArray(a.hsColor)?go(Number(a.hsColor[0])):Array.isArray(a.rgbColor)?wj(a.rgbColor):0:0}function Ej({entity:a,connected:l,onToggle:i,onSetColor:r}){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",v=!l||f,y=v||!h,[b,N]=m.useState(()=>zp(a)),M=m.useRef(b),C=m.useRef(!1),E=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 L=zp(a);M.current=L,N(L)},[a.rgbColor,a.hsColor,h]);const j=L=>{const Z=go(Number(L.target.value));M.current=Z,N(Z)},R=()=>{y||!r||r(a.id,Lp(M.current))},A=L=>{L.stopPropagation()},O=`rgb(${(h?Lp(M.current):[255,255,255]).join(",")})`;return o.jsxs("button",{type:"button",onClick:()=>i(a.id),disabled:v,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 ${E} disabled:opacity-60 disabled:hover:bg-inherit`,children:[o.jsxs("div",{className:"min-w-0 flex-1",children:[o.jsxs("div",{className:"flex items-center gap-0.5 text-sm leading-normal",children:[o.jsx("span",{className:"truncate font-semibold text-white",children:a.name||a.id}),h&&o.jsxs(o.Fragment,{children:[o.jsx(Nf,{label:x,tone:p}),!l&&o.jsx("span",{className:"text-xs text-amber-200",children:"Offline"})]})]}),h?o.jsx("div",{className:"-mt-0.5 w-full",onClick:A,onPointerDown:L=>{A(L),C.current=!0},onPointerUp:L=>{A(L),C.current=!1,R()},onPointerCancel:L=>{A(L),C.current=!1,R()},onKeyUp:L=>{A(L),R()},onBlur:R,children:o.jsx("div",{className:"relative w-full",children:o.jsx("input",{type:"range",min:"0",max:"360",step:"1",value:b,onChange:j,disabled:y,"aria-label":`Set color for ${a.name||a.id}`,className:"ha-hue-slider w-full"})})}):o.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-400",children:[o.jsx(Nf,{label:x,tone:p}),!l&&o.jsx("span",{className:"text-amber-200",children:" · Offline"})]})]}),h&&o.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:O}}),!h&&o.jsx("div",{className:"self-center text-xs font-semibold text-white/90",children:d?"Turn off":"Turn on"})]})}function cx(){const{state:{keymap:a}}=Bt(),{session:l,homeAssistantToggle:i,homeAssistantSetLightColor:r}=$e(),f=l?.homeAssistant,d=m.useMemo(()=>f?.entities||[],[f?.entities]),h=Mn(a?.homeAssistantOn?.[0]),p=Mn(a?.homeAssistantOff?.[0]);if(!f?.enabled)return o.jsxs("section",{className:"panel-section space-y-0.5 text-sm text-slate-400",children:[o.jsx("p",{className:"text-slate-300",children:"Room Controls"}),o.jsx("p",{className:"text-slate-500",children:"Not configured on the server."})]});if(d.length===0)return o.jsxs("section",{className:"panel-section space-y-0.5 text-sm text-slate-400",children:[o.jsx("p",{className:"text-slate-300",children:"Room Controls"}),o.jsx("p",{className:"text-slate-500",children:"No lights or switches configured."})]});const x=!!f?.connected;return o.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[o.jsxs("header",{className:"flex items-center justify-between gap-0.5 text-sm text-slate-400",children:[o.jsxs("div",{className:"flex items-center gap-0.5",children:[o.jsx("p",{children:"Room Controls"}),o.jsx("span",{className:"text-xs text-slate-500",children:d.length}),o.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-300 background-black",children:[o.jsxs("span",{className:"flex items-center gap-0.5",children:[o.jsx("span",{children:"On"}),h?o.jsx(Up,{label:h}):null]}),o.jsxs("span",{className:"flex items-center gap-0.5",children:[o.jsx("span",{children:"Off"}),p?o.jsx(Up,{label:p}):null]})]})]}),o.jsx(Nf,{label:x?"Connected":"Offline",tone:x?"success":"warn"})]}),o.jsx("div",{className:"flex flex-wrap gap-0.5",children:d.map(v=>o.jsx(Ej,{entity:v,connected:x,onToggle:i,onSetColor:r},v.id))})]})}function Up({label:a}){return a?o.jsx("span",{className:"rounded border border-white/40 px-1 text-[0.7rem] text-white",children:a}):null}function ux(){const{login:a,setRole:l}=$e(),[i,r]=m.useState(""),[f,d]=m.useState(""),[h,p]=m.useState(!1);async function x(v){v.preventDefault(),p(!0);try{await a(i,f),r(""),d("")}catch(y){alert(y.message)}finally{p(!1)}}return o.jsxs("div",{className:"panel-section space-y-0.5 text-base",children:[o.jsx("p",{className:"text-sm text-slate-400",children:"Admin login"}),o.jsxs("form",{className:"flex flex-col gap-0.5",onSubmit:x,children:[o.jsx("input",{className:"field-input",placeholder:"Username",value:i,onChange:v=>r(v.target.value)}),o.jsx("input",{className:"field-input",placeholder:"Password",type:"password",value:f,onChange:v=>d(v.target.value)}),o.jsx("button",{type:"submit",disabled:h,className:"button-dark w-full disabled:opacity-50",children:h?"Logging in…":"Login"})]})]})}function Hp(...a){return a.filter(Boolean).join(" ")}function Cj(a){const l=a?.batteryState?.percentDisplay;return l==null?"--":`${l}%`}function qj(a){return a?.batteryState?a.batteryState.urgentActive?"text-red-400":a.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function Mj({title:a,roster:l=[],emptyText:i="No roster data.",renderActions:r=null}){return o.jsxs("div",{className:"space-y-0.5 text-sm",children:[a&&o.jsx("p",{className:"text-sm text-slate-400",children:a}),l.length===0?o.jsx("p",{className:"text-sm text-slate-500",children:i}):o.jsx("ul",{className:"space-y-0.5 text-sm",children:l.map(f=>o.jsxs("li",{className:Hp("surface flex flex-wrap items-center justify-between gap-0.5",f.locked&&"bg-red-900/40"),children:[o.jsxs("div",{children:[o.jsx("p",{className:"text-slate-200",children:f.name}),o.jsxs("p",{className:"text-xs text-slate-500 flex flex-wrap items-center gap-0.5",children:[o.jsx("span",{children:f.locked?"locked":"free"}),f.lockReason&&o.jsx("span",{className:"rounded bg-black/30 px-1",children:f.lockReason}),o.jsxs("span",{className:Hp("font-semibold",qj(f)),children:["Battery ",Cj(f)]})]})]}),r?r(f):null]},f.id))})]})}const Rj=[{key:"open",label:"Open"},{key:"turns",label:"Turns"},{key:"admin",label:"Admin"},{key:"lockdown",label:"Lockdown"}];function kj(){const{session:a,lockRover:l,setMode:i,requestControl:r,setCommunityGoal:f,setAdminReason:d,rebootRover:h,rebootServer:p,adminLogs:x}=$e(),v=m.useMemo(()=>a?.roster??[],[a?.roster]),[y,b]=m.useState({}),[N,M]=m.useState({}),[C,E]=m.useState(!1),j=a?.health||null,R=a?.communityGoal?.text||"",A=a?.communityGoal?.updatedAt||null,[T,O]=m.useState(R),L=a?.adminReason?.text||"",Z=a?.adminReason?.updatedAt||null,[D,$]=m.useState(L),Y=a?.role==="admin"||a?.role==="lockdown"||a?.role==="lockdown-admin",F=a?.mode??"open",Q=async(w,z)=>{try{await l(w,z),b(P=>({...P,[w]:z}))}catch(P){alert(P.message)}},ne=async w=>{const z=w.target.value;try{await i(z)}catch(P){alert(P.message)}},ue=async w=>{try{await r(w,{force:!0})}catch(z){alert(z.message)}},he=async w=>{if(!(!w?.id||!window.confirm(`Reboot rover "${w.name||w.id}" now?`))){M(P=>({...P,[w.id]:!0}));try{await h(w.id)}catch(P){alert(P.message)}finally{M(P=>({...P,[w.id]:!1}))}}},V=async()=>{if(window.confirm("Reboot the server host now? This will disconnect all users.")){E(!0);try{await p()}catch(z){alert(z.message),E(!1)}}},q=async()=>{try{await f(T)}catch(w){alert(w.message)}},X=async()=>{try{await f(null)}catch(w){alert(w.message)}},W=async()=>{try{await d(D)}catch(w){alert(w.message)}},oe=async()=>{try{await d(null)}catch(w){alert(w.message)}};m.useEffect(()=>{O(R)},[R]),m.useEffect(()=>{$(L)},[L]);const fe=m.useMemo(()=>{const w={};return v.forEach(z=>{w[z.id]=y[z.id]??z.locked}),w},[v,y]);return Y?o.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[o.jsxs("div",{className:"flex items-center justify-between gap-0.5 text-sm",children:[o.jsx("span",{children:"Admin controls"}),o.jsx("select",{value:F,onChange:ne,className:"field-input text-sm",children:Rj.map(w=>o.jsx("option",{value:w.key,children:w.label},w.key))})]}),o.jsx("div",{className:"flex gap-0.5 text-xs",children:o.jsx("button",{type:"button",onClick:V,disabled:C,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:C?"Server rebooting...":"Reboot Server"})}),o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[o.jsx("span",{children:"Community goal"}),A?o.jsxs("span",{children:["Updated ",new Date(A).toLocaleString()]}):null]}),o.jsx("input",{type:"text",value:T,onChange:w=>O(w.target.value),placeholder:"Set a community goal",className:"field-input text-sm"}),o.jsxs("div",{className:"flex gap-0.5 text-xs",children:[o.jsx("button",{type:"button",onClick:q,className:"button-dark",children:"Set goal"}),o.jsx("button",{type:"button",onClick:X,className:"button-danger",children:"Clear"})]})]}),o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[o.jsx("span",{children:"Admin mode reason"}),Z?o.jsxs("span",{children:["Updated ",new Date(Z).toLocaleString()]}):null]}),o.jsx("textarea",{value:D,onChange:w=>$(w.target.value),placeholder:"Set an admin mode reason",className:"field-input text-sm min-h-[3.5rem]"}),o.jsxs("div",{className:"flex gap-0.5 text-xs",children:[o.jsx("button",{type:"button",onClick:W,className:"button-dark",children:"Set reason"}),o.jsx("button",{type:"button",onClick:oe,className:"button-danger",children:"Clear"})]})]}),o.jsx(Mj,{roster:v,renderActions:w=>o.jsxs("div",{className:"flex flex-wrap gap-0.5 text-xs",children:[o.jsx("button",{type:"button",onClick:()=>Q(w.id,!fe[w.id]),className:"button-dark",children:fe[w.id]?"Unlock":"Lock"}),o.jsx("button",{type:"button",onClick:()=>ue(w.id),className:"button-dark",children:"Force"}),o.jsx("button",{type:"button",onClick:()=>he(w),disabled:!!N[w.id],className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:N[w.id]?"Rebooting...":"Reboot"})]})}),o.jsx(Tj,{health:j}),o.jsx(_j,{entries:x})]}):null}function Tj({health:a}){if(!a)return null;const l=a.replay||{sources:[],readyCount:0,totalCount:0},i=a.snapshots||{rovers:[],rooms:[]},r=`${l.readyCount}/${l.totalCount} sources ready`,f=i.rovers.filter(h=>h.stale).length,d=i.rooms.filter(h=>h.stale).length;return o.jsxs("div",{className:"space-y-0.5",children:[o.jsx("div",{className:"panel-muted text-xs uppercase",children:"Health"}),o.jsxs("div",{className:"surface space-y-0.5 text-xs text-slate-200",children:[o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsx("span",{children:"Replay segments"}),o.jsx("span",{className:"text-slate-400",children:r})]}),o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsx("span",{children:"Rover snapshots"}),o.jsxs("span",{className:f?"text-amber-300":"text-emerald-300",children:[i.rovers.length-f,"/",i.rovers.length," ok"]})]}),o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsx("span",{children:"Room cameras"}),o.jsxs("span",{className:d?"text-amber-300":"text-emerald-300",children:[i.rooms.length-d,"/",i.rooms.length," ok"]})]})]}),o.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:l.sources.map(h=>o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsx("span",{children:h.label}),o.jsxs("span",{className:h.ready?"text-emerald-300":"text-amber-300",children:[h.recentCount,"/",h.neededCount]})]},`${h.type}:${h.id}`))}),o.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:i.rovers.map(h=>o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsx("span",{children:h.name}),o.jsx("span",{className:h.stale?"text-amber-300":"text-emerald-300",children:h.stale?"stale":"ok"})]},`rover:${h.id}`))}),o.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:i.rooms.map(h=>o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsx("span",{children:h.name}),o.jsx("span",{className:h.stale?"text-amber-300":"text-emerald-300",children:h.error?"error":h.stale?"stale":"ok"})]},`room:${h.id}`))})]})}function _j({entries:a}){const l=a||[];return o.jsxs("div",{className:"panel-section space-y-0.5 text-base",children:[o.jsxs("div",{className:"flex items-center justify-between text-sm text-slate-400",children:[o.jsx("span",{children:"Admin IP log"}),o.jsx("span",{children:l.length})]}),o.jsx("div",{className:"surface h-64 overflow-y-auto font-mono text-xs",children:l.length===0?o.jsx("p",{children:"No admin log entries yet."}):l.slice().reverse().map(i=>o.jsxs("div",{className:"surface",children:[o.jsx("span",{className:"text-amber-400",children:i.ts?new Date(i.ts).toLocaleTimeString():"--"})," ",i.label&&o.jsxs("span",{className:"text-teal-400",children:["[",i.label,"]"]})," ",o.jsx("span",{className:"text-slate-200",children:i.message})," ",i.ip&&o.jsx("span",{className:"text-cyan-300",children:i.ip})," ",i.meta&&o.jsx("span",{className:"text-slate-500",children:JSON.stringify(i.meta)})]},i.id))}),o.jsx("p",{className:"text-xs text-slate-500",children:"Admin-only log stream; IPs never appear in user data."})]})}const Dj=[{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:"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 Oj(a){return a.reduce((l,i)=>((l[i.group]||(l[i.group]=[])).push(i),l),{})}function Bj(a,l){const i=Number(a);return Number.isFinite(i)?Math.max(0,Math.min(500,i)):l}const Jf=5,vi=500,yi=1,If=100;function Lj(a,l){const i=Number(a);return Number.isFinite(i)?Math.max(Jf,Math.min(vi,i)):l}function no(a,l){const i=Number(a);return Number.isFinite(i)?Math.max(yi,Math.min(If,i)):l}function zj(a){const i=(no(a,a)-yi)/(If-yi),r=vi-i*(vi-Jf);return Math.round(r)}function Uj(a){const l=Lj(a,a),i=(vi-l)/(vi-Jf),r=yi+i*(If-yi);return Math.round(r)}function Hj(a){const[l,i]=m.useState(null);return m.useEffect(()=>{if(!l)return;function r(f){if(f.preventDefault(),f.key==="Escape"){i(null);return}const d=Ni(f.key??"");d&&(a(l,d,f),i(null))}return window.addEventListener("keydown",r,{capture:!0}),()=>window.removeEventListener("keydown",r,{capture:!0})},[l,a]),{active:l,startCapture:i,cancel:()=>i(null)}}function Qr({label:a,description:l,value:i,onChange:r,min:f=0,max:d=500,step:h=5}){return o.jsxs("label",{className:"surface-muted block p-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[o.jsx("span",{className:"font-semibold text-slate-100",children:a}),o.jsx("input",{type:"number",min:f,max:d,value:i,onChange:p=>r(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&&o.jsx("p",{className:"text-[0.65rem] text-slate-500",children:l}),o.jsxs("div",{className:"mt-0 flex items-center gap-0.5",children:[o.jsx("input",{type:"range",min:f,max:d,step:h,value:i,onChange:p=>r(p.target.value),className:"h-2 flex-1 accent-emerald-400"}),o.jsx("span",{className:"w-12 text-right text-[0.75rem] font-mono text-slate-400",children:i})]})]})}function Vj(){const{state:{keymap:a},actions:{updateKeyBinding:l,resetKeyBindings:i}}=Bt(),{value:r,save:f}=rt("inputs",qn),d={...qn.keyboard,...r.keyboard??{}},h=m.useMemo(()=>typeof d.tiltSpeed=="number"?no(d.tiltSpeed,qn.keyboard.tiltSpeed):typeof d.tiltIntervalMs=="number"?no(Uj(d.tiltIntervalMs),qn.keyboard.tiltSpeed):qn.keyboard.tiltSpeed,[d.tiltIntervalMs,d.tiltSpeed]),p=m.useMemo(()=>Oj(Dj),[]),{active:x,startCapture:v,cancel:y}=Hj((C,E)=>{l(C,E)}),b=m.useCallback(C=>a?.[C]?.[0]||ui[C]?.[0]||"",[a]),N=m.useCallback((C,E)=>{const j=qn.keyboard,R=Bj(E,j[C]);f(A=>({...A??{},keyboard:{...A?.keyboard??j,[C]:R}}))},[f]),M=m.useCallback(C=>{const E=qn.keyboard,j=no(C,E.tiltSpeed),R=zj(j);f(A=>({...A??{},keyboard:{...A?.keyboard??E,tiltSpeed:j,tiltIntervalMs:R}}))},[f]);return o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsxs("div",{className:"flex items-center justify-between",children:[o.jsxs("div",{children:[o.jsx("p",{className:"text-xs text-slate-400",children:"Keyboard layout"}),o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Per-browser · click to change a binding"})]}),o.jsx("button",{type:"button",onClick:()=>i(),className:"button-dark text-xs",children:"Reset defaults"})]}),o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Keyboard speeds"}),o.jsxs("div",{className:"space-y-0.5",children:[o.jsx(Qr,{label:"Base speed",description:"Normal driving speed",value:d.baseSpeed,onChange:C=>N("baseSpeed",C)}),o.jsx(Qr,{label:"Turbo speed",description:"Used when holding the boost modifier",value:d.turboSpeed,onChange:C=>N("turboSpeed",C)}),o.jsx(Qr,{label:"Precision speed",description:"Used when holding the precision modifier",value:d.precisionSpeed,onChange:C=>N("precisionSpeed",C)})]})]}),o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Camera tilt"}),o.jsx(Qr,{label:"Tilt speed",description:"Higher = faster when holding tilt keys",value:h,onChange:M,min:1,max:100,step:1})]}),o.jsx("div",{className:"space-y-0.5",children:Object.entries(p).map(([C,E])=>o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.75rem] text-slate-400",children:C}),o.jsx("div",{className:"space-y-0.5",children:E.map(j=>{const R=b(j.id),A=x===j.id;return o.jsxs("div",{className:"surface-muted flex items-center justify-between text-xs",children:[o.jsxs("div",{children:[o.jsx("p",{className:"font-semibold text-slate-100",children:j.label}),o.jsx("p",{className:"text-[0.65rem] text-slate-400",children:Mn(R)})]}),o.jsxs("div",{className:"flex items-center gap-0.5",children:[A&&o.jsx("button",{type:"button",onClick:()=>y(),className:"button-danger text-[0.7rem]",children:"Cancel"}),o.jsx("button",{type:"button",onClick:()=>v(j.id),className:`${A?"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:A?"Press a key…":"Change"})]})]},j.id)})})]},C))})]})}const sa=new Intl.NumberFormat(void 0,{maximumFractionDigits:2}),Yj=[{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"}],Vp=.45,$j=.6;function ri({label:a,description:l,min:i,max:r,step:f,value:d,onChange:h}){return o.jsxs("label",{className:"surface-muted block p-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[o.jsx("span",{className:"font-semibold text-slate-100",children:a}),o.jsx("span",{className:"font-mono text-slate-400",children:sa.format(d)})]}),l&&o.jsx("p",{className:"text-[0.65rem] text-slate-500",children:l}),o.jsx("input",{type:"range",min:i,max:r,step:f,value:d,onChange:p=>h(Number(p.target.value)),className:"mt-0 w-full accent-emerald-400"})]})}function Gj(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} ${sa.format(a.threshold??.6)}`}return"Unassigned"}function Xj(a){return a.reduce((l,i)=>((l[i.section]||(l[i.section]=[])).push(i),l),{})}function Qj(a,l){if(!a||a.length===0)return null;if(l){const i=a.find(r=>r.signature===l);if(i)return i}return a[0]}function Kj(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 Fj(a,l,i){const r=(a.axes??[]).map((d,h)=>({index:h,delta:Math.abs((d??0)-(l.axes?.[h]??0)),value:d}));if(r.sort((d,h)=>h.delta-d.delta),i.kind==="axisPair"){const d=r.filter(h=>h.delta>Vp).slice(0,2);return d.length<2?null:{kind:"axisPair",x:d[0].index,y:d[1].index,...i.invertDefaults??{}}}const f=r.find(d=>d.delta>Vp);return f?{kind:"axis",index:f.index,...i.invertDefaults??{}}:null}function Zj(a,l,i){const r=a.buttons??[];for(let d=0;d$j)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 Jj(a,l,i){if(!a)return null;if(i.kind==="axis"||i.kind==="axisPair"){const r=Fj(a,l,i);if(r)return r}return Zj(a,l,i)}function Ij(){const a=MA(),{value:l,save:i}=rt("gamepad",Da),[r,f]=m.useState(null),d=m.useRef(null),h=m.useMemo(()=>Xj(Yj),[]),p=m.useMemo(()=>Qj(a.pads,l.activeSignature),[a.pads,l.activeSignature]),x=p?.signature??null,v=m.useMemo(()=>x?l?.profiles?.[x]??l?.defaults?.profile??ia:l?.defaults?.profile??ia,[x,l?.defaults?.profile,l?.profiles]);m.useEffect(()=>{!p||!x||l?.profiles?.[x]||i(j=>{const R=j??Da;if(R.profiles?.[x])return R;const A=R?.defaults?.profile??ia,T=Kg(p,A);return{...R,profiles:{...R.profiles??{},[x]:T}}})},[p,x,l?.profiles,i]),m.useEffect(()=>{d.current=null},[r,x]),m.useEffect(()=>{if(!r||!p)return;if(!d.current){d.current=Kj(p);return}const j=Jj(p,d.current,r);j&&(i(R=>{const A=R??Da,T=A.profiles?.[x]??A?.defaults?.profile??ia,O={...T,bindings:{...T.bindings??{},[r.id]:{...T.bindings?.[r.id]??{},kind:r.kind,sources:[j]}}};return{...A,profiles:{...A.profiles??{},[x]:O}}}),f(null))},[p,x,r,i]);const y=m.useCallback(j=>{i(R=>({...R??Da,activeSignature:j||null}))},[i]),b=m.useCallback(j=>{i(R=>{const A=R??Da,T=(x&&A.profiles?.[x])??A?.defaults?.profile??ia,O={...T,calibration:{...T.calibration??{},...j}};return x?{...A,profiles:{...A.profiles??{},[x]:O}}:{...A,defaults:{...A.defaults??{},profile:O}}})},[x,i]),N=m.useCallback((j,R)=>{i(A=>{const T=A??Da,O=(x&&T.profiles?.[x])??T?.defaults?.profile??ia,L=R(O.bindings?.[j]??{}),Z={...O,bindings:{...O.bindings??{},[j]:L}};return x?{...T,profiles:{...T.profiles??{},[x]:Z}}:{...T,defaults:{...T.defaults??{},profile:Z}}})},[x,i]),M=m.useCallback(j=>{N(j.id,R=>({...R,sources:[]}))},[N]),C=m.useCallback((j,R)=>{N(j.id,A=>{const T=Array.isArray(A.sources)?[...A.sources]:[];if(!T[0])return A;const O={...T[0]};return R==="invertX"?O.invertX=!O.invertX:R==="invertY"?O.invertY=!O.invertY:O.invert=!O.invert,T[0]=O,{...A,sources:T}})},[N]),E=m.useMemo(()=>p?{outputs:Fg(p,v)}:null,[p,v]);return o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsx("div",{className:"flex items-center justify-between",children:o.jsxs("div",{children:[o.jsx("p",{className:"text-xs text-slate-400",children:"Controller"}),o.jsx("p",{className:"text-[0.65rem] text-slate-500",children:p?"Move sticks or press buttons to bind":"Connect a controller to configure."}),r&&o.jsxs("p",{className:"mt-0 text-[0.7rem] text-emerald-400",children:["Capturing ",r.label,"…"]})]})}),o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Connected controller"}),a.pads.length===0?o.jsx("p",{className:"text-[0.7rem] text-slate-400",children:"No controller detected."}):o.jsxs("div",{className:"flex items-center justify-between gap-0.5 text-xs",children:[o.jsx("select",{value:x??"",onChange:j=>y(j.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(j=>o.jsx("option",{value:j.signature,children:j.id||"Unknown controller"},j.signature))}),o.jsx("span",{className:"text-[0.7rem] text-slate-400",children:p?.mapping??"unknown"})]})]}),o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Calibration"}),o.jsxs("div",{className:"space-y-0.5",children:[o.jsx(ri,{label:"Drive deadzone",description:"Ignore small drive stick drift",min:0,max:.6,step:.01,value:v.calibration?.driveDeadzone??.18,onChange:j=>b({driveDeadzone:j})}),o.jsx(ri,{label:"Camera deadzone",description:"Ignore small camera tilt drift",min:0,max:.4,step:.01,value:v.calibration?.cameraDeadzone??.08,onChange:j=>b({cameraDeadzone:j})}),o.jsx(ri,{label:"Aux deadzone",description:"Ignore small trigger noise",min:0,max:.4,step:.01,value:v.calibration?.auxDeadzone??.05,onChange:j=>b({auxDeadzone:j})}),o.jsx(ri,{label:"Side brush scale",description:"Scale side brush output",min:.3,max:1,step:.05,value:v.calibration?.auxSideScale??.55,onChange:j=>b({auxSideScale:j})}),o.jsxs("label",{className:"surface-muted block p-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[o.jsx("span",{className:"font-semibold text-slate-100",children:"Camera mode"}),o.jsxs("select",{value:v.calibration?.cameraMode??"absolute",onChange:j=>b({cameraMode:j.target.value}),className:"rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:[o.jsx("option",{value:"absolute",children:"Absolute"}),o.jsx("option",{value:"velocity",children:"Velocity"})]})]}),o.jsx("p",{className:"text-[0.65rem] text-slate-500",children:"Absolute maps stick to angle; velocity moves over time."})]}),o.jsx(ri,{label:"Camera sensitivity",description:"Velocity mode degrees per second",min:10,max:180,step:5,value:v.calibration?.cameraSensitivity??60,onChange:j=>b({cameraSensitivity:j})})]})]}),o.jsx("div",{className:"space-y-0.5",children:Object.entries(h).map(([j,R])=>o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:j}),o.jsx("div",{className:"space-y-0.5",children:R.map(A=>{const O=v.bindings?.[A.id]?.sources?.[0]??null;return o.jsxs("div",{className:"surface-muted flex items-center justify-between text-xs",children:[o.jsxs("div",{children:[o.jsx("p",{className:"font-semibold text-slate-100",children:A.label}),o.jsx("p",{className:"text-[0.65rem] text-slate-400",children:Gj(O)})]}),o.jsxs("div",{className:"flex items-center gap-0.5",children:[A.kind==="axisPair"&&o.jsxs(o.Fragment,{children:[o.jsx("button",{type:"button",disabled:!O,onClick:()=>C(A,"invertX"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert X"}),o.jsx("button",{type:"button",disabled:!O,onClick:()=>C(A,"invertY"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert Y"})]}),A.kind==="axis"&&o.jsx("button",{type:"button",disabled:!O,onClick:()=>C(A),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert"}),o.jsx("button",{type:"button",onClick:()=>M(A),className:"button-dark text-[0.7rem]",children:"Clear"}),o.jsx("button",{type:"button",onClick:()=>f(A),className:`${r?.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:r?.id===A.id?"Waiting…":"Capture"})]})]},A.id)})})]},j))}),o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Diagnostics"}),p?o.jsxs("div",{className:"space-y-0.5 text-[0.7rem] text-slate-300",children:[o.jsx("p",{className:"text-slate-400",children:"Raw axes"}),o.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:p.axes.map((j,R)=>o.jsxs("span",{className:"font-mono text-slate-300",children:["A",R,": ",sa.format(j)]},`axis-${R}`))}),o.jsx("p",{className:"text-slate-400",children:"Raw buttons"}),o.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:p.buttons.map((j,R)=>o.jsxs("span",{className:"font-mono text-slate-300",children:["B",R,": ",sa.format(j.value)," ",j.pressed?"●":""]},`btn-${R}`))}),E?.outputs&&o.jsxs(o.Fragment,{children:[o.jsx("p",{className:"text-slate-400",children:"Mapped outputs"}),o.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[o.jsxs("span",{className:"font-mono text-slate-300",children:["Drive: ",sa.format(E.outputs.driveVector.x),","," ",sa.format(E.outputs.driveVector.y)]}),o.jsxs("span",{className:"font-mono text-slate-300",children:["Camera: ",sa.format(E.outputs.cameraAxis)]}),o.jsxs("span",{className:"font-mono text-slate-300",children:["Main: ",sa.format(E.outputs.auxAxis.main)]}),o.jsxs("span",{className:"font-mono text-slate-300",children:["Side: ",sa.format(E.outputs.auxAxis.side)]})]})]})]}):o.jsx("p",{className:"text-[0.7rem] text-slate-400",children:"No controller detected."})]})]})}const Wj={drive:"Drive wheels",aux:"Aux motors"};function Yp(a){return Number.isFinite(a)?`${Math.round(a*100)}%`:"--"}function Pj({value:a,color:l="bg-emerald-500"}){const i=`${Math.round(Math.max(0,Math.min(1,a))*100)}%`;return o.jsx("div",{className:"h-2 w-full overflow-hidden rounded bg-slate-800",children:o.jsx("div",{className:`h-full ${l}`,style:{width:i}})})}function e2(){const{state:{roverId:a},overcurrentLimiter:l}=Bt(),i=m.useMemo(()=>Wl.map(r=>r.key),[]);return o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[o.jsx("span",{children:"Overcurrent limiter"}),o.jsx("span",{children:l?.adminImmune?"Admin immune":"Active"})]}),a?o.jsxs("div",{className:"space-y-0.5",children:[i.map(r=>{const f=l?.caps?.[r]?.cap??0,d=l?.overcurrent?.groups?.[r]??!1,h=l?.scales?.perGroup?.[r]??1;return o.jsxs("div",{className:"surface space-y-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs",children:[o.jsx("span",{className:"text-slate-200",children:Wj[r]||r}),o.jsx("span",{className:d?"text-red-300":"text-slate-400",children:d?"overcurrent":"ok"})]}),o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-[0.7rem] text-slate-400",children:[o.jsx("span",{children:"Cap"}),o.jsx("span",{children:Yp(f)})]}),o.jsx(Pj,{value:f,color:"bg-amber-500"}),o.jsxs("div",{className:"flex items-center justify-between text-[0.7rem] text-slate-400",children:[o.jsx("span",{children:"Scale"}),o.jsx("span",{children:Yp(h)})]})]})]},r)}),o.jsxs("div",{className:"surface text-[0.7rem] text-slate-400",children:[o.jsx("div",{children:`Down rate ${l?.config?.downRatePerSec}/s · Up rate ${l?.config?.upRatePerSec}/s`}),o.jsx("div",{children:`Release delay ${l?.config?.releaseDelaySec}s`}),o.jsx("div",{children:`Output rate ${l?.config?.outputRateMs}ms`})]})]}):o.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to view limiter status."})]})}const fx=m.createContext(null);function dx(){const a=m.useContext(fx);if(!a)throw new Error("Tabs components must be used within a provider.");return a}const $p={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",active:"bg-sky-600 text-white",inactive:"bg-zinc-900 text-slate-300 hover:bg-sky-500 hover:text-white"}},mx="primary";function Wf(...a){return a.filter(Boolean).join(" ")}function Pf({children:a,defaultTab:l,currentTab:i,onTabChange:r,variant:f=mx}){const[d,h]=m.useState(l??null),[p,x]=m.useState([]),v=m.useCallback(M=>M?(x(C=>C.includes(M)?C:[...C,M]),()=>x(C=>C.includes(M)?C.filter(E=>E!==M):C)):()=>{},[]),y=i??d??p[0]??null;m.useEffect(()=>{l&&i===void 0&&h(l)},[l,i]);const b=m.useCallback(M=>{M&&(i===void 0&&h(M),r&&r(M))},[i,r]),N=m.useMemo(()=>({activeTab:y,setActiveTab:b,registerTab:v,variant:f}),[y,b,v,f]);return o.jsx(fx.Provider,{value:N,children:a})}function ed({children:a,className:l=""}){return o.jsx("div",{className:Wf("flex gap-0.5",l),children:a})}function yn({id:a,children:l,className:i="",disabled:r=!1}){const{activeTab:f,setActiveTab:d,registerTab:h,variant:p}=dx();m.useEffect(()=>h(a),[a,h]);const x=$p[p]??$p[mx],v=f===a,y=Wf(x.base,v?x.active:x.inactive,r?"cursor-not-allowed opacity-50":"",i);return o.jsx("button",{type:"button",className:y,onClick:()=>!r&&d(a),"aria-pressed":v,"aria-disabled":r,children:l})}function td({children:a,className:l=""}){return o.jsx("div",{className:Wf("mt-0 space-y-0.5",l),children:a})}function bn({id:a,children:l,keepMounted:i=!1}){const{activeTab:r}=dx(),f=r===a;return!f&&!i?null:!f&&i?o.jsx("div",{hidden:!0,"aria-hidden":"true",children:l}):o.jsx(o.Fragment,{children:l})}function t2(){const{session:a}=$e(),l=m.useMemo(()=>JSON.stringify(a??{},null,2),[a]);return o.jsxs("div",{className:"panel-section flex min-h-0 flex-col gap-0.5 text-xs",children:[o.jsx("p",{className:"text-sm text-slate-400",children:"Session snapshot"}),o.jsx("pre",{className:"surface min-h-0 flex-1 overflow-y-auto font-mono text-[0.7rem] text-lime-300",children:l})]})}const Gp=300,Xp=240;function n2(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,r)=>typeof r=="function"?"[Function]":r instanceof Error?`${r.name}: ${r.message}`:r,0)}catch{return String(a)}}function a2(a){const i=a.map(r=>n2(r)).join(" ");return i.length>Xp?`${i.slice(0,Xp)}…`:i}function l2(){const a=_n(),[l,i]=m.useState([]),r=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,v,y)=>{i(b=>{const N=[...b,{id:`${Date.now()}-${Math.random().toString(16).slice(2)}`,direction:x,event:v,payload:a2(y),timestamp:new Date().toLocaleTimeString()}];return N.length>Gp?N.slice(-Gp):N})},d=(x,...v)=>f("in",x,v),h=(x,...v)=>f("out",x,v);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=(v,...y)=>(h(v,...y),x(v,...y)),p=()=>{a.emit=x}}return()=>{a.offAny(d),p&&p()}},[a]),o.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[o.jsxs("div",{className:"flex items-center justify-between text-sm text-slate-400",children:[o.jsx("span",{children:"Socket activity"}),o.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-400",children:[o.jsxs("span",{className:"surface-muted",children:["in ",r.in]}),o.jsxs("span",{className:"surface-muted",children:["out ",r.out]}),o.jsx("button",{type:"button",className:"button-dark text-xs",onClick:()=>i([]),children:"Clear"})]})]}),o.jsx("div",{className:"surface h-64 overflow-y-auto font-mono text-xs",children:l.length===0?o.jsx("p",{children:"No socket activity yet."}):l.slice().reverse().map(f=>o.jsxs("div",{className:"surface",children:[o.jsx("span",{className:"text-slate-400",children:f.timestamp})," ",o.jsxs("span",{className:f.direction==="in"?"text-emerald-400":"text-amber-400",children:["[",f.direction.toUpperCase(),"]"]})," ",o.jsx("span",{className:"text-cyan-300",children:f.event})," ",o.jsx("span",{className:"text-slate-200",children:f.payload})]},f.id))}),o.jsx("p",{className:"text-xs text-slate-500",children:"Logs are collected only while this panel is open."})]})}const s2=[{key:"start",label:"Start OI"},{key:"safe",label:"Safe"},{key:"full",label:"Full"},{key:"passive",label:"Passive"},{key:"dock",label:"Dock"}];function hx(){const{state:{roverId:a},actions:{sendOiCommand:l,setSensorStream:i}}=Bt(),r=!!a,[f,d]=rx(),h=_n(),{value:p,save:x}=rt("page",{hudMapDesktop:!1,connectionTransport:"websocket"}),{value:v,save:y}=rt("audio",Ct),b=p?.connectionTransport||"websocket",N=Number.isFinite(v?.masterVolume)?v.masterVolume:Ct.masterVolume,M=Number.isFinite(v?.alertVolume)?v.alertVolume:Ct.alertVolume,C=Number.isFinite(v?.roverVolume)?v.roverVolume:Ct.roverVolume,E=typeof v?.autoLevelEnabled=="boolean"?v.autoLevelEnabled:Ct.autoLevelEnabled,j=v?.autoLevelMode==="duck"?"duck":"compressor",R=m.useMemo(()=>[{key:"start",label:"Enable stream",enable:!0},{key:"stop",label:"Disable stream",enable:!1}],[]),A=D=>{a&&i(D)},T=D=>{const $=D.target.value;x(Y=>({...Y??{},connectionTransport:$})),h?.io?.opts&&(h.io.opts.transports=$==="polling"?["polling"]:["websocket","polling"],h.disconnect(),h.connect())},O=D=>$=>{const Y=Number($.target.value),F=Number.isFinite(Y)?Math.max(0,Math.min(1,Y)):0;y(Q=>({...Q??{},[D]:F}))},L=D=>{const $=!!D.target.checked;y(Y=>({...Y??{},autoLevelEnabled:$}))},Z=D=>{const $=D.target.value==="duck"?"duck":"compressor";y(Y=>({...Y??{},autoLevelMode:$}))};return o.jsxs(Pf,{defaultTab:"keybindings",children:[o.jsxs(ed,{children:[o.jsx(yn,{id:"keybindings",children:"Keybindings"}),o.jsx(yn,{id:"controller",children:"Controller"}),o.jsx(yn,{id:"page",children:"Page settings"}),o.jsx(yn,{id:"admin",children:"Admin"})]}),o.jsxs(td,{children:[o.jsx(bn,{id:"keybindings",children:o.jsx("div",{className:"space-y-0.5",children:o.jsx(Vj,{})})}),o.jsx(bn,{id:"controller",children:o.jsx("div",{className:"space-y-0.5",children:o.jsx(Ij,{})})}),o.jsx(bn,{id:"page",children:o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsx("p",{className:"text-slate-400",children:"HUD"}),o.jsxs("label",{className:"flex items-center gap-0.5 text-slate-200",children:[o.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:f,onChange:D=>d(D.target.checked)}),o.jsx("span",{children:"Show top-down map in HUD (desktop)"})]}),o.jsx("p",{className:"text-xs text-slate-500",children:"Mobile HUD keeps the map on by default."})]}),o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsx("p",{className:"text-slate-400",children:"Audio"}),o.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[o.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[o.jsx("span",{children:"Master volume"}),o.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(N*100),"%"]})]}),o.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:N,onChange:O("masterVolume"),className:"w-full accent-emerald-500"})]}),o.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[o.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[o.jsx("span",{children:"Alert/page sounds"}),o.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(M*100),"%"]})]}),o.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:M,onChange:O("alertVolume"),className:"w-full accent-emerald-500"})]}),o.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[o.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[o.jsx("span",{children:"Rover audio"}),o.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(C*100),"%"]})]}),o.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:C,onChange:O("roverVolume"),className:"w-full accent-emerald-500"})]}),o.jsxs("label",{className:"flex items-center justify-between gap-0.5 text-slate-200",children:[o.jsx("span",{children:"Auto-level rover audio"}),o.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:E,onChange:L})]}),o.jsxs("label",{className:"flex items-center justify-between gap-0.5 text-slate-200",children:[o.jsx("span",{children:"Auto-level mode"}),o.jsxs("select",{value:j,onChange:Z,className:"field-input text-sm",disabled:!E,children:[o.jsx("option",{value:"compressor",children:"Compressor (preferred)"}),o.jsx("option",{value:"duck",children:"Brush/vacuum ducking"})]})]}),o.jsx("p",{className:"text-xs text-slate-500",children:"Compressor evens out incoming audio; ducking lowers volume when cleaning motors are active."})]}),o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsx("p",{className:"text-slate-400",children:"Connection"}),o.jsxs("label",{className:"flex items-center justify-between gap-0.5 text-slate-200",children:[o.jsx("span",{children:"Transport"}),o.jsxs("select",{value:b,onChange:T,className:"field-input text-sm",children:[o.jsx("option",{value:"websocket",children:"WebSocket"}),o.jsx("option",{value:"polling",children:"Polling"})]})]}),o.jsx("p",{className:"text-xs text-slate-500",children:"Switching reconnects your session."})]})]})}),o.jsx(bn,{id:"admin",children:o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsx("p",{className:"text-slate-400",children:"Manual OI commands"}),o.jsx("div",{className:"surface flex flex-wrap gap-0.5",children:s2.map(D=>o.jsx("button",{type:"button",onClick:()=>l(D.key),disabled:!r,className:"button-dark text-xs disabled:opacity-30",children:D.label},D.key))})]}),o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsx("p",{className:"text-slate-400",children:"Sensor stream"}),o.jsx("div",{className:"surface flex gap-0.5",children:R.map(D=>o.jsx("button",{type:"button",onClick:()=>A(D.enable),disabled:!r,className:"flex-1 button-dark text-xs disabled:opacity-30",children:D.label},D.key))}),!r&&o.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to toggle streams."})]}),o.jsx(ux,{}),o.jsx(e2,{}),o.jsx(kj,{}),o.jsx(t2,{}),o.jsx(l2,{})]})})]})]})}const Qp={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 i2(a){return Qp[a]||Qp.desktop}function px({actionId:a,keymap:l}){const i=l?.[a]?.[0]??"";return o.jsx("span",{className:"rounded border border-slate-600 bg-slate-900/40 px-1 text-[0.7rem] text-slate-200",children:Mn(i)})}function Kp(a,l){return a.map((i,r)=>typeof i=="string"?o.jsx("span",{children:i},`text-${r}`):i?.action?o.jsx(px,{actionId:i.action,keymap:l},`pill-${i.action}-${r}`):i?.text?o.jsx("span",{children:i.text},`span-${r}`):null)}function xo(a,l,i){if(typeof a=="string")return a;if(Array.isArray(a?.segments))return Kp(a.segments,l);if(a&&typeof a=="object"){if(typeof a.text=="string")return a.text;if(Array.isArray(a))return Kp(a,l)}return String(a??"")}function r2({hero:a,keymap:l}){return a?o.jsxs("div",{className:"surface space-y-0.5 px-0.5 py-0.5",children:[o.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-0.5",children:[o.jsxs("div",{children:[o.jsx("p",{className:"text-sm font-semibold text-white",children:a.title}),a.subtitle&&o.jsx("p",{className:"text-xs text-slate-300",children:a.subtitle})]}),a.chips&&o.jsx("div",{className:"flex flex-wrap gap-0.5 text-[0.7rem] text-slate-200",children:a.chips.map(i=>o.jsx("span",{className:"rounded border border-slate-700 px-1 py-[2px]",children:i},i))})]}),a.bullets&&o.jsx("ul",{className:"space-y-0.5 text-[0.85rem] text-slate-200",children:a.bullets.map((i,r)=>{const f=Array.isArray(i?.segments)?`hero-${r}`:`hero-${r}`;return o.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:xo(i,l)},f)})})]}):null}function o2({block:a,keymap:l}){return o.jsxs("div",{className:"space-y-0.5",children:[o.jsx("p",{className:"text-xs font-semibold text-slate-200",children:a.title}),o.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:a.items.map((i,r)=>{const f=`item-${r}`;return o.jsx("li",{className:"surface-muted flex flex-wrap items-center gap-0.5 px-0.5 py-0.25",children:xo(i,l)},f)})})]})}function c2({block:a}){const l=a.tone==="info"?"border-cyan-500/40":"border-slate-700";return o.jsxs("div",{className:`surface space-y-0.5 border ${l} px-0.5 py-0.5`,children:[o.jsx("p",{className:"text-xs font-semibold text-slate-100",children:a.title}),a.body&&o.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:a.body.map((i,r)=>o.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:xo(i,{})},`callout-${r}`))})]})}function u2({group:a,keymap:l}){return o.jsxs("div",{className:"space-y-0.5 surface",children:[o.jsx("p",{className:"px-0.5 py-0.25 text-[0.75rem] font-semibold text-slate-200",children:a.title}),o.jsx("div",{className:"space-y-0.5 px-0.5 pb-0.25",children:a.items.map(i=>o.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-0.5 py-0.25 text-[0.8rem]",children:[o.jsx("span",{className:"text-slate-200",children:i.label}),o.jsx(px,{actionId:i.action,keymap:l})]},i.action))})]})}function f2({block:a,keymap:l}){return a?o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-200",children:[o.jsx("span",{className:"font-semibold",children:a.title}),a.footnote&&o.jsx("span",{className:"text-[0.7rem] text-slate-400",children:a.footnote})]}),o.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-0.5",children:a.groups?.map(i=>o.jsx(u2,{group:i,keymap:l},i.id))})]}):null}function d2({block:a}){return a?o.jsxs("div",{className:"space-y-0.5",children:[o.jsx("p",{className:"text-xs font-semibold text-slate-200",children:a.title}),o.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:a.items?.map((l,i)=>o.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:xo(l,{})},`gamepad-${i}`))})]}):null}function Fp({block:a,keymap:l}){if(!a)return null;switch(a.type){case"list":return o.jsx(o2,{block:a,keymap:l});case"callout":return o.jsx(c2,{block:a});case"keyboard":return o.jsx(f2,{block:a,keymap:l});case"gamepad":return o.jsx(d2,{block:a});default:return null}}function gx({layout:a,keymap:l}){const i=m.useMemo(()=>i2(a),[a]),r=l||{},f=i.main||[],d=i.aside||[];return o.jsxs("div",{className:"space-y-0.5",children:[o.jsx(r2,{hero:i.hero,keymap:r}),o.jsxs("div",{className:"grid gap-0.5",style:{gridTemplateColumns:"repeat(auto-fit, minmax(360px, 1fr))"},children:[o.jsx("div",{className:"space-y-0.5",children:f.map((h,p)=>o.jsx(Fp,{block:h,keymap:r},h.title||p))}),o.jsx("div",{className:"space-y-0.5",children:d.map((h,p)=>o.jsx(Fp,{block:h,keymap:r},h.title||p))})]})]})}function xx({layout:a,onOpenOverlay:l}){const{state:i}=Bt();return o.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[o.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[o.jsx("span",{children:"Help"}),o.jsx("button",{type:"button",onClick:l,className:"button-dark px-1 py-0.25 text-[0.75rem]",children:"Open full help"})]}),o.jsx(gx,{layout:a,keymap:i?.keymap||{}})]})}const m2=["kal","rms","slt","ksp","bdl"],h2=Array.from({length:10},(a,l)=>l*10);function vo({hideInput:a=!1,hideSpectatorNotice:l=!1,fillHeight:i=!1,allowSpectatorInput:r=!1}){const{session:f}=$e(),{messages:d,typing:h,sendMessage:p,registerInputRef:x,onInputFocus:v,onInputBlur:y,blurChat:b,setTypingActive:N}=Vf(),{value:M,save:C}=rt("tts",{engine:"flite",voice:"rms",pitch:50}),[E,j]=m.useState(""),[R,A]=m.useState(!1),[T,O]=m.useState(!1),[L,Z]=m.useState(()=>M?.engine||"flite"),[D,$]=m.useState(()=>M?.voice||"rms"),[Y,F]=m.useState(()=>Number.isFinite(M?.pitch)?M.pitch:50),Q=f?.role!=="spectator"||r,ne=m.useRef(null),ue=f?.assignment?.roverId||null,V=!!m.useMemo(()=>f?.roster?.find(w=>String(w.id)===String(ue))||null,[ue,f?.roster])?.audio?.ttsEnabled,q=m.useMemo(()=>d.slice(-200),[d]),X=m.useMemo(()=>h||[],[h]);m.useEffect(()=>{ne.current&&(ne.current.scrollTop=ne.current.scrollHeight)},[q,X]),m.useEffect(()=>{const w=M?.engine||"flite",z=M?.voice||"rms",P=Number.isFinite(M?.pitch)?M.pitch:50;L!==w&&Z(w),D!==z&&$(z),Y!==P&&F(P)},[L,Y,M?.engine,M?.pitch,M?.voice,D]),m.useEffect(()=>{O(!!V)},[V]);const W=m.useMemo(()=>!V||!T?null:L==="espeak"?{speak:!0,engine:L,pitch:Y}:{speak:!0,engine:L,voice:D},[L,Y,T,V,D]);async function oe(w){if(w.preventDefault(),!Q||a)return;const P=E.replace(/\\n/g,` `).trim();if(P){A(!0);try{await p(P,W),j(""),b(),N(!1)}catch(te){alert(te.message)}finally{A(!1)}}}const fe=i?"flex-1 min-h-0 overflow-y-auto":"h-48 overflow-y-auto";return o.jsxs("section",{className:`panel-section space-y-0.5 text-base ${i?"flex h-full flex-col overflow-hidden":""}`,children:[o.jsxs("div",{className:`surface overflow-y-auto space-y-0.5 px-0 ${fe}`,ref:ne,children:[q.length===0&&X.length===0?o.jsx("p",{className:"text-sm text-slate-500",children:"No messages yet."}):q.map(w=>o.jsx(zg,{message:w},w.id)),X.map(w=>o.jsx(Ug,{message:w},`typing-${w.typingId||w.id}`))]}),!a&&o.jsxs("form",{className:"flex flex-wrap items-stretch gap-0.5",onSubmit:oe,children:[o.jsx("input",{className:"field-input flex-1 min-w-[10rem]",value:E,onChange:w=>{const z=w.target.value;j(z),N(!!z.trim())},onFocus:w=>{v(w),N(!!E.trim())},onBlur:w=>{y(w),N(!1)},onKeyDown:w=>{w.key==="Enter"&&!E.trim()&&(w.preventDefault(),b(),N(!1))},ref:w=>x(w,{target:"panel"}),placeholder:Q?"Type a message…":l?"":"Spectators cannot chat",disabled:!Q}),V&&o.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 basis-full sm:basis-auto",children:[o.jsxs("label",{className:"flex items-center gap-0.5 text-xs text-slate-300",children:[o.jsx("input",{type:"checkbox",checked:T,onChange:w=>O(w.target.checked),className:"accent-cyan-500"}),o.jsx("span",{children:"Speak"})]}),o.jsxs("select",{value:L,onChange:w=>{const z=w.target.value;Z(z),C(P=>({...P||{},engine:z}))},className:"field-input text-xs",children:[o.jsx("option",{value:"flite",children:"flite"}),o.jsx("option",{value:"espeak",children:"espeak"})]}),L==="flite"?o.jsx("select",{value:D,onChange:w=>{const z=w.target.value;$(z),C(P=>({...P||{},voice:z}))},className:"field-input text-xs",children:m2.map(w=>o.jsx("option",{value:w,children:w},w))}):o.jsx("select",{value:Y,onChange:w=>{const z=Number(w.target.value);F(z),C(P=>({...P||{},pitch:z}))},className:"field-input text-xs",children:h2.map(w=>o.jsxs("option",{value:w,children:["pitch ",w]},w))})]}),o.jsx("button",{type:"submit",disabled:!Q||R,className:"button-dark h-full disabled:opacity-50 self-stretch",children:R?"...":"Send"})]})]})}function nd({compact:a=!1}){const{session:l,setNickname:i}=$e(),{value:r,save:f}=rt("profile",{nickname:""}),[d,h]=m.useState(r.nickname||""),[p,x]=m.useState(!1),v=l?.role!=="spectator";m.useEffect(()=>{h(r.nickname||"")},[r.nickname]);async function y(b){if(b.preventDefault(),!v)return;const N=(d||"").trim().slice(0,32);if(N){x(!0);try{await i(N),f({nickname:N})}catch(M){alert(M.message)}finally{x(!1)}}}return o.jsxs("form",{className:"grid w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-stretch gap-0.5",onSubmit:y,children:[o.jsx("input",{className:"field-input flex-1 min-w-0",value:d,onChange:b=>h(b.target.value),maxLength:32,placeholder:"Enter a nickname",disabled:!v}),o.jsx("button",{type:"submit",disabled:!v||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 p2(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 g2(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",r=a.url||a.link||a.href||null;return r?{id:l,label:i,url:r}:null}function vx({className:a=""}){const{session:l}=$e(),i=p2(l).map(g2).filter(Boolean).slice(0,4);return i.length?o.jsx("div",{className:`grid grid-cols-2 grid-rows-2 gap-0.5 ${a}`,children:i.map(r=>o.jsx(Ff,{...r},`${r.id||r.label}-${r.url}`))}):null}function x2({compact:a=!1}){return o.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:o.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:o.jsx(nd,{compact:a})})})}function v2(){return o.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:o.jsx(vx,{className:"flex-1 min-h-0"})})}function af(...a){return a.filter(Boolean).join(" ")}function y2(a){const l=a?.batteryState?.percentDisplay;return l==null?"--":`${l}%`}function b2(a){return a?.batteryState?a.batteryState.urgentActive?"text-red-400":a.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function S2(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 A2(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 yo({title:a="Rovers"}){const{session:l,requestControl:i}=$e(),[r,f]=m.useState({}),[d,h]=m.useState(()=>Date.now()),p=m.useMemo(()=>l?.role&&l.role!=="spectator",[l?.role]),x=l?.roster??[],v=l?.turnQueues??{},y=l?.users??[],b=l?.socketId||null,N=m.useMemo(()=>Object.values(v||{}).some(j=>j?.deadline||j?.idleDeadline),[v]);m.useEffect(()=>{if(!N)return;const j=setInterval(()=>{h(Date.now())},1e3);return()=>clearInterval(j)},[N]);const M=m.useMemo(()=>{const j=new Set(x.map(A=>String(A.id))),R=Object.keys(v||{}).filter(A=>!j.has(String(A))).map(A=>({id:A,name:A,locked:!1,batteryState:null}));return[...x,...R]},[x,v]);async function C(j){if(j){f(R=>({...R,[j]:!0}));try{await i(j)}catch(R){alert(R.message)}finally{f(R=>({...R,[j]:!1}))}}}const E=j=>y.find(R=>R.socketId===j)||{socketId:j,nickname:null,role:null};return o.jsxs("div",{className:"space-y-0.5 text-sm",children:[a&&o.jsx("p",{className:"text-sm text-slate-400",children:a}),M.length===0?o.jsx("p",{className:"text-sm text-slate-500",children:"No rovers registered."}):o.jsx("ul",{className:"space-y-0.5 text-sm",children:M.map(j=>{const R=String(j.id),A=v?.[R]||null,T=A?.queue||[],O=A?.idleDeadline||A?.deadline||null,L=O&&O>d?Math.ceil((O-d)/1e3):O?0:null,Z=A?.current||null,D=Z?T.findIndex(q=>q===Z):-1,$=T.length>1?D>=0?T[(D+1)%T.length]:T[0]:null,Y=!!(b&&Z&&Z===b),F=!!(b&&$&&$===b),Q=L!=null&&(Y||F),ne=!!j.locked,ue=j.lockReason?`locked: ${j.lockReason}`:"locked",he=ne?ue:r[R]?"...":"request",V=p&&!ne&&!r[R];return o.jsxs("li",{className:af("surface flex flex-wrap items-start justify-between gap-0.5",V&&"cursor-pointer",ne&&"bg-red-900/40"),onClick:()=>{V&&C(j.id)},children:[o.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-0.5",children:[o.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[o.jsxs("div",{className:"flex items-center gap-0.5",children:[o.jsx("p",{className:"text-slate-200",children:j.name}),Q?o.jsx("span",{className:"rounded bg-slate-800 px-1 text-[0.7rem] text-slate-200",children:Y?`${L}s left`:`Your turn in ${L}s`}):null]}),o.jsx("span",{className:af("text-[0.75rem] font-semibold",b2(j)),children:y2(j)})]}),T.length===0?o.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"No queue."}):o.jsx("div",{className:"flex flex-wrap items-center gap-0.5",children:T.map((q,X)=>{const W=E(q),oe=q===Z,fe=!!($&&q===$&&!oe),w=oe?"bg-sky-600 text-white ring-2 ring-amber-300":fe?"bg-emerald-700/60 text-emerald-100 ring-1 ring-emerald-300/70":"bg-slate-800 text-slate-200";return o.jsxs("span",{className:`flex items-center gap-0.5 rounded px-1 text-[0.7rem] ${w}`,children:[o.jsx("span",{className:`${S2(W.role)} font-semibold`,children:A2(W,b)}),oe&&o.jsx("span",{className:"text-[0.65rem] text-slate-200",children:"now"}),fe&&o.jsx("span",{className:"text-[0.65rem] text-emerald-100",children:"next"})]},`${R}-${q}-${X}`)})})]}),p?o.jsx("button",{type:"button",onClick:q=>{q.stopPropagation(),C(j.id)},disabled:r[R]||ne,className:af("button-dark disabled:opacity-40",ne&&"bg-red-600/70 text-white hover:bg-red-600"),children:he}):null]},j.id)})})]})}function j2(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 N2(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 oo({hideNicknameForm:a=!1,hideHeader:l=!1,className:i="",fillHeight:r=!1,compact:f=!1}){const{session:d,setNickname:h}=$e(),{value:p}=rt("profile",{nickname:""}),x=m.useRef(null),v=_n(),y=d?.role!=="spectator",b=d?.users??[],N=d?.socketId||null;m.useEffect(()=>{if(!y||!d?.socketId)return;const E=p.nickname||"";if(!E||d.socketId===x.current)return;const j=d.socketId;h(E).then(()=>{x.current=j}).catch(()=>{})},[y,d?.socketId,h,p.nickname]),m.useEffect(()=>{if(!v)return;const E=()=>{if(!y)return;const j=(p.nickname||"").trim();j&&h(j).then(()=>{x.current=d?.socketId||null}).catch(()=>{})};return v.on("connect",E),()=>v.off("connect",E)},[y,h,v,p.nickname,d?.socketId]);const M=m.useMemo(()=>[...b].sort((E,j)=>E.socketId===N?-1:j.socketId===N?1:(E.nickname||"").localeCompare(j.nickname||"")),[N,b]),C=r?"flex-1 min-h-0 overflow-y-auto":f?"h-28 overflow-y-auto":"h-48 overflow-y-auto";return o.jsxs("section",{className:`panel-section space-y-0.5 text-base ${r?"flex h-full min-h-0 flex-col overflow-hidden":""} ${i}`,children:[!a&&o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("div",{className:"grid gap-0.5 md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]",children:[o.jsx("div",{className:"min-w-0",children:o.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:o.jsx(nd,{compact:f})})}),o.jsx(vx,{})]}),!y&&o.jsx("p",{className:"text-xs text-slate-500",children:"Spectators cannot set nicknames."})]}),o.jsxs("div",{className:`space-y-0.5 ${r?"flex flex-1 min-h-0 flex-col":""}`,children:[!l&&o.jsxs("div",{className:`flex items-center justify-between text-sm text-slate-400 ${f?"text-xs":""}`,children:[o.jsx("span",{children:"Users"}),o.jsx("span",{className:"text-xs text-slate-500",children:M.length})]}),o.jsx("div",{className:`surface flex flex-wrap content-start items-start gap-0.5 px-0 pb-0 ${C} ${f?"text-[0.8rem]":""}`,children:M.length===0?o.jsx("p",{className:"text-sm text-slate-500",children:"Waiting for users…"}):M.map(E=>o.jsx("span",{className:`rounded bg-slate-800/80 px-1 py-0.25 text-[0.7rem] ${j2(E.role)}`,children:N2(E,N)},E.socketId))})]})]})}function w2(){const{state:{roverId:a}}=Bt(),i=is(a)?.sensors||{};return o.jsx("section",{className:"panel-section",children:o.jsx("div",{className:"aspect-square w-full",children:o.jsx(Af,{sensors:i})})})}function E2(){const{state:{roverId:a,keymap:l,camera:i,horn:r},pipeline:f,actions:{setServoAngle:d,setNightVision:h,startHorn:p,stopHorn:x}}=Bt(),v=Wg(a),y=v.docked&&!v.driving,b=i?.config,N=!!(a&&i?.enabled&&b),M=!!(a&&f?.nightVision),C=f?.nightVisionState,E=!!(a&&f?.horn),j=r?.overheated,R=typeof b?.minAngle=="number"?b.minAngle:-30,A=typeof b?.maxAngle=="number"?b.maxAngle:30,T=typeof i?.angle=="number"?i.angle:typeof b?.homeAngle=="number"?b.homeAngle:(R+A)/2,O=Mn(l?.nightVisionToggle?.[0]),L=Mn(l?.hornHonk?.[0]),Z=Mn(l?.cameraUp?.[0]),D=Mn(l?.cameraDown?.[0]);return o.jsxs("section",{className:"panel-section grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] gap-0.5",children:[o.jsx(Pg,{layout:"desktop",expand:!0,driveDockState:v}),y?null:o.jsxs("div",{className:"surface space-y-0.5 p-0 text-sm text-slate-200",children:[M&&o.jsx(ex,{nightVisionOn:C?.nightVisionOn,disabled:!a,onToggle:h,keyLabel:O}),E&&o.jsx(tx,{disabled:!a||j,onStart:p,onStop:x,keyLabel:L,active:r?.active,heat:r?.heat}),N&&o.jsx(nx,{value:T,min:R,max:A,onChange:d,keyDownLabel:D,keyUpLabel:Z,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 C2({layout:a,onOpenHelpOverlay:l}){return o.jsx("section",{className:"panel text-base",children:o.jsxs(Pf,{defaultTab:"telemetry",children:[o.jsxs(ed,{children:[o.jsx(yn,{id:"telemetry",children:"Controls"}),o.jsx(yn,{id:"help",children:"Help"}),o.jsx(yn,{id:"settings",children:"Settings"})]}),o.jsxs(td,{children:[o.jsx(bn,{id:"telemetry",children:o.jsxs("div",{className:"space-y-0.5",children:[o.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]",children:[o.jsx(w2,{}),o.jsx(E2,{})]}),o.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]",children:[o.jsx(yo,{}),o.jsx(zf,{panelId:"replay-sources-desktop",fillHeight:!0}),o.jsx(v2,{})]}),o.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)] h-[14rem]",children:[o.jsx(vo,{fillHeight:!0}),o.jsxs("div",{className:"grid min-h-0 gap-0.5 grid-rows-[minmax(0,1fr)_auto]",children:[o.jsx(oo,{compact:!0,hideNicknameForm:!0,fillHeight:!0}),o.jsx(x2,{compact:!0})]})]}),o.jsx(cx,{}),o.jsx(Gf,{defaultOrientation:"horizontal",panelId:"rightpane-telemetry"})]})}),o.jsx(bn,{id:"help",children:o.jsx(xx,{layout:a,onOpenOverlay:l})}),o.jsx(bn,{id:"settings",children:o.jsx(hx,{})})]})]})})}const q2=new Set(["admin","lockdown","lockdown-admin"]),M2=new Set(["lockdown","lockdown-admin"]),R2=new Set(["admin","lockdown"]);function k2(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 T2(){const{session:a}=$e(),l=a?.mode,i=a?.role,r=a?.adminReason?.text||"";a?.adminReason?.updatedAt;const f=a?.timezone||"UTC",d=a?.socials?.find(N=>String(N?.id||N?.label||"").toLowerCase()==="discord")?.url||a?.discord?.invite||null,h=R2.has(l),p=l==="lockdown"?M2.has(i):q2.has(i),[x,v]=m.useState(()=>new Date),y=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 N=setInterval(()=>v(new Date),1e3);return()=>clearInterval(N)},[]),!h||p)return null;const b=k2(l);return o.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:o.jsxs("div",{className:"surface w-full max-w-md space-y-0.5 text-slate-100 shadow-2xl",children:[o.jsxs("div",{className:"space-y-0.5",children:[o.jsx("p",{className:"text-lg font-semibold",children:b.title}),o.jsx("p",{className:"text-sm text-slate-300",children:b.description})]}),o.jsxs("div",{className:"surface-muted space-y-0.5",children:[o.jsx("p",{className:"text-[0.7rem] tracking-wide text-slate-400",children:"Reason for locking:"}),o.jsx("p",{className:"text-lg font-semibold text-slate-100",children:r||"No reason set."}),o.jsxs("p",{className:"text-center text-sm text-slate-300",children:["Server time: ",y]})]}),o.jsx("div",{className:"surface-muted",children:o.jsx(ux,{})}),o.jsx("div",{className:"w-full justify-center items-center",children:o.jsx(Ff,{id:"discord",label:"Join our Discord server for updates!",url:d})}),"You can still use the chat while the server is locked:",o.jsxs("div",{className:"max-h-80 overflow-y-auto",children:[o.jsx(vo,{}),o.jsx(nd,{})]})]})})}const _2="/assets/turn_alert-DVO8EZJa.mp3";function D2(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 O2(){const{session:a,pushAlert:l}=$e(),{value:i}=rt("audio",Ct),r=Number.isFinite(i?.masterVolume)?i.masterVolume:Ct.masterVolume,f=Number.isFinite(i?.alertVolume)?i.alertVolume:Ct.alertVolume,d=Math.max(0,Math.min(1,r*f)),h=D2(_2,d),p=m.useRef(new Set),x=m.useMemo(()=>a?.turnQueues||{},[a?.turnQueues]),v=a?.socketId||null;return m.useEffect(()=>{if(!v)return;const y=[];Object.entries(x).forEach(([b,N])=>{!N||!N.current||N.current!==v||p.current.has(b)||(y.push(b),p.current.add(b))}),Object.keys(x).forEach(b=>{(!x[b]||x[b].current!==v)&&p.current.delete(b)}),y.length!==0&&(y.forEach(b=>{const N=a?.roster?.find(M=>String(M.id)===String(b))?.name||b;l({title:"Your turn!",message:`You now control ${N}.`,color:"#ff5722"})}),h())},[x,h,l,a?.roster,v]),null}function B2({visible:a,mode:l,onEnterFullscreen:i,onDismiss:r}){if(!a)return null;const f=l==="pwa-hint";return o.jsx("div",{className:"fixed inset-0 z-40 flex items-end justify-center p-2 pointer-events-none sm:items-center",children:o.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:o.jsxs("div",{className:"space-y-0.5 p-4 text-sm text-slate-100",children:[o.jsx("h2",{className:"text-base font-semibold text-white",children:"Better in fullscreen"}),f?o.jsxs("p",{className:"text-slate-300",children:["For fullscreen on iOS, open Safari's share menu and pick ",o.jsx("strong",{children:"Add to Home Screen"}),". Launching from the home screen removes the browser chrome."]}):o.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."}),o.jsxs("div",{className:"flex justify-end gap-0.5 pt-1 text-sm",children:[o.jsx("button",{type:"button",className:"rounded border border-slate-600 px-3 py-1 text-slate-200",onClick:r,children:f?"Got it":"Not now"}),!f&&o.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 yx="fullscreenPromptDismissed",L2=new Set(["mobile-portrait","mobile-landscape"]),bx=()=>{if(typeof window>"u")return null;try{return window.sessionStorage}catch{return null}},z2=()=>{const a=bx();if(!a)return!1;try{return a.getItem(yx)==="1"}catch{return!1}},Zp=()=>{const a=bx();if(a)try{a.setItem(yx,"1")}catch{}},U2=()=>{if(typeof navigator>"u")return!1;const a=navigator.userAgent||navigator.platform||"";return/iphone|ipad|ipod/i.test(a)},H2=()=>{if(typeof document>"u")return!1;const a=document;return!!(a.fullscreenEnabled||a.webkitFullscreenEnabled||a.mozFullScreenEnabled||a.msFullscreenEnabled)};function V2(a){const l=L2.has(a),[i,r]=m.useState(!1),[f,d]=m.useState("native"),h=m.useMemo(()=>U2(),[]),p=m.useCallback(()=>{if(!l){r(!1);return}if(z2()){r(!1);return}if(h){d("pwa-hint"),r(!0);return}if(!H2()){r(!1);return}if(typeof document<"u"&&document.fullscreenElement){r(!1);return}d("native"),r(!0)},[h,l]);m.useEffect(()=>{p()},[p]),m.useEffect(()=>{if(typeof document>"u")return;const y=()=>{if(document.fullscreenElement){r(!1);return}p()};return document.addEventListener("fullscreenchange",y),document.addEventListener("webkitfullscreenchange",y),()=>{document.removeEventListener("fullscreenchange",y),document.removeEventListener("webkitfullscreenchange",y)}},[p]);const x=m.useCallback(()=>{Zp(),r(!1)},[]),v=m.useCallback(async()=>{if(!l||h||typeof document>"u")return!1;const y=document.documentElement;if(!y)return!1;const b=y.requestFullscreen||y.webkitRequestFullscreen||y.mozRequestFullScreen||y.msRequestFullscreen;if(!b)return!1;try{const N=b.call(y);return N&&typeof N.then=="function"&&await N,Zp(),r(!1),!0}catch(N){return console.warn("Failed to enter fullscreen",N),!1}},[h,l]);return m.useEffect(()=>{l||r(!1)},[l]),{visible:i,mode:f,enterFullscreen:v,dismiss:x}}function Y2({visible:a,layout:l,onClose:i,showOnLoad:r,onToggleShowOnLoad:f}){if(!a)return null;const{state:d}=Bt(),h=p=>{const x=!p.target.checked;f?.(x)};return o.jsx("div",{className:"fixed inset-0 z-30 flex items-center justify-center bg-black/80 px-0.5 py-0.5",children:o.jsxs("div",{className:"pointer-events-auto surface w-full max-w-4xl max-h-[90vh] overflow-hidden shadow-2xl",children:[o.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:[o.jsx("span",{className:"font-semibold",children:"Help & controls"}),o.jsxs("div",{className:"flex items-center gap-0.5 text-[0.8rem] text-slate-300",children:[o.jsxs("label",{className:"flex items-center gap-0.5",children:[o.jsx("input",{type:"checkbox",checked:!r,onChange:h,className:"accent-cyan-500"}),o.jsx("span",{children:"Don't show again"})]}),o.jsx("button",{type:"button",onClick:i,className:"button-dark px-2 py-0.25 text-[0.8rem]",children:"Close"})]})]}),o.jsx("div",{className:"max-h-[85vh] overflow-y-auto p-0.5",children:o.jsx(gx,{layout:l,keymap:d?.keymap||{}})})]})})}const $2=`aroma bagel batter diff --git a/server/public/index.html b/server/public/index.html index c3ef3583..b75e2504 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -11,7 +11,7 @@ Multi Roomba Rover - + diff --git a/webui/src/components/VideoTile.jsx b/webui/src/components/VideoTile.jsx index 0ad53f35..d604709c 100644 --- a/webui/src/components/VideoTile.jsx +++ b/webui/src/components/VideoTile.jsx @@ -323,6 +323,7 @@ export default function VideoTile({ return undefined; } const unlock = () => { + if (audioContextRef.current?.state === 'running') return; logAudio('unlock/gesture'); forceUnlockCompressorAudio(); }; @@ -586,7 +587,7 @@ export default function VideoTile({ target.play().then(() => logAudio('audio/play-ok')).catch((err) => logAudio('audio/play-fail', { error: err?.message })); } } - if (['error', 'failed', 'disconnected', 'closed'].includes(nextStatus)) { + if (['error', 'failed'].includes(nextStatus)) { scheduleAudioRestart(); } };