From 48b87b1127c93b6e18c73012c58b13cf30930928 Mon Sep 17 00:00:00 2001 From: legop3 Date: Wed, 27 May 2026 22:44:56 -0400 Subject: [PATCH] change chat card title --- plans/to-do.md | 12 ++++++++---- .../assets/{index-CckAnAnd.js => index-CboMb02p.js} | 2 +- server/public/index.html | 2 +- webui/src/components/ChatPanel/index.jsx | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) rename server/public/assets/{index-CckAnAnd.js => index-CboMb02p.js} (99%) diff --git a/plans/to-do.md b/plans/to-do.md index 9959a03a..64ffc06f 100644 --- a/plans/to-do.md +++ b/plans/to-do.md @@ -1,7 +1,11 @@ -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 +1. FIX NO AUDIO DURING NOT YOUR TURN!!! +2. make sure audio forwarding doesnt stop when you switch tabs +3. add "reboot your own rover" feature + 1. +4. add faster way for admins to login, like an invisible button in top left or something +5. add discord bot typing thing for when someone requests a replay +6. change replay title for ones requested from discord, something other than "requester driving rover" +7. fix rover request spam queue cheat # relative pipe dreams: diff --git a/server/public/assets/index-CckAnAnd.js b/server/public/assets/index-CboMb02p.js similarity index 99% rename from server/public/assets/index-CckAnAnd.js rename to server/public/assets/index-CboMb02p.js index 1cb36296..8c504c1c 100644 --- a/server/public/assets/index-CckAnAnd.js +++ b/server/public/assets/index-CboMb02p.js @@ -13,7 +13,7 @@ ${w}`}).join(` ---------------------------------------- -`):""};return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("div",{className:"panel-muted text-xs uppercase",children:"Overseer Control"}),l.jsx("div",{className:"flex gap-0.5 text-xs",children:l.jsx("button",{type:"button",onClick: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: ",g.lastGenerationMs!=null?`${g.lastGenerationMs}ms`:"--"]})]}),l.jsxs("details",{className:"surface text-xs text-slate-200",open:!0,children:[l.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Latest Context"}),l.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:z.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:z.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(z.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:z.availableTools,blocked:z.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: ",z.modelInputAt?new Date(z.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 kY({health:t,roster:n=[]}){if(!t)return null;const r=t.replay||{sources:[],readyCount:0,totalCount:0},o=t.snapshots||{rovers:[],rooms:[]},h=g=>n.find(z=>String(z.id)===String(g))?.color||null,f=`${r.readyCount}/${r.totalCount} sources ready`,m=o.rovers.filter(g=>g.stale).length,p=o.rooms.filter(g=>g.stale).length;return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("div",{className:"panel-muted text-xs uppercase",children:"Health"}),l.jsxs("div",{className:"surface space-y-0.5 text-xs text-slate-200",children:[l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{children:"Replay segments"}),l.jsx("span",{className:"text-slate-400",children:f})]}),l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{children:"Rover snapshots"}),l.jsxs("span",{className: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(g=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{className:`${g.type==="rover"?"rounded px-1 py-[1px] border border-transparent":""}`,style:g.type==="rover"?a3(h(g.id),.16):void 0,children:g.label}),l.jsxs("span",{className:g.ready?"text-emerald-300":"text-amber-300",children:[g.recentCount,"/",g.neededCount]})]},`${g.type}:${g.id}`))}),l.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rovers.map(g=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{className:"rounded px-1 py-[1px] border border-transparent",style:a3(h(g.id),.16),children:g.name}),l.jsx("span",{className:g.stale?"text-amber-300":"text-emerald-300",children:g.stale?"stale":"ok"})]},`rover:${g.id}`))}),l.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rooms.map(g=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{children:g.name}),l.jsx("span",{className:g.stale?"text-amber-300":"text-emerald-300",children:g.error?"error":g.stale?"stale":"ok"})]},`room:${g.id}`))})]})}function RY({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 EY=[{key:"open",label:"Open"},{key:"turns",label:"Turns"},{key:"admin",label:"Admin"},{key:"lockdown",label:"Lockdown"}];function TY(){const{session:t,lockRover:n,setMode:r,requestControl:o,setGlobalObjective:h,setAdminReason:f,rebootRover:m,rebootServer:p,setAudioLevels:g,setPrivateSafety:z,llmControl:x,overseerControl:M,adminLogs:y,llmCommentaryState:A,overseerControlState:L}=M8(),H=v.useMemo(()=>t?.roster??[],[t?.roster]),[w,C]=v.useState({}),[j,S]=v.useState({}),[B,N]=v.useState(!1),[Z,R]=v.useState(!1),[E,G]=v.useState(!1),O=t?.health||null,K=t?.globalObjective?.text||"",P=t?.globalObjective?.updatedAt||null,[h1,f1]=v.useState(K),r1=t?.adminReason?.text||"",T=t?.adminReason?.updatedAt||null,[U,I]=v.useState(r1),J=t?.audioLevels||{},[Q,F]=v.useState({hornGain:Number.isFinite(J.hornGain)?J.hornGain:1,ttsGain:Number.isFinite(J.ttsGain)?J.ttsGain:1,forwardGain:Number.isFinite(J.forwardGain)?J.forwardGain:1}),[D,c1]=v.useState({}),i1=t?.role==="admin"||t?.role==="lockdown"||t?.role==="lockdown-admin",n1=t?.role==="lockdown"||t?.role==="lockdown-admin",g1=t?.mode??"open",u1=async(a1,v1)=>{try{await n(a1,v1),C(k1=>({...k1,[a1]:v1}))}catch(k1){alert(k1.message)}},x1=async a1=>{const v1=a1.target.value;try{await r(v1)}catch(k1){alert(k1.message)}},z1=async a1=>{try{await o(a1,{force:!0})}catch(v1){alert(v1.message)}},S1=async a1=>{if(!(!a1?.id||!window.confirm(`Reboot rover "${a1.name||a1.id}" now?`))){S(k1=>({...k1,[a1.id]:!0}));try{await m(a1.id)}catch(k1){alert(k1.message)}finally{S(k1=>({...k1,[a1.id]:!1}))}}},V1=async()=>{if(window.confirm("Reboot the server host now? This will disconnect all users.")){N(!0);try{await p()}catch(v1){alert(v1.message),N(!1)}}},E1=async()=>{if(window.confirm("Clear LLM commentary history now? This resets chat context, bot memory, and rover activity metrics for narration.")){R(!0);try{await x("clearHistory")}catch(v1){alert(v1.message)}finally{R(!1)}}},e2=async()=>{if(window.confirm("Clear Overseer Control history now?")){G(!0);try{await M("clearHistory")}catch(v1){alert(v1.message)}finally{G(!1)}}},C1=async()=>{try{await h(h1)}catch(a1){alert(a1.message)}},y1=async()=>{try{await h(null)}catch(a1){alert(a1.message)}},$1=async()=>{try{await f(U)}catch(a1){alert(a1.message)}},G1=async()=>{try{await f(null)}catch(a1){alert(a1.message)}},p2=a1=>v1=>{const k1=Number(v1.target.value);F(c4=>({...c4||{},[a1]:Number.isFinite(k1)?k1:1}))},s1=async()=>{try{await g(Q)}catch(a1){alert(a1.message)}},o1=async()=>{window.dispatchEvent(new CustomEvent("buttonBox:rewardRunLocalTest",{detail:{rewardName:"Admin Mode",count:1e3,goal:1e3}}))};v.useEffect(()=>{f1(K)},[K]),v.useEffect(()=>{I(r1)},[r1]),v.useEffect(()=>{F({hornGain:Number.isFinite(J.hornGain)?J.hornGain:1,ttsGain:Number.isFinite(J.ttsGain)?J.ttsGain:1,forwardGain:Number.isFinite(J.forwardGain)?J.forwardGain:1})},[J.forwardGain,J.hornGain,J.ttsGain]),v.useEffect(()=>{const a1={};(H||[]).forEach(v1=>{v1?.private?.enabled&&(a1[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(a1)},[H]);const m1=v.useMemo(()=>{const a1={};return H.forEach(v1=>{a1[v1.id]=w[v1.id]??v1.locked}),a1},[H,w]),p1=(a1,v1={})=>{c1(k1=>({...k1||{},[a1]:{...k1?.[a1]||{},...v1||{}}}))},j1=async a1=>{try{const v1=D?.[a1];if(!v1)return;await z(a1,v1)}catch(v1){alert(v1.message)}};return i1?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:g1,onChange:x1,className:"field-input text-sm",children:EY.map(a1=>l.jsx("option",{value:a1.key,children:a1.label},a1.key))})]}),l.jsxs("div",{className:"flex gap-0.5 text-xs",children:[l.jsx("button",{type:"button",onClick:V1,disabled:B,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:B?"Server rebooting...":"Reboot Server"}),l.jsx("button",{type:"button",onClick:o1,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:p2("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:p2("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:p2("forwardGain"),className:"w-full accent-emerald-500"})]}),l.jsx("div",{className:"flex gap-0.5 text-xs",children:l.jsx("button",{type:"button",onClick:s1,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:h1,onChange:a1=>f1(a1.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:C1,className:"button-dark",children:"Set objective"}),l.jsx("button",{type:"button",onClick:y1,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"}),T?l.jsxs("span",{children:["Updated ",new Date(T).toLocaleString()]}):null]}),l.jsx("textarea",{value:U,onChange:a1=>I(a1.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:$1,className:"button-dark",children:"Set reason"}),l.jsx("button",{type:"button",onClick:G1,className:"button-danger",children:"Clear"})]})]}),l.jsx(LY,{roster:H,renderActions:a1=>l.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 text-xs",children:[l.jsx("button",{type:"button",onClick:()=>u1(a1.id,!m1[a1.id]),className:"button-dark",children:m1[a1.id]?"Unlock":"Lock"}),l.jsx("button",{type:"button",onClick:()=>z1(a1.id),className:"button-dark",children:"Force"}),l.jsx("button",{type:"button",onClick:()=>S1(a1),disabled:!!j[a1.id],className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:j[a1.id]?"Rebooting...":"Reboot"}),a1?.private?.enabled?l.jsxs("div",{className:"w-full rounded border border-slate-700/70 p-0.5 space-y-0.5 text-[0.7rem]",children:[l.jsx("div",{className:"text-slate-300",children:"Private safety"}),l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!!D?.[a1.id]?.speedLimitEnabled,disabled:!n1,onChange:v1=>p1(a1.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:!n1,value:D?.[a1.id]?.speedLimitMaxWheelSpeed??250,onChange:v1=>p1(a1.id,{speedLimitMaxWheelSpeed:Number(v1.target.value)||250})}),l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!!D?.[a1.id]?.hardOvercurrentEnabled,disabled:!n1,onChange:v1=>p1(a1.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:!!D?.[a1.id]?.hardBumpEnabled,disabled:!n1,onChange:v1=>p1(a1.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:!!D?.[a1.id]?.cliffEnabled,disabled:!n1,onChange:v1=>p1(a1.id,{cliffEnabled:!!v1.target.checked})}),l.jsx("span",{children:"Cliff safety"})]}),l.jsx("button",{type:"button",disabled:!n1,onClick:()=>j1(a1.id),className:"button-dark disabled:opacity-50",children:"Apply Safety"})]}):null]})}),l.jsx(kY,{health:O,roster:H}),l.jsx(FY,{state:A,onClearHistory:E1,clearingHistory:Z}),l.jsx(NY,{state:L,onClearHistory:e2,clearingHistory:E}),l.jsx(RY,{entries:y})]}):null}const qY=[{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 DY(t){return t.reduce((n,r)=>((n[r.group]||(n[r.group]=[])).push(r),n),{})}function OY(t,n){const r=Number(t);return Number.isFinite(r)?Math.max(0,Math.min(500,r)):n}const mc=5,m8=500,p8=1,pc=100;function _Y(t,n){const r=Number(t);return Number.isFinite(r)?Math.max(mc,Math.min(m8,r)):n}function l7(t,n){const r=Number(t);return Number.isFinite(r)?Math.max(p8,Math.min(pc,r)):n}function UY(t){const r=(l7(t,t)-p8)/(pc-p8),o=m8-r*(m8-mc);return Math.round(o)}function ZY(t){const n=_Y(t,t),r=(m8-n)/(m8-mc),o=p8+r*(pc-p8);return Math.round(o)}function GY(t){const[n,r]=v.useState(null),o=v.useCallback(m=>{ne(!0),r(m)},[]),h=v.useCallback(()=>{ne(!1),r(null)},[]),f=v.useCallback((m,p)=>{if(p.preventDefault(),p.stopPropagation(),p.key==="Escape"){h();return}const g=b8(p.key??"");g&&(t(m,g,p),h())},[h,t]);return v.useEffect(()=>()=>ne(!1),[]),{active:n,startCapture:o,cancel:h,captureFromEvent:f}}function Y5({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 $Y(){const{state:{keymap:t},actions:{updateKeyBinding:n,resetKeyBindings:r}}=y2(),{value:o,save:h}=R1("inputs",L4),f={...L4.keyboard,...o.keyboard??{}},m=v.useMemo(()=>typeof f.tiltSpeed=="number"?l7(f.tiltSpeed,L4.keyboard.tiltSpeed):typeof f.tiltIntervalMs=="number"?l7(ZY(f.tiltIntervalMs),L4.keyboard.tiltSpeed):L4.keyboard.tiltSpeed,[f.tiltIntervalMs,f.tiltSpeed]),p=v.useMemo(()=>DY(qY),[]),{active:g,startCapture:z,cancel:x,captureFromEvent:M}=GY((H,w)=>{n(H,w)}),y=v.useCallback(H=>t?.[H]?.[0]||s8[H]?.[0]||"",[t]),A=v.useCallback((H,w)=>{const C=L4.keyboard,j=OY(w,C[H]);h(S=>({...S??{},keyboard:{...S?.keyboard??C,[H]:j}}))},[h]),L=v.useCallback(H=>{const w=L4.keyboard,C=l7(H,w.tiltSpeed),j=UY(C);h(S=>({...S??{},keyboard:{...S?.keyboard??w,tiltSpeed:C,tiltIntervalMs:j}}))},[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(Y5,{label:"Base speed",description:"Normal driving speed",value:f.baseSpeed,onChange:H=>A("baseSpeed",H)}),l.jsx(Y5,{label:"Turbo speed",description:"Used when holding the boost modifier",value:f.turboSpeed,onChange:H=>A("turboSpeed",H)}),l.jsx(Y5,{label:"Precision speed",description:"Used when holding the precision modifier",value:f.precisionSpeed,onChange:H=>A("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(Y5,{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,w])=>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:w.map(C=>{const j=y(C.id),S=g===C.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:C.label}),l.jsx("p",{className:"text-[0.65rem] text-slate-400",children:N2(j)})]}),l.jsxs("div",{className:"flex items-center gap-0.5",children:[S&&l.jsx("button",{type:"button",onClick:()=>x(),className:"button-danger text-[0.7rem]",children:"Cancel"}),l.jsx("button",{type:"button",onClick:()=>z(C.id),onKeyDown:S?B=>M(C.id,B):void 0,autoFocus:S,className:`${S?"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:S?"Press a key…":"Change"})]})]},C.id)})})]},H))})]})}const W4=new Intl.NumberFormat(void 0,{maximumFractionDigits:2}),YY=[{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"}],Mi=.45,IY=.6;function n8({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 PY(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 QY(t){return t.reduce((n,r)=>((n[r.section]||(n[r.section]=[])).push(r),n),{})}function XY(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 KY(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 JY(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>Mi).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>Mi);return h?{kind:"axis",index:h.index,...r.invertDefaults??{}}:null}function WY(t,n,r){const o=t.buttons??[];for(let f=0;fIY)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 tI(t,n,r){if(!t)return null;if(r.kind==="axis"||r.kind==="axisPair"){const o=JY(t,n,r);if(o)return o}return WY(t,n,r)}function eI(){const t=g$(),{value:n,save:r}=R1("gamepad",L3),[o,h]=v.useState(null),f=v.useRef(null),m=v.useMemo(()=>QY(YY),[]),p=v.useMemo(()=>XY(t.pads,n.activeSignature),[t.pads,n.activeSignature]),g=p?.signature??null,z=v.useMemo(()=>g?n?.profiles?.[g]??n?.defaults?.profile??t3:n?.defaults?.profile??t3,[g,n?.defaults?.profile,n?.profiles]);v.useEffect(()=>{!p||!g||n?.profiles?.[g]||r(w=>{const C=w??L3;if(C.profiles?.[g])return C;const j=C?.defaults?.profile??t3,S=Ns(p,j);return{...C,profiles:{...C.profiles??{},[g]:S}}})},[p,g,n?.profiles,r]),v.useEffect(()=>{f.current=null},[o,g]),v.useEffect(()=>{if(!o||!p)return;if(!f.current){f.current=KY(p);return}const w=tI(p,f.current,o);w&&(r(C=>{const j=C??L3,S=j.profiles?.[g]??j?.defaults?.profile??t3,B={...S,bindings:{...S.bindings??{},[o.id]:{...S.bindings?.[o.id]??{},kind:o.kind,sources:[w]}}};return{...j,profiles:{...j.profiles??{},[g]:B}}}),h(null))},[p,g,o,r]);const x=v.useCallback(w=>{r(C=>({...C??L3,activeSignature:w||null}))},[r]),M=v.useCallback(w=>{r(C=>{const j=C??L3,S=(g&&j.profiles?.[g])??j?.defaults?.profile??t3,B={...S,calibration:{...S.calibration??{},...w}};return g?{...j,profiles:{...j.profiles??{},[g]:B}}:{...j,defaults:{...j.defaults??{},profile:B}}})},[g,r]),y=v.useCallback((w,C)=>{r(j=>{const S=j??L3,B=(g&&S.profiles?.[g])??S?.defaults?.profile??t3,N=C(B.bindings?.[w]??{}),Z={...B,bindings:{...B.bindings??{},[w]:N}};return g?{...S,profiles:{...S.profiles??{},[g]:Z}}:{...S,defaults:{...S.defaults??{},profile:Z}}})},[g,r]),A=v.useCallback(w=>{y(w.id,C=>({...C,sources:[]}))},[y]),L=v.useCallback((w,C)=>{y(w.id,j=>{const S=Array.isArray(j.sources)?[...j.sources]:[];if(!S[0])return j;const B={...S[0]};return C==="invertX"?B.invertX=!B.invertX:C==="invertY"?B.invertY=!B.invertY:B.invert=!B.invert,S[0]=B,{...j,sources:S}})},[y]),H=v.useMemo(()=>p?{outputs:ks(p,z)}:null,[p,z]);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:g??"",onChange:w=>x(w.target.value),className:"flex-1 rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:t.pads.map(w=>l.jsx("option",{value:w.signature,children:w.id||"Unknown controller"},w.signature))}),l.jsx("span",{className:"text-[0.7rem] text-slate-400",children:p?.mapping??"unknown"})]})]}),l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Calibration"}),l.jsxs("div",{className:"space-y-0.5",children:[l.jsx(n8,{label:"Drive deadzone",description:"Ignore small drive stick drift",min:0,max:.6,step:.01,value:z.calibration?.driveDeadzone??.18,onChange:w=>M({driveDeadzone:w})}),l.jsx(n8,{label:"Camera deadzone",description:"Ignore small camera tilt drift",min:0,max:.4,step:.01,value:z.calibration?.cameraDeadzone??.08,onChange:w=>M({cameraDeadzone:w})}),l.jsx(n8,{label:"Aux deadzone",description:"Ignore small trigger noise",min:0,max:.4,step:.01,value:z.calibration?.auxDeadzone??.05,onChange:w=>M({auxDeadzone:w})}),l.jsx(n8,{label:"Side brush scale",description:"Scale side brush output",min:.3,max:1,step:.05,value:z.calibration?.auxSideScale??.55,onChange:w=>M({auxSideScale:w})}),l.jsxs("label",{className:"surface-muted block p-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[l.jsx("span",{className:"font-semibold text-slate-100",children:"Camera mode"}),l.jsxs("select",{value:z.calibration?.cameraMode??"absolute",onChange:w=>M({cameraMode:w.target.value}),className:"rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:[l.jsx("option",{value:"absolute",children:"Absolute"}),l.jsx("option",{value:"velocity",children:"Velocity"})]})]}),l.jsx("p",{className:"text-[0.65rem] text-slate-500",children:"Absolute maps stick to angle; velocity moves over time."})]}),l.jsx(n8,{label:"Camera sensitivity",description:"Velocity mode degrees per second",min:10,max:180,step:5,value:z.calibration?.cameraSensitivity??60,onChange:w=>M({cameraSensitivity:w})})]})]}),l.jsx("div",{className:"space-y-0.5",children:Object.entries(m).map(([w,C])=>l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:w}),l.jsx("div",{className:"space-y-0.5",children:C.map(j=>{const B=z.bindings?.[j.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:j.label}),l.jsx("p",{className:"text-[0.65rem] text-slate-400",children:PY(B)})]}),l.jsxs("div",{className:"flex items-center gap-0.5",children:[j.kind==="axisPair"&&l.jsxs(l.Fragment,{children:[l.jsx("button",{type:"button",disabled:!B,onClick:()=>L(j,"invertX"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert X"}),l.jsx("button",{type:"button",disabled:!B,onClick:()=>L(j,"invertY"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert Y"})]}),j.kind==="axis"&&l.jsx("button",{type:"button",disabled:!B,onClick:()=>L(j),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert"}),l.jsx("button",{type:"button",onClick:()=>A(j),className:"button-dark text-[0.7rem]",children:"Clear"}),l.jsx("button",{type:"button",onClick:()=>h(j),className:`${o?.id===j.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===j.id?"Waiting…":"Capture"})]})]},j.id)})})]},w))}),l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Diagnostics"}),p?l.jsxs("div",{className:"space-y-0.5 text-[0.7rem] text-slate-300",children:[l.jsx("p",{className:"text-slate-400",children:"Raw axes"}),l.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:p.axes.map((w,C)=>l.jsxs("span",{className:"font-mono text-slate-300",children:["A",C,": ",W4.format(w)]},`axis-${C}`))}),l.jsx("p",{className:"text-slate-400",children:"Raw buttons"}),l.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:p.buttons.map((w,C)=>l.jsxs("span",{className:"font-mono text-slate-300",children:["B",C,": ",W4.format(w.value)," ",w.pressed?"●":""]},`btn-${C}`))}),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 cI={drive:"Drive wheels",aux:"Aux motors"};function bi(t){return Number.isFinite(t)?`${Math.round(t*100)}%`:"--"}function aI({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 nI(){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:cI[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:bi(h)})]}),l.jsx(aI,{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:bi(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 eo=v.createContext(null);function co(){const t=v.useContext(eo);if(!t)throw new Error("Tabs components must be used within a provider.");return t}const yi={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"}},Ci={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"},ao="primary";function zc(...t){return t.filter(Boolean).join(" ")}function lI(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 wi(t,n){return`rgba(${t.r}, ${t.g}, ${t.b}, ${n})`}function gc({children:t,defaultTab:n,currentTab:r,onTabChange:o,variant:h=ao}){const[f,m]=v.useState(n??null),[p,g]=v.useState([]),z=v.useCallback(A=>A?(g(L=>L.includes(A)?L:[...L,A]),()=>g(L=>L.includes(A)?L.filter(H=>H!==A):L)):()=>{},[]),x=r??f??p[0]??null;v.useEffect(()=>{n&&r===void 0&&m(n)},[n,r]);const M=v.useCallback(A=>{A&&(r===void 0&&m(A),o&&o(A))},[r,o]),y=v.useMemo(()=>({activeTab:x,setActiveTab:M,registerTab:z,variant:h}),[x,M,z,h]);return l.jsx(eo.Provider,{value:y,children:t})}function xc({children:t,className:n=""}){const r=d1(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=lI(r),h=o?{borderColor:wi(o,.35)}:void 0,f=o?{backgroundImage:`linear-gradient(90deg, rgba(23,23,23,0.96) 0%, rgba(38,38,38,0.94) 58%, ${wi(o,.18)} 100%)`}:void 0;return l.jsx("div",{className:zc("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 J2({id:t,children:n,className:r="",disabled:o=!1,highlight:h="none"}){const{activeTab:f,setActiveTab:m,registerTab:p,variant:g}=co();v.useEffect(()=>p(t),[t,p]);const z=yi[g]??yi[ao],x=f===t,M=Ci[h]||Ci.none,y=zc(z.base,M||(x?z.active:z.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 Mc({children:t,className:n=""}){return l.jsx("div",{className:zc("mt-0 space-y-0.5",n),children:t})}function W2({id:t,children:n,keepMounted:r=!1}){const{activeTab:o}=co(),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 rI(){const{session:t}=M8(),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 Hi=300,Vi=240;function iI(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 sI(t){const r=t.map(o=>iI(o)).join(" ");return r.length>Vi?`${r.slice(0,Vi)}…`:r}function oI(){const t=t4(),[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=(g,z,x)=>{r(M=>{const y=[...M,{id:`${Date.now()}-${Math.random().toString(16).slice(2)}`,direction:g,event:z,payload:sI(x),timestamp:new Date().toLocaleTimeString()}];return y.length>Hi?y.slice(-Hi):y})},f=(g,...z)=>h("in",g,z),m=(g,...z)=>h("out",g,z);t.onAny(f);let p=null;if(typeof t.onAnyOutgoing=="function")t.onAnyOutgoing(m),p=()=>t.offAnyOutgoing(m);else{const g=t.emit.bind(t);t.emit=(z,...x)=>(m(z,...x),g(z,...x)),p=()=>{t.emit=g}}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 uI=[{key:"start",label:"Start OI"},{key:"safe",label:"Safe"},{key:"full",label:"Full"},{key:"passive",label:"Passive"},{key:"dock",label:"Dock"}];function no(){const{state:{roverId:t},actions:{sendOiCommand:n,setSensorStream:r}}=y2(),o=!!t,[h,f]=Ys(),m=t4(),{value:p,save:g}=R1("page",{hudMapDesktop:!1,connectionTransport:"websocket",swapMobileControlColumns:!1,driveMacroBackoffEnabled:!0}),{value:z,save:x}=R1("audio",s2),M=p?.connectionTransport||"websocket",y=!!p?.swapMobileControlColumns,A=typeof p?.driveMacroBackoffEnabled=="boolean"?p.driveMacroBackoffEnabled:!0,L=Number.isFinite(z?.masterVolume)?z.masterVolume:s2.masterVolume,H=Number.isFinite(z?.alertVolume)?z.alertVolume:s2.alertVolume,w=Number.isFinite(z?.roverVolume)?z.roverVolume:s2.roverVolume,C=typeof z?.mainBrushDuckEnabled=="boolean"?z.mainBrushDuckEnabled:typeof z?.autoLevelEnabled=="boolean"?z.autoLevelEnabled:s2.mainBrushDuckEnabled,j=Number.isFinite(z?.mainBrushDuckAmount)?Math.max(0,Math.min(1,z.mainBrushDuckAmount)):s2.mainBrushDuckAmount,S=v.useMemo(()=>[{key:"start",label:"Enable stream",enable:!0},{key:"stop",label:"Disable stream",enable:!1}],[]),B=O=>{t&&r(O)},N=O=>{const K=O.target.value;g(P=>({...P??{},connectionTransport:K})),m?.io?.opts&&(m.io.opts.transports=K==="polling"?["polling"]:["websocket","polling"],m.disconnect(),m.connect())},Z=O=>K=>{const P=Number(K.target.value),h1=Number.isFinite(P)?Math.max(0,Math.min(1,P)):0;x(f1=>({...f1??{},[O]:h1}))},R=O=>{const K=!!O.target.checked;x(P=>({...P??{},mainBrushDuckEnabled:K}))},E=O=>{const K=!!O.target.checked;g(P=>({...P??{},swapMobileControlColumns:K}))},G=O=>{const K=!!O.target.checked;g(P=>({...P??{},driveMacroBackoffEnabled:K}))};return l.jsxs(gc,{defaultTab:"keybindings",children:[l.jsxs(xc,{children:[l.jsx(J2,{id:"keybindings",children:"Keybindings"}),l.jsx(J2,{id:"controller",children:"Controller"}),l.jsx(J2,{id:"page",children:"Page settings"}),l.jsx(J2,{id:"admin",children:"Admin"})]}),l.jsxs(Mc,{children:[l.jsx(W2,{id:"keybindings",children:l.jsx("div",{className:"space-y-0.5",children:l.jsx($Y,{})})}),l.jsx(W2,{id:"controller",children:l.jsx("div",{className:"space-y-0.5",children:l.jsx(eI,{})})}),l.jsx(W2,{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:O=>f(O.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:E}),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:A,onChange:G}),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:Z("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:Z("alertVolume"),className:"w-full accent-emerald-500"})]}),l.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[l.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[l.jsx("span",{children:"Rover audio"}),l.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(w*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:w,onChange:Z("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:C,onChange:R})]}),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(j*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:j,onChange:Z("mainBrushDuckAmount"),className:"w-full accent-emerald-500",disabled:!C})]}),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:N,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(W2,{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:uI.map(O=>l.jsx("button",{type:"button",onClick:()=>n(O.key),disabled:!o,className:"button-dark text-xs disabled:opacity-30",children:O.label},O.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:S.map(O=>l.jsx("button",{type:"button",onClick:()=>B(O.enable),disabled:!o,className:"flex-1 button-dark text-xs disabled:opacity-30",children:O.label},O.key))}),!o&&l.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to toggle streams."})]}),l.jsx(to,{}),l.jsx(nI,{}),l.jsx(TY,{}),l.jsx(rI,{}),l.jsx(oI,{})]})})]})]})}const Si={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 hI(t){return Si[t]||Si.desktop}function lo({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 Ai(t,n){return t.map((r,o)=>typeof r=="string"?l.jsx("span",{children:r},`text-${o}`):r?.action?l.jsx(lo,{actionId:r.action,keymap:n},`pill-${r.action}-${o}`):r?.text?l.jsx("span",{children:r.text},`span-${o}`):null)}function S7(t,n,r){if(typeof t=="string")return t;if(Array.isArray(t?.segments))return Ai(t.segments,n);if(t&&typeof t=="object"){if(typeof t.text=="string")return t.text;if(Array.isArray(t))return Ai(t,n)}return String(t??"")}function dI({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:S7(r,n)},h)})})]}):null}function fI({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:S7(r,n)},h)})})]})}function vI({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:S7(r,{})},`callout-${o}`))})]})}function mI({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(lo,{actionId:r.action,keymap:n})]},r.action))})]})}function pI({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(mI,{group:r,keymap:n},r.id))})]}):null}function zI({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:S7(n,{})},`gamepad-${r}`))})]}):null}function Li({block:t,keymap:n}){if(!t)return null;switch(t.type){case"list":return l.jsx(fI,{block:t,keymap:n});case"callout":return l.jsx(vI,{block:t});case"keyboard":return l.jsx(pI,{block:t,keymap:n});case"gamepad":return l.jsx(zI,{block:t});default:return null}}function ro({layout:t,keymap:n}){const r=v.useMemo(()=>hI(t),[t]),o=n||{},h=r.main||[],f=r.aside||[];return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx(dI,{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(Li,{block:m,keymap:o},m.title||p))}),l.jsx("div",{className:"space-y-0.5",children:f.map((m,p)=>l.jsx(Li,{block:m,keymap:o},m.title||p))})]})]})}function io({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(ro,{layout:t,keymap:r?.keymap||{}})]})}function A7({compact:t=!1}){const n=d1(M=>M.session?.role||null),{setNickname:r}=T2(),{value:o,save:h}=R1("profile",{nickname:""}),[f,m]=v.useState(o.nickname||""),[p,g]=v.useState(!1),z=n!=="spectator";v.useEffect(()=>{m(o.nickname||"")},[o.nickname]);async function x(M){if(M.preventDefault(),!z)return;const y=(f||"").trim().slice(0,32);if(y){g(!0);try{await r(y),h({nickname:y})}catch(A){alert(A.message)}finally{g(!1)}}}return l.jsxs("form",{className:"grid w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-stretch gap-0.5",onSubmit: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:!z}),l.jsx("button",{type:"submit",disabled:!z||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 gI=["kal","rms","slt","ksp","bdl"],xI=Array.from({length:10},(t,n)=>n*10);function L7({hideInput:t=!1,hideSpectatorNotice:n=!1,fillHeight:r=!1,allowSpectatorInput:o=!1,title:h="Chat",nicknameLayout:f="inline"}){const m=d1(n1=>n1.session?.role||null),p=d1(n1=>n1.session?.assignment?.roverId||null),g=d1(n1=>n1.session?.roster??[]),{messages:z,typing:x,sendMessage:M,registerInputRef:y,onInputFocus:A,onInputBlur:L,blurChat:H,setTypingActive:w}=ic(),{value:C,save:j}=R1("tts",{engine:"flite",voice:"rms",pitch:50}),[S,B]=v.useState(""),[N,Z]=v.useState(!1),[R,E]=v.useState(!1),[G,O]=v.useState(()=>C?.engine||"flite"),[K,P]=v.useState(()=>C?.voice||"rms"),[h1,f1]=v.useState(()=>Number.isFinite(C?.pitch)?C.pitch:50),r1=m!=="spectator"||o,T=v.useRef(null),I=!!v.useMemo(()=>g.find(n1=>String(n1.id)===String(p))||null,[p,g])?.audio?.ttsEnabled,J=v.useMemo(()=>z.slice(-200),[z]),Q=v.useMemo(()=>x||[],[x]);v.useEffect(()=>{T.current&&(T.current.scrollTop=T.current.scrollHeight)},[J,Q]),v.useEffect(()=>{const n1=C?.engine||"flite",g1=C?.voice||"rms",u1=Number.isFinite(C?.pitch)?C.pitch:50;G!==n1&&O(n1),K!==g1&&P(g1),h1!==u1&&f1(u1)},[G,h1,C?.engine,C?.pitch,C?.voice,K]),v.useEffect(()=>{E(!!I)},[I]);const F=v.useMemo(()=>!I||!R?null:G==="espeak"?{speak:!0,engine:G,pitch:h1}:{speak:!0,engine:G,voice:K},[G,h1,R,I,K]);async function D(n1){if(n1.preventDefault(),!r1||t)return;const u1=S.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: ",g.lastGenerationMs!=null?`${g.lastGenerationMs}ms`:"--"]})]}),l.jsxs("details",{className:"surface text-xs text-slate-200",open:!0,children:[l.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Latest Context"}),l.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:z.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:z.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(z.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:z.availableTools,blocked:z.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: ",z.modelInputAt?new Date(z.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 kY({health:t,roster:n=[]}){if(!t)return null;const r=t.replay||{sources:[],readyCount:0,totalCount:0},o=t.snapshots||{rovers:[],rooms:[]},h=g=>n.find(z=>String(z.id)===String(g))?.color||null,f=`${r.readyCount}/${r.totalCount} sources ready`,m=o.rovers.filter(g=>g.stale).length,p=o.rooms.filter(g=>g.stale).length;return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("div",{className:"panel-muted text-xs uppercase",children:"Health"}),l.jsxs("div",{className:"surface space-y-0.5 text-xs text-slate-200",children:[l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{children:"Replay segments"}),l.jsx("span",{className:"text-slate-400",children:f})]}),l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{children:"Rover snapshots"}),l.jsxs("span",{className: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(g=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{className:`${g.type==="rover"?"rounded px-1 py-[1px] border border-transparent":""}`,style:g.type==="rover"?a3(h(g.id),.16):void 0,children:g.label}),l.jsxs("span",{className:g.ready?"text-emerald-300":"text-amber-300",children:[g.recentCount,"/",g.neededCount]})]},`${g.type}:${g.id}`))}),l.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rovers.map(g=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{className:"rounded px-1 py-[1px] border border-transparent",style:a3(h(g.id),.16),children:g.name}),l.jsx("span",{className:g.stale?"text-amber-300":"text-emerald-300",children:g.stale?"stale":"ok"})]},`rover:${g.id}`))}),l.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rooms.map(g=>l.jsxs("div",{className:"flex items-center justify-between",children:[l.jsx("span",{children:g.name}),l.jsx("span",{className:g.stale?"text-amber-300":"text-emerald-300",children:g.error?"error":g.stale?"stale":"ok"})]},`room:${g.id}`))})]})}function RY({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 EY=[{key:"open",label:"Open"},{key:"turns",label:"Turns"},{key:"admin",label:"Admin"},{key:"lockdown",label:"Lockdown"}];function TY(){const{session:t,lockRover:n,setMode:r,requestControl:o,setGlobalObjective:h,setAdminReason:f,rebootRover:m,rebootServer:p,setAudioLevels:g,setPrivateSafety:z,llmControl:x,overseerControl:M,adminLogs:y,llmCommentaryState:A,overseerControlState:L}=M8(),H=v.useMemo(()=>t?.roster??[],[t?.roster]),[w,C]=v.useState({}),[j,S]=v.useState({}),[B,N]=v.useState(!1),[Z,R]=v.useState(!1),[E,G]=v.useState(!1),O=t?.health||null,K=t?.globalObjective?.text||"",P=t?.globalObjective?.updatedAt||null,[h1,f1]=v.useState(K),r1=t?.adminReason?.text||"",T=t?.adminReason?.updatedAt||null,[U,I]=v.useState(r1),J=t?.audioLevels||{},[Q,F]=v.useState({hornGain:Number.isFinite(J.hornGain)?J.hornGain:1,ttsGain:Number.isFinite(J.ttsGain)?J.ttsGain:1,forwardGain:Number.isFinite(J.forwardGain)?J.forwardGain:1}),[D,c1]=v.useState({}),i1=t?.role==="admin"||t?.role==="lockdown"||t?.role==="lockdown-admin",n1=t?.role==="lockdown"||t?.role==="lockdown-admin",g1=t?.mode??"open",u1=async(a1,v1)=>{try{await n(a1,v1),C(k1=>({...k1,[a1]:v1}))}catch(k1){alert(k1.message)}},x1=async a1=>{const v1=a1.target.value;try{await r(v1)}catch(k1){alert(k1.message)}},z1=async a1=>{try{await o(a1,{force:!0})}catch(v1){alert(v1.message)}},S1=async a1=>{if(!(!a1?.id||!window.confirm(`Reboot rover "${a1.name||a1.id}" now?`))){S(k1=>({...k1,[a1.id]:!0}));try{await m(a1.id)}catch(k1){alert(k1.message)}finally{S(k1=>({...k1,[a1.id]:!1}))}}},V1=async()=>{if(window.confirm("Reboot the server host now? This will disconnect all users.")){N(!0);try{await p()}catch(v1){alert(v1.message),N(!1)}}},E1=async()=>{if(window.confirm("Clear LLM commentary history now? This resets chat context, bot memory, and rover activity metrics for narration.")){R(!0);try{await x("clearHistory")}catch(v1){alert(v1.message)}finally{R(!1)}}},e2=async()=>{if(window.confirm("Clear Overseer Control history now?")){G(!0);try{await M("clearHistory")}catch(v1){alert(v1.message)}finally{G(!1)}}},C1=async()=>{try{await h(h1)}catch(a1){alert(a1.message)}},y1=async()=>{try{await h(null)}catch(a1){alert(a1.message)}},$1=async()=>{try{await f(U)}catch(a1){alert(a1.message)}},G1=async()=>{try{await f(null)}catch(a1){alert(a1.message)}},p2=a1=>v1=>{const k1=Number(v1.target.value);F(c4=>({...c4||{},[a1]:Number.isFinite(k1)?k1:1}))},s1=async()=>{try{await g(Q)}catch(a1){alert(a1.message)}},o1=async()=>{window.dispatchEvent(new CustomEvent("buttonBox:rewardRunLocalTest",{detail:{rewardName:"Admin Mode",count:1e3,goal:1e3}}))};v.useEffect(()=>{f1(K)},[K]),v.useEffect(()=>{I(r1)},[r1]),v.useEffect(()=>{F({hornGain:Number.isFinite(J.hornGain)?J.hornGain:1,ttsGain:Number.isFinite(J.ttsGain)?J.ttsGain:1,forwardGain:Number.isFinite(J.forwardGain)?J.forwardGain:1})},[J.forwardGain,J.hornGain,J.ttsGain]),v.useEffect(()=>{const a1={};(H||[]).forEach(v1=>{v1?.private?.enabled&&(a1[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(a1)},[H]);const m1=v.useMemo(()=>{const a1={};return H.forEach(v1=>{a1[v1.id]=w[v1.id]??v1.locked}),a1},[H,w]),p1=(a1,v1={})=>{c1(k1=>({...k1||{},[a1]:{...k1?.[a1]||{},...v1||{}}}))},j1=async a1=>{try{const v1=D?.[a1];if(!v1)return;await z(a1,v1)}catch(v1){alert(v1.message)}};return i1?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:g1,onChange:x1,className:"field-input text-sm",children:EY.map(a1=>l.jsx("option",{value:a1.key,children:a1.label},a1.key))})]}),l.jsxs("div",{className:"flex gap-0.5 text-xs",children:[l.jsx("button",{type:"button",onClick:V1,disabled:B,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:B?"Server rebooting...":"Reboot Server"}),l.jsx("button",{type:"button",onClick:o1,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:p2("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:p2("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:p2("forwardGain"),className:"w-full accent-emerald-500"})]}),l.jsx("div",{className:"flex gap-0.5 text-xs",children:l.jsx("button",{type:"button",onClick:s1,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:h1,onChange:a1=>f1(a1.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:C1,className:"button-dark",children:"Set objective"}),l.jsx("button",{type:"button",onClick:y1,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"}),T?l.jsxs("span",{children:["Updated ",new Date(T).toLocaleString()]}):null]}),l.jsx("textarea",{value:U,onChange:a1=>I(a1.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:$1,className:"button-dark",children:"Set reason"}),l.jsx("button",{type:"button",onClick:G1,className:"button-danger",children:"Clear"})]})]}),l.jsx(LY,{roster:H,renderActions:a1=>l.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 text-xs",children:[l.jsx("button",{type:"button",onClick:()=>u1(a1.id,!m1[a1.id]),className:"button-dark",children:m1[a1.id]?"Unlock":"Lock"}),l.jsx("button",{type:"button",onClick:()=>z1(a1.id),className:"button-dark",children:"Force"}),l.jsx("button",{type:"button",onClick:()=>S1(a1),disabled:!!j[a1.id],className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:j[a1.id]?"Rebooting...":"Reboot"}),a1?.private?.enabled?l.jsxs("div",{className:"w-full rounded border border-slate-700/70 p-0.5 space-y-0.5 text-[0.7rem]",children:[l.jsx("div",{className:"text-slate-300",children:"Private safety"}),l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!!D?.[a1.id]?.speedLimitEnabled,disabled:!n1,onChange:v1=>p1(a1.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:!n1,value:D?.[a1.id]?.speedLimitMaxWheelSpeed??250,onChange:v1=>p1(a1.id,{speedLimitMaxWheelSpeed:Number(v1.target.value)||250})}),l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!!D?.[a1.id]?.hardOvercurrentEnabled,disabled:!n1,onChange:v1=>p1(a1.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:!!D?.[a1.id]?.hardBumpEnabled,disabled:!n1,onChange:v1=>p1(a1.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:!!D?.[a1.id]?.cliffEnabled,disabled:!n1,onChange:v1=>p1(a1.id,{cliffEnabled:!!v1.target.checked})}),l.jsx("span",{children:"Cliff safety"})]}),l.jsx("button",{type:"button",disabled:!n1,onClick:()=>j1(a1.id),className:"button-dark disabled:opacity-50",children:"Apply Safety"})]}):null]})}),l.jsx(kY,{health:O,roster:H}),l.jsx(FY,{state:A,onClearHistory:E1,clearingHistory:Z}),l.jsx(NY,{state:L,onClearHistory:e2,clearingHistory:E}),l.jsx(RY,{entries:y})]}):null}const qY=[{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 DY(t){return t.reduce((n,r)=>((n[r.group]||(n[r.group]=[])).push(r),n),{})}function OY(t,n){const r=Number(t);return Number.isFinite(r)?Math.max(0,Math.min(500,r)):n}const mc=5,m8=500,p8=1,pc=100;function _Y(t,n){const r=Number(t);return Number.isFinite(r)?Math.max(mc,Math.min(m8,r)):n}function l7(t,n){const r=Number(t);return Number.isFinite(r)?Math.max(p8,Math.min(pc,r)):n}function UY(t){const r=(l7(t,t)-p8)/(pc-p8),o=m8-r*(m8-mc);return Math.round(o)}function ZY(t){const n=_Y(t,t),r=(m8-n)/(m8-mc),o=p8+r*(pc-p8);return Math.round(o)}function GY(t){const[n,r]=v.useState(null),o=v.useCallback(m=>{ne(!0),r(m)},[]),h=v.useCallback(()=>{ne(!1),r(null)},[]),f=v.useCallback((m,p)=>{if(p.preventDefault(),p.stopPropagation(),p.key==="Escape"){h();return}const g=b8(p.key??"");g&&(t(m,g,p),h())},[h,t]);return v.useEffect(()=>()=>ne(!1),[]),{active:n,startCapture:o,cancel:h,captureFromEvent:f}}function Y5({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 $Y(){const{state:{keymap:t},actions:{updateKeyBinding:n,resetKeyBindings:r}}=y2(),{value:o,save:h}=R1("inputs",L4),f={...L4.keyboard,...o.keyboard??{}},m=v.useMemo(()=>typeof f.tiltSpeed=="number"?l7(f.tiltSpeed,L4.keyboard.tiltSpeed):typeof f.tiltIntervalMs=="number"?l7(ZY(f.tiltIntervalMs),L4.keyboard.tiltSpeed):L4.keyboard.tiltSpeed,[f.tiltIntervalMs,f.tiltSpeed]),p=v.useMemo(()=>DY(qY),[]),{active:g,startCapture:z,cancel:x,captureFromEvent:M}=GY((H,w)=>{n(H,w)}),y=v.useCallback(H=>t?.[H]?.[0]||s8[H]?.[0]||"",[t]),A=v.useCallback((H,w)=>{const C=L4.keyboard,j=OY(w,C[H]);h(S=>({...S??{},keyboard:{...S?.keyboard??C,[H]:j}}))},[h]),L=v.useCallback(H=>{const w=L4.keyboard,C=l7(H,w.tiltSpeed),j=UY(C);h(S=>({...S??{},keyboard:{...S?.keyboard??w,tiltSpeed:C,tiltIntervalMs:j}}))},[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(Y5,{label:"Base speed",description:"Normal driving speed",value:f.baseSpeed,onChange:H=>A("baseSpeed",H)}),l.jsx(Y5,{label:"Turbo speed",description:"Used when holding the boost modifier",value:f.turboSpeed,onChange:H=>A("turboSpeed",H)}),l.jsx(Y5,{label:"Precision speed",description:"Used when holding the precision modifier",value:f.precisionSpeed,onChange:H=>A("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(Y5,{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,w])=>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:w.map(C=>{const j=y(C.id),S=g===C.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:C.label}),l.jsx("p",{className:"text-[0.65rem] text-slate-400",children:N2(j)})]}),l.jsxs("div",{className:"flex items-center gap-0.5",children:[S&&l.jsx("button",{type:"button",onClick:()=>x(),className:"button-danger text-[0.7rem]",children:"Cancel"}),l.jsx("button",{type:"button",onClick:()=>z(C.id),onKeyDown:S?B=>M(C.id,B):void 0,autoFocus:S,className:`${S?"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:S?"Press a key…":"Change"})]})]},C.id)})})]},H))})]})}const W4=new Intl.NumberFormat(void 0,{maximumFractionDigits:2}),YY=[{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"}],Mi=.45,IY=.6;function n8({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 PY(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 QY(t){return t.reduce((n,r)=>((n[r.section]||(n[r.section]=[])).push(r),n),{})}function XY(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 KY(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 JY(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>Mi).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>Mi);return h?{kind:"axis",index:h.index,...r.invertDefaults??{}}:null}function WY(t,n,r){const o=t.buttons??[];for(let f=0;fIY)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 tI(t,n,r){if(!t)return null;if(r.kind==="axis"||r.kind==="axisPair"){const o=JY(t,n,r);if(o)return o}return WY(t,n,r)}function eI(){const t=g$(),{value:n,save:r}=R1("gamepad",L3),[o,h]=v.useState(null),f=v.useRef(null),m=v.useMemo(()=>QY(YY),[]),p=v.useMemo(()=>XY(t.pads,n.activeSignature),[t.pads,n.activeSignature]),g=p?.signature??null,z=v.useMemo(()=>g?n?.profiles?.[g]??n?.defaults?.profile??t3:n?.defaults?.profile??t3,[g,n?.defaults?.profile,n?.profiles]);v.useEffect(()=>{!p||!g||n?.profiles?.[g]||r(w=>{const C=w??L3;if(C.profiles?.[g])return C;const j=C?.defaults?.profile??t3,S=Ns(p,j);return{...C,profiles:{...C.profiles??{},[g]:S}}})},[p,g,n?.profiles,r]),v.useEffect(()=>{f.current=null},[o,g]),v.useEffect(()=>{if(!o||!p)return;if(!f.current){f.current=KY(p);return}const w=tI(p,f.current,o);w&&(r(C=>{const j=C??L3,S=j.profiles?.[g]??j?.defaults?.profile??t3,B={...S,bindings:{...S.bindings??{},[o.id]:{...S.bindings?.[o.id]??{},kind:o.kind,sources:[w]}}};return{...j,profiles:{...j.profiles??{},[g]:B}}}),h(null))},[p,g,o,r]);const x=v.useCallback(w=>{r(C=>({...C??L3,activeSignature:w||null}))},[r]),M=v.useCallback(w=>{r(C=>{const j=C??L3,S=(g&&j.profiles?.[g])??j?.defaults?.profile??t3,B={...S,calibration:{...S.calibration??{},...w}};return g?{...j,profiles:{...j.profiles??{},[g]:B}}:{...j,defaults:{...j.defaults??{},profile:B}}})},[g,r]),y=v.useCallback((w,C)=>{r(j=>{const S=j??L3,B=(g&&S.profiles?.[g])??S?.defaults?.profile??t3,N=C(B.bindings?.[w]??{}),Z={...B,bindings:{...B.bindings??{},[w]:N}};return g?{...S,profiles:{...S.profiles??{},[g]:Z}}:{...S,defaults:{...S.defaults??{},profile:Z}}})},[g,r]),A=v.useCallback(w=>{y(w.id,C=>({...C,sources:[]}))},[y]),L=v.useCallback((w,C)=>{y(w.id,j=>{const S=Array.isArray(j.sources)?[...j.sources]:[];if(!S[0])return j;const B={...S[0]};return C==="invertX"?B.invertX=!B.invertX:C==="invertY"?B.invertY=!B.invertY:B.invert=!B.invert,S[0]=B,{...j,sources:S}})},[y]),H=v.useMemo(()=>p?{outputs:ks(p,z)}:null,[p,z]);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:g??"",onChange:w=>x(w.target.value),className:"flex-1 rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:t.pads.map(w=>l.jsx("option",{value:w.signature,children:w.id||"Unknown controller"},w.signature))}),l.jsx("span",{className:"text-[0.7rem] text-slate-400",children:p?.mapping??"unknown"})]})]}),l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Calibration"}),l.jsxs("div",{className:"space-y-0.5",children:[l.jsx(n8,{label:"Drive deadzone",description:"Ignore small drive stick drift",min:0,max:.6,step:.01,value:z.calibration?.driveDeadzone??.18,onChange:w=>M({driveDeadzone:w})}),l.jsx(n8,{label:"Camera deadzone",description:"Ignore small camera tilt drift",min:0,max:.4,step:.01,value:z.calibration?.cameraDeadzone??.08,onChange:w=>M({cameraDeadzone:w})}),l.jsx(n8,{label:"Aux deadzone",description:"Ignore small trigger noise",min:0,max:.4,step:.01,value:z.calibration?.auxDeadzone??.05,onChange:w=>M({auxDeadzone:w})}),l.jsx(n8,{label:"Side brush scale",description:"Scale side brush output",min:.3,max:1,step:.05,value:z.calibration?.auxSideScale??.55,onChange:w=>M({auxSideScale:w})}),l.jsxs("label",{className:"surface-muted block p-0.5",children:[l.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-300",children:[l.jsx("span",{className:"font-semibold text-slate-100",children:"Camera mode"}),l.jsxs("select",{value:z.calibration?.cameraMode??"absolute",onChange:w=>M({cameraMode:w.target.value}),className:"rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-[0.75rem] text-slate-100",children:[l.jsx("option",{value:"absolute",children:"Absolute"}),l.jsx("option",{value:"velocity",children:"Velocity"})]})]}),l.jsx("p",{className:"text-[0.65rem] text-slate-500",children:"Absolute maps stick to angle; velocity moves over time."})]}),l.jsx(n8,{label:"Camera sensitivity",description:"Velocity mode degrees per second",min:10,max:180,step:5,value:z.calibration?.cameraSensitivity??60,onChange:w=>M({cameraSensitivity:w})})]})]}),l.jsx("div",{className:"space-y-0.5",children:Object.entries(m).map(([w,C])=>l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:w}),l.jsx("div",{className:"space-y-0.5",children:C.map(j=>{const B=z.bindings?.[j.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:j.label}),l.jsx("p",{className:"text-[0.65rem] text-slate-400",children:PY(B)})]}),l.jsxs("div",{className:"flex items-center gap-0.5",children:[j.kind==="axisPair"&&l.jsxs(l.Fragment,{children:[l.jsx("button",{type:"button",disabled:!B,onClick:()=>L(j,"invertX"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert X"}),l.jsx("button",{type:"button",disabled:!B,onClick:()=>L(j,"invertY"),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert Y"})]}),j.kind==="axis"&&l.jsx("button",{type:"button",disabled:!B,onClick:()=>L(j),className:"button-dark text-[0.7rem] font-medium disabled:opacity-50",children:"Invert"}),l.jsx("button",{type:"button",onClick:()=>A(j),className:"button-dark text-[0.7rem]",children:"Clear"}),l.jsx("button",{type:"button",onClick:()=>h(j),className:`${o?.id===j.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===j.id?"Waiting…":"Capture"})]})]},j.id)})})]},w))}),l.jsxs("div",{className:"space-y-0.5 surface",children:[l.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"Diagnostics"}),p?l.jsxs("div",{className:"space-y-0.5 text-[0.7rem] text-slate-300",children:[l.jsx("p",{className:"text-slate-400",children:"Raw axes"}),l.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:p.axes.map((w,C)=>l.jsxs("span",{className:"font-mono text-slate-300",children:["A",C,": ",W4.format(w)]},`axis-${C}`))}),l.jsx("p",{className:"text-slate-400",children:"Raw buttons"}),l.jsx("div",{className:"grid grid-cols-2 gap-0.5",children:p.buttons.map((w,C)=>l.jsxs("span",{className:"font-mono text-slate-300",children:["B",C,": ",W4.format(w.value)," ",w.pressed?"●":""]},`btn-${C}`))}),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 cI={drive:"Drive wheels",aux:"Aux motors"};function bi(t){return Number.isFinite(t)?`${Math.round(t*100)}%`:"--"}function aI({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 nI(){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:cI[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:bi(h)})]}),l.jsx(aI,{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:bi(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 eo=v.createContext(null);function co(){const t=v.useContext(eo);if(!t)throw new Error("Tabs components must be used within a provider.");return t}const yi={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"}},Ci={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"},ao="primary";function zc(...t){return t.filter(Boolean).join(" ")}function lI(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 wi(t,n){return`rgba(${t.r}, ${t.g}, ${t.b}, ${n})`}function gc({children:t,defaultTab:n,currentTab:r,onTabChange:o,variant:h=ao}){const[f,m]=v.useState(n??null),[p,g]=v.useState([]),z=v.useCallback(A=>A?(g(L=>L.includes(A)?L:[...L,A]),()=>g(L=>L.includes(A)?L.filter(H=>H!==A):L)):()=>{},[]),x=r??f??p[0]??null;v.useEffect(()=>{n&&r===void 0&&m(n)},[n,r]);const M=v.useCallback(A=>{A&&(r===void 0&&m(A),o&&o(A))},[r,o]),y=v.useMemo(()=>({activeTab:x,setActiveTab:M,registerTab:z,variant:h}),[x,M,z,h]);return l.jsx(eo.Provider,{value:y,children:t})}function xc({children:t,className:n=""}){const r=d1(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=lI(r),h=o?{borderColor:wi(o,.35)}:void 0,f=o?{backgroundImage:`linear-gradient(90deg, rgba(23,23,23,0.96) 0%, rgba(38,38,38,0.94) 58%, ${wi(o,.18)} 100%)`}:void 0;return l.jsx("div",{className:zc("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 J2({id:t,children:n,className:r="",disabled:o=!1,highlight:h="none"}){const{activeTab:f,setActiveTab:m,registerTab:p,variant:g}=co();v.useEffect(()=>p(t),[t,p]);const z=yi[g]??yi[ao],x=f===t,M=Ci[h]||Ci.none,y=zc(z.base,M||(x?z.active:z.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 Mc({children:t,className:n=""}){return l.jsx("div",{className:zc("mt-0 space-y-0.5",n),children:t})}function W2({id:t,children:n,keepMounted:r=!1}){const{activeTab:o}=co(),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 rI(){const{session:t}=M8(),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 Hi=300,Vi=240;function iI(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 sI(t){const r=t.map(o=>iI(o)).join(" ");return r.length>Vi?`${r.slice(0,Vi)}…`:r}function oI(){const t=t4(),[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=(g,z,x)=>{r(M=>{const y=[...M,{id:`${Date.now()}-${Math.random().toString(16).slice(2)}`,direction:g,event:z,payload:sI(x),timestamp:new Date().toLocaleTimeString()}];return y.length>Hi?y.slice(-Hi):y})},f=(g,...z)=>h("in",g,z),m=(g,...z)=>h("out",g,z);t.onAny(f);let p=null;if(typeof t.onAnyOutgoing=="function")t.onAnyOutgoing(m),p=()=>t.offAnyOutgoing(m);else{const g=t.emit.bind(t);t.emit=(z,...x)=>(m(z,...x),g(z,...x)),p=()=>{t.emit=g}}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 uI=[{key:"start",label:"Start OI"},{key:"safe",label:"Safe"},{key:"full",label:"Full"},{key:"passive",label:"Passive"},{key:"dock",label:"Dock"}];function no(){const{state:{roverId:t},actions:{sendOiCommand:n,setSensorStream:r}}=y2(),o=!!t,[h,f]=Ys(),m=t4(),{value:p,save:g}=R1("page",{hudMapDesktop:!1,connectionTransport:"websocket",swapMobileControlColumns:!1,driveMacroBackoffEnabled:!0}),{value:z,save:x}=R1("audio",s2),M=p?.connectionTransport||"websocket",y=!!p?.swapMobileControlColumns,A=typeof p?.driveMacroBackoffEnabled=="boolean"?p.driveMacroBackoffEnabled:!0,L=Number.isFinite(z?.masterVolume)?z.masterVolume:s2.masterVolume,H=Number.isFinite(z?.alertVolume)?z.alertVolume:s2.alertVolume,w=Number.isFinite(z?.roverVolume)?z.roverVolume:s2.roverVolume,C=typeof z?.mainBrushDuckEnabled=="boolean"?z.mainBrushDuckEnabled:typeof z?.autoLevelEnabled=="boolean"?z.autoLevelEnabled:s2.mainBrushDuckEnabled,j=Number.isFinite(z?.mainBrushDuckAmount)?Math.max(0,Math.min(1,z.mainBrushDuckAmount)):s2.mainBrushDuckAmount,S=v.useMemo(()=>[{key:"start",label:"Enable stream",enable:!0},{key:"stop",label:"Disable stream",enable:!1}],[]),B=O=>{t&&r(O)},N=O=>{const K=O.target.value;g(P=>({...P??{},connectionTransport:K})),m?.io?.opts&&(m.io.opts.transports=K==="polling"?["polling"]:["websocket","polling"],m.disconnect(),m.connect())},Z=O=>K=>{const P=Number(K.target.value),h1=Number.isFinite(P)?Math.max(0,Math.min(1,P)):0;x(f1=>({...f1??{},[O]:h1}))},R=O=>{const K=!!O.target.checked;x(P=>({...P??{},mainBrushDuckEnabled:K}))},E=O=>{const K=!!O.target.checked;g(P=>({...P??{},swapMobileControlColumns:K}))},G=O=>{const K=!!O.target.checked;g(P=>({...P??{},driveMacroBackoffEnabled:K}))};return l.jsxs(gc,{defaultTab:"keybindings",children:[l.jsxs(xc,{children:[l.jsx(J2,{id:"keybindings",children:"Keybindings"}),l.jsx(J2,{id:"controller",children:"Controller"}),l.jsx(J2,{id:"page",children:"Page settings"}),l.jsx(J2,{id:"admin",children:"Admin"})]}),l.jsxs(Mc,{children:[l.jsx(W2,{id:"keybindings",children:l.jsx("div",{className:"space-y-0.5",children:l.jsx($Y,{})})}),l.jsx(W2,{id:"controller",children:l.jsx("div",{className:"space-y-0.5",children:l.jsx(eI,{})})}),l.jsx(W2,{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:O=>f(O.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:E}),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:A,onChange:G}),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:Z("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:Z("alertVolume"),className:"w-full accent-emerald-500"})]}),l.jsxs("label",{className:"grid gap-0.5 text-slate-200",children:[l.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[l.jsx("span",{children:"Rover audio"}),l.jsxs("span",{className:"text-xs text-slate-400",children:[Math.round(w*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:w,onChange:Z("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:C,onChange:R})]}),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(j*100),"%"]})]}),l.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:j,onChange:Z("mainBrushDuckAmount"),className:"w-full accent-emerald-500",disabled:!C})]}),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:N,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(W2,{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:uI.map(O=>l.jsx("button",{type:"button",onClick:()=>n(O.key),disabled:!o,className:"button-dark text-xs disabled:opacity-30",children:O.label},O.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:S.map(O=>l.jsx("button",{type:"button",onClick:()=>B(O.enable),disabled:!o,className:"flex-1 button-dark text-xs disabled:opacity-30",children:O.label},O.key))}),!o&&l.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to toggle streams."})]}),l.jsx(to,{}),l.jsx(nI,{}),l.jsx(TY,{}),l.jsx(rI,{}),l.jsx(oI,{})]})})]})]})}const Si={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 hI(t){return Si[t]||Si.desktop}function lo({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 Ai(t,n){return t.map((r,o)=>typeof r=="string"?l.jsx("span",{children:r},`text-${o}`):r?.action?l.jsx(lo,{actionId:r.action,keymap:n},`pill-${r.action}-${o}`):r?.text?l.jsx("span",{children:r.text},`span-${o}`):null)}function S7(t,n,r){if(typeof t=="string")return t;if(Array.isArray(t?.segments))return Ai(t.segments,n);if(t&&typeof t=="object"){if(typeof t.text=="string")return t.text;if(Array.isArray(t))return Ai(t,n)}return String(t??"")}function dI({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:S7(r,n)},h)})})]}):null}function fI({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:S7(r,n)},h)})})]})}function vI({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:S7(r,{})},`callout-${o}`))})]})}function mI({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(lo,{actionId:r.action,keymap:n})]},r.action))})]})}function pI({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(mI,{group:r,keymap:n},r.id))})]}):null}function zI({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:S7(n,{})},`gamepad-${r}`))})]}):null}function Li({block:t,keymap:n}){if(!t)return null;switch(t.type){case"list":return l.jsx(fI,{block:t,keymap:n});case"callout":return l.jsx(vI,{block:t});case"keyboard":return l.jsx(pI,{block:t,keymap:n});case"gamepad":return l.jsx(zI,{block:t});default:return null}}function ro({layout:t,keymap:n}){const r=v.useMemo(()=>hI(t),[t]),o=n||{},h=r.main||[],f=r.aside||[];return l.jsxs("div",{className:"space-y-0.5",children:[l.jsx(dI,{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(Li,{block:m,keymap:o},m.title||p))}),l.jsx("div",{className:"space-y-0.5",children:f.map((m,p)=>l.jsx(Li,{block:m,keymap:o},m.title||p))})]})]})}function io({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(ro,{layout:t,keymap:r?.keymap||{}})]})}function A7({compact:t=!1}){const n=d1(M=>M.session?.role||null),{setNickname:r}=T2(),{value:o,save:h}=R1("profile",{nickname:""}),[f,m]=v.useState(o.nickname||""),[p,g]=v.useState(!1),z=n!=="spectator";v.useEffect(()=>{m(o.nickname||"")},[o.nickname]);async function x(M){if(M.preventDefault(),!z)return;const y=(f||"").trim().slice(0,32);if(y){g(!0);try{await r(y),h({nickname:y})}catch(A){alert(A.message)}finally{g(!1)}}}return l.jsxs("form",{className:"grid w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-stretch gap-0.5",onSubmit: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:!z}),l.jsx("button",{type:"submit",disabled:!z||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 gI=["kal","rms","slt","ksp","bdl"],xI=Array.from({length:10},(t,n)=>n*10);function L7({hideInput:t=!1,hideSpectatorNotice:n=!1,fillHeight:r=!1,allowSpectatorInput:o=!1,title:h="Chat and TTS",nicknameLayout:f="inline"}){const m=d1(n1=>n1.session?.role||null),p=d1(n1=>n1.session?.assignment?.roverId||null),g=d1(n1=>n1.session?.roster??[]),{messages:z,typing:x,sendMessage:M,registerInputRef:y,onInputFocus:A,onInputBlur:L,blurChat:H,setTypingActive:w}=ic(),{value:C,save:j}=R1("tts",{engine:"flite",voice:"rms",pitch:50}),[S,B]=v.useState(""),[N,Z]=v.useState(!1),[R,E]=v.useState(!1),[G,O]=v.useState(()=>C?.engine||"flite"),[K,P]=v.useState(()=>C?.voice||"rms"),[h1,f1]=v.useState(()=>Number.isFinite(C?.pitch)?C.pitch:50),r1=m!=="spectator"||o,T=v.useRef(null),I=!!v.useMemo(()=>g.find(n1=>String(n1.id)===String(p))||null,[p,g])?.audio?.ttsEnabled,J=v.useMemo(()=>z.slice(-200),[z]),Q=v.useMemo(()=>x||[],[x]);v.useEffect(()=>{T.current&&(T.current.scrollTop=T.current.scrollHeight)},[J,Q]),v.useEffect(()=>{const n1=C?.engine||"flite",g1=C?.voice||"rms",u1=Number.isFinite(C?.pitch)?C.pitch:50;G!==n1&&O(n1),K!==g1&&P(g1),h1!==u1&&f1(u1)},[G,h1,C?.engine,C?.pitch,C?.voice,K]),v.useEffect(()=>{E(!!I)},[I]);const F=v.useMemo(()=>!I||!R?null:G==="espeak"?{speak:!0,engine:G,pitch:h1}:{speak:!0,engine:G,voice:K},[G,h1,R,I,K]);async function D(n1){if(n1.preventDefault(),!r1||t)return;const u1=S.replace(/\\n/g,` `).trim();if(u1){Z(!0);try{await M(u1,F),B(""),H(),w(!1)}catch(x1){alert(x1.message)}finally{Z(!1)}}}const c1=r?"flex-1 min-h-0 overflow-y-auto":"h-48 overflow-y-auto",i1=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 ${c1}`,ref:T,children:[J.length===0&&Q.length===0?l.jsx("p",{className:"text-sm text-slate-500",children:"No messages yet."}):J.map(n1=>l.jsx(nc,{message:n1},n1.id)),Q.map(n1=>l.jsx(ys,{message:n1},`typing-${n1.typingId||n1.id}`))]}),!t&&l.jsxs("form",{className:`flex items-stretch gap-0.5 ${i1?"flex-col":"flex-wrap"}`,onSubmit:D,children:[l.jsx("div",{className:i1?"w-full":"w-[9rem] sm:w-[10rem] shrink-0",children:l.jsx(A7,{compact:!0})}),l.jsx("input",{className:`field-input ${i1?"w-full min-w-0":"flex-1 min-w-[10rem]"}`,value:S,onChange:n1=>{const g1=n1.target.value;B(g1),w(!!g1.trim())},onFocus:n1=>{A(n1),w(!!S.trim())},onBlur:n1=>{L(n1),w(!1)},onKeyDown:n1=>{n1.key==="Enter"&&!S.trim()&&(n1.preventDefault(),H(),w(!1))},ref:n1=>y(n1,{target:"panel"}),placeholder:r1?"Type a message…":n?"":"Spectators cannot chat",disabled:!r1}),I&&l.jsxs("div",{className:`flex flex-wrap items-center gap-0.5 ${i1?"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:R,onChange:n1=>E(n1.target.checked),className:"accent-cyan-500"}),l.jsx("span",{children:"Speak"})]}),l.jsxs("select",{value:G,onChange:n1=>{const g1=n1.target.value;O(g1),j(u1=>({...u1||{},engine:g1}))},className:"field-input text-xs",children:[l.jsx("option",{value:"flite",children:"flite"}),l.jsx("option",{value:"espeak",children:"espeak"})]}),G==="flite"?l.jsx("select",{value:K,onChange:n1=>{const g1=n1.target.value;P(g1),j(u1=>({...u1||{},voice:g1}))},className:"field-input text-xs",children:gI.map(n1=>l.jsx("option",{value:n1,children:n1},n1))}):l.jsx("select",{value:h1,onChange:n1=>{const g1=Number(n1.target.value);f1(g1),j(u1=>({...u1||{},pitch:g1}))},className:"field-input text-xs",children:xI.map(n1=>l.jsxs("option",{value:n1,children:["pitch ",n1]},n1))})]}),l.jsx("button",{type:"submit",disabled:!r1||N,className:`button-dark disabled:opacity-50 ${i1?"w-full h-8":"h-full self-stretch"}`,children:N?"...":"Send"})]})]})}function bc({className:t=""}){const n=d1(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(C7,{id:r?.id||r?.key||r?.service||null},`${r?.id||r?.label||"social"}-${r?.url||""}`))}):null}function MI(){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(bc,{className:"flex-1 min-h-0"})})}function ge(...t){return t.filter(Boolean).join(" ")}function bI(t){const n=t?.batteryState?.percentDisplay;return n==null?"--":`${n}%`}function yI(t){return t?.batteryState?t.batteryState.urgentActive?"text-red-400":t.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function CI(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 wI(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 B7({title:t="Rovers"}){const n=d1(C=>C.session?.role||null),r=d1(C=>C.session?.roster??[]),o=d1(C=>C.session?.turnQueues??{}),h=d1(C=>C.session?.users??[]),f=d1(C=>C.session?.socketId||null),{requestControl:m}=T2(),[p,g]=v.useState({}),[z,x]=v.useState(()=>Date.now()),M=v.useMemo(()=>n&&n!=="spectator",[n]),y=v.useMemo(()=>n==="admin"||n==="lockdown"||n==="lockdown-admin",[n]),A=v.useMemo(()=>Object.values(o||{}).some(C=>C?.deadline||C?.idleDeadline),[o]);v.useEffect(()=>{if(!A)return;const C=setInterval(()=>{x(Date.now())},1e3);return()=>clearInterval(C)},[A]);const L=v.useMemo(()=>{const C=new Set(r.map(S=>String(S.id))),j=Object.keys(o||{}).filter(S=>!C.has(String(S))).map(S=>({id:S,name:S,locked:!1,batteryState:null}));return[...r,...j]},[r,o]);async function H(C){if(C){g(j=>({...j,[C]:!0}));try{await m(C)}catch(j){alert(j.message)}finally{g(j=>({...j,[C]:!1}))}}}const w=C=>h.find(j=>j.socketId===C)||{socketId:C,nickname:null,role:null};return l.jsx(u2,{title:t,bodyClassName:"space-y-0.5 text-sm",children:L.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:L.map(C=>{const j=String(C.id),S=o?.[j]||null,B=S?.queue||[],N=S?.idleDeadline||S?.deadline||null,Z=N&&N>z?Math.ceil((N-z)/1e3):N?0:null,R=S?.current||null,E=R?B.findIndex(J=>J===R):-1,G=B.length>1?E>=0?B[(E+1)%B.length]:B[0]:null,O=!!(f&&R&&R===f),K=!!(f&&G&&G===f),P=Z!=null&&(O||K),h1=!!C.locked,f1=h1&&!y,r1=!!(C?.private?.enabled&&C?.private?.open),T=C.lockReason?`locked: ${C.lockReason}`:"locked",U=h1?T:p[j]?"...":"request",I=M&&!f1&&!p[j];return l.jsxs("li",{className:ge("surface flex flex-wrap items-start justify-between gap-0.5",I&&"cursor-pointer",h1?"bg-red-900/40":r1?"bg-amber-700/35 border border-amber-200/30":null),onClick:()=>{I&&H(C.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(C.color,.16),children:C.name}),C.description?l.jsx("span",{className:"min-w-0 flex-1 truncate text-[0.7rem] text-slate-400",children:C.description}):null]}),P?l.jsx("span",{className:"rounded bg-slate-800 px-1 text-[0.7rem] text-slate-200",children:O?`${Z}s left`:`Your turn in ${Z}s`}):null]}),l.jsx("span",{className:ge("text-[0.75rem] font-semibold",yI(C)),children:bI(C)})]}),B.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:B.map((J,Q)=>{const F=w(J),D=J===R,c1=!!(G&&J===G&&!D),i1=D?"bg-sky-600 text-white ring-2 ring-amber-300":c1?"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] ${i1}`,children:[l.jsx("span",{className:`${CI(F.role)} font-semibold`,children:wI(F,f)}),D&&l.jsx("span",{className:"text-[0.65rem] text-slate-200",children:"now"}),c1&&l.jsx("span",{className:"text-[0.65rem] text-emerald-100",children:"next"})]},`${j}-${J}-${Q}`)})})]}),M?l.jsx("button",{type:"button",onClick:J=>{J.stopPropagation(),H(C.id)},disabled:p[j]||f1,className:ge("button-dark disabled:opacity-40",h1&&"bg-red-600/70 text-white hover:bg-red-600"),children:U}):null]},C.id)})})})}function HI(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 VI(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 p7({hideNicknameForm:t=!1,hideHeader:n=!1,className:r="",fillHeight:o=!1,compact:h=!1}){const f=d1(M=>M.session?.role||null),m=d1(M=>M.session?.users??[]),p=d1(M=>M.session?.socketId||null),g=f!=="spectator",z=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(A7,{compact:h})})}),l.jsx(bc,{})]}),!g&&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:z.length===0?l.jsx("p",{className:"text-sm text-slate-500",children:"Waiting for users…"}):z.map(M=>l.jsx("span",{className:`rounded bg-slate-800/80 px-1 py-0.25 text-[0.7rem] ${HI(M.role)}`,children:VI(M,p)},M.socketId))})})]})}const SI=/^cu_[a-f0-9]{32}$/,e0="field-input w-full max-w-sm text-left focus:ring-emerald-500",y8="mx-auto w-full max-w-xl flex justify-center",X6="mx-auto flex w-full max-w-md flex-col items-center space-y-0.5 text-center";function AI(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 Bi=8*1024*1024,W3=16e3,LI={iceServers:[{urls:"stun:stun.l.google.com:19302"}],bundlePolicy:"max-bundle",rtcpMuxPolicy:"require"};function ji(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 Fi(t){if(!t)return!1;const n=t.connectionState,r=t.iceConnectionState;return n==="connected"||r==="connected"||r==="completed"}function NI(t,n=1e4){return new Promise((r,o)=>{if(!t){o(new Error("Peer connection missing"));return}if(Fi(t)){r();return}const h=setTimeout(()=>{m(),o(new Error("Peer connection timeout"))},n),f=()=>{Fi(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 kI(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 RI(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(g=>g.track?.kind==="audio");for(const g of p){const z=await g.getStats();for(const x of z.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 EI(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 TI(t){const n=new Int16Array(t.length);for(let r=0;r{for(let x=0;xx&&n?.[x]||null,[n,x]),c1=!!(D&&D.state!=="offline"&&!D.error),i1=D?.source==="upload"&&D?.state==="playing",n1=D?.source==="mic-whip"&&(D?.state==="starting"||D?.state==="playing"),g1=!!(!C&&G.current&&(H||F)),u1=!C&&(j==="live"||j==="starting"),x1=C&&B==="recording",z1=C&&B==="sending",S1=N2(p?.keymap?.micPtt?.[0])||"M",V1=v.useCallback(o1=>{const m1=o1==="clip"?"clip":"live";z(p1=>({...p1||{},pttMode:m1}))},[z]),E1=async()=>{if(!x){R("Take control of your rover first.");return}if(!M){R("Select an audio file first.");return}if(M.size>Bi){R(`File too large (max ${Bi} bytes).`);return}L(!0),R("");try{const o1=await M.arrayBuffer(),m1=ji(new Uint8Array(o1));await r?.({roverId:x,name:M.name,mime:M.type||"",dataBase64:m1}),R("Upload playback started.")}catch(o1){R(o1?.message||"Failed to play upload.")}finally{L(!1)}},e2=async()=>{if(!x){R("Take control of your rover first.");return}L(!0),R("");try{await o?.(x),R("Upload playback stopped.")}catch(o1){R(o1?.message||"Failed to stop upload.")}finally{L(!1)}},C1=v.useCallback(async o1=>{const m1=String(o1||P.current||"").trim();if(K.current=!1,S("idle"),O.current){try{O.current.getSenders().forEach(p1=>p1.track?.stop())}catch{}try{O.current.close()}catch{}}if(O.current=null,E.current)try{E.current.getTracks().forEach(p1=>p1.stop())}catch{}if(E.current=null,G.current=null,m1)try{await m?.(m1)}catch{}P.current=""},[m]),y1=v.useCallback(async o1=>{const m1=await h?.(o1),p1=String(m1?.whipUrl||"").trim(),j1=String(m1?.token||"").trim();if(!p1||!j1)throw new Error("WHIP endpoint unavailable");const a1=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:W3,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}});E.current=a1;const v1=a1.getAudioTracks()?.[0];if(G.current=v1||null,v1&&(v1.enabled=!!(H||F)),v1?.applyConstraints)try{await v1.applyConstraints({channelCount:1,sampleRate:W3,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1})}catch{}const k1=new RTCPeerConnection(LI);O.current=k1,k1.onconnectionstatechange=()=>{if(!K.current)return;const H2=k1.connectionState;if(H2==="connected"){S("live");return}(H2==="failed"||H2==="disconnected"||H2==="closed")&&(S("error"),R(`WHIP transport ${H2}.`))},a1.getAudioTracks().forEach(H2=>{const H1=k1.addTrack(H2,a1);kI(H1)});const c4=await k1.createOffer({offerToReceiveAudio:!1,offerToReceiveVideo:!1});await k1.setLocalDescription(c4),await FI(k1,1800);const a4=await fetch(p1,{method:"POST",headers:{"Content-Type":"application/sdp",...jI(j1)},body:k1.localDescription?.sdp||c4.sdp});if(!a4.ok)throw new Error(`WHIP request failed: ${a4.status}`);const n4=await a4.text();await k1.setRemoteDescription({type:"answer",sdp:n4}),await NI(k1,1e4),await RI(k1,6e3),await f?.(o1)},[H,F,f,h]),$1=v.useCallback(async()=>{if(J.current=!1,T.current){try{T.current.disconnect()}catch{}T.current.onaudioprocess=null}if(r1.current)try{r1.current.disconnect()}catch{}if(U.current)try{U.current.disconnect()}catch{}if(h1.current)try{h1.current.getTracks().forEach(o1=>o1.stop())}catch{}if(f1.current)try{await f1.current.close()}catch{}I.current=[],T.current=null,r1.current=null,U.current=null,h1.current=null,f1.current=null,Q.current=W3},[]),G1=v.useCallback(async()=>{if(h1.current&&f1.current&&T.current)return;N(v1=>v1==="recording"?v1:"arming");const o1=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:W3,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}}),m1=new AudioContext({latencyHint:"interactive"}),p1=m1.createMediaStreamSource(o1),j1=m1.createScriptProcessor(1024,1,1),a1=m1.createGain();a1.gain.value=0,j1.onaudioprocess=v1=>{if(!J.current)return;const k1=v1.inputBuffer.getChannelData(0);I.current.push(new Float32Array(k1))},p1.connect(j1),j1.connect(a1),a1.connect(m1.destination),h1.current=o1,f1.current=m1,r1.current=p1,T.current=j1,U.current=a1,Q.current=m1.sampleRate||W3},[]),p2=v.useCallback(async({send:o1})=>{const m1=J.current;if(J.current=!1,!m1){o1||N("idle");return}const p1=I.current;if(I.current=[],!o1){N("idle");return}if(!x){N("idle");return}if(!p1.length){N("idle");return}N("sending");try{const j1=EI(p1),a1=qI(j1,Q.current||W3),v1=ji(a1);await r?.({roverId:x,name:`ptt-${Date.now()}.wav`,mime:"audio/wav",dataBase64:v1}),N("idle")}catch(j1){N("error"),R(j1?.message||"Failed to send PTT clip.")}},[r,x]),s1=v.useCallback(async()=>{if(!x){R("Take control of your rover first.");return}await G1(),I.current=[],J.current=!0,N("recording")},[G1,x]);return v.useEffect(()=>{let o1=!1;async function m1(){if(C){await C1(P.current||x);return}if(!x){await C1(P.current);return}if(!(K.current&&P.current===x)&&!(!H&&!F))try{S("starting"),R(""),K.current=!0,P.current=x,await y1(x),o1||S("live")}catch(p1){o1||(S("error"),R(p1?.message||"Failed to start mic forwarding.")),await C1(x)}}return m1(),()=>{o1=!0}},[C,H,F,x,y1,C1]),v.useEffect(()=>{const o1=G.current;o1&&(o1.enabled=!!(!C&&(H||F)))},[C,H,F]),v.useEffect(()=>{let o1=!1;async function m1(){if(!C||!x){J.current&&await p2({send:!1}),N("idle");return}try{await G1(),!o1&&!J.current&&N("idle")}catch(p1){o1||(N("error"),R(p1?.message||"Microphone access failed for clip mode."))}}return m1(),()=>{o1=!0}},[C,G1,p2,x]),v.useEffect(()=>{let o1=!1;async function m1(){if(C){if(!x){J.current&&await p2({send:!1});return}if(F){if(!J.current)try{R(""),await s1()}catch(p1){o1||(N("error"),R(p1?.message||"Failed to start clip recording."))}}else J.current&&await p2({send:!0})}}return m1(),()=>{o1=!0}},[C,p2,F,x,s1]),v.useEffect(()=>()=>{C1(P.current||x),$1()},[x,C1,$1]),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(Oe,{label:S1})]})}),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 ${C?"":"bg-emerald-500 text-white hover:bg-emerald-500"}`,onClick:()=>V1("live"),children:"Live Mic (WHIP)"}),l.jsx("button",{type:"button",className:`button-dark w-full text-sm ${C?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,onClick:()=>V1("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(p4,{label:"Mode",active:C,detail:C?"record clip":"live mic"}),l.jsx(p4,{label:"PTT hold",active:F,detail:F?"held":"released"}),l.jsx(p4,{label:"Recording",active:x1,detail:x1?"capturing":"idle"}),l.jsx(p4,{label:"Playback",active:C&&i1,detail:C&&i1?"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:`${e0} text-center`,type:"file",accept:".mp3,.wav,.ogg,audio/mpeg,audio/wav,audio/ogg",disabled:A||!x,onChange:o1=>y(o1.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:A||!x,onClick:E1,children:A?"Working...":"Play Upload"}),l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:A||!x,onClick:e2,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||C,onChange:o1=>z(m1=>({...m1||{},openMicEnabled:!!o1.target.checked}))}),l.jsx("span",{children:C?"Open mic (live mode only)":"Open mic"})]}),l.jsxs("div",{className:"grid gap-0.5 content-start",children:[l.jsx(p4,{label:"WHIP link",active:u1,detail:j}),l.jsx(p4,{label:"Mic hot",active:g1,detail:g1?"transmitting":"muted"}),l.jsxs("div",{className:"surface-muted text-center text-xs text-slate-400",children:["Hold ",l.jsx(Oe,{label:S1})," 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(p4,{label:"Forward pipe",active:c1,detail:c1?"connected":"offline"}),l.jsx(p4,{label:"Upload playback",active:i1,detail:i1?"playing":"idle"}),l.jsx(p4,{label:"Mic relay",active:n1,detail:n1?"active":"idle"}),l.jsx(p4,{label:"WHIP transport",active:u1,detail:j}),l.jsx(p4,{label:"Clip recording",active:x1,detail:x1?"recording":"idle"}),l.jsx(p4,{label:"Clip sending",active:z1,detail:z1?"sending":"idle"})]}),Z?l.jsx("div",{className:"text-xs text-slate-400 text-center",children:Z}):null]})})]})})}function OI({pendingRequestId:t,currentStoredKey:n,nickname:r,requestVerification:o,applyIdentityKey:h,onMessage:f,fullWidth:m=!1}){const[p,g]=v.useState(0),[z,x]=v.useState(""),[M,y]=v.useState(!1),[A,L]=v.useState(!1),H=()=>{g(1),x(n),y(!1),f?.("")},w=()=>{g(0),y(!1)},C=async S=>{if(S.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 B=await h(z);await o?.(),x(B),g(0),y(!1),f?.("Verification request sent to lockdown admins.")}catch(B){f?.(B.message||"Failed to submit request.")}finally{L(!1)}},j=m?"w-full":y8;return t?l.jsx(u2,{title:"Verification",className:j,bodyClassName:"text-sm text-slate-300",children:l.jsxs("div",{className:X6,children:["Verification request pending: ",t]})}):p===0?l.jsx(u2,{title:"Verification",className:j,children:l.jsx("div",{className:X6,children:l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:H,disabled:A,children:"Request Verification"})})}):l.jsx(u2,{title:"Request verification",className:j,children:l.jsx("form",{onSubmit:C,children:l.jsxs("div",{className:X6,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(A7,{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:S=>y(S.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:()=>g(2),children:"Continue"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:w,children:"Cancel"})]})]}):null,p===2?l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-xs text-slate-400",children:"Save your identity key in a safe place. You can use it to restore your identity in another browser."}),l.jsx("input",{className:e0,type:"password",name:"identity_key_request",autoComplete:"current-password",maxLength:35,value:z,onChange:S=>x(S.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:!z,onClick:()=>navigator.clipboard?.writeText(z),children:"Copy Key"}),l.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!z,onClick:()=>g(3),children:"Continue"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>g(1),children:"Back"})]})]}):null,p===3?l.jsxs("div",{className:"space-y-0.5",children:[l.jsx("p",{className:"text-xs text-slate-400",children:"Final step: confirm and send your verification request."}),l.jsx("input",{className:e0,type:"password",name:"identity_key_request_final",autoComplete:"current-password",maxLength:35,value:z,onChange:S=>x(S.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:A||!z,children:"Confirm Request"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>g(2),children:"Back"})]})]}):null]})})})}function _I({currentStoredKey:t,applyIdentityKey:n,onMessage:r,fullWidth:o=!1}){const[h,f]=v.useState(0),[m,p]=v.useState(""),[g,z]=v.useState(!1),x=()=>{f(1),p(""),r?.("")},M=()=>{f(0),p("")},y=async L=>{if(L.preventDefault(),!(h<2)){z(!0),r?.("");try{await n(m),f(0),p(""),r?.("Identity key restored.")}catch(H){r?.(H.message||"Failed to restore key.")}finally{z(!1)}}},A=o?"w-full":y8;return l.jsx(u2,{title:"Identity key",className:A,children:l.jsxs("div",{className:X6,children:[l.jsxs("p",{className:"text-xs text-slate-500",children:["Current: ",AI(t)||"not set yet"]}),l.jsx("input",{className:e0,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:g,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:e0,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:g||!m,children:"Confirm Restore"}),l.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>f(1),children:"Back"})]})]}):null]})})}function UI({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]),g=h?"w-full":y8,z=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:g,bodyClassName:"text-sm text-slate-300",children:l.jsx("div",{className:X6,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],A=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})]}),A?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:()=>z(M),disabled:y,className:"button-dark text-xs disabled:opacity-50",children:y?"Sending...":"Request"})]},M)})})})})}function r7(t){return String(t||"").trim()}function ZI(t){const n=r7(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 so(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 I5({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 ${so(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 GI({neato:t,onStart:n,onSendHome:r,onLocate:o,onClearErrors:h,onPowerCycle:f,fullWidth:m=!1}){const[p,g]=v.useState(""),z=m?"w-full":"w-full max-w-xl",x=!!t?.configured,M=!!(t?.enabled&&t?.connected),y=!!t?.telemetry?.extPowerPresent,A=!!t?.telemetry?.chargingActive,L=ZI(t?.telemetry?.uiState),H=r7(t?.telemetry?.robotState)||"--",w=t?.telemetry?.batteryPercent,C=Number.isFinite(w)?`${w}%`:"--",j=t?.telemetry?.batteryVoltage,S=Number.isFinite(j)?`${j.toFixed(2)} V`:"--",B=r7(t?.telemetry?.robotError)||"--",N=r7(t?.telemetry?.robotAlert)||"--",Z=t?.controls||{},R=!!Z?.start?.available,E=!!Z?.sendHome?.available,G=!!Z?.locate?.available,O=!!Z?.clearErrors?.available,K=!!Z?.powerCycle?.available,P=x&&M&&R,h1=x&&M&&E,f1=x&&M&&G,r1=x&&M&&O,T=x&&M&&K,U=async(D,c1)=>{if(c1){g(D);try{await c1()}catch{}finally{g("")}}},I=x?M?"Online":"Offline":"Not configured",J=x?M?"good":"danger":"warn",Q=w==null?"muted":w>=60?"good":w>=25?"warn":"danger",F=y?"Docked":L!=="--"?L:"Away from dock";return l.jsx(u2,{title:"Neato Controls",className: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 ${so(J)}`,children:I}),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:!h1||!!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:!f1||!!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:!T||!!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(I5,{label:"Battery",value:C,tone:Q}),l.jsx(I5,{label:"Voltage",value:S,tone:"muted"}),l.jsx(I5,{label:"Docked",value:y?"Docked":"Not docked",tone:y?"good":"muted",hideLabel:!0}),l.jsx(I5,{label:"Charging",value:A?"charging":"not charging",tone:A?"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:F})]}),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:B})]}),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:N})]})]})]})})]}),!x||!M||!R||!E||!G||!O||!K?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 $I(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 YI(t){return t==="up"?"Up":t==="down"?"Down":t==="stopped"?"Transitioning...":t==="conflict"?"Conflict":"--"}function II({lift:t,onUp:n,onDown:r,fullWidth:o=!1}){const[h,f]=v.useState(""),[m,p]=v.useState(()=>Date.now()),g=o?"w-full":"w-full max-w-xl",z=!!t?.configured,x=!!(t?.enabled&&t?.connected),M=!!t?.busy,y=String(t?.target||"").toLowerCase(),A=String(t?.position||"").toLowerCase(),L=!!t?.availability?.upSwitch,H=!!t?.availability?.downSwitch,w=Number(t?.lastActionAt||0),C=Math.max(0,Number(t?.commandCooldownMs||0)),j=Math.max(0,w+C-m),S=!M&&j>0,B=M||S;v.useEffect(()=>{if(!B)return;const O=setInterval(()=>{p(Date.now())},100);return()=>clearInterval(O)},[B]),v.useEffect(()=>{p(Date.now())},[w,C,M]);const N=z?x?B?"Busy":"Ready":"Offline":"Not configured",Z=z?x?B?"warn":"good":"danger":"warn",R=z&&x&&!B&&!h&&L&&H,E=v.useMemo(()=>Math.ceil(j/1e3),[j]),G=async(O,K)=>{if(K){f(O);try{await K()}catch{}finally{f("")}}};return l.jsxs(u2,{title:"Lift Controls",className:`relative ${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 ${$I(Z)}`,children:N}),children:[B?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&&S?l.jsxs("p",{className:"text-xs text-slate-400",children:["About ",E,"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:YI(A)})]}),l.jsx("button",{type:"button",disabled:!R,onClick:()=>G("down",r),className:`button-dark w-full text-sm disabled:opacity-50 ${A==="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:!R,onClick:()=>G("up",n),className:`button-dark w-full text-sm disabled:opacity-50 ${A==="up"||y==="up"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:h==="up"||y==="up"?"Raising...":"Up"})]})}),!z||!x||!L||!H?l.jsx("p",{className:"text-xs text-slate-400 text-center",children:z?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 PI(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 QI({isVerified:t=!1,fullWidth:n=!1,onMessage:r}){const{value:o,save:h}=R1("profile",{nickname:"",profileImageUrl:""}),f=String(o?.profileImageUrl||"").trim(),[m,p]=v.useState(f),[g,z]=v.useState(!1);v.useEffect(()=>{p(f)},[f]);const x=v.useMemo(()=>PI(m),[m]),M=String(m||"").trim()!==f,y=!!(t&&M&&(x||!String(m||"").trim())),A=n?"w-full":y8,L=async H=>{if(H.preventDefault(),!t){r?.("Verify your account before setting a profile image URL.");return}z(!0),r?.("");try{const w=x||"";h(C=>({...C||{},profileImageUrl:w})),r?.(w?"Profile image URL saved.":"Profile image URL cleared.")}finally{z(!1)}};return l.jsx(u2,{title:"Chat profile image URL",className:A,bodyClassName:"text-sm text-slate-300",children:l.jsxs("form",{className:X6,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:e0,type:"url",name:"vip_profile_image_url",placeholder:"https://example.com/avatar.png",value:m,onChange:H=>p(H.target.value),disabled:!t||g}),!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||g,children:g?"Saving...":"Save URL"}),l.jsx("button",{type:"button",className:"button-dark text-sm disabled:opacity-50",disabled:!t||g||!f,onClick:()=>p(""),children:"Clear"})]})]})})}function oo({isActive:t=!0}){const n=d1(f1=>f1.session),{identifySession:r,requestVerification:o,requestPrivateRoverAccess:h,playUploadedAudio:f,stopUploadedAudio:m,startMicWhip:p,readyMicWhip:g,stopMicWhip:z,neatoStart:x,neatoSendHome:M,neatoLocate:y,neatoClearErrors:A,neatoPowerCycle:L,liftUp:H,liftDown:w}=T2(),{value:C,save:j}=R1("identity",{cookieUserId:""}),{value:S}=R1("profile",{nickname:""}),B=v.useMemo(()=>(C?.cookieUserId||"").trim(),[C?.cookieUserId]),N=(S?.nickname||"").trim(),Z=!!n?.isVerified,R=n?.verification?.pendingRequestId||null,E=n?.privateRoverAccess?.requestableRovers||[],G=n?.privateRoverAccess?.pendingRequests||[],O=String(n?.assignment?.roverId||"").trim(),[K,P]=v.useState(""),h1=async f1=>{const r1=String(f1||"").trim().toLowerCase();if(!r1)throw new Error("Identity key required.");if(!SI.test(r1))throw new Error("Identity key must match format: cu_ + 32 lowercase hex chars.");return j(T=>({...T||{},cookieUserId:r1})),await r({cookieUserId:r1,nickname:N}),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:[Z?null:l.jsx("div",{className:"lg:col-span-2",children:l.jsx(OI,{pendingRequestId:R,currentStoredKey:B,nickname:N,requestVerification:o,applyIdentityKey:h1,onMessage:P,fullWidth:!0})}),l.jsx("div",{className:"lg:col-span-2",children:Z?l.jsxs("div",{className:"space-y-2",children:[l.jsx(GI,{neato:n?.neato||null,onStart:x,onSendHome:M,onLocate:y,onClearErrors:A,onPowerCycle:L,fullWidth:!0}),l.jsx(II,{lift:n?.lift||null,onUp:H,onDown:w,fullWidth:!0}),l.jsx(DI,{ownRoverId:O,audioForwardByRover:n?.audioForward||{},playUploadedAudio:f,stopUploadedAudio:m,startMicWhip:p,readyMicWhip:g,stopMicWhip:z})]}):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(UI,{requestableRovers:E,pendingRequests:G,requestPrivateRoverAccess:h,onMessage:P,fullWidth:!0})}),l.jsx("div",{className:"lg:col-span-2",children:l.jsx(_I,{currentStoredKey:B,applyIdentityKey:h1,onMessage:P,fullWidth:!0})}),l.jsx("div",{className:"lg:col-span-2",children:l.jsx(QI,{isVerified:Z,onMessage:P,fullWidth:!0})})]}),K?l.jsx("div",{className:y8,children:l.jsx("p",{className:"text-xs text-slate-400 text-center",children:K})}):null]})}const XI=420,KI=1200,JI={1:262,2:330,3:392,4:523};function z7(){const t=d1(H=>H.session?.buttonBox?.buttons??[]),n=t4(),{value:r}=R1("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(w=>H.find(C=>Number(C?.id)===w)||{id:w,count:0,goal:0,rewardName:null,rewardId:null,rewardNumber:null,lastRewardAt:null})},[t]),[p,g]=v.useState({}),[z,x]=v.useState({}),M=v.useRef(new Map),y=v.useRef(new Map),A=v.useRef({}),L=v.useRef(null);return v.useEffect(()=>{const H={};m.forEach(C=>{H[C.id]=Number(C.lastRewardAt||0)});const w=A.current||{};m.forEach(C=>{const j=Number(C.id),S=H[j]||0,B=Number(w[j]||0);if(S>0&&S!==B){x(R=>({...R,[j]:!0}));const N=y.current.get(j);N&&clearTimeout(N);const Z=setTimeout(()=>{x(R=>({...R,[j]:!1})),y.current.delete(j)},KI);y.current.set(j,Z)}}),A.current=H},[m]),v.useEffect(()=>{function H(C){const j=JI[C];if(!j||f<=0)return;let S=L.current;if(!S){const R=window.AudioContext||window.webkitAudioContext;if(!R)return;S=new R,L.current=S}const B=S.createOscillator(),N=S.createGain();B.type="square",B.frequency.value=j;const Z=.14*f;N.gain.setValueAtTime(1e-4,S.currentTime),N.gain.exponentialRampToValueAtTime(Math.max(1e-4,Z),S.currentTime+.01),N.gain.exponentialRampToValueAtTime(1e-4,S.currentTime+.2),B.connect(N),N.connect(S.destination),B.start(),B.stop(S.currentTime+.21)}function w(C={}){const j=Number(C.buttonId);if(!Number.isFinite(j)||j<1||j>4)return;g(N=>({...N,[j]:!0}));const S=M.current.get(j);S&&clearTimeout(S);const B=setTimeout(()=>{g(N=>({...N,[j]:!1})),M.current.delete(j)},XI);M.current.set(j,B),H(j)}return n.on("buttonBox:increment",w),()=>{n.off("buttonBox:increment",w)}},[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 w=Number(H.id),C=Number.isFinite(H.count)?H.count:0,j=Number.isFinite(H.goal)?H.goal:0,S=typeof H.rewardName=="string"&&H.rewardName.trim()?H.rewardName.trim():"Unassigned",B=Number.isFinite(H.rewardNumber)?H.rewardNumber:"?",N=!!p[w],Z=!!z[w];return l.jsx(Cs,{buttonId:w,count:C,goal:j,rewardNumber:B,rewardName:S,className:[N?"bg-cyan-900/45":"",Z?"bg-fuchsia-900/45":""].filter(Boolean).join(" ")},w)})})})}function uo(){const{identifySession:t}=T2(),n=d1(z=>z.session?.overseerVote||null),{value:r}=R1("profile",{nickname:""}),{value:o}=R1("identity",{cookieUserId:""}),{value:h,save:f}=R1("overseerPreference",{enabled:!1}),m=!!h?.enabled,p=!!n?.running,g=async z=>{const x=!!z?.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:g}),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 WI(){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(Ps,{sensors:r})})})}function tP(){const{state:{roverId:t,keymap:n,camera:r,horn:o},pipeline:h,actions:{setServoAngle:f,setNightVision:m,startHorn:p,stopHorn:g}}=y2(),z=l0(),x=Ds(t),M=x.docked&&!x.driving,y=r?.config,A=!!(t&&r?.enabled&&y),L=!!(t&&h?.nightVision),H=h?.nightVisionState,w=!!(t&&h?.horn),C=o?.overheated,j=typeof y?.minAngle=="number"?y.minAngle:-30,S=typeof y?.maxAngle=="number"?y.maxAngle:30,B=typeof r?.angle=="number"?r.angle:typeof y?.homeAngle=="number"?y.homeAngle:(j+S)/2,N=N2(n?.nightVisionToggle?.[0]),Z=N2(n?.hornHonk?.[0]),R=N2(n?.cameraUp?.[0]),E=N2(n?.cameraDown?.[0]),G=!!(!t||z.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(Os,{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(_s,{nightVisionOn:H?.nightVisionOn,disabled:!t,onToggle:m,keyLabel:N}),w&&l.jsx(Us,{disabled:!t||C,onStart:p,onStop:g,keyLabel:Z,active:o?.active,heat:o?.heat}),A&&l.jsx(Zs,{value:B,min:j,max:S,label:"Camera tilt",disabled:G,onChange:f,keyDownLabel:E,keyUpLabel:R,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 eP({layout:t,onOpenHelpOverlay:n}){const[r,o]=v.useState("telemetry"),h=d1(H=>!!H.session?.isVerified),f=d1(H=>String(H.session?.assignment?.roverId||"").trim()),m=d1(H=>{const w=String(H.session?.assignment?.roverId||"").trim();return w&&H.session?.audioForward?.[w]||null}),{state:p}=y2(),{value:g}=R1("vipAudio",{openMicEnabled:!1,pttMode:"live"}),z=h?"bg-emerald-400":"bg-red-600",x=!!p?.mic?.pttActive,M=!!g?.openMicEnabled,y=g?.pttMode==="clip"?"clip":"live",A=!!(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(gc,{defaultTab:"telemetry",currentTab:r,onTabChange:o,children:[l.jsxs(xc,{children:[l.jsx(J2,{id:"telemetry",children:"Controls"}),l.jsx(J2,{id:"vip",highlight:L?"green":A?"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 ${z}`,"aria-hidden":"true",title:h?"Verified":"Not verified"})]})}),l.jsx(J2,{id:"help",children:"Help"}),l.jsx(J2,{id:"settings",children:"Settings"})]}),l.jsxs(Mc,{children:[l.jsx(W2,{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(WI,{}),l.jsx(tP,{})]}),l.jsxs("div",{className:"grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]",children:[l.jsx(B7,{}),l.jsx(cc,{panelId:"replay-sources-desktop",fillHeight:!0}),l.jsx(MI,{})]}),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(L7,{fillHeight:!0}),l.jsxs("div",{className:"grid min-h-0 gap-0.5 grid-rows-[auto_minmax(0,1fr)]",children:[l.jsx(uo,{}),l.jsx(p7,{compact:!0,hideNicknameForm:!0,fillHeight:!0})]})]}),l.jsx(Ws,{}),l.jsx(z7,{}),l.jsx(uc,{defaultOrientation:"horizontal",panelId:"rightpane-telemetry"})]})}),l.jsx(W2,{id:"vip",keepMounted:!0,children:l.jsx(oo,{isActive:r==="vip"})}),l.jsx(W2,{id:"help",children:l.jsx(io,{layout:t,onOpenOverlay:n})}),l.jsx(W2,{id:"settings",children:l.jsx(no,{})})]})]})})}const cP=new Set(["admin","lockdown","lockdown-admin"]),aP=new Set(["lockdown","lockdown-admin"]),nP=new Set(["admin","lockdown"]);function lP(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 rP(){const t=d1(x=>x.session?.mode||null),n=d1(x=>x.session?.role||null),r=d1(x=>x.session?.adminReason?.text||"");d1(x=>x.session?.adminReason?.updatedAt||null);const o=d1(x=>x.session?.timezone||"UTC"),h=nP.has(t),f=t==="lockdown"?aP.has(n):cP.has(n),[m,p]=v.useState(()=>new Date),g=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 z=lP(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:z.title}),l.jsx("p",{className:"text-sm text-slate-300",children:z.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: ",g]})]}),l.jsx("div",{className:"surface-muted",children:l.jsx(to,{})}),l.jsx("div",{className:"w-full justify-center items-center",children:l.jsx(C7,{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(L7,{nicknameLayout:"stacked"})})]})})}const iP="/assets/turn_alert-DVO8EZJa.mp3";function sP(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 oP(){const t=d1(x=>x.session?.socketId||null),n=d1(x=>x.session?.turnQueues||{}),r=d1(x=>x.session?.roster||[]),{pushAlert:o}=T2(),{value:h}=R1("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)),g=sP(iP,p),z=v.useRef(new Set);return v.useEffect(()=>{if(!t)return;const x=[];Object.entries(n).forEach(([M,y])=>{!y||!y.current||y.current!==t||z.current.has(M)||(x.push(M),z.current.add(M))}),Object.keys(n).forEach(M=>{(!n[M]||n[M].current!==t)&&z.current.delete(M)}),x.length!==0&&(x.forEach(M=>{const y=r.find(A=>String(A.id)===String(M))?.name||M;o({title:"Your turn!",message:`You now control ${y}.`,color:"#ff5722"})}),g())},[n,g,o,r,t]),null}function uP({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 hP({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 dP=new Set(["mobile-portrait","mobile-landscape"]),fP=()=>{if(typeof navigator>"u")return!1;const t=navigator.userAgent||navigator.platform||"";return/iphone|ipad|ipod/i.test(t)},Ni=()=>{if(typeof document>"u")return!1;const t=document;return!!(t.fullscreenEnabled||t.webkitFullscreenEnabled||t.mozFullScreenEnabled||t.msFullscreenEnabled)};function vP(t){const{value:n,save:r,status:o}=R1("page",{fullscreenPromptShown:!1}),h=dP.has(t),[f,m]=v.useState(!1),[p,g]=v.useState("native"),[z,x]=v.useState(!1),M=!!n?.fullscreenPromptShown,y=v.useMemo(()=>fP(),[]),A=v.useMemo(()=>Ni(),[]),L=v.useCallback(()=>{o==="ready"&&r(S=>S?.fullscreenPromptShown?S:{...S||{},fullscreenPromptShown:!0})},[r,o]),H=v.useCallback(()=>{if(o!=="ready"){m(!1);return}if(!h){m(!1),x(!1);return}if(!z&&M){m(!1);return}if(y){g("pwa-hint"),m(!0);return}if(!Ni()){m(!1);return}if(typeof document<"u"&&document.fullscreenElement){m(!1);return}g("native"),m(!0)},[z,M,y,h,o]);v.useEffect(()=>{H()},[H]),v.useEffect(()=>{if(typeof document>"u")return;const S=()=>{if(document.fullscreenElement){x(!1),m(!1);return}H()};return document.addEventListener("fullscreenchange",S),document.addEventListener("webkitfullscreenchange",S),()=>{document.removeEventListener("fullscreenchange",S),document.removeEventListener("webkitfullscreenchange",S)}},[H]);const w=v.useCallback(()=>{L(),x(!1),m(!1)},[L]),C=v.useCallback(async()=>{if(!h||y||typeof document>"u")return!1;if(document.fullscreenElement)return!0;const S=document.documentElement;if(!S)return!1;const B=S.requestFullscreen||S.webkitRequestFullscreen||S.mozRequestFullScreen||S.msRequestFullscreen;if(!B)return!1;try{const N=B.call(S);return N&&typeof N.then=="function"&&await N,L(),x(!1),m(!1),!0}catch(N){return console.warn("Failed to enter fullscreen",N),!1}},[y,h,L]),j=v.useCallback(()=>{h&&(x(!0),g(y?"pwa-hint":"native"),m(!0))},[y,h]);return v.useEffect(()=>{h||(x(!1),m(!1))},[h]),{visible:f,mode:p,isIOS:y,nativeSupported:A,enterFullscreen:C,dismiss:w,showPrompt:j}}function mP({visible:t,layout:n,onClose:r,showOnLoad:o,onToggleShowOnLoad:h}){if(!t)return null;const{state:f}=y2(),m=p=>{const g=!p.target.checked;h?.(g)};return l.jsx("div",{className:"fixed inset-0 z-30 flex items-center justify-center bg-black/80 px-0.5 py-0.5",children:l.jsxs("div",{className:"pointer-events-auto surface w-full max-w-4xl max-h-[90vh] overflow-hidden shadow-2xl",children:[l.jsxs("div",{className:"flex items-center justify-between border-b border-slate-700 px-0.5 py-0.25 text-sm text-slate-200",children:[l.jsx("span",{className:"font-semibold",children:"Help & controls"}),l.jsxs("div",{className:"flex items-center gap-0.5 text-[0.8rem] text-slate-300",children:[l.jsxs("label",{className:"flex items-center gap-0.5",children:[l.jsx("input",{type:"checkbox",checked:!o,onChange: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(ro,{layout:n,keymap:f?.keymap||{}})})]})})}function I6({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(Oe,{label:n})]})}function pP({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(I6,{label:"Forward",keyLabel:N2(t?.driveForward?.[0])}),l.jsx(I6,{label:"Backward",keyLabel:N2(t?.driveBackward?.[0])}),l.jsx(I6,{label:"Turn Left",keyLabel:N2(t?.driveLeft?.[0])}),l.jsx(I6,{label:"Turn Right",keyLabel:N2(t?.driveRight?.[0])}),l.jsx(I6,{label:"Move faster",keyLabel:N2(t?.boostModifier?.[0])}),l.jsx(I6,{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 zP(){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 gP({visible:t,layout:n,showOnLoad:r,onToggleShowOnLoad:o,onOpenHelp:h,onClose:f}){const{state:m}=y2(),p=n==="desktop",g=v.useMemo(()=>m?.keymap||{},[m?.keymap]);if(!t)return null;const z=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(pP,{keymap:g}):l.jsx(zP,{})}),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(A7,{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(C7,{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:z,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 xP=`aroma bagel batter diff --git a/server/public/index.html b/server/public/index.html index 2e1310b7..833b5471 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -11,7 +11,7 @@ Roomba Rover - + diff --git a/webui/src/components/ChatPanel/index.jsx b/webui/src/components/ChatPanel/index.jsx index 33269483..22814220 100644 --- a/webui/src/components/ChatPanel/index.jsx +++ b/webui/src/components/ChatPanel/index.jsx @@ -18,7 +18,7 @@ export default function ChatPanel({ hideSpectatorNotice = false, fillHeight = false, allowSpectatorInput = false, - title = 'Chat', + title = 'Chat and TTS', nicknameLayout = 'inline', }) { const role = useSessionSelector((state) => state.session?.role || null);