From f9f2e1e2ed1cf587ed11fb9e6f792f5fe8cce4df Mon Sep 17 00:00:00 2001 From: legop3 Date: Wed, 20 May 2026 23:29:10 -0400 Subject: [PATCH] FIXATYPOLOL --- server/public/assets/{index-DHhBFQ9m.js => index-jP8aZ7aV.js} | 2 +- server/public/index.html | 2 +- webui/src/components/OverseerPreferencePanel/index.jsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename server/public/assets/{index-DHhBFQ9m.js => index-jP8aZ7aV.js} (97%) diff --git a/server/public/assets/index-DHhBFQ9m.js b/server/public/assets/index-jP8aZ7aV.js similarity index 97% rename from server/public/assets/index-DHhBFQ9m.js rename to server/public/assets/index-jP8aZ7aV.js index 58cdf102..9745807b 100644 --- a/server/public/assets/index-DHhBFQ9m.js +++ b/server/public/assets/index-jP8aZ7aV.js @@ -20,7 +20,7 @@ ${w}`}).join(` ---------------------------------------- `):""};return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("div",{className:"panel-muted text-xs uppercase",children:"Overseer Control"}),l.jsx("div",{className:"flex gap-0.5 text-xs",children:l.jsx("button",{type:"button",onClick:a,disabled:!!r,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:r?"Clearing...":"Clear Overseer History"})}),l.jsxs("div",{className:"surface flex flex-wrap gap-0.5 text-xs",children:[l.jsxs("span",{className:"surface-muted",children:["running: ",f.running?"yes":"no"]}),l.jsxs("span",{className:"surface-muted",children:["phase: ",f.phase||"--"]}),l.jsxs("span",{className:"surface-muted",children:["tick: ",f.tickCount??0]}),l.jsxs("span",{className:"surface-muted",children:["trigger: ",f.lastTriggerReason||"--"]}),l.jsxs("span",{className:"surface-muted",children:["name: ",h.name||"--"]}),l.jsxs("span",{className:"surface-muted",children:["model: ",h.model||"--"]}),l.jsxs("span",{className:"surface-muted",children:["observeOnly: ",h.observeOnly?"yes":"no"]}),l.jsxs("span",{className:"surface-muted",children:["decision: ",p.normalized||"--"]}),l.jsxs("span",{className:"surface-muted",children:["outcome: ",p.outcome||"--"]}),l.jsxs("span",{className:"surface-muted",children:["reason: ",p.reason||"--"]}),l.jsxs("span",{className:"surface-muted",children:["last gen: ",g.lastGenerationMs!=null?`${g.lastGenerationMs}ms`:"--"]})]}),l.jsxs("details",{className:"surface text-xs text-slate-200",open:!0,children:[l.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Latest Context"}),l.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:x.stateUpdate||""})]}),l.jsxs("details",{className:"surface text-xs text-slate-200",open:!0,children:[l.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Exact Model Input (messages[])"}),l.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:b()})]}),l.jsxs("details",{className:"surface text-xs text-slate-200",children:[l.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Exact System Prompt"}),l.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:x.systemPrompt||""})]}),l.jsxs("details",{className:"surface text-xs text-slate-200",children:[l.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Exact Transcript Rows"}),l.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify(x.transcript||[],null,2)})]}),l.jsxs("details",{className:"surface text-xs text-slate-200",children:[l.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Tool Availability"}),l.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify({available:x.availableTools,blocked:x.blockedTools},null,2)})]}),l.jsxs("details",{className:"surface text-xs text-slate-200",open:!0,children:[l.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Exact Model Output"}),l.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:p.raw||""})]}),l.jsxs("details",{className:"surface text-xs text-slate-200",open:!0,children:[l.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Live Tool Calls"}),l.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify(p.liveToolCalls||[],null,2)})]}),l.jsxs("div",{className:"surface text-xs text-slate-200",children:[l.jsxs("div",{children:["Normalized decision: ",p.normalized||"--"]}),l.jsxs("div",{children:["Model input at: ",x.modelInputAt?new Date(x.modelInputAt).toLocaleString():"n/a"]}),l.jsxs("div",{children:["Model output at: ",p.outputAt?new Date(p.outputAt).toLocaleString():"n/a"]})]}),v.message?l.jsxs("div",{className:"surface text-xs text-red-300",children:["Error: ",v.message]}):null,l.jsxs("details",{className:"surface text-xs text-slate-200",children:[l.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Recent Runs"}),l.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify(n.history||[],null,2)})]}),l.jsx("button",{type:"button",className:"button-dark text-xs",onClick:()=>u(S=>!S),children:o?"Hide Full Payload":"Show Full Payload"}),o?l.jsx("pre",{className:"surface whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify(n,null,2)}):null]})}function Vw({health:n,roster:a=[]}){if(!n)return null;const r=n.replay||{sources:[],readyCount:0,totalCount:0},o=n.snapshots||{rovers:[],rooms:[]},u=g=>a.find(x=>String(x.id)===String(g))?.color||null,f=`${r.readyCount}/${r.totalCount} sources ready`,h=o.rovers.filter(g=>g.stale).length,p=o.rooms.filter(g=>g.stale).length;return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("div",{className:"panel-muted text-xs uppercase",children:"Health"}),l.jsxs("div",{className:"surface space-y-0.5 text-xs text-slate-200",children:[l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{children:"Replay segments"}),l.jsx("span",{className:"text-slate-400",children:f})]}),l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{children:"Rover snapshots"}),l.jsxs("span",{className:h?"text-amber-300":"text-emerald-300",children:[o.rovers.length-h,"/",o.rovers.length," ok"]})]}),l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{children:"Room cameras"}),l.jsxs("span",{className:p?"text-amber-300":"text-emerald-300",children:[o.rooms.length-p,"/",o.rooms.length," ok"]})]})]}),l.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:r.sources.map(g=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{className:`${g.type==="rover"?"rounded px-1 py-[1px] border border-transparent":""}`,style:g.type==="rover"?ns(u(g.id),.16):void 0,children:g.label}),l.jsxs("span",{className:g.ready?"text-emerald-300":"text-amber-300",children:[g.recentCount,"/",g.neededCount]})]},`${g.type}:${g.id}`))}),l.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rovers.map(g=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{className:"rounded px-1 py-[1px] border border-transparent",style:ns(u(g.id),.16),children:g.name}),l.jsx("span",{className:g.stale?"text-amber-300":"text-emerald-300",children:g.stale?"stale":"ok"})]},`rover:${g.id}`))}),l.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rooms.map(g=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{children:g.name}),l.jsx("span",{className:g.stale?"text-amber-300":"text-emerald-300",children:g.error?"error":g.stale?"stale":"ok"})]},`room:${g.id}`))})]})}function $w({entries:n}){const a=n||[];return l.jsxs("div",{className:"panel-section space-y-0.5 text-base",children:[l.jsxs("div",{className:"flex items-center justify-between text-sm text-slate-400",children:[l.jsx("span",{children:"Admin IP log"}),l.jsx("span",{children:a.length})]}),l.jsx("div",{className:"surface h-64 overflow-y-auto font-mono text-xs",children:a.length===0?l.jsx("p",{children:"No admin log entries yet."}):a.slice().reverse().map(r=>l.jsxs("div",{className:"surface",children:[l.jsx("span",{className:"text-amber-400",children:r.ts?new Date(r.ts).toLocaleTimeString():"--"})," ",r.label&&l.jsxs("span",{className:"text-teal-400",children:["[",r.label,"]"]})," ",l.jsx("span",{className:"text-slate-200",children:r.message})," ",r.ip&&l.jsx("span",{className:"text-cyan-300",children:r.ip})," ",r.meta&&l.jsx("span",{className:"text-slate-500",children:JSON.stringify(r.meta)})]},r.id))}),l.jsx("p",{className:"text-xs text-slate-500",children:"Admin-only log stream; IPs never appear in user data."})]})}const Gw=[{key:"open",label:"Open"},{key:"turns",label:"Turns"},{key:"admin",label:"Admin"},{key:"lockdown",label:"Lockdown"}];function Yw(){const{session:n,lockRover:a,setMode:r,requestControl:o,setGlobalObjective:u,setAdminReason:f,rebootRover:h,rebootServer:p,setAudioLevels:g,setPrivateSafety:x,llmControl:v,overseerControl:b,adminLogs:S,llmCommentaryState:k,overseerControlState:E}=yr(),N=m.useMemo(()=>n?.roster??[],[n?.roster]),[w,j]=m.useState({}),[M,A]=m.useState({}),[R,q]=m.useState(!1),[$,O]=m.useState(!1),[L,F]=m.useState(!1),U=n?.health||null,Z=n?.globalObjective?.text||"",Q=n?.globalObjective?.updatedAt||null,[ce,ue]=m.useState(Z),le=n?.adminReason?.text||"",B=n?.adminReason?.updatedAt||null,[V,I]=m.useState(le),J=n?.audioLevels||{},[ie,T]=m.useState({hornGain:Number.isFinite(J.hornGain)?J.hornGain:1,ttsGain:Number.isFinite(J.ttsGain)?J.ttsGain:1,forwardGain:Number.isFinite(J.forwardGain)?J.forwardGain:1}),[D,K]=m.useState({}),ae=n?.role==="admin"||n?.role==="lockdown"||n?.role==="lockdown-admin",he=n?.role==="lockdown"||n?.role==="lockdown-admin",we=n?.mode??"open",pe=async(te,me)=>{try{await a(te,me),j(_e=>({..._e,[te]:me}))}catch(_e){alert(_e.message)}},ge=async te=>{const me=te.target.value;try{await r(me)}catch(_e){alert(_e.message)}},xe=async te=>{try{await o(te,{force:!0})}catch(me){alert(me.message)}},ye=async te=>{if(!(!te?.id||!window.confirm(`Reboot rover "${te.name||te.id}" now?`))){A(_e=>({..._e,[te.id]:!0}));try{await h(te.id)}catch(_e){alert(_e.message)}finally{A(_e=>({..._e,[te.id]:!1}))}}},Ce=async()=>{if(window.confirm("Reboot the server host now? This will disconnect all users.")){q(!0);try{await p()}catch(me){alert(me.message),q(!1)}}},qe=async()=>{if(window.confirm("Clear LLM commentary history now? This resets chat context, bot memory, and rover activity metrics for narration.")){O(!0);try{await v("clearHistory")}catch(me){alert(me.message)}finally{O(!1)}}},Ze=async()=>{if(window.confirm("Clear Overseer Control history now?")){F(!0);try{await b("clearHistory")}catch(me){alert(me.message)}finally{F(!1)}}},Se=async()=>{try{await u(ce)}catch(te){alert(te.message)}},at=async()=>{try{await u(null)}catch(te){alert(te.message)}},ht=async()=>{try{await f(V)}catch(te){alert(te.message)}},Ge=async()=>{try{await f(null)}catch(te){alert(te.message)}},je=te=>me=>{const _e=Number(me.target.value);T(en=>({...en||{},[te]:Number.isFinite(_e)?_e:1}))},se=async()=>{try{await g(ie)}catch(te){alert(te.message)}},re=async()=>{window.dispatchEvent(new CustomEvent("buttonBox:rewardRunLocalTest",{detail:{rewardName:"Admin Mode",count:1e3,goal:1e3}}))};m.useEffect(()=>{ue(Z)},[Z]),m.useEffect(()=>{I(le)},[le]),m.useEffect(()=>{T({hornGain:Number.isFinite(J.hornGain)?J.hornGain:1,ttsGain:Number.isFinite(J.ttsGain)?J.ttsGain:1,forwardGain:Number.isFinite(J.forwardGain)?J.forwardGain:1})},[J.forwardGain,J.hornGain,J.ttsGain]),m.useEffect(()=>{const te={};(N||[]).forEach(me=>{me?.private?.enabled&&(te[me.id]={speedLimitEnabled:!!me?.private?.safety?.speedLimitEnabled,speedLimitMaxWheelSpeed:Number.isFinite(me?.private?.safety?.speedLimitMaxWheelSpeed)?me.private.safety.speedLimitMaxWheelSpeed:250,hardOvercurrentEnabled:!!me?.private?.safety?.hardOvercurrentEnabled,overcurrentStopMs:Number.isFinite(me?.private?.safety?.overcurrentStopMs)?me.private.safety.overcurrentStopMs:300,hardBumpEnabled:!!me?.private?.safety?.hardBumpEnabled,bumpBackoffSpeed:Number.isFinite(me?.private?.safety?.bumpBackoffSpeed)?me.private.safety.bumpBackoffSpeed:250,bumpBackoffMs:Number.isFinite(me?.private?.safety?.bumpBackoffMs)?me.private.safety.bumpBackoffMs:350,cliffEnabled:!!me?.private?.safety?.cliffEnabled,cliffBackoffSpeed:Number.isFinite(me?.private?.safety?.cliffBackoffSpeed)?me.private.safety.cliffBackoffSpeed:250,cliffBackoffMs:Number.isFinite(me?.private?.safety?.cliffBackoffMs)?me.private.safety.cliffBackoffMs:500,triggerCooldownMs:Number.isFinite(me?.private?.safety?.triggerCooldownMs)?me.private.safety.triggerCooldownMs:800})}),K(te)},[N]);const de=m.useMemo(()=>{const te={};return N.forEach(me=>{te[me.id]=w[me.id]??me.locked}),te},[N,w]),fe=(te,me={})=>{K(_e=>({..._e||{},[te]:{..._e?.[te]||{},...me||{}}}))},Re=async te=>{try{const me=D?.[te];if(!me)return;await x(te,me)}catch(me){alert(me.message)}};return ae?l.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[l.jsxs("div",{className:"flex items-center justify-between gap-0.5 text-sm",children:[l.jsx("span",{children:"Admin controls"}),l.jsx("select",{value:we,onChange:ge,className:"field-input text-sm",children:Gw.map(te=>l.jsx("option",{value:te.key,children:te.label},te.key))})]}),l.jsxs("div",{className:"flex gap-0.5 text-xs",children:[l.jsx("button",{type:"button",onClick:Ce,disabled:R,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:R?"Server rebooting...":"Reboot Server"}),l.jsx("button",{type:"button",onClick:re,className:"button-dark",children:"Test Reward Popup"})]}),l.jsxs("div",{className:"space-y-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[l.jsx("span",{children:"Global audio levels"}),n?.audioLevels?.updatedAt?l.jsxs("span",{children:["Updated ",new Date(n.audioLevels.updatedAt).toLocaleString()]}):null]}),l.jsxs("label",{className:"grid gap-0.5 text-xs text-slate-200",children:[l.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[l.jsx("span",{children:"Horn gain"}),l.jsxs("span",{children:[ie.hornGain.toFixed(2),"x"]})]}),l.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:ie.hornGain,onChange:je("hornGain"),className:"w-full accent-emerald-500"})]}),l.jsxs("label",{className:"grid gap-0.5 text-xs text-slate-200",children:[l.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[l.jsx("span",{children:"TTS gain"}),l.jsxs("span",{children:[ie.ttsGain.toFixed(2),"x"]})]}),l.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:ie.ttsGain,onChange:je("ttsGain"),className:"w-full accent-emerald-500"})]}),l.jsxs("label",{className:"grid gap-0.5 text-xs text-slate-200",children:[l.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[l.jsx("span",{children:"Forward gain"}),l.jsxs("span",{children:[ie.forwardGain.toFixed(2),"x"]})]}),l.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:ie.forwardGain,onChange:je("forwardGain"),className:"w-full accent-emerald-500"})]}),l.jsx("div",{className:"flex gap-0.5 text-xs",children:l.jsx("button",{type:"button",onClick:se,className:"button-dark",children:"Apply audio levels"})})]}),l.jsxs("div",{className:"space-y-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[l.jsx("span",{children:"Global objective"}),Q?l.jsxs("span",{children:["Updated ",new Date(Q).toLocaleString()]}):null]}),l.jsx("input",{type:"text",value:ce,onChange:te=>ue(te.target.value),placeholder:"Set a global objective",className:"field-input text-sm"}),l.jsxs("div",{className:"flex gap-0.5 text-xs",children:[l.jsx("button",{type:"button",onClick:Se,className:"button-dark",children:"Set objective"}),l.jsx("button",{type:"button",onClick:at,className:"button-danger",children:"Clear"})]})]}),l.jsxs("div",{className:"space-y-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[l.jsx("span",{children:"Admin mode reason"}),B?l.jsxs("span",{children:["Updated ",new Date(B).toLocaleString()]}):null]}),l.jsx("textarea",{value:V,onChange:te=>I(te.target.value),placeholder:"Set an admin mode reason",className:"field-input text-sm min-h-[3.5rem]"}),l.jsxs("div",{className:"flex gap-0.5 text-xs",children:[l.jsx("button",{type:"button",onClick:ht,className:"button-dark",children:"Set reason"}),l.jsx("button",{type:"button",onClick:Ge,className:"button-danger",children:"Clear"})]})]}),l.jsx(Bw,{roster:N,renderActions:te=>l.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 text-xs",children:[l.jsx("button",{type:"button",onClick:()=>pe(te.id,!de[te.id]),className:"button-dark",children:de[te.id]?"Unlock":"Lock"}),l.jsx("button",{type:"button",onClick:()=>xe(te.id),className:"button-dark",children:"Force"}),l.jsx("button",{type:"button",onClick:()=>ye(te),disabled:!!M[te.id],className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:M[te.id]?"Rebooting...":"Reboot"}),te?.private?.enabled?l.jsxs("div",{className:"w-full rounded border border-slate-700/70 p-0.5 space-y-0.5 text-[0.7rem]",children:[l.jsx("div",{className:"text-slate-300",children:"Private safety"}),l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!!D?.[te.id]?.speedLimitEnabled,disabled:!he,onChange:me=>fe(te.id,{speedLimitEnabled:!!me.target.checked})}),l.jsx("span",{children:"Speed limit"})]}),l.jsx("input",{type:"number",min:"1",max:"500",className:"field-input text-xs w-20",disabled:!he,value:D?.[te.id]?.speedLimitMaxWheelSpeed??250,onChange:me=>fe(te.id,{speedLimitMaxWheelSpeed:Number(me.target.value)||250})}),l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!!D?.[te.id]?.hardOvercurrentEnabled,disabled:!he,onChange:me=>fe(te.id,{hardOvercurrentEnabled:!!me.target.checked})}),l.jsx("span",{children:"Hard overcurrent"})]}),l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!!D?.[te.id]?.hardBumpEnabled,disabled:!he,onChange:me=>fe(te.id,{hardBumpEnabled:!!me.target.checked})}),l.jsx("span",{children:"Hard bump"})]}),l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!!D?.[te.id]?.cliffEnabled,disabled:!he,onChange:me=>fe(te.id,{cliffEnabled:!!me.target.checked})}),l.jsx("span",{children:"Cliff safety"})]}),l.jsx("button",{type:"button",disabled:!he,onClick:()=>Re(te.id),className:"button-dark disabled:opacity-50",children:"Apply Safety"})]}):null]})}),l.jsx(Vw,{health:U,roster:N}),l.jsx(Uw,{state:k,onClearHistory:qe,clearingHistory:$}),l.jsx(Hw,{state:E,onClearHistory:Ze,clearingHistory:L}),l.jsx($w,{entries:S})]}):null}const Fw=[{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 Iw(n){return n.reduce((a,r)=>((a[r.group]||(a[r.group]=[])).push(r),a),{})}function Xw(n,a){const r=Number(n);return Number.isFinite(r)?Math.max(0,Math.min(500,r)):a}const pf=5,pr=500,xr=1,xf=100;function Qw(n,a){const r=Number(n);return Number.isFinite(r)?Math.max(pf,Math.min(pr,r)):a}function ro(n,a){const r=Number(n);return Number.isFinite(r)?Math.max(xr,Math.min(xf,r)):a}function Kw(n){const r=(ro(n,n)-xr)/(xf-xr),o=pr-r*(pr-pf);return Math.round(o)}function Zw(n){const a=Qw(n,n),r=(pr-a)/(pr-pf),o=xr+r*(xf-xr);return Math.round(o)}function Jw(n){const[a,r]=m.useState(null),o=m.useCallback(h=>{ad(!0),r(h)},[]),u=m.useCallback(()=>{ad(!1),r(null)},[]),f=m.useCallback((h,p)=>{if(p.preventDefault(),p.stopPropagation(),p.key==="Escape"){u();return}const g=jr(p.key??"");g&&(n(h,g,p),u())},[u,n]);return m.useEffect(()=>()=>ad(!1),[]),{active:a,startCapture:o,cancel:u,captureFromEvent:f}}function Xi({label:n,description:a,value:r,onChange:o,min:u=0,max:f=500,step:h=5}){return l.jsxs("label",{className:"surface-muted block p-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[l.jsx("span",{className:"font-semibold text-slate-100",children:n}),l.jsx("input",{type:"number",min:u,max:f,value:r,onChange:p=>o(p.target.value),className:"w-16 rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-right text-[0.75rem] font-mono text-slate-100"})]}),a&&l.jsx("p",{className:"text-[0.65rem] text-slate-500",children:a}),l.jsxs("div",{className:"mt-0 flex items-center gap-0.5",children:[l.jsx("input",{type:"range",min:u,max:f,step:h,value:r,onChange:p=>o(p.target.value),className:"h-2 flex-1 accent-emerald-400"}),l.jsx("span",{className:"w-12 text-right text-[0.75rem] font-mono text-slate-400",children:r})]})]})}function Pw(){const{state:{keymap:n},actions:{updateKeyBinding:a,resetKeyBindings:r}}=yt(),{value:o,save:u}=Oe("inputs",En),f={...En.keyboard,...o.keyboard??{}},h=m.useMemo(()=>typeof f.tiltSpeed=="number"?ro(f.tiltSpeed,En.keyboard.tiltSpeed):typeof f.tiltIntervalMs=="number"?ro(Zw(f.tiltIntervalMs),En.keyboard.tiltSpeed):En.keyboard.tiltSpeed,[f.tiltIntervalMs,f.tiltSpeed]),p=m.useMemo(()=>Iw(Fw),[]),{active:g,startCapture:x,cancel:v,captureFromEvent:b}=Jw((N,w)=>{a(N,w)}),S=m.useCallback(N=>n?.[N]?.[0]||or[N]?.[0]||"",[n]),k=m.useCallback((N,w)=>{const j=En.keyboard,M=Xw(w,j[N]);u(A=>({...A??{},keyboard:{...A?.keyboard??j,[N]:M}}))},[u]),E=m.useCallback(N=>{const w=En.keyboard,j=ro(N,w.tiltSpeed),M=Kw(j);u(A=>({...A??{},keyboard:{...A?.keyboard??w,tiltSpeed:j,tiltIntervalMs:M}}))},[u]);return l.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsxs("div",{children:[l.jsx("p",{className:"text-xs text-slate-400",children:"Keyboard layout"}),l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Per-browser · click to change a binding"})]}),l.jsx("button",{type:"button",onClick:()=>r(),className:"button-dark text-xs",children:"Reset defaults"})]}),l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Keyboard speeds"}),l.jsxs("div",{className:"space-y-0.5",children:[l.jsx(Xi,{label:"Base speed",description:"Normal driving speed",value:f.baseSpeed,onChange:N=>k("baseSpeed",N)}),l.jsx(Xi,{label:"Turbo speed",description:"Used when holding the boost modifier",value:f.turboSpeed,onChange:N=>k("turboSpeed",N)}),l.jsx(Xi,{label:"Precision speed",description:"Used when holding the precision modifier",value:f.precisionSpeed,onChange:N=>k("precisionSpeed",N)})]})]}),l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Camera tilt"}),l.jsx(Xi,{label:"Tilt speed",description:"Higher = faster when holding tilt keys",value:h,onChange:E,min:1,max:100,step:1})]}),l.jsx("div",{className:"space-y-0.5",children:Object.entries(p).map(([N,w])=>l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.75rem] text-slate-400",children:N}),l.jsx("div",{className:"space-y-0.5",children:w.map(j=>{const M=S(j.id),A=g===j.id;return l.jsxs("div",{className:"surface-muted flex items-center justify-between text-xs",children:[l.jsxs("div",{children:[l.jsx("p",{className:"font-semibold text-slate-100",children:j.label}),l.jsx("p",{className:"text-[0.65rem] text-slate-400",children:Tt(M)})]}),l.jsxs("div",{className:"flex items-center gap-0.5",children:[A&&l.jsx("button",{type:"button",onClick:()=>v(),className:"button-danger text-[0.7rem]",children:"Cancel"}),l.jsx("button",{type:"button",onClick:()=>x(j.id),onKeyDown:A?R=>b(j.id,R):void 0,autoFocus:A,className:`${A?"px-0.5 py-0.5 bg-emerald-500 text-emerald-950 hover:bg-emerald-400":"button-dark"} text-[0.7rem] font-medium transition-colors`,children:A?"Press a key…":"Change"})]})]},j.id)})})]},N))})]})}const Pn=new Intl.NumberFormat(void 0,{maximumFractionDigits:2}),Ww=[{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"}],bx=.45,eA=.6;function ar({label:n,description:a,min:r,max:o,step:u,value:f,onChange:h}){return l.jsxs("label",{className:"surface-muted block p-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[l.jsx("span",{className:"font-semibold text-slate-100",children:n}),l.jsx("span",{className:"font-mono text-slate-400",children:Pn.format(f)})]}),a&&l.jsx("p",{className:"text-[0.65rem] text-slate-500",children:a}),l.jsx("input",{type:"range",min:r,max:o,step:u,value:f,onChange:p=>h(Number(p.target.value)),className:"mt-0 w-full accent-emerald-400"})]})}function tA(n){if(!n)return"Unassigned";if(n.kind==="axisPair"){const a=`${n.invertX?"X inv ":""}${n.invertY?"Y inv":""}`.trim();return`Axes ${n.x}/${n.y}${a?` (${a})`:""}`}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 a=n.direction<0?"<":">";return`Axis ${n.index} ${a} ${Pn.format(n.threshold??.6)}`}return"Unassigned"}function nA(n){return n.reduce((a,r)=>((a[r.section]||(a[r.section]=[])).push(r),a),{})}function sA(n,a){if(!n||n.length===0)return null;if(a){const r=n.find(o=>o.signature===a);if(r)return r}return n[0]}function aA(n){return{axes:Array.from(n.axes??[]),buttons:(n.buttons??[]).map(a=>({pressed:!!a?.pressed,value:typeof a?.value=="number"?a.value:a?.pressed?1:0}))}}function lA(n,a,r){const o=(n.axes??[]).map((f,h)=>({index:h,delta:Math.abs((f??0)-(a.axes?.[h]??0)),value:f}));if(o.sort((f,h)=>h.delta-f.delta),r.kind==="axisPair"){const f=o.filter(h=>h.delta>bx).slice(0,2);return f.length<2?null:{kind:"axisPair",x:f[0].index,y:f[1].index,...r.invertDefaults??{}}}const u=o.find(f=>f.delta>bx);return u?{kind:"axis",index:u.index,...r.invertDefaults??{}}:null}function rA(n,a,r){const o=n.buttons??[];for(let f=0;feA)return r.kind==="axis"?{kind:"buttonAxis",index:f}:{kind:"button",index:f}}const u=n.axes??[];for(let f=0;f.7&&p>.5)return r.kind==="axis"?{kind:"axis",index:f,...r.invertDefaults??{}}:{kind:"axisButton",index:f,direction:h>=0?1:-1,threshold:.6}}return null}function iA(n,a,r){if(!n)return null;if(r.kind==="axis"||r.kind==="axisPair"){const o=lA(n,a,r);if(o)return o}return rA(n,a,r)}function oA(){const n=AN(),{value:a,save:r}=Oe("gamepad",Es),[o,u]=m.useState(null),f=m.useRef(null),h=m.useMemo(()=>nA(Ww),[]),p=m.useMemo(()=>sA(n.pads,a.activeSignature),[n.pads,a.activeSignature]),g=p?.signature??null,x=m.useMemo(()=>g?a?.profiles?.[g]??a?.defaults?.profile??Wn:a?.defaults?.profile??Wn,[g,a?.defaults?.profile,a?.profiles]);m.useEffect(()=>{!p||!g||a?.profiles?.[g]||r(w=>{const j=w??Es;if(j.profiles?.[g])return j;const M=j?.defaults?.profile??Wn,A=Rg(p,M);return{...j,profiles:{...j.profiles??{},[g]:A}}})},[p,g,a?.profiles,r]),m.useEffect(()=>{f.current=null},[o,g]),m.useEffect(()=>{if(!o||!p)return;if(!f.current){f.current=aA(p);return}const w=iA(p,f.current,o);w&&(r(j=>{const M=j??Es,A=M.profiles?.[g]??M?.defaults?.profile??Wn,R={...A,bindings:{...A.bindings??{},[o.id]:{...A.bindings?.[o.id]??{},kind:o.kind,sources:[w]}}};return{...M,profiles:{...M.profiles??{},[g]:R}}}),u(null))},[p,g,o,r]);const v=m.useCallback(w=>{r(j=>({...j??Es,activeSignature:w||null}))},[r]),b=m.useCallback(w=>{r(j=>{const M=j??Es,A=(g&&M.profiles?.[g])??M?.defaults?.profile??Wn,R={...A,calibration:{...A.calibration??{},...w}};return g?{...M,profiles:{...M.profiles??{},[g]:R}}:{...M,defaults:{...M.defaults??{},profile:R}}})},[g,r]),S=m.useCallback((w,j)=>{r(M=>{const A=M??Es,R=(g&&A.profiles?.[g])??A?.defaults?.profile??Wn,q=j(R.bindings?.[w]??{}),$={...R,bindings:{...R.bindings??{},[w]:q}};return g?{...A,profiles:{...A.profiles??{},[g]:$}}:{...A,defaults:{...A.defaults??{},profile:$}}})},[g,r]),k=m.useCallback(w=>{S(w.id,j=>({...j,sources:[]}))},[S]),E=m.useCallback((w,j)=>{S(w.id,M=>{const A=Array.isArray(M.sources)?[...M.sources]:[];if(!A[0])return M;const R={...A[0]};return j==="invertX"?R.invertX=!R.invertX:j==="invertY"?R.invertY=!R.invertY:R.invert=!R.invert,A[0]=R,{...M,sources:A}})},[S]),N=m.useMemo(()=>p?{outputs:Tg(p,x)}:null,[p,x]);return l.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[l.jsx("div",{className:"flex items-center justify-between",children:l.jsxs("div",{children:[l.jsx("p",{className:"text-xs text-slate-400",children:"Controller"}),l.jsx("p",{className:"text-[0.65rem] text-slate-500",children:p?"Move sticks or press buttons to bind":"Connect a controller to configure."}),o&&l.jsxs("p",{className:"mt-0 text-[0.7rem] text-emerald-400",children:["Capturing ",o.label,"…"]})]})}),l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Connected controller"}),n.pads.length===0?l.jsx("p",{className:"text-[0.7rem] text-slate-400",children:"No controller detected."}):l.jsxs("div",{className:"flex items-center justify-between gap-0.5 text-xs",children:[l.jsx("select",{value:g??"",onChange:w=>v(w.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(w=>l.jsx("option",{value:w.signature,children:w.id||"Unknown controller"},w.signature))}),l.jsx("span",{className:"text-[0.7rem] text-slate-400",children:p?.mapping??"unknown"})]})]}),l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Calibration"}),l.jsxs("div",{className:"space-y-0.5",children:[l.jsx(ar,{label:"Drive deadzone",description:"Ignore small drive stick drift",min:0,max:.6,step:.01,value:x.calibration?.driveDeadzone??.18,onChange:w=>b({driveDeadzone:w})}),l.jsx(ar,{label:"Camera deadzone",description:"Ignore small camera tilt drift",min:0,max:.4,step:.01,value:x.calibration?.cameraDeadzone??.08,onChange:w=>b({cameraDeadzone:w})}),l.jsx(ar,{label:"Aux deadzone",description:"Ignore small trigger noise",min:0,max:.4,step:.01,value:x.calibration?.auxDeadzone??.05,onChange:w=>b({auxDeadzone:w})}),l.jsx(ar,{label:"Side brush scale",description:"Scale side brush output",min:.3,max:1,step:.05,value:x.calibration?.auxSideScale??.55,onChange:w=>b({auxSideScale:w})}),l.jsxs("label",{className:"surface-muted block p-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[l.jsx("span",{className:"font-semibold text-slate-100",children:"Camera mode"}),l.jsxs("select",{value:x.calibration?.cameraMode??"absolute",onChange:w=>b({cameraMode:w.target.value}),className:"rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:[l.jsx("option",{value:"absolute",children:"Absolute"}),l.jsx("option",{value:"velocity",children:"Velocity"})]})]}),l.jsx("p",{className:"text-[0.65rem] text-slate-500",children:"Absolute maps stick to angle; velocity moves over time."})]}),l.jsx(ar,{label:"Camera sensitivity",description:"Velocity mode degrees per second",min:10,max:180,step:5,value:x.calibration?.cameraSensitivity??60,onChange:w=>b({cameraSensitivity:w})})]})]}),l.jsx("div",{className:"space-y-0.5",children:Object.entries(h).map(([w,j])=>l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:w}),l.jsx("div",{className:"space-y-0.5",children:j.map(M=>{const R=x.bindings?.[M.id]?.sources?.[0]??null;return l.jsxs("div",{className:"surface-muted flex items-center justify-between text-xs",children:[l.jsxs("div",{children:[l.jsx("p",{className:"font-semibold text-slate-100",children:M.label}),l.jsx("p",{className:"text-[0.65rem] text-slate-400",children:tA(R)})]}),l.jsxs("div",{className:"flex items-center gap-0.5",children:[M.kind==="axisPair"&&l.jsxs(l.Fragment,{children:[l.jsx("button",{type:"button",disabled:!R,onClick:()=>E(M,"invertX"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert X"}),l.jsx("button",{type:"button",disabled:!R,onClick:()=>E(M,"invertY"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert Y"})]}),M.kind==="axis"&&l.jsx("button",{type:"button",disabled:!R,onClick:()=>E(M),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert"}),l.jsx("button",{type:"button",onClick:()=>k(M),className:"button-dark text-[0.7rem]",children:"Clear"}),l.jsx("button",{type:"button",onClick:()=>u(M),className:`${o?.id===M.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===M.id?"Waiting…":"Capture"})]})]},M.id)})})]},w))}),l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Diagnostics"}),p?l.jsxs("div",{className:"space-y-0.5 text-[0.7rem] text-slate-300",children:[l.jsx("p",{className:"text-slate-400",children:"Raw axes"}),l.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:p.axes.map((w,j)=>l.jsxs("span",{className:"font-mono text-slate-300",children:["A",j,": ",Pn.format(w)]},`axis-${j}`))}),l.jsx("p",{className:"text-slate-400",children:"Raw buttons"}),l.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:p.buttons.map((w,j)=>l.jsxs("span",{className:"font-mono text-slate-300",children:["B",j,": ",Pn.format(w.value)," ",w.pressed?"●":""]},`btn-${j}`))}),N?.outputs&&l.jsxs(l.Fragment,{children:[l.jsx("p",{className:"text-slate-400",children:"Mapped outputs"}),l.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[l.jsxs("span",{className:"font-mono text-slate-300",children:["Drive: ",Pn.format(N.outputs.driveVector.x),","," ",Pn.format(N.outputs.driveVector.y)]}),l.jsxs("span",{className:"font-mono text-slate-300",children:["Camera: ",Pn.format(N.outputs.cameraAxis)]}),l.jsxs("span",{className:"font-mono text-slate-300",children:["Main: ",Pn.format(N.outputs.auxAxis.main)]}),l.jsxs("span",{className:"font-mono text-slate-300",children:["Side: ",Pn.format(N.outputs.auxAxis.side)]})]})]})]}):l.jsx("p",{className:"text-[0.7rem] text-slate-400",children:"No controller detected."})]})]})}const cA={drive:"Drive wheels",aux:"Aux motors"};function yx(n){return Number.isFinite(n)?`${Math.round(n*100)}%`:"--"}function uA({value:n,color:a="bg-emerald-500"}){const r=`${Math.round(Math.max(0,Math.min(1,n))*100)}%`;return l.jsx("div",{className:"h-2 w-full overflow-hidden rounded bg-slate-800",children:l.jsx("div",{className:`h-full ${a}`,style:{width:r}})})}function dA(){const{state:{roverId:n},overcurrentLimiter:a}=yt(),r=m.useMemo(()=>Ws.map(o=>o.key),[]);return l.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[l.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[l.jsx("span",{children:"Overcurrent limiter"}),l.jsx("span",{children:a?.adminImmune?"Admin immune":"Active"})]}),n?l.jsxs("div",{className:"space-y-0.5",children:[r.map(o=>{const u=a?.caps?.[o]?.cap??0,f=a?.overcurrent?.groups?.[o]??!1,h=a?.scales?.perGroup?.[o]??1;return l.jsxs("div",{className:"surface space-y-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between text-xs",children:[l.jsx("span",{className:"text-slate-200",children:cA[o]||o}),l.jsx("span",{className:f?"text-red-300":"text-slate-400",children:f?"overcurrent":"ok"})]}),l.jsxs("div",{className:"space-y-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between text-[0.7rem] text-slate-400",children:[l.jsx("span",{children:"Cap"}),l.jsx("span",{children:yx(u)})]}),l.jsx(uA,{value:u,color:"bg-amber-500"}),l.jsxs("div",{className:"flex items-center justify-between text-[0.7rem] text-slate-400",children:[l.jsx("span",{children:"Scale"}),l.jsx("span",{children:yx(h)})]})]})]},o)}),l.jsxs("div",{className:"surface text-[0.7rem] text-slate-400",children:[l.jsx("div",{children:`Down rate ${a?.config?.downRatePerSec}/s · Up rate ${a?.config?.upRatePerSec}/s`}),l.jsx("div",{children:`Release delay ${a?.config?.releaseDelaySec}s`}),l.jsx("div",{children:`Output rate ${a?.config?.outputRateMs}ms`})]})]}):l.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to view limiter status."})]})}const Pg=m.createContext(null);function Wg(){const n=m.useContext(Pg);if(!n)throw new Error("Tabs components must be used within a provider.");return n}const Sx={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"}},jx={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"},ev="primary";function gf(...n){return n.filter(Boolean).join(" ")}function vf({children:n,defaultTab:a,currentTab:r,onTabChange:o,variant:u=ev}){const[f,h]=m.useState(a??null),[p,g]=m.useState([]),x=m.useCallback(k=>k?(g(E=>E.includes(k)?E:[...E,k]),()=>g(E=>E.includes(k)?E.filter(N=>N!==k):E)):()=>{},[]),v=r??f??p[0]??null;m.useEffect(()=>{a&&r===void 0&&h(a)},[a,r]);const b=m.useCallback(k=>{k&&(r===void 0&&h(k),o&&o(k))},[r,o]),S=m.useMemo(()=>({activeTab:v,setActiveTab:b,registerTab:x,variant:u}),[v,b,x,u]);return l.jsx(Pg.Provider,{value:S,children:n})}function bf({children:n,className:a=""}){return l.jsx("div",{className:gf("flex gap-0.5",a),children:n})}function Zt({id:n,children:a,className:r="",disabled:o=!1,highlight:u="none"}){const{activeTab:f,setActiveTab:h,registerTab:p,variant:g}=Wg();m.useEffect(()=>p(n),[n,p]);const x=Sx[g]??Sx[ev],v=f===n,b=jx[u]||jx.none,S=gf(x.base,b||(v?x.active:x.inactive),o?"cursor-not-allowed opacity-50":"",r);return l.jsx("button",{type:"button",className:S,onClick:()=>!o&&h(n),"aria-pressed":v,"aria-disabled":o,children:a})}function yf({children:n,className:a=""}){return l.jsx("div",{className:gf("mt-0 space-y-0.5",a),children:n})}function Jt({id:n,children:a,keepMounted:r=!1}){const{activeTab:o}=Wg(),u=o===n;return!u&&!r?null:!u&&r?l.jsx("div",{hidden:!0,"aria-hidden":"true",children:a}):l.jsx(l.Fragment,{children:a})}function fA(){const{session:n}=yr(),a=m.useMemo(()=>JSON.stringify(n??{},null,2),[n]);return l.jsxs("div",{className:"panel-section flex min-h-0 flex-col gap-0.5 text-xs",children:[l.jsx("p",{className:"text-sm text-slate-400",children:"Session snapshot"}),l.jsx("pre",{className:"surface min-h-0 flex-1 overflow-y-auto font-mono text-[0.7rem] text-lime-300",children:a})]})}const Nx=300,wx=240;function mA(n,a){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(a||(a=new WeakSet),typeof n=="object"){if(a.has(n))return"[Circular]";a.add(n)}try{return JSON.stringify(n,(r,o)=>typeof o=="function"?"[Function]":o instanceof Error?`${o.name}: ${o.message}`:o,0)}catch{return String(n)}}function hA(n){const r=n.map(o=>mA(o)).join(" ");return r.length>wx?`${r.slice(0,wx)}…`:r}function pA(){const n=Pt(),[a,r]=m.useState([]),o=m.useMemo(()=>a.reduce((u,f)=>(f.direction==="in"&&(u.in+=1),f.direction==="out"&&(u.out+=1),u),{in:0,out:0}),[a]);return m.useEffect(()=>{const u=(g,x,v)=>{r(b=>{const S=[...b,{id:`${Date.now()}-${Math.random().toString(16).slice(2)}`,direction:g,event:x,payload:hA(v),timestamp:new Date().toLocaleTimeString()}];return S.length>Nx?S.slice(-Nx):S})},f=(g,...x)=>u("in",g,x),h=(g,...x)=>u("out",g,x);n.onAny(f);let p=null;if(typeof n.onAnyOutgoing=="function")n.onAnyOutgoing(h),p=()=>n.offAnyOutgoing(h);else{const g=n.emit.bind(n);n.emit=(x,...v)=>(h(x,...v),g(x,...v)),p=()=>{n.emit=g}}return()=>{n.offAny(f),p&&p()}},[n]),l.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[l.jsxs("div",{className:"flex items-center justify-between text-sm text-slate-400",children:[l.jsx("span",{children:"Socket activity"}),l.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-400",children:[l.jsxs("span",{className:"surface-muted",children:["in ",o.in]}),l.jsxs("span",{className:"surface-muted",children:["out ",o.out]}),l.jsx("button",{type:"button",className:"button-dark text-xs",onClick:()=>r([]),children:"Clear"})]})]}),l.jsx("div",{className:"surface h-64 overflow-y-auto font-mono text-xs",children:a.length===0?l.jsx("p",{children:"No socket activity yet."}):a.slice().reverse().map(u=>l.jsxs("div",{className:"surface",children:[l.jsx("span",{className:"text-slate-400",children:u.timestamp})," ",l.jsxs("span",{className:u.direction==="in"?"text-emerald-400":"text-amber-400",children:["[",u.direction.toUpperCase(),"]"]})," ",l.jsx("span",{className:"text-cyan-300",children:u.event})," ",l.jsx("span",{className:"text-slate-200",children:u.payload})]},u.id))}),l.jsx("p",{className:"text-xs text-slate-500",children:"Logs are collected only while this panel is open."})]})}const xA=[{key:"start",label:"Start OI"},{key:"safe",label:"Safe"},{key:"full",label:"Full"},{key:"passive",label:"Passive"},{key:"dock",label:"Dock"}];function tv(){const{state:{roverId:n},actions:{sendOiCommand:a,setSensorStream:r}}=yt(),o=!!n,[u,f]=Gg(),h=Pt(),{value:p,save:g}=Oe("page",{hudMapDesktop:!1,connectionTransport:"websocket",swapMobileControlColumns:!1,driveMacroBackoffEnabled:!0}),{value:x,save:v}=Oe("audio",it),b=p?.connectionTransport||"websocket",S=!!p?.swapMobileControlColumns,k=typeof p?.driveMacroBackoffEnabled=="boolean"?p.driveMacroBackoffEnabled:!0,E=Number.isFinite(x?.masterVolume)?x.masterVolume:it.masterVolume,N=Number.isFinite(x?.alertVolume)?x.alertVolume:it.alertVolume,w=Number.isFinite(x?.roverVolume)?x.roverVolume:it.roverVolume,j=typeof x?.mainBrushDuckEnabled=="boolean"?x.mainBrushDuckEnabled:typeof x?.autoLevelEnabled=="boolean"?x.autoLevelEnabled:it.mainBrushDuckEnabled,M=Number.isFinite(x?.mainBrushDuckAmount)?Math.max(0,Math.min(1,x.mainBrushDuckAmount)):it.mainBrushDuckAmount,A=m.useMemo(()=>[{key:"start",label:"Enable stream",enable:!0},{key:"stop",label:"Disable stream",enable:!1}],[]),R=U=>{n&&r(U)},q=U=>{const Z=U.target.value;g(Q=>({...Q??{},connectionTransport:Z})),h?.io?.opts&&(h.io.opts.transports=Z==="polling"?["polling"]:["websocket","polling"],h.disconnect(),h.connect())},$=U=>Z=>{const Q=Number(Z.target.value),ce=Number.isFinite(Q)?Math.max(0,Math.min(1,Q)):0;v(ue=>({...ue??{},[U]:ce}))},O=U=>{const Z=!!U.target.checked;v(Q=>({...Q??{},mainBrushDuckEnabled:Z}))},L=U=>{const Z=!!U.target.checked;g(Q=>({...Q??{},swapMobileControlColumns:Z}))},F=U=>{const Z=!!U.target.checked;g(Q=>({...Q??{},driveMacroBackoffEnabled:Z}))};return l.jsxs(vf,{defaultTab:"keybindings",children:[l.jsxs(bf,{children:[l.jsx(Zt,{id:"keybindings",children:"Keybindings"}),l.jsx(Zt,{id:"controller",children:"Controller"}),l.jsx(Zt,{id:"page",children:"Page settings"}),l.jsx(Zt,{id:"admin",children:"Admin"})]}),l.jsxs(yf,{children:[l.jsx(Jt,{id:"keybindings",children:l.jsx("div",{className:"space-y-0.5",children:l.jsx(Pw,{})})}),l.jsx(Jt,{id:"controller",children:l.jsx("div",{className:"space-y-0.5",children:l.jsx(oA,{})})}),l.jsx(Jt,{id:"page",children:l.jsxs("div",{className:"space-y-0.5",children:[l.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[l.jsx("p",{className:"text-slate-400",children:"HUD"}),l.jsxs("label",{className:"flex items-center gap-0.5 text-slate-200",children:[l.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:u,onChange:U=>f(U.target.checked)}),l.jsx("span",{children:"Show top-down map in HUD (desktop)"})]}),l.jsx("p",{className:"text-xs text-slate-500",children:"Mobile HUD keeps the map on by default."})]}),l.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[l.jsx("p",{className:"text-slate-400",children:"Mobile controls"}),l.jsxs("label",{className:"flex items-center gap-0.5 text-slate-200",children:[l.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:S,onChange:L}),l.jsx("span",{children:"Swap control columns (put joystick on the left)"})]})]}),l.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[l.jsx("p",{className:"text-slate-400",children:"Macros"}),l.jsxs("label",{className:"flex items-center gap-0.5 text-slate-200",children:[l.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:k,onChange:F}),l.jsx("span",{children:"Enable backward bump in drive macro"})]})]}),l.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[l.jsx("p",{className:"text-slate-400",children:"Audio"}),l.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[l.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[l.jsx("span",{children:"Master volume"}),l.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(E*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:E,onChange:$("masterVolume"),className:"w-full accent-emerald-500"})]}),l.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[l.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[l.jsx("span",{children:"Alert/page sounds"}),l.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(N*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:N,onChange:$("alertVolume"),className:"w-full accent-emerald-500"})]}),l.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[l.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[l.jsx("span",{children:"Rover audio"}),l.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(w*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:w,onChange:$("roverVolume"),className:"w-full accent-emerald-500"})]}),l.jsxs("label",{className:"flex items-center justify-between gap-0.5 text-slate-200",children:[l.jsx("span",{children:"Main brush ducking"}),l.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:j,onChange:O})]}),l.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[l.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[l.jsx("span",{children:"Main brush duck amount"}),l.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(M*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:M,onChange:$("mainBrushDuckAmount"),className:"w-full accent-emerald-500",disabled:!j})]}),l.jsx("p",{className:"text-xs text-slate-500",children:"Lowers rover audio only while the main brush is running."})]}),l.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[l.jsx("p",{className:"text-slate-400",children:"Connection"}),l.jsxs("label",{className:"flex items-center justify-between gap-0.5 text-slate-200",children:[l.jsx("span",{children:"Transport"}),l.jsxs("select",{value:b,onChange:q,className:"field-input text-sm",children:[l.jsx("option",{value:"websocket",children:"WebSocket"}),l.jsx("option",{value:"polling",children:"Polling"})]})]}),l.jsx("p",{className:"text-xs text-slate-500",children:"Switching reconnects your session."})]})]})}),l.jsx(Jt,{id:"admin",children:l.jsxs("div",{className:"space-y-0.5",children:[l.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[l.jsx("p",{className:"text-slate-400",children:"Manual OI commands"}),l.jsx("div",{className:"surface flex flex-wrap gap-0.5",children:xA.map(U=>l.jsx("button",{type:"button",onClick:()=>a(U.key),disabled:!o,className:"button-dark text-xs disabled:opacity-30",children:U.label},U.key))})]}),l.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[l.jsx("p",{className:"text-slate-400",children:"Sensor stream"}),l.jsx("div",{className:"surface flex gap-0.5",children:A.map(U=>l.jsx("button",{type:"button",onClick:()=>R(U.enable),disabled:!o,className:"flex-1 button-dark text-xs disabled:opacity-30",children:U.label},U.key))}),!o&&l.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to toggle streams."})]}),l.jsx(Jg,{}),l.jsx(dA,{}),l.jsx(Yw,{}),l.jsx(fA,{}),l.jsx(pA,{})]})})]})]})}const Ax={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:['If the rover shows "Docking in Progress", it is already auto-seeking the dock.',"To dock manually, enter Docking Assist from the drive panel.",{segments:['Press "Enter Docking Assist", or press ',{action:"dockMacro"},"."]},"In assist mode, camera tilts down and driving speed is limited for precise alignment."]}],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:"Docking assist toggle"},{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:['If you see "Docking in Progress", the rover is currently auto-seeking the dock.',{segments:['For manual docking, press "Enter Docking Assist".']},"Assist mode tilts camera down and limits speed for precise alignment."]}]},"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:['If you see "Docking in Progress", the rover is currently auto-seeking the dock.',{segments:['For manual docking, press "Enter Docking Assist" (or "Dock" button).']},"Assist mode tilts camera down and limits speed for precise alignment."]}]}};function gA(n){return Ax[n]||Ax.desktop}function nv({actionId:n,keymap:a}){const r=a?.[n]?.[0]??"";return l.jsx("span",{className:"rounded border border-slate-600 bg-slate-900/40 px-1 text-[0.7rem] text-slate-200",children:Tt(r)})}function Cx(n,a){return n.map((r,o)=>typeof r=="string"?l.jsx("span",{children:r},`text-${o}`):r?.action?l.jsx(nv,{actionId:r.action,keymap:a},`pill-${r.action}-${o}`):r?.text?l.jsx("span",{children:r.text},`span-${o}`):null)}function Mo(n,a,r){if(typeof n=="string")return n;if(Array.isArray(n?.segments))return Cx(n.segments,a);if(n&&typeof n=="object"){if(typeof n.text=="string")return n.text;if(Array.isArray(n))return Cx(n,a)}return String(n??"")}function vA({hero:n,keymap:a}){return n?l.jsxs("div",{className:"surface space-y-0.5 px-0.5 py-0.5",children:[l.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-0.5",children:[l.jsxs("div",{children:[l.jsx("p",{className:"text-sm font-semibold text-white",children:n.title}),n.subtitle&&l.jsx("p",{className:"text-xs text-slate-300",children:n.subtitle})]}),n.chips&&l.jsx("div",{className:"flex flex-wrap gap-0.5 text-[0.7rem] text-slate-200",children:n.chips.map(r=>l.jsx("span",{className:"rounded border border-slate-700 px-1 py-[2px]",children:r},r))})]}),n.bullets&&l.jsx("ul",{className:"space-y-0.5 text-[0.85rem] text-slate-200",children:n.bullets.map((r,o)=>{const u=Array.isArray(r?.segments)?`hero-${o}`:`hero-${o}`;return l.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:Mo(r,a)},u)})})]}):null}function bA({block:n,keymap:a}){return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-xs font-semibold text-slate-200",children:n.title}),l.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:n.items.map((r,o)=>{const u=`item-${o}`;return l.jsx("li",{className:"surface-muted flex flex-wrap items-center gap-0.5 px-0.5 py-0.25",children:Mo(r,a)},u)})})]})}function yA({block:n}){const a=n.tone==="info"?"border-cyan-500/40":"border-slate-700";return l.jsxs("div",{className:`surface space-y-0.5 border ${a} px-0.5 py-0.5`,children:[l.jsx("p",{className:"text-xs font-semibold text-slate-100",children:n.title}),n.body&&l.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:n.body.map((r,o)=>l.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:Mo(r,{})},`callout-${o}`))})]})}function SA({group:n,keymap:a}){return l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"px-0.5 py-0.25 text-[0.75rem] font-semibold text-slate-200",children:n.title}),l.jsx("div",{className:"space-y-0.5 px-0.5 pb-0.25",children:n.items.map(r=>l.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-0.5 py-0.25 text-[0.8rem]",children:[l.jsx("span",{className:"text-slate-200",children:r.label}),l.jsx(nv,{actionId:r.action,keymap:a})]},r.action))})]})}function jA({block:n,keymap:a}){return n?l.jsxs("div",{className:"space-y-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-200",children:[l.jsx("span",{className:"font-semibold",children:n.title}),n.footnote&&l.jsx("span",{className:"text-[0.7rem] text-slate-400",children:n.footnote})]}),l.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-0.5",children:n.groups?.map(r=>l.jsx(SA,{group:r,keymap:a},r.id))})]}):null}function NA({block:n}){return n?l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-xs font-semibold text-slate-200",children:n.title}),l.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:n.items?.map((a,r)=>l.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:Mo(a,{})},`gamepad-${r}`))})]}):null}function kx({block:n,keymap:a}){if(!n)return null;switch(n.type){case"list":return l.jsx(bA,{block:n,keymap:a});case"callout":return l.jsx(yA,{block:n});case"keyboard":return l.jsx(jA,{block:n,keymap:a});case"gamepad":return l.jsx(NA,{block:n});default:return null}}function sv({layout:n,keymap:a}){const r=m.useMemo(()=>gA(n),[n]),o=a||{},u=r.main||[],f=r.aside||[];return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx(vA,{hero:r.hero,keymap:o}),l.jsxs("div",{className:"grid gap-0.5",style:{gridTemplateColumns:"repeat(auto-fit, minmax(360px, 1fr))"},children:[l.jsx("div",{className:"space-y-0.5",children:u.map((h,p)=>l.jsx(kx,{block:h,keymap:o},h.title||p))}),l.jsx("div",{className:"space-y-0.5",children:f.map((h,p)=>l.jsx(kx,{block:h,keymap:o},h.title||p))})]})]})}function av({layout:n,onOpenOverlay:a}){const{state:r}=yt();return l.jsxs("section",{className:"panel-section space-y-0.5 text-sm",children:[l.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[l.jsx("span",{children:"Help"}),l.jsx("button",{type:"button",onClick:a,className:"button-dark px-1 py-0.25 text-[0.75rem]",children:"Open full help"})]}),l.jsx(sv,{layout:n,keymap:r?.keymap||{}})]})}const wA=["kal","rms","slt","ksp","bdl"],AA=Array.from({length:10},(n,a)=>a*10);function Ro({hideInput:n=!1,hideSpectatorNotice:a=!1,fillHeight:r=!1,allowSpectatorInput:o=!1}){const u=oe(D=>D.session?.role||null),f=oe(D=>D.session?.assignment?.roverId||null),h=oe(D=>D.session?.roster??[]),{messages:p,typing:g,sendMessage:x,registerInputRef:v,onInputFocus:b,onInputBlur:S,blurChat:k,setTypingActive:E}=rf(),{value:N,save:w}=Oe("tts",{engine:"flite",voice:"rms",pitch:50}),[j,M]=m.useState(""),[A,R]=m.useState(!1),[q,$]=m.useState(!1),[O,L]=m.useState(()=>N?.engine||"flite"),[F,U]=m.useState(()=>N?.voice||"rms"),[Z,Q]=m.useState(()=>Number.isFinite(N?.pitch)?N.pitch:50),ce=u!=="spectator"||o,ue=m.useRef(null),B=!!m.useMemo(()=>h.find(D=>String(D.id)===String(f))||null,[f,h])?.audio?.ttsEnabled,V=m.useMemo(()=>p.slice(-200),[p]),I=m.useMemo(()=>g||[],[g]);m.useEffect(()=>{ue.current&&(ue.current.scrollTop=ue.current.scrollHeight)},[V,I]),m.useEffect(()=>{const D=N?.engine||"flite",K=N?.voice||"rms",ae=Number.isFinite(N?.pitch)?N.pitch:50;O!==D&&L(D),F!==K&&U(K),Z!==ae&&Q(ae)},[O,Z,N?.engine,N?.pitch,N?.voice,F]),m.useEffect(()=>{$(!!B)},[B]);const J=m.useMemo(()=>!B||!q?null:O==="espeak"?{speak:!0,engine:O,pitch:Z}:{speak:!0,engine:O,voice:F},[O,Z,q,B,F]);async function ie(D){if(D.preventDefault(),!ce||n)return;const ae=j.replace(/\\n/g,` -`).trim();if(ae){R(!0);try{await x(ae,J),M(""),k(),E(!1)}catch(he){alert(he.message)}finally{R(!1)}}}const T=r?"flex-1 min-h-0 overflow-y-auto":"h-48 overflow-y-auto";return l.jsxs("section",{className:`panel-section space-y-0.5 text-base ${r?"flex h-full flex-col overflow-hidden":""}`,children:[l.jsxs("div",{className:`surface overflow-y-auto space-y-0.5 px-0 ${T}`,ref:ue,children:[V.length===0&&I.length===0?l.jsx("p",{className:"text-sm text-slate-500",children:"No messages yet."}):V.map(D=>l.jsx(sf,{message:D},D.id)),I.map(D=>l.jsx(bg,{message:D},`typing-${D.typingId||D.id}`))]}),!n&&l.jsxs("form",{className:"flex flex-wrap items-stretch gap-0.5",onSubmit:ie,children:[l.jsx("input",{className:"field-input flex-1 min-w-[10rem]",value:j,onChange:D=>{const K=D.target.value;M(K),E(!!K.trim())},onFocus:D=>{b(D),E(!!j.trim())},onBlur:D=>{S(D),E(!1)},onKeyDown:D=>{D.key==="Enter"&&!j.trim()&&(D.preventDefault(),k(),E(!1))},ref:D=>v(D,{target:"panel"}),placeholder:ce?"Type a message…":a?"":"Spectators cannot chat",disabled:!ce}),B&&l.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 basis-full sm:basis-auto",children:[l.jsxs("label",{className:"flex items-center gap-0.5 text-xs text-slate-300",children:[l.jsx("input",{type:"checkbox",checked:q,onChange:D=>$(D.target.checked),className:"accent-cyan-500"}),l.jsx("span",{children:"Speak"})]}),l.jsxs("select",{value:O,onChange:D=>{const K=D.target.value;L(K),w(ae=>({...ae||{},engine:K}))},className:"field-input text-xs",children:[l.jsx("option",{value:"flite",children:"flite"}),l.jsx("option",{value:"espeak",children:"espeak"})]}),O==="flite"?l.jsx("select",{value:F,onChange:D=>{const K=D.target.value;U(K),w(ae=>({...ae||{},voice:K}))},className:"field-input text-xs",children:wA.map(D=>l.jsx("option",{value:D,children:D},D))}):l.jsx("select",{value:Z,onChange:D=>{const K=Number(D.target.value);Q(K),w(ae=>({...ae||{},pitch:K}))},className:"field-input text-xs",children:AA.map(D=>l.jsxs("option",{value:D,children:["pitch ",D]},D))})]}),l.jsx("button",{type:"submit",disabled:!ce||A,className:"button-dark h-full disabled:opacity-50 self-stretch",children:A?"...":"Send"})]})]})}function ll({compact:n=!1}){const a=oe(b=>b.session?.role||null),{setNickname:r}=Ot(),{value:o,save:u}=Oe("profile",{nickname:""}),[f,h]=m.useState(o.nickname||""),[p,g]=m.useState(!1),x=a!=="spectator";m.useEffect(()=>{h(o.nickname||"")},[o.nickname]);async function v(b){if(b.preventDefault(),!x)return;const S=(f||"").trim().slice(0,32);if(S){g(!0);try{await r(S),u({nickname:S})}catch(k){alert(k.message)}finally{g(!1)}}}return l.jsxs("form",{className:"grid w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-stretch gap-0.5",onSubmit:v,children:[l.jsx("input",{className:"field-input flex-1 min-w-0",value:f,onChange:b=>h(b.target.value),maxLength:32,placeholder:"Enter a nickname",disabled:!x}),l.jsx("button",{type:"submit",disabled:!x||p,className:"button-dark h-full shrink-0 whitespace-nowrap px-0.5 py-0 disabled:opacity-50",children:p?"Saving…":n?"Set":"Save"})]})}function CA({socials:n,discordInvite:a,kofiLink:r}){const o=Array.isArray(n)?n:null;if(o&&o.length)return o;const u=[];return a&&u.push({id:"discord",label:"Discord",url:a}),r&&u.push({id:"kofi",label:"Ko-fi",url:r}),u}function kA(n){if(!n||typeof n!="object")return null;const a=n.id||n.service||n.key||n.name||null,r=n.label||n.title||n.name||a||"Link",o=n.url||n.link||n.href||null;return o?{id:a,label:r,url:o}:null}function Sf({className:n=""}){const a=oe(o=>({socials:o.session?.socials??[],discordInvite:o.session?.discord?.invite||null,kofiLink:o.session?.kofi?.link||null})),r=CA(a).map(kA).filter(Boolean).slice(0,4);return r.length?l.jsx("div",{className:`grid grid-cols-2 grid-rows-2 gap-0.5 ${n}`,children:r.map(o=>l.jsx(Ao,{...o},`${o.id||o.label}-${o.url}`))}):null}function EA({compact:n=!1}){return l.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:l.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:l.jsx(ll,{compact:n})})})}function MA(){return l.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:l.jsx(Sf,{className:"flex-1 min-h-0"})})}function vd(...n){return n.filter(Boolean).join(" ")}function RA(n){const a=n?.batteryState?.percentDisplay;return a==null?"--":`${a}%`}function TA(n){return n?.batteryState?n.batteryState.urgentActive?"text-red-400":n.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function qA(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 DA(n,a){if(!n)return"";const r=n.nickname||n.socketId?.slice(0,6)||"unknown";return n.socketId&&n.socketId===a?`${r} (you)`:r}function To({title:n="Rovers"}){const a=oe(j=>j.session?.role||null),r=oe(j=>j.session?.roster??[]),o=oe(j=>j.session?.turnQueues??{}),u=oe(j=>j.session?.users??[]),f=oe(j=>j.session?.socketId||null),{requestControl:h}=Ot(),[p,g]=m.useState({}),[x,v]=m.useState(()=>Date.now()),b=m.useMemo(()=>a&&a!=="spectator",[a]),S=m.useMemo(()=>a==="admin"||a==="lockdown"||a==="lockdown-admin",[a]),k=m.useMemo(()=>Object.values(o||{}).some(j=>j?.deadline||j?.idleDeadline),[o]);m.useEffect(()=>{if(!k)return;const j=setInterval(()=>{v(Date.now())},1e3);return()=>clearInterval(j)},[k]);const E=m.useMemo(()=>{const j=new Set(r.map(A=>String(A.id))),M=Object.keys(o||{}).filter(A=>!j.has(String(A))).map(A=>({id:A,name:A,locked:!1,batteryState:null}));return[...r,...M]},[r,o]);async function N(j){if(j){g(M=>({...M,[j]:!0}));try{await h(j)}catch(M){alert(M.message)}finally{g(M=>({...M,[j]:!1}))}}}const w=j=>u.find(M=>M.socketId===j)||{socketId:j,nickname:null,role:null};return l.jsxs("div",{className:"space-y-0.5 text-sm",children:[n&&l.jsx("p",{className:"text-sm text-slate-400",children:n}),E.length===0?l.jsx("p",{className:"text-sm text-slate-500",children:"No rovers registered."}):l.jsx("ul",{className:"space-y-0.5 text-sm",children:E.map(j=>{const M=String(j.id),A=o?.[M]||null,R=A?.queue||[],q=A?.idleDeadline||A?.deadline||null,$=q&&q>x?Math.ceil((q-x)/1e3):q?0:null,O=A?.current||null,L=O?R.findIndex(J=>J===O):-1,F=R.length>1?L>=0?R[(L+1)%R.length]:R[0]:null,U=!!(f&&O&&O===f),Z=!!(f&&F&&F===f),Q=$!=null&&(U||Z),ce=!!j.locked,ue=ce&&!S,le=!!(j?.private?.enabled&&j?.private?.open),B=j.lockReason?`locked: ${j.lockReason}`:"locked",V=ce?B:p[M]?"...":"request",I=b&&!ue&&!p[M];return l.jsxs("li",{className:vd("surface flex flex-wrap items-start justify-between gap-0.5",I&&"cursor-pointer",ce?"bg-red-900/40":le?"bg-amber-700/35 border border-amber-200/30":null),onClick:()=>{I&&N(j.id)},children:[l.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[l.jsxs("div",{className:"flex min-w-0 items-center gap-0.5",children:[l.jsxs("p",{className:"min-w-0 flex items-center gap-0.5 whitespace-nowrap text-slate-200",children:[l.jsx("span",{className:"rounded px-1 py-[1px] border border-transparent",style:ns(j.color,.16),children:j.name}),j.description?l.jsx("span",{className:"min-w-0 flex-1 truncate text-[0.7rem] text-slate-400",children:j.description}):null]}),Q?l.jsx("span",{className:"rounded bg-slate-800 px-1 text-[0.7rem] text-slate-200",children:U?`${$}s left`:`Your turn in ${$}s`}):null]}),l.jsx("span",{className:vd("text-[0.75rem] font-semibold",TA(j)),children:RA(j)})]}),R.length===0?l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"No queue."}):l.jsx("div",{className:"flex flex-wrap items-center gap-0.5",children:R.map((J,ie)=>{const T=w(J),D=J===O,K=!!(F&&J===F&&!D),ae=D?"bg-sky-600 text-white ring-2 ring-amber-300":K?"bg-emerald-700/60 text-emerald-100 ring-1 ring-emerald-300/70":"bg-slate-800 text-slate-200";return l.jsxs("span",{className:`flex items-center gap-0.5 rounded px-1 text-[0.7rem] ${ae}`,children:[l.jsx("span",{className:`${qA(T.role)} font-semibold`,children:DA(T,f)}),D&&l.jsx("span",{className:"text-[0.65rem] text-slate-200",children:"now"}),K&&l.jsx("span",{className:"text-[0.65rem] text-emerald-100",children:"next"})]},`${M}-${J}-${ie}`)})})]}),b?l.jsx("button",{type:"button",onClick:J=>{J.stopPropagation(),N(j.id)},disabled:p[M]||ue,className:vd("button-dark disabled:opacity-40",ce&&"bg-red-600/70 text-white hover:bg-red-600"),children:V}):null]},j.id)})})]})}function _A(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 OA(n,a){if(!n)return"";const r=n.nickname||n.socketId?.slice(0,6)||"unknown";return n.socketId&&n.socketId===a?`${r} (you)`:r}function vo({hideNicknameForm:n=!1,hideHeader:a=!1,className:r="",fillHeight:o=!1,compact:u=!1}){const f=oe(b=>b.session?.role||null),h=oe(b=>b.session?.users??[]),p=oe(b=>b.session?.socketId||null),g=f!=="spectator",x=m.useMemo(()=>[...h].sort((b,S)=>b.socketId===p?-1:S.socketId===p?1:(b.nickname||"").localeCompare(S.nickname||"")),[p,h]),v=o?"flex-1 min-h-0 overflow-y-auto":u?"h-28 overflow-y-auto":"h-48 overflow-y-auto";return l.jsxs("section",{className:`panel-section space-y-0.5 text-base ${o?"flex h-full min-h-0 flex-col overflow-hidden":""} ${r}`,children:[!n&&l.jsxs("div",{className:"space-y-0.5",children:[l.jsxs("div",{className:"grid gap-0.5 md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]",children:[l.jsx("div",{className:"min-w-0",children:l.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:l.jsx(ll,{compact:u})})}),l.jsx(Sf,{})]}),!g&&l.jsx("p",{className:"text-xs text-slate-500",children:"Spectators cannot set nicknames."})]}),l.jsxs("div",{className:`space-y-0.5 ${o?"flex flex-1 min-h-0 flex-col":""}`,children:[!a&&l.jsxs("div",{className:`flex items-center justify-between text-sm text-slate-400 ${u?"text-xs":""}`,children:[l.jsx("span",{children:"Users"}),l.jsx("span",{className:"text-xs text-slate-500",children:x.length})]}),l.jsx("div",{className:`surface flex flex-wrap content-start items-start gap-0.5 px-0 pb-0 ${v} ${u?"text-[0.8rem]":""}`,children:x.length===0?l.jsx("p",{className:"text-sm text-slate-500",children:"Waiting for users…"}):x.map(b=>l.jsx("span",{className:`rounded bg-slate-800/80 px-1 py-0.25 text-[0.7rem] ${_A(b.role)}`,children:OA(b,p)},b.socketId))})]})]})}const BA=/^cu_[a-f0-9]{32}$/,el="field-input w-full max-w-sm text-left focus:ring-emerald-500",Nr="mx-auto w-full max-w-xl flex justify-center",Ka="mx-auto flex w-full max-w-md flex-col items-center space-y-0.5 text-center";function LA(n){const a=String(n||"").trim();return a?a.length<=10?`${a.slice(0,2)}***${a.slice(-2)}`:`${a.slice(0,6)}...${a.slice(-6)}`:""}const Ex=8*1024*1024,Ps=16e3,zA={iceServers:[{urls:"stun:stun.l.google.com:19302"}],bundlePolicy:"max-bundle",rtcpMuxPolicy:"require"};function Mx(n){let a="";for(let o=0;o{if(!n||n.iceGatheringState==="complete"){r();return}const o=setTimeout(()=>{n.removeEventListener("icegatheringstatechange",u),r()},a);function u(){n.iceGatheringState==="complete"&&(clearTimeout(o),n.removeEventListener("icegatheringstatechange",u),r())}n.addEventListener("icegatheringstatechange",u)})}function Rx(n){if(!n)return!1;const a=n.connectionState,r=n.iceConnectionState;return a==="connected"||r==="connected"||r==="completed"}function $A(n,a=1e4){return new Promise((r,o)=>{if(!n){o(new Error("Peer connection missing"));return}if(Rx(n)){r();return}const u=setTimeout(()=>{h(),o(new Error("Peer connection timeout"))},a),f=()=>{Rx(n)?(h(),r()):(n.connectionState==="failed"||n.connectionState==="closed"||n.iceConnectionState==="failed")&&(h(),o(new Error(`Peer connection ${n.connectionState||n.iceConnectionState}`)))};function h(){clearTimeout(u),n.removeEventListener("connectionstatechange",f),n.removeEventListener("iceconnectionstatechange",f)}n.addEventListener("connectionstatechange",f),n.addEventListener("iceconnectionstatechange",f)})}async function GA(n){if(!n?.getParameters||!n?.setParameters)return;const a=n.getParameters()||{},r=a.encodings&&a.encodings[0]||{};a.encodings=[{...r,maxBitrate:64e3,dtx:"disabled"}];try{await n.setParameters(a)}catch{}}function YA(n,a=6e3){return new Promise((r,o)=>{if(!n){o(new Error("Peer connection missing"));return}const u=Date.now();let f=-1;const h=setInterval(async()=>{if(Date.now()-u>a){clearInterval(h),o(new Error("WHIP connected but no outbound audio flow"));return}try{const p=n.getSenders().filter(g=>g.track?.kind==="audio");for(const g of p){const x=await g.getStats();for(const v of x.values()){if(v.type!=="outbound-rtp"||v.kind!=="audio")continue;const b=Number(v.bytesSent||0),S=Number(v.packetsSent||0);if(f<0)f=b;else if(b>f+200||S>5){clearInterval(h),r();return}}}}catch{}},250)})}function FA(n){const a=n.reduce((u,f)=>u+f.length,0),r=new Float32Array(a);let o=0;for(const u of n)r.set(u,o),o+=u.length;return r}function IA(n){const a=new Int16Array(n.length);for(let r=0;r{for(let v=0;vv&&a?.[v]||null,[a,v]),K=!!(D&&D.state!=="offline"&&!D.error),ae=D?.source==="upload"&&D?.state==="playing",he=D?.source==="mic-whip"&&(D?.state==="starting"||D?.state==="playing"),we=!!(!j&&F.current&&(N||T)),pe=!j&&(M==="live"||M==="starting"),ge=j&&R==="recording",xe=j&&R==="sending",ye=Tt(p?.keymap?.micPtt?.[0])||"M",Ce=m.useCallback(re=>{const de=re==="clip"?"clip":"live";x(fe=>({...fe||{},pttMode:de}))},[x]),qe=async()=>{if(!v){O("Take control of your rover first.");return}if(!b){O("Select an audio file first.");return}if(b.size>Ex){O(`File too large (max ${Ex} bytes).`);return}E(!0),O("");try{const re=await b.arrayBuffer(),de=Mx(new Uint8Array(re));await r?.({roverId:v,name:b.name,mime:b.type||"",dataBase64:de}),O("Upload playback started.")}catch(re){O(re?.message||"Failed to play upload.")}finally{E(!1)}},Ze=async()=>{if(!v){O("Take control of your rover first.");return}E(!0),O("");try{await o?.(v),O("Upload playback stopped.")}catch(re){O(re?.message||"Failed to stop upload.")}finally{E(!1)}},Se=m.useCallback(async re=>{const de=String(re||Q.current||"").trim();if(Z.current=!1,A("idle"),U.current){try{U.current.getSenders().forEach(fe=>fe.track?.stop())}catch{}try{U.current.close()}catch{}}if(U.current=null,L.current)try{L.current.getTracks().forEach(fe=>fe.stop())}catch{}if(L.current=null,F.current=null,de)try{await h?.(de)}catch{}Q.current=""},[h]),at=m.useCallback(async re=>{const de=await u?.(re),fe=String(de?.whipUrl||"").trim(),Re=String(de?.token||"").trim();if(!fe||!Re)throw new Error("WHIP endpoint unavailable");const te=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:Ps,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}});L.current=te;const me=te.getAudioTracks()?.[0];if(F.current=me||null,me&&(me.enabled=!!(N||T)),me?.applyConstraints)try{await me.applyConstraints({channelCount:1,sampleRate:Ps,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1})}catch{}const _e=new RTCPeerConnection(zA);U.current=_e,_e.onconnectionstatechange=()=>{if(!Z.current)return;const Nt=_e.connectionState;if(Nt==="connected"){A("live");return}(Nt==="failed"||Nt==="disconnected"||Nt==="closed")&&(A("error"),O(`WHIP transport ${Nt}.`))},te.getAudioTracks().forEach(Nt=>{const Ae=_e.addTrack(Nt,te);GA(Ae)});const en=await _e.createOffer({offerToReceiveAudio:!1,offerToReceiveVideo:!1});await _e.setLocalDescription(en),await VA(_e,1800);const tn=await fetch(fe,{method:"POST",headers:{"Content-Type":"application/sdp",...HA(Re)},body:_e.localDescription?.sdp||en.sdp});if(!tn.ok)throw new Error(`WHIP request failed: ${tn.status}`);const nn=await tn.text();await _e.setRemoteDescription({type:"answer",sdp:nn}),await $A(_e,1e4),await YA(_e,6e3),await f?.(re)},[N,T,f,u]),ht=m.useCallback(async()=>{if(J.current=!1,B.current){try{B.current.disconnect()}catch{}B.current.onaudioprocess=null}if(le.current)try{le.current.disconnect()}catch{}if(V.current)try{V.current.disconnect()}catch{}if(ce.current)try{ce.current.getTracks().forEach(re=>re.stop())}catch{}if(ue.current)try{await ue.current.close()}catch{}I.current=[],B.current=null,le.current=null,V.current=null,ce.current=null,ue.current=null,ie.current=Ps},[]),Ge=m.useCallback(async()=>{if(ce.current&&ue.current&&B.current)return;q(me=>me==="recording"?me:"arming");const re=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:Ps,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}}),de=new AudioContext({latencyHint:"interactive"}),fe=de.createMediaStreamSource(re),Re=de.createScriptProcessor(1024,1,1),te=de.createGain();te.gain.value=0,Re.onaudioprocess=me=>{if(!J.current)return;const _e=me.inputBuffer.getChannelData(0);I.current.push(new Float32Array(_e))},fe.connect(Re),Re.connect(te),te.connect(de.destination),ce.current=re,ue.current=de,le.current=fe,B.current=Re,V.current=te,ie.current=de.sampleRate||Ps},[]),je=m.useCallback(async({send:re})=>{const de=J.current;if(J.current=!1,!de){re||q("idle");return}const fe=I.current;if(I.current=[],!re){q("idle");return}if(!v){q("idle");return}if(!fe.length){q("idle");return}q("sending");try{const Re=FA(fe),te=XA(Re,ie.current||Ps),me=Mx(te);await r?.({roverId:v,name:`ptt-${Date.now()}.wav`,mime:"audio/wav",dataBase64:me}),q("idle")}catch(Re){q("error"),O(Re?.message||"Failed to send PTT clip.")}},[r,v]),se=m.useCallback(async()=>{if(!v){O("Take control of your rover first.");return}await Ge(),I.current=[],J.current=!0,q("recording")},[Ge,v]);return m.useEffect(()=>{let re=!1;async function de(){if(j){await Se(Q.current||v);return}if(!v){await Se(Q.current);return}if(!(Z.current&&Q.current===v)&&!(!N&&!T))try{A("starting"),O(""),Z.current=!0,Q.current=v,await at(v),re||A("live")}catch(fe){re||(A("error"),O(fe?.message||"Failed to start mic forwarding.")),await Se(v)}}return de(),()=>{re=!0}},[j,N,T,v,at,Se]),m.useEffect(()=>{const re=F.current;re&&(re.enabled=!!(!j&&(N||T)))},[j,N,T]),m.useEffect(()=>{let re=!1;async function de(){if(!j||!v){J.current&&await je({send:!1}),q("idle");return}try{await Ge(),!re&&!J.current&&q("idle")}catch(fe){re||(q("error"),O(fe?.message||"Microphone access failed for clip mode."))}}return de(),()=>{re=!0}},[j,Ge,je,v]),m.useEffect(()=>{let re=!1;async function de(){if(j){if(!v){J.current&&await je({send:!1});return}if(T){if(!J.current)try{O(""),await se()}catch(fe){re||(q("error"),O(fe?.message||"Failed to start clip recording."))}}else J.current&&await je({send:!0})}}return de(),()=>{re=!0}},[j,je,T,v,se]),m.useEffect(()=>()=>{Se(Q.current||v),ht()},[v,Se,ht]),l.jsx("section",{className:"surface",children:l.jsxs("div",{className:"grid gap-1",children:[l.jsx("p",{className:"text-sm text-slate-100 text-center",children:"Audio Controls"}),l.jsx("section",{className:"surface",children:l.jsxs("div",{className:"flex items-center justify-center gap-0.5 py-0.25 text-xs text-slate-300",children:[l.jsx("span",{children:"Push-to-Talk Key"}),l.jsx(Ud,{label:ye})]})}),l.jsxs("div",{className:"grid gap-0.5 grid-cols-1 lg:grid-cols-2",children:[l.jsx("section",{className:"surface h-full",children:l.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr]",children:[l.jsx("p",{className:"text-sm text-slate-200 text-center",children:"PTT Mode"}),l.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."}),l.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[l.jsx("button",{type:"button",className:`button-dark w-full text-sm ${j?"":"bg-emerald-500 text-white hover:bg-emerald-500"}`,onClick:()=>Ce("live"),children:"Live Mic (WHIP)"}),l.jsx("button",{type:"button",className:`button-dark w-full text-sm ${j?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,onClick:()=>Ce("clip"),children:"Record Clip"})]})]})}),l.jsx("section",{className:"surface h-full",children:l.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_1fr]",children:[l.jsx("p",{className:"text-sm text-slate-200 text-center",children:"PTT Clip Status"}),l.jsxs("div",{className:"grid gap-0.5 content-start",children:[l.jsx(pn,{label:"Mode",active:j,detail:j?"record clip":"live mic"}),l.jsx(pn,{label:"PTT hold",active:T,detail:T?"held":"released"}),l.jsx(pn,{label:"Recording",active:ge,detail:ge?"capturing":"idle"}),l.jsx(pn,{label:"Playback",active:j&&ae,detail:j&&ae?"playing":"idle"})]})]})})]}),l.jsxs("div",{className:"grid gap-0.5 grid-cols-1 lg:grid-cols-2",children:[l.jsx("section",{className:"surface h-full",children:l.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr_auto]",children:[l.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Audio Upload"}),l.jsxs("label",{className:"mx-auto grid w-full max-w-sm gap-0.5 text-xs text-slate-300 text-center",children:[l.jsx("span",{children:"Audio file (mp3 / wav / ogg)"}),l.jsx("input",{className:`${el} text-center`,type:"file",accept:".mp3,.wav,.ogg,audio/mpeg,audio/wav,audio/ogg",disabled:k||!v,onChange:re=>S(re.target.files?.[0]||null)})]}),l.jsx("div",{className:"mx-auto w-full max-w-sm text-center",children:b?l.jsxs("div",{className:"surface-muted text-xs text-slate-300",children:[b.name," (",b.size," bytes)"]}):l.jsx("div",{className:"surface-muted text-xs text-slate-500",children:"No file selected"})}),l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:k||!v,onClick:qe,children:k?"Working...":"Play Upload"}),l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:k||!v,onClick:Ze,children:"Stop"})]})]})}),l.jsx("section",{className:"surface h-full",children:l.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr]",children:[l.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Live Microphone"}),l.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:[l.jsx("input",{type:"checkbox",checked:N,disabled:!v||j,onChange:re=>x(de=>({...de||{},openMicEnabled:!!re.target.checked}))}),l.jsx("span",{children:j?"Open mic (live mode only)":"Open mic"})]}),l.jsxs("div",{className:"grid gap-0.5 content-start",children:[l.jsx(pn,{label:"WHIP link",active:pe,detail:M}),l.jsx(pn,{label:"Mic hot",active:we,detail:we?"transmitting":"muted"}),l.jsxs("div",{className:"surface-muted text-center text-xs text-slate-400",children:["Hold ",l.jsx(Ud,{label:ye})," to talk"]})]})]})})]}),l.jsx("section",{className:"surface",children:l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Audio Status"}),l.jsxs("div",{className:"grid gap-0.5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-6",children:[l.jsx(pn,{label:"Forward pipe",active:K,detail:K?"connected":"offline"}),l.jsx(pn,{label:"Upload playback",active:ae,detail:ae?"playing":"idle"}),l.jsx(pn,{label:"Mic relay",active:he,detail:he?"active":"idle"}),l.jsx(pn,{label:"WHIP transport",active:pe,detail:M}),l.jsx(pn,{label:"Clip recording",active:ge,detail:ge?"recording":"idle"}),l.jsx(pn,{label:"Clip sending",active:xe,detail:xe?"sending":"idle"})]}),$?l.jsx("div",{className:"text-xs text-slate-400 text-center",children:$}):null]})})]})})}function KA({pendingRequestId:n,currentStoredKey:a,nickname:r,requestVerification:o,applyIdentityKey:u,onMessage:f,fullWidth:h=!1}){const[p,g]=m.useState(0),[x,v]=m.useState(""),[b,S]=m.useState(!1),[k,E]=m.useState(!1),N=()=>{g(1),v(a),S(!1),f?.("")},w=()=>{g(0),S(!1)},j=async A=>{if(A.preventDefault(),!b){f?.("Please confirm your nickname agreement before sending.");return}if(p<3){f?.("Complete all request steps before sending.");return}E(!0),f?.("");try{const R=await u(x);await o?.(),v(R),g(0),S(!1),f?.("Verification request sent to lockdown admins.")}catch(R){f?.(R.message||"Failed to submit request.")}finally{E(!1)}},M=h?"w-full":Nr;return n?l.jsx("section",{className:`surface text-sm text-slate-300 ${M}`,children:l.jsxs("div",{className:Ka,children:["Verification request pending: ",n]})}):p===0?l.jsx("section",{className:`surface ${M}`,children:l.jsxs("div",{className:Ka,children:[l.jsx("p",{className:"text-sm text-slate-300",children:"Verification"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:N,disabled:k,children:"Request Verification"})]})}):l.jsx("form",{className:`surface ${M}`,onSubmit:j,children:l.jsxs("div",{className:Ka,children:[l.jsx("p",{className:"text-sm text-slate-300",children:"Request verification"}),l.jsxs("p",{className:"text-xs text-slate-500",children:["Step ",p," of 3"]}),p===1?l.jsxs("div",{className:"space-y-0.5",children:[l.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."}),l.jsx("div",{className:"mx-auto w-full max-w-sm",children:l.jsx(ll,{compact:!0})}),l.jsxs("div",{className:"surface-muted mx-auto w-full max-w-sm text-xs text-slate-300 text-center",children:["Current nickname: ",l.jsx("span",{className:"font-semibold",children:r||"(not set)"})]}),l.jsxs("label",{className:"flex items-center justify-center gap-0.5 text-xs text-slate-300 text-center",children:[l.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:b,onChange:A=>S(A.target.checked)}),l.jsx("span",{children:"I understand this nickname is tied to my verification."})]}),l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!r||!b,onClick:()=>g(2),children:"Continue"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:w,children:"Cancel"})]})]}):null,p===2?l.jsxs("div",{className:"space-y-0.5",children:[l.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."}),l.jsx("input",{className:el,type:"password",name:"identity_key_request",autoComplete:"current-password",maxLength:35,value:x,onChange:A=>v(A.target.value.toLowerCase()),placeholder:"Identity key for this request"}),l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!x,onClick:()=>navigator.clipboard?.writeText(x),children:"Copy Key"}),l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!x,onClick:()=>g(3),children:"Continue"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>g(1),children:"Back"})]})]}):null,p===3?l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-xs text-slate-400",children:"Final step: confirm and send your verification request."}),l.jsx("input",{className:el,type:"password",name:"identity_key_request_final",autoComplete:"current-password",maxLength:35,value:x,onChange:A=>v(A.target.value.toLowerCase()),placeholder:"Identity key for this request"}),l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:k||!x,children:"Confirm Request"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>g(2),children:"Back"})]})]}):null]})})}function ZA({currentStoredKey:n,applyIdentityKey:a,onMessage:r,fullWidth:o=!1}){const[u,f]=m.useState(0),[h,p]=m.useState(""),[g,x]=m.useState(!1),v=()=>{f(1),p(""),r?.("")},b=()=>{f(0),p("")},S=async E=>{if(E.preventDefault(),!(u<2)){x(!0),r?.("");try{await a(h),f(0),p(""),r?.("Identity key restored.")}catch(N){r?.(N.message||"Failed to restore key.")}finally{x(!1)}}},k=o?"w-full":Nr;return l.jsx("section",{className:`surface ${k}`,children:l.jsxs("div",{className:Ka,children:[l.jsx("p",{className:"text-sm text-slate-300",children:"Identity key"}),l.jsxs("p",{className:"text-xs text-slate-500",children:["Current: ",LA(n)||"not set yet"]}),l.jsx("input",{className:el,type:"password",name:"identity_key_current",autoComplete:"current-password",maxLength:35,value:n,readOnly:!0,placeholder:"Identity key"}),u===0?l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!n,onClick:()=>navigator.clipboard?.writeText(n),children:"Copy Key"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:v,disabled:g,children:"Restore Key"})]}):null,u===1?l.jsxs("div",{className:"space-y-0.5",children:[l.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."}),l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(2),children:"Continue"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:b,children:"Cancel"})]})]}):null,u===2?l.jsxs("form",{className:"space-y-0.5",onSubmit:S,children:[l.jsx("input",{className:el,type:"password",name:"identity_key_restore",autoComplete:"current-password",maxLength:35,value:h,onChange:E=>p(E.target.value.toLowerCase()),placeholder:"Paste key to restore"}),l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:g||!h,children:"Confirm Restore"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(1),children:"Back"})]})]}):null]})})}function JA({requestableRovers:n=[],pendingRequests:a=[],requestPrivateRoverAccess:r,onMessage:o,fullWidth:u=!1}){const[f,h]=m.useState({}),p=m.useMemo(()=>{const v=new Map;return(a||[]).forEach(b=>{b?.roverId&&v.set(String(b.roverId),b)}),v},[a]),g=u?"w-full":Nr,x=async v=>{if(v){h(b=>({...b,[v]:!0})),o?.("");try{(await r?.(v))?.existing?o?.("You already have a pending request for that rover."):o?.("Private rover access request sent to lockdown admins.")}catch(b){o?.(b.message||"Failed to send request.")}finally{h(b=>({...b,[v]:!1}))}}};return l.jsx("section",{className:`surface text-sm text-slate-300 ${g}`,children:l.jsxs("div",{className:Ka,children:[l.jsx("p",{className:"text-sm text-slate-300",children:"Private rover access requests"}),n.length===0?l.jsx("p",{className:"text-xs text-slate-500",children:"No closed private rovers are available to request right now."}):l.jsx("div",{className:"w-full space-y-0.5",children:n.map(v=>{const b=String(v.id),S=!!f[b],k=p.get(b);return l.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-1 py-0.5",children:[l.jsxs("div",{className:"min-w-0 text-left",children:[l.jsx("p",{className:"truncate text-xs font-semibold text-slate-100",children:v.name||b}),l.jsx("p",{className:"truncate text-[0.7rem] text-slate-500",children:b})]}),k?l.jsx("span",{className:"rounded bg-amber-700/30 px-1 py-0.5 text-[0.7rem] text-amber-200",children:"Pending"}):l.jsx("button",{type:"button",onClick:()=>x(b),disabled:S,className:"button-dark text-xs disabled:opacity-50",children:S?"Sending...":"Request"})]},b)})})]})})}function io(n){return String(n||"").trim()}function PA(n){const a=io(n);return a?a.includes("DOCKINGRUNNING")?"Returning":a.includes("PAUSED")?"Paused":a.includes("HOUSECLEANINGRUNNING")?"Cleaning":a.includes("SPOTCLEANINGRUNNING")?"Spot cleaning":a.includes("STATE_START")?"Starting":a.includes("STATE_IDLE")||a.includes("STATE_STANDBY")?"Idle":a:"--"}function lv(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 Qi({label:n,value:a,tone:r="muted",valueClass:o="",hideLabel:u=!1}){return l.jsxs("div",{className:`rounded-md px-1 py-0.75 text-center ${lv(r)}`,children:[u?null:l.jsx("div",{className:"text-[0.72rem] opacity-90",children:n}),l.jsx("div",{className:`text-sm font-semibold ${o}`,title:String(a||""),children:a})]})}function WA({neato:n,onStart:a,onSendHome:r,onLocate:o,onClearErrors:u,onPowerCycle:f,fullWidth:h=!1}){const[p,g]=m.useState(""),x=h?"w-full":"w-full max-w-xl",v=!!n?.configured,b=!!(n?.enabled&&n?.connected),S=!!n?.telemetry?.extPowerPresent,k=!!n?.telemetry?.chargingActive,E=PA(n?.telemetry?.uiState),N=io(n?.telemetry?.robotState)||"--",w=n?.telemetry?.batteryPercent,j=Number.isFinite(w)?`${w}%`:"--",M=n?.telemetry?.batteryVoltage,A=Number.isFinite(M)?`${M.toFixed(2)} V`:"--",R=io(n?.telemetry?.robotError)||"--",q=io(n?.telemetry?.robotAlert)||"--",$=n?.controls||{},O=!!$?.start?.available,L=!!$?.sendHome?.available,F=!!$?.locate?.available,U=!!$?.clearErrors?.available,Z=!!$?.powerCycle?.available,Q=v&&b&&O,ce=v&&b&&L,ue=v&&b&&F,le=v&&b&&U,B=v&&b&&Z,V=async(D,K)=>{if(K){g(D);try{await K()}catch{}finally{g("")}}},I=v?b?"Online":"Offline":"Not configured",J=v?b?"good":"danger":"warn",ie=w==null?"muted":w>=60?"good":w>=25?"warn":"danger",T=S?"Docked":E!=="--"?E:"Away from dock";return l.jsx("section",{className:`surface text-sm text-slate-200 ${x}`,children:l.jsxs("div",{className:"grid gap-0.5",children:[l.jsxs("div",{className:"relative flex items-center justify-center min-h-[1.5rem]",children:[l.jsxs("div",{className:"text-center",children:[l.jsx("p",{className:"text-sm text-slate-100",children:"Neato Controls"}),l.jsx("p",{className:"text-xs text-slate-400",children:"Control the autonomous Neato robovac. Be nice to him."})]}),l.jsx("span",{className:`absolute right-0 inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${lv(J)}`,children:I})]}),l.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-0.5",children:[l.jsxs("div",{className:"surface-muted grid gap-0.5",children:[l.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Controls"}),l.jsx("button",{type:"button",disabled:!Q||!!p,onClick:()=>V("start",a),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:p==="start"?"Starting...":"Start cleaning"}),l.jsx("button",{type:"button",disabled:!ce||!!p,onClick:()=>V("sendHome",r),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:p==="sendHome"?"Sending...":"Send to dock"}),l.jsxs("div",{className:"grid grid-cols-3 gap-0.5",children:[l.jsx("button",{type:"button",disabled:!ue||!!p,onClick:()=>V("locate",o),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:p==="locate"?"Playing...":"Play sound"}),l.jsx("button",{type:"button",disabled:!le||!!p,onClick:()=>V("clearErrors",u),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:p==="clearErrors"?"Clearing...":"Clear errors"}),l.jsx("button",{type:"button",disabled:!B||!!p,onClick:()=>V("powerCycle",f),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:p==="powerCycle"?"Cycling...":"Power cycle"})]}),l.jsxs("div",{className:"surface-muted grid gap-0.5 pt-0.25",children:[l.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Power status"}),l.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[l.jsx(Qi,{label:"Battery",value:j,tone:ie}),l.jsx(Qi,{label:"Voltage",value:A,tone:"muted"}),l.jsx(Qi,{label:"Docked",value:S?"Docked":"Not docked",tone:S?"good":"muted",hideLabel:!0}),l.jsx(Qi,{label:"Charging",value:k?"charging":"not charging",tone:k?"good":"muted",hideLabel:!0})]})]})]}),l.jsx("div",{className:"grid gap-0.5",children:l.jsxs("div",{className:"surface-muted grid gap-0.5",children:[l.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Robot Status"}),l.jsxs("div",{className:"grid gap-0.5",children:[l.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[l.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot state (raw)"}),l.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:N})]}),l.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[l.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Basic state"}),l.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:T})]}),l.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[l.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"UI state"}),l.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:E})]}),l.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[l.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot error"}),l.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:R})]}),l.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[l.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot alert"}),l.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:q})]})]})]})})]}),!v||!b||!O||!L||!F||!U||!Z?l.jsx("p",{className:"text-xs text-slate-400 text-center",children:v?b?"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 e2(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 t2(n){return n==="up"?"Up":n==="down"?"Down":n==="stopped"?"Transitioning...":n==="conflict"?"Conflict":"--"}function n2({lift:n,onUp:a,onDown:r,fullWidth:o=!1}){const[u,f]=m.useState(""),[h,p]=m.useState(()=>Date.now()),g=o?"w-full":"w-full max-w-xl",x=!!n?.configured,v=!!(n?.enabled&&n?.connected),b=!!n?.busy,S=String(n?.target||"").toLowerCase(),k=String(n?.position||"").toLowerCase(),E=!!n?.availability?.upSwitch,N=!!n?.availability?.downSwitch,w=Number(n?.lastActionAt||0),j=Math.max(0,Number(n?.commandCooldownMs||0)),M=Math.max(0,w+j-h),A=!b&&M>0,R=b||A;m.useEffect(()=>{if(!R)return;const U=setInterval(()=>{p(Date.now())},100);return()=>clearInterval(U)},[R]),m.useEffect(()=>{p(Date.now())},[w,j,b]);const q=x?v?R?"Busy":"Ready":"Offline":"Not configured",$=x?v?R?"warn":"good":"danger":"warn",O=x&&v&&!R&&!u&&E&&N,L=m.useMemo(()=>Math.ceil(M/1e3),[M]),F=async(U,Z)=>{if(Z){f(U);try{await Z()}catch{}finally{f("")}}};return l.jsxs("section",{className:`surface relative text-sm text-slate-200 ${g}`,children:[R?l.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:l.jsxs("div",{className:"space-y-0.25",children:[l.jsx("p",{className:"text-sm font-semibold text-slate-100",children:b?"Preparing to move":"Moving!"}),l.jsx("p",{className:"text-xs text-slate-300",children:"Controls are disabled while the lift is moving, otherwise its tiny brain would get confused."}),!b&&A?l.jsxs("p",{className:"text-xs text-slate-400",children:["About ",L,"s remaining."]}):null]})}):null,l.jsxs("div",{className:"grid gap-0.5",children:[l.jsxs("div",{className:"relative flex items-center justify-center min-h-[1.5rem]",children:[l.jsxs("div",{className:"text-center",children:[l.jsx("p",{className:"text-sm text-slate-100",children:"Lift Controls"}),l.jsx("p",{className:"text-xs text-slate-400",children:"Move the lift up and down. Please don't break anything..."})]}),l.jsx("span",{className:`absolute right-0 inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${e2($)}`,children:q})]}),l.jsx("section",{className:"surface-muted px-0.5 py-0.5",children:l.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-0.5",children:[l.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.75 text-center",children:[l.jsx("div",{className:"text-xs text-slate-300",children:"Position"}),l.jsx("div",{className:"text-base font-semibold text-slate-100",children:t2(k)})]}),l.jsx("button",{type:"button",disabled:!O,onClick:()=>F("down",r),className:`button-dark w-full text-sm disabled:opacity-50 ${k==="down"||S==="down"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:u==="down"||S==="down"?"Lowering...":"Down"}),l.jsx("button",{type:"button",disabled:!O,onClick:()=>F("up",a),className:`button-dark w-full text-sm disabled:opacity-50 ${k==="up"||S==="up"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:u==="up"||S==="up"?"Raising...":"Up"})]})}),!x||!v||!E||!N?l.jsx("p",{className:"text-xs text-slate-400 text-center",children:x?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?l.jsxs("p",{className:"text-xs text-rose-300 text-center",children:["Last error: ",n.lastError]}):null]})]})}function s2(n){const a=String(n||"").trim();if(!a)return"";try{const r=new URL(a),o=String(r.protocol||"").toLowerCase();return o!=="http:"&&o!=="https:"?"":r.toString()}catch{return""}}function a2({isVerified:n=!1,fullWidth:a=!1,onMessage:r}){const{value:o,save:u}=Oe("profile",{nickname:"",profileImageUrl:""}),f=String(o?.profileImageUrl||"").trim(),[h,p]=m.useState(f),[g,x]=m.useState(!1);m.useEffect(()=>{p(f)},[f]);const v=m.useMemo(()=>s2(h),[h]),b=String(h||"").trim()!==f,S=!!(n&&b&&(v||!String(h||"").trim())),k=a?"w-full":Nr,E=async N=>{if(N.preventDefault(),!n){r?.("Verify your account before setting a profile image URL.");return}x(!0),r?.("");try{const w=v||"";u(j=>({...j||{},profileImageUrl:w})),r?.(w?"Profile image URL saved.":"Profile image URL cleared.")}finally{x(!1)}};return l.jsx("section",{className:`surface text-sm text-slate-300 ${k}`,children:l.jsxs("form",{className:Ka,onSubmit:E,children:[l.jsx("p",{className:"text-sm text-slate-300",children:"Chat profile image URL"}),l.jsx("p",{className:"text-xs text-slate-500",children:"Verified users can set a custom avatar for chat and Discord bridge messages."}),l.jsx("input",{className:el,type:"url",name:"vip_profile_image_url",placeholder:"https://example.com/avatar.png",value:h,onChange:N=>p(N.target.value),disabled:!n||g}),!v&&String(h||"").trim()?l.jsx("p",{className:"text-xs text-amber-300",children:"Enter a valid http/https image URL."}):null,l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"submit",className:"button-dark text-sm disabled:opacity-50",disabled:!S||g,children:g?"Saving...":"Save URL"}),l.jsx("button",{type:"button",className:"button-dark text-sm disabled:opacity-50",disabled:!n||g||!f,onClick:()=>p(""),children:"Clear"})]})]})})}function rv({isActive:n=!0}){const a=oe(ue=>ue.session),{identifySession:r,requestVerification:o,requestPrivateRoverAccess:u,playUploadedAudio:f,stopUploadedAudio:h,startMicWhip:p,readyMicWhip:g,stopMicWhip:x,neatoStart:v,neatoSendHome:b,neatoLocate:S,neatoClearErrors:k,neatoPowerCycle:E,liftUp:N,liftDown:w}=Ot(),{value:j,save:M}=Oe("identity",{cookieUserId:""}),{value:A}=Oe("profile",{nickname:""}),R=m.useMemo(()=>(j?.cookieUserId||"").trim(),[j?.cookieUserId]),q=(A?.nickname||"").trim(),$=!!a?.isVerified,O=a?.verification?.pendingRequestId||null,L=a?.privateRoverAccess?.requestableRovers||[],F=a?.privateRoverAccess?.pendingRequests||[],U=String(a?.assignment?.roverId||"").trim(),[Z,Q]=m.useState(""),ce=async ue=>{const le=String(ue||"").trim().toLowerCase();if(!le)throw new Error("Identity key required.");if(!BA.test(le))throw new Error("Identity key must match format: cu_ + 32 lowercase hex chars.");return M(B=>({...B||{},cookieUserId:le})),await r({cookieUserId:le,nickname:q}),le};return l.jsxs("section",{className:"space-y-2 text-base",children:[l.jsxs("div",{className:"grid gap-2 grid-cols-1 lg:grid-cols-[minmax(0,1.25fr)_minmax(0,0.9fr)]",children:[$?null:l.jsx("div",{className:"lg:col-span-2",children:l.jsx(KA,{pendingRequestId:O,currentStoredKey:R,nickname:q,requestVerification:o,applyIdentityKey:ce,onMessage:Q,fullWidth:!0})}),l.jsx("div",{className:"lg:col-span-2",children:$?l.jsxs("div",{className:"space-y-2",children:[l.jsx(WA,{neato:a?.neato||null,onStart:v,onSendHome:b,onLocate:S,onClearErrors:k,onPowerCycle:E,fullWidth:!0}),l.jsx(n2,{lift:a?.lift||null,onUp:N,onDown:w,fullWidth:!0}),l.jsx(QA,{ownRoverId:U,audioForwardByRover:a?.audioForward||{},playUploadedAudio:f,stopUploadedAudio:h,startMicWhip:p,readyMicWhip:g,stopMicWhip:x})]}):l.jsx("section",{className:"surface h-full",children:l.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."})})}),l.jsx("div",{className:"lg:col-span-2",children:l.jsx(JA,{requestableRovers:L,pendingRequests:F,requestPrivateRoverAccess:u,onMessage:Q,fullWidth:!0})}),l.jsx("div",{className:"lg:col-span-2",children:l.jsx(ZA,{currentStoredKey:R,applyIdentityKey:ce,onMessage:Q,fullWidth:!0})}),l.jsx("div",{className:"lg:col-span-2",children:l.jsx(a2,{isVerified:$,onMessage:Q,fullWidth:!0})})]}),Z?l.jsx("div",{className:Nr,children:l.jsx("p",{className:"text-xs text-slate-400 text-center",children:Z})}):null]})}const l2=420,r2=1200,i2={1:262,2:330,3:392,4:523};function bo(){const n=oe(N=>N.session?.buttonBox?.buttons??[]),a=Pt(),{value:r}=Oe("audio",it),o=Number.isFinite(r?.masterVolume)?r.masterVolume:it.masterVolume,u=Number.isFinite(r?.alertVolume)?r.alertVolume:it.alertVolume,f=Math.max(0,Math.min(1,o*u)),h=m.useMemo(()=>{const N=Array.isArray(n)?n:[];return N.length===4?N:[1,2,3,4].map(w=>N.find(j=>Number(j?.id)===w)||{id:w,count:0,goal:0,rewardName:null,rewardId:null,rewardNumber:null,lastRewardAt:null})},[n]),[p,g]=m.useState({}),[x,v]=m.useState({}),b=m.useRef(new Map),S=m.useRef(new Map),k=m.useRef({}),E=m.useRef(null);return m.useEffect(()=>{const N={};h.forEach(j=>{N[j.id]=Number(j.lastRewardAt||0)});const w=k.current||{};h.forEach(j=>{const M=Number(j.id),A=N[M]||0,R=Number(w[M]||0);if(A>0&&A!==R){v(O=>({...O,[M]:!0}));const q=S.current.get(M);q&&clearTimeout(q);const $=setTimeout(()=>{v(O=>({...O,[M]:!1})),S.current.delete(M)},r2);S.current.set(M,$)}}),k.current=N},[h]),m.useEffect(()=>{function N(j){const M=i2[j];if(!M||f<=0)return;let A=E.current;if(!A){const O=window.AudioContext||window.webkitAudioContext;if(!O)return;A=new O,E.current=A}const R=A.createOscillator(),q=A.createGain();R.type="square",R.frequency.value=M;const $=.14*f;q.gain.setValueAtTime(1e-4,A.currentTime),q.gain.exponentialRampToValueAtTime(Math.max(1e-4,$),A.currentTime+.01),q.gain.exponentialRampToValueAtTime(1e-4,A.currentTime+.2),R.connect(q),q.connect(A.destination),R.start(),R.stop(A.currentTime+.21)}function w(j={}){const M=Number(j.buttonId);if(!Number.isFinite(M)||M<1||M>4)return;g(q=>({...q,[M]:!0}));const A=b.current.get(M);A&&clearTimeout(A);const R=setTimeout(()=>{g(q=>({...q,[M]:!1})),b.current.delete(M)},l2);b.current.set(M,R),N(M)}return a.on("buttonBox:increment",w),()=>{a.off("buttonBox:increment",w)}},[f,a]),l.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[l.jsx("header",{className:"panel-muted text-center text-sm",children:l.jsx("p",{children:"Button Box"})}),l.jsx("div",{className:"grid grid-cols-4 gap-0.5",children:h.map(N=>{const w=Number(N.id),j=Number.isFinite(N.count)?N.count:0,M=Number.isFinite(N.goal)?N.goal:0,A=typeof N.rewardName=="string"&&N.rewardName.trim()?N.rewardName.trim():"Unassigned",R=Number.isFinite(N.rewardNumber)?N.rewardNumber:"?",q=!!p[w],$=!!x[w];return l.jsx(yg,{buttonId:w,count:j,goal:M,rewardNumber:R,rewardName:A,className:[q?"bg-cyan-900/45":"",$?"bg-fuchsia-900/45":""].filter(Boolean).join(" ")},w)})})]})}function iv(){const{identifySession:n}=Ot(),a=oe(x=>x.session?.overseerVote||null),{value:r}=Oe("profile",{nickname:""}),{value:o}=Oe("identity",{cookieUserId:""}),{value:u,save:f}=Oe("overseerPreference",{enabled:!0}),h=!!u?.enabled,p=!!a?.running,g=async x=>{const v=!!x?.target?.checked;await f(b=>({...b||{},enabled:v})),await n({cookieUserId:String(o?.cookieUserId||"").trim(),nickname:String(r?.nickname||"").trim(),overseerEnabled:v})};return l.jsxs("section",{className:"surface p-1 text-xs text-slate-200 text-center",children:[l.jsxs("label",{className:"flex items-center justify-center gap-1.5 bg-gray-700 surface",children:[l.jsx("input",{type:"checkbox",checked:h,onChange:g}),l.jsx("span",{children:"Enable Overseer LLM"})]}),l.jsx("span",{className:"text-xs",children:"Cast your to enable Overseer in chat."}),l.jsxs("div",{className:"mt-0.5 text-slate-400",children:["Yes!: ",Number(a?.yesCount||0),", No...: ",Number(a?.noCount||0)]}),l.jsx("div",{className:"mt-0.5 flex justify-center",children:l.jsx("span",{className:`inline-flex items-center rounded px-1.5 py-0.5 text-[0.7rem] font-medium ${p?"bg-emerald-600/80 text-emerald-50":"bg-slate-600/70 text-slate-100"}`,children:p?"Running!":"Stopped..."})})]})}function o2(){const{state:{roverId:n}}=yt(),r=Wt(n)?.sensors||{};return l.jsx("section",{className:"panel-section",children:l.jsx("div",{className:"aspect-square w-full",children:l.jsx(Fg,{sensors:r})})})}function c2(){const{state:{roverId:n,keymap:a,camera:r,horn:o},pipeline:u,actions:{setServoAngle:f,setNightVision:h,startHorn:p,stopHorn:g}}=yt(),x=al(),v=Bg(n),b=v.docked&&!v.driving,S=r?.config,k=!!(n&&r?.enabled&&S),E=!!(n&&u?.nightVision),N=u?.nightVisionState,w=!!(n&&u?.horn),j=o?.overheated,M=typeof S?.minAngle=="number"?S.minAngle:-30,A=typeof S?.maxAngle=="number"?S.maxAngle:30,R=typeof r?.angle=="number"?r.angle:typeof S?.homeAngle=="number"?S.homeAngle:(M+A)/2,q=Tt(a?.nightVisionToggle?.[0]),$=Tt(a?.hornHonk?.[0]),O=Tt(a?.cameraUp?.[0]),L=Tt(a?.cameraDown?.[0]),F=!!(!n||x.cameraLocked);return l.jsxs("section",{className:"panel-section grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] gap-0.5",children:[l.jsx(Lg,{layout:"desktop",expand:!0,driveDockState:v}),b?null:l.jsxs("div",{className:"surface space-y-0.5 p-0 text-sm text-slate-200",children:[E&&l.jsx(zg,{nightVisionOn:N?.nightVisionOn,disabled:!n,onToggle:h,keyLabel:q}),w&&l.jsx(Ug,{disabled:!n||j,onStart:p,onStop:g,keyLabel:$,active:o?.active,heat:o?.heat}),k&&l.jsx(Hg,{value:R,min:M,max:A,disabled:F,onChange:f,keyDownLabel:L,keyUpLabel:O,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 u2({layout:n,onOpenHelpOverlay:a}){const[r,o]=m.useState("telemetry"),u=oe(N=>!!N.session?.isVerified),f=oe(N=>String(N.session?.assignment?.roverId||"").trim()),h=oe(N=>{const w=String(N.session?.assignment?.roverId||"").trim();return w&&N.session?.audioForward?.[w]||null}),{state:p}=yt(),{value:g}=Oe("vipAudio",{openMicEnabled:!1,pttMode:"live"}),x=u?"bg-emerald-400":"bg-red-600",v=!!p?.mic?.pttActive,b=!!g?.openMicEnabled,S=g?.pttMode==="clip"?"clip":"live",k=!!(f&&u&&(S==="clip"?v:b||v)),E=!!(f&&u&&S==="clip"&&h?.source==="upload"&&h?.state==="playing");return l.jsx("section",{className:"panel text-base",children:l.jsxs(vf,{defaultTab:"telemetry",currentTab:r,onTabChange:o,children:[l.jsxs(bf,{children:[l.jsx(Zt,{id:"telemetry",children:"Controls"}),l.jsx(Zt,{id:"vip",highlight:E?"green":k?"pink":"none",children:l.jsxs("span",{className:"inline-flex items-center gap-2",children:[l.jsx("span",{children:"VIP"}),l.jsx("span",{className:`inline-block h-3 w-3 rounded-full ${x}`,"aria-hidden":"true",title:u?"Verified":"Not verified"})]})}),l.jsx(Zt,{id:"help",children:"Help"}),l.jsx(Zt,{id:"settings",children:"Settings"})]}),l.jsxs(yf,{children:[l.jsx(Jt,{id:"telemetry",children:l.jsxs("div",{className:"space-y-0.5",children:[l.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]",children:[l.jsx(o2,{}),l.jsx(c2,{})]}),l.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]",children:[l.jsx(To,{}),l.jsx(tf,{panelId:"replay-sources-desktop",fillHeight:!0}),l.jsx(MA,{})]}),l.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)] h-[14rem]",children:[l.jsx(Ro,{fillHeight:!0}),l.jsxs("div",{className:"grid min-h-0 gap-0.5 grid-rows-[auto_minmax(0,1fr)_auto]",children:[l.jsx(iv,{}),l.jsx(vo,{compact:!0,hideNicknameForm:!0,fillHeight:!0}),l.jsx(EA,{compact:!0})]})]}),l.jsx(Zg,{}),l.jsx(bo,{}),l.jsx(uf,{defaultOrientation:"horizontal",panelId:"rightpane-telemetry"})]})}),l.jsx(Jt,{id:"vip",keepMounted:!0,children:l.jsx(rv,{isActive:r==="vip"})}),l.jsx(Jt,{id:"help",children:l.jsx(av,{layout:n,onOpenOverlay:a})}),l.jsx(Jt,{id:"settings",children:l.jsx(tv,{})})]})]})})}const d2=new Set(["admin","lockdown","lockdown-admin"]),f2=new Set(["lockdown","lockdown-admin"]),m2=new Set(["admin","lockdown"]);function h2(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 p2(){const n=oe(b=>b.session?.mode||null),a=oe(b=>b.session?.role||null),r=oe(b=>b.session?.adminReason?.text||"");oe(b=>b.session?.adminReason?.updatedAt||null);const o=oe(b=>b.session?.timezone||"UTC"),u=oe(b=>(b.session?.socials||[]).find(E=>String(E?.id||E?.label||"").toLowerCase()==="discord")?.url||b.session?.discord?.invite||null),f=m2.has(n),h=n==="lockdown"?f2.has(a):d2.has(a),[p,g]=m.useState(()=>new Date),x=m.useMemo(()=>{try{return new Intl.DateTimeFormat("en-US",{timeZone:o,hour:"numeric",minute:"2-digit",second:"2-digit"}).format(p)}catch{return p.toLocaleTimeString()}},[p,o]);if(m.useEffect(()=>{const b=setInterval(()=>g(new Date),1e3);return()=>clearInterval(b)},[]),!f||h)return null;const v=h2(n);return l.jsx("div",{className:"pointer-events-auto fixed inset-0 z-50 flex items-center justify-center bg-black px-0.5 py-0.5",children:l.jsxs("div",{className:"surface w-full max-w-md space-y-0.5 text-slate-100 shadow-2xl",children:[l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-lg font-semibold",children:v.title}),l.jsx("p",{className:"text-sm text-slate-300",children:v.description})]}),l.jsxs("div",{className:"surface-muted space-y-0.5",children:[l.jsx("p",{className:"text-[0.7rem] tracking-wide text-slate-400",children:"Reason for locking:"}),l.jsx("p",{className:"text-lg font-semibold text-slate-100",children:r||"No reason set."}),l.jsxs("p",{className:"text-center text-sm text-slate-300",children:["Server time: ",x]})]}),l.jsx("div",{className:"surface-muted",children:l.jsx(Jg,{})}),l.jsx("div",{className:"w-full justify-center items-center",children:l.jsx(Ao,{id:"discord",label:"Join our Discord server for updates!",url:u})}),"You can still use the chat while the server is locked:",l.jsxs("div",{className:"max-h-80 overflow-y-auto",children:[l.jsx(Ro,{}),l.jsx(ll,{})]})]})})}const x2="/assets/turn_alert-DVO8EZJa.mp3";function g2(n,a=1){const r=m.useRef(null);return m.useEffect(()=>{r.current=new Audio(n),r.current.volume=a,r.current.load()},[n,a]),()=>{r.current&&(r.current.volume=a,r.current.currentTime=0,r.current.play().catch(()=>{}))}}function v2(){const n=oe(v=>v.session?.socketId||null),a=oe(v=>v.session?.turnQueues||{}),r=oe(v=>v.session?.roster||[]),{pushAlert:o}=Ot(),{value:u}=Oe("audio",it),f=Number.isFinite(u?.masterVolume)?u.masterVolume:it.masterVolume,h=Number.isFinite(u?.alertVolume)?u.alertVolume:it.alertVolume,p=Math.max(0,Math.min(1,f*h)),g=g2(x2,p),x=m.useRef(new Set);return m.useEffect(()=>{if(!n)return;const v=[];Object.entries(a).forEach(([b,S])=>{!S||!S.current||S.current!==n||x.current.has(b)||(v.push(b),x.current.add(b))}),Object.keys(a).forEach(b=>{(!a[b]||a[b].current!==n)&&x.current.delete(b)}),v.length!==0&&(v.forEach(b=>{const S=r.find(k=>String(k.id)===String(b))?.name||b;o({title:"Your turn!",message:`You now control ${S}.`,color:"#ff5722"})}),g())},[a,g,o,r,n]),null}function b2({visible:n,mode:a,onEnterFullscreen:r,onDismiss:o}){if(!n)return null;const u=a==="pwa-hint";return l.jsx("div",{className:"fixed inset-0 z-40 flex items-center justify-center p-1 pointer-events-none bg-black/20",children:l.jsx("div",{className:"pointer-events-auto w-full max-w-sm surface center",children:l.jsxs("div",{className:"space-y-0.5 p-1 text-sm text-slate-100",children:[l.jsx("h2",{className:"text-base font-semibold text-white border-b border-slate-700",children:"Better in fullscreen!"}),u?l.jsxs("p",{className:"text-slate-300",children:["For fullscreen on iOS, open Safari's share menu and pick ",l.jsx("strong",{children:"Add to Home Screen"}),". Launching from the home screen then makes it fullscreen."]}):l.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."}),l.jsx("p",{className:"text-xs border-t border-b border-slate-700 p-0.5 text-blue-300 text-center",children:"This will only show once just to let you know. There is a fullscreen button in the bottom right for later use."}),l.jsxs("div",{className:"flex justify-end gap-0.5 pt-1 text-sm",children:[l.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&&l.jsx("button",{type:"button",className:"rounded bg-cyan-500 px-3 py-1 font-semibold text-black hover:bg-cyan-400",onClick:r,children:"Enter fullscreen"})]})]})})})}function y2({side:n="right",onClick:a}){const r=n==="left"?"left-2":"right-2";return l.jsx("button",{type:"button",onClick:a,className:`fixed bottom-2 ${r} z-30 flex h-10 w-10 items-center justify-center rounded-full border border-slate-700 bg-black text-slate-200 transition hover:bg-zinc-900 active:scale-95`,"aria-label":"Enter fullscreen",title:"Enter fullscreen",children:l.jsx("svg",{viewBox:"0 0 24 24",width:"18",height:"18","aria-hidden":"true",className:"pointer-events-none",children:l.jsx("path",{d:"M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})})}const S2=new Set(["mobile-portrait","mobile-landscape"]),j2=()=>{if(typeof navigator>"u")return!1;const n=navigator.userAgent||navigator.platform||"";return/iphone|ipad|ipod/i.test(n)},Tx=()=>{if(typeof document>"u")return!1;const n=document;return!!(n.fullscreenEnabled||n.webkitFullscreenEnabled||n.mozFullScreenEnabled||n.msFullscreenEnabled)};function N2(n){const{value:a,save:r,status:o}=Oe("page",{fullscreenPromptShown:!1}),u=S2.has(n),[f,h]=m.useState(!1),[p,g]=m.useState("native"),[x,v]=m.useState(!1),b=!!a?.fullscreenPromptShown,S=m.useMemo(()=>j2(),[]),k=m.useMemo(()=>Tx(),[]),E=m.useCallback(()=>{o==="ready"&&r(A=>A?.fullscreenPromptShown?A:{...A||{},fullscreenPromptShown:!0})},[r,o]),N=m.useCallback(()=>{if(o!=="ready"){h(!1);return}if(!u){h(!1),v(!1);return}if(!x&&b){h(!1);return}if(S){g("pwa-hint"),h(!0);return}if(!Tx()){h(!1);return}if(typeof document<"u"&&document.fullscreenElement){h(!1);return}g("native"),h(!0)},[x,b,S,u,o]);m.useEffect(()=>{N()},[N]),m.useEffect(()=>{if(typeof document>"u")return;const A=()=>{if(document.fullscreenElement){v(!1),h(!1);return}N()};return document.addEventListener("fullscreenchange",A),document.addEventListener("webkitfullscreenchange",A),()=>{document.removeEventListener("fullscreenchange",A),document.removeEventListener("webkitfullscreenchange",A)}},[N]);const w=m.useCallback(()=>{E(),v(!1),h(!1)},[E]),j=m.useCallback(async()=>{if(!u||S||typeof document>"u")return!1;if(document.fullscreenElement)return!0;const A=document.documentElement;if(!A)return!1;const R=A.requestFullscreen||A.webkitRequestFullscreen||A.mozRequestFullScreen||A.msRequestFullscreen;if(!R)return!1;try{const q=R.call(A);return q&&typeof q.then=="function"&&await q,E(),v(!1),h(!1),!0}catch(q){return console.warn("Failed to enter fullscreen",q),!1}},[S,u,E]),M=m.useCallback(()=>{u&&(v(!0),g(S?"pwa-hint":"native"),h(!0))},[S,u]);return m.useEffect(()=>{u||(v(!1),h(!1))},[u]),{visible:f,mode:p,isIOS:S,nativeSupported:k,enterFullscreen:j,dismiss:w,showPrompt:M}}function w2({visible:n,layout:a,onClose:r,showOnLoad:o,onToggleShowOnLoad:u}){if(!n)return null;const{state:f}=yt(),h=p=>{const g=!p.target.checked;u?.(g)};return l.jsx("div",{className:"fixed inset-0 z-30 flex items-center justify-center bg-black/80 px-0.5 py-0.5",children:l.jsxs("div",{className:"pointer-events-auto surface w-full max-w-4xl max-h-[90vh] overflow-hidden shadow-2xl",children:[l.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:[l.jsx("span",{className:"font-semibold",children:"Help & controls"}),l.jsxs("div",{className:"flex items-center gap-0.5 text-[0.8rem] text-slate-300",children:[l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!o,onChange:h,className:"accent-cyan-500"}),l.jsx("span",{children:"Don't show again"})]}),l.jsx("button",{type:"button",onClick:r,className:"button-dark px-2 py-0.25 text-[0.8rem]",children:"Close"})]})]}),l.jsx("div",{className:"max-h-[85vh] overflow-y-auto p-0.5",children:l.jsx(sv,{layout:a,keymap:f?.keymap||{}})})]})})}function Ia({label:n,keyLabel:a}){return l.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-0.5 py-0.35 text-[0.8rem] text-slate-200",children:[l.jsx("span",{children:n}),l.jsx(Ud,{label:a})]})}function A2({keymap:n}){return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-sm text-slate-200",children:'1. Press "Start Driving" put your rover into driving mode.'}),l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-sm text-slate-200",children:"2. Use the drive controls to move your rover:"}),l.jsxs("div",{className:"space-y-0.5",children:[l.jsx(Ia,{label:"Forward",keyLabel:Tt(n?.driveForward?.[0])}),l.jsx(Ia,{label:"Backward",keyLabel:Tt(n?.driveBackward?.[0])}),l.jsx(Ia,{label:"Turn Left",keyLabel:Tt(n?.driveLeft?.[0])}),l.jsx(Ia,{label:"Turn Right",keyLabel:Tt(n?.driveRight?.[0])}),l.jsx(Ia,{label:"Move faster",keyLabel:Tt(n?.boostModifier?.[0])}),l.jsx(Ia,{label:"Move slower",keyLabel:Tt(n?.slowModifier?.[0])})]})]}),l.jsx("p",{className:"text-sm text-slate-200",children:'3. When done, enter "Docking Assist" and line up the front sensor with the dock sensor.'})]})}function C2(){return l.jsxs("div",{className:"space-y-0.5 text-sm text-slate-200",children:[l.jsx("p",{children:'1. Press "Start Driving" put your rover into driving mode.'}),l.jsx("p",{children:"2. Touch and hold in Joystick area to move."}),l.jsx("p",{children:"3. Use the other column for motor, horn, and camera controls."}),l.jsx("p",{children:'4. When done, enter "Docking Assist" and line up the front sensor with the dock sensor.'})]})}function k2({visible:n,layout:a,showOnLoad:r,onToggleShowOnLoad:o,onOpenHelp:u,onClose:f}){const{state:h}=yt(),p=a==="desktop",g=oe(b=>(b.session?.socials||[]).find(E=>String(E?.id||E?.label||"").toLowerCase()==="discord")?.url||b.session?.discord?.invite||null),x=m.useMemo(()=>h?.keymap||{},[h?.keymap]);if(!n)return null;const v=b=>{const S=!b.target.checked;o?.(S)};return l.jsx("div",{className:"fixed inset-0 z-40 flex items-end justify-center bg-black/75 p-0.5 items-center",children:l.jsxs("div",{className:"pointer-events-auto surface w-full max-w-3xl overflow-hidden shadow-2xl",children:[l.jsx("div",{className:"flex items-center justify-between border-b border-slate-700 px-0.5 py-0.35 text-sm text-slate-200",children:l.jsx("span",{className:"font-semibold text-xl",children:"Welcome! To get started:"})}),l.jsxs("div",{className:`grid gap-0.5 p-0.5 ${p?"md:grid-cols-[minmax(0,1.5fr)_minmax(0,1fr)]":"grid-cols-1"}`,children:[l.jsx("section",{className:"space-y-0.5 border-b border-slate-700",children:p?l.jsx(A2,{keymap:x}):l.jsx(C2,{})}),l.jsxs("section",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-left",children:"Next..."}),l.jsxs("div",{className:"surface space-y-0.5 p-0.5 border-b border-slate-700",children:[l.jsx("p",{className:"text-xl font-semibold text-slate-200",children:"Set your nickname"}),l.jsx("p",{className:"text-sm font-semibold text-slate-200",children:"Nicknames are assigned randomly by default, you can change yours here."}),l.jsx(ll,{compact:!0})]}),l.jsxs("div",{className:"surface p-0.5",children:[l.jsx("p",{className:"text-xl font-semibold text-slate-200",children:"Join our Discord server!"}),l.jsx("p",{className:"text-sm font-semibold text-slate-200",children:"We have an active and welcoming community :3"}),l.jsx(Ao,{id:"discord",label:"Join Discord",url:g})]})]})]}),l.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-0.5 border-t border-slate-700 px-0.5 py-0.35 text-[0.8rem]",children:[l.jsxs("label",{className:"flex items-center gap-0.5 text-slate-300",children:[l.jsx("input",{type:"checkbox",checked:!r,onChange:v,className:"accent-cyan-500"}),l.jsx("span",{children:"Don't show again"})]}),l.jsx("div",{className:"flex items-center gap-0.5",children:l.jsx("button",{type:"button",onClick:f,className:"button-dark px-1 py-0.25 text-2xl bg-green-600 hover:bg-green-400 hover:border-green-100 border-green-400",children:"Got it! Let me in!"})})]})]})})}const E2=`aroma +`).trim();if(ae){R(!0);try{await x(ae,J),M(""),k(),E(!1)}catch(he){alert(he.message)}finally{R(!1)}}}const T=r?"flex-1 min-h-0 overflow-y-auto":"h-48 overflow-y-auto";return l.jsxs("section",{className:`panel-section space-y-0.5 text-base ${r?"flex h-full flex-col overflow-hidden":""}`,children:[l.jsxs("div",{className:`surface overflow-y-auto space-y-0.5 px-0 ${T}`,ref:ue,children:[V.length===0&&I.length===0?l.jsx("p",{className:"text-sm text-slate-500",children:"No messages yet."}):V.map(D=>l.jsx(sf,{message:D},D.id)),I.map(D=>l.jsx(bg,{message:D},`typing-${D.typingId||D.id}`))]}),!n&&l.jsxs("form",{className:"flex flex-wrap items-stretch gap-0.5",onSubmit:ie,children:[l.jsx("input",{className:"field-input flex-1 min-w-[10rem]",value:j,onChange:D=>{const K=D.target.value;M(K),E(!!K.trim())},onFocus:D=>{b(D),E(!!j.trim())},onBlur:D=>{S(D),E(!1)},onKeyDown:D=>{D.key==="Enter"&&!j.trim()&&(D.preventDefault(),k(),E(!1))},ref:D=>v(D,{target:"panel"}),placeholder:ce?"Type a message…":a?"":"Spectators cannot chat",disabled:!ce}),B&&l.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 basis-full sm:basis-auto",children:[l.jsxs("label",{className:"flex items-center gap-0.5 text-xs text-slate-300",children:[l.jsx("input",{type:"checkbox",checked:q,onChange:D=>$(D.target.checked),className:"accent-cyan-500"}),l.jsx("span",{children:"Speak"})]}),l.jsxs("select",{value:O,onChange:D=>{const K=D.target.value;L(K),w(ae=>({...ae||{},engine:K}))},className:"field-input text-xs",children:[l.jsx("option",{value:"flite",children:"flite"}),l.jsx("option",{value:"espeak",children:"espeak"})]}),O==="flite"?l.jsx("select",{value:F,onChange:D=>{const K=D.target.value;U(K),w(ae=>({...ae||{},voice:K}))},className:"field-input text-xs",children:wA.map(D=>l.jsx("option",{value:D,children:D},D))}):l.jsx("select",{value:Z,onChange:D=>{const K=Number(D.target.value);Q(K),w(ae=>({...ae||{},pitch:K}))},className:"field-input text-xs",children:AA.map(D=>l.jsxs("option",{value:D,children:["pitch ",D]},D))})]}),l.jsx("button",{type:"submit",disabled:!ce||A,className:"button-dark h-full disabled:opacity-50 self-stretch",children:A?"...":"Send"})]})]})}function ll({compact:n=!1}){const a=oe(b=>b.session?.role||null),{setNickname:r}=Ot(),{value:o,save:u}=Oe("profile",{nickname:""}),[f,h]=m.useState(o.nickname||""),[p,g]=m.useState(!1),x=a!=="spectator";m.useEffect(()=>{h(o.nickname||"")},[o.nickname]);async function v(b){if(b.preventDefault(),!x)return;const S=(f||"").trim().slice(0,32);if(S){g(!0);try{await r(S),u({nickname:S})}catch(k){alert(k.message)}finally{g(!1)}}}return l.jsxs("form",{className:"grid w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-stretch gap-0.5",onSubmit:v,children:[l.jsx("input",{className:"field-input flex-1 min-w-0",value:f,onChange:b=>h(b.target.value),maxLength:32,placeholder:"Enter a nickname",disabled:!x}),l.jsx("button",{type:"submit",disabled:!x||p,className:"button-dark h-full shrink-0 whitespace-nowrap px-0.5 py-0 disabled:opacity-50",children:p?"Saving…":n?"Set":"Save"})]})}function CA({socials:n,discordInvite:a,kofiLink:r}){const o=Array.isArray(n)?n:null;if(o&&o.length)return o;const u=[];return a&&u.push({id:"discord",label:"Discord",url:a}),r&&u.push({id:"kofi",label:"Ko-fi",url:r}),u}function kA(n){if(!n||typeof n!="object")return null;const a=n.id||n.service||n.key||n.name||null,r=n.label||n.title||n.name||a||"Link",o=n.url||n.link||n.href||null;return o?{id:a,label:r,url:o}:null}function Sf({className:n=""}){const a=oe(o=>({socials:o.session?.socials??[],discordInvite:o.session?.discord?.invite||null,kofiLink:o.session?.kofi?.link||null})),r=CA(a).map(kA).filter(Boolean).slice(0,4);return r.length?l.jsx("div",{className:`grid grid-cols-2 grid-rows-2 gap-0.5 ${n}`,children:r.map(o=>l.jsx(Ao,{...o},`${o.id||o.label}-${o.url}`))}):null}function EA({compact:n=!1}){return l.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:l.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:l.jsx(ll,{compact:n})})})}function MA(){return l.jsx("section",{className:"panel-section flex h-full min-h-0 flex-col gap-0.5 text-base",children:l.jsx(Sf,{className:"flex-1 min-h-0"})})}function vd(...n){return n.filter(Boolean).join(" ")}function RA(n){const a=n?.batteryState?.percentDisplay;return a==null?"--":`${a}%`}function TA(n){return n?.batteryState?n.batteryState.urgentActive?"text-red-400":n.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function qA(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 DA(n,a){if(!n)return"";const r=n.nickname||n.socketId?.slice(0,6)||"unknown";return n.socketId&&n.socketId===a?`${r} (you)`:r}function To({title:n="Rovers"}){const a=oe(j=>j.session?.role||null),r=oe(j=>j.session?.roster??[]),o=oe(j=>j.session?.turnQueues??{}),u=oe(j=>j.session?.users??[]),f=oe(j=>j.session?.socketId||null),{requestControl:h}=Ot(),[p,g]=m.useState({}),[x,v]=m.useState(()=>Date.now()),b=m.useMemo(()=>a&&a!=="spectator",[a]),S=m.useMemo(()=>a==="admin"||a==="lockdown"||a==="lockdown-admin",[a]),k=m.useMemo(()=>Object.values(o||{}).some(j=>j?.deadline||j?.idleDeadline),[o]);m.useEffect(()=>{if(!k)return;const j=setInterval(()=>{v(Date.now())},1e3);return()=>clearInterval(j)},[k]);const E=m.useMemo(()=>{const j=new Set(r.map(A=>String(A.id))),M=Object.keys(o||{}).filter(A=>!j.has(String(A))).map(A=>({id:A,name:A,locked:!1,batteryState:null}));return[...r,...M]},[r,o]);async function N(j){if(j){g(M=>({...M,[j]:!0}));try{await h(j)}catch(M){alert(M.message)}finally{g(M=>({...M,[j]:!1}))}}}const w=j=>u.find(M=>M.socketId===j)||{socketId:j,nickname:null,role:null};return l.jsxs("div",{className:"space-y-0.5 text-sm",children:[n&&l.jsx("p",{className:"text-sm text-slate-400",children:n}),E.length===0?l.jsx("p",{className:"text-sm text-slate-500",children:"No rovers registered."}):l.jsx("ul",{className:"space-y-0.5 text-sm",children:E.map(j=>{const M=String(j.id),A=o?.[M]||null,R=A?.queue||[],q=A?.idleDeadline||A?.deadline||null,$=q&&q>x?Math.ceil((q-x)/1e3):q?0:null,O=A?.current||null,L=O?R.findIndex(J=>J===O):-1,F=R.length>1?L>=0?R[(L+1)%R.length]:R[0]:null,U=!!(f&&O&&O===f),Z=!!(f&&F&&F===f),Q=$!=null&&(U||Z),ce=!!j.locked,ue=ce&&!S,le=!!(j?.private?.enabled&&j?.private?.open),B=j.lockReason?`locked: ${j.lockReason}`:"locked",V=ce?B:p[M]?"...":"request",I=b&&!ue&&!p[M];return l.jsxs("li",{className:vd("surface flex flex-wrap items-start justify-between gap-0.5",I&&"cursor-pointer",ce?"bg-red-900/40":le?"bg-amber-700/35 border border-amber-200/30":null),onClick:()=>{I&&N(j.id)},children:[l.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[l.jsxs("div",{className:"flex min-w-0 items-center gap-0.5",children:[l.jsxs("p",{className:"min-w-0 flex items-center gap-0.5 whitespace-nowrap text-slate-200",children:[l.jsx("span",{className:"rounded px-1 py-[1px] border border-transparent",style:ns(j.color,.16),children:j.name}),j.description?l.jsx("span",{className:"min-w-0 flex-1 truncate text-[0.7rem] text-slate-400",children:j.description}):null]}),Q?l.jsx("span",{className:"rounded bg-slate-800 px-1 text-[0.7rem] text-slate-200",children:U?`${$}s left`:`Your turn in ${$}s`}):null]}),l.jsx("span",{className:vd("text-[0.75rem] font-semibold",TA(j)),children:RA(j)})]}),R.length===0?l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"No queue."}):l.jsx("div",{className:"flex flex-wrap items-center gap-0.5",children:R.map((J,ie)=>{const T=w(J),D=J===O,K=!!(F&&J===F&&!D),ae=D?"bg-sky-600 text-white ring-2 ring-amber-300":K?"bg-emerald-700/60 text-emerald-100 ring-1 ring-emerald-300/70":"bg-slate-800 text-slate-200";return l.jsxs("span",{className:`flex items-center gap-0.5 rounded px-1 text-[0.7rem] ${ae}`,children:[l.jsx("span",{className:`${qA(T.role)} font-semibold`,children:DA(T,f)}),D&&l.jsx("span",{className:"text-[0.65rem] text-slate-200",children:"now"}),K&&l.jsx("span",{className:"text-[0.65rem] text-emerald-100",children:"next"})]},`${M}-${J}-${ie}`)})})]}),b?l.jsx("button",{type:"button",onClick:J=>{J.stopPropagation(),N(j.id)},disabled:p[M]||ue,className:vd("button-dark disabled:opacity-40",ce&&"bg-red-600/70 text-white hover:bg-red-600"),children:V}):null]},j.id)})})]})}function _A(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 OA(n,a){if(!n)return"";const r=n.nickname||n.socketId?.slice(0,6)||"unknown";return n.socketId&&n.socketId===a?`${r} (you)`:r}function vo({hideNicknameForm:n=!1,hideHeader:a=!1,className:r="",fillHeight:o=!1,compact:u=!1}){const f=oe(b=>b.session?.role||null),h=oe(b=>b.session?.users??[]),p=oe(b=>b.session?.socketId||null),g=f!=="spectator",x=m.useMemo(()=>[...h].sort((b,S)=>b.socketId===p?-1:S.socketId===p?1:(b.nickname||"").localeCompare(S.nickname||"")),[p,h]),v=o?"flex-1 min-h-0 overflow-y-auto":u?"h-28 overflow-y-auto":"h-48 overflow-y-auto";return l.jsxs("section",{className:`panel-section space-y-0.5 text-base ${o?"flex h-full min-h-0 flex-col overflow-hidden":""} ${r}`,children:[!n&&l.jsxs("div",{className:"space-y-0.5",children:[l.jsxs("div",{className:"grid gap-0.5 md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]",children:[l.jsx("div",{className:"min-w-0",children:l.jsx("div",{className:"surface flex w-full items-center px-0 py-0",children:l.jsx(ll,{compact:u})})}),l.jsx(Sf,{})]}),!g&&l.jsx("p",{className:"text-xs text-slate-500",children:"Spectators cannot set nicknames."})]}),l.jsxs("div",{className:`space-y-0.5 ${o?"flex flex-1 min-h-0 flex-col":""}`,children:[!a&&l.jsxs("div",{className:`flex items-center justify-between text-sm text-slate-400 ${u?"text-xs":""}`,children:[l.jsx("span",{children:"Users"}),l.jsx("span",{className:"text-xs text-slate-500",children:x.length})]}),l.jsx("div",{className:`surface flex flex-wrap content-start items-start gap-0.5 px-0 pb-0 ${v} ${u?"text-[0.8rem]":""}`,children:x.length===0?l.jsx("p",{className:"text-sm text-slate-500",children:"Waiting for users…"}):x.map(b=>l.jsx("span",{className:`rounded bg-slate-800/80 px-1 py-0.25 text-[0.7rem] ${_A(b.role)}`,children:OA(b,p)},b.socketId))})]})]})}const BA=/^cu_[a-f0-9]{32}$/,el="field-input w-full max-w-sm text-left focus:ring-emerald-500",Nr="mx-auto w-full max-w-xl flex justify-center",Ka="mx-auto flex w-full max-w-md flex-col items-center space-y-0.5 text-center";function LA(n){const a=String(n||"").trim();return a?a.length<=10?`${a.slice(0,2)}***${a.slice(-2)}`:`${a.slice(0,6)}...${a.slice(-6)}`:""}const Ex=8*1024*1024,Ps=16e3,zA={iceServers:[{urls:"stun:stun.l.google.com:19302"}],bundlePolicy:"max-bundle",rtcpMuxPolicy:"require"};function Mx(n){let a="";for(let o=0;o{if(!n||n.iceGatheringState==="complete"){r();return}const o=setTimeout(()=>{n.removeEventListener("icegatheringstatechange",u),r()},a);function u(){n.iceGatheringState==="complete"&&(clearTimeout(o),n.removeEventListener("icegatheringstatechange",u),r())}n.addEventListener("icegatheringstatechange",u)})}function Rx(n){if(!n)return!1;const a=n.connectionState,r=n.iceConnectionState;return a==="connected"||r==="connected"||r==="completed"}function $A(n,a=1e4){return new Promise((r,o)=>{if(!n){o(new Error("Peer connection missing"));return}if(Rx(n)){r();return}const u=setTimeout(()=>{h(),o(new Error("Peer connection timeout"))},a),f=()=>{Rx(n)?(h(),r()):(n.connectionState==="failed"||n.connectionState==="closed"||n.iceConnectionState==="failed")&&(h(),o(new Error(`Peer connection ${n.connectionState||n.iceConnectionState}`)))};function h(){clearTimeout(u),n.removeEventListener("connectionstatechange",f),n.removeEventListener("iceconnectionstatechange",f)}n.addEventListener("connectionstatechange",f),n.addEventListener("iceconnectionstatechange",f)})}async function GA(n){if(!n?.getParameters||!n?.setParameters)return;const a=n.getParameters()||{},r=a.encodings&&a.encodings[0]||{};a.encodings=[{...r,maxBitrate:64e3,dtx:"disabled"}];try{await n.setParameters(a)}catch{}}function YA(n,a=6e3){return new Promise((r,o)=>{if(!n){o(new Error("Peer connection missing"));return}const u=Date.now();let f=-1;const h=setInterval(async()=>{if(Date.now()-u>a){clearInterval(h),o(new Error("WHIP connected but no outbound audio flow"));return}try{const p=n.getSenders().filter(g=>g.track?.kind==="audio");for(const g of p){const x=await g.getStats();for(const v of x.values()){if(v.type!=="outbound-rtp"||v.kind!=="audio")continue;const b=Number(v.bytesSent||0),S=Number(v.packetsSent||0);if(f<0)f=b;else if(b>f+200||S>5){clearInterval(h),r();return}}}}catch{}},250)})}function FA(n){const a=n.reduce((u,f)=>u+f.length,0),r=new Float32Array(a);let o=0;for(const u of n)r.set(u,o),o+=u.length;return r}function IA(n){const a=new Int16Array(n.length);for(let r=0;r{for(let v=0;vv&&a?.[v]||null,[a,v]),K=!!(D&&D.state!=="offline"&&!D.error),ae=D?.source==="upload"&&D?.state==="playing",he=D?.source==="mic-whip"&&(D?.state==="starting"||D?.state==="playing"),we=!!(!j&&F.current&&(N||T)),pe=!j&&(M==="live"||M==="starting"),ge=j&&R==="recording",xe=j&&R==="sending",ye=Tt(p?.keymap?.micPtt?.[0])||"M",Ce=m.useCallback(re=>{const de=re==="clip"?"clip":"live";x(fe=>({...fe||{},pttMode:de}))},[x]),qe=async()=>{if(!v){O("Take control of your rover first.");return}if(!b){O("Select an audio file first.");return}if(b.size>Ex){O(`File too large (max ${Ex} bytes).`);return}E(!0),O("");try{const re=await b.arrayBuffer(),de=Mx(new Uint8Array(re));await r?.({roverId:v,name:b.name,mime:b.type||"",dataBase64:de}),O("Upload playback started.")}catch(re){O(re?.message||"Failed to play upload.")}finally{E(!1)}},Ze=async()=>{if(!v){O("Take control of your rover first.");return}E(!0),O("");try{await o?.(v),O("Upload playback stopped.")}catch(re){O(re?.message||"Failed to stop upload.")}finally{E(!1)}},Se=m.useCallback(async re=>{const de=String(re||Q.current||"").trim();if(Z.current=!1,A("idle"),U.current){try{U.current.getSenders().forEach(fe=>fe.track?.stop())}catch{}try{U.current.close()}catch{}}if(U.current=null,L.current)try{L.current.getTracks().forEach(fe=>fe.stop())}catch{}if(L.current=null,F.current=null,de)try{await h?.(de)}catch{}Q.current=""},[h]),at=m.useCallback(async re=>{const de=await u?.(re),fe=String(de?.whipUrl||"").trim(),Re=String(de?.token||"").trim();if(!fe||!Re)throw new Error("WHIP endpoint unavailable");const te=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:Ps,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}});L.current=te;const me=te.getAudioTracks()?.[0];if(F.current=me||null,me&&(me.enabled=!!(N||T)),me?.applyConstraints)try{await me.applyConstraints({channelCount:1,sampleRate:Ps,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1})}catch{}const _e=new RTCPeerConnection(zA);U.current=_e,_e.onconnectionstatechange=()=>{if(!Z.current)return;const Nt=_e.connectionState;if(Nt==="connected"){A("live");return}(Nt==="failed"||Nt==="disconnected"||Nt==="closed")&&(A("error"),O(`WHIP transport ${Nt}.`))},te.getAudioTracks().forEach(Nt=>{const Ae=_e.addTrack(Nt,te);GA(Ae)});const en=await _e.createOffer({offerToReceiveAudio:!1,offerToReceiveVideo:!1});await _e.setLocalDescription(en),await VA(_e,1800);const tn=await fetch(fe,{method:"POST",headers:{"Content-Type":"application/sdp",...HA(Re)},body:_e.localDescription?.sdp||en.sdp});if(!tn.ok)throw new Error(`WHIP request failed: ${tn.status}`);const nn=await tn.text();await _e.setRemoteDescription({type:"answer",sdp:nn}),await $A(_e,1e4),await YA(_e,6e3),await f?.(re)},[N,T,f,u]),ht=m.useCallback(async()=>{if(J.current=!1,B.current){try{B.current.disconnect()}catch{}B.current.onaudioprocess=null}if(le.current)try{le.current.disconnect()}catch{}if(V.current)try{V.current.disconnect()}catch{}if(ce.current)try{ce.current.getTracks().forEach(re=>re.stop())}catch{}if(ue.current)try{await ue.current.close()}catch{}I.current=[],B.current=null,le.current=null,V.current=null,ce.current=null,ue.current=null,ie.current=Ps},[]),Ge=m.useCallback(async()=>{if(ce.current&&ue.current&&B.current)return;q(me=>me==="recording"?me:"arming");const re=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:Ps,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}}),de=new AudioContext({latencyHint:"interactive"}),fe=de.createMediaStreamSource(re),Re=de.createScriptProcessor(1024,1,1),te=de.createGain();te.gain.value=0,Re.onaudioprocess=me=>{if(!J.current)return;const _e=me.inputBuffer.getChannelData(0);I.current.push(new Float32Array(_e))},fe.connect(Re),Re.connect(te),te.connect(de.destination),ce.current=re,ue.current=de,le.current=fe,B.current=Re,V.current=te,ie.current=de.sampleRate||Ps},[]),je=m.useCallback(async({send:re})=>{const de=J.current;if(J.current=!1,!de){re||q("idle");return}const fe=I.current;if(I.current=[],!re){q("idle");return}if(!v){q("idle");return}if(!fe.length){q("idle");return}q("sending");try{const Re=FA(fe),te=XA(Re,ie.current||Ps),me=Mx(te);await r?.({roverId:v,name:`ptt-${Date.now()}.wav`,mime:"audio/wav",dataBase64:me}),q("idle")}catch(Re){q("error"),O(Re?.message||"Failed to send PTT clip.")}},[r,v]),se=m.useCallback(async()=>{if(!v){O("Take control of your rover first.");return}await Ge(),I.current=[],J.current=!0,q("recording")},[Ge,v]);return m.useEffect(()=>{let re=!1;async function de(){if(j){await Se(Q.current||v);return}if(!v){await Se(Q.current);return}if(!(Z.current&&Q.current===v)&&!(!N&&!T))try{A("starting"),O(""),Z.current=!0,Q.current=v,await at(v),re||A("live")}catch(fe){re||(A("error"),O(fe?.message||"Failed to start mic forwarding.")),await Se(v)}}return de(),()=>{re=!0}},[j,N,T,v,at,Se]),m.useEffect(()=>{const re=F.current;re&&(re.enabled=!!(!j&&(N||T)))},[j,N,T]),m.useEffect(()=>{let re=!1;async function de(){if(!j||!v){J.current&&await je({send:!1}),q("idle");return}try{await Ge(),!re&&!J.current&&q("idle")}catch(fe){re||(q("error"),O(fe?.message||"Microphone access failed for clip mode."))}}return de(),()=>{re=!0}},[j,Ge,je,v]),m.useEffect(()=>{let re=!1;async function de(){if(j){if(!v){J.current&&await je({send:!1});return}if(T){if(!J.current)try{O(""),await se()}catch(fe){re||(q("error"),O(fe?.message||"Failed to start clip recording."))}}else J.current&&await je({send:!0})}}return de(),()=>{re=!0}},[j,je,T,v,se]),m.useEffect(()=>()=>{Se(Q.current||v),ht()},[v,Se,ht]),l.jsx("section",{className:"surface",children:l.jsxs("div",{className:"grid gap-1",children:[l.jsx("p",{className:"text-sm text-slate-100 text-center",children:"Audio Controls"}),l.jsx("section",{className:"surface",children:l.jsxs("div",{className:"flex items-center justify-center gap-0.5 py-0.25 text-xs text-slate-300",children:[l.jsx("span",{children:"Push-to-Talk Key"}),l.jsx(Ud,{label:ye})]})}),l.jsxs("div",{className:"grid gap-0.5 grid-cols-1 lg:grid-cols-2",children:[l.jsx("section",{className:"surface h-full",children:l.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr]",children:[l.jsx("p",{className:"text-sm text-slate-200 text-center",children:"PTT Mode"}),l.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."}),l.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[l.jsx("button",{type:"button",className:`button-dark w-full text-sm ${j?"":"bg-emerald-500 text-white hover:bg-emerald-500"}`,onClick:()=>Ce("live"),children:"Live Mic (WHIP)"}),l.jsx("button",{type:"button",className:`button-dark w-full text-sm ${j?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,onClick:()=>Ce("clip"),children:"Record Clip"})]})]})}),l.jsx("section",{className:"surface h-full",children:l.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_1fr]",children:[l.jsx("p",{className:"text-sm text-slate-200 text-center",children:"PTT Clip Status"}),l.jsxs("div",{className:"grid gap-0.5 content-start",children:[l.jsx(pn,{label:"Mode",active:j,detail:j?"record clip":"live mic"}),l.jsx(pn,{label:"PTT hold",active:T,detail:T?"held":"released"}),l.jsx(pn,{label:"Recording",active:ge,detail:ge?"capturing":"idle"}),l.jsx(pn,{label:"Playback",active:j&&ae,detail:j&&ae?"playing":"idle"})]})]})})]}),l.jsxs("div",{className:"grid gap-0.5 grid-cols-1 lg:grid-cols-2",children:[l.jsx("section",{className:"surface h-full",children:l.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr_auto]",children:[l.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Audio Upload"}),l.jsxs("label",{className:"mx-auto grid w-full max-w-sm gap-0.5 text-xs text-slate-300 text-center",children:[l.jsx("span",{children:"Audio file (mp3 / wav / ogg)"}),l.jsx("input",{className:`${el} text-center`,type:"file",accept:".mp3,.wav,.ogg,audio/mpeg,audio/wav,audio/ogg",disabled:k||!v,onChange:re=>S(re.target.files?.[0]||null)})]}),l.jsx("div",{className:"mx-auto w-full max-w-sm text-center",children:b?l.jsxs("div",{className:"surface-muted text-xs text-slate-300",children:[b.name," (",b.size," bytes)"]}):l.jsx("div",{className:"surface-muted text-xs text-slate-500",children:"No file selected"})}),l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:k||!v,onClick:qe,children:k?"Working...":"Play Upload"}),l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:k||!v,onClick:Ze,children:"Stop"})]})]})}),l.jsx("section",{className:"surface h-full",children:l.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr]",children:[l.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Live Microphone"}),l.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:[l.jsx("input",{type:"checkbox",checked:N,disabled:!v||j,onChange:re=>x(de=>({...de||{},openMicEnabled:!!re.target.checked}))}),l.jsx("span",{children:j?"Open mic (live mode only)":"Open mic"})]}),l.jsxs("div",{className:"grid gap-0.5 content-start",children:[l.jsx(pn,{label:"WHIP link",active:pe,detail:M}),l.jsx(pn,{label:"Mic hot",active:we,detail:we?"transmitting":"muted"}),l.jsxs("div",{className:"surface-muted text-center text-xs text-slate-400",children:["Hold ",l.jsx(Ud,{label:ye})," to talk"]})]})]})})]}),l.jsx("section",{className:"surface",children:l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Audio Status"}),l.jsxs("div",{className:"grid gap-0.5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-6",children:[l.jsx(pn,{label:"Forward pipe",active:K,detail:K?"connected":"offline"}),l.jsx(pn,{label:"Upload playback",active:ae,detail:ae?"playing":"idle"}),l.jsx(pn,{label:"Mic relay",active:he,detail:he?"active":"idle"}),l.jsx(pn,{label:"WHIP transport",active:pe,detail:M}),l.jsx(pn,{label:"Clip recording",active:ge,detail:ge?"recording":"idle"}),l.jsx(pn,{label:"Clip sending",active:xe,detail:xe?"sending":"idle"})]}),$?l.jsx("div",{className:"text-xs text-slate-400 text-center",children:$}):null]})})]})})}function KA({pendingRequestId:n,currentStoredKey:a,nickname:r,requestVerification:o,applyIdentityKey:u,onMessage:f,fullWidth:h=!1}){const[p,g]=m.useState(0),[x,v]=m.useState(""),[b,S]=m.useState(!1),[k,E]=m.useState(!1),N=()=>{g(1),v(a),S(!1),f?.("")},w=()=>{g(0),S(!1)},j=async A=>{if(A.preventDefault(),!b){f?.("Please confirm your nickname agreement before sending.");return}if(p<3){f?.("Complete all request steps before sending.");return}E(!0),f?.("");try{const R=await u(x);await o?.(),v(R),g(0),S(!1),f?.("Verification request sent to lockdown admins.")}catch(R){f?.(R.message||"Failed to submit request.")}finally{E(!1)}},M=h?"w-full":Nr;return n?l.jsx("section",{className:`surface text-sm text-slate-300 ${M}`,children:l.jsxs("div",{className:Ka,children:["Verification request pending: ",n]})}):p===0?l.jsx("section",{className:`surface ${M}`,children:l.jsxs("div",{className:Ka,children:[l.jsx("p",{className:"text-sm text-slate-300",children:"Verification"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:N,disabled:k,children:"Request Verification"})]})}):l.jsx("form",{className:`surface ${M}`,onSubmit:j,children:l.jsxs("div",{className:Ka,children:[l.jsx("p",{className:"text-sm text-slate-300",children:"Request verification"}),l.jsxs("p",{className:"text-xs text-slate-500",children:["Step ",p," of 3"]}),p===1?l.jsxs("div",{className:"space-y-0.5",children:[l.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."}),l.jsx("div",{className:"mx-auto w-full max-w-sm",children:l.jsx(ll,{compact:!0})}),l.jsxs("div",{className:"surface-muted mx-auto w-full max-w-sm text-xs text-slate-300 text-center",children:["Current nickname: ",l.jsx("span",{className:"font-semibold",children:r||"(not set)"})]}),l.jsxs("label",{className:"flex items-center justify-center gap-0.5 text-xs text-slate-300 text-center",children:[l.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:b,onChange:A=>S(A.target.checked)}),l.jsx("span",{children:"I understand this nickname is tied to my verification."})]}),l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!r||!b,onClick:()=>g(2),children:"Continue"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:w,children:"Cancel"})]})]}):null,p===2?l.jsxs("div",{className:"space-y-0.5",children:[l.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."}),l.jsx("input",{className:el,type:"password",name:"identity_key_request",autoComplete:"current-password",maxLength:35,value:x,onChange:A=>v(A.target.value.toLowerCase()),placeholder:"Identity key for this request"}),l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!x,onClick:()=>navigator.clipboard?.writeText(x),children:"Copy Key"}),l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!x,onClick:()=>g(3),children:"Continue"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>g(1),children:"Back"})]})]}):null,p===3?l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-xs text-slate-400",children:"Final step: confirm and send your verification request."}),l.jsx("input",{className:el,type:"password",name:"identity_key_request_final",autoComplete:"current-password",maxLength:35,value:x,onChange:A=>v(A.target.value.toLowerCase()),placeholder:"Identity key for this request"}),l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:k||!x,children:"Confirm Request"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>g(2),children:"Back"})]})]}):null]})})}function ZA({currentStoredKey:n,applyIdentityKey:a,onMessage:r,fullWidth:o=!1}){const[u,f]=m.useState(0),[h,p]=m.useState(""),[g,x]=m.useState(!1),v=()=>{f(1),p(""),r?.("")},b=()=>{f(0),p("")},S=async E=>{if(E.preventDefault(),!(u<2)){x(!0),r?.("");try{await a(h),f(0),p(""),r?.("Identity key restored.")}catch(N){r?.(N.message||"Failed to restore key.")}finally{x(!1)}}},k=o?"w-full":Nr;return l.jsx("section",{className:`surface ${k}`,children:l.jsxs("div",{className:Ka,children:[l.jsx("p",{className:"text-sm text-slate-300",children:"Identity key"}),l.jsxs("p",{className:"text-xs text-slate-500",children:["Current: ",LA(n)||"not set yet"]}),l.jsx("input",{className:el,type:"password",name:"identity_key_current",autoComplete:"current-password",maxLength:35,value:n,readOnly:!0,placeholder:"Identity key"}),u===0?l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!n,onClick:()=>navigator.clipboard?.writeText(n),children:"Copy Key"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:v,disabled:g,children:"Restore Key"})]}):null,u===1?l.jsxs("div",{className:"space-y-0.5",children:[l.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."}),l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(2),children:"Continue"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:b,children:"Cancel"})]})]}):null,u===2?l.jsxs("form",{className:"space-y-0.5",onSubmit:S,children:[l.jsx("input",{className:el,type:"password",name:"identity_key_restore",autoComplete:"current-password",maxLength:35,value:h,onChange:E=>p(E.target.value.toLowerCase()),placeholder:"Paste key to restore"}),l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:g||!h,children:"Confirm Restore"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(1),children:"Back"})]})]}):null]})})}function JA({requestableRovers:n=[],pendingRequests:a=[],requestPrivateRoverAccess:r,onMessage:o,fullWidth:u=!1}){const[f,h]=m.useState({}),p=m.useMemo(()=>{const v=new Map;return(a||[]).forEach(b=>{b?.roverId&&v.set(String(b.roverId),b)}),v},[a]),g=u?"w-full":Nr,x=async v=>{if(v){h(b=>({...b,[v]:!0})),o?.("");try{(await r?.(v))?.existing?o?.("You already have a pending request for that rover."):o?.("Private rover access request sent to lockdown admins.")}catch(b){o?.(b.message||"Failed to send request.")}finally{h(b=>({...b,[v]:!1}))}}};return l.jsx("section",{className:`surface text-sm text-slate-300 ${g}`,children:l.jsxs("div",{className:Ka,children:[l.jsx("p",{className:"text-sm text-slate-300",children:"Private rover access requests"}),n.length===0?l.jsx("p",{className:"text-xs text-slate-500",children:"No closed private rovers are available to request right now."}):l.jsx("div",{className:"w-full space-y-0.5",children:n.map(v=>{const b=String(v.id),S=!!f[b],k=p.get(b);return l.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-1 py-0.5",children:[l.jsxs("div",{className:"min-w-0 text-left",children:[l.jsx("p",{className:"truncate text-xs font-semibold text-slate-100",children:v.name||b}),l.jsx("p",{className:"truncate text-[0.7rem] text-slate-500",children:b})]}),k?l.jsx("span",{className:"rounded bg-amber-700/30 px-1 py-0.5 text-[0.7rem] text-amber-200",children:"Pending"}):l.jsx("button",{type:"button",onClick:()=>x(b),disabled:S,className:"button-dark text-xs disabled:opacity-50",children:S?"Sending...":"Request"})]},b)})})]})})}function io(n){return String(n||"").trim()}function PA(n){const a=io(n);return a?a.includes("DOCKINGRUNNING")?"Returning":a.includes("PAUSED")?"Paused":a.includes("HOUSECLEANINGRUNNING")?"Cleaning":a.includes("SPOTCLEANINGRUNNING")?"Spot cleaning":a.includes("STATE_START")?"Starting":a.includes("STATE_IDLE")||a.includes("STATE_STANDBY")?"Idle":a:"--"}function lv(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 Qi({label:n,value:a,tone:r="muted",valueClass:o="",hideLabel:u=!1}){return l.jsxs("div",{className:`rounded-md px-1 py-0.75 text-center ${lv(r)}`,children:[u?null:l.jsx("div",{className:"text-[0.72rem] opacity-90",children:n}),l.jsx("div",{className:`text-sm font-semibold ${o}`,title:String(a||""),children:a})]})}function WA({neato:n,onStart:a,onSendHome:r,onLocate:o,onClearErrors:u,onPowerCycle:f,fullWidth:h=!1}){const[p,g]=m.useState(""),x=h?"w-full":"w-full max-w-xl",v=!!n?.configured,b=!!(n?.enabled&&n?.connected),S=!!n?.telemetry?.extPowerPresent,k=!!n?.telemetry?.chargingActive,E=PA(n?.telemetry?.uiState),N=io(n?.telemetry?.robotState)||"--",w=n?.telemetry?.batteryPercent,j=Number.isFinite(w)?`${w}%`:"--",M=n?.telemetry?.batteryVoltage,A=Number.isFinite(M)?`${M.toFixed(2)} V`:"--",R=io(n?.telemetry?.robotError)||"--",q=io(n?.telemetry?.robotAlert)||"--",$=n?.controls||{},O=!!$?.start?.available,L=!!$?.sendHome?.available,F=!!$?.locate?.available,U=!!$?.clearErrors?.available,Z=!!$?.powerCycle?.available,Q=v&&b&&O,ce=v&&b&&L,ue=v&&b&&F,le=v&&b&&U,B=v&&b&&Z,V=async(D,K)=>{if(K){g(D);try{await K()}catch{}finally{g("")}}},I=v?b?"Online":"Offline":"Not configured",J=v?b?"good":"danger":"warn",ie=w==null?"muted":w>=60?"good":w>=25?"warn":"danger",T=S?"Docked":E!=="--"?E:"Away from dock";return l.jsx("section",{className:`surface text-sm text-slate-200 ${x}`,children:l.jsxs("div",{className:"grid gap-0.5",children:[l.jsxs("div",{className:"relative flex items-center justify-center min-h-[1.5rem]",children:[l.jsxs("div",{className:"text-center",children:[l.jsx("p",{className:"text-sm text-slate-100",children:"Neato Controls"}),l.jsx("p",{className:"text-xs text-slate-400",children:"Control the autonomous Neato robovac. Be nice to him."})]}),l.jsx("span",{className:`absolute right-0 inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${lv(J)}`,children:I})]}),l.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-0.5",children:[l.jsxs("div",{className:"surface-muted grid gap-0.5",children:[l.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Controls"}),l.jsx("button",{type:"button",disabled:!Q||!!p,onClick:()=>V("start",a),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:p==="start"?"Starting...":"Start cleaning"}),l.jsx("button",{type:"button",disabled:!ce||!!p,onClick:()=>V("sendHome",r),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:p==="sendHome"?"Sending...":"Send to dock"}),l.jsxs("div",{className:"grid grid-cols-3 gap-0.5",children:[l.jsx("button",{type:"button",disabled:!ue||!!p,onClick:()=>V("locate",o),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:p==="locate"?"Playing...":"Play sound"}),l.jsx("button",{type:"button",disabled:!le||!!p,onClick:()=>V("clearErrors",u),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:p==="clearErrors"?"Clearing...":"Clear errors"}),l.jsx("button",{type:"button",disabled:!B||!!p,onClick:()=>V("powerCycle",f),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:p==="powerCycle"?"Cycling...":"Power cycle"})]}),l.jsxs("div",{className:"surface-muted grid gap-0.5 pt-0.25",children:[l.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Power status"}),l.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[l.jsx(Qi,{label:"Battery",value:j,tone:ie}),l.jsx(Qi,{label:"Voltage",value:A,tone:"muted"}),l.jsx(Qi,{label:"Docked",value:S?"Docked":"Not docked",tone:S?"good":"muted",hideLabel:!0}),l.jsx(Qi,{label:"Charging",value:k?"charging":"not charging",tone:k?"good":"muted",hideLabel:!0})]})]})]}),l.jsx("div",{className:"grid gap-0.5",children:l.jsxs("div",{className:"surface-muted grid gap-0.5",children:[l.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Robot Status"}),l.jsxs("div",{className:"grid gap-0.5",children:[l.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[l.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot state (raw)"}),l.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:N})]}),l.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[l.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Basic state"}),l.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:T})]}),l.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[l.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"UI state"}),l.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:E})]}),l.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[l.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot error"}),l.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:R})]}),l.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[l.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot alert"}),l.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:q})]})]})]})})]}),!v||!b||!O||!L||!F||!U||!Z?l.jsx("p",{className:"text-xs text-slate-400 text-center",children:v?b?"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 e2(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 t2(n){return n==="up"?"Up":n==="down"?"Down":n==="stopped"?"Transitioning...":n==="conflict"?"Conflict":"--"}function n2({lift:n,onUp:a,onDown:r,fullWidth:o=!1}){const[u,f]=m.useState(""),[h,p]=m.useState(()=>Date.now()),g=o?"w-full":"w-full max-w-xl",x=!!n?.configured,v=!!(n?.enabled&&n?.connected),b=!!n?.busy,S=String(n?.target||"").toLowerCase(),k=String(n?.position||"").toLowerCase(),E=!!n?.availability?.upSwitch,N=!!n?.availability?.downSwitch,w=Number(n?.lastActionAt||0),j=Math.max(0,Number(n?.commandCooldownMs||0)),M=Math.max(0,w+j-h),A=!b&&M>0,R=b||A;m.useEffect(()=>{if(!R)return;const U=setInterval(()=>{p(Date.now())},100);return()=>clearInterval(U)},[R]),m.useEffect(()=>{p(Date.now())},[w,j,b]);const q=x?v?R?"Busy":"Ready":"Offline":"Not configured",$=x?v?R?"warn":"good":"danger":"warn",O=x&&v&&!R&&!u&&E&&N,L=m.useMemo(()=>Math.ceil(M/1e3),[M]),F=async(U,Z)=>{if(Z){f(U);try{await Z()}catch{}finally{f("")}}};return l.jsxs("section",{className:`surface relative text-sm text-slate-200 ${g}`,children:[R?l.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:l.jsxs("div",{className:"space-y-0.25",children:[l.jsx("p",{className:"text-sm font-semibold text-slate-100",children:b?"Preparing to move":"Moving!"}),l.jsx("p",{className:"text-xs text-slate-300",children:"Controls are disabled while the lift is moving, otherwise its tiny brain would get confused."}),!b&&A?l.jsxs("p",{className:"text-xs text-slate-400",children:["About ",L,"s remaining."]}):null]})}):null,l.jsxs("div",{className:"grid gap-0.5",children:[l.jsxs("div",{className:"relative flex items-center justify-center min-h-[1.5rem]",children:[l.jsxs("div",{className:"text-center",children:[l.jsx("p",{className:"text-sm text-slate-100",children:"Lift Controls"}),l.jsx("p",{className:"text-xs text-slate-400",children:"Move the lift up and down. Please don't break anything..."})]}),l.jsx("span",{className:`absolute right-0 inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${e2($)}`,children:q})]}),l.jsx("section",{className:"surface-muted px-0.5 py-0.5",children:l.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-0.5",children:[l.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.75 text-center",children:[l.jsx("div",{className:"text-xs text-slate-300",children:"Position"}),l.jsx("div",{className:"text-base font-semibold text-slate-100",children:t2(k)})]}),l.jsx("button",{type:"button",disabled:!O,onClick:()=>F("down",r),className:`button-dark w-full text-sm disabled:opacity-50 ${k==="down"||S==="down"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:u==="down"||S==="down"?"Lowering...":"Down"}),l.jsx("button",{type:"button",disabled:!O,onClick:()=>F("up",a),className:`button-dark w-full text-sm disabled:opacity-50 ${k==="up"||S==="up"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:u==="up"||S==="up"?"Raising...":"Up"})]})}),!x||!v||!E||!N?l.jsx("p",{className:"text-xs text-slate-400 text-center",children:x?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?l.jsxs("p",{className:"text-xs text-rose-300 text-center",children:["Last error: ",n.lastError]}):null]})]})}function s2(n){const a=String(n||"").trim();if(!a)return"";try{const r=new URL(a),o=String(r.protocol||"").toLowerCase();return o!=="http:"&&o!=="https:"?"":r.toString()}catch{return""}}function a2({isVerified:n=!1,fullWidth:a=!1,onMessage:r}){const{value:o,save:u}=Oe("profile",{nickname:"",profileImageUrl:""}),f=String(o?.profileImageUrl||"").trim(),[h,p]=m.useState(f),[g,x]=m.useState(!1);m.useEffect(()=>{p(f)},[f]);const v=m.useMemo(()=>s2(h),[h]),b=String(h||"").trim()!==f,S=!!(n&&b&&(v||!String(h||"").trim())),k=a?"w-full":Nr,E=async N=>{if(N.preventDefault(),!n){r?.("Verify your account before setting a profile image URL.");return}x(!0),r?.("");try{const w=v||"";u(j=>({...j||{},profileImageUrl:w})),r?.(w?"Profile image URL saved.":"Profile image URL cleared.")}finally{x(!1)}};return l.jsx("section",{className:`surface text-sm text-slate-300 ${k}`,children:l.jsxs("form",{className:Ka,onSubmit:E,children:[l.jsx("p",{className:"text-sm text-slate-300",children:"Chat profile image URL"}),l.jsx("p",{className:"text-xs text-slate-500",children:"Verified users can set a custom avatar for chat and Discord bridge messages."}),l.jsx("input",{className:el,type:"url",name:"vip_profile_image_url",placeholder:"https://example.com/avatar.png",value:h,onChange:N=>p(N.target.value),disabled:!n||g}),!v&&String(h||"").trim()?l.jsx("p",{className:"text-xs text-amber-300",children:"Enter a valid http/https image URL."}):null,l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"submit",className:"button-dark text-sm disabled:opacity-50",disabled:!S||g,children:g?"Saving...":"Save URL"}),l.jsx("button",{type:"button",className:"button-dark text-sm disabled:opacity-50",disabled:!n||g||!f,onClick:()=>p(""),children:"Clear"})]})]})})}function rv({isActive:n=!0}){const a=oe(ue=>ue.session),{identifySession:r,requestVerification:o,requestPrivateRoverAccess:u,playUploadedAudio:f,stopUploadedAudio:h,startMicWhip:p,readyMicWhip:g,stopMicWhip:x,neatoStart:v,neatoSendHome:b,neatoLocate:S,neatoClearErrors:k,neatoPowerCycle:E,liftUp:N,liftDown:w}=Ot(),{value:j,save:M}=Oe("identity",{cookieUserId:""}),{value:A}=Oe("profile",{nickname:""}),R=m.useMemo(()=>(j?.cookieUserId||"").trim(),[j?.cookieUserId]),q=(A?.nickname||"").trim(),$=!!a?.isVerified,O=a?.verification?.pendingRequestId||null,L=a?.privateRoverAccess?.requestableRovers||[],F=a?.privateRoverAccess?.pendingRequests||[],U=String(a?.assignment?.roverId||"").trim(),[Z,Q]=m.useState(""),ce=async ue=>{const le=String(ue||"").trim().toLowerCase();if(!le)throw new Error("Identity key required.");if(!BA.test(le))throw new Error("Identity key must match format: cu_ + 32 lowercase hex chars.");return M(B=>({...B||{},cookieUserId:le})),await r({cookieUserId:le,nickname:q}),le};return l.jsxs("section",{className:"space-y-2 text-base",children:[l.jsxs("div",{className:"grid gap-2 grid-cols-1 lg:grid-cols-[minmax(0,1.25fr)_minmax(0,0.9fr)]",children:[$?null:l.jsx("div",{className:"lg:col-span-2",children:l.jsx(KA,{pendingRequestId:O,currentStoredKey:R,nickname:q,requestVerification:o,applyIdentityKey:ce,onMessage:Q,fullWidth:!0})}),l.jsx("div",{className:"lg:col-span-2",children:$?l.jsxs("div",{className:"space-y-2",children:[l.jsx(WA,{neato:a?.neato||null,onStart:v,onSendHome:b,onLocate:S,onClearErrors:k,onPowerCycle:E,fullWidth:!0}),l.jsx(n2,{lift:a?.lift||null,onUp:N,onDown:w,fullWidth:!0}),l.jsx(QA,{ownRoverId:U,audioForwardByRover:a?.audioForward||{},playUploadedAudio:f,stopUploadedAudio:h,startMicWhip:p,readyMicWhip:g,stopMicWhip:x})]}):l.jsx("section",{className:"surface h-full",children:l.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."})})}),l.jsx("div",{className:"lg:col-span-2",children:l.jsx(JA,{requestableRovers:L,pendingRequests:F,requestPrivateRoverAccess:u,onMessage:Q,fullWidth:!0})}),l.jsx("div",{className:"lg:col-span-2",children:l.jsx(ZA,{currentStoredKey:R,applyIdentityKey:ce,onMessage:Q,fullWidth:!0})}),l.jsx("div",{className:"lg:col-span-2",children:l.jsx(a2,{isVerified:$,onMessage:Q,fullWidth:!0})})]}),Z?l.jsx("div",{className:Nr,children:l.jsx("p",{className:"text-xs text-slate-400 text-center",children:Z})}):null]})}const l2=420,r2=1200,i2={1:262,2:330,3:392,4:523};function bo(){const n=oe(N=>N.session?.buttonBox?.buttons??[]),a=Pt(),{value:r}=Oe("audio",it),o=Number.isFinite(r?.masterVolume)?r.masterVolume:it.masterVolume,u=Number.isFinite(r?.alertVolume)?r.alertVolume:it.alertVolume,f=Math.max(0,Math.min(1,o*u)),h=m.useMemo(()=>{const N=Array.isArray(n)?n:[];return N.length===4?N:[1,2,3,4].map(w=>N.find(j=>Number(j?.id)===w)||{id:w,count:0,goal:0,rewardName:null,rewardId:null,rewardNumber:null,lastRewardAt:null})},[n]),[p,g]=m.useState({}),[x,v]=m.useState({}),b=m.useRef(new Map),S=m.useRef(new Map),k=m.useRef({}),E=m.useRef(null);return m.useEffect(()=>{const N={};h.forEach(j=>{N[j.id]=Number(j.lastRewardAt||0)});const w=k.current||{};h.forEach(j=>{const M=Number(j.id),A=N[M]||0,R=Number(w[M]||0);if(A>0&&A!==R){v(O=>({...O,[M]:!0}));const q=S.current.get(M);q&&clearTimeout(q);const $=setTimeout(()=>{v(O=>({...O,[M]:!1})),S.current.delete(M)},r2);S.current.set(M,$)}}),k.current=N},[h]),m.useEffect(()=>{function N(j){const M=i2[j];if(!M||f<=0)return;let A=E.current;if(!A){const O=window.AudioContext||window.webkitAudioContext;if(!O)return;A=new O,E.current=A}const R=A.createOscillator(),q=A.createGain();R.type="square",R.frequency.value=M;const $=.14*f;q.gain.setValueAtTime(1e-4,A.currentTime),q.gain.exponentialRampToValueAtTime(Math.max(1e-4,$),A.currentTime+.01),q.gain.exponentialRampToValueAtTime(1e-4,A.currentTime+.2),R.connect(q),q.connect(A.destination),R.start(),R.stop(A.currentTime+.21)}function w(j={}){const M=Number(j.buttonId);if(!Number.isFinite(M)||M<1||M>4)return;g(q=>({...q,[M]:!0}));const A=b.current.get(M);A&&clearTimeout(A);const R=setTimeout(()=>{g(q=>({...q,[M]:!1})),b.current.delete(M)},l2);b.current.set(M,R),N(M)}return a.on("buttonBox:increment",w),()=>{a.off("buttonBox:increment",w)}},[f,a]),l.jsxs("section",{className:"panel-section space-y-0.5 text-base",children:[l.jsx("header",{className:"panel-muted text-center text-sm",children:l.jsx("p",{children:"Button Box"})}),l.jsx("div",{className:"grid grid-cols-4 gap-0.5",children:h.map(N=>{const w=Number(N.id),j=Number.isFinite(N.count)?N.count:0,M=Number.isFinite(N.goal)?N.goal:0,A=typeof N.rewardName=="string"&&N.rewardName.trim()?N.rewardName.trim():"Unassigned",R=Number.isFinite(N.rewardNumber)?N.rewardNumber:"?",q=!!p[w],$=!!x[w];return l.jsx(yg,{buttonId:w,count:j,goal:M,rewardNumber:R,rewardName:A,className:[q?"bg-cyan-900/45":"",$?"bg-fuchsia-900/45":""].filter(Boolean).join(" ")},w)})})]})}function iv(){const{identifySession:n}=Ot(),a=oe(x=>x.session?.overseerVote||null),{value:r}=Oe("profile",{nickname:""}),{value:o}=Oe("identity",{cookieUserId:""}),{value:u,save:f}=Oe("overseerPreference",{enabled:!0}),h=!!u?.enabled,p=!!a?.running,g=async x=>{const v=!!x?.target?.checked;await f(b=>({...b||{},enabled:v})),await n({cookieUserId:String(o?.cookieUserId||"").trim(),nickname:String(r?.nickname||"").trim(),overseerEnabled:v})};return l.jsxs("section",{className:"surface p-1 text-xs text-slate-200 text-center",children:[l.jsxs("label",{className:"flex items-center justify-center gap-1.5 bg-gray-700 surface",children:[l.jsx("input",{type:"checkbox",checked:h,onChange:g}),l.jsx("span",{children:"Enable Overseer LLM"})]}),l.jsx("span",{className:"text-xs",children:"Cast your vote to enable Overseer in chat."}),l.jsxs("div",{className:"mt-0.5 text-slate-400",children:["Yes!: ",Number(a?.yesCount||0),", No...: ",Number(a?.noCount||0)]}),l.jsx("div",{className:"mt-0.5 flex justify-center",children:l.jsx("span",{className:`inline-flex items-center rounded px-1.5 py-0.5 text-[0.7rem] font-medium ${p?"bg-emerald-600/80 text-emerald-50":"bg-slate-600/70 text-slate-100"}`,children:p?"Running!":"Stopped..."})})]})}function o2(){const{state:{roverId:n}}=yt(),r=Wt(n)?.sensors||{};return l.jsx("section",{className:"panel-section",children:l.jsx("div",{className:"aspect-square w-full",children:l.jsx(Fg,{sensors:r})})})}function c2(){const{state:{roverId:n,keymap:a,camera:r,horn:o},pipeline:u,actions:{setServoAngle:f,setNightVision:h,startHorn:p,stopHorn:g}}=yt(),x=al(),v=Bg(n),b=v.docked&&!v.driving,S=r?.config,k=!!(n&&r?.enabled&&S),E=!!(n&&u?.nightVision),N=u?.nightVisionState,w=!!(n&&u?.horn),j=o?.overheated,M=typeof S?.minAngle=="number"?S.minAngle:-30,A=typeof S?.maxAngle=="number"?S.maxAngle:30,R=typeof r?.angle=="number"?r.angle:typeof S?.homeAngle=="number"?S.homeAngle:(M+A)/2,q=Tt(a?.nightVisionToggle?.[0]),$=Tt(a?.hornHonk?.[0]),O=Tt(a?.cameraUp?.[0]),L=Tt(a?.cameraDown?.[0]),F=!!(!n||x.cameraLocked);return l.jsxs("section",{className:"panel-section grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] gap-0.5",children:[l.jsx(Lg,{layout:"desktop",expand:!0,driveDockState:v}),b?null:l.jsxs("div",{className:"surface space-y-0.5 p-0 text-sm text-slate-200",children:[E&&l.jsx(zg,{nightVisionOn:N?.nightVisionOn,disabled:!n,onToggle:h,keyLabel:q}),w&&l.jsx(Ug,{disabled:!n||j,onStart:p,onStop:g,keyLabel:$,active:o?.active,heat:o?.heat}),k&&l.jsx(Hg,{value:R,min:M,max:A,disabled:F,onChange:f,keyDownLabel:L,keyUpLabel:O,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 u2({layout:n,onOpenHelpOverlay:a}){const[r,o]=m.useState("telemetry"),u=oe(N=>!!N.session?.isVerified),f=oe(N=>String(N.session?.assignment?.roverId||"").trim()),h=oe(N=>{const w=String(N.session?.assignment?.roverId||"").trim();return w&&N.session?.audioForward?.[w]||null}),{state:p}=yt(),{value:g}=Oe("vipAudio",{openMicEnabled:!1,pttMode:"live"}),x=u?"bg-emerald-400":"bg-red-600",v=!!p?.mic?.pttActive,b=!!g?.openMicEnabled,S=g?.pttMode==="clip"?"clip":"live",k=!!(f&&u&&(S==="clip"?v:b||v)),E=!!(f&&u&&S==="clip"&&h?.source==="upload"&&h?.state==="playing");return l.jsx("section",{className:"panel text-base",children:l.jsxs(vf,{defaultTab:"telemetry",currentTab:r,onTabChange:o,children:[l.jsxs(bf,{children:[l.jsx(Zt,{id:"telemetry",children:"Controls"}),l.jsx(Zt,{id:"vip",highlight:E?"green":k?"pink":"none",children:l.jsxs("span",{className:"inline-flex items-center gap-2",children:[l.jsx("span",{children:"VIP"}),l.jsx("span",{className:`inline-block h-3 w-3 rounded-full ${x}`,"aria-hidden":"true",title:u?"Verified":"Not verified"})]})}),l.jsx(Zt,{id:"help",children:"Help"}),l.jsx(Zt,{id:"settings",children:"Settings"})]}),l.jsxs(yf,{children:[l.jsx(Jt,{id:"telemetry",children:l.jsxs("div",{className:"space-y-0.5",children:[l.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]",children:[l.jsx(o2,{}),l.jsx(c2,{})]}),l.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]",children:[l.jsx(To,{}),l.jsx(tf,{panelId:"replay-sources-desktop",fillHeight:!0}),l.jsx(MA,{})]}),l.jsxs("div",{className:"grid items-stretch gap-0.5 grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)] h-[14rem]",children:[l.jsx(Ro,{fillHeight:!0}),l.jsxs("div",{className:"grid min-h-0 gap-0.5 grid-rows-[auto_minmax(0,1fr)_auto]",children:[l.jsx(iv,{}),l.jsx(vo,{compact:!0,hideNicknameForm:!0,fillHeight:!0}),l.jsx(EA,{compact:!0})]})]}),l.jsx(Zg,{}),l.jsx(bo,{}),l.jsx(uf,{defaultOrientation:"horizontal",panelId:"rightpane-telemetry"})]})}),l.jsx(Jt,{id:"vip",keepMounted:!0,children:l.jsx(rv,{isActive:r==="vip"})}),l.jsx(Jt,{id:"help",children:l.jsx(av,{layout:n,onOpenOverlay:a})}),l.jsx(Jt,{id:"settings",children:l.jsx(tv,{})})]})]})})}const d2=new Set(["admin","lockdown","lockdown-admin"]),f2=new Set(["lockdown","lockdown-admin"]),m2=new Set(["admin","lockdown"]);function h2(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 p2(){const n=oe(b=>b.session?.mode||null),a=oe(b=>b.session?.role||null),r=oe(b=>b.session?.adminReason?.text||"");oe(b=>b.session?.adminReason?.updatedAt||null);const o=oe(b=>b.session?.timezone||"UTC"),u=oe(b=>(b.session?.socials||[]).find(E=>String(E?.id||E?.label||"").toLowerCase()==="discord")?.url||b.session?.discord?.invite||null),f=m2.has(n),h=n==="lockdown"?f2.has(a):d2.has(a),[p,g]=m.useState(()=>new Date),x=m.useMemo(()=>{try{return new Intl.DateTimeFormat("en-US",{timeZone:o,hour:"numeric",minute:"2-digit",second:"2-digit"}).format(p)}catch{return p.toLocaleTimeString()}},[p,o]);if(m.useEffect(()=>{const b=setInterval(()=>g(new Date),1e3);return()=>clearInterval(b)},[]),!f||h)return null;const v=h2(n);return l.jsx("div",{className:"pointer-events-auto fixed inset-0 z-50 flex items-center justify-center bg-black px-0.5 py-0.5",children:l.jsxs("div",{className:"surface w-full max-w-md space-y-0.5 text-slate-100 shadow-2xl",children:[l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-lg font-semibold",children:v.title}),l.jsx("p",{className:"text-sm text-slate-300",children:v.description})]}),l.jsxs("div",{className:"surface-muted space-y-0.5",children:[l.jsx("p",{className:"text-[0.7rem] tracking-wide text-slate-400",children:"Reason for locking:"}),l.jsx("p",{className:"text-lg font-semibold text-slate-100",children:r||"No reason set."}),l.jsxs("p",{className:"text-center text-sm text-slate-300",children:["Server time: ",x]})]}),l.jsx("div",{className:"surface-muted",children:l.jsx(Jg,{})}),l.jsx("div",{className:"w-full justify-center items-center",children:l.jsx(Ao,{id:"discord",label:"Join our Discord server for updates!",url:u})}),"You can still use the chat while the server is locked:",l.jsxs("div",{className:"max-h-80 overflow-y-auto",children:[l.jsx(Ro,{}),l.jsx(ll,{})]})]})})}const x2="/assets/turn_alert-DVO8EZJa.mp3";function g2(n,a=1){const r=m.useRef(null);return m.useEffect(()=>{r.current=new Audio(n),r.current.volume=a,r.current.load()},[n,a]),()=>{r.current&&(r.current.volume=a,r.current.currentTime=0,r.current.play().catch(()=>{}))}}function v2(){const n=oe(v=>v.session?.socketId||null),a=oe(v=>v.session?.turnQueues||{}),r=oe(v=>v.session?.roster||[]),{pushAlert:o}=Ot(),{value:u}=Oe("audio",it),f=Number.isFinite(u?.masterVolume)?u.masterVolume:it.masterVolume,h=Number.isFinite(u?.alertVolume)?u.alertVolume:it.alertVolume,p=Math.max(0,Math.min(1,f*h)),g=g2(x2,p),x=m.useRef(new Set);return m.useEffect(()=>{if(!n)return;const v=[];Object.entries(a).forEach(([b,S])=>{!S||!S.current||S.current!==n||x.current.has(b)||(v.push(b),x.current.add(b))}),Object.keys(a).forEach(b=>{(!a[b]||a[b].current!==n)&&x.current.delete(b)}),v.length!==0&&(v.forEach(b=>{const S=r.find(k=>String(k.id)===String(b))?.name||b;o({title:"Your turn!",message:`You now control ${S}.`,color:"#ff5722"})}),g())},[a,g,o,r,n]),null}function b2({visible:n,mode:a,onEnterFullscreen:r,onDismiss:o}){if(!n)return null;const u=a==="pwa-hint";return l.jsx("div",{className:"fixed inset-0 z-40 flex items-center justify-center p-1 pointer-events-none bg-black/20",children:l.jsx("div",{className:"pointer-events-auto w-full max-w-sm surface center",children:l.jsxs("div",{className:"space-y-0.5 p-1 text-sm text-slate-100",children:[l.jsx("h2",{className:"text-base font-semibold text-white border-b border-slate-700",children:"Better in fullscreen!"}),u?l.jsxs("p",{className:"text-slate-300",children:["For fullscreen on iOS, open Safari's share menu and pick ",l.jsx("strong",{children:"Add to Home Screen"}),". Launching from the home screen then makes it fullscreen."]}):l.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."}),l.jsx("p",{className:"text-xs border-t border-b border-slate-700 p-0.5 text-blue-300 text-center",children:"This will only show once just to let you know. There is a fullscreen button in the bottom right for later use."}),l.jsxs("div",{className:"flex justify-end gap-0.5 pt-1 text-sm",children:[l.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&&l.jsx("button",{type:"button",className:"rounded bg-cyan-500 px-3 py-1 font-semibold text-black hover:bg-cyan-400",onClick:r,children:"Enter fullscreen"})]})]})})})}function y2({side:n="right",onClick:a}){const r=n==="left"?"left-2":"right-2";return l.jsx("button",{type:"button",onClick:a,className:`fixed bottom-2 ${r} z-30 flex h-10 w-10 items-center justify-center rounded-full border border-slate-700 bg-black text-slate-200 transition hover:bg-zinc-900 active:scale-95`,"aria-label":"Enter fullscreen",title:"Enter fullscreen",children:l.jsx("svg",{viewBox:"0 0 24 24",width:"18",height:"18","aria-hidden":"true",className:"pointer-events-none",children:l.jsx("path",{d:"M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})})}const S2=new Set(["mobile-portrait","mobile-landscape"]),j2=()=>{if(typeof navigator>"u")return!1;const n=navigator.userAgent||navigator.platform||"";return/iphone|ipad|ipod/i.test(n)},Tx=()=>{if(typeof document>"u")return!1;const n=document;return!!(n.fullscreenEnabled||n.webkitFullscreenEnabled||n.mozFullScreenEnabled||n.msFullscreenEnabled)};function N2(n){const{value:a,save:r,status:o}=Oe("page",{fullscreenPromptShown:!1}),u=S2.has(n),[f,h]=m.useState(!1),[p,g]=m.useState("native"),[x,v]=m.useState(!1),b=!!a?.fullscreenPromptShown,S=m.useMemo(()=>j2(),[]),k=m.useMemo(()=>Tx(),[]),E=m.useCallback(()=>{o==="ready"&&r(A=>A?.fullscreenPromptShown?A:{...A||{},fullscreenPromptShown:!0})},[r,o]),N=m.useCallback(()=>{if(o!=="ready"){h(!1);return}if(!u){h(!1),v(!1);return}if(!x&&b){h(!1);return}if(S){g("pwa-hint"),h(!0);return}if(!Tx()){h(!1);return}if(typeof document<"u"&&document.fullscreenElement){h(!1);return}g("native"),h(!0)},[x,b,S,u,o]);m.useEffect(()=>{N()},[N]),m.useEffect(()=>{if(typeof document>"u")return;const A=()=>{if(document.fullscreenElement){v(!1),h(!1);return}N()};return document.addEventListener("fullscreenchange",A),document.addEventListener("webkitfullscreenchange",A),()=>{document.removeEventListener("fullscreenchange",A),document.removeEventListener("webkitfullscreenchange",A)}},[N]);const w=m.useCallback(()=>{E(),v(!1),h(!1)},[E]),j=m.useCallback(async()=>{if(!u||S||typeof document>"u")return!1;if(document.fullscreenElement)return!0;const A=document.documentElement;if(!A)return!1;const R=A.requestFullscreen||A.webkitRequestFullscreen||A.mozRequestFullScreen||A.msRequestFullscreen;if(!R)return!1;try{const q=R.call(A);return q&&typeof q.then=="function"&&await q,E(),v(!1),h(!1),!0}catch(q){return console.warn("Failed to enter fullscreen",q),!1}},[S,u,E]),M=m.useCallback(()=>{u&&(v(!0),g(S?"pwa-hint":"native"),h(!0))},[S,u]);return m.useEffect(()=>{u||(v(!1),h(!1))},[u]),{visible:f,mode:p,isIOS:S,nativeSupported:k,enterFullscreen:j,dismiss:w,showPrompt:M}}function w2({visible:n,layout:a,onClose:r,showOnLoad:o,onToggleShowOnLoad:u}){if(!n)return null;const{state:f}=yt(),h=p=>{const g=!p.target.checked;u?.(g)};return l.jsx("div",{className:"fixed inset-0 z-30 flex items-center justify-center bg-black/80 px-0.5 py-0.5",children:l.jsxs("div",{className:"pointer-events-auto surface w-full max-w-4xl max-h-[90vh] overflow-hidden shadow-2xl",children:[l.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:[l.jsx("span",{className:"font-semibold",children:"Help & controls"}),l.jsxs("div",{className:"flex items-center gap-0.5 text-[0.8rem] text-slate-300",children:[l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!o,onChange:h,className:"accent-cyan-500"}),l.jsx("span",{children:"Don't show again"})]}),l.jsx("button",{type:"button",onClick:r,className:"button-dark px-2 py-0.25 text-[0.8rem]",children:"Close"})]})]}),l.jsx("div",{className:"max-h-[85vh] overflow-y-auto p-0.5",children:l.jsx(sv,{layout:a,keymap:f?.keymap||{}})})]})})}function Ia({label:n,keyLabel:a}){return l.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-0.5 py-0.35 text-[0.8rem] text-slate-200",children:[l.jsx("span",{children:n}),l.jsx(Ud,{label:a})]})}function A2({keymap:n}){return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-sm text-slate-200",children:'1. Press "Start Driving" put your rover into driving mode.'}),l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-sm text-slate-200",children:"2. Use the drive controls to move your rover:"}),l.jsxs("div",{className:"space-y-0.5",children:[l.jsx(Ia,{label:"Forward",keyLabel:Tt(n?.driveForward?.[0])}),l.jsx(Ia,{label:"Backward",keyLabel:Tt(n?.driveBackward?.[0])}),l.jsx(Ia,{label:"Turn Left",keyLabel:Tt(n?.driveLeft?.[0])}),l.jsx(Ia,{label:"Turn Right",keyLabel:Tt(n?.driveRight?.[0])}),l.jsx(Ia,{label:"Move faster",keyLabel:Tt(n?.boostModifier?.[0])}),l.jsx(Ia,{label:"Move slower",keyLabel:Tt(n?.slowModifier?.[0])})]})]}),l.jsx("p",{className:"text-sm text-slate-200",children:'3. When done, enter "Docking Assist" and line up the front sensor with the dock sensor.'})]})}function C2(){return l.jsxs("div",{className:"space-y-0.5 text-sm text-slate-200",children:[l.jsx("p",{children:'1. Press "Start Driving" put your rover into driving mode.'}),l.jsx("p",{children:"2. Touch and hold in Joystick area to move."}),l.jsx("p",{children:"3. Use the other column for motor, horn, and camera controls."}),l.jsx("p",{children:'4. When done, enter "Docking Assist" and line up the front sensor with the dock sensor.'})]})}function k2({visible:n,layout:a,showOnLoad:r,onToggleShowOnLoad:o,onOpenHelp:u,onClose:f}){const{state:h}=yt(),p=a==="desktop",g=oe(b=>(b.session?.socials||[]).find(E=>String(E?.id||E?.label||"").toLowerCase()==="discord")?.url||b.session?.discord?.invite||null),x=m.useMemo(()=>h?.keymap||{},[h?.keymap]);if(!n)return null;const v=b=>{const S=!b.target.checked;o?.(S)};return l.jsx("div",{className:"fixed inset-0 z-40 flex items-end justify-center bg-black/75 p-0.5 items-center",children:l.jsxs("div",{className:"pointer-events-auto surface w-full max-w-3xl overflow-hidden shadow-2xl",children:[l.jsx("div",{className:"flex items-center justify-between border-b border-slate-700 px-0.5 py-0.35 text-sm text-slate-200",children:l.jsx("span",{className:"font-semibold text-xl",children:"Welcome! To get started:"})}),l.jsxs("div",{className:`grid gap-0.5 p-0.5 ${p?"md:grid-cols-[minmax(0,1.5fr)_minmax(0,1fr)]":"grid-cols-1"}`,children:[l.jsx("section",{className:"space-y-0.5 border-b border-slate-700",children:p?l.jsx(A2,{keymap:x}):l.jsx(C2,{})}),l.jsxs("section",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-left",children:"Next..."}),l.jsxs("div",{className:"surface space-y-0.5 p-0.5 border-b border-slate-700",children:[l.jsx("p",{className:"text-xl font-semibold text-slate-200",children:"Set your nickname"}),l.jsx("p",{className:"text-sm font-semibold text-slate-200",children:"Nicknames are assigned randomly by default, you can change yours here."}),l.jsx(ll,{compact:!0})]}),l.jsxs("div",{className:"surface p-0.5",children:[l.jsx("p",{className:"text-xl font-semibold text-slate-200",children:"Join our Discord server!"}),l.jsx("p",{className:"text-sm font-semibold text-slate-200",children:"We have an active and welcoming community :3"}),l.jsx(Ao,{id:"discord",label:"Join Discord",url:g})]})]})]}),l.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-0.5 border-t border-slate-700 px-0.5 py-0.35 text-[0.8rem]",children:[l.jsxs("label",{className:"flex items-center gap-0.5 text-slate-300",children:[l.jsx("input",{type:"checkbox",checked:!r,onChange:v,className:"accent-cyan-500"}),l.jsx("span",{children:"Don't show again"})]}),l.jsx("div",{className:"flex items-center gap-0.5",children:l.jsx("button",{type:"button",onClick:f,className:"button-dark px-1 py-0.25 text-2xl bg-green-600 hover:bg-green-400 hover:border-green-100 border-green-400",children:"Got it! Let me in!"})})]})]})})}const E2=`aroma bagel batter beans diff --git a/server/public/index.html b/server/public/index.html index 0c9588f6..caba2cf7 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -11,7 +11,7 @@ Roomba Rover - + diff --git a/webui/src/components/OverseerPreferencePanel/index.jsx b/webui/src/components/OverseerPreferencePanel/index.jsx index e3049185..f33b711e 100644 --- a/webui/src/components/OverseerPreferencePanel/index.jsx +++ b/webui/src/components/OverseerPreferencePanel/index.jsx @@ -26,7 +26,7 @@ export default function OverseerPreferencePanel() { Enable Overseer LLM - Cast your to enable Overseer in chat. + Cast your vote to enable Overseer in chat.
Yes!: {Number(vote?.yesCount || 0)}, No...: {Number(vote?.noCount || 0)}