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