diff --git a/plans/to-do.md b/plans/to-do.md index 22190ef1..9f3242ce 100644 --- a/plans/to-do.md +++ b/plans/to-do.md @@ -1,16 +1,8 @@ -1. make sure audio forwarding doesnt stop when you switch tabs -2. add "reboot your own rover" feature - 1. time delay and you can only do it if youre the only one on. server locks the rover then tells it to reboot -3. add a clear socket disconnection indicator in the web UI - 1. fix reconnection stuff, make sure identity always sends. maybe even on a timer - 2. stop allowing dead sockets (no identity) on rovers... idk how to handle this - 1. disconnect dead sockets after 2 minutes, if identity is sent every minute no matter what, no one should ever get disconnected while functional -4. add votekicking - 1. simply just disconnect the users socket. -5. add faster way for admins to login, like an invisible button in top left or something -6. add discord bot typing thing for when someone requests a replay -7. change replay title for ones requested from discord, something other than "requester driving rover" -8. fix rover request spam queue cheat +1. FIX THE NICKNAME THING BEING BROKEN!!!! +1. add faster way for admins to login, like an invisible button in top left or something +2. add discord bot typing thing for when someone requests a replay +3. change replay title for ones requested from discord, something other than "requester driving rover" +4. fix rover request spam queue cheat # relative pipe dreams: diff --git a/server/public/assets/index-DbRTVNLN.js b/server/public/assets/index-DC5PqMFo.js similarity index 99% rename from server/public/assets/index-DbRTVNLN.js rename to server/public/assets/index-DC5PqMFo.js index 33b10173..f9163afe 100644 --- a/server/public/assets/index-DbRTVNLN.js +++ b/server/public/assets/index-DC5PqMFo.js @@ -13,7 +13,7 @@ ${C}`}).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:n,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: ",m.name||"--"]}),l.jsxs("span",{className:"surface-muted",children:["model: ",m.model||"--"]}),l.jsxs("span",{className:"surface-muted",children:["observeOnly: ",m.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: ",z.lastGenerationMs!=null?`${z.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:g.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:M()})]}),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:g.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(g.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:g.availableTools,blocked:g.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: ",g.modelInputAt?new Date(g.modelInputAt).toLocaleString():"n/a"]}),l.jsxs("div",{children:["Model output at: ",p.outputAt?new Date(p.outputAt).toLocaleString():"n/a"]})]}),x.message?l.jsxs("div",{className:"surface text-xs text-red-300",children:["Error: ",x.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(t.history||[],null,2)})]}),l.jsx("button",{type:"button",className:"button-dark text-xs",onClick:()=>h(y=>!y),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(t,null,2)}):null]})}function GY({health:t,roster:n=[]}){if(!t)return null;const r=t.replay||{sources:[],readyCount:0,totalCount:0},o=t.snapshots||{rovers:[],rooms:[]},h=z=>n.find(g=>String(g.id)===String(z))?.color||null,f=`${r.readyCount}/${r.totalCount} sources ready`,m=o.rovers.filter(z=>z.stale).length,p=o.rooms.filter(z=>z.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:m?"text-amber-300":"text-emerald-300",children:[o.rovers.length-m,"/",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(z=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{className:`${z.type==="rover"?"rounded px-1 py-[1px] border border-transparent":""}`,style:z.type==="rover"?a3(h(z.id),.16):void 0,children:z.label}),l.jsxs("span",{className:z.ready?"text-emerald-300":"text-amber-300",children:[z.recentCount,"/",z.neededCount]})]},`${z.type}:${z.id}`))}),l.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rovers.map(z=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{className:"rounded px-1 py-[1px] border border-transparent",style:a3(h(z.id),.16),children:z.name}),l.jsx("span",{className:z.stale?"text-amber-300":"text-emerald-300",children:z.stale?"stale":"ok"})]},`rover:${z.id}`))}),l.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rooms.map(z=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{children:z.name}),l.jsx("span",{className:z.stale?"text-amber-300":"text-emerald-300",children:z.error?"error":z.stale?"stale":"ok"})]},`room:${z.id}`))})]})}function $Y({entries:t}){const n=t||[];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:n.length})]}),l.jsx("div",{className:"surface h-64 overflow-y-auto font-mono text-xs",children:n.length===0?l.jsx("p",{children:"No admin log entries yet."}):n.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 YY=[{key:"open",label:"Open"},{key:"turns",label:"Turns"},{key:"admin",label:"Admin"},{key:"lockdown",label:"Lockdown"}];function IY(){const{session:t,lockRover:n,setMode:r,requestControl:o,setGlobalObjective:h,setAdminReason:f,rebootRover:m,rebootServer:p,setAudioLevels:z,setPrivateSafety:g,llmControl:x,overseerControl:M,adminLogs:y,llmCommentaryState:V,overseerControlState:L}=y8(),H=v.useMemo(()=>t?.roster??[],[t?.roster]),[C,w]=v.useState({}),[B,A]=v.useState({}),[j,k]=v.useState(!1),[G,T]=v.useState(!1),[F,O]=v.useState(!1),D=t?.health||null,$=t?.globalObjective?.text||"",P=t?.globalObjective?.updatedAt||null,[d1,h1]=v.useState($),r1=t?.adminReason?.text||"",E=t?.adminReason?.updatedAt||null,[U,X]=v.useState(r1),W=t?.audioLevels||{},[Q,N]=v.useState({hornGain:Number.isFinite(W.hornGain)?W.hornGain:1,ttsGain:Number.isFinite(W.ttsGain)?W.ttsGain:1,forwardGain:Number.isFinite(W.forwardGain)?W.forwardGain:1}),[_,c1]=v.useState({}),s1=t?.role==="admin"||t?.role==="lockdown"||t?.role==="lockdown-admin",z1=t?.role==="lockdown"||t?.role==="lockdown-admin",M1=t?.mode??"open",f1=async(n1,v1)=>{try{await n(n1,v1),w(N1=>({...N1,[n1]:v1}))}catch(N1){alert(N1.message)}},g1=async n1=>{const v1=n1.target.value;try{await r(v1)}catch(N1){alert(N1.message)}},t1=async n1=>{try{await o(n1,{force:!0})}catch(v1){alert(v1.message)}},p1=async n1=>{if(!(!n1?.id||!window.confirm(`Reboot rover "${n1.name||n1.id}" now?`))){A(N1=>({...N1,[n1.id]:!0}));try{await m(n1.id)}catch(N1){alert(N1.message)}finally{A(N1=>({...N1,[n1.id]:!1}))}}},x1=async()=>{if(window.confirm("Reboot the server host now? This will disconnect all users.")){k(!0);try{await p()}catch(v1){alert(v1.message),k(!1)}}},S1=async()=>{if(window.confirm("Clear LLM commentary history now? This resets chat context, bot memory, and rover activity metrics for narration.")){T(!0);try{await x("clearHistory")}catch(v1){alert(v1.message)}finally{T(!1)}}},X1=async()=>{if(window.confirm("Clear Overseer Control history now?")){O(!0);try{await M("clearHistory")}catch(v1){alert(v1.message)}finally{O(!1)}}},A1=async()=>{try{await h(d1)}catch(n1){alert(n1.message)}},H1=async()=>{try{await h(null)}catch(n1){alert(n1.message)}},G1=async()=>{try{await f(U)}catch(n1){alert(n1.message)}},Y1=async()=>{try{await f(null)}catch(n1){alert(n1.message)}},r2=n1=>v1=>{const N1=Number(v1.target.value);N($2=>({...$2||{},[n1]:Number.isFinite(N1)?N1:1}))},o1=async()=>{try{await z(Q)}catch(n1){alert(n1.message)}},i1=async()=>{window.dispatchEvent(new CustomEvent("buttonBox:rewardRunLocalTest",{detail:{rewardName:"Admin Mode",count:1e3,goal:1e3}}))};v.useEffect(()=>{h1($)},[$]),v.useEffect(()=>{X(r1)},[r1]),v.useEffect(()=>{N({hornGain:Number.isFinite(W.hornGain)?W.hornGain:1,ttsGain:Number.isFinite(W.ttsGain)?W.ttsGain:1,forwardGain:Number.isFinite(W.forwardGain)?W.forwardGain:1})},[W.forwardGain,W.hornGain,W.ttsGain]),v.useEffect(()=>{const n1={};(H||[]).forEach(v1=>{v1?.private?.enabled&&(n1[v1.id]={speedLimitEnabled:!!v1?.private?.safety?.speedLimitEnabled,speedLimitMaxWheelSpeed:Number.isFinite(v1?.private?.safety?.speedLimitMaxWheelSpeed)?v1.private.safety.speedLimitMaxWheelSpeed:250,hardOvercurrentEnabled:!!v1?.private?.safety?.hardOvercurrentEnabled,overcurrentStopMs:Number.isFinite(v1?.private?.safety?.overcurrentStopMs)?v1.private.safety.overcurrentStopMs:300,hardBumpEnabled:!!v1?.private?.safety?.hardBumpEnabled,bumpBackoffSpeed:Number.isFinite(v1?.private?.safety?.bumpBackoffSpeed)?v1.private.safety.bumpBackoffSpeed:250,bumpBackoffMs:Number.isFinite(v1?.private?.safety?.bumpBackoffMs)?v1.private.safety.bumpBackoffMs:350,cliffEnabled:!!v1?.private?.safety?.cliffEnabled,cliffBackoffSpeed:Number.isFinite(v1?.private?.safety?.cliffBackoffSpeed)?v1.private.safety.cliffBackoffSpeed:250,cliffBackoffMs:Number.isFinite(v1?.private?.safety?.cliffBackoffMs)?v1.private.safety.cliffBackoffMs:500,triggerCooldownMs:Number.isFinite(v1?.private?.safety?.triggerCooldownMs)?v1.private.safety.triggerCooldownMs:800})}),c1(n1)},[H]);const m1=v.useMemo(()=>{const n1={};return H.forEach(v1=>{n1[v1.id]=C[v1.id]??v1.locked}),n1},[H,C]),y1=(n1,v1={})=>{c1(N1=>({...N1||{},[n1]:{...N1?.[n1]||{},...v1||{}}}))},w1=async n1=>{try{const v1=_?.[n1];if(!v1)return;await g(n1,v1)}catch(v1){alert(v1.message)}};return s1?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:M1,onChange:g1,className:"field-input text-sm",children:YY.map(n1=>l.jsx("option",{value:n1.key,children:n1.label},n1.key))})]}),l.jsxs("div",{className:"flex gap-0.5 text-xs",children:[l.jsx("button",{type:"button",onClick:x1,disabled:j,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:j?"Server rebooting...":"Reboot Server"}),l.jsx("button",{type:"button",onClick:i1,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"}),t?.audioLevels?.updatedAt?l.jsxs("span",{children:["Updated ",new Date(t.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:[Q.hornGain.toFixed(2),"x"]})]}),l.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:Q.hornGain,onChange:r2("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:[Q.ttsGain.toFixed(2),"x"]})]}),l.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:Q.ttsGain,onChange:r2("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:[Q.forwardGain.toFixed(2),"x"]})]}),l.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:Q.forwardGain,onChange:r2("forwardGain"),className:"w-full accent-emerald-500"})]}),l.jsx("div",{className:"flex gap-0.5 text-xs",children:l.jsx("button",{type:"button",onClick:o1,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"}),P?l.jsxs("span",{children:["Updated ",new Date(P).toLocaleString()]}):null]}),l.jsx("input",{type:"text",value:d1,onChange:n1=>h1(n1.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:A1,className:"button-dark",children:"Set objective"}),l.jsx("button",{type:"button",onClick:H1,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"}),E?l.jsxs("span",{children:["Updated ",new Date(E).toLocaleString()]}):null]}),l.jsx("textarea",{value:U,onChange:n1=>X(n1.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:G1,className:"button-dark",children:"Set reason"}),l.jsx("button",{type:"button",onClick:Y1,className:"button-danger",children:"Clear"})]})]}),l.jsx(DY,{roster:H,renderActions:n1=>l.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 text-xs",children:[l.jsx("button",{type:"button",onClick:()=>f1(n1.id,!m1[n1.id]),className:"button-dark",children:m1[n1.id]?"Unlock":"Lock"}),l.jsx("button",{type:"button",onClick:()=>t1(n1.id),className:"button-dark",children:"Force"}),l.jsx("button",{type:"button",onClick:()=>p1(n1),disabled:!!B[n1.id],className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:B[n1.id]?"Rebooting...":"Reboot"}),n1?.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:!!_?.[n1.id]?.speedLimitEnabled,disabled:!z1,onChange:v1=>y1(n1.id,{speedLimitEnabled:!!v1.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:!z1,value:_?.[n1.id]?.speedLimitMaxWheelSpeed??250,onChange:v1=>y1(n1.id,{speedLimitMaxWheelSpeed:Number(v1.target.value)||250})}),l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!!_?.[n1.id]?.hardOvercurrentEnabled,disabled:!z1,onChange:v1=>y1(n1.id,{hardOvercurrentEnabled:!!v1.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:!!_?.[n1.id]?.hardBumpEnabled,disabled:!z1,onChange:v1=>y1(n1.id,{hardBumpEnabled:!!v1.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:!!_?.[n1.id]?.cliffEnabled,disabled:!z1,onChange:v1=>y1(n1.id,{cliffEnabled:!!v1.target.checked})}),l.jsx("span",{children:"Cliff safety"})]}),l.jsx("button",{type:"button",disabled:!z1,onClick:()=>w1(n1.id),className:"button-dark disabled:opacity-50",children:"Apply Safety"})]}):null]})}),l.jsx(GY,{health:D,roster:H}),l.jsx(UY,{state:V,onClearHistory:S1,clearingHistory:G}),l.jsx(ZY,{state:L,onClearHistory:X1,clearingHistory:F}),l.jsx($Y,{entries:y})]}):null}const PY=[{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 QY(t){return t.reduce((n,r)=>((n[r.group]||(n[r.group]=[])).push(r),n),{})}function XY(t,n){const r=Number(t);return Number.isFinite(r)?Math.max(0,Math.min(500,r)):n}const gc=5,g8=500,z8=1,zc=100;function KY(t,n){const r=Number(t);return Number.isFinite(r)?Math.max(gc,Math.min(g8,r)):n}function r7(t,n){const r=Number(t);return Number.isFinite(r)?Math.max(z8,Math.min(zc,r)):n}function JY(t){const r=(r7(t,t)-z8)/(zc-z8),o=g8-r*(g8-gc);return Math.round(o)}function WY(t){const n=KY(t,t),r=(g8-n)/(g8-gc),o=z8+r*(zc-z8);return Math.round(o)}function tI(t){const[n,r]=v.useState(null),o=v.useCallback(m=>{re(!0),r(m)},[]),h=v.useCallback(()=>{re(!1),r(null)},[]),f=v.useCallback((m,p)=>{if(p.preventDefault(),p.stopPropagation(),p.key==="Escape"){h();return}const z=C8(p.key??"");z&&(t(m,z,p),h())},[h,t]);return v.useEffect(()=>()=>re(!1),[]),{active:n,startCapture:o,cancel:h,captureFromEvent:f}}function I5({label:t,description:n,value:r,onChange:o,min:h=0,max:f=500,step:m=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:t}),l.jsx("input",{type:"number",min:h,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"})]}),n&&l.jsx("p",{className:"text-[0.65rem] text-slate-500",children:n}),l.jsxs("div",{className:"mt-0 flex items-center gap-0.5",children:[l.jsx("input",{type:"range",min:h,max:f,step:m,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 eI(){const{state:{keymap:t},actions:{updateKeyBinding:n,resetKeyBindings:r}}=y2(),{value:o,save:h}=T1("inputs",L4),f={...L4.keyboard,...o.keyboard??{}},m=v.useMemo(()=>typeof f.tiltSpeed=="number"?r7(f.tiltSpeed,L4.keyboard.tiltSpeed):typeof f.tiltIntervalMs=="number"?r7(WY(f.tiltIntervalMs),L4.keyboard.tiltSpeed):L4.keyboard.tiltSpeed,[f.tiltIntervalMs,f.tiltSpeed]),p=v.useMemo(()=>QY(PY),[]),{active:z,startCapture:g,cancel:x,captureFromEvent:M}=tI((H,C)=>{n(H,C)}),y=v.useCallback(H=>t?.[H]?.[0]||u8[H]?.[0]||"",[t]),V=v.useCallback((H,C)=>{const w=L4.keyboard,B=XY(C,w[H]);h(A=>({...A??{},keyboard:{...A?.keyboard??w,[H]:B}}))},[h]),L=v.useCallback(H=>{const C=L4.keyboard,w=r7(H,C.tiltSpeed),B=JY(w);h(A=>({...A??{},keyboard:{...A?.keyboard??C,tiltSpeed:w,tiltIntervalMs:B}}))},[h]);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(I5,{label:"Base speed",description:"Normal driving speed",value:f.baseSpeed,onChange:H=>V("baseSpeed",H)}),l.jsx(I5,{label:"Turbo speed",description:"Used when holding the boost modifier",value:f.turboSpeed,onChange:H=>V("turboSpeed",H)}),l.jsx(I5,{label:"Precision speed",description:"Used when holding the precision modifier",value:f.precisionSpeed,onChange:H=>V("precisionSpeed",H)})]})]}),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(I5,{label:"Tilt speed",description:"Higher = faster when holding tilt keys",value:m,onChange:L,min:1,max:100,step:1})]}),l.jsx("div",{className:"space-y-0.5",children:Object.entries(p).map(([H,C])=>l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.75rem] text-slate-400",children:H}),l.jsx("div",{className:"space-y-0.5",children:C.map(w=>{const B=y(w.id),A=z===w.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:w.label}),l.jsx("p",{className:"text-[0.65rem] text-slate-400",children:N2(B)})]}),l.jsxs("div",{className:"flex items-center gap-0.5",children:[A&&l.jsx("button",{type:"button",onClick:()=>x(),className:"button-danger text-[0.7rem]",children:"Cancel"}),l.jsx("button",{type:"button",onClick:()=>g(w.id),onKeyDown:A?j=>M(w.id,j):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"})]})]},w.id)})})]},H))})]})}const W4=new Intl.NumberFormat(void 0,{maximumFractionDigits:2}),cI=[{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"}],bi=.45,aI=.6;function r8({label:t,description:n,min:r,max:o,step:h,value:f,onChange:m}){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:t}),l.jsx("span",{className:"font-mono text-slate-400",children:W4.format(f)})]}),n&&l.jsx("p",{className:"text-[0.65rem] text-slate-500",children:n}),l.jsx("input",{type:"range",min:r,max:o,step:h,value:f,onChange:p=>m(Number(p.target.value)),className:"mt-0 w-full accent-emerald-400"})]})}function nI(t){if(!t)return"Unassigned";if(t.kind==="axisPair"){const n=`${t.invertX?"X inv ":""}${t.invertY?"Y inv":""}`.trim();return`Axes ${t.x}/${t.y}${n?` (${n})`:""}`}if(t.kind==="axis")return`Axis ${t.index}${t.invert?" (inv)":""}`;if(t.kind==="button")return`Button ${t.index}`;if(t.kind==="buttonAxis")return`Button ${t.index} (analog)`;if(t.kind==="axisButton"){const n=t.direction<0?"<":">";return`Axis ${t.index} ${n} ${W4.format(t.threshold??.6)}`}return"Unassigned"}function lI(t){return t.reduce((n,r)=>((n[r.section]||(n[r.section]=[])).push(r),n),{})}function rI(t,n){if(!t||t.length===0)return null;if(n){const r=t.find(o=>o.signature===n);if(r)return r}return t[0]}function iI(t){return{axes:Array.from(t.axes??[]),buttons:(t.buttons??[]).map(n=>({pressed:!!n?.pressed,value:typeof n?.value=="number"?n.value:n?.pressed?1:0}))}}function sI(t,n,r){const o=(t.axes??[]).map((f,m)=>({index:m,delta:Math.abs((f??0)-(n.axes?.[m]??0)),value:f}));if(o.sort((f,m)=>m.delta-f.delta),r.kind==="axisPair"){const f=o.filter(m=>m.delta>bi).slice(0,2);return f.length<2?null:{kind:"axisPair",x:f[0].index,y:f[1].index,...r.invertDefaults??{}}}const h=o.find(f=>f.delta>bi);return h?{kind:"axis",index:h.index,...r.invertDefaults??{}}:null}function oI(t,n,r){const o=t.buttons??[];for(let f=0;faI)return r.kind==="axis"?{kind:"buttonAxis",index:f}:{kind:"button",index:f}}const h=t.axes??[];for(let f=0;f.7&&p>.5)return r.kind==="axis"?{kind:"axis",index:f,...r.invertDefaults??{}}:{kind:"axisButton",index:f,direction:m>=0?1:-1,threshold:.6}}return null}function uI(t,n,r){if(!t)return null;if(r.kind==="axis"||r.kind==="axisPair"){const o=sI(t,n,r);if(o)return o}return oI(t,n,r)}function hI(){const t=A$(),{value:n,save:r}=T1("gamepad",L3),[o,h]=v.useState(null),f=v.useRef(null),m=v.useMemo(()=>lI(cI),[]),p=v.useMemo(()=>rI(t.pads,n.activeSignature),[t.pads,n.activeSignature]),z=p?.signature??null,g=v.useMemo(()=>z?n?.profiles?.[z]??n?.defaults?.profile??t3:n?.defaults?.profile??t3,[z,n?.defaults?.profile,n?.profiles]);v.useEffect(()=>{!p||!z||n?.profiles?.[z]||r(C=>{const w=C??L3;if(w.profiles?.[z])return w;const B=w?.defaults?.profile??t3,A=Os(p,B);return{...w,profiles:{...w.profiles??{},[z]:A}}})},[p,z,n?.profiles,r]),v.useEffect(()=>{f.current=null},[o,z]),v.useEffect(()=>{if(!o||!p)return;if(!f.current){f.current=iI(p);return}const C=uI(p,f.current,o);C&&(r(w=>{const B=w??L3,A=B.profiles?.[z]??B?.defaults?.profile??t3,j={...A,bindings:{...A.bindings??{},[o.id]:{...A.bindings?.[o.id]??{},kind:o.kind,sources:[C]}}};return{...B,profiles:{...B.profiles??{},[z]:j}}}),h(null))},[p,z,o,r]);const x=v.useCallback(C=>{r(w=>({...w??L3,activeSignature:C||null}))},[r]),M=v.useCallback(C=>{r(w=>{const B=w??L3,A=(z&&B.profiles?.[z])??B?.defaults?.profile??t3,j={...A,calibration:{...A.calibration??{},...C}};return z?{...B,profiles:{...B.profiles??{},[z]:j}}:{...B,defaults:{...B.defaults??{},profile:j}}})},[z,r]),y=v.useCallback((C,w)=>{r(B=>{const A=B??L3,j=(z&&A.profiles?.[z])??A?.defaults?.profile??t3,k=w(j.bindings?.[C]??{}),G={...j,bindings:{...j.bindings??{},[C]:k}};return z?{...A,profiles:{...A.profiles??{},[z]:G}}:{...A,defaults:{...A.defaults??{},profile:G}}})},[z,r]),V=v.useCallback(C=>{y(C.id,w=>({...w,sources:[]}))},[y]),L=v.useCallback((C,w)=>{y(C.id,B=>{const A=Array.isArray(B.sources)?[...B.sources]:[];if(!A[0])return B;const j={...A[0]};return w==="invertX"?j.invertX=!j.invertX:w==="invertY"?j.invertY=!j.invertY:j.invert=!j.invert,A[0]=j,{...B,sources:A}})},[y]),H=v.useMemo(()=>p?{outputs:_s(p,g)}:null,[p,g]);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"}),t.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:z??"",onChange:C=>x(C.target.value),className:"flex-1 rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:t.pads.map(C=>l.jsx("option",{value:C.signature,children:C.id||"Unknown controller"},C.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(r8,{label:"Drive deadzone",description:"Ignore small drive stick drift",min:0,max:.6,step:.01,value:g.calibration?.driveDeadzone??.18,onChange:C=>M({driveDeadzone:C})}),l.jsx(r8,{label:"Camera deadzone",description:"Ignore small camera tilt drift",min:0,max:.4,step:.01,value:g.calibration?.cameraDeadzone??.08,onChange:C=>M({cameraDeadzone:C})}),l.jsx(r8,{label:"Aux deadzone",description:"Ignore small trigger noise",min:0,max:.4,step:.01,value:g.calibration?.auxDeadzone??.05,onChange:C=>M({auxDeadzone:C})}),l.jsx(r8,{label:"Side brush scale",description:"Scale side brush output",min:.3,max:1,step:.05,value:g.calibration?.auxSideScale??.55,onChange:C=>M({auxSideScale:C})}),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:g.calibration?.cameraMode??"absolute",onChange:C=>M({cameraMode:C.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(r8,{label:"Camera sensitivity",description:"Velocity mode degrees per second",min:10,max:180,step:5,value:g.calibration?.cameraSensitivity??60,onChange:C=>M({cameraSensitivity:C})})]})]}),l.jsx("div",{className:"space-y-0.5",children:Object.entries(m).map(([C,w])=>l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:C}),l.jsx("div",{className:"space-y-0.5",children:w.map(B=>{const j=g.bindings?.[B.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:B.label}),l.jsx("p",{className:"text-[0.65rem] text-slate-400",children:nI(j)})]}),l.jsxs("div",{className:"flex items-center gap-0.5",children:[B.kind==="axisPair"&&l.jsxs(l.Fragment,{children:[l.jsx("button",{type:"button",disabled:!j,onClick:()=>L(B,"invertX"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert X"}),l.jsx("button",{type:"button",disabled:!j,onClick:()=>L(B,"invertY"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert Y"})]}),B.kind==="axis"&&l.jsx("button",{type:"button",disabled:!j,onClick:()=>L(B),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert"}),l.jsx("button",{type:"button",onClick:()=>V(B),className:"button-dark text-[0.7rem]",children:"Clear"}),l.jsx("button",{type:"button",onClick:()=>h(B),className:`${o?.id===B.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===B.id?"Waiting…":"Capture"})]})]},B.id)})})]},C))}),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((C,w)=>l.jsxs("span",{className:"font-mono text-slate-300",children:["A",w,": ",W4.format(C)]},`axis-${w}`))}),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((C,w)=>l.jsxs("span",{className:"font-mono text-slate-300",children:["B",w,": ",W4.format(C.value)," ",C.pressed?"●":""]},`btn-${w}`))}),H?.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: ",W4.format(H.outputs.driveVector.x),","," ",W4.format(H.outputs.driveVector.y)]}),l.jsxs("span",{className:"font-mono text-slate-300",children:["Camera: ",W4.format(H.outputs.cameraAxis)]}),l.jsxs("span",{className:"font-mono text-slate-300",children:["Main: ",W4.format(H.outputs.auxAxis.main)]}),l.jsxs("span",{className:"font-mono text-slate-300",children:["Side: ",W4.format(H.outputs.auxAxis.side)]})]})]})]}):l.jsx("p",{className:"text-[0.7rem] text-slate-400",children:"No controller detected."})]})]})}const dI={drive:"Drive wheels",aux:"Aux motors"};function yi(t){return Number.isFinite(t)?`${Math.round(t*100)}%`:"--"}function fI({value:t,color:n="bg-emerald-500"}){const r=`${Math.round(Math.max(0,Math.min(1,t))*100)}%`;return l.jsx("div",{className:"h-2 w-full overflow-hidden rounded bg-slate-800",children:l.jsx("div",{className:`h-full ${n}`,style:{width:r}})})}function vI(){const{state:{roverId:t},overcurrentLimiter:n}=y2(),r=v.useMemo(()=>t6.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:n?.adminImmune?"Admin immune":"Active"})]}),t?l.jsxs("div",{className:"space-y-0.5",children:[r.map(o=>{const h=n?.caps?.[o]?.cap??0,f=n?.overcurrent?.groups?.[o]??!1,m=n?.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:dI[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:yi(h)})]}),l.jsx(fI,{value:h,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:yi(m)})]})]})]},o)}),l.jsxs("div",{className:"surface text-[0.7rem] text-slate-400",children:[l.jsx("div",{children:`Down rate ${n?.config?.downRatePerSec}/s · Up rate ${n?.config?.upRatePerSec}/s`}),l.jsx("div",{children:`Release delay ${n?.config?.releaseDelaySec}s`}),l.jsx("div",{children:`Output rate ${n?.config?.outputRateMs}ms`})]})]}):l.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to view limiter status."})]})}const so=v.createContext(null);function oo(){const t=v.useContext(so);if(!t)throw new Error("Tabs components must be used within a provider.");return t}const Ci={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"}},wi={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"},uo="primary";function xc(...t){return t.filter(Boolean).join(" ")}function mI(t){const n=String(t||"").trim(),r=n.startsWith("#")?n.slice(1):n;if(!/^[0-9a-fA-F]{3}$|^[0-9a-fA-F]{6}$/.test(r))return null;const o=r.length===3?r.split("").map(h=>h+h).join(""):r;return{r:Number.parseInt(o.slice(0,2),16),g:Number.parseInt(o.slice(2,4),16),b:Number.parseInt(o.slice(4,6),16)}}function Hi(t,n){return`rgba(${t.r}, ${t.g}, ${t.b}, ${n})`}function Mc({children:t,defaultTab:n,currentTab:r,onTabChange:o,variant:h=uo}){const[f,m]=v.useState(n??null),[p,z]=v.useState([]),g=v.useCallback(V=>V?(z(L=>L.includes(V)?L:[...L,V]),()=>z(L=>L.includes(V)?L.filter(H=>H!==V):L)):()=>{},[]),x=r??f??p[0]??null;v.useEffect(()=>{n&&r===void 0&&m(n)},[n,r]);const M=v.useCallback(V=>{V&&(r===void 0&&m(V),o&&o(V))},[r,o]),y=v.useMemo(()=>({activeTab:x,setActiveTab:M,registerTab:g,variant:h}),[x,M,g,h]);return l.jsx(so.Provider,{value:y,children:t})}function bc({children:t,className:n=""}){const r=u1(m=>{const p=String(m.session?.assignment?.roverId||"").trim();return p&&(Array.isArray(m.session?.roster)?m.session.roster:[]).find(x=>String(x?.id)===p)?.color||null}),o=mI(r),h=o?{borderColor:Hi(o,.35)}:void 0,f=o?{backgroundImage:`linear-gradient(90deg, rgba(23,23,23,0.96) 0%, rgba(38,38,38,0.94) 58%, ${Hi(o,.18)} 100%)`}:void 0;return l.jsx("div",{className:xc("panel-section overflow-hidden border border-neutral-500/60 bg-neutral-900/95 p-0.5 shadow-[0_1px_0_rgba(255,255,255,0.05)_inset,0_10px_24px_rgba(0,0,0,0.28)]",n),style:h,children:l.jsx("div",{className:"flex gap-0.5",style:f,children:t})})}function W2({id:t,children:n,className:r="",disabled:o=!1,highlight:h="none"}){const{activeTab:f,setActiveTab:m,registerTab:p,variant:z}=oo();v.useEffect(()=>p(t),[t,p]);const g=Ci[z]??Ci[uo],x=f===t,M=wi[h]||wi.none,y=xc(g.base,M||(x?g.active:g.inactive),o?"cursor-not-allowed opacity-50":"",r);return l.jsx("button",{type:"button",className:y,onClick:()=>!o&&m(t),"aria-pressed":x,"aria-disabled":o,children:n})}function yc({children:t,className:n=""}){return l.jsx("div",{className:xc("mt-0 space-y-0.5",n),children:t})}function t4({id:t,children:n,keepMounted:r=!1}){const{activeTab:o}=oo(),h=o===t;return!h&&!r?null:!h&&r?l.jsx("div",{hidden:!0,"aria-hidden":"true",children:n}):l.jsx(l.Fragment,{children:n})}function pI(){const{session:t}=y8(),n=v.useMemo(()=>JSON.stringify(t??{},null,2),[t]);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:n})]})}const Vi=300,Si=240;function gI(t,n){if(t===void 0)return"undefined";if(t===null)return"null";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return String(t);if(typeof t=="function")return"[Function]";if(t instanceof Error)return`${t.name}: ${t.message}`;if(t instanceof Date)return t.toISOString();if(n||(n=new WeakSet),typeof t=="object"){if(n.has(t))return"[Circular]";n.add(t)}try{return JSON.stringify(t,(r,o)=>typeof o=="function"?"[Function]":o instanceof Error?`${o.name}: ${o.message}`:o,0)}catch{return String(t)}}function zI(t){const r=t.map(o=>gI(o)).join(" ");return r.length>Si?`${r.slice(0,Si)}…`:r}function xI(){const t=G2(),[n,r]=v.useState([]),o=v.useMemo(()=>n.reduce((h,f)=>(f.direction==="in"&&(h.in+=1),f.direction==="out"&&(h.out+=1),h),{in:0,out:0}),[n]);return v.useEffect(()=>{const h=(z,g,x)=>{r(M=>{const y=[...M,{id:`${Date.now()}-${Math.random().toString(16).slice(2)}`,direction:z,event:g,payload:zI(x),timestamp:new Date().toLocaleTimeString()}];return y.length>Vi?y.slice(-Vi):y})},f=(z,...g)=>h("in",z,g),m=(z,...g)=>h("out",z,g);t.onAny(f);let p=null;if(typeof t.onAnyOutgoing=="function")t.onAnyOutgoing(m),p=()=>t.offAnyOutgoing(m);else{const z=t.emit.bind(t);t.emit=(g,...x)=>(m(g,...x),z(g,...x)),p=()=>{t.emit=z}}return()=>{t.offAny(f),p&&p()}},[t]),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:n.length===0?l.jsx("p",{children:"No socket activity yet."}):n.slice().reverse().map(h=>l.jsxs("div",{className:"surface",children:[l.jsx("span",{className:"text-slate-400",children:h.timestamp})," ",l.jsxs("span",{className:h.direction==="in"?"text-emerald-400":"text-amber-400",children:["[",h.direction.toUpperCase(),"]"]})," ",l.jsx("span",{className:"text-cyan-300",children:h.event})," ",l.jsx("span",{className:"text-slate-200",children:h.payload})]},h.id))}),l.jsx("p",{className:"text-xs text-slate-500",children:"Logs are collected only while this panel is open."})]})}const MI=[{key:"start",label:"Start OI"},{key:"safe",label:"Safe"},{key:"full",label:"Full"},{key:"passive",label:"Passive"},{key:"dock",label:"Dock"}];function ho(){const{state:{roverId:t},actions:{sendOiCommand:n,setSensorStream:r}}=y2(),o=!!t,[h,f]=Ws(),m=G2(),{value:p,save:z}=T1("page",{hudMapDesktop:!1,connectionTransport:"websocket",swapMobileControlColumns:!1,driveMacroBackoffEnabled:!0}),{value:g,save:x}=T1("audio",s2),M=p?.connectionTransport||"websocket",y=!!p?.swapMobileControlColumns,V=typeof p?.driveMacroBackoffEnabled=="boolean"?p.driveMacroBackoffEnabled:!0,L=Number.isFinite(g?.masterVolume)?g.masterVolume:s2.masterVolume,H=Number.isFinite(g?.alertVolume)?g.alertVolume:s2.alertVolume,C=Number.isFinite(g?.roverVolume)?g.roverVolume:s2.roverVolume,w=typeof g?.mainBrushDuckEnabled=="boolean"?g.mainBrushDuckEnabled:typeof g?.autoLevelEnabled=="boolean"?g.autoLevelEnabled:s2.mainBrushDuckEnabled,B=Number.isFinite(g?.mainBrushDuckAmount)?Math.max(0,Math.min(1,g.mainBrushDuckAmount)):s2.mainBrushDuckAmount,A=v.useMemo(()=>[{key:"start",label:"Enable stream",enable:!0},{key:"stop",label:"Disable stream",enable:!1}],[]),j=D=>{t&&r(D)},k=D=>{const $=D.target.value;z(P=>({...P??{},connectionTransport:$})),m?.io?.opts&&(m.io.opts.transports=$==="polling"?["polling"]:["websocket","polling"],m.disconnect(),m.connect())},G=D=>$=>{const P=Number($.target.value),d1=Number.isFinite(P)?Math.max(0,Math.min(1,P)):0;x(h1=>({...h1??{},[D]:d1}))},T=D=>{const $=!!D.target.checked;x(P=>({...P??{},mainBrushDuckEnabled:$}))},F=D=>{const $=!!D.target.checked;z(P=>({...P??{},swapMobileControlColumns:$}))},O=D=>{const $=!!D.target.checked;z(P=>({...P??{},driveMacroBackoffEnabled:$}))};return l.jsxs(Mc,{defaultTab:"keybindings",children:[l.jsxs(bc,{children:[l.jsx(W2,{id:"keybindings",children:"Keybindings"}),l.jsx(W2,{id:"controller",children:"Controller"}),l.jsx(W2,{id:"page",children:"Page settings"}),l.jsx(W2,{id:"admin",children:"Admin"})]}),l.jsxs(yc,{children:[l.jsx(t4,{id:"keybindings",children:l.jsx("div",{className:"space-y-0.5",children:l.jsx(eI,{})})}),l.jsx(t4,{id:"controller",children:l.jsx("div",{className:"space-y-0.5",children:l.jsx(hI,{})})}),l.jsx(t4,{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:h,onChange:D=>f(D.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:y,onChange:F}),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:V,onChange:O}),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(L*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:L,onChange:G("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(H*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:H,onChange:G("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(C*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:C,onChange:G("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:w,onChange:T})]}),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(B*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:B,onChange:G("mainBrushDuckAmount"),className:"w-full accent-emerald-500",disabled:!w})]}),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:M,onChange:k,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(t4,{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:MI.map(D=>l.jsx("button",{type:"button",onClick:()=>n(D.key),disabled:!o,className:"button-dark text-xs disabled:opacity-30",children:D.label},D.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(D=>l.jsx("button",{type:"button",onClick:()=>j(D.enable),disabled:!o,className:"flex-1 button-dark text-xs disabled:opacity-30",children:D.label},D.key))}),!o&&l.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to toggle streams."})]}),l.jsx(io,{}),l.jsx(vI,{}),l.jsx(IY,{}),l.jsx(pI,{}),l.jsx(xI,{})]})})]})]})}const Ai={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 bI(t){return Ai[t]||Ai.desktop}function fo({actionId:t,keymap:n}){const r=n?.[t]?.[0]??"";return l.jsx("span",{className:"rounded border border-slate-600 bg-slate-900/40 px-1 text-[0.7rem] text-slate-200",children:N2(r)})}function Li(t,n){return t.map((r,o)=>typeof r=="string"?l.jsx("span",{children:r},`text-${o}`):r?.action?l.jsx(fo,{actionId:r.action,keymap:n},`pill-${r.action}-${o}`):r?.text?l.jsx("span",{children:r.text},`span-${o}`):null)}function L7(t,n,r){if(typeof t=="string")return t;if(Array.isArray(t?.segments))return Li(t.segments,n);if(t&&typeof t=="object"){if(typeof t.text=="string")return t.text;if(Array.isArray(t))return Li(t,n)}return String(t??"")}function yI({hero:t,keymap:n}){return t?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:t.title}),t.subtitle&&l.jsx("p",{className:"text-xs text-slate-300",children:t.subtitle})]}),t.chips&&l.jsx("div",{className:"flex flex-wrap gap-0.5 text-[0.7rem] text-slate-200",children:t.chips.map(r=>l.jsx("span",{className:"rounded border border-slate-700 px-1 py-[2px]",children:r},r))})]}),t.bullets&&l.jsx("ul",{className:"space-y-0.5 text-[0.85rem] text-slate-200",children:t.bullets.map((r,o)=>{const h=Array.isArray(r?.segments)?`hero-${o}`:`hero-${o}`;return l.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:L7(r,n)},h)})})]}):null}function CI({block:t,keymap:n}){return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-xs font-semibold text-slate-200",children:t.title}),l.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:t.items.map((r,o)=>{const h=`item-${o}`;return l.jsx("li",{className:"surface-muted flex flex-wrap items-center gap-0.5 px-0.5 py-0.25",children:L7(r,n)},h)})})]})}function wI({block:t}){const n=t.tone==="info"?"border-cyan-500/40":"border-slate-700";return l.jsxs("div",{className:`surface space-y-0.5 border ${n} px-0.5 py-0.5`,children:[l.jsx("p",{className:"text-xs font-semibold text-slate-100",children:t.title}),t.body&&l.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:t.body.map((r,o)=>l.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:L7(r,{})},`callout-${o}`))})]})}function HI({group:t,keymap:n}){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:t.title}),l.jsx("div",{className:"space-y-0.5 px-0.5 pb-0.25",children:t.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(fo,{actionId:r.action,keymap:n})]},r.action))})]})}function VI({block:t,keymap:n}){return t?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:t.title}),t.footnote&&l.jsx("span",{className:"text-[0.7rem] text-slate-400",children:t.footnote})]}),l.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-0.5",children:t.groups?.map(r=>l.jsx(HI,{group:r,keymap:n},r.id))})]}):null}function SI({block:t}){return t?l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-xs font-semibold text-slate-200",children:t.title}),l.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:t.items?.map((n,r)=>l.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:L7(n,{})},`gamepad-${r}`))})]}):null}function Bi({block:t,keymap:n}){if(!t)return null;switch(t.type){case"list":return l.jsx(CI,{block:t,keymap:n});case"callout":return l.jsx(wI,{block:t});case"keyboard":return l.jsx(VI,{block:t,keymap:n});case"gamepad":return l.jsx(SI,{block:t});default:return null}}function vo({layout:t,keymap:n}){const r=v.useMemo(()=>bI(t),[t]),o=n||{},h=r.main||[],f=r.aside||[];return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx(yI,{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:h.map((m,p)=>l.jsx(Bi,{block:m,keymap:o},m.title||p))}),l.jsx("div",{className:"space-y-0.5",children:f.map((m,p)=>l.jsx(Bi,{block:m,keymap:o},m.title||p))})]})]})}function mo({layout:t,onOpenOverlay:n}){const{state:r}=y2();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:n,className:"button-dark px-1 py-0.25 text-[0.75rem]",children:"Open full help"})]}),l.jsx(vo,{layout:t,keymap:r?.keymap||{}})]})}function B7({compact:t=!1}){const n=u1(M=>M.session?.role||null),{setNickname:r}=E2(),{value:o,save:h}=T1("profile",{nickname:""}),[f,m]=v.useState(o.nickname||""),[p,z]=v.useState(!1),g=n!=="spectator";v.useEffect(()=>{m(o.nickname||"")},[o.nickname]);async function x(M){if(M.preventDefault(),!g)return;const y=(f||"").trim().slice(0,32);if(y){z(!0);try{await r(y),h({nickname:y})}catch(V){alert(V.message)}finally{z(!1)}}}return l.jsxs("form",{className:"grid w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-stretch gap-0.5",onSubmit:x,children:[l.jsx("input",{className:"field-input flex-1 min-w-0",value:f,onChange:M=>m(M.target.value),maxLength:32,placeholder:"Enter a nickname",disabled:!g}),l.jsx("button",{type:"submit",disabled:!g||p,className:"button-dark h-full shrink-0 whitespace-nowrap px-0.5 py-0 disabled:opacity-50",children:p?"Saving…":t?"Set":"Save"})]})}const ji=["kal","rms","slt","ksp","bdl"],Ni=["sfg","iob","iog","iol","iom","tpc","tpd","tpf"],AI=Array.from({length:10},(t,n)=>n*10),Fi=[.5,.75,1,1.25,1.5,1.75,2],I6=1;function j7({hideInput:t=!1,hideSpectatorNotice:n=!1,fillHeight:r=!1,allowSpectatorInput:o=!1,title:h="Chat and TTS",nicknameLayout:f="inline"}){const m=u1(t1=>t1.session?.role||null),p=u1(t1=>t1.session?.assignment?.roverId||null),z=u1(t1=>t1.session?.roster??[]),{messages:g,typing:x,sendMessage:M,registerInputRef:y,onInputFocus:V,onInputBlur:L,blurChat:H,setTypingActive:C}=oc(),{value:w,save:B}=T1("tts",{engine:"flite",voice:"rms",pitch:50,googlePitch:I6,googleSpeed:I6}),[A,j]=v.useState(""),[k,G]=v.useState(!1),[T,F]=v.useState(!1),[O,D]=v.useState(()=>w?.engine||"flite"),[$,P]=v.useState(()=>w?.voice||"rms"),[d1,h1]=v.useState(()=>Number.isFinite(w?.pitch)?w.pitch:50),[r1,E]=v.useState(()=>Number.isFinite(w?.googlePitch)?w.googlePitch:I6),[U,X]=v.useState(()=>Number.isFinite(w?.googleSpeed)?w.googleSpeed:I6),W=m!=="spectator"||o,Q=v.useRef(null),_=!!v.useMemo(()=>z.find(t1=>String(t1.id)===String(p))||null,[p,z])?.audio?.ttsEnabled,c1=v.useMemo(()=>g.slice(-200),[g]),s1=v.useMemo(()=>x||[],[x]);v.useEffect(()=>{Q.current&&(Q.current.scrollTop=Q.current.scrollHeight)},[c1,s1]),v.useEffect(()=>{const t1=w?.engine||"flite",p1=w?.voice||"rms",x1=Number.isFinite(w?.pitch)?w.pitch:50,S1=Number.isFinite(w?.googlePitch)?w.googlePitch:I6,X1=Number.isFinite(w?.googleSpeed)?w.googleSpeed:I6;O!==t1&&D(t1),$!==p1&&P(p1),d1!==x1&&h1(x1),r1!==S1&&E(S1),U!==X1&&X(X1)},[O,r1,U,d1,w?.engine,w?.googlePitch,w?.googleSpeed,w?.pitch,w?.voice,$]),v.useEffect(()=>{F(!!_)},[_]);const z1=v.useMemo(()=>!_||!T?null:O==="espeak"?{speak:!0,engine:O,pitch:d1}:O==="chromegtts"?{speak:!0,engine:O,voice:$,pitch:r1,speed:U}:{speak:!0,engine:O,voice:$},[O,r1,U,d1,T,_,$]);async function M1(t1){if(t1.preventDefault(),!W||t)return;const x1=A.replace(/\\n/g,` +`):""};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:n,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: ",m.name||"--"]}),l.jsxs("span",{className:"surface-muted",children:["model: ",m.model||"--"]}),l.jsxs("span",{className:"surface-muted",children:["observeOnly: ",m.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: ",z.lastGenerationMs!=null?`${z.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:g.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:M()})]}),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:g.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(g.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:g.availableTools,blocked:g.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: ",g.modelInputAt?new Date(g.modelInputAt).toLocaleString():"n/a"]}),l.jsxs("div",{children:["Model output at: ",p.outputAt?new Date(p.outputAt).toLocaleString():"n/a"]})]}),x.message?l.jsxs("div",{className:"surface text-xs text-red-300",children:["Error: ",x.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(t.history||[],null,2)})]}),l.jsx("button",{type:"button",className:"button-dark text-xs",onClick:()=>h(y=>!y),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(t,null,2)}):null]})}function GY({health:t,roster:n=[]}){if(!t)return null;const r=t.replay||{sources:[],readyCount:0,totalCount:0},o=t.snapshots||{rovers:[],rooms:[]},h=z=>n.find(g=>String(g.id)===String(z))?.color||null,f=`${r.readyCount}/${r.totalCount} sources ready`,m=o.rovers.filter(z=>z.stale).length,p=o.rooms.filter(z=>z.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:m?"text-amber-300":"text-emerald-300",children:[o.rovers.length-m,"/",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(z=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{className:`${z.type==="rover"?"rounded px-1 py-[1px] border border-transparent":""}`,style:z.type==="rover"?a3(h(z.id),.16):void 0,children:z.label}),l.jsxs("span",{className:z.ready?"text-emerald-300":"text-amber-300",children:[z.recentCount,"/",z.neededCount]})]},`${z.type}:${z.id}`))}),l.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rovers.map(z=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{className:"rounded px-1 py-[1px] border border-transparent",style:a3(h(z.id),.16),children:z.name}),l.jsx("span",{className:z.stale?"text-amber-300":"text-emerald-300",children:z.stale?"stale":"ok"})]},`rover:${z.id}`))}),l.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rooms.map(z=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{children:z.name}),l.jsx("span",{className:z.stale?"text-amber-300":"text-emerald-300",children:z.error?"error":z.stale?"stale":"ok"})]},`room:${z.id}`))})]})}function $Y({entries:t}){const n=t||[];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:n.length})]}),l.jsx("div",{className:"surface h-64 overflow-y-auto font-mono text-xs",children:n.length===0?l.jsx("p",{children:"No admin log entries yet."}):n.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 YY=[{key:"open",label:"Open"},{key:"turns",label:"Turns"},{key:"admin",label:"Admin"},{key:"lockdown",label:"Lockdown"}];function IY(){const{session:t,lockRover:n,setMode:r,requestControl:o,setGlobalObjective:h,setAdminReason:f,rebootRover:m,rebootServer:p,setAudioLevels:z,setPrivateSafety:g,llmControl:x,overseerControl:M,adminLogs:y,llmCommentaryState:V,overseerControlState:L}=y8(),H=v.useMemo(()=>t?.roster??[],[t?.roster]),[C,w]=v.useState({}),[B,A]=v.useState({}),[j,k]=v.useState(!1),[G,T]=v.useState(!1),[F,O]=v.useState(!1),D=t?.health||null,$=t?.globalObjective?.text||"",P=t?.globalObjective?.updatedAt||null,[d1,h1]=v.useState($),r1=t?.adminReason?.text||"",E=t?.adminReason?.updatedAt||null,[U,X]=v.useState(r1),W=t?.audioLevels||{},[Q,N]=v.useState({hornGain:Number.isFinite(W.hornGain)?W.hornGain:1,ttsGain:Number.isFinite(W.ttsGain)?W.ttsGain:1,forwardGain:Number.isFinite(W.forwardGain)?W.forwardGain:1}),[_,c1]=v.useState({}),s1=t?.role==="admin"||t?.role==="lockdown"||t?.role==="lockdown-admin",z1=t?.role==="lockdown"||t?.role==="lockdown-admin",M1=t?.mode??"open",f1=async(n1,v1)=>{try{await n(n1,v1),w(N1=>({...N1,[n1]:v1}))}catch(N1){alert(N1.message)}},g1=async n1=>{const v1=n1.target.value;try{await r(v1)}catch(N1){alert(N1.message)}},t1=async n1=>{try{await o(n1,{force:!0})}catch(v1){alert(v1.message)}},p1=async n1=>{if(!(!n1?.id||!window.confirm(`Reboot rover "${n1.name||n1.id}" now?`))){A(N1=>({...N1,[n1.id]:!0}));try{await m(n1.id)}catch(N1){alert(N1.message)}finally{A(N1=>({...N1,[n1.id]:!1}))}}},x1=async()=>{if(window.confirm("Reboot the server host now? This will disconnect all users.")){k(!0);try{await p()}catch(v1){alert(v1.message),k(!1)}}},S1=async()=>{if(window.confirm("Clear LLM commentary history now? This resets chat context, bot memory, and rover activity metrics for narration.")){T(!0);try{await x("clearHistory")}catch(v1){alert(v1.message)}finally{T(!1)}}},X1=async()=>{if(window.confirm("Clear Overseer Control history now?")){O(!0);try{await M("clearHistory")}catch(v1){alert(v1.message)}finally{O(!1)}}},A1=async()=>{try{await h(d1)}catch(n1){alert(n1.message)}},H1=async()=>{try{await h(null)}catch(n1){alert(n1.message)}},G1=async()=>{try{await f(U)}catch(n1){alert(n1.message)}},Y1=async()=>{try{await f(null)}catch(n1){alert(n1.message)}},r2=n1=>v1=>{const N1=Number(v1.target.value);N($2=>({...$2||{},[n1]:Number.isFinite(N1)?N1:1}))},o1=async()=>{try{await z(Q)}catch(n1){alert(n1.message)}},i1=async()=>{window.dispatchEvent(new CustomEvent("buttonBox:rewardRunLocalTest",{detail:{rewardName:"Admin Mode",count:1e3,goal:1e3}}))};v.useEffect(()=>{h1($)},[$]),v.useEffect(()=>{X(r1)},[r1]),v.useEffect(()=>{N({hornGain:Number.isFinite(W.hornGain)?W.hornGain:1,ttsGain:Number.isFinite(W.ttsGain)?W.ttsGain:1,forwardGain:Number.isFinite(W.forwardGain)?W.forwardGain:1})},[W.forwardGain,W.hornGain,W.ttsGain]),v.useEffect(()=>{const n1={};(H||[]).forEach(v1=>{v1?.private?.enabled&&(n1[v1.id]={speedLimitEnabled:!!v1?.private?.safety?.speedLimitEnabled,speedLimitMaxWheelSpeed:Number.isFinite(v1?.private?.safety?.speedLimitMaxWheelSpeed)?v1.private.safety.speedLimitMaxWheelSpeed:250,hardOvercurrentEnabled:!!v1?.private?.safety?.hardOvercurrentEnabled,overcurrentStopMs:Number.isFinite(v1?.private?.safety?.overcurrentStopMs)?v1.private.safety.overcurrentStopMs:300,hardBumpEnabled:!!v1?.private?.safety?.hardBumpEnabled,bumpBackoffSpeed:Number.isFinite(v1?.private?.safety?.bumpBackoffSpeed)?v1.private.safety.bumpBackoffSpeed:250,bumpBackoffMs:Number.isFinite(v1?.private?.safety?.bumpBackoffMs)?v1.private.safety.bumpBackoffMs:350,cliffEnabled:!!v1?.private?.safety?.cliffEnabled,cliffBackoffSpeed:Number.isFinite(v1?.private?.safety?.cliffBackoffSpeed)?v1.private.safety.cliffBackoffSpeed:250,cliffBackoffMs:Number.isFinite(v1?.private?.safety?.cliffBackoffMs)?v1.private.safety.cliffBackoffMs:500,triggerCooldownMs:Number.isFinite(v1?.private?.safety?.triggerCooldownMs)?v1.private.safety.triggerCooldownMs:800})}),c1(n1)},[H]);const m1=v.useMemo(()=>{const n1={};return H.forEach(v1=>{n1[v1.id]=C[v1.id]??v1.locked}),n1},[H,C]),y1=(n1,v1={})=>{c1(N1=>({...N1||{},[n1]:{...N1?.[n1]||{},...v1||{}}}))},w1=async n1=>{try{const v1=_?.[n1];if(!v1)return;await g(n1,v1)}catch(v1){alert(v1.message)}};return s1?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:M1,onChange:g1,className:"field-input text-sm",children:YY.map(n1=>l.jsx("option",{value:n1.key,children:n1.label},n1.key))})]}),l.jsxs("div",{className:"flex gap-0.5 text-xs",children:[l.jsx("button",{type:"button",onClick:x1,disabled:j,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:j?"Server rebooting...":"Reboot Server"}),l.jsx("button",{type:"button",onClick:i1,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"}),t?.audioLevels?.updatedAt?l.jsxs("span",{children:["Updated ",new Date(t.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:[Q.hornGain.toFixed(2),"x"]})]}),l.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:Q.hornGain,onChange:r2("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:[Q.ttsGain.toFixed(2),"x"]})]}),l.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:Q.ttsGain,onChange:r2("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:[Q.forwardGain.toFixed(2),"x"]})]}),l.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:Q.forwardGain,onChange:r2("forwardGain"),className:"w-full accent-emerald-500"})]}),l.jsx("div",{className:"flex gap-0.5 text-xs",children:l.jsx("button",{type:"button",onClick:o1,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"}),P?l.jsxs("span",{children:["Updated ",new Date(P).toLocaleString()]}):null]}),l.jsx("input",{type:"text",value:d1,onChange:n1=>h1(n1.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:A1,className:"button-dark",children:"Set objective"}),l.jsx("button",{type:"button",onClick:H1,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"}),E?l.jsxs("span",{children:["Updated ",new Date(E).toLocaleString()]}):null]}),l.jsx("textarea",{value:U,onChange:n1=>X(n1.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:G1,className:"button-dark",children:"Set reason"}),l.jsx("button",{type:"button",onClick:Y1,className:"button-danger",children:"Clear"})]})]}),l.jsx(DY,{roster:H,renderActions:n1=>l.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 text-xs",children:[l.jsx("button",{type:"button",onClick:()=>f1(n1.id,!m1[n1.id]),className:"button-dark",children:m1[n1.id]?"Unlock":"Lock"}),l.jsx("button",{type:"button",onClick:()=>t1(n1.id),className:"button-dark",children:"Force"}),l.jsx("button",{type:"button",onClick:()=>p1(n1),disabled:!!B[n1.id],className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:B[n1.id]?"Rebooting...":"Reboot"}),n1?.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:!!_?.[n1.id]?.speedLimitEnabled,disabled:!z1,onChange:v1=>y1(n1.id,{speedLimitEnabled:!!v1.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:!z1,value:_?.[n1.id]?.speedLimitMaxWheelSpeed??250,onChange:v1=>y1(n1.id,{speedLimitMaxWheelSpeed:Number(v1.target.value)||250})}),l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!!_?.[n1.id]?.hardOvercurrentEnabled,disabled:!z1,onChange:v1=>y1(n1.id,{hardOvercurrentEnabled:!!v1.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:!!_?.[n1.id]?.hardBumpEnabled,disabled:!z1,onChange:v1=>y1(n1.id,{hardBumpEnabled:!!v1.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:!!_?.[n1.id]?.cliffEnabled,disabled:!z1,onChange:v1=>y1(n1.id,{cliffEnabled:!!v1.target.checked})}),l.jsx("span",{children:"Cliff safety"})]}),l.jsx("button",{type:"button",disabled:!z1,onClick:()=>w1(n1.id),className:"button-dark disabled:opacity-50",children:"Apply Safety"})]}):null]})}),l.jsx(GY,{health:D,roster:H}),l.jsx(UY,{state:V,onClearHistory:S1,clearingHistory:G}),l.jsx(ZY,{state:L,onClearHistory:X1,clearingHistory:F}),l.jsx($Y,{entries:y})]}):null}const PY=[{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 QY(t){return t.reduce((n,r)=>((n[r.group]||(n[r.group]=[])).push(r),n),{})}function XY(t,n){const r=Number(t);return Number.isFinite(r)?Math.max(0,Math.min(500,r)):n}const gc=5,g8=500,z8=1,zc=100;function KY(t,n){const r=Number(t);return Number.isFinite(r)?Math.max(gc,Math.min(g8,r)):n}function r7(t,n){const r=Number(t);return Number.isFinite(r)?Math.max(z8,Math.min(zc,r)):n}function JY(t){const r=(r7(t,t)-z8)/(zc-z8),o=g8-r*(g8-gc);return Math.round(o)}function WY(t){const n=KY(t,t),r=(g8-n)/(g8-gc),o=z8+r*(zc-z8);return Math.round(o)}function tI(t){const[n,r]=v.useState(null),o=v.useCallback(m=>{re(!0),r(m)},[]),h=v.useCallback(()=>{re(!1),r(null)},[]),f=v.useCallback((m,p)=>{if(p.preventDefault(),p.stopPropagation(),p.key==="Escape"){h();return}const z=C8(p.key??"");z&&(t(m,z,p),h())},[h,t]);return v.useEffect(()=>()=>re(!1),[]),{active:n,startCapture:o,cancel:h,captureFromEvent:f}}function I5({label:t,description:n,value:r,onChange:o,min:h=0,max:f=500,step:m=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:t}),l.jsx("input",{type:"number",min:h,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"})]}),n&&l.jsx("p",{className:"text-[0.65rem] text-slate-500",children:n}),l.jsxs("div",{className:"mt-0 flex items-center gap-0.5",children:[l.jsx("input",{type:"range",min:h,max:f,step:m,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 eI(){const{state:{keymap:t},actions:{updateKeyBinding:n,resetKeyBindings:r}}=y2(),{value:o,save:h}=T1("inputs",L4),f={...L4.keyboard,...o.keyboard??{}},m=v.useMemo(()=>typeof f.tiltSpeed=="number"?r7(f.tiltSpeed,L4.keyboard.tiltSpeed):typeof f.tiltIntervalMs=="number"?r7(WY(f.tiltIntervalMs),L4.keyboard.tiltSpeed):L4.keyboard.tiltSpeed,[f.tiltIntervalMs,f.tiltSpeed]),p=v.useMemo(()=>QY(PY),[]),{active:z,startCapture:g,cancel:x,captureFromEvent:M}=tI((H,C)=>{n(H,C)}),y=v.useCallback(H=>t?.[H]?.[0]||u8[H]?.[0]||"",[t]),V=v.useCallback((H,C)=>{const w=L4.keyboard,B=XY(C,w[H]);h(A=>({...A??{},keyboard:{...A?.keyboard??w,[H]:B}}))},[h]),L=v.useCallback(H=>{const C=L4.keyboard,w=r7(H,C.tiltSpeed),B=JY(w);h(A=>({...A??{},keyboard:{...A?.keyboard??C,tiltSpeed:w,tiltIntervalMs:B}}))},[h]);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(I5,{label:"Base speed",description:"Normal driving speed",value:f.baseSpeed,onChange:H=>V("baseSpeed",H)}),l.jsx(I5,{label:"Turbo speed",description:"Used when holding the boost modifier",value:f.turboSpeed,onChange:H=>V("turboSpeed",H)}),l.jsx(I5,{label:"Precision speed",description:"Used when holding the precision modifier",value:f.precisionSpeed,onChange:H=>V("precisionSpeed",H)})]})]}),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(I5,{label:"Tilt speed",description:"Higher = faster when holding tilt keys",value:m,onChange:L,min:1,max:100,step:1})]}),l.jsx("div",{className:"space-y-0.5",children:Object.entries(p).map(([H,C])=>l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.75rem] text-slate-400",children:H}),l.jsx("div",{className:"space-y-0.5",children:C.map(w=>{const B=y(w.id),A=z===w.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:w.label}),l.jsx("p",{className:"text-[0.65rem] text-slate-400",children:N2(B)})]}),l.jsxs("div",{className:"flex items-center gap-0.5",children:[A&&l.jsx("button",{type:"button",onClick:()=>x(),className:"button-danger text-[0.7rem]",children:"Cancel"}),l.jsx("button",{type:"button",onClick:()=>g(w.id),onKeyDown:A?j=>M(w.id,j):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"})]})]},w.id)})})]},H))})]})}const W4=new Intl.NumberFormat(void 0,{maximumFractionDigits:2}),cI=[{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"}],bi=.45,aI=.6;function r8({label:t,description:n,min:r,max:o,step:h,value:f,onChange:m}){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:t}),l.jsx("span",{className:"font-mono text-slate-400",children:W4.format(f)})]}),n&&l.jsx("p",{className:"text-[0.65rem] text-slate-500",children:n}),l.jsx("input",{type:"range",min:r,max:o,step:h,value:f,onChange:p=>m(Number(p.target.value)),className:"mt-0 w-full accent-emerald-400"})]})}function nI(t){if(!t)return"Unassigned";if(t.kind==="axisPair"){const n=`${t.invertX?"X inv ":""}${t.invertY?"Y inv":""}`.trim();return`Axes ${t.x}/${t.y}${n?` (${n})`:""}`}if(t.kind==="axis")return`Axis ${t.index}${t.invert?" (inv)":""}`;if(t.kind==="button")return`Button ${t.index}`;if(t.kind==="buttonAxis")return`Button ${t.index} (analog)`;if(t.kind==="axisButton"){const n=t.direction<0?"<":">";return`Axis ${t.index} ${n} ${W4.format(t.threshold??.6)}`}return"Unassigned"}function lI(t){return t.reduce((n,r)=>((n[r.section]||(n[r.section]=[])).push(r),n),{})}function rI(t,n){if(!t||t.length===0)return null;if(n){const r=t.find(o=>o.signature===n);if(r)return r}return t[0]}function iI(t){return{axes:Array.from(t.axes??[]),buttons:(t.buttons??[]).map(n=>({pressed:!!n?.pressed,value:typeof n?.value=="number"?n.value:n?.pressed?1:0}))}}function sI(t,n,r){const o=(t.axes??[]).map((f,m)=>({index:m,delta:Math.abs((f??0)-(n.axes?.[m]??0)),value:f}));if(o.sort((f,m)=>m.delta-f.delta),r.kind==="axisPair"){const f=o.filter(m=>m.delta>bi).slice(0,2);return f.length<2?null:{kind:"axisPair",x:f[0].index,y:f[1].index,...r.invertDefaults??{}}}const h=o.find(f=>f.delta>bi);return h?{kind:"axis",index:h.index,...r.invertDefaults??{}}:null}function oI(t,n,r){const o=t.buttons??[];for(let f=0;faI)return r.kind==="axis"?{kind:"buttonAxis",index:f}:{kind:"button",index:f}}const h=t.axes??[];for(let f=0;f.7&&p>.5)return r.kind==="axis"?{kind:"axis",index:f,...r.invertDefaults??{}}:{kind:"axisButton",index:f,direction:m>=0?1:-1,threshold:.6}}return null}function uI(t,n,r){if(!t)return null;if(r.kind==="axis"||r.kind==="axisPair"){const o=sI(t,n,r);if(o)return o}return oI(t,n,r)}function hI(){const t=A$(),{value:n,save:r}=T1("gamepad",L3),[o,h]=v.useState(null),f=v.useRef(null),m=v.useMemo(()=>lI(cI),[]),p=v.useMemo(()=>rI(t.pads,n.activeSignature),[t.pads,n.activeSignature]),z=p?.signature??null,g=v.useMemo(()=>z?n?.profiles?.[z]??n?.defaults?.profile??t3:n?.defaults?.profile??t3,[z,n?.defaults?.profile,n?.profiles]);v.useEffect(()=>{!p||!z||n?.profiles?.[z]||r(C=>{const w=C??L3;if(w.profiles?.[z])return w;const B=w?.defaults?.profile??t3,A=Os(p,B);return{...w,profiles:{...w.profiles??{},[z]:A}}})},[p,z,n?.profiles,r]),v.useEffect(()=>{f.current=null},[o,z]),v.useEffect(()=>{if(!o||!p)return;if(!f.current){f.current=iI(p);return}const C=uI(p,f.current,o);C&&(r(w=>{const B=w??L3,A=B.profiles?.[z]??B?.defaults?.profile??t3,j={...A,bindings:{...A.bindings??{},[o.id]:{...A.bindings?.[o.id]??{},kind:o.kind,sources:[C]}}};return{...B,profiles:{...B.profiles??{},[z]:j}}}),h(null))},[p,z,o,r]);const x=v.useCallback(C=>{r(w=>({...w??L3,activeSignature:C||null}))},[r]),M=v.useCallback(C=>{r(w=>{const B=w??L3,A=(z&&B.profiles?.[z])??B?.defaults?.profile??t3,j={...A,calibration:{...A.calibration??{},...C}};return z?{...B,profiles:{...B.profiles??{},[z]:j}}:{...B,defaults:{...B.defaults??{},profile:j}}})},[z,r]),y=v.useCallback((C,w)=>{r(B=>{const A=B??L3,j=(z&&A.profiles?.[z])??A?.defaults?.profile??t3,k=w(j.bindings?.[C]??{}),G={...j,bindings:{...j.bindings??{},[C]:k}};return z?{...A,profiles:{...A.profiles??{},[z]:G}}:{...A,defaults:{...A.defaults??{},profile:G}}})},[z,r]),V=v.useCallback(C=>{y(C.id,w=>({...w,sources:[]}))},[y]),L=v.useCallback((C,w)=>{y(C.id,B=>{const A=Array.isArray(B.sources)?[...B.sources]:[];if(!A[0])return B;const j={...A[0]};return w==="invertX"?j.invertX=!j.invertX:w==="invertY"?j.invertY=!j.invertY:j.invert=!j.invert,A[0]=j,{...B,sources:A}})},[y]),H=v.useMemo(()=>p?{outputs:_s(p,g)}:null,[p,g]);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"}),t.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:z??"",onChange:C=>x(C.target.value),className:"flex-1 rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:t.pads.map(C=>l.jsx("option",{value:C.signature,children:C.id||"Unknown controller"},C.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(r8,{label:"Drive deadzone",description:"Ignore small drive stick drift",min:0,max:.6,step:.01,value:g.calibration?.driveDeadzone??.18,onChange:C=>M({driveDeadzone:C})}),l.jsx(r8,{label:"Camera deadzone",description:"Ignore small camera tilt drift",min:0,max:.4,step:.01,value:g.calibration?.cameraDeadzone??.08,onChange:C=>M({cameraDeadzone:C})}),l.jsx(r8,{label:"Aux deadzone",description:"Ignore small trigger noise",min:0,max:.4,step:.01,value:g.calibration?.auxDeadzone??.05,onChange:C=>M({auxDeadzone:C})}),l.jsx(r8,{label:"Side brush scale",description:"Scale side brush output",min:.3,max:1,step:.05,value:g.calibration?.auxSideScale??.55,onChange:C=>M({auxSideScale:C})}),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:g.calibration?.cameraMode??"absolute",onChange:C=>M({cameraMode:C.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(r8,{label:"Camera sensitivity",description:"Velocity mode degrees per second",min:10,max:180,step:5,value:g.calibration?.cameraSensitivity??60,onChange:C=>M({cameraSensitivity:C})})]})]}),l.jsx("div",{className:"space-y-0.5",children:Object.entries(m).map(([C,w])=>l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:C}),l.jsx("div",{className:"space-y-0.5",children:w.map(B=>{const j=g.bindings?.[B.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:B.label}),l.jsx("p",{className:"text-[0.65rem] text-slate-400",children:nI(j)})]}),l.jsxs("div",{className:"flex items-center gap-0.5",children:[B.kind==="axisPair"&&l.jsxs(l.Fragment,{children:[l.jsx("button",{type:"button",disabled:!j,onClick:()=>L(B,"invertX"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert X"}),l.jsx("button",{type:"button",disabled:!j,onClick:()=>L(B,"invertY"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert Y"})]}),B.kind==="axis"&&l.jsx("button",{type:"button",disabled:!j,onClick:()=>L(B),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert"}),l.jsx("button",{type:"button",onClick:()=>V(B),className:"button-dark text-[0.7rem]",children:"Clear"}),l.jsx("button",{type:"button",onClick:()=>h(B),className:`${o?.id===B.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===B.id?"Waiting…":"Capture"})]})]},B.id)})})]},C))}),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((C,w)=>l.jsxs("span",{className:"font-mono text-slate-300",children:["A",w,": ",W4.format(C)]},`axis-${w}`))}),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((C,w)=>l.jsxs("span",{className:"font-mono text-slate-300",children:["B",w,": ",W4.format(C.value)," ",C.pressed?"●":""]},`btn-${w}`))}),H?.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: ",W4.format(H.outputs.driveVector.x),","," ",W4.format(H.outputs.driveVector.y)]}),l.jsxs("span",{className:"font-mono text-slate-300",children:["Camera: ",W4.format(H.outputs.cameraAxis)]}),l.jsxs("span",{className:"font-mono text-slate-300",children:["Main: ",W4.format(H.outputs.auxAxis.main)]}),l.jsxs("span",{className:"font-mono text-slate-300",children:["Side: ",W4.format(H.outputs.auxAxis.side)]})]})]})]}):l.jsx("p",{className:"text-[0.7rem] text-slate-400",children:"No controller detected."})]})]})}const dI={drive:"Drive wheels",aux:"Aux motors"};function yi(t){return Number.isFinite(t)?`${Math.round(t*100)}%`:"--"}function fI({value:t,color:n="bg-emerald-500"}){const r=`${Math.round(Math.max(0,Math.min(1,t))*100)}%`;return l.jsx("div",{className:"h-2 w-full overflow-hidden rounded bg-slate-800",children:l.jsx("div",{className:`h-full ${n}`,style:{width:r}})})}function vI(){const{state:{roverId:t},overcurrentLimiter:n}=y2(),r=v.useMemo(()=>t6.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:n?.adminImmune?"Admin immune":"Active"})]}),t?l.jsxs("div",{className:"space-y-0.5",children:[r.map(o=>{const h=n?.caps?.[o]?.cap??0,f=n?.overcurrent?.groups?.[o]??!1,m=n?.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:dI[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:yi(h)})]}),l.jsx(fI,{value:h,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:yi(m)})]})]})]},o)}),l.jsxs("div",{className:"surface text-[0.7rem] text-slate-400",children:[l.jsx("div",{children:`Down rate ${n?.config?.downRatePerSec}/s · Up rate ${n?.config?.upRatePerSec}/s`}),l.jsx("div",{children:`Release delay ${n?.config?.releaseDelaySec}s`}),l.jsx("div",{children:`Output rate ${n?.config?.outputRateMs}ms`})]})]}):l.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to view limiter status."})]})}const so=v.createContext(null);function oo(){const t=v.useContext(so);if(!t)throw new Error("Tabs components must be used within a provider.");return t}const Ci={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"}},wi={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"},uo="primary";function xc(...t){return t.filter(Boolean).join(" ")}function mI(t){const n=String(t||"").trim(),r=n.startsWith("#")?n.slice(1):n;if(!/^[0-9a-fA-F]{3}$|^[0-9a-fA-F]{6}$/.test(r))return null;const o=r.length===3?r.split("").map(h=>h+h).join(""):r;return{r:Number.parseInt(o.slice(0,2),16),g:Number.parseInt(o.slice(2,4),16),b:Number.parseInt(o.slice(4,6),16)}}function Hi(t,n){return`rgba(${t.r}, ${t.g}, ${t.b}, ${n})`}function Mc({children:t,defaultTab:n,currentTab:r,onTabChange:o,variant:h=uo}){const[f,m]=v.useState(n??null),[p,z]=v.useState([]),g=v.useCallback(V=>V?(z(L=>L.includes(V)?L:[...L,V]),()=>z(L=>L.includes(V)?L.filter(H=>H!==V):L)):()=>{},[]),x=r??f??p[0]??null;v.useEffect(()=>{n&&r===void 0&&m(n)},[n,r]);const M=v.useCallback(V=>{V&&(r===void 0&&m(V),o&&o(V))},[r,o]),y=v.useMemo(()=>({activeTab:x,setActiveTab:M,registerTab:g,variant:h}),[x,M,g,h]);return l.jsx(so.Provider,{value:y,children:t})}function bc({children:t,className:n=""}){const r=u1(m=>{const p=String(m.session?.assignment?.roverId||"").trim();return p&&(Array.isArray(m.session?.roster)?m.session.roster:[]).find(x=>String(x?.id)===p)?.color||null}),o=mI(r),h=o?{borderColor:Hi(o,.35)}:void 0,f=o?{backgroundImage:`linear-gradient(90deg, rgba(23,23,23,0.96) 0%, rgba(38,38,38,0.94) 58%, ${Hi(o,.18)} 100%)`}:void 0;return l.jsx("div",{className:xc("panel-section overflow-hidden border border-neutral-500/60 bg-neutral-900/95 p-0.5 shadow-[0_1px_0_rgba(255,255,255,0.05)_inset,0_10px_24px_rgba(0,0,0,0.28)]",n),style:h,children:l.jsx("div",{className:"flex gap-0.5",style:f,children:t})})}function W2({id:t,children:n,className:r="",disabled:o=!1,highlight:h="none"}){const{activeTab:f,setActiveTab:m,registerTab:p,variant:z}=oo();v.useEffect(()=>p(t),[t,p]);const g=Ci[z]??Ci[uo],x=f===t,M=wi[h]||wi.none,y=xc(g.base,M||(x?g.active:g.inactive),o?"cursor-not-allowed opacity-50":"",r);return l.jsx("button",{type:"button",className:y,onClick:()=>!o&&m(t),"aria-pressed":x,"aria-disabled":o,children:n})}function yc({children:t,className:n=""}){return l.jsx("div",{className:xc("mt-0 space-y-0.5",n),children:t})}function t4({id:t,children:n,keepMounted:r=!1}){const{activeTab:o}=oo(),h=o===t;return!h&&!r?null:!h&&r?l.jsx("div",{hidden:!0,"aria-hidden":"true",children:n}):l.jsx(l.Fragment,{children:n})}function pI(){const{session:t}=y8(),n=v.useMemo(()=>JSON.stringify(t??{},null,2),[t]);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:n})]})}const Vi=300,Si=240;function gI(t,n){if(t===void 0)return"undefined";if(t===null)return"null";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return String(t);if(typeof t=="function")return"[Function]";if(t instanceof Error)return`${t.name}: ${t.message}`;if(t instanceof Date)return t.toISOString();if(n||(n=new WeakSet),typeof t=="object"){if(n.has(t))return"[Circular]";n.add(t)}try{return JSON.stringify(t,(r,o)=>typeof o=="function"?"[Function]":o instanceof Error?`${o.name}: ${o.message}`:o,0)}catch{return String(t)}}function zI(t){const r=t.map(o=>gI(o)).join(" ");return r.length>Si?`${r.slice(0,Si)}…`:r}function xI(){const t=G2(),[n,r]=v.useState([]),o=v.useMemo(()=>n.reduce((h,f)=>(f.direction==="in"&&(h.in+=1),f.direction==="out"&&(h.out+=1),h),{in:0,out:0}),[n]);return v.useEffect(()=>{const h=(z,g,x)=>{r(M=>{const y=[...M,{id:`${Date.now()}-${Math.random().toString(16).slice(2)}`,direction:z,event:g,payload:zI(x),timestamp:new Date().toLocaleTimeString()}];return y.length>Vi?y.slice(-Vi):y})},f=(z,...g)=>h("in",z,g),m=(z,...g)=>h("out",z,g);t.onAny(f);let p=null;if(typeof t.onAnyOutgoing=="function")t.onAnyOutgoing(m),p=()=>t.offAnyOutgoing(m);else{const z=t.emit.bind(t);t.emit=(g,...x)=>(m(g,...x),z(g,...x)),p=()=>{t.emit=z}}return()=>{t.offAny(f),p&&p()}},[t]),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:n.length===0?l.jsx("p",{children:"No socket activity yet."}):n.slice().reverse().map(h=>l.jsxs("div",{className:"surface",children:[l.jsx("span",{className:"text-slate-400",children:h.timestamp})," ",l.jsxs("span",{className:h.direction==="in"?"text-emerald-400":"text-amber-400",children:["[",h.direction.toUpperCase(),"]"]})," ",l.jsx("span",{className:"text-cyan-300",children:h.event})," ",l.jsx("span",{className:"text-slate-200",children:h.payload})]},h.id))}),l.jsx("p",{className:"text-xs text-slate-500",children:"Logs are collected only while this panel is open."})]})}const MI=[{key:"start",label:"Start OI"},{key:"safe",label:"Safe"},{key:"full",label:"Full"},{key:"passive",label:"Passive"},{key:"dock",label:"Dock"}];function ho(){const{state:{roverId:t},actions:{sendOiCommand:n,setSensorStream:r}}=y2(),o=!!t,[h,f]=Ws(),m=G2(),{value:p,save:z}=T1("page",{hudMapDesktop:!1,connectionTransport:"websocket",swapMobileControlColumns:!1,driveMacroBackoffEnabled:!0}),{value:g,save:x}=T1("audio",s2),M=p?.connectionTransport||"websocket",y=!!p?.swapMobileControlColumns,V=typeof p?.driveMacroBackoffEnabled=="boolean"?p.driveMacroBackoffEnabled:!0,L=Number.isFinite(g?.masterVolume)?g.masterVolume:s2.masterVolume,H=Number.isFinite(g?.alertVolume)?g.alertVolume:s2.alertVolume,C=Number.isFinite(g?.roverVolume)?g.roverVolume:s2.roverVolume,w=typeof g?.mainBrushDuckEnabled=="boolean"?g.mainBrushDuckEnabled:typeof g?.autoLevelEnabled=="boolean"?g.autoLevelEnabled:s2.mainBrushDuckEnabled,B=Number.isFinite(g?.mainBrushDuckAmount)?Math.max(0,Math.min(1,g.mainBrushDuckAmount)):s2.mainBrushDuckAmount,A=v.useMemo(()=>[{key:"start",label:"Enable stream",enable:!0},{key:"stop",label:"Disable stream",enable:!1}],[]),j=D=>{t&&r(D)},k=D=>{const $=D.target.value;z(P=>({...P??{},connectionTransport:$})),m?.io?.opts&&(m.io.opts.transports=$==="polling"?["polling"]:["websocket","polling"],m.disconnect(),m.connect())},G=D=>$=>{const P=Number($.target.value),d1=Number.isFinite(P)?Math.max(0,Math.min(1,P)):0;x(h1=>({...h1??{},[D]:d1}))},T=D=>{const $=!!D.target.checked;x(P=>({...P??{},mainBrushDuckEnabled:$}))},F=D=>{const $=!!D.target.checked;z(P=>({...P??{},swapMobileControlColumns:$}))},O=D=>{const $=!!D.target.checked;z(P=>({...P??{},driveMacroBackoffEnabled:$}))};return l.jsxs(Mc,{defaultTab:"keybindings",children:[l.jsxs(bc,{children:[l.jsx(W2,{id:"keybindings",children:"Keybindings"}),l.jsx(W2,{id:"controller",children:"Controller"}),l.jsx(W2,{id:"page",children:"Page settings"}),l.jsx(W2,{id:"admin",children:"Admin"})]}),l.jsxs(yc,{children:[l.jsx(t4,{id:"keybindings",children:l.jsx("div",{className:"space-y-0.5",children:l.jsx(eI,{})})}),l.jsx(t4,{id:"controller",children:l.jsx("div",{className:"space-y-0.5",children:l.jsx(hI,{})})}),l.jsx(t4,{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:h,onChange:D=>f(D.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:y,onChange:F}),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:V,onChange:O}),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(L*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:L,onChange:G("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(H*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:H,onChange:G("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(C*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:C,onChange:G("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:w,onChange:T})]}),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(B*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:B,onChange:G("mainBrushDuckAmount"),className:"w-full accent-emerald-500",disabled:!w})]}),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:M,onChange:k,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(t4,{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:MI.map(D=>l.jsx("button",{type:"button",onClick:()=>n(D.key),disabled:!o,className:"button-dark text-xs disabled:opacity-30",children:D.label},D.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(D=>l.jsx("button",{type:"button",onClick:()=>j(D.enable),disabled:!o,className:"flex-1 button-dark text-xs disabled:opacity-30",children:D.label},D.key))}),!o&&l.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to toggle streams."})]}),l.jsx(io,{}),l.jsx(vI,{}),l.jsx(IY,{}),l.jsx(pI,{}),l.jsx(xI,{})]})})]})]})}const Ai={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 bI(t){return Ai[t]||Ai.desktop}function fo({actionId:t,keymap:n}){const r=n?.[t]?.[0]??"";return l.jsx("span",{className:"rounded border border-slate-600 bg-slate-900/40 px-1 text-[0.7rem] text-slate-200",children:N2(r)})}function Li(t,n){return t.map((r,o)=>typeof r=="string"?l.jsx("span",{children:r},`text-${o}`):r?.action?l.jsx(fo,{actionId:r.action,keymap:n},`pill-${r.action}-${o}`):r?.text?l.jsx("span",{children:r.text},`span-${o}`):null)}function L7(t,n,r){if(typeof t=="string")return t;if(Array.isArray(t?.segments))return Li(t.segments,n);if(t&&typeof t=="object"){if(typeof t.text=="string")return t.text;if(Array.isArray(t))return Li(t,n)}return String(t??"")}function yI({hero:t,keymap:n}){return t?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:t.title}),t.subtitle&&l.jsx("p",{className:"text-xs text-slate-300",children:t.subtitle})]}),t.chips&&l.jsx("div",{className:"flex flex-wrap gap-0.5 text-[0.7rem] text-slate-200",children:t.chips.map(r=>l.jsx("span",{className:"rounded border border-slate-700 px-1 py-[2px]",children:r},r))})]}),t.bullets&&l.jsx("ul",{className:"space-y-0.5 text-[0.85rem] text-slate-200",children:t.bullets.map((r,o)=>{const h=Array.isArray(r?.segments)?`hero-${o}`:`hero-${o}`;return l.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:L7(r,n)},h)})})]}):null}function CI({block:t,keymap:n}){return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-xs font-semibold text-slate-200",children:t.title}),l.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:t.items.map((r,o)=>{const h=`item-${o}`;return l.jsx("li",{className:"surface-muted flex flex-wrap items-center gap-0.5 px-0.5 py-0.25",children:L7(r,n)},h)})})]})}function wI({block:t}){const n=t.tone==="info"?"border-cyan-500/40":"border-slate-700";return l.jsxs("div",{className:`surface space-y-0.5 border ${n} px-0.5 py-0.5`,children:[l.jsx("p",{className:"text-xs font-semibold text-slate-100",children:t.title}),t.body&&l.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:t.body.map((r,o)=>l.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:L7(r,{})},`callout-${o}`))})]})}function HI({group:t,keymap:n}){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:t.title}),l.jsx("div",{className:"space-y-0.5 px-0.5 pb-0.25",children:t.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(fo,{actionId:r.action,keymap:n})]},r.action))})]})}function VI({block:t,keymap:n}){return t?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:t.title}),t.footnote&&l.jsx("span",{className:"text-[0.7rem] text-slate-400",children:t.footnote})]}),l.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-0.5",children:t.groups?.map(r=>l.jsx(HI,{group:r,keymap:n},r.id))})]}):null}function SI({block:t}){return t?l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-xs font-semibold text-slate-200",children:t.title}),l.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:t.items?.map((n,r)=>l.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:L7(n,{})},`gamepad-${r}`))})]}):null}function Bi({block:t,keymap:n}){if(!t)return null;switch(t.type){case"list":return l.jsx(CI,{block:t,keymap:n});case"callout":return l.jsx(wI,{block:t});case"keyboard":return l.jsx(VI,{block:t,keymap:n});case"gamepad":return l.jsx(SI,{block:t});default:return null}}function vo({layout:t,keymap:n}){const r=v.useMemo(()=>bI(t),[t]),o=n||{},h=r.main||[],f=r.aside||[];return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx(yI,{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:h.map((m,p)=>l.jsx(Bi,{block:m,keymap:o},m.title||p))}),l.jsx("div",{className:"space-y-0.5",children:f.map((m,p)=>l.jsx(Bi,{block:m,keymap:o},m.title||p))})]})]})}function mo({layout:t,onOpenOverlay:n}){const{state:r}=y2();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:n,className:"button-dark px-1 py-0.25 text-[0.75rem]",children:"Open full help"})]}),l.jsx(vo,{layout:t,keymap:r?.keymap||{}})]})}function B7({compact:t=!1}){const n=u1(M=>M.session?.role||null),{setNickname:r}=E2(),{value:o,save:h}=T1("profile",{nickname:""}),[f,m]=v.useState(o.nickname||""),[p,z]=v.useState(!1),g=n!=="spectator";v.useEffect(()=>{m(o.nickname||"")},[o.nickname]);async function x(M){if(M.preventDefault(),!g)return;const y=(f||"").trim().slice(0,32);if(y){z(!0);try{await r(y),h({nickname:y})}catch(V){alert(V.message)}finally{z(!1)}}}return l.jsxs("div",{className:"grid w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-stretch gap-0.5",children:[l.jsx("input",{className:"field-input flex-1 min-w-0",value:f,onChange:M=>m(M.target.value),onKeyDown:M=>{M.key==="Enter"&&(M.preventDefault(),x(M))},maxLength:32,placeholder:"Enter a nickname",disabled:!g}),l.jsx("button",{type:"button",onClick:x,disabled:!g||p,className:"button-dark h-full shrink-0 whitespace-nowrap px-0.5 py-0 disabled:opacity-50",children:p?"Saving…":t?"Set":"Save"})]})}const ji=["kal","rms","slt","ksp","bdl"],Ni=["sfg","iob","iog","iol","iom","tpc","tpd","tpf"],AI=Array.from({length:10},(t,n)=>n*10),Fi=[.5,.75,1,1.25,1.5,1.75,2],I6=1;function j7({hideInput:t=!1,hideSpectatorNotice:n=!1,fillHeight:r=!1,allowSpectatorInput:o=!1,title:h="Chat and TTS",nicknameLayout:f="inline"}){const m=u1(t1=>t1.session?.role||null),p=u1(t1=>t1.session?.assignment?.roverId||null),z=u1(t1=>t1.session?.roster??[]),{messages:g,typing:x,sendMessage:M,registerInputRef:y,onInputFocus:V,onInputBlur:L,blurChat:H,setTypingActive:C}=oc(),{value:w,save:B}=T1("tts",{engine:"flite",voice:"rms",pitch:50,googlePitch:I6,googleSpeed:I6}),[A,j]=v.useState(""),[k,G]=v.useState(!1),[T,F]=v.useState(!1),[O,D]=v.useState(()=>w?.engine||"flite"),[$,P]=v.useState(()=>w?.voice||"rms"),[d1,h1]=v.useState(()=>Number.isFinite(w?.pitch)?w.pitch:50),[r1,E]=v.useState(()=>Number.isFinite(w?.googlePitch)?w.googlePitch:I6),[U,X]=v.useState(()=>Number.isFinite(w?.googleSpeed)?w.googleSpeed:I6),W=m!=="spectator"||o,Q=v.useRef(null),_=!!v.useMemo(()=>z.find(t1=>String(t1.id)===String(p))||null,[p,z])?.audio?.ttsEnabled,c1=v.useMemo(()=>g.slice(-200),[g]),s1=v.useMemo(()=>x||[],[x]);v.useEffect(()=>{Q.current&&(Q.current.scrollTop=Q.current.scrollHeight)},[c1,s1]),v.useEffect(()=>{const t1=w?.engine||"flite",p1=w?.voice||"rms",x1=Number.isFinite(w?.pitch)?w.pitch:50,S1=Number.isFinite(w?.googlePitch)?w.googlePitch:I6,X1=Number.isFinite(w?.googleSpeed)?w.googleSpeed:I6;O!==t1&&D(t1),$!==p1&&P(p1),d1!==x1&&h1(x1),r1!==S1&&E(S1),U!==X1&&X(X1)},[O,r1,U,d1,w?.engine,w?.googlePitch,w?.googleSpeed,w?.pitch,w?.voice,$]),v.useEffect(()=>{F(!!_)},[_]);const z1=v.useMemo(()=>!_||!T?null:O==="espeak"?{speak:!0,engine:O,pitch:d1}:O==="chromegtts"?{speak:!0,engine:O,voice:$,pitch:r1,speed:U}:{speak:!0,engine:O,voice:$},[O,r1,U,d1,T,_,$]);async function M1(t1){if(t1.preventDefault(),!W||t)return;const x1=A.replace(/\\n/g,` `).trim();if(x1){G(!0);try{await M(x1,z1),j(""),H(),C(!1)}catch(S1){alert(S1.message)}finally{G(!1)}}}const f1=r?"flex-1 min-h-0 overflow-y-auto":"h-48 overflow-y-auto",g1=f==="stacked";return l.jsxs(u2,{title:h,hideHeader:!h,fillHeight:r,bodyClassName:"space-y-0.5 text-base",children:[l.jsxs("div",{className:`surface overflow-y-auto space-y-0.5 px-0 ${f1}`,ref:Q,children:[c1.length===0&&s1.length===0?l.jsx("p",{className:"text-sm text-slate-500",children:"No messages yet."}):c1.map(t1=>l.jsx(rc,{message:t1},t1.id)),s1.map(t1=>l.jsx(Ls,{message:t1},`typing-${t1.typingId||t1.id}`))]}),!t&&l.jsxs("form",{className:`flex items-stretch gap-0.5 ${g1?"flex-col":"flex-wrap"}`,onSubmit:M1,children:[l.jsx("div",{className:g1?"w-full":"w-[9rem] sm:w-[10rem] shrink-0",children:l.jsx(B7,{compact:!0})}),l.jsx("input",{className:`field-input ${g1?"w-full min-w-0":"flex-1 min-w-[10rem]"}`,value:A,onChange:t1=>{const p1=t1.target.value;j(p1),C(!!p1.trim())},onFocus:t1=>{V(t1),C(!!A.trim())},onBlur:t1=>{L(t1),C(!1)},onKeyDown:t1=>{t1.key==="Enter"&&!A.trim()&&(t1.preventDefault(),H(),C(!1))},ref:t1=>y(t1,{target:"panel"}),placeholder:W?"Type a message…":n?"":"Spectators cannot chat",disabled:!W}),_&&l.jsxs("div",{className:`flex flex-wrap items-center gap-0.5 ${g1?"w-full":"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:T,onChange:t1=>F(t1.target.checked),className:"accent-cyan-500"}),l.jsx("span",{children:"Speak"})]}),l.jsxs("select",{value:O,onChange:t1=>{const p1=t1.target.value,x1=p1==="chromegtts"&&!Ni.includes($)?"tpf":p1==="flite"&&!ji.includes($)?"rms":$;D(p1),x1!==$&&P(x1),B(S1=>({...S1||{},engine:p1,voice:x1}))},className:"field-input text-xs",children:[l.jsx("option",{value:"flite",children:"flite"}),l.jsx("option",{value:"espeak",children:"espeak"}),l.jsx("option",{value:"chromegtts",children:"Google TTS"})]}),O==="flite"||O==="chromegtts"?l.jsxs(l.Fragment,{children:[l.jsx("select",{value:$,onChange:t1=>{const p1=t1.target.value;P(p1),B(x1=>({...x1||{},voice:p1}))},className:"field-input text-xs",children:(O==="chromegtts"?Ni:ji).map(t1=>l.jsx("option",{value:t1,children:t1},t1))}),O==="chromegtts"&&l.jsxs(l.Fragment,{children:[l.jsx("select",{value:r1,onChange:t1=>{const p1=Number(t1.target.value);E(p1),B(x1=>({...x1||{},googlePitch:p1}))},className:"field-input text-xs",children:Fi.map(t1=>l.jsxs("option",{value:t1,children:["pitch ",t1.toFixed(2)]},`pitch-${t1}`))}),l.jsx("select",{value:U,onChange:t1=>{const p1=Number(t1.target.value);X(p1),B(x1=>({...x1||{},googleSpeed:p1}))},className:"field-input text-xs",children:Fi.map(t1=>l.jsxs("option",{value:t1,children:["speed ",t1.toFixed(2)]},`speed-${t1}`))})]})]}):l.jsx("select",{value:d1,onChange:t1=>{const p1=Number(t1.target.value);h1(p1),B(x1=>({...x1||{},pitch:p1}))},className:"field-input text-xs",children:AI.map(t1=>l.jsxs("option",{value:t1,children:["pitch ",t1]},t1))})]}),l.jsx("button",{type:"submit",disabled:!W||k,className:`button-dark disabled:opacity-50 ${g1?"w-full h-8":"h-full self-stretch"}`,children:k?"...":"Send"})]})]})}function Cc({className:t=""}){const n=u1(r=>r.session?.socials??[]).slice(0,4);return n.length?l.jsx("div",{className:`grid grid-cols-2 grid-rows-2 gap-0.5 ${t}`,children:n.map(r=>l.jsx(H7,{id:r?.id||r?.key||r?.service||null},`${r?.id||r?.label||"social"}-${r?.url||""}`))}):null}function LI(){return l.jsx(u2,{title:"Links!",fillHeight:!0,bodyClassName:"flex flex-1 min-h-0 flex-col gap-0.5 text-base",children:l.jsx(Cc,{className:"flex-1 min-h-0"})})}function Me(...t){return t.filter(Boolean).join(" ")}function BI(t){const n=t?.batteryState?.percentDisplay;return n==null?"--":`${n}%`}function jI(t){return t?.batteryState?t.batteryState.urgentActive?"text-red-400":t.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function NI(t){switch(t){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function FI(t,n){if(!t)return"";const r=t.nickname||t.socketId?.slice(0,6)||"unknown";return t.socketId&&t.socketId===n?`${r} (you)`:r}function N7({title:t="Rovers"}){const n=u1(F=>F.session?.role||null),r=u1(F=>F.session?.roster??[]),o=u1(F=>F.session?.turnQueues??{}),h=u1(F=>F.session?.users??[]),f=u1(F=>F.session?.socketId||null),m=u1(F=>String(F.session?.assignment?.roverId||"").trim()),p=u1(F=>{const O=String(F.session?.assignment?.roverId||"").trim();return O?(F.session?.roster||[]).find($=>String($?.id)===O)?.name||O:""}),{requestControl:z,rebootOwnRover:g}=E2(),[x,M]=v.useState({}),[y,V]=v.useState(!1),[L,H]=v.useState(()=>Date.now()),C=v.useMemo(()=>n&&n!=="spectator",[n]),w=v.useMemo(()=>n==="admin"||n==="lockdown"||n==="lockdown-admin",[n]),B=v.useMemo(()=>Object.values(o||{}).some(F=>F?.deadline||F?.idleDeadline),[o]);v.useEffect(()=>{if(!B)return;const F=setInterval(()=>{H(Date.now())},1e3);return()=>clearInterval(F)},[B]);const A=v.useMemo(()=>{const F=new Set(r.map(D=>String(D.id))),O=Object.keys(o||{}).filter(D=>!F.has(String(D))).map(D=>({id:D,name:D,locked:!1,batteryState:null}));return[...r,...O]},[r,o]);async function j(F){if(F){M(O=>({...O,[F]:!0}));try{await z(F)}catch(O){alert(O.message)}finally{M(O=>({...O,[F]:!1}))}}}const k=F=>h.find(O=>O.socketId===F)||{socketId:F,nickname:null,role:null};async function G(){if(!(y||!window.confirm(`Reboot your rover "${p}" now?`))){V(!0);try{await g(),alert("Reboot command sent.")}catch(O){alert(O.message)}finally{V(!1)}}}const T=n!=="spectator"&&m?l.jsx("button",{type:"button",onClick:G,disabled:y,className:"button-dark bg-amber-700/80 text-amber-50 hover:bg-amber-600 disabled:opacity-40",title:"Reboot your assigned rover",children:y?"Rebooting...":"Reboot My Rover"}):null;return l.jsx(u2,{title:t,actions:T,bodyClassName:"space-y-0.5 text-sm",children:A.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:A.map(F=>{const O=String(F.id),D=o?.[O]||null,$=D?.queue||[],P=D?.idleDeadline||D?.deadline||null,d1=P&&P>L?Math.ceil((P-L)/1e3):P?0:null,h1=D?.current||null,r1=h1?$.findIndex(M1=>M1===h1):-1,E=$.length>1?r1>=0?$[(r1+1)%$.length]:$[0]:null,U=!!(f&&h1&&h1===f),X=!!(f&&E&&E===f),W=d1!=null&&(U||X),Q=!!F.locked,N=Q&&!w,_=!!(F?.private?.enabled&&F?.private?.open),c1=F.lockReason?`locked: ${F.lockReason}`:"locked",s1=Q?c1:x[O]?"...":"request",z1=C&&!N&&!x[O];return l.jsxs("li",{className:Me("surface flex flex-wrap items-start justify-between gap-0.5",z1&&"cursor-pointer",Q?"bg-red-900/40":_?"bg-amber-700/35 border border-amber-200/30":null),onClick:()=>{z1&&j(F.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:a3(F.color,.16),children:F.name}),F.description?l.jsx("span",{className:"min-w-0 flex-1 truncate text-[0.7rem] text-slate-400",children:F.description}):null]}),W?l.jsx("span",{className:"rounded bg-slate-800 px-1 text-[0.7rem] text-slate-200",children:U?`${d1}s left`:`Your turn in ${d1}s`}):null]}),l.jsx("span",{className:Me("text-[0.75rem] font-semibold",jI(F)),children:BI(F)})]}),$.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:$.map((M1,f1)=>{const g1=k(M1),t1=M1===h1,p1=!!(E&&M1===E&&!t1),x1=t1?"bg-sky-600 text-white ring-2 ring-amber-300":p1?"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] ${x1}`,children:[l.jsx("span",{className:`${NI(g1.role)} font-semibold`,children:FI(g1,f)}),t1&&l.jsx("span",{className:"text-[0.65rem] text-slate-200",children:"now"}),p1&&l.jsx("span",{className:"text-[0.65rem] text-emerald-100",children:"next"})]},`${O}-${M1}-${f1}`)})})]}),C?l.jsx("button",{type:"button",onClick:M1=>{M1.stopPropagation(),j(F.id)},disabled:x[O]||N,className:Me("button-dark disabled:opacity-40",Q&&"bg-red-600/70 text-white hover:bg-red-600"),children:s1}):null]},F.id)})})})}function kI(t){switch(t){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function RI(t,n){if(!t)return"";const r=t.nickname||t.socketId?.slice(0,6)||"unknown";return t.socketId&&t.socketId===n?`${r} (you)`:r}function z7({hideNicknameForm:t=!1,hideHeader:n=!1,className:r="",fillHeight:o=!1,compact:h=!1}){const f=u1(M=>M.session?.role||null),m=u1(M=>M.session?.users??[]),p=u1(M=>M.session?.socketId||null),z=f!=="spectator",g=v.useMemo(()=>[...m].sort((M,y)=>M.socketId===p?-1:y.socketId===p?1:(M.nickname||"").localeCompare(y.nickname||"")),[p,m]),x=o?"flex-1 min-h-0 overflow-y-auto":h?"h-28 overflow-y-auto":"h-48 overflow-y-auto";return l.jsxs(u2,{title:n?"":"Users",hideHeader:n,fillHeight:o,className:r,bodyClassName:"space-y-0.5 text-base",children:[!t&&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(B7,{compact:h})})}),l.jsx(Cc,{})]}),!z&&l.jsx("p",{className:"text-xs text-slate-500",children:"Spectators cannot set nicknames."})]}),l.jsx("div",{className:`space-y-0.5 ${o?"flex flex-1 min-h-0 flex-col":""}`,children:l.jsx("div",{className:`surface flex flex-wrap content-start items-start gap-0.5 px-0 pb-0 ${x} ${h?"text-[0.8rem]":""}`,children:g.length===0?l.jsx("p",{className:"text-sm text-slate-500",children:"Waiting for users…"}):g.map(M=>l.jsx("span",{className:`rounded bg-slate-800/80 px-1 py-0.25 text-[0.7rem] ${kI(M.role)}`,children:RI(M,p)},M.socketId))})})]})}const EI=/^cu_[a-f0-9]{32}$/,c0="field-input w-full max-w-sm text-left focus:ring-emerald-500",w8="mx-auto w-full max-w-xl flex justify-center",K6="mx-auto flex w-full max-w-md flex-col items-center space-y-0.5 text-center";function TI(t){const n=String(t||"").trim();return n?n.length<=10?`${n.slice(0,2)}***${n.slice(-2)}`:`${n.slice(0,6)}...${n.slice(-6)}`:""}const ki=8*1024*1024,W3=16e3,qI={iceServers:[{urls:"stun:stun.l.google.com:19302"}],bundlePolicy:"max-bundle",rtcpMuxPolicy:"require"};function Ri(t){let n="";for(let o=0;o{if(!t||t.iceGatheringState==="complete"){r();return}const o=setTimeout(()=>{t.removeEventListener("icegatheringstatechange",h),r()},n);function h(){t.iceGatheringState==="complete"&&(clearTimeout(o),t.removeEventListener("icegatheringstatechange",h),r())}t.addEventListener("icegatheringstatechange",h)})}function Ei(t){if(!t)return!1;const n=t.connectionState,r=t.iceConnectionState;return n==="connected"||r==="connected"||r==="completed"}function UI(t,n=1e4){return new Promise((r,o)=>{if(!t){o(new Error("Peer connection missing"));return}if(Ei(t)){r();return}const h=setTimeout(()=>{m(),o(new Error("Peer connection timeout"))},n),f=()=>{Ei(t)?(m(),r()):(t.connectionState==="failed"||t.connectionState==="closed"||t.iceConnectionState==="failed")&&(m(),o(new Error(`Peer connection ${t.connectionState||t.iceConnectionState}`)))};function m(){clearTimeout(h),t.removeEventListener("connectionstatechange",f),t.removeEventListener("iceconnectionstatechange",f)}t.addEventListener("connectionstatechange",f),t.addEventListener("iceconnectionstatechange",f)})}async function ZI(t){if(!t?.getParameters||!t?.setParameters)return;const n=t.getParameters()||{},r=n.encodings&&n.encodings[0]||{};n.encodings=[{...r,maxBitrate:64e3,dtx:"disabled"}];try{await t.setParameters(n)}catch{}}function GI(t,n=6e3){return new Promise((r,o)=>{if(!t){o(new Error("Peer connection missing"));return}const h=Date.now();let f=-1;const m=setInterval(async()=>{if(Date.now()-h>n){clearInterval(m),o(new Error("WHIP connected but no outbound audio flow"));return}try{const p=t.getSenders().filter(z=>z.track?.kind==="audio");for(const z of p){const g=await z.getStats();for(const x of g.values()){if(x.type!=="outbound-rtp"||x.kind!=="audio")continue;const M=Number(x.bytesSent||0),y=Number(x.packetsSent||0);if(f<0)f=M;else if(M>f+200||y>5){clearInterval(m),r();return}}}}catch{}},250)})}function $I(t){const n=t.reduce((h,f)=>h+f.length,0),r=new Float32Array(n);let o=0;for(const h of t)r.set(h,o),o+=h.length;return r}function YI(t){const n=new Int16Array(t.length);for(let r=0;r{for(let x=0;xx&&n?.[x]||null,[n,x]),c1=!!(_&&_.state!=="offline"&&!_.error),s1=_?.source==="upload"&&_?.state==="playing",z1=_?.source==="mic-whip"&&(_?.state==="starting"||_?.state==="playing"),M1=!!(!w&&O.current&&(H||N)),f1=!w&&(B==="live"||B==="starting"),g1=w&&j==="recording",t1=w&&j==="sending",p1=N2(p?.keymap?.micPtt?.[0])||"M",x1=v.useCallback(i1=>{const m1=i1==="clip"?"clip":"live";g(y1=>({...y1||{},pttMode:m1}))},[g]),S1=async()=>{if(!x){T("Take control of your rover first.");return}if(!M){T("Select an audio file first.");return}if(M.size>ki){T(`File too large (max ${ki} bytes).`);return}L(!0),T("");try{const i1=await M.arrayBuffer(),m1=Ri(new Uint8Array(i1));await r?.({roverId:x,name:M.name,mime:M.type||"",dataBase64:m1}),T("Upload playback started.")}catch(i1){T(i1?.message||"Failed to play upload.")}finally{L(!1)}},X1=async()=>{if(!x){T("Take control of your rover first.");return}L(!0),T("");try{await o?.(x),T("Upload playback stopped.")}catch(i1){T(i1?.message||"Failed to stop upload.")}finally{L(!1)}},A1=v.useCallback(async i1=>{const m1=String(i1||P.current||"").trim();if($.current=!1,A("idle"),D.current){try{D.current.getSenders().forEach(y1=>y1.track?.stop())}catch{}try{D.current.close()}catch{}}if(D.current=null,F.current)try{F.current.getTracks().forEach(y1=>y1.stop())}catch{}if(F.current=null,O.current=null,m1)try{await m?.(m1)}catch{}P.current=""},[m]),H1=v.useCallback(async i1=>{const m1=await h?.(i1),y1=String(m1?.whipUrl||"").trim(),w1=String(m1?.token||"").trim();if(!y1||!w1)throw new Error("WHIP endpoint unavailable");const n1=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:W3,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}});F.current=n1;const v1=n1.getAudioTracks()?.[0];if(O.current=v1||null,v1&&(v1.enabled=!!(H||N)),v1?.applyConstraints)try{await v1.applyConstraints({channelCount:1,sampleRate:W3,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1})}catch{}const N1=new RTCPeerConnection(qI);D.current=N1,N1.onconnectionstatechange=()=>{if(!$.current)return;const C2=N1.connectionState;if(C2==="connected"){A("live");return}(C2==="failed"||C2==="disconnected"||C2==="closed")&&(A("error"),T(`WHIP transport ${C2}.`))},n1.getAudioTracks().forEach(C2=>{const i0=N1.addTrack(C2,n1);ZI(i0)});const $2=await N1.createOffer({offerToReceiveAudio:!1,offerToReceiveVideo:!1});await N1.setLocalDescription($2),await _I(N1,1800);const c4=await fetch(y1,{method:"POST",headers:{"Content-Type":"application/sdp",...OI(w1)},body:N1.localDescription?.sdp||$2.sdp});if(!c4.ok)throw new Error(`WHIP request failed: ${c4.status}`);const p4=await c4.text();await N1.setRemoteDescription({type:"answer",sdp:p4}),await UI(N1,1e4),await GI(N1,6e3),await f?.(i1)},[H,N,f,h]),G1=v.useCallback(async()=>{if(W.current=!1,E.current){try{E.current.disconnect()}catch{}E.current.onaudioprocess=null}if(r1.current)try{r1.current.disconnect()}catch{}if(U.current)try{U.current.disconnect()}catch{}if(d1.current)try{d1.current.getTracks().forEach(i1=>i1.stop())}catch{}if(h1.current)try{await h1.current.close()}catch{}X.current=[],E.current=null,r1.current=null,U.current=null,d1.current=null,h1.current=null,Q.current=W3},[]),Y1=v.useCallback(async()=>{if(d1.current&&h1.current&&E.current)return;k(v1=>v1==="recording"?v1:"arming");const i1=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:W3,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}}),m1=new AudioContext({latencyHint:"interactive"}),y1=m1.createMediaStreamSource(i1),w1=m1.createScriptProcessor(1024,1,1),n1=m1.createGain();n1.gain.value=0,w1.onaudioprocess=v1=>{if(!W.current)return;const N1=v1.inputBuffer.getChannelData(0);X.current.push(new Float32Array(N1))},y1.connect(w1),w1.connect(n1),n1.connect(m1.destination),d1.current=i1,h1.current=m1,r1.current=y1,E.current=w1,U.current=n1,Q.current=m1.sampleRate||W3},[]),r2=v.useCallback(async({send:i1})=>{const m1=W.current;if(W.current=!1,!m1){i1||k("idle");return}const y1=X.current;if(X.current=[],!i1){k("idle");return}if(!x){k("idle");return}if(!y1.length){k("idle");return}k("sending");try{const w1=$I(y1),n1=II(w1,Q.current||W3),v1=Ri(n1);await r?.({roverId:x,name:`ptt-${Date.now()}.wav`,mime:"audio/wav",dataBase64:v1}),k("idle")}catch(w1){k("error"),T(w1?.message||"Failed to send PTT clip.")}},[r,x]),o1=v.useCallback(async()=>{if(!x){T("Take control of your rover first.");return}await Y1(),X.current=[],W.current=!0,k("recording")},[Y1,x]);return v.useEffect(()=>{let i1=!1;async function m1(){if(w){await A1(P.current||x);return}if(!x){await A1(P.current);return}if(!($.current&&P.current===x)&&!(!H&&!N))try{A("starting"),T(""),$.current=!0,P.current=x,await H1(x),i1||A("live")}catch(y1){i1||(A("error"),T(y1?.message||"Failed to start mic forwarding.")),await A1(x)}}return m1(),()=>{i1=!0}},[w,H,N,x,H1,A1]),v.useEffect(()=>{const i1=O.current;i1&&(i1.enabled=!!(!w&&(H||N)))},[w,H,N]),v.useEffect(()=>{let i1=!1;async function m1(){if(!w||!x){W.current&&await r2({send:!1}),k("idle");return}try{await Y1(),!i1&&!W.current&&k("idle")}catch(y1){i1||(k("error"),T(y1?.message||"Microphone access failed for clip mode."))}}return m1(),()=>{i1=!0}},[w,Y1,r2,x]),v.useEffect(()=>{let i1=!1;async function m1(){if(w){if(!x){W.current&&await r2({send:!1});return}if(N){if(!W.current)try{T(""),await o1()}catch(y1){i1||(k("error"),T(y1?.message||"Failed to start clip recording."))}}else W.current&&await r2({send:!0})}}return m1(),()=>{i1=!0}},[w,r2,N,x,o1]),v.useEffect(()=>()=>{A1(P.current||x),G1()},[x,A1,G1]),l.jsx(u2,{title:"Audio Controls",children:l.jsxs("div",{className:"grid gap-1",children:[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(Ue,{label:p1})]})}),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 ${w?"":"bg-emerald-500 text-white hover:bg-emerald-500"}`,onClick:()=>x1("live"),children:"Live Mic (WHIP)"}),l.jsx("button",{type:"button",className:`button-dark w-full text-sm ${w?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,onClick:()=>x1("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(v4,{label:"Mode",active:w,detail:w?"record clip":"live mic"}),l.jsx(v4,{label:"PTT hold",active:N,detail:N?"held":"released"}),l.jsx(v4,{label:"Recording",active:g1,detail:g1?"capturing":"idle"}),l.jsx(v4,{label:"Playback",active:w&&s1,detail:w&&s1?"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:`${c0} text-center`,type:"file",accept:".mp3,.wav,.ogg,audio/mpeg,audio/wav,audio/ogg",disabled:V||!x,onChange:i1=>y(i1.target.files?.[0]||null)})]}),l.jsx("div",{className:"mx-auto w-full max-w-sm text-center",children:M?l.jsxs("div",{className:"surface-muted text-xs text-slate-300",children:[M.name," (",M.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:V||!x,onClick:S1,children:V?"Working...":"Play Upload"}),l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:V||!x,onClick:X1,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:H,disabled:!x||w,onChange:i1=>g(m1=>({...m1||{},openMicEnabled:!!i1.target.checked}))}),l.jsx("span",{children:w?"Open mic (live mode only)":"Open mic"})]}),l.jsxs("div",{className:"grid gap-0.5 content-start",children:[l.jsx(v4,{label:"WHIP link",active:f1,detail:B}),l.jsx(v4,{label:"Mic hot",active:M1,detail:M1?"transmitting":"muted"}),l.jsxs("div",{className:"surface-muted text-center text-xs text-slate-400",children:["Hold ",l.jsx(Ue,{label:p1})," 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(v4,{label:"Forward pipe",active:c1,detail:c1?"connected":"offline"}),l.jsx(v4,{label:"Upload playback",active:s1,detail:s1?"playing":"idle"}),l.jsx(v4,{label:"Mic relay",active:z1,detail:z1?"active":"idle"}),l.jsx(v4,{label:"WHIP transport",active:f1,detail:B}),l.jsx(v4,{label:"Clip recording",active:g1,detail:g1?"recording":"idle"}),l.jsx(v4,{label:"Clip sending",active:t1,detail:t1?"sending":"idle"})]}),G?l.jsx("div",{className:"text-xs text-slate-400 text-center",children:G}):null]})})]})})}function QI({pendingRequestId:t,currentStoredKey:n,nickname:r,requestVerification:o,applyIdentityKey:h,onMessage:f,fullWidth:m=!1}){const[p,z]=v.useState(0),[g,x]=v.useState(""),[M,y]=v.useState(!1),[V,L]=v.useState(!1),H=()=>{z(1),x(n),y(!1),f?.("")},C=()=>{z(0),y(!1)},w=async A=>{if(A.preventDefault(),!M){f?.("Please confirm your nickname agreement before sending.");return}if(p<3){f?.("Complete all request steps before sending.");return}L(!0),f?.("");try{const j=await h(g);await o?.(),x(j),z(0),y(!1),f?.("Verification request sent to lockdown admins.")}catch(j){f?.(j.message||"Failed to submit request.")}finally{L(!1)}},B=m?"w-full":w8;return t?l.jsx(u2,{title:"Verification",className:B,bodyClassName:"text-sm text-slate-300",children:l.jsxs("div",{className:K6,children:["Verification request pending: ",t]})}):p===0?l.jsx(u2,{title:"Verification",className:B,children:l.jsx("div",{className:K6,children:l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:H,disabled:V,children:"Request Verification"})})}):l.jsx(u2,{title:"Request verification",className:B,children:l.jsx("form",{onSubmit:w,children:l.jsxs("div",{className:K6,children:[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(B7,{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:M,onChange:A=>y(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||!M,onClick:()=>z(2),children:"Continue"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:C,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:c0,type:"password",name:"identity_key_request",autoComplete:"current-password",maxLength:35,value:g,onChange:A=>x(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:!g,onClick:()=>navigator.clipboard?.writeText(g),children:"Copy Key"}),l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!g,onClick:()=>z(3),children:"Continue"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>z(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:c0,type:"password",name:"identity_key_request_final",autoComplete:"current-password",maxLength:35,value:g,onChange:A=>x(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:V||!g,children:"Confirm Request"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>z(2),children:"Back"})]})]}):null]})})})}function XI({currentStoredKey:t,applyIdentityKey:n,onMessage:r,fullWidth:o=!1}){const[h,f]=v.useState(0),[m,p]=v.useState(""),[z,g]=v.useState(!1),x=()=>{f(1),p(""),r?.("")},M=()=>{f(0),p("")},y=async L=>{if(L.preventDefault(),!(h<2)){g(!0),r?.("");try{await n(m),f(0),p(""),r?.("Identity key restored.")}catch(H){r?.(H.message||"Failed to restore key.")}finally{g(!1)}}},V=o?"w-full":w8;return l.jsx(u2,{title:"Identity key",className:V,children:l.jsxs("div",{className:K6,children:[l.jsxs("p",{className:"text-xs text-slate-500",children:["Current: ",TI(t)||"not set yet"]}),l.jsx("input",{className:c0,type:"password",name:"identity_key_current",autoComplete:"current-password",maxLength:35,value:t,readOnly:!0,placeholder:"Identity key"}),h===0?l.jsxs("div",{className:"flex justify-center gap-0.5",children:[l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!t,onClick:()=>navigator.clipboard?.writeText(t),children:"Copy Key"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:x,disabled:z,children:"Restore Key"})]}):null,h===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:M,children:"Cancel"})]})]}):null,h===2?l.jsxs("form",{className:"space-y-0.5",onSubmit:y,children:[l.jsx("input",{className:c0,type:"password",name:"identity_key_restore",autoComplete:"current-password",maxLength:35,value:m,onChange:L=>p(L.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:z||!m,children:"Confirm Restore"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(1),children:"Back"})]})]}):null]})})}function KI({requestableRovers:t=[],pendingRequests:n=[],requestPrivateRoverAccess:r,onMessage:o,fullWidth:h=!1}){const[f,m]=v.useState({}),p=v.useMemo(()=>{const x=new Map;return(n||[]).forEach(M=>{M?.roverId&&x.set(String(M.roverId),M)}),x},[n]),z=h?"w-full":w8,g=async x=>{if(x){m(M=>({...M,[x]:!0})),o?.("");try{(await r?.(x))?.existing?o?.("You already have a pending request for that rover."):o?.("Private rover access request sent to lockdown admins.")}catch(M){o?.(M.message||"Failed to send request.")}finally{m(M=>({...M,[x]:!1}))}}};return l.jsx(u2,{title:"Private rover access requests",className:z,bodyClassName:"text-sm text-slate-300",children:l.jsx("div",{className:K6,children:t.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:t.map(x=>{const M=String(x.id),y=!!f[M],V=p.get(M);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:x.name||M}),l.jsx("p",{className:"truncate text-[0.7rem] text-slate-500",children:M})]}),V?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:()=>g(M),disabled:y,className:"button-dark text-xs disabled:opacity-50",children:y?"Sending...":"Request"})]},M)})})})})}function i7(t){return String(t||"").trim()}function JI(t){const n=i7(t);return n?n.includes("DOCKINGRUNNING")?"Returning":n.includes("PAUSED")?"Paused":n.includes("HOUSECLEANINGRUNNING")?"Cleaning":n.includes("SPOTCLEANINGRUNNING")?"Spot cleaning":n.includes("STATE_START")?"Starting":n.includes("STATE_IDLE")||n.includes("STATE_STANDBY")?"Idle":n:"--"}function po(t){return t==="good"?"bg-emerald-600 text-white":t==="warn"?"bg-amber-500 text-slate-900":t==="danger"?"bg-rose-600 text-white":"bg-slate-700 text-slate-100"}function P5({label:t,value:n,tone:r="muted",valueClass:o="",hideLabel:h=!1}){return l.jsxs("div",{className:`rounded-md px-1 py-0.75 text-center ${po(r)}`,children:[h?null:l.jsx("div",{className:"text-[0.72rem] opacity-90",children:t}),l.jsx("div",{className:`text-sm font-semibold ${o}`,title:String(n||""),children:n})]})}function WI({neato:t,onStart:n,onSendHome:r,onLocate:o,onClearErrors:h,onPowerCycle:f,fullWidth:m=!1}){const[p,z]=v.useState(""),g=m?"w-full":"w-full max-w-xl",x=!!t?.configured,M=!!(t?.enabled&&t?.connected),y=!!t?.telemetry?.extPowerPresent,V=!!t?.telemetry?.chargingActive,L=JI(t?.telemetry?.uiState),H=i7(t?.telemetry?.robotState)||"--",C=t?.telemetry?.batteryPercent,w=Number.isFinite(C)?`${C}%`:"--",B=t?.telemetry?.batteryVoltage,A=Number.isFinite(B)?`${B.toFixed(2)} V`:"--",j=i7(t?.telemetry?.robotError)||"--",k=i7(t?.telemetry?.robotAlert)||"--",G=t?.controls||{},T=!!G?.start?.available,F=!!G?.sendHome?.available,O=!!G?.locate?.available,D=!!G?.clearErrors?.available,$=!!G?.powerCycle?.available,P=x&&M&&T,d1=x&&M&&F,h1=x&&M&&O,r1=x&&M&&D,E=x&&M&&$,U=async(_,c1)=>{if(c1){z(_);try{await c1()}catch{}finally{z("")}}},X=x?M?"Online":"Offline":"Not configured",W=x?M?"good":"danger":"warn",Q=C==null?"muted":C>=60?"good":C>=25?"warn":"danger",N=y?"Docked":L!=="--"?L:"Away from dock";return l.jsx(u2,{title:"Neato Controls",className:g,bodyClassName:"text-sm text-slate-200",actions:l.jsx("span",{className:`inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${po(W)}`,children:X}),children:l.jsxs("div",{className:"grid gap-0.5",children:[l.jsx("div",{className:"text-center",children:l.jsx("p",{className:"text-xs text-slate-400",children:"Control the autonomous Neato robovac. Be nice to him."})}),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:!P||!!p,onClick:()=>U("start",n),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:!d1||!!p,onClick:()=>U("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:!h1||!!p,onClick:()=>U("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:!r1||!!p,onClick:()=>U("clearErrors",h),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:!E||!!p,onClick:()=>U("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(P5,{label:"Battery",value:w,tone:Q}),l.jsx(P5,{label:"Voltage",value:A,tone:"muted"}),l.jsx(P5,{label:"Docked",value:y?"Docked":"Not docked",tone:y?"good":"muted",hideLabel:!0}),l.jsx(P5,{label:"Charging",value:V?"charging":"not charging",tone:V?"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:H})]}),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: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:"UI state"}),l.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:L})]}),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:j})]}),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:k})]})]})]})})]}),!x||!M||!T||!F||!O||!D||!$?l.jsx("p",{className:"text-xs text-slate-400 text-center",children:x?M?"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 tP(t){return t==="good"?"bg-emerald-600 text-white":t==="warn"?"bg-amber-500 text-slate-900":t==="danger"?"bg-rose-600 text-white":"bg-slate-700 text-slate-100"}function eP(t){return t==="up"?"Up":t==="down"?"Down":t==="stopped"?"Transitioning...":t==="conflict"?"Conflict":"--"}function cP({lift:t,onUp:n,onDown:r,fullWidth:o=!1}){const[h,f]=v.useState(""),[m,p]=v.useState(()=>Date.now()),z=o?"w-full":"w-full max-w-xl",g=!!t?.configured,x=!!(t?.enabled&&t?.connected),M=!!t?.busy,y=String(t?.target||"").toLowerCase(),V=String(t?.position||"").toLowerCase(),L=!!t?.availability?.upSwitch,H=!!t?.availability?.downSwitch,C=Number(t?.lastActionAt||0),w=Math.max(0,Number(t?.commandCooldownMs||0)),B=Math.max(0,C+w-m),A=!M&&B>0,j=M||A;v.useEffect(()=>{if(!j)return;const D=setInterval(()=>{p(Date.now())},100);return()=>clearInterval(D)},[j]),v.useEffect(()=>{p(Date.now())},[C,w,M]);const k=g?x?j?"Busy":"Ready":"Offline":"Not configured",G=g?x?j?"warn":"good":"danger":"warn",T=g&&x&&!j&&!h&&L&&H,F=v.useMemo(()=>Math.ceil(B/1e3),[B]),O=async(D,$)=>{if($){f(D);try{await $()}catch{}finally{f("")}}};return l.jsxs(u2,{title:"Lift Controls",className:`relative ${z}`,bodyClassName:"text-sm text-slate-200",actions:l.jsx("span",{className:`inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${tP(G)}`,children:k}),children:[j?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:M?"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."}),!M&&A?l.jsxs("p",{className:"text-xs text-slate-400",children:["About ",F,"s remaining."]}):null]})}):null,l.jsxs("div",{className:"grid gap-0.5",children:[l.jsx("div",{className:"text-center",children:l.jsx("p",{className:"text-xs text-slate-400",children:"Move the lift up and down. Please don't break anything..."})}),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:eP(V)})]}),l.jsx("button",{type:"button",disabled:!T,onClick:()=>O("down",r),className:`button-dark w-full text-sm disabled:opacity-50 ${V==="down"||y==="down"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:h==="down"||y==="down"?"Lowering...":"Down"}),l.jsx("button",{type:"button",disabled:!T,onClick:()=>O("up",n),className:`button-dark w-full text-sm disabled:opacity-50 ${V==="up"||y==="up"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:h==="up"||y==="up"?"Raising...":"Up"})]})}),!g||!x||!L||!H?l.jsx("p",{className:"text-xs text-slate-400 text-center",children:g?x?"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,t?.lastError?l.jsxs("p",{className:"text-xs text-rose-300 text-center",children:["Last error: ",t.lastError]}):null]})]})}function aP(t){const n=String(t||"").trim();if(!n)return"";try{const r=new URL(n),o=String(r.protocol||"").toLowerCase();return o!=="http:"&&o!=="https:"?"":r.toString()}catch{return""}}function nP({isVerified:t=!1,fullWidth:n=!1,onMessage:r}){const{value:o,save:h}=T1("profile",{nickname:"",profileImageUrl:""}),f=String(o?.profileImageUrl||"").trim(),[m,p]=v.useState(f),[z,g]=v.useState(!1);v.useEffect(()=>{p(f)},[f]);const x=v.useMemo(()=>aP(m),[m]),M=String(m||"").trim()!==f,y=!!(t&&M&&(x||!String(m||"").trim())),V=n?"w-full":w8,L=async H=>{if(H.preventDefault(),!t){r?.("Verify your account before setting a profile image URL.");return}g(!0),r?.("");try{const C=x||"";h(w=>({...w||{},profileImageUrl:C})),r?.(C?"Profile image URL saved.":"Profile image URL cleared.")}finally{g(!1)}};return l.jsx(u2,{title:"Chat profile image URL",className:V,bodyClassName:"text-sm text-slate-300",children:l.jsxs("form",{className:K6,onSubmit:L,children:[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:c0,type:"url",name:"vip_profile_image_url",placeholder:"https://example.com/avatar.png",value:m,onChange:H=>p(H.target.value),disabled:!t||z}),!x&&String(m||"").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:!y||z,children:z?"Saving...":"Save URL"}),l.jsx("button",{type:"button",className:"button-dark text-sm disabled:opacity-50",disabled:!t||z||!f,onClick:()=>p(""),children:"Clear"})]})]})})}function go({isActive:t=!0}){const n=u1(h1=>h1.session),{identifySession:r,requestVerification:o,requestPrivateRoverAccess:h,playUploadedAudio:f,stopUploadedAudio:m,startMicWhip:p,readyMicWhip:z,stopMicWhip:g,neatoStart:x,neatoSendHome:M,neatoLocate:y,neatoClearErrors:V,neatoPowerCycle:L,liftUp:H,liftDown:C}=E2(),{value:w,save:B}=T1("identity",{cookieUserId:""}),{value:A}=T1("profile",{nickname:""}),j=v.useMemo(()=>(w?.cookieUserId||"").trim(),[w?.cookieUserId]),k=(A?.nickname||"").trim(),G=!!n?.isVerified,T=n?.verification?.pendingRequestId||null,F=n?.privateRoverAccess?.requestableRovers||[],O=n?.privateRoverAccess?.pendingRequests||[],D=String(n?.assignment?.roverId||"").trim(),[$,P]=v.useState(""),d1=async h1=>{const r1=String(h1||"").trim().toLowerCase();if(!r1)throw new Error("Identity key required.");if(!EI.test(r1))throw new Error("Identity key must match format: cu_ + 32 lowercase hex chars.");return B(E=>({...E||{},cookieUserId:r1})),await r({cookieUserId:r1,nickname:k}),r1};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:[G?null:l.jsx("div",{className:"lg:col-span-2",children:l.jsx(QI,{pendingRequestId:T,currentStoredKey:j,nickname:k,requestVerification:o,applyIdentityKey:d1,onMessage:P,fullWidth:!0})}),l.jsx("div",{className:"lg:col-span-2",children:G?l.jsxs("div",{className:"space-y-2",children:[l.jsx(WI,{neato:n?.neato||null,onStart:x,onSendHome:M,onLocate:y,onClearErrors:V,onPowerCycle:L,fullWidth:!0}),l.jsx(cP,{lift:n?.lift||null,onUp:H,onDown:C,fullWidth:!0}),l.jsx(PI,{ownRoverId:D,audioForwardByRover:n?.audioForward||{},playUploadedAudio:f,stopUploadedAudio:m,startMicWhip:p,readyMicWhip:z,stopMicWhip:g})]}):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(KI,{requestableRovers:F,pendingRequests:O,requestPrivateRoverAccess:h,onMessage:P,fullWidth:!0})}),l.jsx("div",{className:"lg:col-span-2",children:l.jsx(XI,{currentStoredKey:j,applyIdentityKey:d1,onMessage:P,fullWidth:!0})}),l.jsx("div",{className:"lg:col-span-2",children:l.jsx(nP,{isVerified:G,onMessage:P,fullWidth:!0})})]}),$?l.jsx("div",{className:w8,children:l.jsx("p",{className:"text-xs text-slate-400 text-center",children:$})}):null]})}const lP=420,rP=1200,iP={1:262,2:330,3:392,4:523};function x7(){const t=u1(H=>H.session?.buttonBox?.buttons??[]),n=G2(),{value:r}=T1("audio",s2),o=Number.isFinite(r?.masterVolume)?r.masterVolume:s2.masterVolume,h=Number.isFinite(r?.alertVolume)?r.alertVolume:s2.alertVolume,f=Math.max(0,Math.min(1,o*h)),m=v.useMemo(()=>{const H=Array.isArray(t)?t:[];return H.length===4?H:[1,2,3,4].map(C=>H.find(w=>Number(w?.id)===C)||{id:C,count:0,goal:0,rewardName:null,rewardId:null,rewardNumber:null,lastRewardAt:null})},[t]),[p,z]=v.useState({}),[g,x]=v.useState({}),M=v.useRef(new Map),y=v.useRef(new Map),V=v.useRef({}),L=v.useRef(null);return v.useEffect(()=>{const H={};m.forEach(w=>{H[w.id]=Number(w.lastRewardAt||0)});const C=V.current||{};m.forEach(w=>{const B=Number(w.id),A=H[B]||0,j=Number(C[B]||0);if(A>0&&A!==j){x(T=>({...T,[B]:!0}));const k=y.current.get(B);k&&clearTimeout(k);const G=setTimeout(()=>{x(T=>({...T,[B]:!1})),y.current.delete(B)},rP);y.current.set(B,G)}}),V.current=H},[m]),v.useEffect(()=>{function H(w){const B=iP[w];if(!B||f<=0)return;let A=L.current;if(!A){const T=window.AudioContext||window.webkitAudioContext;if(!T)return;A=new T,L.current=A}const j=A.createOscillator(),k=A.createGain();j.type="square",j.frequency.value=B;const G=.14*f;k.gain.setValueAtTime(1e-4,A.currentTime),k.gain.exponentialRampToValueAtTime(Math.max(1e-4,G),A.currentTime+.01),k.gain.exponentialRampToValueAtTime(1e-4,A.currentTime+.2),j.connect(k),k.connect(A.destination),j.start(),j.stop(A.currentTime+.21)}function C(w={}){const B=Number(w.buttonId);if(!Number.isFinite(B)||B<1||B>4)return;z(k=>({...k,[B]:!0}));const A=M.current.get(B);A&&clearTimeout(A);const j=setTimeout(()=>{z(k=>({...k,[B]:!1})),M.current.delete(B)},lP);M.current.set(B,j),H(B)}return n.on("buttonBox:increment",C),()=>{n.off("buttonBox:increment",C)}},[f,n]),l.jsx(u2,{title:"Button Box",bodyClassName:"space-y-0.5 text-base",children:l.jsx("div",{className:"grid grid-cols-4 gap-0.5",children:m.map(H=>{const C=Number(H.id),w=Number.isFinite(H.count)?H.count:0,B=Number.isFinite(H.goal)?H.goal:0,A=typeof H.rewardName=="string"&&H.rewardName.trim()?H.rewardName.trim():"Unassigned",j=Number.isFinite(H.rewardNumber)?H.rewardNumber:"?",k=!!p[C],G=!!g[C];return l.jsx(Bs,{buttonId:C,count:w,goal:B,rewardNumber:j,rewardName:A,className:[k?"bg-cyan-900/45":"",G?"bg-fuchsia-900/45":""].filter(Boolean).join(" ")},C)})})})}function zo(){const{identifySession:t}=E2(),n=u1(g=>g.session?.overseerVote||null),{value:r}=T1("profile",{nickname:""}),{value:o}=T1("identity",{cookieUserId:""}),{value:h,save:f}=T1("overseerPreference",{enabled:!1}),m=!!h?.enabled,p=!!n?.running,z=async g=>{const x=!!g?.target?.checked;await f(M=>({...M||{},enabled:x})),await t({cookieUserId:String(o?.cookieUserId||"").trim(),nickname:String(r?.nickname||"").trim(),overseerEnabled:x})};return l.jsxs(u2,{title:"Overseer vote",bodyClassName:"space-y-0.5 text-center text-xs text-slate-200",children:[l.jsxs("label",{className:`flex items-center justify-center gap-1.5 rounded px-1 py-0.5 ${m?"bg-emerald-600/80 text-emerald-50":"bg-slate-600/70 text-slate-100"}`,children:[l.jsx("input",{type:"checkbox",checked:m,onChange:z}),l.jsxs("span",{children:["Your vote: ",m?"Yes":"No"]})]}),l.jsx("span",{className:"text-xs",children:"Enable a local LLM in chat"}),l.jsxs("div",{className:"mt-0.5 text-slate-400",children:["Yes!: ",Number(n?.yesCount||0),", No...: ",Number(n?.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 by vote."})})]})}function sP(){const{state:{roverId:t}}=y2(),r=e4(t)?.sensors||{};return l.jsx(u2,{hideHeader:!0,children:l.jsx("div",{className:"aspect-square w-full",children:l.jsx(eo,{sensors:r})})})}function oP(){const{state:{roverId:t,keymap:n,camera:r,horn:o},pipeline:h,actions:{setServoAngle:f,setNightVision:m,startHorn:p,stopHorn:z}}=y2(),g=r0(),x=Ys(t),M=x.docked&&!x.driving,y=r?.config,V=!!(t&&r?.enabled&&y),L=!!(t&&h?.nightVision),H=h?.nightVisionState,C=!!(t&&h?.horn),w=o?.overheated,B=typeof y?.minAngle=="number"?y.minAngle:-30,A=typeof y?.maxAngle=="number"?y.maxAngle:30,j=typeof r?.angle=="number"?r.angle:typeof y?.homeAngle=="number"?y.homeAngle:(B+A)/2,k=N2(n?.nightVisionToggle?.[0]),G=N2(n?.hornHonk?.[0]),T=N2(n?.cameraUp?.[0]),F=N2(n?.cameraDown?.[0]),O=!!(!t||g.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(Is,{layout:"desktop",expand:!0,driveDockState:x}),M?null:l.jsxs("div",{className:"surface space-y-0.5 p-0 text-sm text-slate-200",children:[L&&l.jsx(Ps,{nightVisionOn:H?.nightVisionOn,disabled:!t,onToggle:m,keyLabel:k}),C&&l.jsx(Qs,{disabled:!t||w,onStart:p,onStop:z,keyLabel:G,active:o?.active,heat:o?.heat}),V&&l.jsx(Xs,{value:j,min:B,max:A,label:"Camera tilt",disabled:O,onChange:f,keyDownLabel:F,keyUpLabel:T,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 uP({layout:t,onOpenHelpOverlay:n}){const[r,o]=v.useState("telemetry"),h=u1(H=>!!H.session?.isVerified),f=u1(H=>String(H.session?.assignment?.roverId||"").trim()),m=u1(H=>{const C=String(H.session?.assignment?.roverId||"").trim();return C&&H.session?.audioForward?.[C]||null}),{state:p}=y2(),{value:z}=T1("vipAudio",{openMicEnabled:!1,pttMode:"live"}),g=h?"bg-emerald-400":"bg-red-600",x=!!p?.mic?.pttActive,M=!!z?.openMicEnabled,y=z?.pttMode==="clip"?"clip":"live",V=!!(f&&h&&(y==="clip"?x:M||x)),L=!!(f&&h&&y==="clip"&&m?.source==="upload"&&m?.state==="playing");return l.jsx("section",{className:"panel text-base",children:l.jsxs(Mc,{defaultTab:"telemetry",currentTab:r,onTabChange:o,children:[l.jsxs(bc,{children:[l.jsx(W2,{id:"telemetry",children:"Controls"}),l.jsx(W2,{id:"vip",highlight:L?"green":V?"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 ${g}`,"aria-hidden":"true",title:h?"Verified":"Not verified"})]})}),l.jsx(W2,{id:"help",children:"Help"}),l.jsx(W2,{id:"settings",children:"Settings"})]}),l.jsxs(yc,{children:[l.jsx(t4,{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(sP,{}),l.jsx(oP,{})]}),l.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]",children:[l.jsx(N7,{}),l.jsx(nc,{panelId:"replay-sources-desktop",fillHeight:!0}),l.jsx(LI,{})]}),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(j7,{fillHeight:!0}),l.jsxs("div",{className:"grid min-h-0 gap-0.5 grid-rows-[auto_minmax(0,1fr)]",children:[l.jsx(zo,{}),l.jsx(z7,{compact:!0,hideNicknameForm:!0,fillHeight:!0})]})]}),l.jsx(ro,{}),l.jsx(x7,{}),l.jsx(dc,{defaultOrientation:"horizontal",panelId:"rightpane-telemetry"})]})}),l.jsx(t4,{id:"vip",keepMounted:!0,children:l.jsx(go,{isActive:r==="vip"})}),l.jsx(t4,{id:"help",children:l.jsx(mo,{layout:t,onOpenOverlay:n})}),l.jsx(t4,{id:"settings",children:l.jsx(ho,{})})]})]})})}const hP=new Set(["admin","lockdown","lockdown-admin"]),dP=new Set(["lockdown","lockdown-admin"]),fP=new Set(["admin","lockdown"]);function vP(t="admin"){return t==="lockdown"?{title:"Lockdown mode active",description:"Only the server owners can access the interface at this time."}:{title:"Admin mode active"}}function mP(){const t=u1(x=>x.session?.mode||null),n=u1(x=>x.session?.role||null),r=u1(x=>x.session?.adminReason?.text||"");u1(x=>x.session?.adminReason?.updatedAt||null);const o=u1(x=>x.session?.timezone||"UTC"),h=fP.has(t),f=t==="lockdown"?dP.has(n):hP.has(n),[m,p]=v.useState(()=>new Date),z=v.useMemo(()=>{try{return new Intl.DateTimeFormat("en-US",{timeZone:o,hour:"numeric",minute:"2-digit",second:"2-digit"}).format(m)}catch{return m.toLocaleTimeString()}},[m,o]);if(v.useEffect(()=>{const x=setInterval(()=>p(new Date),1e3);return()=>clearInterval(x)},[]),!h||f)return null;const g=vP(t);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:g.title}),l.jsx("p",{className:"text-sm text-slate-300",children:g.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: ",z]})]}),l.jsx("div",{className:"surface-muted",children:l.jsx(io,{})}),l.jsx("div",{className:"w-full justify-center items-center",children:l.jsx(H7,{id:"discord",label:"Join our Discord server for updates!"})}),"You can still use the chat while the server is locked:",l.jsx("div",{className:"max-h-80 overflow-y-auto",children:l.jsx(j7,{nicknameLayout:"stacked"})})]})})}const pP="/assets/turn_alert-DVO8EZJa.mp3";function gP(t,n=1){const r=v.useRef(null);return v.useEffect(()=>{r.current=new Audio(t),r.current.volume=n,r.current.load()},[t,n]),()=>{r.current&&(r.current.volume=n,r.current.currentTime=0,r.current.play().catch(()=>{}))}}function zP(){const t=u1(x=>x.session?.socketId||null),n=u1(x=>x.session?.turnQueues||{}),r=u1(x=>x.session?.roster||[]),{pushAlert:o}=E2(),{value:h}=T1("audio",s2),f=Number.isFinite(h?.masterVolume)?h.masterVolume:s2.masterVolume,m=Number.isFinite(h?.alertVolume)?h.alertVolume:s2.alertVolume,p=Math.max(0,Math.min(1,f*m)),z=gP(pP,p),g=v.useRef(new Set);return v.useEffect(()=>{if(!t)return;const x=[];Object.entries(n).forEach(([M,y])=>{!y||!y.current||y.current!==t||g.current.has(M)||(x.push(M),g.current.add(M))}),Object.keys(n).forEach(M=>{(!n[M]||n[M].current!==t)&&g.current.delete(M)}),x.length!==0&&(x.forEach(M=>{const y=r.find(V=>String(V.id)===String(M))?.name||M;o({title:"Your turn!",message:`You now control ${y}.`,color:"#ff5722"})}),z())},[n,z,o,r,t]),null}function xP({visible:t,mode:n,onEnterFullscreen:r,onDismiss:o}){if(!t)return null;const h=n==="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!"}),h?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:h?"Got it":"Not now"}),!h&&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 MP({side:t="right",onClick:n}){const r=t==="left"?"left-2":"right-2";return l.jsx("button",{type:"button",onClick:n,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 bP=new Set(["mobile-portrait","mobile-landscape"]),yP=()=>{if(typeof navigator>"u")return!1;const t=navigator.userAgent||navigator.platform||"";return/iphone|ipad|ipod/i.test(t)},Ti=()=>{if(typeof document>"u")return!1;const t=document;return!!(t.fullscreenEnabled||t.webkitFullscreenEnabled||t.mozFullScreenEnabled||t.msFullscreenEnabled)};function CP(t){const{value:n,save:r,status:o}=T1("page",{fullscreenPromptShown:!1}),h=bP.has(t),[f,m]=v.useState(!1),[p,z]=v.useState("native"),[g,x]=v.useState(!1),M=!!n?.fullscreenPromptShown,y=v.useMemo(()=>yP(),[]),V=v.useMemo(()=>Ti(),[]),L=v.useCallback(()=>{o==="ready"&&r(A=>A?.fullscreenPromptShown?A:{...A||{},fullscreenPromptShown:!0})},[r,o]),H=v.useCallback(()=>{if(o!=="ready"){m(!1);return}if(!h){m(!1),x(!1);return}if(!g&&M){m(!1);return}if(y){z("pwa-hint"),m(!0);return}if(!Ti()){m(!1);return}if(typeof document<"u"&&document.fullscreenElement){m(!1);return}z("native"),m(!0)},[g,M,y,h,o]);v.useEffect(()=>{H()},[H]),v.useEffect(()=>{if(typeof document>"u")return;const A=()=>{if(document.fullscreenElement){x(!1),m(!1);return}H()};return document.addEventListener("fullscreenchange",A),document.addEventListener("webkitfullscreenchange",A),()=>{document.removeEventListener("fullscreenchange",A),document.removeEventListener("webkitfullscreenchange",A)}},[H]);const C=v.useCallback(()=>{L(),x(!1),m(!1)},[L]),w=v.useCallback(async()=>{if(!h||y||typeof document>"u")return!1;if(document.fullscreenElement)return!0;const A=document.documentElement;if(!A)return!1;const j=A.requestFullscreen||A.webkitRequestFullscreen||A.mozRequestFullScreen||A.msRequestFullscreen;if(!j)return!1;try{const k=j.call(A);return k&&typeof k.then=="function"&&await k,L(),x(!1),m(!1),!0}catch(k){return console.warn("Failed to enter fullscreen",k),!1}},[y,h,L]),B=v.useCallback(()=>{h&&(x(!0),z(y?"pwa-hint":"native"),m(!0))},[y,h]);return v.useEffect(()=>{h||(x(!1),m(!1))},[h]),{visible:f,mode:p,isIOS:y,nativeSupported:V,enterFullscreen:w,dismiss:C,showPrompt:B}}function wP({visible:t,layout:n,onClose:r,showOnLoad:o,onToggleShowOnLoad:h}){if(!t)return null;const{state:f}=y2(),m=p=>{const z=!p.target.checked;h?.(z)};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:m,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(vo,{layout:n,keymap:f?.keymap||{}})})]})})}function P6({label:t,keyLabel:n}){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:t}),l.jsx(Ue,{label:n})]})}function HP({keymap:t}){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(P6,{label:"Forward",keyLabel:N2(t?.driveForward?.[0])}),l.jsx(P6,{label:"Backward",keyLabel:N2(t?.driveBackward?.[0])}),l.jsx(P6,{label:"Turn Left",keyLabel:N2(t?.driveLeft?.[0])}),l.jsx(P6,{label:"Turn Right",keyLabel:N2(t?.driveRight?.[0])}),l.jsx(P6,{label:"Move faster",keyLabel:N2(t?.boostModifier?.[0])}),l.jsx(P6,{label:"Move slower",keyLabel:N2(t?.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 VP(){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 SP({visible:t,layout:n,showOnLoad:r,onToggleShowOnLoad:o,onOpenHelp:h,onClose:f}){const{state:m}=y2(),p=n==="desktop",z=v.useMemo(()=>m?.keymap||{},[m?.keymap]);if(!t)return null;const g=x=>{const M=!x.target.checked;o?.(M)};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(HP,{keymap:z}):l.jsx(VP,{})}),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(B7,{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(H7,{id:"discord",label:"Join Discord"})]})]})]}),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:g,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 AP=`aroma bagel batter diff --git a/server/public/index.html b/server/public/index.html index 8b949d4c..a621aa0d 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -11,7 +11,7 @@ Roomba Rover - + diff --git a/webui/src/components/NicknameForm/index.jsx b/webui/src/components/NicknameForm/index.jsx index dc1add25..108dafb1 100644 --- a/webui/src/components/NicknameForm/index.jsx +++ b/webui/src/components/NicknameForm/index.jsx @@ -35,22 +35,28 @@ export default function NicknameForm({ compact = false }) { } return ( -
+
setNicknameInput(e.target.value)} + onKeyDown={(event) => { + if (event.key !== 'Enter') return; + event.preventDefault(); + handleSave(event); + }} maxLength={32} placeholder="Enter a nickname" disabled={!canSetNickname} /> - +
); }