From 03cd8c00dd657efbd22d60960f44d6a9a658d5fd Mon Sep 17 00:00:00 2001 From: legop3 Date: Fri, 1 May 2026 23:19:22 -0400 Subject: [PATCH] lidar adjustmints --- .../{index-CyrLefed.js => index-DW9KJfni.js} | 2 +- server/public/index.html | 2 +- .../src/services/replayEngineV2/constants.js | 2 +- webui/src/components/vip/VipNeatoCard.jsx | 20 ++++++++++--------- 4 files changed, 14 insertions(+), 12 deletions(-) rename server/public/assets/{index-CyrLefed.js => index-DW9KJfni.js} (95%) diff --git a/server/public/assets/index-CyrLefed.js b/server/public/assets/index-DW9KJfni.js similarity index 95% rename from server/public/assets/index-CyrLefed.js rename to server/public/assets/index-DW9KJfni.js index 84d84861..73c9ecc9 100644 --- a/server/public/assets/index-CyrLefed.js +++ b/server/public/assets/index-DW9KJfni.js @@ -15,7 +15,7 @@ Please change the parent to l&&l>o){const u=Si/100,f=1-u;if(n<=o)return 0;if(n<=l){const h=(n-o)/(l-o);return Math.round(Math.max(0,Math.min(1,h))*Si)}if(n>=s)return 100;const p=(n-l)/(s-l);return Math.round((u+Math.max(0,Math.min(1,p))*f)*100)}return null}const $N=1600,GN=800;function An(...n){return n.filter(Boolean).join(" ")}function Ud({visual:n,orientation:s="horizontal",variant:l="inline",compact:o=!1,showLabel:u,className:f=""}){const p=l==="background",h=s==="vertical",x=u??!p;if(!n?.available){if(p){const ee=Si,oe=An("absolute z-10",h?"left-0 right-0 h-[2px]":"top-0 bottom-0 w-[2px]","bg-black/70",M&&!S&&"battery-tick-warn"),ce=h?{bottom:`${ee}%`}:{left:`${ee}%`},U=An("absolute inset-0 pointer-events-none",f);return r.jsxs("div",{className:U,children:[r.jsx("div",{className:"absolute inset-0 bg-slate-900/35"}),r.jsx("div",{className:oe,style:ce})]})}return h?null:r.jsx("p",{className:"text-xs text-slate-500",children:"Battery telemetry unavailable"})}const g=n.percentDisplay??0,v=Math.max(0,Math.min(100,g)),y=n.warnDisplayPercent??Si,S=!!n.urgentActive,M=!!n.warnActive,C=S?`${GN}ms`:M?`${$N}ms`:void 0,N=p?S||M?"bg-red-500/45":"bg-emerald-500/45":S||M?"bg-red-500":"bg-emerald-500",w=S||M?"bg-red-900/35":"bg-slate-900/35",A=S||M?"bg-red-900/45":"bg-slate-900/35",q=An(p?"absolute inset-0 pointer-events-none":"relative pointer-events-auto",h&&!p?"flex flex-col items-center justify-center":"w-full",f);if(p){const ee=h?{height:`${v}%`}:{width:`${v}%`},oe=An("absolute",h?"bottom-0 left-0 right-0":"left-0 top-0 bottom-0",N,"transition-[width,height]",S&&"battery-urgent-flash"),ce=An("absolute z-10",h?"left-0 right-0 h-[2px]":"top-0 bottom-0 w-[2px]","bg-black/70",M&&!S&&"battery-tick-warn"),U=h?{bottom:`${y}%`}:{left:`${y}%`};return r.jsxs("div",{className:q,children:[r.jsx("div",{className:An("absolute inset-0",w,S&&"battery-urgent-flash")}),r.jsx("div",{className:oe,style:{...ee,animationDuration:C}}),r.jsx("div",{className:ce,style:U})]})}const k=An("relative overflow-hidden rounded-sm border border-slate-800",S||M?A:"bg-zinc-900/90",S&&"battery-urgent-flash",h?"w-full flex-1":o?"h-4":"h-5"),B=h?{height:`${v}%`}:{width:`${v}%`},Q=An("absolute",h?"bottom-0 left-0 right-0":"left-0 top-0 bottom-0",N,"transition-[width,height]",S&&"battery-urgent-flash"),D=An("absolute z-10",h?"left-0 right-0 h-[2px]":"top-0 bottom-0 w-[2px]","bg-black/70",M&&!S&&"battery-tick-warn"),H=h?{bottom:`${y}%`}:{left:`${y}%`},K=`Battery ${g}%`,G=An("pointer-events-none absolute inset-0 flex items-center justify-center text-center font-semibold",h?o?"text-[0.55rem]":"text-[0.65rem]":"text-xs"),$=An("rounded px-1.5 py-0.5",p?"bg-black/30 text-slate-100":"bg-black/50 text-slate-100");return r.jsxs("div",{className:q,children:[r.jsxs("div",{className:k,children:[r.jsx("div",{className:Q,style:{...B,animationDuration:C}}),r.jsx("div",{className:D,style:H})]}),x&&!h?r.jsx("div",{className:G,children:r.jsx("span",{className:$,children:K})}):null,x&&h?r.jsx("div",{className:"mt-0.5 text-[0.65rem] font-semibold text-slate-100",children:r.jsxs("span",{className:$,children:[g,"%"]})}):null]})}function YN({mobileHud:n=!1,isActiveDriver:s=!1,idleSkipSeconds:l=null}){const o=n?"text-3xl":"text-5xl",u=n?"text-xs":"text-sm",f=n?"text-[0.55rem]":"text-[0.75rem]",p=n?"px-4 py-3":"px-6 py-4",h=s&&typeof l=="number";return r.jsx("div",{className:"pointer-events-none absolute inset-0 z-30 flex items-center justify-center bg-black/55",children:r.jsxs("div",{className:`flex flex-col items-center gap-0.5 rounded border border-amber-300/80 bg-black/70 ${p}`,children:[r.jsx("div",{className:`font-semibold text-amber-200 ${o}`,children:"IT IS YOUR TURN!"}),r.jsx("div",{className:`text-amber-200/80 ${u}`,children:"Start driving!"}),h?r.jsxs("div",{className:`text-red-100/90 ${f}`,children:["Idle skip in ",l,"s"]}):null]})})}function Mo(n){return Math.max(0,Math.min(1,n))}function Ug(n){return(n-90)*Math.PI/180}function pi(n,s,l,o){const u=Ug(o);return{x:n+l*Math.cos(u),y:s+l*Math.sin(u)}}function FN(n,s,l,o,u){let f=o,p=u;p<=f&&(p+=360);const h=pi(n,s,l,f),x=pi(n,s,l,p);return["M",h.x,h.y,"A",l,l,0,0,1,x.x,x.y].join(" ")}function pf(n,s){if(s)return"#ef4444";const l=Math.abs(n);return l>900?"#f59e0b":l>300?"#22c55e":"#64748b"}function XN(n,s){if(n==null||n<=0)return"hsl(200 60% 18%)";const o=Mo(n/(s||1));return`hsl(${(200+Math.pow(o,.45)*360)%360} 100% 60%)`}function QN(n,s){if(s)return"#ef4444";const l=Mo(n==null?0:n/4095),o=[47,55,69],u=[245,158,11],[f,p,h]=o.map((x,g)=>Math.round(x+(u[g]-x)*l));return`rgb(${f}, ${p}, ${h})`}function KN({count:n,totalSpan:s,gap:l,startAngle:o}){const f=(s-l*(n-1))/n,p=[];let h=o;for(let x=0;x0?.65:null;return r.jsxs("g",{style:{transformOrigin:`${n}px ${s}px`,animation:h?`spin ${h}s linear infinite`:"none",animationDirection:h?"reverse":"normal"},children:[r.jsx("circle",{cx:n,cy:s,r:10,fill:"#64748b",stroke:"#64748b",strokeWidth:"1"}),[0,120,240].map(g=>{const v=Ug(g),y=n+p*Math.cos(v),S=s+p*Math.sin(v);return r.jsx("line",{x1:n,y1:s,x2:y,y2:S,stroke:f,strokeWidth:"4",strokeLinecap:"round"},g)}),o?r.jsx("circle",{cx:n,cy:s,r:p+8,stroke:"#ef4444",strokeWidth:"3",fill:"none",className:"animate-pulse"}):null]})}function JN({cx:n,cy:s,current:l,overcurrent:o,variant:u,dirtLeft:f,dirtRight:p}){const h=Math.abs(l),x=pf(l,o),g=1,v=96,y=12,S=`main-brush-pattern-a-${u}`,M=`main-brush-pattern-b-${u}`,C=h>0?.6:null,E=l>=0?1:-1;return r.jsxs("g",{children:[r.jsx("defs",{children:[S,M].map((N,w)=>r.jsxs("pattern",{id:N,patternUnits:"userSpaceOnUse",width:"6",height:"6",children:[r.jsx("rect",{width:"6",height:"6",fill:x,opacity:g}),r.jsx("path",{d:"M0 6 L6 0",stroke:"#0f172a",strokeWidth:"1"}),C?r.jsx("animateTransform",{attributeName:"patternTransform",type:"translate",from:"0 0",to:`0 ${E*(w===0?6:-6)}`,dur:`${C}s`,repeatCount:"indefinite"}):null]},N))}),r.jsx("rect",{x:n-v/2,y:s-14,width:v,height:y,rx:"3",fill:`url(#${S})`,stroke:"#64748b",strokeWidth:"1"}),r.jsx("rect",{x:n-v/2,y:s+4,width:v,height:y,rx:"3",fill:`url(#${M})`,stroke:"#64748b",strokeWidth:"1"}),r.jsx("circle",{cx:n-v/4,cy:s-8,r:3.5,fill:"#fbbf24"}),r.jsx("circle",{cx:n+v/4,cy:s+8,r:3.5,fill:"#fbbf24"}),o?r.jsx("rect",{x:n-v/2-4,y:s-16,width:v+8,height:y*2+16,rx:"6",stroke:"#ef4444",strokeWidth:"3",fill:"none",className:"animate-pulse"}):null]})}function Hd({sensors:n={},variant:s="full",size:l,overlay:o=!1}){const u=l||(s==="mini"?190:260),f=u/2,p=u*.07,h=f,x=f+p,g=f*.8,v=g-4,y=g+4,S=g-14,M=g-6,C=g*.65,E=n?.bumpsAndWheelDrops||{},N=n?.wheelOvercurrents||{},w=n?.wheelLeftCurrentMa??0,A=n?.wheelRightCurrentMa??0,q=n?.sideBrushCurrentMa??0,k=n?.mainBrushCurrentMa??0,B=6,Q=E.bumpLeft?B:0,D=E.bumpRight?B:0,H=KN({count:6,totalSpan:140,gap:6,startAngle:-70}),K=["L","FL","CL","CR","FR","R"],G=[n?.lightBumpLeftSignal,n?.lightBumpFrontLeftSignal,n?.lightBumpCenterLeftSignal,n?.lightBumpCenterRightSignal,n?.lightBumpFrontRightSignal,n?.lightBumpRightSignal],$=H.map((U,R)=>({label:K[R],start:U.start,end:U.end,value:G[R]})),ee=[{label:"Cliff L",start:-60,end:-46,value:n?.cliffLeftSignal,active:n?.cliffLeft},{label:"Cliff FL",start:-32,end:-16,value:n?.cliffFrontLeftSignal,active:n?.cliffFrontLeft},{label:"Cliff FR",start:16,end:32,value:n?.cliffFrontRightSignal,active:n?.cliffFrontRight},{label:"Cliff R",start:46,end:60,value:n?.cliffRightSignal,active:n?.cliffRight}],oe=G.filter(U=>U!=null),ce=oe.length?Math.max(...oe,1200):1200;return r.jsxs("div",{className:`${o?"relative":"surface relative p-1"}`,style:o?{width:`${u}px`,height:`${u}px`}:{height:"100%",width:"100%",aspectRatio:"1 / 1"},children:[o?null:r.jsxs(r.Fragment,{children:[r.jsx("div",{className:"absolute left-1 top-1 text-xs text-slate-400",children:"Top-down"}),r.jsxs("div",{className:"absolute right-1 top-1 text-[0.65rem] text-slate-500",children:["0–",ce]})]}),r.jsxs("svg",{width:"100%",height:"100%",viewBox:`0 0 ${u} ${u}`,preserveAspectRatio:"xMidYMid meet",className:"mx-auto block",children:[r.jsx("circle",{cx:h,cy:x,r:g,fill:"#0f172a",stroke:"#334155",strokeWidth:"2"}),r.jsx(ix,{cx:h-C,cy:x,current:w,drop:E.wheelDropLeft,overcurrent:N.leftWheel,label:"L"}),r.jsx(ix,{cx:h+C,cy:x,current:A,drop:E.wheelDropRight,overcurrent:N.rightWheel,label:"R"}),r.jsx(IN,{cx:h+g*.65,cy:x-g*.55,current:q,overcurrent:N.sideBrush}),r.jsx(JN,{cx:h,cy:x,current:k,overcurrent:N.mainBrush,variant:s,dirtLeft:n?.dirtDetectLeft,dirtRight:n?.dirtDetect}),$.map(U=>{const R=XN(U.value,ce),L=y+4,Z=L+28;return r.jsx(ZN,{cx:h,cy:x,rBase:Z,rTip:L,startDeg:U.start,endDeg:U.end,color:R,value:U.value,max:ce},U.label)}),ee.map(U=>r.jsx(bd,{cx:h,cy:x,rInner:S,rOuter:M,startDeg:U.start,endDeg:U.end,color:QN(U.value,U.active),opacity:1,pulse:!!U.active},U.label)),r.jsx(bd,{cx:h,cy:x,rInner:v-Q,rOuter:y-Q,startDeg:-70,endDeg:-6,color:E.bumpLeft?"#ef4444":"#475569",opacity:1,pulse:!!E.bumpLeft}),r.jsx(bd,{cx:h,cy:x,rInner:v-D,rOuter:y-D,startDeg:6,endDeg:70,color:E.bumpRight?"#ef4444":"#475569",opacity:1,pulse:!!E.bumpRight})]})]})}function PN({sensors:n,label:s,roverColor:l=null,status:o,audioStatus:u,levelStatus:f,layoutFormat:p="desktop",variant:h="default",driverLabel:x=null,showTopDown:g=!1,mobileHud:v=!1,mapPosition:y="top-center",turnTimerText:S=null,labelScale:M=1}){const C=v,E=p==="mobile-portrait",N=C?"text-[0.45rem]":"text-[0.65rem]",w=C?"px-0.25 py-0.25":"px-1 py-0.5",A=C?"px-0.25 py-0.25":"px-0.5 py-0.5",q=C?"text-[0.55rem]":"text-[0.8rem]",k=C?"left-0.5 top-0.5":"left-1 top-1",B=C?"text-[0.5rem]":"text-[0.7rem]",Q=C?"px-0.5 py-0.25":"px-1 py-0.5",D=C?"left-0.5 top-1/2":"left-1 top-1/2",H="bottom-0.5",K={transform:`translateX(-50%) scale(${M})`,transformOrigin:"center bottom"},G="240px",$=E?.3:C?.33:.7,oe={width:G,height:G,opacity:C?.6:.7,transform:y==="top-center"?`translateX(-50%) scale(${$})`:`scale(${$})`,transformOrigin:y==="bottom-left"?"bottom left":y==="top-center"?"top center":"top right",...y==="bottom-left"?{left:"0.25rem",bottom:"0.25rem"}:y==="top-center"?{left:"50%",top:"0.25rem"}:{right:"0.25rem",top:"0.25rem"}};if(h==="none")return null;if(h==="spectator"){const ce=[["Voltage",n?.voltageMv!=null?`${(n.voltageMv/1e3).toFixed(2)} V`:"--"],["Current",n?.currentMa!=null?`${n.currentMa} mA`:"--"],["Charge",n?.batteryChargeMah!=null?`${n.batteryChargeMah}`:"--"],["OI",n?.oiMode?.label||"--"]],U=!!n?.chargingSources?.homeBase,R=n?.chargingState?.label||"",L=!!(R&&R.toLowerCase()!=="not charging"),Z=n?.oiMode?.label||"Unknown",se=Z.toLowerCase(),ue=se==="full"?"bg-emerald-500/80 text-emerald-50":se==="safe"?"bg-amber-400/80 text-amber-950":se==="passive"?"bg-slate-700/80 text-slate-100":"bg-slate-700/60 text-slate-200",T=U?"bg-emerald-500/80 text-emerald-50":"bg-slate-700/70 text-slate-200",V=L?"bg-emerald-500/80 text-emerald-50":U?"bg-amber-400/80 text-amber-950":"bg-slate-700/70 text-slate-200";return r.jsxs(r.Fragment,{children:[r.jsxs("div",{className:"pointer-events-none absolute inset-0 flex items-center justify-center",children:[r.jsx("div",{className:`absolute ${k} font-medium text-slate-100 ${N}`,children:r.jsxs("div",{className:"flex flex-col gap-0.5 leading-none",children:[r.jsxs("span",{children:["Status: ",o]}),u?r.jsxs("span",{children:["Audio: ",u]}):null,f?r.jsx("span",{className:"text-cyan-300",children:f}):null]})}),r.jsx("div",{className:`absolute ${D} flex -translate-y-1/2 flex-col gap-0.5 bg-black/70 text-slate-100 ${N} ${w}`,children:r.jsxs("div",{className:"space-y-0.5 leading-tight",children:[r.jsxs("div",{className:"flex flex-col gap-0.5 text-[0.75rem] font-semibold uppercase tracking-wide",children:[r.jsx("span",{className:`rounded px-1.5 py-0.5 ${T}`,children:U?"Docked":"Undocked"}),r.jsx("span",{className:`rounded px-1.5 py-0.5 ${V}`,children:L?"Charging":"Not charging"}),r.jsxs("span",{className:`rounded px-1.5 py-0.5 ${ue}`,children:["OI: ",Z]})]}),ce.map(([ne,ae])=>r.jsxs("span",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{className:"text-slate-400",children:ne}),r.jsx("span",{className:"font-semibold text-white",children:ae})]},ne))]})}),r.jsx("div",{className:`absolute ${H} left-1/2`,style:K,children:r.jsxs("div",{className:`flex items-center gap-0.5 bg-black/80 text-slate-100 ${A} ${q}`,children:[r.jsx("span",{className:"font-semibold text-white rounded px-1 py-[1px] border border-transparent",style:oa(l,.18),children:s||"Unnamed Rover"}),x?r.jsxs("span",{className:"text-slate-300",children:["• ",x]}):null]})})]}),g?r.jsx("div",{className:"pointer-events-none absolute rounded",style:{...oe},children:r.jsx(Hd,{sensors:n,size:240,overlay:!0})}):null]})}return r.jsxs("div",{className:"pointer-events-none absolute inset-0 flex items-center justify-center",children:[r.jsx("div",{className:`absolute ${k} font-medium text-slate-100 ${N}`,children:r.jsxs("div",{className:"flex flex-col gap-0.5 leading-none",children:[r.jsxs("span",{children:["Status: ",o]}),u?r.jsxs("span",{children:["Audio: ",u]}):null,f?r.jsx("span",{className:"text-cyan-300",children:f}):null]})}),S?r.jsx("div",{className:`absolute left-1/2 top-0.5 -translate-x-1/2 rounded bg-black/70 text-slate-100 ${Q} ${B}`,children:S}):null,r.jsx("div",{className:`absolute ${H} left-1/2`,style:K,children:r.jsx("div",{className:`flex gap-0.5 bg-black/80 text-slate-100 ${A} ${q}`,children:r.jsxs("span",{children:["Rover:"," ",r.jsxs("span",{className:"rounded px-1 py-[1px] border border-transparent",style:oa(l,.18),children:['"',s||"Unnamed Rover",'"']})]})})}),g&&h!=="spectator"?r.jsx("div",{className:"pointer-events-none absolute rounded",style:{...oe},children:r.jsx(Hd,{sensors:n,size:240,overlay:!0})}):null]})}const rx=2e3,WN=3e3,eA=3e3,tA=40,nA=1e3,aA={leftWheel:"Left wheel",rightWheel:"Right wheel",mainBrush:"Main brush",sideBrush:"Side brush",limiter:"Overcurrent limit"};function sA({motors:n,fill:s=0,compact:l=!1}){if(!n?.length)return null;const o=n.map(v=>aA[v]||v),u=l?"w-[12rem] h-[3.5rem]":"w-[20rem] h-[7rem]",f=l?"px-2 py-1":"px-4 py-2",p=l?"text-lg":"text-4xl",h=l?"text-xs":"text-xl",x=Math.max(0,Math.min(1,s)),g=`${Math.round(x*100)}%`;return r.jsx("div",{className:`pointer-events-none absolute flex items-center justify-center bg-red-900/50 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 ${u}`,children:r.jsxs("div",{className:"relative h-full w-full",children:[r.jsx("div",{className:"absolute inset-0 overflow-hidden",children:r.jsx("div",{className:"h-full bg-red-700/60",style:{width:g}})}),r.jsxs("div",{className:`relative z-10 flex h-full w-full flex-col items-center justify-center text-center font-semibold text-white animate-pulse ${p} ${f}`,children:[r.jsx("div",{children:"OVERCURRENT"}),r.jsx("div",{className:`mt-0 font-medium text-white ${h}`,children:o.join(", ")})]})]})})}function lA({battery:n,compact:s=!1}){if(!n?.available||!n.warnActive&&!n.urgentActive)return null;const l=n.urgentActive?"BATTERY VERY LOW, DOCK THE ROVER AND CHARGE IMMEDIATELY!!":"Battery low! please dock and charge the rover soon.",o=s?"p-2 top-6":"p-4 top-10",u=s?"text-sm":"text-2xl";return r.jsx("div",{className:`pointer-events-none absolute flex items-center justify-center bg-amber-900/60 left-1/2 -translate-x-1/2 ${o}`,children:r.jsx("div",{className:`text-center font-semibold text-white animate-pulse ${u}`,children:r.jsx("div",{children:l})})})}function iA({sensors:n}){const s=[n?.lightBumpLeftSignal,n?.lightBumpFrontLeftSignal,n?.lightBumpCenterLeftSignal,n?.lightBumpCenterRightSignal,n?.lightBumpFrontRightSignal,n?.lightBumpRightSignal],l=s.filter(h=>h!=null).reduce((h,x)=>Math.max(h,x),1200),o=h=>Math.pow(Math.max(0,Math.min(1,(h??0)/l)),.35),u=h=>h==null||h<=0?"hsl(200 60% 18%)":`hsl(${(200+o(h)*360)%360} 100% 60%)`,f=6,p=12;return r.jsx("div",{className:"flex w-full items-center justify-center gap-0.5",children:s.map((h,x)=>{const g=o(h),v=xD.session),{sendMessage:l,onInputFocus:o,onInputBlur:u,blurChat:f,registerInputRef:p,setTypingActive:h}=of(),{value:x}=Ge("tts",{engine:"flite",voice:"rms",pitch:50}),[g,v]=m.useState(""),[y,S]=m.useState(!1),M=s?.role!=="spectator",C=s?.role==="spectator",E=s?.assignment?.roverId||null,w=!!m.useMemo(()=>s?.roster?.find(D=>String(D.id)===String(E))||null,[E,s?.roster])?.audio?.ttsEnabled,A=m.useMemo(()=>{if(!w)return null;const D=x?.engine==="espeak"?"espeak":"flite";if(D==="espeak"){let K=Number.isFinite(x?.pitch)?Math.round(x.pitch):void 0;return typeof K=="number"&&(K=Math.max(0,Math.min(99,K))),{speak:!0,engine:D,pitch:K}}const H=typeof x?.voice=="string"?x.voice:void 0;return{speak:!0,engine:D,voice:H}},[x?.engine,x?.pitch,x?.voice,w]),q=n?"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",k=n?"min-w-0 flex-1 bg-transparent text-[0.55rem] text-slate-100 placeholder:text-slate-400 focus:outline-none":"min-w-0 flex-1 bg-transparent text-[0.7rem] text-slate-100 placeholder:text-slate-400 focus:outline-none",B=n?"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 Q(D){if(D.preventDefault(),!M)return;const H=g.trim();if(H){S(!0);try{await l(H,A),v(""),f(),h(!1)}catch(K){alert(K.message)}finally{S(!1)}}}return C?null:r.jsxs("form",{onSubmit:Q,className:q,children:[r.jsx("input",{className:k,value:g,onChange:D=>{const H=D.target.value;v(H),h(!!H.trim())},onFocus:D=>{o(D),h(!!g.trim())},onBlur:D=>{u(D),h(!1)},onKeyDown:D=>{D.key==="Enter"&&!g.trim()&&(D.preventDefault(),f(),h(!1))},ref:D=>p(D,{target:"hud"}),placeholder:M?"Chat (TTS)":"Spectator",disabled:!M}),r.jsx("button",{type:"submit",disabled:!M||y,className:B,children:"Speak"})]})}function ji({sessionInfo:n,audioSessionInfo:s,videoMode:l="whep",snapshotFeed:o=null,qualityNotice:u=null,label:f,roverColor:p=null,forceMute:h=!1,telemetryFrame:x,batteryConfig:g,layoutFormat:v="desktop",hudVariant:y="default",driverLabel:S=null,hudForceMap:M=!1,hudMapPosition:C="top-center",hudLabelScale:E=1,fitParent:N=!1,overcurrentLimiter:w=null,showTurnCue:A=!1,turnTimerText:q=null,isActiveDriver:k=!1,idleSkipSeconds:B=null}){const Q=rt(Se=>Se.session),D=m.useRef(null),H=m.useRef(null),K=m.useRef(null),G=m.useRef(null),$=m.useRef(null),ee=m.useRef(null),oe=m.useRef(null),ce=m.useRef(null),[U,R]=m.useState("idle"),[L,Z]=m.useState(null),[se,ue]=m.useState("idle"),[T,V]=m.useState(null),[ne,ae]=m.useState(0),[he,re]=m.useState(0),[de,ge]=m.useState(!0),ve=!!s?.url,pe=l==="snapshot",Oe=x?.sensors,{value:Me}=Ge("audio",lt),ye=Number.isFinite(Me?.masterVolume)?Me.masterVolume:lt.masterVolume,Ye=Number.isFinite(Me?.roverVolume)?Me.roverVolume:lt.roverVolume,ze=typeof Me?.mainBrushDuckEnabled=="boolean"?Me.mainBrushDuckEnabled:typeof Me?.autoLevelEnabled=="boolean"?Me.autoLevelEnabled:lt.mainBrushDuckEnabled,at=Number.isFinite(Me?.mainBrushDuckAmount)?Math.max(0,Math.min(1,Me.mainBrushDuckAmount)):lt.mainBrushDuckAmount,ie=Math.max(0,Math.min(1,ye*Ye)),fe=Oe?.batteryChargeMah??null,J=!(v==="desktop"),xe=J?"top-right":C,[Ae]=Lg(),ht=M||J?!0:Ae,Le=zg({charge:fe,config:g}),ot=Oe?.wheelOvercurrents||null,ct=ot==null?[]:Object.entries(ot).filter(([,Se])=>!!Se).map(([Se])=>Se),gt=w?.caps||null,kt=w?.overcurrent?.groups||null,us=typeof window<"u"&&new URLSearchParams(window.location.search).has("debugAudio"),Lt=typeof window<"u"&&new URLSearchParams(window.location.search).has("debugHud"),dl=m.useMemo(()=>{if(!gt)return null;const Se=Number.isFinite(gt?.drive?.cap)?gt.drive.cap:1,Je=Number.isFinite(gt?.aux?.cap)?gt.aux.cap:1;return Math.max(0,Math.min(1,1-Math.min(Se,Je)))},[gt]),fl=!!w?.isActive,yn=ct.length?ct:fl?["limiter"]:[],wt=dl??(ct.length?1:0),an=!!yn.length,vt=!!((Number(Oe?.mainBrushCurrentMa)||0)>tA||Oe?.wheelOvercurrents?.mainBrush),Oo=ze&&vt?1-at:1,ds=Math.max(0,Math.min(1,ie*Oo)),Lo=ze&&vt&&at>0?`Volume decreased ${Math.round(at*1e3)/10}%`:null,Oa=m.useRef({hasDedicatedAudio:!1,audioUrl:null,mainBrushDuckEnabled:!1,mainBrushDuckAmount:0,mainBrushActive:!1,baseRoverGain:0,effectiveRoverGain:0});m.useEffect(()=>{Oa.current={hasDedicatedAudio:ve,audioUrl:s?.url||null,mainBrushDuckEnabled:ze,mainBrushDuckAmount:at,mainBrushActive:vt,baseRoverGain:ie,effectiveRoverGain:ds}},[ve,s?.url,ze,at,vt,ie,ds]);const et=m.useCallback((Se,Je={})=>{if(!us)return;const Pe=H.current,ke=Oa.current,Fe={event:Se,ts:Date.now(),roverLabel:f||null,hasDedicatedAudio:ke.hasDedicatedAudio,audioUrl:ke.audioUrl,mainBrushDuckEnabled:ke.mainBrushDuckEnabled,mainBrushDuckAmount:ke.mainBrushDuckAmount,mainBrushActive:ke.mainBrushActive,baseRoverGain:ke.baseRoverGain,effectiveRoverGain:ke.effectiveRoverGain,element:Pe?{muted:Pe.muted,volume:Pe.volume,paused:Pe.paused,ended:Pe.ended,readyState:Pe.readyState,networkState:Pe.networkState}:null,...Je};try{console.log(`[AudioDebug] ${Se} ${JSON.stringify(Fe)}`)}catch{console.log("[AudioDebug]",Se,Fe)}},[us,f]),fs=Q?.socials?.find(Se=>String(Se?.id||Se?.label||"").toLowerCase()==="discord")?.url||Q?.discord?.invite||null;m.useEffect(()=>{Lt&&console.log("[OvercurrentHUD]",{overlayVisible:an,overlayMotors:yn,overlayFill:wt,limiterActive:fl,limiterCaps:gt,limiterGroups:kt,wheelOvercurrents:ot})},[Lt,wt,yn,an,fl,gt,kt,ot]),m.useEffect(()=>{et("settings/update")},[et]);const Ft=m.useCallback(()=>{clearTimeout(K.current),K.current=setTimeout(()=>ae(Date.now()),rx)},[]),ua=m.useCallback(()=>{clearTimeout(G.current),G.current=setTimeout(()=>re(Date.now()),rx)},[]),Mn=m.useCallback(async()=>{const Se=D.current;if(Se)try{Se.muted=!0,await Se.play()}catch{}},[]),qi=m.useCallback((Se=0)=>{if(h||ve)return;clearTimeout(ee.current);const Je=()=>{clearTimeout(ee.current),ee.current=setTimeout(()=>{h||Pe()},WN)},Pe=async()=>{const ke=D.current;if(ke)try{await Mn(),ke.muted=!1,await ke.play(),ge(!1)}catch{ke.muted=!0,ge(!0),Je()}};ee.current=setTimeout(Pe,Se)},[Mn,h,ve]);m.useEffect(()=>()=>{clearTimeout(K.current),clearTimeout(G.current),clearTimeout(ee.current),clearInterval($.current),oe.current&&(cancelAnimationFrame(oe.current),oe.current=null)},[]),m.useEffect(()=>{U==="playing"&&qi(0)},[U,qi]),m.useEffect(()=>{pe&&(R("snapshot"),Z(null))},[pe]),m.useEffect(()=>{if(pe||!n?.url||!D.current)return;let Se=!0,Je;const Pe=setTimeout(()=>ge(!0),0),ke=(Fe,Mt)=>{Se&&(et("video/status",{nextStatus:Fe,info:Mt||null}),R(Fe),Z(Mt||null),Fe==="playing"&&Mn(),["error","failed","disconnected","closed"].includes(Fe)&&Ft())};return Je=new lx({url:n.url,token:n.token,video:D.current,receiveAudio:!ve,onStatus:ke}),Je.start().catch(Fe=>{Se&&(R("error"),Z(Fe.message),Ft())}),()=>{Se=!1,clearTimeout(Pe),Je?.stop()}},[pe,n?.url,n?.token,ne,Ft,Mn,ve,et]),m.useEffect(()=>{U==="stopped"&&n?.url&&Ft()},[U,n?.url,Ft]),m.useEffect(()=>{const Se=H.current;if(!Se||!s?.url){et("route/no-audio-url"),ce.current=null;return}const Je=Math.max(0,Math.min(1,ds)),Pe=ce.current==null?Math.max(0,Math.min(1,Se.volume)):ce.current;if(oe.current&&(cancelAnimationFrame(oe.current),oe.current=null),Je>Pe){const ke=performance.now(),Fe=Pe,Mt=Je-Fe,zt=ut=>{const Ue=Math.min(1,(ut-ke)/nA),ln=Fe+Mt*Ue;Se.volume=ln,ce.current=ln,Ue<1?oe.current=requestAnimationFrame(zt):oe.current=null};oe.current=requestAnimationFrame(zt)}else Se.volume=Je,ce.current=Je;et("route/direct",{elementVolume:Je,duckEnabled:ze,mainBrushActive:vt,duckAmount:at})},[s?.url,ds,ze,vt,at,et]),m.useEffect(()=>{if(!s?.url||!H.current)return;let Se=!0,Je;const Pe=(ke,Fe)=>{Se&&(et("audio/status",{nextStatus:ke,info:Fe||null}),V(Fe||(ke==="connected"?"connected":null)),ue(Mt=>ke==="connected"&&(Mt==="playing"||Mt==="connecting")||ke==="new"?Mt:ke),["error","failed"].includes(ke)&&ua())};return Je=new lx({url:s.url,token:s.token,video:H.current,audioOnly:!0,onStatus:Pe}),Je.start().catch(ke=>{Se&&(ue("error"),V(ke.message),ua())}),()=>{Se=!1,Je?.stop()}},[s?.url,s?.token,he,ua,et]),m.useEffect(()=>{const Se=H.current;if(!s?.url||!Se){clearInterval($.current);return}if(!["connecting","connected","playing","paused"].includes(se)){clearInterval($.current);return}const Pe=async()=>{const ke=H.current;ke&&(ke.muted=!1,!(!ke.paused&&!ke.ended)&&(et("retry/play-attempt"),ke.play().then(()=>{et("retry/play-ok"),ue(Fe=>Fe==="connected"?"playing":Fe),V(Fe=>Fe==="paused"?null:Fe)}).catch(Fe=>{et("retry/play-fail",{error:Fe?.message}),V(Mt=>Mt||Fe?.message||"autoplay blocked")})))};return Pe(),$.current=setInterval(Pe,eA),()=>clearInterval($.current)},[s?.url,se,et]),m.useEffect(()=>{const Se=H.current;if(!Se)return;const Je=()=>{et("element/play"),ue(Ue=>Ue==="error"?Ue:"playing"),V(null)},Pe=()=>{et("element/pause"),ue(Ue=>["error","failed","disconnected","closed","stopped"].includes(Ue)?Ue:"paused"),V(Ue=>Ue||"paused")},ke=()=>{et("element/ended"),ue(Ue=>Ue==="error"?Ue:"stopped"),V(Ue=>Ue||"ended")},Fe=()=>{const{error:Ue}=Se,ln=Ue?.message||"audio error";et("element/error",{error:ln}),ue("error"),V(ln)},Mt=()=>et("element/waiting"),zt=()=>et("element/canplay"),ut=()=>et("element/stalled");return Se.addEventListener("play",Je),Se.addEventListener("pause",Pe),Se.addEventListener("ended",ke),Se.addEventListener("error",Fe),Se.addEventListener("waiting",Mt),Se.addEventListener("canplay",zt),Se.addEventListener("stalled",ut),()=>{Se.removeEventListener("play",Je),Se.removeEventListener("pause",Pe),Se.removeEventListener("ended",ke),Se.removeEventListener("error",Fe),Se.removeEventListener("waiting",Mt),Se.removeEventListener("canplay",zt),Se.removeEventListener("stalled",ut)}},[s?.url,et]);const Ti=o?.error?`Error: ${o.error}`:o?.objectUrl?"snapshot":o?.status||"waiting",ml=pe?Ti:n?.url?U==="error"?`Error: ${L||"unknown"}`:L?`${U} (${L})`:U:"waiting",La=s?.error?`Error: ${s.error}`:s?.url?se==="error"?`Error: ${T||"unknown"}`:T?`${se} (${T})`:se:null,Di=y==="spectator",sn=y==="none";return r.jsxs("div",{className:`flex flex-col gap-0.5 ${N?"h-full":""}`,children:[r.jsxs("div",{className:`relative w-full overflow-hidden bg-black ${N?"h-full flex-1":"aspect-[4/3]"}`,children:[pe?o?.objectUrl?r.jsx("img",{src:o.objectUrl,alt:f,className:"h-full w-full object-contain",draggable:!1}):r.jsx("div",{className:"flex h-full w-full items-center justify-center text-sm text-slate-300",children:"Waiting for frame…"}):r.jsx("video",{ref:D,muted:h||de||ve,playsInline:!0,autoPlay:!0,controls:!1,className:"h-full w-full object-contain"}),r.jsx("audio",{ref:H,autoPlay:!0,hidden:!0}),!sn&&A?r.jsx(YN,{mobileHud:J,isActiveDriver:k,idleSkipSeconds:B}):null,sn?null:r.jsx(PN,{sensors:Oe,label:f,roverColor:p,status:ml,audioStatus:La,levelStatus:Lo,layoutFormat:v,variant:y,driverLabel:S,showTopDown:ht,mobileHud:J,mapPosition:xe,turnTimerText:q,labelScale:E}),sn?null:r.jsx(rA,{compact:J}),!sn&&Lt?r.jsx("div",{className:"pointer-events-none absolute left-1 top-1 z-40 rounded bg-black/80 px-1 py-0.5 text-[0.6rem] text-lime-200",children:`OC vis:${an?1:0} motors:${yn.length} fill:${Math.round(wt*100)}%`}):null,sn?null:r.jsx(sA,{motors:yn,fill:wt,compact:J}),sn?null:r.jsx(lA,{battery:Le,compact:J}),!sn&&Di&&Le.available?r.jsx("div",{className:"pointer-events-none absolute right-1 top-1/2 flex h-[70%] -translate-y-1/2 flex-col items-center justify-center rounded bg-black/60 px-0.5 pb-1 pt-1",children:r.jsx(Ud,{visual:Le,orientation:"vertical",variant:"inline",compact:J,className:"h-full w-4"})}):null,!sn&&u?r.jsx("div",{className:"pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2",children:r.jsxs("div",{className:`mx-auto w-fit rounded border border-amber-300/80 bg-black/75 text-amber-200 ${J?"px-2 py-1 text-[0.6rem]":"px-3 py-1.5 text-sm"}`,children:[r.jsx("div",{className:"text-center",children:u}),r.jsx("div",{className:"pointer-events-auto mt-0",children:r.jsx(hf,{id:"discord",label:"Join our Discord server while you wait!",url:fs})})]})}):null]}),!sn&&!Di&&r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(iA,{sensors:Oe}),r.jsx("div",{className:"panel-section space-y-0.5 text-sm",children:r.jsx(Ud,{visual:Le,compact:J})})]})]})}function xf({layoutFormat:n="desktop"}){const s=rt(ae=>ae.session),{state:{song:l,lastControlIntentAt:o},overcurrentLimiter:u}=_t(),[f,p]=m.useState(()=>Date.now()),[h,x]=m.useState(!1),[g,v]=m.useState(null),y=m.useRef({active:!1,roverId:null});m.useEffect(()=>{if(s?.mode!=="turns")return;const ae=setInterval(()=>p(Date.now()),250);return()=>clearInterval(ae)},[s?.mode]);const S=s?.assignment?.roverId,C=!!(S&&s?.roster?s.roster.find(ae=>String(ae.id)===String(S)):null)?.media?.audioPublishUrl,E=S?s?.turnQueues?.[S]:null,N=s?.socketId||null,w=S?s?.activeDrivers?.[S]:null,A=!!(N&&w===N),q=m.useMemo(()=>{const ae=E?.queue||[];if(!ae.length||!E?.current||ae.length<=1)return null;const he=ae.findIndex(re=>re===E.current);return he===-1?ae[0]||null:ae[(he+1)%ae.length]||null},[E?.queue,E?.current]),k=!!(N&&q===N),B=E?.deadline||null,Q=E?.idleDeadline||null,D=B?B-f:null,H=Q?Q-f:null,K=s?.mode==="turns"&&k&&D!=null&&D<=5e3&&D>0,G=s?.mode!=="turns"||A||K,$=D!=null&&Number.isFinite(D)?Math.max(0,Math.ceil(D/1e3)):null,ee=H!=null&&Number.isFinite(H)?Math.max(0,Math.ceil(H/1e3)):null,oe=A?$!=null?`${$}s left`:null:k&&$!=null?`Your turn in ${$}s`:null,ce=S?[...G?[{type:"rover",id:S,key:S}]:[],...C?[{type:"rover",id:`${S}-audio`,key:`${S}-audio`}]:[]]:[],U=yi(ce),R=S&&G?U[S]:null,L=S&&C?U[`${S}-audio`]:null,Z=mf(S?[S]:[],{enabled:!!S,version:s?.mode}),se=S&&Z[S]||null,ue=ul(S),T=S&&s?.roster?s.roster.find(ae=>String(ae.id)===String(S)):null,V=T?.battery??null,ne=T?.name||(S?`Rover ${S}`:"");return m.useEffect(()=>{if(s?.mode!=="turns"){x(!1),v(null),y.current={active:!1,roverId:null};return}const ae=y.current,he=A&&!ae.active,re=A&&S&&S!==ae.roverId;he||re?(x(!0),v(Date.now())):!A&&ae.active&&(x(!1),v(null)),y.current={active:A,roverId:S}},[A,S,s?.mode]),m.useEffect(()=>{!h||!g||o>g&&x(!1)},[o,g,h]),r.jsx("section",{className:"panel",children:S?r.jsx(ji,{sessionInfo:R,videoMode:G?"whep":"snapshot",snapshotFeed:se,audioSessionInfo:L,label:ne,roverColor:T?.color||null,telemetryFrame:ue,batteryConfig:V,layoutFormat:n,overcurrentLimiter:u,songNote:l?.note,qualityNotice:G?null:"Preview feed (low FPS) until your turn.",showTurnCue:h,turnTimerText:oe,turnSeconds:$,isActiveDriver:A,idleSkipSeconds:ee}):r.jsxs("div",{className:"panel-muted content-center text-center text-sm text-slate-400 aspect-[4/3]",children:[r.jsx("p",{children:"You are not assigned to a rover."}),r.jsx("p",{className:"mt-0",children:r.jsx("a",{href:"/spectate",className:"text-blue-400 underline hover:text-blue-500",children:"Click here to visit the spectator page."})})]})})}function So({label:n,tone:s="muted"}){const l=s==="success"?"bg-emerald-900 text-emerald-100":s==="warn"?"bg-amber-900 text-amber-100":"bg-slate-800 text-slate-200";return r.jsx("span",{className:`rounded px-1 py-0.5 text-xs font-semibold leading-none ${l}`,children:n})}function Ro(n){if(!Number.isFinite(n))return 0;const s=n%360;return s<0?s+360:s}function oA(n){if(!Array.isArray(n)||n.length<3)return 0;const[s,l,o]=n,u=Math.max(0,Math.min(255,Number(s)))/255,f=Math.max(0,Math.min(255,Number(l)))/255,p=Math.max(0,Math.min(255,Number(o)))/255,h=Math.max(u,f,p),x=Math.min(u,f,p),g=h-x;if(g===0)return 0;let v=0;return h===u?v=(f-p)/g%6:h===f?v=(p-u)/g+2:v=(u-f)/g+4,Ro(Math.round(v*60))}function ox(n){const s=Ro(n),l=1,o=1-Math.abs(s/60%2-1);let u=0,f=0,p=0;return s<60?(u=l,f=o):s<120?(u=o,f=l):s<180?(f=l,p=o):s<240?(f=o,p=l):s<300?(u=o,p=l):(u=l,p=o),[Math.round(u*255),Math.round(f*255),Math.round(p*255)]}function cx(n){return n?Array.isArray(n.hsColor)?Ro(Number(n.hsColor[0])):Array.isArray(n.rgbColor)?oA(n.rgbColor):0:0}function cA({entity:n,connected:s,controlsLocked:l,onToggle:o,onSetColor:u,onSetWhite:f}){const p=n.state==="unavailable"||!n.available,h=n.state==="on",x=n.type==="light"&&n.supportsColor,g=p?"warn":h?"success":"muted",v=p?"Unavailable":h?"On":"Off",y=l||!s||p,S=y||!x,[M,C]=m.useState(()=>cx(n)),E=m.useRef(M),N=m.useRef(!1),w=p?"border-slate-800 bg-slate-900 text-slate-400 cursor-not-allowed":l?"border-slate-800 bg-slate-900 text-slate-300 cursor-not-allowed":h?"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(!x||N.current)return;const H=cx(n);E.current=H,C(H)},[n.rgbColor,n.hsColor,x]);const A=H=>{const K=Ro(Number(H.target.value));E.current=K,C(K)},q=()=>{S||!u||u(n.id,ox(E.current))},k=H=>{H.stopPropagation()},B=H=>{k(H),!(S||!f)&&f(n.id)},D=`rgb(${(x?ox(E.current):[255,255,255]).join(",")})`;return r.jsxs("button",{type:"button",onClick:()=>o(n.id),disabled:y,className:`relative flex min-w-[12rem] flex-[1_1_12rem] items-start justify-between gap-0.5 rounded px-1 py-0.5 text-left transition-colors ${w} disabled:opacity-60 disabled:hover:bg-inherit`,children:[r.jsxs("div",{className:"min-w-0 flex-1",children:[r.jsxs("div",{className:"flex items-center gap-0.5 text-sm leading-normal",children:[r.jsx("span",{className:"truncate font-semibold text-white",children:n.name||n.id}),x&&r.jsxs(r.Fragment,{children:[r.jsx(So,{label:v,tone:g}),!s&&r.jsx("span",{className:"text-xs text-amber-200",children:"Offline"})]})]}),x?r.jsx("div",{className:"-mt-0.5 w-full",onClick:k,onPointerDown:H=>{k(H),N.current=!0},onPointerUp:H=>{k(H),N.current=!1,q()},onPointerCancel:H=>{k(H),N.current=!1,q()},onKeyUp:H=>{k(H),q()},onBlur:q,children:r.jsx("div",{className:"relative w-full",children:r.jsx("input",{type:"range",min:"0",max:"360",step:"1",value:M,onChange:A,disabled:S,"aria-label":`Set color for ${n.name||n.id}`,className:"ha-hue-slider w-full"})})}):r.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-400",children:[r.jsx(So,{label:v,tone:g}),!s&&r.jsx("span",{className:"text-amber-200",children:" · Offline"})]})]}),x&&r.jsxs("div",{className:"absolute right-1 top-1 flex items-center gap-0.5",children:[r.jsx("span",{role:"button",tabIndex:S?-1:0,onClick:B,onPointerDown:k,onKeyDown:H=>{S||(H.key==="Enter"||H.key===" ")&&(H.preventDefault(),B(H))},title:"Set white color temperature","aria-label":`Set ${n.name||n.id} to white`,"aria-disabled":S,className:`inline-flex items-center justify-center rounded border border-slate-200 bg-white px-1 py-0.5 text-xs font-semibold leading-none text-slate-900 ${S?"cursor-not-allowed opacity-60":"cursor-pointer hover:bg-slate-100"}`,children:"white"}),r.jsx("span",{className:"pointer-events-none h-2.5 w-2.5 rounded-full border border-white/60",style:{backgroundColor:D}})]}),!x&&r.jsx("div",{className:"self-center text-xs font-semibold text-white/90",children:h?"Turn off":"Turn on"})]})}function Hg(){const{state:{keymap:n}}=_t(),s=rt(M=>M.session),{homeAssistantToggle:l,homeAssistantSetLightColor:o,homeAssistantSetLightWhite:u}=kn(),f=s?.homeAssistant,p=m.useMemo(()=>f?.entities||[],[f?.entities]),h=f?.lightPolicy||null,x=!!(h?.locked||h?.lockedOn),g=h?.lockState||(h?.lockedOn?"on":null),v=Cn(n?.homeAssistantOn?.[0]),y=Cn(n?.homeAssistantOff?.[0]);if(!f?.enabled)return r.jsxs("section",{className:"panel-section space-y-0.5 text-sm text-slate-400",children:[r.jsx("p",{className:"text-slate-300",children:"Room Controls"}),r.jsx("p",{className:"text-slate-500",children:"Not configured on the server."})]});if(p.length===0)return r.jsxs("section",{className:"panel-section space-y-0.5 text-sm text-slate-400",children:[r.jsx("p",{className:"text-slate-300",children:"Room Controls"}),r.jsx("p",{className:"text-slate-500",children:"No lights or switches configured."})]});const S=!!f?.connected;return r.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[r.jsxs("header",{className:"flex items-center justify-between gap-0.5 text-sm text-slate-400",children:[r.jsxs("div",{className:"flex items-center gap-0.5",children:[r.jsx("p",{children:"Room Controls"}),r.jsx("span",{className:"text-xs text-slate-500",children:p.length}),x?r.jsx(So,{label:g==="off"?"Locked Off":"Locked On",tone:"warn"}):null,r.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-300 background-black",children:[r.jsxs("span",{className:"flex items-center gap-0.5",children:[r.jsx("span",{children:"On"}),v?r.jsx(ux,{label:v}):null]}),r.jsxs("span",{className:"flex items-center gap-0.5",children:[r.jsx("span",{children:"Off"}),y?r.jsx(ux,{label:y}):null]})]})]}),r.jsx(So,{label:S?"Connected":"Offline",tone:S?"success":"warn"})]}),x?r.jsx("p",{className:"rounded border border-amber-600/60 bg-amber-900/40 px-1 py-0.5 text-xs text-amber-100",children:g==="off"?"Lights are locked off. Room controls are disabled.":"Lights are locked on. Room controls are disabled."}):null,r.jsx("div",{className:"flex flex-wrap gap-0.5",children:p.map(M=>r.jsx(cA,{entity:M,connected:S,controlsLocked:x,onToggle:l,onSetColor:o,onSetWhite:u},M.id))})]})}function ux({label:n}){return n?r.jsx("span",{className:"rounded border border-white/40 px-1 text-[0.7rem] text-white",children:n}):null}function Vg(){const{login:n,setRole:s}=kn(),[l,o]=m.useState(""),[u,f]=m.useState(""),[p,h]=m.useState(!1);async function x(g){g.preventDefault(),h(!0);try{await n(l,u),o(""),f("")}catch(v){alert(v.message)}finally{h(!1)}}return r.jsxs("div",{className:"panel-section space-y-0.5 text-base",children:[r.jsx("p",{className:"text-sm text-slate-400",children:"Admin login"}),r.jsxs("form",{className:"flex flex-col gap-0.5",onSubmit:x,children:[r.jsx("input",{className:"field-input",placeholder:"Username",value:l,onChange:g=>o(g.target.value)}),r.jsx("input",{className:"field-input",placeholder:"Password",type:"password",value:u,onChange:g=>f(g.target.value)}),r.jsx("button",{type:"submit",disabled:p,className:"button-dark w-full disabled:opacity-50",children:p?"Logging in…":"Login"})]})]})}function dx(...n){return n.filter(Boolean).join(" ")}function uA(n){const s=n?.batteryState?.percentDisplay;return s==null?"--":`${s}%`}function dA(n){return n?.batteryState?n.batteryState.urgentActive?"text-red-400":n.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function fA({title:n,roster:s=[],emptyText:l="No roster data.",renderActions:o=null}){return r.jsxs("div",{className:"space-y-0.5 text-sm",children:[n&&r.jsx("p",{className:"text-sm text-slate-400",children:n}),s.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:l}):r.jsx("ul",{className:"space-y-0.5 text-sm",children:s.map(u=>{const f=!!(u?.private?.enabled&&u?.private?.open),p=!!u?.locked;return r.jsxs("li",{className:dx("surface flex flex-wrap items-center justify-between gap-0.5",p?"bg-red-900/40":f?"bg-amber-700/35 border border-amber-200/30":null),children:[r.jsxs("div",{children:[r.jsx("p",{className:"text-slate-200",children:r.jsx("span",{className:"rounded px-1 py-[1px] border border-transparent",style:oa(u.color,.16),children:u.name})}),r.jsxs("p",{className:"text-xs text-slate-500 flex flex-wrap items-center gap-0.5",children:[r.jsx("span",{children:u.locked?"locked":"free"}),u.lockReason&&r.jsx("span",{className:"rounded bg-black/30 px-1",children:u.lockReason}),r.jsxs("span",{className:dx("font-semibold",dA(u)),children:["Battery ",uA(u)]})]})]}),o?o(u):null]},u.id)})})]})}function mA(n){const s=n?.runtime||{},l=n?.output||{},o=n?.errors||{};return s.inFlight?{label:"IN FLIGHT",detail:s.reason||"Generating commentary now",className:"border-amber-400/60 bg-amber-700/20 text-amber-200"}:s.outcome==="posted"?{label:"POSTED",detail:l.posted?`Last: ${l.posted}`:"Commentary posted",className:"border-emerald-400/60 bg-emerald-700/20 text-emerald-200"}:s.outcome==="skipped"?{label:"SKIPPED",detail:s.reason||"Model chose to skip",className:"border-slate-400/60 bg-slate-700/30 text-slate-200"}:s.outcome==="failed"?{label:"FAILED",detail:o.message||s.reason||"Tick failed",className:"border-red-400/60 bg-red-700/20 text-red-200"}:{label:s.running?"IDLE":"STOPPED",detail:s.reason||"Waiting for next tick",className:"border-sky-400/50 bg-sky-700/20 text-sky-200"}}function hA(n,s){const l=Date.now(),u=(Array.isArray(n)?n:[]).map((f,p)=>{const h=String(f?.role||"").toLowerCase(),x=typeof f?.content=="string"?f.content:JSON.stringify(f?.content??null,null,2),g=h==="system"?"LLM System":h==="assistant"?"LLM Context":h==="user"?"LLM Input":"LLM Message";return{id:`llm-msg-${p}`,message:{ts:l+p,nickname:g,text:x,role:"spectator",system:h==="system"}}});if(s!=null){const f=String(s);u.push({id:"llm-output",message:{ts:l+u.length+1,nickname:"LLM Output",text:f.trim()?f:"",role:"spectator",system:!0}})}return u}function pA({state:n,onClearHistory:s,clearingHistory:l}){const[o,u]=m.useState(null);if(!n)return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"LLM Commentary"}),r.jsx("div",{className:"surface text-xs text-slate-300",children:"No status received yet."})]});const f=n.runtime||{},p=n.counters||{},h=n.timings||{},x=n.input||{},g=n.output||{},v=n.errors||{},y=Array.isArray(n.history)?n.history:[],S=y.find(N=>N.runId===o)||(y.length?y[y.length-1]:null),M=mA(n),C=hA(x.modelMessages,g.raw),E=[{label:"running",value:f.running?"yes":"no"},{label:"in flight",value:f.inFlight?"yes":"no"},{label:"phase",value:f.phase||"--"},{label:"run id",value:f.currentRunId??"--"},{label:"tick count",value:f.tickCount??0},{label:"last tick",value:f.lastTickAt?new Date(f.lastTickAt).toLocaleString():"never"},{label:"next run",value:f.nextRunAt?new Date(f.nextRunAt).toLocaleString():"n/a"},{label:"outcome",value:f.outcome||"--"},{label:"reason",value:f.reason||"--"},{label:"skip streak",value:p.skipStreak??0},{label:"clear count",value:p.clearCount??0},{label:"last gen",value:h.lastGenerationMs!=null?`${h.lastGenerationMs} ms`:"--"},{label:"avg gen",value:h.avgGenerationMs!=null?`${h.avgGenerationMs} ms`:"--"},{label:"gen count",value:h.generationCount??0},{label:"prompt chars",value:p.promptChars??0},{label:"active drivers",value:p.snapshotSummary?.activeDrivers??0},{label:"snapshot rovers",value:p.snapshotSummary?.rovers??0},{label:"snapshot chat",value:p.snapshotSummary?.chatMessages??0}];return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"LLM Commentary"}),r.jsx("div",{className:"flex gap-0.5 text-xs",children:r.jsx("button",{type:"button",onClick:s,disabled:!!l,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:l?"Clearing...":"Clear LLM History"})}),r.jsxs("div",{className:`surface border text-center ${M.className}`,children:[r.jsx("div",{className:"text-[1.1rem] font-bold tracking-wide",children:M.label}),r.jsx("div",{className:"text-xs text-slate-200",children:M.detail})]}),r.jsx("div",{className:"surface flex flex-wrap gap-0.5 text-xs",children:E.map(N=>r.jsxs("span",{className:"rounded border border-slate-600/60 bg-slate-800/70 px-0.5 py-0.25 text-[0.72rem] leading-tight text-slate-200",children:[N.label,": ",N.value]},N.label))}),v.message?r.jsxs("div",{className:"surface text-xs text-red-300 break-words",children:["Error: ",v.message]}):null,v.details?r.jsxs("details",{className:"surface text-xs text-red-200",children:[r.jsx("summary",{className:"cursor-pointer select-none text-red-300",children:"Failure details"}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-red-200",children:JSON.stringify(v.details,null,2)})]}):null,g.generated?r.jsxs("div",{className:"surface text-xs text-slate-200 break-words",children:["Generated: ",g.generated]}):null,g.posted?r.jsxs("div",{className:"surface text-xs text-emerald-200 break-words",children:["Posted: ",g.posted]}):null,r.jsxs("div",{className:"grid gap-0.5 md:grid-cols-2",children:[r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"Live Input Conversation"}),r.jsx("div",{className:"surface max-h-72 space-y-0.5 overflow-y-auto",children:C.length?C.map(N=>r.jsx(sf,{message:N.message},N.id)):r.jsx("div",{className:"text-xs text-slate-300",children:"No model conversation captured yet."})})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"Output + Error"}),r.jsxs("div",{className:"surface space-y-0.5 text-xs text-slate-200",children:[r.jsxs("div",{children:["Raw output: ",g.raw?.trim()?g.raw:""]}),r.jsxs("div",{children:["Posted: ",g.posted||""]}),r.jsxs("div",{children:["Output at: ",g.modelOutputAt?new Date(g.modelOutputAt).toLocaleString():"n/a"]}),r.jsxs("div",{children:["Failed at: ",v.failedAt?new Date(v.failedAt).toLocaleString():"n/a"]})]})]})]}),r.jsxs("details",{className:"surface text-xs text-slate-200",children:[r.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Full Monitor Payload"}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify(n,null,2)})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"Recent Runs"}),r.jsx("div",{className:"surface max-h-52 space-y-0.5 overflow-y-auto text-xs",children:y.length?y.slice().reverse().map(N=>r.jsxs("button",{type:"button",onClick:()=>u(N.runId),className:`w-full text-left surface ${S?.runId===N.runId?"border border-sky-400/50":""}`,children:[r.jsxs("span",{className:"text-slate-300",children:["#",N.runId]})," ",r.jsx("span",{className:"text-slate-200",children:N.outcome||N.phase||"--"})," ",r.jsx("span",{className:"text-slate-400",children:N.durationMs!=null?`${N.durationMs}ms`:"--"})," ",r.jsx("span",{className:"text-slate-500",children:N.reason||""})]},N.runId)):r.jsx("div",{className:"text-slate-300",children:"No runs recorded yet."})})]}),S?r.jsxs("details",{className:"surface text-xs text-slate-200",open:!0,children:[r.jsxs("summary",{className:"cursor-pointer select-none text-slate-300",children:["Run #",S.runId," details"]}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify(S,null,2)})]}):null]})}function xA({health:n,roster:s=[]}){if(!n)return null;const l=n.replay||{sources:[],readyCount:0,totalCount:0},o=n.snapshots||{rovers:[],rooms:[]},u=x=>s.find(g=>String(g.id)===String(x))?.color||null,f=`${l.readyCount}/${l.totalCount} sources ready`,p=o.rovers.filter(x=>x.stale).length,h=o.rooms.filter(x=>x.stale).length;return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"Health"}),r.jsxs("div",{className:"surface space-y-0.5 text-xs text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:"Replay segments"}),r.jsx("span",{className:"text-slate-400",children:f})]}),r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:"Rover snapshots"}),r.jsxs("span",{className:p?"text-amber-300":"text-emerald-300",children:[o.rovers.length-p,"/",o.rovers.length," ok"]})]}),r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:"Room cameras"}),r.jsxs("span",{className:h?"text-amber-300":"text-emerald-300",children:[o.rooms.length-h,"/",o.rooms.length," ok"]})]})]}),r.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:l.sources.map(x=>r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{className:`${x.type==="rover"?"rounded px-1 py-[1px] border border-transparent":""}`,style:x.type==="rover"?oa(u(x.id),.16):void 0,children:x.label}),r.jsxs("span",{className:x.ready?"text-emerald-300":"text-amber-300",children:[x.recentCount,"/",x.neededCount]})]},`${x.type}:${x.id}`))}),r.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rovers.map(x=>r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{className:"rounded px-1 py-[1px] border border-transparent",style:oa(u(x.id),.16),children:x.name}),r.jsx("span",{className:x.stale?"text-amber-300":"text-emerald-300",children:x.stale?"stale":"ok"})]},`rover:${x.id}`))}),r.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rooms.map(x=>r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:x.name}),r.jsx("span",{className:x.stale?"text-amber-300":"text-emerald-300",children:x.error?"error":x.stale?"stale":"ok"})]},`room:${x.id}`))})]})}function gA({entries:n}){const s=n||[];return r.jsxs("div",{className:"panel-section space-y-0.5 text-base",children:[r.jsxs("div",{className:"flex items-center justify-between text-sm text-slate-400",children:[r.jsx("span",{children:"Admin IP log"}),r.jsx("span",{children:s.length})]}),r.jsx("div",{className:"surface h-64 overflow-y-auto font-mono text-xs",children:s.length===0?r.jsx("p",{children:"No admin log entries yet."}):s.slice().reverse().map(l=>r.jsxs("div",{className:"surface",children:[r.jsx("span",{className:"text-amber-400",children:l.ts?new Date(l.ts).toLocaleTimeString():"--"})," ",l.label&&r.jsxs("span",{className:"text-teal-400",children:["[",l.label,"]"]})," ",r.jsx("span",{className:"text-slate-200",children:l.message})," ",l.ip&&r.jsx("span",{className:"text-cyan-300",children:l.ip})," ",l.meta&&r.jsx("span",{className:"text-slate-500",children:JSON.stringify(l.meta)})]},l.id))}),r.jsx("p",{className:"text-xs text-slate-500",children:"Admin-only log stream; IPs never appear in user data."})]})}const vA=[{key:"open",label:"Open"},{key:"turns",label:"Turns"},{key:"admin",label:"Admin"},{key:"lockdown",label:"Lockdown"}];function bA(){const{session:n,lockRover:s,setMode:l,requestControl:o,setGlobalObjective:u,setAdminReason:f,rebootRover:p,rebootServer:h,setAudioLevels:x,setPrivateSafety:g,llmControl:v,adminLogs:y,llmCommentaryState:S}=nn(),M=m.useMemo(()=>n?.roster??[],[n?.roster]),[C,E]=m.useState({}),[N,w]=m.useState({}),[A,q]=m.useState(!1),[k,B]=m.useState(!1),Q=n?.health||null,D=n?.globalObjective?.text||"",H=n?.globalObjective?.updatedAt||null,[K,G]=m.useState(D),$=n?.adminReason?.text||"",ee=n?.adminReason?.updatedAt||null,[oe,ce]=m.useState($),U=n?.audioLevels||{},[R,L]=m.useState({hornGain:Number.isFinite(U.hornGain)?U.hornGain:1,ttsGain:Number.isFinite(U.ttsGain)?U.ttsGain:1,forwardGain:Number.isFinite(U.forwardGain)?U.forwardGain:1}),[Z,se]=m.useState({}),ue=n?.role==="admin"||n?.role==="lockdown"||n?.role==="lockdown-admin",T=n?.role==="lockdown"||n?.role==="lockdown-admin",V=n?.mode??"open",ne=async(le,J)=>{try{await s(le,J),E(xe=>({...xe,[le]:J}))}catch(xe){alert(xe.message)}},ae=async le=>{const J=le.target.value;try{await l(J)}catch(xe){alert(xe.message)}},he=async le=>{try{await o(le,{force:!0})}catch(J){alert(J.message)}},re=async le=>{if(!(!le?.id||!window.confirm(`Reboot rover "${le.name||le.id}" now?`))){w(xe=>({...xe,[le.id]:!0}));try{await p(le.id)}catch(xe){alert(xe.message)}finally{w(xe=>({...xe,[le.id]:!1}))}}},de=async()=>{if(window.confirm("Reboot the server host now? This will disconnect all users.")){q(!0);try{await h()}catch(J){alert(J.message),q(!1)}}},ge=async()=>{if(window.confirm("Clear LLM commentary history now? This resets chat context, bot memory, and rover activity metrics for narration.")){B(!0);try{await v("clearHistory")}catch(J){alert(J.message)}finally{B(!1)}}},ve=async()=>{try{await u(K)}catch(le){alert(le.message)}},pe=async()=>{try{await u(null)}catch(le){alert(le.message)}},Oe=async()=>{try{await f(oe)}catch(le){alert(le.message)}},Me=async()=>{try{await f(null)}catch(le){alert(le.message)}},ye=le=>J=>{const xe=Number(J.target.value);L(Ae=>({...Ae||{},[le]:Number.isFinite(xe)?xe:1}))},Ye=async()=>{try{await x(R)}catch(le){alert(le.message)}},ze=async()=>{window.dispatchEvent(new CustomEvent("buttonBox:rewardRunLocalTest",{detail:{rewardName:"Admin Mode",count:1e3,goal:1e3}}))};m.useEffect(()=>{G(D)},[D]),m.useEffect(()=>{ce($)},[$]),m.useEffect(()=>{L({hornGain:Number.isFinite(U.hornGain)?U.hornGain:1,ttsGain:Number.isFinite(U.ttsGain)?U.ttsGain:1,forwardGain:Number.isFinite(U.forwardGain)?U.forwardGain:1})},[U.forwardGain,U.hornGain,U.ttsGain]),m.useEffect(()=>{const le={};(M||[]).forEach(J=>{J?.private?.enabled&&(le[J.id]={speedLimitEnabled:!!J?.private?.safety?.speedLimitEnabled,speedLimitMaxWheelSpeed:Number.isFinite(J?.private?.safety?.speedLimitMaxWheelSpeed)?J.private.safety.speedLimitMaxWheelSpeed:250,hardOvercurrentEnabled:!!J?.private?.safety?.hardOvercurrentEnabled,overcurrentStopMs:Number.isFinite(J?.private?.safety?.overcurrentStopMs)?J.private.safety.overcurrentStopMs:300,hardBumpEnabled:!!J?.private?.safety?.hardBumpEnabled,bumpBackoffSpeed:Number.isFinite(J?.private?.safety?.bumpBackoffSpeed)?J.private.safety.bumpBackoffSpeed:250,bumpBackoffMs:Number.isFinite(J?.private?.safety?.bumpBackoffMs)?J.private.safety.bumpBackoffMs:350,cliffEnabled:!!J?.private?.safety?.cliffEnabled,cliffBackoffSpeed:Number.isFinite(J?.private?.safety?.cliffBackoffSpeed)?J.private.safety.cliffBackoffSpeed:250,cliffBackoffMs:Number.isFinite(J?.private?.safety?.cliffBackoffMs)?J.private.safety.cliffBackoffMs:500,triggerCooldownMs:Number.isFinite(J?.private?.safety?.triggerCooldownMs)?J.private.safety.triggerCooldownMs:800})}),se(le)},[M]);const at=m.useMemo(()=>{const le={};return M.forEach(J=>{le[J.id]=C[J.id]??J.locked}),le},[M,C]),ie=(le,J={})=>{se(xe=>({...xe||{},[le]:{...xe?.[le]||{},...J||{}}}))},fe=async le=>{try{const J=Z?.[le];if(!J)return;await g(le,J)}catch(J){alert(J.message)}};return ue?r.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5 text-sm",children:[r.jsx("span",{children:"Admin controls"}),r.jsx("select",{value:V,onChange:ae,className:"field-input text-sm",children:vA.map(le=>r.jsx("option",{value:le.key,children:le.label},le.key))})]}),r.jsxs("div",{className:"flex gap-0.5 text-xs",children:[r.jsx("button",{type:"button",onClick:de,disabled:A,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:A?"Server rebooting...":"Reboot Server"}),r.jsx("button",{type:"button",onClick:ze,className:"button-dark",children:"Test Reward Popup"})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Global audio levels"}),n?.audioLevels?.updatedAt?r.jsxs("span",{children:["Updated ",new Date(n.audioLevels.updatedAt).toLocaleString()]}):null]}),r.jsxs("label",{className:"grid gap-0.5 text-xs text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Horn gain"}),r.jsxs("span",{children:[R.hornGain.toFixed(2),"x"]})]}),r.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:R.hornGain,onChange:ye("hornGain"),className:"w-full accent-emerald-500"})]}),r.jsxs("label",{className:"grid gap-0.5 text-xs text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"TTS gain"}),r.jsxs("span",{children:[R.ttsGain.toFixed(2),"x"]})]}),r.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:R.ttsGain,onChange:ye("ttsGain"),className:"w-full accent-emerald-500"})]}),r.jsxs("label",{className:"grid gap-0.5 text-xs text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Forward gain"}),r.jsxs("span",{children:[R.forwardGain.toFixed(2),"x"]})]}),r.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:R.forwardGain,onChange:ye("forwardGain"),className:"w-full accent-emerald-500"})]}),r.jsx("div",{className:"flex gap-0.5 text-xs",children:r.jsx("button",{type:"button",onClick:Ye,className:"button-dark",children:"Apply audio levels"})})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Global objective"}),H?r.jsxs("span",{children:["Updated ",new Date(H).toLocaleString()]}):null]}),r.jsx("input",{type:"text",value:K,onChange:le=>G(le.target.value),placeholder:"Set a global objective",className:"field-input text-sm"}),r.jsxs("div",{className:"flex gap-0.5 text-xs",children:[r.jsx("button",{type:"button",onClick:ve,className:"button-dark",children:"Set objective"}),r.jsx("button",{type:"button",onClick:pe,className:"button-danger",children:"Clear"})]})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Admin mode reason"}),ee?r.jsxs("span",{children:["Updated ",new Date(ee).toLocaleString()]}):null]}),r.jsx("textarea",{value:oe,onChange:le=>ce(le.target.value),placeholder:"Set an admin mode reason",className:"field-input text-sm min-h-[3.5rem]"}),r.jsxs("div",{className:"flex gap-0.5 text-xs",children:[r.jsx("button",{type:"button",onClick:Oe,className:"button-dark",children:"Set reason"}),r.jsx("button",{type:"button",onClick:Me,className:"button-danger",children:"Clear"})]})]}),r.jsx(fA,{roster:M,renderActions:le=>r.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 text-xs",children:[r.jsx("button",{type:"button",onClick:()=>ne(le.id,!at[le.id]),className:"button-dark",children:at[le.id]?"Unlock":"Lock"}),r.jsx("button",{type:"button",onClick:()=>he(le.id),className:"button-dark",children:"Force"}),r.jsx("button",{type:"button",onClick:()=>re(le),disabled:!!N[le.id],className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:N[le.id]?"Rebooting...":"Reboot"}),le?.private?.enabled?r.jsxs("div",{className:"w-full rounded border border-slate-700/70 p-0.5 space-y-0.5 text-[0.7rem]",children:[r.jsx("div",{className:"text-slate-300",children:"Private safety"}),r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!!Z?.[le.id]?.speedLimitEnabled,disabled:!T,onChange:J=>ie(le.id,{speedLimitEnabled:!!J.target.checked})}),r.jsx("span",{children:"Speed limit"})]}),r.jsx("input",{type:"number",min:"1",max:"500",className:"field-input text-xs w-20",disabled:!T,value:Z?.[le.id]?.speedLimitMaxWheelSpeed??250,onChange:J=>ie(le.id,{speedLimitMaxWheelSpeed:Number(J.target.value)||250})}),r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!!Z?.[le.id]?.hardOvercurrentEnabled,disabled:!T,onChange:J=>ie(le.id,{hardOvercurrentEnabled:!!J.target.checked})}),r.jsx("span",{children:"Hard overcurrent"})]}),r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!!Z?.[le.id]?.hardBumpEnabled,disabled:!T,onChange:J=>ie(le.id,{hardBumpEnabled:!!J.target.checked})}),r.jsx("span",{children:"Hard bump"})]}),r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!!Z?.[le.id]?.cliffEnabled,disabled:!T,onChange:J=>ie(le.id,{cliffEnabled:!!J.target.checked})}),r.jsx("span",{children:"Cliff safety"})]}),r.jsx("button",{type:"button",disabled:!T,onClick:()=>fe(le.id),className:"button-dark disabled:opacity-50",children:"Apply Safety"})]}):null]})}),r.jsx(xA,{health:Q,roster:M}),r.jsx(pA,{state:S,onClearHistory:ge,clearingHistory:k}),r.jsx(gA,{entries:y})]}):null}const yA=[{id:"driveForward",label:"Drive Forward",group:"Driving"},{id:"driveBackward",label:"Drive Backward",group:"Driving"},{id:"driveLeft",label:"Turn Left",group:"Driving"},{id:"driveRight",label:"Turn Right",group:"Driving"},{id:"boostModifier",label:"Boost Modifier",group:"Driving"},{id:"slowModifier",label:"Slow Modifier",group:"Driving"},{id:"auxMainForward",label:"Main Brush Forward",group:"Aux Motors"},{id:"auxMainReverse",label:"Main Brush Reverse",group:"Aux Motors"},{id:"auxSideForward",label:"Side Brush Forward",group:"Aux Motors"},{id:"auxSideReverse",label:"Side Brush Reverse",group:"Aux Motors"},{id:"auxVacuumFast",label:"Vacuum Max",group:"Aux Motors"},{id:"auxVacuumSlow",label:"Vacuum Low",group:"Aux Motors"},{id:"auxAllForward",label:"All Aux Forward",group:"Aux Motors"},{id:"cameraUp",label:"Camera Up",group:"Camera"},{id:"cameraDown",label:"Camera Down",group:"Camera"},{id:"nightVisionToggle",label:"Toggle Night Vision",group:"Camera"},{id:"hornHonk",label:"Horn (Hold)",group:"Audio"},{id:"micPtt",label:"Mic Push To Talk",group:"Audio"},{id:"driveMacro",label:"Drive Macro",group:"Macros"},{id:"dockMacro",label:"Dock Macro",group:"Macros"},{id:"chatFocus",label:"Toggle Chat",group:"Chat"},{id:"songNoteUp",label:"Song Note Up",group:"Audio"},{id:"songNoteDown",label:"Song Note Down",group:"Audio"},{id:"homeAssistantOn",label:"Room Controls On (Cycle)",group:"Room Controls"},{id:"homeAssistantOff",label:"Room Controls Off (Cycle)",group:"Room Controls"}];function SA(n){return n.reduce((s,l)=>((s[l.group]||(s[l.group]=[])).push(l),s),{})}function jA(n,s){const l=Number(n);return Number.isFinite(l)?Math.max(0,Math.min(500,l)):s}const gf=5,Ni=500,Ai=1,vf=100;function NA(n,s){const l=Number(n);return Number.isFinite(l)?Math.max(gf,Math.min(Ni,l)):s}function mo(n,s){const l=Number(n);return Number.isFinite(l)?Math.max(Ai,Math.min(vf,l)):s}function AA(n){const l=(mo(n,n)-Ai)/(vf-Ai),o=Ni-l*(Ni-gf);return Math.round(o)}function wA(n){const s=NA(n,n),l=(Ni-s)/(Ni-gf),o=Ai+l*(vf-Ai);return Math.round(o)}function CA(n){const[s,l]=m.useState(null),o=m.useCallback(p=>{id(!0),l(p)},[]),u=m.useCallback(()=>{id(!1),l(null)},[]),f=m.useCallback((p,h)=>{if(h.preventDefault(),h.stopPropagation(),h.key==="Escape"){u();return}const x=Ri(h.key??"");x&&(n(p,x,h),u())},[u,n]);return m.useEffect(()=>()=>id(!1),[]),{active:s,startCapture:o,cancel:u,captureFromEvent:f}}function eo({label:n,description:s,value:l,onChange:o,min:u=0,max:f=500,step:p=5}){return r.jsxs("label",{className:"surface-muted block p-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[r.jsx("span",{className:"font-semibold text-slate-100",children:n}),r.jsx("input",{type:"number",min:u,max:f,value:l,onChange:h=>o(h.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"})]}),s&&r.jsx("p",{className:"text-[0.65rem] text-slate-500",children:s}),r.jsxs("div",{className:"mt-0 flex items-center gap-0.5",children:[r.jsx("input",{type:"range",min:u,max:f,step:p,value:l,onChange:h=>o(h.target.value),className:"h-2 flex-1 accent-emerald-400"}),r.jsx("span",{className:"w-12 text-right text-[0.75rem] font-mono text-slate-400",children:l})]})]})}function EA(){const{state:{keymap:n},actions:{updateKeyBinding:s,resetKeyBindings:l}}=_t(),{value:o,save:u}=Ge("inputs",_n),f={..._n.keyboard,...o.keyboard??{}},p=m.useMemo(()=>typeof f.tiltSpeed=="number"?mo(f.tiltSpeed,_n.keyboard.tiltSpeed):typeof f.tiltIntervalMs=="number"?mo(wA(f.tiltIntervalMs),_n.keyboard.tiltSpeed):_n.keyboard.tiltSpeed,[f.tiltIntervalMs,f.tiltSpeed]),h=m.useMemo(()=>SA(yA),[]),{active:x,startCapture:g,cancel:v,captureFromEvent:y}=CA((E,N)=>{s(E,N)}),S=m.useCallback(E=>n?.[E]?.[0]||hi[E]?.[0]||"",[n]),M=m.useCallback((E,N)=>{const w=_n.keyboard,A=jA(N,w[E]);u(q=>({...q??{},keyboard:{...q?.keyboard??w,[E]:A}}))},[u]),C=m.useCallback(E=>{const N=_n.keyboard,w=mo(E,N.tiltSpeed),A=AA(w);u(q=>({...q??{},keyboard:{...q?.keyboard??N,tiltSpeed:w,tiltIntervalMs:A}}))},[u]);return r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsxs("div",{children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Keyboard layout"}),r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Per-browser · click to change a binding"})]}),r.jsx("button",{type:"button",onClick:()=>l(),className:"button-dark text-xs",children:"Reset defaults"})]}),r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Keyboard speeds"}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(eo,{label:"Base speed",description:"Normal driving speed",value:f.baseSpeed,onChange:E=>M("baseSpeed",E)}),r.jsx(eo,{label:"Turbo speed",description:"Used when holding the boost modifier",value:f.turboSpeed,onChange:E=>M("turboSpeed",E)}),r.jsx(eo,{label:"Precision speed",description:"Used when holding the precision modifier",value:f.precisionSpeed,onChange:E=>M("precisionSpeed",E)})]})]}),r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Camera tilt"}),r.jsx(eo,{label:"Tilt speed",description:"Higher = faster when holding tilt keys",value:p,onChange:C,min:1,max:100,step:1})]}),r.jsx("div",{className:"space-y-0.5",children:Object.entries(h).map(([E,N])=>r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.75rem] text-slate-400",children:E}),r.jsx("div",{className:"space-y-0.5",children:N.map(w=>{const A=S(w.id),q=x===w.id;return r.jsxs("div",{className:"surface-muted flex items-center justify-between text-xs",children:[r.jsxs("div",{children:[r.jsx("p",{className:"font-semibold text-slate-100",children:w.label}),r.jsx("p",{className:"text-[0.65rem] text-slate-400",children:Cn(A)})]}),r.jsxs("div",{className:"flex items-center gap-0.5",children:[q&&r.jsx("button",{type:"button",onClick:()=>v(),className:"button-danger text-[0.7rem]",children:"Cancel"}),r.jsx("button",{type:"button",onClick:()=>g(w.id),onKeyDown:q?k=>y(w.id,k):void 0,autoFocus:q,className:`${q?"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:q?"Press a key…":"Change"})]})]},w.id)})})]},E))})]})}const sa=new Intl.NumberFormat(void 0,{maximumFractionDigits:2}),kA=[{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"}],fx=.45,MA=.6;function ui({label:n,description:s,min:l,max:o,step:u,value:f,onChange:p}){return r.jsxs("label",{className:"surface-muted block p-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[r.jsx("span",{className:"font-semibold text-slate-100",children:n}),r.jsx("span",{className:"font-mono text-slate-400",children:sa.format(f)})]}),s&&r.jsx("p",{className:"text-[0.65rem] text-slate-500",children:s}),r.jsx("input",{type:"range",min:l,max:o,step:u,value:f,onChange:h=>p(Number(h.target.value)),className:"mt-0 w-full accent-emerald-400"})]})}function RA(n){if(!n)return"Unassigned";if(n.kind==="axisPair"){const s=`${n.invertX?"X inv ":""}${n.invertY?"Y inv":""}`.trim();return`Axes ${n.x}/${n.y}${s?` (${s})`:""}`}if(n.kind==="axis")return`Axis ${n.index}${n.invert?" (inv)":""}`;if(n.kind==="button")return`Button ${n.index}`;if(n.kind==="buttonAxis")return`Button ${n.index} (analog)`;if(n.kind==="axisButton"){const s=n.direction<0?"<":">";return`Axis ${n.index} ${s} ${sa.format(n.threshold??.6)}`}return"Unassigned"}function qA(n){return n.reduce((s,l)=>((s[l.section]||(s[l.section]=[])).push(l),s),{})}function TA(n,s){if(!n||n.length===0)return null;if(s){const l=n.find(o=>o.signature===s);if(l)return l}return n[0]}function DA(n){return{axes:Array.from(n.axes??[]),buttons:(n.buttons??[]).map(s=>({pressed:!!s?.pressed,value:typeof s?.value=="number"?s.value:s?.pressed?1:0}))}}function _A(n,s,l){const o=(n.axes??[]).map((f,p)=>({index:p,delta:Math.abs((f??0)-(s.axes?.[p]??0)),value:f}));if(o.sort((f,p)=>p.delta-f.delta),l.kind==="axisPair"){const f=o.filter(p=>p.delta>fx).slice(0,2);return f.length<2?null:{kind:"axisPair",x:f[0].index,y:f[1].index,...l.invertDefaults??{}}}const u=o.find(f=>f.delta>fx);return u?{kind:"axis",index:u.index,...l.invertDefaults??{}}:null}function BA(n,s,l){const o=n.buttons??[];for(let f=0;fMA)return l.kind==="axis"?{kind:"buttonAxis",index:f}:{kind:"button",index:f}}const u=n.axes??[];for(let f=0;f.7&&h>.5)return l.kind==="axis"?{kind:"axis",index:f,...l.invertDefaults??{}}:{kind:"axisButton",index:f,direction:p>=0?1:-1,threshold:.6}}return null}function OA(n,s,l){if(!n)return null;if(l.kind==="axis"||l.kind==="axisPair"){const o=_A(n,s,l);if(o)return o}return BA(n,s,l)}function LA(){const n=fN(),{value:s,save:l}=Ge("gamepad",_a),[o,u]=m.useState(null),f=m.useRef(null),p=m.useMemo(()=>qA(kA),[]),h=m.useMemo(()=>TA(n.pads,s.activeSignature),[n.pads,s.activeSignature]),x=h?.signature??null,g=m.useMemo(()=>x?s?.profiles?.[x]??s?.defaults?.profile??la:s?.defaults?.profile??la,[x,s?.defaults?.profile,s?.profiles]);m.useEffect(()=>{!h||!x||s?.profiles?.[x]||l(N=>{const w=N??_a;if(w.profiles?.[x])return w;const A=w?.defaults?.profile??la,q=wg(h,A);return{...w,profiles:{...w.profiles??{},[x]:q}}})},[h,x,s?.profiles,l]),m.useEffect(()=>{f.current=null},[o,x]),m.useEffect(()=>{if(!o||!h)return;if(!f.current){f.current=DA(h);return}const N=OA(h,f.current,o);N&&(l(w=>{const A=w??_a,q=A.profiles?.[x]??A?.defaults?.profile??la,k={...q,bindings:{...q.bindings??{},[o.id]:{...q.bindings?.[o.id]??{},kind:o.kind,sources:[N]}}};return{...A,profiles:{...A.profiles??{},[x]:k}}}),u(null))},[h,x,o,l]);const v=m.useCallback(N=>{l(w=>({...w??_a,activeSignature:N||null}))},[l]),y=m.useCallback(N=>{l(w=>{const A=w??_a,q=(x&&A.profiles?.[x])??A?.defaults?.profile??la,k={...q,calibration:{...q.calibration??{},...N}};return x?{...A,profiles:{...A.profiles??{},[x]:k}}:{...A,defaults:{...A.defaults??{},profile:k}}})},[x,l]),S=m.useCallback((N,w)=>{l(A=>{const q=A??_a,k=(x&&q.profiles?.[x])??q?.defaults?.profile??la,B=w(k.bindings?.[N]??{}),Q={...k,bindings:{...k.bindings??{},[N]:B}};return x?{...q,profiles:{...q.profiles??{},[x]:Q}}:{...q,defaults:{...q.defaults??{},profile:Q}}})},[x,l]),M=m.useCallback(N=>{S(N.id,w=>({...w,sources:[]}))},[S]),C=m.useCallback((N,w)=>{S(N.id,A=>{const q=Array.isArray(A.sources)?[...A.sources]:[];if(!q[0])return A;const k={...q[0]};return w==="invertX"?k.invertX=!k.invertX:w==="invertY"?k.invertY=!k.invertY:k.invert=!k.invert,q[0]=k,{...A,sources:q}})},[S]),E=m.useMemo(()=>h?{outputs:Cg(h,g)}:null,[h,g]);return r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("div",{className:"flex items-center justify-between",children:r.jsxs("div",{children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Controller"}),r.jsx("p",{className:"text-[0.65rem] text-slate-500",children:h?"Move sticks or press buttons to bind":"Connect a controller to configure."}),o&&r.jsxs("p",{className:"mt-0 text-[0.7rem] text-emerald-400",children:["Capturing ",o.label,"…"]})]})}),r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Connected controller"}),n.pads.length===0?r.jsx("p",{className:"text-[0.7rem] text-slate-400",children:"No controller detected."}):r.jsxs("div",{className:"flex items-center justify-between gap-0.5 text-xs",children:[r.jsx("select",{value:x??"",onChange:N=>v(N.target.value),className:"flex-1 rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:n.pads.map(N=>r.jsx("option",{value:N.signature,children:N.id||"Unknown controller"},N.signature))}),r.jsx("span",{className:"text-[0.7rem] text-slate-400",children:h?.mapping??"unknown"})]})]}),r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Calibration"}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(ui,{label:"Drive deadzone",description:"Ignore small drive stick drift",min:0,max:.6,step:.01,value:g.calibration?.driveDeadzone??.18,onChange:N=>y({driveDeadzone:N})}),r.jsx(ui,{label:"Camera deadzone",description:"Ignore small camera tilt drift",min:0,max:.4,step:.01,value:g.calibration?.cameraDeadzone??.08,onChange:N=>y({cameraDeadzone:N})}),r.jsx(ui,{label:"Aux deadzone",description:"Ignore small trigger noise",min:0,max:.4,step:.01,value:g.calibration?.auxDeadzone??.05,onChange:N=>y({auxDeadzone:N})}),r.jsx(ui,{label:"Side brush scale",description:"Scale side brush output",min:.3,max:1,step:.05,value:g.calibration?.auxSideScale??.55,onChange:N=>y({auxSideScale:N})}),r.jsxs("label",{className:"surface-muted block p-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[r.jsx("span",{className:"font-semibold text-slate-100",children:"Camera mode"}),r.jsxs("select",{value:g.calibration?.cameraMode??"absolute",onChange:N=>y({cameraMode:N.target.value}),className:"rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:[r.jsx("option",{value:"absolute",children:"Absolute"}),r.jsx("option",{value:"velocity",children:"Velocity"})]})]}),r.jsx("p",{className:"text-[0.65rem] text-slate-500",children:"Absolute maps stick to angle; velocity moves over time."})]}),r.jsx(ui,{label:"Camera sensitivity",description:"Velocity mode degrees per second",min:10,max:180,step:5,value:g.calibration?.cameraSensitivity??60,onChange:N=>y({cameraSensitivity:N})})]})]}),r.jsx("div",{className:"space-y-0.5",children:Object.entries(p).map(([N,w])=>r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:N}),r.jsx("div",{className:"space-y-0.5",children:w.map(A=>{const k=g.bindings?.[A.id]?.sources?.[0]??null;return r.jsxs("div",{className:"surface-muted flex items-center justify-between text-xs",children:[r.jsxs("div",{children:[r.jsx("p",{className:"font-semibold text-slate-100",children:A.label}),r.jsx("p",{className:"text-[0.65rem] text-slate-400",children:RA(k)})]}),r.jsxs("div",{className:"flex items-center gap-0.5",children:[A.kind==="axisPair"&&r.jsxs(r.Fragment,{children:[r.jsx("button",{type:"button",disabled:!k,onClick:()=>C(A,"invertX"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert X"}),r.jsx("button",{type:"button",disabled:!k,onClick:()=>C(A,"invertY"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert Y"})]}),A.kind==="axis"&&r.jsx("button",{type:"button",disabled:!k,onClick:()=>C(A),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert"}),r.jsx("button",{type:"button",onClick:()=>M(A),className:"button-dark text-[0.7rem]",children:"Clear"}),r.jsx("button",{type:"button",onClick:()=>u(A),className:`${o?.id===A.id?"px-0.5 py-0.5 bg-emerald-500 text-emerald-950 hover:bg-emerald-400":"button-dark"} text-[0.7rem] font-medium`,children:o?.id===A.id?"Waiting…":"Capture"})]})]},A.id)})})]},N))}),r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Diagnostics"}),h?r.jsxs("div",{className:"space-y-0.5 text-[0.7rem] text-slate-300",children:[r.jsx("p",{className:"text-slate-400",children:"Raw axes"}),r.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:h.axes.map((N,w)=>r.jsxs("span",{className:"font-mono text-slate-300",children:["A",w,": ",sa.format(N)]},`axis-${w}`))}),r.jsx("p",{className:"text-slate-400",children:"Raw buttons"}),r.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:h.buttons.map((N,w)=>r.jsxs("span",{className:"font-mono text-slate-300",children:["B",w,": ",sa.format(N.value)," ",N.pressed?"●":""]},`btn-${w}`))}),E?.outputs&&r.jsxs(r.Fragment,{children:[r.jsx("p",{className:"text-slate-400",children:"Mapped outputs"}),r.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[r.jsxs("span",{className:"font-mono text-slate-300",children:["Drive: ",sa.format(E.outputs.driveVector.x),","," ",sa.format(E.outputs.driveVector.y)]}),r.jsxs("span",{className:"font-mono text-slate-300",children:["Camera: ",sa.format(E.outputs.cameraAxis)]}),r.jsxs("span",{className:"font-mono text-slate-300",children:["Main: ",sa.format(E.outputs.auxAxis.main)]}),r.jsxs("span",{className:"font-mono text-slate-300",children:["Side: ",sa.format(E.outputs.auxAxis.side)]})]})]})]}):r.jsx("p",{className:"text-[0.7rem] text-slate-400",children:"No controller detected."})]})]})}const zA={drive:"Drive wheels",aux:"Aux motors"};function mx(n){return Number.isFinite(n)?`${Math.round(n*100)}%`:"--"}function UA({value:n,color:s="bg-emerald-500"}){const l=`${Math.round(Math.max(0,Math.min(1,n))*100)}%`;return r.jsx("div",{className:"h-2 w-full overflow-hidden rounded bg-slate-800",children:r.jsx("div",{className:`h-full ${s}`,style:{width:l}})})}function HA(){const{state:{roverId:n},overcurrentLimiter:s}=_t(),l=m.useMemo(()=>ls.map(o=>o.key),[]);return r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Overcurrent limiter"}),r.jsx("span",{children:s?.adminImmune?"Admin immune":"Active"})]}),n?r.jsxs("div",{className:"space-y-0.5",children:[l.map(o=>{const u=s?.caps?.[o]?.cap??0,f=s?.overcurrent?.groups?.[o]??!1,p=s?.scales?.perGroup?.[o]??1;return r.jsxs("div",{className:"surface space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs",children:[r.jsx("span",{className:"text-slate-200",children:zA[o]||o}),r.jsx("span",{className:f?"text-red-300":"text-slate-400",children:f?"overcurrent":"ok"})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-[0.7rem] text-slate-400",children:[r.jsx("span",{children:"Cap"}),r.jsx("span",{children:mx(u)})]}),r.jsx(UA,{value:u,color:"bg-amber-500"}),r.jsxs("div",{className:"flex items-center justify-between text-[0.7rem] text-slate-400",children:[r.jsx("span",{children:"Scale"}),r.jsx("span",{children:mx(p)})]})]})]},o)}),r.jsxs("div",{className:"surface text-[0.7rem] text-slate-400",children:[r.jsx("div",{children:`Down rate ${s?.config?.downRatePerSec}/s · Up rate ${s?.config?.upRatePerSec}/s`}),r.jsx("div",{children:`Release delay ${s?.config?.releaseDelaySec}s`}),r.jsx("div",{children:`Output rate ${s?.config?.outputRateMs}ms`})]})]}):r.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to view limiter status."})]})}const $g=m.createContext(null);function Gg(){const n=m.useContext($g);if(!n)throw new Error("Tabs components must be used within a provider.");return n}const hx={primary:{base:"flex-1 px-0.5 py-0.5 text-sm font-medium focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-slate-500 rounded-md border border-slate-800",active:"bg-sky-600 text-white border-white",inactive:"bg-zinc-900 text-slate-300 hover:bg-sky-500 hover:text-white"}},px={none:"",pink:"bg-pink-500 text-white border-pink-200 hover:bg-pink-400",green:"bg-emerald-500 text-white border-emerald-200 hover:bg-emerald-400"},Yg="primary";function bf(...n){return n.filter(Boolean).join(" ")}function yf({children:n,defaultTab:s,currentTab:l,onTabChange:o,variant:u=Yg}){const[f,p]=m.useState(s??null),[h,x]=m.useState([]),g=m.useCallback(M=>M?(x(C=>C.includes(M)?C:[...C,M]),()=>x(C=>C.includes(M)?C.filter(E=>E!==M):C)):()=>{},[]),v=l??f??h[0]??null;m.useEffect(()=>{s&&l===void 0&&p(s)},[s,l]);const y=m.useCallback(M=>{M&&(l===void 0&&p(M),o&&o(M))},[l,o]),S=m.useMemo(()=>({activeTab:v,setActiveTab:y,registerTab:g,variant:u}),[v,y,g,u]);return r.jsx($g.Provider,{value:S,children:n})}function Sf({children:n,className:s=""}){return r.jsx("div",{className:bf("flex gap-0.5",s),children:n})}function Wt({id:n,children:s,className:l="",disabled:o=!1,highlight:u="none"}){const{activeTab:f,setActiveTab:p,registerTab:h,variant:x}=Gg();m.useEffect(()=>h(n),[n,h]);const g=hx[x]??hx[Yg],v=f===n,y=px[u]||px.none,S=bf(g.base,y||(v?g.active:g.inactive),o?"cursor-not-allowed opacity-50":"",l);return r.jsx("button",{type:"button",className:S,onClick:()=>!o&&p(n),"aria-pressed":v,"aria-disabled":o,children:s})}function jf({children:n,className:s=""}){return r.jsx("div",{className:bf("mt-0 space-y-0.5",s),children:n})}function en({id:n,children:s,keepMounted:l=!1}){const{activeTab:o}=Gg(),u=o===n;return!u&&!l?null:!u&&l?r.jsx("div",{hidden:!0,"aria-hidden":"true",children:s}):r.jsx(r.Fragment,{children:s})}function VA(){const{session:n}=nn(),s=m.useMemo(()=>JSON.stringify(n??{},null,2),[n]);return r.jsxs("div",{className:"panel-section flex min-h-0 flex-col gap-0.5 text-xs",children:[r.jsx("p",{className:"text-sm text-slate-400",children:"Session snapshot"}),r.jsx("pre",{className:"surface min-h-0 flex-1 overflow-y-auto font-mono text-[0.7rem] text-lime-300",children:s})]})}const xx=300,gx=240;function $A(n,s){if(n===void 0)return"undefined";if(n===null)return"null";if(typeof n=="string")return n;if(typeof n=="number"||typeof n=="boolean")return String(n);if(typeof n=="function")return"[Function]";if(n instanceof Error)return`${n.name}: ${n.message}`;if(n instanceof Date)return n.toISOString();if(s||(s=new WeakSet),typeof n=="object"){if(s.has(n))return"[Circular]";s.add(n)}try{return JSON.stringify(n,(l,o)=>typeof o=="function"?"[Function]":o instanceof Error?`${o.name}: ${o.message}`:o,0)}catch{return String(n)}}function GA(n){const l=n.map(o=>$A(o)).join(" ");return l.length>gx?`${l.slice(0,gx)}…`:l}function YA(){const n=tn(),[s,l]=m.useState([]),o=m.useMemo(()=>s.reduce((u,f)=>(f.direction==="in"&&(u.in+=1),f.direction==="out"&&(u.out+=1),u),{in:0,out:0}),[s]);return m.useEffect(()=>{const u=(x,g,v)=>{l(y=>{const S=[...y,{id:`${Date.now()}-${Math.random().toString(16).slice(2)}`,direction:x,event:g,payload:GA(v),timestamp:new Date().toLocaleTimeString()}];return S.length>xx?S.slice(-xx):S})},f=(x,...g)=>u("in",x,g),p=(x,...g)=>u("out",x,g);n.onAny(f);let h=null;if(typeof n.onAnyOutgoing=="function")n.onAnyOutgoing(p),h=()=>n.offAnyOutgoing(p);else{const x=n.emit.bind(n);n.emit=(g,...v)=>(p(g,...v),x(g,...v)),h=()=>{n.emit=x}}return()=>{n.offAny(f),h&&h()}},[n]),r.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[r.jsxs("div",{className:"flex items-center justify-between text-sm text-slate-400",children:[r.jsx("span",{children:"Socket activity"}),r.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-400",children:[r.jsxs("span",{className:"surface-muted",children:["in ",o.in]}),r.jsxs("span",{className:"surface-muted",children:["out ",o.out]}),r.jsx("button",{type:"button",className:"button-dark text-xs",onClick:()=>l([]),children:"Clear"})]})]}),r.jsx("div",{className:"surface h-64 overflow-y-auto font-mono text-xs",children:s.length===0?r.jsx("p",{children:"No socket activity yet."}):s.slice().reverse().map(u=>r.jsxs("div",{className:"surface",children:[r.jsx("span",{className:"text-slate-400",children:u.timestamp})," ",r.jsxs("span",{className:u.direction==="in"?"text-emerald-400":"text-amber-400",children:["[",u.direction.toUpperCase(),"]"]})," ",r.jsx("span",{className:"text-cyan-300",children:u.event})," ",r.jsx("span",{className:"text-slate-200",children:u.payload})]},u.id))}),r.jsx("p",{className:"text-xs text-slate-500",children:"Logs are collected only while this panel is open."})]})}const FA=[{key:"start",label:"Start OI"},{key:"safe",label:"Safe"},{key:"full",label:"Full"},{key:"passive",label:"Passive"},{key:"dock",label:"Dock"}];function Fg(){const{state:{roverId:n},actions:{sendOiCommand:s,setSensorStream:l}}=_t(),o=!!n,[u,f]=Lg(),p=tn(),{value:h,save:x}=Ge("page",{hudMapDesktop:!1,connectionTransport:"websocket",swapMobileControlColumns:!1,driveMacroBackoffEnabled:!0}),{value:g,save:v}=Ge("audio",lt),y=h?.connectionTransport||"websocket",S=!!h?.swapMobileControlColumns,M=typeof h?.driveMacroBackoffEnabled=="boolean"?h.driveMacroBackoffEnabled:!0,C=Number.isFinite(g?.masterVolume)?g.masterVolume:lt.masterVolume,E=Number.isFinite(g?.alertVolume)?g.alertVolume:lt.alertVolume,N=Number.isFinite(g?.roverVolume)?g.roverVolume:lt.roverVolume,w=typeof g?.mainBrushDuckEnabled=="boolean"?g.mainBrushDuckEnabled:typeof g?.autoLevelEnabled=="boolean"?g.autoLevelEnabled:lt.mainBrushDuckEnabled,A=Number.isFinite(g?.mainBrushDuckAmount)?Math.max(0,Math.min(1,g.mainBrushDuckAmount)):lt.mainBrushDuckAmount,q=m.useMemo(()=>[{key:"start",label:"Enable stream",enable:!0},{key:"stop",label:"Disable stream",enable:!1}],[]),k=G=>{n&&l(G)},B=G=>{const $=G.target.value;x(ee=>({...ee??{},connectionTransport:$})),p?.io?.opts&&(p.io.opts.transports=$==="polling"?["polling"]:["websocket","polling"],p.disconnect(),p.connect())},Q=G=>$=>{const ee=Number($.target.value),oe=Number.isFinite(ee)?Math.max(0,Math.min(1,ee)):0;v(ce=>({...ce??{},[G]:oe}))},D=G=>{const $=!!G.target.checked;v(ee=>({...ee??{},mainBrushDuckEnabled:$}))},H=G=>{const $=!!G.target.checked;x(ee=>({...ee??{},swapMobileControlColumns:$}))},K=G=>{const $=!!G.target.checked;x(ee=>({...ee??{},driveMacroBackoffEnabled:$}))};return r.jsxs(yf,{defaultTab:"keybindings",children:[r.jsxs(Sf,{children:[r.jsx(Wt,{id:"keybindings",children:"Keybindings"}),r.jsx(Wt,{id:"controller",children:"Controller"}),r.jsx(Wt,{id:"page",children:"Page settings"}),r.jsx(Wt,{id:"admin",children:"Admin"})]}),r.jsxs(jf,{children:[r.jsx(en,{id:"keybindings",children:r.jsx("div",{className:"space-y-0.5",children:r.jsx(EA,{})})}),r.jsx(en,{id:"controller",children:r.jsx("div",{className:"space-y-0.5",children:r.jsx(LA,{})})}),r.jsx(en,{id:"page",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("p",{className:"text-slate-400",children:"HUD"}),r.jsxs("label",{className:"flex items-center gap-0.5 text-slate-200",children:[r.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:u,onChange:G=>f(G.target.checked)}),r.jsx("span",{children:"Show top-down map in HUD (desktop)"})]}),r.jsx("p",{className:"text-xs text-slate-500",children:"Mobile HUD keeps the map on by default."})]}),r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("p",{className:"text-slate-400",children:"Mobile controls"}),r.jsxs("label",{className:"flex items-center gap-0.5 text-slate-200",children:[r.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:S,onChange:H}),r.jsx("span",{children:"Swap control columns (put joystick on the left)"})]})]}),r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("p",{className:"text-slate-400",children:"Macros"}),r.jsxs("label",{className:"flex items-center gap-0.5 text-slate-200",children:[r.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:M,onChange:K}),r.jsx("span",{children:"Enable backward bump in drive macro"})]})]}),r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("p",{className:"text-slate-400",children:"Audio"}),r.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Master volume"}),r.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(C*100),"%"]})]}),r.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:C,onChange:Q("masterVolume"),className:"w-full accent-emerald-500"})]}),r.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Alert/page sounds"}),r.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(E*100),"%"]})]}),r.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:E,onChange:Q("alertVolume"),className:"w-full accent-emerald-500"})]}),r.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Rover audio"}),r.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(N*100),"%"]})]}),r.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:N,onChange:Q("roverVolume"),className:"w-full accent-emerald-500"})]}),r.jsxs("label",{className:"flex items-center justify-between gap-0.5 text-slate-200",children:[r.jsx("span",{children:"Main brush ducking"}),r.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:w,onChange:D})]}),r.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Main brush duck amount"}),r.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(A*100),"%"]})]}),r.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:A,onChange:Q("mainBrushDuckAmount"),className:"w-full accent-emerald-500",disabled:!w})]}),r.jsx("p",{className:"text-xs text-slate-500",children:"Lowers rover audio only while the main brush is running."})]}),r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("p",{className:"text-slate-400",children:"Connection"}),r.jsxs("label",{className:"flex items-center justify-between gap-0.5 text-slate-200",children:[r.jsx("span",{children:"Transport"}),r.jsxs("select",{value:y,onChange:B,className:"field-input text-sm",children:[r.jsx("option",{value:"websocket",children:"WebSocket"}),r.jsx("option",{value:"polling",children:"Polling"})]})]}),r.jsx("p",{className:"text-xs text-slate-500",children:"Switching reconnects your session."})]})]})}),r.jsx(en,{id:"admin",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("p",{className:"text-slate-400",children:"Manual OI commands"}),r.jsx("div",{className:"surface flex flex-wrap gap-0.5",children:FA.map(G=>r.jsx("button",{type:"button",onClick:()=>s(G.key),disabled:!o,className:"button-dark text-xs disabled:opacity-30",children:G.label},G.key))})]}),r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsx("p",{className:"text-slate-400",children:"Sensor stream"}),r.jsx("div",{className:"surface flex gap-0.5",children:q.map(G=>r.jsx("button",{type:"button",onClick:()=>k(G.enable),disabled:!o,className:"flex-1 button-dark text-xs disabled:opacity-30",children:G.label},G.key))}),!o&&r.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to toggle streams."})]}),r.jsx(Vg,{}),r.jsx(HA,{}),r.jsx(bA,{}),r.jsx(VA,{}),r.jsx(YA,{})]})})]})]})}const vx={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 column to move the rover, and hold the aux buttons in the other control column."]},{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 column beside the video feed to move the rover, and hold the aux buttons in the other control column."]},{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 XA(n){return vx[n]||vx.desktop}function Xg({actionId:n,keymap:s}){const l=s?.[n]?.[0]??"";return r.jsx("span",{className:"rounded border border-slate-600 bg-slate-900/40 px-1 text-[0.7rem] text-slate-200",children:Cn(l)})}function bx(n,s){return n.map((l,o)=>typeof l=="string"?r.jsx("span",{children:l},`text-${o}`):l?.action?r.jsx(Xg,{actionId:l.action,keymap:s},`pill-${l.action}-${o}`):l?.text?r.jsx("span",{children:l.text},`span-${o}`):null)}function qo(n,s,l){if(typeof n=="string")return n;if(Array.isArray(n?.segments))return bx(n.segments,s);if(n&&typeof n=="object"){if(typeof n.text=="string")return n.text;if(Array.isArray(n))return bx(n,s)}return String(n??"")}function QA({hero:n,keymap:s}){return n?r.jsxs("div",{className:"surface space-y-0.5 px-0.5 py-0.5",children:[r.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-0.5",children:[r.jsxs("div",{children:[r.jsx("p",{className:"text-sm font-semibold text-white",children:n.title}),n.subtitle&&r.jsx("p",{className:"text-xs text-slate-300",children:n.subtitle})]}),n.chips&&r.jsx("div",{className:"flex flex-wrap gap-0.5 text-[0.7rem] text-slate-200",children:n.chips.map(l=>r.jsx("span",{className:"rounded border border-slate-700 px-1 py-[2px]",children:l},l))})]}),n.bullets&&r.jsx("ul",{className:"space-y-0.5 text-[0.85rem] text-slate-200",children:n.bullets.map((l,o)=>{const u=Array.isArray(l?.segments)?`hero-${o}`:`hero-${o}`;return r.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:qo(l,s)},u)})})]}):null}function KA({block:n,keymap:s}){return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs font-semibold text-slate-200",children:n.title}),r.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:n.items.map((l,o)=>{const u=`item-${o}`;return r.jsx("li",{className:"surface-muted flex flex-wrap items-center gap-0.5 px-0.5 py-0.25",children:qo(l,s)},u)})})]})}function ZA({block:n}){const s=n.tone==="info"?"border-cyan-500/40":"border-slate-700";return r.jsxs("div",{className:`surface space-y-0.5 border ${s} px-0.5 py-0.5`,children:[r.jsx("p",{className:"text-xs font-semibold text-slate-100",children:n.title}),n.body&&r.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:n.body.map((l,o)=>r.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:qo(l,{})},`callout-${o}`))})]})}function IA({group:n,keymap:s}){return r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"px-0.5 py-0.25 text-[0.75rem] font-semibold text-slate-200",children:n.title}),r.jsx("div",{className:"space-y-0.5 px-0.5 pb-0.25",children:n.items.map(l=>r.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-0.5 py-0.25 text-[0.8rem]",children:[r.jsx("span",{className:"text-slate-200",children:l.label}),r.jsx(Xg,{actionId:l.action,keymap:s})]},l.action))})]})}function JA({block:n,keymap:s}){return n?r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-200",children:[r.jsx("span",{className:"font-semibold",children:n.title}),n.footnote&&r.jsx("span",{className:"text-[0.7rem] text-slate-400",children:n.footnote})]}),r.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-0.5",children:n.groups?.map(l=>r.jsx(IA,{group:l,keymap:s},l.id))})]}):null}function PA({block:n}){return n?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs font-semibold text-slate-200",children:n.title}),r.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:n.items?.map((s,l)=>r.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:qo(s,{})},`gamepad-${l}`))})]}):null}function yx({block:n,keymap:s}){if(!n)return null;switch(n.type){case"list":return r.jsx(KA,{block:n,keymap:s});case"callout":return r.jsx(ZA,{block:n});case"keyboard":return r.jsx(JA,{block:n,keymap:s});case"gamepad":return r.jsx(PA,{block:n});default:return null}}function Qg({layout:n,keymap:s}){const l=m.useMemo(()=>XA(n),[n]),o=s||{},u=l.main||[],f=l.aside||[];return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(QA,{hero:l.hero,keymap:o}),r.jsxs("div",{className:"grid gap-0.5",style:{gridTemplateColumns:"repeat(auto-fit, minmax(360px, 1fr))"},children:[r.jsx("div",{className:"space-y-0.5",children:u.map((p,h)=>r.jsx(yx,{block:p,keymap:o},p.title||h))}),r.jsx("div",{className:"space-y-0.5",children:f.map((p,h)=>r.jsx(yx,{block:p,keymap:o},p.title||h))})]})]})}function Kg({layout:n,onOpenOverlay:s}){const{state:l}=_t();return r.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Help"}),r.jsx("button",{type:"button",onClick:s,className:"button-dark px-1 py-0.25 text-[0.75rem]",children:"Open full help"})]}),r.jsx(Qg,{layout:n,keymap:l?.keymap||{}})]})}const WA=["kal","rms","slt","ksp","bdl"],ew=Array.from({length:10},(n,s)=>s*10);function To({hideInput:n=!1,hideSpectatorNotice:s=!1,fillHeight:l=!1,allowSpectatorInput:o=!1}){const u=rt(T=>T.session),{messages:f,typing:p,sendMessage:h,registerInputRef:x,onInputFocus:g,onInputBlur:v,blurChat:y,setTypingActive:S}=of(),{value:M,save:C}=Ge("tts",{engine:"flite",voice:"rms",pitch:50}),[E,N]=m.useState(""),[w,A]=m.useState(!1),[q,k]=m.useState(!1),[B,Q]=m.useState(()=>M?.engine||"flite"),[D,H]=m.useState(()=>M?.voice||"rms"),[K,G]=m.useState(()=>Number.isFinite(M?.pitch)?M.pitch:50),$=u?.role!=="spectator"||o,ee=m.useRef(null),oe=u?.assignment?.roverId||null,U=!!m.useMemo(()=>u?.roster?.find(T=>String(T.id)===String(oe))||null,[oe,u?.roster])?.audio?.ttsEnabled,R=m.useMemo(()=>f.slice(-200),[f]),L=m.useMemo(()=>p||[],[p]);m.useEffect(()=>{ee.current&&(ee.current.scrollTop=ee.current.scrollHeight)},[R,L]),m.useEffect(()=>{const T=M?.engine||"flite",V=M?.voice||"rms",ne=Number.isFinite(M?.pitch)?M.pitch:50;B!==T&&Q(T),D!==V&&H(V),K!==ne&&G(ne)},[B,K,M?.engine,M?.pitch,M?.voice,D]),m.useEffect(()=>{k(!!U)},[U]);const Z=m.useMemo(()=>!U||!q?null:B==="espeak"?{speak:!0,engine:B,pitch:K}:{speak:!0,engine:B,voice:D},[B,K,q,U,D]);async function se(T){if(T.preventDefault(),!$||n)return;const ne=E.replace(/\\n/g,` -`).trim();if(ne){A(!0);try{await h(ne,Z),N(""),y(),S(!1)}catch(ae){alert(ae.message)}finally{A(!1)}}}const ue=l?"flex-1 min-h-0 overflow-y-auto":"h-48 overflow-y-auto";return r.jsxs("section",{className:`panel-section space-y-0.5 text-base ${l?"flex h-full flex-col overflow-hidden":""}`,children:[r.jsxs("div",{className:`surface overflow-y-auto space-y-0.5 px-0 ${ue}`,ref:ee,children:[R.length===0&&L.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"No messages yet."}):R.map(T=>r.jsx(sf,{message:T},T.id)),L.map(T=>r.jsx(pg,{message:T},`typing-${T.typingId||T.id}`))]}),!n&&r.jsxs("form",{className:"flex flex-wrap items-stretch gap-0.5",onSubmit:se,children:[r.jsx("input",{className:"field-input flex-1 min-w-[10rem]",value:E,onChange:T=>{const V=T.target.value;N(V),S(!!V.trim())},onFocus:T=>{g(T),S(!!E.trim())},onBlur:T=>{v(T),S(!1)},onKeyDown:T=>{T.key==="Enter"&&!E.trim()&&(T.preventDefault(),y(),S(!1))},ref:T=>x(T,{target:"panel"}),placeholder:$?"Type a message…":s?"":"Spectators cannot chat",disabled:!$}),U&&r.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 basis-full sm:basis-auto",children:[r.jsxs("label",{className:"flex items-center gap-0.5 text-xs text-slate-300",children:[r.jsx("input",{type:"checkbox",checked:q,onChange:T=>k(T.target.checked),className:"accent-cyan-500"}),r.jsx("span",{children:"Speak"})]}),r.jsxs("select",{value:B,onChange:T=>{const V=T.target.value;Q(V),C(ne=>({...ne||{},engine:V}))},className:"field-input text-xs",children:[r.jsx("option",{value:"flite",children:"flite"}),r.jsx("option",{value:"espeak",children:"espeak"})]}),B==="flite"?r.jsx("select",{value:D,onChange:T=>{const V=T.target.value;H(V),C(ne=>({...ne||{},voice:V}))},className:"field-input text-xs",children:WA.map(T=>r.jsx("option",{value:T,children:T},T))}):r.jsx("select",{value:K,onChange:T=>{const V=Number(T.target.value);G(V),C(ne=>({...ne||{},pitch:V}))},className:"field-input text-xs",children:ew.map(T=>r.jsxs("option",{value:T,children:["pitch ",T]},T))})]}),r.jsx("button",{type:"submit",disabled:!$||w,className:"button-dark h-full disabled:opacity-50 self-stretch",children:w?"...":"Send"})]})]})}function Do({compact:n=!1}){const{session:s,setNickname:l}=nn(),{value:o,save:u}=Ge("profile",{nickname:""}),[f,p]=m.useState(o.nickname||""),[h,x]=m.useState(!1),g=s?.role!=="spectator";m.useEffect(()=>{p(o.nickname||"")},[o.nickname]);async function v(y){if(y.preventDefault(),!g)return;const S=(f||"").trim().slice(0,32);if(S){x(!0);try{await l(S),u({nickname:S})}catch(M){alert(M.message)}finally{x(!1)}}}return r.jsxs("form",{className:"grid w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-stretch gap-0.5",onSubmit:v,children:[r.jsx("input",{className:"field-input flex-1 min-w-0",value:f,onChange:y=>p(y.target.value),maxLength:32,placeholder:"Enter a nickname",disabled:!g}),r.jsx("button",{type:"submit",disabled:!g||h,className:"button-dark h-full shrink-0 whitespace-nowrap px-0.5 py-0 disabled:opacity-50",children:h?"Saving…":n?"Set":"Save"})]})}function tw(n){const s=Array.isArray(n?.socials)?n.socials:null;if(s&&s.length)return s;const l=[];return n?.discord?.invite&&l.push({id:"discord",label:"Discord",url:n.discord.invite}),n?.kofi?.link&&l.push({id:"kofi",label:"Ko-fi",url:n.kofi.link}),l}function nw(n){if(!n||typeof n!="object")return null;const s=n.id||n.service||n.key||n.name||null,l=n.label||n.title||n.name||s||"Link",o=n.url||n.link||n.href||null;return o?{id:s,label:l,url:o}:null}function Zg({className:n=""}){const{session:s}=nn(),l=tw(s).map(nw).filter(Boolean).slice(0,4);return l.length?r.jsx("div",{className:`grid grid-cols-2 grid-rows-2 gap-0.5 ${n}`,children:l.map(o=>r.jsx(hf,{...o},`${o.id||o.label}-${o.url}`))}):null}function aw({compact:n=!1}){return r.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:r.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:r.jsx(Do,{compact:n})})})}function sw(){return r.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:r.jsx(Zg,{className:"flex-1 min-h-0"})})}function yd(...n){return n.filter(Boolean).join(" ")}function lw(n){const s=n?.batteryState?.percentDisplay;return s==null?"--":`${s}%`}function iw(n){return n?.batteryState?n.batteryState.urgentActive?"text-red-400":n.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function rw(n){switch(n){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function ow(n,s){if(!n)return"";const l=n.nickname||n.socketId?.slice(0,6)||"unknown";return n.socketId&&n.socketId===s?`${l} (you)`:l}function _o({title:n="Rovers"}){const s=rt(w=>w.session),{requestControl:l}=kn(),[o,u]=m.useState({}),[f,p]=m.useState(()=>Date.now()),h=m.useMemo(()=>s?.role&&s.role!=="spectator",[s?.role]),x=m.useMemo(()=>s?.role==="admin"||s?.role==="lockdown"||s?.role==="lockdown-admin",[s?.role]),g=s?.roster??[],v=s?.turnQueues??{},y=s?.users??[],S=s?.socketId||null,M=m.useMemo(()=>Object.values(v||{}).some(w=>w?.deadline||w?.idleDeadline),[v]);m.useEffect(()=>{if(!M)return;const w=setInterval(()=>{p(Date.now())},1e3);return()=>clearInterval(w)},[M]);const C=m.useMemo(()=>{const w=new Set(g.map(q=>String(q.id))),A=Object.keys(v||{}).filter(q=>!w.has(String(q))).map(q=>({id:q,name:q,locked:!1,batteryState:null}));return[...g,...A]},[g,v]);async function E(w){if(w){u(A=>({...A,[w]:!0}));try{await l(w)}catch(A){alert(A.message)}finally{u(A=>({...A,[w]:!1}))}}}const N=w=>y.find(A=>A.socketId===w)||{socketId:w,nickname:null,role:null};return r.jsxs("div",{className:"space-y-0.5 text-sm",children:[n&&r.jsx("p",{className:"text-sm text-slate-400",children:n}),C.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"No rovers registered."}):r.jsx("ul",{className:"space-y-0.5 text-sm",children:C.map(w=>{const A=String(w.id),q=v?.[A]||null,k=q?.queue||[],B=q?.idleDeadline||q?.deadline||null,Q=B&&B>f?Math.ceil((B-f)/1e3):B?0:null,D=q?.current||null,H=D?k.findIndex(se=>se===D):-1,K=k.length>1?H>=0?k[(H+1)%k.length]:k[0]:null,G=!!(S&&D&&D===S),$=!!(S&&K&&K===S),ee=Q!=null&&(G||$),oe=!!w.locked,ce=oe&&!x,U=!!(w?.private?.enabled&&w?.private?.open),R=w.lockReason?`locked: ${w.lockReason}`:"locked",L=oe?R:o[A]?"...":"request",Z=h&&!ce&&!o[A];return r.jsxs("li",{className:yd("surface flex flex-wrap items-start justify-between gap-0.5",Z&&"cursor-pointer",oe?"bg-red-900/40":U?"bg-amber-700/35 border border-amber-200/30":null),onClick:()=>{Z&&E(w.id)},children:[r.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsxs("div",{className:"flex items-center gap-0.5",children:[r.jsx("p",{className:"text-slate-200",children:r.jsx("span",{className:"rounded px-1 py-[1px] border border-transparent",style:oa(w.color,.16),children:w.name})}),ee?r.jsx("span",{className:"rounded bg-slate-800 px-1 text-[0.7rem] text-slate-200",children:G?`${Q}s left`:`Your turn in ${Q}s`}):null]}),r.jsx("span",{className:yd("text-[0.75rem] font-semibold",iw(w)),children:lw(w)})]}),k.length===0?r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"No queue."}):r.jsx("div",{className:"flex flex-wrap items-center gap-0.5",children:k.map((se,ue)=>{const T=N(se),V=se===D,ne=!!(K&&se===K&&!V),ae=V?"bg-sky-600 text-white ring-2 ring-amber-300":ne?"bg-emerald-700/60 text-emerald-100 ring-1 ring-emerald-300/70":"bg-slate-800 text-slate-200";return r.jsxs("span",{className:`flex items-center gap-0.5 rounded px-1 text-[0.7rem] ${ae}`,children:[r.jsx("span",{className:`${rw(T.role)} font-semibold`,children:ow(T,S)}),V&&r.jsx("span",{className:"text-[0.65rem] text-slate-200",children:"now"}),ne&&r.jsx("span",{className:"text-[0.65rem] text-emerald-100",children:"next"})]},`${A}-${se}-${ue}`)})})]}),h?r.jsx("button",{type:"button",onClick:se=>{se.stopPropagation(),E(w.id)},disabled:o[A]||ce,className:yd("button-dark disabled:opacity-40",oe&&"bg-red-600/70 text-white hover:bg-red-600"),children:L}):null]},w.id)})})]})}function cw(n){switch(n){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function uw(n,s){if(!n)return"";const l=n.nickname||n.socketId?.slice(0,6)||"unknown";return n.socketId&&n.socketId===s?`${l} (you)`:l}function jo({hideNicknameForm:n=!1,hideHeader:s=!1,className:l="",fillHeight:o=!1,compact:u=!1}){const{session:f}=nn(),p=f?.role!=="spectator",h=f?.users??[],x=f?.socketId||null,g=m.useMemo(()=>[...h].sort((y,S)=>y.socketId===x?-1:S.socketId===x?1:(y.nickname||"").localeCompare(S.nickname||"")),[x,h]),v=o?"flex-1 min-h-0 overflow-y-auto":u?"h-28 overflow-y-auto":"h-48 overflow-y-auto";return r.jsxs("section",{className:`panel-section space-y-0.5 text-base ${o?"flex h-full min-h-0 flex-col overflow-hidden":""} ${l}`,children:[!n&&r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"grid gap-0.5 md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]",children:[r.jsx("div",{className:"min-w-0",children:r.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:r.jsx(Do,{compact:u})})}),r.jsx(Zg,{})]}),!p&&r.jsx("p",{className:"text-xs text-slate-500",children:"Spectators cannot set nicknames."})]}),r.jsxs("div",{className:`space-y-0.5 ${o?"flex flex-1 min-h-0 flex-col":""}`,children:[!s&&r.jsxs("div",{className:`flex items-center justify-between text-sm text-slate-400 ${u?"text-xs":""}`,children:[r.jsx("span",{children:"Users"}),r.jsx("span",{className:"text-xs text-slate-500",children:g.length})]}),r.jsx("div",{className:`surface flex flex-wrap content-start items-start gap-0.5 px-0 pb-0 ${v} ${u?"text-[0.8rem]":""}`,children:g.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"Waiting for users…"}):g.map(y=>r.jsx("span",{className:`rounded bg-slate-800/80 px-1 py-0.25 text-[0.7rem] ${cw(y.role)}`,children:uw(y,x)},y.socketId))})]})]})}const dw=/^cu_[a-f0-9]{32}$/,wi="field-input w-full max-w-sm text-left focus:ring-emerald-500",Bo="mx-auto w-full max-w-xl flex justify-center",xi="mx-auto flex w-full max-w-md flex-col items-center space-y-0.5 text-center";function fw(n){const s=String(n||"").trim();return s?s.length<=10?`${s.slice(0,2)}***${s.slice(-2)}`:`${s.slice(0,6)}...${s.slice(-6)}`:""}const Sx=8*1024*1024,ss=16e3,mw={iceServers:[{urls:"stun:stun.l.google.com:19302"}],bundlePolicy:"max-bundle",rtcpMuxPolicy:"require"};function jx(n){let s="";for(let o=0;o{if(!n||n.iceGatheringState==="complete"){l();return}const o=setTimeout(()=>{n.removeEventListener("icegatheringstatechange",u),l()},s);function u(){n.iceGatheringState==="complete"&&(clearTimeout(o),n.removeEventListener("icegatheringstatechange",u),l())}n.addEventListener("icegatheringstatechange",u)})}function Nx(n){if(!n)return!1;const s=n.connectionState,l=n.iceConnectionState;return s==="connected"||l==="connected"||l==="completed"}function gw(n,s=1e4){return new Promise((l,o)=>{if(!n){o(new Error("Peer connection missing"));return}if(Nx(n)){l();return}const u=setTimeout(()=>{p(),o(new Error("Peer connection timeout"))},s),f=()=>{Nx(n)?(p(),l()):(n.connectionState==="failed"||n.connectionState==="closed"||n.iceConnectionState==="failed")&&(p(),o(new Error(`Peer connection ${n.connectionState||n.iceConnectionState}`)))};function p(){clearTimeout(u),n.removeEventListener("connectionstatechange",f),n.removeEventListener("iceconnectionstatechange",f)}n.addEventListener("connectionstatechange",f),n.addEventListener("iceconnectionstatechange",f)})}async function vw(n){if(!n?.getParameters||!n?.setParameters)return;const s=n.getParameters()||{},l=s.encodings&&s.encodings[0]||{};s.encodings=[{...l,maxBitrate:64e3,dtx:"disabled"}];try{await n.setParameters(s)}catch{}}function bw(n,s=6e3){return new Promise((l,o)=>{if(!n){o(new Error("Peer connection missing"));return}const u=Date.now();let f=-1;const p=setInterval(async()=>{if(Date.now()-u>s){clearInterval(p),o(new Error("WHIP connected but no outbound audio flow"));return}try{const h=n.getSenders().filter(x=>x.track?.kind==="audio");for(const x of h){const g=await x.getStats();for(const v of g.values()){if(v.type!=="outbound-rtp"||v.kind!=="audio")continue;const y=Number(v.bytesSent||0),S=Number(v.packetsSent||0);if(f<0)f=y;else if(y>f+200||S>5){clearInterval(p),l();return}}}}catch{}},250)})}function yw(n){const s=n.reduce((u,f)=>u+f.length,0),l=new Float32Array(s);let o=0;for(const u of n)l.set(u,o),o+=u.length;return l}function Sw(n){const s=new Int16Array(n.length);for(let l=0;l{for(let v=0;vv&&s?.[v]||null,[s,v]),ne=!!(V&&V.state!=="offline"&&!V.error),ae=V?.source==="upload"&&V?.state==="playing",he=V?.source==="mic-whip"&&(V?.state==="starting"||V?.state==="playing"),re=!!(!w&&K.current&&(E||T)),de=!w&&(A==="live"||A==="starting"),ge=w&&k==="recording",ve=w&&k==="sending",pe=Cn(h?.keymap?.micPtt?.[0])||"M",Oe=m.useCallback(J=>{const xe=J==="clip"?"clip":"live";g(Ae=>({...Ae||{},pttMode:xe}))},[g]),Me=async()=>{if(!v){D("Take control of your rover first.");return}if(!y){D("Select an audio file first.");return}if(y.size>Sx){D(`File too large (max ${Sx} bytes).`);return}C(!0),D("");try{const J=await y.arrayBuffer(),xe=jx(new Uint8Array(J));await l?.({roverId:v,name:y.name,mime:y.type||"",dataBase64:xe}),D("Upload playback started.")}catch(J){D(J?.message||"Failed to play upload.")}finally{C(!1)}},ye=async()=>{if(!v){D("Take control of your rover first.");return}C(!0),D("");try{await o?.(v),D("Upload playback stopped.")}catch(J){D(J?.message||"Failed to stop upload.")}finally{C(!1)}},Ye=m.useCallback(async J=>{const xe=String(J||ee.current||"").trim();if($.current=!1,q("idle"),G.current){try{G.current.getSenders().forEach(Ae=>Ae.track?.stop())}catch{}try{G.current.close()}catch{}}if(G.current=null,H.current)try{H.current.getTracks().forEach(Ae=>Ae.stop())}catch{}if(H.current=null,K.current=null,xe)try{await p?.(xe)}catch{}ee.current=""},[p]),ze=m.useCallback(async J=>{const xe=await u?.(J),Ae=String(xe?.whipUrl||"").trim(),ht=String(xe?.token||"").trim();if(!Ae||!ht)throw new Error("WHIP endpoint unavailable");const Le=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:ss,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}});H.current=Le;const ot=Le.getAudioTracks()?.[0];if(K.current=ot||null,ot&&(ot.enabled=!!(E||T)),ot?.applyConstraints)try{await ot.applyConstraints({channelCount:1,sampleRate:ss,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1})}catch{}const ct=new RTCPeerConnection(mw);G.current=ct,ct.onconnectionstatechange=()=>{if(!$.current)return;const Lt=ct.connectionState;if(Lt==="connected"){q("live");return}(Lt==="failed"||Lt==="disconnected"||Lt==="closed")&&(q("error"),D(`WHIP transport ${Lt}.`))},Le.getAudioTracks().forEach(Lt=>{const dl=ct.addTrack(Lt,Le);vw(dl)});const gt=await ct.createOffer({offerToReceiveAudio:!1,offerToReceiveVideo:!1});await ct.setLocalDescription(gt),await xw(ct,1800);const kt=await fetch(Ae,{method:"POST",headers:{"Content-Type":"application/sdp",...pw(ht)},body:ct.localDescription?.sdp||gt.sdp});if(!kt.ok)throw new Error(`WHIP request failed: ${kt.status}`);const us=await kt.text();await ct.setRemoteDescription({type:"answer",sdp:us}),await gw(ct,1e4),await bw(ct,6e3),await f?.(J)},[E,T,f,u]),at=m.useCallback(async()=>{if(se.current=!1,R.current){try{R.current.disconnect()}catch{}R.current.onaudioprocess=null}if(U.current)try{U.current.disconnect()}catch{}if(L.current)try{L.current.disconnect()}catch{}if(oe.current)try{oe.current.getTracks().forEach(J=>J.stop())}catch{}if(ce.current)try{await ce.current.close()}catch{}Z.current=[],R.current=null,U.current=null,L.current=null,oe.current=null,ce.current=null,ue.current=ss},[]),ie=m.useCallback(async()=>{if(oe.current&&ce.current&&R.current)return;B(ot=>ot==="recording"?ot:"arming");const J=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:ss,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}}),xe=new AudioContext({latencyHint:"interactive"}),Ae=xe.createMediaStreamSource(J),ht=xe.createScriptProcessor(1024,1,1),Le=xe.createGain();Le.gain.value=0,ht.onaudioprocess=ot=>{if(!se.current)return;const ct=ot.inputBuffer.getChannelData(0);Z.current.push(new Float32Array(ct))},Ae.connect(ht),ht.connect(Le),Le.connect(xe.destination),oe.current=J,ce.current=xe,U.current=Ae,R.current=ht,L.current=Le,ue.current=xe.sampleRate||ss},[]),fe=m.useCallback(async({send:J})=>{const xe=se.current;if(se.current=!1,!xe){J||B("idle");return}const Ae=Z.current;if(Z.current=[],!J){B("idle");return}if(!v){B("idle");return}if(!Ae.length){B("idle");return}B("sending");try{const ht=yw(Ae),Le=jw(ht,ue.current||ss),ot=jx(Le);await l?.({roverId:v,name:`ptt-${Date.now()}.wav`,mime:"audio/wav",dataBase64:ot}),B("idle")}catch(ht){B("error"),D(ht?.message||"Failed to send PTT clip.")}},[l,v]),le=m.useCallback(async()=>{if(!v){D("Take control of your rover first.");return}await ie(),Z.current=[],se.current=!0,B("recording")},[ie,v]);return m.useEffect(()=>{let J=!1;async function xe(){if(w){await Ye(ee.current||v);return}if(!v){await Ye(ee.current);return}if(!($.current&&ee.current===v)&&!(!E&&!T))try{q("starting"),D(""),$.current=!0,ee.current=v,await ze(v),J||q("live")}catch(Ae){J||(q("error"),D(Ae?.message||"Failed to start mic forwarding.")),await Ye(v)}}return xe(),()=>{J=!0}},[w,E,T,v,ze,Ye]),m.useEffect(()=>{const J=K.current;J&&(J.enabled=!!(!w&&(E||T)))},[w,E,T]),m.useEffect(()=>{let J=!1;async function xe(){if(!w||!v){se.current&&await fe({send:!1}),B("idle");return}try{await ie(),!J&&!se.current&&B("idle")}catch(Ae){J||(B("error"),D(Ae?.message||"Microphone access failed for clip mode."))}}return xe(),()=>{J=!0}},[w,ie,fe,v]),m.useEffect(()=>{let J=!1;async function xe(){if(w){if(!v){se.current&&await fe({send:!1});return}if(T){if(!se.current)try{D(""),await le()}catch(Ae){J||(B("error"),D(Ae?.message||"Failed to start clip recording."))}}else se.current&&await fe({send:!0})}}return xe(),()=>{J=!0}},[w,fe,T,v,le]),m.useEffect(()=>()=>{Ye(ee.current||v),at()},[v,Ye,at]),r.jsx("section",{className:"surface",children:r.jsxs("div",{className:"grid gap-1",children:[r.jsx("p",{className:"text-sm text-slate-100 text-center",children:"Audio Controls"}),r.jsx("section",{className:"surface",children:r.jsxs("div",{className:"flex items-center justify-center gap-0.5 py-0.25 text-xs text-slate-300",children:[r.jsx("span",{children:"Push-to-Talk Key"}),r.jsx(Ax,{label:pe})]})}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-1 lg:grid-cols-2",children:[r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"PTT Mode"}),r.jsx("p",{className:"text-xs text-slate-400 text-center",children:"Choose how holding your PTT key behaves: live mic stream, or record-and-send clip. If the live stream doesn't work, switch to clip mode."}),r.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[r.jsx("button",{type:"button",className:`button-dark w-full text-sm ${w?"":"bg-emerald-500 text-white hover:bg-emerald-500"}`,onClick:()=>Oe("live"),children:"Live Mic (WHIP)"}),r.jsx("button",{type:"button",className:`button-dark w-full text-sm ${w?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,onClick:()=>Oe("clip"),children:"Record Clip"})]})]})}),r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_1fr]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"PTT Clip Status"}),r.jsxs("div",{className:"grid gap-0.5 content-start",children:[r.jsx(vn,{label:"Mode",active:w,detail:w?"record clip":"live mic"}),r.jsx(vn,{label:"PTT hold",active:T,detail:T?"held":"released"}),r.jsx(vn,{label:"Recording",active:ge,detail:ge?"capturing":"idle"}),r.jsx(vn,{label:"Playback",active:w&&ae,detail:w&&ae?"playing":"idle"})]})]})})]}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-1 lg:grid-cols-2",children:[r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr_auto]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Audio Upload"}),r.jsxs("label",{className:"mx-auto grid w-full max-w-sm gap-0.5 text-xs text-slate-300 text-center",children:[r.jsx("span",{children:"Audio file (mp3 / wav / ogg)"}),r.jsx("input",{className:`${wi} text-center`,type:"file",accept:".mp3,.wav,.ogg,audio/mpeg,audio/wav,audio/ogg",disabled:M||!v,onChange:J=>S(J.target.files?.[0]||null)})]}),r.jsx("div",{className:"mx-auto w-full max-w-sm text-center",children:y?r.jsxs("div",{className:"surface-muted text-xs text-slate-300",children:[y.name," (",y.size," bytes)"]}):r.jsx("div",{className:"surface-muted text-xs text-slate-500",children:"No file selected"})}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:M||!v,onClick:Me,children:M?"Working...":"Play Upload"}),r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:M||!v,onClick:ye,children:"Stop"})]})]})}),r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Live Microphone"}),r.jsxs("label",{className:"surface-muted mx-auto flex w-full max-w-sm items-center justify-center gap-0.5 px-0.5 py-0.5 text-xs text-slate-300",children:[r.jsx("input",{type:"checkbox",checked:E,disabled:!v||w,onChange:J=>g(xe=>({...xe||{},openMicEnabled:!!J.target.checked}))}),r.jsx("span",{children:w?"Open mic (live mode only)":"Open mic"})]}),r.jsxs("div",{className:"grid gap-0.5 content-start",children:[r.jsx(vn,{label:"WHIP link",active:de,detail:A}),r.jsx(vn,{label:"Mic hot",active:re,detail:re?"transmitting":"muted"}),r.jsxs("div",{className:"surface-muted text-center text-xs text-slate-400",children:["Hold ",r.jsx(Ax,{label:pe})," to talk"]})]})]})})]}),r.jsx("section",{className:"surface",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Audio Status"}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-6",children:[r.jsx(vn,{label:"Forward pipe",active:ne,detail:ne?"connected":"offline"}),r.jsx(vn,{label:"Upload playback",active:ae,detail:ae?"playing":"idle"}),r.jsx(vn,{label:"Mic relay",active:he,detail:he?"active":"idle"}),r.jsx(vn,{label:"WHIP transport",active:de,detail:A}),r.jsx(vn,{label:"Clip recording",active:ge,detail:ge?"recording":"idle"}),r.jsx(vn,{label:"Clip sending",active:ve,detail:ve?"sending":"idle"})]}),Q?r.jsx("div",{className:"text-xs text-slate-400 text-center",children:Q}):null]})})]})})}function Aw({pendingRequestId:n,currentStoredKey:s,nickname:l,requestVerification:o,applyIdentityKey:u,onMessage:f,fullWidth:p=!1}){const[h,x]=m.useState(0),[g,v]=m.useState(""),[y,S]=m.useState(!1),[M,C]=m.useState(!1),E=()=>{x(1),v(s),S(!1),f?.("")},N=()=>{x(0),S(!1)},w=async q=>{if(q.preventDefault(),!y){f?.("Please confirm your nickname agreement before sending.");return}if(h<3){f?.("Complete all request steps before sending.");return}C(!0),f?.("");try{const k=await u(g);await o?.(),v(k),x(0),S(!1),f?.("Verification request sent to lockdown admins.")}catch(k){f?.(k.message||"Failed to submit request.")}finally{C(!1)}},A=p?"w-full":Bo;return n?r.jsx("section",{className:`surface text-sm text-slate-300 ${A}`,children:r.jsxs("div",{className:xi,children:["Verification request pending: ",n]})}):h===0?r.jsx("section",{className:`surface ${A}`,children:r.jsxs("div",{className:xi,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Verification"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:E,disabled:M,children:"Request Verification"})]})}):r.jsx("form",{className:`surface ${A}`,onSubmit:w,children:r.jsxs("div",{className:xi,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Request verification"}),r.jsxs("p",{className:"text-xs text-slate-500",children:["Step ",h," of 3"]}),h===1?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Confirm your nickname, which is used as part of the verification process. You can edit it here before requesting."}),r.jsx("div",{className:"mx-auto w-full max-w-sm",children:r.jsx(Do,{compact:!0})}),r.jsxs("div",{className:"surface-muted mx-auto w-full max-w-sm text-xs text-slate-300 text-center",children:["Current nickname: ",r.jsx("span",{className:"font-semibold",children:l||"(not set)"})]}),r.jsxs("label",{className:"flex items-center justify-center gap-0.5 text-xs text-slate-300 text-center",children:[r.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:y,onChange:q=>S(q.target.checked)}),r.jsx("span",{children:"I understand this nickname is tied to my verification."})]}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!l||!y,onClick:()=>x(2),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:N,children:"Cancel"})]})]}):null,h===2?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Save your identity key in a safe place. You can use it to restore your identity in another browser."}),r.jsx("input",{className:wi,type:"password",name:"identity_key_request",autoComplete:"current-password",maxLength:35,value:g,onChange:q=>v(q.target.value.toLowerCase()),placeholder:"Identity key for this request"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!g,onClick:()=>navigator.clipboard?.writeText(g),children:"Copy Key"}),r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!g,onClick:()=>x(3),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>x(1),children:"Back"})]})]}):null,h===3?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Final step: confirm and send your verification request."}),r.jsx("input",{className:wi,type:"password",name:"identity_key_request_final",autoComplete:"current-password",maxLength:35,value:g,onChange:q=>v(q.target.value.toLowerCase()),placeholder:"Identity key for this request"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:M||!g,children:"Confirm Request"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>x(2),children:"Back"})]})]}):null]})})}function ww({currentStoredKey:n,applyIdentityKey:s,onMessage:l,fullWidth:o=!1}){const[u,f]=m.useState(0),[p,h]=m.useState(""),[x,g]=m.useState(!1),v=()=>{f(1),h(""),l?.("")},y=()=>{f(0),h("")},S=async C=>{if(C.preventDefault(),!(u<2)){g(!0),l?.("");try{await s(p),f(0),h(""),l?.("Identity key restored.")}catch(E){l?.(E.message||"Failed to restore key.")}finally{g(!1)}}},M=o?"w-full":Bo;return r.jsx("section",{className:`surface ${M}`,children:r.jsxs("div",{className:xi,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Identity key"}),r.jsxs("p",{className:"text-xs text-slate-500",children:["Current: ",fw(n)||"not set yet"]}),r.jsx("input",{className:wi,type:"password",name:"identity_key_current",autoComplete:"current-password",maxLength:35,value:n,readOnly:!0,placeholder:"Identity key"}),u===0?r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!n,onClick:()=>navigator.clipboard?.writeText(n),children:"Copy Key"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:v,disabled:x,children:"Restore Key"})]}):null,u===1?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Restoring your key should only be done when needed. Use this to move your identity to another browser."}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(2),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:y,children:"Cancel"})]})]}):null,u===2?r.jsxs("form",{className:"space-y-0.5",onSubmit:S,children:[r.jsx("input",{className:wi,type:"password",name:"identity_key_restore",autoComplete:"current-password",maxLength:35,value:p,onChange:C=>h(C.target.value.toLowerCase()),placeholder:"Paste key to restore"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:x||!p,children:"Confirm Restore"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(1),children:"Back"})]})]}):null]})})}function Cw({requestableRovers:n=[],pendingRequests:s=[],requestPrivateRoverAccess:l,onMessage:o,fullWidth:u=!1}){const[f,p]=m.useState({}),h=m.useMemo(()=>{const v=new Map;return(s||[]).forEach(y=>{y?.roverId&&v.set(String(y.roverId),y)}),v},[s]),x=u?"w-full":Bo,g=async v=>{if(v){p(y=>({...y,[v]:!0})),o?.("");try{(await l?.(v))?.existing?o?.("You already have a pending request for that rover."):o?.("Private rover access request sent to lockdown admins.")}catch(y){o?.(y.message||"Failed to send request.")}finally{p(y=>({...y,[v]:!1}))}}};return r.jsx("section",{className:`surface text-sm text-slate-300 ${x}`,children:r.jsxs("div",{className:xi,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Private rover access requests"}),n.length===0?r.jsx("p",{className:"text-xs text-slate-500",children:"No closed private rovers are available to request right now."}):r.jsx("div",{className:"w-full space-y-0.5",children:n.map(v=>{const y=String(v.id),S=!!f[y],M=h.get(y);return r.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-1 py-0.5",children:[r.jsxs("div",{className:"min-w-0 text-left",children:[r.jsx("p",{className:"truncate text-xs font-semibold text-slate-100",children:v.name||y}),r.jsx("p",{className:"truncate text-[0.7rem] text-slate-500",children:y})]}),M?r.jsx("span",{className:"rounded bg-amber-700/30 px-1 py-0.5 text-[0.7rem] text-amber-200",children:"Pending"}):r.jsx("button",{type:"button",onClick:()=>g(y),disabled:S,className:"button-dark text-xs disabled:opacity-50",children:S?"Sending...":"Request"})]},y)})})]})})}function tl(n){return String(n||"").trim()}function Ew(n){const s=tl(n);return s?s.includes("DOCKINGRUNNING")?"Returning":s.includes("PAUSED")?"Paused":s.includes("HOUSECLEANINGRUNNING")?"Cleaning":s.includes("SPOTCLEANINGRUNNING")?"Spot cleaning":s.includes("STATE_START")?"Starting":s.includes("STATE_IDLE")||s.includes("STATE_STANDBY")?"Idle":s:"--"}function Ig(n){return n==="good"?"bg-emerald-600 text-white":n==="warn"?"bg-amber-500 text-slate-900":n==="danger"?"bg-rose-600 text-white":"bg-slate-700 text-slate-100"}function to({label:n,value:s,tone:l="muted",valueClass:o="",hideLabel:u=!1}){return r.jsxs("div",{className:`rounded-md px-1 py-0.75 text-center ${Ig(l)}`,children:[u?null:r.jsx("div",{className:"text-[0.72rem] opacity-90",children:n}),r.jsx("div",{className:`text-sm font-semibold ${o}`,title:String(s||""),children:s})]})}function kw(n=[]){return n.filter(u=>u&&u.valid&&Number.isFinite(u.angleDeg)&&Number.isFinite(u.distanceMm)).map(u=>{const f=(Number(u.angleDeg)-90)*Math.PI/180,h=Math.max(0,Math.min(1,Number(u.distanceMm)/4e3))*92,x=110+Math.cos(f)*h,g=110+Math.sin(f)*h;return`${x},${g}`}).join(" ")}function Mw({neato:n,lidar:s,onStart:l,onSendHome:o,onLocate:u,onClearErrors:f,onPowerCycle:p,fullWidth:h=!1}){const[x,g]=m.useState(""),[v,y]=m.useState(!1),[S,M]=m.useState(!1),C=h?"w-full":"w-full max-w-xl",E=!!n?.configured,N=!!(n?.enabled&&n?.connected),w=!!n?.telemetry?.extPowerPresent,A=!!n?.telemetry?.chargingActive,q=Ew(n?.telemetry?.uiState),k=tl(n?.telemetry?.robotState)||"--",B=n?.telemetry?.batteryPercent,Q=Number.isFinite(B)?`${B}%`:"--",D=n?.telemetry?.batteryVoltage,H=Number.isFinite(D)?`${D.toFixed(2)} V`:"--",K=tl(n?.telemetry?.robotError)||"--",G=tl(n?.telemetry?.robotAlert)||"--",$=Array.isArray(s?.points)?s.points:[],ee=kw($),oe=tl(s?.status)||"--",ce=s?.debug&&typeof s.debug=="object"?s.debug:null,U=tl(ce?.reason)||"--",R=ce?.stats&&typeof ce.stats=="object"?ce.stats:null,L=n?.controls||{},Z=!!L?.start?.available,se=!!L?.sendHome?.available,ue=!!L?.locate?.available,T=!!L?.clearErrors?.available,V=!!L?.powerCycle?.available,ne=E&&N&&Z,ae=E&&N&&se,he=E&&N&&ue,re=E&&N&&T,de=E&&N&&V,ge=async(ye,Ye)=>{if(Ye){g(ye);try{await Ye()}catch{}finally{g("")}}},ve=E?N?"Online":"Offline":"Not configured",pe=E?N?"good":"danger":"warn",Oe=B==null?"muted":B>=60?"good":B>=25?"warn":"danger",Me=w?"Docked":q!=="--"?q:"Away from dock";return m.useEffect(()=>{if(!s||!Array.isArray(s.points))return;y(!0);const ye=setTimeout(()=>{y(!1)},120);return()=>clearTimeout(ye)},[s]),r.jsx("section",{className:`surface text-sm text-slate-200 ${C}`,children:r.jsxs("div",{className:"grid gap-0.5",children:[r.jsxs("div",{className:"relative flex items-center justify-center min-h-[1.5rem]",children:[r.jsxs("div",{className:"text-center",children:[r.jsx("p",{className:"text-sm text-slate-100",children:"Neato Controls"}),r.jsx("p",{className:"text-xs text-slate-400",children:"Control the autonomous Neato robovac. Be nice to him."})]}),r.jsx("span",{className:`absolute right-0 inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${Ig(pe)}`,children:ve})]}),r.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)] gap-0.5",children:[r.jsxs("div",{className:"surface-muted grid gap-0.5",children:[r.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Controls"}),r.jsx("button",{type:"button",disabled:!ne||!!x,onClick:()=>ge("start",l),className:"rounded-md border border-sky-300 bg-emerald-600 px-1 py-1 text-base font-semibold text-white transition hover:border-sky-500 hover:bg-emerald-500 disabled:opacity-50",children:x==="start"?"Starting...":"Start cleaning"}),r.jsx("button",{type:"button",disabled:!ae||!!x,onClick:()=>ge("sendHome",o),className:"rounded-md border border-sky-300 bg-sky-600 px-1 py-1 text-base font-semibold text-white transition hover:border-sky-500 hover:bg-sky-500 disabled:opacity-50",children:x==="sendHome"?"Sending...":"Send to dock"}),r.jsxs("div",{className:"grid grid-cols-3 gap-0.5",children:[r.jsx("button",{type:"button",disabled:!he||!!x,onClick:()=>ge("locate",u),className:"w-full rounded-md border border-sky-300 bg-fuchsia-600 px-1 py-0.5 text-xs font-semibold text-white transition hover:border-sky-500 hover:bg-fuchsia-500 disabled:opacity-50",children:x==="locate"?"Playing...":"Play sound"}),r.jsx("button",{type:"button",disabled:!re||!!x,onClick:()=>ge("clearErrors",f),className:"w-full rounded-md border border-sky-300 bg-amber-500 px-1 py-0.5 text-xs font-semibold text-slate-900 transition hover:border-sky-500 hover:bg-amber-400 disabled:opacity-50",children:x==="clearErrors"?"Clearing...":"Clear errors"}),r.jsx("button",{type:"button",disabled:!de||!!x,onClick:()=>ge("powerCycle",p),className:"w-full rounded-md border border-rose-300 bg-rose-600 px-1 py-0.5 text-xs font-semibold text-white transition hover:border-rose-500 hover:bg-rose-500 disabled:opacity-50",children:x==="powerCycle"?"Cycling...":"Power cycle"})]}),r.jsxs("div",{className:"surface-muted grid gap-0.5 pt-0.25",children:[r.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Power status"}),r.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[r.jsx(to,{label:"Battery",value:Q,tone:Oe}),r.jsx(to,{label:"Voltage",value:H,tone:"muted"}),r.jsx(to,{label:"Docked",value:w?"Docked":"Not docked",tone:w?"good":"muted",hideLabel:!0}),r.jsx(to,{label:"Charging",value:A?"charging":"not charging",tone:A?"good":"muted",hideLabel:!0})]})]})]}),r.jsx("div",{className:"grid gap-0.5",children:r.jsxs("div",{className:"surface-muted grid gap-0.5",children:[r.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Robot Status"}),r.jsxs("div",{className:"grid grid-cols-1 xl:grid-cols-[minmax(0,1fr)_minmax(13rem,15rem)] gap-0.5 items-start",children:[r.jsxs("div",{className:"grid gap-0.5",children:[r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot state (raw)"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:k})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Basic state"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:Me})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"UI state"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:q})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot error"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:K})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot alert"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:G})]})]}),r.jsxs("div",{className:`rounded-md px-1 py-0.5 ${v?"bg-emerald-900":"bg-slate-800"}`,children:[r.jsx("button",{type:"button",onClick:()=>M(ye=>!ye),className:"w-full text-left text-[0.72rem] text-slate-300",children:"Lidar"}),r.jsx("div",{className:"mt-0.25 aspect-square rounded-md bg-slate-900 p-0.25",children:ee?r.jsxs("svg",{viewBox:"0 0 220 220",className:"h-full w-full",children:[r.jsx("rect",{x:"0",y:"0",width:"220",height:"220",fill:"#020617"}),r.jsx("circle",{cx:"110",cy:"110",r:"92",fill:"none",stroke:"#166534",strokeWidth:"1"}),r.jsx("circle",{cx:"110",cy:"110",r:"61",fill:"none",stroke:"#15803d",strokeWidth:"1"}),r.jsx("circle",{cx:"110",cy:"110",r:"31",fill:"none",stroke:"#22c55e",strokeWidth:"1"}),r.jsx("line",{x1:"110",y1:"18",x2:"110",y2:"202",stroke:"#166534",strokeWidth:"1"}),r.jsx("line",{x1:"18",y1:"110",x2:"202",y2:"110",stroke:"#166534",strokeWidth:"1"}),r.jsx("circle",{cx:"110",cy:"110",r:"4",fill:"#4ade80"}),$.filter(ye=>ye&&ye.valid&&Number.isFinite(ye.angleDeg)&&Number.isFinite(ye.distanceMm)).map(ye=>{const Ye=(Number(ye.angleDeg)-90)*Math.PI/180,at=Math.max(0,Math.min(1,Number(ye.distanceMm)/4e3))*92,ie=110+Math.cos(Ye)*at,fe=110+Math.sin(Ye)*at;return r.jsx("circle",{cx:ie,cy:fe,r:"1.6",fill:"#86efac"},`${ye.angleDeg}-${ye.distanceMm}-${ye.intensity}`)})]}):r.jsx("div",{className:"flex h-full items-center justify-center text-center text-xs text-emerald-300",children:"Waiting for scan"})}),S?r.jsxs("div",{className:"mt-0.5 rounded-md bg-slate-900 px-0.75 py-0.5 font-mono text-[0.68rem] text-slate-300",children:[r.jsxs("div",{children:["Status: ",oe]}),r.jsxs("div",{children:["Reason: ",U]}),r.jsxs("div",{children:["Points: ",$.length]}),r.jsxs("div",{children:["Rotation: ",Number.isFinite(s?.rotationSpeed)?s.rotationSpeed.toFixed(2):"--"]}),r.jsxs("div",{children:["Request in flight: ",ce?.requestInFlight?"yes":"no"]}),r.jsxs("div",{children:["Started at: ",ce?.requestStartedAt||"--"]}),r.jsxs("div",{children:["Headers: ",R?.headersSeen??"--"]}),r.jsxs("div",{children:["Rotations: ",R?.rotationsSeen??"--"]}),r.jsxs("div",{children:["Scans ok: ",R?.scansOk??"--"]}),r.jsxs("div",{children:["Timeouts: ",R?.scansTimedOut??"--"]}),r.jsxs("div",{children:["Restarts: ",R?.scansRestarted??"--"]}),r.jsxs("div",{children:["Rotation no scan: ",R?.rotationsWithoutScan??"--"]}),r.jsxs("div",{children:["Parseable payloads: ",R?.parseablePayloads??"--"]}),r.jsxs("div",{children:["Point payloads: ",R?.pointPayloads??"--"]})]}):null]})]})]})})]}),!E||!N||!Z||!se||!ue||!T||!V?r.jsx("p",{className:"text-xs text-slate-400 text-center",children:E?N?"Waiting for required Neato entities in Home Assistant.":"Home Assistant is offline.":"Set homeAssistant.neato.device in server config to enable Neato controls."}):null]})})}function Rw(n){return n==="good"?"bg-emerald-600 text-white":n==="warn"?"bg-amber-500 text-slate-900":n==="danger"?"bg-rose-600 text-white":"bg-slate-700 text-slate-100"}function qw(n){return n==="up"?"Up":n==="down"?"Down":n==="stopped"?"Transitioning...":n==="conflict"?"Conflict":"--"}function Tw({lift:n,onUp:s,onDown:l,fullWidth:o=!1}){const[u,f]=m.useState(""),[p,h]=m.useState(()=>Date.now()),x=o?"w-full":"w-full max-w-xl",g=!!n?.configured,v=!!(n?.enabled&&n?.connected),y=!!n?.busy,S=String(n?.target||"").toLowerCase(),M=String(n?.position||"").toLowerCase(),C=!!n?.availability?.upSwitch,E=!!n?.availability?.downSwitch,N=Number(n?.lastActionAt||0),w=Math.max(0,Number(n?.commandCooldownMs||0)),A=Math.max(0,N+w-p),q=!y&&A>0,k=y||q;m.useEffect(()=>{if(!k)return;const G=setInterval(()=>{h(Date.now())},100);return()=>clearInterval(G)},[k]),m.useEffect(()=>{h(Date.now())},[N,w,y]);const B=g?v?k?"Busy":"Ready":"Offline":"Not configured",Q=g?v?k?"warn":"good":"danger":"warn",D=g&&v&&!k&&!u&&C&&E,H=m.useMemo(()=>Math.ceil(A/1e3),[A]),K=async(G,$)=>{if($){f(G);try{await $()}catch{}finally{f("")}}};return r.jsxs("section",{className:`surface relative text-sm text-slate-200 ${x}`,children:[k?r.jsx("div",{className:"absolute inset-0 z-20 flex items-center justify-center rounded-md bg-slate-950/80 px-1.5 text-center",children:r.jsxs("div",{className:"space-y-0.25",children:[r.jsx("p",{className:"text-sm font-semibold text-slate-100",children:y?"Motion in progress":"Motion cooldown active"}),r.jsx("p",{className:"text-xs text-slate-300",children:"Controls are disabled while the lift is moving, otherwise it's tiny brain would get confused."}),!y&&q?r.jsxs("p",{className:"text-xs text-slate-400",children:["About ",H,"s remaining."]}):null]})}):null,r.jsxs("div",{className:"grid gap-0.5",children:[r.jsxs("div",{className:"relative flex items-center justify-center min-h-[1.5rem]",children:[r.jsxs("div",{className:"text-center",children:[r.jsx("p",{className:"text-sm text-slate-100",children:"Lift Controls"}),r.jsx("p",{className:"text-xs text-slate-400",children:"Move the lift up and down. Please don't break anything..."})]}),r.jsx("span",{className:`absolute right-0 inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${Rw(Q)}`,children:B})]}),r.jsx("section",{className:"surface-muted px-0.5 py-0.5",children:r.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-0.5",children:[r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.75 text-center",children:[r.jsx("div",{className:"text-xs text-slate-300",children:"Position"}),r.jsx("div",{className:"text-base font-semibold text-slate-100",children:qw(M)})]}),r.jsx("button",{type:"button",disabled:!D,onClick:()=>K("down",l),className:`button-dark w-full text-sm disabled:opacity-50 ${M==="down"||S==="down"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:u==="down"||S==="down"?"Lowering...":"Down"}),r.jsx("button",{type:"button",disabled:!D,onClick:()=>K("up",s),className:`button-dark w-full text-sm disabled:opacity-50 ${M==="up"||S==="up"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:u==="up"||S==="up"?"Raising...":"Up"})]})}),!g||!v||!C||!E?r.jsx("p",{className:"text-xs text-slate-400 text-center",children:g?v?"Waiting for required lift switch entities in Home Assistant.":"Home Assistant is offline.":"Set homeAssistant.lift.upSwitch and homeAssistant.lift.downSwitch in server config."}):null,n?.lastError?r.jsxs("p",{className:"text-xs text-rose-300 text-center",children:["Last error: ",n.lastError]}):null]})]})}function Jg(){const{session:n,neatoLidar:s,identifySession:l,requestVerification:o,requestPrivateRoverAccess:u,playUploadedAudio:f,stopUploadedAudio:p,startMicWhip:h,readyMicWhip:x,stopMicWhip:g,neatoStart:v,neatoSendHome:y,neatoLocate:S,neatoClearErrors:M,neatoPowerCycle:C,liftUp:E,liftDown:N}=nn(),{value:w,save:A}=Ge("identity",{cookieUserId:""}),{value:q}=Ge("profile",{nickname:""}),k=m.useMemo(()=>(w?.cookieUserId||"").trim(),[w?.cookieUserId]),B=(q?.nickname||"").trim(),Q=!!n?.isVerified,D=n?.verification?.pendingRequestId||null,H=n?.privateRoverAccess?.requestableRovers||[],K=n?.privateRoverAccess?.pendingRequests||[],G=String(n?.assignment?.roverId||"").trim(),[$,ee]=m.useState(""),oe=async ce=>{const U=String(ce||"").trim().toLowerCase();if(!U)throw new Error("Identity key required.");if(!dw.test(U))throw new Error("Identity key must match format: cu_ + 32 lowercase hex chars.");return A(R=>({...R||{},cookieUserId:U})),await l({cookieUserId:U,nickname:B}),U};return r.jsxs("section",{className:"space-y-2 text-base",children:[r.jsxs("div",{className:"grid gap-2 grid-cols-1 lg:grid-cols-[minmax(0,1.25fr)_minmax(0,0.9fr)]",children:[Q?null:r.jsx("div",{className:"lg:col-span-2",children:r.jsx(Aw,{pendingRequestId:D,currentStoredKey:k,nickname:B,requestVerification:o,applyIdentityKey:oe,onMessage:ee,fullWidth:!0})}),r.jsx("div",{className:"lg:col-span-2",children:Q?r.jsxs("div",{className:"space-y-2",children:[r.jsx(Mw,{neato:n?.neato||null,lidar:s,onStart:v,onSendHome:y,onLocate:S,onClearErrors:M,onPowerCycle:C,fullWidth:!0}),r.jsx(Tw,{lift:n?.lift||null,onUp:E,onDown:N,fullWidth:!0}),r.jsx(Nw,{ownRoverId:G,audioForwardByRover:n?.audioForward||{},playUploadedAudio:f,stopUploadedAudio:p,startMicWhip:h,readyMicWhip:x,stopMicWhip:g})]}):r.jsx("section",{className:"surface h-full",children:r.jsx("div",{className:"flex h-full flex-col items-center justify-center text-center text-xs text-slate-400",children:"Verify your account to unlock VIP controls."})})}),r.jsx("div",{className:"lg:col-span-2",children:r.jsx(Cw,{requestableRovers:H,pendingRequests:K,requestPrivateRoverAccess:u,onMessage:ee,fullWidth:!0})}),r.jsx("div",{className:"lg:col-span-2",children:r.jsx(ww,{currentStoredKey:k,applyIdentityKey:oe,onMessage:ee,fullWidth:!0})})]}),$?r.jsx("div",{className:Bo,children:r.jsx("p",{className:"text-xs text-slate-400 text-center",children:$})}):null]})}const Dw=420,_w=1200,Bw={1:262,2:330,3:392,4:523};function No(){const{session:n}=nn(),s=tn(),{value:l}=Ge("audio",lt),o=Number.isFinite(l?.masterVolume)?l.masterVolume:lt.masterVolume,u=Number.isFinite(l?.alertVolume)?l.alertVolume:lt.alertVolume,f=Math.max(0,Math.min(1,o*u)),p=m.useMemo(()=>{const E=Array.isArray(n?.buttonBox?.buttons)?n.buttonBox.buttons:[];return E.length===4?E:[1,2,3,4].map(N=>E.find(w=>Number(w?.id)===N)||{id:N,count:0,goal:0,rewardName:null,rewardId:null,rewardNumber:null,lastRewardAt:null})},[n?.buttonBox?.buttons]),[h,x]=m.useState({}),[g,v]=m.useState({}),y=m.useRef(new Map),S=m.useRef(new Map),M=m.useRef({}),C=m.useRef(null);return m.useEffect(()=>{const E={};p.forEach(w=>{E[w.id]=Number(w.lastRewardAt||0)});const N=M.current||{};p.forEach(w=>{const A=Number(w.id),q=E[A]||0,k=Number(N[A]||0);if(q>0&&q!==k){v(D=>({...D,[A]:!0}));const B=S.current.get(A);B&&clearTimeout(B);const Q=setTimeout(()=>{v(D=>({...D,[A]:!1})),S.current.delete(A)},_w);S.current.set(A,Q)}}),M.current=E},[p]),m.useEffect(()=>{function E(w){const A=Bw[w];if(!A||f<=0)return;let q=C.current;if(!q){const D=window.AudioContext||window.webkitAudioContext;if(!D)return;q=new D,C.current=q}const k=q.createOscillator(),B=q.createGain();k.type="square",k.frequency.value=A;const Q=.14*f;B.gain.setValueAtTime(1e-4,q.currentTime),B.gain.exponentialRampToValueAtTime(Math.max(1e-4,Q),q.currentTime+.01),B.gain.exponentialRampToValueAtTime(1e-4,q.currentTime+.2),k.connect(B),B.connect(q.destination),k.start(),k.stop(q.currentTime+.21)}function N(w={}){const A=Number(w.buttonId);if(!Number.isFinite(A)||A<1||A>4)return;x(B=>({...B,[A]:!0}));const q=y.current.get(A);q&&clearTimeout(q);const k=setTimeout(()=>{x(B=>({...B,[A]:!1})),y.current.delete(A)},Dw);y.current.set(A,k),E(A)}return s.on("buttonBox:increment",N),()=>{s.off("buttonBox:increment",N)}},[f,s]),r.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[r.jsx("header",{className:"panel-muted text-center text-sm",children:r.jsx("p",{children:"Button Box"})}),r.jsx("div",{className:"grid grid-cols-4 gap-0.5",children:p.map(E=>{const N=Number(E.id),w=Number.isFinite(E.count)?E.count:0,A=Number.isFinite(E.goal)?E.goal:0,q=typeof E.rewardName=="string"&&E.rewardName.trim()?E.rewardName.trim():"Unassigned",k=Number.isFinite(E.rewardNumber)?E.rewardNumber:"?",B=!!h[N],Q=!!g[N];return r.jsx(xg,{buttonId:N,count:w,goal:A,rewardNumber:k,rewardName:q,className:[B?"bg-cyan-900/45":"",Q?"bg-fuchsia-900/45":""].filter(Boolean).join(" ")},N)})})]})}function Ow(){const{state:{roverId:n}}=_t(),l=ul(n)?.sensors||{};return r.jsx("section",{className:"panel-section",children:r.jsx("div",{className:"aspect-square w-full",children:r.jsx(Hd,{sensors:l})})})}function Lw(){const{state:{roverId:n,keymap:s,camera:l,horn:o},pipeline:u,actions:{setServoAngle:f,setNightVision:p,startHorn:h,stopHorn:x}}=_t(),g=qg(n),v=g.docked&&!g.driving,y=l?.config,S=!!(n&&l?.enabled&&y),M=!!(n&&u?.nightVision),C=u?.nightVisionState,E=!!(n&&u?.horn),N=o?.overheated,w=typeof y?.minAngle=="number"?y.minAngle:-30,A=typeof y?.maxAngle=="number"?y.maxAngle:30,q=typeof l?.angle=="number"?l.angle:typeof y?.homeAngle=="number"?y.homeAngle:(w+A)/2,k=Cn(s?.nightVisionToggle?.[0]),B=Cn(s?.hornHonk?.[0]),Q=Cn(s?.cameraUp?.[0]),D=Cn(s?.cameraDown?.[0]);return r.jsxs("section",{className:"panel-section grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] gap-0.5",children:[r.jsx(Tg,{layout:"desktop",expand:!0,driveDockState:g}),v?null:r.jsxs("div",{className:"surface space-y-0.5 p-0 text-sm text-slate-200",children:[M&&r.jsx(Dg,{nightVisionOn:C?.nightVisionOn,disabled:!n,onToggle:p,keyLabel:k}),E&&r.jsx(_g,{disabled:!n||N,onStart:h,onStop:x,keyLabel:B,active:o?.active,heat:o?.heat}),S&&r.jsx(Bg,{value:q,min:w,max:A,onChange:f,keyDownLabel:D,keyUpLabel:Q,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 zw({layout:n,onOpenHelpOverlay:s}){const l=rt(M=>M.session),{state:o}=_t(),{value:u}=Ge("vipAudio",{openMicEnabled:!1,pttMode:"live"}),f=l?.isVerified?"bg-emerald-400":"bg-red-600",p=String(l?.assignment?.roverId||"").trim(),h=p?l?.audioForward?.[p]:null,x=!!o?.mic?.pttActive,g=!!u?.openMicEnabled,v=u?.pttMode==="clip"?"clip":"live",y=!!(p&&l?.isVerified&&(v==="clip"?x:g||x)),S=!!(p&&l?.isVerified&&v==="clip"&&h?.source==="upload"&&h?.state==="playing");return r.jsx("section",{className:"panel text-base",children:r.jsxs(yf,{defaultTab:"telemetry",children:[r.jsxs(Sf,{children:[r.jsx(Wt,{id:"telemetry",children:"Controls"}),r.jsx(Wt,{id:"vip",highlight:S?"green":y?"pink":"none",children:r.jsxs("span",{className:"inline-flex items-center gap-2",children:[r.jsx("span",{children:"VIP"}),r.jsx("span",{className:`inline-block h-3 w-3 rounded-full ${f}`,"aria-hidden":"true",title:l?.isVerified?"Verified":"Not verified"})]})}),r.jsx(Wt,{id:"help",children:"Help"}),r.jsx(Wt,{id:"settings",children:"Settings"})]}),r.jsxs(jf,{children:[r.jsx(en,{id:"telemetry",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]",children:[r.jsx(Ow,{}),r.jsx(Lw,{})]}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]",children:[r.jsx(_o,{}),r.jsx(nf,{panelId:"replay-sources-desktop",fillHeight:!0}),r.jsx(sw,{})]}),r.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)] h-[14rem]",children:[r.jsx(To,{fillHeight:!0}),r.jsxs("div",{className:"grid min-h-0 gap-0.5 grid-rows-[minmax(0,1fr)_auto]",children:[r.jsx(jo,{compact:!0,hideNicknameForm:!0,fillHeight:!0}),r.jsx(aw,{compact:!0})]})]}),r.jsx(Hg,{}),r.jsx(No,{}),r.jsx(df,{defaultOrientation:"horizontal",panelId:"rightpane-telemetry"})]})}),r.jsx(en,{id:"vip",keepMounted:!0,children:r.jsx(Jg,{})}),r.jsx(en,{id:"help",children:r.jsx(Kg,{layout:n,onOpenOverlay:s})}),r.jsx(en,{id:"settings",children:r.jsx(Fg,{})})]})]})})}const Uw=new Set(["admin","lockdown","lockdown-admin"]),Hw=new Set(["lockdown","lockdown-admin"]),Vw=new Set(["admin","lockdown"]);function $w(n="admin"){return n==="lockdown"?{title:"Lockdown mode active",description:"Only the server owners can access the interface at this time."}:{title:"Admin mode active"}}function Gw(){const n=rt(S=>S.session),s=n?.mode,l=n?.role,o=n?.adminReason?.text||"";n?.adminReason?.updatedAt;const u=n?.timezone||"UTC",f=n?.socials?.find(S=>String(S?.id||S?.label||"").toLowerCase()==="discord")?.url||n?.discord?.invite||null,p=Vw.has(s),h=s==="lockdown"?Hw.has(l):Uw.has(l),[x,g]=m.useState(()=>new Date),v=m.useMemo(()=>{try{return new Intl.DateTimeFormat("en-US",{timeZone:u,hour:"numeric",minute:"2-digit",second:"2-digit"}).format(x)}catch{return x.toLocaleTimeString()}},[x,u]);if(m.useEffect(()=>{const S=setInterval(()=>g(new Date),1e3);return()=>clearInterval(S)},[]),!p||h)return null;const y=$w(s);return r.jsx("div",{className:"pointer-events-auto fixed inset-0 z-50 flex items-center justify-center bg-black/85 px-0.5 py-0.5",children:r.jsxs("div",{className:"surface w-full max-w-md space-y-0.5 text-slate-100 shadow-2xl",children:[r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-lg font-semibold",children:y.title}),r.jsx("p",{className:"text-sm text-slate-300",children:y.description})]}),r.jsxs("div",{className:"surface-muted space-y-0.5",children:[r.jsx("p",{className:"text-[0.7rem] tracking-wide text-slate-400",children:"Reason for locking:"}),r.jsx("p",{className:"text-lg font-semibold text-slate-100",children:o||"No reason set."}),r.jsxs("p",{className:"text-center text-sm text-slate-300",children:["Server time: ",v]})]}),r.jsx("div",{className:"surface-muted",children:r.jsx(Vg,{})}),r.jsx("div",{className:"w-full justify-center items-center",children:r.jsx(hf,{id:"discord",label:"Join our Discord server for updates!",url:f})}),"You can still use the chat while the server is locked:",r.jsxs("div",{className:"max-h-80 overflow-y-auto",children:[r.jsx(To,{}),r.jsx(Do,{})]})]})})}const Yw="/assets/turn_alert-DVO8EZJa.mp3";function Fw(n,s=1){const l=m.useRef(null);return m.useEffect(()=>{l.current=new Audio(n),l.current.volume=s,l.current.load()},[n,s]),()=>{l.current&&(l.current.volume=s,l.current.currentTime=0,l.current.play().catch(()=>{}))}}function Xw(){const n=rt(v=>v.session),{pushAlert:s}=kn(),{value:l}=Ge("audio",lt),o=Number.isFinite(l?.masterVolume)?l.masterVolume:lt.masterVolume,u=Number.isFinite(l?.alertVolume)?l.alertVolume:lt.alertVolume,f=Math.max(0,Math.min(1,o*u)),p=Fw(Yw,f),h=m.useRef(new Set),x=m.useMemo(()=>n?.turnQueues||{},[n?.turnQueues]),g=n?.socketId||null;return m.useEffect(()=>{if(!g)return;const v=[];Object.entries(x).forEach(([y,S])=>{!S||!S.current||S.current!==g||h.current.has(y)||(v.push(y),h.current.add(y))}),Object.keys(x).forEach(y=>{(!x[y]||x[y].current!==g)&&h.current.delete(y)}),v.length!==0&&(v.forEach(y=>{const S=n?.roster?.find(M=>String(M.id)===String(y))?.name||y;s({title:"Your turn!",message:`You now control ${S}.`,color:"#ff5722"})}),p())},[x,p,s,n?.roster,g]),null}function Qw({visible:n,mode:s,onEnterFullscreen:l,onDismiss:o}){if(!n)return null;const u=s==="pwa-hint";return r.jsx("div",{className:"fixed inset-0 z-40 flex items-end justify-center p-2 pointer-events-none sm:items-center",children:r.jsx("div",{className:"pointer-events-auto w-full max-w-sm rounded-lg border border-cyan-500/40 bg-zinc-950/95 shadow-xl",children:r.jsxs("div",{className:"space-y-0.5 p-4 text-sm text-slate-100",children:[r.jsx("h2",{className:"text-base font-semibold text-white",children:"Better in fullscreen"}),u?r.jsxs("p",{className:"text-slate-300",children:["For fullscreen on iOS, open Safari's share menu and pick ",r.jsx("strong",{children:"Add to Home Screen"}),". Launching from the home screen removes the browser chrome."]}):r.jsx("p",{className:"text-slate-300",children:"Enable fullscreen to free up more space for the video feed and controls. You can exit fullscreen at any time via the system back or home gesture."}),r.jsxs("div",{className:"flex justify-end gap-0.5 pt-1 text-sm",children:[r.jsx("button",{type:"button",className:"rounded border border-slate-600 px-3 py-1 text-slate-200",onClick:o,children:u?"Got it":"Not now"}),!u&&r.jsx("button",{type:"button",className:"rounded bg-cyan-500 px-3 py-1 font-semibold text-black hover:bg-cyan-400",onClick:l,children:"Enter fullscreen"})]})]})})})}function Kw({side:n="right",onClick:s}){const l=n==="left"?"left-2":"right-2";return r.jsx("button",{type:"button",onClick:s,className:`fixed bottom-2 ${l} z-30 flex h-10 w-10 items-center justify-center rounded-full border border-cyan-200/50 bg-slate-900/70 text-cyan-100 shadow-lg backdrop-blur-sm transition hover:bg-slate-800/80 active:scale-95`,"aria-label":"Enter fullscreen",title:"Enter fullscreen",children:r.jsx("svg",{viewBox:"0 0 24 24",width:"18",height:"18","aria-hidden":"true",className:"pointer-events-none",children:r.jsx("path",{d:"M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})})}const Pg="fullscreenPromptDismissed",Zw=new Set(["mobile-portrait","mobile-landscape"]),Wg=()=>{if(typeof window>"u")return null;try{return window.sessionStorage}catch{return null}},Iw=()=>{const n=Wg();if(!n)return!1;try{return n.getItem(Pg)==="1"}catch{return!1}},wx=()=>{const n=Wg();if(n)try{n.setItem(Pg,"1")}catch{}},Jw=()=>{if(typeof navigator>"u")return!1;const n=navigator.userAgent||navigator.platform||"";return/iphone|ipad|ipod/i.test(n)},Cx=()=>{if(typeof document>"u")return!1;const n=document;return!!(n.fullscreenEnabled||n.webkitFullscreenEnabled||n.mozFullScreenEnabled||n.msFullscreenEnabled)};function Pw(n){const s=Zw.has(n),[l,o]=m.useState(!1),[u,f]=m.useState("native"),[p,h]=m.useState(!1),x=m.useMemo(()=>Jw(),[]),g=m.useMemo(()=>Cx(),[]),v=m.useCallback(()=>{if(!s){o(!1),h(!1);return}if(!p&&Iw()){o(!1);return}if(x){f("pwa-hint"),o(!0);return}if(!Cx()){o(!1);return}if(typeof document<"u"&&document.fullscreenElement){o(!1);return}f("native"),o(!0)},[p,x,s]);m.useEffect(()=>{v()},[v]),m.useEffect(()=>{if(typeof document>"u")return;const C=()=>{if(document.fullscreenElement){h(!1),o(!1);return}v()};return document.addEventListener("fullscreenchange",C),document.addEventListener("webkitfullscreenchange",C),()=>{document.removeEventListener("fullscreenchange",C),document.removeEventListener("webkitfullscreenchange",C)}},[v]);const y=m.useCallback(()=>{wx(),h(!1),o(!1)},[]),S=m.useCallback(async()=>{if(!s||x||typeof document>"u")return!1;if(document.fullscreenElement)return!0;const C=document.documentElement;if(!C)return!1;const E=C.requestFullscreen||C.webkitRequestFullscreen||C.mozRequestFullScreen||C.msRequestFullscreen;if(!E)return!1;try{const N=E.call(C);return N&&typeof N.then=="function"&&await N,wx(),h(!1),o(!1),!0}catch(N){return console.warn("Failed to enter fullscreen",N),!1}},[x,s]),M=m.useCallback(()=>{s&&(h(!0),f(x?"pwa-hint":"native"),o(!0))},[x,s]);return m.useEffect(()=>{s||(h(!1),o(!1))},[s]),{visible:l,mode:u,isIOS:x,nativeSupported:g,enterFullscreen:S,dismiss:y,showPrompt:M}}function Ww({visible:n,layout:s,onClose:l,showOnLoad:o,onToggleShowOnLoad:u}){if(!n)return null;const{state:f}=_t(),p=h=>{const x=!h.target.checked;u?.(x)};return r.jsx("div",{className:"fixed inset-0 z-30 flex items-center justify-center bg-black/80 px-0.5 py-0.5",children:r.jsxs("div",{className:"pointer-events-auto surface w-full max-w-4xl max-h-[90vh] overflow-hidden shadow-2xl",children:[r.jsxs("div",{className:"flex items-center justify-between border-b border-slate-700 px-0.5 py-0.25 text-sm text-slate-200",children:[r.jsx("span",{className:"font-semibold",children:"Help & controls"}),r.jsxs("div",{className:"flex items-center gap-0.5 text-[0.8rem] text-slate-300",children:[r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!o,onChange:p,className:"accent-cyan-500"}),r.jsx("span",{children:"Don't show again"})]}),r.jsx("button",{type:"button",onClick:l,className:"button-dark px-2 py-0.25 text-[0.8rem]",children:"Close"})]})]}),r.jsx("div",{className:"max-h-[85vh] overflow-y-auto p-0.5",children:r.jsx(Qg,{layout:s,keymap:f?.keymap||{}})})]})})}const e2=`aroma +`).trim();if(ne){A(!0);try{await h(ne,Z),N(""),y(),S(!1)}catch(ae){alert(ae.message)}finally{A(!1)}}}const ue=l?"flex-1 min-h-0 overflow-y-auto":"h-48 overflow-y-auto";return r.jsxs("section",{className:`panel-section space-y-0.5 text-base ${l?"flex h-full flex-col overflow-hidden":""}`,children:[r.jsxs("div",{className:`surface overflow-y-auto space-y-0.5 px-0 ${ue}`,ref:ee,children:[R.length===0&&L.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"No messages yet."}):R.map(T=>r.jsx(sf,{message:T},T.id)),L.map(T=>r.jsx(pg,{message:T},`typing-${T.typingId||T.id}`))]}),!n&&r.jsxs("form",{className:"flex flex-wrap items-stretch gap-0.5",onSubmit:se,children:[r.jsx("input",{className:"field-input flex-1 min-w-[10rem]",value:E,onChange:T=>{const V=T.target.value;N(V),S(!!V.trim())},onFocus:T=>{g(T),S(!!E.trim())},onBlur:T=>{v(T),S(!1)},onKeyDown:T=>{T.key==="Enter"&&!E.trim()&&(T.preventDefault(),y(),S(!1))},ref:T=>x(T,{target:"panel"}),placeholder:$?"Type a message…":s?"":"Spectators cannot chat",disabled:!$}),U&&r.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 basis-full sm:basis-auto",children:[r.jsxs("label",{className:"flex items-center gap-0.5 text-xs text-slate-300",children:[r.jsx("input",{type:"checkbox",checked:q,onChange:T=>k(T.target.checked),className:"accent-cyan-500"}),r.jsx("span",{children:"Speak"})]}),r.jsxs("select",{value:B,onChange:T=>{const V=T.target.value;Q(V),C(ne=>({...ne||{},engine:V}))},className:"field-input text-xs",children:[r.jsx("option",{value:"flite",children:"flite"}),r.jsx("option",{value:"espeak",children:"espeak"})]}),B==="flite"?r.jsx("select",{value:D,onChange:T=>{const V=T.target.value;H(V),C(ne=>({...ne||{},voice:V}))},className:"field-input text-xs",children:WA.map(T=>r.jsx("option",{value:T,children:T},T))}):r.jsx("select",{value:K,onChange:T=>{const V=Number(T.target.value);G(V),C(ne=>({...ne||{},pitch:V}))},className:"field-input text-xs",children:ew.map(T=>r.jsxs("option",{value:T,children:["pitch ",T]},T))})]}),r.jsx("button",{type:"submit",disabled:!$||w,className:"button-dark h-full disabled:opacity-50 self-stretch",children:w?"...":"Send"})]})]})}function Do({compact:n=!1}){const{session:s,setNickname:l}=nn(),{value:o,save:u}=Ge("profile",{nickname:""}),[f,p]=m.useState(o.nickname||""),[h,x]=m.useState(!1),g=s?.role!=="spectator";m.useEffect(()=>{p(o.nickname||"")},[o.nickname]);async function v(y){if(y.preventDefault(),!g)return;const S=(f||"").trim().slice(0,32);if(S){x(!0);try{await l(S),u({nickname:S})}catch(M){alert(M.message)}finally{x(!1)}}}return r.jsxs("form",{className:"grid w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-stretch gap-0.5",onSubmit:v,children:[r.jsx("input",{className:"field-input flex-1 min-w-0",value:f,onChange:y=>p(y.target.value),maxLength:32,placeholder:"Enter a nickname",disabled:!g}),r.jsx("button",{type:"submit",disabled:!g||h,className:"button-dark h-full shrink-0 whitespace-nowrap px-0.5 py-0 disabled:opacity-50",children:h?"Saving…":n?"Set":"Save"})]})}function tw(n){const s=Array.isArray(n?.socials)?n.socials:null;if(s&&s.length)return s;const l=[];return n?.discord?.invite&&l.push({id:"discord",label:"Discord",url:n.discord.invite}),n?.kofi?.link&&l.push({id:"kofi",label:"Ko-fi",url:n.kofi.link}),l}function nw(n){if(!n||typeof n!="object")return null;const s=n.id||n.service||n.key||n.name||null,l=n.label||n.title||n.name||s||"Link",o=n.url||n.link||n.href||null;return o?{id:s,label:l,url:o}:null}function Zg({className:n=""}){const{session:s}=nn(),l=tw(s).map(nw).filter(Boolean).slice(0,4);return l.length?r.jsx("div",{className:`grid grid-cols-2 grid-rows-2 gap-0.5 ${n}`,children:l.map(o=>r.jsx(hf,{...o},`${o.id||o.label}-${o.url}`))}):null}function aw({compact:n=!1}){return r.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:r.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:r.jsx(Do,{compact:n})})})}function sw(){return r.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:r.jsx(Zg,{className:"flex-1 min-h-0"})})}function yd(...n){return n.filter(Boolean).join(" ")}function lw(n){const s=n?.batteryState?.percentDisplay;return s==null?"--":`${s}%`}function iw(n){return n?.batteryState?n.batteryState.urgentActive?"text-red-400":n.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function rw(n){switch(n){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function ow(n,s){if(!n)return"";const l=n.nickname||n.socketId?.slice(0,6)||"unknown";return n.socketId&&n.socketId===s?`${l} (you)`:l}function _o({title:n="Rovers"}){const s=rt(w=>w.session),{requestControl:l}=kn(),[o,u]=m.useState({}),[f,p]=m.useState(()=>Date.now()),h=m.useMemo(()=>s?.role&&s.role!=="spectator",[s?.role]),x=m.useMemo(()=>s?.role==="admin"||s?.role==="lockdown"||s?.role==="lockdown-admin",[s?.role]),g=s?.roster??[],v=s?.turnQueues??{},y=s?.users??[],S=s?.socketId||null,M=m.useMemo(()=>Object.values(v||{}).some(w=>w?.deadline||w?.idleDeadline),[v]);m.useEffect(()=>{if(!M)return;const w=setInterval(()=>{p(Date.now())},1e3);return()=>clearInterval(w)},[M]);const C=m.useMemo(()=>{const w=new Set(g.map(q=>String(q.id))),A=Object.keys(v||{}).filter(q=>!w.has(String(q))).map(q=>({id:q,name:q,locked:!1,batteryState:null}));return[...g,...A]},[g,v]);async function E(w){if(w){u(A=>({...A,[w]:!0}));try{await l(w)}catch(A){alert(A.message)}finally{u(A=>({...A,[w]:!1}))}}}const N=w=>y.find(A=>A.socketId===w)||{socketId:w,nickname:null,role:null};return r.jsxs("div",{className:"space-y-0.5 text-sm",children:[n&&r.jsx("p",{className:"text-sm text-slate-400",children:n}),C.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"No rovers registered."}):r.jsx("ul",{className:"space-y-0.5 text-sm",children:C.map(w=>{const A=String(w.id),q=v?.[A]||null,k=q?.queue||[],B=q?.idleDeadline||q?.deadline||null,Q=B&&B>f?Math.ceil((B-f)/1e3):B?0:null,D=q?.current||null,H=D?k.findIndex(se=>se===D):-1,K=k.length>1?H>=0?k[(H+1)%k.length]:k[0]:null,G=!!(S&&D&&D===S),$=!!(S&&K&&K===S),ee=Q!=null&&(G||$),oe=!!w.locked,ce=oe&&!x,U=!!(w?.private?.enabled&&w?.private?.open),R=w.lockReason?`locked: ${w.lockReason}`:"locked",L=oe?R:o[A]?"...":"request",Z=h&&!ce&&!o[A];return r.jsxs("li",{className:yd("surface flex flex-wrap items-start justify-between gap-0.5",Z&&"cursor-pointer",oe?"bg-red-900/40":U?"bg-amber-700/35 border border-amber-200/30":null),onClick:()=>{Z&&E(w.id)},children:[r.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsxs("div",{className:"flex items-center gap-0.5",children:[r.jsx("p",{className:"text-slate-200",children:r.jsx("span",{className:"rounded px-1 py-[1px] border border-transparent",style:oa(w.color,.16),children:w.name})}),ee?r.jsx("span",{className:"rounded bg-slate-800 px-1 text-[0.7rem] text-slate-200",children:G?`${Q}s left`:`Your turn in ${Q}s`}):null]}),r.jsx("span",{className:yd("text-[0.75rem] font-semibold",iw(w)),children:lw(w)})]}),k.length===0?r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"No queue."}):r.jsx("div",{className:"flex flex-wrap items-center gap-0.5",children:k.map((se,ue)=>{const T=N(se),V=se===D,ne=!!(K&&se===K&&!V),ae=V?"bg-sky-600 text-white ring-2 ring-amber-300":ne?"bg-emerald-700/60 text-emerald-100 ring-1 ring-emerald-300/70":"bg-slate-800 text-slate-200";return r.jsxs("span",{className:`flex items-center gap-0.5 rounded px-1 text-[0.7rem] ${ae}`,children:[r.jsx("span",{className:`${rw(T.role)} font-semibold`,children:ow(T,S)}),V&&r.jsx("span",{className:"text-[0.65rem] text-slate-200",children:"now"}),ne&&r.jsx("span",{className:"text-[0.65rem] text-emerald-100",children:"next"})]},`${A}-${se}-${ue}`)})})]}),h?r.jsx("button",{type:"button",onClick:se=>{se.stopPropagation(),E(w.id)},disabled:o[A]||ce,className:yd("button-dark disabled:opacity-40",oe&&"bg-red-600/70 text-white hover:bg-red-600"),children:L}):null]},w.id)})})]})}function cw(n){switch(n){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function uw(n,s){if(!n)return"";const l=n.nickname||n.socketId?.slice(0,6)||"unknown";return n.socketId&&n.socketId===s?`${l} (you)`:l}function jo({hideNicknameForm:n=!1,hideHeader:s=!1,className:l="",fillHeight:o=!1,compact:u=!1}){const{session:f}=nn(),p=f?.role!=="spectator",h=f?.users??[],x=f?.socketId||null,g=m.useMemo(()=>[...h].sort((y,S)=>y.socketId===x?-1:S.socketId===x?1:(y.nickname||"").localeCompare(S.nickname||"")),[x,h]),v=o?"flex-1 min-h-0 overflow-y-auto":u?"h-28 overflow-y-auto":"h-48 overflow-y-auto";return r.jsxs("section",{className:`panel-section space-y-0.5 text-base ${o?"flex h-full min-h-0 flex-col overflow-hidden":""} ${l}`,children:[!n&&r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"grid gap-0.5 md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]",children:[r.jsx("div",{className:"min-w-0",children:r.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:r.jsx(Do,{compact:u})})}),r.jsx(Zg,{})]}),!p&&r.jsx("p",{className:"text-xs text-slate-500",children:"Spectators cannot set nicknames."})]}),r.jsxs("div",{className:`space-y-0.5 ${o?"flex flex-1 min-h-0 flex-col":""}`,children:[!s&&r.jsxs("div",{className:`flex items-center justify-between text-sm text-slate-400 ${u?"text-xs":""}`,children:[r.jsx("span",{children:"Users"}),r.jsx("span",{className:"text-xs text-slate-500",children:g.length})]}),r.jsx("div",{className:`surface flex flex-wrap content-start items-start gap-0.5 px-0 pb-0 ${v} ${u?"text-[0.8rem]":""}`,children:g.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"Waiting for users…"}):g.map(y=>r.jsx("span",{className:`rounded bg-slate-800/80 px-1 py-0.25 text-[0.7rem] ${cw(y.role)}`,children:uw(y,x)},y.socketId))})]})]})}const dw=/^cu_[a-f0-9]{32}$/,wi="field-input w-full max-w-sm text-left focus:ring-emerald-500",Bo="mx-auto w-full max-w-xl flex justify-center",xi="mx-auto flex w-full max-w-md flex-col items-center space-y-0.5 text-center";function fw(n){const s=String(n||"").trim();return s?s.length<=10?`${s.slice(0,2)}***${s.slice(-2)}`:`${s.slice(0,6)}...${s.slice(-6)}`:""}const Sx=8*1024*1024,ss=16e3,mw={iceServers:[{urls:"stun:stun.l.google.com:19302"}],bundlePolicy:"max-bundle",rtcpMuxPolicy:"require"};function jx(n){let s="";for(let o=0;o{if(!n||n.iceGatheringState==="complete"){l();return}const o=setTimeout(()=>{n.removeEventListener("icegatheringstatechange",u),l()},s);function u(){n.iceGatheringState==="complete"&&(clearTimeout(o),n.removeEventListener("icegatheringstatechange",u),l())}n.addEventListener("icegatheringstatechange",u)})}function Nx(n){if(!n)return!1;const s=n.connectionState,l=n.iceConnectionState;return s==="connected"||l==="connected"||l==="completed"}function gw(n,s=1e4){return new Promise((l,o)=>{if(!n){o(new Error("Peer connection missing"));return}if(Nx(n)){l();return}const u=setTimeout(()=>{p(),o(new Error("Peer connection timeout"))},s),f=()=>{Nx(n)?(p(),l()):(n.connectionState==="failed"||n.connectionState==="closed"||n.iceConnectionState==="failed")&&(p(),o(new Error(`Peer connection ${n.connectionState||n.iceConnectionState}`)))};function p(){clearTimeout(u),n.removeEventListener("connectionstatechange",f),n.removeEventListener("iceconnectionstatechange",f)}n.addEventListener("connectionstatechange",f),n.addEventListener("iceconnectionstatechange",f)})}async function vw(n){if(!n?.getParameters||!n?.setParameters)return;const s=n.getParameters()||{},l=s.encodings&&s.encodings[0]||{};s.encodings=[{...l,maxBitrate:64e3,dtx:"disabled"}];try{await n.setParameters(s)}catch{}}function bw(n,s=6e3){return new Promise((l,o)=>{if(!n){o(new Error("Peer connection missing"));return}const u=Date.now();let f=-1;const p=setInterval(async()=>{if(Date.now()-u>s){clearInterval(p),o(new Error("WHIP connected but no outbound audio flow"));return}try{const h=n.getSenders().filter(x=>x.track?.kind==="audio");for(const x of h){const g=await x.getStats();for(const v of g.values()){if(v.type!=="outbound-rtp"||v.kind!=="audio")continue;const y=Number(v.bytesSent||0),S=Number(v.packetsSent||0);if(f<0)f=y;else if(y>f+200||S>5){clearInterval(p),l();return}}}}catch{}},250)})}function yw(n){const s=n.reduce((u,f)=>u+f.length,0),l=new Float32Array(s);let o=0;for(const u of n)l.set(u,o),o+=u.length;return l}function Sw(n){const s=new Int16Array(n.length);for(let l=0;l{for(let v=0;vv&&s?.[v]||null,[s,v]),ne=!!(V&&V.state!=="offline"&&!V.error),ae=V?.source==="upload"&&V?.state==="playing",he=V?.source==="mic-whip"&&(V?.state==="starting"||V?.state==="playing"),re=!!(!w&&K.current&&(E||T)),de=!w&&(A==="live"||A==="starting"),ge=w&&k==="recording",ve=w&&k==="sending",pe=Cn(h?.keymap?.micPtt?.[0])||"M",Oe=m.useCallback(J=>{const xe=J==="clip"?"clip":"live";g(Ae=>({...Ae||{},pttMode:xe}))},[g]),Me=async()=>{if(!v){D("Take control of your rover first.");return}if(!y){D("Select an audio file first.");return}if(y.size>Sx){D(`File too large (max ${Sx} bytes).`);return}C(!0),D("");try{const J=await y.arrayBuffer(),xe=jx(new Uint8Array(J));await l?.({roverId:v,name:y.name,mime:y.type||"",dataBase64:xe}),D("Upload playback started.")}catch(J){D(J?.message||"Failed to play upload.")}finally{C(!1)}},ye=async()=>{if(!v){D("Take control of your rover first.");return}C(!0),D("");try{await o?.(v),D("Upload playback stopped.")}catch(J){D(J?.message||"Failed to stop upload.")}finally{C(!1)}},Ye=m.useCallback(async J=>{const xe=String(J||ee.current||"").trim();if($.current=!1,q("idle"),G.current){try{G.current.getSenders().forEach(Ae=>Ae.track?.stop())}catch{}try{G.current.close()}catch{}}if(G.current=null,H.current)try{H.current.getTracks().forEach(Ae=>Ae.stop())}catch{}if(H.current=null,K.current=null,xe)try{await p?.(xe)}catch{}ee.current=""},[p]),ze=m.useCallback(async J=>{const xe=await u?.(J),Ae=String(xe?.whipUrl||"").trim(),ht=String(xe?.token||"").trim();if(!Ae||!ht)throw new Error("WHIP endpoint unavailable");const Le=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:ss,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}});H.current=Le;const ot=Le.getAudioTracks()?.[0];if(K.current=ot||null,ot&&(ot.enabled=!!(E||T)),ot?.applyConstraints)try{await ot.applyConstraints({channelCount:1,sampleRate:ss,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1})}catch{}const ct=new RTCPeerConnection(mw);G.current=ct,ct.onconnectionstatechange=()=>{if(!$.current)return;const Lt=ct.connectionState;if(Lt==="connected"){q("live");return}(Lt==="failed"||Lt==="disconnected"||Lt==="closed")&&(q("error"),D(`WHIP transport ${Lt}.`))},Le.getAudioTracks().forEach(Lt=>{const dl=ct.addTrack(Lt,Le);vw(dl)});const gt=await ct.createOffer({offerToReceiveAudio:!1,offerToReceiveVideo:!1});await ct.setLocalDescription(gt),await xw(ct,1800);const kt=await fetch(Ae,{method:"POST",headers:{"Content-Type":"application/sdp",...pw(ht)},body:ct.localDescription?.sdp||gt.sdp});if(!kt.ok)throw new Error(`WHIP request failed: ${kt.status}`);const us=await kt.text();await ct.setRemoteDescription({type:"answer",sdp:us}),await gw(ct,1e4),await bw(ct,6e3),await f?.(J)},[E,T,f,u]),at=m.useCallback(async()=>{if(se.current=!1,R.current){try{R.current.disconnect()}catch{}R.current.onaudioprocess=null}if(U.current)try{U.current.disconnect()}catch{}if(L.current)try{L.current.disconnect()}catch{}if(oe.current)try{oe.current.getTracks().forEach(J=>J.stop())}catch{}if(ce.current)try{await ce.current.close()}catch{}Z.current=[],R.current=null,U.current=null,L.current=null,oe.current=null,ce.current=null,ue.current=ss},[]),ie=m.useCallback(async()=>{if(oe.current&&ce.current&&R.current)return;B(ot=>ot==="recording"?ot:"arming");const J=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:ss,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}}),xe=new AudioContext({latencyHint:"interactive"}),Ae=xe.createMediaStreamSource(J),ht=xe.createScriptProcessor(1024,1,1),Le=xe.createGain();Le.gain.value=0,ht.onaudioprocess=ot=>{if(!se.current)return;const ct=ot.inputBuffer.getChannelData(0);Z.current.push(new Float32Array(ct))},Ae.connect(ht),ht.connect(Le),Le.connect(xe.destination),oe.current=J,ce.current=xe,U.current=Ae,R.current=ht,L.current=Le,ue.current=xe.sampleRate||ss},[]),fe=m.useCallback(async({send:J})=>{const xe=se.current;if(se.current=!1,!xe){J||B("idle");return}const Ae=Z.current;if(Z.current=[],!J){B("idle");return}if(!v){B("idle");return}if(!Ae.length){B("idle");return}B("sending");try{const ht=yw(Ae),Le=jw(ht,ue.current||ss),ot=jx(Le);await l?.({roverId:v,name:`ptt-${Date.now()}.wav`,mime:"audio/wav",dataBase64:ot}),B("idle")}catch(ht){B("error"),D(ht?.message||"Failed to send PTT clip.")}},[l,v]),le=m.useCallback(async()=>{if(!v){D("Take control of your rover first.");return}await ie(),Z.current=[],se.current=!0,B("recording")},[ie,v]);return m.useEffect(()=>{let J=!1;async function xe(){if(w){await Ye(ee.current||v);return}if(!v){await Ye(ee.current);return}if(!($.current&&ee.current===v)&&!(!E&&!T))try{q("starting"),D(""),$.current=!0,ee.current=v,await ze(v),J||q("live")}catch(Ae){J||(q("error"),D(Ae?.message||"Failed to start mic forwarding.")),await Ye(v)}}return xe(),()=>{J=!0}},[w,E,T,v,ze,Ye]),m.useEffect(()=>{const J=K.current;J&&(J.enabled=!!(!w&&(E||T)))},[w,E,T]),m.useEffect(()=>{let J=!1;async function xe(){if(!w||!v){se.current&&await fe({send:!1}),B("idle");return}try{await ie(),!J&&!se.current&&B("idle")}catch(Ae){J||(B("error"),D(Ae?.message||"Microphone access failed for clip mode."))}}return xe(),()=>{J=!0}},[w,ie,fe,v]),m.useEffect(()=>{let J=!1;async function xe(){if(w){if(!v){se.current&&await fe({send:!1});return}if(T){if(!se.current)try{D(""),await le()}catch(Ae){J||(B("error"),D(Ae?.message||"Failed to start clip recording."))}}else se.current&&await fe({send:!0})}}return xe(),()=>{J=!0}},[w,fe,T,v,le]),m.useEffect(()=>()=>{Ye(ee.current||v),at()},[v,Ye,at]),r.jsx("section",{className:"surface",children:r.jsxs("div",{className:"grid gap-1",children:[r.jsx("p",{className:"text-sm text-slate-100 text-center",children:"Audio Controls"}),r.jsx("section",{className:"surface",children:r.jsxs("div",{className:"flex items-center justify-center gap-0.5 py-0.25 text-xs text-slate-300",children:[r.jsx("span",{children:"Push-to-Talk Key"}),r.jsx(Ax,{label:pe})]})}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-1 lg:grid-cols-2",children:[r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"PTT Mode"}),r.jsx("p",{className:"text-xs text-slate-400 text-center",children:"Choose how holding your PTT key behaves: live mic stream, or record-and-send clip. If the live stream doesn't work, switch to clip mode."}),r.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[r.jsx("button",{type:"button",className:`button-dark w-full text-sm ${w?"":"bg-emerald-500 text-white hover:bg-emerald-500"}`,onClick:()=>Oe("live"),children:"Live Mic (WHIP)"}),r.jsx("button",{type:"button",className:`button-dark w-full text-sm ${w?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,onClick:()=>Oe("clip"),children:"Record Clip"})]})]})}),r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_1fr]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"PTT Clip Status"}),r.jsxs("div",{className:"grid gap-0.5 content-start",children:[r.jsx(vn,{label:"Mode",active:w,detail:w?"record clip":"live mic"}),r.jsx(vn,{label:"PTT hold",active:T,detail:T?"held":"released"}),r.jsx(vn,{label:"Recording",active:ge,detail:ge?"capturing":"idle"}),r.jsx(vn,{label:"Playback",active:w&&ae,detail:w&&ae?"playing":"idle"})]})]})})]}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-1 lg:grid-cols-2",children:[r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr_auto]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Audio Upload"}),r.jsxs("label",{className:"mx-auto grid w-full max-w-sm gap-0.5 text-xs text-slate-300 text-center",children:[r.jsx("span",{children:"Audio file (mp3 / wav / ogg)"}),r.jsx("input",{className:`${wi} text-center`,type:"file",accept:".mp3,.wav,.ogg,audio/mpeg,audio/wav,audio/ogg",disabled:M||!v,onChange:J=>S(J.target.files?.[0]||null)})]}),r.jsx("div",{className:"mx-auto w-full max-w-sm text-center",children:y?r.jsxs("div",{className:"surface-muted text-xs text-slate-300",children:[y.name," (",y.size," bytes)"]}):r.jsx("div",{className:"surface-muted text-xs text-slate-500",children:"No file selected"})}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:M||!v,onClick:Me,children:M?"Working...":"Play Upload"}),r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:M||!v,onClick:ye,children:"Stop"})]})]})}),r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Live Microphone"}),r.jsxs("label",{className:"surface-muted mx-auto flex w-full max-w-sm items-center justify-center gap-0.5 px-0.5 py-0.5 text-xs text-slate-300",children:[r.jsx("input",{type:"checkbox",checked:E,disabled:!v||w,onChange:J=>g(xe=>({...xe||{},openMicEnabled:!!J.target.checked}))}),r.jsx("span",{children:w?"Open mic (live mode only)":"Open mic"})]}),r.jsxs("div",{className:"grid gap-0.5 content-start",children:[r.jsx(vn,{label:"WHIP link",active:de,detail:A}),r.jsx(vn,{label:"Mic hot",active:re,detail:re?"transmitting":"muted"}),r.jsxs("div",{className:"surface-muted text-center text-xs text-slate-400",children:["Hold ",r.jsx(Ax,{label:pe})," to talk"]})]})]})})]}),r.jsx("section",{className:"surface",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Audio Status"}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-6",children:[r.jsx(vn,{label:"Forward pipe",active:ne,detail:ne?"connected":"offline"}),r.jsx(vn,{label:"Upload playback",active:ae,detail:ae?"playing":"idle"}),r.jsx(vn,{label:"Mic relay",active:he,detail:he?"active":"idle"}),r.jsx(vn,{label:"WHIP transport",active:de,detail:A}),r.jsx(vn,{label:"Clip recording",active:ge,detail:ge?"recording":"idle"}),r.jsx(vn,{label:"Clip sending",active:ve,detail:ve?"sending":"idle"})]}),Q?r.jsx("div",{className:"text-xs text-slate-400 text-center",children:Q}):null]})})]})})}function Aw({pendingRequestId:n,currentStoredKey:s,nickname:l,requestVerification:o,applyIdentityKey:u,onMessage:f,fullWidth:p=!1}){const[h,x]=m.useState(0),[g,v]=m.useState(""),[y,S]=m.useState(!1),[M,C]=m.useState(!1),E=()=>{x(1),v(s),S(!1),f?.("")},N=()=>{x(0),S(!1)},w=async q=>{if(q.preventDefault(),!y){f?.("Please confirm your nickname agreement before sending.");return}if(h<3){f?.("Complete all request steps before sending.");return}C(!0),f?.("");try{const k=await u(g);await o?.(),v(k),x(0),S(!1),f?.("Verification request sent to lockdown admins.")}catch(k){f?.(k.message||"Failed to submit request.")}finally{C(!1)}},A=p?"w-full":Bo;return n?r.jsx("section",{className:`surface text-sm text-slate-300 ${A}`,children:r.jsxs("div",{className:xi,children:["Verification request pending: ",n]})}):h===0?r.jsx("section",{className:`surface ${A}`,children:r.jsxs("div",{className:xi,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Verification"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:E,disabled:M,children:"Request Verification"})]})}):r.jsx("form",{className:`surface ${A}`,onSubmit:w,children:r.jsxs("div",{className:xi,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Request verification"}),r.jsxs("p",{className:"text-xs text-slate-500",children:["Step ",h," of 3"]}),h===1?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Confirm your nickname, which is used as part of the verification process. You can edit it here before requesting."}),r.jsx("div",{className:"mx-auto w-full max-w-sm",children:r.jsx(Do,{compact:!0})}),r.jsxs("div",{className:"surface-muted mx-auto w-full max-w-sm text-xs text-slate-300 text-center",children:["Current nickname: ",r.jsx("span",{className:"font-semibold",children:l||"(not set)"})]}),r.jsxs("label",{className:"flex items-center justify-center gap-0.5 text-xs text-slate-300 text-center",children:[r.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:y,onChange:q=>S(q.target.checked)}),r.jsx("span",{children:"I understand this nickname is tied to my verification."})]}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!l||!y,onClick:()=>x(2),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:N,children:"Cancel"})]})]}):null,h===2?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Save your identity key in a safe place. You can use it to restore your identity in another browser."}),r.jsx("input",{className:wi,type:"password",name:"identity_key_request",autoComplete:"current-password",maxLength:35,value:g,onChange:q=>v(q.target.value.toLowerCase()),placeholder:"Identity key for this request"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!g,onClick:()=>navigator.clipboard?.writeText(g),children:"Copy Key"}),r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!g,onClick:()=>x(3),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>x(1),children:"Back"})]})]}):null,h===3?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Final step: confirm and send your verification request."}),r.jsx("input",{className:wi,type:"password",name:"identity_key_request_final",autoComplete:"current-password",maxLength:35,value:g,onChange:q=>v(q.target.value.toLowerCase()),placeholder:"Identity key for this request"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:M||!g,children:"Confirm Request"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>x(2),children:"Back"})]})]}):null]})})}function ww({currentStoredKey:n,applyIdentityKey:s,onMessage:l,fullWidth:o=!1}){const[u,f]=m.useState(0),[p,h]=m.useState(""),[x,g]=m.useState(!1),v=()=>{f(1),h(""),l?.("")},y=()=>{f(0),h("")},S=async C=>{if(C.preventDefault(),!(u<2)){g(!0),l?.("");try{await s(p),f(0),h(""),l?.("Identity key restored.")}catch(E){l?.(E.message||"Failed to restore key.")}finally{g(!1)}}},M=o?"w-full":Bo;return r.jsx("section",{className:`surface ${M}`,children:r.jsxs("div",{className:xi,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Identity key"}),r.jsxs("p",{className:"text-xs text-slate-500",children:["Current: ",fw(n)||"not set yet"]}),r.jsx("input",{className:wi,type:"password",name:"identity_key_current",autoComplete:"current-password",maxLength:35,value:n,readOnly:!0,placeholder:"Identity key"}),u===0?r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!n,onClick:()=>navigator.clipboard?.writeText(n),children:"Copy Key"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:v,disabled:x,children:"Restore Key"})]}):null,u===1?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Restoring your key should only be done when needed. Use this to move your identity to another browser."}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(2),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:y,children:"Cancel"})]})]}):null,u===2?r.jsxs("form",{className:"space-y-0.5",onSubmit:S,children:[r.jsx("input",{className:wi,type:"password",name:"identity_key_restore",autoComplete:"current-password",maxLength:35,value:p,onChange:C=>h(C.target.value.toLowerCase()),placeholder:"Paste key to restore"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:x||!p,children:"Confirm Restore"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(1),children:"Back"})]})]}):null]})})}function Cw({requestableRovers:n=[],pendingRequests:s=[],requestPrivateRoverAccess:l,onMessage:o,fullWidth:u=!1}){const[f,p]=m.useState({}),h=m.useMemo(()=>{const v=new Map;return(s||[]).forEach(y=>{y?.roverId&&v.set(String(y.roverId),y)}),v},[s]),x=u?"w-full":Bo,g=async v=>{if(v){p(y=>({...y,[v]:!0})),o?.("");try{(await l?.(v))?.existing?o?.("You already have a pending request for that rover."):o?.("Private rover access request sent to lockdown admins.")}catch(y){o?.(y.message||"Failed to send request.")}finally{p(y=>({...y,[v]:!1}))}}};return r.jsx("section",{className:`surface text-sm text-slate-300 ${x}`,children:r.jsxs("div",{className:xi,children:[r.jsx("p",{className:"text-sm text-slate-300",children:"Private rover access requests"}),n.length===0?r.jsx("p",{className:"text-xs text-slate-500",children:"No closed private rovers are available to request right now."}):r.jsx("div",{className:"w-full space-y-0.5",children:n.map(v=>{const y=String(v.id),S=!!f[y],M=h.get(y);return r.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-1 py-0.5",children:[r.jsxs("div",{className:"min-w-0 text-left",children:[r.jsx("p",{className:"truncate text-xs font-semibold text-slate-100",children:v.name||y}),r.jsx("p",{className:"truncate text-[0.7rem] text-slate-500",children:y})]}),M?r.jsx("span",{className:"rounded bg-amber-700/30 px-1 py-0.5 text-[0.7rem] text-amber-200",children:"Pending"}):r.jsx("button",{type:"button",onClick:()=>g(y),disabled:S,className:"button-dark text-xs disabled:opacity-50",children:S?"Sending...":"Request"})]},y)})})]})})}function tl(n){return String(n||"").trim()}function Ew(n){const s=tl(n);return s?s.includes("DOCKINGRUNNING")?"Returning":s.includes("PAUSED")?"Paused":s.includes("HOUSECLEANINGRUNNING")?"Cleaning":s.includes("SPOTCLEANINGRUNNING")?"Spot cleaning":s.includes("STATE_START")?"Starting":s.includes("STATE_IDLE")||s.includes("STATE_STANDBY")?"Idle":s:"--"}function Ig(n){return n==="good"?"bg-emerald-600 text-white":n==="warn"?"bg-amber-500 text-slate-900":n==="danger"?"bg-rose-600 text-white":"bg-slate-700 text-slate-100"}function to({label:n,value:s,tone:l="muted",valueClass:o="",hideLabel:u=!1}){return r.jsxs("div",{className:`rounded-md px-1 py-0.75 text-center ${Ig(l)}`,children:[u?null:r.jsx("div",{className:"text-[0.72rem] opacity-90",children:n}),r.jsx("div",{className:`text-sm font-semibold ${o}`,title:String(s||""),children:s})]})}function kw(n=[]){return n.filter(u=>u&&u.valid&&Number.isFinite(u.angleDeg)&&Number.isFinite(u.distanceMm)).map(u=>{const f=(Number(u.angleDeg)-90)*Math.PI/180,h=Math.max(0,Math.min(1,Number(u.distanceMm)/4e3))*92,x=110+Math.cos(f)*h,g=110+Math.sin(f)*h;return`${x},${g}`}).join(" ")}function Mw({neato:n,lidar:s,onStart:l,onSendHome:o,onLocate:u,onClearErrors:f,onPowerCycle:p,fullWidth:h=!1}){const[x,g]=m.useState(""),[v,y]=m.useState(!1),[S,M]=m.useState(!1),C=h?"w-full":"w-full max-w-xl",E=!!n?.configured,N=!!(n?.enabled&&n?.connected),w=!!n?.telemetry?.extPowerPresent,A=!!n?.telemetry?.chargingActive,q=Ew(n?.telemetry?.uiState),k=tl(n?.telemetry?.robotState)||"--",B=n?.telemetry?.batteryPercent,Q=Number.isFinite(B)?`${B}%`:"--",D=n?.telemetry?.batteryVoltage,H=Number.isFinite(D)?`${D.toFixed(2)} V`:"--",K=tl(n?.telemetry?.robotError)||"--",G=tl(n?.telemetry?.robotAlert)||"--",$=Array.isArray(s?.points)?s.points:[],ee=kw($),oe=tl(s?.status)||"--",ce=s?.debug&&typeof s.debug=="object"?s.debug:null,U=tl(ce?.reason)||"--",R=ce?.stats&&typeof ce.stats=="object"?ce.stats:null,L=n?.controls||{},Z=!!L?.start?.available,se=!!L?.sendHome?.available,ue=!!L?.locate?.available,T=!!L?.clearErrors?.available,V=!!L?.powerCycle?.available,ne=E&&N&&Z,ae=E&&N&&se,he=E&&N&&ue,re=E&&N&&T,de=E&&N&&V,ge=async(ye,Ye)=>{if(Ye){g(ye);try{await Ye()}catch{}finally{g("")}}},ve=E?N?"Online":"Offline":"Not configured",pe=E?N?"good":"danger":"warn",Oe=B==null?"muted":B>=60?"good":B>=25?"warn":"danger",Me=w?"Docked":q!=="--"?q:"Away from dock";return m.useEffect(()=>{if(!s||!Array.isArray(s.points))return;y(!0);const ye=setTimeout(()=>{y(!1)},120);return()=>clearTimeout(ye)},[s]),r.jsx("section",{className:`surface text-sm text-slate-200 ${C}`,children:r.jsxs("div",{className:"grid gap-0.5",children:[r.jsxs("div",{className:"relative flex items-center justify-center min-h-[1.5rem]",children:[r.jsxs("div",{className:"text-center",children:[r.jsx("p",{className:"text-sm text-slate-100",children:"Neato Controls"}),r.jsx("p",{className:"text-xs text-slate-400",children:"Control the autonomous Neato robovac. Be nice to him."})]}),r.jsx("span",{className:`absolute right-0 inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${Ig(pe)}`,children:ve})]}),r.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)] gap-0.5",children:[r.jsxs("div",{className:"surface-muted grid gap-0.5",children:[r.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Controls"}),r.jsx("button",{type:"button",disabled:!ne||!!x,onClick:()=>ge("start",l),className:"rounded-md border border-sky-300 bg-emerald-600 px-1 py-1 text-base font-semibold text-white transition hover:border-sky-500 hover:bg-emerald-500 disabled:opacity-50",children:x==="start"?"Starting...":"Start cleaning"}),r.jsx("button",{type:"button",disabled:!ae||!!x,onClick:()=>ge("sendHome",o),className:"rounded-md border border-sky-300 bg-sky-600 px-1 py-1 text-base font-semibold text-white transition hover:border-sky-500 hover:bg-sky-500 disabled:opacity-50",children:x==="sendHome"?"Sending...":"Send to dock"}),r.jsxs("div",{className:"grid grid-cols-3 gap-0.5",children:[r.jsx("button",{type:"button",disabled:!he||!!x,onClick:()=>ge("locate",u),className:"w-full rounded-md border border-sky-300 bg-fuchsia-600 px-1 py-0.5 text-xs font-semibold text-white transition hover:border-sky-500 hover:bg-fuchsia-500 disabled:opacity-50",children:x==="locate"?"Playing...":"Play sound"}),r.jsx("button",{type:"button",disabled:!re||!!x,onClick:()=>ge("clearErrors",f),className:"w-full rounded-md border border-sky-300 bg-amber-500 px-1 py-0.5 text-xs font-semibold text-slate-900 transition hover:border-sky-500 hover:bg-amber-400 disabled:opacity-50",children:x==="clearErrors"?"Clearing...":"Clear errors"}),r.jsx("button",{type:"button",disabled:!de||!!x,onClick:()=>ge("powerCycle",p),className:"w-full rounded-md border border-rose-300 bg-rose-600 px-1 py-0.5 text-xs font-semibold text-white transition hover:border-rose-500 hover:bg-rose-500 disabled:opacity-50",children:x==="powerCycle"?"Cycling...":"Power cycle"})]}),r.jsxs("div",{className:"surface-muted grid gap-0.5 pt-0.25",children:[r.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Power status"}),r.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[r.jsx(to,{label:"Battery",value:Q,tone:Oe}),r.jsx(to,{label:"Voltage",value:H,tone:"muted"}),r.jsx(to,{label:"Docked",value:w?"Docked":"Not docked",tone:w?"good":"muted",hideLabel:!0}),r.jsx(to,{label:"Charging",value:A?"charging":"not charging",tone:A?"good":"muted",hideLabel:!0})]})]})]}),r.jsx("div",{className:"grid gap-0.5",children:r.jsxs("div",{className:"surface-muted grid gap-0.5",children:[r.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Robot Status"}),r.jsxs("div",{className:"grid grid-cols-1 xl:grid-cols-[minmax(0,1fr)_minmax(13rem,15rem)] gap-0.5 items-start",children:[r.jsxs("div",{className:"grid gap-0.5",children:[r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot state (raw)"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:k})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Basic state"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:Me})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"UI state"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:q})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot error"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:K})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot alert"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:G})]})]}),r.jsxs("div",{className:`rounded-md px-1 py-0.5 ${v?"bg-emerald-900":"bg-slate-800"}`,children:[r.jsx("button",{type:"button",onClick:()=>M(ye=>!ye),className:"w-full text-left text-[0.72rem] text-slate-300",children:"Lidar"}),r.jsx("div",{className:"mt-0.25 text-center text-[0.68rem] text-slate-400",children:"Front of robot"}),r.jsx("div",{className:"mt-0.25 aspect-square rounded-md bg-slate-900 p-0.25",children:ee?r.jsxs("svg",{viewBox:"0 0 220 220",className:"h-full w-full",children:[r.jsx("rect",{x:"0",y:"0",width:"220",height:"220",fill:"#0f172a"}),r.jsx("circle",{cx:"110",cy:"110",r:"23",fill:"none",stroke:"#1e293b",strokeWidth:"1"}),r.jsx("circle",{cx:"110",cy:"110",r:"46",fill:"none",stroke:"#334155",strokeWidth:"1"}),r.jsx("circle",{cx:"110",cy:"110",r:"69",fill:"none",stroke:"#334155",strokeWidth:"1"}),r.jsx("circle",{cx:"110",cy:"110",r:"92",fill:"none",stroke:"#475569",strokeWidth:"1"}),r.jsx("line",{x1:"110",y1:"18",x2:"110",y2:"202",stroke:"#334155",strokeWidth:"1"}),r.jsx("line",{x1:"18",y1:"110",x2:"202",y2:"110",stroke:"#334155",strokeWidth:"1"}),r.jsx("circle",{cx:"110",cy:"110",r:"4",fill:"#e2e8f0"}),$.filter(ye=>ye&&ye.valid&&Number.isFinite(ye.angleDeg)&&Number.isFinite(ye.distanceMm)).map(ye=>{const Ye=(Number(ye.angleDeg)-90)*Math.PI/180,at=Math.max(0,Math.min(1,Number(ye.distanceMm)/4e3))*92,ie=110+Math.cos(Ye)*at,fe=110+Math.sin(Ye)*at;return r.jsx("circle",{cx:ie,cy:fe,r:"1.6",fill:"#38bdf8"},`${ye.angleDeg}-${ye.distanceMm}-${ye.intensity}`)})]}):r.jsx("div",{className:"flex h-full items-center justify-center text-center text-xs text-slate-400",children:"Waiting for scan"})}),S?r.jsxs("div",{className:"mt-0.5 rounded-md bg-slate-900 px-0.75 py-0.5 font-mono text-[0.68rem] text-slate-300",children:[r.jsxs("div",{children:["Status: ",oe]}),r.jsxs("div",{children:["Reason: ",U]}),r.jsxs("div",{children:["Points: ",$.length]}),r.jsxs("div",{children:["Rotation: ",Number.isFinite(s?.rotationSpeed)?s.rotationSpeed.toFixed(2):"--"]}),r.jsxs("div",{children:["Request in flight: ",ce?.requestInFlight?"yes":"no"]}),r.jsxs("div",{children:["Started at: ",ce?.requestStartedAt||"--"]}),r.jsxs("div",{children:["Headers: ",R?.headersSeen??"--"]}),r.jsxs("div",{children:["Rotations: ",R?.rotationsSeen??"--"]}),r.jsxs("div",{children:["Scans ok: ",R?.scansOk??"--"]}),r.jsxs("div",{children:["Timeouts: ",R?.scansTimedOut??"--"]}),r.jsxs("div",{children:["Restarts: ",R?.scansRestarted??"--"]}),r.jsxs("div",{children:["Rotation no scan: ",R?.rotationsWithoutScan??"--"]}),r.jsxs("div",{children:["Parseable payloads: ",R?.parseablePayloads??"--"]}),r.jsxs("div",{children:["Point payloads: ",R?.pointPayloads??"--"]})]}):null]})]})]})})]}),!E||!N||!Z||!se||!ue||!T||!V?r.jsx("p",{className:"text-xs text-slate-400 text-center",children:E?N?"Waiting for required Neato entities in Home Assistant.":"Home Assistant is offline.":"Set homeAssistant.neato.device in server config to enable Neato controls."}):null]})})}function Rw(n){return n==="good"?"bg-emerald-600 text-white":n==="warn"?"bg-amber-500 text-slate-900":n==="danger"?"bg-rose-600 text-white":"bg-slate-700 text-slate-100"}function qw(n){return n==="up"?"Up":n==="down"?"Down":n==="stopped"?"Transitioning...":n==="conflict"?"Conflict":"--"}function Tw({lift:n,onUp:s,onDown:l,fullWidth:o=!1}){const[u,f]=m.useState(""),[p,h]=m.useState(()=>Date.now()),x=o?"w-full":"w-full max-w-xl",g=!!n?.configured,v=!!(n?.enabled&&n?.connected),y=!!n?.busy,S=String(n?.target||"").toLowerCase(),M=String(n?.position||"").toLowerCase(),C=!!n?.availability?.upSwitch,E=!!n?.availability?.downSwitch,N=Number(n?.lastActionAt||0),w=Math.max(0,Number(n?.commandCooldownMs||0)),A=Math.max(0,N+w-p),q=!y&&A>0,k=y||q;m.useEffect(()=>{if(!k)return;const G=setInterval(()=>{h(Date.now())},100);return()=>clearInterval(G)},[k]),m.useEffect(()=>{h(Date.now())},[N,w,y]);const B=g?v?k?"Busy":"Ready":"Offline":"Not configured",Q=g?v?k?"warn":"good":"danger":"warn",D=g&&v&&!k&&!u&&C&&E,H=m.useMemo(()=>Math.ceil(A/1e3),[A]),K=async(G,$)=>{if($){f(G);try{await $()}catch{}finally{f("")}}};return r.jsxs("section",{className:`surface relative text-sm text-slate-200 ${x}`,children:[k?r.jsx("div",{className:"absolute inset-0 z-20 flex items-center justify-center rounded-md bg-slate-950/80 px-1.5 text-center",children:r.jsxs("div",{className:"space-y-0.25",children:[r.jsx("p",{className:"text-sm font-semibold text-slate-100",children:y?"Motion in progress":"Motion cooldown active"}),r.jsx("p",{className:"text-xs text-slate-300",children:"Controls are disabled while the lift is moving, otherwise it's tiny brain would get confused."}),!y&&q?r.jsxs("p",{className:"text-xs text-slate-400",children:["About ",H,"s remaining."]}):null]})}):null,r.jsxs("div",{className:"grid gap-0.5",children:[r.jsxs("div",{className:"relative flex items-center justify-center min-h-[1.5rem]",children:[r.jsxs("div",{className:"text-center",children:[r.jsx("p",{className:"text-sm text-slate-100",children:"Lift Controls"}),r.jsx("p",{className:"text-xs text-slate-400",children:"Move the lift up and down. Please don't break anything..."})]}),r.jsx("span",{className:`absolute right-0 inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${Rw(Q)}`,children:B})]}),r.jsx("section",{className:"surface-muted px-0.5 py-0.5",children:r.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-0.5",children:[r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.75 text-center",children:[r.jsx("div",{className:"text-xs text-slate-300",children:"Position"}),r.jsx("div",{className:"text-base font-semibold text-slate-100",children:qw(M)})]}),r.jsx("button",{type:"button",disabled:!D,onClick:()=>K("down",l),className:`button-dark w-full text-sm disabled:opacity-50 ${M==="down"||S==="down"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:u==="down"||S==="down"?"Lowering...":"Down"}),r.jsx("button",{type:"button",disabled:!D,onClick:()=>K("up",s),className:`button-dark w-full text-sm disabled:opacity-50 ${M==="up"||S==="up"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:u==="up"||S==="up"?"Raising...":"Up"})]})}),!g||!v||!C||!E?r.jsx("p",{className:"text-xs text-slate-400 text-center",children:g?v?"Waiting for required lift switch entities in Home Assistant.":"Home Assistant is offline.":"Set homeAssistant.lift.upSwitch and homeAssistant.lift.downSwitch in server config."}):null,n?.lastError?r.jsxs("p",{className:"text-xs text-rose-300 text-center",children:["Last error: ",n.lastError]}):null]})]})}function Jg(){const{session:n,neatoLidar:s,identifySession:l,requestVerification:o,requestPrivateRoverAccess:u,playUploadedAudio:f,stopUploadedAudio:p,startMicWhip:h,readyMicWhip:x,stopMicWhip:g,neatoStart:v,neatoSendHome:y,neatoLocate:S,neatoClearErrors:M,neatoPowerCycle:C,liftUp:E,liftDown:N}=nn(),{value:w,save:A}=Ge("identity",{cookieUserId:""}),{value:q}=Ge("profile",{nickname:""}),k=m.useMemo(()=>(w?.cookieUserId||"").trim(),[w?.cookieUserId]),B=(q?.nickname||"").trim(),Q=!!n?.isVerified,D=n?.verification?.pendingRequestId||null,H=n?.privateRoverAccess?.requestableRovers||[],K=n?.privateRoverAccess?.pendingRequests||[],G=String(n?.assignment?.roverId||"").trim(),[$,ee]=m.useState(""),oe=async ce=>{const U=String(ce||"").trim().toLowerCase();if(!U)throw new Error("Identity key required.");if(!dw.test(U))throw new Error("Identity key must match format: cu_ + 32 lowercase hex chars.");return A(R=>({...R||{},cookieUserId:U})),await l({cookieUserId:U,nickname:B}),U};return r.jsxs("section",{className:"space-y-2 text-base",children:[r.jsxs("div",{className:"grid gap-2 grid-cols-1 lg:grid-cols-[minmax(0,1.25fr)_minmax(0,0.9fr)]",children:[Q?null:r.jsx("div",{className:"lg:col-span-2",children:r.jsx(Aw,{pendingRequestId:D,currentStoredKey:k,nickname:B,requestVerification:o,applyIdentityKey:oe,onMessage:ee,fullWidth:!0})}),r.jsx("div",{className:"lg:col-span-2",children:Q?r.jsxs("div",{className:"space-y-2",children:[r.jsx(Mw,{neato:n?.neato||null,lidar:s,onStart:v,onSendHome:y,onLocate:S,onClearErrors:M,onPowerCycle:C,fullWidth:!0}),r.jsx(Tw,{lift:n?.lift||null,onUp:E,onDown:N,fullWidth:!0}),r.jsx(Nw,{ownRoverId:G,audioForwardByRover:n?.audioForward||{},playUploadedAudio:f,stopUploadedAudio:p,startMicWhip:h,readyMicWhip:x,stopMicWhip:g})]}):r.jsx("section",{className:"surface h-full",children:r.jsx("div",{className:"flex h-full flex-col items-center justify-center text-center text-xs text-slate-400",children:"Verify your account to unlock VIP controls."})})}),r.jsx("div",{className:"lg:col-span-2",children:r.jsx(Cw,{requestableRovers:H,pendingRequests:K,requestPrivateRoverAccess:u,onMessage:ee,fullWidth:!0})}),r.jsx("div",{className:"lg:col-span-2",children:r.jsx(ww,{currentStoredKey:k,applyIdentityKey:oe,onMessage:ee,fullWidth:!0})})]}),$?r.jsx("div",{className:Bo,children:r.jsx("p",{className:"text-xs text-slate-400 text-center",children:$})}):null]})}const Dw=420,_w=1200,Bw={1:262,2:330,3:392,4:523};function No(){const{session:n}=nn(),s=tn(),{value:l}=Ge("audio",lt),o=Number.isFinite(l?.masterVolume)?l.masterVolume:lt.masterVolume,u=Number.isFinite(l?.alertVolume)?l.alertVolume:lt.alertVolume,f=Math.max(0,Math.min(1,o*u)),p=m.useMemo(()=>{const E=Array.isArray(n?.buttonBox?.buttons)?n.buttonBox.buttons:[];return E.length===4?E:[1,2,3,4].map(N=>E.find(w=>Number(w?.id)===N)||{id:N,count:0,goal:0,rewardName:null,rewardId:null,rewardNumber:null,lastRewardAt:null})},[n?.buttonBox?.buttons]),[h,x]=m.useState({}),[g,v]=m.useState({}),y=m.useRef(new Map),S=m.useRef(new Map),M=m.useRef({}),C=m.useRef(null);return m.useEffect(()=>{const E={};p.forEach(w=>{E[w.id]=Number(w.lastRewardAt||0)});const N=M.current||{};p.forEach(w=>{const A=Number(w.id),q=E[A]||0,k=Number(N[A]||0);if(q>0&&q!==k){v(D=>({...D,[A]:!0}));const B=S.current.get(A);B&&clearTimeout(B);const Q=setTimeout(()=>{v(D=>({...D,[A]:!1})),S.current.delete(A)},_w);S.current.set(A,Q)}}),M.current=E},[p]),m.useEffect(()=>{function E(w){const A=Bw[w];if(!A||f<=0)return;let q=C.current;if(!q){const D=window.AudioContext||window.webkitAudioContext;if(!D)return;q=new D,C.current=q}const k=q.createOscillator(),B=q.createGain();k.type="square",k.frequency.value=A;const Q=.14*f;B.gain.setValueAtTime(1e-4,q.currentTime),B.gain.exponentialRampToValueAtTime(Math.max(1e-4,Q),q.currentTime+.01),B.gain.exponentialRampToValueAtTime(1e-4,q.currentTime+.2),k.connect(B),B.connect(q.destination),k.start(),k.stop(q.currentTime+.21)}function N(w={}){const A=Number(w.buttonId);if(!Number.isFinite(A)||A<1||A>4)return;x(B=>({...B,[A]:!0}));const q=y.current.get(A);q&&clearTimeout(q);const k=setTimeout(()=>{x(B=>({...B,[A]:!1})),y.current.delete(A)},Dw);y.current.set(A,k),E(A)}return s.on("buttonBox:increment",N),()=>{s.off("buttonBox:increment",N)}},[f,s]),r.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[r.jsx("header",{className:"panel-muted text-center text-sm",children:r.jsx("p",{children:"Button Box"})}),r.jsx("div",{className:"grid grid-cols-4 gap-0.5",children:p.map(E=>{const N=Number(E.id),w=Number.isFinite(E.count)?E.count:0,A=Number.isFinite(E.goal)?E.goal:0,q=typeof E.rewardName=="string"&&E.rewardName.trim()?E.rewardName.trim():"Unassigned",k=Number.isFinite(E.rewardNumber)?E.rewardNumber:"?",B=!!h[N],Q=!!g[N];return r.jsx(xg,{buttonId:N,count:w,goal:A,rewardNumber:k,rewardName:q,className:[B?"bg-cyan-900/45":"",Q?"bg-fuchsia-900/45":""].filter(Boolean).join(" ")},N)})})]})}function Ow(){const{state:{roverId:n}}=_t(),l=ul(n)?.sensors||{};return r.jsx("section",{className:"panel-section",children:r.jsx("div",{className:"aspect-square w-full",children:r.jsx(Hd,{sensors:l})})})}function Lw(){const{state:{roverId:n,keymap:s,camera:l,horn:o},pipeline:u,actions:{setServoAngle:f,setNightVision:p,startHorn:h,stopHorn:x}}=_t(),g=qg(n),v=g.docked&&!g.driving,y=l?.config,S=!!(n&&l?.enabled&&y),M=!!(n&&u?.nightVision),C=u?.nightVisionState,E=!!(n&&u?.horn),N=o?.overheated,w=typeof y?.minAngle=="number"?y.minAngle:-30,A=typeof y?.maxAngle=="number"?y.maxAngle:30,q=typeof l?.angle=="number"?l.angle:typeof y?.homeAngle=="number"?y.homeAngle:(w+A)/2,k=Cn(s?.nightVisionToggle?.[0]),B=Cn(s?.hornHonk?.[0]),Q=Cn(s?.cameraUp?.[0]),D=Cn(s?.cameraDown?.[0]);return r.jsxs("section",{className:"panel-section grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] gap-0.5",children:[r.jsx(Tg,{layout:"desktop",expand:!0,driveDockState:g}),v?null:r.jsxs("div",{className:"surface space-y-0.5 p-0 text-sm text-slate-200",children:[M&&r.jsx(Dg,{nightVisionOn:C?.nightVisionOn,disabled:!n,onToggle:p,keyLabel:k}),E&&r.jsx(_g,{disabled:!n||N,onStart:h,onStop:x,keyLabel:B,active:o?.active,heat:o?.heat}),S&&r.jsx(Bg,{value:q,min:w,max:A,onChange:f,keyDownLabel:D,keyUpLabel:Q,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 zw({layout:n,onOpenHelpOverlay:s}){const l=rt(M=>M.session),{state:o}=_t(),{value:u}=Ge("vipAudio",{openMicEnabled:!1,pttMode:"live"}),f=l?.isVerified?"bg-emerald-400":"bg-red-600",p=String(l?.assignment?.roverId||"").trim(),h=p?l?.audioForward?.[p]:null,x=!!o?.mic?.pttActive,g=!!u?.openMicEnabled,v=u?.pttMode==="clip"?"clip":"live",y=!!(p&&l?.isVerified&&(v==="clip"?x:g||x)),S=!!(p&&l?.isVerified&&v==="clip"&&h?.source==="upload"&&h?.state==="playing");return r.jsx("section",{className:"panel text-base",children:r.jsxs(yf,{defaultTab:"telemetry",children:[r.jsxs(Sf,{children:[r.jsx(Wt,{id:"telemetry",children:"Controls"}),r.jsx(Wt,{id:"vip",highlight:S?"green":y?"pink":"none",children:r.jsxs("span",{className:"inline-flex items-center gap-2",children:[r.jsx("span",{children:"VIP"}),r.jsx("span",{className:`inline-block h-3 w-3 rounded-full ${f}`,"aria-hidden":"true",title:l?.isVerified?"Verified":"Not verified"})]})}),r.jsx(Wt,{id:"help",children:"Help"}),r.jsx(Wt,{id:"settings",children:"Settings"})]}),r.jsxs(jf,{children:[r.jsx(en,{id:"telemetry",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]",children:[r.jsx(Ow,{}),r.jsx(Lw,{})]}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]",children:[r.jsx(_o,{}),r.jsx(nf,{panelId:"replay-sources-desktop",fillHeight:!0}),r.jsx(sw,{})]}),r.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)] h-[14rem]",children:[r.jsx(To,{fillHeight:!0}),r.jsxs("div",{className:"grid min-h-0 gap-0.5 grid-rows-[minmax(0,1fr)_auto]",children:[r.jsx(jo,{compact:!0,hideNicknameForm:!0,fillHeight:!0}),r.jsx(aw,{compact:!0})]})]}),r.jsx(Hg,{}),r.jsx(No,{}),r.jsx(df,{defaultOrientation:"horizontal",panelId:"rightpane-telemetry"})]})}),r.jsx(en,{id:"vip",keepMounted:!0,children:r.jsx(Jg,{})}),r.jsx(en,{id:"help",children:r.jsx(Kg,{layout:n,onOpenOverlay:s})}),r.jsx(en,{id:"settings",children:r.jsx(Fg,{})})]})]})})}const Uw=new Set(["admin","lockdown","lockdown-admin"]),Hw=new Set(["lockdown","lockdown-admin"]),Vw=new Set(["admin","lockdown"]);function $w(n="admin"){return n==="lockdown"?{title:"Lockdown mode active",description:"Only the server owners can access the interface at this time."}:{title:"Admin mode active"}}function Gw(){const n=rt(S=>S.session),s=n?.mode,l=n?.role,o=n?.adminReason?.text||"";n?.adminReason?.updatedAt;const u=n?.timezone||"UTC",f=n?.socials?.find(S=>String(S?.id||S?.label||"").toLowerCase()==="discord")?.url||n?.discord?.invite||null,p=Vw.has(s),h=s==="lockdown"?Hw.has(l):Uw.has(l),[x,g]=m.useState(()=>new Date),v=m.useMemo(()=>{try{return new Intl.DateTimeFormat("en-US",{timeZone:u,hour:"numeric",minute:"2-digit",second:"2-digit"}).format(x)}catch{return x.toLocaleTimeString()}},[x,u]);if(m.useEffect(()=>{const S=setInterval(()=>g(new Date),1e3);return()=>clearInterval(S)},[]),!p||h)return null;const y=$w(s);return r.jsx("div",{className:"pointer-events-auto fixed inset-0 z-50 flex items-center justify-center bg-black/85 px-0.5 py-0.5",children:r.jsxs("div",{className:"surface w-full max-w-md space-y-0.5 text-slate-100 shadow-2xl",children:[r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-lg font-semibold",children:y.title}),r.jsx("p",{className:"text-sm text-slate-300",children:y.description})]}),r.jsxs("div",{className:"surface-muted space-y-0.5",children:[r.jsx("p",{className:"text-[0.7rem] tracking-wide text-slate-400",children:"Reason for locking:"}),r.jsx("p",{className:"text-lg font-semibold text-slate-100",children:o||"No reason set."}),r.jsxs("p",{className:"text-center text-sm text-slate-300",children:["Server time: ",v]})]}),r.jsx("div",{className:"surface-muted",children:r.jsx(Vg,{})}),r.jsx("div",{className:"w-full justify-center items-center",children:r.jsx(hf,{id:"discord",label:"Join our Discord server for updates!",url:f})}),"You can still use the chat while the server is locked:",r.jsxs("div",{className:"max-h-80 overflow-y-auto",children:[r.jsx(To,{}),r.jsx(Do,{})]})]})})}const Yw="/assets/turn_alert-DVO8EZJa.mp3";function Fw(n,s=1){const l=m.useRef(null);return m.useEffect(()=>{l.current=new Audio(n),l.current.volume=s,l.current.load()},[n,s]),()=>{l.current&&(l.current.volume=s,l.current.currentTime=0,l.current.play().catch(()=>{}))}}function Xw(){const n=rt(v=>v.session),{pushAlert:s}=kn(),{value:l}=Ge("audio",lt),o=Number.isFinite(l?.masterVolume)?l.masterVolume:lt.masterVolume,u=Number.isFinite(l?.alertVolume)?l.alertVolume:lt.alertVolume,f=Math.max(0,Math.min(1,o*u)),p=Fw(Yw,f),h=m.useRef(new Set),x=m.useMemo(()=>n?.turnQueues||{},[n?.turnQueues]),g=n?.socketId||null;return m.useEffect(()=>{if(!g)return;const v=[];Object.entries(x).forEach(([y,S])=>{!S||!S.current||S.current!==g||h.current.has(y)||(v.push(y),h.current.add(y))}),Object.keys(x).forEach(y=>{(!x[y]||x[y].current!==g)&&h.current.delete(y)}),v.length!==0&&(v.forEach(y=>{const S=n?.roster?.find(M=>String(M.id)===String(y))?.name||y;s({title:"Your turn!",message:`You now control ${S}.`,color:"#ff5722"})}),p())},[x,p,s,n?.roster,g]),null}function Qw({visible:n,mode:s,onEnterFullscreen:l,onDismiss:o}){if(!n)return null;const u=s==="pwa-hint";return r.jsx("div",{className:"fixed inset-0 z-40 flex items-end justify-center p-2 pointer-events-none sm:items-center",children:r.jsx("div",{className:"pointer-events-auto w-full max-w-sm rounded-lg border border-cyan-500/40 bg-zinc-950/95 shadow-xl",children:r.jsxs("div",{className:"space-y-0.5 p-4 text-sm text-slate-100",children:[r.jsx("h2",{className:"text-base font-semibold text-white",children:"Better in fullscreen"}),u?r.jsxs("p",{className:"text-slate-300",children:["For fullscreen on iOS, open Safari's share menu and pick ",r.jsx("strong",{children:"Add to Home Screen"}),". Launching from the home screen removes the browser chrome."]}):r.jsx("p",{className:"text-slate-300",children:"Enable fullscreen to free up more space for the video feed and controls. You can exit fullscreen at any time via the system back or home gesture."}),r.jsxs("div",{className:"flex justify-end gap-0.5 pt-1 text-sm",children:[r.jsx("button",{type:"button",className:"rounded border border-slate-600 px-3 py-1 text-slate-200",onClick:o,children:u?"Got it":"Not now"}),!u&&r.jsx("button",{type:"button",className:"rounded bg-cyan-500 px-3 py-1 font-semibold text-black hover:bg-cyan-400",onClick:l,children:"Enter fullscreen"})]})]})})})}function Kw({side:n="right",onClick:s}){const l=n==="left"?"left-2":"right-2";return r.jsx("button",{type:"button",onClick:s,className:`fixed bottom-2 ${l} z-30 flex h-10 w-10 items-center justify-center rounded-full border border-cyan-200/50 bg-slate-900/70 text-cyan-100 shadow-lg backdrop-blur-sm transition hover:bg-slate-800/80 active:scale-95`,"aria-label":"Enter fullscreen",title:"Enter fullscreen",children:r.jsx("svg",{viewBox:"0 0 24 24",width:"18",height:"18","aria-hidden":"true",className:"pointer-events-none",children:r.jsx("path",{d:"M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})})}const Pg="fullscreenPromptDismissed",Zw=new Set(["mobile-portrait","mobile-landscape"]),Wg=()=>{if(typeof window>"u")return null;try{return window.sessionStorage}catch{return null}},Iw=()=>{const n=Wg();if(!n)return!1;try{return n.getItem(Pg)==="1"}catch{return!1}},wx=()=>{const n=Wg();if(n)try{n.setItem(Pg,"1")}catch{}},Jw=()=>{if(typeof navigator>"u")return!1;const n=navigator.userAgent||navigator.platform||"";return/iphone|ipad|ipod/i.test(n)},Cx=()=>{if(typeof document>"u")return!1;const n=document;return!!(n.fullscreenEnabled||n.webkitFullscreenEnabled||n.mozFullScreenEnabled||n.msFullscreenEnabled)};function Pw(n){const s=Zw.has(n),[l,o]=m.useState(!1),[u,f]=m.useState("native"),[p,h]=m.useState(!1),x=m.useMemo(()=>Jw(),[]),g=m.useMemo(()=>Cx(),[]),v=m.useCallback(()=>{if(!s){o(!1),h(!1);return}if(!p&&Iw()){o(!1);return}if(x){f("pwa-hint"),o(!0);return}if(!Cx()){o(!1);return}if(typeof document<"u"&&document.fullscreenElement){o(!1);return}f("native"),o(!0)},[p,x,s]);m.useEffect(()=>{v()},[v]),m.useEffect(()=>{if(typeof document>"u")return;const C=()=>{if(document.fullscreenElement){h(!1),o(!1);return}v()};return document.addEventListener("fullscreenchange",C),document.addEventListener("webkitfullscreenchange",C),()=>{document.removeEventListener("fullscreenchange",C),document.removeEventListener("webkitfullscreenchange",C)}},[v]);const y=m.useCallback(()=>{wx(),h(!1),o(!1)},[]),S=m.useCallback(async()=>{if(!s||x||typeof document>"u")return!1;if(document.fullscreenElement)return!0;const C=document.documentElement;if(!C)return!1;const E=C.requestFullscreen||C.webkitRequestFullscreen||C.mozRequestFullScreen||C.msRequestFullscreen;if(!E)return!1;try{const N=E.call(C);return N&&typeof N.then=="function"&&await N,wx(),h(!1),o(!1),!0}catch(N){return console.warn("Failed to enter fullscreen",N),!1}},[x,s]),M=m.useCallback(()=>{s&&(h(!0),f(x?"pwa-hint":"native"),o(!0))},[x,s]);return m.useEffect(()=>{s||(h(!1),o(!1))},[s]),{visible:l,mode:u,isIOS:x,nativeSupported:g,enterFullscreen:S,dismiss:y,showPrompt:M}}function Ww({visible:n,layout:s,onClose:l,showOnLoad:o,onToggleShowOnLoad:u}){if(!n)return null;const{state:f}=_t(),p=h=>{const x=!h.target.checked;u?.(x)};return r.jsx("div",{className:"fixed inset-0 z-30 flex items-center justify-center bg-black/80 px-0.5 py-0.5",children:r.jsxs("div",{className:"pointer-events-auto surface w-full max-w-4xl max-h-[90vh] overflow-hidden shadow-2xl",children:[r.jsxs("div",{className:"flex items-center justify-between border-b border-slate-700 px-0.5 py-0.25 text-sm text-slate-200",children:[r.jsx("span",{className:"font-semibold",children:"Help & controls"}),r.jsxs("div",{className:"flex items-center gap-0.5 text-[0.8rem] text-slate-300",children:[r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!o,onChange:p,className:"accent-cyan-500"}),r.jsx("span",{children:"Don't show again"})]}),r.jsx("button",{type:"button",onClick:l,className:"button-dark px-2 py-0.25 text-[0.8rem]",children:"Close"})]})]}),r.jsx("div",{className:"max-h-[85vh] overflow-y-auto p-0.5",children:r.jsx(Qg,{layout:s,keymap:f?.keymap||{}})})]})})}const e2=`aroma bagel batter beans diff --git a/server/public/index.html b/server/public/index.html index 334d0985..862b5c5e 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -11,7 +11,7 @@ Multi Roomba Rover - + diff --git a/server/src/services/replayEngineV2/constants.js b/server/src/services/replayEngineV2/constants.js index 7f5e8908..10e0cf8e 100644 --- a/server/src/services/replayEngineV2/constants.js +++ b/server/src/services/replayEngineV2/constants.js @@ -9,7 +9,7 @@ const SEGMENT_ROOT = path.join(resolveDataDir(), 'replay-segments'); const SEGMENT_SECONDS = Math.max(1, Number.parseInt(process.env.REPLAY_SEGMENT_SECONDS || '1', 10)); const BUFFER_SECONDS = Math.max(20, Number.parseInt(process.env.REPLAY_BUFFER_SECONDS || '45', 10)); const CLEANUP_INTERVAL_MS = 10_000; -const BUILD_DURATION_MS = Math.max(5000, Number.parseInt(process.env.REPLAY_DURATION_MS || '20000', 10)); +const BUILD_DURATION_MS = Math.max(5000, Number.parseInt(process.env.REPLAY_DURATION_MS || '35000', 10)); const BUILD_GUARD_MS = Math.max(200, Number.parseInt(process.env.REPLAY_GUARD_MS || '1200', 10)); const TARGET_FPS = Math.max(10, Number.parseInt(process.env.REPLAY_TARGET_FPS || '30', 10)); const MAX_WIDTH = Math.max(320, Number.parseInt(process.env.REPLAY_MAX_WIDTH || '1280', 10)); diff --git a/webui/src/components/vip/VipNeatoCard.jsx b/webui/src/components/vip/VipNeatoCard.jsx index 6a901687..8bdae05d 100644 --- a/webui/src/components/vip/VipNeatoCard.jsx +++ b/webui/src/components/vip/VipNeatoCard.jsx @@ -248,16 +248,18 @@ export default function VipNeatoCard({ > Lidar +
Front of robot
{lidarDots ? ( - - - - - - - + + + + + + + + {lidarPoints .filter( (point) => @@ -269,11 +271,11 @@ export default function VipNeatoCard({ const scaledRadius = normalized * 92; const x = 110 + Math.cos(angleRad) * scaledRadius; const y = 110 + Math.sin(angleRad) * scaledRadius; - return ; + return ; })} ) : ( -
+
Waiting for scan
)}