const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/latin-400-BcR6IdUc.css","assets/latin-500-DCnAHIrw.css","assets/latin-600-CnkPk-vb.css","assets/latin-700-UC1i-VSI.css"])))=>i.map(i=>d[i]);
import{e as _e,dw as ze,dx as _t,dy as kt,r as a,eD as is,eE as ls,j as e,c as b,_ as Me,ab as fe,H as St,G as Ct,eF as Tt,f as B,bK as Oe,a6 as Y,a4 as Ne,cW as we,cy as cs,eG as Ft,B as F,$ as rs,eH as Vs,aB as Xs,bY as Le,a1 as Ys,eI as Et,aa as Rt,aA as Hs,c4 as Pt,c8 as os,dA as Qs,eJ as Mt,eK as $s,eL as At,eM as Dt,eN as Lt,R as It,eO as de,I as Ot,a5 as zt,a7 as Kt}from"./index-C4inJ1rU.js";import{g as Vt,a as Ht,s as $t,c as Ut,u as Bt,d as qt}from"./transport-CW_z2teL.js";import{c as Gt,r as Jt}from"./system-Yjyk7rn7.js";import{C as Wt}from"./ConfirmModal-Dr1cpMpw.js";import{S as Xt,A as Yt,a as Qt,N as Zt}from"./AutoConfigureDialog-DMNUnKrx.js";import{S,a as Ae}from"./setting-field-DctR3Z3r.js";import{i as ea,g as sa,s as ta,a as aa,k as na}from"./keycap-sfx-ByZp-njj.js";import{C as q,P as la,a as ra,B as oa}from"./PageLayout-CdGXajgM.js";import{T as Zs}from"./trash-2-1fU70LRT.js";import{P as ia}from"./pencil-DJI0xtor.js";import{R as ca}from"./rotate-ccw-CGkNWEop.js";import{W as da}from"./wand-sparkles-CmoRvTcx.js";import{S as ua}from"./shield-rLi3RwxU.js";import{R as ma}from"./repeat-CyMKCb2G.js";import{G as fa}from"./git-branch-Dnk0SYSJ.js";import{C as xa}from"./clock-BOSF_clE.js";import{C as et}from"./chevron-right-CU8UihKL.js";import{R as X,C as ue}from"./Grid-rJkrpDAP.js";import{A as pa}from"./antenna-BFrGO3fo.js";import{F as Us}from"./folder-lock-ubr7YWga.js";import{P as Bs}from"./plus-BvSKm1y6.js";import{K as Ze}from"./key-round-Dx44ZbzS.js";import"./arrow-right-CKEtkWtu.js";/**
 * @license lucide-react v0.559.0 - ISC
 *
 * This source code is licensed under the ISC license.
 * See the LICENSE file in the root directory of this source tree.
 */const ha=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],st=_e("external-link",ha);/**
 * @license lucide-react v0.559.0 - ISC
 *
 * This source code is licensed under the ISC license.
 * See the LICENSE file in the root directory of this source tree.
 */const ya=[["circle",{cx:"16",cy:"20",r:"2",key:"1vifvg"}],["path",{d:"M10 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v2",key:"3hgo9p"}],["path",{d:"m22 14-4.5 4.5",key:"1ef6z8"}],["path",{d:"m21 15 1 1",key:"1ejcpy"}]],ga=_e("folder-key",ya);/**
 * @license lucide-react v0.559.0 - ISC
 *
 * This source code is licensed under the ISC license.
 * See the LICENSE file in the root directory of this source tree.
 */const ba=[["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}],["line",{x1:"3",x2:"9",y1:"12",y2:"12",key:"1dyftd"}],["line",{x1:"15",x2:"21",y1:"12",y2:"12",key:"oup4p8"}]],ja=_e("git-commit-horizontal",ba);/**
 * @license lucide-react v0.559.0 - ISC
 *
 * This source code is licensed under the ISC license.
 * See the LICENSE file in the root directory of this source tree.
 */const va=[["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2",key:"48i651"}],["line",{x1:"8",x2:"16",y1:"21",y2:"21",key:"1svkeh"}],["line",{x1:"12",x2:"12",y1:"17",y2:"21",key:"vw1qmm"}]],Na=_e("monitor",va);/**
 * @license lucide-react v0.559.0 - ISC
 *
 * This source code is licensed under the ISC license.
 * See the LICENSE file in the root directory of this source tree.
 */const wa=[["path",{d:"M10.17 4.193a2 2 0 0 1 3.666.013",key:"pltmmw"}],["path",{d:"M14 21h2",key:"v4qezv"}],["path",{d:"m15.874 7.743 1 1.732",key:"10m0iw"}],["path",{d:"m18.849 12.952 1 1.732",key:"zadnam"}],["path",{d:"M21.824 18.18a2 2 0 0 1-1.835 2.824",key:"fvwuk4"}],["path",{d:"M4.024 21a2 2 0 0 1-1.839-2.839",key:"1e1kah"}],["path",{d:"m5.136 12.952-1 1.732",key:"1u4ldi"}],["path",{d:"M8 21h2",key:"i9zjee"}],["path",{d:"m8.102 7.743-1 1.732",key:"1zzo4u"}]],_a=_e("triangle-dashed",wa);async function ka(){try{return await ze("/api/auth/tokens")}catch{return{tokens:[]}}}async function Sa(t){return ze("/api/auth/tokens",{method:"POST",body:JSON.stringify({name:t})})}async function Ca(t){return ze(`/api/auth/tokens/${t}`,{method:"DELETE"})}async function Ta(t,l){const n=_t();if(!n)return{success:!1,error:"Not authenticated"};try{const c=await fetch(`${kt}/auth/change_password`,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify({current_password:t,new_password:l})}),r=c.headers.get("content-type"),o=r!=null&&r.includes("application/json")?await c.json():null;return!c.ok||!(o!=null&&o.success)?{success:!1,error:(o==null?void 0:o.error)||`Password change failed: ${c.status} ${c.statusText}`}:o}catch(c){return{success:!1,error:c instanceof Error?c.message:"Failed to change password"}}}async function qs(t){return ze("/api/update_web_config",{method:"POST",body:JSON.stringify(t)})}a.memo(function(){const{theme:l}=is(),{themeId:n}=l,c=ls.find(r=>r.meta.id===n)??ls[0];return e.jsx("div",{className:"h-10 flex items-center justify-center",children:e.jsx("span",{className:"font-medium transition-all duration-300 leading-none text-center text-fg-secondary",style:{fontFamily:"Inter, system-ui, sans-serif",fontSize:"clamp(0.75rem, 0.6rem + 1.5vw, 1.1rem)",letterSpacing:"-0.02em"},children:c.meta.name})})});const es=a.memo(function({compact:l=!1}){const{theme:n,setTheme:c}=is(),{themeId:r}=n;return e.jsx("div",{className:b("grid grid-cols-2 gap-2",l?"w-full min-w-0 self-stretch":"w-full"),children:[...ls].reverse().map(o=>{const y=r===o.meta.id,p=o.meta.isDark;return e.jsxs("button",{type:"button",title:o.meta.name,className:b("flex flex-row items-stretch radius-inner transition-all duration-200 ease-out overflow-hidden cursor-pointer",l&&"h-6",y?"ring-2 ring-sys-blue scale-[1.02]":"opacity-70 hover:opacity-100"),onClick:()=>c(o.meta.id),children:[e.jsx("div",{className:b("flex-1 h-full",p?"bg-[#18181b]":"bg-[#EFF0F1]")}),e.jsx("div",{className:"flex flex-col h-full",children:o.display.palette.map((h,u)=>e.jsx("div",{className:"flex flex-1",children:h.map((i,j)=>e.jsx("div",{className:"w-2 h-full",style:{backgroundColor:i}},j))},u))})]},o.meta.id)})})});let Gs=!1,De=null;async function Fa(){if(!Gs)return De||(De=(async()=>{try{await Promise.all([Me(()=>Promise.resolve({}),__vite__mapDeps([0])),Me(()=>Promise.resolve({}),__vite__mapDeps([1])),Me(()=>Promise.resolve({}),__vite__mapDeps([2])),Me(()=>Promise.resolve({}),__vite__mapDeps([3]))]),Gs=!0}catch(t){console.warn("Failed to load theme fonts:",t)}})(),De)}const ss=a.memo(function(){const{theme:l,setTheme:n}=is(),{themeId:c}=l,r=c==="breeze dark",o=a.useCallback(y=>{n(y?"breeze dark":"breeze light")},[n]);return e.jsx(fe,{enabled:r,onChange:o,label:e.jsxs(e.Fragment,{children:["Dark Mode: ",r?e.jsx(St,{className:"w-3.5 h-3.5 text-sys-blue inline"}):e.jsx(Ct,{className:"w-3.5 h-3.5 text-[#e8a517] inline"})]}),tooltip:"Switch between dark and light mode",size:"md"})}),Ea=a.memo(function({layout:l="horizontal"}){return a.useEffect(()=>{Fa()},[]),l==="toggle-only"?e.jsx(ss,{}):l==="thumbnails-only"?e.jsx(es,{}):l==="vertical"?e.jsxs("div",{className:"flex flex-col items-start gap-3",children:[e.jsx(ss,{}),e.jsx(es,{})]}):e.jsxs("div",{className:"flex items-stretch gap-4",children:[e.jsx("div",{className:"flex-shrink-0 flex items-start",children:e.jsx(ss,{})}),e.jsx(es,{compact:!0})]})}),Ra=1e4,ts=2e3;function as(t,l,n){const[c,r]=a.useState("idle"),[o,y]=a.useState(null),p=a.useRef(!0),h=a.useRef(null),u=a.useRef(null),i=a.useRef(!1),j=a.useRef("idle"),D=a.useRef(t),v=a.useRef(l),E=a.useRef(n);j.current=c,D.current=t,v.current=l,E.current=n,a.useEffect(()=>(p.current=!0,()=>{p.current=!1,h.current&&clearTimeout(h.current),u.current&&clearTimeout(u.current)}),[]);const C=a.useCallback(async m=>{if(j.current==="loading")return;const g=D.current;h.current&&clearTimeout(h.current),u.current&&clearTimeout(u.current),i.current=!1,y(m),r("loading"),h.current=setTimeout(()=>{i.current||!p.current||(y(g),r("error"),u.current=setTimeout(()=>{p.current&&(r("idle"),y(null))},ts))},Ra);try{if(await v.current(m),i.current=!0,h.current&&clearTimeout(h.current),!p.current)return;r("success"),u.current=setTimeout(()=>{var k;p.current&&(r("idle"),y(null),(k=E.current)==null||k.call(E))},ts)}catch{if(i.current=!0,h.current&&clearTimeout(h.current),!p.current)return;y(g),r("error"),u.current=setTimeout(()=>{p.current&&(r("idle"),y(null))},ts)}},[]);return{enabled:o!==null?o:t,status:c,toggle:C}}function Pa(){const{latitude:t,longitude:l,enabled:n,setLocation:c,enable:r,disable:o,clear:y}=Tt(),[p,h]=a.useState(!1),[u,i]=a.useState(""),[j,D]=a.useState(""),[v,E]=a.useState("idle"),[C,N]=a.useState(null),m=a.useRef(null),g=a.useRef(null),k=a.useRef(!0);a.useEffect(()=>(k.current=!0,()=>{k.current=!1,g.current&&clearTimeout(g.current)}),[]);const R=()=>{i((t==null?void 0:t.toFixed(6))??""),D((l==null?void 0:l.toFixed(6))??""),N(null),h(!0)},J=()=>{h(!1),N(null)};a.useEffect(()=>{if(!p)return;const P=z=>{v!=="loading"&&m.current&&!m.current.contains(z.target)&&J()};return document.addEventListener("mouseup",P),()=>document.removeEventListener("mouseup",P)},[p,v]);const V=(P,z)=>{const H=parseFloat(P),U=parseFloat(z);return isNaN(H)||isNaN(U)?"Please enter valid numbers":H<-90||H>90?"Latitude must be between -90 and 90":U<-180||U>180?"Longitude must be between -180 and 180":H===0&&U===0?"Coordinates (0, 0) are invalid":null},w=()=>{if(v==="loading")return;g.current&&clearTimeout(g.current);const P=V(u,j);if(P){N(P);return}E("loading"),N(null);const z=parseFloat(u),H=parseFloat(j);c(z,H),n||r(),E("success"),g.current=setTimeout(()=>{k.current&&(E("idle"),h(!1))},1e3)},T=()=>{n?o():r()},f=()=>{y(),h(!1)},O=t!==null&&l!==null;return e.jsx(q,{children:e.jsxs("div",{ref:m,children:[e.jsxs("div",{className:"flex items-center justify-between mb-4",children:[e.jsx(B,{icon:e.jsx(_a,{}),title:"Stealth"}),e.jsxs("div",{className:"flex items-center gap-1",children:[O&&!p&&e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:T,className:b("relative inline-flex h-6 w-11 items-center rounded-full transition-colors border-2 mr-2",n?"bg-toggle-on border-toggle-on":"bg-toggle-off border-edge-subtle"),title:n?"Disable stealth location":"Enable stealth location",children:e.jsx("span",{className:b("inline-block h-4 w-4 transform rounded-full bg-white transition-transform shadow-lg",n?"translate-x-5":"translate-x-0.5")})}),e.jsx("button",{onClick:f,className:"p-2 rounded-lg transition-colors text-fg-muted hover:text-sys-red hover:bg-sys-red/10",title:"Clear stealth location",children:e.jsx(Zs,{className:"w-4 h-4"})})]}),p?e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:J,disabled:v==="loading",className:b("p-2 rounded-lg transition-colors",v==="loading"?"text-fg-muted cursor-not-allowed":"text-fg-muted hover:text-sys-red hover:bg-sys-red/10"),title:"Cancel",children:e.jsx(Oe,{className:"w-4 h-4"})}),e.jsx("button",{onClick:w,disabled:v==="loading",className:b("p-2 rounded-lg transition-colors",v==="loading"?"text-sys-blue cursor-wait":v==="success"?"text-sys-green":v==="error"?"text-sys-red":"text-sys-green hover:bg-sys-green/10"),title:"Save",children:v==="loading"?e.jsx(Y,{className:"w-4 h-4 animate-spin"}):v==="success"?e.jsx(Ne,{className:"w-4 h-4"}):v==="error"?e.jsx(Oe,{className:"w-4 h-4"}):e.jsx(Ne,{className:"w-4 h-4"})})]}):e.jsx("button",{onClick:R,className:"p-2 rounded-lg transition-colors text-fg-muted hover:text-fg-primary hover:bg-subtle",title:O?"Edit stealth location":"Set stealth location",children:e.jsx(ia,{className:"w-4 h-4"})})]})]}),e.jsx("div",{className:"bg-sys-cyan/5 rounded-xl p-3 mb-4",children:e.jsxs("div",{className:"flex gap-2",children:[e.jsx(we,{className:"w-4 h-4 text-sys-cyan flex-shrink-0 mt-0.5"}),e.jsxs("p",{className:"type-body-sm text-fg-muted",children:[e.jsx("span",{className:"text-fg-secondary",children:"Stealth mode"})," allows you to appear on the map and in topology analysis without broadcasting your location. Coordinates are stored locally in your browser only — ",e.jsx("span",{className:"text-sys-cyan",children:"never sent to config.yaml or the mesh"}),"."]})]})}),C&&e.jsx("div",{className:"text-xs mb-3 px-2 py-1.5 rounded-md text-sys-red bg-sys-red/10",children:C}),e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsxs("div",{children:[e.jsx("label",{className:"type-label text-fg-muted block mb-1",children:p?"Latitude (°)":"Latitude"}),e.jsx("div",{className:b("h-[38px] rounded-full px-4 flex items-center transition-colors","bg-subtle-fill"),children:p?e.jsx("input",{type:"number",value:u,onChange:P=>i(P.target.value),step:"0.000001",min:"-90",max:"90",placeholder:"e.g. 34.052234",className:"w-full bg-transparent text-fg-primary focus:outline-none type-data placeholder:text-fg-muted/50"}):e.jsx("span",{className:b("type-data",O&&n?"text-fg-primary":"text-fg-muted"),children:O?t.toFixed(6):"Not set"})})]}),e.jsxs("div",{children:[e.jsx("label",{className:"type-label text-fg-muted block mb-1",children:p?"Longitude (°)":"Longitude"}),e.jsx("div",{className:b("h-[38px] rounded-full px-4 flex items-center transition-colors","bg-subtle-fill"),children:p?e.jsx("input",{type:"number",value:j,onChange:P=>D(P.target.value),step:"0.000001",min:"-180",max:"180",placeholder:"e.g. -118.243685",className:"w-full bg-transparent text-fg-primary focus:outline-none type-data placeholder:text-fg-muted/50"}):e.jsx("span",{className:b("type-data",O&&n?"text-fg-primary":"text-fg-muted"),children:O?l.toFixed(6):"Not set"})})]})]}),O&&!p&&e.jsx("div",{className:"mt-3 pt-3 border-t border-edge-subtle",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:b("w-2 h-2 rounded-full",n?"bg-sys-green":"bg-fg-muted")}),e.jsx("span",{className:"type-body-sm text-fg-muted",children:n?"Stealth location active — you appear on maps with these coordinates":"Stealth location disabled — coordinates saved but not in use"})]})})]})})}function me(t){if(t<60)return`${t}s`;if(t<3600){const c=Math.floor(t/60),r=t%60;return r>0?`${c}m ${r}s`:`${c}m`}const l=Math.floor(t/3600),n=Math.floor(t%3600/60);return n>0?`${l}h ${n}m`:`${l}h`}function ns({title:t,defaultOpen:l=!1,children:n}){const[c,r]=a.useState(l);return e.jsxs("div",{className:"bg-subtle-fill radius-inner overflow-hidden",children:[e.jsxs("button",{type:"button",onClick:()=>r(!c),className:"w-full flex items-center justify-between px-4 py-3 hover:bg-subtle-fill-hover transition-colors",children:[e.jsx("span",{className:"type-label text-fg-muted",children:t}),e.jsx(Xs,{className:b("w-4 h-4 text-fg-muted transition-transform",c&&"rotate-180")})]}),c&&e.jsx("div",{className:"px-4 pb-4 pt-1",children:n})]})}const Ie={off:{label:"Off",description:"All rate limiting disabled. Every advert is forwarded unconditionally.",color:"text-fg-muted",config:Xt},light:{label:"Light",description:"Gentle throttling for sparse meshes. Allows short bursts but penalizes repeat offenders. Good for small networks with few nodes.",color:"text-sys-green",config:{rate_limit_enabled:!0,bucket_capacity:3,refill_tokens:1,refill_interval_seconds:3600,min_interval_seconds:15,penalty_enabled:!0,violation_threshold:2,base_penalty_seconds:1800,penalty_multiplier:2,max_penalty_seconds:43200,violation_decay_seconds:7200,adaptive_enabled:!0,ewma_alpha:.15,hysteresis_seconds:480,quiet_max:2,normal_max:6,busy_max:10}},standard:{label:"Standard",description:"Balanced protection for most meshes. One advert per source is enough for discovery — repeated adverts are suppressed to protect channel capacity.",color:"text-sys-blue",recommended:!0,config:{rate_limit_enabled:!0,bucket_capacity:2,refill_tokens:1,refill_interval_seconds:7200,min_interval_seconds:30,penalty_enabled:!0,violation_threshold:1,base_penalty_seconds:7200,penalty_multiplier:4,max_penalty_seconds:172800,violation_decay_seconds:14400,adaptive_enabled:!0,ewma_alpha:.1,hysteresis_seconds:300,quiet_max:1,normal_max:3,busy_max:8}},strict:{label:"Strict",description:"Maximum suppression for dense or congested meshes. One token, long refill, immediate jail. Protects GRP_TXT capacity at all costs.",color:"text-sys-amber",config:{rate_limit_enabled:!0,bucket_capacity:1,refill_tokens:1,refill_interval_seconds:14400,min_interval_seconds:60,penalty_enabled:!0,violation_threshold:1,base_penalty_seconds:10800,penalty_multiplier:4,max_penalty_seconds:259200,violation_decay_seconds:21600,adaptive_enabled:!0,ewma_alpha:.05,hysteresis_seconds:180,quiet_max:.5,normal_max:2,busy_max:5}}},tt=["off","light","standard","strict"];function Ma(t,l,n){if(!t&&!l&&!n||!((t==null?void 0:t.enabled)||(l==null?void 0:l.enabled)||(n==null?void 0:n.enabled)))return"off";for(const r of tt){if(r==="off")continue;const o=Ie[r].config;if((t==null?void 0:t.bucket_capacity)===o.bucket_capacity&&(t==null?void 0:t.refill_tokens)===o.refill_tokens&&(t==null?void 0:t.refill_interval_seconds)===o.refill_interval_seconds&&(t==null?void 0:t.min_interval_seconds)===o.min_interval_seconds&&(l==null?void 0:l.violation_threshold)===o.violation_threshold&&(l==null?void 0:l.base_penalty_seconds)===o.base_penalty_seconds&&(l==null?void 0:l.penalty_multiplier)===o.penalty_multiplier&&(l==null?void 0:l.max_penalty_seconds)===o.max_penalty_seconds&&(l==null?void 0:l.violation_decay_seconds)===o.violation_decay_seconds&&(n==null?void 0:n.ewma_alpha)===o.ewma_alpha&&(n==null?void 0:n.hysteresis_seconds)===o.hysteresis_seconds)return r}return null}function Aa(){var P,z,H,U,se,Q;const{stats:t,fetchStats:l}=cs(),n=a.useRef(!0),[c,r]=a.useState(!0),[o,y]=a.useState(!1),[p,h]=a.useState(null),[u,i]=a.useState("idle"),j=a.useRef(null),[D,v]=a.useState(!1),[E,C]=a.useState(new Set);a.useEffect(()=>(n.current=!0,()=>{n.current=!1,j.current&&clearTimeout(j.current)}),[]);const N=a.useCallback(async()=>{var d;try{const M=await Ft();if(!n.current)return;M.success&&M.data&&((d=M.data.adaptive)!=null&&d.current_tier)?r(!0):r(!1)}catch{n.current&&r(!1)}},[]);a.useEffect(()=>{N();const d=setInterval(N,3e4);return()=>clearInterval(d)},[N]);const m=(z=(P=t==null?void 0:t.config)==null?void 0:P.repeater)==null?void 0:z.advert_rate_limit,g=(U=(H=t==null?void 0:t.config)==null?void 0:H.repeater)==null?void 0:U.advert_penalty_box,k=(Q=(se=t==null?void 0:t.config)==null?void 0:se.repeater)==null?void 0:Q.advert_adaptive;if(!c&&!m&&!g&&!k)return null;const R=d=>E.has(d),J=d=>{C(M=>{if(!M.has(d))return M;const G=new Set(M);return G.delete(d),G})},V=Ma(m,g,k),w=V?Ie[V]:null,T=async(d,M)=>{J(d),y(!0),h(null),j.current&&clearTimeout(j.current);try{const G=await Vs({[d]:M});if(!n.current)return;G.success?(i("success"),await Promise.all([l({force:!0}).catch(()=>{}),N()])):(i("error"),h(G.error||"Failed to save"))}catch{n.current&&(i("error"),h("Failed to save"))}finally{n.current&&(y(!1),j.current=setTimeout(()=>{n.current&&(i("idle"),h(null))},1500))}},f=async d=>{if(!(d===V||o)){y(!0),h(null),C(new Set),j.current&&clearTimeout(j.current);try{const M=await Vs(Ie[d].config);if(!n.current)return;M.success?(i("success"),await Promise.all([l({force:!0}).catch(()=>{}),N()])):(i("error"),h(M.error||"Failed to save"))}catch{n.current&&(i("error"),h("Failed to save"))}finally{n.current&&(y(!1),j.current=setTimeout(()=>{n.current&&i("idle")},1500))}}},O=()=>f("off");return e.jsxs(q,{neomorphic:!0,children:[e.jsx(B,{icon:e.jsx(ua,{}),title:"Advert Rate Limiting",actions:e.jsxs("div",{className:"flex items-center gap-3",children:[u!=="idle"&&e.jsxs("span",{className:"flex items-center",children:[u==="success"&&e.jsx(Ne,{className:"w-4 h-4 text-sys-green"}),u==="error"&&e.jsx(Oe,{className:"w-4 h-4 text-sys-red"})]}),e.jsxs(F,{plain:!0,onClick:O,className:"flex items-center gap-1.5 text-zinc-500 hover:text-zinc-400 transition-colors",children:[e.jsx(ca,{className:"w-3.5 h-3.5"}),e.jsx("span",{className:"type-body-xs font-medium hidden sm:inline",children:"Reset"})]}),e.jsxs(F,{plain:!0,onClick:()=>v(!0),className:"flex items-center gap-1.5 text-sys-blue hover:text-sys-blue/80 transition-colors",children:[e.jsx(da,{className:"w-3.5 h-3.5"}),e.jsx("span",{className:"type-body-xs font-medium",children:"Auto"})]})]})}),p&&e.jsx("div",{className:"type-body-sm mb-3 px-3 py-2 rounded-full text-sys-red bg-sys-red/10",children:p}),e.jsx("div",{className:"toggle-group w-full mb-4",children:tt.map(d=>{const M=Ie[d],G=d===V;return e.jsx("button",{onClick:()=>f(d),disabled:o,className:b("toggle-group-item flex-1",G&&"active",G&&M.color),children:M.label},d)})}),e.jsx("div",{className:"bg-subtle-fill radius-inner p-4 mb-4",children:e.jsxs("div",{className:"flex gap-3",children:[e.jsx("div",{className:"w-8 h-8 rounded-full bg-sys-blue/20 flex items-center justify-center flex-shrink-0",children:e.jsx(we,{className:"w-4 h-4 text-sys-blue"})}),e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:b("type-data-xs font-medium uppercase tracking-wide",(w==null?void 0:w.color)??"text-fg-muted"),children:(w==null?void 0:w.label)??"Custom"}),(w==null?void 0:w.recommended)&&e.jsx(rs,{color:"green",children:"Recommended"}),!V&&e.jsx(rs,{color:"zinc",children:"Custom"})]}),e.jsx("p",{className:"type-body-sm text-fg-muted mt-1",children:(w==null?void 0:w.description)??"Settings have been manually tuned and don’t match any preset."})]})]})}),e.jsxs("div",{className:b("space-y-3",V==="off"&&"opacity-40 pointer-events-none"),children:[e.jsx(ns,{title:"Token Rules",children:m?e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsx(S,{label:"Bucket Capacity",value:String(m.bucket_capacity),type:"number",suffix:"tokens",min:1,max:100,tooltip:"Max tokens a source can hold — determines burst capacity. Each advert costs one token; tokens refill over time.",autoConfigured:R("bucket_capacity"),onSave:async d=>T("bucket_capacity",parseInt(d,10))}),e.jsx(S,{label:"Refill Tokens",value:String(m.refill_tokens),type:"number",suffix:"tokens",min:1,max:50,tooltip:"Tokens added back each refill cycle. Higher = more forgiving over time.",autoConfigured:R("refill_tokens"),onSave:async d=>T("refill_tokens",parseInt(d,10))}),e.jsx(S,{label:"Refill Interval",value:me(m.refill_interval_seconds),editValue:String(parseFloat((m.refill_interval_seconds/3600).toFixed(2))),type:"number",suffix:"hr",step:.5,min:.5,max:24,tooltip:"How often the bucket gets topped up. Shorter = faster recovery from bursts.",autoConfigured:R("refill_interval_seconds"),onSave:async d=>T("refill_interval_seconds",Math.round(parseFloat(d)*3600))}),e.jsx(S,{label:"Min Interval",value:me(m.min_interval_seconds),editValue:String(m.min_interval_seconds),type:"number",suffix:"sec",min:0,max:3600,tooltip:"Minimum gap between adverts from the same source, even if tokens are available. 0 = no minimum.",autoConfigured:R("min_interval_seconds"),onSave:async d=>T("min_interval_seconds",parseInt(d,10))})]}):e.jsx("p",{className:"type-body-sm text-fg-muted",children:"Not configured on this backend."})}),e.jsx(ns,{title:"Jail Settings",children:g?e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsx(S,{label:"Violation Threshold",value:String(g.violation_threshold),type:"number",suffix:"violations",min:1,max:100,tooltip:"How many rate-limit violations before a source gets penalized. Think of it as strikes before timeout.",autoConfigured:R("violation_threshold"),onSave:async d=>T("violation_threshold",parseInt(d,10))}),e.jsx(S,{label:"Base Penalty",value:me(g.base_penalty_seconds),editValue:String(parseFloat((g.base_penalty_seconds/3600).toFixed(2))),type:"number",suffix:"hr",step:.5,min:.5,max:24,tooltip:"First offense penalty duration. Repeat offenders get this multiplied.",autoConfigured:R("base_penalty_seconds"),onSave:async d=>T("base_penalty_seconds",Math.round(parseFloat(d)*3600))}),e.jsx(S,{label:"Multiplier",value:`${g.penalty_multiplier}x`,editValue:String(g.penalty_multiplier),type:"number",suffix:"x",step:.5,min:1,max:10,tooltip:"Each repeat offense multiplies the penalty by this factor. 2x means penalties double each time.",autoConfigured:R("penalty_multiplier"),onSave:async d=>T("penalty_multiplier",parseFloat(d))}),e.jsx(S,{label:"Max Penalty",value:me(g.max_penalty_seconds),editValue:String(parseFloat((g.max_penalty_seconds/3600).toFixed(2))),type:"number",suffix:"hr",step:1,min:1,max:168,tooltip:"Upper limit on penalty duration. No matter how many offenses, it won't exceed this.",autoConfigured:R("max_penalty_seconds"),onSave:async d=>T("max_penalty_seconds",Math.round(parseFloat(d)*3600))}),e.jsx(S,{label:"Violation Decay",value:me(g.violation_decay_seconds),editValue:String(parseFloat((g.violation_decay_seconds/3600).toFixed(2))),type:"number",suffix:"hr",step:1,min:1,max:168,tooltip:"How long until a source's violation count resets. Good behavior earns a clean slate.",autoConfigured:R("violation_decay_seconds"),onSave:async d=>T("violation_decay_seconds",Math.round(parseFloat(d)*3600))})]}):e.jsx("p",{className:"type-body-sm text-fg-muted",children:"Not configured on this backend."})}),e.jsx(ns,{title:"Traffic Tiers",children:k?e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsx(S,{label:"EWMA α",value:String(k.ewma_alpha),type:"number",step:.01,min:.01,max:1,tooltip:"How quickly the system reacts to traffic changes. Lower = smoother, higher = more responsive.",autoConfigured:R("ewma_alpha"),onSave:async d=>T("ewma_alpha",parseFloat(d))}),e.jsx(S,{label:"Hysteresis",value:me(k.hysteresis_seconds),editValue:String(parseFloat((k.hysteresis_seconds/60).toFixed(2))),type:"number",suffix:"min",step:.5,min:0,max:60,tooltip:"Cooldown before switching tiers again. Prevents rapid flip-flopping during traffic spikes.",autoConfigured:R("hysteresis_seconds"),onSave:async d=>T("hysteresis_seconds",Math.round(parseFloat(d)*60))}),e.jsx(S,{label:"Quiet Max",value:String(k.thresholds.quiet_max),type:"number",min:0,max:1e4,suffix:"pkts/hr",tooltip:"Traffic below this = quiet mesh. Rate limits are most relaxed here.",autoConfigured:R("quiet_max"),onSave:async d=>T("quiet_max",parseFloat(d))}),e.jsx(S,{label:"Normal Max",value:String(k.thresholds.normal_max),type:"number",min:0,max:1e4,suffix:"pkts/hr",tooltip:"Traffic below this = normal activity. Standard rate limits apply.",autoConfigured:R("normal_max"),onSave:async d=>T("normal_max",parseFloat(d))}),e.jsx(S,{label:"Busy Max",value:String(k.thresholds.busy_max),type:"number",min:0,max:1e4,suffix:"pkts/hr",tooltip:"Traffic above this = congested mesh. Strictest rate limits kick in.",autoConfigured:R("busy_max"),onSave:async d=>T("busy_max",parseFloat(d))})]}):e.jsx("p",{className:"type-body-sm text-fg-muted",children:"Not configured on this backend."})})]}),e.jsx("div",{className:"bg-sys-blue/5 radius-inner p-4 mt-4",children:e.jsxs("div",{className:"flex gap-3",children:[e.jsx("div",{className:"w-8 h-8 rounded-full bg-sys-blue/20 flex items-center justify-center flex-shrink-0",children:e.jsx(we,{className:"w-4 h-4 text-sys-blue"})}),e.jsx("div",{className:"type-body-sm text-fg-muted",children:e.jsxs("p",{children:[e.jsx("span",{className:"text-fg-secondary",children:"Three layers"})," work together: the ",e.jsx("span",{className:"text-sys-blue",children:"Token Rules"})," throttle per-source adverts, the ",e.jsx("span",{className:"text-sys-amber",children:"Jail"})," escalates penalties for repeat violators, and ",e.jsx("span",{className:"text-sys-green",children:"Traffic Tiers"})," adjust limits based on overall mesh activity. The ",e.jsx("span",{className:"text-sys-blue",children:"“Auto”"})," button analyzes 1 week of mesh traffic to suggest optimal settings."]})})]})}),e.jsx(Yt,{open:D,onClose:()=>v(!1),currentConfig:{tokenBucket:m,penaltyBox:g,adaptive:k},onApplied:async()=>{C(new Set(Qt)),await Promise.all([l({force:!0}).catch(()=>{}),N()])}})]})}const Js={off:{label:"Off",threshold:null,description:"No loop detection. All flood packets forwarded regardless of path history.",color:"text-fg-muted"},minimal:{label:"Minimal",threshold:4,description:"Drops flood packets when your local hash appears 4+ times in the path. Catches only the most obvious routing loops.",color:"text-sys-green",recommended:!0},moderate:{label:"Moderate",threshold:2,description:"Drops flood packets when your local hash appears 2+ times in the path. Good balance for most networks.",color:"text-sys-blue"},strict:{label:"Strict",threshold:1,description:"Drops flood packets if your local hash appears even once in the path. Strongest loop prevention — may drop some legitimate retransmissions in dense meshes.",color:"text-sys-amber"}},Da=["off","minimal","moderate","strict"];function La(){var N;const{stats:t,fetchStats:l}=cs(),n=a.useRef(!0),c=(N=t==null?void 0:t.config)==null?void 0:N.mesh,r=!!c,o=(c==null?void 0:c.loop_detect)??"off",y=Js[o],p=t==null?void 0:t.local_hash,[h,u]=a.useState(!1),[i,j]=a.useState(null),[D,v]=a.useState("idle"),E=a.useRef(null);if(a.useEffect(()=>(n.current=!0,()=>{n.current=!1,E.current&&clearTimeout(E.current)}),[]),a.useEffect(()=>{if(!i)return;const m=setTimeout(()=>{n.current&&j(null)},4e3);return()=>clearTimeout(m)},[i]),!t)return null;const C=async m=>{if(!(!r||m===o||h)){u(!0),j(null),E.current&&clearTimeout(E.current);try{const g=await Ys({loop_detect:m});if(!n.current)return;g.success?(v("success"),await l({force:!0}).catch(()=>{})):(v("error"),j(g.error||"Failed to save"))}catch{n.current&&(v("error"),j("Failed to save"))}finally{n.current&&(u(!1),E.current=setTimeout(()=>{n.current&&v("idle")},1500))}}};return e.jsxs(q,{neomorphic:!0,children:[e.jsx(B,{icon:e.jsx(ma,{}),title:"Flood Loop Detection",actions:D!=="idle"?e.jsxs("span",{className:"flex items-center",children:[D==="success"&&e.jsx(Ne,{className:"w-4 h-4 text-sys-green"}),D==="error"&&e.jsx(Oe,{className:"w-4 h-4 text-sys-red"})]}):void 0}),!r&&e.jsxs("div",{className:"flex items-start gap-2.5 mb-3 px-3 py-2.5 radius-inner bg-sys-amber/10 text-sys-amber",children:[e.jsx(Le,{className:"w-4 h-4 mt-0.5 flex-shrink-0"}),e.jsxs("p",{className:"type-body-sm",children:["Backend does not expose mesh config. Update pyMC_Repeater to enable live configuration. Set ",e.jsx("code",{className:"font-mono text-fg-secondary",children:"loop_detect"})," in"," ",e.jsx("code",{className:"font-mono text-fg-secondary",children:"config.yaml"})," manually."]})]}),i&&e.jsx("div",{className:"type-body-sm mb-3 px-3 py-2 rounded-full text-sys-red bg-sys-red/10",children:i}),e.jsx("div",{className:b("toggle-group w-full mb-4",!r&&"opacity-50 pointer-events-none"),children:Da.map(m=>{const g=Js[m],k=m===o;return e.jsx("button",{onClick:()=>C(m),disabled:h||!r,className:b("toggle-group-item flex-1",k&&"active",k&&g.color),children:g.label},m)})}),e.jsx("div",{className:"bg-subtle-fill radius-inner p-4",children:e.jsxs("div",{className:"flex gap-3",children:[e.jsx("div",{className:"w-8 h-8 rounded-full bg-sys-blue/20 flex items-center justify-center flex-shrink-0",children:e.jsx(we,{className:"w-4 h-4 text-sys-blue"})}),e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:b("type-data-xs font-medium uppercase tracking-wide",y.color),children:y.label}),y.threshold!==null&&e.jsxs("span",{className:"data-box data-box-compact text-fg-secondary tabular-nums",children:["≥",y.threshold,"× → drop"]}),y.recommended&&e.jsx(rs,{color:"green",children:"Recommended"})]}),e.jsx("p",{className:"type-body-sm text-fg-muted mt-1",children:y.description}),p&&o!=="off"&&e.jsxs("p",{className:"type-data-xs text-fg-muted/60 mt-2 font-mono",children:["Local hash: ",p]})]})]})})]})}function Ia(t){if(!t)return"Never";const l=new Date(t).getTime();if(isNaN(l))return"Never";const c=(Date.now()-l)/1e3;return c<60?"Just now":c<3600?`${Math.floor(c/60)}m ago`:c<86400?`${Math.floor(c/3600)}h ago`:`${Math.floor(c/86400)}d ago`}function Oa(){const{status:t,updateAvailable:l,isChecking:n,channels:c,changelog:r,error:o,available:y,checkForUpdate:p,fetchChannels:h,fetchChangelog:u,setChannel:i,openModal:j}=Et(),D=Rt(),[v,E]=a.useState(!1),[C,N]=a.useState(!1);if(a.useEffect(()=>{h(),u()},[h,u]),!y)return null;const m=f=>f.split("+")[0],g=f=>f!=null&&f.includes("+")?f.split("+")[1]:null,k=(t==null?void 0:t.current_version)??(D==null?void 0:D.version)??null,R=(t==null?void 0:t.latest_version)??null,J=k?m(k):null,V=R?m(R):null,w=g(k),T=(t==null?void 0:t.channel)??"main";return e.jsxs(q,{neomorphic:!0,children:[e.jsx(B,{icon:e.jsx(Hs,{}),title:"Software Update"}),e.jsxs("div",{className:"grid grid-cols-2 sm:grid-cols-3 gap-4 mb-4",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("span",{className:"type-label text-fg-secondary",children:"Installed"}),e.jsxs("div",{className:"flex items-center gap-1.5 mt-0.5",children:[e.jsx("p",{className:"type-data text-fg-primary truncate font-mono",children:J?`v${J}`:"—"}),w&&e.jsx(Pt,{copy:!0,copyValue:w,title:`Build: ${w}`,children:w})]})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("span",{className:"type-label text-fg-secondary",children:"Git Branch"}),e.jsxs("div",{className:"mt-0.5 flex items-center gap-1.5",children:[e.jsx(fa,{className:"w-3.5 h-3.5 text-sys-indigo flex-shrink-0"}),e.jsx("span",{className:"type-data text-sys-indigo truncate",children:T})]})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("span",{className:"type-label text-fg-secondary",children:"Last Checked"}),e.jsxs("div",{className:"mt-0.5 flex items-center gap-1.5",children:[e.jsx(xa,{className:"w-3.5 h-3.5 text-fg-muted flex-shrink-0"}),e.jsx("span",{className:"type-data text-fg-muted",children:Ia((t==null?void 0:t.last_checked)??null)})]})]})]}),c.length>0&&e.jsxs("div",{className:"bg-subtle-fill radius-inner p-4 mb-4",children:[e.jsx("div",{className:"flex items-center justify-between gap-3",children:e.jsxs("div",{children:[e.jsx("h4",{className:"type-body-sm text-fg-primary font-medium",children:"Release Channel"}),e.jsx("p",{className:"type-data-xs text-fg-muted mt-0.5",children:"Switch between stable and development branches"})]})}),e.jsx("div",{className:"flex flex-wrap gap-2 mt-3",children:c.map(f=>e.jsxs("button",{onClick:async()=>{f.active||C||(N(!0),await i(f.name),N(!1))},disabled:f.active||C,className:b("px-3 py-1.5 radius-inner type-data-xs font-medium transition-colors",f.active?"bg-sys-indigo/15 text-sys-indigo ring-1 ring-sys-indigo/30":"bg-subtle-fill-strong text-fg-muted hover:text-fg-primary hover:bg-subtle-fill-hover",C&&!f.active&&"opacity-50 cursor-not-allowed"),children:[f.name,f.active&&e.jsx(os,{className:"w-3 h-3 ml-1.5 inline-block"})]},f.name))})]}),r.length>0&&e.jsxs("div",{className:"bg-subtle-fill radius-inner mb-4",children:[e.jsxs("button",{onClick:()=>E(!v),className:"w-full flex items-center gap-2 px-4 py-3 text-left hover:bg-subtle-fill-hover transition-colors radius-inner",children:[v?e.jsx(Xs,{className:"w-4 h-4 text-fg-muted flex-shrink-0"}):e.jsx(et,{className:"w-4 h-4 text-fg-muted flex-shrink-0"}),e.jsx("span",{className:"type-body-sm text-fg-primary font-medium flex-1",children:"Changelog"}),e.jsxs("span",{className:"type-data-xs text-fg-muted",children:[r.length," commit",r.length!==1?"s":""]})]}),v&&e.jsx("div",{className:"px-4 pb-3 space-y-1 max-h-[300px] overflow-y-auto",children:r.map(f=>e.jsxs("div",{className:"flex items-start gap-2 py-1.5 group",children:[e.jsx(ja,{className:"w-3.5 h-3.5 text-fg-muted flex-shrink-0 mt-0.5"}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[f.url?e.jsx("a",{href:f.url,target:"_blank",rel:"noopener noreferrer",className:"type-data-xs text-fg-primary truncate hover:text-sys-blue transition-colors",children:f.title}):e.jsx("p",{className:"type-data-xs text-fg-primary truncate",children:f.title}),f.url&&e.jsx("a",{href:f.url,target:"_blank",rel:"noopener noreferrer",className:"opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0",children:e.jsx(st,{className:"w-3 h-3 text-fg-muted"})})]}),f.body&&e.jsx("p",{className:"type-data-xs text-fg-muted mt-0.5 line-clamp-2 whitespace-pre-line",children:f.body}),e.jsxs("div",{className:"flex items-center gap-2 mt-0.5",children:[e.jsx("span",{className:"type-data-xs text-fg-muted font-mono",children:f.short_sha}),e.jsx("span",{className:"text-fg-muted/30",children:"·"}),e.jsx("span",{className:"type-data-xs text-fg-muted",children:f.author}),e.jsx("span",{className:"text-fg-muted/30",children:"·"}),e.jsx("span",{className:"type-data-xs text-fg-muted",children:f.date?new Date(f.date).toLocaleDateString():""})]})]})]},f.sha))})]}),o&&e.jsx("div",{className:"type-body-sm mb-3 px-3 py-2 rounded-full text-sys-red bg-sys-red/10",children:o}),e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[l&&e.jsxs(F,{color:"danger",onClick:j,children:[e.jsx(Hs,{"data-slot":"icon"}),"Update to v",V]}),e.jsx("div",{className:"flex-1"}),!l&&t&&e.jsxs("span",{className:"flex items-center gap-1.5 type-data-xs text-sys-green",children:[e.jsx(os,{className:"w-3.5 h-3.5"}),"Up to date"]}),e.jsx(F,{color:"primary",outline:!0,onClick:()=>p(!0),disabled:n,children:n?e.jsxs(e.Fragment,{children:[e.jsx(Y,{"data-slot":"icon",className:"animate-spin"}),"Checking..."]}):e.jsxs(e.Fragment,{children:[e.jsx(Qs,{"data-slot":"icon"}),"Check for Updates"]})})]})]})}const za=[{value:7.8,label:"7.8 kHz"},{value:10.4,label:"10.4 kHz"},{value:15.6,label:"15.6 kHz"},{value:20.8,label:"20.8 kHz"},{value:31.25,label:"31.25 kHz"},{value:41.7,label:"41.7 kHz"},{value:62.5,label:"62.5 kHz"},{value:125,label:"125 kHz"},{value:250,label:"250 kHz"},{value:500,label:"500 kHz"}],Ka=[5,6,7,8,9,10,11,12],Va=[{value:5,label:"4/5"},{value:6,label:"4/6"},{value:7,label:"4/7"},{value:8,label:"4/8"}],Ha=[{value:0,label:"1-byte — 0x00"},{value:1,label:"2-byte — 0x0000"},{value:2,label:"3-byte — 0x000000"}],Ws={RADIO_CONFIG:"radioConfig"};function $a(t){if(!t)return"Never";const n=Date.now()-t*1e3,c=Math.floor(n/6e4),r=Math.floor(n/36e5),o=Math.floor(n/864e5);return c<1?"Just now":c<60?`${c}m ago`:r<24?`${r}h ago`:o<365?`${o}d ago`:`${Math.floor(o/365)}y ago`}function at({node:t,level:l,expandedKeys:n,selectedKeyId:c,onSelect:r,onToggle:o}){const y=n.has(t.id),p=c===t.id,h=t.children&&t.children.length>0;return e.jsxs("div",{children:[e.jsxs("div",{className:b("flex items-center gap-2 h-[36px] px-3 rounded-full cursor-pointer transition-colors",p?"bg-sys-blue/15":"hover:bg-subtle-fill"),style:{marginLeft:`${l*16}px`},onClick:()=>r(t.id),children:[h?e.jsx("button",{onClick:u=>{u.stopPropagation(),o(t.id)},className:"p-1 hover:bg-subtle-fill-hover rounded-full transition-colors",children:e.jsx(et,{className:b("w-3.5 h-3.5 text-fg-muted transition-transform",y&&"rotate-90")})}):e.jsx("span",{className:"w-5"}),e.jsx(ga,{className:"w-4 h-4 text-sys-blue flex-shrink-0"}),e.jsx("span",{className:"type-data text-fg-primary flex-1 truncate",children:t.name}),e.jsx("span",{className:"type-data-xs text-fg-muted hidden sm:inline",title:t.last_used?new Date(t.last_used*1e3).toLocaleString():void 0,children:$a(t.last_used)}),e.jsx("span",{className:b("type-data-xs px-2 py-0.5 rounded-full",t.floodPolicy==="allow"?"bg-sys-green/15 text-sys-green":"bg-sys-red/15 text-sys-red"),children:t.floodPolicy==="allow"?"Allow":"Deny"})]}),h&&y&&e.jsx("div",{children:t.children.map(u=>e.jsx(at,{node:u,level:l+1,expandedKeys:n,selectedKeyId:c,onSelect:r,onToggle:o},u.id))})]})}function Ua(){const[t,l]=a.useState(ea),[n,c]=a.useState(sa),[r,o]=a.useState(!1),y=a.useCallback(u=>{l(u),ta(u)},[]),p=a.useCallback(u=>{const i=parseFloat(u.target.value);c(i),aa(i)},[]),h=a.useCallback(async()=>{r||(o(!0),await na(),o(!1))},[r]);return e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsx(fe,{enabled:t,onChange:y,label:e.jsxs(e.Fragment,{children:["Sound: ",e.jsx("span",{className:t?"text-sys-green":"text-sys-red",children:t?"On":"Off"})]}),tooltip:"Enable or disable UI sound effects and haptic audio feedback",size:"md"}),e.jsx(F,{color:"primary",outline:!0,onClick:h,disabled:!t||r,className:"w-16 justify-center",children:r?"…":"Test"})]}),e.jsxs("div",{className:b("flex items-center gap-3",!t&&"opacity-40 pointer-events-none"),children:[e.jsx("span",{className:"type-label text-fg-muted w-14 shrink-0",children:"Volume"}),e.jsx("input",{type:"range",min:0,max:1,step:.05,value:n,onChange:p,className:"flex-1 accent-sys-blue h-1.5 cursor-pointer"}),e.jsxs("span",{className:"data-box w-16 justify-center",children:[Math.round(n*100),"%"]})]})]})}function pn(){var Rs,Ps,Ms,As,Ds,Ls,Is,Os,zs,Ks;const{stats:t,setMode:l,clearModeMutation:n,setDutyCycleConfig:c,clearDutyCycleMutation:r,fetchStats:o,startMutation:y,clearMutation:p}=cs(),h=Mt(),u=(Rs=t==null?void 0:t.config)==null?void 0:Rs.radio,i=(Ps=t==null?void 0:t.config)==null?void 0:Ps.repeater,j=(Ms=t==null?void 0:t.config)==null?void 0:Ms.duty_cycle,D=(t==null?void 0:t.node_name)||((As=t==null?void 0:t.config)==null?void 0:As.node_name)||"Unknown Node",v=(i==null?void 0:i.mode)??"forward",E=(j==null?void 0:j.enforcement_enabled)??!1,C=(Ds=t==null?void 0:t.config)==null?void 0:Ds.delays,N=a.useRef(!0);a.useEffect(()=>(N.current=!0,()=>{N.current=!1}),[]);const m=(Ls=t==null?void 0:t.config)==null?void 0:Ls.web,[g,k]=a.useState([]),[R,J]=a.useState(!1),[V,w]=a.useState(null),[T,f]=a.useState(!1),[O,P]=a.useState(""),[z,H]=a.useState(null),[U,se]=a.useState(!1),[Q,d]=a.useState(null),[M,G]=a.useState(!1),[ds,us]=a.useState(!1),[ms,fs]=a.useState(!1),[Ke,xs]=a.useState(""),[xe,ps]=a.useState(""),[Ve,hs]=a.useState(""),[te,pe]=a.useState("idle"),[ys,ae]=a.useState(null),[Z,nt]=a.useState([]),[lt,gs]=a.useState(!1),[bs,L]=a.useState(null),[He,rt]=a.useState(()=>localStorage.getItem("pymc:unscopedFloodPolicy")==="allow"?"allow":"deny"),js=a.useCallback(s=>{rt(s),localStorage.setItem("pymc:unscopedFloodPolicy",s)},[]),[vs,Ns]=a.useState(!1),[K,$e]=a.useState(null),[ot,it]=a.useState(new Set),[ke,he]=a.useState(!1),[Se,oe]=a.useState(!1),[Ce,ie]=a.useState(!1),[I,ee]=a.useState(null),[ne,ye]=a.useState(null),[Te,Fe]=a.useState(""),[Ue,ge]=a.useState("allow"),[W,ce]=a.useState(!1),[Ee,ws]=a.useState(!0),[be,Be]=a.useState(!1),[ct,_s]=a.useState(!0),[je,le]=a.useState("idle"),[dt,qe]=a.useState(!1),[Ge,ut]=a.useState(!1),ks=a.useCallback(()=>{const s=C==null?void 0:C.tx_delay_factor;return s&&typeof s=="object"&&"parsedValue"in s?s.parsedValue??1:typeof s=="number"?s:1},[C]),Ss=a.useCallback(()=>{const s=C==null?void 0:C.direct_tx_delay_factor;return typeof s=="number"?s:.5},[C]),Je=a.useCallback(()=>{const s=j==null?void 0:j.max_airtime_percent;return typeof s=="number"?s:s&&typeof s=="object"&&"parsedValue"in s?s.parsedValue??6:6},[j]),We=as(v==="forward",async s=>{await l(s?"forward":"monitor")},n),Xe=as(E,async s=>{await c({max_airtime_percent:Je(),enforcement_enabled:s})},r),Ye=as((m==null?void 0:m.cors_enabled)??!1,async s=>{var _;const x=await qs({web:{cors_enabled:s}});if(!x.success&&!((_=x.data)!=null&&_.persisted))throw new Error("CORS update failed");await o()}),$=a.useCallback(async s=>{y(Ws.RADIO_CONFIG);try{const x=await Ys(s);if(!x.success)throw new Error(x.error||"Failed to save");await o({force:!0}).catch(()=>{})}finally{p(Ws.RADIO_CONFIG)}},[y,p,o]);a.useEffect(()=>{(async()=>{_s(!0);try{const x=await Gt();x.success&&x.data?Be(x.data.exists):Be(!1);const _=m==null?void 0:m.web_path;ws(!_||_==="")}catch{Be(!1)}finally{_s(!1)}})()},[m==null?void 0:m.web_path]);const Cs=async s=>{var x;if(je!=="loading"){le("loading");try{const _=await qs({web:{web_path:s?null:"/opt/pymc_console/web/html"}});if(!N.current)return;_.success||(x=_.data)!=null&&x.persisted?(ws(s),le("success"),qe(!0),setTimeout(()=>{N.current&&le("idle")},2e3)):(le("error"),setTimeout(()=>{N.current&&le("idle")},2e3))}catch{if(!N.current)return;le("error"),setTimeout(()=>{N.current&&le("idle")},2e3)}}},mt=async()=>{if(!Ge){ut(!0);try{await Jt(),qe(!1),setTimeout(()=>{window.location.reload()},2e3)}catch{qe(!1),setTimeout(()=>{window.location.reload()},3e3)}}},Re=a.useCallback(async()=>{J(!0),w(null);try{const s=await ka();k(s.tokens||[])}catch(s){w(s instanceof Error?s.message:"Failed to fetch tokens")}finally{J(!1)}},[]);a.useEffect(()=>{Re()},[Re]);const Ts=async()=>{if(!O.trim()){w("Token name is required");return}G(!0),w(null);try{const s=await Sa(O.trim());H(s.token||null),f(!1),se(!0),P(""),await Re()}catch(s){w(s instanceof Error?s.message:"Failed to create token")}finally{G(!1)}},ft=async()=>{if(Q){us(!0),w(null);try{await Ca(Q.id),await Re(),d(null)}catch(s){w(s instanceof Error?s.message:"Failed to revoke token")}finally{us(!1)}}},xt=async()=>{z&&(await Kt(z),fs(!0),setTimeout(()=>fs(!1),2e3))},pt=s=>s?new Date(s*1e3).toLocaleString():"Never",ht=async s=>{if(s==null||s.preventDefault(),te!=="loading"){if(pe("error"),!Ke||!xe||!Ve){ae("Enter your current password, new password, and confirmation.");return}if(xe.length<8){ae("New password must be at least 8 characters long.");return}if(xe!==Ve){ae("New password and confirmation do not match.");return}pe("loading"),ae(null);try{const x=await Ta(Ke,xe);if(!N.current)return;x.success?(xs(""),ps(""),hs(""),pe("success"),ae(x.message||"Password changed successfully. Use the new password next time you log in.")):(pe("error"),ae(x.error||"Failed to change password."))}catch(x){if(!N.current)return;pe("error"),ae(x instanceof Error?x.message:"Failed to change password.")}}},Fs=a.useCallback(s=>{const x=new Map,_=[];return s.forEach(A=>{const Pe={id:A.id,name:A.name,floodPolicy:A.flood_policy,transport_key:A.transport_key,last_used:A.last_used,parent_id:A.parent_id,children:[]};x.set(A.id,Pe)}),x.forEach(A=>{A.parent_id&&x.has(A.parent_id)?x.get(A.parent_id).children.push(A):_.push(A)}),_},[]),ve=a.useCallback(async()=>{gs(!0),L(null);try{const s=await Vt();if(s.success&&s.data){const x=Fs(s.data);nt(x),$e(_=>{if(_===null)return null;const A=Pe=>{for(const Qe of Pe)if(Qe.id===_||Qe.children&&A(Qe.children))return!0;return!1};return A(x)?_:null})}else L(s.error||"Failed to load transport keys")}catch(s){L(s instanceof Error?s.message:"Unknown error occurred")}finally{gs(!1)}},[Fs]);a.useEffect(()=>{ve(),Ht().then(s=>{s.success&&s.data&&js(s.data.unscoped_flood_allow?"allow":"deny")}).catch(()=>{})},[ve]);const re=a.useCallback((s,x)=>{for(const _ of s){if(_.id===x)return _;if(_.children){const A=re(_.children,x);if(A)return A}}return null},[]),yt=a.useCallback(()=>{if(!K)return;const s=re(Z,K);return s==null?void 0:s.name},[K,Z,re]),gt=a.useCallback(s=>{it(x=>{const _=new Set(x);return _.has(s)?_.delete(s):_.add(s),_})},[]),bt=async s=>{Ns(!0),L(null);try{const x=await $t(s==="allow");x.success?js(s):L(x.error||"Failed to update unscoped flood policy")}catch(x){L(x instanceof Error?x.message:"Failed to update unscoped flood policy")}finally{Ns(!1)}},Es=async()=>{if(!Te.trim()){L("Key name is required");return}ce(!0),L(null);try{const s=await Ut({name:Te.trim(),flood_policy:Ue,parent_id:K??void 0});s.success?(await ve(),he(!1),Fe(""),ge("allow")):L(s.error||"Failed to add transport key")}catch(s){L(s instanceof Error?s.message:"Failed to add transport key")}finally{ce(!1)}},jt=async()=>{if(I){ce(!0),L(null);try{const s=await Bt(I.id,{name:I.name,flood_policy:I.floodPolicy});s.success?(await ve(),oe(!1),ee(null)):L(s.error||"Failed to update transport key")}catch(s){L(s instanceof Error?s.message:"Failed to update transport key")}finally{ce(!1)}}},vt=async()=>{if(ne){ce(!0),L(null);try{const s=await qt(ne.id);s.success?(await ve(),ie(!1),ye(null),$e(null)):L(s.error||"Failed to delete transport key")}catch(s){L(s instanceof Error?s.message:"Failed to delete transport key")}finally{ce(!1)}}},Nt=a.useCallback(()=>{if(!K)return;const s=re(Z,K);s&&(ee({...s}),oe(!0))},[K,Z,re]);a.useEffect(()=>{const s=_=>{_.key==="Escape"&&(ke&&(he(!1),Fe(""),ge("allow")),Se&&(oe(!1),ee(null)),Ce&&(ie(!1),ye(null)),T&&(f(!1),P("")),U&&(se(!1),H(null)))};if(ke||Se||Ce||T||U)return window.addEventListener("keydown",s),()=>window.removeEventListener("keydown",s)},[ke,Se,Ce,T,U]);const wt=a.useCallback(()=>{if(!K)return;const s=re(Z,K);s&&(ye(s),ie(!0))},[K,Z,re]);return e.jsxs(la,{children:[e.jsx(ra,{title:"Configuration",icon:e.jsx($s,{})}),e.jsxs(oa,{ready:h,children:[e.jsxs(X,{template:"standard",children:[e.jsx(ue,{span:12,md:6,children:e.jsxs(q,{neomorphic:!0,children:[e.jsx(B,{icon:e.jsx(At,{}),title:"Repeater Settings"}),i?e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsx("div",{className:"col-span-2",children:e.jsx(S,{label:"Node Name",value:D,onSave:async s=>$({node_name:s}),placeholder:"Enter node name",maxLength:50})}),e.jsx(S,{label:"Latitude",value:i.latitude!==0?i.latitude.toFixed(6):"Not set",editValue:String(i.latitude),type:"number",suffix:"°",step:1e-6,min:-90,max:90,onSave:async s=>$({latitude:parseFloat(s)})}),e.jsx(S,{label:"Longitude",value:i.longitude!==0?i.longitude.toFixed(6):"Not set",editValue:String(i.longitude),type:"number",suffix:"°",step:1e-6,min:-180,max:180,onSave:async s=>$({longitude:parseFloat(s)})}),e.jsx(S,{label:"Advert Interval",value:i.send_advert_interval_hours>0?`${i.send_advert_interval_hours}h`:"Disabled",editValue:String(i.send_advert_interval_hours),type:"number",suffix:"hours",min:0,max:48,description:"0 = disabled, 3-48 hours",onSave:async s=>$({flood_advert_interval_hours:parseInt(s,10)})}),e.jsx(S,{label:"Score-based TX",value:i.use_score_for_tx?"Enabled":"Disabled",readOnly:!0})]}):e.jsx("p",{className:"text-fg-muted",children:"Loading repeater settings..."})]})}),e.jsx(ue,{span:12,md:6,children:e.jsxs(q,{neomorphic:!0,children:[e.jsx(B,{icon:e.jsx(pa,{}),title:"Radio Configuration"}),u?e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsx(S,{label:"Frequency",value:Dt(u.frequency),editValue:(u.frequency/1e6).toFixed(3),type:"number",suffix:"MHz",step:.001,min:400,max:930,onSave:async s=>$({frequency_mhz:parseFloat(s)})}),e.jsx(S,{label:"TX Power",value:`${u.tx_power} dBm`,editValue:String(u.tx_power),type:"number",suffix:"dBm",min:2,max:30,onSave:async s=>$({tx_power:parseInt(s,10)})}),e.jsx(Ae,{label:"Bandwidth",value:Lt(u.bandwidth),editValue:u.bandwidth/1e3,options:za,onSave:async s=>$({bandwidth_khz:parseFloat(s)})}),e.jsx(Ae,{label:"Spreading Factor",value:`SF${u.spreading_factor}`,editValue:u.spreading_factor,options:Ka.map(s=>({value:s,label:`SF${s}`})),onSave:async s=>$({spreading_factor:parseInt(s,10)})}),e.jsx(Ae,{label:"Coding Rate",value:`4/${u.coding_rate}`,editValue:u.coding_rate,options:Va,onSave:async s=>$({coding_rate:parseInt(s,10)})}),e.jsx(S,{label:"Preamble",value:`${u.preamble_length} symbols`,readOnly:!0})]}):e.jsx("p",{className:"text-fg-muted",children:"Loading radio configuration..."})]})})]}),e.jsxs(X,{template:"standard",children:[e.jsx(ue,{span:12,md:6,children:e.jsxs(q,{neomorphic:!0,children:[e.jsx(B,{icon:e.jsx(It,{}),title:"Operating Mode"}),e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsx(fe,{enabled:We.enabled,onChange:We.toggle,label:"Repeat",tooltip:"Toggles repeater or monitor mode. In monitor mode, the repeater will not repeat packets from the mesh.",status:We.status,size:"md"}),e.jsx(fe,{enabled:Xe.enabled,onChange:Xe.toggle,label:"Duty Cycle",tooltip:"Limit transmission time to comply with airtime utilization regulations",status:Xe.status,size:"md"}),e.jsx(S,{label:"Max Airtime",value:`${Je().toFixed(1)}%`,editValue:String(Je()),type:"number",suffix:"%",step:.5,min:.1,max:100,onSave:async s=>{await c({max_airtime_percent:parseFloat(s),enforcement_enabled:E}),r()}}),e.jsx(S,{label:"Flood TX Delay",value:`${ks().toFixed(2)}x`,editValue:String(ks()),type:"number",suffix:"x",step:.1,min:0,max:5,onSave:async s=>$({tx_delay_factor:parseFloat(s)})}),e.jsx(S,{label:"Direct TX Delay",value:`${Ss().toFixed(2)}x`,editValue:String(Ss()),type:"number",suffix:"x",description:"MeshCore TX delay factor, not seconds",step:.1,min:0,max:5,onSave:async s=>$({direct_tx_delay_factor:parseFloat(s)})}),e.jsx(S,{label:"Max Flood Hops",value:String((i==null?void 0:i.max_flood_hops)??3),type:"number",min:0,max:64,description:"0–64 hops",onSave:async s=>$({max_flood_hops:parseInt(s,10)})})]}),e.jsx("div",{className:"mt-4",children:e.jsx(Ae,{label:"Path Hash Size",value:`${(((Os=(Is=t==null?void 0:t.config)==null?void 0:Is.mesh)==null?void 0:Os.path_hash_mode)??0)+1}-byte`,editValue:((Ks=(zs=t==null?void 0:t.config)==null?void 0:zs.mesh)==null?void 0:Ks.path_hash_mode)??0,options:Ha,onSave:async s=>$({path_hash_mode:parseInt(s,10)})})})]})}),e.jsx(ue,{span:12,md:6,children:e.jsxs(q,{neomorphic:!0,children:[e.jsx(B,{icon:e.jsx($s,{}),title:"App Settings"}),e.jsx("p",{className:"type-body-sm text-fg-muted mb-4",children:"Appearance and web server options."}),e.jsxs("div",{className:"space-y-4",children:[e.jsx(fe,{enabled:Ye.enabled,onChange:Ye.toggle,label:"Enable CORS",tooltip:"Allow cross-origin API access",status:Ye.status,size:"md"}),e.jsx("div",{children:e.jsx(Ea,{})}),e.jsx(Ua,{})]})]})})]}),e.jsx(X,{template:"auto",children:e.jsx(Aa,{})}),e.jsx(X,{template:"auto",children:e.jsxs(q,{neomorphic:!0,children:[e.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 mb-4",children:[e.jsx(B,{icon:e.jsx(Us,{}),title:"Region Configuration"}),e.jsxs("div",{className:"flex gap-2 flex-wrap",children:[e.jsxs(F,{color:"primary",outline:!0,onClick:()=>he(!0),children:[e.jsx(Bs,{"data-slot":"icon"}),"Add"]}),e.jsx(F,{color:"primary",outline:!0,onClick:Nt,disabled:!K,children:"Edit"}),e.jsx(F,{color:"danger",outline:!0,onClick:wt,disabled:!K,children:"Delete"})]})]}),e.jsxs("div",{className:"bg-subtle-fill radius-inner p-4 mb-4",children:[e.jsx("p",{className:"type-body-sm text-fg-muted mb-3",children:"Region configuration controls where flood packets are allowed to travel. Add named regions, nest child regions under parents, and set the flood policy for each branch of the region tree."}),e.jsx(fe,{enabled:He==="allow",onChange:s=>bt(s?"allow":"deny"),label:e.jsxs(e.Fragment,{children:["Unscoped Flood: ",e.jsx("span",{className:He==="allow"?"text-sys-green":"text-sys-red",children:He==="allow"?"Allow":"Deny"})]}),tooltip:"Forward flood packets that have no region transport key. This is independent of region scope configuration.",status:vs?"loading":"idle",disabled:vs,dangerOff:!0,size:"md",className:"flex-row-reverse justify-between w-full"})]}),bs&&e.jsx("div",{className:"type-body-sm mb-3 px-3 py-2 rounded-full text-sys-red bg-sys-red/10",children:bs}),e.jsx("div",{className:"bg-subtle-fill radius-inner p-4 min-h-[120px]",children:lt?e.jsxs("div",{className:"flex items-center justify-center py-6",children:[e.jsx(Y,{className:"w-5 h-5 animate-spin text-sys-blue mr-2"}),e.jsx("span",{className:"type-body-sm text-fg-muted",children:"Loading transport keys..."})]}):Z.length===0?e.jsxs("div",{className:"text-center py-6",children:[e.jsx("div",{className:"w-12 h-12 rounded-full bg-subtle-fill mx-auto mb-3 flex items-center justify-center",children:e.jsx(Us,{className:"w-6 h-6 text-fg-muted/50"})}),e.jsx("p",{className:"type-body text-fg-muted",children:"No regions configured"}),e.jsx("p",{className:"type-body-sm text-fg-muted/70 mt-1",children:"Add your first region to start configuring flood scope."})]}):e.jsx("div",{className:"space-y-1",children:Z.map(s=>e.jsx(at,{node:s,level:0,expandedKeys:ot,selectedKeyId:K,onSelect:$e,onToggle:gt},s.id))})})]})}),e.jsxs(X,{template:"standard",children:[e.jsx(ue,{span:12,md:6,children:e.jsx(La,{})}),e.jsx(ue,{span:12,md:6,children:e.jsxs(q,{neomorphic:!0,className:"h-full flex flex-col",children:[e.jsx(B,{icon:e.jsx(Ze,{}),title:"API Tokens"}),V&&e.jsx("div",{className:"type-body-sm mb-3 px-3 py-2 rounded-full text-sys-red bg-sys-red/10",children:V}),e.jsx("div",{className:"flex-1 flex flex-col min-h-0",children:R&&g.length===0?e.jsxs("div",{className:"flex-1 flex items-center justify-center",children:[e.jsx(Y,{className:"w-4 h-4 animate-spin text-sys-blue mr-2"}),e.jsx("span",{className:"type-body-sm text-fg-muted",children:"Loading tokens..."})]}):g.length>0?e.jsx("div",{className:"space-y-1.5 flex-1 overflow-y-auto mb-3",children:g.map(s=>e.jsxs("div",{className:"bg-subtle-fill hover:bg-subtle-fill-strong radius-inner px-3 py-2 transition-base flex items-center justify-between gap-2",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("h3",{className:"type-body-sm text-fg-primary font-medium truncate",children:s.name}),e.jsxs("span",{className:"type-data-xs text-fg-muted truncate",children:["Last used: ",pt(s.last_used)]})]}),e.jsx("button",{type:"button",onClick:()=>d({id:s.id,name:s.name}),disabled:ds,className:"text-sys-red/70 hover:text-sys-red transition-colors flex-shrink-0 p-1",title:"Revoke token",children:e.jsx(Zs,{className:"w-3.5 h-3.5"})})]},s.id))}):e.jsxs("div",{className:"bg-subtle-fill radius-inner flex-1 flex flex-col items-center justify-center mb-3",children:[e.jsx(Ze,{className:"w-5 h-5 text-fg-muted/50 mb-2"}),e.jsx("p",{className:"type-body-sm text-fg-muted",children:"No API tokens"})]})}),e.jsxs(F,{color:"primary",outline:!0,onClick:()=>f(!0),className:"w-full justify-center mt-auto",children:[e.jsx(Bs,{"data-slot":"icon"}),"Create Token"]})]})})]}),e.jsx(X,{template:"auto",children:e.jsxs(q,{neomorphic:!0,children:[e.jsx(B,{icon:e.jsx(Na,{}),title:"Web Frontend"}),e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"space-y-3",children:[e.jsxs("label",{className:b("flex items-start gap-3 p-4 bg-subtle-fill radius-card border-2 cursor-pointer transition-base",Ee?"border-sys-blue bg-sys-blue/5":"border-transparent hover:border-sys-blue/30",je==="loading"&&"opacity-50 pointer-events-none"),children:[e.jsx("input",{type:"radio",name:"frontend",checked:Ee,onChange:()=>Cs(!0),disabled:je==="loading",className:"mt-1 h-4 w-4 text-sys-blue focus:ring-sys-blue focus:ring-offset-bg-elevated"}),e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("div",{className:"type-body text-fg-primary font-medium",children:"Default Frontend"}),e.jsx("a",{href:"https://github.com/pyMC-dev/pyMC_Repeater",target:"_blank",rel:"noopener noreferrer",onClick:s=>s.stopPropagation(),className:"type-data-xs bg-sys-cyan/15 text-sys-cyan px-2.5 py-0.5 rounded-full font-medium hover:bg-sys-cyan/25 transition-colors",children:"@RightUp"})]}),e.jsx("div",{className:"type-body-sm text-fg-muted mt-1",children:"Built-in openHop Repeater web interface"}),e.jsx("div",{className:"type-data-xs text-fg-muted/60 mt-1",children:"/opt/openhop_repeater/repeater/web/html"})]})]}),e.jsxs("label",{className:b("flex items-start gap-3 p-4 bg-subtle-fill radius-card border-2 cursor-pointer transition-base",Ee?"border-transparent hover:border-sys-blue/30":"border-sys-blue bg-sys-blue/5",je==="loading"&&"opacity-50 pointer-events-none"),children:[e.jsx("input",{type:"radio",name:"frontend",checked:!Ee,onChange:()=>Cs(!1),disabled:je==="loading",className:"mt-1 h-4 w-4 text-sys-blue focus:ring-sys-blue focus:ring-offset-bg-elevated"}),e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("div",{className:"type-body text-fg-primary font-medium",children:"openHop Console"}),e.jsx("span",{className:"type-data-xs bg-sys-indigo/15 text-sys-indigo px-2.5 py-0.5 rounded-full font-medium",children:"@Treehouse ⚡"})]}),e.jsx("div",{className:"type-body-sm text-fg-muted mt-1",children:"Alternative web interface for openHop Repeater"}),e.jsx("div",{className:"type-data-xs text-fg-muted/60 mt-1",children:"/opt/pymc_console/web/html"})]})]})]}),!ct&&e.jsx("div",{className:b("bg-subtle-fill radius-card p-4",be?"border border-sys-green/20":"border border-sys-cyan/20"),children:e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("div",{className:b("w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0",be?"bg-sys-green/20":"bg-sys-cyan/20"),children:be?e.jsx(os,{className:"w-4 h-4 text-sys-green"}):e.jsx(we,{className:"w-4 h-4 text-sys-cyan"})}),e.jsxs("div",{className:"flex-1",children:[e.jsx("h4",{className:"type-body text-fg-primary font-medium",children:be?"openHop Console has been detected":"openHop Console Not Installed"}),be?e.jsxs("p",{className:"type-body-sm text-fg-muted mt-1",children:["openHop Console is installed at ",e.jsx("code",{className:"type-code bg-sys-green/10 px-1.5 py-0.5 rounded-full text-sys-green",children:"/opt/pymc_console/web/html"})]}):e.jsxs(e.Fragment,{children:[e.jsxs("p",{className:"type-body-sm text-fg-muted mt-1 mb-3",children:["openHop Console must be installed at ",e.jsx("code",{className:"type-code bg-sys-cyan/10 px-1.5 py-0.5 rounded-full text-sys-cyan",children:"/opt/pymc_console/web/html"})," before selecting this option."]}),e.jsxs(F,{color:"primary",outline:!0,href:"https://github.com/dmduran12/pymc_console-dist",children:[e.jsx(st,{"data-slot":"icon"}),"openHop Console Install Instructions"]})]})]})]})}),dt&&e.jsx("div",{className:"bg-subtle-fill radius-card p-4 border border-sys-indigo/30",children:e.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3",children:[e.jsxs("div",{className:"flex items-start gap-3 flex-1",children:[e.jsx("div",{className:"w-8 h-8 rounded-full bg-sys-indigo/20 flex items-center justify-center flex-shrink-0",children:e.jsx(Le,{className:"w-4 h-4 text-sys-indigo"})}),e.jsxs("div",{className:"flex-1",children:[e.jsx("h4",{className:"type-body text-fg-primary font-medium",children:"Service restart required"}),e.jsx("p",{className:"type-body-sm text-fg-muted mt-1",children:"Web frontend changes will take effect after restarting the openhop-repeater service."})]})]}),e.jsx(F,{color:"warning",outline:!0,onClick:mt,disabled:Ge,children:Ge?e.jsxs(e.Fragment,{children:[e.jsx(Y,{"data-slot":"icon",className:"animate-spin"}),"Restarting..."]}):e.jsxs(e.Fragment,{children:[e.jsx(Qs,{"data-slot":"icon"}),"Restart Now"]})})]})})]})]})}),e.jsx(X,{template:"auto",children:e.jsx(Oa,{})}),t&&e.jsx(X,{template:"auto",children:e.jsx(Zt,{nodeName:D,publicKey:t.public_key})}),t&&((i==null?void 0:i.latitude)===0||!(i!=null&&i.latitude))&&((i==null?void 0:i.longitude)===0||!(i!=null&&i.longitude))&&e.jsx(X,{template:"auto",children:e.jsx(Pa,{})}),e.jsx(X,{template:"auto",children:e.jsxs(q,{neomorphic:!0,children:[e.jsx(B,{icon:e.jsx(Ze,{}),title:"Change Password"}),e.jsx("p",{className:"type-body-sm text-fg-muted mb-4",children:"Update the admin password used to sign in to this repeater. The new password must be at least 8 characters long."}),e.jsxs("form",{onSubmit:ht,className:"space-y-4",children:[ys&&e.jsx("div",{className:b("type-body-sm px-3 py-2 rounded-full",te==="success"?"text-sys-green bg-sys-green/10":"text-sys-red bg-sys-red/10"),children:ys}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4",children:[e.jsx(de,{label:"Current Password",type:"password",value:Ke,onChange:s=>xs(s.target.value),autoComplete:"current-password",disabled:te==="loading",required:!0}),e.jsx(de,{label:"New Password",type:"password",value:xe,onChange:s=>ps(s.target.value),autoComplete:"new-password",minLength:8,disabled:te==="loading",required:!0}),e.jsx(de,{label:"Confirm New Password",type:"password",value:Ve,onChange:s=>hs(s.target.value),autoComplete:"new-password",minLength:8,disabled:te==="loading",required:!0})]}),e.jsx("div",{className:"flex justify-end",children:e.jsx(F,{type:"submit",color:"primary",disabled:te==="loading",children:te==="loading"?e.jsxs(e.Fragment,{children:[e.jsx(Y,{"data-slot":"icon",className:"animate-spin"}),"Changing..."]}):"Change Password"})})]})]})})]}),T&&e.jsx("div",{className:"fixed inset-0 z-[10010] flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm",onClick:s=>s.target===s.currentTarget&&f(!1),role:"dialog","aria-modal":"true","aria-labelledby":"create-token-modal-title",children:e.jsxs("div",{className:"surface-modal radius-card p-6 max-w-md w-full",children:[e.jsx("h3",{id:"create-token-modal-title",className:"type-micro mb-4",children:"Create API Token"}),e.jsxs("div",{className:"space-y-4",children:[e.jsx(de,{label:"Token Name",type:"text",value:O,onChange:s=>P(s.target.value),placeholder:"e.g., Production Server, CI/CD Pipeline",description:"Give your token a descriptive name to identify its purpose",onKeyDown:s=>s.key==="Enter"&&Ts(),autoFocus:!0}),e.jsxs("div",{className:"flex justify-end gap-3 mt-6",children:[e.jsx(F,{color:"muted",outline:!0,onClick:()=>{f(!1),P(""),w(null)},disabled:M,children:"Cancel"}),e.jsx(F,{color:"primary",onClick:Ts,disabled:M||!O.trim(),children:M?e.jsxs(e.Fragment,{children:[e.jsx(Y,{"data-slot":"icon",className:"animate-spin"}),"Creating..."]}):"Create Token"})]})]})]})}),U&&z&&e.jsx("div",{className:"fixed inset-0 z-[10010] flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm",onClick:s=>s.target===s.currentTarget&&se(!1),role:"dialog","aria-modal":"true","aria-labelledby":"show-token-modal-title",children:e.jsxs("div",{className:"surface-modal radius-card p-6 max-w-lg w-full",children:[e.jsx("h3",{id:"show-token-modal-title",className:"type-micro mb-4",children:"Token Created Successfully"}),e.jsxs("div",{className:"space-y-4",children:[e.jsx("div",{className:"bg-sys-indigo/10 border border-sys-indigo/30 radius-inner p-3",children:e.jsxs("div",{className:"flex gap-2",children:[e.jsx(Le,{className:"w-4 h-4 text-sys-indigo flex-shrink-0 mt-0.5"}),e.jsxs("div",{className:"text-sm text-fg-secondary",children:[e.jsx("strong",{children:"Save this token now!"})," For security reasons, it will not be shown again."]})]})}),e.jsxs("div",{children:[e.jsx("label",{className:"type-label text-fg-primary block mb-1.5",children:"Your API Token"}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(Ot,{value:z,readOnly:!0,className:"flex-1 font-mono"}),e.jsxs(F,{color:"primary",outline:!0,onClick:xt,children:[ms?e.jsx(Ne,{"data-slot":"icon",className:"text-sys-green"}):e.jsx(zt,{"data-slot":"icon"}),ms?"Copied!":"Copy"]})]})]}),e.jsxs("div",{className:"bg-sys-cyan/10 border border-sys-cyan/30 radius-inner p-3",children:[e.jsx("p",{className:"text-sm text-fg-secondary mb-2",children:e.jsx("strong",{children:"Usage Example:"})}),e.jsxs("code",{className:"block bg-sys-cyan/20 px-3 py-2 rounded type-code text-sys-cyan overflow-x-auto",children:['curl -H "X-API-Key: ',z.slice(0,12),'..." ',window.location.origin,"/api/stats"]})]}),e.jsx("div",{className:"flex justify-end mt-6",children:e.jsx(F,{color:"primary",onClick:()=>{se(!1),H(null)},children:"Done"})})]})]})}),e.jsx(Wt,{isOpen:!!Q,title:"Revoke API Token",message:`Are you sure you want to revoke the token '${Q==null?void 0:Q.name}'? This action cannot be undone.`,confirmLabel:ds?"Revoking...":"Revoke",cancelLabel:"Cancel",variant:"danger",onConfirm:ft,onCancel:()=>d(null)}),ke&&e.jsx("div",{className:"fixed inset-0 z-[10010] flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm",onClick:s=>s.target===s.currentTarget&&he(!1),role:"dialog","aria-modal":"true","aria-labelledby":"add-key-modal-title",children:e.jsxs("div",{className:"surface-modal radius-card p-6 max-w-md w-full",children:[e.jsx("h3",{id:"add-key-modal-title",className:"type-micro mb-4",children:"Add Region"}),e.jsxs("div",{className:"space-y-4",children:[K&&e.jsxs("div",{className:"bg-sys-cyan/10 border border-sys-cyan/30 radius-inner p-3 text-sm text-fg-secondary",children:["Adding region as child of: ",e.jsx("strong",{className:"text-fg-primary",children:yt()})]}),e.jsx(de,{label:"Region Name",type:"text",value:Te,onChange:s=>Fe(s.target.value),placeholder:"e.g., uk, au, us",onKeyDown:s=>s.key==="Enter"&&Es(),autoFocus:!0}),e.jsx("p",{className:"type-data-xs text-fg-muted -mt-2",children:"The # prefix is added automatically for regions."}),e.jsxs("div",{children:[e.jsx("label",{className:"type-label text-fg-muted block mb-2",children:"Flood Policy"}),e.jsxs("div",{className:"toggle-group w-full",children:[e.jsx("button",{onClick:()=>ge("deny"),className:b("toggle-group-item flex-1",Ue==="deny"&&"active !text-sys-red"),children:"Deny"}),e.jsx("button",{onClick:()=>ge("allow"),className:b("toggle-group-item flex-1",Ue==="allow"&&"active !text-sys-green"),children:"Allow"})]})]}),e.jsxs("div",{className:"flex justify-end gap-3 mt-6",children:[e.jsx(F,{color:"muted",outline:!0,onClick:()=>{he(!1),Fe(""),ge("allow")},disabled:W,children:"Cancel"}),e.jsx(F,{color:"primary",onClick:Es,disabled:W||!Te.trim(),children:W?e.jsxs(e.Fragment,{children:[e.jsx(Y,{"data-slot":"icon",className:"animate-spin"}),"Adding..."]}):"Add Region"})]})]})]})}),Se&&I&&e.jsx("div",{className:"fixed inset-0 z-[10010] flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm",onClick:s=>s.target===s.currentTarget&&oe(!1),role:"dialog","aria-modal":"true","aria-labelledby":"edit-key-modal-title",children:e.jsxs("div",{className:"surface-modal radius-card p-6 max-w-md w-full",children:[e.jsx("h3",{id:"edit-key-modal-title",className:"type-micro mb-4",children:"Edit Region"}),e.jsxs("div",{className:"space-y-4",children:[e.jsx(de,{label:"Region Name",type:"text",value:I.name,onChange:s=>ee({...I,name:s.target.value}),autoFocus:!0}),e.jsx("p",{className:"type-data-xs text-fg-muted -mt-2",children:"The # prefix is added automatically for regions."}),e.jsxs("div",{children:[e.jsx("label",{className:"type-label text-fg-muted block mb-2",children:"Flood Policy"}),e.jsxs("div",{className:"toggle-group w-full",children:[e.jsx("button",{onClick:()=>ee({...I,floodPolicy:"deny"}),className:b("toggle-group-item flex-1",I.floodPolicy==="deny"&&"active !text-sys-red"),children:"Deny"}),e.jsx("button",{onClick:()=>ee({...I,floodPolicy:"allow"}),className:b("toggle-group-item flex-1",I.floodPolicy==="allow"&&"active !text-sys-green"),children:"Allow"})]})]}),I.transport_key&&e.jsxs("div",{children:[e.jsx("label",{className:"type-label text-fg-muted block mb-2",children:"Transport Key"}),e.jsx("code",{className:"block bg-input-bg px-3 py-2 radius-inner border border-input-border type-code text-fg-muted overflow-x-auto",children:I.transport_key})]}),e.jsxs("div",{className:"flex justify-between gap-3 mt-6",children:[e.jsx(F,{color:"danger",outline:!0,onClick:()=>{oe(!1),ye(I),ie(!0),ee(null)},disabled:W,children:"Delete"}),e.jsxs("div",{className:"flex gap-3",children:[e.jsx(F,{color:"muted",outline:!0,onClick:()=>{oe(!1),ee(null)},disabled:W,children:"Cancel"}),e.jsx(F,{color:"primary",onClick:jt,disabled:W||!I.name.trim(),children:W?e.jsxs(e.Fragment,{children:[e.jsx(Y,{"data-slot":"icon",className:"animate-spin"}),"Saving..."]}):"Save"})]})]})]})]})}),Ce&&ne&&e.jsx("div",{className:"fixed inset-0 z-[10010] flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm",onClick:s=>s.target===s.currentTarget&&ie(!1),role:"dialog","aria-modal":"true","aria-labelledby":"delete-key-modal-title",children:e.jsxs("div",{className:"surface-modal radius-card p-6 max-w-md w-full",children:[e.jsx("h3",{id:"delete-key-modal-title",className:"type-micro mb-4",children:"Delete Region"}),e.jsxs("p",{className:"text-fg-secondary mb-4",children:["Are you sure you want to delete ",e.jsxs("strong",{className:"text-fg-primary",children:["'",ne.name,"'"]}),"?"]}),ne.children&&ne.children.length>0&&e.jsx("div",{className:"bg-sys-indigo/10 border border-sys-indigo/30 radius-inner p-3 mb-4",children:e.jsxs("div",{className:"flex gap-2",children:[e.jsx(Le,{className:"w-4 h-4 text-sys-indigo flex-shrink-0 mt-0.5"}),e.jsxs("div",{className:"text-sm text-fg-secondary",children:["This region has ",ne.children.length," child region(s). Deleting will also remove all children."]})]})}),e.jsxs("div",{className:"flex justify-end gap-3",children:[e.jsx(F,{color:"muted",outline:!0,onClick:()=>{ie(!1),ye(null)},disabled:W,children:"Cancel"}),e.jsx(F,{color:"danger",onClick:vt,disabled:W,children:W?e.jsxs(e.Fragment,{children:[e.jsx(Y,{"data-slot":"icon",className:"animate-spin"}),"Deleting..."]}):"Delete"})]})]})})]})}export{pn as default};
