From c2d5bcfbd1f7cc8244a437cce683c7e1a20d8b02 Mon Sep 17 00:00:00 2001 From: matt+claude Date: Thu, 4 Jun 2026 02:30:33 +0000 Subject: [PATCH] feat(central): v0.5.4 -- region-aware subscriptions using Central v0.9.20 regional subjects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-v0.5.4 every Central subscription used a bare wildcard (central.wx.>, central.fire.>, central.traffic.>, central.quake.>, central.hydro.>, central.space.>), so a Magic Valley operator flipping nws -> central was in fact subscribing to the all-US firehose and discarding 95% of events locally. Central v0.9.20 (2026-05-28) added per-region subject suffixes so the firehose can be filtered server-side. This wires meshai to use them. Backend (meshai/central/consumer.py): - New _subjects_for(adapter, region) replaces the static ADAPTER_SUBJECTS dict. ADAPTER_SUBJECTS is retained as an alias to _SUBJECTS_BARE for any legacy importers; the dispatcher path is unchanged. - Per-adapter subject patterns (region='us.id' default): nws -> central.wx.alert.us.id.> (region BEFORE wildcard) usgs_quake -> central.quake.event.>.us.id (region AFTER wildcard) firms -> central.fire.hotspot.>.us.id fires -> central.fire.incident.id.> (state token at fixed depth) central.fire.perimeter.id.> traffic -> central.traffic.>.id (bare state, no us. prefix) roads511 -> central.traffic.>.id (shared with traffic, sub-adapter routing) usgs -> central.hydro.>.us.id central.hydro.>.unknown (workaround until v0.9.20.1) swpc -> central.space.> (planetary; region ignored) - Empty/None region falls back to bare wildcards (pre-v0.9.20 behaviour). - _subject_owned() pulls region from env.central.region and routes through _subjects_for; v0.5.3 sub-adapter routing (owned-sources set) still applies on shared subjects like central.traffic.>.id. - start() logs the active region at connect-time for ops visibility. Config (meshai/config.py): - CentralConsumerConfig.region: str = "us.id". One region per consumer applies to every central-flipped adapter; per-adapter overrides can land in v0.6 when there is a real use case. Frontend (dashboard-frontend/src/pages/Environment.tsx): - Central Connection panel gets a Region text input next to URL/Durable. - EnvConfig.central type extended with region: string. - Static bundle rebuilt; index-DCFmSeOM.js -> index-B24tHcYj.js. Tests: - tests/test_central_region_routing.py (new, 9 cases): asserts the exact v0.9.20 subject string for each adapter at region='us.id', the SWPC global-stays-global rule, the USGS .unknown workaround, the empty-region backward-compat fallback for all 8 adapters, and integration through CentralConsumer._subject_owned() with the default region. - tests/test_central_consumer.py + tests/test_central_sub_adapter_routing.py: the two tests that asserted bare-wildcard subjects now set env.central.region = "" explicitly to preserve their original concern (no region semantics — backward-compat path only). Why swpc stays global: space weather is planetary -- a CME is detected on the sun, the geomagnetic response is hemispheric. There is no Idaho-only solar event; subscribing per-region would only drop events we want. Why hydro has the .unknown workaround: Central v0.9.20 leaves gauges whose USGS state can't be inferred on central.hydro.>.unknown. Until v0.9.20.1 backfills the state tag we subscribe to both filters to avoid silently losing those rows. Idaho downstream-filtering on data['_enriched']['usgs_site']['state'] is future v0.6 work. Orthogonal to v0.5.2 dispatcher guards (staleness / cooldown / dedup) and v0.5.3 sub-adapter routing: the region filter operates at the NATS subscription layer (server-side), upstream of everything else. Verified: pytest 327 passed (318 prior + 9 new region-routing tests); py_compile clean; frontend build clean. Safe-mode preserved -- no toggle enabled, no master enabled, no central enabled. Co-Authored-By: Claude Opus 4.7 (1M context) --- dashboard-frontend/src/pages/Environment.tsx | 6 +- meshai/central/consumer.py | 80 +++++++++++++++-- meshai/config.py | 10 ++- .../{index-DCFmSeOM.js => index-B24tHcYj.js} | 2 +- meshai/dashboard/static/index.html | 2 +- tests/test_central_consumer.py | 3 + tests/test_central_region_routing.py | 88 +++++++++++++++++++ tests/test_central_sub_adapter_routing.py | 13 ++- 8 files changed, 190 insertions(+), 14 deletions(-) rename meshai/dashboard/static/assets/{index-DCFmSeOM.js => index-B24tHcYj.js} (94%) create mode 100644 tests/test_central_region_routing.py diff --git a/dashboard-frontend/src/pages/Environment.tsx b/dashboard-frontend/src/pages/Environment.tsx index 31ecb10..5a124de 100644 --- a/dashboard-frontend/src/pages/Environment.tsx +++ b/dashboard-frontend/src/pages/Environment.tsx @@ -27,7 +27,7 @@ interface EnvConfig { traffic: { enabled: boolean; tick_seconds: number; api_key: string; corridors: { name: string; lat: number; lon: number }[]; feed_source?: FeedSource } roads511: { enabled: boolean; tick_seconds: number; api_key: string; base_url: string; endpoints: string[]; bbox: number[]; feed_source?: FeedSource } firms: { enabled: boolean; tick_seconds: number; map_key: string; source: string; bbox: number[]; day_range: number; confidence_min: string; proximity_km: number; feed_source?: FeedSource } - central?: { enabled: boolean; url: string; durable: string } + central?: { enabled: boolean; url: string; durable: string; region: string } } type FeedHealth = EnvStatus['feeds'][number] @@ -406,6 +406,10 @@ export default function Environment() { up({ central: { ...env.central!, durable: v } })} placeholder="meshai-v04" /> + up({ central: { ...env.central!, region: v } })} + placeholder="us.id" + helper="Central v0.9.20 region token (dotted, e.g. 'us.id'). Empty = bare wildcards (all-US firehose)." /> )} diff --git a/meshai/central/consumer.py b/meshai/central/consumer.py index e347209..fe7785e 100644 --- a/meshai/central/consumer.py +++ b/meshai/central/consumer.py @@ -33,10 +33,11 @@ def consumer_config(): return ConsumerConfig(deliver_policy=DeliverPolicy.NEW) -# meshai adapter (env-config attr) -> Central subject filters it consumes. -# Adapters with no Central equivalent (avalanche, ducting, roads511) are absent; -# setting source=central on those subscribes to nothing (logged). -ADAPTER_SUBJECTS: dict[str, list[str]] = { +# Bare-wildcard subjects, pre-v0.9.20. Still used when `central.region` is +# empty (backward-compat fallback) and as the canonical adapter -> family map. +# Adapters with no Central equivalent (avalanche, ducting) are absent; flipping +# those to source=central subscribes to nothing (logged). +_SUBJECTS_BARE: dict[str, list[str]] = { "nws": ["central.wx.>"], "fires": ["central.fire.incident.>", "central.fire.perimeter.>"], "firms": ["central.fire.>"], @@ -44,9 +45,59 @@ ADAPTER_SUBJECTS: dict[str, list[str]] = { "usgs": ["central.hydro.>"], "swpc": ["central.space.>"], "traffic": ["central.traffic.>"], - "roads511": ["central.traffic.>"], + "roads511": ["central.traffic.>"], # shared with traffic; sub-adapter routing } +# Backwards-compat: keep ADAPTER_SUBJECTS importable for legacy readers/tests. +ADAPTER_SUBJECTS = _SUBJECTS_BARE + + +def _subjects_for(adapter: str, region: Optional[str]) -> list[str]: + """Build region-aware Central subject filters for an adapter (v0.5.4). + + Central v0.9.20 (shipped 2026-05-28) added per-region subject suffixes so + consumers interested in a single region can have the firehose filtered + server-side instead of dragging all-US events and discarding 95% locally. + + `region` is a dotted token tree, e.g. 'us.id' for Idaho. Adapters use + one of three suffix patterns; the v0.9.20 scheme is not uniform: + + - region BEFORE the wildcard (nws): + central.wx.alert.us.id.> + - region AFTER the wildcard (quake / firms / usgs / traffic): + central.quake.event.>.us.id + central.fire.hotspot.>.us.id + central.hydro.>.us.id (+ ".unknown" workaround, see below) + central.traffic.>.id (state only, no us. prefix) + - state-only token at a fixed depth (fires): + central.fire.incident..> + central.fire.perimeter..> + - region ignored (swpc) — space weather is planetary. + + The .unknown workaround: v0.9.20 leaves USGS hydro events whose gauge + state can't be inferred on `central.hydro.>.unknown`. Subscribing to + both avoids losing those rows until v0.9.20.1 backfills the state tag. + + Empty/None region returns the bare-wildcard form (v0.5.3 behaviour). + Adapters without a Central equivalent (avalanche, ducting) return []. + """ + if not region: + return list(_SUBJECTS_BARE.get(adapter, [])) + state = region.split(".")[-1] + table: dict[str, list[str]] = { + "nws": [f"central.wx.alert.{region}.>"], + "fires": [f"central.fire.incident.{state}.>", + f"central.fire.perimeter.{state}.>"], + "firms": [f"central.fire.hotspot.>.{region}"], + "usgs_quake": [f"central.quake.event.>.{region}"], + "usgs": [f"central.hydro.>.{region}", + "central.hydro.>.unknown"], + "swpc": ["central.space.>"], + "traffic": [f"central.traffic.>.{state}"], + "roads511": [f"central.traffic.>.{state}"], # shared with traffic + } + return list(table.get(adapter, [])) + # Bridge between Central's adapter taxonomy and meshai's family-tab source names. # Central names some adapters differently (e.g. "wfigs_incidents" vs meshai's # "fires"); remap so dashboard per-adapter event filtering (which keys on the @@ -164,16 +215,24 @@ class CentralConsumer: self._subs: list = [] # ---- subject derivation ---- + def _region(self) -> str: + """Active Central region (v0.5.4). Empty string = pre-v0.9.20 bare wildcards.""" + if self._central is None: + return "" + return getattr(self._central, "region", "") or "" + def _subject_owned(self) -> dict: """Map each Central subject filter -> set of meshai source names (adapter attrs) that are feed_source=central and consume it. A shared subject - (central.traffic.> for both traffic and roads511) carries multiple owned - sources; _handle drops events whose remapped source isn't in the set.""" + (central.traffic.>.id for both traffic and roads511) carries multiple + owned sources; _handle drops events whose remapped source isn't in the + set. v0.5.4: subject shapes are region-aware via _subjects_for().""" + region = self._region() owned: dict = {} - for attr, subjects in ADAPTER_SUBJECTS.items(): + for attr in _SUBJECTS_BARE.keys(): cfg = getattr(self._env, attr, None) if cfg is not None and getattr(cfg, "feed_source", "native") == "central": - for subj in subjects: + for subj in _subjects_for(attr, region): owned.setdefault(subj, set()).add(attr) for attr in ("avalanche", "ducting"): cfg = getattr(self._env, attr, None) @@ -304,6 +363,9 @@ class CentralConsumer: sorted(subject_owned)) return + region = self._region() + logger.info("CentralConsumer: connecting region=%r subjects=%s", + region or "(bare wildcards)", sorted(subject_owned)) import nats # lazy: no NATS dependency at boot unless actually consuming self._nc = await nats.connect( self._central.url, diff --git a/meshai/config.py b/meshai/config.py index a99c28d..d9db27e 100644 --- a/meshai/config.py +++ b/meshai/config.py @@ -440,12 +440,20 @@ class FIRMSConfig(_SourcedFeed): @dataclass class CentralConsumerConfig: - """Connection settings for the Central NATS JetStream consumer (v0.4).""" + """Connection settings for the Central NATS JetStream consumer (v0.4). + + v0.5.4 adds `region` — a dotted v0.9.20 region token (e.g. 'us.id' for + Idaho) appended to each subscribed Central subject so the firehose is + filtered server-side. Empty string falls back to bare wildcards (pre- + v0.9.20 behaviour). One region applies to all central adapters; per- + adapter overrides can land in v0.6. + """ enabled: bool = False url: str = "nats://central.echo6.mesh:4222" durable: str = "meshai-consumer" connect_timeout: float = 10.0 + region: str = "us.id" @dataclass diff --git a/meshai/dashboard/static/assets/index-DCFmSeOM.js b/meshai/dashboard/static/assets/index-B24tHcYj.js similarity index 94% rename from meshai/dashboard/static/assets/index-DCFmSeOM.js rename to meshai/dashboard/static/assets/index-B24tHcYj.js index 09cc6b0..5afc1a2 100644 --- a/meshai/dashboard/static/assets/index-DCFmSeOM.js +++ b/meshai/dashboard/static/assets/index-B24tHcYj.js @@ -515,4 +515,4 @@ yyyy`);var n=Zo(t),i=r?"getUTC":"get",a=n[i+"FullYear"](),o=n[i+"Month"]()+1,s=n `||i==="")){var a=e.isSelected(i);ye(r,i)?r[i]=r[i]&&a:r[i]=a}}),r}function iqe(e){e.registerAction("legendToggleSelect","legendselectchanged",Fe(sg,"toggleSelected")),e.registerAction("legendAllSelect","legendselectall",Fe(sg,"allSelect")),e.registerAction("legendInverseSelect","legendinverseselect",Fe(sg,"inverseSelect")),e.registerAction("legendSelect","legendselected",Fe(sg,"select")),e.registerAction("legendUnSelect","legendunselected",Fe(sg,"unSelect"))}function xie(e){e.registerComponentModel(BO),e.registerComponentView(yie),e.registerProcessor(e.PRIORITY.PROCESSOR.SERIES_FILTER,nqe),e.registerSubTypeDefaulter("legend",function(){return"plain"}),iqe(e)}var aqe=function(e){q(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r}return t.prototype.setScrollDataIndex=function(r){this.option.scrollDataIndex=r},t.prototype.init=function(r,n,i){var a=Mf(r);e.prototype.init.call(this,r,n,i),JH(this,r,a)},t.prototype.mergeOption=function(r,n){e.prototype.mergeOption.call(this,r,n),JH(this,this.option,r)},t.type="legend.scroll",t.defaultOption=Su(BO.defaultOption,{scrollDataIndex:0,pageButtonItemGap:5,pageButtonGap:null,pageButtonPosition:"end",pageFormatter:"{current}/{total}",pageIcons:{horizontal:["M0,0L12,-10L12,10z","M0,0L-12,-10L-12,10z"],vertical:["M0,0L20,0L10,-20z","M0,0L20,0L10,20z"]},pageIconColor:Q.color.accent50,pageIconInactiveColor:Q.color.accent10,pageIconSize:15,pageTextStyle:{color:Q.color.tertiary},animationDurationUpdate:800}),t}(BO);function JH(e,t,r){var n=e.getOrient(),i=[1,1];i[n.index]=0,Vo(t,r,{type:"box",ignoreSize:!!i})}var eU=Me,rP=["width","height"],nP=["x","y"],oqe=function(e){q(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r.newlineDisabled=!0,r._currentIndex=0,r}return t.prototype.init=function(){e.prototype.init.call(this),this.group.add(this._containerGroup=new eU),this._containerGroup.add(this.getContentGroup()),this.group.add(this._controllerGroup=new eU)},t.prototype.resetInner=function(){e.prototype.resetInner.call(this),this._controllerGroup.removeAll(),this._containerGroup.removeClipPath(),this._containerGroup.__rectSize=null},t.prototype.renderInner=function(r,n,i,a,o,s,l){var u=this;e.prototype.renderInner.call(this,r,n,i,a,o,s,l);var c=this._controllerGroup,f=n.get("pageIconSize",!0),h=ae(f)?f:[f,f];v("pagePrev",0);var d=n.getModel("pageTextStyle");c.add(new at({name:"pageText",style:{text:"xx/xx",fill:d.getTextColor(),font:d.getFont(),verticalAlign:"middle",align:"center"},silent:!0})),v("pageNext",1);function v(g,m){var y=g+"DataIndex",x=Fv(n.get("pageIcons",!0)[n.getOrient().name][m],{onclick:ge(u._pageGo,u,y,n,a)},{x:-h[0]/2,y:-h[1]/2,width:h[0],height:h[1]});x.name=g,c.add(x)}},t.prototype.layoutInner=function(r,n,i,a,o,s){var l=this.getSelectorGroup(),u=r.getOrient().index,c=rP[u],f=nP[u],h=rP[1-u],d=nP[1-u];o&&Vc("horizontal",l,r.get("selectorItemGap",!0));var v=r.get("selectorButtonGap",!0),g=l.getBoundingRect(),m=[-g.x,-g.y],y=Ae(i);o&&(y[c]=i[c]-g[c]-v);var x=this._layoutContentAndController(r,a,y,u,c,h,d,f);if(o){if(s==="end")m[u]+=x[c]+v;else{var b=g[c]+v;m[u]-=b,x[f]-=b}x[c]+=g[c]+v,m[1-u]+=x[d]+x[h]/2-g[h]/2,x[h]=Math.max(x[h],g[h]),x[d]=Math.min(x[d],g[d]+m[1-u]),l.x=m[0],l.y=m[1],l.markRedraw()}return x},t.prototype._layoutContentAndController=function(r,n,i,a,o,s,l,u){var c=this.getContentGroup(),f=this._containerGroup,h=this._controllerGroup;Vc(r.get("orient"),c,r.get("itemGap"),a?i.width:null,a?null:i.height),Vc("horizontal",h,r.get("pageButtonItemGap",!0));var d=c.getBoundingRect(),v=h.getBoundingRect(),g=this._showController=d[o]>i[o],m=[-d.x,-d.y];n||(m[a]=c[u]);var y=[0,0],x=[-v.x,-v.y],b=be(r.get("pageButtonGap",!0),r.get("itemGap",!0));if(g){var S=r.get("pageButtonPosition",!0);S==="end"?x[a]+=i[o]-v[o]:y[a]+=v[o]+b}x[1-a]+=d[s]/2-v[s]/2,c.setPosition(m),f.setPosition(y),h.setPosition(x);var T={x:0,y:0};if(T[o]=g?i[o]:d[o],T[s]=Math.max(d[s],v[s]),T[l]=Math.min(0,v[l]+x[1-a]),f.__rectSize=i[o],g){var C={x:0,y:0};C[o]=Math.max(i[o]-v[o]-b,0),C[s]=T[s],f.setClipPath(new Xe({shape:C})),f.__rectSize=C[o]}else h.eachChild(function(P){P.attr({invisible:!0,silent:!0})});var M=this._getPageInfo(r);return M.pageIndex!=null&<(c,{x:M.contentPosition[0],y:M.contentPosition[1]},g?r:null),this._updatePageInfoView(r,M),T},t.prototype._pageGo=function(r,n,i){var a=this._getPageInfo(n)[r];a!=null&&i.dispatchAction({type:"legendScroll",scrollDataIndex:a,legendId:n.id})},t.prototype._updatePageInfoView=function(r,n){var i=this._controllerGroup;B(["pagePrev","pageNext"],function(c){var f=c+"DataIndex",h=n[f]!=null,d=i.childOfName(c);d&&(d.setStyle("fill",h?r.get("pageIconColor",!0):r.get("pageIconInactiveColor",!0)),d.cursor=h?"pointer":"default")});var a=i.childOfName("pageText"),o=r.get("pageFormatter"),s=n.pageIndex,l=s!=null?s+1:0,u=n.pageCount;a&&o&&a.setStyle("text",pe(o)?o.replace("{current}",l==null?"":l+"").replace("{total}",u==null?"":u+""):o({current:l,total:u}))},t.prototype._getPageInfo=function(r){var n=r.get("scrollDataIndex",!0),i=this.getContentGroup(),a=this._containerGroup.__rectSize,o=r.getOrient().index,s=rP[o],l=nP[o],u=this._findTargetItemIndex(n),c=i.children(),f=c[u],h=c.length,d=h?1:0,v={contentPosition:[i.x,i.y],pageCount:d,pageIndex:d-1,pagePrevDataIndex:null,pageNextDataIndex:null};if(!f)return v;var g=S(f);v.contentPosition[o]=-g.s;for(var m=u+1,y=g,x=g,b=null;m<=h;++m)b=S(c[m]),(!b&&x.e>y.s+a||b&&!T(b,y.s))&&(x.i>y.i?y=x:y=b,y&&(v.pageNextDataIndex==null&&(v.pageNextDataIndex=y.i),++v.pageCount)),x=b;for(var m=u-1,y=g,x=g,b=null;m>=-1;--m)b=S(c[m]),(!b||!T(x,b.s))&&y.i=M&&C.s<=M+a}},t.prototype._findTargetItemIndex=function(r){if(!this._showController)return 0;var n,i=this.getContentGroup(),a;return i.eachChild(function(o,s){var l=o.__legendDataIndex;a==null&&l!=null&&(a=s),l===r&&(n=s)}),n??a},t.type="legend.scroll",t}(yie);function sqe(e){e.registerAction("legendScroll","legendscroll",function(t,r){var n=t.scrollDataIndex;n!=null&&r.eachComponent({mainType:"legend",subType:"scroll",query:t},function(i){i.setScrollDataIndex(n)})})}function lqe(e){Ze(xie),e.registerComponentModel(aqe),e.registerComponentView(oqe),sqe(e)}function uqe(e){Ze(xie),Ze(lqe)}var cqe=function(e){q(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r}return t.type="dataZoom.inside",t.defaultOption=Su(zy.defaultOption,{disabled:!1,zoomLock:!1,zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!1,preventDefaultMouseMove:!0}),t}(zy),NR=Qe();function fqe(e,t,r){NR(e).coordSysRecordMap.each(function(n){var i=n.dataZoomInfoMap.get(t.uid);i&&(i.getRange=r)})}function hqe(e,t){for(var r=NR(e).coordSysRecordMap,n=r.keys(),i=0;ia[i+n]&&(n=u),o=o&&l.get("preventDefaultMouseMove",!0)}),{controlType:n,opt:{zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!0,preventDefaultMouseMove:!!o,api:r,zInfo:{component:t.model},triggerInfo:{roamTrigger:null,isInSelf:t.containsPoint}}}}function mqe(e){e.registerProcessor(e.PRIORITY.PROCESSOR.FILTER,function(t,r){var n=NR(r),i=n.coordSysRecordMap||(n.coordSysRecordMap=xe());i.each(function(a){a.dataZoomInfoMap=null}),t.eachComponent({mainType:"dataZoom",subType:"inside"},function(a){var o=nie(a);B(o.infoList,function(s){var l=s.model.uid,u=i.get(l)||i.set(l,dqe(r,s.model)),c=u.dataZoomInfoMap||(u.dataZoomInfoMap=xe());c.set(a.uid,{dzReferCoordSysInfo:s,model:a,getRange:null})})}),i.each(function(a){var o=a.controller,s,l=a.dataZoomInfoMap;if(l){var u=l.keys()[0];u!=null&&(s=l.get(u))}if(!s){bie(i,a);return}var c=gqe(l,a,r);o.enable(c.controlType,c.opt),Zv(a,"dispatchAction",s.model.get("throttle",!0),"fixRate")})})}var yqe=function(e){q(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type="dataZoom.inside",r}return t.prototype.render=function(r,n,i){if(e.prototype.render.apply(this,arguments),r.noTarget()){this._clear();return}this.range=r.getPercentRange(),fqe(i,r,{pan:ge(iP.pan,this),zoom:ge(iP.zoom,this),scrollMove:ge(iP.scrollMove,this)})},t.prototype.dispose=function(){this._clear(),e.prototype.dispose.apply(this,arguments)},t.prototype._clear=function(){hqe(this.api,this.dataZoomModel),this.range=null},t.type="dataZoom.inside",t}(MR),iP={zoom:function(e,t,r,n){var i=this.range,a=i.slice(),o=e.axisModels[0];if(o){var s=aP[t](null,[n.originX,n.originY],o,r,e),l=(s.signal>0?s.pixelStart+s.pixelLength-s.pixel:s.pixel-s.pixelStart)/s.pixelLength*(a[1]-a[0])+a[0],u=Math.max(1/n.scale,0);a[0]=(a[0]-l)*u+l,a[1]=(a[1]-l)*u+l;var c=this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();if(uu(0,a,[0,100],0,c.minSpan,c.maxSpan),this.range=a,i[0]!==a[0]||i[1]!==a[1])return a}},pan:tU(function(e,t,r,n,i,a){var o=aP[n]([a.oldX,a.oldY],[a.newX,a.newY],t,i,r);return o.signal*(e[1]-e[0])*o.pixel/o.pixelLength}),scrollMove:tU(function(e,t,r,n,i,a){var o=aP[n]([0,0],[a.scrollDelta,a.scrollDelta],t,i,r);return o.signal*(e[1]-e[0])*a.scrollDelta})};function tU(e){return function(t,r,n,i){var a=this.range,o=a.slice(),s=t.axisModels[0];if(s){var l=e(o,s,t,r,n,i);if(uu(l,o,[0,100],"all"),this.range=o,a[0]!==o[0]||a[1]!==o[1])return o}}}var aP={grid:function(e,t,r,n,i){var a=r.axis,o={},s=i.model.coordinateSystem.getRect();return e=e||[0,0],a.dim==="x"?(o.pixel=t[0]-e[0],o.pixelLength=s.width,o.pixelStart=s.x,o.signal=a.inverse?1:-1):(o.pixel=t[1]-e[1],o.pixelLength=s.height,o.pixelStart=s.y,o.signal=a.inverse?-1:1),o},polar:function(e,t,r,n,i){var a=r.axis,o={},s=i.model.coordinateSystem,l=s.getRadiusAxis().getExtent(),u=s.getAngleAxis().getExtent();return e=e?s.pointToCoord(e):[0,0],t=s.pointToCoord(t),r.mainType==="radiusAxis"?(o.pixel=t[0]-e[0],o.pixelLength=l[1]-l[0],o.pixelStart=l[0],o.signal=a.inverse?1:-1):(o.pixel=t[1]-e[1],o.pixelLength=u[1]-u[0],o.pixelStart=u[0],o.signal=a.inverse?-1:1),o},singleAxis:function(e,t,r,n,i){var a=r.axis,o=i.model.coordinateSystem.getRect(),s={};return e=e||[0,0],a.orient==="horizontal"?(s.pixel=t[0]-e[0],s.pixelLength=o.width,s.pixelStart=o.x,s.signal=a.inverse?1:-1):(s.pixel=t[1]-e[1],s.pixelLength=o.height,s.pixelStart=o.y,s.signal=a.inverse?-1:1),s}};function wie(e){PR(e),e.registerComponentModel(cqe),e.registerComponentView(yqe),mqe(e)}var _qe=function(e){q(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r}return t.type="dataZoom.slider",t.layoutMode="box",t.defaultOption=Su(zy.defaultOption,{show:!0,right:"ph",top:"ph",width:"ph",height:"ph",left:null,bottom:null,borderColor:Q.color.accent10,borderRadius:0,backgroundColor:Q.color.transparent,dataBackground:{lineStyle:{color:Q.color.accent30,width:.5},areaStyle:{color:Q.color.accent20,opacity:.2}},selectedDataBackground:{lineStyle:{color:Q.color.accent40,width:.5},areaStyle:{color:Q.color.accent20,opacity:.3}},fillerColor:"rgba(135,175,274,0.2)",handleIcon:"path://M-9.35,34.56V42m0-40V9.5m-2,0h4a2,2,0,0,1,2,2v21a2,2,0,0,1-2,2h-4a2,2,0,0,1-2-2v-21A2,2,0,0,1-11.35,9.5Z",handleSize:"100%",handleStyle:{color:Q.color.neutral00,borderColor:Q.color.accent20},moveHandleSize:7,moveHandleIcon:"path://M-320.9-50L-320.9-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-348-41-339-50-320.9-50z M-212.3-50L-212.3-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-239.4-41-230.4-50-212.3-50z M-103.7-50L-103.7-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-130.9-41-121.8-50-103.7-50z",moveHandleStyle:{color:Q.color.accent40,opacity:.5},showDetail:!0,showDataShadow:"auto",realtime:!0,zoomLock:!1,textStyle:{color:Q.color.tertiary},brushSelect:!0,brushStyle:{color:Q.color.accent30,opacity:.3},emphasis:{handleLabel:{show:!0},handleStyle:{borderColor:Q.color.accent40},moveHandleStyle:{opacity:.8}},defaultLocationEdgeGap:15}),t}(zy),lg=Xe,xqe=1,oP=30,bqe=7,ug="horizontal",rU="vertical",wqe=5,Sqe=["line","bar","candlestick","scatter"],Tqe={easing:"cubicOut",duration:100,delay:0},Cqe=function(e){q(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r._displayables={},r}return t.prototype.init=function(r,n){this.api=n,this._onBrush=ge(this._onBrush,this),this._onBrushEnd=ge(this._onBrushEnd,this)},t.prototype.render=function(r,n,i,a){if(e.prototype.render.apply(this,arguments),Zv(this,"_dispatchZoomAction",r.get("throttle"),"fixRate"),this._orient=r.getOrient(),r.get("show")===!1){this.group.removeAll();return}if(r.noTarget()){this._clear(),this.group.removeAll();return}(!a||a.type!=="dataZoom"||a.from!==this.uid)&&this._buildView(),this._updateView()},t.prototype.dispose=function(){this._clear(),e.prototype.dispose.apply(this,arguments)},t.prototype._clear=function(){Ty(this,"_dispatchZoomAction");var r=this.api.getZr();r.off("mousemove",this._onBrush),r.off("mouseup",this._onBrushEnd)},t.prototype._buildView=function(){var r=this.group;r.removeAll(),this._brushing=!1,this._displayables.brushRect=null,this._resetLocation(),this._resetInterval();var n=this._displayables.sliderGroup=new Me;this._renderBackground(),this._renderHandle(),this._renderDataShadow(),r.add(n),this._positionGroup()},t.prototype._resetLocation=function(){var r=this.dataZoomModel,n=this.api,i=r.get("brushSelect"),a=i?bqe:0,o=jr(r,n).refContainer,s=this._findCoordRect(),l=r.get("defaultLocationEdgeGap",!0)||0,u=this._orient===ug?{right:o.width-s.x-s.width,top:o.height-oP-l-a,width:s.width,height:oP}:{right:l,top:s.y,width:oP,height:s.height},c=Mf(r.option);B(["right","top","width","height"],function(h){c[h]==="ph"&&(c[h]=u[h])});var f=zt(c,o);this._location={x:f.x,y:f.y},this._size=[f.width,f.height],this._orient===rU&&this._size.reverse()},t.prototype._positionGroup=function(){var r=this.group,n=this._location,i=this._orient,a=this.dataZoomModel.getFirstTargetAxisModel(),o=a&&a.get("inverse"),s=this._displayables.sliderGroup,l=(this._dataShadowInfo||{}).otherAxisInverse;s.attr(i===ug&&!o?{scaleY:l?1:-1,scaleX:1}:i===ug&&o?{scaleY:l?1:-1,scaleX:-1}:i===rU&&!o?{scaleY:l?-1:1,scaleX:1,rotation:Math.PI/2}:{scaleY:l?-1:1,scaleX:-1,rotation:Math.PI/2});var u=r.getBoundingRect([s]);r.x=n.x-u.x,r.y=n.y-u.y,r.markRedraw()},t.prototype._getViewExtent=function(){return[0,this._size[0]]},t.prototype._renderBackground=function(){var r=this.dataZoomModel,n=this._size,i=this._displayables.sliderGroup,a=r.get("brushSelect");i.add(new lg({silent:!0,shape:{x:0,y:0,width:n[0],height:n[1]},style:{fill:r.get("backgroundColor")},z2:-40}));var o=new lg({shape:{x:0,y:0,width:n[0],height:n[1]},style:{fill:"transparent"},z2:0,onclick:ge(this._onClickPanel,this)}),s=this.api.getZr();a?(o.on("mousedown",this._onBrushStart,this),o.cursor="crosshair",s.on("mousemove",this._onBrush),s.on("mouseup",this._onBrushEnd)):(s.off("mousemove",this._onBrush),s.off("mouseup",this._onBrushEnd)),i.add(o)},t.prototype._renderDataShadow=function(){var r=this._dataShadowInfo=this._prepareDataShadowInfo();if(this._displayables.dataShadowSegs=[],!r)return;var n=this._size,i=this._shadowSize||[],a=r.series,o=a.getRawData(),s=a.getShadowDim&&a.getShadowDim(),l=s&&o.getDimensionInfo(s)?a.getShadowDim():r.otherDim;if(l==null)return;var u=this._shadowPolygonPts,c=this._shadowPolylinePts;if(o!==this._shadowData||l!==this._shadowDim||n[0]!==i[0]||n[1]!==i[1]){var f=o.getDataExtent(r.thisDim),h=o.getDataExtent(l),d=(h[1]-h[0])*.3;h=[h[0]-d,h[1]+d];var v=[0,n[1]],g=[0,n[0]],m=[[n[0],0],[0,0]],y=[],x=g[1]/Math.max(1,o.count()-1),b=n[0]/(f[1]-f[0]),S=r.thisAxis.type==="time",T=-x,C=Math.round(o.count()/n[0]),M;o.each([r.thisDim,l],function(D,j,N){if(C>0&&N%C){S||(T+=x);return}T=S?(+D-f[0])*b:T+x;var z=j==null||isNaN(j)||j==="",$=z?0:gt(j,h,v,!0);z&&!M&&N?(m.push([m[m.length-1][0],0]),y.push([y[y.length-1][0],0])):!z&&M&&(m.push([T,0]),y.push([T,0])),z||(m.push([T,$]),y.push([T,$])),M=z}),u=this._shadowPolygonPts=m,c=this._shadowPolylinePts=y}this._shadowData=o,this._shadowDim=l,this._shadowSize=[n[0],n[1]];var P=this.dataZoomModel;function I(D){var j=P.getModel(D?"selectedDataBackground":"dataBackground"),N=new Me,z=new bn({shape:{points:u},segmentIgnoreThreshold:1,style:j.getModel("areaStyle").getAreaStyle(),silent:!0,z2:-20}),$=new an({shape:{points:c},segmentIgnoreThreshold:1,style:j.getModel("lineStyle").getLineStyle(),silent:!0,z2:-19});return N.add(z),N.add($),N}for(var k=0;k<3;k++){var E=I(k===1);this._displayables.sliderGroup.add(E),this._displayables.dataShadowSegs.push(E)}},t.prototype._prepareDataShadowInfo=function(){var r=this.dataZoomModel,n=r.get("showDataShadow");if(n!==!1){var i,a=this.ecModel;return r.eachTargetAxis(function(o,s){var l=r.getAxisProxy(o,s).getTargetSeriesModels();B(l,function(u){if(!i&&!(n!==!0&&We(Sqe,u.get("type"))<0)){var c=a.getComponent(Nl(o),s).axis,f=Aqe(o),h,d=u.coordinateSystem;f!=null&&d.getOtherAxis&&(h=d.getOtherAxis(c).inverse),f=u.getData().mapDimension(f);var v=u.getData().mapDimension(o);i={thisAxis:c,series:u,thisDim:v,otherDim:f,otherAxisInverse:h}}},this)},this),i}},t.prototype._renderHandle=function(){var r=this.group,n=this._displayables,i=n.handles=[null,null],a=n.handleLabels=[null,null],o=this._displayables.sliderGroup,s=this._size,l=this.dataZoomModel,u=this.api,c=l.get("borderRadius")||0,f=l.get("brushSelect"),h=n.filler=new lg({silent:f,style:{fill:l.get("fillerColor")},textConfig:{position:"inside"}});o.add(h),o.add(new lg({silent:!0,subPixelOptimize:!0,shape:{x:0,y:0,width:s[0],height:s[1],r:c},style:{stroke:l.get("dataBackgroundColor")||l.get("borderColor"),lineWidth:xqe,fill:Q.color.transparent}})),B([0,1],function(b){var S=l.get("handleIcon");!yw[S]&&S.indexOf("path://")<0&&S.indexOf("image://")<0&&(S="path://"+S);var T=_r(S,-1,0,2,2,null,!0);T.attr({cursor:Mqe(this._orient),draggable:!0,drift:ge(this._onDragMove,this,b),ondragend:ge(this._onDragEnd,this),onmouseover:ge(this._showDataInfo,this,!0),onmouseout:ge(this._showDataInfo,this,!1),z2:5});var C=T.getBoundingRect(),M=l.get("handleSize");this._handleHeight=ve(M,this._size[1]),this._handleWidth=C.width/C.height*this._handleHeight,T.setStyle(l.getModel("handleStyle").getItemStyle()),T.style.strokeNoScale=!0,T.rectHover=!0,T.ensureState("emphasis").style=l.getModel(["emphasis","handleStyle"]).getItemStyle(),Xl(T);var P=l.get("handleColor");P!=null&&(T.style.fill=P),o.add(i[b]=T);var I=l.getModel("textStyle"),k=l.get("handleLabel")||{},E=k.show||!1;r.add(a[b]=new at({silent:!0,invisible:!E,style:Mt(I,{x:0,y:0,text:"",verticalAlign:"middle",align:"center",fill:I.getTextColor(),font:I.getFont()}),z2:10}))},this);var d=h;if(f){var v=ve(l.get("moveHandleSize"),s[1]),g=n.moveHandle=new Xe({style:l.getModel("moveHandleStyle").getItemStyle(),silent:!0,shape:{r:[0,0,2,2],y:s[1]-.5,height:v}}),m=v*.8,y=n.moveHandleIcon=_r(l.get("moveHandleIcon"),-m/2,-m/2,m,m,Q.color.neutral00,!0);y.silent=!0,y.y=s[1]+v/2-.5,g.ensureState("emphasis").style=l.getModel(["emphasis","moveHandleStyle"]).getItemStyle();var x=Math.min(s[1]/2,Math.max(v,10));d=n.moveZone=new Xe({invisible:!0,shape:{y:s[1]-x,height:v+x}}),d.on("mouseover",function(){u.enterEmphasis(g)}).on("mouseout",function(){u.leaveEmphasis(g)}),o.add(g),o.add(y),o.add(d)}d.attr({draggable:!0,cursor:"default",drift:ge(this._onDragMove,this,"all"),ondragstart:ge(this._showDataInfo,this,!0),ondragend:ge(this._onDragEnd,this),onmouseover:ge(this._showDataInfo,this,!0),onmouseout:ge(this._showDataInfo,this,!1)})},t.prototype._resetInterval=function(){var r=this._range=this.dataZoomModel.getPercentRange(),n=this._getViewExtent();this._handleEnds=[gt(r[0],[0,100],n,!0),gt(r[1],[0,100],n,!0)]},t.prototype._updateInterval=function(r,n){var i=this.dataZoomModel,a=this._handleEnds,o=this._getViewExtent(),s=i.findRepresentativeAxisProxy().getMinMaxSpan(),l=[0,100];uu(n,a,o,i.get("zoomLock")?"all":r,s.minSpan!=null?gt(s.minSpan,l,o,!0):null,s.maxSpan!=null?gt(s.maxSpan,l,o,!0):null);var u=this._range,c=this._range=Ai([gt(a[0],o,l,!0),gt(a[1],o,l,!0)]);return!u||u[0]!==c[0]||u[1]!==c[1]},t.prototype._updateView=function(r){var n=this._displayables,i=this._handleEnds,a=Ai(i.slice()),o=this._size;B([0,1],function(d){var v=n.handles[d],g=this._handleHeight;v.attr({scaleX:g/2,scaleY:g/2,x:i[d]+(d?-1:1),y:o[1]/2-g/2})},this),n.filler.setShape({x:a[0],y:0,width:a[1]-a[0],height:o[1]});var s={x:a[0],width:a[1]-a[0]};n.moveHandle&&(n.moveHandle.setShape(s),n.moveZone.setShape(s),n.moveZone.getBoundingRect(),n.moveHandleIcon&&n.moveHandleIcon.attr("x",s.x+s.width/2));for(var l=n.dataShadowSegs,u=[0,a[0],a[1],o[0]],c=0;cn[0]||i[1]<0||i[1]>n[1])){var a=this._handleEnds,o=(a[0]+a[1])/2,s=this._updateInterval("all",i[0]-o);this._updateView(),s&&this._dispatchZoomAction(!1)}},t.prototype._onBrushStart=function(r){var n=r.offsetX,i=r.offsetY;this._brushStart=new Ie(n,i),this._brushing=!0,this._brushStartTime=+new Date},t.prototype._onBrushEnd=function(r){if(this._brushing){var n=this._displayables.brushRect;if(this._brushing=!1,!!n){n.attr("ignore",!0);var i=n.shape,a=+new Date;if(!(a-this._brushStartTime<200&&Math.abs(i.width)<5)){var o=this._getViewExtent(),s=[0,100],l=this._handleEnds=[i.x,i.x+i.width],u=this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();uu(0,l,o,0,u.minSpan!=null?gt(u.minSpan,s,o,!0):null,u.maxSpan!=null?gt(u.maxSpan,s,o,!0):null),this._range=Ai([gt(l[0],o,s,!0),gt(l[1],o,s,!0)]),this._updateView(),this._dispatchZoomAction(!1)}}}},t.prototype._onBrush=function(r){this._brushing&&(zs(r.event),this._updateBrushRect(r.offsetX,r.offsetY))},t.prototype._updateBrushRect=function(r,n){var i=this._displayables,a=this.dataZoomModel,o=i.brushRect;o||(o=i.brushRect=new lg({silent:!0,style:a.getModel("brushStyle").getItemStyle()}),i.sliderGroup.add(o)),o.attr("ignore",!1);var s=this._brushStart,l=this._displayables.sliderGroup,u=l.transformCoordToLocal(r,n),c=l.transformCoordToLocal(s.x,s.y),f=this._size;u[0]=Math.max(Math.min(f[0],u[0]),0),o.setShape({x:c[0],y:0,width:u[0]-c[0],height:f[1]})},t.prototype._dispatchZoomAction=function(r){var n=this._range;this.api.dispatchAction({type:"dataZoom",from:this.uid,dataZoomId:this.dataZoomModel.id,animation:r?Tqe:null,start:n[0],end:n[1]})},t.prototype._findCoordRect=function(){var r,n=nie(this.dataZoomModel).infoList;if(!r&&n.length){var i=n[0].model.coordinateSystem;r=i.getRect&&i.getRect()}if(!r){var a=this.api.getWidth(),o=this.api.getHeight();r={x:a*.2,y:o*.2,width:a*.6,height:o*.6}}return r},t.type="dataZoom.slider",t}(MR);function Aqe(e){var t={x:"y",y:"x",radius:"angle",angle:"radius"};return t[e]}function Mqe(e){return e==="vertical"?"ns-resize":"ew-resize"}function Sie(e){e.registerComponentModel(_qe),e.registerComponentView(Cqe),PR(e)}function Pqe(e){Ze(wie),Ze(Sie)}var Tie={get:function(e,t,r){var n=Ae((Lqe[e]||{})[t]);return r&&ae(n)?n[n.length-1]:n}},Lqe={color:{active:["#006edd","#e0ffff"],inactive:[Q.color.transparent]},colorHue:{active:[0,360],inactive:[0,0]},colorSaturation:{active:[.3,1],inactive:[0,0]},colorLightness:{active:[.9,.5],inactive:[0,0]},colorAlpha:{active:[.3,1],inactive:[0,0]},opacity:{active:[.3,1],inactive:[0,0]},symbol:{active:["circle","roundRect","diamond"],inactive:["none"]},symbolSize:{active:[10,50],inactive:[0,0]}},nU=Hr.mapVisual,kqe=Hr.eachVisual,Iqe=ae,iU=B,Oqe=Ai,Eqe=gt,Jw=function(e){q(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r.stateList=["inRange","outOfRange"],r.replacableOptionKeys=["inRange","outOfRange","target","controller","color"],r.layoutMode={type:"box",ignoreSize:!0},r.dataBound=[-1/0,1/0],r.targetVisuals={},r.controllerVisuals={},r}return t.prototype.init=function(r,n,i){this.mergeDefaultAndTheme(r,i)},t.prototype.optionUpdated=function(r,n){var i=this.option;!n&&die(i,r,this.replacableOptionKeys),this.textStyleModel=this.getModel("textStyle"),this.resetItemSize(),this.completeVisualOption()},t.prototype.resetVisual=function(r){var n=this.stateList;r=ge(r,this),this.controllerVisuals=NO(this.option.controller,n,r),this.targetVisuals=NO(this.option.target,n,r)},t.prototype.getItemSymbol=function(){return null},t.prototype.getTargetSeriesIndices=function(){var r=this.option.seriesId,n=this.option.seriesIndex;n==null&&r==null&&(n="all");var i=jv(this.ecModel,"series",{index:n,id:r},{useDefault:!1,enableAll:!0,enableNone:!1}).models;return se(i,function(a){return a.componentIndex})},t.prototype.eachTargetSeries=function(r,n){B(this.getTargetSeriesIndices(),function(i){var a=this.ecModel.getSeriesByIndex(i);a&&r.call(n,a)},this)},t.prototype.isTargetSeries=function(r){var n=!1;return this.eachTargetSeries(function(i){i===r&&(n=!0)}),n},t.prototype.formatValueText=function(r,n,i){var a=this.option,o=a.precision,s=this.dataBound,l=a.formatter,u;i=i||["<",">"],ae(r)&&(r=r.slice(),u=!0);var c=n?r:u?[f(r[0]),f(r[1])]:f(r);if(pe(l))return l.replace("{value}",u?c[0]:c).replace("{value2}",u?c[1]:c);if(Ce(l))return u?l(r[0],r[1]):l(r);if(u)return r[0]===s[0]?i[0]+" "+c[1]:r[1]===s[1]?i[1]+" "+c[0]:c[0]+" - "+c[1];return c;function f(h){return h===s[0]?"min":h===s[1]?"max":(+h).toFixed(Math.min(o,20))}},t.prototype.resetExtent=function(){var r=this.option,n=Oqe([r.min,r.max]);this._dataExtent=n},t.prototype.getDataDimensionIndex=function(r){var n=this.option.dimension;if(n!=null)return r.getDimensionIndex(n);for(var i=r.dimensions,a=i.length-1;a>=0;a--){var o=i[a],s=r.getDimensionInfo(o);if(!s.isCalculationCoord)return s.storeDimIndex}},t.prototype.getExtent=function(){return this._dataExtent.slice()},t.prototype.completeVisualOption=function(){var r=this.ecModel,n=this.option,i={inRange:n.inRange,outOfRange:n.outOfRange},a=n.target||(n.target={}),o=n.controller||(n.controller={});He(a,i),He(o,i);var s=this.isCategory();l.call(this,a),l.call(this,o),u.call(this,a,"inRange","outOfRange"),c.call(this,o);function l(f){Iqe(n.color)&&!f.inRange&&(f.inRange={color:n.color.slice().reverse()}),f.inRange=f.inRange||{color:r.get("gradientColor")}}function u(f,h,d){var v=f[h],g=f[d];v&&!g&&(g=f[d]={},iU(v,function(m,y){if(Hr.isValidType(y)){var x=Tie.get(y,"inactive",s);x!=null&&(g[y]=x,y==="color"&&!g.hasOwnProperty("opacity")&&!g.hasOwnProperty("colorAlpha")&&(g.opacity=[0,0]))}}))}function c(f){var h=(f.inRange||{}).symbol||(f.outOfRange||{}).symbol,d=(f.inRange||{}).symbolSize||(f.outOfRange||{}).symbolSize,v=this.get("inactiveColor"),g=this.getItemSymbol(),m=g||"roundRect";iU(this.stateList,function(y){var x=this.itemSize,b=f[y];b||(b=f[y]={color:s?v:[v]}),b.symbol==null&&(b.symbol=h&&Ae(h)||(s?m:[m])),b.symbolSize==null&&(b.symbolSize=d&&Ae(d)||(s?x[0]:[x[0],x[0]])),b.symbol=nU(b.symbol,function(C){return C==="none"?m:C});var S=b.symbolSize;if(S!=null){var T=-1/0;kqe(S,function(C){C>T&&(T=C)}),b.symbolSize=nU(S,function(C){return Eqe(C,[0,T],[0,x[0]],!0)})}},this)}},t.prototype.resetItemSize=function(){this.itemSize=[parseFloat(this.get("itemWidth")),parseFloat(this.get("itemHeight"))]},t.prototype.isCategory=function(){return!!this.option.categories},t.prototype.setSelected=function(r){},t.prototype.getSelected=function(){return null},t.prototype.getValueState=function(r){return null},t.prototype.getVisualMeta=function(r){return null},t.type="visualMap",t.dependencies=["series"],t.defaultOption={show:!0,z:4,min:0,max:200,left:0,right:null,top:null,bottom:0,itemWidth:null,itemHeight:null,inverse:!1,orient:"vertical",backgroundColor:Q.color.transparent,borderColor:Q.color.borderTint,contentColor:Q.color.theme[0],inactiveColor:Q.color.disabled,borderWidth:0,padding:Q.size.m,textGap:10,precision:0,textStyle:{color:Q.color.secondary}},t}(Ke),aU=[20,140],Dqe=function(e){q(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r}return t.prototype.optionUpdated=function(r,n){e.prototype.optionUpdated.apply(this,arguments),this.resetExtent(),this.resetVisual(function(i){i.mappingMethod="linear",i.dataExtent=this.getExtent()}),this._resetRange()},t.prototype.resetItemSize=function(){e.prototype.resetItemSize.apply(this,arguments);var r=this.itemSize;(r[0]==null||isNaN(r[0]))&&(r[0]=aU[0]),(r[1]==null||isNaN(r[1]))&&(r[1]=aU[1])},t.prototype._resetRange=function(){var r=this.getExtent(),n=this.option.range;!n||n.auto?(r.auto=1,this.option.range=r):ae(n)&&(n[0]>n[1]&&n.reverse(),n[0]=Math.max(n[0],r[0]),n[1]=Math.min(n[1],r[1]))},t.prototype.completeVisualOption=function(){e.prototype.completeVisualOption.apply(this,arguments),B(this.stateList,function(r){var n=this.option.controller[r].symbolSize;n&&n[0]!==n[1]&&(n[0]=n[1]/3)},this)},t.prototype.setSelected=function(r){this.option.range=r.slice(),this._resetRange()},t.prototype.getSelected=function(){var r=this.getExtent(),n=Ai((this.get("range")||[]).slice());return n[0]>r[1]&&(n[0]=r[1]),n[1]>r[1]&&(n[1]=r[1]),n[0]=i[1]||r<=n[1])?"inRange":"outOfRange"},t.prototype.findTargetDataIndices=function(r){var n=[];return this.eachTargetSeries(function(i){var a=[],o=i.getData();o.each(this.getDataDimensionIndex(o),function(s,l){r[0]<=s&&s<=r[1]&&a.push(l)},this),n.push({seriesId:i.id,dataIndex:a})},this),n},t.prototype.getVisualMeta=function(r){var n=oU(this,"outOfRange",this.getExtent()),i=oU(this,"inRange",this.option.range.slice()),a=[];function o(d,v){a.push({value:d,color:r(d,v)})}for(var s=0,l=0,u=i.length,c=n.length;lr[1])break;a.push({color:this.getControllerVisual(l,"color",n),offset:s/i})}return a.push({color:this.getControllerVisual(r[1],"color",n),offset:1}),a},t.prototype._createBarPoints=function(r,n){var i=this.visualMapModel.itemSize;return[[i[0]-n[0],r[0]],[i[0],r[0]],[i[0],r[1]],[i[0]-n[1],r[1]]]},t.prototype._createBarGroup=function(r){var n=this._orient,i=this.visualMapModel.get("inverse");return new Me(n==="horizontal"&&!i?{scaleX:r==="bottom"?1:-1,rotation:Math.PI/2}:n==="horizontal"&&i?{scaleX:r==="bottom"?-1:1,rotation:-Math.PI/2}:n==="vertical"&&!i?{scaleX:r==="left"?1:-1,scaleY:-1}:{scaleX:r==="left"?1:-1})},t.prototype._updateHandle=function(r,n){if(this._useHandle){var i=this._shapes,a=this.visualMapModel,o=i.handleThumbs,s=i.handleLabels,l=a.itemSize,u=a.getExtent(),c=this._applyTransform("left",i.mainGroup);Nqe([0,1],function(f){var h=o[f];h.setStyle("fill",n.handlesColor[f]),h.y=r[f];var d=vo(r[f],[0,l[1]],u,!0),v=this.getControllerVisual(d,"symbolSize");h.scaleX=h.scaleY=v/l[0],h.x=l[0]-v/2;var g=Ga(i.handleLabelPoints[f],ql(h,this.group));if(this._orient==="horizontal"){var m=c==="left"||c==="top"?(l[0]-v)/2:(l[0]-v)/-2;g[1]+=m}s[f].setStyle({x:g[0],y:g[1],text:a.formatValueText(this._dataInterval[f]),verticalAlign:"middle",align:this._orient==="vertical"?this._applyTransform("left",i.mainGroup):"center"})},this)}},t.prototype._showIndicator=function(r,n,i,a){var o=this.visualMapModel,s=o.getExtent(),l=o.itemSize,u=[0,l[1]],c=this._shapes,f=c.indicator;if(f){f.attr("invisible",!1);var h={convertOpacityToAlpha:!0},d=this.getControllerVisual(r,"color",h),v=this.getControllerVisual(r,"symbolSize"),g=vo(r,s,u,!0),m=l[0]-v/2,y={x:f.x,y:f.y};f.y=g,f.x=m;var x=Ga(c.indicatorLabelPoint,ql(f,this.group)),b=c.indicatorLabel;b.attr("invisible",!1);var S=this._applyTransform("left",c.mainGroup),T=this._orient,C=T==="horizontal";b.setStyle({text:(i||"")+o.formatValueText(n),verticalAlign:C?S:"middle",align:C?"center":S});var M={x:m,y:g,style:{fill:d}},P={style:{x:x[0],y:x[1]}};if(o.ecModel.isAnimationEnabled()&&!this._firstShowIndicator){var I={duration:100,easing:"cubicInOut",additive:!0};f.x=y.x,f.y=y.y,f.animateTo(M,I),b.animateTo(P,I)}else f.attr(M),b.attr(P);this._firstShowIndicator=!1;var k=this._shapes.handleLabels;if(k)for(var E=0;Eo[1]&&(f[1]=1/0),n&&(f[0]===-1/0?this._showIndicator(c,f[1],"< ",l):f[1]===1/0?this._showIndicator(c,f[0],"> ",l):this._showIndicator(c,c,"≈ ",l));var h=this._hoverLinkDataIndices,d=[];(n||cU(i))&&(d=this._hoverLinkDataIndices=i.findTargetDataIndices(f));var v=yRe(h,d);this._dispatchHighDown("downplay",mb(v[0],i)),this._dispatchHighDown("highlight",mb(v[1],i))}},t.prototype._hoverLinkFromSeriesMouseOver=function(r){var n;if(Ic(r.target,function(l){var u=De(l);if(u.dataIndex!=null)return n=u,!0},!0),!!n){var i=this.ecModel.getSeriesByIndex(n.seriesIndex),a=this.visualMapModel;if(a.isTargetSeries(i)){var o=i.getData(n.dataType),s=o.getStore().get(a.getDataDimensionIndex(o),n.dataIndex);isNaN(s)||this._showIndicator(s,s)}}},t.prototype._hideIndicator=function(){var r=this._shapes;r.indicator&&r.indicator.attr("invisible",!0),r.indicatorLabel&&r.indicatorLabel.attr("invisible",!0);var n=this._shapes.handleLabels;if(n)for(var i=0;i=0&&(a.dimension=o,n.push(a))}}),e.getData().setVisual("visualMeta",n)}}];function Gqe(e,t,r,n){for(var i=t.targetVisuals[n],a=Hr.prepareVisualTypes(i),o={color:p0(e.getData(),"color")},s=0,l=a.length;s0:t.splitNumber>0)||t.calculable)?"continuous":"piecewise"}),e.registerAction($qe,Fqe),B(Vqe,function(t){e.registerVisual(e.PRIORITY.VISUAL.COMPONENT,t)}),e.registerPreprocessor(Wqe))}function Pie(e){e.registerComponentModel(Dqe),e.registerComponentView(Bqe),Mie(e)}var Hqe=function(e){q(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r._pieceList=[],r}return t.prototype.optionUpdated=function(r,n){e.prototype.optionUpdated.apply(this,arguments),this.resetExtent();var i=this._mode=this._determineMode();this._pieceList=[],Uqe[this._mode].call(this,this._pieceList),this._resetSelected(r,n);var a=this.option.categories;this.resetVisual(function(o,s){i==="categories"?(o.mappingMethod="category",o.categories=Ae(a)):(o.dataExtent=this.getExtent(),o.mappingMethod="piecewise",o.pieceList=se(this._pieceList,function(l){return l=Ae(l),s!=="inRange"&&(l.visual=null),l}))})},t.prototype.completeVisualOption=function(){var r=this.option,n={},i=Hr.listVisualTypes(),a=this.isCategory();B(r.pieces,function(s){B(i,function(l){s.hasOwnProperty(l)&&(n[l]=1)})}),B(n,function(s,l){var u=!1;B(this.stateList,function(c){u=u||o(r,c,l)||o(r.target,c,l)},this),!u&&B(this.stateList,function(c){(r[c]||(r[c]={}))[l]=Tie.get(l,c==="inRange"?"active":"inactive",a)})},this);function o(s,l,u){return s&&s[l]&&s[l].hasOwnProperty(u)}e.prototype.completeVisualOption.apply(this,arguments)},t.prototype._resetSelected=function(r,n){var i=this.option,a=this._pieceList,o=(n?i:r).selected||{};if(i.selected=o,B(a,function(l,u){var c=this.getSelectedMapKey(l);o.hasOwnProperty(c)||(o[c]=!0)},this),i.selectedMode==="single"){var s=!1;B(a,function(l,u){var c=this.getSelectedMapKey(l);o[c]&&(s?o[c]=!1:s=!0)},this)}},t.prototype.getItemSymbol=function(){return this.get("itemSymbol")},t.prototype.getSelectedMapKey=function(r){return this._mode==="categories"?r.value+"":r.index+""},t.prototype.getPieceList=function(){return this._pieceList},t.prototype._determineMode=function(){var r=this.option;return r.pieces&&r.pieces.length>0?"pieces":this.option.categories?"categories":"splitNumber"},t.prototype.setSelected=function(r){this.option.selected=Ae(r)},t.prototype.getValueState=function(r){var n=Hr.findPieceIndex(r,this._pieceList);return n!=null&&this.option.selected[this.getSelectedMapKey(this._pieceList[n])]?"inRange":"outOfRange"},t.prototype.findTargetDataIndices=function(r){var n=[],i=this._pieceList;return this.eachTargetSeries(function(a){var o=[],s=a.getData();s.each(this.getDataDimensionIndex(s),function(l,u){var c=Hr.findPieceIndex(l,i);c===r&&o.push(u)},this),n.push({seriesId:a.id,dataIndex:o})},this),n},t.prototype.getRepresentValue=function(r){var n;if(this.isCategory())n=r.value;else if(r.value!=null)n=r.value;else{var i=r.interval||[];n=i[0]===-1/0&&i[1]===1/0?0:(i[0]+i[1])/2}return n},t.prototype.getVisualMeta=function(r){if(this.isCategory())return;var n=[],i=["",""],a=this;function o(c,f){var h=a.getRepresentValue({interval:c});f||(f=a.getValueState(h));var d=r(h,f);c[0]===-1/0?i[0]=d:c[1]===1/0?i[1]=d:n.push({value:c[0],color:d},{value:c[1],color:d})}var s=this._pieceList.slice();if(!s.length)s.push({interval:[-1/0,1/0]});else{var l=s[0].interval[0];l!==-1/0&&s.unshift({interval:[-1/0,l]}),l=s[s.length-1].interval[1],l!==1/0&&s.push({interval:[l,1/0]})}var u=-1/0;return B(s,function(c){var f=c.interval;f&&(f[0]>u&&o([u,f[0]],"outOfRange"),o(f.slice()),u=f[1])},this),{stops:n,outerColors:i}},t.type="visualMap.piecewise",t.defaultOption=Su(Jw.defaultOption,{selected:null,minOpen:!1,maxOpen:!1,align:"auto",itemWidth:20,itemHeight:14,itemSymbol:"roundRect",pieces:null,categories:null,splitNumber:5,selectedMode:"multiple",itemGap:10,hoverLink:!0}),t}(Jw),Uqe={splitNumber:function(e){var t=this.option,r=Math.min(t.precision,20),n=this.getExtent(),i=t.splitNumber;i=Math.max(parseInt(i,10),1),t.splitNumber=i;for(var a=(n[1]-n[0])/i;+a.toFixed(r)!==a&&r<5;)r++;t.precision=r,a=+a.toFixed(r),t.minOpen&&e.push({interval:[-1/0,n[0]],close:[0,0]});for(var o=0,s=n[0];o","≥"][n[0]]];r.text=r.text||this.formatValueText(r.value!=null?r.value:r.interval,!1,i)},this)}};function vU(e,t){var r=e.inverse;(e.orient==="vertical"?!r:r)&&t.reverse()}var Zqe=function(e){q(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r}return t.prototype.doRender=function(){var r=this.group;r.removeAll();var n=this.visualMapModel,i=n.get("textGap"),a=n.textStyleModel,o=this._getItemAlign(),s=n.itemSize,l=this._getViewData(),u=l.endsText,c=rn(n.get("showLabel",!0),!u),f=!n.get("selectedMode");u&&this._renderEndsText(r,u[0],s,c,o),B(l.viewPieceList,function(h){var d=h.piece,v=new Me;v.onclick=ge(this._onItemClick,this,d),this._enableHoverLink(v,h.indexInModelPieceList);var g=n.getRepresentValue(d);if(this._createItemSymbol(v,g,[0,0,s[0],s[1]],f),c){var m=this.visualMapModel.getValueState(g),y=a.get("align")||o;v.add(new at({style:Mt(a,{x:y==="right"?-i:s[0]+i,y:s[1]/2,text:d.text,verticalAlign:a.get("verticalAlign")||"middle",align:y,opacity:be(a.get("opacity"),m==="outOfRange"?.5:1)}),silent:f}))}r.add(v)},this),u&&this._renderEndsText(r,u[1],s,c,o),Vc(n.get("orient"),r,n.get("itemGap")),this.renderBackground(r),this.positionGroup(r)},t.prototype._enableHoverLink=function(r,n){var i=this;r.on("mouseover",function(){return a("highlight")}).on("mouseout",function(){return a("downplay")});var a=function(o){var s=i.visualMapModel;s.option.hoverLink&&i.api.dispatchAction({type:o,batch:mb(s.findTargetDataIndices(n),s)})}},t.prototype._getItemAlign=function(){var r=this.visualMapModel,n=r.option;if(n.orient==="vertical")return Aie(r,this.api,r.itemSize);var i=n.align;return(!i||i==="auto")&&(i="left"),i},t.prototype._renderEndsText=function(r,n,i,a,o){if(n){var s=new Me,l=this.visualMapModel.textStyleModel;s.add(new at({style:Mt(l,{x:a?o==="right"?i[0]:0:i[0]/2,y:i[1]/2,verticalAlign:"middle",align:a?o:"center",text:n})})),r.add(s)}},t.prototype._getViewData=function(){var r=this.visualMapModel,n=se(r.getPieceList(),function(s,l){return{piece:s,indexInModelPieceList:l}}),i=r.get("text"),a=r.get("orient"),o=r.get("inverse");return(a==="horizontal"?o:!o)?n.reverse():i&&(i=i.slice().reverse()),{viewPieceList:n,endsText:i}},t.prototype._createItemSymbol=function(r,n,i,a){var o=_r(this.getControllerVisual(n,"symbol"),i[0],i[1],i[2],i[3],this.getControllerVisual(n,"color"));o.silent=a,r.add(o)},t.prototype._onItemClick=function(r){var n=this.visualMapModel,i=n.option,a=i.selectedMode;if(a){var o=Ae(i.selected),s=n.getSelectedMapKey(r);a==="single"||a===!0?(o[s]=!0,B(o,function(l,u){o[u]=u===s})):o[s]=!o[s],this.api.dispatchAction({type:"selectDataRange",from:this.uid,visualMapId:this.visualMapModel.id,selected:o})}},t.type="visualMap.piecewise",t}(Cie);function Lie(e){e.registerComponentModel(Hqe),e.registerComponentView(Zqe),Mie(e)}function Yqe(e){Ze(Pie),Ze(Lie)}var Xqe=function(){function e(t){this._thumbnailModel=t}return e.prototype.reset=function(t){this._renderVersion=t.getMainProcessVersion()},e.prototype.renderContent=function(t){var r=t.api.getViewOfComponentModel(this._thumbnailModel);r&&(t.group.silent=!0,r.renderContent({group:t.group,targetTrans:t.targetTrans,z2Range:JQ(t.group),roamType:t.roamType,viewportRect:t.viewportRect,renderVersion:this._renderVersion}))},e.prototype.updateWindow=function(t,r){var n=r.getViewOfComponentModel(this._thumbnailModel);n&&n.updateWindow({targetTrans:t,renderVersion:this._renderVersion})},e}(),qqe=function(e){q(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r.preventAutoZ=!0,r}return t.prototype.optionUpdated=function(r,n){this._updateBridge()},t.prototype._updateBridge=function(){var r=this._birdge=this._birdge||new Xqe(this);if(this._target=null,this.ecModel.eachSeries(function(i){zW(i,null)}),this.shouldShow()){var n=this.getTarget();zW(n.baseMapProvider,r)}},t.prototype.shouldShow=function(){return this.getShallow("show",!0)},t.prototype.getBridge=function(){return this._birdge},t.prototype.getTarget=function(){if(this._target)return this._target;var r=this.getReferringComponents("series",{useDefault:!1,enableAll:!1,enableNone:!1}).models[0];return r?r.subType!=="graph"&&(r=null):r=this.ecModel.queryComponents({mainType:"series",subType:"graph"})[0],this._target={baseMapProvider:r},this._target},t.type="thumbnail",t.layoutMode="box",t.dependencies=["series","geo"],t.defaultOption={show:!0,right:1,bottom:1,height:"25%",width:"25%",itemStyle:{borderColor:Q.color.border,borderWidth:2},windowStyle:{borderWidth:1,color:Q.color.neutral30,borderColor:Q.color.neutral40,opacity:.3},z:10},t}(Ke),Kqe=function(e){q(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r}return t.prototype.render=function(r,n,i){if(this._api=i,this._model=r,this._coordSys||(this._coordSys=new Of),!this._isEnabled()){this._clear();return}this._renderVersion=i.getMainProcessVersion();var a=this.group;a.removeAll();var o=r.getModel("itemStyle"),s=o.getItemStyle();s.fill==null&&(s.fill=n.get("backgroundColor")||Q.color.neutral00);var l=jr(r,i).refContainer,u=zt(_J(r,!0),l),c=s.lineWidth||0,f=this._contentRect=uf(u.clone(),c/2,!0,!0),h=new Me;a.add(h),h.setClipPath(new Xe({shape:f.plain()}));var d=this._targetGroup=new Me;h.add(d);var v=u.plain();v.r=o.getShallow("borderRadius",!0),a.add(this._bgRect=new Xe({style:s,shape:v,silent:!1,cursor:"grab"}));var g=r.getModel("windowStyle"),m=g.getShallow("borderRadius",!0);h.add(this._windowRect=new Xe({shape:{x:0,y:0,width:0,height:0,r:m},style:g.getItemStyle(),silent:!1,cursor:"grab"})),this._dealRenderContent(),this._dealUpdateWindow(),gU(r,this)},t.prototype.renderContent=function(r){this._bridgeRendered=r,this._isEnabled()&&(this._dealRenderContent(),this._dealUpdateWindow(),gU(this._model,this))},t.prototype._dealRenderContent=function(){var r=this._bridgeRendered;if(!(!r||r.renderVersion!==this._renderVersion)){var n=this._targetGroup,i=this._coordSys,a=this._contentRect;if(n.removeAll(),!!r){var o=r.group,s=o.getBoundingRect();n.add(o),this._bgRect.z2=r.z2Range.min-10,i.setBoundingRect(s.x,s.y,s.width,s.height);var l=zt({left:"center",top:"center",aspect:s.width/s.height},a);i.setViewRect(l.x,l.y,l.width,l.height),o.attr(i.getTransformInfo().raw),this._windowRect.z2=r.z2Range.max+10,this._resetRoamController(r.roamType)}}},t.prototype.updateWindow=function(r){var n=this._bridgeRendered;n&&n.renderVersion===r.renderVersion&&(n.targetTrans=r.targetTrans),this._isEnabled()&&this._dealUpdateWindow()},t.prototype._dealUpdateWindow=function(){var r=this._bridgeRendered;if(!(!r||r.renderVersion!==this._renderVersion)){var n=da([],r.targetTrans),i=Fa([],this._coordSys.transform,n);this._transThisToTarget=da([],i);var a=r.viewportRect;a?a=a.clone():a=new Oe(0,0,this._api.getWidth(),this._api.getHeight()),a.applyTransform(i);var o=this._windowRect,s=o.shape.r;o.setShape(Pe({r:s},a))}},t.prototype._resetRoamController=function(r){var n=this,i=this._api,a=this._roamController;if(a||(a=this._roamController=new If(i.getZr())),!r||!this._isEnabled()){a.disable();return}a.enable(r,{api:i,zInfo:{component:this._model},triggerInfo:{roamTrigger:null,isInSelf:function(o,s,l){return n._contentRect.contain(s,l)}}}),a.off("pan").off("zoom").on("pan",ge(this._onPan,this)).on("zoom",ge(this._onZoom,this))},t.prototype._onPan=function(r){var n=this._transThisToTarget;if(!(!this._isEnabled()||!n)){var i=lr([],[r.oldX,r.oldY],n),a=lr([],[r.oldX-r.dx,r.oldY-r.dy],n);this._api.dispatchAction(pU(this._model.getTarget().baseMapProvider,{dx:a[0]-i[0],dy:a[1]-i[1]}))}},t.prototype._onZoom=function(r){var n=this._transThisToTarget;if(!(!this._isEnabled()||!n)){var i=lr([],[r.originX,r.originY],n);this._api.dispatchAction(pU(this._model.getTarget().baseMapProvider,{zoom:1/r.scale,originX:i[0],originY:i[1]}))}},t.prototype._isEnabled=function(){var r=this._model;if(!r||!r.shouldShow())return!1;var n=r.getTarget().baseMapProvider;return!!n},t.prototype._clear=function(){this.group.removeAll(),this._bridgeRendered=null,this._roamController&&this._roamController.disable()},t.prototype.remove=function(){this._clear()},t.prototype.dispose=function(){this._clear()},t.type="thumbnail",t}(Lt);function pU(e,t){var r=e.mainType==="series"?e.subType+"Roam":e.mainType+"Roam",n={type:r};return n[e.mainType+"Id"]=e.id,ne(n,t),n}function gU(e,t){var r=cf(e);jT(t.group,r.z,r.zlevel)}function Qqe(e){e.registerComponentModel(qqe),e.registerComponentView(Kqe)}var Jqe={label:{enabled:!0},decal:{show:!1}},mU=Qe(),eKe={};function tKe(e,t){var r=e.getModel("aria");if(!r.get("enabled"))return;var n=Ae(Jqe);He(n.label,e.getLocaleModel().get("aria"),!1),He(r.option,n,!1),i(),a();function i(){var u=r.getModel("decal"),c=u.get("show");if(c){var f=xe();e.eachSeries(function(h){if(!h.isColorBySeries()){var d=f.get(h.type);d||(d={},f.set(h.type,d)),mU(h).scope=d}}),e.eachRawSeries(function(h){if(e.isSeriesFiltered(h))return;if(Ce(h.enableAriaDecal)){h.enableAriaDecal();return}var d=h.getData();if(h.isColorBySeries()){var x=MI(h.ecModel,h.name,eKe,e.getSeriesCount()),b=d.getVisual("decal");d.setVisual("decal",S(b,x))}else{var v=h.getRawData(),g={},m=mU(h).scope;d.each(function(T){var C=d.getRawIndex(T);g[C]=T});var y=v.count();v.each(function(T){var C=g[T],M=v.getName(T)||T+"",P=MI(h.ecModel,M,m,y),I=d.getItemVisual(C,"decal");d.setItemVisual(C,"decal",S(I,P))})}function S(T,C){var M=T?ne(ne({},C),T):C;return M.dirty=!0,M}})}}function a(){var u=t.getZr().dom;if(u){var c=e.getLocaleModel().get("aria"),f=r.getModel("label");if(f.option=Pe(f.option,c),!!f.get("enabled")){if(u.setAttribute("role","img"),f.get("description")){u.setAttribute("aria-label",f.get("description"));return}var h=e.getSeriesCount(),d=f.get(["data","maxCount"])||10,v=f.get(["series","maxCount"])||10,g=Math.min(h,v),m;if(!(h<1)){var y=s();if(y){var x=f.get(["general","withTitle"]);m=o(x,{title:y})}else m=f.get(["general","withoutTitle"]);var b=[],S=h>1?f.get(["series","multiple","prefix"]):f.get(["series","single","prefix"]);m+=o(S,{seriesCount:h}),e.eachSeries(function(P,I){if(I1?f.get(["series","multiple",D]):f.get(["series","single",D]),k=o(k,{seriesId:P.seriesIndex,seriesName:P.get("name"),seriesType:l(P.subType)});var j=P.getData();if(j.count()>d){var N=f.get(["data","partialData"]);k+=o(N,{displayCnt:d})}else k+=f.get(["data","allData"]);for(var z=f.get(["data","separator","middle"]),$=f.get(["data","separator","end"]),Z=f.get(["data","excludeDimensionId"]),F=[],G=0;G":"gt",">=":"gte","=":"eq","!=":"ne","<>":"ne"},iKe=function(){function e(t){var r=this._condVal=pe(t)?new RegExp(t):SK(t)?t:null;if(r==null){var n="";mt(n)}}return e.prototype.evaluate=function(t){var r=typeof t;return pe(r)?this._condVal.test(t):ot(r)?this._condVal.test(t+""):!1},e}(),aKe=function(){function e(){}return e.prototype.evaluate=function(){return this.value},e}(),oKe=function(){function e(){}return e.prototype.evaluate=function(){for(var t=this.children,r=0;r2&&n.push(i),i=[j,N]}function c(j,N,z,$){Zh(j,z)&&Zh(N,$)||i.push(j,N,z,$,z,$)}function f(j,N,z,$,Z,F){var G=Math.abs(N-j),V=Math.tan(G/4)*4/3,H=NP:E2&&n.push(i),n}function GO(e,t,r,n,i,a,o,s,l,u){if(Zh(e,r)&&Zh(t,n)&&Zh(i,o)&&Zh(a,s)){l.push(o,s);return}var c=2/u,f=c*c,h=o-e,d=s-t,v=Math.sqrt(h*h+d*d);h/=v,d/=v;var g=r-e,m=n-t,y=i-o,x=a-s,b=g*g+m*m,S=y*y+x*x;if(b=0&&P=0){l.push(o,s);return}var I=[],k=[];ou(e,r,i,o,.5,I),ou(t,n,a,s,.5,k),GO(I[0],k[0],I[1],k[1],I[2],k[2],I[3],k[3],l,u),GO(I[4],k[4],I[5],k[5],I[6],k[6],I[7],k[7],l,u)}function xKe(e,t){var r=VO(e),n=[];t=t||1;for(var i=0;i0)for(var u=0;uMath.abs(u),f=Iie([l,u],c?0:1,t),h=(c?s:u)/f.length,d=0;di,o=Iie([n,i],a?0:1,t),s=a?"width":"height",l=a?"height":"width",u=a?"x":"y",c=a?"y":"x",f=e[s]/o.length,h=0;h1?null:new Ie(g*l+e,g*u+t)}function SKe(e,t,r){var n=new Ie;Ie.sub(n,r,t),n.normalize();var i=new Ie;Ie.sub(i,e,t);var a=i.dot(n);return a}function yh(e,t){var r=e[e.length-1];r&&r[0]===t[0]&&r[1]===t[1]||e.push(t)}function TKe(e,t,r){for(var n=e.length,i=[],a=0;ao?(u.x=c.x=s+a/2,u.y=l,c.y=l+o):(u.y=c.y=l+o/2,u.x=s,c.x=s+a),TKe(t,u,c)}function eS(e,t,r,n){if(r===1)n.push(t);else{var i=Math.floor(r/2),a=e(t);eS(e,a[0],i,n),eS(e,a[1],r-i,n)}return n}function CKe(e,t){for(var r=[],n=0;n0;u/=2){var c=0,f=0;(e&u)>0&&(c=1),(t&u)>0&&(f=1),s+=u*u*(3*c^f),f===0&&(c===1&&(e=u-1-e,t=u-1-t),l=e,e=t,t=l)}return s}function nS(e){var t=1/0,r=1/0,n=-1/0,i=-1/0,a=se(e,function(s){var l=s.getBoundingRect(),u=s.getComputedTransform(),c=l.x+l.width/2+(u?u[4]:0),f=l.y+l.height/2+(u?u[5]:0);return t=Math.min(c,t),r=Math.min(f,r),n=Math.max(c,n),i=Math.max(f,i),[c,f]}),o=se(a,function(s,l){return{cp:s,z:DKe(s[0],s[1],t,r,n,i),path:e[l]}});return o.sort(function(s,l){return s.z-l.z}).map(function(s){return s.path})}function Die(e){return PKe(e.path,e.count)}function WO(){return{fromIndividuals:[],toIndividuals:[],count:0}}function NKe(e,t,r){var n=[];function i(T){for(var C=0;C=0;i--)if(!r[i].many.length){var l=r[s].many;if(l.length<=1)if(s)s=0;else return r;var a=l.length,u=Math.ceil(a/2);r[i].many=l.slice(u,a),r[s].many=l.slice(0,u),s++}return r}var RKe={clone:function(e){for(var t=[],r=1-Math.pow(1-e.path.style.opacity,1/e.count),n=0;n0))return;var s=n.getModel("universalTransition").get("delay"),l=Object.assign({setToFinal:!0},o),u,c;AU(e)&&(u=e,c=t),AU(t)&&(u=t,c=e);function f(y,x,b,S,T){var C=y.many,M=y.one;if(C.length===1&&!T){var P=x?C[0]:M,I=x?M:C[0];if(tS(P))f({many:[P],one:I},!0,b,S,!0);else{var k=s?Pe({delay:s(b,S)},l):l;RR(P,I,k),a(P,I,P,I,k)}}else for(var E=Pe({dividePath:RKe[r],individualDelay:s&&function(Z,F,G,V){return s(Z+b,S)}},l),D=x?NKe(C,M,E):jKe(M,C,E),j=D.fromIndividuals,N=D.toIndividuals,z=j.length,$=0;$t.length,d=u?MU(c,u):MU(h?t:e,[h?e:t]),v=0,g=0;gNie))for(var a=n.getIndices(),o=0;o0&&C.group.traverse(function(P){P instanceof tt&&!P.animators.length&&P.animateFrom({style:{opacity:0}},M)})})}function OU(e){var t=e.getModel("universalTransition").get("seriesKey");return t||e.id}function EU(e){return ae(e)?e.sort().join(","):e}function xl(e){if(e.hostModel)return e.hostModel.getModel("universalTransition").get("divideShape")}function WKe(e,t){var r=xe(),n=xe(),i=xe();return B(e.oldSeries,function(a,o){var s=e.oldDataGroupIds[o],l=e.oldData[o],u=OU(a),c=EU(u);n.set(c,{dataGroupId:s,data:l}),ae(u)&&B(u,function(f){i.set(f,{key:c,dataGroupId:s,data:l})})}),B(t.updatedSeries,function(a){if(a.isUniversalTransitionEnabled()&&a.isAnimationEnabled()){var o=a.get("dataGroupId"),s=a.getData(),l=OU(a),u=EU(l),c=n.get(u);if(c)r.set(u,{oldSeries:[{dataGroupId:c.dataGroupId,divide:xl(c.data),data:c.data}],newSeries:[{dataGroupId:o,divide:xl(s),data:s}]});else if(ae(l)){var f=[];B(l,function(v){var g=n.get(v);g.data&&f.push({dataGroupId:g.dataGroupId,divide:xl(g.data),data:g.data})}),f.length&&r.set(u,{oldSeries:f,newSeries:[{dataGroupId:o,data:s,divide:xl(s)}]})}else{var h=i.get(l);if(h){var d=r.get(h.key);d||(d={oldSeries:[{dataGroupId:h.dataGroupId,data:h.data,divide:xl(h.data)}],newSeries:[]},r.set(h.key,d)),d.newSeries.push({dataGroupId:o,data:s,divide:xl(s)})}}}}),r}function DU(e,t){for(var r=0;r=0&&i.push({dataGroupId:t.oldDataGroupIds[s],data:t.oldData[s],divide:xl(t.oldData[s]),groupIdDim:o.dimension})}),B(Pt(e.to),function(o){var s=DU(r.updatedSeries,o);if(s>=0){var l=r.updatedSeries[s].getData();a.push({dataGroupId:t.oldDataGroupIds[s],data:l,divide:xl(l),groupIdDim:o.dimension})}}),i.length>0&&a.length>0&&jie(i,a,n)}function UKe(e){e.registerUpdateLifecycle("series:beforeupdate",function(t,r,n){B(Pt(n.seriesTransition),function(i){B(Pt(i.to),function(a){for(var o=n.updatedSeries,s=0;so.vmin?r+=o.vmin-n+(t-o.vmin)/(o.vmax-o.vmin)*o.gapReal:r+=t-n,n=o.vmax,i=!1;break}r+=o.vmin-n+o.gapReal,n=o.vmax}return i&&(r+=t-n),r},e.prototype.unelapse=function(t){for(var r=NU,n=jU,i=!0,a=0,o=0;ol?a=s.vmin+(t-l)/(u-l)*(s.vmax-s.vmin):a=n+t-r,n=s.vmax,i=!1;break}r=u,n=s.vmax}return i&&(a=n+t-r),a},e}();function YKe(){return new ZKe}var NU=0,jU=0;function XKe(e,t){var r=0,n={tpAbs:{span:0,val:0},tpPrct:{span:0,val:0}},i=function(){return{has:!1,span:NaN,inExtFrac:NaN,val:NaN}},a={S:{tpAbs:i(),tpPrct:i()},E:{tpAbs:i(),tpPrct:i()}};B(e.breaks,function(s){var l=s.gapParsed;l.type==="tpPrct"&&(r+=l.val);var u=BR(s,t);if(u){var c=u.vmin!==s.vmin,f=u.vmax!==s.vmax,h=u.vmax-u.vmin;if(!(c&&f))if(c||f){var d=c?"S":"E";a[d][l.type].has=!0,a[d][l.type].span=h,a[d][l.type].inExtFrac=h/(s.vmax-s.vmin),a[d][l.type].val=l.val}else n[l.type].span+=h,n[l.type].val+=l.val}});var o=r*(0+(t[1]-t[0])+(n.tpAbs.val-n.tpAbs.span)+(a.S.tpAbs.has?(a.S.tpAbs.val-a.S.tpAbs.span)*a.S.tpAbs.inExtFrac:0)+(a.E.tpAbs.has?(a.E.tpAbs.val-a.E.tpAbs.span)*a.E.tpAbs.inExtFrac:0)-n.tpPrct.span-(a.S.tpPrct.has?a.S.tpPrct.span*a.S.tpPrct.inExtFrac:0)-(a.E.tpPrct.has?a.E.tpPrct.span*a.E.tpPrct.inExtFrac:0))/(1-n.tpPrct.val-(a.S.tpPrct.has?a.S.tpPrct.val*a.S.tpPrct.inExtFrac:0)-(a.E.tpPrct.has?a.E.tpPrct.val*a.E.tpPrct.inExtFrac:0));B(e.breaks,function(s){var l=s.gapParsed;l.type==="tpPrct"&&(s.gapReal=r!==0?Math.max(o,0)*l.val/r:0),l.type==="tpAbs"&&(s.gapReal=l.val),s.gapReal==null&&(s.gapReal=0)})}function qKe(e,t,r,n,i,a){e!=="no"&&B(r,function(o){var s=BR(o,a);if(s)for(var l=t.length-1;l>=0;l--){var u=t[l],c=n(u),f=i*3/4;c>s.vmin-f&&ct[0]&&r=0&&o<1-1e-5}B(e,function(o){if(!(!o||o.start==null||o.end==null)&&!o.isExpanded){var s={breakOption:Ae(o),vmin:t(o.start),vmax:t(o.end),gapParsed:{type:"tpAbs",val:0},gapReal:null};if(o.gap!=null){var l=!1;if(pe(o.gap)){var u=Ci(o.gap);if(u.match(/%$/)){var c=parseFloat(u)/100;i(c)||(c=0),s.gapParsed.type="tpPrct",s.gapParsed.val=c,l=!0}}if(!l){var f=t(o.gap);(!isFinite(f)||f<0)&&(f=0),s.gapParsed.type="tpAbs",s.gapParsed.val=f}}if(s.vmin===s.vmax&&(s.gapParsed.type="tpAbs",s.gapParsed.val=0),r&&r.noNegative&&B(["vmin","vmax"],function(d){s[d]<0&&(s[d]=0)}),s.vmin>s.vmax){var h=s.vmax;s.vmax=s.vmin,s.vmin=h}n.push(s)}}),n.sort(function(o,s){return o.vmin-s.vmin});var a=-1/0;return B(n,function(o,s){a>o.vmin&&(n[s]=null),a=o.vmax}),{breaks:n.filter(function(o){return!!o})}}function zR(e,t){return UO(t)===UO(e)}function UO(e){return e.start+"_\0_"+e.end}function QKe(e,t,r){var n=[];B(e,function(a,o){var s=t(a);s&&s.type==="vmin"&&n.push([o])}),B(e,function(a,o){var s=t(a);if(s&&s.type==="vmax"){var l=xu(n,function(u){return zR(t(e[u[0]]).parsedBreak.breakOption,s.parsedBreak.breakOption)});l&&l.push(o)}});var i=[];return B(n,function(a){a.length===2&&i.push(r?a:[e[a[0]],e[a[1]]])}),i}function JKe(e,t,r,n){var i,a;if(e.break){var o=e.break.parsedBreak,s=xu(r,function(f){return zR(f.breakOption,e.break.parsedBreak.breakOption)}),l=n(Math.pow(t,o.vmin),s.vmin),u=n(Math.pow(t,o.vmax),s.vmax),c={type:o.gapParsed.type,val:o.gapParsed.type==="tpAbs"?gr(Math.pow(t,o.vmin+o.gapParsed.val))-l:o.gapParsed.val};i={type:e.break.type,parsedBreak:{breakOption:o.breakOption,vmin:l,vmax:u,gapParsed:c,gapReal:o.gapReal}},a=s[e.break.type]}return{brkRoundingCriterion:a,vBreak:i}}function eQe(e,t,r){var n={noNegative:!0},i=HO(e,r,n),a=HO(e,r,n),o=Math.log(t);return a.breaks=se(a.breaks,function(s){var l=Math.log(s.vmin)/o,u=Math.log(s.vmax)/o,c={type:s.gapParsed.type,val:s.gapParsed.type==="tpAbs"?Math.log(s.vmin+s.gapParsed.val)/o-l:s.gapParsed.val};return{vmin:l,vmax:u,gapParsed:c,gapReal:s.gapReal,breakOption:s.breakOption}}),{parsedOriginal:i,parsedLogged:a}}var tQe={vmin:"start",vmax:"end"};function rQe(e,t){return t&&(e=e||{},e.break={type:tQe[t.type],start:t.parsedBreak.vmin,end:t.parsedBreak.vmax}),e}function nQe(){CBe({createScaleBreakContext:YKe,pruneTicksByBreak:qKe,addBreaksToTicks:KKe,parseAxisBreakOption:HO,identifyAxisBreak:zR,serializeAxisBreakIdentifier:UO,retrieveAxisBreakPairs:QKe,getTicksLogTransformBreak:JKe,logarithmicParseBreaksFromOption:eQe,makeAxisLabelFormatterParamBreak:rQe})}var RU=Qe();function iQe(e,t){var r=xu(e,function(n){return Sr().identifyAxisBreak(n.parsedBreak.breakOption,t.breakOption)});return r||e.push(r={zigzagRandomList:[],parsedBreak:t,shouldRemove:!1}),r}function aQe(e){B(e,function(t){return t.shouldRemove=!0})}function oQe(e){for(var t=e.length-1;t>=0;t--)e[t].shouldRemove&&e.splice(t,1)}function sQe(e,t,r,n,i){var a=r.axis;if(a.scale.isBlank()||!Sr())return;var o=Sr().retrieveAxisBreakPairs(a.scale.getTicks({breakTicks:"only_break"}),function(I){return I.break},!1);if(!o.length)return;var s=r.getModel("breakArea"),l=s.get("zigzagAmplitude"),u=s.get("zigzagMinSpan"),c=s.get("zigzagMaxSpan");u=Math.max(2,u||0),c=Math.max(u,c||0);var f=s.get("expandOnClick"),h=s.get("zigzagZ"),d=s.getModel("itemStyle"),v=d.getItemStyle(),g=v.stroke,m=v.lineWidth,y=v.lineDash,x=v.fill,b=new Me({ignoreModelZ:!0}),S=a.isHorizontal(),T=RU(t).visualList||(RU(t).visualList=[]);aQe(T);for(var C=function(I){var k=o[I][0].break.parsedBreak,E=[];E[0]=a.toGlobalCoord(a.dataToCoord(k.vmin,!0)),E[1]=a.toGlobalCoord(a.dataToCoord(k.vmax,!0)),E[1]=F;fe&&(K=F);var Be=[],_e=[];Be[$]=E,_e[$]=D,!le&&!fe&&(Be[$]+=Y?-l:l,_e[$]-=Y?l:-l),Be[Z]=K,_e[Z]=K,V.push(Be),H.push(_e);var ie=void 0;if(eex[1]&&x.reverse(),{coordPair:x,brkId:Sr().serializeAxisBreakIdentifier(y.breakOption)}});l.sort(function(m,y){return m.coordPair[0]-y.coordPair[0]});for(var u=o[0],c=null,f=0;f=0?l[0].width:l[1].width),h=(f+c.x)/2-u.x,d=Math.min(h,h-c.x),v=Math.max(h,h-c.x),g=v<0?v:d>0?d:0;s=(h-g)/c.x}var m=new Ie,y=new Ie;Ie.scale(m,n,-s),Ie.scale(y,n,1-s),ZI(r[0],m),ZI(r[1],y)}function cQe(e,t){var r={breaks:[]};return B(t.breaks,function(n){if(n){var i=xu(e.get("breaks",!0),function(s){return Sr().identifyAxisBreak(s,n)});if(i){var a=t.type,o={isExpanded:!!i.isExpanded};i.isExpanded=a===ZT?!0:a===nre?!1:a===ire?!i.isExpanded:i.isExpanded,r.breaks.push({start:i.start,end:i.end,isExpanded:!!i.isExpanded,old:o})}}}),r}function fQe(){o6e({adjustBreakLabelPair:uQe,buildAxisBreakLine:lQe,rectCoordBuildBreakAxis:sQe,updateModelAxisBreak:cQe})}function hQe(e){h6e(e),nQe(),fQe()}function dQe(){E6e(vQe)}function vQe(e,t){B(e,function(r){if(!r.model.get(["axisLabel","inside"])){var n=pQe(r);if(n){var i=r.isHorizontal()?"height":"width",a=r.model.get(["axisLabel","margin"]);t[i]-=n[i]+a,r.position==="top"?t.y+=n.height+a:r.position==="left"&&(t.x+=n.width+a)}}})}function pQe(e){var t=e.model,r=e.scale;if(!t.get(["axisLabel","show"])||r.isBlank())return;var n,i,a=r.getExtent();r instanceof rv?i=r.count():(n=r.getTicks(),i=n.length);var o=e.getLabelModel(),s=qv(e),l,u=1;i>40&&(u=Math.ceil(i/40));for(var c=0;c1&&arguments[1]!==void 0?arguments[1]:60,i=null;return function(){for(var a=this,o=arguments.length,s=new Array(o),l=0;l12?"#22c55e":e>8?"#4ade80":e>5?"#f59e0b":e>3?"#f97316":"#ef4444"}function OQe(e){return e===null||e>46?0:e>44.5?1:e>43?2:3}function EQe(e){return e==="ROUTER"||e==="ROUTER_LATE"?30:e==="REPEATER"||e==="TRACKER"?25:e==="CLIENT_MUTE"?7:e==="CLIENT_BASE"?12:15}function DQe({nodes:e,edges:t,selectedNodeId:r,onSelectNode:n}){const i=U.useRef(null),[a,o]=U.useState("connected"),s=U.useMemo(()=>{const m=new Set;return t.forEach(y=>{m.add(y.from_node),m.add(y.to_node)}),m},[t]),l=U.useMemo(()=>{let m=e;return a==="connected"?m=m.filter(y=>s.has(y.node_num)):a==="infra"&&(m=m.filter(y=>$U.includes(y.role))),m},[e,a,s]),u=U.useMemo(()=>new Map(l.map(m=>[m.node_num,m])),[l]),c=U.useMemo(()=>t.filter(m=>u.has(m.from_node)&&u.has(m.to_node)),[t,u]),f=U.useMemo(()=>{const m=new Set;return r!==null&&c.forEach(y=>{y.from_node===r&&m.add(y.to_node),y.to_node===r&&m.add(y.from_node)}),m},[r,c]),h=U.useMemo(()=>{const m=l.map(x=>{const b=OQe(x.latitude),S=zU[b%zU.length],T=$U.includes(x.role),C=x.node_num===r,M=f.has(x.node_num),P=r===null||C||M;return{id:String(x.node_num),name:x.short_name,value:x.node_num,symbolSize:EQe(x.role),itemStyle:{color:T?S:"#111827",borderColor:S,borderWidth:T?0:2,opacity:P?1:.15},label:{show:!0,position:"bottom",distance:5,fontSize:10,fontFamily:"JetBrains Mono, monospace",color:P?"#94a3b8":"#94a3b820"},nodeNum:x.node_num,longName:x.long_name,role:x.role}}),y=c.map(x=>{const b=r===null||x.from_node===r||x.to_node===r;return{source:String(x.from_node),target:String(x.to_node),value:x.snr,lineStyle:{color:IQe(x.snr),width:b&&r!==null?2:1,opacity:r===null?.4:b?.6:.04}}});return{nodes:m,links:y}},[l,c,r,f]),d=U.useMemo(()=>({backgroundColor:"#111827",tooltip:{trigger:"item",backgroundColor:"#1e293b",borderColor:"#334155",textStyle:{color:"#e2e8f0",fontFamily:"JetBrains Mono, monospace",fontSize:11},formatter:m=>{if(m.data&&m.data.longName){const y=m.data;return`${y.name}
${y.longName}
Role: ${y.role}`}return""}},series:[{type:"graph",layout:"force",roam:!0,draggable:!0,animation:!1,data:h.nodes,links:h.links,force:{repulsion:200,edgeLength:[80,120],gravity:.1},emphasis:{focus:"adjacency",blurScope:"coordinateSystem",scale:1.1,lineStyle:{width:2}},blur:{itemStyle:{opacity:.15},lineStyle:{opacity:.04}},label:{show:!0,position:"bottom",distance:5,fontSize:10,fontFamily:"JetBrains Mono, monospace"},edgeLabel:{show:!1},edgeSymbol:["none","none"]}]}),[h]),v=U.useCallback(m=>{if(m.data&&"nodeNum"in m.data){const y=m.data.nodeNum;n(r===y?null:y??null)}},[r,n]),g=U.useMemo(()=>({click:v}),[v]);return U.useEffect(()=>{var y;const m=(y=i.current)==null?void 0:y.getEchartsInstance();m&&m.setOption(d,{notMerge:!1,lazyUpdate:!0})},[d]),_.jsxs("div",{className:"relative bg-bg-card rounded-lg border border-border overflow-hidden",children:[_.jsx(kQe,{ref:i,option:d,style:{height:"540px",width:"100%"},onEvents:g,opts:{renderer:"canvas"}}),_.jsxs("div",{className:"absolute top-4 left-4 flex items-center gap-2 bg-bg-card/90 backdrop-blur-sm border border-border rounded px-3 py-2",children:[_.jsx(qE,{size:14,className:"text-slate-500"}),_.jsx("div",{className:"flex gap-1",children:[{key:"connected",label:"Connected"},{key:"infra",label:"Infra"},{key:"all",label:"All"}].map(({key:m,label:y})=>_.jsx("button",{onClick:()=>o(m),className:`px-2 py-1 text-xs rounded transition-colors ${a===m?"bg-accent text-white":"bg-bg-hover text-slate-400 hover:text-slate-200"}`,children:y},m))}),_.jsxs("span",{className:"text-xs text-slate-500 ml-2",children:[l.length," nodes • ",c.length," edges"]})]}),_.jsxs("div",{className:"absolute bottom-4 left-4 bg-bg-card/90 backdrop-blur-sm border border-border rounded p-3",children:[_.jsx("div",{className:"text-xs text-slate-400 font-medium mb-2",children:"Edge Quality (SNR)"}),_.jsx("div",{className:"space-y-1",children:[{label:"Excellent (>12)",color:"#22c55e"},{label:"Good (8-12)",color:"#4ade80"},{label:"Fair (5-8)",color:"#f59e0b"},{label:"Marginal (3-5)",color:"#f97316"},{label:"Poor (<3)",color:"#ef4444"}].map(m=>_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx("div",{className:"w-4 h-0.5",style:{backgroundColor:m.color}}),_.jsx("span",{className:"text-xs text-slate-500",children:m.label})]},m.label))})]}),_.jsxs("div",{className:"absolute bottom-4 right-4 bg-bg-card/90 backdrop-blur-sm border border-border rounded p-3",children:[_.jsx("div",{className:"text-xs text-slate-400 font-medium mb-2",children:"Node Type"}),_.jsxs("div",{className:"space-y-2",children:[_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx("div",{className:"w-3 h-3 rounded-full bg-blue-500"}),_.jsx("span",{className:"text-xs text-slate-500",children:"Infrastructure"})]}),_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx("div",{className:"w-3 h-3 rounded-full bg-gray-900 border-2 border-blue-500"}),_.jsx("span",{className:"text-xs text-slate-500",children:"Client"})]})]})]})]})}function zie(e,t){const r=U.useRef(t);U.useEffect(function(){t!==r.current&&e.attributionControl!=null&&(r.current!=null&&e.attributionControl.removeAttribution(r.current),t!=null&&e.attributionControl.addAttribution(t)),r.current=t},[e,t])}function NQe(e,t,r){t.center!==r.center&&e.setLatLng(t.center),t.radius!=null&&t.radius!==r.radius&&e.setRadius(t.radius)}const jQe=1;function RQe(e){return Object.freeze({__version:jQe,map:e})}function $ie(e,t){return Object.freeze({...e,...t})}const Fie=U.createContext(null),Vie=Fie.Provider;function aC(){const e=U.useContext(Fie);if(e==null)throw new Error("No context provided: useLeafletContext() can only be used in a descendant of ");return e}function BQe(e){function t(r,n){const{instance:i,context:a}=e(r).current;return U.useImperativeHandle(n,()=>i),r.children==null?null:J.createElement(Vie,{value:a},r.children)}return U.forwardRef(t)}function zQe(e){function t(r,n){const[i,a]=U.useState(!1),{instance:o}=e(r,a).current;U.useImperativeHandle(n,()=>o),U.useEffect(function(){i&&o.update()},[o,i,r.children]);const s=o._contentNode;return s?uZ.createPortal(r.children,s):null}return U.forwardRef(t)}function $Qe(e){function t(r,n){const{instance:i}=e(r).current;return U.useImperativeHandle(n,()=>i),null}return U.forwardRef(t)}function GR(e,t){const r=U.useRef();U.useEffect(function(){return t!=null&&e.instance.on(t),r.current=t,function(){r.current!=null&&e.instance.off(r.current),r.current=null}},[e,t])}function oC(e,t){const r=e.pane??t.pane;return r?{...e,pane:r}:e}function FQe(e,t){return function(n,i){const a=aC(),o=e(oC(n,a),a);return zie(a.map,n.attribution),GR(o.current,n.eventHandlers),t(o.current,a,n,i),o}}var XO={exports:{}};/* @preserve * Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com * (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade - */(function(e,t){(function(r,n){n(t)})(cg,function(r){var n="1.9.4";function i(p){var w,A,O,R;for(A=1,O=arguments.length;A"u"||!L||!L.Mixin)){p=b(p)?p:[p];for(var w=0;w0?Math.floor(p):Math.ceil(p)};F.prototype={clone:function(){return new F(this.x,this.y)},add:function(p){return this.clone()._add(V(p))},_add:function(p){return this.x+=p.x,this.y+=p.y,this},subtract:function(p){return this.clone()._subtract(V(p))},_subtract:function(p){return this.x-=p.x,this.y-=p.y,this},divideBy:function(p){return this.clone()._divideBy(p)},_divideBy:function(p){return this.x/=p,this.y/=p,this},multiplyBy:function(p){return this.clone()._multiplyBy(p)},_multiplyBy:function(p){return this.x*=p,this.y*=p,this},scaleBy:function(p){return new F(this.x*p.x,this.y*p.y)},unscaleBy:function(p){return new F(this.x/p.x,this.y/p.y)},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.clone()._ceil()},_ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},trunc:function(){return this.clone()._trunc()},_trunc:function(){return this.x=G(this.x),this.y=G(this.y),this},distanceTo:function(p){p=V(p);var w=p.x-this.x,A=p.y-this.y;return Math.sqrt(w*w+A*A)},equals:function(p){return p=V(p),p.x===this.x&&p.y===this.y},contains:function(p){return p=V(p),Math.abs(p.x)<=Math.abs(this.x)&&Math.abs(p.y)<=Math.abs(this.y)},toString:function(){return"Point("+h(this.x)+", "+h(this.y)+")"}};function V(p,w,A){return p instanceof F?p:b(p)?new F(p[0],p[1]):p==null?p:typeof p=="object"&&"x"in p&&"y"in p?new F(p.x,p.y):new F(p,w,A)}function H(p,w){if(p)for(var A=w?[p,w]:p,O=0,R=A.length;O=this.min.x&&A.x<=this.max.x&&w.y>=this.min.y&&A.y<=this.max.y},intersects:function(p){p=Y(p);var w=this.min,A=this.max,O=p.min,R=p.max,W=R.x>=w.x&&O.x<=A.x,X=R.y>=w.y&&O.y<=A.y;return W&&X},overlaps:function(p){p=Y(p);var w=this.min,A=this.max,O=p.min,R=p.max,W=R.x>w.x&&O.xw.y&&O.y=w.lat&&R.lat<=A.lat&&O.lng>=w.lng&&R.lng<=A.lng},intersects:function(p){p=ee(p);var w=this._southWest,A=this._northEast,O=p.getSouthWest(),R=p.getNorthEast(),W=R.lat>=w.lat&&O.lat<=A.lat,X=R.lng>=w.lng&&O.lng<=A.lng;return W&&X},overlaps:function(p){p=ee(p);var w=this._southWest,A=this._northEast,O=p.getSouthWest(),R=p.getNorthEast(),W=R.lat>w.lat&&O.latw.lng&&O.lng1,aae=function(){var p=!1;try{var w=Object.defineProperty({},"passive",{get:function(){p=!0}});window.addEventListener("testPassiveEventSupport",f,w),window.removeEventListener("testPassiveEventSupport",f,w)}catch{}return p}(),oae=function(){return!!document.createElement("canvas").getContext}(),uC=!!(document.createElementNS&&Ge("svg").createSVGRect),sae=!!uC&&function(){var p=document.createElement("div");return p.innerHTML="",(p.firstChild&&p.firstChild.namespaceURI)==="http://www.w3.org/2000/svg"}(),lae=!uC&&function(){try{var p=document.createElement("div");p.innerHTML='';var w=p.firstChild;return w.style.behavior="url(#default#VML)",w&&typeof w.adj=="object"}catch{return!1}}(),uae=navigator.platform.indexOf("Mac")===0,cae=navigator.platform.indexOf("Linux")===0;function eo(p){return navigator.userAgent.toLowerCase().indexOf(p)>=0}var Ue={ie:Ft,ielt9:rr,edge:Nn,webkit:Xr,android:qn,android23:Df,androidStock:C0,opera:sC,chrome:ZR,gecko:YR,safari:qie,phantom:XR,opera12:qR,win:Kie,ie3d:KR,webkit3d:lC,gecko3d:QR,any3d:Qie,mobile:tp,mobileWebkit:Jie,mobileWebkit3d:eae,msPointer:JR,pointer:e5,touch:tae,touchNative:t5,mobileOpera:rae,mobileGecko:nae,retina:iae,passiveEvents:aae,canvas:oae,svg:uC,vml:lae,inlineSvg:sae,mac:uae,linux:cae},r5=Ue.msPointer?"MSPointerDown":"pointerdown",n5=Ue.msPointer?"MSPointerMove":"pointermove",i5=Ue.msPointer?"MSPointerUp":"pointerup",a5=Ue.msPointer?"MSPointerCancel":"pointercancel",cC={touchstart:r5,touchmove:n5,touchend:i5,touchcancel:a5},o5={touchstart:gae,touchmove:A0,touchend:A0,touchcancel:A0},Nf={},s5=!1;function fae(p,w,A){return w==="touchstart"&&pae(),o5[w]?(A=o5[w].bind(this,A),p.addEventListener(cC[w],A,!1),A):(console.warn("wrong event specified:",w),f)}function hae(p,w,A){if(!cC[w]){console.warn("wrong event specified:",w);return}p.removeEventListener(cC[w],A,!1)}function dae(p){Nf[p.pointerId]=p}function vae(p){Nf[p.pointerId]&&(Nf[p.pointerId]=p)}function l5(p){delete Nf[p.pointerId]}function pae(){s5||(document.addEventListener(r5,dae,!0),document.addEventListener(n5,vae,!0),document.addEventListener(i5,l5,!0),document.addEventListener(a5,l5,!0),s5=!0)}function A0(p,w){if(w.pointerType!==(w.MSPOINTER_TYPE_MOUSE||"mouse")){w.touches=[];for(var A in Nf)w.touches.push(Nf[A]);w.changedTouches=[w],p(w)}}function gae(p,w){w.MSPOINTER_TYPE_TOUCH&&w.pointerType===w.MSPOINTER_TYPE_TOUCH&&ln(w),A0(p,w)}function mae(p){var w={},A,O;for(O in p)A=p[O],w[O]=A&&A.bind?A.bind(p):A;return p=w,w.type="dblclick",w.detail=2,w.isTrusted=!1,w._simulated=!0,w}var yae=200;function _ae(p,w){p.addEventListener("dblclick",w);var A=0,O;function R(W){if(W.detail!==1){O=W.detail;return}if(!(W.pointerType==="mouse"||W.sourceCapabilities&&!W.sourceCapabilities.firesTouchEvents)){var X=d5(W);if(!(X.some(function(oe){return oe instanceof HTMLLabelElement&&oe.attributes.for})&&!X.some(function(oe){return oe instanceof HTMLInputElement||oe instanceof HTMLSelectElement}))){var re=Date.now();re-A<=yae?(O++,O===2&&w(mae(W))):O=1,A=re}}}return p.addEventListener("click",R),{dblclick:w,simDblclick:R}}function xae(p,w){p.removeEventListener("dblclick",w.dblclick),p.removeEventListener("click",w.simDblclick)}var fC=L0(["transform","webkitTransform","OTransform","MozTransform","msTransform"]),rp=L0(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),u5=rp==="webkitTransition"||rp==="OTransition"?rp+"End":"transitionend";function c5(p){return typeof p=="string"?document.getElementById(p):p}function np(p,w){var A=p.style[w]||p.currentStyle&&p.currentStyle[w];if((!A||A==="auto")&&document.defaultView){var O=document.defaultView.getComputedStyle(p,null);A=O?O[w]:null}return A==="auto"?null:A}function Ct(p,w,A){var O=document.createElement(p);return O.className=w||"",A&&A.appendChild(O),O}function nr(p){var w=p.parentNode;w&&w.removeChild(p)}function M0(p){for(;p.firstChild;)p.removeChild(p.firstChild)}function jf(p){var w=p.parentNode;w&&w.lastChild!==p&&w.appendChild(p)}function Rf(p){var w=p.parentNode;w&&w.firstChild!==p&&w.insertBefore(p,w.firstChild)}function hC(p,w){if(p.classList!==void 0)return p.classList.contains(w);var A=P0(p);return A.length>0&&new RegExp("(^|\\s)"+w+"(\\s|$)").test(A)}function ut(p,w){if(p.classList!==void 0)for(var A=v(w),O=0,R=A.length;O0?2*window.devicePixelRatio:1;function p5(p){return Ue.edge?p.wheelDeltaY/2:p.deltaY&&p.deltaMode===0?-p.deltaY/Sae:p.deltaY&&p.deltaMode===1?-p.deltaY*20:p.deltaY&&p.deltaMode===2?-p.deltaY*60:p.deltaX||p.deltaZ?0:p.wheelDelta?(p.wheelDeltaY||p.wheelDelta)/2:p.detail&&Math.abs(p.detail)<32765?-p.detail*20:p.detail?p.detail/-32765*60:0}function TC(p,w){var A=w.relatedTarget;if(!A)return!0;try{for(;A&&A!==p;)A=A.parentNode}catch{return!1}return A!==p}var Tae={__proto__:null,on:st,off:Wt,stopPropagation:Pu,disableScrollPropagation:SC,disableClickPropagation:sp,preventDefault:ln,stop:Lu,getPropagationPath:d5,getMousePosition:v5,getWheelDelta:p5,isExternalTarget:TC,addListener:st,removeListener:Wt},g5=Z.extend({run:function(p,w,A,O){this.stop(),this._el=p,this._inProgress=!0,this._duration=A||.25,this._easeOutPower=1/Math.max(O||.5,.2),this._startPos=Mu(p),this._offset=w.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(!0),this._complete())},_animate:function(){this._animId=E(this._animate,this),this._step()},_step:function(p){var w=+new Date-this._startTime,A=this._duration*1e3;wthis.options.maxZoom)?this.setZoom(p):this},panInsideBounds:function(p,w){this._enforcingBounds=!0;var A=this.getCenter(),O=this._limitCenter(A,this._zoom,ee(p));return A.equals(O)||this.panTo(O,w),this._enforcingBounds=!1,this},panInside:function(p,w){w=w||{};var A=V(w.paddingTopLeft||w.padding||[0,0]),O=V(w.paddingBottomRight||w.padding||[0,0]),R=this.project(this.getCenter()),W=this.project(p),X=this.getPixelBounds(),re=Y([X.min.add(A),X.max.subtract(O)]),oe=re.getSize();if(!re.contains(W)){this._enforcingBounds=!0;var de=W.subtract(re.getCenter()),Ee=re.extend(W).getSize().subtract(oe);R.x+=de.x<0?-Ee.x:Ee.x,R.y+=de.y<0?-Ee.y:Ee.y,this.panTo(this.unproject(R),w),this._enforcingBounds=!1}return this},invalidateSize:function(p){if(!this._loaded)return this;p=i({animate:!1,pan:!0},p===!0?{animate:!0}:p);var w=this.getSize();this._sizeChanged=!0,this._lastCenter=null;var A=this.getSize(),O=w.divideBy(2).round(),R=A.divideBy(2).round(),W=O.subtract(R);return!W.x&&!W.y?this:(p.animate&&p.pan?this.panBy(W):(p.pan&&this._rawPanBy(W),this.fire("move"),p.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(o(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:w,newSize:A}))},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(p){if(p=this._locateOptions=i({timeout:1e4,watch:!1},p),!("geolocation"in navigator))return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var w=o(this._handleGeolocationResponse,this),A=o(this._handleGeolocationError,this);return p.watch?this._locationWatchId=navigator.geolocation.watchPosition(w,A,p):navigator.geolocation.getCurrentPosition(w,A,p),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(p){if(this._container._leaflet_id){var w=p.code,A=p.message||(w===1?"permission denied":w===2?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:w,message:"Geolocation error: "+A+"."})}},_handleGeolocationResponse:function(p){if(this._container._leaflet_id){var w=p.coords.latitude,A=p.coords.longitude,O=new le(w,A),R=O.toBounds(p.coords.accuracy*2),W=this._locateOptions;if(W.setView){var X=this.getBoundsZoom(R);this.setView(O,W.maxZoom?Math.min(X,W.maxZoom):X)}var re={latlng:O,bounds:R,timestamp:p.timestamp};for(var oe in p.coords)typeof p.coords[oe]=="number"&&(re[oe]=p.coords[oe]);this.fire("locationfound",re)}},addHandler:function(p,w){if(!w)return this;var A=this[p]=new w(this);return this._handlers.push(A),this.options[p]&&A.enable(),this},remove:function(){if(this._initEvents(!0),this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch{this._container._leaflet_id=void 0,this._containerId=void 0}this._locationWatchId!==void 0&&this.stopLocate(),this._stop(),nr(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._resizeRequest&&(D(this._resizeRequest),this._resizeRequest=null),this._clearHandlers(),this._loaded&&this.fire("unload");var p;for(p in this._layers)this._layers[p].remove();for(p in this._panes)nr(this._panes[p]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(p,w){var A="leaflet-pane"+(p?" leaflet-"+p.replace("Pane","")+"-pane":""),O=Ct("div",A,w||this._mapPane);return p&&(this._panes[p]=O),O},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter.clone():this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var p=this.getPixelBounds(),w=this.unproject(p.getBottomLeft()),A=this.unproject(p.getTopRight());return new K(w,A)},getMinZoom:function(){return this.options.minZoom===void 0?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return this.options.maxZoom===void 0?this._layersMaxZoom===void 0?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(p,w,A){p=ee(p),A=V(A||[0,0]);var O=this.getZoom()||0,R=this.getMinZoom(),W=this.getMaxZoom(),X=p.getNorthWest(),re=p.getSouthEast(),oe=this.getSize().subtract(A),de=Y(this.project(re,O),this.project(X,O)).getSize(),Ee=Ue.any3d?this.options.zoomSnap:1,Je=oe.x/de.x,pt=oe.y/de.y,jn=w?Math.max(Je,pt):Math.min(Je,pt);return O=this.getScaleZoom(jn,O),Ee&&(O=Math.round(O/(Ee/100))*(Ee/100),O=w?Math.ceil(O/Ee)*Ee:Math.floor(O/Ee)*Ee),Math.max(R,Math.min(W,O))},getSize:function(){return(!this._size||this._sizeChanged)&&(this._size=new F(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(p,w){var A=this._getTopLeftPoint(p,w);return new H(A,A.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(p){return this.options.crs.getProjectedBounds(p===void 0?this.getZoom():p)},getPane:function(p){return typeof p=="string"?this._panes[p]:p},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(p,w){var A=this.options.crs;return w=w===void 0?this._zoom:w,A.scale(p)/A.scale(w)},getScaleZoom:function(p,w){var A=this.options.crs;w=w===void 0?this._zoom:w;var O=A.zoom(p*A.scale(w));return isNaN(O)?1/0:O},project:function(p,w){return w=w===void 0?this._zoom:w,this.options.crs.latLngToPoint(fe(p),w)},unproject:function(p,w){return w=w===void 0?this._zoom:w,this.options.crs.pointToLatLng(V(p),w)},layerPointToLatLng:function(p){var w=V(p).add(this.getPixelOrigin());return this.unproject(w)},latLngToLayerPoint:function(p){var w=this.project(fe(p))._round();return w._subtract(this.getPixelOrigin())},wrapLatLng:function(p){return this.options.crs.wrapLatLng(fe(p))},wrapLatLngBounds:function(p){return this.options.crs.wrapLatLngBounds(ee(p))},distance:function(p,w){return this.options.crs.distance(fe(p),fe(w))},containerPointToLayerPoint:function(p){return V(p).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(p){return V(p).add(this._getMapPanePos())},containerPointToLatLng:function(p){var w=this.containerPointToLayerPoint(V(p));return this.layerPointToLatLng(w)},latLngToContainerPoint:function(p){return this.layerPointToContainerPoint(this.latLngToLayerPoint(fe(p)))},mouseEventToContainerPoint:function(p){return v5(p,this._container)},mouseEventToLayerPoint:function(p){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(p))},mouseEventToLatLng:function(p){return this.layerPointToLatLng(this.mouseEventToLayerPoint(p))},_initContainer:function(p){var w=this._container=c5(p);if(w){if(w._leaflet_id)throw new Error("Map container is already initialized.")}else throw new Error("Map container not found.");st(w,"scroll",this._onScroll,this),this._containerId=l(w)},_initLayout:function(){var p=this._container;this._fadeAnimated=this.options.fadeAnimation&&Ue.any3d,ut(p,"leaflet-container"+(Ue.touch?" leaflet-touch":"")+(Ue.retina?" leaflet-retina":"")+(Ue.ielt9?" leaflet-oldie":"")+(Ue.safari?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":""));var w=np(p,"position");w!=="absolute"&&w!=="relative"&&w!=="fixed"&&w!=="sticky"&&(p.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var p=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),Ar(this._mapPane,new F(0,0)),this.createPane("tilePane"),this.createPane("overlayPane"),this.createPane("shadowPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(ut(p.markerPane,"leaflet-zoom-hide"),ut(p.shadowPane,"leaflet-zoom-hide"))},_resetView:function(p,w,A){Ar(this._mapPane,new F(0,0));var O=!this._loaded;this._loaded=!0,w=this._limitZoom(w),this.fire("viewprereset");var R=this._zoom!==w;this._moveStart(R,A)._move(p,w)._moveEnd(R),this.fire("viewreset"),O&&this.fire("load")},_moveStart:function(p,w){return p&&this.fire("zoomstart"),w||this.fire("movestart"),this},_move:function(p,w,A,O){w===void 0&&(w=this._zoom);var R=this._zoom!==w;return this._zoom=w,this._lastCenter=p,this._pixelOrigin=this._getNewPixelOrigin(p),O?A&&A.pinch&&this.fire("zoom",A):((R||A&&A.pinch)&&this.fire("zoom",A),this.fire("move",A)),this},_moveEnd:function(p){return p&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return D(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(p){Ar(this._mapPane,this._getMapPanePos().subtract(p))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(p){this._targets={},this._targets[l(this._container)]=this;var w=p?Wt:st;w(this._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress keydown keyup",this._handleDOMEvent,this),this.options.trackResize&&w(window,"resize",this._onResize,this),Ue.any3d&&this.options.transform3DLimit&&(p?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){D(this._resizeRequest),this._resizeRequest=E(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var p=this._getMapPanePos();Math.max(Math.abs(p.x),Math.abs(p.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(p,w){for(var A=[],O,R=w==="mouseout"||w==="mouseover",W=p.target||p.srcElement,X=!1;W;){if(O=this._targets[l(W)],O&&(w==="click"||w==="preclick")&&this._draggableMoved(O)){X=!0;break}if(O&&O.listens(w,!0)&&(R&&!TC(W,p)||(A.push(O),R))||W===this._container)break;W=W.parentNode}return!A.length&&!X&&!R&&this.listens(w,!0)&&(A=[this]),A},_isClickDisabled:function(p){for(;p&&p!==this._container;){if(p._leaflet_disable_click)return!0;p=p.parentNode}},_handleDOMEvent:function(p){var w=p.target||p.srcElement;if(!(!this._loaded||w._leaflet_disable_events||p.type==="click"&&this._isClickDisabled(w))){var A=p.type;A==="mousedown"&&yC(w),this._fireDOMEvent(p,A)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(p,w,A){if(p.type==="click"){var O=i({},p);O.type="preclick",this._fireDOMEvent(O,O.type,A)}var R=this._findEventTargets(p,w);if(A){for(var W=[],X=0;X0?Math.round(p-w)/2:Math.max(0,Math.ceil(p))-Math.max(0,Math.floor(w))},_limitZoom:function(p){var w=this.getMinZoom(),A=this.getMaxZoom(),O=Ue.any3d?this.options.zoomSnap:1;return O&&(p=Math.round(p/O)*O),Math.max(w,Math.min(A,p))},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){xr(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(p,w){var A=this._getCenterOffset(p)._trunc();return(w&&w.animate)!==!0&&!this.getSize().contains(A)?!1:(this.panBy(A,w),!0)},_createAnimProxy:function(){var p=this._proxy=Ct("div","leaflet-proxy leaflet-zoom-animated");this._panes.mapPane.appendChild(p),this.on("zoomanim",function(w){var A=fC,O=this._proxy.style[A];Au(this._proxy,this.project(w.center,w.zoom),this.getZoomScale(w.zoom,1)),O===this._proxy.style[A]&&this._animatingZoom&&this._onZoomTransitionEnd()},this),this.on("load moveend",this._animMoveEnd,this),this._on("unload",this._destroyAnimProxy,this)},_destroyAnimProxy:function(){nr(this._proxy),this.off("load moveend",this._animMoveEnd,this),delete this._proxy},_animMoveEnd:function(){var p=this.getCenter(),w=this.getZoom();Au(this._proxy,this.project(p,w),this.getZoomScale(w,1))},_catchTransitionEnd:function(p){this._animatingZoom&&p.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(p,w,A){if(this._animatingZoom)return!0;if(A=A||{},!this._zoomAnimated||A.animate===!1||this._nothingToAnimate()||Math.abs(w-this._zoom)>this.options.zoomAnimationThreshold)return!1;var O=this.getZoomScale(w),R=this._getCenterOffset(p)._divideBy(1-1/O);return A.animate!==!0&&!this.getSize().contains(R)?!1:(E(function(){this._moveStart(!0,A.noMoveStart||!1)._animateZoom(p,w,!0)},this),!0)},_animateZoom:function(p,w,A,O){this._mapPane&&(A&&(this._animatingZoom=!0,this._animateToCenter=p,this._animateToZoom=w,ut(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:p,zoom:w,noUpdate:O}),this._tempFireZoomEvent||(this._tempFireZoomEvent=this._zoom!==this._animateToZoom),this._move(this._animateToCenter,this._animateToZoom,void 0,!0),setTimeout(o(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&xr(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom,void 0,!0),this._tempFireZoomEvent&&this.fire("zoom"),delete this._tempFireZoomEvent,this.fire("move"),this._moveEnd(!0))}});function Cae(p,w){return new wt(p,w)}var ba=N.extend({options:{position:"topright"},initialize:function(p){g(this,p)},getPosition:function(){return this.options.position},setPosition:function(p){var w=this._map;return w&&w.removeControl(this),this.options.position=p,w&&w.addControl(this),this},getContainer:function(){return this._container},addTo:function(p){this.remove(),this._map=p;var w=this._container=this.onAdd(p),A=this.getPosition(),O=p._controlCorners[A];return ut(w,"leaflet-control"),A.indexOf("bottom")!==-1?O.insertBefore(w,O.firstChild):O.appendChild(w),this._map.on("unload",this.remove,this),this},remove:function(){return this._map?(nr(this._container),this.onRemove&&this.onRemove(this._map),this._map.off("unload",this.remove,this),this._map=null,this):this},_refocusOnMap:function(p){this._map&&p&&p.screenX>0&&p.screenY>0&&this._map.getContainer().focus()}}),lp=function(p){return new ba(p)};wt.include({addControl:function(p){return p.addTo(this),this},removeControl:function(p){return p.remove(),this},_initControlPos:function(){var p=this._controlCorners={},w="leaflet-",A=this._controlContainer=Ct("div",w+"control-container",this._container);function O(R,W){var X=w+R+" "+w+W;p[R+W]=Ct("div",X,A)}O("top","left"),O("top","right"),O("bottom","left"),O("bottom","right")},_clearControlPos:function(){for(var p in this._controlCorners)nr(this._controlCorners[p]);nr(this._controlContainer),delete this._controlCorners,delete this._controlContainer}});var m5=ba.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0,hideSingleBase:!1,sortLayers:!1,sortFunction:function(p,w,A,O){return A1,this._baseLayersList.style.display=p?"":"none"),this._separator.style.display=w&&p?"":"none",this},_onLayerChange:function(p){this._handlingClick||this._update();var w=this._getLayer(l(p.target)),A=w.overlay?p.type==="add"?"overlayadd":"overlayremove":p.type==="add"?"baselayerchange":null;A&&this._map.fire(A,w)},_createRadioElement:function(p,w){var A='",O=document.createElement("div");return O.innerHTML=A,O.firstChild},_addItem:function(p){var w=document.createElement("label"),A=this._map.hasLayer(p.layer),O;p.overlay?(O=document.createElement("input"),O.type="checkbox",O.className="leaflet-control-layers-selector",O.defaultChecked=A):O=this._createRadioElement("leaflet-base-layers_"+l(this),A),this._layerControlInputs.push(O),O.layerId=l(p.layer),st(O,"click",this._onInputClick,this);var R=document.createElement("span");R.innerHTML=" "+p.name;var W=document.createElement("span");w.appendChild(W),W.appendChild(O),W.appendChild(R);var X=p.overlay?this._overlaysList:this._baseLayersList;return X.appendChild(w),this._checkDisabledLayers(),w},_onInputClick:function(){if(!this._preventClick){var p=this._layerControlInputs,w,A,O=[],R=[];this._handlingClick=!0;for(var W=p.length-1;W>=0;W--)w=p[W],A=this._getLayer(w.layerId).layer,w.checked?O.push(A):w.checked||R.push(A);for(W=0;W=0;R--)w=p[R],A=this._getLayer(w.layerId).layer,w.disabled=A.options.minZoom!==void 0&&OA.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expandSafely:function(){var p=this._section;this._preventClick=!0,st(p,"click",ln),this.expand();var w=this;setTimeout(function(){Wt(p,"click",ln),w._preventClick=!1})}}),Aae=function(p,w,A){return new m5(p,w,A)},CC=ba.extend({options:{position:"topleft",zoomInText:'',zoomInTitle:"Zoom in",zoomOutText:'',zoomOutTitle:"Zoom out"},onAdd:function(p){var w="leaflet-control-zoom",A=Ct("div",w+" leaflet-bar"),O=this.options;return this._zoomInButton=this._createButton(O.zoomInText,O.zoomInTitle,w+"-in",A,this._zoomIn),this._zoomOutButton=this._createButton(O.zoomOutText,O.zoomOutTitle,w+"-out",A,this._zoomOut),this._updateDisabled(),p.on("zoomend zoomlevelschange",this._updateDisabled,this),A},onRemove:function(p){p.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(p){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(p.shiftKey?3:1))},_createButton:function(p,w,A,O,R){var W=Ct("a",A,O);return W.innerHTML=p,W.href="#",W.title=w,W.setAttribute("role","button"),W.setAttribute("aria-label",w),sp(W),st(W,"click",Lu),st(W,"click",R,this),st(W,"click",this._refocusOnMap,this),W},_updateDisabled:function(){var p=this._map,w="leaflet-disabled";xr(this._zoomInButton,w),xr(this._zoomOutButton,w),this._zoomInButton.setAttribute("aria-disabled","false"),this._zoomOutButton.setAttribute("aria-disabled","false"),(this._disabled||p._zoom===p.getMinZoom())&&(ut(this._zoomOutButton,w),this._zoomOutButton.setAttribute("aria-disabled","true")),(this._disabled||p._zoom===p.getMaxZoom())&&(ut(this._zoomInButton,w),this._zoomInButton.setAttribute("aria-disabled","true"))}});wt.mergeOptions({zoomControl:!0}),wt.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new CC,this.addControl(this.zoomControl))});var Mae=function(p){return new CC(p)},y5=ba.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(p){var w="leaflet-control-scale",A=Ct("div",w),O=this.options;return this._addScales(O,w+"-line",A),p.on(O.updateWhenIdle?"moveend":"move",this._update,this),p.whenReady(this._update,this),A},onRemove:function(p){p.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(p,w,A){p.metric&&(this._mScale=Ct("div",w,A)),p.imperial&&(this._iScale=Ct("div",w,A))},_update:function(){var p=this._map,w=p.getSize().y/2,A=p.distance(p.containerPointToLatLng([0,w]),p.containerPointToLatLng([this.options.maxWidth,w]));this._updateScales(A)},_updateScales:function(p){this.options.metric&&p&&this._updateMetric(p),this.options.imperial&&p&&this._updateImperial(p)},_updateMetric:function(p){var w=this._getRoundNum(p),A=w<1e3?w+" m":w/1e3+" km";this._updateScale(this._mScale,A,w/p)},_updateImperial:function(p){var w=p*3.2808399,A,O,R;w>5280?(A=w/5280,O=this._getRoundNum(A),this._updateScale(this._iScale,O+" mi",O/A)):(R=this._getRoundNum(w),this._updateScale(this._iScale,R+" ft",R/w))},_updateScale:function(p,w,A){p.style.width=Math.round(this.options.maxWidth*A)+"px",p.innerHTML=w},_getRoundNum:function(p){var w=Math.pow(10,(Math.floor(p)+"").length-1),A=p/w;return A=A>=10?10:A>=5?5:A>=3?3:A>=2?2:1,w*A}}),Pae=function(p){return new y5(p)},Lae='',AC=ba.extend({options:{position:"bottomright",prefix:''+(Ue.inlineSvg?Lae+" ":"")+"Leaflet"},initialize:function(p){g(this,p),this._attributions={}},onAdd:function(p){p.attributionControl=this,this._container=Ct("div","leaflet-control-attribution"),sp(this._container);for(var w in p._layers)p._layers[w].getAttribution&&this.addAttribution(p._layers[w].getAttribution());return this._update(),p.on("layeradd",this._addAttribution,this),this._container},onRemove:function(p){p.off("layeradd",this._addAttribution,this)},_addAttribution:function(p){p.layer.getAttribution&&(this.addAttribution(p.layer.getAttribution()),p.layer.once("remove",function(){this.removeAttribution(p.layer.getAttribution())},this))},setPrefix:function(p){return this.options.prefix=p,this._update(),this},addAttribution:function(p){return p?(this._attributions[p]||(this._attributions[p]=0),this._attributions[p]++,this._update(),this):this},removeAttribution:function(p){return p?(this._attributions[p]&&(this._attributions[p]--,this._update()),this):this},_update:function(){if(this._map){var p=[];for(var w in this._attributions)this._attributions[w]&&p.push(w);var A=[];this.options.prefix&&A.push(this.options.prefix),p.length&&A.push(p.join(", ")),this._container.innerHTML=A.join(' ')}}});wt.mergeOptions({attributionControl:!0}),wt.addInitHook(function(){this.options.attributionControl&&new AC().addTo(this)});var kae=function(p){return new AC(p)};ba.Layers=m5,ba.Zoom=CC,ba.Scale=y5,ba.Attribution=AC,lp.layers=Aae,lp.zoom=Mae,lp.scale=Pae,lp.attribution=kae;var ro=N.extend({initialize:function(p){this._map=p},enable:function(){return this._enabled?this:(this._enabled=!0,this.addHooks(),this)},disable:function(){return this._enabled?(this._enabled=!1,this.removeHooks(),this):this},enabled:function(){return!!this._enabled}});ro.addTo=function(p,w){return p.addHandler(w,this),this};var Iae={Events:$},_5=Ue.touch?"touchstart mousedown":"mousedown",Js=Z.extend({options:{clickTolerance:3},initialize:function(p,w,A,O){g(this,O),this._element=p,this._dragStartTarget=w||p,this._preventOutline=A},enable:function(){this._enabled||(st(this._dragStartTarget,_5,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Js._dragging===this&&this.finishDrag(!0),Wt(this._dragStartTarget,_5,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(p){if(this._enabled&&(this._moved=!1,!hC(this._element,"leaflet-zoom-anim"))){if(p.touches&&p.touches.length!==1){Js._dragging===this&&this.finishDrag();return}if(!(Js._dragging||p.shiftKey||p.which!==1&&p.button!==1&&!p.touches)&&(Js._dragging=this,this._preventOutline&&yC(this._element),pC(),ip(),!this._moving)){this.fire("down");var w=p.touches?p.touches[0]:p,A=f5(this._element);this._startPoint=new F(w.clientX,w.clientY),this._startPos=Mu(this._element),this._parentScale=_C(A);var O=p.type==="mousedown";st(document,O?"mousemove":"touchmove",this._onMove,this),st(document,O?"mouseup":"touchend touchcancel",this._onUp,this)}}},_onMove:function(p){if(this._enabled){if(p.touches&&p.touches.length>1){this._moved=!0;return}var w=p.touches&&p.touches.length===1?p.touches[0]:p,A=new F(w.clientX,w.clientY)._subtract(this._startPoint);!A.x&&!A.y||Math.abs(A.x)+Math.abs(A.y)W&&(X=re,W=oe);W>A&&(w[X]=1,PC(p,w,A,O,X),PC(p,w,A,X,R))}function Nae(p,w){for(var A=[p[0]],O=1,R=0,W=p.length;Ow&&(A.push(p[O]),R=O);return Rw.max.x&&(A|=2),p.yw.max.y&&(A|=8),A}function jae(p,w){var A=w.x-p.x,O=w.y-p.y;return A*A+O*O}function up(p,w,A,O){var R=w.x,W=w.y,X=A.x-R,re=A.y-W,oe=X*X+re*re,de;return oe>0&&(de=((p.x-R)*X+(p.y-W)*re)/oe,de>1?(R=A.x,W=A.y):de>0&&(R+=X*de,W+=re*de)),X=p.x-R,re=p.y-W,O?X*X+re*re:new F(R,W)}function Bi(p){return!b(p[0])||typeof p[0][0]!="object"&&typeof p[0][0]<"u"}function A5(p){return console.warn("Deprecated use of _flat, please use L.LineUtil.isFlat instead."),Bi(p)}function M5(p,w){var A,O,R,W,X,re,oe,de;if(!p||p.length===0)throw new Error("latlngs not passed");Bi(p)||(console.warn("latlngs are not flat! Only the first ring will be used"),p=p[0]);var Ee=fe([0,0]),Je=ee(p),pt=Je.getNorthWest().distanceTo(Je.getSouthWest())*Je.getNorthEast().distanceTo(Je.getNorthWest());pt<1700&&(Ee=MC(p));var jn=p.length,qr=[];for(A=0;AO){oe=(W-O)/R,de=[re.x-oe*(re.x-X.x),re.y-oe*(re.y-X.y)];break}var Kn=w.unproject(V(de));return fe([Kn.lat+Ee.lat,Kn.lng+Ee.lng])}var Rae={__proto__:null,simplify:w5,pointToSegmentDistance:S5,closestPointOnSegment:Eae,clipSegment:C5,_getEdgeIntersection:O0,_getBitCode:ku,_sqClosestPointOnSegment:up,isFlat:Bi,_flat:A5,polylineCenter:M5},LC={project:function(p){return new F(p.lng,p.lat)},unproject:function(p){return new le(p.y,p.x)},bounds:new H([-180,-90],[180,90])},kC={R:6378137,R_MINOR:6356752314245179e-9,bounds:new H([-2003750834279e-5,-1549657073972e-5],[2003750834279e-5,1876465623138e-5]),project:function(p){var w=Math.PI/180,A=this.R,O=p.lat*w,R=this.R_MINOR/A,W=Math.sqrt(1-R*R),X=W*Math.sin(O),re=Math.tan(Math.PI/4-O/2)/Math.pow((1-X)/(1+X),W/2);return O=-A*Math.log(Math.max(re,1e-10)),new F(p.lng*w*A,O)},unproject:function(p){for(var w=180/Math.PI,A=this.R,O=this.R_MINOR/A,R=Math.sqrt(1-O*O),W=Math.exp(-p.y/A),X=Math.PI/2-2*Math.atan(W),re=0,oe=.1,de;re<15&&Math.abs(oe)>1e-7;re++)de=R*Math.sin(X),de=Math.pow((1-de)/(1+de),R/2),oe=Math.PI/2-2*Math.atan(W*de)-X,X+=oe;return new le(X*w,p.x*w/A)}},Bae={__proto__:null,LonLat:LC,Mercator:kC,SphericalMercator:he},zae=i({},_e,{code:"EPSG:3395",projection:kC,transformation:function(){var p=.5/(Math.PI*kC.R);return te(p,.5,-p,.5)}()}),P5=i({},_e,{code:"EPSG:4326",projection:LC,transformation:te(1/180,1,-1/180,.5)}),$ae=i({},Be,{projection:LC,transformation:te(1,0,-1,0),scale:function(p){return Math.pow(2,p)},zoom:function(p){return Math.log(p)/Math.LN2},distance:function(p,w){var A=w.lng-p.lng,O=w.lat-p.lat;return Math.sqrt(A*A+O*O)},infinite:!0});Be.Earth=_e,Be.EPSG3395=zae,Be.EPSG3857=Ve,Be.EPSG900913=Se,Be.EPSG4326=P5,Be.Simple=$ae;var wa=Z.extend({options:{pane:"overlayPane",attribution:null,bubblingMouseEvents:!0},addTo:function(p){return p.addLayer(this),this},remove:function(){return this.removeFrom(this._map||this._mapToAdd)},removeFrom:function(p){return p&&p.removeLayer(this),this},getPane:function(p){return this._map.getPane(p?this.options[p]||p:this.options.pane)},addInteractiveTarget:function(p){return this._map._targets[l(p)]=this,this},removeInteractiveTarget:function(p){return delete this._map._targets[l(p)],this},getAttribution:function(){return this.options.attribution},_layerAdd:function(p){var w=p.target;if(w.hasLayer(this)){if(this._map=w,this._zoomAnimated=w._zoomAnimated,this.getEvents){var A=this.getEvents();w.on(A,this),this.once("remove",function(){w.off(A,this)},this)}this.onAdd(w),this.fire("add"),w.fire("layeradd",{layer:this})}}});wt.include({addLayer:function(p){if(!p._layerAdd)throw new Error("The provided object is not a Layer.");var w=l(p);return this._layers[w]?this:(this._layers[w]=p,p._mapToAdd=this,p.beforeAdd&&p.beforeAdd(this),this.whenReady(p._layerAdd,p),this)},removeLayer:function(p){var w=l(p);return this._layers[w]?(this._loaded&&p.onRemove(this),delete this._layers[w],this._loaded&&(this.fire("layerremove",{layer:p}),p.fire("remove")),p._map=p._mapToAdd=null,this):this},hasLayer:function(p){return l(p)in this._layers},eachLayer:function(p,w){for(var A in this._layers)p.call(w,this._layers[A]);return this},_addLayers:function(p){p=p?b(p)?p:[p]:[];for(var w=0,A=p.length;wthis._layersMaxZoom&&this.setZoom(this._layersMaxZoom),this.options.minZoom===void 0&&this._layersMinZoom&&this.getZoom()=2&&w[0]instanceof le&&w[0].equals(w[A-1])&&w.pop(),w},_setLatLngs:function(p){Ko.prototype._setLatLngs.call(this,p),Bi(this._latlngs)&&(this._latlngs=[this._latlngs])},_defaultShape:function(){return Bi(this._latlngs[0])?this._latlngs[0]:this._latlngs[0][0]},_clipPoints:function(){var p=this._renderer._bounds,w=this.options.weight,A=new F(w,w);if(p=new H(p.min.subtract(A),p.max.add(A)),this._parts=[],!(!this._pxBounds||!this._pxBounds.intersects(p))){if(this.options.noClip){this._parts=this._rings;return}for(var O=0,R=this._rings.length,W;Op.y!=R.y>p.y&&p.x<(R.x-O.x)*(p.y-O.y)/(R.y-O.y)+O.x&&(w=!w);return w||Ko.prototype._containsPoint.call(this,p,!0)}});function Yae(p,w){return new $f(p,w)}var Qo=qo.extend({initialize:function(p,w){g(this,w),this._layers={},p&&this.addData(p)},addData:function(p){var w=b(p)?p:p.features,A,O,R;if(w){for(A=0,O=w.length;A0&&R.push(R[0].slice()),R}function Ff(p,w){return p.feature?i({},p.feature,{geometry:w}):B0(w)}function B0(p){return p.type==="Feature"||p.type==="FeatureCollection"?p:{type:"Feature",properties:{},geometry:p}}var DC={toGeoJSON:function(p){return Ff(this,{type:"Point",coordinates:EC(this.getLatLng(),p)})}};E0.include(DC),IC.include(DC),D0.include(DC),Ko.include({toGeoJSON:function(p){var w=!Bi(this._latlngs),A=R0(this._latlngs,w?1:0,!1,p);return Ff(this,{type:(w?"Multi":"")+"LineString",coordinates:A})}}),$f.include({toGeoJSON:function(p){var w=!Bi(this._latlngs),A=w&&!Bi(this._latlngs[0]),O=R0(this._latlngs,A?2:w?1:0,!0,p);return w||(O=[O]),Ff(this,{type:(A?"Multi":"")+"Polygon",coordinates:O})}}),Bf.include({toMultiPoint:function(p){var w=[];return this.eachLayer(function(A){w.push(A.toGeoJSON(p).geometry.coordinates)}),Ff(this,{type:"MultiPoint",coordinates:w})},toGeoJSON:function(p){var w=this.feature&&this.feature.geometry&&this.feature.geometry.type;if(w==="MultiPoint")return this.toMultiPoint(p);var A=w==="GeometryCollection",O=[];return this.eachLayer(function(R){if(R.toGeoJSON){var W=R.toGeoJSON(p);if(A)O.push(W.geometry);else{var X=B0(W);X.type==="FeatureCollection"?O.push.apply(O,X.features):O.push(X)}}}),A?Ff(this,{geometries:O,type:"GeometryCollection"}):{type:"FeatureCollection",features:O}}});function I5(p,w){return new Qo(p,w)}var Xae=I5,z0=wa.extend({options:{opacity:1,alt:"",interactive:!1,crossOrigin:!1,errorOverlayUrl:"",zIndex:1,className:""},initialize:function(p,w,A){this._url=p,this._bounds=ee(w),g(this,A)},onAdd:function(){this._image||(this._initImage(),this.options.opacity<1&&this._updateOpacity()),this.options.interactive&&(ut(this._image,"leaflet-interactive"),this.addInteractiveTarget(this._image)),this.getPane().appendChild(this._image),this._reset()},onRemove:function(){nr(this._image),this.options.interactive&&this.removeInteractiveTarget(this._image)},setOpacity:function(p){return this.options.opacity=p,this._image&&this._updateOpacity(),this},setStyle:function(p){return p.opacity&&this.setOpacity(p.opacity),this},bringToFront:function(){return this._map&&jf(this._image),this},bringToBack:function(){return this._map&&Rf(this._image),this},setUrl:function(p){return this._url=p,this._image&&(this._image.src=p),this},setBounds:function(p){return this._bounds=ee(p),this._map&&this._reset(),this},getEvents:function(){var p={zoom:this._reset,viewreset:this._reset};return this._zoomAnimated&&(p.zoomanim=this._animateZoom),p},setZIndex:function(p){return this.options.zIndex=p,this._updateZIndex(),this},getBounds:function(){return this._bounds},getElement:function(){return this._image},_initImage:function(){var p=this._url.tagName==="IMG",w=this._image=p?this._url:Ct("img");if(ut(w,"leaflet-image-layer"),this._zoomAnimated&&ut(w,"leaflet-zoom-animated"),this.options.className&&ut(w,this.options.className),w.onselectstart=f,w.onmousemove=f,w.onload=o(this.fire,this,"load"),w.onerror=o(this._overlayOnError,this,"error"),(this.options.crossOrigin||this.options.crossOrigin==="")&&(w.crossOrigin=this.options.crossOrigin===!0?"":this.options.crossOrigin),this.options.zIndex&&this._updateZIndex(),p){this._url=w.src;return}w.src=this._url,w.alt=this.options.alt},_animateZoom:function(p){var w=this._map.getZoomScale(p.zoom),A=this._map._latLngBoundsToNewLayerBounds(this._bounds,p.zoom,p.center).min;Au(this._image,A,w)},_reset:function(){var p=this._image,w=new H(this._map.latLngToLayerPoint(this._bounds.getNorthWest()),this._map.latLngToLayerPoint(this._bounds.getSouthEast())),A=w.getSize();Ar(p,w.min),p.style.width=A.x+"px",p.style.height=A.y+"px"},_updateOpacity:function(){Ri(this._image,this.options.opacity)},_updateZIndex:function(){this._image&&this.options.zIndex!==void 0&&this.options.zIndex!==null&&(this._image.style.zIndex=this.options.zIndex)},_overlayOnError:function(){this.fire("error");var p=this.options.errorOverlayUrl;p&&this._url!==p&&(this._url=p,this._image.src=p)},getCenter:function(){return this._bounds.getCenter()}}),qae=function(p,w,A){return new z0(p,w,A)},O5=z0.extend({options:{autoplay:!0,loop:!0,keepAspectRatio:!0,muted:!1,playsInline:!0},_initImage:function(){var p=this._url.tagName==="VIDEO",w=this._image=p?this._url:Ct("video");if(ut(w,"leaflet-image-layer"),this._zoomAnimated&&ut(w,"leaflet-zoom-animated"),this.options.className&&ut(w,this.options.className),w.onselectstart=f,w.onmousemove=f,w.onloadeddata=o(this.fire,this,"load"),p){for(var A=w.getElementsByTagName("source"),O=[],R=0;R0?O:[w.src];return}b(this._url)||(this._url=[this._url]),!this.options.keepAspectRatio&&Object.prototype.hasOwnProperty.call(w.style,"objectFit")&&(w.style.objectFit="fill"),w.autoplay=!!this.options.autoplay,w.loop=!!this.options.loop,w.muted=!!this.options.muted,w.playsInline=!!this.options.playsInline;for(var W=0;WR?(w.height=R+"px",ut(p,W)):xr(p,W),this._containerWidth=this._container.offsetWidth},_animateZoom:function(p){var w=this._map._latLngToNewLayerPoint(this._latlng,p.zoom,p.center),A=this._getAnchor();Ar(this._container,w.add(A))},_adjustPan:function(){if(this.options.autoPan){if(this._map._panAnim&&this._map._panAnim.stop(),this._autopanning){this._autopanning=!1;return}var p=this._map,w=parseInt(np(this._container,"marginBottom"),10)||0,A=this._container.offsetHeight+w,O=this._containerWidth,R=new F(this._containerLeft,-A-this._containerBottom);R._add(Mu(this._container));var W=p.layerPointToContainerPoint(R),X=V(this.options.autoPanPadding),re=V(this.options.autoPanPaddingTopLeft||X),oe=V(this.options.autoPanPaddingBottomRight||X),de=p.getSize(),Ee=0,Je=0;W.x+O+oe.x>de.x&&(Ee=W.x+O-de.x+oe.x),W.x-Ee-re.x<0&&(Ee=W.x-re.x),W.y+A+oe.y>de.y&&(Je=W.y+A-de.y+oe.y),W.y-Je-re.y<0&&(Je=W.y-re.y),(Ee||Je)&&(this.options.keepInView&&(this._autopanning=!0),p.fire("autopanstart").panBy([Ee,Je]))}},_getAnchor:function(){return V(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}}),Jae=function(p,w){return new $0(p,w)};wt.mergeOptions({closePopupOnClick:!0}),wt.include({openPopup:function(p,w,A){return this._initOverlay($0,p,w,A).openOn(this),this},closePopup:function(p){return p=arguments.length?p:this._popup,p&&p.close(),this}}),wa.include({bindPopup:function(p,w){return this._popup=this._initOverlay($0,this._popup,p,w),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(p){return this._popup&&(this instanceof qo||(this._popup._source=this),this._popup._prepareOpen(p||this._latlng)&&this._popup.openOn(this._map)),this},closePopup:function(){return this._popup&&this._popup.close(),this},togglePopup:function(){return this._popup&&this._popup.toggle(this),this},isPopupOpen:function(){return this._popup?this._popup.isOpen():!1},setPopupContent:function(p){return this._popup&&this._popup.setContent(p),this},getPopup:function(){return this._popup},_openPopup:function(p){if(!(!this._popup||!this._map)){Lu(p);var w=p.layer||p.target;if(this._popup._source===w&&!(w instanceof el)){this._map.hasLayer(this._popup)?this.closePopup():this.openPopup(p.latlng);return}this._popup._source=w,this.openPopup(p.latlng)}},_movePopup:function(p){this._popup.setLatLng(p.latlng)},_onKeyPress:function(p){p.originalEvent.keyCode===13&&this._openPopup(p)}});var F0=no.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,opacity:.9},onAdd:function(p){no.prototype.onAdd.call(this,p),this.setOpacity(this.options.opacity),p.fire("tooltipopen",{tooltip:this}),this._source&&(this.addEventParent(this._source),this._source.fire("tooltipopen",{tooltip:this},!0))},onRemove:function(p){no.prototype.onRemove.call(this,p),p.fire("tooltipclose",{tooltip:this}),this._source&&(this.removeEventParent(this._source),this._source.fire("tooltipclose",{tooltip:this},!0))},getEvents:function(){var p=no.prototype.getEvents.call(this);return this.options.permanent||(p.preclick=this.close),p},_initLayout:function(){var p="leaflet-tooltip",w=p+" "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=Ct("div",w),this._container.setAttribute("role","tooltip"),this._container.setAttribute("id","leaflet-tooltip-"+l(this))},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(p){var w,A,O=this._map,R=this._container,W=O.latLngToContainerPoint(O.getCenter()),X=O.layerPointToContainerPoint(p),re=this.options.direction,oe=R.offsetWidth,de=R.offsetHeight,Ee=V(this.options.offset),Je=this._getAnchor();re==="top"?(w=oe/2,A=de):re==="bottom"?(w=oe/2,A=0):re==="center"?(w=oe/2,A=de/2):re==="right"?(w=0,A=de/2):re==="left"?(w=oe,A=de/2):X.xthis.options.maxZoom||AO?this._retainParent(R,W,X,O):!1)},_retainChildren:function(p,w,A,O){for(var R=2*p;R<2*p+2;R++)for(var W=2*w;W<2*w+2;W++){var X=new F(R,W);X.z=A+1;var re=this._tileCoordsToKey(X),oe=this._tiles[re];if(oe&&oe.active){oe.retain=!0;continue}else oe&&oe.loaded&&(oe.retain=!0);A+1this.options.maxZoom||this.options.minZoom!==void 0&&R1){this._setView(p,A);return}for(var Je=R.min.y;Je<=R.max.y;Je++)for(var pt=R.min.x;pt<=R.max.x;pt++){var jn=new F(pt,Je);if(jn.z=this._tileZoom,!!this._isValidTile(jn)){var qr=this._tiles[this._tileCoordsToKey(jn)];qr?qr.current=!0:X.push(jn)}}if(X.sort(function(Kn,Gf){return Kn.distanceTo(W)-Gf.distanceTo(W)}),X.length!==0){this._loading||(this._loading=!0,this.fire("loading"));var zi=document.createDocumentFragment();for(pt=0;ptA.max.x)||!w.wrapLat&&(p.yA.max.y))return!1}if(!this.options.bounds)return!0;var O=this._tileCoordsToBounds(p);return ee(this.options.bounds).overlaps(O)},_keyToBounds:function(p){return this._tileCoordsToBounds(this._keyToTileCoords(p))},_tileCoordsToNwSe:function(p){var w=this._map,A=this.getTileSize(),O=p.scaleBy(A),R=O.add(A),W=w.unproject(O,p.z),X=w.unproject(R,p.z);return[W,X]},_tileCoordsToBounds:function(p){var w=this._tileCoordsToNwSe(p),A=new K(w[0],w[1]);return this.options.noWrap||(A=this._map.wrapLatLngBounds(A)),A},_tileCoordsToKey:function(p){return p.x+":"+p.y+":"+p.z},_keyToTileCoords:function(p){var w=p.split(":"),A=new F(+w[0],+w[1]);return A.z=+w[2],A},_removeTile:function(p){var w=this._tiles[p];w&&(nr(w.el),delete this._tiles[p],this.fire("tileunload",{tile:w.el,coords:this._keyToTileCoords(p)}))},_initTile:function(p){ut(p,"leaflet-tile");var w=this.getTileSize();p.style.width=w.x+"px",p.style.height=w.y+"px",p.onselectstart=f,p.onmousemove=f,Ue.ielt9&&this.options.opacity<1&&Ri(p,this.options.opacity)},_addTile:function(p,w){var A=this._getTilePos(p),O=this._tileCoordsToKey(p),R=this.createTile(this._wrapCoords(p),o(this._tileReady,this,p));this._initTile(R),this.createTile.length<2&&E(o(this._tileReady,this,p,null,R)),Ar(R,A),this._tiles[O]={el:R,coords:p,current:!0},w.appendChild(R),this.fire("tileloadstart",{tile:R,coords:p})},_tileReady:function(p,w,A){w&&this.fire("tileerror",{error:w,tile:A,coords:p});var O=this._tileCoordsToKey(p);A=this._tiles[O],A&&(A.loaded=+new Date,this._map._fadeAnimated?(Ri(A.el,0),D(this._fadeFrame),this._fadeFrame=E(this._updateOpacity,this)):(A.active=!0,this._pruneTiles()),w||(ut(A.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:A.el,coords:p})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),Ue.ielt9||!this._map._fadeAnimated?E(this._pruneTiles,this):setTimeout(o(this._pruneTiles,this),250)))},_getTilePos:function(p){return p.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(p){var w=new F(this._wrapX?c(p.x,this._wrapX):p.x,this._wrapY?c(p.y,this._wrapY):p.y);return w.z=p.z,w},_pxBoundsToTileRange:function(p){var w=this.getTileSize();return new H(p.min.unscaleBy(w).floor(),p.max.unscaleBy(w).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var p in this._tiles)if(!this._tiles[p].loaded)return!1;return!0}});function roe(p){return new fp(p)}var Vf=fp.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1,referrerPolicy:!1},initialize:function(p,w){this._url=p,w=g(this,w),w.detectRetina&&Ue.retina&&w.maxZoom>0?(w.tileSize=Math.floor(w.tileSize/2),w.zoomReverse?(w.zoomOffset--,w.minZoom=Math.min(w.maxZoom,w.minZoom+1)):(w.zoomOffset++,w.maxZoom=Math.max(w.minZoom,w.maxZoom-1)),w.minZoom=Math.max(0,w.minZoom)):w.zoomReverse?w.minZoom=Math.min(w.maxZoom,w.minZoom):w.maxZoom=Math.max(w.minZoom,w.maxZoom),typeof w.subdomains=="string"&&(w.subdomains=w.subdomains.split("")),this.on("tileunload",this._onTileRemove)},setUrl:function(p,w){return this._url===p&&w===void 0&&(w=!0),this._url=p,w||this.redraw(),this},createTile:function(p,w){var A=document.createElement("img");return st(A,"load",o(this._tileOnLoad,this,w,A)),st(A,"error",o(this._tileOnError,this,w,A)),(this.options.crossOrigin||this.options.crossOrigin==="")&&(A.crossOrigin=this.options.crossOrigin===!0?"":this.options.crossOrigin),typeof this.options.referrerPolicy=="string"&&(A.referrerPolicy=this.options.referrerPolicy),A.alt="",A.src=this.getTileUrl(p),A},getTileUrl:function(p){var w={r:Ue.retina?"@2x":"",s:this._getSubdomain(p),x:p.x,y:p.y,z:this._getZoomForUrl()};if(this._map&&!this._map.options.crs.infinite){var A=this._globalTileRange.max.y-p.y;this.options.tms&&(w.y=A),w["-y"]=A}return x(this._url,i(w,this.options))},_tileOnLoad:function(p,w){Ue.ielt9?setTimeout(o(p,this,null,w),0):p(null,w)},_tileOnError:function(p,w,A){var O=this.options.errorTileUrl;O&&w.getAttribute("src")!==O&&(w.src=O),p(A,w)},_onTileRemove:function(p){p.tile.onload=null},_getZoomForUrl:function(){var p=this._tileZoom,w=this.options.maxZoom,A=this.options.zoomReverse,O=this.options.zoomOffset;return A&&(p=w-p),p+O},_getSubdomain:function(p){var w=Math.abs(p.x+p.y)%this.options.subdomains.length;return this.options.subdomains[w]},_abortLoading:function(){var p,w;for(p in this._tiles)if(this._tiles[p].coords.z!==this._tileZoom&&(w=this._tiles[p].el,w.onload=f,w.onerror=f,!w.complete)){w.src=T;var A=this._tiles[p].coords;nr(w),delete this._tiles[p],this.fire("tileabort",{tile:w,coords:A})}},_removeTile:function(p){var w=this._tiles[p];if(w)return w.el.setAttribute("src",T),fp.prototype._removeTile.call(this,p)},_tileReady:function(p,w,A){if(!(!this._map||A&&A.getAttribute("src")===T))return fp.prototype._tileReady.call(this,p,w,A)}});function N5(p,w){return new Vf(p,w)}var j5=Vf.extend({defaultWmsParams:{service:"WMS",request:"GetMap",layers:"",styles:"",format:"image/jpeg",transparent:!1,version:"1.1.1"},options:{crs:null,uppercase:!1},initialize:function(p,w){this._url=p;var A=i({},this.defaultWmsParams);for(var O in w)O in this.options||(A[O]=w[O]);w=g(this,w);var R=w.detectRetina&&Ue.retina?2:1,W=this.getTileSize();A.width=W.x*R,A.height=W.y*R,this.wmsParams=A},onAdd:function(p){this._crs=this.options.crs||p.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var w=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[w]=this._crs.code,Vf.prototype.onAdd.call(this,p)},getTileUrl:function(p){var w=this._tileCoordsToNwSe(p),A=this._crs,O=Y(A.project(w[0]),A.project(w[1])),R=O.min,W=O.max,X=(this._wmsVersion>=1.3&&this._crs===P5?[R.y,R.x,W.y,W.x]:[R.x,R.y,W.x,W.y]).join(","),re=Vf.prototype.getTileUrl.call(this,p);return re+m(this.wmsParams,re,this.options.uppercase)+(this.options.uppercase?"&BBOX=":"&bbox=")+X},setParams:function(p,w){return i(this.wmsParams,p),w||this.redraw(),this}});function noe(p,w){return new j5(p,w)}Vf.WMS=j5,N5.wms=noe;var Jo=wa.extend({options:{padding:.1},initialize:function(p){g(this,p),l(this),this._layers=this._layers||{}},onAdd:function(){this._container||(this._initContainer(),ut(this._container,"leaflet-zoom-animated")),this.getPane().appendChild(this._container),this._update(),this.on("update",this._updatePaths,this)},onRemove:function(){this.off("update",this._updatePaths,this),this._destroyContainer()},getEvents:function(){var p={viewreset:this._reset,zoom:this._onZoom,moveend:this._update,zoomend:this._onZoomEnd};return this._zoomAnimated&&(p.zoomanim=this._onAnimZoom),p},_onAnimZoom:function(p){this._updateTransform(p.center,p.zoom)},_onZoom:function(){this._updateTransform(this._map.getCenter(),this._map.getZoom())},_updateTransform:function(p,w){var A=this._map.getZoomScale(w,this._zoom),O=this._map.getSize().multiplyBy(.5+this.options.padding),R=this._map.project(this._center,w),W=O.multiplyBy(-A).add(R).subtract(this._map._getNewPixelOrigin(p,w));Ue.any3d?Au(this._container,W,A):Ar(this._container,W)},_reset:function(){this._update(),this._updateTransform(this._center,this._zoom);for(var p in this._layers)this._layers[p]._reset()},_onZoomEnd:function(){for(var p in this._layers)this._layers[p]._project()},_updatePaths:function(){for(var p in this._layers)this._layers[p]._update()},_update:function(){var p=this.options.padding,w=this._map.getSize(),A=this._map.containerPointToLayerPoint(w.multiplyBy(-p)).round();this._bounds=new H(A,A.add(w.multiplyBy(1+p*2)).round()),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}}),R5=Jo.extend({options:{tolerance:0},getEvents:function(){var p=Jo.prototype.getEvents.call(this);return p.viewprereset=this._onViewPreReset,p},_onViewPreReset:function(){this._postponeUpdatePaths=!0},onAdd:function(){Jo.prototype.onAdd.call(this),this._draw()},_initContainer:function(){var p=this._container=document.createElement("canvas");st(p,"mousemove",this._onMouseMove,this),st(p,"click dblclick mousedown mouseup contextmenu",this._onClick,this),st(p,"mouseout",this._handleMouseOut,this),p._leaflet_disable_events=!0,this._ctx=p.getContext("2d")},_destroyContainer:function(){D(this._redrawRequest),delete this._ctx,nr(this._container),Wt(this._container),delete this._container},_updatePaths:function(){if(!this._postponeUpdatePaths){var p;this._redrawBounds=null;for(var w in this._layers)p=this._layers[w],p._update();this._redraw()}},_update:function(){if(!(this._map._animatingZoom&&this._bounds)){Jo.prototype._update.call(this);var p=this._bounds,w=this._container,A=p.getSize(),O=Ue.retina?2:1;Ar(w,p.min),w.width=O*A.x,w.height=O*A.y,w.style.width=A.x+"px",w.style.height=A.y+"px",Ue.retina&&this._ctx.scale(2,2),this._ctx.translate(-p.min.x,-p.min.y),this.fire("update")}},_reset:function(){Jo.prototype._reset.call(this),this._postponeUpdatePaths&&(this._postponeUpdatePaths=!1,this._updatePaths())},_initPath:function(p){this._updateDashArray(p),this._layers[l(p)]=p;var w=p._order={layer:p,prev:this._drawLast,next:null};this._drawLast&&(this._drawLast.next=w),this._drawLast=w,this._drawFirst=this._drawFirst||this._drawLast},_addPath:function(p){this._requestRedraw(p)},_removePath:function(p){var w=p._order,A=w.next,O=w.prev;A?A.prev=O:this._drawLast=O,O?O.next=A:this._drawFirst=A,delete p._order,delete this._layers[l(p)],this._requestRedraw(p)},_updatePath:function(p){this._extendRedrawBounds(p),p._project(),p._update(),this._requestRedraw(p)},_updateStyle:function(p){this._updateDashArray(p),this._requestRedraw(p)},_updateDashArray:function(p){if(typeof p.options.dashArray=="string"){var w=p.options.dashArray.split(/[, ]+/),A=[],O,R;for(R=0;R')}}catch{}return function(p){return document.createElement("<"+p+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}(),ioe={_initContainer:function(){this._container=Ct("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(Jo.prototype._update.call(this),this.fire("update"))},_initPath:function(p){var w=p._container=hp("shape");ut(w,"leaflet-vml-shape "+(this.options.className||"")),w.coordsize="1 1",p._path=hp("path"),w.appendChild(p._path),this._updateStyle(p),this._layers[l(p)]=p},_addPath:function(p){var w=p._container;this._container.appendChild(w),p.options.interactive&&p.addInteractiveTarget(w)},_removePath:function(p){var w=p._container;nr(w),p.removeInteractiveTarget(w),delete this._layers[l(p)]},_updateStyle:function(p){var w=p._stroke,A=p._fill,O=p.options,R=p._container;R.stroked=!!O.stroke,R.filled=!!O.fill,O.stroke?(w||(w=p._stroke=hp("stroke")),R.appendChild(w),w.weight=O.weight+"px",w.color=O.color,w.opacity=O.opacity,O.dashArray?w.dashStyle=b(O.dashArray)?O.dashArray.join(" "):O.dashArray.replace(/( *, *)/g," "):w.dashStyle="",w.endcap=O.lineCap.replace("butt","flat"),w.joinstyle=O.lineJoin):w&&(R.removeChild(w),p._stroke=null),O.fill?(A||(A=p._fill=hp("fill")),R.appendChild(A),A.color=O.fillColor||O.color,A.opacity=O.fillOpacity):A&&(R.removeChild(A),p._fill=null)},_updateCircle:function(p){var w=p._point.round(),A=Math.round(p._radius),O=Math.round(p._radiusY||A);this._setPath(p,p._empty()?"M0 0":"AL "+w.x+","+w.y+" "+A+","+O+" 0,"+65535*360)},_setPath:function(p,w){p._path.v=w},_bringToFront:function(p){jf(p._container)},_bringToBack:function(p){Rf(p._container)}},V0=Ue.vml?hp:Ge,dp=Jo.extend({_initContainer:function(){this._container=V0("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=V0("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){nr(this._container),Wt(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_update:function(){if(!(this._map._animatingZoom&&this._bounds)){Jo.prototype._update.call(this);var p=this._bounds,w=p.getSize(),A=this._container;(!this._svgSize||!this._svgSize.equals(w))&&(this._svgSize=w,A.setAttribute("width",w.x),A.setAttribute("height",w.y)),Ar(A,p.min),A.setAttribute("viewBox",[p.min.x,p.min.y,w.x,w.y].join(" ")),this.fire("update")}},_initPath:function(p){var w=p._path=V0("path");p.options.className&&ut(w,p.options.className),p.options.interactive&&ut(w,"leaflet-interactive"),this._updateStyle(p),this._layers[l(p)]=p},_addPath:function(p){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(p._path),p.addInteractiveTarget(p._path)},_removePath:function(p){nr(p._path),p.removeInteractiveTarget(p._path),delete this._layers[l(p)]},_updatePath:function(p){p._project(),p._update()},_updateStyle:function(p){var w=p._path,A=p.options;w&&(A.stroke?(w.setAttribute("stroke",A.color),w.setAttribute("stroke-opacity",A.opacity),w.setAttribute("stroke-width",A.weight),w.setAttribute("stroke-linecap",A.lineCap),w.setAttribute("stroke-linejoin",A.lineJoin),A.dashArray?w.setAttribute("stroke-dasharray",A.dashArray):w.removeAttribute("stroke-dasharray"),A.dashOffset?w.setAttribute("stroke-dashoffset",A.dashOffset):w.removeAttribute("stroke-dashoffset")):w.setAttribute("stroke","none"),A.fill?(w.setAttribute("fill",A.fillColor||A.color),w.setAttribute("fill-opacity",A.fillOpacity),w.setAttribute("fill-rule",A.fillRule||"evenodd")):w.setAttribute("fill","none"))},_updatePoly:function(p,w){this._setPath(p,Ye(p._parts,w))},_updateCircle:function(p){var w=p._point,A=Math.max(Math.round(p._radius),1),O=Math.max(Math.round(p._radiusY),1)||A,R="a"+A+","+O+" 0 1,0 ",W=p._empty()?"M0 0":"M"+(w.x-A)+","+w.y+R+A*2+",0 "+R+-A*2+",0 ";this._setPath(p,W)},_setPath:function(p,w){p._path.setAttribute("d",w)},_bringToFront:function(p){jf(p._path)},_bringToBack:function(p){Rf(p._path)}});Ue.vml&&dp.include(ioe);function z5(p){return Ue.svg||Ue.vml?new dp(p):null}wt.include({getRenderer:function(p){var w=p.options.renderer||this._getPaneRenderer(p.options.pane)||this.options.renderer||this._renderer;return w||(w=this._renderer=this._createRenderer()),this.hasLayer(w)||this.addLayer(w),w},_getPaneRenderer:function(p){if(p==="overlayPane"||p===void 0)return!1;var w=this._paneRenderers[p];return w===void 0&&(w=this._createRenderer({pane:p}),this._paneRenderers[p]=w),w},_createRenderer:function(p){return this.options.preferCanvas&&B5(p)||z5(p)}});var $5=$f.extend({initialize:function(p,w){$f.prototype.initialize.call(this,this._boundsToLatLngs(p),w)},setBounds:function(p){return this.setLatLngs(this._boundsToLatLngs(p))},_boundsToLatLngs:function(p){return p=ee(p),[p.getSouthWest(),p.getNorthWest(),p.getNorthEast(),p.getSouthEast()]}});function aoe(p,w){return new $5(p,w)}dp.create=V0,dp.pointsToPath=Ye,Qo.geometryToLayer=N0,Qo.coordsToLatLng=OC,Qo.coordsToLatLngs=j0,Qo.latLngToCoords=EC,Qo.latLngsToCoords=R0,Qo.getFeature=Ff,Qo.asFeature=B0,wt.mergeOptions({boxZoom:!0});var F5=ro.extend({initialize:function(p){this._map=p,this._container=p._container,this._pane=p._panes.overlayPane,this._resetStateTimeout=0,p.on("unload",this._destroy,this)},addHooks:function(){st(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){Wt(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){nr(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){this._resetStateTimeout!==0&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(p){if(!p.shiftKey||p.which!==1&&p.button!==1)return!1;this._clearDeferredResetState(),this._resetState(),ip(),pC(),this._startPoint=this._map.mouseEventToContainerPoint(p),st(document,{contextmenu:Lu,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(p){this._moved||(this._moved=!0,this._box=Ct("div","leaflet-zoom-box",this._container),ut(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(p);var w=new H(this._point,this._startPoint),A=w.getSize();Ar(this._box,w.min),this._box.style.width=A.x+"px",this._box.style.height=A.y+"px"},_finish:function(){this._moved&&(nr(this._box),xr(this._container,"leaflet-crosshair")),ap(),gC(),Wt(document,{contextmenu:Lu,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(p){if(!(p.which!==1&&p.button!==1)&&(this._finish(),!!this._moved)){this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(o(this._resetState,this),0);var w=new K(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(w).fire("boxzoomend",{boxZoomBounds:w})}},_onKeyDown:function(p){p.keyCode===27&&(this._finish(),this._clearDeferredResetState(),this._resetState())}});wt.addInitHook("addHandler","boxZoom",F5),wt.mergeOptions({doubleClickZoom:!0});var V5=ro.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(p){var w=this._map,A=w.getZoom(),O=w.options.zoomDelta,R=p.originalEvent.shiftKey?A-O:A+O;w.options.doubleClickZoom==="center"?w.setZoom(R):w.setZoomAround(p.containerPoint,R)}});wt.addInitHook("addHandler","doubleClickZoom",V5),wt.mergeOptions({dragging:!0,inertia:!0,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var G5=ro.extend({addHooks:function(){if(!this._draggable){var p=this._map;this._draggable=new Js(p._mapPane,p._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),p.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),p.on("zoomend",this._onZoomEnd,this),p.whenReady(this._onZoomEnd,this))}ut(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){xr(this._map._container,"leaflet-grab"),xr(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var p=this._map;if(p._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var w=ee(this._map.options.maxBounds);this._offsetLimit=Y(this._map.latLngToContainerPoint(w.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(w.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;p.fire("movestart").fire("dragstart"),p.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(p){if(this._map.options.inertia){var w=this._lastTime=+new Date,A=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(A),this._times.push(w),this._prunePositions(w)}this._map.fire("move",p).fire("drag",p)},_prunePositions:function(p){for(;this._positions.length>1&&p-this._times[0]>50;)this._positions.shift(),this._times.shift()},_onZoomEnd:function(){var p=this._map.getSize().divideBy(2),w=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=w.subtract(p).x,this._worldWidth=this._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(p,w){return p-(p-w)*this._viscosity},_onPreDragLimit:function(){if(!(!this._viscosity||!this._offsetLimit)){var p=this._draggable._newPos.subtract(this._draggable._startPos),w=this._offsetLimit;p.xw.max.x&&(p.x=this._viscousLimit(p.x,w.max.x)),p.y>w.max.y&&(p.y=this._viscousLimit(p.y,w.max.y)),this._draggable._newPos=this._draggable._startPos.add(p)}},_onPreDragWrap:function(){var p=this._worldWidth,w=Math.round(p/2),A=this._initialWorldOffset,O=this._draggable._newPos.x,R=(O-w+A)%p+w-A,W=(O+w+A)%p-w-A,X=Math.abs(R+A)0?W:-W))-w;this._delta=0,this._startTime=null,X&&(p.options.scrollWheelZoom==="center"?p.setZoom(w+X):p.setZoomAround(this._lastMousePos,w+X))}});wt.addInitHook("addHandler","scrollWheelZoom",H5);var ooe=600;wt.mergeOptions({tapHold:Ue.touchNative&&Ue.safari&&Ue.mobile,tapTolerance:15});var U5=ro.extend({addHooks:function(){st(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){Wt(this._map._container,"touchstart",this._onDown,this)},_onDown:function(p){if(clearTimeout(this._holdTimeout),p.touches.length===1){var w=p.touches[0];this._startPos=this._newPos=new F(w.clientX,w.clientY),this._holdTimeout=setTimeout(o(function(){this._cancel(),this._isTapValid()&&(st(document,"touchend",ln),st(document,"touchend touchcancel",this._cancelClickPrevent),this._simulateEvent("contextmenu",w))},this),ooe),st(document,"touchend touchcancel contextmenu",this._cancel,this),st(document,"touchmove",this._onMove,this)}},_cancelClickPrevent:function p(){Wt(document,"touchend",ln),Wt(document,"touchend touchcancel",p)},_cancel:function(){clearTimeout(this._holdTimeout),Wt(document,"touchend touchcancel contextmenu",this._cancel,this),Wt(document,"touchmove",this._onMove,this)},_onMove:function(p){var w=p.touches[0];this._newPos=new F(w.clientX,w.clientY)},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_simulateEvent:function(p,w){var A=new MouseEvent(p,{bubbles:!0,cancelable:!0,view:window,screenX:w.screenX,screenY:w.screenY,clientX:w.clientX,clientY:w.clientY});A._simulated=!0,w.target.dispatchEvent(A)}});wt.addInitHook("addHandler","tapHold",U5),wt.mergeOptions({touchZoom:Ue.touch,bounceAtZoomLimits:!0});var Z5=ro.extend({addHooks:function(){ut(this._map._container,"leaflet-touch-zoom"),st(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){xr(this._map._container,"leaflet-touch-zoom"),Wt(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(p){var w=this._map;if(!(!p.touches||p.touches.length!==2||w._animatingZoom||this._zooming)){var A=w.mouseEventToContainerPoint(p.touches[0]),O=w.mouseEventToContainerPoint(p.touches[1]);this._centerPoint=w.getSize()._divideBy(2),this._startLatLng=w.containerPointToLatLng(this._centerPoint),w.options.touchZoom!=="center"&&(this._pinchStartLatLng=w.containerPointToLatLng(A.add(O)._divideBy(2))),this._startDist=A.distanceTo(O),this._startZoom=w.getZoom(),this._moved=!1,this._zooming=!0,w._stop(),st(document,"touchmove",this._onTouchMove,this),st(document,"touchend touchcancel",this._onTouchEnd,this),ln(p)}},_onTouchMove:function(p){if(!(!p.touches||p.touches.length!==2||!this._zooming)){var w=this._map,A=w.mouseEventToContainerPoint(p.touches[0]),O=w.mouseEventToContainerPoint(p.touches[1]),R=A.distanceTo(O)/this._startDist;if(this._zoom=w.getScaleZoom(R,this._startZoom),!w.options.bounceAtZoomLimits&&(this._zoomw.getMaxZoom()&&R>1)&&(this._zoom=w._limitZoom(this._zoom)),w.options.touchZoom==="center"){if(this._center=this._startLatLng,R===1)return}else{var W=A._add(O)._divideBy(2)._subtract(this._centerPoint);if(R===1&&W.x===0&&W.y===0)return;this._center=w.unproject(w.project(this._pinchStartLatLng,this._zoom).subtract(W),this._zoom)}this._moved||(w._moveStart(!0,!1),this._moved=!0),D(this._animRequest);var X=o(w._move,w,this._center,this._zoom,{pinch:!0,round:!1},void 0);this._animRequest=E(X,this,!0),ln(p)}},_onTouchEnd:function(){if(!this._moved||!this._zooming){this._zooming=!1;return}this._zooming=!1,D(this._animRequest),Wt(document,"touchmove",this._onTouchMove,this),Wt(document,"touchend touchcancel",this._onTouchEnd,this),this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.zoomSnap):this._map._resetView(this._center,this._map._limitZoom(this._zoom))}});wt.addInitHook("addHandler","touchZoom",Z5),wt.BoxZoom=F5,wt.DoubleClickZoom=V5,wt.Drag=G5,wt.Keyboard=W5,wt.ScrollWheelZoom=H5,wt.TapHold=U5,wt.TouchZoom=Z5,r.Bounds=H,r.Browser=Ue,r.CRS=Be,r.Canvas=R5,r.Circle=IC,r.CircleMarker=D0,r.Class=N,r.Control=ba,r.DivIcon=D5,r.DivOverlay=no,r.DomEvent=Tae,r.DomUtil=wae,r.Draggable=Js,r.Evented=Z,r.FeatureGroup=qo,r.GeoJSON=Qo,r.GridLayer=fp,r.Handler=ro,r.Icon=zf,r.ImageOverlay=z0,r.LatLng=le,r.LatLngBounds=K,r.Layer=wa,r.LayerGroup=Bf,r.LineUtil=Rae,r.Map=wt,r.Marker=E0,r.Mixin=Iae,r.Path=el,r.Point=F,r.PolyUtil=Oae,r.Polygon=$f,r.Polyline=Ko,r.Popup=$0,r.PosAnimation=g5,r.Projection=Bae,r.Rectangle=$5,r.Renderer=Jo,r.SVG=dp,r.SVGOverlay=E5,r.TileLayer=Vf,r.Tooltip=F0,r.Transformation=ue,r.Util=j,r.VideoOverlay=O5,r.bind=o,r.bounds=Y,r.canvas=B5,r.circle=Uae,r.circleMarker=Hae,r.control=lp,r.divIcon=toe,r.extend=i,r.featureGroup=Vae,r.geoJSON=I5,r.geoJson=Xae,r.gridLayer=roe,r.icon=Gae,r.imageOverlay=qae,r.latLng=fe,r.latLngBounds=ee,r.layerGroup=Fae,r.map=Cae,r.marker=Wae,r.point=V,r.polygon=Yae,r.polyline=Zae,r.popup=Jae,r.rectangle=aoe,r.setOptions=g,r.stamp=l,r.svg=z5,r.svgOverlay=Qae,r.tileLayer=N5,r.tooltip=eoe,r.transformation=te,r.version=n,r.videoOverlay=Kae;var soe=window.L;r.noConflict=function(){return window.L=soe,this},window.L=r})})(XO,XO.exports);var Ef=XO.exports;const Gie=$t(Ef);function S0(e,t,r){return Object.freeze({instance:e,context:t,container:r})}function WR(e,t){return t==null?function(n,i){const a=U.useRef();return a.current||(a.current=e(n,i)),a}:function(n,i){const a=U.useRef();a.current||(a.current=e(n,i));const o=U.useRef(n),{instance:s}=a.current;return U.useEffect(function(){o.current!==n&&(t(s,n,o.current),o.current=n)},[s,n,i]),a}}function Wie(e,t){U.useEffect(function(){return(t.layerContainer??t.map).addLayer(e.instance),function(){var a;(a=t.layerContainer)==null||a.removeLayer(e.instance),t.map.removeLayer(e.instance)}},[t,e])}function VQe(e){return function(r){const n=aC(),i=e(oC(r,n),n);return zie(n.map,r.attribution),GR(i.current,r.eventHandlers),Wie(i.current,n),i}}function GQe(e,t){const r=U.useRef();U.useEffect(function(){if(t.pathOptions!==r.current){const i=t.pathOptions??{};e.instance.setStyle(i),r.current=i}},[e,t])}function WQe(e){return function(r){const n=aC(),i=e(oC(r,n),n);return GR(i.current,r.eventHandlers),Wie(i.current,n),GQe(i.current,r),i}}function Hie(e,t){const r=WR(e),n=FQe(r,t);return zQe(n)}function Uie(e,t){const r=WR(e,t),n=WQe(r);return BQe(n)}function HQe(e,t){const r=WR(e,t),n=VQe(r);return $Qe(n)}function UQe(e,t,r){const{opacity:n,zIndex:i}=t;n!=null&&n!==r.opacity&&e.setOpacity(n),i!=null&&i!==r.zIndex&&e.setZIndex(i)}function ZQe(){return aC().map}const YQe=Uie(function({center:t,children:r,...n},i){const a=new Ef.CircleMarker(t,n);return S0(a,$ie(i,{overlayContainer:a}))},NQe);function qO(){return qO=Object.assign||function(e){for(var t=1;t(d==null?void 0:d.map)??null,[d]);const g=U.useCallback(y=>{if(y!==null&&d===null){const x=new Ef.Map(y,c);r!=null&&u!=null?x.setView(r,u):e!=null&&x.fitBounds(e,t),l!=null&&x.whenReady(l),v(RQe(x))}},[]);U.useEffect(()=>()=>{d==null||d.map.remove()},[d]);const m=d?J.createElement(Vie,{value:d},n):o??null;return J.createElement("div",qO({},h,{ref:g}),m)}const qQe=U.forwardRef(XQe),KQe=Uie(function({positions:t,...r},n){const i=new Ef.Polyline(t,r);return S0(i,$ie(n,{overlayContainer:i}))},function(t,r,n){r.positions!==n.positions&&t.setLatLngs(r.positions)}),QQe=Hie(function(t,r){const n=new Ef.Popup(t,r.overlayContainer);return S0(n,r)},function(t,r,{position:n},i){U.useEffect(function(){const{instance:o}=t;function s(u){u.popup===o&&(o.update(),i(!0))}function l(u){u.popup===o&&i(!1)}return r.map.on({popupopen:s,popupclose:l}),r.overlayContainer==null?(n!=null&&o.setLatLng(n),o.openOn(r.map)):r.overlayContainer.bindPopup(o),function(){var c;r.map.off({popupopen:s,popupclose:l}),(c=r.overlayContainer)==null||c.unbindPopup(),r.map.removeLayer(o)}},[t,r,i,n])}),JQe=HQe(function({url:t,...r},n){const i=new Ef.TileLayer(t,oC(r,n));return S0(i,n)},function(t,r,n){UQe(t,r,n);const{url:i}=r;i!=null&&i!==n.url&&t.setUrl(i)}),eJe=Hie(function(t,r){const n=new Ef.Tooltip(t,r.overlayContainer);return S0(n,r)},function(t,r,{position:n},i){U.useEffect(function(){const o=r.overlayContainer;if(o==null)return;const{instance:s}=t,l=c=>{c.tooltip===s&&(n!=null&&s.setLatLng(n),s.update(),i(!0))},u=c=>{c.tooltip===s&&i(!1)};return o.on({tooltipopen:l,tooltipclose:u}),o.bindTooltip(s),function(){o.off({tooltipopen:l,tooltipclose:u}),o._map!=null&&o.unbindTooltip()}},[t,r,i,n])}),tJe="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAFgUlEQVR4Aa1XA5BjWRTN2oW17d3YaZtr2962HUzbDNpjszW24mRt28p47v7zq/bXZtrp/lWnXr337j3nPCe85NcypgSFdugCpW5YoDAMRaIMqRi6aKq5E3YqDQO3qAwjVWrD8Ncq/RBpykd8oZUb/kaJutow8r1aP9II0WmLKLIsJyv1w/kqw9Ch2MYdB++12Onxee/QMwvf4/Dk/Lfp/i4nxTXtOoQ4pW5Aj7wpici1A9erdAN2OH64x8OSP9j3Ft3b7aWkTg/Fm91siTra0f9on5sQr9INejH6CUUUpavjFNq1B+Oadhxmnfa8RfEmN8VNAsQhPqF55xHkMzz3jSmChWU6f7/XZKNH+9+hBLOHYozuKQPxyMPUKkrX/K0uWnfFaJGS1QPRtZsOPtr3NsW0uyh6NNCOkU3Yz+bXbT3I8G3xE5EXLXtCXbbqwCO9zPQYPRTZ5vIDXD7U+w7rFDEoUUf7ibHIR4y6bLVPXrz8JVZEql13trxwue/uDivd3fkWRbS6/IA2bID4uk0UpF1N8qLlbBlXs4Ee7HLTfV1j54APvODnSfOWBqtKVvjgLKzF5YdEk5ewRkGlK0i33Eofffc7HT56jD7/6U+qH3Cx7SBLNntH5YIPvODnyfIXZYRVDPqgHtLs5ABHD3YzLuespb7t79FY34DjMwrVrcTuwlT55YMPvOBnRrJ4VXTdNnYug5ucHLBjEpt30701A3Ts+HEa73u6dT3FNWwflY86eMHPk+Yu+i6pzUpRrW7SNDg5JHR4KapmM5Wv2E8Tfcb1HoqqHMHU+uWDD7zg54mz5/2BSnizi9T1Dg4QQXLToGNCkb6tb1NU+QAlGr1++eADrzhn/u8Q2YZhQVlZ5+CAOtqfbhmaUCS1ezNFVm2imDbPmPng5wmz+gwh+oHDce0eUtQ6OGDIyR0uUhUsoO3vfDmmgOezH0mZN59x7MBi++WDL1g/eEiU3avlidO671bkLfwbw5XV2P8Pzo0ydy4t2/0eu33xYSOMOD8hTf4CrBtGMSoXfPLchX+J0ruSePw3LZeK0juPJbYzrhkH0io7B3k164hiGvawhOKMLkrQLyVpZg8rHFW7E2uHOL888IBPlNZ1FPzstSJM694fWr6RwpvcJK60+0HCILTBzZLFNdtAzJaohze60T8qBzyh5ZuOg5e7uwQppofEmf2++DYvmySqGBuKaicF1blQjhuHdvCIMvp8whTTfZzI7RldpwtSzL+F1+wkdZ2TBOW2gIF88PBTzD/gpeREAMEbxnJcaJHNHrpzji0gQCS6hdkEeYt9DF/2qPcEC8RM28Hwmr3sdNyht00byAut2k3gufWNtgtOEOFGUwcXWNDbdNbpgBGxEvKkOQsxivJx33iow0Vw5S6SVTrpVq11ysA2Rp7gTfPfktc6zhtXBBC+adRLshf6sG2RfHPZ5EAc4sVZ83yCN00Fk/4kggu40ZTvIEm5g24qtU4KjBrx/BTTH8ifVASAG7gKrnWxJDcU7x8X6Ecczhm3o6YicvsLXWfh3Ch1W0k8x0nXF+0fFxgt4phz8QvypiwCCFKMqXCnqXExjq10beH+UUA7+nG6mdG/Pu0f3LgFcGrl2s0kNNjpmoJ9o4B29CMO8dMT4Q5ox8uitF6fqsrJOr8qnwNbRzv6hSnG5wP+64C7h9lp30hKNtKdWjtdkbuPA19nJ7Tz3zR/ibgARbhb4AlhavcBebmTHcFl2fvYEnW0ox9xMxKBS8btJ+KiEbq9zA4RthQXDhPa0T9TEe69gWupwc6uBUphquXgf+/FrIjweHQS4/pduMe5ERUMHUd9xv8ZR98CxkS4F2n3EUrUZ10EYNw7BWm9x1GiPssi3GgiGRDKWRYZfXlON+dfNbM+GgIwYdwAAAAASUVORK5CYII=",rJe="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAABSCAMAAAAhFXfZAAAC91BMVEVMaXEzeak2f7I4g7g3g7cua5gzeKg8hJo3grY4g7c3grU0gLI2frE0daAubJc2gbQwd6QzeKk2gLMtd5sxdKIua5g1frA2f7IydaM0e6w2fq41fK01eqo3grgubJgta5cxdKI1f7AydaQydaMxc6EubJgvbJkwcZ4ubZkwcJwubZgubJcydqUydKIxapgubJctbJcubZcubJcvbJYubJcvbZkubJctbJctbZcubJg2f7AubJcrbZcubJcubJcua5g3grY0fq8ubJcubJdEkdEwhsw6i88vhswuhcsuhMtBjMgthMsrg8srgss6is8qgcs8i9A9iMYtg8spgcoogMo7hcMngMonf8olfso4gr8kfck5iM8jfMk4iM8he8k1fro7itAgesk2hs8eecgzfLcofssdeMg0hc4cd8g2hcsxeLQbdsgZdcgxeLImfcszhM0vda4xgckzhM4xg84wf8Yxgs4udKsvfcQucqhUndROmdM1fK0wcZ8vb5w0eqpQm9MzeKhXoNVcpdYydKNWn9VZotVKltJFjsIwcJ1Rms9OlslLmtH///8+kc9epdYzd6dbo9VHkMM2f7FHmNBClM8ydqVcpNY9hro3gLM9hLczealQmcw3fa46f7A8gLMxc6I3eagyc6FIldJMl9JSnNRSntNNl9JPnNJFi75UnM9ZodVKksg8kM45jc09e6ZHltFBk883gbRBh7pDk9EwcaBzn784g7dKkcY2i81Om9M7j85Llc81is09g7Q4grY/j9A0eqxKmdFFltBEjcXf6fFImdBCiLxJl9FGlNFBi78yiMxVndEvbpo6js74+vx+psPP3+o/ks5HkcpGmNCjwdZCkNDM3ehYoNJEls+lxNkxh8xHks0+jdC1zd5Lg6r+/v/H2ufz9/o3jM3t8/edvdM/k89Th61OiLBSjbZklbaTt9BfptdjmL1AicBHj8hGk9FAgK1dkLNTjLRekrdClc/k7fM0icy0y9tgp9c4jc2NtM9Dlc8zicxeXZn3AAAAQ3RSTlMAHDdTb4yPA+LtnEQmC4L2EmHqB7XA0d0sr478x4/Yd5i1zOfyPkf1sLVq4Nh3FvjxopQ2/STNuFzUwFIwxKaejILpIBEV9wAABhVJREFUeF6s1NdyFEcYBeBeoQIhRAkLlRDGrhIgY3BJL8CVeKzuyXFzzjkn5ZxzzuScg3PO8cKzu70JkO0LfxdTU//pM9vTu7Xgf6KqOVTb9X7toRrVEfBf1HTVjZccrT/2by1VV928Yty9ZbVuucdz90frG8DBjl9pVApbOstvmMuvVgaNXSfAAd6pGxpy6yxf5ph43pS/4f3uoaGm2rdu72S9xzOvMymkZFq/ptDrk90mhW7e4zl7HLzhxGWPR20xmSxJ/VqldG5m9XhaVOA1DadsNh3Pu5L2N6QtPO/32JpqQBVVk20oy/Pi2s23WEvyfHbe1thadVQttvm7Llf65gGmXK67XtupyoM7HQhmXdLS8oGWJNeOJ3C5fG5XCEJnkez3/oFdsvgJ4l2ANZwhrJKk/7OSXa+3Vw2WJMlKnGkobouYk6T0TyX30klOUnTD9HJ5qpckL3EW/w4XF3Xd0FGywXUrstrclVsqz5Pd/sXFYyDnPdrLcQODmGOK47IZb4CmibmMn+MYRzFZ5jg33ZL/EJrWcszHmANy3ARBK/IXtciJy8VsitPSdE3uuHxzougojcUdr8/32atnz/ev3f/K5wtpxUTpcaI45zusVDpYtZi+jg0oU9b3x74h7+n9ABvYEZeKaVq0sh0AtLKsFtqNBdeT0MrSzwwlq9+x6xAO4tgOtSzbCjrNQQiNvQUbUEubvzBUeGw26yDCsRHCoLkTHDa7IdOLIThs/gHvChszh2CimE8peRs47cxANI0lYNB5y1DljpOF0IhzBDPOZnDOqYYbeGKECbPzWnXludPphw5c2YBq5zlwXphIbO4VDCZ0gnPfUO1TwZoYwAs2ExPCedAu9DAjfQUjzITQb3jNj0KG2Sgt6BHaQUdYzWz+XmBktOHwanXjaSTcwwziBcuMOtwBmqPrTOxFQR/DRKKPqyur0aiW6cULYsx6tBm0jXpR/AUWR6HRq9WVW6MRhIq5jLyjbaCTDCijyYJNpCajdyobP/eTw0iexBAKkJ3gA5KcQb2zBXsIBckn+xVv8jkZSaEFHE+jFEleAEfayRU0MouNoBmB/L50Ai/HSLIHxcrpCvnhSQAuakKp2C/YbCylJjXRVy/z3+Kv/RrNcCo+WUzlVEhzKffnTQnxeN9fWF88fiNCUdSTsaufaChKWInHeysygfpIqagoakW+vV20J8uyl6TyNKEZWV4oRSPyCkWpgOLSbkCObT8o2r6tlG58HQquf6O0v50tB7JM7F4EORd2dx/K0w/KHsVkLPaoYrwgP/y7krr3SSMA4zj+OBgmjYkxcdIJQyQRKgg2viX9Hddi9UBb29LrKR7CVVEEEXWojUkXNyfTNDE14W9gbHJNuhjDettN3ZvbOvdOqCD3Jp/9l+/wJE+9PkYGjx/fqkys3S2rMozM/o2106rfMUINo6hVqz+eu/hd1c4xTg0TAfy5kV+4UG6+IthHTU9woWmxuKNbTfuCSfovBCxq7EtHqvYL4Sm6F8GVxsSXHMQ07TOi1DKtZxjWaaIyi4CXWjxPccUw8WVbMYY5wxC1mzEyXMJWkllpRloi+Kkoq69sxBTlElF6aAxYUbjXNlhlDZilDnM4U5SlN5biRsRHnbx3mbeWjEh4mEyiuJDl5XcWVmX5GvNkFgLWZM5qwsop4/AWfLhU1cR7k1VVvcYCWRkOI6Xy5gmnphCYIkvzuNYzHzosq2oNk2RtSs8khfUOfHIDgR6ysYBaMpl4uEgk2U/oJTs9AaTSwma7dT69geAE2ZpEjUsn2ieJNHeKfrI3EcAGJ2ZaNgVuC8EBctCLc57P5u5led6IOBkIYkuQMrmmjChs4VkfOerHqSBkPzZlhe06RslZ3zMjk2sscqKwY0RcjKK+LWbzd7KiHhkncs/siFJ+V5eXxD34B8nVuJEpGJNmxN2gH3vSvp7J70tF+D1Ej8qUJD1TkErAND2GZwTFg/LubvmgiBG3SOvdlsqFQrkEzJCL1rstlnVFROixZoDDSuXQFHESwVGlcuQcMb/b42NgjLowh5MTDFE3vNB5qStRIErdCQEh6pLPR92anSUb/wAIhldAaDMpGgAAAABJRU5ErkJggg==",nJe="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAApCAQAAAACach9AAACMUlEQVR4Ae3ShY7jQBAE0Aoz/f9/HTMzhg1zrdKUrJbdx+Kd2nD8VNudfsL/Th///dyQN2TH6f3y/BGpC379rV+S+qqetBOxImNQXL8JCAr2V4iMQXHGNJxeCfZXhSRBcQMfvkOWUdtfzlLgAENmZDcmo2TVmt8OSM2eXxBp3DjHSMFutqS7SbmemzBiR+xpKCNUIRkdkkYxhAkyGoBvyQFEJEefwSmmvBfJuJ6aKqKWnAkvGZOaZXTUgFqYULWNSHUckZuR1HIIimUExutRxwzOLROIG4vKmCKQt364mIlhSyzAf1m9lHZHJZrlAOMMztRRiKimp/rpdJDc9Awry5xTZCte7FHtuS8wJgeYGrex28xNTd086Dik7vUMscQOa8y4DoGtCCSkAKlNwpgNtphjrC6MIHUkR6YWxxs6Sc5xqn222mmCRFzIt8lEdKx+ikCtg91qS2WpwVfBelJCiQJwvzixfI9cxZQWgiSJelKnwBElKYtDOb2MFbhmUigbReQBV0Cg4+qMXSxXSyGUn4UbF8l+7qdSGnTC0XLCmahIgUHLhLOhpVCtw4CzYXvLQWQbJNmxoCsOKAxSgBJno75avolkRw8iIAFcsdc02e9iyCd8tHwmeSSoKTowIgvscSGZUOA7PuCN5b2BX9mQM7S0wYhMNU74zgsPBj3HU7wguAfnxxjFQGBE6pwN+GjME9zHY7zGp8wVxMShYX9NXvEWD3HbwJf4giO4CFIQxXScH1/TM+04kkBiAAAAAElFTkSuQmCC";delete Gie.Icon.Default.prototype._getIconUrl;Gie.Icon.Default.mergeOptions({iconUrl:tJe,iconRetinaUrl:rJe,shadowUrl:nJe});const FU=["#3b82f6","#a78bfa","#06b6d4","#f59e0b","#22c55e","#ec4899","#8b5cf6","#14b8a6"],iJe=["ROUTER","ROUTER_LATE","REPEATER","TRACKER"];function aJe(e){return e>12?"#22c55e":e>8?"#4ade80":e>5?"#f59e0b":e>3?"#f97316":"#ef4444"}function oJe(e){return e===null||e>46?0:e>44.5?1:e>43?2:3}function sJe(e){if(!e)return"Unknown";const t=new Date(e),n=new Date().getTime()-t.getTime(),i=Math.floor(n/6e4),a=Math.floor(n/36e5),o=Math.floor(n/864e5);return i<1?"Just now":i<60?`${i}m ago`:a<24?`${a}h ago`:`${o}d ago`}function lJe({bounds:e}){const t=ZQe();return U.useEffect(()=>{e&&t.fitBounds(e,{padding:[50,50]})},[t,e]),null}function uJe({node:e}){const t=e.latitude!==null&&e.longitude!==null,r=e.battery_level!==null?e.battery_level>100||e.voltage&&e.voltage>4.1?"USB ⚡":`${e.battery_level.toFixed(0)}%`:"Unknown";return _.jsxs("div",{className:"min-w-[200px]",children:[_.jsx("div",{className:"font-semibold text-slate-800",children:e.short_name}),_.jsx("div",{className:"text-xs text-slate-600 mb-2",children:e.long_name}),_.jsxs("div",{className:"grid grid-cols-2 gap-x-4 gap-y-1 text-xs",children:[_.jsx("div",{className:"text-slate-500",children:"Role"}),_.jsx("div",{className:"text-slate-700 font-medium",children:e.role}),_.jsx("div",{className:"text-slate-500",children:"Hardware"}),_.jsx("div",{className:"text-slate-700",children:e.hardware||"Unknown"}),_.jsx("div",{className:"text-slate-500",children:"Battery"}),_.jsx("div",{className:"text-slate-700",children:r}),_.jsx("div",{className:"text-slate-500",children:"Last Heard"}),_.jsx("div",{className:"text-slate-700",children:sJe(e.last_heard)})]}),t&&_.jsxs("div",{className:"mt-3 pt-2 border-t border-slate-200 flex gap-2",children:[_.jsxs("a",{href:`https://www.google.com/maps?q=${e.latitude},${e.longitude}`,target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-1 text-xs text-blue-600 hover:text-blue-800",children:[_.jsx(wd,{size:10}),"Google Maps"]}),_.jsxs("a",{href:`https://www.openstreetmap.org/?mlat=${e.latitude}&mlon=${e.longitude}&zoom=14`,target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-1 text-xs text-blue-600 hover:text-blue-800",children:[_.jsx(wd,{size:10}),"OSM"]})]})]})}function cJe({nodes:e,edges:t,selectedNodeId:r,onSelectNode:n}){const i=U.useMemo(()=>e.filter(f=>f.latitude!==null&&f.longitude!==null),[e]),a=e.length-i.length,o=U.useMemo(()=>new Map(i.map(f=>[f.node_num,f])),[i]),s=U.useMemo(()=>t.filter(f=>o.has(f.from_node)&&o.has(f.to_node)),[t,o]),l=U.useMemo(()=>{if(i.length===0)return null;const f=i.map(d=>d.latitude),h=i.map(d=>d.longitude);return[[Math.min(...f),Math.min(...h)],[Math.max(...f),Math.max(...h)]]},[i]),u=[43.6,-114.4],c=U.useMemo(()=>{const f=new Set;return r!==null&&t.forEach(h=>{h.from_node===r&&f.add(h.to_node),h.to_node===r&&f.add(h.from_node)}),f},[r,t]);return _.jsxs("div",{className:"relative bg-bg-card rounded-lg border border-border overflow-hidden",children:[_.jsxs(qQe,{center:u,zoom:7,style:{width:"100%",height:"540px"},className:"z-0",children:[_.jsx(JQe,{url:"https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",attribution:'© OpenStreetMap, © CARTO'}),_.jsx(lJe,{bounds:l}),s.map((f,h)=>{const d=o.get(f.from_node),v=o.get(f.to_node),g=r===null||f.from_node===r||f.to_node===r;return _.jsx(KQe,{positions:[[d.latitude,d.longitude],[v.latitude,v.longitude]],color:aJe(f.snr),weight:g&&r!==null?2.5:1.5,opacity:r===null?.3:g?.6:.08},h)}),i.map(f=>{const h=f.node_num===r,d=c.has(f.node_num),v=r===null||h||d,g=iJe.includes(f.role),m=oJe(f.latitude),y=FU[m%FU.length];return _.jsxs(YQe,{center:[f.latitude,f.longitude],radius:g?8:5,fillColor:g?y:"#111827",fillOpacity:v?.9:.2,stroke:!0,color:h?"#ffffff":y,weight:h?3:g?0:2,opacity:v?1:.3,eventHandlers:{click:()=>n(h?null:f.node_num)},children:[_.jsx(eJe,{direction:"top",offset:[0,-8],children:_.jsx("span",{className:"font-mono text-xs",children:f.short_name})}),_.jsx(QQe,{children:_.jsx(uJe,{node:f})})]},f.node_num)})]}),_.jsxs("div",{className:"absolute bottom-4 left-4 bg-bg-card/90 backdrop-blur-sm border border-border rounded px-3 py-2 text-xs text-slate-400 flex items-center gap-2",children:[_.jsx(KE,{size:12}),_.jsxs("span",{children:["Showing ",i.length," of ",e.length," nodes",a>0&&_.jsxs("span",{className:"text-slate-500",children:[" (",a," without coordinates)"]})]})]})]})}const VU=["#3b82f6","#a78bfa","#06b6d4","#f59e0b","#22c55e","#ec4899","#8b5cf6","#14b8a6"],fJe=["ROUTER","ROUTER_LATE","REPEATER","TRACKER"];function GU(e){return e>12?"#22c55e":e>8?"#4ade80":e>5?"#f59e0b":e>3?"#f97316":"#ef4444"}function hJe(e){return e>12?"excellent":e>8?"good":e>5?"fair":e>3?"marginal":"poor"}function dJe(e){return e===null||e>46?0:e>44.5?1:e>43?2:3}function vJe(e){return["Northern ID","Central ID","SW Idaho","SC Idaho"][e]||"Unknown"}function pJe(e){if(!e)return"Unknown";const t=new Date(e),n=new Date().getTime()-t.getTime(),i=Math.floor(n/6e4),a=Math.floor(n/36e5),o=Math.floor(n/864e5);return i<1?"Just now":i<60?`${i}m ago`:a<24?`${a}h ago`:`${o}d ago`}function gJe(e){if(!e)return"bg-slate-500";const t=new Date(e),n=(new Date().getTime()-t.getTime())/36e5;return n<1?"bg-green-500":n<24?"bg-amber-500":"bg-slate-500"}function mJe({node:e,edges:t,nodes:r,onSelectNode:n}){const i=U.useMemo(()=>{if(!e)return[];const f=new Map(r.map(d=>[d.node_num,d])),h=[];return t.forEach(d=>{if(d.from_node===e.node_num){const v=f.get(d.to_node);v&&h.push({node:v,snr:d.snr,quality:d.quality})}else if(d.to_node===e.node_num){const v=f.get(d.from_node);v&&h.push({node:v,snr:d.snr,quality:d.quality})}}),h.sort((d,v)=>v.snr-d.snr)},[e,t,r]);if(!e)return _.jsxs("div",{className:"w-[250px] flex-shrink-0 bg-bg-card border-l border-border p-4 flex flex-col items-center justify-center h-[540px]",children:[_.jsx("div",{className:"w-12 h-12 rounded-full bg-bg-hover border border-border flex items-center justify-center mb-3",children:_.jsx(Za,{size:24,className:"text-slate-500"})}),_.jsx("p",{className:"text-sm text-slate-500 text-center",children:"Click a node to inspect"})]});const a=fJe.includes(e.role),o=dJe(e.latitude),s=VU[o%VU.length],l=e.latitude!==null&&e.longitude!==null,u=e.battery_level!==null?e.battery_level>100||e.voltage&&e.voltage>4.1?"USB":`${e.battery_level.toFixed(0)}%`:"—",c=e.battery_level!==null&&(e.battery_level>100||e.voltage&&e.voltage>4.1);return _.jsxs("div",{className:"w-[250px] flex-shrink-0 bg-bg-card border-l border-border flex flex-col h-[540px] overflow-hidden",children:[_.jsxs("div",{className:"p-4 border-b border-border",children:[_.jsx("div",{className:"inline-flex items-center px-2 py-0.5 rounded text-xs font-mono mb-2",style:{backgroundColor:`${s}20`,color:s},children:e.node_id_hex}),_.jsx("div",{className:"font-mono text-lg text-slate-100",children:e.short_name}),_.jsx("div",{className:"text-xs text-slate-500 truncate",children:e.long_name})]}),_.jsxs("div",{className:"p-4 border-b border-border grid grid-cols-2 gap-3",children:[_.jsxs("div",{children:[_.jsx("div",{className:"text-xs text-slate-500 mb-0.5",children:"Role"}),_.jsx("div",{className:`text-sm font-medium ${a?"text-cyan-400":"text-slate-300"}`,children:e.role})]}),_.jsxs("div",{children:[_.jsx("div",{className:"text-xs text-slate-500 mb-0.5",children:"Region"}),_.jsx("div",{className:"text-sm text-slate-300",children:vJe(o)})]}),_.jsxs("div",{children:[_.jsx("div",{className:"text-xs text-slate-500 mb-0.5",children:"Battery"}),_.jsxs("div",{className:"text-sm text-slate-300 flex items-center gap-1",children:[c&&_.jsx(Mm,{size:12,className:"text-amber-400"}),u]})]}),_.jsxs("div",{children:[_.jsx("div",{className:"text-xs text-slate-500 mb-0.5",children:"Status"}),_.jsxs("div",{className:"flex items-center gap-1.5",children:[_.jsx("div",{className:`w-2 h-2 rounded-full ${gJe(e.last_heard)}`}),_.jsx("span",{className:"text-sm text-slate-300",children:pJe(e.last_heard)})]})]}),_.jsxs("div",{className:"col-span-2",children:[_.jsx("div",{className:"text-xs text-slate-500 mb-0.5",children:"Hardware"}),_.jsx("div",{className:"text-sm text-slate-300 font-mono truncate",children:e.hardware||"Unknown"})]})]}),l&&_.jsxs("div",{className:"px-4 py-3 border-b border-border flex gap-3",children:[_.jsxs("a",{href:`https://www.google.com/maps?q=${e.latitude},${e.longitude}`,target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-1 text-xs text-blue-400 hover:text-blue-300",children:[_.jsx(wd,{size:10}),"Google Maps"]}),_.jsxs("a",{href:`https://www.openstreetmap.org/?mlat=${e.latitude}&mlon=${e.longitude}&zoom=14`,target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-1 text-xs text-blue-400 hover:text-blue-300",children:[_.jsx(wd,{size:10}),"OSM"]})]}),_.jsxs("div",{className:"flex-1 overflow-y-auto",children:[_.jsxs("div",{className:"px-4 py-2 text-xs text-slate-500 font-medium sticky top-0 bg-bg-card border-b border-border",children:["Neighbors (",i.length,")"]}),i.length>0?_.jsx("div",{className:"divide-y divide-border",children:i.map(f=>_.jsxs("button",{onClick:()=>n(f.node.node_num),className:"w-full px-4 py-2 text-left hover:bg-bg-hover transition-colors flex items-center gap-2",style:{borderLeftWidth:3,borderLeftColor:GU(f.snr)},children:[_.jsxs("div",{className:"flex-1 min-w-0",children:[_.jsx("div",{className:"text-sm text-slate-200 font-mono truncate",children:f.node.short_name}),_.jsx("div",{className:"text-xs text-slate-500 truncate",children:f.node.long_name})]}),_.jsxs("div",{className:"text-right flex-shrink-0",children:[_.jsxs("div",{className:"text-xs font-mono",style:{color:GU(f.snr)},children:[f.snr.toFixed(1)," dB"]}),_.jsx("div",{className:"text-xs text-slate-500",children:hJe(f.snr)})]})]},f.node.node_num))}):_.jsx("div",{className:"px-4 py-6 text-center text-sm text-slate-500",children:"No known neighbors"})]})]})}const WU=["ROUTER","ROUTER_LATE","REPEATER","TRACKER"];function yJe(e){if(!e)return"bg-slate-500";const t=new Date(e),n=(new Date().getTime()-t.getTime())/36e5;return n<1?"bg-green-500":n<24?"bg-amber-500":"bg-slate-500"}function _Je(e){if(!e)return"—";const t=new Date(e),n=new Date().getTime()-t.getTime(),i=Math.floor(n/6e4),a=Math.floor(n/36e5),o=Math.floor(n/864e5);return i<1?"Just now":i<60?`${i}m ago`:a<24?`${a}h ago`:`${o}d ago`}function xJe(e){return e.battery_level===null?"—":e.battery_level>100||e.voltage&&e.voltage>4.1?"USB ⚡":`${e.battery_level.toFixed(0)}%`}function HU(e){return e===null?"—":e>46?"Northern":e>44.5?"Central":e>43?"SW Idaho":"SC Idaho"}function bJe({nodes:e,selectedNodeId:t,onSelectNode:r}){const[n,i]=U.useState(""),[a,o]=U.useState("short_name"),[s,l]=U.useState("asc"),[u,c]=U.useState("all"),f=U.useMemo(()=>{let v=[...e];if(u==="infra"?v=v.filter(g=>WU.includes(g.role)):u==="online"&&(v=v.filter(g=>{if(!g.last_heard)return!1;const m=new Date(g.last_heard);return(new Date().getTime()-m.getTime())/36e5<1})),n){const g=n.toLowerCase();v=v.filter(m=>m.short_name.toLowerCase().includes(g)||m.long_name.toLowerCase().includes(g)||m.role.toLowerCase().includes(g)||HU(m.latitude).toLowerCase().includes(g))}return v.sort((g,m)=>{let y="",x="";switch(a){case"short_name":y=g.short_name.toLowerCase(),x=m.short_name.toLowerCase();break;case"role":y=g.role,x=m.role;break;case"battery_level":y=g.battery_level??-1,x=m.battery_level??-1;break;case"last_heard":y=g.last_heard?new Date(g.last_heard).getTime():0,x=m.last_heard?new Date(m.last_heard).getTime():0;break;case"hardware":y=g.hardware.toLowerCase(),x=m.hardware.toLowerCase();break}return yx?s==="asc"?1:-1:0}),v},[e,n,a,s,u]),h=v=>{a===v?l(s==="asc"?"desc":"asc"):(o(v),l("asc"))},d=({field:v})=>a!==v?null:s==="asc"?_.jsx(ece,{size:14,className:"inline ml-1"}):_.jsx(mv,{size:14,className:"inline ml-1"});return _.jsxs("div",{className:"bg-bg-card border border-border rounded-lg overflow-hidden",children:[_.jsxs("div",{className:"p-3 border-b border-border flex items-center gap-3",children:[_.jsxs("div",{className:"relative flex-1 max-w-xs",children:[_.jsx(eD,{size:14,className:"absolute left-3 top-1/2 -translate-y-1/2 text-slate-500"}),_.jsx("input",{type:"text",placeholder:"Search nodes...",value:n,onChange:v=>i(v.target.value),className:"w-full pl-9 pr-3 py-1.5 bg-bg-hover border border-border rounded text-sm text-slate-200 placeholder-slate-500 focus:outline-none focus:border-accent"})]}),_.jsxs("div",{className:"flex items-center gap-1",children:[_.jsx(qE,{size:14,className:"text-slate-500 mr-1"}),["all","infra","online"].map(v=>_.jsx("button",{onClick:()=>c(v),className:`px-2 py-1 text-xs rounded transition-colors ${u===v?"bg-accent text-white":"bg-bg-hover text-slate-400 hover:text-slate-200"}`,children:v==="all"?"All":v==="infra"?"Infra":"Online"},v))]}),_.jsxs("div",{className:"text-xs text-slate-500 ml-auto",children:[f.length," of ",e.length," nodes"]})]}),_.jsxs("div",{className:"overflow-x-auto",children:[_.jsxs("table",{className:"w-full text-sm",children:[_.jsx("thead",{children:_.jsxs("tr",{className:"bg-bg-hover text-slate-400 text-xs",children:[_.jsx("th",{className:"w-8 px-3 py-2"}),_.jsxs("th",{className:"px-3 py-2 text-left cursor-pointer hover:text-slate-200",onClick:()=>h("short_name"),children:["Name ",_.jsx(d,{field:"short_name"})]}),_.jsxs("th",{className:"px-3 py-2 text-left cursor-pointer hover:text-slate-200",onClick:()=>h("role"),children:["Role ",_.jsx(d,{field:"role"})]}),_.jsx("th",{className:"px-3 py-2 text-left",children:"Region"}),_.jsxs("th",{className:"px-3 py-2 text-left cursor-pointer hover:text-slate-200",onClick:()=>h("battery_level"),children:["Battery ",_.jsx(d,{field:"battery_level"})]}),_.jsxs("th",{className:"px-3 py-2 text-left cursor-pointer hover:text-slate-200",onClick:()=>h("last_heard"),children:["Last Heard ",_.jsx(d,{field:"last_heard"})]}),_.jsxs("th",{className:"px-3 py-2 text-left cursor-pointer hover:text-slate-200",onClick:()=>h("hardware"),children:["Hardware ",_.jsx(d,{field:"hardware"})]})]})}),_.jsx("tbody",{className:"divide-y divide-border",children:f.slice(0,100).map(v=>{const g=WU.includes(v.role),m=v.node_num===t;return _.jsxs("tr",{onClick:()=>r(v.node_num),className:`cursor-pointer transition-colors ${m?"bg-accent/10":"hover:bg-bg-hover"}`,children:[_.jsx("td",{className:"px-3 py-2",children:_.jsx("div",{className:`w-2 h-2 rounded-full ${yJe(v.last_heard)}`})}),_.jsxs("td",{className:"px-3 py-2",children:[_.jsx("div",{className:"font-mono text-slate-200",children:v.short_name}),_.jsx("div",{className:"text-xs text-slate-500 truncate max-w-[200px]",children:v.long_name})]}),_.jsx("td",{className:"px-3 py-2",children:_.jsx("span",{className:`inline-block px-1.5 py-0.5 rounded text-xs font-medium ${g?"bg-cyan-500/20 text-cyan-400":"bg-slate-500/20 text-slate-400"}`,children:v.role})}),_.jsx("td",{className:"px-3 py-2 text-slate-400",children:HU(v.latitude)}),_.jsx("td",{className:"px-3 py-2 font-mono text-slate-300",children:xJe(v)}),_.jsx("td",{className:"px-3 py-2 text-slate-400",children:_Je(v.last_heard)}),_.jsx("td",{className:"px-3 py-2 font-mono text-xs text-slate-400 truncate max-w-[150px]",children:v.hardware||"—"})]},v.node_num)})})]}),f.length>100&&_.jsxs("div",{className:"px-3 py-2 text-xs text-slate-500 text-center border-t border-border",children:["Showing first 100 of ",f.length," nodes"]}),f.length===0&&_.jsx("div",{className:"px-3 py-8 text-sm text-slate-500 text-center",children:"No nodes match your filters"})]})]})}function wJe(){const[e,t]=U.useState([]),[r,n]=U.useState([]),[i,a]=U.useState([]),[o,s]=U.useState(null),[l,u]=U.useState("topo"),[c,f]=U.useState(!0),[h,d]=U.useState(null);U.useEffect(()=>{document.title="Mesh — MeshAI",Promise.all([dce(),vce(),_ce()]).then(([m,y,x])=>{t(m),n(y),a(x),f(!1)}).catch(m=>{d(m.message),f(!1)})},[]);const v=U.useMemo(()=>e.find(m=>m.node_num===o)||null,[e,o]),g=U.useCallback(m=>{s(m)},[]);return c?_.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsx("div",{className:"text-slate-400",children:"Loading mesh data..."})}):h?_.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsxs("div",{className:"text-red-400",children:["Error: ",h]})}):_.jsxs("div",{className:"space-y-6",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("div",{className:"text-sm text-slate-400",children:[e.length," nodes • ",r.length," edges"]}),_.jsxs("div",{className:"flex items-center bg-bg-card border border-border rounded-lg p-1",children:[_.jsxs("button",{onClick:()=>u("topo"),className:`flex items-center gap-2 px-3 py-1.5 rounded text-sm transition-colors ${l==="topo"?"bg-accent text-white":"text-slate-400 hover:text-slate-200"}`,children:[_.jsx(uce,{size:14}),"Topology"]}),_.jsxs("button",{onClick:()=>u("geo"),className:`flex items-center gap-2 px-3 py-1.5 rounded text-sm transition-colors ${l==="geo"?"bg-accent text-white":"text-slate-400 hover:text-slate-200"}`,children:[_.jsx(sce,{size:14}),"Geographic"]})]})]}),_.jsxs("div",{className:"flex gap-0",children:[_.jsx("div",{className:"flex-1 min-w-0",children:l==="topo"?_.jsx(DQe,{nodes:e,edges:r,selectedNodeId:o,onSelectNode:g}):_.jsx(cJe,{nodes:e,edges:r,selectedNodeId:o,onSelectNode:g})}),_.jsx(mJe,{node:v,edges:r,nodes:e,onSelectNode:g})]}),_.jsx(bJe,{nodes:e,selectedNodeId:o,onSelectNode:g})]})}function HR({label:e,value:t,onChange:r,helper:n,info:i,roleFilter:a,valueType:o="short_name"}){const[s,l]=U.useState([]),[u,c]=U.useState(!0),[f,h]=U.useState(""),[d,v]=U.useState(!1);U.useEffect(()=>{fetch("/api/nodes").then(S=>S.json()).then(S=>{l(S),c(!1)}).catch(()=>{l([]),c(!1)})},[]);const g=U.useMemo(()=>{let S=s;if(a&&(S=S.filter(T=>a==="ROUTER"||a==="infrastructure"?T.is_infrastructure||T.role==="ROUTER"||T.role==="ROUTER_CLIENT"||T.role==="REPEATER":T.role===a)),f.trim()){const T=f.toLowerCase();S=S.filter(C=>{var M,P,I,k;return((M=C.short_name)==null?void 0:M.toLowerCase().includes(T))||((P=C.long_name)==null?void 0:P.toLowerCase().includes(T))||((I=C.role)==null?void 0:I.toLowerCase().includes(T))||((k=C.node_id_hex)==null?void 0:k.toLowerCase().includes(T))})}return S.sort((T,C)=>(T.short_name||"").localeCompare(C.short_name||""))},[s,f,a]),m=S=>{switch(o){case"node_num":return String(S.node_num);case"node_id_hex":return S.node_id_hex;default:return S.short_name||String(S.node_num)}},y=S=>{const T=m(S);return t.includes(T)},x=S=>{const T=m(S);t.includes(T)?r(t.filter(C=>C!==T)):r([...t,T])},b=S=>{const T=[S.short_name];return S.long_name&&S.long_name!==S.short_name&&T.push(`— ${S.long_name}`),S.role&&T.push(`(${S.role})`),T.join(" ")};return!u&&s.length===0?_.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"block text-xs text-slate-500 uppercase tracking-wide",children:e}),_.jsx("input",{type:"text",value:t.join(", "),onChange:S=>r(S.target.value.split(",").map(T=>T.trim()).filter(Boolean)),placeholder:"Enter node IDs separated by commas",className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent"}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]}):_.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"block text-xs text-slate-500 uppercase tracking-wide",children:e}),t.length>0&&_.jsx("div",{className:"flex flex-wrap gap-2 mb-2",children:t.map(S=>{const T=s.find(C=>m(C)===S);return _.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-1 bg-accent/20 text-accent rounded text-sm",children:[T?T.short_name:S,_.jsx("button",{type:"button",onClick:()=>r(t.filter(C=>C!==S)),className:"hover:text-white",children:_.jsx(au,{size:14})})]},S)})}),_.jsxs("div",{className:"relative",children:[_.jsxs("div",{className:"relative",children:[_.jsx(eD,{size:14,className:"absolute left-3 top-1/2 -translate-y-1/2 text-slate-500"}),_.jsx("input",{type:"text",value:f,onChange:S=>h(S.target.value),onFocus:()=>v(!0),placeholder:u?"Loading nodes...":"Search nodes...",className:"w-full pl-9 pr-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent"})]}),d&&!u&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"fixed inset-0 z-40",onClick:()=>v(!1)}),_.jsx("div",{className:"absolute left-0 right-0 top-full mt-1 z-50 max-h-64 overflow-y-auto bg-[#0a0e17] border border-[#1e2a3a] rounded-lg shadow-xl",children:g.length===0?_.jsx("div",{className:"p-3 text-sm text-slate-500 text-center",children:"No nodes found"}):g.map(S=>_.jsxs("button",{type:"button",onClick:()=>x(S),className:`w-full flex items-center gap-2 px-3 py-2 text-left text-sm hover:bg-[#1e2a3a] ${y(S)?"bg-accent/10":""}`,children:[_.jsx("div",{className:`w-4 h-4 rounded border flex items-center justify-center ${y(S)?"bg-accent border-accent":"border-slate-600"}`,children:y(S)&&_.jsx(Qc,{size:12,className:"text-white"})}),_.jsx("span",{className:"text-slate-200",children:b(S)})]},S.node_num))})]})]}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]})}function UR(e){const[t,r]=U.useState([]),[n,i]=U.useState(!0);U.useEffect(()=>{fetch("/api/channels").then(h=>h.json()).then(h=>{r(h),i(!1)}).catch(()=>{r([]),i(!1)})},[]);const a=h=>{const d=h.role==="PRIMARY"?"Primary":h.role==="SECONDARY"?"Secondary":"";return`${h.index}: ${h.name}${d?` (${d})`:""}`};if(!n&&t.length===0)return e.mode==="single"?_.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"block text-xs text-slate-500 uppercase tracking-wide",children:e.label}),_.jsx("input",{type:"number",value:e.value,onChange:h=>e.onChange(Number(h.target.value)),min:e.includeDisabled?-1:0,max:7,className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent"}),e.helper&&_.jsx("p",{className:"text-xs text-slate-600",children:e.helper})]}):_.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"block text-xs text-slate-500 uppercase tracking-wide",children:e.label}),_.jsx("input",{type:"text",value:e.value.join(", "),onChange:h=>{const d=h.target.value.split(",").map(v=>parseInt(v.trim())).filter(v=>!isNaN(v));e.onChange(d)},placeholder:"Enter channel numbers separated by commas",className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent"}),e.helper&&_.jsx("p",{className:"text-xs text-slate-600",children:e.helper})]});if(e.mode==="single"){const{value:h,onChange:d,label:v,helper:g,includeDisabled:m}=e,y=t.filter(x=>x.enabled);return _.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"block text-xs text-slate-500 uppercase tracking-wide",children:v}),_.jsxs("select",{value:h,onChange:x=>d(Number(x.target.value)),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent",children:[m&&_.jsx("option",{value:-1,children:"Disabled"}),y.map(x=>_.jsx("option",{value:x.index,children:a(x)},x.index))]}),g&&_.jsx("p",{className:"text-xs text-slate-600",children:g})]})}const{value:o,onChange:s,label:l,helper:u}=e,c=t.filter(h=>h.enabled),f=h=>{o.includes(h)?s(o.filter(d=>d!==h)):s([...o,h].sort((d,v)=>d-v))};return _.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"block text-xs text-slate-500 uppercase tracking-wide",children:l}),_.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg p-2 space-y-1",children:[c.map(h=>_.jsxs("label",{onClick:()=>f(h.index),className:"flex items-center gap-2 p-2 rounded hover:bg-[#0a0e17] cursor-pointer",children:[_.jsx("div",{className:`w-4 h-4 rounded border flex items-center justify-center ${o.includes(h.index)?"bg-accent border-accent":"border-slate-600"}`,children:o.includes(h.index)&&_.jsx(Qc,{size:12,className:"text-white"})}),_.jsx("span",{className:"text-sm text-slate-200",children:a(h)})]},h.index)),c.length===0&&_.jsx("div",{className:"text-sm text-slate-500 p-2",children:"No channels available"})]}),u&&_.jsx("p",{className:"text-xs text-slate-600",children:u})]})}const UU=[{key:"bot",label:"Bot",icon:que},{key:"connection",label:"Connection",icon:LS},{key:"response",label:"Response",icon:kZ},{key:"history",label:"History",icon:ice},{key:"memory",label:"Memory",icon:Kue},{key:"context",label:"Context",icon:XE},{key:"commands",label:"Commands",icon:EZ},{key:"llm",label:"LLM",icon:CZ},{key:"weather",label:"Weather",icon:nu},{key:"meshmonitor",label:"MeshMonitor",icon:Za},{key:"knowledge",label:"Knowledge",icon:SZ},{key:"mesh_sources",label:"Mesh Sources",icon:PZ},{key:"mesh_intelligence",label:"Intelligence",icon:gv},{key:"dashboard",label:"Dashboard",icon:LZ}],fi={bot:"Identity and behavior settings for the bot on the mesh network.",connection:"How MeshAI connects to your Meshtastic radio.",response:"Controls how quickly and how much the bot responds on the mesh.",history:"Conversation history storage and cleanup.",memory:"Short-term conversation memory management. Controls how the bot maintains context within a conversation.",context:"Passive channel monitoring. The bot listens to mesh channels and uses recent messages as context when responding.",commands:"Mesh commands available via the configured prefix. Toggle individual commands on or off.",llm:"AI model configuration. MeshAI uses an LLM to understand questions and generate responses.",weather:"Weather data for the !weather command. This is separate from NWS environmental alerts.",meshmonitor:"AIDA MeshMonitor integration. An additional data source for mesh network monitoring.",knowledge:"Knowledge base for answering questions from stored documents. Connects to Qdrant vector database or local SQLite.",mesh_sources:"Data sources for mesh network information. MeshAI can pull data from multiple sources simultaneously and merge them into a unified view.",mesh_intelligence:"Advanced mesh analysis: health scoring, region management, and automated alerting. The intelligence engine monitors your mesh and detects problems automatically.",dashboard:"Web dashboard settings. You're looking at it right now."},SJe=[{name:"help",description:"Show available commands and usage"},{name:"health",description:"Mesh network health overview with status dots"},{name:"status",description:"Quick mesh status summary"},{name:"region",description:"List regions or get detailed region breakdown"},{name:"neighbors",description:"Show top infrastructure neighbors with signal quality"},{name:"ping",description:"Test bot responsiveness"},{name:"clear",description:"Clear your conversation history"},{name:"reset",description:"Reset conversation context"},{name:"sub",description:"Subscribe to scheduled reports or alerts"},{name:"unsub",description:"Remove a subscription"},{name:"mysubs",description:"List your active subscriptions"},{name:"alerts",description:"Active NWS weather alerts for mesh area"},{name:"solar",description:"Space weather and HF propagation conditions"},{name:"hf",description:"HF radio propagation (alias for !solar)"},{name:"fire",description:"Active wildfires near the mesh"},{name:"avy",description:"Avalanche advisories for configured zones"},{name:"hotspots",description:"NASA FIRMS satellite fire detections"},{name:"streams",description:"USGS stream gauge readings"},{name:"roads",description:"Road conditions and closures"},{name:"traffic",description:"Traffic flow on monitored corridors"}],TJe=[{value:"US-AL",label:"Alabama"},{value:"US-AK",label:"Alaska"},{value:"US-AZ",label:"Arizona"},{value:"US-AR",label:"Arkansas"},{value:"US-CA",label:"California"},{value:"US-CO",label:"Colorado"},{value:"US-CT",label:"Connecticut"},{value:"US-DE",label:"Delaware"},{value:"US-FL",label:"Florida"},{value:"US-GA",label:"Georgia"},{value:"US-HI",label:"Hawaii"},{value:"US-ID",label:"Idaho"},{value:"US-IL",label:"Illinois"},{value:"US-IN",label:"Indiana"},{value:"US-IA",label:"Iowa"},{value:"US-KS",label:"Kansas"},{value:"US-KY",label:"Kentucky"},{value:"US-LA",label:"Louisiana"},{value:"US-ME",label:"Maine"},{value:"US-MD",label:"Maryland"},{value:"US-MA",label:"Massachusetts"},{value:"US-MI",label:"Michigan"},{value:"US-MN",label:"Minnesota"},{value:"US-MS",label:"Mississippi"},{value:"US-MO",label:"Missouri"},{value:"US-MT",label:"Montana"},{value:"US-NE",label:"Nebraska"},{value:"US-NV",label:"Nevada"},{value:"US-NH",label:"New Hampshire"},{value:"US-NJ",label:"New Jersey"},{value:"US-NM",label:"New Mexico"},{value:"US-NY",label:"New York"},{value:"US-NC",label:"North Carolina"},{value:"US-ND",label:"North Dakota"},{value:"US-OH",label:"Ohio"},{value:"US-OK",label:"Oklahoma"},{value:"US-OR",label:"Oregon"},{value:"US-PA",label:"Pennsylvania"},{value:"US-RI",label:"Rhode Island"},{value:"US-SC",label:"South Carolina"},{value:"US-SD",label:"South Dakota"},{value:"US-TN",label:"Tennessee"},{value:"US-TX",label:"Texas"},{value:"US-UT",label:"Utah"},{value:"US-VT",label:"Vermont"},{value:"US-VA",label:"Virginia"},{value:"US-WA",label:"Washington"},{value:"US-WV",label:"West Virginia"},{value:"US-WI",label:"Wisconsin"},{value:"US-WY",label:"Wyoming"}];function Ho({info:e,link:t,linkText:r="Learn more"}){const[n,i]=U.useState(!1),a=U.useRef(null);return U.useEffect(()=>{if(!n)return;function o(l){a.current&&!a.current.contains(l.target)&&i(!1)}const s=setTimeout(()=>document.addEventListener("mousedown",o),0);return()=>{clearTimeout(s),document.removeEventListener("mousedown",o)}},[n]),_.jsxs("div",{className:"relative inline-block",ref:a,children:[_.jsx("button",{type:"button",onClick:o=>{o.stopPropagation(),i(!n)},className:"ml-1.5 w-4 h-4 rounded-full bg-slate-700 hover:bg-slate-600 text-slate-400 hover:text-slate-200 inline-flex items-center justify-center text-xs transition-colors",title:"More info",children:"?"}),n&&_.jsxs("div",{className:"absolute left-0 top-6 z-50 w-72 p-3 bg-[#1a2332] border border-[#2a3a4a] rounded-lg shadow-xl text-xs text-slate-300 leading-relaxed",children:[_.jsx("button",{type:"button",onClick:()=>i(!1),className:"absolute top-1 right-1 w-5 h-5 rounded hover:bg-slate-700 text-slate-500 hover:text-slate-300 inline-flex items-center justify-center transition-colors","aria-label":"Close",children:_.jsx(au,{size:12})}),_.jsx("div",{className:"pr-4",children:e}),t&&_.jsxs("a",{href:t,target:"_blank",rel:"noopener noreferrer",className:"mt-2 flex items-center gap-1 text-accent hover:underline",onClick:o=>o.stopPropagation(),children:[r," ",_.jsx(wd,{size:10})]})]})]})}function hi({text:e}){return _.jsx("p",{className:"text-sm text-slate-500 mb-6 pb-4 border-b border-[#1e2a3a]",children:e})}function _t({label:e,value:t,onChange:r,type:n="text",placeholder:i="",helper:a="",info:o="",infoLink:s=""}){const[l,u]=U.useState(!1),c=n==="password";return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,o&&_.jsx(Ho,{info:o,link:s})]}),_.jsxs("div",{className:"relative",children:[_.jsx("input",{type:c&&!l?"password":"text",value:t,onChange:f=>r(f.target.value),placeholder:i,className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent placeholder-slate-600"}),c&&_.jsx("button",{type:"button",onClick:()=>u(!l),className:"absolute right-2 top-1/2 -translate-y-1/2 text-slate-500 hover:text-slate-300",children:l?_.jsx(MZ,{size:16}):_.jsx(XE,{size:16})})]}),a&&_.jsx("p",{className:"text-xs text-slate-600",children:a})]})}function rt({label:e,value:t,onChange:r,min:n,max:i,step:a=1,helper:o="",info:s="",infoLink:l=""}){return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,s&&_.jsx(Ho,{info:s,link:l})]}),_.jsx("input",{type:"number",value:t,onChange:u=>r(Number(u.target.value)),min:n,max:i,step:a,className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent"}),o&&_.jsx("p",{className:"text-xs text-slate-600",children:o})]})}function yr({label:e,checked:t,onChange:r,helper:n="",info:i="",infoLink:a=""}){return _.jsxs("div",{className:"flex items-center justify-between py-2",children:[_.jsxs("div",{children:[_.jsxs("span",{className:"flex items-center text-sm text-slate-300",children:[e,i&&_.jsx(Ho,{info:i,link:a})]}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]}),_.jsx("button",{type:"button",onClick:()=>r(!t),className:`relative w-11 h-6 rounded-full transition-colors ${t?"bg-accent":"bg-[#1e2a3a]"}`,children:_.jsx("span",{className:`absolute top-1 left-1 w-4 h-4 rounded-full bg-white transition-transform ${t?"translate-x-5":""}`})})]})}function Po({label:e,value:t,onChange:r,options:n,helper:i="",info:a="",infoLink:o=""}){return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,a&&_.jsx(Ho,{info:a,link:o})]}),_.jsx("select",{value:t,onChange:s=>r(s.target.value),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent",children:n.map(s=>_.jsx("option",{value:s.value,children:s.label},s.value))}),i&&_.jsx("p",{className:"text-xs text-slate-600",children:i})]})}function CJe({label:e,value:t,onChange:r,rows:n=4,helper:i="",info:a="",infoLink:o=""}){return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,a&&_.jsx(Ho,{info:a,link:o})]}),_.jsx("textarea",{value:t,onChange:s=>r(s.target.value),rows:n,className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent resize-y"}),i&&_.jsx("p",{className:"text-xs text-slate-600",children:i})]})}function Yh({label:e,value:t,onChange:r,helper:n="",info:i="",infoLink:a=""}){const[o,s]=U.useState(t.join(", "));U.useEffect(()=>{s(t.join(", "))},[t]);const l=()=>{const u=o.split(",").map(c=>c.trim()).filter(Boolean);r(u)};return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,i&&_.jsx(Ho,{info:i,link:a})]}),_.jsx("input",{type:"text",value:o,onChange:u=>s(u.target.value),onBlur:l,placeholder:"item1, item2, item3",className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent placeholder-slate-600"}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]})}function AJe({label:e,value:t,onChange:r,helper:n="",info:i="",infoLink:a=""}){const[o,s]=U.useState(t.join(", "));U.useEffect(()=>{s(t.join(", "))},[t]);const l=()=>{const u=o.split(",").map(c=>parseInt(c.trim(),10)).filter(c=>!isNaN(c));r(u)};return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,i&&_.jsx(Ho,{info:i,link:a})]}),_.jsx("input",{type:"text",value:o,onChange:u=>s(u.target.value),onBlur:l,placeholder:"0, 1, 2",className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent placeholder-slate-600"}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]})}function Cn({label:e,description:t,checked:r,onChange:n,threshold:i,onThresholdChange:a,thresholdLabel:o,thresholdMin:s,thresholdMax:l,thresholdStep:u=1,thresholdSuffix:c=""}){return _.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg p-3 space-y-2",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("div",{className:"flex-1",children:[_.jsx("span",{className:"text-sm text-slate-300",children:e}),_.jsx("p",{className:"text-xs text-slate-600",children:t})]}),_.jsx("button",{type:"button",onClick:()=>n(!r),className:`relative w-11 h-6 rounded-full transition-colors flex-shrink-0 ml-3 ${r?"bg-accent":"bg-[#1e2a3a]"}`,children:_.jsx("span",{className:`absolute top-1 left-1 w-4 h-4 rounded-full bg-white transition-transform ${r?"translate-x-5":""}`})})]}),r&&i!==void 0&&a&&_.jsxs("div",{className:"flex items-center gap-2 pt-2 border-t border-[#1e2a3a]",children:[_.jsxs("span",{className:"text-xs text-slate-500",children:[o||"Threshold",":"]}),_.jsx("input",{type:"number",value:i,onChange:f=>a(Number(f.target.value)),min:s,max:l,step:u,className:"w-20 px-2 py-1 bg-[#0a0e17] border border-[#1e2a3a] rounded text-xs text-slate-200 font-mono"}),c&&_.jsx("span",{className:"text-xs text-slate-500",children:c})]})]})}function MJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.bot}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Bot Name",value:e.name,onChange:r=>t({...e,name:r}),helper:"Name the bot responds to on the mesh",info:"When someone sends a message containing this name, the bot will respond. Also used as the sender name in broadcasts. Changing this requires a restart."}),_.jsx(_t,{label:"Owner",value:e.owner,onChange:r=>t({...e,owner:r}),helper:"Your callsign or identifier",info:"Identifies the bot operator. Shown in !help responses and used for admin-level commands."})]}),_.jsx(yr,{label:"Respond to DMs",checked:e.respond_to_dms,onChange:r=>t({...e,respond_to_dms:r}),helper:"Reply when someone sends a direct message",info:"When enabled, the bot responds to direct messages from any node. When disabled, the bot only responds to channel messages that mention its name."}),_.jsx(yr,{label:"Filter BBS Protocols",checked:e.filter_bbs_protocols,onChange:r=>t({...e,filter_bbs_protocols:r}),helper:"Ignore BBS bulletin board traffic",info:"Filters out automated BBS protocol messages (advBBS, MAIL*, BOARD*) so the bot doesn't try to respond to machine-to-machine traffic."})]})}function PJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.connection}),_.jsx(Po,{label:"Connection Type",value:e.type,onChange:r=>t({...e,type:r}),options:[{value:"serial",label:"Serial (USB)"},{value:"tcp",label:"TCP (Network)"}],helper:"Serial for USB-connected radios, TCP for network or meshtasticd",info:"Serial: direct USB connection to a Meshtastic radio. TCP: connect over the network to a radio's IP or to meshtasticd running on another machine."}),e.type==="serial"?_.jsx(_t,{label:"Serial Port",value:e.serial_port,onChange:r=>t({...e,serial_port:r}),placeholder:"/dev/ttyUSB0",helper:"Device path for your USB radio",info:"Usually /dev/ttyUSB0 on Linux or /dev/ttyACM0. Check with 'ls /dev/tty*' after plugging in your radio."}):_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"TCP Host",value:e.tcp_host,onChange:r=>t({...e,tcp_host:r}),placeholder:"192.168.1.100",helper:"IP address or hostname of the radio/meshtasticd"}),_.jsx(rt,{label:"TCP Port",value:e.tcp_port,onChange:r=>t({...e,tcp_port:r}),min:1,max:65535,helper:"Default 4403 for meshtasticd"})]})]})}function LJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.response}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Delay Min (sec)",value:e.delay_min,onChange:r=>t({...e,delay_min:r}),min:0,step:.1,helper:"Minimum wait before responding",info:"Adds a random delay between min and max before the bot sends a response. Prevents the bot from appearing to respond instantly, which can feel unnatural on a radio network."}),_.jsx(rt,{label:"Delay Max (sec)",value:e.delay_max,onChange:r=>t({...e,delay_max:r}),min:0,step:.1,helper:"Maximum wait before responding",info:"Also prevents collisions with other traffic by staggering transmissions."})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Max Length",value:e.max_length,onChange:r=>t({...e,max_length:r}),min:50,max:500,helper:"Maximum characters per response message",info:"Meshtastic packets have limited size. This caps how long each message chunk can be. The bot will split longer responses into multiple messages up to Max Messages."}),_.jsx(rt,{label:"Max Messages",value:e.max_messages,onChange:r=>t({...e,max_messages:r}),min:1,max:10,helper:"Maximum chunks per response",info:"If a response is longer than Max Length, the bot splits it into this many chunks at most. Higher values = more complete answers but more airtime used."})]})]})}function kJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.history}),_.jsx(_t,{label:"Database Path",value:e.database,onChange:r=>t({...e,database:r}),helper:"SQLite file for storing conversation history",info:"Path to the SQLite database file. Created automatically if it doesn't exist. Stores all conversation history for context."}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Max Messages Per User",value:e.max_messages_per_user,onChange:r=>t({...e,max_messages_per_user:r}),min:0,helper:"History limit per user (0 = unlimited)",info:"Limits how many messages are stored per user. Older messages are pruned when the limit is reached. Set to 0 for no limit."}),_.jsx(rt,{label:"Conversation Timeout (sec)",value:e.conversation_timeout,onChange:r=>t({...e,conversation_timeout:r}),min:0,helper:"Seconds before context resets",info:"If a user doesn't message for this long, their next message starts a new conversation context. The bot won't remember the previous topic."})]}),_.jsx(yr,{label:"Auto Cleanup",checked:e.auto_cleanup,onChange:r=>t({...e,auto_cleanup:r}),helper:"Automatically prune old conversations"}),e.auto_cleanup&&_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Cleanup Interval (hours)",value:e.cleanup_interval_hours,onChange:r=>t({...e,cleanup_interval_hours:r}),min:1,helper:"Hours between cleanup runs"}),_.jsx(rt,{label:"Max Age (days)",value:e.max_age_days,onChange:r=>t({...e,max_age_days:r}),min:1,helper:"Delete conversations older than this"})]})]})}function IJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.memory}),_.jsx(yr,{label:"Enable Memory",checked:e.enabled,onChange:r=>t({...e,enabled:r}),helper:"Keep conversation context between messages"}),e.enabled&&_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Window Size",value:e.window_size,onChange:r=>t({...e,window_size:r}),min:1,helper:"Recent message pairs kept in full",info:"The bot keeps this many recent exchanges (user message + bot response pairs) as full text in context. Older messages are summarized to save token space."}),_.jsx(rt,{label:"Summarize Threshold",value:e.summarize_threshold,onChange:r=>t({...e,summarize_threshold:r}),min:1,helper:"Messages before older context is summarized",info:"When the conversation exceeds this many messages, older ones outside the window are compressed into a summary by the LLM."})]})]})}function OJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.context}),_.jsx(yr,{label:"Enable Passive Context",checked:e.enabled,onChange:r=>t({...e,enabled:r}),helper:"Listen to channel traffic for context",info:"When enabled, the bot monitors mesh channels and includes recent messages in its context. This lets the bot reference things other people said on the channel."}),e.enabled&&_.jsxs(_.Fragment,{children:[_.jsx(UR,{label:"Observe Channels",value:e.observe_channels,onChange:r=>t({...e,observe_channels:r}),helper:"Channels to monitor (empty = all)",info:"Meshtastic channels to listen on. Leave empty to monitor all channels.",mode:"multi"}),_.jsx(HR,{label:"Ignore Nodes",value:e.ignore_nodes,onChange:r=>t({...e,ignore_nodes:r}),helper:"Nodes to exclude from context",info:"Messages from these nodes won't be included in passive context. Useful for filtering out noisy automated nodes."}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Max Age (sec)",value:e.max_age,onChange:r=>t({...e,max_age:r}),min:0,helper:"Ignore messages older than this"}),_.jsx(rt,{label:"Max Context Items",value:e.max_context_items,onChange:r=>t({...e,max_context_items:r}),min:1,helper:"Maximum recent messages to include"})]})]})]})}function EJe({data:e,onChange:t}){const r=new Set(e.disabled_commands.map(i=>i.toLowerCase())),n=i=>{const a=i.toLowerCase();r.has(a)?t({...e,disabled_commands:e.disabled_commands.filter(o=>o.toLowerCase()!==a)}):t({...e,disabled_commands:[...e.disabled_commands,i]})};return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.commands}),_.jsx(yr,{label:"Enable Commands",checked:e.enabled,onChange:i=>t({...e,enabled:i}),helper:"Allow !commands on the mesh"}),e.enabled&&_.jsxs(_.Fragment,{children:[_.jsx(_t,{label:"Command Prefix",value:e.prefix,onChange:i=>t({...e,prefix:i}),helper:"Character that triggers commands (e.g. ! for !help)",info:"Users type this character followed by the command name. Only single characters recommended."}),_.jsxs("div",{className:"space-y-2",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Available Commands",_.jsx(Ho,{info:"Toggle commands on or off. Disabled commands won't respond when users invoke them."})]}),_.jsx("div",{className:"grid gap-1",children:SJe.map(i=>{const a=!r.has(i.name.toLowerCase());return _.jsxs("div",{className:"flex items-center justify-between p-2 bg-[#0a0e17] border border-[#1e2a3a] rounded hover:border-[#2a3a4a] transition-colors",children:[_.jsxs("div",{className:"flex items-center gap-3",children:[_.jsxs("code",{className:"text-accent text-sm",children:["!",i.name]}),_.jsx("span",{className:"text-xs text-slate-500",children:i.description})]}),_.jsx("button",{type:"button",onClick:()=>n(i.name),className:`relative w-9 h-5 rounded-full transition-colors ${a?"bg-accent":"bg-[#1e2a3a]"}`,children:_.jsx("span",{className:`absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-transform ${a?"translate-x-4":""}`})})]},i.name)})})]})]})]})}function DJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.llm}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(Po,{label:"Backend",value:e.backend,onChange:r=>t({...e,backend:r}),options:[{value:"openai",label:"OpenAI"},{value:"anthropic",label:"Anthropic"},{value:"google",label:"Google (Gemini)"}],helper:"LLM provider to use",info:"OpenAI: GPT models (gpt-4o, gpt-4o-mini). Anthropic: Claude models (claude-sonnet-4-20250514). Google: Gemini models. Can also point to compatible APIs like Ollama, LM Studio, or Open WebUI by changing the Base URL."}),_.jsx(_t,{label:"Model",value:e.model,onChange:r=>t({...e,model:r}),placeholder:"gpt-4o-mini",helper:"Specific model name",info:"The specific model to use. Common choices: gpt-4o-mini (fast, cheap), gpt-4o (better, costs more), claude-sonnet-4-20250514 (Anthropic equivalent). For local models via Ollama, use the model name you pulled (e.g. llama3.1)."})]}),_.jsx(_t,{label:"API Key",value:e.api_key,onChange:r=>t({...e,api_key:r}),type:"password",helper:"Supports ${ENV_VAR} syntax",info:"Your API key from the provider. You can also use ${ENV_VAR} syntax to read from an environment variable instead of storing the key in the config file."}),_.jsx(_t,{label:"Base URL",value:e.base_url,onChange:r=>t({...e,base_url:r}),placeholder:"https://api.openai.com/v1",helper:"API endpoint (change for local LLMs)",info:"Default API endpoint for the selected backend. Change this to point to a local LLM server (Ollama at http://localhost:11434/v1, Open WebUI, LM Studio, etc.) or a proxy."}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Timeout (sec)",value:e.timeout,onChange:r=>t({...e,timeout:r}),min:5,max:120,helper:"Maximum seconds to wait for response"}),_.jsx(rt,{label:"Max Response Tokens",value:e.max_response_tokens,onChange:r=>t({...e,max_response_tokens:r}),min:100,helper:"Token limit for LLM responses"})]}),_.jsx(yr,{label:"Use System Prompt",checked:e.use_system_prompt,onChange:r=>t({...e,use_system_prompt:r}),helper:"Enable custom system instructions"}),e.use_system_prompt&&_.jsx(CJe,{label:"System Prompt",value:e.system_prompt,onChange:r=>t({...e,system_prompt:r}),rows:6,helper:"Instructions that shape the bot's personality",info:"Instructions that shape the bot's personality and behavior. The bot always follows these instructions. MeshAI adds mesh health data and environmental context automatically — you don't need to include those here."}),_.jsx(yr,{label:"Web Search",checked:e.web_search,onChange:r=>t({...e,web_search:r}),helper:"Enable web search tool (Open WebUI feature)"}),_.jsx(yr,{label:"Google Grounding",checked:e.google_grounding,onChange:r=>t({...e,google_grounding:r}),helper:"Ground responses in web search (Gemini only)"})]})}function NJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.weather}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(Po,{label:"Primary Provider",value:e.primary,onChange:r=>t({...e,primary:r}),options:[{value:"openmeteo",label:"Open-Meteo"},{value:"wttr",label:"wttr.in"},{value:"llm",label:"LLM"}],helper:"Main weather data source"}),_.jsx(Po,{label:"Fallback Provider",value:e.fallback,onChange:r=>t({...e,fallback:r}),options:[{value:"openmeteo",label:"Open-Meteo"},{value:"wttr",label:"wttr.in"},{value:"llm",label:"LLM"},{value:"none",label:"None"}],helper:"Backup if primary fails"})]}),_.jsx(_t,{label:"Default Location",value:e.default_location,onChange:r=>t({...e,default_location:r}),placeholder:"Your city, state",helper:"Location when none specified"})]})}function jJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.meshmonitor}),_.jsx(yr,{label:"Enable MeshMonitor",checked:e.enabled,onChange:r=>t({...e,enabled:r}),helper:"Connect to AIDA MeshMonitor instance",info:"MeshMonitor by Yeraze provides node data, battery info, telemetry, and auto-responder patterns. MeshAI uses this as a data source and avoids duplicate responses."}),e.enabled&&_.jsxs(_.Fragment,{children:[_.jsx(_t,{label:"URL",value:e.url,onChange:r=>t({...e,url:r}),placeholder:"http://192.168.1.100:8080",helper:"MeshMonitor API endpoint",info:"Full URL to your MeshMonitor instance. Usually runs on port 8080."}),_.jsx(yr,{label:"Inject Into Prompt",checked:e.inject_into_prompt,onChange:r=>t({...e,inject_into_prompt:r}),helper:"Tell LLM about MeshMonitor commands",info:"Adds MeshMonitor's auto-responder patterns to the LLM context so it knows what commands MeshMonitor handles."}),_.jsx(rt,{label:"Refresh Interval (sec)",value:e.refresh_interval,onChange:r=>t({...e,refresh_interval:r}),min:10,helper:"How often to fetch patterns"}),_.jsx(yr,{label:"Polite Mode",checked:e.polite_mode,onChange:r=>t({...e,polite_mode:r}),helper:"Reduce polling frequency",info:"Reduces polling frequency for shared instances to be a good neighbor."})]})]})}function RJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.knowledge}),_.jsx(yr,{label:"Enable Knowledge Base",checked:e.enabled,onChange:r=>t({...e,enabled:r}),helper:"Answer questions from stored documents",info:"Uses RAG (Retrieval-Augmented Generation) to answer questions from a knowledge base. Supports Qdrant vector database or local SQLite with FTS5."}),e.enabled&&_.jsxs(_.Fragment,{children:[_.jsx(Po,{label:"Backend",value:e.backend,onChange:r=>t({...e,backend:r}),options:[{value:"auto",label:"Auto (Qdrant -> SQLite)"},{value:"qdrant",label:"Qdrant"},{value:"sqlite",label:"SQLite"}],helper:"Knowledge storage backend",info:"Auto tries Qdrant first, falls back to SQLite. Qdrant provides hybrid search with dense+sparse embeddings. SQLite uses FTS5 keyword search."}),(e.backend==="qdrant"||e.backend==="auto")&&_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Qdrant Host",value:e.qdrant_host,onChange:r=>t({...e,qdrant_host:r}),helper:"Qdrant server hostname",info:"IP or hostname of your Qdrant vector database server."}),_.jsx(rt,{label:"Qdrant Port",value:e.qdrant_port,onChange:r=>t({...e,qdrant_port:r}),helper:"Default 6333"})]}),_.jsx(_t,{label:"Collection",value:e.qdrant_collection,onChange:r=>t({...e,qdrant_collection:r}),helper:"Qdrant collection name"}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"TEI Host",value:e.tei_host,onChange:r=>t({...e,tei_host:r}),helper:"Text Embeddings Inference host",info:"TEI service for generating dense embeddings. Uses BAAI/bge-m3 model."}),_.jsx(rt,{label:"TEI Port",value:e.tei_port,onChange:r=>t({...e,tei_port:r}),helper:"Default 8090"})]}),_.jsx(yr,{label:"Use Sparse Embeddings",checked:e.use_sparse,onChange:r=>t({...e,use_sparse:r}),helper:"Enable hybrid search with sparse vectors",info:"Combines dense embeddings with sparse (keyword-based) embeddings using Reciprocal Rank Fusion for better search results."})]}),_.jsx(_t,{label:"SQLite DB Path",value:e.db_path,onChange:r=>t({...e,db_path:r}),helper:"Local knowledge database file"}),_.jsx(rt,{label:"Top K Results",value:e.top_k,onChange:r=>t({...e,top_k:r}),min:1,max:20,helper:"Number of documents to retrieve"})]})]})}function BJe({source:e,onChange:t,onDelete:r}){const[n,i]=U.useState(!1),a={meshview:"Web-based mesh monitoring tool. Enter the full URL of a MeshView instance. No API key typically required.",meshmonitor:"AIDA MeshMonitor API. Provides node data and network statistics. Requires API token.",mqtt:"Subscribe directly to a Meshtastic MQTT broker for real-time packet data. This is push-based (instant) vs the polling approach of MeshView/MeshMonitor."};return _.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg overflow-hidden",children:[_.jsxs("div",{className:"flex items-center justify-between p-3 bg-[#0a0e17] cursor-pointer",onClick:()=>i(!n),children:[_.jsxs("div",{className:"flex items-center gap-3",children:[n?_.jsx(mv,{size:16}):_.jsx(_d,{size:16}),_.jsx("div",{className:`w-2 h-2 rounded-full ${e.enabled?"bg-green-500":"bg-slate-500"}`}),_.jsx("span",{className:"font-mono text-sm text-slate-200",children:e.name||"Unnamed Source"}),_.jsx("span",{className:"text-xs text-slate-500 bg-[#1e2a3a] px-2 py-0.5 rounded",children:e.type})]}),_.jsx("button",{onClick:o=>{o.stopPropagation(),r()},className:"p-1 text-red-400 hover:text-red-300 hover:bg-red-500/10 rounded",children:_.jsx(tD,{size:14})})]}),n&&_.jsxs("div",{className:"p-4 space-y-4 border-t border-[#1e2a3a]",children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Name",value:e.name,onChange:o=>t({...e,name:o}),helper:"Friendly name for this source"}),_.jsx(Po,{label:"Type",value:e.type,onChange:o=>t({...e,type:o}),options:[{value:"meshview",label:"MeshView"},{value:"meshmonitor",label:"MeshMonitor"},{value:"mqtt",label:"MQTT Broker"}],info:a[e.type]||""})]}),e.type!=="mqtt"&&_.jsx(_t,{label:"URL",value:e.url,onChange:o=>t({...e,url:o}),helper:"Full URL including protocol"}),e.type==="meshmonitor"&&_.jsx(_t,{label:"API Token",value:e.api_token,onChange:o=>t({...e,api_token:o}),type:"password",helper:"Bearer token for authentication"}),e.type==="mqtt"&&_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Host",value:e.host||"",onChange:o=>t({...e,host:o}),helper:"MQTT broker hostname"}),_.jsx(rt,{label:"Port",value:e.port||1883,onChange:o=>t({...e,port:o}),min:1,max:65535,helper:"1883 plain, 8883 TLS"})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Username",value:e.username||"",onChange:o=>t({...e,username:o})}),_.jsx(_t,{label:"Password",value:e.password||"",onChange:o=>t({...e,password:o}),type:"password"})]}),_.jsx(_t,{label:"Topic Root",value:e.topic_root||"msh/US",onChange:o=>t({...e,topic_root:o}),helper:"Base topic to subscribe to"}),_.jsx(yr,{label:"Use TLS",checked:e.use_tls||!1,onChange:o=>t({...e,use_tls:o}),helper:"Encrypt MQTT connection"})]}),_.jsx(rt,{label:"Refresh Interval (sec)",value:e.refresh_interval,onChange:o=>t({...e,refresh_interval:o}),min:10,helper:"Polling frequency"}),_.jsx(yr,{label:"Enabled",checked:e.enabled,onChange:o=>t({...e,enabled:o})}),_.jsx(yr,{label:"Polite Mode",checked:e.polite_mode,onChange:o=>t({...e,polite_mode:o}),helper:"Reduce polling for shared instances"})]})]})}function zJe({data:e,onChange:t}){const r=()=>{t([...e,{name:"New Source",type:"meshview",url:"",api_token:"",refresh_interval:30,polite_mode:!1,enabled:!0,host:"",port:1883,username:"",password:"",topic_root:"msh/US",use_tls:!1}])};return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.mesh_sources}),e.map((n,i)=>_.jsx(BJe,{source:n,onChange:a=>{const o=[...e];o[i]=a,t(o)},onDelete:()=>{confirm(`Delete source "${n.name}"?`)&&t(e.filter((a,o)=>o!==i))}},i)),_.jsxs("button",{onClick:r,className:"w-full py-2 border border-dashed border-[#1e2a3a] rounded-lg text-slate-500 hover:text-slate-300 hover:border-accent flex items-center justify-center gap-2 transition-colors",children:[_.jsx(MS,{size:16})," Add Source"]})]})}function $Je({data:e,onChange:t}){const[r,n]=U.useState(null);return _.jsxs("div",{className:"space-y-6",children:[_.jsx(hi,{text:fi.mesh_intelligence}),_.jsx(yr,{label:"Enable Mesh Intelligence",checked:e.enabled,onChange:i=>t({...e,enabled:i}),helper:"Activate health scoring and alerting"}),e.enabled&&_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Locality Radius (miles)",value:e.locality_radius_miles,onChange:i=>t({...e,locality_radius_miles:i}),min:1,step:.5,helper:"Region assignment radius",info:"Nodes within this distance of a region anchor point are assigned to that region."}),_.jsx(rt,{label:"Offline Threshold (hours)",value:e.offline_threshold_hours,onChange:i=>t({...e,offline_threshold_hours:i}),min:1,helper:"Time until node marked offline",info:"A node is considered offline after not being heard for this many hours."})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Packet Threshold",value:e.packet_threshold,onChange:i=>t({...e,packet_threshold:i}),min:0,helper:"Min packets per 24h to flag",info:"Minimum packets per 24 hours. Nodes below this are flagged as low activity."}),_.jsx(rt,{label:"Battery Warning %",value:e.battery_warning_percent,onChange:i=>t({...e,battery_warning_percent:i}),min:1,max:100,helper:"Global battery warning level"})]}),_.jsx(HR,{label:"Critical Nodes",value:e.critical_nodes,onChange:i=>t({...e,critical_nodes:i}),helper:"Critical infrastructure nodes",info:"Nodes that get priority alerting when they go offline.",roleFilter:"infrastructure"}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(UR,{label:"Alert Channel",value:e.alert_channel,onChange:i=>t({...e,alert_channel:i}),helper:"Channel for broadcast alerts",info:"Meshtastic channel for broadcast alerts. Select Disabled to turn off channel broadcasting.",mode:"single",includeDisabled:!0}),_.jsx(rt,{label:"Alert Cooldown (min)",value:e.alert_cooldown_minutes,onChange:i=>t({...e,alert_cooldown_minutes:i}),min:1,helper:"Min time between repeat alerts",info:"Minimum minutes between repeated alerts for the same condition. Uses scaling cooldown (12h, 24h, 48h)."})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Regions",_.jsx(Ho,{info:"Regions group mesh nodes by geographic area. Each region has an anchor point (lat/lon) and nodes within the region radius are automatically assigned. Regions enable localized reports, alerts, and health scoring."})]}),e.regions.map((i,a)=>_.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg overflow-hidden",children:[_.jsxs("div",{className:"flex items-center justify-between p-3 bg-[#0a0e17] cursor-pointer",onClick:()=>n(r===a?null:a),children:[_.jsxs("div",{className:"flex items-center gap-3",children:[r===a?_.jsx(mv,{size:16}):_.jsx(_d,{size:16}),_.jsx("span",{className:"font-medium text-slate-200",children:i.name||"Unnamed Region"}),_.jsx("span",{className:"text-xs text-slate-500",children:i.local_name})]}),_.jsx("button",{onClick:o=>{if(o.stopPropagation(),confirm(`Delete region "${i.name||"Unnamed Region"}"?`)){const s=e.regions.filter((l,u)=>u!==a);t({...e,regions:s})}},className:"p-1 text-red-400 hover:text-red-300 hover:bg-red-500/10 rounded",children:_.jsx(tD,{size:14})})]}),r===a&&_.jsxs("div",{className:"p-4 space-y-3 border-t border-[#1e2a3a]",children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Name",value:i.name,onChange:o=>{const s=[...e.regions];s[a]={...i,name:o},t({...e,regions:s})}}),_.jsx(_t,{label:"Local Name",value:i.local_name,onChange:o=>{const s=[...e.regions];s[a]={...i,local_name:o},t({...e,regions:s})}})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Latitude",value:i.lat,onChange:o=>{const s=[...e.regions];s[a]={...i,lat:o},t({...e,regions:s})},step:1e-4}),_.jsx(rt,{label:"Longitude",value:i.lon,onChange:o=>{const s=[...e.regions];s[a]={...i,lon:o},t({...e,regions:s})},step:1e-4})]}),_.jsx(_t,{label:"Description",value:i.description,onChange:o=>{const s=[...e.regions];s[a]={...i,description:o},t({...e,regions:s})}}),_.jsx(Yh,{label:"Aliases",value:i.aliases,onChange:o=>{const s=[...e.regions];s[a]={...i,aliases:o},t({...e,regions:s})}}),_.jsx(Yh,{label:"Cities",value:i.cities,onChange:o=>{const s=[...e.regions];s[a]={...i,cities:o},t({...e,regions:s})}})]})]},a)),_.jsxs("button",{onClick:()=>{const i={name:"",local_name:"",lat:0,lon:0,description:"",aliases:[],cities:[]};t({...e,regions:[...e.regions,i]}),n(e.regions.length)},className:"w-full py-2 border border-dashed border-[#1e2a3a] rounded-lg text-slate-500 hover:text-slate-300 hover:border-accent flex items-center justify-center gap-2 transition-colors",children:[_.jsx(MS,{size:16})," Add Region"]})]}),_.jsxs("div",{className:"space-y-3",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Alert Rules",_.jsx(Ho,{info:"Configure which conditions trigger alerts. Each rule can have an optional threshold value."})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("h4",{className:"text-xs text-slate-400 font-medium",children:"Infrastructure"}),_.jsx(Cn,{label:"Infra Offline",description:"Alert when an infrastructure node (router/repeater) goes offline",checked:e.alert_rules.infra_offline,onChange:i=>t({...e,alert_rules:{...e.alert_rules,infra_offline:i}})}),_.jsx(Cn,{label:"Infra Recovery",description:"Alert when an offline infrastructure node comes back online",checked:e.alert_rules.infra_recovery,onChange:i=>t({...e,alert_rules:{...e.alert_rules,infra_recovery:i}})}),_.jsx(Cn,{label:"New Router",description:"Alert when a new router/repeater appears on the mesh",checked:e.alert_rules.new_router,onChange:i=>t({...e,alert_rules:{...e.alert_rules,new_router:i}})}),_.jsx(Cn,{label:"Feeder Offline",description:"Alert when a data source (MeshView/MeshMonitor) stops responding",checked:e.alert_rules.feeder_offline,onChange:i=>t({...e,alert_rules:{...e.alert_rules,feeder_offline:i}})}),_.jsx(Cn,{label:"Single Gateway",description:"Alert when an infrastructure node has only one connection path",checked:e.alert_rules.infra_single_gateway,onChange:i=>t({...e,alert_rules:{...e.alert_rules,infra_single_gateway:i}})}),_.jsx(Cn,{label:"Region Blackout",description:"Alert when all infrastructure in a region goes offline",checked:e.alert_rules.region_total_blackout,onChange:i=>t({...e,alert_rules:{...e.alert_rules,region_total_blackout:i}})})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("h4",{className:"text-xs text-slate-400 font-medium",children:"Power"}),_.jsx(Cn,{label:"Battery Warning",description:"Alert when infra node battery drops below warning threshold",checked:e.alert_rules.battery_warning,onChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_warning:i}}),threshold:e.alert_rules.battery_warning_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_warning_threshold:i}}),thresholdLabel:"Below",thresholdMin:10,thresholdMax:90,thresholdSuffix:"%"}),_.jsx(Cn,{label:"Battery Critical",description:"Alert at critical battery level",checked:e.alert_rules.battery_critical,onChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_critical:i}}),threshold:e.alert_rules.battery_critical_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_critical_threshold:i}}),thresholdLabel:"Below",thresholdMin:5,thresholdMax:50,thresholdSuffix:"%"}),_.jsx(Cn,{label:"Battery Emergency",description:"Alert at emergency battery level",checked:e.alert_rules.battery_emergency,onChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_emergency:i}}),threshold:e.alert_rules.battery_emergency_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_emergency_threshold:i}}),thresholdLabel:"Below",thresholdMin:1,thresholdMax:25,thresholdSuffix:"%"}),_.jsx(Cn,{label:"Battery Trend Declining",description:"Alert when battery shows a declining trend over 7 days",checked:e.alert_rules.battery_trend_declining,onChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_trend_declining:i}})}),_.jsx(Cn,{label:"Power Source Change",description:"Alert when a node switches between battery and USB power",checked:e.alert_rules.power_source_change,onChange:i=>t({...e,alert_rules:{...e.alert_rules,power_source_change:i}})}),_.jsx(Cn,{label:"Solar Not Charging",description:"Alert when a solar-powered node isn't charging during daylight",checked:e.alert_rules.solar_not_charging,onChange:i=>t({...e,alert_rules:{...e.alert_rules,solar_not_charging:i}})})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("h4",{className:"text-xs text-slate-400 font-medium",children:"Utilization"}),_.jsx(Cn,{label:"High Utilization",description:"Alert when channel utilization stays high for extended periods",checked:e.alert_rules.sustained_high_util,onChange:i=>t({...e,alert_rules:{...e.alert_rules,sustained_high_util:i}}),threshold:e.alert_rules.high_util_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,high_util_threshold:i}}),thresholdLabel:"Above",thresholdMin:5,thresholdMax:50,thresholdSuffix:`% for ${e.alert_rules.high_util_hours}h`}),_.jsx(Cn,{label:"Packet Flood",description:"Alert when a single node sends excessive packets",checked:e.alert_rules.packet_flood,onChange:i=>t({...e,alert_rules:{...e.alert_rules,packet_flood:i}}),threshold:e.alert_rules.packet_flood_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,packet_flood_threshold:i}}),thresholdLabel:"Over",thresholdMin:100,thresholdMax:2e3,thresholdSuffix:"pkts/24h"})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("h4",{className:"text-xs text-slate-400 font-medium",children:"Health Scores"}),_.jsx(Cn,{label:"Mesh Score Alert",description:"Alert when overall mesh health score drops below threshold",checked:e.alert_rules.mesh_score_alert,onChange:i=>t({...e,alert_rules:{...e.alert_rules,mesh_score_alert:i}}),threshold:e.alert_rules.mesh_score_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,mesh_score_threshold:i}}),thresholdLabel:"Below",thresholdMin:30,thresholdMax:90,thresholdSuffix:"/100"}),_.jsx(Cn,{label:"Region Score Alert",description:"Alert when a region's health score drops below threshold",checked:e.alert_rules.region_score_alert,onChange:i=>t({...e,alert_rules:{...e.alert_rules,region_score_alert:i}}),threshold:e.alert_rules.region_score_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,region_score_threshold:i}}),thresholdLabel:"Below",thresholdMin:30,thresholdMax:90,thresholdSuffix:"/100"})]})]})]})]})}function FJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.dashboard}),_.jsx(yr,{label:"Enable Dashboard",checked:e.enabled,onChange:r=>t({...e,enabled:r}),helper:"Run the web dashboard"}),e.enabled&&_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Host",value:e.host,onChange:r=>t({...e,host:r}),placeholder:"0.0.0.0",helper:"Network bind address",info:"0.0.0.0 = accessible from any device on the network. 127.0.0.1 = only accessible from this machine."}),_.jsx(rt,{label:"Port",value:e.port,onChange:r=>t({...e,port:r}),min:1,max:65535,helper:"Dashboard URL port",info:"Port number for the web dashboard URL. You access the dashboard at http://your-ip:port"})]})]})}function VJe(){var I;const[e,t]=U.useState(null),[r,n]=U.useState(null),[i,a]=U.useState("bot"),[o,s]=U.useState(!0),[l,u]=U.useState(!1),[c,f]=U.useState(null),[h,d]=U.useState(null),[v,g]=U.useState(!1),[m,y]=U.useState(!1),x=U.useCallback(async()=>{try{const k=await fetch("/api/config");if(!k.ok)throw new Error("Failed to fetch config");const E=await k.json();t(E),n(JSON.parse(JSON.stringify(E))),y(!1),f(null)}catch(k){f(k instanceof Error?k.message:"Unknown error")}finally{s(!1)}},[]);U.useEffect(()=>{document.title="Config — MeshAI",x()},[x]),U.useEffect(()=>{e&&r&&y(JSON.stringify(e)!==JSON.stringify(r))},[e,r]);const b=async()=>{if(e){u(!0),f(null),d(null);try{const k=e[i],E=await fetch(`/api/config/${i}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(k)}),D=await E.json();if(!E.ok)throw new Error(D.detail||"Save failed");d(`${i} saved successfully`),n(JSON.parse(JSON.stringify(e))),y(!1),D.restart_required&&g(!0),setTimeout(()=>d(null),3e3)}catch(k){f(k instanceof Error?k.message:"Save failed")}finally{u(!1)}}},S=()=>{r&&(t(JSON.parse(JSON.stringify(r))),y(!1))},T=async()=>{try{await fetch("/api/restart",{method:"POST"}),g(!1),d("Restart initiated")}catch{f("Restart failed")}},C=(k,E)=>{e&&t({...e,[k]:E})};if(o)return _.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsx("div",{className:"text-slate-400",children:"Loading configuration..."})});if(!e)return _.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsx("div",{className:"text-red-400",children:"Failed to load configuration"})});const M=()=>{switch(i){case"bot":return _.jsx(MJe,{data:e.bot,onChange:k=>C("bot",k)});case"connection":return _.jsx(PJe,{data:e.connection,onChange:k=>C("connection",k)});case"response":return _.jsx(LJe,{data:e.response,onChange:k=>C("response",k)});case"history":return _.jsx(kJe,{data:e.history,onChange:k=>C("history",k)});case"memory":return _.jsx(IJe,{data:e.memory,onChange:k=>C("memory",k)});case"context":return _.jsx(OJe,{data:e.context,onChange:k=>C("context",k)});case"commands":return _.jsx(EJe,{data:e.commands,onChange:k=>C("commands",k)});case"llm":return _.jsx(DJe,{data:e.llm,onChange:k=>C("llm",k)});case"weather":return _.jsx(NJe,{data:e.weather,onChange:k=>C("weather",k)});case"meshmonitor":return _.jsx(jJe,{data:e.meshmonitor,onChange:k=>C("meshmonitor",k)});case"knowledge":return _.jsx(RJe,{data:e.knowledge,onChange:k=>C("knowledge",k)});case"mesh_sources":return _.jsx(zJe,{data:e.mesh_sources,onChange:k=>C("mesh_sources",k)});case"mesh_intelligence":return _.jsx($Je,{data:e.mesh_intelligence,onChange:k=>C("mesh_intelligence",k)});case"dashboard":return _.jsx(FJe,{data:e.dashboard,onChange:k=>C("dashboard",k)});default:return null}},P=((I=UU.find(k=>k.key===i))==null?void 0:I.label)||i;return _.jsxs("div",{className:"flex gap-6 h-[calc(100vh-8rem)]",children:[_.jsx("div",{className:"w-48 flex-shrink-0 space-y-1",children:UU.map(({key:k,label:E,icon:D})=>_.jsxs("button",{onClick:()=>a(k),className:`w-full flex items-center gap-2 px-3 py-2 rounded text-sm transition-colors ${i===k?"bg-accent text-white":"text-slate-400 hover:text-slate-200 hover:bg-bg-hover"}`,children:[_.jsx(D,{size:16}),_.jsx("span",{children:E}),m&&i===k&&_.jsx("span",{className:"ml-auto w-2 h-2 bg-amber-500 rounded-full"})]},k))}),_.jsxs("div",{className:"flex-1 flex flex-col min-w-0",children:[_.jsxs("div",{className:"flex items-center justify-between mb-6",children:[_.jsxs("div",{className:"flex items-center gap-3",children:[_.jsx(IZ,{size:20,className:"text-slate-500"}),_.jsx("h2",{className:"text-lg font-semibold text-slate-200",children:P})]}),_.jsxs("div",{className:"flex items-center gap-2",children:[m&&_.jsxs("button",{onClick:S,className:"flex items-center gap-1.5 px-3 py-1.5 text-sm text-slate-400 hover:text-slate-200 bg-bg-hover rounded transition-colors",children:[_.jsx(QE,{size:14}),"Discard"]}),_.jsxs("button",{onClick:b,disabled:l||!m,className:"flex items-center gap-1.5 px-4 py-1.5 text-sm bg-accent text-white rounded hover:bg-accent/80 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",children:[l?_.jsx(Am,{size:14,className:"animate-spin"}):_.jsx(JE,{size:14}),"Save"]})]})]}),v&&_.jsxs("div",{className:"flex items-center justify-between p-3 mb-4 bg-amber-500/10 border border-amber-500/30 rounded-lg",children:[_.jsxs("div",{className:"flex items-center gap-2 text-amber-400",children:[_.jsx(iu,{size:16}),_.jsx("span",{className:"text-sm",children:"Restart required for changes to take effect"})]}),_.jsx("button",{onClick:T,className:"px-3 py-1 text-sm bg-amber-500 text-white rounded hover:bg-amber-600 transition-colors",children:"Restart Now"})]}),c&&_.jsxs("div",{className:"flex items-center gap-2 p-3 mb-4 bg-red-500/10 border border-red-500/30 rounded-lg text-red-400",children:[_.jsx(au,{size:16}),_.jsx("span",{className:"text-sm",children:c})]}),h&&_.jsxs("div",{className:"flex items-center gap-2 p-3 mb-4 bg-green-500/10 border border-green-500/30 rounded-lg text-green-400",children:[_.jsx(Qc,{size:16}),_.jsx("span",{className:"text-sm",children:h})]}),_.jsx("div",{className:"flex-1 overflow-y-auto pr-2",children:_.jsx("div",{className:"bg-bg-card border border-border rounded-lg p-6",children:M()})})]})]})}function GJe({feed:e}){const t=e.is_loaded?e.consecutive_errors>0?"bg-amber-500":"bg-green-500":"bg-red-500",r=e.is_loaded?e.consecutive_errors>0?`${e.consecutive_errors} errors`:"Healthy":"Not loaded",n=e.last_fetch?new Date(e.last_fetch*1e3).toLocaleTimeString():"Never";return _.jsxs("div",{className:"bg-bg-hover rounded-lg p-4",children:[_.jsxs("div",{className:"flex items-center justify-between mb-2",children:[_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx("div",{className:`w-2 h-2 rounded-full ${t}`}),_.jsx("span",{className:"text-sm font-medium text-slate-200 uppercase",children:e.source})]}),_.jsx("span",{className:"text-xs text-slate-400",children:r})]}),_.jsxs("div",{className:"text-xs text-slate-500 space-y-1",children:[_.jsxs("div",{children:["Events: ",e.event_count]}),_.jsxs("div",{children:["Last fetch: ",n]}),e.last_error&&_.jsx("div",{className:"text-amber-500 truncate",children:e.last_error})]})]})}function WJe({event:e}){const t=e.severity.toLowerCase(),r=t==="extreme"||t==="severe"||t==="immediate"?{bg:"bg-red-500/10",border:"border-red-500",Icon:xd,color:"text-red-500"}:t==="moderate"||t==="warning"||t==="priority"?{bg:"bg-amber-500/10",border:"border-amber-500",Icon:iu,color:"text-amber-500"}:{bg:"bg-blue-500/10",border:"border-blue-500",Icon:CS,color:"text-blue-500"},n=r.Icon;return _.jsx("div",{className:`p-3 rounded-lg ${r.bg} border-l-2 ${r.border}`,children:_.jsxs("div",{className:"flex items-start gap-3",children:[_.jsx(n,{size:16,className:r.color}),_.jsxs("div",{className:"flex-1 min-w-0",children:[_.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[_.jsx("span",{className:"text-sm font-medium text-slate-200",children:e.event_type}),_.jsx("span",{className:`text-xs px-1.5 py-0.5 rounded ${r.bg} ${r.color}`,children:e.severity})]}),_.jsx("div",{className:"text-sm text-slate-300",children:e.headline})]})]})})}function Zie({value:e,onChange:t,disabled:r,centralDisabled:n}){const i="px-2 py-1 text-xs transition-colors";return _.jsxs("div",{className:`flex rounded border border-[#1e2a3a] overflow-hidden ${r?"opacity-40":""}`,children:[_.jsx("button",{type:"button",disabled:r,onClick:()=>t("native"),className:`${i} ${e==="native"?"bg-accent text-white":"text-slate-400 hover:text-slate-200"}`,children:"native"}),_.jsx("button",{type:"button",disabled:r||n,title:n?"Central not available for this adapter":"",onClick:()=>{n||t("central")},className:`${i} ${n?"text-slate-600 cursor-not-allowed":e==="central"?"bg-accent text-white":"text-slate-400 hover:text-slate-200"}`,children:"central"})]})}function HJe({title:e,subtitle:t,enabled:r,onEnabled:n,feedSource:i,onFeedSource:a,hasCentral:o,nativeOnly:s,hasKey:l,health:u,events:c,children:f}){const h=s||!o;return _.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg p-4 space-y-3",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("div",{children:[_.jsx("span",{className:"text-sm font-medium text-slate-300",children:e}),t&&_.jsx("p",{className:"text-xs text-slate-600",children:t})]}),_.jsxs("div",{className:"flex items-center gap-4",children:[_.jsxs("div",{className:"flex items-center gap-1",children:[_.jsx("span",{className:"text-[10px] uppercase tracking-wide text-slate-600",children:"source"}),_.jsx(Zie,{value:i,onChange:a,disabled:!r,centralDisabled:h})]}),_.jsx(yr,{label:"",checked:r,onChange:n})]})]}),!l&&_.jsx("div",{className:"text-xs text-amber-400 bg-amber-500/10 rounded p-2",children:"API key not configured — contact admin"}),s&&_.jsx("div",{className:"text-[11px] text-slate-600",children:"Central not available for this adapter — native only"}),_.jsx("div",{className:r?"space-y-3":"space-y-3 opacity-40 pointer-events-none select-none",children:f}),(u||c&&c.length>0)&&_.jsxs("div",{className:"pt-2 border-t border-[#1e2a3a] space-y-3",children:[_.jsx("div",{className:"text-[10px] uppercase tracking-wide text-slate-600",children:"Live status"}),u?_.jsx(GJe,{feed:u}):_.jsx("div",{className:"text-xs text-slate-600",children:"No status reported."}),c&&c.length>0&&_.jsx("div",{className:"space-y-2",children:c.slice(0,5).map((d,v)=>_.jsx(WJe,{event:d},v))})]})]})}const cl={nws:{label:"NWS Weather Alerts",subtitle:"National Weather Service alerts",health:"nws",hasCentral:!0,nativeOnly:!1,hasKey:!0},fires:{label:"NIFC Fire Perimeters",subtitle:"Active wildfires (National Interagency Fire Center)",health:"nifc",hasCentral:!0,nativeOnly:!1,hasKey:!0},firms:{label:"NASA FIRMS Hotspots",subtitle:"Satellite thermal-anomaly detections",health:"firms",hasCentral:!0,nativeOnly:!1,hasKey:!1},swpc:{label:"NOAA Space Weather (SWPC)",subtitle:"Solar indices, geomagnetic storms",health:"swpc",hasCentral:!0,nativeOnly:!1,hasKey:!0},ducting:{label:"Tropospheric Ducting",subtitle:"VHF/UHF extended-range conditions",health:"ducting",hasCentral:!1,nativeOnly:!0,hasKey:!0},traffic:{label:"TomTom Traffic",subtitle:"Traffic flow on monitored corridors",health:"traffic",hasCentral:!0,nativeOnly:!1,hasKey:!0},roads511:{label:"511 Road Conditions",subtitle:"State DOT road events and closures",health:"roads511",hasCentral:!0,nativeOnly:!1,hasKey:!1},usgs_quake:{label:"USGS Earthquakes",subtitle:"Seismic events from the USGS feed",health:"usgs_quake",hasCentral:!0,nativeOnly:!1,hasKey:!0},usgs:{label:"USGS Stream Gauges",subtitle:"River and stream water levels",health:"usgs",hasCentral:!0,nativeOnly:!1,hasKey:!0},avalanche:{label:"Avalanche Advisories",subtitle:"Backcountry avalanche danger ratings",health:"avalanche",hasCentral:!1,nativeOnly:!0,hasKey:!0}},dP=[{key:"weather",label:"Weather",icon:nu,adapters:["nws"]},{key:"fire",label:"Fire",icon:TS,adapters:["fires","firms"]},{key:"rf",label:"RF Propagation",icon:Za,adapters:["swpc","ducting"]},{key:"roads",label:"Roads",icon:SS,adapters:["traffic","roads511"]},{key:"geohazards",label:"Geohazards",icon:AS,adapters:["usgs_quake","usgs","avalanche"]},{key:"tracking",label:"Tracking",icon:PS,adapters:[]},{key:"mesh",label:"Mesh Health",icon:gv,adapters:[]}];function UJe(){var F,G;const[e,t]=U.useState(null),[r,n]=U.useState(""),[i,a]=U.useState(null),[o,s]=U.useState([]),[l,u]=U.useState(!0),[c,f]=U.useState(!1),[h,d]=U.useState(null),[v,g]=U.useState(null),[m,y]=U.useState(!1),[x,b]=U.useState("weather"),[S,T]=U.useState("nws");U.useEffect(()=>{document.title="Environment — MeshAI",(async()=>{try{const H=await(await fetch("/api/config/environmental")).json();t(H),n(JSON.stringify(H))}catch(V){d(V instanceof Error?V.message:"Failed to load config")}finally{u(!1)}})()},[]),U.useEffect(()=>{const V=async()=>{try{a(await jZ()),s(await RZ())}catch{}};V();const H=setInterval(V,3e4);return()=>clearInterval(H)},[]);const C=e!==null&&JSON.stringify(e)!==r,M=async()=>{if(e){f(!0),d(null),g(null);try{const V=await fetch("/api/config/environmental",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),H=await V.json();if(!V.ok)throw new Error(H.detail||"Save failed");n(JSON.stringify(e)),g("Environmental config saved"),H.restart_required&&y(!0),setTimeout(()=>g(null),3e3)}catch(V){d(V instanceof Error?V.message:"Save failed")}finally{f(!1)}}},P=()=>{e&&t(JSON.parse(r))},I=async()=>{try{await fetch("/api/restart",{method:"POST"}),y(!1),g("Restart initiated")}catch{d("Restart failed")}},k=V=>e&&t({...e,...V});if(l)return _.jsx("div",{className:"flex items-center justify-center h-64 text-slate-400",children:"Loading environmental config…"});if(!e)return _.jsx("div",{className:"flex items-center justify-center h-64 text-red-400",children:h||"No config"});const E=V=>i==null?void 0:i.feeds.find(H=>H.source===cl[V].health),D=V=>o.filter(H=>H.source===cl[V].health),j=dP.find(V=>V.key===x),N=j.adapters.length===0?null:S&&j.adapters.includes(S)?S:j.adapters[0],z=V=>{switch(V){case"nws":return _.jsxs(_.Fragment,{children:[_.jsx(Yh,{label:"NWS Zones",value:e.nws_zones,onChange:H=>k({nws_zones:H}),helper:"Zone IDs like IDZ016, IDZ030",infoLink:"https://www.weather.gov/pimar/PubZone"}),_.jsx(_t,{label:"User Agent",value:e.nws.user_agent,onChange:H=>k({nws:{...e.nws,user_agent:H}}),placeholder:"(MeshAI, you@email.com)",helper:"Format: (app_name, contact_email)"}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Tick Seconds",value:e.nws.tick_seconds,onChange:H=>k({nws:{...e.nws,tick_seconds:H}}),min:30}),_.jsx(Po,{label:"Min Severity",value:e.nws.severity_min,onChange:H=>k({nws:{...e.nws,severity_min:H}}),options:[{value:"minor",label:"Minor"},{value:"moderate",label:"Moderate"},{value:"severe",label:"Severe"},{value:"extreme",label:"Extreme"}]})]})]});case"swpc":return _.jsx("div",{className:"text-xs text-slate-500",children:"No additional settings."});case"ducting":return _.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[_.jsx(rt,{label:"Tick Seconds",value:e.ducting.tick_seconds,onChange:H=>k({ducting:{...e.ducting,tick_seconds:H}}),min:60}),_.jsx(rt,{label:"Latitude",value:e.ducting.latitude,onChange:H=>k({ducting:{...e.ducting,latitude:H}}),step:.01}),_.jsx(rt,{label:"Longitude",value:e.ducting.longitude,onChange:H=>k({ducting:{...e.ducting,longitude:H}}),step:.01})]});case"fires":return _.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Tick Seconds",value:e.fires.tick_seconds,onChange:H=>k({fires:{...e.fires,tick_seconds:H}}),min:60}),_.jsx(Po,{label:"State",value:e.fires.state,onChange:H=>k({fires:{...e.fires,state:H}}),options:TJe})]});case"avalanche":return _.jsxs(_.Fragment,{children:[_.jsx(rt,{label:"Tick Seconds",value:e.avalanche.tick_seconds,onChange:H=>k({avalanche:{...e.avalanche,tick_seconds:H}}),min:60}),_.jsx(Yh,{label:"Center IDs",value:e.avalanche.center_ids,onChange:H=>k({avalanche:{...e.avalanche,center_ids:H}}),helper:"e.g., SNFAC",infoLink:"https://avalanche.org/avalanche-centers/"}),_.jsx(AJe,{label:"Season Months",value:e.avalanche.season_months,onChange:H=>k({avalanche:{...e.avalanche,season_months:H}}),helper:"e.g., 12, 1, 2, 3, 4"})]});case"usgs":return _.jsxs(_.Fragment,{children:[_.jsx(rt,{label:"Tick Seconds",value:e.usgs.tick_seconds,onChange:H=>k({usgs:{...e.usgs,tick_seconds:H}}),min:900,helper:"Minimum 15 min (900s)"}),_.jsx(Yh,{label:"Site IDs",value:e.usgs.sites,onChange:H=>k({usgs:{...e.usgs,sites:H}}),helper:"USGS gauge site numbers",infoLink:"https://waterdata.usgs.gov/nwis"})]});case"usgs_quake":return _.jsxs(_.Fragment,{children:[_.jsx(rt,{label:"Tick Seconds",value:e.usgs_quake.tick_seconds,onChange:H=>k({usgs_quake:{...e.usgs_quake,tick_seconds:H}}),min:60}),_.jsx(rt,{label:"Min Magnitude",value:e.usgs_quake.min_magnitude,onChange:H=>k({usgs_quake:{...e.usgs_quake,min_magnitude:H}}),step:.1,min:0}),_.jsx(_t,{label:"Region Tag",value:e.usgs_quake.region,onChange:H=>k({usgs_quake:{...e.usgs_quake,region:H}})}),_.jsx("div",{className:"grid grid-cols-4 gap-2",children:["West","South","East","North"].map((H,Y)=>{var K;return _.jsx(rt,{label:H,value:((K=e.usgs_quake.bbox)==null?void 0:K[Y])??0,onChange:ee=>{const le=[...e.usgs_quake.bbox||[0,0,0,0]];le[Y]=ee,k({usgs_quake:{...e.usgs_quake,bbox:le}})},step:.01},H)})}),_.jsx("div",{className:"text-xs text-slate-500",children:"Bounding box [W,S,E,N] geographic filter"})]});case"traffic":return _.jsxs(_.Fragment,{children:[_.jsx(_t,{label:"API Key",value:e.traffic.api_key,onChange:H=>k({traffic:{...e.traffic,api_key:H}}),type:"password",helper:"developer.tomtom.com"}),_.jsx(rt,{label:"Tick Seconds",value:e.traffic.tick_seconds,onChange:H=>k({traffic:{...e.traffic,tick_seconds:H}}),min:60}),_.jsx("div",{className:"text-xs text-slate-500 mt-2",children:"Corridors:"}),(e.traffic.corridors||[]).map((H,Y)=>_.jsxs("div",{className:"grid grid-cols-4 gap-2 items-end",children:[_.jsx(_t,{label:"Name",value:H.name,onChange:K=>{const ee=[...e.traffic.corridors];ee[Y]={...H,name:K},k({traffic:{...e.traffic,corridors:ee}})}}),_.jsx(rt,{label:"Lat",value:H.lat,onChange:K=>{const ee=[...e.traffic.corridors];ee[Y]={...H,lat:K},k({traffic:{...e.traffic,corridors:ee}})},step:.01}),_.jsx(rt,{label:"Lon",value:H.lon,onChange:K=>{const ee=[...e.traffic.corridors];ee[Y]={...H,lon:K},k({traffic:{...e.traffic,corridors:ee}})},step:.01}),_.jsx("button",{onClick:()=>k({traffic:{...e.traffic,corridors:e.traffic.corridors.filter((K,ee)=>ee!==Y)}}),className:"px-2 py-2 text-xs text-red-400 hover:text-red-300 border border-red-400/30 rounded",children:"Remove"})]},Y)),_.jsx("button",{onClick:()=>k({traffic:{...e.traffic,corridors:[...e.traffic.corridors||[],{name:"",lat:0,lon:0}]}}),className:"text-xs text-accent hover:underline",children:"+ Add Corridor"})]});case"roads511":return _.jsxs(_.Fragment,{children:[_.jsx(_t,{label:"Base URL",value:e.roads511.base_url,onChange:H=>k({roads511:{...e.roads511,base_url:H}}),placeholder:"https://511.yourstate.gov/api/v2"}),_.jsx(_t,{label:"API Key",value:e.roads511.api_key,onChange:H=>k({roads511:{...e.roads511,api_key:H}}),type:"password",helper:"Leave empty if not required"}),_.jsx(rt,{label:"Tick Seconds",value:e.roads511.tick_seconds,onChange:H=>k({roads511:{...e.roads511,tick_seconds:H}}),min:60}),_.jsx(Yh,{label:"Endpoints",value:e.roads511.endpoints,onChange:H=>k({roads511:{...e.roads511,endpoints:H}}),helper:"e.g., /get/event"}),_.jsx("div",{className:"grid grid-cols-4 gap-2",children:["West","South","East","North"].map((H,Y)=>{var K;return _.jsx(rt,{label:H,value:((K=e.roads511.bbox)==null?void 0:K[Y])??0,onChange:ee=>{const le=[...e.roads511.bbox||[0,0,0,0]];le[Y]=ee,k({roads511:{...e.roads511,bbox:le}})},step:.01},H)})})]});case"firms":return _.jsxs(_.Fragment,{children:[_.jsx(_t,{label:"MAP Key",value:e.firms.map_key,onChange:H=>k({firms:{...e.firms,map_key:H}}),type:"password",helper:"firms.modaps.eosdis.nasa.gov/api/area/",infoLink:"https://firms.modaps.eosdis.nasa.gov/api/area/"}),_.jsx(rt,{label:"Tick Seconds",value:e.firms.tick_seconds,onChange:H=>k({firms:{...e.firms,tick_seconds:H}}),min:300}),_.jsx(Po,{label:"Satellite Source",value:e.firms.source,onChange:H=>k({firms:{...e.firms,source:H}}),options:[{value:"VIIRS_SNPP_NRT",label:"VIIRS SNPP (NRT)"},{value:"VIIRS_NOAA20_NRT",label:"VIIRS NOAA-20 (NRT)"},{value:"MODIS_NRT",label:"MODIS (NRT)"}]}),_.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[_.jsx(rt,{label:"Day Range",value:e.firms.day_range,onChange:H=>k({firms:{...e.firms,day_range:H}}),min:1,max:10}),_.jsx(Po,{label:"Min Confidence",value:e.firms.confidence_min,onChange:H=>k({firms:{...e.firms,confidence_min:H}}),options:[{value:"low",label:"Low"},{value:"nominal",label:"Nominal"},{value:"high",label:"High"}]}),_.jsx(rt,{label:"Proximity (km)",value:e.firms.proximity_km,onChange:H=>k({firms:{...e.firms,proximity_km:H}}),step:.5})]}),_.jsx("div",{className:"grid grid-cols-4 gap-2",children:["West","South","East","North"].map((H,Y)=>{var K;return _.jsx(rt,{label:H,value:((K=e.firms.bbox)==null?void 0:K[Y])??0,onChange:ee=>{const le=[...e.firms.bbox||[0,0,0,0]];le[Y]=ee,k({firms:{...e.firms,bbox:le}})},step:.01},H)})})]})}},$=e,Z=(V,H)=>{const Y=e[V]||{};k({[V]:{...Y,...H}})};return _.jsxs("div",{className:"space-y-6",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsx("h1",{className:"text-xl font-semibold text-slate-200",children:"Environment"}),_.jsxs("div",{className:"flex items-center gap-3",children:[_.jsx(yr,{label:"Feeds Enabled",checked:e.enabled,onChange:V=>k({enabled:V})}),C&&_.jsxs(_.Fragment,{children:[_.jsxs("button",{onClick:P,className:"flex items-center gap-1 px-3 py-1.5 text-sm text-slate-400 hover:text-slate-200 border border-border rounded",children:[_.jsx(QE,{size:14})," Discard"]}),_.jsxs("button",{onClick:M,disabled:c,className:"flex items-center gap-1 px-3 py-1.5 text-sm bg-accent text-white rounded disabled:opacity-50",children:[_.jsx(JE,{size:14})," ",c?"Saving…":"Save"]})]})]})]}),h&&_.jsx("div",{className:"text-sm text-red-400 bg-red-500/10 rounded p-3",children:h}),v&&_.jsx("div",{className:"text-sm text-green-400 bg-green-500/10 rounded p-3",children:v}),m&&_.jsxs("div",{className:"flex items-center justify-between text-sm text-amber-400 bg-amber-500/10 border border-amber-500/30 rounded p-3",children:[_.jsxs("span",{className:"flex items-center gap-2",children:[_.jsx(Am,{size:14})," A restart is required for some changes to take effect."]}),_.jsx("button",{onClick:I,className:"px-3 py-1 bg-amber-500/20 hover:bg-amber-500/30 rounded",children:"Restart now"})]}),e.central&&_.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg p-4 space-y-3",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("div",{children:[_.jsx("span",{className:"text-sm font-medium text-slate-300",children:"Central Connection"}),_.jsx("p",{className:"text-xs text-slate-600",children:'NATS JetStream source for any adapter set to "central"'})]}),_.jsx(yr,{label:"",checked:!!e.central.enabled,onChange:V=>k({central:{...e.central,enabled:V}})})]}),_.jsxs("div",{className:e.central.enabled?"space-y-3":"space-y-3 opacity-40 pointer-events-none select-none",children:[_.jsx(_t,{label:"URL",value:e.central.url||"",onChange:V=>k({central:{...e.central,url:V}}),placeholder:"nats://central.echo6.mesh:4222"}),_.jsx(_t,{label:"Durable",value:e.central.durable||"",onChange:V=>k({central:{...e.central,durable:V}}),placeholder:"meshai-v04"})]})]}),_.jsx("div",{className:"flex gap-1 border-b border-border overflow-x-auto",children:dP.map(({key:V,label:H,icon:Y})=>_.jsxs("button",{onClick:()=>{b(V);const K=dP.find(ee=>ee.key===V);T(K.adapters[0]??null)},className:`flex items-center gap-2 px-4 py-2 text-sm whitespace-nowrap border-b-2 -mb-px transition-colors ${x===V?"border-accent text-accent":"border-transparent text-slate-400 hover:text-slate-200"}`,children:[_.jsx(Y,{size:15})," ",H]},V))}),x==="tracking"&&_.jsxs("div",{className:"flex flex-col items-center justify-center h-[40vh] text-center",children:[_.jsx(PS,{size:32,className:"text-slate-600 mb-4"}),_.jsx("p",{className:"text-slate-500 max-w-md",children:"No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5."})]}),x==="mesh"&&_.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg p-4 space-y-3",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("div",{children:[_.jsx("span",{className:"text-sm font-medium text-slate-300",children:"Mesh Health"}),_.jsx("p",{className:"text-xs text-slate-600",children:"Node/infra telemetry — sourced from the mesh, not an environmental feed."})]}),_.jsxs("div",{className:"flex items-center gap-1",children:[_.jsx("span",{className:"text-[10px] uppercase tracking-wide text-slate-600",children:"source"}),_.jsx(Zie,{value:"native",onChange:()=>{},disabled:!1,centralDisabled:!0})]})]}),_.jsx("div",{className:"text-[11px] text-slate-600",children:"Central not available — reserved for a future migration."})]}),j.adapters.length>0&&N&&_.jsxs(_.Fragment,{children:[j.adapters.length>1&&_.jsx("div",{className:"flex gap-1",children:j.adapters.map(V=>_.jsx("button",{onClick:()=>T(V),className:`px-3 py-1.5 text-sm rounded ${N===V?"bg-bg-hover text-slate-100":"text-slate-400 hover:text-slate-200"}`,children:cl[V].label},V))}),_.jsx(HJe,{title:cl[N].label,subtitle:cl[N].subtitle,enabled:((F=$[N])==null?void 0:F.enabled)??!1,onEnabled:V=>Z(N,{enabled:V}),feedSource:((G=$[N])==null?void 0:G.feed_source)??"native",onFeedSource:V=>Z(N,{feed_source:V}),hasCentral:cl[N].hasCentral,nativeOnly:cl[N].nativeOnly,hasKey:cl[N].hasKey,health:E(N),events:D(N),children:z(N)})]})]})}const ZU={infra_offline:DZ,infra_recovery:LS,battery_warning:uA,battery_critical:uA,battery_emergency:uA,hf_blackout:Mm,uhf_ducting:Za,weather_warning:nu,weather_watch:nu,new_router:Za,packet_flood:iu,sustained_high_util:iu,region_blackout:xd,default:Cm};function ZJe(e){return ZU[e]||ZU.default}function Yie(e){switch(e==null?void 0:e.toLowerCase()){case"immediate":return{bg:"bg-red-500/10",border:"border-red-500",badge:"bg-red-500/20 text-red-400",iconColor:"text-red-500"};case"priority":return{bg:"bg-amber-500/10",border:"border-amber-500",badge:"bg-amber-500/20 text-amber-400",iconColor:"text-amber-500"};case"routine":default:return{bg:"bg-blue-500/10",border:"border-blue-500",badge:"bg-blue-500/20 text-blue-400",iconColor:"text-blue-500"}}}function YJe(e){const t=typeof e=="number"?new Date(e*1e3):new Date(e),n=new Date().getTime()-t.getTime(),i=Math.floor(n/1e3),a=Math.floor(i/60),o=Math.floor(a/60),s=Math.floor(o/24);return i<60?"Just now":a<60?`${a}m ago`:o<24?`${o}h ago`:`${s}d ago`}function XJe(e){return(typeof e=="number"?new Date(e*1e3):new Date(e)).toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1})}function qJe(e){return e<60?`${e}s`:e<3600?`${Math.floor(e/60)}m`:e<86400?`${Math.floor(e/3600)}h ${Math.floor(e%3600/60)}m`:`${Math.floor(e/86400)}d`}function KJe({alert:e,onAcknowledge:t}){var i;const r=Yie(e.severity),n=ZJe(e.type);return _.jsx("div",{className:`p-4 rounded-lg ${r.bg} border-l-4 ${r.border}`,children:_.jsxs("div",{className:"flex items-start gap-3",children:[_.jsx(n,{size:20,className:r.iconColor}),_.jsxs("div",{className:"flex-1 min-w-0",children:[_.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[_.jsx("span",{className:`text-xs px-2 py-0.5 rounded-full ${r.badge}`,children:(i=e.severity)==null?void 0:i.toUpperCase()}),_.jsx("span",{className:"text-xs text-slate-500",children:e.type})]}),_.jsx("div",{className:"text-sm text-slate-200",children:e.message}),_.jsxs("div",{className:"flex items-center gap-4 mt-2 text-xs text-slate-500",children:[_.jsxs("span",{className:"flex items-center gap-1",children:[_.jsx(bd,{size:12}),e.timestamp?YJe(e.timestamp):"Just now"]}),e.scope_value&&_.jsxs("span",{children:[e.scope_type,": ",e.scope_value]})]})]}),_.jsx("button",{onClick:()=>t(e),className:"px-3 py-1 text-xs text-slate-400 hover:text-slate-200 border border-border rounded hover:bg-bg-hover transition-colors",children:"Acknowledge"})]})})}function QJe({history:e,typeFilter:t,severityFilter:r,onTypeFilterChange:n,onSeverityFilterChange:i,page:a,totalPages:o,onPageChange:s}){const l=["all","infra_offline","infra_recovery","battery_warning","battery_critical","hf_blackout","uhf_ducting","weather_warning","new_router","packet_flood"],u=["all","immediate","priority","routine"];return _.jsxs("div",{className:"bg-bg-card border border-border rounded-lg",children:[_.jsxs("div",{className:"p-4 border-b border-border flex items-center gap-4",children:[_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx(qE,{size:14,className:"text-slate-400"}),_.jsx("span",{className:"text-sm text-slate-400",children:"Filter:"})]}),_.jsx("select",{value:t,onChange:c=>n(c.target.value),className:"bg-bg border border-border rounded px-3 py-1.5 text-sm text-slate-200 focus:outline-none focus:border-blue-500",children:l.map(c=>_.jsx("option",{value:c,children:c==="all"?"All Types":c.replace(/_/g," ")},c))}),_.jsx("select",{value:r,onChange:c=>i(c.target.value),className:"bg-bg border border-border rounded px-3 py-1.5 text-sm text-slate-200 focus:outline-none focus:border-blue-500",children:u.map(c=>_.jsx("option",{value:c,children:c==="all"?"All Severities":c.charAt(0).toUpperCase()+c.slice(1)},c))})]}),_.jsx("div",{className:"overflow-x-auto",children:_.jsxs("table",{className:"w-full",children:[_.jsx("thead",{children:_.jsxs("tr",{className:"border-b border-border",children:[_.jsx("th",{className:"text-left text-xs font-medium text-slate-400 p-4",children:"Time"}),_.jsx("th",{className:"text-left text-xs font-medium text-slate-400 p-4",children:"Type"}),_.jsx("th",{className:"text-left text-xs font-medium text-slate-400 p-4",children:"Severity"}),_.jsx("th",{className:"text-left text-xs font-medium text-slate-400 p-4",children:"Message"}),_.jsx("th",{className:"text-left text-xs font-medium text-slate-400 p-4",children:"Duration"})]})}),_.jsx("tbody",{children:e.length>0?e.map((c,f)=>{const h=Yie(c.severity);return _.jsxs("tr",{className:"border-b border-border hover:bg-bg-hover",children:[_.jsx("td",{className:"p-4 text-sm text-slate-400 font-mono whitespace-nowrap",children:XJe(c.timestamp)}),_.jsx("td",{className:"p-4 text-sm text-slate-300",children:c.type.replace(/_/g," ")}),_.jsx("td",{className:"p-4",children:_.jsx("span",{className:`text-xs px-2 py-0.5 rounded-full ${h.badge}`,children:c.severity})}),_.jsx("td",{className:"p-4 text-sm text-slate-200 max-w-md truncate",children:c.message}),_.jsx("td",{className:"p-4 text-sm text-slate-400 font-mono",children:c.duration?qJe(c.duration):"-"})]},c.id||f)}):_.jsx("tr",{children:_.jsx("td",{colSpan:5,className:"p-8 text-center text-slate-500",children:"No alert history available"})})})]})}),o>1&&_.jsxs("div",{className:"p-4 border-t border-border flex items-center justify-between",children:[_.jsxs("span",{className:"text-sm text-slate-400",children:["Page ",a," of ",o]}),_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx("button",{onClick:()=>s(a-1),disabled:a<=1,className:"p-2 text-slate-400 hover:text-slate-200 disabled:opacity-50 disabled:cursor-not-allowed",children:_.jsx(Jue,{size:16})}),_.jsx("button",{onClick:()=>s(a+1),disabled:a>=o,className:"p-2 text-slate-400 hover:text-slate-200 disabled:opacity-50 disabled:cursor-not-allowed",children:_.jsx(_d,{size:16})})]})]})]})}function JJe({subscription:e,nodes:t}){const r=o=>{const s=t.find(l=>l.node_id_hex===o||String(l.node_num)===o||l.short_name===o);return s?s.long_name&&s.long_name!==s.short_name?`${s.short_name} (${s.long_name})`:s.short_name:o},n=()=>{if(e.sub_type==="alerts")return"Real-time";const o=e.schedule_time||"0000",s=parseInt(o.slice(0,2)),l=o.slice(2),u=s>=12?"PM":"AM";let f=`${s%12||12}:${l} ${u}`;return e.sub_type==="weekly"&&e.schedule_day&&(f+=` ${e.schedule_day.charAt(0).toUpperCase()}${e.schedule_day.slice(1)}`),f},a=(()=>{switch(e.sub_type){case"alerts":return Cm;case"daily":return bd;case"weekly":return bd;default:return Cm}})();return _.jsx("div",{className:"p-4 rounded-lg bg-bg-hover border border-border",children:_.jsxs("div",{className:"flex items-center gap-3",children:[_.jsx("div",{className:"w-10 h-10 rounded-lg bg-blue-500/10 flex items-center justify-center",children:_.jsx(a,{size:18,className:"text-blue-400"})}),_.jsxs("div",{className:"flex-1",children:[_.jsxs("div",{className:"text-sm text-slate-200 font-medium",children:[e.sub_type.charAt(0).toUpperCase()+e.sub_type.slice(1),e.scope_type!=="mesh"&&e.scope_value&&_.jsxs("span",{className:"text-slate-400 font-normal ml-2",children:["(",e.scope_type,": ",e.scope_value,")"]})]}),_.jsxs("div",{className:"text-xs text-slate-500 mt-0.5",children:[n()," • ",r(e.user_id)]})]}),_.jsx("div",{className:`w-2 h-2 rounded-full ${e.enabled?"bg-green-500":"bg-slate-500"}`})]})})}function eet(){const[e,t]=U.useState([]),[r,n]=U.useState([]),[i,a]=U.useState([]),[o,s]=U.useState([]),[l,u]=U.useState(!0),[c,f]=U.useState(null),[h,d]=U.useState("all"),[v,g]=U.useState("all"),[m,y]=U.useState(1),[x,b]=U.useState(1),S=20,[T,C]=U.useState(new Set),{lastAlert:M}=rD();U.useEffect(()=>{document.title="Alerts — MeshAI"},[]),U.useEffect(()=>{Promise.all([NZ().catch(()=>[]),m3(S,0).catch(()=>({items:[],total:0})),gce().catch(()=>[]),fetch("/api/nodes").then(k=>k.json()).catch(()=>[])]).then(([k,E,D,j])=>{t(k),Array.isArray(E)?(n(E),b(1)):(n(E.items||[]),b(Math.ceil((E.total||0)/S))),a(D),s(j),u(!1)}).catch(k=>{f(k.message),u(!1)})},[]),U.useEffect(()=>{M&&t(k=>k.some(D=>D.type===M.type&&D.message===M.message)?k:[M,...k])},[M]),U.useEffect(()=>{const k=(m-1)*S;m3(S,k,h,v).then(E=>{Array.isArray(E)?(n(E),b(1)):(n(E.items||[]),b(Math.ceil((E.total||0)/S)))}).catch(()=>{})},[m,h,v]);const P=U.useCallback(k=>{const E=`${k.type}-${k.message}-${k.timestamp}`;C(D=>new Set([...D,E]))},[]),I=e.filter(k=>{const E=`${k.type}-${k.message}-${k.timestamp}`;return!T.has(E)});return l?_.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsx("div",{className:"text-slate-400",children:"Loading alerts..."})}):c?_.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsxs("div",{className:"text-red-400",children:["Error: ",c]})}):_.jsxs("div",{className:"space-y-6",children:[_.jsxs("div",{className:"bg-bg-card border border-border rounded-lg p-6",children:[_.jsxs("h2",{className:"text-sm font-medium text-slate-400 mb-4 flex items-center gap-2",children:[_.jsx(iu,{size:14}),"Active Alerts (",I.length,")"]}),I.length>0?_.jsx("div",{className:"space-y-3",children:I.map((k,E)=>_.jsx(KJe,{alert:k,onAcknowledge:P},`${k.type}-${k.timestamp}-${E}`))}):_.jsxs("div",{className:"flex items-center gap-2 text-slate-500 py-8",children:[_.jsx(YE,{size:20,className:"text-green-500"}),_.jsx("span",{children:"No active alerts — all systems nominal"})]})]}),_.jsxs("div",{children:[_.jsxs("h2",{className:"text-sm font-medium text-slate-400 mb-4 flex items-center gap-2",children:[_.jsx(bd,{size:14}),"Alert History"]}),_.jsx(QJe,{history:r,typeFilter:h,severityFilter:v,onTypeFilterChange:k=>{d(k),y(1)},onSeverityFilterChange:k=>{g(k),y(1)},page:m,totalPages:x,onPageChange:y})]}),_.jsxs("div",{className:"bg-bg-card border border-border rounded-lg p-6",children:[_.jsxs("h2",{className:"text-sm font-medium text-slate-400 mb-4 flex items-center gap-2",children:[_.jsx(fce,{size:14}),"Mesh Subscriptions (",i.length,")"]}),i.length>0?_.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3",children:i.map(k=>_.jsx(JJe,{subscription:k,nodes:o},k.id))}):_.jsxs("div",{className:"text-slate-500 py-4",children:[_.jsx("p",{children:"No active subscriptions."}),_.jsxs("p",{className:"text-xs mt-2",children:["Manage subscriptions via ",_.jsx("code",{className:"text-blue-400",children:"!subscribe"})," on mesh"]})]})]})]})}const yb=[{value:"routine",label:"Routine",description:"Informational, no time pressure (ducting, new node, weather advisory, battery declining)"},{value:"priority",label:"Priority",description:"Needs attention soon (severe weather, fire nearby, node offline, HF blackout)"},{value:"immediate",label:"Immediate",description:"Act now, drop everything (fire at infrastructure, extreme weather, region blackout)"}],YU=[{id:"mesh_health",name:"Mesh Health Monitoring",description:"Infrastructure problems - offline nodes, low battery, channel congestion",rule:{name:"Mesh Health Monitoring",enabled:!0,trigger_type:"condition",categories:["infra_offline","critical_node_down","infra_recovery","battery_warning","battery_critical","battery_emergency","high_utilization","packet_flood","mesh_score_low"],min_severity:"routine",delivery_type:"mesh_broadcast",broadcast_channel:0,cooldown_minutes:30,override_quiet:!1,schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"",schedule_days:[],message_type:"",custom_message:"",node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{}}},{id:"weather_fire",name:"Weather & Fire Alerts",description:"Environmental threats - severe weather, nearby wildfires, new ignitions, flooding",rule:{name:"Weather & Fire Alerts",enabled:!0,trigger_type:"condition",categories:["weather_warning","fire_proximity","new_ignition","stream_flood_warning"],min_severity:"priority",delivery_type:"mesh_broadcast",broadcast_channel:0,cooldown_minutes:15,override_quiet:!1,schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"",schedule_days:[],message_type:"",custom_message:"",node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{}}},{id:"rf_conditions",name:"RF Conditions",description:"Propagation changes - solar events, HF blackouts, tropospheric ducting",rule:{name:"RF Conditions",enabled:!0,trigger_type:"condition",categories:["hf_blackout","tropospheric_ducting","geomagnetic_storm"],min_severity:"routine",delivery_type:"mesh_broadcast",broadcast_channel:0,cooldown_minutes:60,override_quiet:!1,schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"",schedule_days:[],message_type:"",custom_message:"",node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{}}},{id:"road_traffic",name:"Road & Traffic",description:"Road closures and severe congestion",rule:{name:"Road & Traffic",enabled:!0,trigger_type:"condition",categories:["road_closure","traffic_congestion"],min_severity:"routine",delivery_type:"mesh_broadcast",broadcast_channel:0,cooldown_minutes:30,override_quiet:!1,schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"",schedule_days:[],message_type:"",custom_message:"",node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{}}},{id:"everything_critical",name:"Everything Critical",description:"All emergency-level events regardless of type",rule:{name:"Everything Critical",enabled:!0,trigger_type:"condition",categories:[],min_severity:"immediate",delivery_type:"mesh_broadcast",broadcast_channel:0,cooldown_minutes:5,override_quiet:!0,schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"",schedule_days:[],message_type:"",custom_message:"",node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{}}},{id:"morning_briefing",name:"Morning Briefing",description:"Daily health and conditions summary at 7am",rule:{name:"Morning Briefing",enabled:!0,trigger_type:"schedule",categories:[],min_severity:"routine",schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"",schedule_days:[],message_type:"mesh_health_summary",custom_message:"",delivery_type:"mesh_broadcast",broadcast_channel:0,cooldown_minutes:0,override_quiet:!1,node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{}}}];function vP(e){if(!e)return"Never";const r=Date.now()/1e3-e;return r<60?"Just now":r<3600?`${Math.floor(r/60)}m ago`:r<86400?`${Math.floor(r/3600)}h ago`:r<604800?`${Math.floor(r/86400)}d ago`:new Date(e*1e3).toLocaleDateString()}function Ha({info:e}){const[t,r]=U.useState(!1);return _.jsxs("div",{className:"relative inline-block",children:[_.jsx("button",{type:"button",onClick:n=>{n.stopPropagation(),r(!t)},className:"ml-1.5 w-4 h-4 rounded-full bg-slate-700 hover:bg-slate-600 text-slate-400 hover:text-slate-200 inline-flex items-center justify-center text-xs transition-colors",title:"More info",children:"?"}),t&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"fixed inset-0 z-40",onClick:()=>r(!1)}),_.jsx("div",{className:"absolute left-0 top-6 z-50 w-72 p-3 bg-[#1a2332] border border-[#2a3a4a] rounded-lg shadow-xl text-xs text-slate-300 leading-relaxed",children:e})]})]})}function bl({label:e,value:t,onChange:r,type:n="text",placeholder:i="",helper:a="",info:o=""}){const[s,l]=U.useState(!1),u=n==="password";return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,o&&_.jsx(Ha,{info:o})]}),_.jsxs("div",{className:"relative",children:[_.jsx("input",{type:u&&!s?"password":"text",value:t,onChange:c=>r(c.target.value),placeholder:i,className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent placeholder-slate-600"}),u&&_.jsx("button",{type:"button",onClick:()=>l(!s),className:"absolute right-2 top-1/2 -translate-y-1/2 text-slate-500 hover:text-slate-300",children:s?_.jsx(MZ,{size:16}):_.jsx(XE,{size:16})})]}),a&&_.jsx("p",{className:"text-xs text-slate-600",children:a})]})}function iS({label:e,value:t,onChange:r,min:n,max:i,step:a=1,helper:o="",info:s=""}){return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,s&&_.jsx(Ha,{info:s})]}),_.jsx("input",{type:"number",value:t,onChange:l=>r(Number(l.target.value)),min:n,max:i,step:a,className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent"}),o&&_.jsx("p",{className:"text-xs text-slate-600",children:o})]})}function cv({label:e,checked:t,onChange:r,helper:n="",info:i=""}){return _.jsxs("div",{className:"flex items-center justify-between py-2",children:[_.jsxs("div",{children:[_.jsxs("span",{className:"flex items-center text-sm text-slate-300",children:[e,i&&_.jsx(Ha,{info:i})]}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]}),_.jsx("button",{type:"button",onClick:()=>r(!t),className:`relative w-11 h-6 rounded-full transition-colors ${t?"bg-accent":"bg-[#1e2a3a]"}`,children:_.jsx("span",{className:`absolute top-1 left-1 w-4 h-4 rounded-full bg-white transition-transform ${t?"translate-x-5":""}`})})]})}function aS({label:e,value:t,onChange:r,helper:n="",info:i=""}){return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,i&&_.jsx(Ha,{info:i})]}),_.jsx("input",{type:"time",value:t,onChange:a=>r(a.target.value),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent"}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]})}function _b({label:e,value:t,onChange:r,placeholder:n="Add item...",helper:i="",info:a=""}){const[o,s]=U.useState(""),l=()=>{o.trim()&&!t.includes(o.trim())&&(r([...t,o.trim()]),s(""))},u=c=>{r(t.filter((f,h)=>h!==c))};return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,a&&_.jsx(Ha,{info:a})]}),_.jsxs("div",{className:"flex gap-2",children:[_.jsx("input",{type:"text",value:o,onChange:c=>s(c.target.value),onKeyDown:c=>c.key==="Enter"&&(c.preventDefault(),l()),className:"flex-1 px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent",placeholder:n}),_.jsx("button",{type:"button",onClick:l,className:"px-3 py-2 bg-accent hover:bg-accent/80 rounded text-sm text-white transition-colors",children:_.jsx(MS,{size:16})})]}),t.length>0&&_.jsx("div",{className:"flex flex-wrap gap-2 mt-2",children:t.map((c,f)=>_.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-1 bg-[#1e2a3a] rounded text-sm text-slate-300",children:[c,_.jsx("button",{type:"button",onClick:()=>u(f),className:"text-slate-500 hover:text-red-400",children:_.jsx(au,{size:14})})]},f))}),i&&_.jsx("p",{className:"text-xs text-slate-600",children:i})]})}function Xie({value:e,onChange:t}){const[r,n]=U.useState(!1),i=yb.find(a=>a.value===e)||yb[0];return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Severity Threshold",_.jsx(Ha,{info:"Only alerts at or above this severity trigger this rule. ROUTINE = informational, PRIORITY = needs attention, IMMEDIATE = act now."})]}),_.jsxs("div",{className:"relative",children:[_.jsxs("button",{type:"button",onClick:()=>n(!r),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-left flex items-center justify-between hover:border-accent transition-colors",children:[_.jsxs("div",{children:[_.jsx("span",{className:"text-slate-200",children:i.label}),_.jsxs("span",{className:"text-slate-500 ml-2",children:["- ",i.description]})]}),_.jsx(mv,{size:16,className:`text-slate-500 transition-transform ${r?"rotate-180":""}`})]}),r&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"fixed inset-0 z-40",onClick:()=>n(!1)}),_.jsx("div",{className:"absolute left-0 right-0 top-full mt-1 z-50 bg-[#0a0e17] border border-[#1e2a3a] rounded-lg shadow-xl overflow-hidden",children:yb.map(a=>_.jsxs("button",{type:"button",onClick:()=>{t(a.value),n(!1)},className:`w-full px-3 py-2.5 text-left text-sm hover:bg-[#1e2a3a] transition-colors ${e===a.value?"bg-accent/10":""}`,children:[_.jsx("div",{className:"font-medium text-slate-200",children:a.label}),_.jsx("div",{className:"text-xs text-slate-500",children:a.description})]},a.value))})]})]}),_.jsx("p",{className:"text-xs text-slate-600",children:'Lower = more notifications. "Warning" recommended for most rules.'})]})}function Bx({rule:e}){const[t,r]=U.useState(!1),[n,i]=U.useState(null),a=async()=>{r(!0),i(null);try{let s={type:e.delivery_type};e.delivery_type==="mesh_broadcast"?s.channel_index=e.broadcast_channel:e.delivery_type==="mesh_dm"?s.node_ids=e.node_ids:e.delivery_type==="email"?s={type:"email",smtp_host:e.smtp_host,smtp_port:e.smtp_port,smtp_user:e.smtp_user,smtp_password:e.smtp_password,smtp_tls:e.smtp_tls,from_address:e.from_address,recipients:e.recipients}:e.delivery_type==="webhook"&&(s={type:"webhook",url:e.webhook_url,headers:e.webhook_headers});const u=await(await fetch("/api/notifications/channels/test",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)})).json();i(u)}catch(s){i({success:!1,message:"Test failed",error:s instanceof Error?s.message:"Unknown error",details:{}})}finally{r(!1)}};if(!e.delivery_type)return null;const o={mesh_broadcast:_.jsx(Za,{size:14}),mesh_dm:_.jsx(kZ,{size:14}),email:_.jsx(oce,{size:14}),webhook:_.jsx(ace,{size:14})}[e.delivery_type]||_.jsx(LS,{size:14});return _.jsxs("div",{className:"space-y-2",children:[_.jsx("button",{type:"button",onClick:a,disabled:t,className:"flex items-center gap-2 px-3 py-1.5 bg-slate-700 hover:bg-slate-600 rounded text-sm disabled:opacity-50",children:t?_.jsxs(_.Fragment,{children:[_.jsx(Am,{size:14,className:"animate-spin"}),"Testing..."]}):_.jsxs(_.Fragment,{children:[o,"Test Channel"]})}),n&&_.jsx("div",{className:`p-2 rounded text-xs ${n.success?"bg-green-500/10 border border-green-500/30 text-green-400":"bg-red-500/10 border border-red-500/30 text-red-400"}`,children:_.jsxs("div",{className:"flex items-start gap-2",children:[n.success?_.jsx(Qc,{size:14,className:"mt-0.5 flex-shrink-0"}):_.jsx(au,{size:14,className:"mt-0.5 flex-shrink-0"}),_.jsxs("div",{children:[_.jsx("div",{className:"font-medium",children:n.message}),n.error&&_.jsx("div",{className:"mt-1 text-red-300",children:n.error})]})]})})]})}function tet({rule:e,ruleIndex:t,categories:r,quietHoursEnabled:n,onChange:i,onDelete:a,onDuplicate:o,onTest:s}){var k,E,D,j;const[l,u]=U.useState(!e.name),[c,f]=U.useState(!1),[h,d]=U.useState(null),[v,g]=U.useState(null);U.useEffect(()=>{var N;e.name&&t>=0&&(fetch(`/api/notifications/rules/${t}/stats`).then(z=>z.json()).then(z=>d(z)).catch(()=>{}),(N=e.categories)!=null&&N.length&&fetch("/api/notifications/rules/sources",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({categories:e.categories})}).then(z=>z.json()).then(z=>g(z)).catch(()=>{}))},[e.name,t,e.categories]);const m=[{value:"",label:"(None)",description:"Rule matches but does not deliver"},{value:"mesh_broadcast",label:"Mesh Broadcast",description:"Send to a mesh radio channel"},{value:"mesh_dm",label:"Mesh DM",description:"Direct message to specific nodes"},{value:"email",label:"Email",description:"Send via SMTP"},{value:"webhook",label:"Webhook",description:"POST to any URL"}],y=[{value:"daily",label:"Daily"},{value:"twice_daily",label:"Twice Daily"},{value:"weekly",label:"Weekly"}],x=[{value:"mesh_health_summary",label:"Mesh Health Summary",description:"Current health score, pillar breakdown, problem nodes"},{value:"rf_propagation_report",label:"RF Propagation Report",description:"Solar indices, Kp, ducting conditions"},{value:"alerts_digest",label:"Active Alerts Digest",description:"Summary of all active environmental alerts"},{value:"environmental_conditions",label:"Environmental Conditions",description:"Full conditions: weather, fire, streams, roads"},{value:"custom",label:"Custom Message",description:"Write your own with template tokens"}],b=["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],S=N=>{const z=e.categories||[];z.includes(N)?i({...e,categories:z.filter($=>$!==N)}):i({...e,categories:[...z,N]})},T=N=>{const z=e.schedule_days||[];z.includes(N)?i({...e,schedule_days:z.filter($=>$!==N)}):i({...e,schedule_days:[...z,N]})},C=async()=>{f(!0),await s(),f(!1)},M=()=>{if(e.trigger_type==="schedule")return"[Scheduled report preview would appear here]";const N=e.categories||[];if(N.length===0&&r.length>0)return r[0].example_message||"Alert notification";const z=r.find($=>N.includes($.id));return(z==null?void 0:z.example_message)||"Alert notification"},P=()=>{var z,$,Z,F,G,V,H,Y;const N=[];if(e.trigger_type==="schedule"){const K=((z=y.find(le=>le.value===e.schedule_frequency))==null?void 0:z.label)||e.schedule_frequency,ee=(($=x.find(le=>le.value===e.message_type))==null?void 0:$.label)||e.message_type;N.push(`${K} at ${e.schedule_time||"??:??"}`),N.push(ee)}else{const K=((Z=e.categories)==null?void 0:Z.length)||0,ee=K===0?"All":r.filter(fe=>{var Be;return(Be=e.categories)==null?void 0:Be.includes(fe.id)}).map(fe=>fe.name).slice(0,2).join(", ")+(K>2?` +${K-2}`:""),le=((F=yb.find(fe=>fe.value===e.min_severity))==null?void 0:F.label)||e.min_severity;N.push(`${ee} at ${le}+`)}if(!e.delivery_type)N.push("No delivery");else{const K=((G=m.find(le=>le.value===e.delivery_type))==null?void 0:G.label)||e.delivery_type;let ee="";if(e.delivery_type==="mesh_broadcast")ee=`Ch ${e.broadcast_channel}`;else if(e.delivery_type==="mesh_dm")ee=`${((V=e.node_ids)==null?void 0:V.length)||0} nodes`;else if(e.delivery_type==="email")ee=(H=e.recipients)!=null&&H.length?e.recipients[0]+(e.recipients.length>1?` +${e.recipients.length-1}`:""):"no recipients";else if(e.delivery_type==="webhook")try{ee=new URL(e.webhook_url).hostname}catch{ee=((Y=e.webhook_url)==null?void 0:Y.slice(0,20))||"no URL"}N.push(`${K}${ee?` (${ee})`:""}`)}return N.join(" -> ")},I=()=>{var z;if(!v||!((z=e.categories)!=null&&z.length))return null;const N=new Map;for(const[,$]of Object.entries(v)){const Z=N.get($.source);Z?(Z.events+=$.active_events,Z.enabled=Z.enabled&&$.enabled):N.set($.source,{enabled:$.enabled,events:$.active_events})}return Array.from(N.entries()).map(([$,{enabled:Z,events:F}])=>_.jsxs("span",{className:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-xs ${Z?"bg-green-500/10 text-green-400":"bg-red-500/10 text-red-400"}`,title:Z?`${F} active`:"Not enabled",children:[Z?_.jsx(LS,{size:10}):_.jsx(DZ,{size:10}),$.toUpperCase(),Z&&F>0&&` (${F})`]},$))};return _.jsxs("div",{className:`border rounded-lg overflow-hidden ${e.enabled?"border-[#1e2a3a]":"border-slate-700 opacity-60"}`,children:[_.jsxs("div",{className:"flex items-center justify-between p-3 bg-[#0a0e17] cursor-pointer",onClick:()=>u(!l),children:[_.jsxs("div",{className:"flex items-center gap-3 min-w-0 flex-1",children:[l?_.jsx(mv,{size:16,className:"text-slate-500 flex-shrink-0"}):_.jsx(_d,{size:16,className:"text-slate-500 flex-shrink-0"}),_.jsx("button",{onClick:N=>{N.stopPropagation(),i({...e,enabled:!e.enabled})},className:`w-2 h-2 rounded-full flex-shrink-0 ${e.enabled?"bg-green-500":"bg-slate-500"}`,title:e.enabled?"Enabled":"Disabled"}),e.trigger_type==="schedule"?_.jsx(bd,{size:14,className:"text-blue-400 flex-shrink-0"}):_.jsx(Mm,{size:14,className:"text-yellow-400 flex-shrink-0"}),_.jsx("span",{className:"font-medium text-slate-200 truncate",children:e.name||"New Rule"}),!l&&_.jsx("span",{className:`text-xs truncate hidden sm:block ${e.delivery_type?"text-slate-500":"text-amber-400"}`,children:P()})]}),_.jsxs("div",{className:"flex items-center gap-1 flex-shrink-0",children:[h&&!l&&_.jsx("span",{className:"hidden sm:inline-flex items-center gap-1 px-2 py-0.5 bg-slate-800 rounded text-xs text-slate-400 mr-2",children:h.last_fired?vP(h.last_fired):"Never fired"}),!l&&_.jsx("div",{className:"hidden md:flex items-center gap-1 mr-2",children:I()}),_.jsx("button",{onClick:N=>{N.stopPropagation(),C()},disabled:c||!e.name,className:"p-1.5 text-blue-400 hover:text-blue-300 hover:bg-blue-500/10 rounded disabled:opacity-50",title:"Test rule",children:_.jsx(p3,{size:14})}),_.jsx("button",{onClick:N=>{N.stopPropagation(),o()},className:"p-1.5 text-slate-400 hover:text-slate-200 hover:bg-slate-500/10 rounded",title:"Duplicate",children:_.jsx(nce,{size:14})}),_.jsx("button",{onClick:N=>{N.stopPropagation(),a()},className:"p-1.5 text-red-400 hover:text-red-300 hover:bg-red-500/10 rounded",title:"Delete",children:_.jsx(tD,{size:14})})]})]}),!l&&e.name&&_.jsxs("div",{className:"px-3 pb-2 pt-0 bg-[#0a0e17] flex items-center gap-2 flex-wrap text-xs",children:[!e.delivery_type&&_.jsxs("span",{className:"inline-flex items-center gap-1 px-1.5 py-0.5 bg-amber-500/10 text-amber-400 rounded",children:[_.jsx(xd,{size:10}),"No delivery method"]}),(h==null?void 0:h.fire_count)!==void 0&&h.fire_count>0&&_.jsxs("span",{className:"text-slate-500",children:["Fired ",h.fire_count,"x"]})]}),l&&_.jsxs("div",{className:"p-4 space-y-6 border-t border-[#1e2a3a]",children:[_.jsx(bl,{label:"Rule Name",value:e.name,onChange:N=>i({...e,name:N}),placeholder:"e.g., Emergency Broadcast, Daily Health Report",helper:"A descriptive name for this rule"}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Trigger Type"}),_.jsxs("div",{className:"flex gap-2",children:[_.jsxs("button",{type:"button",onClick:()=>i({...e,trigger_type:"condition"}),className:`flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg border transition-colors ${e.trigger_type!=="schedule"?"bg-accent/10 border-accent text-accent":"bg-[#0a0e17] border-[#1e2a3a] text-slate-400 hover:text-slate-200"}`,children:[_.jsx(Mm,{size:16}),_.jsx("span",{children:"Condition"})]}),_.jsxs("button",{type:"button",onClick:()=>i({...e,trigger_type:"schedule"}),className:`flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg border transition-colors ${e.trigger_type==="schedule"?"bg-accent/10 border-accent text-accent":"bg-[#0a0e17] border-[#1e2a3a] text-slate-400 hover:text-slate-200"}`,children:[_.jsx(bd,{size:16}),_.jsx("span",{children:"Schedule"})]})]}),_.jsx("p",{className:"text-xs text-slate-600",children:e.trigger_type==="schedule"?"Send reports on a schedule (daily briefings, weekly digests)":"React to alert conditions (fires, outages, weather warnings)"})]}),e.trigger_type!=="schedule"&&_.jsxs("div",{className:"space-y-4 p-4 bg-[#0a0e17] rounded-lg border border-[#1e2a3a]",children:[_.jsxs("div",{className:"flex items-center gap-2 text-sm font-medium text-slate-300",children:[_.jsx(iu,{size:14}),"WHEN (Condition)"]}),_.jsx(Xie,{value:e.min_severity,onChange:N=>i({...e,min_severity:N})}),_.jsxs("div",{className:"space-y-2",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Alert Categories",_.jsx(Ha,{info:"Select which types of alerts trigger this rule. Leave all unchecked to match ALL categories."})]}),_.jsx("div",{className:"text-xs text-slate-500 mb-2",children:(((k=e.categories)==null?void 0:k.length)||0)===0?"All categories (none selected)":`${(E=e.categories)==null?void 0:E.length} selected`}),_.jsx("div",{className:"max-h-48 overflow-y-auto border border-[#1e2a3a] rounded-lg p-2 space-y-1",children:r.map(N=>{var z,$;return _.jsxs("label",{onClick:()=>S(N.id),className:"flex items-start gap-2 p-2 rounded hover:bg-[#1e2a3a]/50 cursor-pointer",children:[_.jsx("div",{className:`w-4 h-4 mt-0.5 rounded border flex items-center justify-center flex-shrink-0 ${(z=e.categories)!=null&&z.includes(N.id)?"bg-accent border-accent":"border-slate-600"}`,children:(($=e.categories)==null?void 0:$.includes(N.id))&&_.jsx(Qc,{size:12,className:"text-white"})}),_.jsxs("div",{className:"flex-1 min-w-0",children:[_.jsx("div",{className:"text-sm text-slate-200",children:N.name}),_.jsx("div",{className:"text-xs text-slate-500",children:N.description})]})]},N.id)})})]}),v&&Object.keys(v).length>0&&_.jsxs("div",{className:"space-y-2",children:[_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Data Sources"}),_.jsx("div",{className:"flex flex-wrap gap-2",children:I()})]})]}),e.trigger_type==="schedule"&&_.jsxs("div",{className:"space-y-4 p-4 bg-[#0a0e17] rounded-lg border border-[#1e2a3a]",children:[_.jsxs("div",{className:"flex items-center gap-2 text-sm font-medium text-slate-300",children:[_.jsx(Que,{size:14}),"WHEN (Schedule)"]}),_.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Frequency"}),_.jsx("select",{value:e.schedule_frequency||"daily",onChange:N=>i({...e,schedule_frequency:N.target.value}),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent",children:y.map(N=>_.jsx("option",{value:N.value,children:N.label},N.value))})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(aS,{label:"Time",value:e.schedule_time||"07:00",onChange:N=>i({...e,schedule_time:N})}),e.schedule_frequency==="twice_daily"&&_.jsx(aS,{label:"Second Time",value:e.schedule_time_2||"19:00",onChange:N=>i({...e,schedule_time_2:N})})]}),e.schedule_frequency==="weekly"&&_.jsxs("div",{className:"space-y-2",children:[_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Days"}),_.jsx("div",{className:"flex flex-wrap gap-2",children:b.map(N=>{var z;return _.jsx("button",{type:"button",onClick:()=>T(N),className:`px-3 py-1.5 rounded text-sm capitalize transition-colors ${(z=e.schedule_days)!=null&&z.includes(N)?"bg-accent text-white":"bg-[#1e2a3a] text-slate-400 hover:text-slate-200"}`,children:N.slice(0,3)},N)})})]}),_.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Report Type"}),_.jsx("select",{value:e.message_type||"mesh_health_summary",onChange:N=>i({...e,message_type:N.target.value}),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent",children:x.map(N=>_.jsx("option",{value:N.value,children:N.label},N.value))}),_.jsx("p",{className:"text-xs text-slate-600",children:(D=x.find(N=>N.value===e.message_type))==null?void 0:D.description})]}),e.message_type==="custom"&&_.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Custom Message",_.jsx(Ha,{info:"Available tokens: {MESH_SCORE}, {NODE_COUNT}, {NODES_ONLINE}, {ACTIVE_ALERTS}, {KP}, {SFI}, {DATE}, {TIME}"})]}),_.jsx("textarea",{value:e.custom_message||"",onChange:N=>i({...e,custom_message:N.target.value}),rows:4,placeholder:"Good morning! Mesh health: {MESH_SCORE}/100 with {NODE_COUNT} nodes online.",className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent placeholder-slate-600"})]})]}),_.jsxs("div",{className:"space-y-4 p-4 bg-[#0a0e17] rounded-lg border border-[#1e2a3a]",children:[_.jsxs("div",{className:"flex items-center gap-2 text-sm font-medium text-slate-300",children:[_.jsx(p3,{size:14}),"SEND VIA"]}),_.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Delivery Method",_.jsx(Ha,{info:"Where this notification gets delivered. Select (None) to save the rule without delivery - it will match conditions but won't send until you configure a delivery method."})]}),_.jsx("select",{value:e.delivery_type||"",onChange:N=>i({...e,delivery_type:N.target.value}),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent",children:m.map(N=>_.jsx("option",{value:N.value,children:N.label},N.value))}),_.jsx("p",{className:"text-xs text-slate-600",children:(j=m.find(N=>N.value===(e.delivery_type||"")))==null?void 0:j.description})]}),!e.delivery_type&&_.jsxs("div",{className:"flex items-start gap-2 p-3 bg-amber-500/10 border border-amber-500/20 rounded-lg",children:[_.jsx(xd,{size:16,className:"text-amber-400 mt-0.5 flex-shrink-0"}),_.jsx("div",{className:"text-sm text-amber-300",children:"Rule will log matches but not deliver until a delivery method is configured."})]}),e.delivery_type==="mesh_broadcast"&&_.jsxs(_.Fragment,{children:[_.jsx(UR,{label:"Broadcast Channel",value:e.broadcast_channel??0,onChange:N=>i({...e,broadcast_channel:N}),helper:"Select the mesh radio channel",mode:"single"}),_.jsx(Bx,{rule:e})]}),e.delivery_type==="mesh_dm"&&_.jsxs(_.Fragment,{children:[_.jsx(HR,{label:"Recipient Nodes",value:e.node_ids||[],onChange:N=>i({...e,node_ids:N}),helper:"Nodes that receive direct messages",valueType:"node_id_hex"}),_.jsx(Bx,{rule:e})]}),e.delivery_type==="email"&&_.jsxs("div",{className:"space-y-4",children:[_.jsx(_b,{label:"Recipients",value:e.recipients||[],onChange:N=>i({...e,recipients:N}),placeholder:"email@example.com",helper:"Email addresses to receive alerts"}),_.jsxs("details",{className:"group",children:[_.jsxs("summary",{className:"flex items-center gap-2 cursor-pointer text-sm text-slate-400 hover:text-slate-200",children:[_.jsx(_d,{size:14,className:"group-open:rotate-90 transition-transform"}),"SMTP Configuration"]}),_.jsxs("div",{className:"mt-4 space-y-4 pl-6 border-l border-[#1e2a3a]",children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(bl,{label:"SMTP Host",value:e.smtp_host||"",onChange:N=>i({...e,smtp_host:N}),placeholder:"smtp.gmail.com"}),_.jsx(iS,{label:"SMTP Port",value:e.smtp_port??587,onChange:N=>i({...e,smtp_port:N}),min:1,max:65535})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(bl,{label:"Username",value:e.smtp_user||"",onChange:N=>i({...e,smtp_user:N})}),_.jsx(bl,{label:"Password",value:e.smtp_password||"",onChange:N=>i({...e,smtp_password:N}),type:"password",info:"Gmail users: use an App Password from myaccount.google.com/apppasswords"})]}),_.jsx(cv,{label:"Use TLS",checked:e.smtp_tls??!0,onChange:N=>i({...e,smtp_tls:N})}),_.jsx(bl,{label:"From Address",value:e.from_address||"",onChange:N=>i({...e,from_address:N}),placeholder:"alerts@yourdomain.com"})]})]}),_.jsx(Bx,{rule:e})]}),e.delivery_type==="webhook"&&_.jsxs(_.Fragment,{children:[_.jsx(bl,{label:"Webhook URL",value:e.webhook_url||"",onChange:N=>i({...e,webhook_url:N}),placeholder:"https://discord.com/api/webhooks/...",helper:"POST alert as JSON",info:"Works with Discord webhooks, ntfy.sh, Slack, Home Assistant, Pushover, or any HTTP POST endpoint."}),_.jsx(Bx,{rule:e})]})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(iS,{label:"Cooldown (minutes)",value:e.cooldown_minutes??10,onChange:N=>i({...e,cooldown_minutes:N}),min:0,helper:"Min time between repeat sends",info:"Prevents alert spam. Same condition won't re-trigger this rule within this window."}),n&&_.jsx("div",{className:"flex items-end pb-1",children:_.jsx(cv,{label:"Override Quiet Hours",checked:e.override_quiet??!1,onChange:N=>i({...e,override_quiet:N}),helper:"Deliver during quiet hours"})})]}),h&&_.jsxs("div",{className:"flex items-center gap-4 text-xs text-slate-500",children:[_.jsxs("span",{children:["Last fired: ",vP(h.last_fired)]}),_.jsxs("span",{children:["Last tested: ",vP(h.last_test)]}),_.jsxs("span",{children:["Total fires: ",h.fire_count]})]}),e.trigger_type!=="schedule"&&_.jsxs("div",{className:"space-y-2",children:[_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Example Message"}),_.jsx("div",{className:"p-3 bg-[#1e2a3a]/50 rounded-lg border border-[#1e2a3a]",children:_.jsx("p",{className:"text-sm text-slate-300 font-mono",children:M()})}),_.jsx("p",{className:"text-xs text-slate-600",children:"This is an example of what this rule would send."})]})]})]})}const ret=[{key:"mesh_health",label:"Mesh Health",Icon:gv},{key:"weather",label:"Weather",Icon:nu},{key:"fire",label:"Fire",Icon:TS},{key:"rf_propagation",label:"RF Propagation",Icon:Za},{key:"roads",label:"Roads",Icon:SS},{key:"avalanche",label:"Avalanche",Icon:cce},{key:"seismic",label:"Seismic",Icon:AS},{key:"tracking",label:"Tracking",Icon:KE}],XU=["digest","mesh_broadcast","mesh_dm","email","webhook"],net=["routine","priority","immediate"];function iet({toggles:e,onChange:t}){const[r,n]=U.useState(null),i=(a,o)=>t({...e,[a]:{...e[a]||{},name:a,...o}});return _.jsxs("div",{className:"space-y-3 mb-8",children:[_.jsxs("div",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Master Toggles",_.jsx(Ha,{info:"Per-family notification policy: enable a family, set its severity threshold, choose which channels fire at each severity, and scope to regions (PagerDuty/Grafana-style)."})]}),_.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-3",children:ret.map(({key:a,label:o,Icon:s})=>{const l=e[a]||{},u=r===a,c=Object.values(l.severity_channels||{}).reduce((h,d)=>h+((d==null?void 0:d.length)||0),0),f=(l.regions||[]).length;return _.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg p-3",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("button",{type:"button",onClick:()=>n(u?null:a),className:"flex items-center gap-2 text-sm text-slate-200",children:[_.jsx(s,{size:15})," ",o,u?_.jsx(mv,{size:14}):_.jsx(_d,{size:14})]}),_.jsx(cv,{label:"",checked:!!l.enabled,onChange:h=>i(a,{enabled:h})})]}),!u&&_.jsx("div",{className:"text-xs text-slate-600 mt-1",children:l.enabled?`${f||"all"} region${f===1?"":"s"}, ${c} channel${c===1?"":"s"} at ${l.min_severity||"priority"}+`:"OFF"}),u&&_.jsxs("div",{className:`mt-3 space-y-3 ${l.enabled?"":"opacity-40 pointer-events-none select-none"}`,children:[_.jsx(Xie,{value:l.min_severity||"priority",onChange:h=>i(a,{min_severity:h})}),_.jsx("div",{className:"text-xs text-slate-500",children:"Severity → channels"}),_.jsxs("table",{className:"text-xs w-full",children:[_.jsx("thead",{children:_.jsxs("tr",{children:[_.jsx("th",{}),XU.map(h=>_.jsx("th",{className:"text-slate-500 font-normal px-1",children:h.replace("_"," ")},h))]})}),_.jsx("tbody",{children:net.map(h=>_.jsxs("tr",{children:[_.jsx("td",{className:"text-slate-400 pr-2",children:h}),XU.map(d=>{var g;const v=(((g=l.severity_channels)==null?void 0:g[h])||[]).includes(d);return _.jsx("td",{className:"text-center",children:_.jsx("input",{type:"checkbox",checked:v,onChange:m=>{const y={...l.severity_channels||{}},x=new Set(y[h]||[]);m.target.checked?x.add(d):x.delete(d),y[h]=Array.from(x),i(a,{severity_channels:y})}})},d)})]},h))})]}),_.jsx(_b,{label:"Regions (empty = all)",value:l.regions||[],onChange:h=>i(a,{regions:h}),placeholder:"Add region..."}),_.jsx(cv,{label:"Quiet-hours override (immediate only)",checked:!!l.quiet_hours_override,onChange:h=>i(a,{quiet_hours_override:h})}),_.jsx("div",{className:"text-xs text-slate-500 pt-1",children:"Channel config"}),_.jsx(iS,{label:"Broadcast channel",value:l.broadcast_channel??0,onChange:h=>i(a,{broadcast_channel:h})}),_.jsx(_b,{label:"DM node IDs",value:l.node_ids||[],onChange:h=>i(a,{node_ids:h}),placeholder:"!nodeid"}),_.jsx(_b,{label:"Email recipients",value:l.recipients||[],onChange:h=>i(a,{recipients:h}),placeholder:"ops@example.com"}),_.jsx(bl,{label:"SMTP host",value:l.smtp_host||"",onChange:h=>i(a,{smtp_host:h}),placeholder:"smtp.example.com"}),_.jsx(iS,{label:"SMTP port",value:l.smtp_port??587,onChange:h=>i(a,{smtp_port:h})}),_.jsx(bl,{label:"Webhook URL",value:l.webhook_url||"",onChange:h=>i(a,{webhook_url:h}),placeholder:"https://..."})]})]},a)})})]})}function aet(){var $,Z,F;const[e,t]=U.useState(null),[r,n]=U.useState(null),[i,a]=U.useState([]),[o,s]=U.useState(!0),[l,u]=U.useState(!1),[c,f]=U.useState(null),[h,d]=U.useState(null),[v,g]=U.useState(null),[m,y]=U.useState({open:!1,ruleIndex:-1,loading:!1,action:""}),[x,b]=U.useState(!1),[S,T]=U.useState(!1),C=U.useCallback(async()=>{try{const[G,V]=await Promise.all([fetch("/api/config/notifications"),fetch("/api/notifications/categories")]);if(!G.ok)throw new Error("Failed to fetch notifications config");const H=await G.json(),Y=await V.json();t(H),n(JSON.parse(JSON.stringify(H))),a(Y),T(!1),f(null)}catch(G){f(G instanceof Error?G.message:"Unknown error")}finally{s(!1)}},[]);U.useEffect(()=>{document.title="Notifications - MeshAI",C()},[C]),U.useEffect(()=>{e&&r&&T(JSON.stringify(e)!==JSON.stringify(r))},[e,r]);const M=async()=>{if(e){u(!0),f(null),d(null);try{const G=await fetch("/api/config/notifications",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),V=await G.json();if(!G.ok)throw new Error(V.detail||"Save failed");d("Notifications config saved successfully"),n(JSON.parse(JSON.stringify(e))),T(!1),setTimeout(()=>d(null),3e3)}catch(G){f(G instanceof Error?G.message:"Save failed")}finally{u(!1)}}},P=()=>{r&&(t(JSON.parse(JSON.stringify(r))),T(!1))},I=()=>({name:"",enabled:!0,trigger_type:"condition",categories:[],min_severity:"routine",schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"19:00",schedule_days:["monday"],message_type:"mesh_health_summary",custom_message:"",delivery_type:"",broadcast_channel:0,node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{},cooldown_minutes:10,override_quiet:!1}),k=()=>{e&&t({...e,rules:[...e.rules||[],I()]})},E=G=>{if(!e)return;const V=YU.find(H=>H.id===G);V&&(t({...e,rules:[...e.rules||[],{...V.rule}]}),b(!1))},D=G=>{if(!e)return;const V=e.rules[G],H={...JSON.parse(JSON.stringify(V)),name:`${V.name} (copy)`},Y=[...e.rules];Y.splice(G+1,0,H),t({...e,rules:Y})},j=async G=>{y({open:!0,ruleIndex:G,loading:!0,action:""});try{const H=await(await fetch(`/api/notifications/rules/${G}/test`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"preview"})})).json();g(H),y(Y=>({...Y,loading:!1}))}catch{g({success:!1,message:"Failed to get preview"}),y(V=>({...V,loading:!1}))}},N=async G=>{const V=m.ruleIndex;y(H=>({...H,loading:!0,action:G}));try{const Y=await(await fetch(`/api/notifications/rules/${V}/test`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:G})})).json();g(Y),y(K=>({...K,loading:!1}))}catch{g({success:!1,message:`Failed to ${G}`}),y(H=>({...H,loading:!1}))}},z=()=>{y({open:!1,ruleIndex:-1,loading:!1,action:""}),g(null)};return o?_.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsx("div",{className:"text-slate-400",children:"Loading notifications config..."})}):e?_.jsxs("div",{className:"max-w-4xl mx-auto space-y-6",children:[m.open&&_.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/50",children:_.jsxs("div",{className:"bg-[#1a2332] border border-[#2a3a4a] rounded-lg shadow-xl max-w-2xl w-full mx-4 max-h-[85vh] overflow-auto",children:[_.jsxs("div",{className:"p-4 border-b border-[#2a3a4a] flex items-center justify-between sticky top-0 bg-[#1a2332]",children:[_.jsx("h3",{className:"text-lg font-semibold",children:"Test Notification Rule"}),_.jsx("button",{onClick:z,className:"text-slate-500 hover:text-slate-300",children:_.jsx(au,{size:20})})]}),_.jsx("div",{className:"p-4 space-y-4",children:m.loading?_.jsxs("div",{className:"flex items-center justify-center py-8",children:[_.jsx(Am,{size:20,className:"animate-spin text-slate-400 mr-2"}),_.jsx("div",{className:"text-slate-400",children:m.action?`${m.action.replace("_"," ").replace("send ","Sending ")}...`:"Loading current data..."})]}):v?_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"space-y-2",children:[_.jsx("div",{className:"text-sm font-medium text-slate-400 uppercase tracking-wide",children:"Current Data"}),v.live_data_summary&&v.live_data_summary.length>0?_.jsx("div",{className:"p-3 bg-slate-800/50 rounded space-y-1",children:v.live_data_summary.map((G,V)=>_.jsx("div",{className:`text-sm font-mono ${G.startsWith("[!]")?"text-amber-400":""}`,children:G},V))}):_.jsx("div",{className:"p-3 bg-slate-800/50 rounded text-sm text-slate-500",children:"No live data available for this rule's categories"})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("div",{className:"text-sm font-medium text-slate-400 uppercase tracking-wide",children:"Rule Matching"}),_.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[v.conditions_matched&&v.conditions_matched>0?_.jsxs("span",{className:"px-2 py-1 bg-green-500/20 text-green-400 rounded text-sm",children:[v.conditions_matched," condition",v.conditions_matched!==1?"s":""," match - this rule WOULD fire"]}):_.jsx("span",{className:"px-2 py-1 bg-slate-700 text-slate-400 rounded text-sm",children:"No conditions trigger this rule right now"}),v.conditions_below_threshold&&v.conditions_below_threshold>0&&_.jsxs("span",{className:"px-2 py-1 bg-yellow-500/20 text-yellow-400 rounded text-sm",children:[v.conditions_below_threshold," below threshold"]})]}),v.conditions_below_threshold&&v.conditions_below_threshold>0&&_.jsxs("div",{className:"p-3 bg-yellow-500/10 border border-yellow-500/30 rounded text-sm space-y-2",children:[_.jsx("div",{className:"text-yellow-300",children:v.below_threshold_summary}),v.below_threshold_events&&v.below_threshold_events.length>0&&_.jsx("div",{className:"space-y-1 text-yellow-200/80",children:v.below_threshold_events.slice(0,3).map((G,V)=>_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx("span",{className:"text-xs px-1.5 py-0.5 bg-yellow-500/20 rounded",children:G.severity}),_.jsx("span",{children:G.headline})]},V))}),v.suggestion&&_.jsxs("div",{className:"text-yellow-400 text-xs mt-2",children:["Tip: ",v.suggestion]})]})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("div",{className:"text-sm font-medium text-slate-400 uppercase tracking-wide",children:v.is_example?"Example Messages":"Messages That Would Fire"}),($=v.preview_messages)==null?void 0:$.map((G,V)=>_.jsx("div",{className:"p-3 bg-slate-800 rounded text-sm font-mono break-words",children:G},V))]}),v.delivered!==void 0&&v.delivery_result&&_.jsx("div",{className:`p-3 rounded text-sm ${v.delivered?"bg-green-500/10 border border-green-500/30 text-green-400":"bg-red-500/10 border border-red-500/30 text-red-400"}`,children:_.jsxs("div",{className:"flex items-start gap-2",children:[v.delivered?_.jsx(Qc,{size:16,className:"mt-0.5"}):_.jsx(au,{size:16,className:"mt-0.5"}),_.jsxs("div",{children:[_.jsx("div",{children:v.delivery_result}),v.delivery_error&&_.jsx("div",{className:"mt-1 text-red-300",children:v.delivery_error})]})]})}),v.message&&!v.preview_messages&&_.jsx("div",{className:`p-3 rounded text-sm ${v.success?"bg-green-500/10 text-green-400":"bg-red-500/10 text-red-400"}`,children:v.message})]}):null}),_.jsxs("div",{className:"p-4 border-t border-[#2a3a4a] flex justify-between sticky bottom-0 bg-[#1a2332]",children:[_.jsx("button",{onClick:z,className:"px-4 py-2 text-slate-400 hover:text-slate-200",children:"Close"}),v&&!v.delivered&&_.jsx("div",{className:"flex gap-2",children:v.delivery_method?_.jsxs(_.Fragment,{children:[v.live_data_summary&&v.live_data_summary.length>0&&_.jsx("button",{onClick:()=>N("send_status"),disabled:m.loading,className:"px-3 py-2 bg-slate-700 hover:bg-slate-600 rounded text-sm disabled:opacity-50",title:"Send current conditions summary",children:"Send Current Conditions"}),_.jsx("button",{onClick:()=>N("send_test"),disabled:m.loading,className:"px-3 py-2 bg-slate-700 hover:bg-slate-600 rounded text-sm disabled:opacity-50",title:"Send example alert message",children:"Send Example Alert"}),v.can_send_live&&_.jsx("button",{onClick:()=>N("send_live"),disabled:m.loading,className:"px-3 py-2 bg-accent hover:bg-accent/80 rounded text-sm disabled:opacity-50",title:"Send actual live alert",children:"Send Live Alert"})]}):_.jsx("span",{className:"px-3 py-2 text-amber-400 text-sm",children:"Configure a delivery method to send test messages"})})]})]})}),_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsx("div",{children:_.jsx("p",{className:"text-sm text-slate-500",children:"Alert delivery and scheduled reports. Rules define what triggers a notification and where it gets sent."})}),_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx("button",{onClick:C,className:"p-2 text-slate-400 hover:text-slate-200 hover:bg-bg-hover rounded transition-colors",title:"Refresh",children:_.jsx(Am,{size:18})}),_.jsxs("button",{onClick:P,disabled:!S,className:"flex items-center gap-2 px-3 py-2 text-slate-400 hover:text-slate-200 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",children:[_.jsx(QE,{size:16}),"Discard"]}),_.jsxs("button",{onClick:M,disabled:l||!S,className:"flex items-center gap-2 px-4 py-2 bg-accent hover:bg-accent/80 disabled:bg-slate-700 disabled:cursor-not-allowed rounded text-white transition-colors",children:[_.jsx(JE,{size:16}),l?"Saving...":"Save"]})]})]}),c&&_.jsx("div",{className:"p-3 rounded-lg text-sm bg-red-500/10 text-red-400 border border-red-500/20",children:c}),h&&_.jsxs("div",{className:"p-3 rounded-lg text-sm bg-green-500/10 text-green-400 border border-green-500/20",children:[_.jsx(Qc,{size:14,className:"inline mr-2"}),h]}),_.jsxs("div",{className:"bg-bg-card border border-border rounded-lg p-6 space-y-6",children:[_.jsx(cv,{label:"Enable Notifications",checked:e.enabled,onChange:G=>t({...e,enabled:G}),helper:"Master switch for all notification delivery",info:"When disabled, no alerts or scheduled messages will be delivered. Alerts still get recorded to history."}),e.enabled&&_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"space-y-3 p-4 bg-[#0a0e17] rounded-lg border border-[#1e2a3a]",children:[_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx(lce,{size:14,className:"text-slate-400"}),_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Quiet Hours"})]}),_.jsx(cv,{label:"Enable Quiet Hours",checked:e.quiet_hours_enabled??!0,onChange:G=>t({...e,quiet_hours_enabled:G}),helper:"Suppress non-emergency alerts during sleeping hours",info:"When enabled, ROUTINE alerts are suppressed during quiet hours. PRIORITY and IMMEDIATE always deliver."}),e.quiet_hours_enabled&&_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(aS,{label:"Start Time",value:e.quiet_hours_start||"22:00",onChange:G=>t({...e,quiet_hours_start:G}),helper:"When quiet hours begin"}),_.jsx(aS,{label:"End Time",value:e.quiet_hours_end||"06:00",onChange:G=>t({...e,quiet_hours_end:G}),helper:"When quiet hours end"})]}),_.jsx("p",{className:"text-xs text-slate-600",children:'Emergency alerts and rules with "Override Quiet Hours" enabled always deliver.'})]})]}),e.toggles&&_.jsx(iet,{toggles:e.toggles,onChange:G=>t({...e,toggles:G})}),_.jsxs("div",{className:"space-y-3",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Notification Rules",_.jsx(Ha,{info:"Each rule is self-contained: define what triggers it (condition or schedule), where to send it (mesh, email, webhook), and behavior settings."})]}),_.jsxs("span",{className:"text-xs text-slate-500",children:[((Z=e.rules)==null?void 0:Z.length)||0," rule",(((F=e.rules)==null?void 0:F.length)||0)!==1?"s":""]})]}),(e.rules||[]).map((G,V)=>_.jsx(tet,{rule:G,ruleIndex:V,categories:i,quietHoursEnabled:e.quiet_hours_enabled??!0,onChange:H=>{const Y=[...e.rules||[]];Y[V]=H,t({...e,rules:Y})},onDelete:()=>{confirm(`Delete rule "${G.name||"New Rule"}"?`)&&t({...e,rules:(e.rules||[]).filter((H,Y)=>Y!==V)})},onDuplicate:()=>D(V),onTest:()=>j(V)},V)),_.jsxs("div",{className:"flex gap-2",children:[_.jsxs("button",{onClick:k,className:"flex-1 py-3 border border-dashed border-[#1e2a3a] rounded-lg text-slate-500 hover:text-slate-300 hover:border-accent flex items-center justify-center gap-2 transition-colors",children:[_.jsx(MS,{size:16})," Add Rule"]}),_.jsxs("div",{className:"relative",children:[_.jsxs("button",{onClick:()=>b(!x),className:"py-3 px-4 border border-dashed border-[#1e2a3a] rounded-lg text-slate-500 hover:text-slate-300 hover:border-accent flex items-center gap-2 transition-colors",children:[_.jsx(PZ,{size:16})," Add from Template"]}),x&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"fixed inset-0 z-40",onClick:()=>b(!1)}),_.jsxs("div",{className:"absolute right-0 top-full mt-2 z-50 w-80 bg-[#1a2332] border border-[#2a3a4a] rounded-lg shadow-xl overflow-hidden",children:[_.jsx("div",{className:"p-2 border-b border-[#2a3a4a] text-xs text-slate-500 uppercase",children:"Rule Templates"}),YU.map(G=>_.jsxs("button",{onClick:()=>E(G.id),className:"w-full p-3 text-left hover:bg-[#2a3a4a] transition-colors",children:[_.jsx("div",{className:"font-medium text-slate-200",children:G.name}),_.jsx("div",{className:"text-xs text-slate-500 mt-0.5",children:G.description})]},G.id))]})]})]})]})]})]})]})]}):_.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsx("div",{className:"text-red-400",children:"Failed to load notifications config"})})}const qU=[{id:"stream-gauges",label:"Stream Gauges",icon:AZ},{id:"wildfire",label:"Wildfire",icon:TS},{id:"firms",label:"Satellite Fire Detection (FIRMS)",icon:PS},{id:"weather-alerts",label:"Weather Alerts",icon:tce},{id:"solar",label:"Solar & Geomagnetic",icon:OZ},{id:"ducting",label:"Tropospheric Ducting",icon:Za},{id:"avalanche",label:"Avalanche Danger",icon:AS},{id:"traffic",label:"Traffic Flow",icon:SS},{id:"roads-511",label:"Road Conditions (511)",icon:TZ},{id:"mesh-health",label:"Mesh Health",icon:gv},{id:"notifications",label:"Notifications",icon:Cm},{id:"commands",label:"Commands",icon:EZ},{id:"api",label:"API Reference",icon:rce}];function ir({color:e}){const t={green:"bg-green-500",yellow:"bg-yellow-500",orange:"bg-orange-500",red:"bg-red-500",black:"bg-slate-800 border border-slate-600"};return _.jsx("span",{className:`inline-block w-3 h-3 rounded-full ${t[e]}`})}function jt({headers:e,rows:t}){return _.jsx("div",{className:"overflow-x-auto my-4",children:_.jsxs("table",{className:"w-full text-sm",children:[_.jsx("thead",{children:_.jsx("tr",{className:"bg-[#1a2332] border-b border-[#2a3a4a]",children:e.map((r,n)=>_.jsx("th",{className:"px-4 py-2 text-left text-slate-400 font-medium",children:r},n))})}),_.jsx("tbody",{children:t.map((r,n)=>_.jsx("tr",{className:`border-b border-[#1e2a3a] ${n%2===0?"bg-[#0d1219]":"bg-[#0a0e17]"}`,children:r.map((i,a)=>_.jsx("td",{className:"px-4 py-2 text-slate-300",children:i},a))},n))})]})})}function Rt({href:e,children:t}){return _.jsxs("a",{href:e,target:"_blank",rel:"noopener noreferrer",className:"text-accent hover:underline inline-flex items-center gap-1",children:[t," ",_.jsx(wd,{size:12})]})}function Te({children:e}){return _.jsx("h3",{className:"text-lg font-semibold text-slate-200 mt-6 mb-3",children:e})}function fl({children:e}){return _.jsx("h4",{className:"text-base font-medium text-slate-300 mt-4 mb-2",children:e})}function $e({children:e}){return _.jsx("code",{className:"font-mono text-accent bg-[#1a2332] px-1 rounded",children:e})}function vi({id:e,title:t,children:r}){return _.jsxs("section",{id:e,className:"mb-12 scroll-mt-6",children:[_.jsx("h2",{className:"text-2xl font-bold text-slate-100 mb-4 pb-2 border-b border-[#2a3a4a]",children:t}),_.jsx("div",{className:"text-slate-300 leading-relaxed space-y-4",children:r})]})}function oet(){const e=pv(),[t,r]=U.useState(""),[n,i]=U.useState("stream-gauges"),a=U.useRef(null);U.useEffect(()=>{const l=e.hash.replace("#","");if(l&&qU.find(u=>u.id===l)){i(l);const u=document.getElementById(l);u&&u.scrollIntoView({behavior:"smooth"})}},[e.hash]);const o=qU.filter(l=>l.label.toLowerCase().includes(t.toLowerCase())),s=l=>{i(l);const u=document.getElementById(l);u&&u.scrollIntoView({behavior:"smooth"}),window.history.replaceState(null,"",`#${l}`)};return _.jsxs("div",{className:"flex h-full -m-6",children:[_.jsxs("aside",{className:"w-64 flex-shrink-0 bg-bg-card border-r border-border overflow-y-auto",children:[_.jsx("div",{className:"p-4 border-b border-border",children:_.jsxs("div",{className:"relative",children:[_.jsx(eD,{size:16,className:"absolute left-3 top-1/2 -translate-y-1/2 text-slate-500"}),_.jsx("input",{type:"text",value:t,onChange:l=>r(l.target.value),placeholder:"Search topics...",className:"w-full pl-9 pr-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent placeholder-slate-600"})]})}),_.jsx("nav",{className:"py-2",children:o.map(l=>{const u=l.icon,c=n===l.id;return _.jsxs("button",{onClick:()=>s(l.id),className:`w-full flex items-center gap-3 px-4 py-2.5 text-sm text-left transition-colors ${c?"text-accent bg-accent/10 border-l-2 border-accent":"text-slate-400 hover:text-slate-200 hover:bg-bg-hover border-l-2 border-transparent"}`,children:[_.jsx(u,{size:16}),l.label]},l.id)})})]}),_.jsx("div",{ref:a,className:"flex-1 overflow-y-auto p-6",children:_.jsxs("div",{className:"max-w-4xl",children:[_.jsx("p",{className:"text-slate-400 mb-8",children:"Everything you need to understand and configure MeshAI's monitoring and alerting systems."}),_.jsxs(vi,{id:"stream-gauges",title:"Stream Gauges",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"MeshAI watches river and stream levels at gauges you configure. Each gauge reports two things:"}),_.jsxs("p",{children:[_.jsx("strong",{children:"Water Level (Gage Height)"}),` — how high the water is, measured in feet. Important: this is NOT the depth of the river. It's the height above a fixed measuring point that's different at every gauge. A reading of "10 feet" at one gauge means something completely different than "10 feet" at another. You can only compare readings from the SAME gauge over time.`]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Flow (Discharge)"}),` — how much water is moving past the gauge, in cubic feet per second (CFS). Think of it as the river's "throughput." For scale:`]}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsx("li",{children:"A small creek: 50-200 CFS"}),_.jsx("li",{children:"A mid-size river: 1,000-5,000 CFS"}),_.jsx("li",{children:"A big river in spring runoff: 10,000+ CFS"})]}),_.jsx(Te,{children:"When Does It Flood?"}),_.jsxs("p",{children:["Flood levels are set by the ",_.jsx("strong",{children:"National Weather Service"}),', not USGS. NWS looks at each specific gauge location and decides "at what water level does the road flood? At what level do buildings get water?" Those levels are different everywhere.']}),_.jsxs("p",{children:[_.jsx("strong",{children:"Action Stage"})," — water is rising, time to start paying attention. Usually still inside the riverbanks."]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Minor Flood"})," — low-lying roads start getting water on them. NWS issues a Flood Advisory."]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Moderate Flood"})," — water in buildings near the river. Some people need to evacuate. NWS issues a Flood Warning."]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Major Flood"})," — widespread flooding. Many people evacuating. Serious property damage."]}),_.jsx("p",{children:"MeshAI automatically looks up the flood levels for your gauge from NWS when you add a site. Some remote gauges don't have flood levels assigned — for those, you set them manually if you know what water levels cause problems in your area."}),_.jsx(Te,{children:"Low Water / Drought"}),_.jsx("p",{children:`There's no official "drought stage" for most gauges. If you need to monitor low water (irrigation, fish habitat), set a manual low-water threshold based on what you know about your local river.`}),_.jsx(Te,{children:"Setting It Up"}),_.jsxs("ol",{className:"list-decimal list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:["Find your gauge at ",_.jsx(Rt,{href:"https://waterdata.usgs.gov/nwis",children:"waterdata.usgs.gov/nwis"})]}),_.jsxs("li",{children:["Copy the site number (like ",_.jsx($e,{children:"13090500"}),")"]}),_.jsx("li",{children:"Add it in Config → Environmental → USGS"}),_.jsx("li",{children:"MeshAI auto-fills the gauge name and flood levels from NWS"})]}),_.jsx("p",{children:"If NWS flood levels don't populate, your gauge may not have them. Set manual thresholds if you know your local conditions."}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://waterdata.usgs.gov/nwis",children:"USGS Water Data"})," — find gauges near you"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://water.noaa.gov",children:"NWS Water Prediction Service"})," — flood forecasts and thresholds"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.usgs.gov/special-topics/water-science-school/science/how-streamflow-measured",children:"Understanding Streamflow"})," — USGS explainer"]})]})]}),_.jsxs(vi,{id:"wildfire",title:"Wildfire",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"MeshAI tracks active wildfire perimeters from the National Interagency Fire Center (NIFC). For each fire, you see the name, size, how much is contained, and how far it is from your mesh nodes."}),_.jsx(Te,{children:"Fire Size — How Big Is It?"}),_.jsx(jt,{headers:["Size","What That Means"],rows:[["10 acres","Small fire. Usually handled quickly by initial crews."],["100 acres","Notable fire. Active firefighting effort."],["1,000 acres","Large fire. Major resources being deployed."],["10,000+ acres","Very large fire. Multiple teams, aircraft, heavy equipment."],["100,000+ acres","Mega-fire. These make the national news."]]}),_.jsx("p",{children:"For reference, 1,000 acres is about 1.5 square miles."}),_.jsx(Te,{children:"Containment — Is It Under Control?"}),_.jsx("p",{children:"Containment means the percentage of the fire's edge where firefighters have built a control line (a cleared strip to stop the fire from spreading further). It does NOT mean the fire is out inside that line."}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx("strong",{children:"0-30%"})," — Essentially uncontrolled. The fire goes where it wants."]}),_.jsxs("li",{children:[_.jsx("strong",{children:"50%"})," — Good progress, but half the edge can still grow."]}),_.jsxs("li",{children:[_.jsx("strong",{children:"80%+"})," — Well controlled. Major growth unlikely."]}),_.jsxs("li",{children:[_.jsx("strong",{children:"100%"}),' — The edge is fully controlled. But the fire may STILL be actively burning inside. "100% contained" does NOT mean "out."']})]}),_.jsx(Te,{children:"How Far Away Should I Worry?"}),_.jsx(jt,{headers:["Distance","What To Do"],rows:[[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"red"})," Under 5 km (3 miles)"]}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Immediate threat."})," This is evacuation-order range. Embers can fly this far in wind."]})],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"orange"})," 5-15 km (3-10 miles)"]}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Prepare."})," The fire could reach you in hours under bad conditions. Have a plan."]})],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"yellow"})," 15-30 km (10-20 miles)"]}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Watch."})," Smoke is likely. Wind shifts could change things fast."]})],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"green"})," Over 30 km (20 miles)"]}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Awareness."})," Keep an eye on it, but no immediate threat."]})]]}),_.jsx("p",{children:"How fast can a fire travel? In grass with wind: up to 14 mph. In heavy timber: 1-6 mph. A fire 10 miles away could theoretically reach you in 1-2 hours under worst-case conditions, but typical spread is much slower."}),_.jsx(Te,{children:"Which Matters More — Size or Distance?"}),_.jsxs("p",{children:[_.jsx("strong",{children:"Distance is the immediate concern."})," A small uncontained fire 10 km away is more dangerous right now than a huge fire 50 km away. But big fires have more energy and can grow fast under wind shifts — keep watching them."]}),_.jsx(Te,{children:"Setting It Up"}),_.jsxs("p",{children:["Just configure your state code (like ",_.jsx($e,{children:"US-ID"})," for Idaho) in Config → Environmental → Fires. MeshAI polls NIFC every 10 minutes for active fires in that state and computes the distance to your mesh nodes automatically."]}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://inciweb.nwcg.gov",children:"InciWeb"})," — detailed incident information"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://data-nifc.opendata.arcgis.com",children:"NIFC Fire Map"})," — raw perimeter data"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.ready.gov/wildfires",children:"Ready.gov Wildfires"})," — preparedness guide"]})]})]}),_.jsxs(vi,{id:"firms",title:"Satellite Fire Detection (FIRMS)",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:`NASA's VIIRS satellites orbit the Earth and look for heat signatures on the ground. When they see something hot — a fire, a factory, a sunlit building — they flag it as a "hotspot." MeshAI checks these detections for your area.`}),_.jsxs("p",{children:[_.jsx("strong",{children:"Why this matters"}),": satellite hotspots show up ",_.jsx("strong",{children:"hours before"})," official fire perimeters are mapped. If a new fire starts near your mesh, the satellite might see it before anyone on the ground reports it."]}),_.jsx(Te,{children:"Confidence — Is It Really a Fire?"}),_.jsx("p",{children:"Each detection gets a confidence rating:"}),_.jsx(jt,{headers:["Confidence","What It Means"],rows:[["High","Almost certainly a real fire. Strong heat signature."],["Nominal","Probably a real fire. Most actual fires get this rating."],["Low","Maybe a fire, maybe not. Could be a hot roof, sun reflecting off water, a factory, or a gas flare. Lots of false alarms."]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Recommendation"}),`: Set the filter to "Nominal + High." If you include "Low" you'll get alerts for every hot parking lot on a summer day.`]}),_.jsx(Te,{children:"FRP — How Intense Is It?"}),_.jsx("p",{children:'FRP (Fire Radiative Power) measures the heat output in megawatts. Think of it as "how hot is this thing":'}),_.jsx(jt,{headers:["FRP","What It Probably Is"],rows:[["Under 5 MW","Hot surface, small agricultural burn, gas flare, or warm ground"],["5-50 MW","An actual fire — brush fire, grass fire, typical wildfire"],["50-300 MW","Intense fire — trees fully burning, active fire front"],["Over 300 MW","Extreme fire — major wildfire in full force"]]}),_.jsx("p",{children:"Setting the minimum FRP to 5 MW filters out most industrial and agricultural false alarms."}),_.jsx(Te,{children:"New Ignition Detection"}),_.jsxs("p",{children:["MeshAI cross-references satellite hotspots against known NIFC fire perimeters. If a hotspot is NOT near any known fire, it gets flagged as a ",_.jsx("strong",{children:"potential new ignition"})," — maybe a new fire just started. These get elevated priority regardless of confidence level."]}),_.jsx(Te,{children:"Timing"}),_.jsxs("p",{children:["Satellite data arrives ",_.jsx("strong",{children:"1-3 hours"})," after the satellite passes overhead. Each location gets observed about ",_.jsx("strong",{children:"6 times per day"}),` across all satellites, so there are multi-hour gaps. This is not real-time — it's "pretty recent."`]}),_.jsx(Te,{children:"Getting an API Key"}),_.jsxs("ol",{className:"list-decimal list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:["Go to ",_.jsx(Rt,{href:"https://firms.modaps.eosdis.nasa.gov/api/area/",children:"FIRMS API page"})]}),_.jsx("li",{children:'Click "Get MAP_KEY"'}),_.jsx("li",{children:"Register for a free Earthdata account"}),_.jsx("li",{children:"Your key arrives by email"}),_.jsx("li",{children:"Enter it in Config → Environmental → FIRMS"})]}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://firms.modaps.eosdis.nasa.gov",children:"FIRMS Fire Map"})," — see hotspots on a map"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://earthdata.nasa.gov/data/tools/firms/faq",children:"FIRMS FAQ"})," — how it works"]})]})]}),_.jsxs(vi,{id:"weather-alerts",title:"Weather Alerts",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"MeshAI watches for NWS (National Weather Service) alerts affecting your area — warnings, watches, and advisories."}),_.jsx(Te,{children:"Alert Severity — How Serious Is It?"}),_.jsx(jt,{headers:["Severity","What It Means","Example"],rows:[["Extreme","Life-threatening. The most serious events.","Tornado Emergency, Hurricane Warning, Tsunami Warning"],["Severe","Dangerous. Take protective action.","Tornado Warning, Flash Flood Warning, Blizzard Warning, Red Flag Warning"],["Moderate","Be prepared. Could become dangerous.","Winter Weather Advisory, Wind Advisory, Flood Watch, Heat Advisory"],["Minor","Good to know. Probably won't hurt anyone.","Special Weather Statement, Air Quality Alert"]]}),_.jsx(Te,{children:"When Should I Act? (Urgency)"}),_.jsx(jt,{headers:["Urgency","What It Means"],rows:[["Immediate","Do something NOW"],["Expected","Do something within the hour"],["Future","Coming in the next several hours"],["Past","It's over — NWS is clearing the alert"]]}),_.jsx(Te,{children:"How Sure Are They? (Certainty)"}),_.jsx(jt,{headers:["Certainty","What It Means"],rows:[["Observed","It's happening right now. Verified."],["Likely","More than 50% chance"],["Possible","Could happen, but less than 50%"],["Unlikely","Probably won't, but mentioned for awareness"]]}),_.jsx(Te,{children:"These Are Separate Scales"}),_.jsx("p",{children:'A single alert has all three. A hurricane warning for next week is "Severe + Future + Likely." A tornado spotted on the ground is "Extreme + Immediate + Observed." An air quality advisory is "Minor + Expected + Possible."'}),_.jsx(Te,{children:"What Minimum Severity Should I Set?"}),_.jsx(jt,{headers:["Setting","What You Get","What You Miss"],rows:[["Minor","Everything — high volume","Nothing"],[_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Moderate"})," ✓"]}),"Watches, Advisories, and Warnings","Special Weather Statements"],["Severe","Only Warnings — things happening NOW","Watches (which give you hours of advance warning)"],["Extreme","Only the rarest events","Most Tornado and Severe Thunderstorm Warnings"]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Moderate is recommended."})," It catches Watches (advance warning that conditions may worsen) and Advisories (conditions exist but aren't severe) while filtering out the informational stuff."]}),_.jsx(Te,{children:"Finding Your NWS Zone"}),_.jsxs("ol",{className:"list-decimal list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:["Go to ",_.jsx(Rt,{href:"https://www.weather.gov",children:"weather.gov"})]}),_.jsx("li",{children:"Enter your location"}),_.jsxs("li",{children:["Find your zone code at ",_.jsx(Rt,{href:"https://www.weather.gov/pimar/PubZone",children:"NWS Zone Map"})]}),_.jsxs("li",{children:["Zone codes look like: ",_.jsx($e,{children:"IDZ016"}),", ",_.jsx($e,{children:"UTZ040"}),", etc."]})]}),_.jsx(Te,{children:"The User-Agent Field"}),_.jsx("p",{children:"NWS wants to know who's using their API — not for approval, just so they can contact you if something breaks. You make it up:"}),_.jsx("p",{children:_.jsx($e,{children:"(meshai, you@email.com)"})}),_.jsx("p",{children:"No registration. No waiting. Just type it in."}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://alerts.weather.gov",children:"NWS Active Alerts"})," — see current alerts"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.weather.gov/documentation/services-web-api",children:"NWS API Docs"})," — technical details"]})]})]}),_.jsxs(vi,{id:"solar",title:"Solar & Geomagnetic Conditions",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"MeshAI tracks space weather — solar activity and its effects on Earth's magnetic field. This matters for radio operators because the sun directly controls how well HF radio works, and major solar events can affect all radio communications."}),_.jsx(Te,{children:"Solar Flux Index (SFI)"}),_.jsx("p",{children:'Think of SFI as a "how active is the sun" number. Higher = better for HF radio, but also higher risk of solar flares.'}),_.jsx(jt,{headers:["SFI","What It Means for You"],rows:[["Below 70","Quiet sun. Higher HF bands (10m, 15m) are probably dead. Stick to lower bands."],["70-90","Getting better. Some openings on 15m and above, but inconsistent."],["90-120","Good. Most HF bands work. Reliable contacts on 20m and 15m."],["120-170","Great. All HF bands open. 10m works for worldwide contacts."],["Above 170","Excellent. Best HF conditions — but watch for flares."]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Quick rule"}),": SFI above 90 and Kp below 4 = good day for HF radio."]}),_.jsx(Te,{children:"Kp Index"}),_.jsx("p",{children:"Kp measures how disturbed Earth's magnetic field is, on a 0-9 scale. Higher = more disturbance = worse for HF radio but better for aurora viewing."}),_.jsx(jt,{headers:["Kp","What It Means for You"],rows:[["0-2","Quiet. Best HF conditions."],["3","Slightly unsettled. You probably won't notice."],["4","Active. Some noise and fading on HF, especially if you're at higher latitudes."],[_.jsx("strong",{children:"5"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Minor storm (G1)."})," HF noticeably degraded. Aurora visible at high latitudes (~60°N)."]})],[_.jsx("strong",{children:"6"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Moderate storm (G2)."})," HF getting rough. Aurora moving south (~55°N)."]})],[_.jsx("strong",{children:"7"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Strong storm (G3)."})," HF unreliable for 1-2 days. Aurora at mid-latitudes."]})],[_.jsx("strong",{children:"8-9"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Severe/Extreme storm."})," HF may black out completely. Aurora visible at very low latitudes. Power grid stress possible."]})]]}),_.jsx(Te,{children:"R / S / G Scales"}),_.jsx("p",{children:"NOAA's shorthand for three types of space weather events:"}),_.jsx(fl,{children:"R (Radio Blackouts) — from solar flares:"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsx("li",{children:"R1-R2: Brief HF disruption. You might not notice."}),_.jsx("li",{children:"R3: HF goes out for about an hour on the sunlit side of Earth."}),_.jsx("li",{children:"R4-R5: HF dead for hours. Serious."})]}),_.jsx(fl,{children:"S (Solar Radiation Storms) — from energetic particles:"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsx("li",{children:"Mostly affects polar regions and satellites"}),_.jsx("li",{children:"S3+: Polar HF goes out entirely"})]}),_.jsx(fl,{children:"G (Geomagnetic Storms) — from solar wind disturbances:"}),_.jsx("ul",{className:"list-disc list-inside ml-4 space-y-1",children:_.jsx("li",{children:"Same as the Kp scale: G1 = Kp 5, up to G5 = Kp 9"})}),_.jsx(Te,{children:"Bz — The Storm Predictor"}),_.jsx("p",{children:"Bz measures the direction of the solar wind's magnetic field. When it points south (negative values), the solar wind can dump energy into Earth's magnetic field, causing storms."}),_.jsx(jt,{headers:["Bz","What It Means"],rows:[["Positive","All good. Solar wind bouncing off."],["0 to -5","Slight coupling. Nothing dramatic."],["-5 to -10","Things starting to pick up. Storm possible."],["Below -10","Storm likely. Kp will start climbing."],["Below -20","Severe storm probable."]]}),_.jsx("p",{children:"Bz can change fast — minute to minute. What matters is whether it stays negative for hours, not brief dips."}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.swpc.noaa.gov",children:"SWPC Space Weather Dashboard"})," — live data"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.swpc.noaa.gov/noaa-scales-explanation",children:"NOAA Space Weather Scales"})," — what R/S/G mean"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.hamqsl.com/solar.html",children:"HamQSL Solar Page"})," — ham-friendly display"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.swpc.noaa.gov/products/planetary-k-index",children:"Planetary K-Index"})," — live Kp"]})]})]}),_.jsxs(vi,{id:"ducting",title:"Tropospheric Ducting",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:'Sometimes the atmosphere creates an invisible "pipe" that traps radio signals and carries them much farther than normal. This is called tropospheric ducting. It mostly affects VHF and UHF frequencies.'}),_.jsx("p",{children:"MeshAI watches for these conditions by analyzing weather data (temperature and humidity at different altitudes) over your mesh area."}),_.jsx(Te,{children:"How Do I Know If Ducting Is Happening?"}),_.jsx("p",{children:'MeshAI reports a "condition" based on the atmospheric profile:'}),_.jsx(jt,{headers:["Condition","What It Means"],rows:[["Normal","Standard propagation. Nothing unusual."],["Super-refraction","Slightly enhanced range. You might hear a few more distant stations than usual."],["Surface Duct","Radio signals trapped near the ground. You may hear stations hundreds of km away that you've never heard before."],["Elevated Duct",'Same effect but the "pipe" is up in the atmosphere. Affects signals passing through that altitude.']]}),_.jsx(Te,{children:"What You'll Actually Notice"}),_.jsx("p",{children:"When ducting happens on your mesh:"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsx("li",{children:"Distant repeaters you've never heard suddenly come in"}),_.jsx("li",{children:"Nodes appear from far outside your normal range"}),_.jsx("li",{children:"You hear FM radio stations from other cities"}),_.jsx("li",{children:"ADS-B flight tracking range gets much longer"}),_.jsx("li",{children:"There might be interference from distant stations on your frequency"})]}),_.jsx(Te,{children:"The dM/dz Number"}),_.jsx("p",{children:`The dashboard shows a "dM/dz" value in "M-units/km." You don't need to understand the math — just know:`}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx("strong",{children:"Around 118"})," = normal atmosphere"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Below 79"})," = enhanced propagation starting"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Below 0 (negative)"})," = ducting is happening"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Below -50"})," = strong ducting — classic VHF/UHF DX event"]})]}),_.jsx(Te,{children:"When Does Ducting Happen?"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsx("li",{children:"Under high-pressure weather systems (clear, stable air)"}),_.jsx("li",{children:"When warm air sits on top of cool air (temperature inversion)"}),_.jsx("li",{children:"Most common in late summer and early fall"}),_.jsx("li",{children:"Strongest along coastlines and over water"}),_.jsx("li",{children:"In mountain valleys: cold air pooling in fall/winter can create surface ducts"})]}),_.jsx(Te,{children:"Setting It Up"}),_.jsx("p",{children:"Just configure the latitude and longitude of the center of your mesh area in Config → Environmental → Ducting. MeshAI checks the atmospheric conditions there every 3 hours using free weather model data. No API key needed."}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://dxinfocentre.com/tropo.html",children:"Tropo Forecast Maps (Hepburn)"})," — 6-day tropo prediction"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://dxmaps.com",children:"DX Maps"})," — real-time VHF/UHF propagation reports"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://en.wikipedia.org/wiki/Tropospheric_propagation",children:"Wikipedia: Tropospheric Propagation"})," — background"]})]})]}),_.jsxs(vi,{id:"avalanche",title:"Avalanche Danger",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"MeshAI pulls avalanche forecasts from your regional avalanche center during winter months. The danger scale has 5 levels and it's the same across all of North America."}),_.jsx(Te,{children:"The Danger Scale"}),_.jsx(jt,{headers:["Level","Name","Color","What To Do"],rows:[["1","Low",_.jsx(ir,{color:"green"}),"Generally safe. Normal caution in steep terrain."],["2","Moderate",_.jsx(ir,{color:"yellow"}),"Be careful on specific terrain features. Evaluate conditions."],["3","Considerable",_.jsx(ir,{color:"orange"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"DANGEROUS."}),` This is where most people die in avalanches — they see "3 out of 5" and think it's fine. It's not. Use extreme caution.`]})],["4","High",_.jsx(ir,{color:"red"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Very dangerous."})," Stay off anything steep."]})],["5","Extreme",_.jsx(ir,{color:"black"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Don't go out."})," Avalanches are happening on their own."]})]]}),_.jsx(Te,{children:"The Most Important Thing to Know"}),_.jsxs("p",{children:[_.jsx("strong",{children:"Level 3 (Considerable) kills more people than any other level."}),' People look at "3 out of 5" and think "middle of the road, probably okay." In reality, the risk roughly doubles at each step up the scale. Level 3 is where dangerous conditions overlap with people thinking they can handle it.']}),_.jsx(Te,{children:"Seasonal"}),_.jsx("p",{children:'MeshAI only checks avalanche conditions during winter months (configurable, default December through April). Outside season, it shows "off season" and saves API calls.'}),_.jsx(Te,{children:"Finding Your Avalanche Center"}),_.jsxs("p",{children:["Go to ",_.jsx(Rt,{href:"https://avalanche.org/avalanche-centers/",children:"avalanche.org/avalanche-centers/"})," for a map. Common center codes:"]}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx($e,{children:"SNFAC"})," — Sawtooth (central Idaho)"]}),_.jsxs("li",{children:[_.jsx($e,{children:"UAC"})," — Utah"]}),_.jsxs("li",{children:[_.jsx($e,{children:"NWAC"})," — Cascades/Olympics (WA/OR)"]}),_.jsxs("li",{children:[_.jsx($e,{children:"CAIC"})," — Colorado"]}),_.jsxs("li",{children:[_.jsx($e,{children:"SAC"})," — Sierra Nevada (CA)"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GNFAC"})," — Gallatin (SW Montana)"]})]}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://avalanche.org",children:"Avalanche.org"})," — US forecasts"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://avalanche.org/avalanche-encyclopedia/human/resources/north-american-public-avalanche-danger-scale/",children:"Avalanche Danger Scale"})," — full scale explanation"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://kbyg.org",children:"Know Before You Go"})," — avalanche awareness"]})]})]}),_.jsxs(vi,{id:"traffic",title:"Traffic Flow",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"MeshAI monitors traffic speed on road segments you configure, using data from TomTom (real vehicles with navigation apps reporting their speed)."}),_.jsx(Te,{children:"Speed Ratio — The Key Number"}),_.jsx("p",{children:'MeshAI compares current speed to "free-flow speed" (what traffic normally does when the road is empty). The ratio tells you how congested it is:'}),_.jsx(jt,{headers:["Ratio","What It Means"],rows:[[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"green"})," Above 85%"]}),"Normal. Traffic flowing fine."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"yellow"})," 65-85%"]}),"Slow. Heavier than usual but moving."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"orange"})," 40-65%"]}),"Congested. Significant delays."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"red"})," Below 40%"]}),"Gridlock. Barely moving."]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Note"}),`: "free-flow speed" is NOT the speed limit. It's what traffic actually does on that road when nobody's in the way. Drivers often exceed speed limits on open highways.`]}),_.jsx(Te,{children:"Confidence — Can You Trust the Data?"}),_.jsx("p",{children:"TomTom's confidence score tells you how much of the reading comes from real vehicles right now vs historical averages:"}),_.jsx(jt,{headers:["Confidence","What It Means"],rows:[["Above 0.9","Very reliable — lots of real-time probe data"],["0.7-0.9","Good — mix of real-time and historical"],["Below 0.7",_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Unreliable"})," — mostly guessing from historical patterns. Don't alert on this."]})]]}),_.jsx("p",{children:"Set minimum confidence to 0.7 to avoid false congestion alerts at night or on rural roads where few probe vehicles drive."}),_.jsx(Te,{children:"Setting Up Corridors"}),_.jsx("p",{children:'Each "corridor" is a point on a road you want to monitor. To add one:'}),_.jsxs("ol",{className:"list-decimal list-inside ml-4 space-y-1",children:[_.jsx("li",{children:"Go to Google Maps, find the road"}),_.jsx("li",{children:`Right-click the road → "What's here?" → copy the coordinates`}),_.jsx("li",{children:"Add the corridor in Config with a name and those coordinates"}),_.jsx("li",{children:"TomTom finds the nearest road segment automatically"})]}),_.jsx(Te,{children:"Getting an API Key"}),_.jsxs("ol",{className:"list-decimal list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:["Sign up at ",_.jsx(Rt,{href:"https://developer.tomtom.com",children:"developer.tomtom.com"})," (free)"]}),_.jsx("li",{children:"Create an app → get your API key"}),_.jsx("li",{children:"Free tier: 2,500 requests/day (plenty for 5-10 corridors)"})]}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://developer.tomtom.com",children:"TomTom Developer Portal"})," — API docs and key signup"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.tomtom.com/traffic-index/",children:"TomTom Traffic Index"})," — city congestion rankings"]})]})]}),_.jsxs(vi,{id:"roads-511",title:"Road Conditions (511)",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"511 systems report road closures, construction, weather events, mountain pass conditions, and incidents. Every state runs their own 511 system — there is no national API."}),_.jsx(Te,{children:"Setting It Up"}),_.jsx("p",{children:"You need to find YOUR state's 511 developer API. MeshAI does not include a default URL because every state is different. Some states have free public APIs, some require registration, and some don't have developer APIs at all."}),_.jsx("p",{children:"Configure in Config → Environmental → 511:"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx("strong",{children:"Base URL"})," — your state's API endpoint"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"API Key"})," — if required by your state"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Endpoints"})," — which data feeds to poll (varies by state)"]})]}),_.jsx(Te,{children:"Learn More"}),_.jsx("p",{children:"Check your state's 511 or DOT website for developer information."})]}),_.jsxs(vi,{id:"mesh-health",title:"Mesh Health",children:[_.jsx(Te,{children:"Health Score"}),_.jsx("p",{children:"MeshAI computes a 0-100 health score for your mesh network by looking at five areas, each weighted differently:"}),_.jsx(jt,{headers:["Pillar","Weight","What It Measures"],rows:[[_.jsx("strong",{children:"Infrastructure"}),"30%","Are your routers online?"],[_.jsx("strong",{children:"Utilization"}),"25%","Is the radio channel congested?"],[_.jsx("strong",{children:"Coverage"}),"20%","Do nodes have redundant paths to gateways?"],[_.jsx("strong",{children:"Behavior"}),"15%","Are any nodes flooding the channel?"],[_.jsx("strong",{children:"Power"}),"10%","Are battery-powered nodes running low?"]]}),_.jsx("p",{children:"The overall score is the weighted sum:"}),_.jsx("p",{className:"p-3 bg-slate-800 rounded font-mono text-sm",children:"Score = (Infrastructure × 30%) + (Utilization × 25%) + (Coverage × 20%) + (Behavior × 15%) + (Power × 10%)"}),_.jsx(Te,{children:"How Each Pillar Is Calculated"}),_.jsx(fl,{children:"Infrastructure (30%)"}),_.jsx("p",{children:"This is the simplest pillar — what percentage of your infrastructure nodes are currently online?"}),_.jsx("p",{className:"p-3 bg-slate-800 rounded font-mono text-sm",children:"(routers online ÷ total routers) × 100"}),_.jsxs("p",{children:["Only nodes with the ",_.jsx($e,{children:"ROUTER"}),", ",_.jsx($e,{children:"ROUTER_LATE"}),", or ",_.jsx($e,{children:"ROUTER_CLIENT"})," role count as infrastructure. Regular client nodes going offline doesn't affect this score. If you have 5 routers and 3 are online, infrastructure scores 60."]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Special case:"})," If you have no routers at all (all clients), this pillar scores 100. You're not penalized for not having infrastructure — you just don't have any to track."]}),_.jsx(fl,{children:"Utilization (25%)"}),_.jsxs("p",{children:["MeshAI reads the channel utilization that each router reports in its telemetry — this is the firmware's own measurement of how busy the radio channel is. MeshAI uses the ",_.jsx("strong",{children:"highest"})," value from any infrastructure node because the busiest router is the bottleneck for the whole mesh."]}),_.jsx("p",{children:_.jsx("strong",{children:"How it works:"})}),_.jsxs("ol",{className:"list-decimal list-inside space-y-1 ml-4",children:[_.jsxs("li",{children:["Collect ",_.jsx($e,{children:"channel_utilization"})," from all infrastructure nodes that report it"]}),_.jsx("li",{children:"If no infra nodes have telemetry, try all nodes"}),_.jsxs("li",{children:["Use the ",_.jsx("strong",{children:"maximum"})," value for scoring (busiest node = bottleneck)"]}),_.jsx("li",{children:"If no nodes report utilization (older firmware), fall back to packet count estimate"})]}),_.jsxs("p",{className:"mt-4",children:[_.jsx("strong",{children:"Fallback method"})," (when telemetry unavailable): estimates from packet counts using 200ms/packet airtime. This is less accurate — it assumes MediumFast preset and sums packets across all nodes."]}),_.jsx(jt,{headers:["Channel Utilization","Score","What It Means"],rows:[["Under 20%","100","Channel is clear — this is the goal"],["20-25%","75-100","Slight degradation, occasional collisions"],["25-35%","50-75","Severe degradation — firmware throttling active"],["35-45%","25-50","Mesh struggling badly — reliability dropping"],["Over 45%","0-25","Mesh is effectively unusable"]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Special case:"})," If no utilization data is available (no telemetry and no packet data), this pillar scores 100. You're not penalized for missing data."]}),_.jsx(fl,{children:"Coverage (20%)"}),_.jsx("p",{children:'Measures gateway redundancy — how many of your data sources can "see" each node. A node reported by all 3 of your gateways has full coverage. A node only seen by 1 gateway is a single point of failure.'}),_.jsxs("p",{className:"p-3 bg-slate-800 rounded font-mono text-sm",children:["coverage_ratio = average_gateways_per_node ÷ total_sources",_.jsx("br",{}),"single_gw_penalty = (single_gateway_nodes ÷ total_nodes) × 40"]}),_.jsx("p",{children:"If a node is seen by 2 out of 3 sources, its coverage ratio is 0.67. Infrastructure nodes with only single-gateway coverage get an extra penalty — they're critical but have no backup path."}),_.jsx(jt,{headers:["Coverage Ratio","Base Score","After Penalty"],rows:[["100% (all sources)","100","100 minus single-gw penalty"],["70-99%","90","Minus penalties"],["50-69%","70","Minus penalties"],["Under 50%","50 or less","Heavy penalty"]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Special case:"})," With only 1 data source, this pillar can't score well — there's no redundancy to measure. Coverage becomes meaningful when you have 2+ sources (MeshMonitor + MQTT, multiple gateways, etc.)."]}),_.jsx(fl,{children:"Behavior (15%)"}),_.jsx("p",{children:"Counts how many nodes are sending an unusually high number of non-text packets. This catches firmware bugs, stuck transmitters, and misconfigured nodes that are flooding the channel."}),_.jsxs("p",{children:[_.jsx("strong",{children:"What counts as flooding:"})," More than 500 non-text packets in 24 hours. Text messages don't count — the behavior pillar only flags telemetry, position, and routing packet floods."]}),_.jsx(jt,{headers:["Flagged Nodes","Score"],rows:[["0","100"],["1","80"],["2-3","60"],["4-5","40"],["6+","20"]]}),_.jsx("p",{children:"A single misbehaving node only drops the score to 80. It takes multiple problem nodes to seriously hurt the behavior pillar."}),_.jsx(fl,{children:"Power (10%)"}),_.jsx("p",{children:"Measures what fraction of battery-powered nodes are below the warning threshold (default 20%)."}),_.jsx("p",{className:"p-3 bg-slate-800 rounded font-mono text-sm",children:"100 × (1 − low_battery_nodes ÷ total_battery_nodes)"}),_.jsx("p",{children:"If 2 out of 10 battery nodes are below 20%, power scores 80."}),_.jsxs("p",{children:[_.jsx("strong",{children:"Important:"})," USB-powered nodes are excluded from this calculation. Many nodes report 100% battery even when running on wall power with no battery installed. Only nodes actually running on batteries affect this pillar."]}),_.jsx(Te,{children:"Health Tiers"}),_.jsx(jt,{headers:["Score","Tier","What It Means"],rows:[["90-100",_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"green"})," Healthy"]}),"Everything's working well."],["75-89",_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"yellow"})," Slight degradation"]}),"Some issues but the mesh is functional."],["50-74",_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"orange"})," Unhealthy"]}),"Multiple problems. Reliability is affected."],["25-49",_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"red"})," Warning"]}),"Significant issues. The mesh is struggling."],["0-24",_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"black"})," Critical"]}),"Major failures. Barely functional."]]}),_.jsx(Te,{children:"Channel Utilization — Is the Radio Channel Full?"}),_.jsx("p",{children:"Meshtastic radios share one LoRa channel. If too many nodes are transmitting too often, they step on each other and messages get lost."}),_.jsx(jt,{headers:["Utilization","What's Happening"],rows:[[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"green"})," Under 25%"]}),"Healthy. The firmware itself starts throttling above 25% to protect the channel — so under 25% is the target."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"yellow"})," 25-40%"]}),"Getting busy. Common on larger meshes. Worth watching."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"orange"})," 40-50%"]}),"Congested. The firmware throttles GPS updates above 40%. Messages are colliding and retrying."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"red"})," Over 50%"]}),"Serious problem. More time is spent retrying than communicating. Mesh reliability drops fast."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"black"})," Over 65%"]}),"Documented failure point on busy LONG_FAST meshes. The mesh becomes unusable."]]}),_.jsx(Te,{children:"Packet Flooding"}),_.jsx("p",{className:"p-3 bg-yellow-500/10 border border-yellow-500/30 rounded text-yellow-200",children:_.jsx("strong",{children:'⚠️ "Packet flooding" means a node sending too many RADIO PACKETS. This has nothing to do with water flooding.'})}),_.jsx("p",{children:"A normal Meshtastic node sends a packet every few minutes (announcing itself, reporting telemetry, updating position). If a node starts blasting packets every few seconds, something is wrong — firmware bug, stuck transmitter, or misconfiguration."}),_.jsx(jt,{headers:["Packets per Minute","What It Means"],rows:[["1-5","Normal"],["5-10","Elevated — might be someone chatting a lot"],["10-20","Suspicious — worth investigating"],["Over 30","Something is broken. This node is actively hurting the mesh."]]}),_.jsx(Te,{children:"Battery Levels"}),_.jsx("p",{children:"Most Meshtastic radios (T-Beam, RAK4631, Heltec V3) use a single lithium battery cell. The voltage tells you how much charge is left:"}),_.jsx(jt,{headers:["Voltage","Charge","What To Do"],rows:[["4.20V","100%","Full"],["3.80V","~60%","Fine"],[_.jsx("strong",{children:"3.60V"}),_.jsx("strong",{children:"~30%"}),_.jsx(_.Fragment,{children:_.jsx("strong",{children:"⚠️ Warning — charge it soon"})})],[_.jsx("strong",{children:"3.50V"}),_.jsx("strong",{children:"~15%"}),_.jsx(_.Fragment,{children:_.jsx("strong",{children:"🔴 Low — charge it now"})})],[_.jsx("strong",{children:"3.40V"}),_.jsx("strong",{children:"~7%"}),_.jsx(_.Fragment,{children:_.jsx("strong",{children:"⚫ About to die"})})],["3.30V","~3%","Device shutting down"]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"USB-powered nodes"})," report 100% battery even if there's no battery installed. Battery alerts only matter for nodes actually running on battery power."]}),_.jsx(Te,{children:"Node Offline Detection"}),_.jsx("p",{children:`MeshAI marks a node as "offline" when it hasn't been heard for a configurable time period. Different node types need different thresholds:`}),_.jsx(jt,{headers:["Node Type","Recommended Threshold","Why"],rows:[["Fixed infrastructure (wall power)",_.jsx("strong",{children:"2 hours"}),"These should always be transmitting. 2 hours of silence means something is wrong."],["Fixed client (wall power)","2-4 hours","Same logic, slightly more lenient."],["Mobile / vehicle","4-8 hours","They go behind mountains, into garages, out of range. Normal."],["Solar-powered","12-24 hours","May shut down at night when solar stops charging."]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Rule of thumb"}),`: set the threshold to about 4× the node's beacon interval. Too tight and nodes will constantly flap "offline/online" from normal gaps. Too loose and real outages go unnoticed.`]})]}),_.jsxs(vi,{id:"notifications",title:"Notifications",children:[_.jsx(Te,{children:"How It Works"}),_.jsxs("ol",{className:"list-decimal list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx("strong",{children:"Something happens"})," — a fire is detected, weather warning issued, node goes offline, etc."]}),_.jsxs("li",{children:[_.jsx("strong",{children:"MeshAI checks your rules"})," — does this event match any of your notification rules? Is it severe enough? Are we in quiet hours?"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"If a rule matches"})," — MeshAI sends the notification through whatever delivery method that rule is configured for."]})]}),_.jsx(Te,{children:"Building Rules"}),_.jsx("p",{children:"Each rule answers three questions:"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx("strong",{children:"WHEN"})," does it trigger? (which categories, what severity)"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"WHERE"})," does it send? (mesh broadcast, email, webhook, etc.)"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"HOW OFTEN"})," at most? (cooldown period)"]})]}),_.jsx("p",{children:'Use "Add from Template" to start with a pre-built rule and customize it, or build from scratch with "Add Rule."'}),_.jsx(Te,{children:"Severity Levels — What Should I Set?"}),_.jsx(jt,{headers:["Level","When It's Used","Notification Volume"],rows:[["Info","Routine stuff (ducting detected, new router appeared)","High — lots of messages"],["Advisory","Worth knowing (weather advisory, slow traffic, battery declining)","Moderate"],["Watch","Pay attention (fire within 50km, weather watch, stream rising)","Low-moderate"],[_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Warning"})," ✓"]}),"Take action (fire within 15km, severe weather, critical battery)","Low — recommended for most rules"],["Emergency","Life safety (extreme weather, fire at infrastructure, total blackout)","Very rare"]]}),_.jsxs("p",{children:[_.jsx("strong",{children:'"Warning" is the sweet spot for most rules.'})," You get alerted when something actually needs your attention without being overwhelmed by every minor event."]}),_.jsx(Te,{children:"Quiet Hours"}),_.jsx("p",{children:'When enabled, non-emergency notifications are held during sleeping hours (default 10pm-6am). Emergency alerts and rules marked "Override Quiet Hours" always get through.'}),_.jsx("p",{children:"You can turn quiet hours off entirely if you don't want them."}),_.jsx(Te,{children:"Webhook — The Swiss Army Knife"}),_.jsx("p",{children:"A webhook sends your alert as an HTTP POST to any URL. This one delivery method works with:"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx("strong",{children:"Discord"})," — use a Discord webhook URL"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Slack"})," — use a Slack incoming webhook URL"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"ntfy.sh"})," — POST to ",_.jsx($e,{children:"https://ntfy.sh/your-topic"})]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Pushover"})," — POST to the Pushover API"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Home Assistant"})," — POST to an automation webhook URL"]}),_.jsx("li",{children:"Anything else that accepts HTTP POST"})]}),_.jsx("p",{children:"MeshAI doesn't need to know what's on the other end. Give it the URL and it works."})]}),_.jsxs(vi,{id:"commands",title:"Commands",children:[_.jsxs("p",{children:["All commands use the ",_.jsx($e,{children:"!"})," prefix (configurable). Send these as a direct message to MeshAI on your mesh."]}),_.jsx(Te,{children:"Basic Commands"}),_.jsx(jt,{headers:["Command","What It Does"],rows:[[_.jsx($e,{children:"!help"}),"Shows all available commands"],[_.jsx($e,{children:"!ping"}),"Tests if the bot is alive"],[_.jsx($e,{children:"!status"}),"Quick mesh summary (nodes online, health score)"],[_.jsx($e,{children:"!health"}),"Detailed health report with pillar scores"],[_.jsx($e,{children:"!weather"}),"Current weather for your area"]]}),_.jsx(Te,{children:"Environmental Commands"}),_.jsx(jt,{headers:["Command","What It Does"],rows:[[_.jsx($e,{children:"!alerts"}),"Active NWS weather alerts for your area"],[_.jsxs(_.Fragment,{children:[_.jsx($e,{children:"!solar"})," (or ",_.jsx($e,{children:"!hf"}),")"]}),"Current solar indices and RF conditions"],[_.jsx($e,{children:"!fire"}),"Active wildfires near your mesh"],[_.jsx($e,{children:"!avy"}),'Avalanche advisory (seasonal — shows "off season" in summer)'],[_.jsxs(_.Fragment,{children:[_.jsx($e,{children:"!streams"})," (or ",_.jsx($e,{children:"!gauges"}),")"]}),"Stream gauge readings"],[_.jsxs(_.Fragment,{children:[_.jsx($e,{children:"!roads"})," (or ",_.jsx($e,{children:"!traffic"}),")"]}),"Road conditions and traffic flow"],[_.jsx($e,{children:"!hotspots"}),"Satellite fire detections"]]}),_.jsx(Te,{children:"Subscription Commands"}),_.jsx(jt,{headers:["Command","What It Does"],rows:[[_.jsx($e,{children:"!subscribe"}),"Lists all alert categories you can subscribe to"],[_.jsx($e,{children:"!subscribe fire_proximity"}),"Subscribe to a specific category"],[_.jsx($e,{children:"!subscribe all"}),"Subscribe to everything"],[_.jsx($e,{children:"!unsubscribe fire_proximity"}),"Unsubscribe from a category"],[_.jsx($e,{children:"!subscriptions"}),"Shows what you're currently subscribed to"]]}),_.jsx(Te,{children:"Conversational"}),_.jsx("p",{children:`MeshAI isn't just commands — you can ask it questions in plain English. "How's the mesh doing?" "Is there any ducting?" "What's the fire situation?" "How's traffic on I-84?" It uses the live environmental data and mesh health data to answer.`})]}),_.jsxs(vi,{id:"api",title:"API Reference",children:[_.jsxs("p",{children:["MeshAI's REST API is available at ",_.jsx($e,{children:"http://your-host:8080"}),". All endpoints return JSON."]}),_.jsx(Te,{children:"System"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/status"})," — version, uptime, node count"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/channels"})," — radio channel list"]}),_.jsxs("li",{children:[_.jsx($e,{children:"POST /api/restart"})," — restart the bot"]})]}),_.jsx(Te,{children:"Mesh Data"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/health"})," — health score and pillars"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/nodes"})," — all nodes with positions and telemetry"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/edges"})," — neighbor links with signal quality"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/regions"})," — region summaries"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/sources"})," — data source health"]})]}),_.jsx(Te,{children:"Configuration"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/config"})," — full config"]}),_.jsxs("li",{children:[_.jsxs($e,{children:["GET /api/config/","{section}"]})," — one section"]}),_.jsxs("li",{children:[_.jsxs($e,{children:["PUT /api/config/","{section}"]})," — update a section"]})]}),_.jsx(Te,{children:"Environmental"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/env/status"})," — per-feed health"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/env/active"})," — all active events"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/env/swpc"})," — solar/geomagnetic data"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/env/ducting"})," — atmospheric profile"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/env/fires"})," — wildfire perimeters"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/env/hotspots"})," — satellite fire detections"]})]}),_.jsx(Te,{children:"Alerts"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/alerts/active"})," — current alerts"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/alerts/history"})," — past alerts"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/notifications/categories"})," — available alert categories"]})]}),_.jsx(Te,{children:"Real-time"}),_.jsx("ul",{className:"list-disc list-inside ml-4 space-y-1",children:_.jsxs("li",{children:[_.jsx($e,{children:"ws://your-host:8080/ws/live"})," — WebSocket for live updates"]})})]})]})})]})}function set(){return _.jsx(Sce,{children:_.jsx(Ace,{children:_.jsxs(Due,{children:[_.jsx(hl,{path:"/",element:_.jsx(aNe,{})}),_.jsx(hl,{path:"/mesh",element:_.jsx(wJe,{})}),_.jsx(hl,{path:"/environment",element:_.jsx(UJe,{})}),_.jsx(hl,{path:"/config",element:_.jsx(VJe,{})}),_.jsx(hl,{path:"/alerts",element:_.jsx(eet,{})}),_.jsx(hl,{path:"/notifications",element:_.jsx(aet,{})}),_.jsx(hl,{path:"/reference",element:_.jsx(oet,{})})]})})})}pP.createRoot(document.getElementById("root")).render(_.jsx(J.StrictMode,{children:_.jsx(Fue,{children:_.jsx(set,{})})})); + */(function(e,t){(function(r,n){n(t)})(cg,function(r){var n="1.9.4";function i(p){var w,A,O,R;for(A=1,O=arguments.length;A"u"||!L||!L.Mixin)){p=b(p)?p:[p];for(var w=0;w0?Math.floor(p):Math.ceil(p)};F.prototype={clone:function(){return new F(this.x,this.y)},add:function(p){return this.clone()._add(V(p))},_add:function(p){return this.x+=p.x,this.y+=p.y,this},subtract:function(p){return this.clone()._subtract(V(p))},_subtract:function(p){return this.x-=p.x,this.y-=p.y,this},divideBy:function(p){return this.clone()._divideBy(p)},_divideBy:function(p){return this.x/=p,this.y/=p,this},multiplyBy:function(p){return this.clone()._multiplyBy(p)},_multiplyBy:function(p){return this.x*=p,this.y*=p,this},scaleBy:function(p){return new F(this.x*p.x,this.y*p.y)},unscaleBy:function(p){return new F(this.x/p.x,this.y/p.y)},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.clone()._ceil()},_ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},trunc:function(){return this.clone()._trunc()},_trunc:function(){return this.x=G(this.x),this.y=G(this.y),this},distanceTo:function(p){p=V(p);var w=p.x-this.x,A=p.y-this.y;return Math.sqrt(w*w+A*A)},equals:function(p){return p=V(p),p.x===this.x&&p.y===this.y},contains:function(p){return p=V(p),Math.abs(p.x)<=Math.abs(this.x)&&Math.abs(p.y)<=Math.abs(this.y)},toString:function(){return"Point("+h(this.x)+", "+h(this.y)+")"}};function V(p,w,A){return p instanceof F?p:b(p)?new F(p[0],p[1]):p==null?p:typeof p=="object"&&"x"in p&&"y"in p?new F(p.x,p.y):new F(p,w,A)}function H(p,w){if(p)for(var A=w?[p,w]:p,O=0,R=A.length;O=this.min.x&&A.x<=this.max.x&&w.y>=this.min.y&&A.y<=this.max.y},intersects:function(p){p=Y(p);var w=this.min,A=this.max,O=p.min,R=p.max,W=R.x>=w.x&&O.x<=A.x,X=R.y>=w.y&&O.y<=A.y;return W&&X},overlaps:function(p){p=Y(p);var w=this.min,A=this.max,O=p.min,R=p.max,W=R.x>w.x&&O.xw.y&&O.y=w.lat&&R.lat<=A.lat&&O.lng>=w.lng&&R.lng<=A.lng},intersects:function(p){p=ee(p);var w=this._southWest,A=this._northEast,O=p.getSouthWest(),R=p.getNorthEast(),W=R.lat>=w.lat&&O.lat<=A.lat,X=R.lng>=w.lng&&O.lng<=A.lng;return W&&X},overlaps:function(p){p=ee(p);var w=this._southWest,A=this._northEast,O=p.getSouthWest(),R=p.getNorthEast(),W=R.lat>w.lat&&O.latw.lng&&O.lng1,aae=function(){var p=!1;try{var w=Object.defineProperty({},"passive",{get:function(){p=!0}});window.addEventListener("testPassiveEventSupport",f,w),window.removeEventListener("testPassiveEventSupport",f,w)}catch{}return p}(),oae=function(){return!!document.createElement("canvas").getContext}(),uC=!!(document.createElementNS&&Ge("svg").createSVGRect),sae=!!uC&&function(){var p=document.createElement("div");return p.innerHTML="",(p.firstChild&&p.firstChild.namespaceURI)==="http://www.w3.org/2000/svg"}(),lae=!uC&&function(){try{var p=document.createElement("div");p.innerHTML='';var w=p.firstChild;return w.style.behavior="url(#default#VML)",w&&typeof w.adj=="object"}catch{return!1}}(),uae=navigator.platform.indexOf("Mac")===0,cae=navigator.platform.indexOf("Linux")===0;function eo(p){return navigator.userAgent.toLowerCase().indexOf(p)>=0}var Ue={ie:Ft,ielt9:rr,edge:Nn,webkit:Xr,android:qn,android23:Df,androidStock:C0,opera:sC,chrome:ZR,gecko:YR,safari:qie,phantom:XR,opera12:qR,win:Kie,ie3d:KR,webkit3d:lC,gecko3d:QR,any3d:Qie,mobile:tp,mobileWebkit:Jie,mobileWebkit3d:eae,msPointer:JR,pointer:e5,touch:tae,touchNative:t5,mobileOpera:rae,mobileGecko:nae,retina:iae,passiveEvents:aae,canvas:oae,svg:uC,vml:lae,inlineSvg:sae,mac:uae,linux:cae},r5=Ue.msPointer?"MSPointerDown":"pointerdown",n5=Ue.msPointer?"MSPointerMove":"pointermove",i5=Ue.msPointer?"MSPointerUp":"pointerup",a5=Ue.msPointer?"MSPointerCancel":"pointercancel",cC={touchstart:r5,touchmove:n5,touchend:i5,touchcancel:a5},o5={touchstart:gae,touchmove:A0,touchend:A0,touchcancel:A0},Nf={},s5=!1;function fae(p,w,A){return w==="touchstart"&&pae(),o5[w]?(A=o5[w].bind(this,A),p.addEventListener(cC[w],A,!1),A):(console.warn("wrong event specified:",w),f)}function hae(p,w,A){if(!cC[w]){console.warn("wrong event specified:",w);return}p.removeEventListener(cC[w],A,!1)}function dae(p){Nf[p.pointerId]=p}function vae(p){Nf[p.pointerId]&&(Nf[p.pointerId]=p)}function l5(p){delete Nf[p.pointerId]}function pae(){s5||(document.addEventListener(r5,dae,!0),document.addEventListener(n5,vae,!0),document.addEventListener(i5,l5,!0),document.addEventListener(a5,l5,!0),s5=!0)}function A0(p,w){if(w.pointerType!==(w.MSPOINTER_TYPE_MOUSE||"mouse")){w.touches=[];for(var A in Nf)w.touches.push(Nf[A]);w.changedTouches=[w],p(w)}}function gae(p,w){w.MSPOINTER_TYPE_TOUCH&&w.pointerType===w.MSPOINTER_TYPE_TOUCH&&ln(w),A0(p,w)}function mae(p){var w={},A,O;for(O in p)A=p[O],w[O]=A&&A.bind?A.bind(p):A;return p=w,w.type="dblclick",w.detail=2,w.isTrusted=!1,w._simulated=!0,w}var yae=200;function _ae(p,w){p.addEventListener("dblclick",w);var A=0,O;function R(W){if(W.detail!==1){O=W.detail;return}if(!(W.pointerType==="mouse"||W.sourceCapabilities&&!W.sourceCapabilities.firesTouchEvents)){var X=d5(W);if(!(X.some(function(oe){return oe instanceof HTMLLabelElement&&oe.attributes.for})&&!X.some(function(oe){return oe instanceof HTMLInputElement||oe instanceof HTMLSelectElement}))){var re=Date.now();re-A<=yae?(O++,O===2&&w(mae(W))):O=1,A=re}}}return p.addEventListener("click",R),{dblclick:w,simDblclick:R}}function xae(p,w){p.removeEventListener("dblclick",w.dblclick),p.removeEventListener("click",w.simDblclick)}var fC=L0(["transform","webkitTransform","OTransform","MozTransform","msTransform"]),rp=L0(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),u5=rp==="webkitTransition"||rp==="OTransition"?rp+"End":"transitionend";function c5(p){return typeof p=="string"?document.getElementById(p):p}function np(p,w){var A=p.style[w]||p.currentStyle&&p.currentStyle[w];if((!A||A==="auto")&&document.defaultView){var O=document.defaultView.getComputedStyle(p,null);A=O?O[w]:null}return A==="auto"?null:A}function Ct(p,w,A){var O=document.createElement(p);return O.className=w||"",A&&A.appendChild(O),O}function nr(p){var w=p.parentNode;w&&w.removeChild(p)}function M0(p){for(;p.firstChild;)p.removeChild(p.firstChild)}function jf(p){var w=p.parentNode;w&&w.lastChild!==p&&w.appendChild(p)}function Rf(p){var w=p.parentNode;w&&w.firstChild!==p&&w.insertBefore(p,w.firstChild)}function hC(p,w){if(p.classList!==void 0)return p.classList.contains(w);var A=P0(p);return A.length>0&&new RegExp("(^|\\s)"+w+"(\\s|$)").test(A)}function ut(p,w){if(p.classList!==void 0)for(var A=v(w),O=0,R=A.length;O0?2*window.devicePixelRatio:1;function p5(p){return Ue.edge?p.wheelDeltaY/2:p.deltaY&&p.deltaMode===0?-p.deltaY/Sae:p.deltaY&&p.deltaMode===1?-p.deltaY*20:p.deltaY&&p.deltaMode===2?-p.deltaY*60:p.deltaX||p.deltaZ?0:p.wheelDelta?(p.wheelDeltaY||p.wheelDelta)/2:p.detail&&Math.abs(p.detail)<32765?-p.detail*20:p.detail?p.detail/-32765*60:0}function TC(p,w){var A=w.relatedTarget;if(!A)return!0;try{for(;A&&A!==p;)A=A.parentNode}catch{return!1}return A!==p}var Tae={__proto__:null,on:st,off:Wt,stopPropagation:Pu,disableScrollPropagation:SC,disableClickPropagation:sp,preventDefault:ln,stop:Lu,getPropagationPath:d5,getMousePosition:v5,getWheelDelta:p5,isExternalTarget:TC,addListener:st,removeListener:Wt},g5=Z.extend({run:function(p,w,A,O){this.stop(),this._el=p,this._inProgress=!0,this._duration=A||.25,this._easeOutPower=1/Math.max(O||.5,.2),this._startPos=Mu(p),this._offset=w.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(!0),this._complete())},_animate:function(){this._animId=E(this._animate,this),this._step()},_step:function(p){var w=+new Date-this._startTime,A=this._duration*1e3;wthis.options.maxZoom)?this.setZoom(p):this},panInsideBounds:function(p,w){this._enforcingBounds=!0;var A=this.getCenter(),O=this._limitCenter(A,this._zoom,ee(p));return A.equals(O)||this.panTo(O,w),this._enforcingBounds=!1,this},panInside:function(p,w){w=w||{};var A=V(w.paddingTopLeft||w.padding||[0,0]),O=V(w.paddingBottomRight||w.padding||[0,0]),R=this.project(this.getCenter()),W=this.project(p),X=this.getPixelBounds(),re=Y([X.min.add(A),X.max.subtract(O)]),oe=re.getSize();if(!re.contains(W)){this._enforcingBounds=!0;var de=W.subtract(re.getCenter()),Ee=re.extend(W).getSize().subtract(oe);R.x+=de.x<0?-Ee.x:Ee.x,R.y+=de.y<0?-Ee.y:Ee.y,this.panTo(this.unproject(R),w),this._enforcingBounds=!1}return this},invalidateSize:function(p){if(!this._loaded)return this;p=i({animate:!1,pan:!0},p===!0?{animate:!0}:p);var w=this.getSize();this._sizeChanged=!0,this._lastCenter=null;var A=this.getSize(),O=w.divideBy(2).round(),R=A.divideBy(2).round(),W=O.subtract(R);return!W.x&&!W.y?this:(p.animate&&p.pan?this.panBy(W):(p.pan&&this._rawPanBy(W),this.fire("move"),p.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(o(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:w,newSize:A}))},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(p){if(p=this._locateOptions=i({timeout:1e4,watch:!1},p),!("geolocation"in navigator))return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var w=o(this._handleGeolocationResponse,this),A=o(this._handleGeolocationError,this);return p.watch?this._locationWatchId=navigator.geolocation.watchPosition(w,A,p):navigator.geolocation.getCurrentPosition(w,A,p),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(p){if(this._container._leaflet_id){var w=p.code,A=p.message||(w===1?"permission denied":w===2?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:w,message:"Geolocation error: "+A+"."})}},_handleGeolocationResponse:function(p){if(this._container._leaflet_id){var w=p.coords.latitude,A=p.coords.longitude,O=new le(w,A),R=O.toBounds(p.coords.accuracy*2),W=this._locateOptions;if(W.setView){var X=this.getBoundsZoom(R);this.setView(O,W.maxZoom?Math.min(X,W.maxZoom):X)}var re={latlng:O,bounds:R,timestamp:p.timestamp};for(var oe in p.coords)typeof p.coords[oe]=="number"&&(re[oe]=p.coords[oe]);this.fire("locationfound",re)}},addHandler:function(p,w){if(!w)return this;var A=this[p]=new w(this);return this._handlers.push(A),this.options[p]&&A.enable(),this},remove:function(){if(this._initEvents(!0),this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch{this._container._leaflet_id=void 0,this._containerId=void 0}this._locationWatchId!==void 0&&this.stopLocate(),this._stop(),nr(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._resizeRequest&&(D(this._resizeRequest),this._resizeRequest=null),this._clearHandlers(),this._loaded&&this.fire("unload");var p;for(p in this._layers)this._layers[p].remove();for(p in this._panes)nr(this._panes[p]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(p,w){var A="leaflet-pane"+(p?" leaflet-"+p.replace("Pane","")+"-pane":""),O=Ct("div",A,w||this._mapPane);return p&&(this._panes[p]=O),O},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter.clone():this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var p=this.getPixelBounds(),w=this.unproject(p.getBottomLeft()),A=this.unproject(p.getTopRight());return new K(w,A)},getMinZoom:function(){return this.options.minZoom===void 0?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return this.options.maxZoom===void 0?this._layersMaxZoom===void 0?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(p,w,A){p=ee(p),A=V(A||[0,0]);var O=this.getZoom()||0,R=this.getMinZoom(),W=this.getMaxZoom(),X=p.getNorthWest(),re=p.getSouthEast(),oe=this.getSize().subtract(A),de=Y(this.project(re,O),this.project(X,O)).getSize(),Ee=Ue.any3d?this.options.zoomSnap:1,Je=oe.x/de.x,pt=oe.y/de.y,jn=w?Math.max(Je,pt):Math.min(Je,pt);return O=this.getScaleZoom(jn,O),Ee&&(O=Math.round(O/(Ee/100))*(Ee/100),O=w?Math.ceil(O/Ee)*Ee:Math.floor(O/Ee)*Ee),Math.max(R,Math.min(W,O))},getSize:function(){return(!this._size||this._sizeChanged)&&(this._size=new F(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(p,w){var A=this._getTopLeftPoint(p,w);return new H(A,A.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(p){return this.options.crs.getProjectedBounds(p===void 0?this.getZoom():p)},getPane:function(p){return typeof p=="string"?this._panes[p]:p},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(p,w){var A=this.options.crs;return w=w===void 0?this._zoom:w,A.scale(p)/A.scale(w)},getScaleZoom:function(p,w){var A=this.options.crs;w=w===void 0?this._zoom:w;var O=A.zoom(p*A.scale(w));return isNaN(O)?1/0:O},project:function(p,w){return w=w===void 0?this._zoom:w,this.options.crs.latLngToPoint(fe(p),w)},unproject:function(p,w){return w=w===void 0?this._zoom:w,this.options.crs.pointToLatLng(V(p),w)},layerPointToLatLng:function(p){var w=V(p).add(this.getPixelOrigin());return this.unproject(w)},latLngToLayerPoint:function(p){var w=this.project(fe(p))._round();return w._subtract(this.getPixelOrigin())},wrapLatLng:function(p){return this.options.crs.wrapLatLng(fe(p))},wrapLatLngBounds:function(p){return this.options.crs.wrapLatLngBounds(ee(p))},distance:function(p,w){return this.options.crs.distance(fe(p),fe(w))},containerPointToLayerPoint:function(p){return V(p).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(p){return V(p).add(this._getMapPanePos())},containerPointToLatLng:function(p){var w=this.containerPointToLayerPoint(V(p));return this.layerPointToLatLng(w)},latLngToContainerPoint:function(p){return this.layerPointToContainerPoint(this.latLngToLayerPoint(fe(p)))},mouseEventToContainerPoint:function(p){return v5(p,this._container)},mouseEventToLayerPoint:function(p){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(p))},mouseEventToLatLng:function(p){return this.layerPointToLatLng(this.mouseEventToLayerPoint(p))},_initContainer:function(p){var w=this._container=c5(p);if(w){if(w._leaflet_id)throw new Error("Map container is already initialized.")}else throw new Error("Map container not found.");st(w,"scroll",this._onScroll,this),this._containerId=l(w)},_initLayout:function(){var p=this._container;this._fadeAnimated=this.options.fadeAnimation&&Ue.any3d,ut(p,"leaflet-container"+(Ue.touch?" leaflet-touch":"")+(Ue.retina?" leaflet-retina":"")+(Ue.ielt9?" leaflet-oldie":"")+(Ue.safari?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":""));var w=np(p,"position");w!=="absolute"&&w!=="relative"&&w!=="fixed"&&w!=="sticky"&&(p.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var p=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),Ar(this._mapPane,new F(0,0)),this.createPane("tilePane"),this.createPane("overlayPane"),this.createPane("shadowPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(ut(p.markerPane,"leaflet-zoom-hide"),ut(p.shadowPane,"leaflet-zoom-hide"))},_resetView:function(p,w,A){Ar(this._mapPane,new F(0,0));var O=!this._loaded;this._loaded=!0,w=this._limitZoom(w),this.fire("viewprereset");var R=this._zoom!==w;this._moveStart(R,A)._move(p,w)._moveEnd(R),this.fire("viewreset"),O&&this.fire("load")},_moveStart:function(p,w){return p&&this.fire("zoomstart"),w||this.fire("movestart"),this},_move:function(p,w,A,O){w===void 0&&(w=this._zoom);var R=this._zoom!==w;return this._zoom=w,this._lastCenter=p,this._pixelOrigin=this._getNewPixelOrigin(p),O?A&&A.pinch&&this.fire("zoom",A):((R||A&&A.pinch)&&this.fire("zoom",A),this.fire("move",A)),this},_moveEnd:function(p){return p&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return D(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(p){Ar(this._mapPane,this._getMapPanePos().subtract(p))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(p){this._targets={},this._targets[l(this._container)]=this;var w=p?Wt:st;w(this._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress keydown keyup",this._handleDOMEvent,this),this.options.trackResize&&w(window,"resize",this._onResize,this),Ue.any3d&&this.options.transform3DLimit&&(p?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){D(this._resizeRequest),this._resizeRequest=E(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var p=this._getMapPanePos();Math.max(Math.abs(p.x),Math.abs(p.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(p,w){for(var A=[],O,R=w==="mouseout"||w==="mouseover",W=p.target||p.srcElement,X=!1;W;){if(O=this._targets[l(W)],O&&(w==="click"||w==="preclick")&&this._draggableMoved(O)){X=!0;break}if(O&&O.listens(w,!0)&&(R&&!TC(W,p)||(A.push(O),R))||W===this._container)break;W=W.parentNode}return!A.length&&!X&&!R&&this.listens(w,!0)&&(A=[this]),A},_isClickDisabled:function(p){for(;p&&p!==this._container;){if(p._leaflet_disable_click)return!0;p=p.parentNode}},_handleDOMEvent:function(p){var w=p.target||p.srcElement;if(!(!this._loaded||w._leaflet_disable_events||p.type==="click"&&this._isClickDisabled(w))){var A=p.type;A==="mousedown"&&yC(w),this._fireDOMEvent(p,A)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(p,w,A){if(p.type==="click"){var O=i({},p);O.type="preclick",this._fireDOMEvent(O,O.type,A)}var R=this._findEventTargets(p,w);if(A){for(var W=[],X=0;X0?Math.round(p-w)/2:Math.max(0,Math.ceil(p))-Math.max(0,Math.floor(w))},_limitZoom:function(p){var w=this.getMinZoom(),A=this.getMaxZoom(),O=Ue.any3d?this.options.zoomSnap:1;return O&&(p=Math.round(p/O)*O),Math.max(w,Math.min(A,p))},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){xr(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(p,w){var A=this._getCenterOffset(p)._trunc();return(w&&w.animate)!==!0&&!this.getSize().contains(A)?!1:(this.panBy(A,w),!0)},_createAnimProxy:function(){var p=this._proxy=Ct("div","leaflet-proxy leaflet-zoom-animated");this._panes.mapPane.appendChild(p),this.on("zoomanim",function(w){var A=fC,O=this._proxy.style[A];Au(this._proxy,this.project(w.center,w.zoom),this.getZoomScale(w.zoom,1)),O===this._proxy.style[A]&&this._animatingZoom&&this._onZoomTransitionEnd()},this),this.on("load moveend",this._animMoveEnd,this),this._on("unload",this._destroyAnimProxy,this)},_destroyAnimProxy:function(){nr(this._proxy),this.off("load moveend",this._animMoveEnd,this),delete this._proxy},_animMoveEnd:function(){var p=this.getCenter(),w=this.getZoom();Au(this._proxy,this.project(p,w),this.getZoomScale(w,1))},_catchTransitionEnd:function(p){this._animatingZoom&&p.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(p,w,A){if(this._animatingZoom)return!0;if(A=A||{},!this._zoomAnimated||A.animate===!1||this._nothingToAnimate()||Math.abs(w-this._zoom)>this.options.zoomAnimationThreshold)return!1;var O=this.getZoomScale(w),R=this._getCenterOffset(p)._divideBy(1-1/O);return A.animate!==!0&&!this.getSize().contains(R)?!1:(E(function(){this._moveStart(!0,A.noMoveStart||!1)._animateZoom(p,w,!0)},this),!0)},_animateZoom:function(p,w,A,O){this._mapPane&&(A&&(this._animatingZoom=!0,this._animateToCenter=p,this._animateToZoom=w,ut(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:p,zoom:w,noUpdate:O}),this._tempFireZoomEvent||(this._tempFireZoomEvent=this._zoom!==this._animateToZoom),this._move(this._animateToCenter,this._animateToZoom,void 0,!0),setTimeout(o(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&xr(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom,void 0,!0),this._tempFireZoomEvent&&this.fire("zoom"),delete this._tempFireZoomEvent,this.fire("move"),this._moveEnd(!0))}});function Cae(p,w){return new wt(p,w)}var ba=N.extend({options:{position:"topright"},initialize:function(p){g(this,p)},getPosition:function(){return this.options.position},setPosition:function(p){var w=this._map;return w&&w.removeControl(this),this.options.position=p,w&&w.addControl(this),this},getContainer:function(){return this._container},addTo:function(p){this.remove(),this._map=p;var w=this._container=this.onAdd(p),A=this.getPosition(),O=p._controlCorners[A];return ut(w,"leaflet-control"),A.indexOf("bottom")!==-1?O.insertBefore(w,O.firstChild):O.appendChild(w),this._map.on("unload",this.remove,this),this},remove:function(){return this._map?(nr(this._container),this.onRemove&&this.onRemove(this._map),this._map.off("unload",this.remove,this),this._map=null,this):this},_refocusOnMap:function(p){this._map&&p&&p.screenX>0&&p.screenY>0&&this._map.getContainer().focus()}}),lp=function(p){return new ba(p)};wt.include({addControl:function(p){return p.addTo(this),this},removeControl:function(p){return p.remove(),this},_initControlPos:function(){var p=this._controlCorners={},w="leaflet-",A=this._controlContainer=Ct("div",w+"control-container",this._container);function O(R,W){var X=w+R+" "+w+W;p[R+W]=Ct("div",X,A)}O("top","left"),O("top","right"),O("bottom","left"),O("bottom","right")},_clearControlPos:function(){for(var p in this._controlCorners)nr(this._controlCorners[p]);nr(this._controlContainer),delete this._controlCorners,delete this._controlContainer}});var m5=ba.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0,hideSingleBase:!1,sortLayers:!1,sortFunction:function(p,w,A,O){return A1,this._baseLayersList.style.display=p?"":"none"),this._separator.style.display=w&&p?"":"none",this},_onLayerChange:function(p){this._handlingClick||this._update();var w=this._getLayer(l(p.target)),A=w.overlay?p.type==="add"?"overlayadd":"overlayremove":p.type==="add"?"baselayerchange":null;A&&this._map.fire(A,w)},_createRadioElement:function(p,w){var A='",O=document.createElement("div");return O.innerHTML=A,O.firstChild},_addItem:function(p){var w=document.createElement("label"),A=this._map.hasLayer(p.layer),O;p.overlay?(O=document.createElement("input"),O.type="checkbox",O.className="leaflet-control-layers-selector",O.defaultChecked=A):O=this._createRadioElement("leaflet-base-layers_"+l(this),A),this._layerControlInputs.push(O),O.layerId=l(p.layer),st(O,"click",this._onInputClick,this);var R=document.createElement("span");R.innerHTML=" "+p.name;var W=document.createElement("span");w.appendChild(W),W.appendChild(O),W.appendChild(R);var X=p.overlay?this._overlaysList:this._baseLayersList;return X.appendChild(w),this._checkDisabledLayers(),w},_onInputClick:function(){if(!this._preventClick){var p=this._layerControlInputs,w,A,O=[],R=[];this._handlingClick=!0;for(var W=p.length-1;W>=0;W--)w=p[W],A=this._getLayer(w.layerId).layer,w.checked?O.push(A):w.checked||R.push(A);for(W=0;W=0;R--)w=p[R],A=this._getLayer(w.layerId).layer,w.disabled=A.options.minZoom!==void 0&&OA.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expandSafely:function(){var p=this._section;this._preventClick=!0,st(p,"click",ln),this.expand();var w=this;setTimeout(function(){Wt(p,"click",ln),w._preventClick=!1})}}),Aae=function(p,w,A){return new m5(p,w,A)},CC=ba.extend({options:{position:"topleft",zoomInText:'',zoomInTitle:"Zoom in",zoomOutText:'',zoomOutTitle:"Zoom out"},onAdd:function(p){var w="leaflet-control-zoom",A=Ct("div",w+" leaflet-bar"),O=this.options;return this._zoomInButton=this._createButton(O.zoomInText,O.zoomInTitle,w+"-in",A,this._zoomIn),this._zoomOutButton=this._createButton(O.zoomOutText,O.zoomOutTitle,w+"-out",A,this._zoomOut),this._updateDisabled(),p.on("zoomend zoomlevelschange",this._updateDisabled,this),A},onRemove:function(p){p.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(p){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(p.shiftKey?3:1))},_createButton:function(p,w,A,O,R){var W=Ct("a",A,O);return W.innerHTML=p,W.href="#",W.title=w,W.setAttribute("role","button"),W.setAttribute("aria-label",w),sp(W),st(W,"click",Lu),st(W,"click",R,this),st(W,"click",this._refocusOnMap,this),W},_updateDisabled:function(){var p=this._map,w="leaflet-disabled";xr(this._zoomInButton,w),xr(this._zoomOutButton,w),this._zoomInButton.setAttribute("aria-disabled","false"),this._zoomOutButton.setAttribute("aria-disabled","false"),(this._disabled||p._zoom===p.getMinZoom())&&(ut(this._zoomOutButton,w),this._zoomOutButton.setAttribute("aria-disabled","true")),(this._disabled||p._zoom===p.getMaxZoom())&&(ut(this._zoomInButton,w),this._zoomInButton.setAttribute("aria-disabled","true"))}});wt.mergeOptions({zoomControl:!0}),wt.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new CC,this.addControl(this.zoomControl))});var Mae=function(p){return new CC(p)},y5=ba.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(p){var w="leaflet-control-scale",A=Ct("div",w),O=this.options;return this._addScales(O,w+"-line",A),p.on(O.updateWhenIdle?"moveend":"move",this._update,this),p.whenReady(this._update,this),A},onRemove:function(p){p.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(p,w,A){p.metric&&(this._mScale=Ct("div",w,A)),p.imperial&&(this._iScale=Ct("div",w,A))},_update:function(){var p=this._map,w=p.getSize().y/2,A=p.distance(p.containerPointToLatLng([0,w]),p.containerPointToLatLng([this.options.maxWidth,w]));this._updateScales(A)},_updateScales:function(p){this.options.metric&&p&&this._updateMetric(p),this.options.imperial&&p&&this._updateImperial(p)},_updateMetric:function(p){var w=this._getRoundNum(p),A=w<1e3?w+" m":w/1e3+" km";this._updateScale(this._mScale,A,w/p)},_updateImperial:function(p){var w=p*3.2808399,A,O,R;w>5280?(A=w/5280,O=this._getRoundNum(A),this._updateScale(this._iScale,O+" mi",O/A)):(R=this._getRoundNum(w),this._updateScale(this._iScale,R+" ft",R/w))},_updateScale:function(p,w,A){p.style.width=Math.round(this.options.maxWidth*A)+"px",p.innerHTML=w},_getRoundNum:function(p){var w=Math.pow(10,(Math.floor(p)+"").length-1),A=p/w;return A=A>=10?10:A>=5?5:A>=3?3:A>=2?2:1,w*A}}),Pae=function(p){return new y5(p)},Lae='',AC=ba.extend({options:{position:"bottomright",prefix:''+(Ue.inlineSvg?Lae+" ":"")+"Leaflet"},initialize:function(p){g(this,p),this._attributions={}},onAdd:function(p){p.attributionControl=this,this._container=Ct("div","leaflet-control-attribution"),sp(this._container);for(var w in p._layers)p._layers[w].getAttribution&&this.addAttribution(p._layers[w].getAttribution());return this._update(),p.on("layeradd",this._addAttribution,this),this._container},onRemove:function(p){p.off("layeradd",this._addAttribution,this)},_addAttribution:function(p){p.layer.getAttribution&&(this.addAttribution(p.layer.getAttribution()),p.layer.once("remove",function(){this.removeAttribution(p.layer.getAttribution())},this))},setPrefix:function(p){return this.options.prefix=p,this._update(),this},addAttribution:function(p){return p?(this._attributions[p]||(this._attributions[p]=0),this._attributions[p]++,this._update(),this):this},removeAttribution:function(p){return p?(this._attributions[p]&&(this._attributions[p]--,this._update()),this):this},_update:function(){if(this._map){var p=[];for(var w in this._attributions)this._attributions[w]&&p.push(w);var A=[];this.options.prefix&&A.push(this.options.prefix),p.length&&A.push(p.join(", ")),this._container.innerHTML=A.join(' ')}}});wt.mergeOptions({attributionControl:!0}),wt.addInitHook(function(){this.options.attributionControl&&new AC().addTo(this)});var kae=function(p){return new AC(p)};ba.Layers=m5,ba.Zoom=CC,ba.Scale=y5,ba.Attribution=AC,lp.layers=Aae,lp.zoom=Mae,lp.scale=Pae,lp.attribution=kae;var ro=N.extend({initialize:function(p){this._map=p},enable:function(){return this._enabled?this:(this._enabled=!0,this.addHooks(),this)},disable:function(){return this._enabled?(this._enabled=!1,this.removeHooks(),this):this},enabled:function(){return!!this._enabled}});ro.addTo=function(p,w){return p.addHandler(w,this),this};var Iae={Events:$},_5=Ue.touch?"touchstart mousedown":"mousedown",Js=Z.extend({options:{clickTolerance:3},initialize:function(p,w,A,O){g(this,O),this._element=p,this._dragStartTarget=w||p,this._preventOutline=A},enable:function(){this._enabled||(st(this._dragStartTarget,_5,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Js._dragging===this&&this.finishDrag(!0),Wt(this._dragStartTarget,_5,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(p){if(this._enabled&&(this._moved=!1,!hC(this._element,"leaflet-zoom-anim"))){if(p.touches&&p.touches.length!==1){Js._dragging===this&&this.finishDrag();return}if(!(Js._dragging||p.shiftKey||p.which!==1&&p.button!==1&&!p.touches)&&(Js._dragging=this,this._preventOutline&&yC(this._element),pC(),ip(),!this._moving)){this.fire("down");var w=p.touches?p.touches[0]:p,A=f5(this._element);this._startPoint=new F(w.clientX,w.clientY),this._startPos=Mu(this._element),this._parentScale=_C(A);var O=p.type==="mousedown";st(document,O?"mousemove":"touchmove",this._onMove,this),st(document,O?"mouseup":"touchend touchcancel",this._onUp,this)}}},_onMove:function(p){if(this._enabled){if(p.touches&&p.touches.length>1){this._moved=!0;return}var w=p.touches&&p.touches.length===1?p.touches[0]:p,A=new F(w.clientX,w.clientY)._subtract(this._startPoint);!A.x&&!A.y||Math.abs(A.x)+Math.abs(A.y)W&&(X=re,W=oe);W>A&&(w[X]=1,PC(p,w,A,O,X),PC(p,w,A,X,R))}function Nae(p,w){for(var A=[p[0]],O=1,R=0,W=p.length;Ow&&(A.push(p[O]),R=O);return Rw.max.x&&(A|=2),p.yw.max.y&&(A|=8),A}function jae(p,w){var A=w.x-p.x,O=w.y-p.y;return A*A+O*O}function up(p,w,A,O){var R=w.x,W=w.y,X=A.x-R,re=A.y-W,oe=X*X+re*re,de;return oe>0&&(de=((p.x-R)*X+(p.y-W)*re)/oe,de>1?(R=A.x,W=A.y):de>0&&(R+=X*de,W+=re*de)),X=p.x-R,re=p.y-W,O?X*X+re*re:new F(R,W)}function Bi(p){return!b(p[0])||typeof p[0][0]!="object"&&typeof p[0][0]<"u"}function A5(p){return console.warn("Deprecated use of _flat, please use L.LineUtil.isFlat instead."),Bi(p)}function M5(p,w){var A,O,R,W,X,re,oe,de;if(!p||p.length===0)throw new Error("latlngs not passed");Bi(p)||(console.warn("latlngs are not flat! Only the first ring will be used"),p=p[0]);var Ee=fe([0,0]),Je=ee(p),pt=Je.getNorthWest().distanceTo(Je.getSouthWest())*Je.getNorthEast().distanceTo(Je.getNorthWest());pt<1700&&(Ee=MC(p));var jn=p.length,qr=[];for(A=0;AO){oe=(W-O)/R,de=[re.x-oe*(re.x-X.x),re.y-oe*(re.y-X.y)];break}var Kn=w.unproject(V(de));return fe([Kn.lat+Ee.lat,Kn.lng+Ee.lng])}var Rae={__proto__:null,simplify:w5,pointToSegmentDistance:S5,closestPointOnSegment:Eae,clipSegment:C5,_getEdgeIntersection:O0,_getBitCode:ku,_sqClosestPointOnSegment:up,isFlat:Bi,_flat:A5,polylineCenter:M5},LC={project:function(p){return new F(p.lng,p.lat)},unproject:function(p){return new le(p.y,p.x)},bounds:new H([-180,-90],[180,90])},kC={R:6378137,R_MINOR:6356752314245179e-9,bounds:new H([-2003750834279e-5,-1549657073972e-5],[2003750834279e-5,1876465623138e-5]),project:function(p){var w=Math.PI/180,A=this.R,O=p.lat*w,R=this.R_MINOR/A,W=Math.sqrt(1-R*R),X=W*Math.sin(O),re=Math.tan(Math.PI/4-O/2)/Math.pow((1-X)/(1+X),W/2);return O=-A*Math.log(Math.max(re,1e-10)),new F(p.lng*w*A,O)},unproject:function(p){for(var w=180/Math.PI,A=this.R,O=this.R_MINOR/A,R=Math.sqrt(1-O*O),W=Math.exp(-p.y/A),X=Math.PI/2-2*Math.atan(W),re=0,oe=.1,de;re<15&&Math.abs(oe)>1e-7;re++)de=R*Math.sin(X),de=Math.pow((1-de)/(1+de),R/2),oe=Math.PI/2-2*Math.atan(W*de)-X,X+=oe;return new le(X*w,p.x*w/A)}},Bae={__proto__:null,LonLat:LC,Mercator:kC,SphericalMercator:he},zae=i({},_e,{code:"EPSG:3395",projection:kC,transformation:function(){var p=.5/(Math.PI*kC.R);return te(p,.5,-p,.5)}()}),P5=i({},_e,{code:"EPSG:4326",projection:LC,transformation:te(1/180,1,-1/180,.5)}),$ae=i({},Be,{projection:LC,transformation:te(1,0,-1,0),scale:function(p){return Math.pow(2,p)},zoom:function(p){return Math.log(p)/Math.LN2},distance:function(p,w){var A=w.lng-p.lng,O=w.lat-p.lat;return Math.sqrt(A*A+O*O)},infinite:!0});Be.Earth=_e,Be.EPSG3395=zae,Be.EPSG3857=Ve,Be.EPSG900913=Se,Be.EPSG4326=P5,Be.Simple=$ae;var wa=Z.extend({options:{pane:"overlayPane",attribution:null,bubblingMouseEvents:!0},addTo:function(p){return p.addLayer(this),this},remove:function(){return this.removeFrom(this._map||this._mapToAdd)},removeFrom:function(p){return p&&p.removeLayer(this),this},getPane:function(p){return this._map.getPane(p?this.options[p]||p:this.options.pane)},addInteractiveTarget:function(p){return this._map._targets[l(p)]=this,this},removeInteractiveTarget:function(p){return delete this._map._targets[l(p)],this},getAttribution:function(){return this.options.attribution},_layerAdd:function(p){var w=p.target;if(w.hasLayer(this)){if(this._map=w,this._zoomAnimated=w._zoomAnimated,this.getEvents){var A=this.getEvents();w.on(A,this),this.once("remove",function(){w.off(A,this)},this)}this.onAdd(w),this.fire("add"),w.fire("layeradd",{layer:this})}}});wt.include({addLayer:function(p){if(!p._layerAdd)throw new Error("The provided object is not a Layer.");var w=l(p);return this._layers[w]?this:(this._layers[w]=p,p._mapToAdd=this,p.beforeAdd&&p.beforeAdd(this),this.whenReady(p._layerAdd,p),this)},removeLayer:function(p){var w=l(p);return this._layers[w]?(this._loaded&&p.onRemove(this),delete this._layers[w],this._loaded&&(this.fire("layerremove",{layer:p}),p.fire("remove")),p._map=p._mapToAdd=null,this):this},hasLayer:function(p){return l(p)in this._layers},eachLayer:function(p,w){for(var A in this._layers)p.call(w,this._layers[A]);return this},_addLayers:function(p){p=p?b(p)?p:[p]:[];for(var w=0,A=p.length;wthis._layersMaxZoom&&this.setZoom(this._layersMaxZoom),this.options.minZoom===void 0&&this._layersMinZoom&&this.getZoom()=2&&w[0]instanceof le&&w[0].equals(w[A-1])&&w.pop(),w},_setLatLngs:function(p){Ko.prototype._setLatLngs.call(this,p),Bi(this._latlngs)&&(this._latlngs=[this._latlngs])},_defaultShape:function(){return Bi(this._latlngs[0])?this._latlngs[0]:this._latlngs[0][0]},_clipPoints:function(){var p=this._renderer._bounds,w=this.options.weight,A=new F(w,w);if(p=new H(p.min.subtract(A),p.max.add(A)),this._parts=[],!(!this._pxBounds||!this._pxBounds.intersects(p))){if(this.options.noClip){this._parts=this._rings;return}for(var O=0,R=this._rings.length,W;Op.y!=R.y>p.y&&p.x<(R.x-O.x)*(p.y-O.y)/(R.y-O.y)+O.x&&(w=!w);return w||Ko.prototype._containsPoint.call(this,p,!0)}});function Yae(p,w){return new $f(p,w)}var Qo=qo.extend({initialize:function(p,w){g(this,w),this._layers={},p&&this.addData(p)},addData:function(p){var w=b(p)?p:p.features,A,O,R;if(w){for(A=0,O=w.length;A0&&R.push(R[0].slice()),R}function Ff(p,w){return p.feature?i({},p.feature,{geometry:w}):B0(w)}function B0(p){return p.type==="Feature"||p.type==="FeatureCollection"?p:{type:"Feature",properties:{},geometry:p}}var DC={toGeoJSON:function(p){return Ff(this,{type:"Point",coordinates:EC(this.getLatLng(),p)})}};E0.include(DC),IC.include(DC),D0.include(DC),Ko.include({toGeoJSON:function(p){var w=!Bi(this._latlngs),A=R0(this._latlngs,w?1:0,!1,p);return Ff(this,{type:(w?"Multi":"")+"LineString",coordinates:A})}}),$f.include({toGeoJSON:function(p){var w=!Bi(this._latlngs),A=w&&!Bi(this._latlngs[0]),O=R0(this._latlngs,A?2:w?1:0,!0,p);return w||(O=[O]),Ff(this,{type:(A?"Multi":"")+"Polygon",coordinates:O})}}),Bf.include({toMultiPoint:function(p){var w=[];return this.eachLayer(function(A){w.push(A.toGeoJSON(p).geometry.coordinates)}),Ff(this,{type:"MultiPoint",coordinates:w})},toGeoJSON:function(p){var w=this.feature&&this.feature.geometry&&this.feature.geometry.type;if(w==="MultiPoint")return this.toMultiPoint(p);var A=w==="GeometryCollection",O=[];return this.eachLayer(function(R){if(R.toGeoJSON){var W=R.toGeoJSON(p);if(A)O.push(W.geometry);else{var X=B0(W);X.type==="FeatureCollection"?O.push.apply(O,X.features):O.push(X)}}}),A?Ff(this,{geometries:O,type:"GeometryCollection"}):{type:"FeatureCollection",features:O}}});function I5(p,w){return new Qo(p,w)}var Xae=I5,z0=wa.extend({options:{opacity:1,alt:"",interactive:!1,crossOrigin:!1,errorOverlayUrl:"",zIndex:1,className:""},initialize:function(p,w,A){this._url=p,this._bounds=ee(w),g(this,A)},onAdd:function(){this._image||(this._initImage(),this.options.opacity<1&&this._updateOpacity()),this.options.interactive&&(ut(this._image,"leaflet-interactive"),this.addInteractiveTarget(this._image)),this.getPane().appendChild(this._image),this._reset()},onRemove:function(){nr(this._image),this.options.interactive&&this.removeInteractiveTarget(this._image)},setOpacity:function(p){return this.options.opacity=p,this._image&&this._updateOpacity(),this},setStyle:function(p){return p.opacity&&this.setOpacity(p.opacity),this},bringToFront:function(){return this._map&&jf(this._image),this},bringToBack:function(){return this._map&&Rf(this._image),this},setUrl:function(p){return this._url=p,this._image&&(this._image.src=p),this},setBounds:function(p){return this._bounds=ee(p),this._map&&this._reset(),this},getEvents:function(){var p={zoom:this._reset,viewreset:this._reset};return this._zoomAnimated&&(p.zoomanim=this._animateZoom),p},setZIndex:function(p){return this.options.zIndex=p,this._updateZIndex(),this},getBounds:function(){return this._bounds},getElement:function(){return this._image},_initImage:function(){var p=this._url.tagName==="IMG",w=this._image=p?this._url:Ct("img");if(ut(w,"leaflet-image-layer"),this._zoomAnimated&&ut(w,"leaflet-zoom-animated"),this.options.className&&ut(w,this.options.className),w.onselectstart=f,w.onmousemove=f,w.onload=o(this.fire,this,"load"),w.onerror=o(this._overlayOnError,this,"error"),(this.options.crossOrigin||this.options.crossOrigin==="")&&(w.crossOrigin=this.options.crossOrigin===!0?"":this.options.crossOrigin),this.options.zIndex&&this._updateZIndex(),p){this._url=w.src;return}w.src=this._url,w.alt=this.options.alt},_animateZoom:function(p){var w=this._map.getZoomScale(p.zoom),A=this._map._latLngBoundsToNewLayerBounds(this._bounds,p.zoom,p.center).min;Au(this._image,A,w)},_reset:function(){var p=this._image,w=new H(this._map.latLngToLayerPoint(this._bounds.getNorthWest()),this._map.latLngToLayerPoint(this._bounds.getSouthEast())),A=w.getSize();Ar(p,w.min),p.style.width=A.x+"px",p.style.height=A.y+"px"},_updateOpacity:function(){Ri(this._image,this.options.opacity)},_updateZIndex:function(){this._image&&this.options.zIndex!==void 0&&this.options.zIndex!==null&&(this._image.style.zIndex=this.options.zIndex)},_overlayOnError:function(){this.fire("error");var p=this.options.errorOverlayUrl;p&&this._url!==p&&(this._url=p,this._image.src=p)},getCenter:function(){return this._bounds.getCenter()}}),qae=function(p,w,A){return new z0(p,w,A)},O5=z0.extend({options:{autoplay:!0,loop:!0,keepAspectRatio:!0,muted:!1,playsInline:!0},_initImage:function(){var p=this._url.tagName==="VIDEO",w=this._image=p?this._url:Ct("video");if(ut(w,"leaflet-image-layer"),this._zoomAnimated&&ut(w,"leaflet-zoom-animated"),this.options.className&&ut(w,this.options.className),w.onselectstart=f,w.onmousemove=f,w.onloadeddata=o(this.fire,this,"load"),p){for(var A=w.getElementsByTagName("source"),O=[],R=0;R0?O:[w.src];return}b(this._url)||(this._url=[this._url]),!this.options.keepAspectRatio&&Object.prototype.hasOwnProperty.call(w.style,"objectFit")&&(w.style.objectFit="fill"),w.autoplay=!!this.options.autoplay,w.loop=!!this.options.loop,w.muted=!!this.options.muted,w.playsInline=!!this.options.playsInline;for(var W=0;WR?(w.height=R+"px",ut(p,W)):xr(p,W),this._containerWidth=this._container.offsetWidth},_animateZoom:function(p){var w=this._map._latLngToNewLayerPoint(this._latlng,p.zoom,p.center),A=this._getAnchor();Ar(this._container,w.add(A))},_adjustPan:function(){if(this.options.autoPan){if(this._map._panAnim&&this._map._panAnim.stop(),this._autopanning){this._autopanning=!1;return}var p=this._map,w=parseInt(np(this._container,"marginBottom"),10)||0,A=this._container.offsetHeight+w,O=this._containerWidth,R=new F(this._containerLeft,-A-this._containerBottom);R._add(Mu(this._container));var W=p.layerPointToContainerPoint(R),X=V(this.options.autoPanPadding),re=V(this.options.autoPanPaddingTopLeft||X),oe=V(this.options.autoPanPaddingBottomRight||X),de=p.getSize(),Ee=0,Je=0;W.x+O+oe.x>de.x&&(Ee=W.x+O-de.x+oe.x),W.x-Ee-re.x<0&&(Ee=W.x-re.x),W.y+A+oe.y>de.y&&(Je=W.y+A-de.y+oe.y),W.y-Je-re.y<0&&(Je=W.y-re.y),(Ee||Je)&&(this.options.keepInView&&(this._autopanning=!0),p.fire("autopanstart").panBy([Ee,Je]))}},_getAnchor:function(){return V(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}}),Jae=function(p,w){return new $0(p,w)};wt.mergeOptions({closePopupOnClick:!0}),wt.include({openPopup:function(p,w,A){return this._initOverlay($0,p,w,A).openOn(this),this},closePopup:function(p){return p=arguments.length?p:this._popup,p&&p.close(),this}}),wa.include({bindPopup:function(p,w){return this._popup=this._initOverlay($0,this._popup,p,w),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(p){return this._popup&&(this instanceof qo||(this._popup._source=this),this._popup._prepareOpen(p||this._latlng)&&this._popup.openOn(this._map)),this},closePopup:function(){return this._popup&&this._popup.close(),this},togglePopup:function(){return this._popup&&this._popup.toggle(this),this},isPopupOpen:function(){return this._popup?this._popup.isOpen():!1},setPopupContent:function(p){return this._popup&&this._popup.setContent(p),this},getPopup:function(){return this._popup},_openPopup:function(p){if(!(!this._popup||!this._map)){Lu(p);var w=p.layer||p.target;if(this._popup._source===w&&!(w instanceof el)){this._map.hasLayer(this._popup)?this.closePopup():this.openPopup(p.latlng);return}this._popup._source=w,this.openPopup(p.latlng)}},_movePopup:function(p){this._popup.setLatLng(p.latlng)},_onKeyPress:function(p){p.originalEvent.keyCode===13&&this._openPopup(p)}});var F0=no.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,opacity:.9},onAdd:function(p){no.prototype.onAdd.call(this,p),this.setOpacity(this.options.opacity),p.fire("tooltipopen",{tooltip:this}),this._source&&(this.addEventParent(this._source),this._source.fire("tooltipopen",{tooltip:this},!0))},onRemove:function(p){no.prototype.onRemove.call(this,p),p.fire("tooltipclose",{tooltip:this}),this._source&&(this.removeEventParent(this._source),this._source.fire("tooltipclose",{tooltip:this},!0))},getEvents:function(){var p=no.prototype.getEvents.call(this);return this.options.permanent||(p.preclick=this.close),p},_initLayout:function(){var p="leaflet-tooltip",w=p+" "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=Ct("div",w),this._container.setAttribute("role","tooltip"),this._container.setAttribute("id","leaflet-tooltip-"+l(this))},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(p){var w,A,O=this._map,R=this._container,W=O.latLngToContainerPoint(O.getCenter()),X=O.layerPointToContainerPoint(p),re=this.options.direction,oe=R.offsetWidth,de=R.offsetHeight,Ee=V(this.options.offset),Je=this._getAnchor();re==="top"?(w=oe/2,A=de):re==="bottom"?(w=oe/2,A=0):re==="center"?(w=oe/2,A=de/2):re==="right"?(w=0,A=de/2):re==="left"?(w=oe,A=de/2):X.xthis.options.maxZoom||AO?this._retainParent(R,W,X,O):!1)},_retainChildren:function(p,w,A,O){for(var R=2*p;R<2*p+2;R++)for(var W=2*w;W<2*w+2;W++){var X=new F(R,W);X.z=A+1;var re=this._tileCoordsToKey(X),oe=this._tiles[re];if(oe&&oe.active){oe.retain=!0;continue}else oe&&oe.loaded&&(oe.retain=!0);A+1this.options.maxZoom||this.options.minZoom!==void 0&&R1){this._setView(p,A);return}for(var Je=R.min.y;Je<=R.max.y;Je++)for(var pt=R.min.x;pt<=R.max.x;pt++){var jn=new F(pt,Je);if(jn.z=this._tileZoom,!!this._isValidTile(jn)){var qr=this._tiles[this._tileCoordsToKey(jn)];qr?qr.current=!0:X.push(jn)}}if(X.sort(function(Kn,Gf){return Kn.distanceTo(W)-Gf.distanceTo(W)}),X.length!==0){this._loading||(this._loading=!0,this.fire("loading"));var zi=document.createDocumentFragment();for(pt=0;ptA.max.x)||!w.wrapLat&&(p.yA.max.y))return!1}if(!this.options.bounds)return!0;var O=this._tileCoordsToBounds(p);return ee(this.options.bounds).overlaps(O)},_keyToBounds:function(p){return this._tileCoordsToBounds(this._keyToTileCoords(p))},_tileCoordsToNwSe:function(p){var w=this._map,A=this.getTileSize(),O=p.scaleBy(A),R=O.add(A),W=w.unproject(O,p.z),X=w.unproject(R,p.z);return[W,X]},_tileCoordsToBounds:function(p){var w=this._tileCoordsToNwSe(p),A=new K(w[0],w[1]);return this.options.noWrap||(A=this._map.wrapLatLngBounds(A)),A},_tileCoordsToKey:function(p){return p.x+":"+p.y+":"+p.z},_keyToTileCoords:function(p){var w=p.split(":"),A=new F(+w[0],+w[1]);return A.z=+w[2],A},_removeTile:function(p){var w=this._tiles[p];w&&(nr(w.el),delete this._tiles[p],this.fire("tileunload",{tile:w.el,coords:this._keyToTileCoords(p)}))},_initTile:function(p){ut(p,"leaflet-tile");var w=this.getTileSize();p.style.width=w.x+"px",p.style.height=w.y+"px",p.onselectstart=f,p.onmousemove=f,Ue.ielt9&&this.options.opacity<1&&Ri(p,this.options.opacity)},_addTile:function(p,w){var A=this._getTilePos(p),O=this._tileCoordsToKey(p),R=this.createTile(this._wrapCoords(p),o(this._tileReady,this,p));this._initTile(R),this.createTile.length<2&&E(o(this._tileReady,this,p,null,R)),Ar(R,A),this._tiles[O]={el:R,coords:p,current:!0},w.appendChild(R),this.fire("tileloadstart",{tile:R,coords:p})},_tileReady:function(p,w,A){w&&this.fire("tileerror",{error:w,tile:A,coords:p});var O=this._tileCoordsToKey(p);A=this._tiles[O],A&&(A.loaded=+new Date,this._map._fadeAnimated?(Ri(A.el,0),D(this._fadeFrame),this._fadeFrame=E(this._updateOpacity,this)):(A.active=!0,this._pruneTiles()),w||(ut(A.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:A.el,coords:p})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),Ue.ielt9||!this._map._fadeAnimated?E(this._pruneTiles,this):setTimeout(o(this._pruneTiles,this),250)))},_getTilePos:function(p){return p.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(p){var w=new F(this._wrapX?c(p.x,this._wrapX):p.x,this._wrapY?c(p.y,this._wrapY):p.y);return w.z=p.z,w},_pxBoundsToTileRange:function(p){var w=this.getTileSize();return new H(p.min.unscaleBy(w).floor(),p.max.unscaleBy(w).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var p in this._tiles)if(!this._tiles[p].loaded)return!1;return!0}});function roe(p){return new fp(p)}var Vf=fp.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1,referrerPolicy:!1},initialize:function(p,w){this._url=p,w=g(this,w),w.detectRetina&&Ue.retina&&w.maxZoom>0?(w.tileSize=Math.floor(w.tileSize/2),w.zoomReverse?(w.zoomOffset--,w.minZoom=Math.min(w.maxZoom,w.minZoom+1)):(w.zoomOffset++,w.maxZoom=Math.max(w.minZoom,w.maxZoom-1)),w.minZoom=Math.max(0,w.minZoom)):w.zoomReverse?w.minZoom=Math.min(w.maxZoom,w.minZoom):w.maxZoom=Math.max(w.minZoom,w.maxZoom),typeof w.subdomains=="string"&&(w.subdomains=w.subdomains.split("")),this.on("tileunload",this._onTileRemove)},setUrl:function(p,w){return this._url===p&&w===void 0&&(w=!0),this._url=p,w||this.redraw(),this},createTile:function(p,w){var A=document.createElement("img");return st(A,"load",o(this._tileOnLoad,this,w,A)),st(A,"error",o(this._tileOnError,this,w,A)),(this.options.crossOrigin||this.options.crossOrigin==="")&&(A.crossOrigin=this.options.crossOrigin===!0?"":this.options.crossOrigin),typeof this.options.referrerPolicy=="string"&&(A.referrerPolicy=this.options.referrerPolicy),A.alt="",A.src=this.getTileUrl(p),A},getTileUrl:function(p){var w={r:Ue.retina?"@2x":"",s:this._getSubdomain(p),x:p.x,y:p.y,z:this._getZoomForUrl()};if(this._map&&!this._map.options.crs.infinite){var A=this._globalTileRange.max.y-p.y;this.options.tms&&(w.y=A),w["-y"]=A}return x(this._url,i(w,this.options))},_tileOnLoad:function(p,w){Ue.ielt9?setTimeout(o(p,this,null,w),0):p(null,w)},_tileOnError:function(p,w,A){var O=this.options.errorTileUrl;O&&w.getAttribute("src")!==O&&(w.src=O),p(A,w)},_onTileRemove:function(p){p.tile.onload=null},_getZoomForUrl:function(){var p=this._tileZoom,w=this.options.maxZoom,A=this.options.zoomReverse,O=this.options.zoomOffset;return A&&(p=w-p),p+O},_getSubdomain:function(p){var w=Math.abs(p.x+p.y)%this.options.subdomains.length;return this.options.subdomains[w]},_abortLoading:function(){var p,w;for(p in this._tiles)if(this._tiles[p].coords.z!==this._tileZoom&&(w=this._tiles[p].el,w.onload=f,w.onerror=f,!w.complete)){w.src=T;var A=this._tiles[p].coords;nr(w),delete this._tiles[p],this.fire("tileabort",{tile:w,coords:A})}},_removeTile:function(p){var w=this._tiles[p];if(w)return w.el.setAttribute("src",T),fp.prototype._removeTile.call(this,p)},_tileReady:function(p,w,A){if(!(!this._map||A&&A.getAttribute("src")===T))return fp.prototype._tileReady.call(this,p,w,A)}});function N5(p,w){return new Vf(p,w)}var j5=Vf.extend({defaultWmsParams:{service:"WMS",request:"GetMap",layers:"",styles:"",format:"image/jpeg",transparent:!1,version:"1.1.1"},options:{crs:null,uppercase:!1},initialize:function(p,w){this._url=p;var A=i({},this.defaultWmsParams);for(var O in w)O in this.options||(A[O]=w[O]);w=g(this,w);var R=w.detectRetina&&Ue.retina?2:1,W=this.getTileSize();A.width=W.x*R,A.height=W.y*R,this.wmsParams=A},onAdd:function(p){this._crs=this.options.crs||p.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var w=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[w]=this._crs.code,Vf.prototype.onAdd.call(this,p)},getTileUrl:function(p){var w=this._tileCoordsToNwSe(p),A=this._crs,O=Y(A.project(w[0]),A.project(w[1])),R=O.min,W=O.max,X=(this._wmsVersion>=1.3&&this._crs===P5?[R.y,R.x,W.y,W.x]:[R.x,R.y,W.x,W.y]).join(","),re=Vf.prototype.getTileUrl.call(this,p);return re+m(this.wmsParams,re,this.options.uppercase)+(this.options.uppercase?"&BBOX=":"&bbox=")+X},setParams:function(p,w){return i(this.wmsParams,p),w||this.redraw(),this}});function noe(p,w){return new j5(p,w)}Vf.WMS=j5,N5.wms=noe;var Jo=wa.extend({options:{padding:.1},initialize:function(p){g(this,p),l(this),this._layers=this._layers||{}},onAdd:function(){this._container||(this._initContainer(),ut(this._container,"leaflet-zoom-animated")),this.getPane().appendChild(this._container),this._update(),this.on("update",this._updatePaths,this)},onRemove:function(){this.off("update",this._updatePaths,this),this._destroyContainer()},getEvents:function(){var p={viewreset:this._reset,zoom:this._onZoom,moveend:this._update,zoomend:this._onZoomEnd};return this._zoomAnimated&&(p.zoomanim=this._onAnimZoom),p},_onAnimZoom:function(p){this._updateTransform(p.center,p.zoom)},_onZoom:function(){this._updateTransform(this._map.getCenter(),this._map.getZoom())},_updateTransform:function(p,w){var A=this._map.getZoomScale(w,this._zoom),O=this._map.getSize().multiplyBy(.5+this.options.padding),R=this._map.project(this._center,w),W=O.multiplyBy(-A).add(R).subtract(this._map._getNewPixelOrigin(p,w));Ue.any3d?Au(this._container,W,A):Ar(this._container,W)},_reset:function(){this._update(),this._updateTransform(this._center,this._zoom);for(var p in this._layers)this._layers[p]._reset()},_onZoomEnd:function(){for(var p in this._layers)this._layers[p]._project()},_updatePaths:function(){for(var p in this._layers)this._layers[p]._update()},_update:function(){var p=this.options.padding,w=this._map.getSize(),A=this._map.containerPointToLayerPoint(w.multiplyBy(-p)).round();this._bounds=new H(A,A.add(w.multiplyBy(1+p*2)).round()),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}}),R5=Jo.extend({options:{tolerance:0},getEvents:function(){var p=Jo.prototype.getEvents.call(this);return p.viewprereset=this._onViewPreReset,p},_onViewPreReset:function(){this._postponeUpdatePaths=!0},onAdd:function(){Jo.prototype.onAdd.call(this),this._draw()},_initContainer:function(){var p=this._container=document.createElement("canvas");st(p,"mousemove",this._onMouseMove,this),st(p,"click dblclick mousedown mouseup contextmenu",this._onClick,this),st(p,"mouseout",this._handleMouseOut,this),p._leaflet_disable_events=!0,this._ctx=p.getContext("2d")},_destroyContainer:function(){D(this._redrawRequest),delete this._ctx,nr(this._container),Wt(this._container),delete this._container},_updatePaths:function(){if(!this._postponeUpdatePaths){var p;this._redrawBounds=null;for(var w in this._layers)p=this._layers[w],p._update();this._redraw()}},_update:function(){if(!(this._map._animatingZoom&&this._bounds)){Jo.prototype._update.call(this);var p=this._bounds,w=this._container,A=p.getSize(),O=Ue.retina?2:1;Ar(w,p.min),w.width=O*A.x,w.height=O*A.y,w.style.width=A.x+"px",w.style.height=A.y+"px",Ue.retina&&this._ctx.scale(2,2),this._ctx.translate(-p.min.x,-p.min.y),this.fire("update")}},_reset:function(){Jo.prototype._reset.call(this),this._postponeUpdatePaths&&(this._postponeUpdatePaths=!1,this._updatePaths())},_initPath:function(p){this._updateDashArray(p),this._layers[l(p)]=p;var w=p._order={layer:p,prev:this._drawLast,next:null};this._drawLast&&(this._drawLast.next=w),this._drawLast=w,this._drawFirst=this._drawFirst||this._drawLast},_addPath:function(p){this._requestRedraw(p)},_removePath:function(p){var w=p._order,A=w.next,O=w.prev;A?A.prev=O:this._drawLast=O,O?O.next=A:this._drawFirst=A,delete p._order,delete this._layers[l(p)],this._requestRedraw(p)},_updatePath:function(p){this._extendRedrawBounds(p),p._project(),p._update(),this._requestRedraw(p)},_updateStyle:function(p){this._updateDashArray(p),this._requestRedraw(p)},_updateDashArray:function(p){if(typeof p.options.dashArray=="string"){var w=p.options.dashArray.split(/[, ]+/),A=[],O,R;for(R=0;R')}}catch{}return function(p){return document.createElement("<"+p+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}(),ioe={_initContainer:function(){this._container=Ct("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(Jo.prototype._update.call(this),this.fire("update"))},_initPath:function(p){var w=p._container=hp("shape");ut(w,"leaflet-vml-shape "+(this.options.className||"")),w.coordsize="1 1",p._path=hp("path"),w.appendChild(p._path),this._updateStyle(p),this._layers[l(p)]=p},_addPath:function(p){var w=p._container;this._container.appendChild(w),p.options.interactive&&p.addInteractiveTarget(w)},_removePath:function(p){var w=p._container;nr(w),p.removeInteractiveTarget(w),delete this._layers[l(p)]},_updateStyle:function(p){var w=p._stroke,A=p._fill,O=p.options,R=p._container;R.stroked=!!O.stroke,R.filled=!!O.fill,O.stroke?(w||(w=p._stroke=hp("stroke")),R.appendChild(w),w.weight=O.weight+"px",w.color=O.color,w.opacity=O.opacity,O.dashArray?w.dashStyle=b(O.dashArray)?O.dashArray.join(" "):O.dashArray.replace(/( *, *)/g," "):w.dashStyle="",w.endcap=O.lineCap.replace("butt","flat"),w.joinstyle=O.lineJoin):w&&(R.removeChild(w),p._stroke=null),O.fill?(A||(A=p._fill=hp("fill")),R.appendChild(A),A.color=O.fillColor||O.color,A.opacity=O.fillOpacity):A&&(R.removeChild(A),p._fill=null)},_updateCircle:function(p){var w=p._point.round(),A=Math.round(p._radius),O=Math.round(p._radiusY||A);this._setPath(p,p._empty()?"M0 0":"AL "+w.x+","+w.y+" "+A+","+O+" 0,"+65535*360)},_setPath:function(p,w){p._path.v=w},_bringToFront:function(p){jf(p._container)},_bringToBack:function(p){Rf(p._container)}},V0=Ue.vml?hp:Ge,dp=Jo.extend({_initContainer:function(){this._container=V0("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=V0("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){nr(this._container),Wt(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_update:function(){if(!(this._map._animatingZoom&&this._bounds)){Jo.prototype._update.call(this);var p=this._bounds,w=p.getSize(),A=this._container;(!this._svgSize||!this._svgSize.equals(w))&&(this._svgSize=w,A.setAttribute("width",w.x),A.setAttribute("height",w.y)),Ar(A,p.min),A.setAttribute("viewBox",[p.min.x,p.min.y,w.x,w.y].join(" ")),this.fire("update")}},_initPath:function(p){var w=p._path=V0("path");p.options.className&&ut(w,p.options.className),p.options.interactive&&ut(w,"leaflet-interactive"),this._updateStyle(p),this._layers[l(p)]=p},_addPath:function(p){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(p._path),p.addInteractiveTarget(p._path)},_removePath:function(p){nr(p._path),p.removeInteractiveTarget(p._path),delete this._layers[l(p)]},_updatePath:function(p){p._project(),p._update()},_updateStyle:function(p){var w=p._path,A=p.options;w&&(A.stroke?(w.setAttribute("stroke",A.color),w.setAttribute("stroke-opacity",A.opacity),w.setAttribute("stroke-width",A.weight),w.setAttribute("stroke-linecap",A.lineCap),w.setAttribute("stroke-linejoin",A.lineJoin),A.dashArray?w.setAttribute("stroke-dasharray",A.dashArray):w.removeAttribute("stroke-dasharray"),A.dashOffset?w.setAttribute("stroke-dashoffset",A.dashOffset):w.removeAttribute("stroke-dashoffset")):w.setAttribute("stroke","none"),A.fill?(w.setAttribute("fill",A.fillColor||A.color),w.setAttribute("fill-opacity",A.fillOpacity),w.setAttribute("fill-rule",A.fillRule||"evenodd")):w.setAttribute("fill","none"))},_updatePoly:function(p,w){this._setPath(p,Ye(p._parts,w))},_updateCircle:function(p){var w=p._point,A=Math.max(Math.round(p._radius),1),O=Math.max(Math.round(p._radiusY),1)||A,R="a"+A+","+O+" 0 1,0 ",W=p._empty()?"M0 0":"M"+(w.x-A)+","+w.y+R+A*2+",0 "+R+-A*2+",0 ";this._setPath(p,W)},_setPath:function(p,w){p._path.setAttribute("d",w)},_bringToFront:function(p){jf(p._path)},_bringToBack:function(p){Rf(p._path)}});Ue.vml&&dp.include(ioe);function z5(p){return Ue.svg||Ue.vml?new dp(p):null}wt.include({getRenderer:function(p){var w=p.options.renderer||this._getPaneRenderer(p.options.pane)||this.options.renderer||this._renderer;return w||(w=this._renderer=this._createRenderer()),this.hasLayer(w)||this.addLayer(w),w},_getPaneRenderer:function(p){if(p==="overlayPane"||p===void 0)return!1;var w=this._paneRenderers[p];return w===void 0&&(w=this._createRenderer({pane:p}),this._paneRenderers[p]=w),w},_createRenderer:function(p){return this.options.preferCanvas&&B5(p)||z5(p)}});var $5=$f.extend({initialize:function(p,w){$f.prototype.initialize.call(this,this._boundsToLatLngs(p),w)},setBounds:function(p){return this.setLatLngs(this._boundsToLatLngs(p))},_boundsToLatLngs:function(p){return p=ee(p),[p.getSouthWest(),p.getNorthWest(),p.getNorthEast(),p.getSouthEast()]}});function aoe(p,w){return new $5(p,w)}dp.create=V0,dp.pointsToPath=Ye,Qo.geometryToLayer=N0,Qo.coordsToLatLng=OC,Qo.coordsToLatLngs=j0,Qo.latLngToCoords=EC,Qo.latLngsToCoords=R0,Qo.getFeature=Ff,Qo.asFeature=B0,wt.mergeOptions({boxZoom:!0});var F5=ro.extend({initialize:function(p){this._map=p,this._container=p._container,this._pane=p._panes.overlayPane,this._resetStateTimeout=0,p.on("unload",this._destroy,this)},addHooks:function(){st(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){Wt(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){nr(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){this._resetStateTimeout!==0&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(p){if(!p.shiftKey||p.which!==1&&p.button!==1)return!1;this._clearDeferredResetState(),this._resetState(),ip(),pC(),this._startPoint=this._map.mouseEventToContainerPoint(p),st(document,{contextmenu:Lu,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(p){this._moved||(this._moved=!0,this._box=Ct("div","leaflet-zoom-box",this._container),ut(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(p);var w=new H(this._point,this._startPoint),A=w.getSize();Ar(this._box,w.min),this._box.style.width=A.x+"px",this._box.style.height=A.y+"px"},_finish:function(){this._moved&&(nr(this._box),xr(this._container,"leaflet-crosshair")),ap(),gC(),Wt(document,{contextmenu:Lu,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(p){if(!(p.which!==1&&p.button!==1)&&(this._finish(),!!this._moved)){this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(o(this._resetState,this),0);var w=new K(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(w).fire("boxzoomend",{boxZoomBounds:w})}},_onKeyDown:function(p){p.keyCode===27&&(this._finish(),this._clearDeferredResetState(),this._resetState())}});wt.addInitHook("addHandler","boxZoom",F5),wt.mergeOptions({doubleClickZoom:!0});var V5=ro.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(p){var w=this._map,A=w.getZoom(),O=w.options.zoomDelta,R=p.originalEvent.shiftKey?A-O:A+O;w.options.doubleClickZoom==="center"?w.setZoom(R):w.setZoomAround(p.containerPoint,R)}});wt.addInitHook("addHandler","doubleClickZoom",V5),wt.mergeOptions({dragging:!0,inertia:!0,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var G5=ro.extend({addHooks:function(){if(!this._draggable){var p=this._map;this._draggable=new Js(p._mapPane,p._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),p.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),p.on("zoomend",this._onZoomEnd,this),p.whenReady(this._onZoomEnd,this))}ut(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){xr(this._map._container,"leaflet-grab"),xr(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var p=this._map;if(p._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var w=ee(this._map.options.maxBounds);this._offsetLimit=Y(this._map.latLngToContainerPoint(w.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(w.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;p.fire("movestart").fire("dragstart"),p.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(p){if(this._map.options.inertia){var w=this._lastTime=+new Date,A=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(A),this._times.push(w),this._prunePositions(w)}this._map.fire("move",p).fire("drag",p)},_prunePositions:function(p){for(;this._positions.length>1&&p-this._times[0]>50;)this._positions.shift(),this._times.shift()},_onZoomEnd:function(){var p=this._map.getSize().divideBy(2),w=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=w.subtract(p).x,this._worldWidth=this._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(p,w){return p-(p-w)*this._viscosity},_onPreDragLimit:function(){if(!(!this._viscosity||!this._offsetLimit)){var p=this._draggable._newPos.subtract(this._draggable._startPos),w=this._offsetLimit;p.xw.max.x&&(p.x=this._viscousLimit(p.x,w.max.x)),p.y>w.max.y&&(p.y=this._viscousLimit(p.y,w.max.y)),this._draggable._newPos=this._draggable._startPos.add(p)}},_onPreDragWrap:function(){var p=this._worldWidth,w=Math.round(p/2),A=this._initialWorldOffset,O=this._draggable._newPos.x,R=(O-w+A)%p+w-A,W=(O+w+A)%p-w-A,X=Math.abs(R+A)0?W:-W))-w;this._delta=0,this._startTime=null,X&&(p.options.scrollWheelZoom==="center"?p.setZoom(w+X):p.setZoomAround(this._lastMousePos,w+X))}});wt.addInitHook("addHandler","scrollWheelZoom",H5);var ooe=600;wt.mergeOptions({tapHold:Ue.touchNative&&Ue.safari&&Ue.mobile,tapTolerance:15});var U5=ro.extend({addHooks:function(){st(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){Wt(this._map._container,"touchstart",this._onDown,this)},_onDown:function(p){if(clearTimeout(this._holdTimeout),p.touches.length===1){var w=p.touches[0];this._startPos=this._newPos=new F(w.clientX,w.clientY),this._holdTimeout=setTimeout(o(function(){this._cancel(),this._isTapValid()&&(st(document,"touchend",ln),st(document,"touchend touchcancel",this._cancelClickPrevent),this._simulateEvent("contextmenu",w))},this),ooe),st(document,"touchend touchcancel contextmenu",this._cancel,this),st(document,"touchmove",this._onMove,this)}},_cancelClickPrevent:function p(){Wt(document,"touchend",ln),Wt(document,"touchend touchcancel",p)},_cancel:function(){clearTimeout(this._holdTimeout),Wt(document,"touchend touchcancel contextmenu",this._cancel,this),Wt(document,"touchmove",this._onMove,this)},_onMove:function(p){var w=p.touches[0];this._newPos=new F(w.clientX,w.clientY)},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_simulateEvent:function(p,w){var A=new MouseEvent(p,{bubbles:!0,cancelable:!0,view:window,screenX:w.screenX,screenY:w.screenY,clientX:w.clientX,clientY:w.clientY});A._simulated=!0,w.target.dispatchEvent(A)}});wt.addInitHook("addHandler","tapHold",U5),wt.mergeOptions({touchZoom:Ue.touch,bounceAtZoomLimits:!0});var Z5=ro.extend({addHooks:function(){ut(this._map._container,"leaflet-touch-zoom"),st(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){xr(this._map._container,"leaflet-touch-zoom"),Wt(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(p){var w=this._map;if(!(!p.touches||p.touches.length!==2||w._animatingZoom||this._zooming)){var A=w.mouseEventToContainerPoint(p.touches[0]),O=w.mouseEventToContainerPoint(p.touches[1]);this._centerPoint=w.getSize()._divideBy(2),this._startLatLng=w.containerPointToLatLng(this._centerPoint),w.options.touchZoom!=="center"&&(this._pinchStartLatLng=w.containerPointToLatLng(A.add(O)._divideBy(2))),this._startDist=A.distanceTo(O),this._startZoom=w.getZoom(),this._moved=!1,this._zooming=!0,w._stop(),st(document,"touchmove",this._onTouchMove,this),st(document,"touchend touchcancel",this._onTouchEnd,this),ln(p)}},_onTouchMove:function(p){if(!(!p.touches||p.touches.length!==2||!this._zooming)){var w=this._map,A=w.mouseEventToContainerPoint(p.touches[0]),O=w.mouseEventToContainerPoint(p.touches[1]),R=A.distanceTo(O)/this._startDist;if(this._zoom=w.getScaleZoom(R,this._startZoom),!w.options.bounceAtZoomLimits&&(this._zoomw.getMaxZoom()&&R>1)&&(this._zoom=w._limitZoom(this._zoom)),w.options.touchZoom==="center"){if(this._center=this._startLatLng,R===1)return}else{var W=A._add(O)._divideBy(2)._subtract(this._centerPoint);if(R===1&&W.x===0&&W.y===0)return;this._center=w.unproject(w.project(this._pinchStartLatLng,this._zoom).subtract(W),this._zoom)}this._moved||(w._moveStart(!0,!1),this._moved=!0),D(this._animRequest);var X=o(w._move,w,this._center,this._zoom,{pinch:!0,round:!1},void 0);this._animRequest=E(X,this,!0),ln(p)}},_onTouchEnd:function(){if(!this._moved||!this._zooming){this._zooming=!1;return}this._zooming=!1,D(this._animRequest),Wt(document,"touchmove",this._onTouchMove,this),Wt(document,"touchend touchcancel",this._onTouchEnd,this),this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.zoomSnap):this._map._resetView(this._center,this._map._limitZoom(this._zoom))}});wt.addInitHook("addHandler","touchZoom",Z5),wt.BoxZoom=F5,wt.DoubleClickZoom=V5,wt.Drag=G5,wt.Keyboard=W5,wt.ScrollWheelZoom=H5,wt.TapHold=U5,wt.TouchZoom=Z5,r.Bounds=H,r.Browser=Ue,r.CRS=Be,r.Canvas=R5,r.Circle=IC,r.CircleMarker=D0,r.Class=N,r.Control=ba,r.DivIcon=D5,r.DivOverlay=no,r.DomEvent=Tae,r.DomUtil=wae,r.Draggable=Js,r.Evented=Z,r.FeatureGroup=qo,r.GeoJSON=Qo,r.GridLayer=fp,r.Handler=ro,r.Icon=zf,r.ImageOverlay=z0,r.LatLng=le,r.LatLngBounds=K,r.Layer=wa,r.LayerGroup=Bf,r.LineUtil=Rae,r.Map=wt,r.Marker=E0,r.Mixin=Iae,r.Path=el,r.Point=F,r.PolyUtil=Oae,r.Polygon=$f,r.Polyline=Ko,r.Popup=$0,r.PosAnimation=g5,r.Projection=Bae,r.Rectangle=$5,r.Renderer=Jo,r.SVG=dp,r.SVGOverlay=E5,r.TileLayer=Vf,r.Tooltip=F0,r.Transformation=ue,r.Util=j,r.VideoOverlay=O5,r.bind=o,r.bounds=Y,r.canvas=B5,r.circle=Uae,r.circleMarker=Hae,r.control=lp,r.divIcon=toe,r.extend=i,r.featureGroup=Vae,r.geoJSON=I5,r.geoJson=Xae,r.gridLayer=roe,r.icon=Gae,r.imageOverlay=qae,r.latLng=fe,r.latLngBounds=ee,r.layerGroup=Fae,r.map=Cae,r.marker=Wae,r.point=V,r.polygon=Yae,r.polyline=Zae,r.popup=Jae,r.rectangle=aoe,r.setOptions=g,r.stamp=l,r.svg=z5,r.svgOverlay=Qae,r.tileLayer=N5,r.tooltip=eoe,r.transformation=te,r.version=n,r.videoOverlay=Kae;var soe=window.L;r.noConflict=function(){return window.L=soe,this},window.L=r})})(XO,XO.exports);var Ef=XO.exports;const Gie=$t(Ef);function S0(e,t,r){return Object.freeze({instance:e,context:t,container:r})}function WR(e,t){return t==null?function(n,i){const a=U.useRef();return a.current||(a.current=e(n,i)),a}:function(n,i){const a=U.useRef();a.current||(a.current=e(n,i));const o=U.useRef(n),{instance:s}=a.current;return U.useEffect(function(){o.current!==n&&(t(s,n,o.current),o.current=n)},[s,n,i]),a}}function Wie(e,t){U.useEffect(function(){return(t.layerContainer??t.map).addLayer(e.instance),function(){var a;(a=t.layerContainer)==null||a.removeLayer(e.instance),t.map.removeLayer(e.instance)}},[t,e])}function VQe(e){return function(r){const n=aC(),i=e(oC(r,n),n);return zie(n.map,r.attribution),GR(i.current,r.eventHandlers),Wie(i.current,n),i}}function GQe(e,t){const r=U.useRef();U.useEffect(function(){if(t.pathOptions!==r.current){const i=t.pathOptions??{};e.instance.setStyle(i),r.current=i}},[e,t])}function WQe(e){return function(r){const n=aC(),i=e(oC(r,n),n);return GR(i.current,r.eventHandlers),Wie(i.current,n),GQe(i.current,r),i}}function Hie(e,t){const r=WR(e),n=FQe(r,t);return zQe(n)}function Uie(e,t){const r=WR(e,t),n=WQe(r);return BQe(n)}function HQe(e,t){const r=WR(e,t),n=VQe(r);return $Qe(n)}function UQe(e,t,r){const{opacity:n,zIndex:i}=t;n!=null&&n!==r.opacity&&e.setOpacity(n),i!=null&&i!==r.zIndex&&e.setZIndex(i)}function ZQe(){return aC().map}const YQe=Uie(function({center:t,children:r,...n},i){const a=new Ef.CircleMarker(t,n);return S0(a,$ie(i,{overlayContainer:a}))},NQe);function qO(){return qO=Object.assign||function(e){for(var t=1;t(d==null?void 0:d.map)??null,[d]);const g=U.useCallback(y=>{if(y!==null&&d===null){const x=new Ef.Map(y,c);r!=null&&u!=null?x.setView(r,u):e!=null&&x.fitBounds(e,t),l!=null&&x.whenReady(l),v(RQe(x))}},[]);U.useEffect(()=>()=>{d==null||d.map.remove()},[d]);const m=d?J.createElement(Vie,{value:d},n):o??null;return J.createElement("div",qO({},h,{ref:g}),m)}const qQe=U.forwardRef(XQe),KQe=Uie(function({positions:t,...r},n){const i=new Ef.Polyline(t,r);return S0(i,$ie(n,{overlayContainer:i}))},function(t,r,n){r.positions!==n.positions&&t.setLatLngs(r.positions)}),QQe=Hie(function(t,r){const n=new Ef.Popup(t,r.overlayContainer);return S0(n,r)},function(t,r,{position:n},i){U.useEffect(function(){const{instance:o}=t;function s(u){u.popup===o&&(o.update(),i(!0))}function l(u){u.popup===o&&i(!1)}return r.map.on({popupopen:s,popupclose:l}),r.overlayContainer==null?(n!=null&&o.setLatLng(n),o.openOn(r.map)):r.overlayContainer.bindPopup(o),function(){var c;r.map.off({popupopen:s,popupclose:l}),(c=r.overlayContainer)==null||c.unbindPopup(),r.map.removeLayer(o)}},[t,r,i,n])}),JQe=HQe(function({url:t,...r},n){const i=new Ef.TileLayer(t,oC(r,n));return S0(i,n)},function(t,r,n){UQe(t,r,n);const{url:i}=r;i!=null&&i!==n.url&&t.setUrl(i)}),eJe=Hie(function(t,r){const n=new Ef.Tooltip(t,r.overlayContainer);return S0(n,r)},function(t,r,{position:n},i){U.useEffect(function(){const o=r.overlayContainer;if(o==null)return;const{instance:s}=t,l=c=>{c.tooltip===s&&(n!=null&&s.setLatLng(n),s.update(),i(!0))},u=c=>{c.tooltip===s&&i(!1)};return o.on({tooltipopen:l,tooltipclose:u}),o.bindTooltip(s),function(){o.off({tooltipopen:l,tooltipclose:u}),o._map!=null&&o.unbindTooltip()}},[t,r,i,n])}),tJe="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAFgUlEQVR4Aa1XA5BjWRTN2oW17d3YaZtr2962HUzbDNpjszW24mRt28p47v7zq/bXZtrp/lWnXr337j3nPCe85NcypgSFdugCpW5YoDAMRaIMqRi6aKq5E3YqDQO3qAwjVWrD8Ncq/RBpykd8oZUb/kaJutow8r1aP9II0WmLKLIsJyv1w/kqw9Ch2MYdB++12Onxee/QMwvf4/Dk/Lfp/i4nxTXtOoQ4pW5Aj7wpici1A9erdAN2OH64x8OSP9j3Ft3b7aWkTg/Fm91siTra0f9on5sQr9INejH6CUUUpavjFNq1B+Oadhxmnfa8RfEmN8VNAsQhPqF55xHkMzz3jSmChWU6f7/XZKNH+9+hBLOHYozuKQPxyMPUKkrX/K0uWnfFaJGS1QPRtZsOPtr3NsW0uyh6NNCOkU3Yz+bXbT3I8G3xE5EXLXtCXbbqwCO9zPQYPRTZ5vIDXD7U+w7rFDEoUUf7ibHIR4y6bLVPXrz8JVZEql13trxwue/uDivd3fkWRbS6/IA2bID4uk0UpF1N8qLlbBlXs4Ee7HLTfV1j54APvODnSfOWBqtKVvjgLKzF5YdEk5ewRkGlK0i33Eofffc7HT56jD7/6U+qH3Cx7SBLNntH5YIPvODnyfIXZYRVDPqgHtLs5ABHD3YzLuespb7t79FY34DjMwrVrcTuwlT55YMPvOBnRrJ4VXTdNnYug5ucHLBjEpt30701A3Ts+HEa73u6dT3FNWwflY86eMHPk+Yu+i6pzUpRrW7SNDg5JHR4KapmM5Wv2E8Tfcb1HoqqHMHU+uWDD7zg54mz5/2BSnizi9T1Dg4QQXLToGNCkb6tb1NU+QAlGr1++eADrzhn/u8Q2YZhQVlZ5+CAOtqfbhmaUCS1ezNFVm2imDbPmPng5wmz+gwh+oHDce0eUtQ6OGDIyR0uUhUsoO3vfDmmgOezH0mZN59x7MBi++WDL1g/eEiU3avlidO671bkLfwbw5XV2P8Pzo0ydy4t2/0eu33xYSOMOD8hTf4CrBtGMSoXfPLchX+J0ruSePw3LZeK0juPJbYzrhkH0io7B3k164hiGvawhOKMLkrQLyVpZg8rHFW7E2uHOL888IBPlNZ1FPzstSJM694fWr6RwpvcJK60+0HCILTBzZLFNdtAzJaohze60T8qBzyh5ZuOg5e7uwQppofEmf2++DYvmySqGBuKaicF1blQjhuHdvCIMvp8whTTfZzI7RldpwtSzL+F1+wkdZ2TBOW2gIF88PBTzD/gpeREAMEbxnJcaJHNHrpzji0gQCS6hdkEeYt9DF/2qPcEC8RM28Hwmr3sdNyht00byAut2k3gufWNtgtOEOFGUwcXWNDbdNbpgBGxEvKkOQsxivJx33iow0Vw5S6SVTrpVq11ysA2Rp7gTfPfktc6zhtXBBC+adRLshf6sG2RfHPZ5EAc4sVZ83yCN00Fk/4kggu40ZTvIEm5g24qtU4KjBrx/BTTH8ifVASAG7gKrnWxJDcU7x8X6Ecczhm3o6YicvsLXWfh3Ch1W0k8x0nXF+0fFxgt4phz8QvypiwCCFKMqXCnqXExjq10beH+UUA7+nG6mdG/Pu0f3LgFcGrl2s0kNNjpmoJ9o4B29CMO8dMT4Q5ox8uitF6fqsrJOr8qnwNbRzv6hSnG5wP+64C7h9lp30hKNtKdWjtdkbuPA19nJ7Tz3zR/ibgARbhb4AlhavcBebmTHcFl2fvYEnW0ox9xMxKBS8btJ+KiEbq9zA4RthQXDhPa0T9TEe69gWupwc6uBUphquXgf+/FrIjweHQS4/pduMe5ERUMHUd9xv8ZR98CxkS4F2n3EUrUZ10EYNw7BWm9x1GiPssi3GgiGRDKWRYZfXlON+dfNbM+GgIwYdwAAAAASUVORK5CYII=",rJe="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAABSCAMAAAAhFXfZAAAC91BMVEVMaXEzeak2f7I4g7g3g7cua5gzeKg8hJo3grY4g7c3grU0gLI2frE0daAubJc2gbQwd6QzeKk2gLMtd5sxdKIua5g1frA2f7IydaM0e6w2fq41fK01eqo3grgubJgta5cxdKI1f7AydaQydaMxc6EubJgvbJkwcZ4ubZkwcJwubZgubJcydqUydKIxapgubJctbJcubZcubJcvbJYubJcvbZkubJctbJctbZcubJg2f7AubJcrbZcubJcubJcua5g3grY0fq8ubJcubJdEkdEwhsw6i88vhswuhcsuhMtBjMgthMsrg8srgss6is8qgcs8i9A9iMYtg8spgcoogMo7hcMngMonf8olfso4gr8kfck5iM8jfMk4iM8he8k1fro7itAgesk2hs8eecgzfLcofssdeMg0hc4cd8g2hcsxeLQbdsgZdcgxeLImfcszhM0vda4xgckzhM4xg84wf8Yxgs4udKsvfcQucqhUndROmdM1fK0wcZ8vb5w0eqpQm9MzeKhXoNVcpdYydKNWn9VZotVKltJFjsIwcJ1Rms9OlslLmtH///8+kc9epdYzd6dbo9VHkMM2f7FHmNBClM8ydqVcpNY9hro3gLM9hLczealQmcw3fa46f7A8gLMxc6I3eagyc6FIldJMl9JSnNRSntNNl9JPnNJFi75UnM9ZodVKksg8kM45jc09e6ZHltFBk883gbRBh7pDk9EwcaBzn784g7dKkcY2i81Om9M7j85Llc81is09g7Q4grY/j9A0eqxKmdFFltBEjcXf6fFImdBCiLxJl9FGlNFBi78yiMxVndEvbpo6js74+vx+psPP3+o/ks5HkcpGmNCjwdZCkNDM3ehYoNJEls+lxNkxh8xHks0+jdC1zd5Lg6r+/v/H2ufz9/o3jM3t8/edvdM/k89Th61OiLBSjbZklbaTt9BfptdjmL1AicBHj8hGk9FAgK1dkLNTjLRekrdClc/k7fM0icy0y9tgp9c4jc2NtM9Dlc8zicxeXZn3AAAAQ3RSTlMAHDdTb4yPA+LtnEQmC4L2EmHqB7XA0d0sr478x4/Yd5i1zOfyPkf1sLVq4Nh3FvjxopQ2/STNuFzUwFIwxKaejILpIBEV9wAABhVJREFUeF6s1NdyFEcYBeBeoQIhRAkLlRDGrhIgY3BJL8CVeKzuyXFzzjkn5ZxzzuScg3PO8cKzu70JkO0LfxdTU//pM9vTu7Xgf6KqOVTb9X7toRrVEfBf1HTVjZccrT/2by1VV928Yty9ZbVuucdz90frG8DBjl9pVApbOstvmMuvVgaNXSfAAd6pGxpy6yxf5ph43pS/4f3uoaGm2rdu72S9xzOvMymkZFq/ptDrk90mhW7e4zl7HLzhxGWPR20xmSxJ/VqldG5m9XhaVOA1DadsNh3Pu5L2N6QtPO/32JpqQBVVk20oy/Pi2s23WEvyfHbe1thadVQttvm7Llf65gGmXK67XtupyoM7HQhmXdLS8oGWJNeOJ3C5fG5XCEJnkez3/oFdsvgJ4l2ANZwhrJKk/7OSXa+3Vw2WJMlKnGkobouYk6T0TyX30klOUnTD9HJ5qpckL3EW/w4XF3Xd0FGywXUrstrclVsqz5Pd/sXFYyDnPdrLcQODmGOK47IZb4CmibmMn+MYRzFZ5jg33ZL/EJrWcszHmANy3ARBK/IXtciJy8VsitPSdE3uuHxzougojcUdr8/32atnz/ev3f/K5wtpxUTpcaI45zusVDpYtZi+jg0oU9b3x74h7+n9ABvYEZeKaVq0sh0AtLKsFtqNBdeT0MrSzwwlq9+x6xAO4tgOtSzbCjrNQQiNvQUbUEubvzBUeGw26yDCsRHCoLkTHDa7IdOLIThs/gHvChszh2CimE8peRs47cxANI0lYNB5y1DljpOF0IhzBDPOZnDOqYYbeGKECbPzWnXludPphw5c2YBq5zlwXphIbO4VDCZ0gnPfUO1TwZoYwAs2ExPCedAu9DAjfQUjzITQb3jNj0KG2Sgt6BHaQUdYzWz+XmBktOHwanXjaSTcwwziBcuMOtwBmqPrTOxFQR/DRKKPqyur0aiW6cULYsx6tBm0jXpR/AUWR6HRq9WVW6MRhIq5jLyjbaCTDCijyYJNpCajdyobP/eTw0iexBAKkJ3gA5KcQb2zBXsIBckn+xVv8jkZSaEFHE+jFEleAEfayRU0MouNoBmB/L50Ai/HSLIHxcrpCvnhSQAuakKp2C/YbCylJjXRVy/z3+Kv/RrNcCo+WUzlVEhzKffnTQnxeN9fWF88fiNCUdSTsaufaChKWInHeysygfpIqagoakW+vV20J8uyl6TyNKEZWV4oRSPyCkWpgOLSbkCObT8o2r6tlG58HQquf6O0v50tB7JM7F4EORd2dx/K0w/KHsVkLPaoYrwgP/y7krr3SSMA4zj+OBgmjYkxcdIJQyQRKgg2viX9Hddi9UBb29LrKR7CVVEEEXWojUkXNyfTNDE14W9gbHJNuhjDettN3ZvbOvdOqCD3Jp/9l+/wJE+9PkYGjx/fqkys3S2rMozM/o2106rfMUINo6hVqz+eu/hd1c4xTg0TAfy5kV+4UG6+IthHTU9woWmxuKNbTfuCSfovBCxq7EtHqvYL4Sm6F8GVxsSXHMQ07TOi1DKtZxjWaaIyi4CXWjxPccUw8WVbMYY5wxC1mzEyXMJWkllpRloi+Kkoq69sxBTlElF6aAxYUbjXNlhlDZilDnM4U5SlN5biRsRHnbx3mbeWjEh4mEyiuJDl5XcWVmX5GvNkFgLWZM5qwsop4/AWfLhU1cR7k1VVvcYCWRkOI6Xy5gmnphCYIkvzuNYzHzosq2oNk2RtSs8khfUOfHIDgR6ysYBaMpl4uEgk2U/oJTs9AaTSwma7dT69geAE2ZpEjUsn2ieJNHeKfrI3EcAGJ2ZaNgVuC8EBctCLc57P5u5led6IOBkIYkuQMrmmjChs4VkfOerHqSBkPzZlhe06RslZ3zMjk2sscqKwY0RcjKK+LWbzd7KiHhkncs/siFJ+V5eXxD34B8nVuJEpGJNmxN2gH3vSvp7J70tF+D1Ej8qUJD1TkErAND2GZwTFg/LubvmgiBG3SOvdlsqFQrkEzJCL1rstlnVFROixZoDDSuXQFHESwVGlcuQcMb/b42NgjLowh5MTDFE3vNB5qStRIErdCQEh6pLPR92anSUb/wAIhldAaDMpGgAAAABJRU5ErkJggg==",nJe="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAApCAQAAAACach9AAACMUlEQVR4Ae3ShY7jQBAE0Aoz/f9/HTMzhg1zrdKUrJbdx+Kd2nD8VNudfsL/Th///dyQN2TH6f3y/BGpC379rV+S+qqetBOxImNQXL8JCAr2V4iMQXHGNJxeCfZXhSRBcQMfvkOWUdtfzlLgAENmZDcmo2TVmt8OSM2eXxBp3DjHSMFutqS7SbmemzBiR+xpKCNUIRkdkkYxhAkyGoBvyQFEJEefwSmmvBfJuJ6aKqKWnAkvGZOaZXTUgFqYULWNSHUckZuR1HIIimUExutRxwzOLROIG4vKmCKQt364mIlhSyzAf1m9lHZHJZrlAOMMztRRiKimp/rpdJDc9Awry5xTZCte7FHtuS8wJgeYGrex28xNTd086Dik7vUMscQOa8y4DoGtCCSkAKlNwpgNtphjrC6MIHUkR6YWxxs6Sc5xqn222mmCRFzIt8lEdKx+ikCtg91qS2WpwVfBelJCiQJwvzixfI9cxZQWgiSJelKnwBElKYtDOb2MFbhmUigbReQBV0Cg4+qMXSxXSyGUn4UbF8l+7qdSGnTC0XLCmahIgUHLhLOhpVCtw4CzYXvLQWQbJNmxoCsOKAxSgBJno75avolkRw8iIAFcsdc02e9iyCd8tHwmeSSoKTowIgvscSGZUOA7PuCN5b2BX9mQM7S0wYhMNU74zgsPBj3HU7wguAfnxxjFQGBE6pwN+GjME9zHY7zGp8wVxMShYX9NXvEWD3HbwJf4giO4CFIQxXScH1/TM+04kkBiAAAAAElFTkSuQmCC";delete Gie.Icon.Default.prototype._getIconUrl;Gie.Icon.Default.mergeOptions({iconUrl:tJe,iconRetinaUrl:rJe,shadowUrl:nJe});const FU=["#3b82f6","#a78bfa","#06b6d4","#f59e0b","#22c55e","#ec4899","#8b5cf6","#14b8a6"],iJe=["ROUTER","ROUTER_LATE","REPEATER","TRACKER"];function aJe(e){return e>12?"#22c55e":e>8?"#4ade80":e>5?"#f59e0b":e>3?"#f97316":"#ef4444"}function oJe(e){return e===null||e>46?0:e>44.5?1:e>43?2:3}function sJe(e){if(!e)return"Unknown";const t=new Date(e),n=new Date().getTime()-t.getTime(),i=Math.floor(n/6e4),a=Math.floor(n/36e5),o=Math.floor(n/864e5);return i<1?"Just now":i<60?`${i}m ago`:a<24?`${a}h ago`:`${o}d ago`}function lJe({bounds:e}){const t=ZQe();return U.useEffect(()=>{e&&t.fitBounds(e,{padding:[50,50]})},[t,e]),null}function uJe({node:e}){const t=e.latitude!==null&&e.longitude!==null,r=e.battery_level!==null?e.battery_level>100||e.voltage&&e.voltage>4.1?"USB ⚡":`${e.battery_level.toFixed(0)}%`:"Unknown";return _.jsxs("div",{className:"min-w-[200px]",children:[_.jsx("div",{className:"font-semibold text-slate-800",children:e.short_name}),_.jsx("div",{className:"text-xs text-slate-600 mb-2",children:e.long_name}),_.jsxs("div",{className:"grid grid-cols-2 gap-x-4 gap-y-1 text-xs",children:[_.jsx("div",{className:"text-slate-500",children:"Role"}),_.jsx("div",{className:"text-slate-700 font-medium",children:e.role}),_.jsx("div",{className:"text-slate-500",children:"Hardware"}),_.jsx("div",{className:"text-slate-700",children:e.hardware||"Unknown"}),_.jsx("div",{className:"text-slate-500",children:"Battery"}),_.jsx("div",{className:"text-slate-700",children:r}),_.jsx("div",{className:"text-slate-500",children:"Last Heard"}),_.jsx("div",{className:"text-slate-700",children:sJe(e.last_heard)})]}),t&&_.jsxs("div",{className:"mt-3 pt-2 border-t border-slate-200 flex gap-2",children:[_.jsxs("a",{href:`https://www.google.com/maps?q=${e.latitude},${e.longitude}`,target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-1 text-xs text-blue-600 hover:text-blue-800",children:[_.jsx(wd,{size:10}),"Google Maps"]}),_.jsxs("a",{href:`https://www.openstreetmap.org/?mlat=${e.latitude}&mlon=${e.longitude}&zoom=14`,target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-1 text-xs text-blue-600 hover:text-blue-800",children:[_.jsx(wd,{size:10}),"OSM"]})]})]})}function cJe({nodes:e,edges:t,selectedNodeId:r,onSelectNode:n}){const i=U.useMemo(()=>e.filter(f=>f.latitude!==null&&f.longitude!==null),[e]),a=e.length-i.length,o=U.useMemo(()=>new Map(i.map(f=>[f.node_num,f])),[i]),s=U.useMemo(()=>t.filter(f=>o.has(f.from_node)&&o.has(f.to_node)),[t,o]),l=U.useMemo(()=>{if(i.length===0)return null;const f=i.map(d=>d.latitude),h=i.map(d=>d.longitude);return[[Math.min(...f),Math.min(...h)],[Math.max(...f),Math.max(...h)]]},[i]),u=[43.6,-114.4],c=U.useMemo(()=>{const f=new Set;return r!==null&&t.forEach(h=>{h.from_node===r&&f.add(h.to_node),h.to_node===r&&f.add(h.from_node)}),f},[r,t]);return _.jsxs("div",{className:"relative bg-bg-card rounded-lg border border-border overflow-hidden",children:[_.jsxs(qQe,{center:u,zoom:7,style:{width:"100%",height:"540px"},className:"z-0",children:[_.jsx(JQe,{url:"https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",attribution:'© OpenStreetMap, © CARTO'}),_.jsx(lJe,{bounds:l}),s.map((f,h)=>{const d=o.get(f.from_node),v=o.get(f.to_node),g=r===null||f.from_node===r||f.to_node===r;return _.jsx(KQe,{positions:[[d.latitude,d.longitude],[v.latitude,v.longitude]],color:aJe(f.snr),weight:g&&r!==null?2.5:1.5,opacity:r===null?.3:g?.6:.08},h)}),i.map(f=>{const h=f.node_num===r,d=c.has(f.node_num),v=r===null||h||d,g=iJe.includes(f.role),m=oJe(f.latitude),y=FU[m%FU.length];return _.jsxs(YQe,{center:[f.latitude,f.longitude],radius:g?8:5,fillColor:g?y:"#111827",fillOpacity:v?.9:.2,stroke:!0,color:h?"#ffffff":y,weight:h?3:g?0:2,opacity:v?1:.3,eventHandlers:{click:()=>n(h?null:f.node_num)},children:[_.jsx(eJe,{direction:"top",offset:[0,-8],children:_.jsx("span",{className:"font-mono text-xs",children:f.short_name})}),_.jsx(QQe,{children:_.jsx(uJe,{node:f})})]},f.node_num)})]}),_.jsxs("div",{className:"absolute bottom-4 left-4 bg-bg-card/90 backdrop-blur-sm border border-border rounded px-3 py-2 text-xs text-slate-400 flex items-center gap-2",children:[_.jsx(KE,{size:12}),_.jsxs("span",{children:["Showing ",i.length," of ",e.length," nodes",a>0&&_.jsxs("span",{className:"text-slate-500",children:[" (",a," without coordinates)"]})]})]})]})}const VU=["#3b82f6","#a78bfa","#06b6d4","#f59e0b","#22c55e","#ec4899","#8b5cf6","#14b8a6"],fJe=["ROUTER","ROUTER_LATE","REPEATER","TRACKER"];function GU(e){return e>12?"#22c55e":e>8?"#4ade80":e>5?"#f59e0b":e>3?"#f97316":"#ef4444"}function hJe(e){return e>12?"excellent":e>8?"good":e>5?"fair":e>3?"marginal":"poor"}function dJe(e){return e===null||e>46?0:e>44.5?1:e>43?2:3}function vJe(e){return["Northern ID","Central ID","SW Idaho","SC Idaho"][e]||"Unknown"}function pJe(e){if(!e)return"Unknown";const t=new Date(e),n=new Date().getTime()-t.getTime(),i=Math.floor(n/6e4),a=Math.floor(n/36e5),o=Math.floor(n/864e5);return i<1?"Just now":i<60?`${i}m ago`:a<24?`${a}h ago`:`${o}d ago`}function gJe(e){if(!e)return"bg-slate-500";const t=new Date(e),n=(new Date().getTime()-t.getTime())/36e5;return n<1?"bg-green-500":n<24?"bg-amber-500":"bg-slate-500"}function mJe({node:e,edges:t,nodes:r,onSelectNode:n}){const i=U.useMemo(()=>{if(!e)return[];const f=new Map(r.map(d=>[d.node_num,d])),h=[];return t.forEach(d=>{if(d.from_node===e.node_num){const v=f.get(d.to_node);v&&h.push({node:v,snr:d.snr,quality:d.quality})}else if(d.to_node===e.node_num){const v=f.get(d.from_node);v&&h.push({node:v,snr:d.snr,quality:d.quality})}}),h.sort((d,v)=>v.snr-d.snr)},[e,t,r]);if(!e)return _.jsxs("div",{className:"w-[250px] flex-shrink-0 bg-bg-card border-l border-border p-4 flex flex-col items-center justify-center h-[540px]",children:[_.jsx("div",{className:"w-12 h-12 rounded-full bg-bg-hover border border-border flex items-center justify-center mb-3",children:_.jsx(Za,{size:24,className:"text-slate-500"})}),_.jsx("p",{className:"text-sm text-slate-500 text-center",children:"Click a node to inspect"})]});const a=fJe.includes(e.role),o=dJe(e.latitude),s=VU[o%VU.length],l=e.latitude!==null&&e.longitude!==null,u=e.battery_level!==null?e.battery_level>100||e.voltage&&e.voltage>4.1?"USB":`${e.battery_level.toFixed(0)}%`:"—",c=e.battery_level!==null&&(e.battery_level>100||e.voltage&&e.voltage>4.1);return _.jsxs("div",{className:"w-[250px] flex-shrink-0 bg-bg-card border-l border-border flex flex-col h-[540px] overflow-hidden",children:[_.jsxs("div",{className:"p-4 border-b border-border",children:[_.jsx("div",{className:"inline-flex items-center px-2 py-0.5 rounded text-xs font-mono mb-2",style:{backgroundColor:`${s}20`,color:s},children:e.node_id_hex}),_.jsx("div",{className:"font-mono text-lg text-slate-100",children:e.short_name}),_.jsx("div",{className:"text-xs text-slate-500 truncate",children:e.long_name})]}),_.jsxs("div",{className:"p-4 border-b border-border grid grid-cols-2 gap-3",children:[_.jsxs("div",{children:[_.jsx("div",{className:"text-xs text-slate-500 mb-0.5",children:"Role"}),_.jsx("div",{className:`text-sm font-medium ${a?"text-cyan-400":"text-slate-300"}`,children:e.role})]}),_.jsxs("div",{children:[_.jsx("div",{className:"text-xs text-slate-500 mb-0.5",children:"Region"}),_.jsx("div",{className:"text-sm text-slate-300",children:vJe(o)})]}),_.jsxs("div",{children:[_.jsx("div",{className:"text-xs text-slate-500 mb-0.5",children:"Battery"}),_.jsxs("div",{className:"text-sm text-slate-300 flex items-center gap-1",children:[c&&_.jsx(Mm,{size:12,className:"text-amber-400"}),u]})]}),_.jsxs("div",{children:[_.jsx("div",{className:"text-xs text-slate-500 mb-0.5",children:"Status"}),_.jsxs("div",{className:"flex items-center gap-1.5",children:[_.jsx("div",{className:`w-2 h-2 rounded-full ${gJe(e.last_heard)}`}),_.jsx("span",{className:"text-sm text-slate-300",children:pJe(e.last_heard)})]})]}),_.jsxs("div",{className:"col-span-2",children:[_.jsx("div",{className:"text-xs text-slate-500 mb-0.5",children:"Hardware"}),_.jsx("div",{className:"text-sm text-slate-300 font-mono truncate",children:e.hardware||"Unknown"})]})]}),l&&_.jsxs("div",{className:"px-4 py-3 border-b border-border flex gap-3",children:[_.jsxs("a",{href:`https://www.google.com/maps?q=${e.latitude},${e.longitude}`,target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-1 text-xs text-blue-400 hover:text-blue-300",children:[_.jsx(wd,{size:10}),"Google Maps"]}),_.jsxs("a",{href:`https://www.openstreetmap.org/?mlat=${e.latitude}&mlon=${e.longitude}&zoom=14`,target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-1 text-xs text-blue-400 hover:text-blue-300",children:[_.jsx(wd,{size:10}),"OSM"]})]}),_.jsxs("div",{className:"flex-1 overflow-y-auto",children:[_.jsxs("div",{className:"px-4 py-2 text-xs text-slate-500 font-medium sticky top-0 bg-bg-card border-b border-border",children:["Neighbors (",i.length,")"]}),i.length>0?_.jsx("div",{className:"divide-y divide-border",children:i.map(f=>_.jsxs("button",{onClick:()=>n(f.node.node_num),className:"w-full px-4 py-2 text-left hover:bg-bg-hover transition-colors flex items-center gap-2",style:{borderLeftWidth:3,borderLeftColor:GU(f.snr)},children:[_.jsxs("div",{className:"flex-1 min-w-0",children:[_.jsx("div",{className:"text-sm text-slate-200 font-mono truncate",children:f.node.short_name}),_.jsx("div",{className:"text-xs text-slate-500 truncate",children:f.node.long_name})]}),_.jsxs("div",{className:"text-right flex-shrink-0",children:[_.jsxs("div",{className:"text-xs font-mono",style:{color:GU(f.snr)},children:[f.snr.toFixed(1)," dB"]}),_.jsx("div",{className:"text-xs text-slate-500",children:hJe(f.snr)})]})]},f.node.node_num))}):_.jsx("div",{className:"px-4 py-6 text-center text-sm text-slate-500",children:"No known neighbors"})]})]})}const WU=["ROUTER","ROUTER_LATE","REPEATER","TRACKER"];function yJe(e){if(!e)return"bg-slate-500";const t=new Date(e),n=(new Date().getTime()-t.getTime())/36e5;return n<1?"bg-green-500":n<24?"bg-amber-500":"bg-slate-500"}function _Je(e){if(!e)return"—";const t=new Date(e),n=new Date().getTime()-t.getTime(),i=Math.floor(n/6e4),a=Math.floor(n/36e5),o=Math.floor(n/864e5);return i<1?"Just now":i<60?`${i}m ago`:a<24?`${a}h ago`:`${o}d ago`}function xJe(e){return e.battery_level===null?"—":e.battery_level>100||e.voltage&&e.voltage>4.1?"USB ⚡":`${e.battery_level.toFixed(0)}%`}function HU(e){return e===null?"—":e>46?"Northern":e>44.5?"Central":e>43?"SW Idaho":"SC Idaho"}function bJe({nodes:e,selectedNodeId:t,onSelectNode:r}){const[n,i]=U.useState(""),[a,o]=U.useState("short_name"),[s,l]=U.useState("asc"),[u,c]=U.useState("all"),f=U.useMemo(()=>{let v=[...e];if(u==="infra"?v=v.filter(g=>WU.includes(g.role)):u==="online"&&(v=v.filter(g=>{if(!g.last_heard)return!1;const m=new Date(g.last_heard);return(new Date().getTime()-m.getTime())/36e5<1})),n){const g=n.toLowerCase();v=v.filter(m=>m.short_name.toLowerCase().includes(g)||m.long_name.toLowerCase().includes(g)||m.role.toLowerCase().includes(g)||HU(m.latitude).toLowerCase().includes(g))}return v.sort((g,m)=>{let y="",x="";switch(a){case"short_name":y=g.short_name.toLowerCase(),x=m.short_name.toLowerCase();break;case"role":y=g.role,x=m.role;break;case"battery_level":y=g.battery_level??-1,x=m.battery_level??-1;break;case"last_heard":y=g.last_heard?new Date(g.last_heard).getTime():0,x=m.last_heard?new Date(m.last_heard).getTime():0;break;case"hardware":y=g.hardware.toLowerCase(),x=m.hardware.toLowerCase();break}return yx?s==="asc"?1:-1:0}),v},[e,n,a,s,u]),h=v=>{a===v?l(s==="asc"?"desc":"asc"):(o(v),l("asc"))},d=({field:v})=>a!==v?null:s==="asc"?_.jsx(ece,{size:14,className:"inline ml-1"}):_.jsx(mv,{size:14,className:"inline ml-1"});return _.jsxs("div",{className:"bg-bg-card border border-border rounded-lg overflow-hidden",children:[_.jsxs("div",{className:"p-3 border-b border-border flex items-center gap-3",children:[_.jsxs("div",{className:"relative flex-1 max-w-xs",children:[_.jsx(eD,{size:14,className:"absolute left-3 top-1/2 -translate-y-1/2 text-slate-500"}),_.jsx("input",{type:"text",placeholder:"Search nodes...",value:n,onChange:v=>i(v.target.value),className:"w-full pl-9 pr-3 py-1.5 bg-bg-hover border border-border rounded text-sm text-slate-200 placeholder-slate-500 focus:outline-none focus:border-accent"})]}),_.jsxs("div",{className:"flex items-center gap-1",children:[_.jsx(qE,{size:14,className:"text-slate-500 mr-1"}),["all","infra","online"].map(v=>_.jsx("button",{onClick:()=>c(v),className:`px-2 py-1 text-xs rounded transition-colors ${u===v?"bg-accent text-white":"bg-bg-hover text-slate-400 hover:text-slate-200"}`,children:v==="all"?"All":v==="infra"?"Infra":"Online"},v))]}),_.jsxs("div",{className:"text-xs text-slate-500 ml-auto",children:[f.length," of ",e.length," nodes"]})]}),_.jsxs("div",{className:"overflow-x-auto",children:[_.jsxs("table",{className:"w-full text-sm",children:[_.jsx("thead",{children:_.jsxs("tr",{className:"bg-bg-hover text-slate-400 text-xs",children:[_.jsx("th",{className:"w-8 px-3 py-2"}),_.jsxs("th",{className:"px-3 py-2 text-left cursor-pointer hover:text-slate-200",onClick:()=>h("short_name"),children:["Name ",_.jsx(d,{field:"short_name"})]}),_.jsxs("th",{className:"px-3 py-2 text-left cursor-pointer hover:text-slate-200",onClick:()=>h("role"),children:["Role ",_.jsx(d,{field:"role"})]}),_.jsx("th",{className:"px-3 py-2 text-left",children:"Region"}),_.jsxs("th",{className:"px-3 py-2 text-left cursor-pointer hover:text-slate-200",onClick:()=>h("battery_level"),children:["Battery ",_.jsx(d,{field:"battery_level"})]}),_.jsxs("th",{className:"px-3 py-2 text-left cursor-pointer hover:text-slate-200",onClick:()=>h("last_heard"),children:["Last Heard ",_.jsx(d,{field:"last_heard"})]}),_.jsxs("th",{className:"px-3 py-2 text-left cursor-pointer hover:text-slate-200",onClick:()=>h("hardware"),children:["Hardware ",_.jsx(d,{field:"hardware"})]})]})}),_.jsx("tbody",{className:"divide-y divide-border",children:f.slice(0,100).map(v=>{const g=WU.includes(v.role),m=v.node_num===t;return _.jsxs("tr",{onClick:()=>r(v.node_num),className:`cursor-pointer transition-colors ${m?"bg-accent/10":"hover:bg-bg-hover"}`,children:[_.jsx("td",{className:"px-3 py-2",children:_.jsx("div",{className:`w-2 h-2 rounded-full ${yJe(v.last_heard)}`})}),_.jsxs("td",{className:"px-3 py-2",children:[_.jsx("div",{className:"font-mono text-slate-200",children:v.short_name}),_.jsx("div",{className:"text-xs text-slate-500 truncate max-w-[200px]",children:v.long_name})]}),_.jsx("td",{className:"px-3 py-2",children:_.jsx("span",{className:`inline-block px-1.5 py-0.5 rounded text-xs font-medium ${g?"bg-cyan-500/20 text-cyan-400":"bg-slate-500/20 text-slate-400"}`,children:v.role})}),_.jsx("td",{className:"px-3 py-2 text-slate-400",children:HU(v.latitude)}),_.jsx("td",{className:"px-3 py-2 font-mono text-slate-300",children:xJe(v)}),_.jsx("td",{className:"px-3 py-2 text-slate-400",children:_Je(v.last_heard)}),_.jsx("td",{className:"px-3 py-2 font-mono text-xs text-slate-400 truncate max-w-[150px]",children:v.hardware||"—"})]},v.node_num)})})]}),f.length>100&&_.jsxs("div",{className:"px-3 py-2 text-xs text-slate-500 text-center border-t border-border",children:["Showing first 100 of ",f.length," nodes"]}),f.length===0&&_.jsx("div",{className:"px-3 py-8 text-sm text-slate-500 text-center",children:"No nodes match your filters"})]})]})}function wJe(){const[e,t]=U.useState([]),[r,n]=U.useState([]),[i,a]=U.useState([]),[o,s]=U.useState(null),[l,u]=U.useState("topo"),[c,f]=U.useState(!0),[h,d]=U.useState(null);U.useEffect(()=>{document.title="Mesh — MeshAI",Promise.all([dce(),vce(),_ce()]).then(([m,y,x])=>{t(m),n(y),a(x),f(!1)}).catch(m=>{d(m.message),f(!1)})},[]);const v=U.useMemo(()=>e.find(m=>m.node_num===o)||null,[e,o]),g=U.useCallback(m=>{s(m)},[]);return c?_.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsx("div",{className:"text-slate-400",children:"Loading mesh data..."})}):h?_.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsxs("div",{className:"text-red-400",children:["Error: ",h]})}):_.jsxs("div",{className:"space-y-6",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("div",{className:"text-sm text-slate-400",children:[e.length," nodes • ",r.length," edges"]}),_.jsxs("div",{className:"flex items-center bg-bg-card border border-border rounded-lg p-1",children:[_.jsxs("button",{onClick:()=>u("topo"),className:`flex items-center gap-2 px-3 py-1.5 rounded text-sm transition-colors ${l==="topo"?"bg-accent text-white":"text-slate-400 hover:text-slate-200"}`,children:[_.jsx(uce,{size:14}),"Topology"]}),_.jsxs("button",{onClick:()=>u("geo"),className:`flex items-center gap-2 px-3 py-1.5 rounded text-sm transition-colors ${l==="geo"?"bg-accent text-white":"text-slate-400 hover:text-slate-200"}`,children:[_.jsx(sce,{size:14}),"Geographic"]})]})]}),_.jsxs("div",{className:"flex gap-0",children:[_.jsx("div",{className:"flex-1 min-w-0",children:l==="topo"?_.jsx(DQe,{nodes:e,edges:r,selectedNodeId:o,onSelectNode:g}):_.jsx(cJe,{nodes:e,edges:r,selectedNodeId:o,onSelectNode:g})}),_.jsx(mJe,{node:v,edges:r,nodes:e,onSelectNode:g})]}),_.jsx(bJe,{nodes:e,selectedNodeId:o,onSelectNode:g})]})}function HR({label:e,value:t,onChange:r,helper:n,info:i,roleFilter:a,valueType:o="short_name"}){const[s,l]=U.useState([]),[u,c]=U.useState(!0),[f,h]=U.useState(""),[d,v]=U.useState(!1);U.useEffect(()=>{fetch("/api/nodes").then(S=>S.json()).then(S=>{l(S),c(!1)}).catch(()=>{l([]),c(!1)})},[]);const g=U.useMemo(()=>{let S=s;if(a&&(S=S.filter(T=>a==="ROUTER"||a==="infrastructure"?T.is_infrastructure||T.role==="ROUTER"||T.role==="ROUTER_CLIENT"||T.role==="REPEATER":T.role===a)),f.trim()){const T=f.toLowerCase();S=S.filter(C=>{var M,P,I,k;return((M=C.short_name)==null?void 0:M.toLowerCase().includes(T))||((P=C.long_name)==null?void 0:P.toLowerCase().includes(T))||((I=C.role)==null?void 0:I.toLowerCase().includes(T))||((k=C.node_id_hex)==null?void 0:k.toLowerCase().includes(T))})}return S.sort((T,C)=>(T.short_name||"").localeCompare(C.short_name||""))},[s,f,a]),m=S=>{switch(o){case"node_num":return String(S.node_num);case"node_id_hex":return S.node_id_hex;default:return S.short_name||String(S.node_num)}},y=S=>{const T=m(S);return t.includes(T)},x=S=>{const T=m(S);t.includes(T)?r(t.filter(C=>C!==T)):r([...t,T])},b=S=>{const T=[S.short_name];return S.long_name&&S.long_name!==S.short_name&&T.push(`— ${S.long_name}`),S.role&&T.push(`(${S.role})`),T.join(" ")};return!u&&s.length===0?_.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"block text-xs text-slate-500 uppercase tracking-wide",children:e}),_.jsx("input",{type:"text",value:t.join(", "),onChange:S=>r(S.target.value.split(",").map(T=>T.trim()).filter(Boolean)),placeholder:"Enter node IDs separated by commas",className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent"}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]}):_.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"block text-xs text-slate-500 uppercase tracking-wide",children:e}),t.length>0&&_.jsx("div",{className:"flex flex-wrap gap-2 mb-2",children:t.map(S=>{const T=s.find(C=>m(C)===S);return _.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-1 bg-accent/20 text-accent rounded text-sm",children:[T?T.short_name:S,_.jsx("button",{type:"button",onClick:()=>r(t.filter(C=>C!==S)),className:"hover:text-white",children:_.jsx(au,{size:14})})]},S)})}),_.jsxs("div",{className:"relative",children:[_.jsxs("div",{className:"relative",children:[_.jsx(eD,{size:14,className:"absolute left-3 top-1/2 -translate-y-1/2 text-slate-500"}),_.jsx("input",{type:"text",value:f,onChange:S=>h(S.target.value),onFocus:()=>v(!0),placeholder:u?"Loading nodes...":"Search nodes...",className:"w-full pl-9 pr-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent"})]}),d&&!u&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"fixed inset-0 z-40",onClick:()=>v(!1)}),_.jsx("div",{className:"absolute left-0 right-0 top-full mt-1 z-50 max-h-64 overflow-y-auto bg-[#0a0e17] border border-[#1e2a3a] rounded-lg shadow-xl",children:g.length===0?_.jsx("div",{className:"p-3 text-sm text-slate-500 text-center",children:"No nodes found"}):g.map(S=>_.jsxs("button",{type:"button",onClick:()=>x(S),className:`w-full flex items-center gap-2 px-3 py-2 text-left text-sm hover:bg-[#1e2a3a] ${y(S)?"bg-accent/10":""}`,children:[_.jsx("div",{className:`w-4 h-4 rounded border flex items-center justify-center ${y(S)?"bg-accent border-accent":"border-slate-600"}`,children:y(S)&&_.jsx(Qc,{size:12,className:"text-white"})}),_.jsx("span",{className:"text-slate-200",children:b(S)})]},S.node_num))})]})]}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]})}function UR(e){const[t,r]=U.useState([]),[n,i]=U.useState(!0);U.useEffect(()=>{fetch("/api/channels").then(h=>h.json()).then(h=>{r(h),i(!1)}).catch(()=>{r([]),i(!1)})},[]);const a=h=>{const d=h.role==="PRIMARY"?"Primary":h.role==="SECONDARY"?"Secondary":"";return`${h.index}: ${h.name}${d?` (${d})`:""}`};if(!n&&t.length===0)return e.mode==="single"?_.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"block text-xs text-slate-500 uppercase tracking-wide",children:e.label}),_.jsx("input",{type:"number",value:e.value,onChange:h=>e.onChange(Number(h.target.value)),min:e.includeDisabled?-1:0,max:7,className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent"}),e.helper&&_.jsx("p",{className:"text-xs text-slate-600",children:e.helper})]}):_.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"block text-xs text-slate-500 uppercase tracking-wide",children:e.label}),_.jsx("input",{type:"text",value:e.value.join(", "),onChange:h=>{const d=h.target.value.split(",").map(v=>parseInt(v.trim())).filter(v=>!isNaN(v));e.onChange(d)},placeholder:"Enter channel numbers separated by commas",className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent"}),e.helper&&_.jsx("p",{className:"text-xs text-slate-600",children:e.helper})]});if(e.mode==="single"){const{value:h,onChange:d,label:v,helper:g,includeDisabled:m}=e,y=t.filter(x=>x.enabled);return _.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"block text-xs text-slate-500 uppercase tracking-wide",children:v}),_.jsxs("select",{value:h,onChange:x=>d(Number(x.target.value)),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent",children:[m&&_.jsx("option",{value:-1,children:"Disabled"}),y.map(x=>_.jsx("option",{value:x.index,children:a(x)},x.index))]}),g&&_.jsx("p",{className:"text-xs text-slate-600",children:g})]})}const{value:o,onChange:s,label:l,helper:u}=e,c=t.filter(h=>h.enabled),f=h=>{o.includes(h)?s(o.filter(d=>d!==h)):s([...o,h].sort((d,v)=>d-v))};return _.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"block text-xs text-slate-500 uppercase tracking-wide",children:l}),_.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg p-2 space-y-1",children:[c.map(h=>_.jsxs("label",{onClick:()=>f(h.index),className:"flex items-center gap-2 p-2 rounded hover:bg-[#0a0e17] cursor-pointer",children:[_.jsx("div",{className:`w-4 h-4 rounded border flex items-center justify-center ${o.includes(h.index)?"bg-accent border-accent":"border-slate-600"}`,children:o.includes(h.index)&&_.jsx(Qc,{size:12,className:"text-white"})}),_.jsx("span",{className:"text-sm text-slate-200",children:a(h)})]},h.index)),c.length===0&&_.jsx("div",{className:"text-sm text-slate-500 p-2",children:"No channels available"})]}),u&&_.jsx("p",{className:"text-xs text-slate-600",children:u})]})}const UU=[{key:"bot",label:"Bot",icon:que},{key:"connection",label:"Connection",icon:LS},{key:"response",label:"Response",icon:kZ},{key:"history",label:"History",icon:ice},{key:"memory",label:"Memory",icon:Kue},{key:"context",label:"Context",icon:XE},{key:"commands",label:"Commands",icon:EZ},{key:"llm",label:"LLM",icon:CZ},{key:"weather",label:"Weather",icon:nu},{key:"meshmonitor",label:"MeshMonitor",icon:Za},{key:"knowledge",label:"Knowledge",icon:SZ},{key:"mesh_sources",label:"Mesh Sources",icon:PZ},{key:"mesh_intelligence",label:"Intelligence",icon:gv},{key:"dashboard",label:"Dashboard",icon:LZ}],fi={bot:"Identity and behavior settings for the bot on the mesh network.",connection:"How MeshAI connects to your Meshtastic radio.",response:"Controls how quickly and how much the bot responds on the mesh.",history:"Conversation history storage and cleanup.",memory:"Short-term conversation memory management. Controls how the bot maintains context within a conversation.",context:"Passive channel monitoring. The bot listens to mesh channels and uses recent messages as context when responding.",commands:"Mesh commands available via the configured prefix. Toggle individual commands on or off.",llm:"AI model configuration. MeshAI uses an LLM to understand questions and generate responses.",weather:"Weather data for the !weather command. This is separate from NWS environmental alerts.",meshmonitor:"AIDA MeshMonitor integration. An additional data source for mesh network monitoring.",knowledge:"Knowledge base for answering questions from stored documents. Connects to Qdrant vector database or local SQLite.",mesh_sources:"Data sources for mesh network information. MeshAI can pull data from multiple sources simultaneously and merge them into a unified view.",mesh_intelligence:"Advanced mesh analysis: health scoring, region management, and automated alerting. The intelligence engine monitors your mesh and detects problems automatically.",dashboard:"Web dashboard settings. You're looking at it right now."},SJe=[{name:"help",description:"Show available commands and usage"},{name:"health",description:"Mesh network health overview with status dots"},{name:"status",description:"Quick mesh status summary"},{name:"region",description:"List regions or get detailed region breakdown"},{name:"neighbors",description:"Show top infrastructure neighbors with signal quality"},{name:"ping",description:"Test bot responsiveness"},{name:"clear",description:"Clear your conversation history"},{name:"reset",description:"Reset conversation context"},{name:"sub",description:"Subscribe to scheduled reports or alerts"},{name:"unsub",description:"Remove a subscription"},{name:"mysubs",description:"List your active subscriptions"},{name:"alerts",description:"Active NWS weather alerts for mesh area"},{name:"solar",description:"Space weather and HF propagation conditions"},{name:"hf",description:"HF radio propagation (alias for !solar)"},{name:"fire",description:"Active wildfires near the mesh"},{name:"avy",description:"Avalanche advisories for configured zones"},{name:"hotspots",description:"NASA FIRMS satellite fire detections"},{name:"streams",description:"USGS stream gauge readings"},{name:"roads",description:"Road conditions and closures"},{name:"traffic",description:"Traffic flow on monitored corridors"}],TJe=[{value:"US-AL",label:"Alabama"},{value:"US-AK",label:"Alaska"},{value:"US-AZ",label:"Arizona"},{value:"US-AR",label:"Arkansas"},{value:"US-CA",label:"California"},{value:"US-CO",label:"Colorado"},{value:"US-CT",label:"Connecticut"},{value:"US-DE",label:"Delaware"},{value:"US-FL",label:"Florida"},{value:"US-GA",label:"Georgia"},{value:"US-HI",label:"Hawaii"},{value:"US-ID",label:"Idaho"},{value:"US-IL",label:"Illinois"},{value:"US-IN",label:"Indiana"},{value:"US-IA",label:"Iowa"},{value:"US-KS",label:"Kansas"},{value:"US-KY",label:"Kentucky"},{value:"US-LA",label:"Louisiana"},{value:"US-ME",label:"Maine"},{value:"US-MD",label:"Maryland"},{value:"US-MA",label:"Massachusetts"},{value:"US-MI",label:"Michigan"},{value:"US-MN",label:"Minnesota"},{value:"US-MS",label:"Mississippi"},{value:"US-MO",label:"Missouri"},{value:"US-MT",label:"Montana"},{value:"US-NE",label:"Nebraska"},{value:"US-NV",label:"Nevada"},{value:"US-NH",label:"New Hampshire"},{value:"US-NJ",label:"New Jersey"},{value:"US-NM",label:"New Mexico"},{value:"US-NY",label:"New York"},{value:"US-NC",label:"North Carolina"},{value:"US-ND",label:"North Dakota"},{value:"US-OH",label:"Ohio"},{value:"US-OK",label:"Oklahoma"},{value:"US-OR",label:"Oregon"},{value:"US-PA",label:"Pennsylvania"},{value:"US-RI",label:"Rhode Island"},{value:"US-SC",label:"South Carolina"},{value:"US-SD",label:"South Dakota"},{value:"US-TN",label:"Tennessee"},{value:"US-TX",label:"Texas"},{value:"US-UT",label:"Utah"},{value:"US-VT",label:"Vermont"},{value:"US-VA",label:"Virginia"},{value:"US-WA",label:"Washington"},{value:"US-WV",label:"West Virginia"},{value:"US-WI",label:"Wisconsin"},{value:"US-WY",label:"Wyoming"}];function Ho({info:e,link:t,linkText:r="Learn more"}){const[n,i]=U.useState(!1),a=U.useRef(null);return U.useEffect(()=>{if(!n)return;function o(l){a.current&&!a.current.contains(l.target)&&i(!1)}const s=setTimeout(()=>document.addEventListener("mousedown",o),0);return()=>{clearTimeout(s),document.removeEventListener("mousedown",o)}},[n]),_.jsxs("div",{className:"relative inline-block",ref:a,children:[_.jsx("button",{type:"button",onClick:o=>{o.stopPropagation(),i(!n)},className:"ml-1.5 w-4 h-4 rounded-full bg-slate-700 hover:bg-slate-600 text-slate-400 hover:text-slate-200 inline-flex items-center justify-center text-xs transition-colors",title:"More info",children:"?"}),n&&_.jsxs("div",{className:"absolute left-0 top-6 z-50 w-72 p-3 bg-[#1a2332] border border-[#2a3a4a] rounded-lg shadow-xl text-xs text-slate-300 leading-relaxed",children:[_.jsx("button",{type:"button",onClick:()=>i(!1),className:"absolute top-1 right-1 w-5 h-5 rounded hover:bg-slate-700 text-slate-500 hover:text-slate-300 inline-flex items-center justify-center transition-colors","aria-label":"Close",children:_.jsx(au,{size:12})}),_.jsx("div",{className:"pr-4",children:e}),t&&_.jsxs("a",{href:t,target:"_blank",rel:"noopener noreferrer",className:"mt-2 flex items-center gap-1 text-accent hover:underline",onClick:o=>o.stopPropagation(),children:[r," ",_.jsx(wd,{size:10})]})]})]})}function hi({text:e}){return _.jsx("p",{className:"text-sm text-slate-500 mb-6 pb-4 border-b border-[#1e2a3a]",children:e})}function _t({label:e,value:t,onChange:r,type:n="text",placeholder:i="",helper:a="",info:o="",infoLink:s=""}){const[l,u]=U.useState(!1),c=n==="password";return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,o&&_.jsx(Ho,{info:o,link:s})]}),_.jsxs("div",{className:"relative",children:[_.jsx("input",{type:c&&!l?"password":"text",value:t,onChange:f=>r(f.target.value),placeholder:i,className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent placeholder-slate-600"}),c&&_.jsx("button",{type:"button",onClick:()=>u(!l),className:"absolute right-2 top-1/2 -translate-y-1/2 text-slate-500 hover:text-slate-300",children:l?_.jsx(MZ,{size:16}):_.jsx(XE,{size:16})})]}),a&&_.jsx("p",{className:"text-xs text-slate-600",children:a})]})}function rt({label:e,value:t,onChange:r,min:n,max:i,step:a=1,helper:o="",info:s="",infoLink:l=""}){return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,s&&_.jsx(Ho,{info:s,link:l})]}),_.jsx("input",{type:"number",value:t,onChange:u=>r(Number(u.target.value)),min:n,max:i,step:a,className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent"}),o&&_.jsx("p",{className:"text-xs text-slate-600",children:o})]})}function yr({label:e,checked:t,onChange:r,helper:n="",info:i="",infoLink:a=""}){return _.jsxs("div",{className:"flex items-center justify-between py-2",children:[_.jsxs("div",{children:[_.jsxs("span",{className:"flex items-center text-sm text-slate-300",children:[e,i&&_.jsx(Ho,{info:i,link:a})]}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]}),_.jsx("button",{type:"button",onClick:()=>r(!t),className:`relative w-11 h-6 rounded-full transition-colors ${t?"bg-accent":"bg-[#1e2a3a]"}`,children:_.jsx("span",{className:`absolute top-1 left-1 w-4 h-4 rounded-full bg-white transition-transform ${t?"translate-x-5":""}`})})]})}function Po({label:e,value:t,onChange:r,options:n,helper:i="",info:a="",infoLink:o=""}){return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,a&&_.jsx(Ho,{info:a,link:o})]}),_.jsx("select",{value:t,onChange:s=>r(s.target.value),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent",children:n.map(s=>_.jsx("option",{value:s.value,children:s.label},s.value))}),i&&_.jsx("p",{className:"text-xs text-slate-600",children:i})]})}function CJe({label:e,value:t,onChange:r,rows:n=4,helper:i="",info:a="",infoLink:o=""}){return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,a&&_.jsx(Ho,{info:a,link:o})]}),_.jsx("textarea",{value:t,onChange:s=>r(s.target.value),rows:n,className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent resize-y"}),i&&_.jsx("p",{className:"text-xs text-slate-600",children:i})]})}function Yh({label:e,value:t,onChange:r,helper:n="",info:i="",infoLink:a=""}){const[o,s]=U.useState(t.join(", "));U.useEffect(()=>{s(t.join(", "))},[t]);const l=()=>{const u=o.split(",").map(c=>c.trim()).filter(Boolean);r(u)};return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,i&&_.jsx(Ho,{info:i,link:a})]}),_.jsx("input",{type:"text",value:o,onChange:u=>s(u.target.value),onBlur:l,placeholder:"item1, item2, item3",className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent placeholder-slate-600"}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]})}function AJe({label:e,value:t,onChange:r,helper:n="",info:i="",infoLink:a=""}){const[o,s]=U.useState(t.join(", "));U.useEffect(()=>{s(t.join(", "))},[t]);const l=()=>{const u=o.split(",").map(c=>parseInt(c.trim(),10)).filter(c=>!isNaN(c));r(u)};return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,i&&_.jsx(Ho,{info:i,link:a})]}),_.jsx("input",{type:"text",value:o,onChange:u=>s(u.target.value),onBlur:l,placeholder:"0, 1, 2",className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent placeholder-slate-600"}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]})}function Cn({label:e,description:t,checked:r,onChange:n,threshold:i,onThresholdChange:a,thresholdLabel:o,thresholdMin:s,thresholdMax:l,thresholdStep:u=1,thresholdSuffix:c=""}){return _.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg p-3 space-y-2",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("div",{className:"flex-1",children:[_.jsx("span",{className:"text-sm text-slate-300",children:e}),_.jsx("p",{className:"text-xs text-slate-600",children:t})]}),_.jsx("button",{type:"button",onClick:()=>n(!r),className:`relative w-11 h-6 rounded-full transition-colors flex-shrink-0 ml-3 ${r?"bg-accent":"bg-[#1e2a3a]"}`,children:_.jsx("span",{className:`absolute top-1 left-1 w-4 h-4 rounded-full bg-white transition-transform ${r?"translate-x-5":""}`})})]}),r&&i!==void 0&&a&&_.jsxs("div",{className:"flex items-center gap-2 pt-2 border-t border-[#1e2a3a]",children:[_.jsxs("span",{className:"text-xs text-slate-500",children:[o||"Threshold",":"]}),_.jsx("input",{type:"number",value:i,onChange:f=>a(Number(f.target.value)),min:s,max:l,step:u,className:"w-20 px-2 py-1 bg-[#0a0e17] border border-[#1e2a3a] rounded text-xs text-slate-200 font-mono"}),c&&_.jsx("span",{className:"text-xs text-slate-500",children:c})]})]})}function MJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.bot}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Bot Name",value:e.name,onChange:r=>t({...e,name:r}),helper:"Name the bot responds to on the mesh",info:"When someone sends a message containing this name, the bot will respond. Also used as the sender name in broadcasts. Changing this requires a restart."}),_.jsx(_t,{label:"Owner",value:e.owner,onChange:r=>t({...e,owner:r}),helper:"Your callsign or identifier",info:"Identifies the bot operator. Shown in !help responses and used for admin-level commands."})]}),_.jsx(yr,{label:"Respond to DMs",checked:e.respond_to_dms,onChange:r=>t({...e,respond_to_dms:r}),helper:"Reply when someone sends a direct message",info:"When enabled, the bot responds to direct messages from any node. When disabled, the bot only responds to channel messages that mention its name."}),_.jsx(yr,{label:"Filter BBS Protocols",checked:e.filter_bbs_protocols,onChange:r=>t({...e,filter_bbs_protocols:r}),helper:"Ignore BBS bulletin board traffic",info:"Filters out automated BBS protocol messages (advBBS, MAIL*, BOARD*) so the bot doesn't try to respond to machine-to-machine traffic."})]})}function PJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.connection}),_.jsx(Po,{label:"Connection Type",value:e.type,onChange:r=>t({...e,type:r}),options:[{value:"serial",label:"Serial (USB)"},{value:"tcp",label:"TCP (Network)"}],helper:"Serial for USB-connected radios, TCP for network or meshtasticd",info:"Serial: direct USB connection to a Meshtastic radio. TCP: connect over the network to a radio's IP or to meshtasticd running on another machine."}),e.type==="serial"?_.jsx(_t,{label:"Serial Port",value:e.serial_port,onChange:r=>t({...e,serial_port:r}),placeholder:"/dev/ttyUSB0",helper:"Device path for your USB radio",info:"Usually /dev/ttyUSB0 on Linux or /dev/ttyACM0. Check with 'ls /dev/tty*' after plugging in your radio."}):_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"TCP Host",value:e.tcp_host,onChange:r=>t({...e,tcp_host:r}),placeholder:"192.168.1.100",helper:"IP address or hostname of the radio/meshtasticd"}),_.jsx(rt,{label:"TCP Port",value:e.tcp_port,onChange:r=>t({...e,tcp_port:r}),min:1,max:65535,helper:"Default 4403 for meshtasticd"})]})]})}function LJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.response}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Delay Min (sec)",value:e.delay_min,onChange:r=>t({...e,delay_min:r}),min:0,step:.1,helper:"Minimum wait before responding",info:"Adds a random delay between min and max before the bot sends a response. Prevents the bot from appearing to respond instantly, which can feel unnatural on a radio network."}),_.jsx(rt,{label:"Delay Max (sec)",value:e.delay_max,onChange:r=>t({...e,delay_max:r}),min:0,step:.1,helper:"Maximum wait before responding",info:"Also prevents collisions with other traffic by staggering transmissions."})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Max Length",value:e.max_length,onChange:r=>t({...e,max_length:r}),min:50,max:500,helper:"Maximum characters per response message",info:"Meshtastic packets have limited size. This caps how long each message chunk can be. The bot will split longer responses into multiple messages up to Max Messages."}),_.jsx(rt,{label:"Max Messages",value:e.max_messages,onChange:r=>t({...e,max_messages:r}),min:1,max:10,helper:"Maximum chunks per response",info:"If a response is longer than Max Length, the bot splits it into this many chunks at most. Higher values = more complete answers but more airtime used."})]})]})}function kJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.history}),_.jsx(_t,{label:"Database Path",value:e.database,onChange:r=>t({...e,database:r}),helper:"SQLite file for storing conversation history",info:"Path to the SQLite database file. Created automatically if it doesn't exist. Stores all conversation history for context."}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Max Messages Per User",value:e.max_messages_per_user,onChange:r=>t({...e,max_messages_per_user:r}),min:0,helper:"History limit per user (0 = unlimited)",info:"Limits how many messages are stored per user. Older messages are pruned when the limit is reached. Set to 0 for no limit."}),_.jsx(rt,{label:"Conversation Timeout (sec)",value:e.conversation_timeout,onChange:r=>t({...e,conversation_timeout:r}),min:0,helper:"Seconds before context resets",info:"If a user doesn't message for this long, their next message starts a new conversation context. The bot won't remember the previous topic."})]}),_.jsx(yr,{label:"Auto Cleanup",checked:e.auto_cleanup,onChange:r=>t({...e,auto_cleanup:r}),helper:"Automatically prune old conversations"}),e.auto_cleanup&&_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Cleanup Interval (hours)",value:e.cleanup_interval_hours,onChange:r=>t({...e,cleanup_interval_hours:r}),min:1,helper:"Hours between cleanup runs"}),_.jsx(rt,{label:"Max Age (days)",value:e.max_age_days,onChange:r=>t({...e,max_age_days:r}),min:1,helper:"Delete conversations older than this"})]})]})}function IJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.memory}),_.jsx(yr,{label:"Enable Memory",checked:e.enabled,onChange:r=>t({...e,enabled:r}),helper:"Keep conversation context between messages"}),e.enabled&&_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Window Size",value:e.window_size,onChange:r=>t({...e,window_size:r}),min:1,helper:"Recent message pairs kept in full",info:"The bot keeps this many recent exchanges (user message + bot response pairs) as full text in context. Older messages are summarized to save token space."}),_.jsx(rt,{label:"Summarize Threshold",value:e.summarize_threshold,onChange:r=>t({...e,summarize_threshold:r}),min:1,helper:"Messages before older context is summarized",info:"When the conversation exceeds this many messages, older ones outside the window are compressed into a summary by the LLM."})]})]})}function OJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.context}),_.jsx(yr,{label:"Enable Passive Context",checked:e.enabled,onChange:r=>t({...e,enabled:r}),helper:"Listen to channel traffic for context",info:"When enabled, the bot monitors mesh channels and includes recent messages in its context. This lets the bot reference things other people said on the channel."}),e.enabled&&_.jsxs(_.Fragment,{children:[_.jsx(UR,{label:"Observe Channels",value:e.observe_channels,onChange:r=>t({...e,observe_channels:r}),helper:"Channels to monitor (empty = all)",info:"Meshtastic channels to listen on. Leave empty to monitor all channels.",mode:"multi"}),_.jsx(HR,{label:"Ignore Nodes",value:e.ignore_nodes,onChange:r=>t({...e,ignore_nodes:r}),helper:"Nodes to exclude from context",info:"Messages from these nodes won't be included in passive context. Useful for filtering out noisy automated nodes."}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Max Age (sec)",value:e.max_age,onChange:r=>t({...e,max_age:r}),min:0,helper:"Ignore messages older than this"}),_.jsx(rt,{label:"Max Context Items",value:e.max_context_items,onChange:r=>t({...e,max_context_items:r}),min:1,helper:"Maximum recent messages to include"})]})]})]})}function EJe({data:e,onChange:t}){const r=new Set(e.disabled_commands.map(i=>i.toLowerCase())),n=i=>{const a=i.toLowerCase();r.has(a)?t({...e,disabled_commands:e.disabled_commands.filter(o=>o.toLowerCase()!==a)}):t({...e,disabled_commands:[...e.disabled_commands,i]})};return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.commands}),_.jsx(yr,{label:"Enable Commands",checked:e.enabled,onChange:i=>t({...e,enabled:i}),helper:"Allow !commands on the mesh"}),e.enabled&&_.jsxs(_.Fragment,{children:[_.jsx(_t,{label:"Command Prefix",value:e.prefix,onChange:i=>t({...e,prefix:i}),helper:"Character that triggers commands (e.g. ! for !help)",info:"Users type this character followed by the command name. Only single characters recommended."}),_.jsxs("div",{className:"space-y-2",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Available Commands",_.jsx(Ho,{info:"Toggle commands on or off. Disabled commands won't respond when users invoke them."})]}),_.jsx("div",{className:"grid gap-1",children:SJe.map(i=>{const a=!r.has(i.name.toLowerCase());return _.jsxs("div",{className:"flex items-center justify-between p-2 bg-[#0a0e17] border border-[#1e2a3a] rounded hover:border-[#2a3a4a] transition-colors",children:[_.jsxs("div",{className:"flex items-center gap-3",children:[_.jsxs("code",{className:"text-accent text-sm",children:["!",i.name]}),_.jsx("span",{className:"text-xs text-slate-500",children:i.description})]}),_.jsx("button",{type:"button",onClick:()=>n(i.name),className:`relative w-9 h-5 rounded-full transition-colors ${a?"bg-accent":"bg-[#1e2a3a]"}`,children:_.jsx("span",{className:`absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-transform ${a?"translate-x-4":""}`})})]},i.name)})})]})]})]})}function DJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.llm}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(Po,{label:"Backend",value:e.backend,onChange:r=>t({...e,backend:r}),options:[{value:"openai",label:"OpenAI"},{value:"anthropic",label:"Anthropic"},{value:"google",label:"Google (Gemini)"}],helper:"LLM provider to use",info:"OpenAI: GPT models (gpt-4o, gpt-4o-mini). Anthropic: Claude models (claude-sonnet-4-20250514). Google: Gemini models. Can also point to compatible APIs like Ollama, LM Studio, or Open WebUI by changing the Base URL."}),_.jsx(_t,{label:"Model",value:e.model,onChange:r=>t({...e,model:r}),placeholder:"gpt-4o-mini",helper:"Specific model name",info:"The specific model to use. Common choices: gpt-4o-mini (fast, cheap), gpt-4o (better, costs more), claude-sonnet-4-20250514 (Anthropic equivalent). For local models via Ollama, use the model name you pulled (e.g. llama3.1)."})]}),_.jsx(_t,{label:"API Key",value:e.api_key,onChange:r=>t({...e,api_key:r}),type:"password",helper:"Supports ${ENV_VAR} syntax",info:"Your API key from the provider. You can also use ${ENV_VAR} syntax to read from an environment variable instead of storing the key in the config file."}),_.jsx(_t,{label:"Base URL",value:e.base_url,onChange:r=>t({...e,base_url:r}),placeholder:"https://api.openai.com/v1",helper:"API endpoint (change for local LLMs)",info:"Default API endpoint for the selected backend. Change this to point to a local LLM server (Ollama at http://localhost:11434/v1, Open WebUI, LM Studio, etc.) or a proxy."}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Timeout (sec)",value:e.timeout,onChange:r=>t({...e,timeout:r}),min:5,max:120,helper:"Maximum seconds to wait for response"}),_.jsx(rt,{label:"Max Response Tokens",value:e.max_response_tokens,onChange:r=>t({...e,max_response_tokens:r}),min:100,helper:"Token limit for LLM responses"})]}),_.jsx(yr,{label:"Use System Prompt",checked:e.use_system_prompt,onChange:r=>t({...e,use_system_prompt:r}),helper:"Enable custom system instructions"}),e.use_system_prompt&&_.jsx(CJe,{label:"System Prompt",value:e.system_prompt,onChange:r=>t({...e,system_prompt:r}),rows:6,helper:"Instructions that shape the bot's personality",info:"Instructions that shape the bot's personality and behavior. The bot always follows these instructions. MeshAI adds mesh health data and environmental context automatically — you don't need to include those here."}),_.jsx(yr,{label:"Web Search",checked:e.web_search,onChange:r=>t({...e,web_search:r}),helper:"Enable web search tool (Open WebUI feature)"}),_.jsx(yr,{label:"Google Grounding",checked:e.google_grounding,onChange:r=>t({...e,google_grounding:r}),helper:"Ground responses in web search (Gemini only)"})]})}function NJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.weather}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(Po,{label:"Primary Provider",value:e.primary,onChange:r=>t({...e,primary:r}),options:[{value:"openmeteo",label:"Open-Meteo"},{value:"wttr",label:"wttr.in"},{value:"llm",label:"LLM"}],helper:"Main weather data source"}),_.jsx(Po,{label:"Fallback Provider",value:e.fallback,onChange:r=>t({...e,fallback:r}),options:[{value:"openmeteo",label:"Open-Meteo"},{value:"wttr",label:"wttr.in"},{value:"llm",label:"LLM"},{value:"none",label:"None"}],helper:"Backup if primary fails"})]}),_.jsx(_t,{label:"Default Location",value:e.default_location,onChange:r=>t({...e,default_location:r}),placeholder:"Your city, state",helper:"Location when none specified"})]})}function jJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.meshmonitor}),_.jsx(yr,{label:"Enable MeshMonitor",checked:e.enabled,onChange:r=>t({...e,enabled:r}),helper:"Connect to AIDA MeshMonitor instance",info:"MeshMonitor by Yeraze provides node data, battery info, telemetry, and auto-responder patterns. MeshAI uses this as a data source and avoids duplicate responses."}),e.enabled&&_.jsxs(_.Fragment,{children:[_.jsx(_t,{label:"URL",value:e.url,onChange:r=>t({...e,url:r}),placeholder:"http://192.168.1.100:8080",helper:"MeshMonitor API endpoint",info:"Full URL to your MeshMonitor instance. Usually runs on port 8080."}),_.jsx(yr,{label:"Inject Into Prompt",checked:e.inject_into_prompt,onChange:r=>t({...e,inject_into_prompt:r}),helper:"Tell LLM about MeshMonitor commands",info:"Adds MeshMonitor's auto-responder patterns to the LLM context so it knows what commands MeshMonitor handles."}),_.jsx(rt,{label:"Refresh Interval (sec)",value:e.refresh_interval,onChange:r=>t({...e,refresh_interval:r}),min:10,helper:"How often to fetch patterns"}),_.jsx(yr,{label:"Polite Mode",checked:e.polite_mode,onChange:r=>t({...e,polite_mode:r}),helper:"Reduce polling frequency",info:"Reduces polling frequency for shared instances to be a good neighbor."})]})]})}function RJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.knowledge}),_.jsx(yr,{label:"Enable Knowledge Base",checked:e.enabled,onChange:r=>t({...e,enabled:r}),helper:"Answer questions from stored documents",info:"Uses RAG (Retrieval-Augmented Generation) to answer questions from a knowledge base. Supports Qdrant vector database or local SQLite with FTS5."}),e.enabled&&_.jsxs(_.Fragment,{children:[_.jsx(Po,{label:"Backend",value:e.backend,onChange:r=>t({...e,backend:r}),options:[{value:"auto",label:"Auto (Qdrant -> SQLite)"},{value:"qdrant",label:"Qdrant"},{value:"sqlite",label:"SQLite"}],helper:"Knowledge storage backend",info:"Auto tries Qdrant first, falls back to SQLite. Qdrant provides hybrid search with dense+sparse embeddings. SQLite uses FTS5 keyword search."}),(e.backend==="qdrant"||e.backend==="auto")&&_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Qdrant Host",value:e.qdrant_host,onChange:r=>t({...e,qdrant_host:r}),helper:"Qdrant server hostname",info:"IP or hostname of your Qdrant vector database server."}),_.jsx(rt,{label:"Qdrant Port",value:e.qdrant_port,onChange:r=>t({...e,qdrant_port:r}),helper:"Default 6333"})]}),_.jsx(_t,{label:"Collection",value:e.qdrant_collection,onChange:r=>t({...e,qdrant_collection:r}),helper:"Qdrant collection name"}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"TEI Host",value:e.tei_host,onChange:r=>t({...e,tei_host:r}),helper:"Text Embeddings Inference host",info:"TEI service for generating dense embeddings. Uses BAAI/bge-m3 model."}),_.jsx(rt,{label:"TEI Port",value:e.tei_port,onChange:r=>t({...e,tei_port:r}),helper:"Default 8090"})]}),_.jsx(yr,{label:"Use Sparse Embeddings",checked:e.use_sparse,onChange:r=>t({...e,use_sparse:r}),helper:"Enable hybrid search with sparse vectors",info:"Combines dense embeddings with sparse (keyword-based) embeddings using Reciprocal Rank Fusion for better search results."})]}),_.jsx(_t,{label:"SQLite DB Path",value:e.db_path,onChange:r=>t({...e,db_path:r}),helper:"Local knowledge database file"}),_.jsx(rt,{label:"Top K Results",value:e.top_k,onChange:r=>t({...e,top_k:r}),min:1,max:20,helper:"Number of documents to retrieve"})]})]})}function BJe({source:e,onChange:t,onDelete:r}){const[n,i]=U.useState(!1),a={meshview:"Web-based mesh monitoring tool. Enter the full URL of a MeshView instance. No API key typically required.",meshmonitor:"AIDA MeshMonitor API. Provides node data and network statistics. Requires API token.",mqtt:"Subscribe directly to a Meshtastic MQTT broker for real-time packet data. This is push-based (instant) vs the polling approach of MeshView/MeshMonitor."};return _.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg overflow-hidden",children:[_.jsxs("div",{className:"flex items-center justify-between p-3 bg-[#0a0e17] cursor-pointer",onClick:()=>i(!n),children:[_.jsxs("div",{className:"flex items-center gap-3",children:[n?_.jsx(mv,{size:16}):_.jsx(_d,{size:16}),_.jsx("div",{className:`w-2 h-2 rounded-full ${e.enabled?"bg-green-500":"bg-slate-500"}`}),_.jsx("span",{className:"font-mono text-sm text-slate-200",children:e.name||"Unnamed Source"}),_.jsx("span",{className:"text-xs text-slate-500 bg-[#1e2a3a] px-2 py-0.5 rounded",children:e.type})]}),_.jsx("button",{onClick:o=>{o.stopPropagation(),r()},className:"p-1 text-red-400 hover:text-red-300 hover:bg-red-500/10 rounded",children:_.jsx(tD,{size:14})})]}),n&&_.jsxs("div",{className:"p-4 space-y-4 border-t border-[#1e2a3a]",children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Name",value:e.name,onChange:o=>t({...e,name:o}),helper:"Friendly name for this source"}),_.jsx(Po,{label:"Type",value:e.type,onChange:o=>t({...e,type:o}),options:[{value:"meshview",label:"MeshView"},{value:"meshmonitor",label:"MeshMonitor"},{value:"mqtt",label:"MQTT Broker"}],info:a[e.type]||""})]}),e.type!=="mqtt"&&_.jsx(_t,{label:"URL",value:e.url,onChange:o=>t({...e,url:o}),helper:"Full URL including protocol"}),e.type==="meshmonitor"&&_.jsx(_t,{label:"API Token",value:e.api_token,onChange:o=>t({...e,api_token:o}),type:"password",helper:"Bearer token for authentication"}),e.type==="mqtt"&&_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Host",value:e.host||"",onChange:o=>t({...e,host:o}),helper:"MQTT broker hostname"}),_.jsx(rt,{label:"Port",value:e.port||1883,onChange:o=>t({...e,port:o}),min:1,max:65535,helper:"1883 plain, 8883 TLS"})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Username",value:e.username||"",onChange:o=>t({...e,username:o})}),_.jsx(_t,{label:"Password",value:e.password||"",onChange:o=>t({...e,password:o}),type:"password"})]}),_.jsx(_t,{label:"Topic Root",value:e.topic_root||"msh/US",onChange:o=>t({...e,topic_root:o}),helper:"Base topic to subscribe to"}),_.jsx(yr,{label:"Use TLS",checked:e.use_tls||!1,onChange:o=>t({...e,use_tls:o}),helper:"Encrypt MQTT connection"})]}),_.jsx(rt,{label:"Refresh Interval (sec)",value:e.refresh_interval,onChange:o=>t({...e,refresh_interval:o}),min:10,helper:"Polling frequency"}),_.jsx(yr,{label:"Enabled",checked:e.enabled,onChange:o=>t({...e,enabled:o})}),_.jsx(yr,{label:"Polite Mode",checked:e.polite_mode,onChange:o=>t({...e,polite_mode:o}),helper:"Reduce polling for shared instances"})]})]})}function zJe({data:e,onChange:t}){const r=()=>{t([...e,{name:"New Source",type:"meshview",url:"",api_token:"",refresh_interval:30,polite_mode:!1,enabled:!0,host:"",port:1883,username:"",password:"",topic_root:"msh/US",use_tls:!1}])};return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.mesh_sources}),e.map((n,i)=>_.jsx(BJe,{source:n,onChange:a=>{const o=[...e];o[i]=a,t(o)},onDelete:()=>{confirm(`Delete source "${n.name}"?`)&&t(e.filter((a,o)=>o!==i))}},i)),_.jsxs("button",{onClick:r,className:"w-full py-2 border border-dashed border-[#1e2a3a] rounded-lg text-slate-500 hover:text-slate-300 hover:border-accent flex items-center justify-center gap-2 transition-colors",children:[_.jsx(MS,{size:16})," Add Source"]})]})}function $Je({data:e,onChange:t}){const[r,n]=U.useState(null);return _.jsxs("div",{className:"space-y-6",children:[_.jsx(hi,{text:fi.mesh_intelligence}),_.jsx(yr,{label:"Enable Mesh Intelligence",checked:e.enabled,onChange:i=>t({...e,enabled:i}),helper:"Activate health scoring and alerting"}),e.enabled&&_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Locality Radius (miles)",value:e.locality_radius_miles,onChange:i=>t({...e,locality_radius_miles:i}),min:1,step:.5,helper:"Region assignment radius",info:"Nodes within this distance of a region anchor point are assigned to that region."}),_.jsx(rt,{label:"Offline Threshold (hours)",value:e.offline_threshold_hours,onChange:i=>t({...e,offline_threshold_hours:i}),min:1,helper:"Time until node marked offline",info:"A node is considered offline after not being heard for this many hours."})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Packet Threshold",value:e.packet_threshold,onChange:i=>t({...e,packet_threshold:i}),min:0,helper:"Min packets per 24h to flag",info:"Minimum packets per 24 hours. Nodes below this are flagged as low activity."}),_.jsx(rt,{label:"Battery Warning %",value:e.battery_warning_percent,onChange:i=>t({...e,battery_warning_percent:i}),min:1,max:100,helper:"Global battery warning level"})]}),_.jsx(HR,{label:"Critical Nodes",value:e.critical_nodes,onChange:i=>t({...e,critical_nodes:i}),helper:"Critical infrastructure nodes",info:"Nodes that get priority alerting when they go offline.",roleFilter:"infrastructure"}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(UR,{label:"Alert Channel",value:e.alert_channel,onChange:i=>t({...e,alert_channel:i}),helper:"Channel for broadcast alerts",info:"Meshtastic channel for broadcast alerts. Select Disabled to turn off channel broadcasting.",mode:"single",includeDisabled:!0}),_.jsx(rt,{label:"Alert Cooldown (min)",value:e.alert_cooldown_minutes,onChange:i=>t({...e,alert_cooldown_minutes:i}),min:1,helper:"Min time between repeat alerts",info:"Minimum minutes between repeated alerts for the same condition. Uses scaling cooldown (12h, 24h, 48h)."})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Regions",_.jsx(Ho,{info:"Regions group mesh nodes by geographic area. Each region has an anchor point (lat/lon) and nodes within the region radius are automatically assigned. Regions enable localized reports, alerts, and health scoring."})]}),e.regions.map((i,a)=>_.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg overflow-hidden",children:[_.jsxs("div",{className:"flex items-center justify-between p-3 bg-[#0a0e17] cursor-pointer",onClick:()=>n(r===a?null:a),children:[_.jsxs("div",{className:"flex items-center gap-3",children:[r===a?_.jsx(mv,{size:16}):_.jsx(_d,{size:16}),_.jsx("span",{className:"font-medium text-slate-200",children:i.name||"Unnamed Region"}),_.jsx("span",{className:"text-xs text-slate-500",children:i.local_name})]}),_.jsx("button",{onClick:o=>{if(o.stopPropagation(),confirm(`Delete region "${i.name||"Unnamed Region"}"?`)){const s=e.regions.filter((l,u)=>u!==a);t({...e,regions:s})}},className:"p-1 text-red-400 hover:text-red-300 hover:bg-red-500/10 rounded",children:_.jsx(tD,{size:14})})]}),r===a&&_.jsxs("div",{className:"p-4 space-y-3 border-t border-[#1e2a3a]",children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Name",value:i.name,onChange:o=>{const s=[...e.regions];s[a]={...i,name:o},t({...e,regions:s})}}),_.jsx(_t,{label:"Local Name",value:i.local_name,onChange:o=>{const s=[...e.regions];s[a]={...i,local_name:o},t({...e,regions:s})}})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Latitude",value:i.lat,onChange:o=>{const s=[...e.regions];s[a]={...i,lat:o},t({...e,regions:s})},step:1e-4}),_.jsx(rt,{label:"Longitude",value:i.lon,onChange:o=>{const s=[...e.regions];s[a]={...i,lon:o},t({...e,regions:s})},step:1e-4})]}),_.jsx(_t,{label:"Description",value:i.description,onChange:o=>{const s=[...e.regions];s[a]={...i,description:o},t({...e,regions:s})}}),_.jsx(Yh,{label:"Aliases",value:i.aliases,onChange:o=>{const s=[...e.regions];s[a]={...i,aliases:o},t({...e,regions:s})}}),_.jsx(Yh,{label:"Cities",value:i.cities,onChange:o=>{const s=[...e.regions];s[a]={...i,cities:o},t({...e,regions:s})}})]})]},a)),_.jsxs("button",{onClick:()=>{const i={name:"",local_name:"",lat:0,lon:0,description:"",aliases:[],cities:[]};t({...e,regions:[...e.regions,i]}),n(e.regions.length)},className:"w-full py-2 border border-dashed border-[#1e2a3a] rounded-lg text-slate-500 hover:text-slate-300 hover:border-accent flex items-center justify-center gap-2 transition-colors",children:[_.jsx(MS,{size:16})," Add Region"]})]}),_.jsxs("div",{className:"space-y-3",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Alert Rules",_.jsx(Ho,{info:"Configure which conditions trigger alerts. Each rule can have an optional threshold value."})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("h4",{className:"text-xs text-slate-400 font-medium",children:"Infrastructure"}),_.jsx(Cn,{label:"Infra Offline",description:"Alert when an infrastructure node (router/repeater) goes offline",checked:e.alert_rules.infra_offline,onChange:i=>t({...e,alert_rules:{...e.alert_rules,infra_offline:i}})}),_.jsx(Cn,{label:"Infra Recovery",description:"Alert when an offline infrastructure node comes back online",checked:e.alert_rules.infra_recovery,onChange:i=>t({...e,alert_rules:{...e.alert_rules,infra_recovery:i}})}),_.jsx(Cn,{label:"New Router",description:"Alert when a new router/repeater appears on the mesh",checked:e.alert_rules.new_router,onChange:i=>t({...e,alert_rules:{...e.alert_rules,new_router:i}})}),_.jsx(Cn,{label:"Feeder Offline",description:"Alert when a data source (MeshView/MeshMonitor) stops responding",checked:e.alert_rules.feeder_offline,onChange:i=>t({...e,alert_rules:{...e.alert_rules,feeder_offline:i}})}),_.jsx(Cn,{label:"Single Gateway",description:"Alert when an infrastructure node has only one connection path",checked:e.alert_rules.infra_single_gateway,onChange:i=>t({...e,alert_rules:{...e.alert_rules,infra_single_gateway:i}})}),_.jsx(Cn,{label:"Region Blackout",description:"Alert when all infrastructure in a region goes offline",checked:e.alert_rules.region_total_blackout,onChange:i=>t({...e,alert_rules:{...e.alert_rules,region_total_blackout:i}})})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("h4",{className:"text-xs text-slate-400 font-medium",children:"Power"}),_.jsx(Cn,{label:"Battery Warning",description:"Alert when infra node battery drops below warning threshold",checked:e.alert_rules.battery_warning,onChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_warning:i}}),threshold:e.alert_rules.battery_warning_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_warning_threshold:i}}),thresholdLabel:"Below",thresholdMin:10,thresholdMax:90,thresholdSuffix:"%"}),_.jsx(Cn,{label:"Battery Critical",description:"Alert at critical battery level",checked:e.alert_rules.battery_critical,onChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_critical:i}}),threshold:e.alert_rules.battery_critical_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_critical_threshold:i}}),thresholdLabel:"Below",thresholdMin:5,thresholdMax:50,thresholdSuffix:"%"}),_.jsx(Cn,{label:"Battery Emergency",description:"Alert at emergency battery level",checked:e.alert_rules.battery_emergency,onChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_emergency:i}}),threshold:e.alert_rules.battery_emergency_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_emergency_threshold:i}}),thresholdLabel:"Below",thresholdMin:1,thresholdMax:25,thresholdSuffix:"%"}),_.jsx(Cn,{label:"Battery Trend Declining",description:"Alert when battery shows a declining trend over 7 days",checked:e.alert_rules.battery_trend_declining,onChange:i=>t({...e,alert_rules:{...e.alert_rules,battery_trend_declining:i}})}),_.jsx(Cn,{label:"Power Source Change",description:"Alert when a node switches between battery and USB power",checked:e.alert_rules.power_source_change,onChange:i=>t({...e,alert_rules:{...e.alert_rules,power_source_change:i}})}),_.jsx(Cn,{label:"Solar Not Charging",description:"Alert when a solar-powered node isn't charging during daylight",checked:e.alert_rules.solar_not_charging,onChange:i=>t({...e,alert_rules:{...e.alert_rules,solar_not_charging:i}})})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("h4",{className:"text-xs text-slate-400 font-medium",children:"Utilization"}),_.jsx(Cn,{label:"High Utilization",description:"Alert when channel utilization stays high for extended periods",checked:e.alert_rules.sustained_high_util,onChange:i=>t({...e,alert_rules:{...e.alert_rules,sustained_high_util:i}}),threshold:e.alert_rules.high_util_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,high_util_threshold:i}}),thresholdLabel:"Above",thresholdMin:5,thresholdMax:50,thresholdSuffix:`% for ${e.alert_rules.high_util_hours}h`}),_.jsx(Cn,{label:"Packet Flood",description:"Alert when a single node sends excessive packets",checked:e.alert_rules.packet_flood,onChange:i=>t({...e,alert_rules:{...e.alert_rules,packet_flood:i}}),threshold:e.alert_rules.packet_flood_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,packet_flood_threshold:i}}),thresholdLabel:"Over",thresholdMin:100,thresholdMax:2e3,thresholdSuffix:"pkts/24h"})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("h4",{className:"text-xs text-slate-400 font-medium",children:"Health Scores"}),_.jsx(Cn,{label:"Mesh Score Alert",description:"Alert when overall mesh health score drops below threshold",checked:e.alert_rules.mesh_score_alert,onChange:i=>t({...e,alert_rules:{...e.alert_rules,mesh_score_alert:i}}),threshold:e.alert_rules.mesh_score_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,mesh_score_threshold:i}}),thresholdLabel:"Below",thresholdMin:30,thresholdMax:90,thresholdSuffix:"/100"}),_.jsx(Cn,{label:"Region Score Alert",description:"Alert when a region's health score drops below threshold",checked:e.alert_rules.region_score_alert,onChange:i=>t({...e,alert_rules:{...e.alert_rules,region_score_alert:i}}),threshold:e.alert_rules.region_score_threshold,onThresholdChange:i=>t({...e,alert_rules:{...e.alert_rules,region_score_threshold:i}}),thresholdLabel:"Below",thresholdMin:30,thresholdMax:90,thresholdSuffix:"/100"})]})]})]})]})}function FJe({data:e,onChange:t}){return _.jsxs("div",{className:"space-y-4",children:[_.jsx(hi,{text:fi.dashboard}),_.jsx(yr,{label:"Enable Dashboard",checked:e.enabled,onChange:r=>t({...e,enabled:r}),helper:"Run the web dashboard"}),e.enabled&&_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(_t,{label:"Host",value:e.host,onChange:r=>t({...e,host:r}),placeholder:"0.0.0.0",helper:"Network bind address",info:"0.0.0.0 = accessible from any device on the network. 127.0.0.1 = only accessible from this machine."}),_.jsx(rt,{label:"Port",value:e.port,onChange:r=>t({...e,port:r}),min:1,max:65535,helper:"Dashboard URL port",info:"Port number for the web dashboard URL. You access the dashboard at http://your-ip:port"})]})]})}function VJe(){var I;const[e,t]=U.useState(null),[r,n]=U.useState(null),[i,a]=U.useState("bot"),[o,s]=U.useState(!0),[l,u]=U.useState(!1),[c,f]=U.useState(null),[h,d]=U.useState(null),[v,g]=U.useState(!1),[m,y]=U.useState(!1),x=U.useCallback(async()=>{try{const k=await fetch("/api/config");if(!k.ok)throw new Error("Failed to fetch config");const E=await k.json();t(E),n(JSON.parse(JSON.stringify(E))),y(!1),f(null)}catch(k){f(k instanceof Error?k.message:"Unknown error")}finally{s(!1)}},[]);U.useEffect(()=>{document.title="Config — MeshAI",x()},[x]),U.useEffect(()=>{e&&r&&y(JSON.stringify(e)!==JSON.stringify(r))},[e,r]);const b=async()=>{if(e){u(!0),f(null),d(null);try{const k=e[i],E=await fetch(`/api/config/${i}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(k)}),D=await E.json();if(!E.ok)throw new Error(D.detail||"Save failed");d(`${i} saved successfully`),n(JSON.parse(JSON.stringify(e))),y(!1),D.restart_required&&g(!0),setTimeout(()=>d(null),3e3)}catch(k){f(k instanceof Error?k.message:"Save failed")}finally{u(!1)}}},S=()=>{r&&(t(JSON.parse(JSON.stringify(r))),y(!1))},T=async()=>{try{await fetch("/api/restart",{method:"POST"}),g(!1),d("Restart initiated")}catch{f("Restart failed")}},C=(k,E)=>{e&&t({...e,[k]:E})};if(o)return _.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsx("div",{className:"text-slate-400",children:"Loading configuration..."})});if(!e)return _.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsx("div",{className:"text-red-400",children:"Failed to load configuration"})});const M=()=>{switch(i){case"bot":return _.jsx(MJe,{data:e.bot,onChange:k=>C("bot",k)});case"connection":return _.jsx(PJe,{data:e.connection,onChange:k=>C("connection",k)});case"response":return _.jsx(LJe,{data:e.response,onChange:k=>C("response",k)});case"history":return _.jsx(kJe,{data:e.history,onChange:k=>C("history",k)});case"memory":return _.jsx(IJe,{data:e.memory,onChange:k=>C("memory",k)});case"context":return _.jsx(OJe,{data:e.context,onChange:k=>C("context",k)});case"commands":return _.jsx(EJe,{data:e.commands,onChange:k=>C("commands",k)});case"llm":return _.jsx(DJe,{data:e.llm,onChange:k=>C("llm",k)});case"weather":return _.jsx(NJe,{data:e.weather,onChange:k=>C("weather",k)});case"meshmonitor":return _.jsx(jJe,{data:e.meshmonitor,onChange:k=>C("meshmonitor",k)});case"knowledge":return _.jsx(RJe,{data:e.knowledge,onChange:k=>C("knowledge",k)});case"mesh_sources":return _.jsx(zJe,{data:e.mesh_sources,onChange:k=>C("mesh_sources",k)});case"mesh_intelligence":return _.jsx($Je,{data:e.mesh_intelligence,onChange:k=>C("mesh_intelligence",k)});case"dashboard":return _.jsx(FJe,{data:e.dashboard,onChange:k=>C("dashboard",k)});default:return null}},P=((I=UU.find(k=>k.key===i))==null?void 0:I.label)||i;return _.jsxs("div",{className:"flex gap-6 h-[calc(100vh-8rem)]",children:[_.jsx("div",{className:"w-48 flex-shrink-0 space-y-1",children:UU.map(({key:k,label:E,icon:D})=>_.jsxs("button",{onClick:()=>a(k),className:`w-full flex items-center gap-2 px-3 py-2 rounded text-sm transition-colors ${i===k?"bg-accent text-white":"text-slate-400 hover:text-slate-200 hover:bg-bg-hover"}`,children:[_.jsx(D,{size:16}),_.jsx("span",{children:E}),m&&i===k&&_.jsx("span",{className:"ml-auto w-2 h-2 bg-amber-500 rounded-full"})]},k))}),_.jsxs("div",{className:"flex-1 flex flex-col min-w-0",children:[_.jsxs("div",{className:"flex items-center justify-between mb-6",children:[_.jsxs("div",{className:"flex items-center gap-3",children:[_.jsx(IZ,{size:20,className:"text-slate-500"}),_.jsx("h2",{className:"text-lg font-semibold text-slate-200",children:P})]}),_.jsxs("div",{className:"flex items-center gap-2",children:[m&&_.jsxs("button",{onClick:S,className:"flex items-center gap-1.5 px-3 py-1.5 text-sm text-slate-400 hover:text-slate-200 bg-bg-hover rounded transition-colors",children:[_.jsx(QE,{size:14}),"Discard"]}),_.jsxs("button",{onClick:b,disabled:l||!m,className:"flex items-center gap-1.5 px-4 py-1.5 text-sm bg-accent text-white rounded hover:bg-accent/80 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",children:[l?_.jsx(Am,{size:14,className:"animate-spin"}):_.jsx(JE,{size:14}),"Save"]})]})]}),v&&_.jsxs("div",{className:"flex items-center justify-between p-3 mb-4 bg-amber-500/10 border border-amber-500/30 rounded-lg",children:[_.jsxs("div",{className:"flex items-center gap-2 text-amber-400",children:[_.jsx(iu,{size:16}),_.jsx("span",{className:"text-sm",children:"Restart required for changes to take effect"})]}),_.jsx("button",{onClick:T,className:"px-3 py-1 text-sm bg-amber-500 text-white rounded hover:bg-amber-600 transition-colors",children:"Restart Now"})]}),c&&_.jsxs("div",{className:"flex items-center gap-2 p-3 mb-4 bg-red-500/10 border border-red-500/30 rounded-lg text-red-400",children:[_.jsx(au,{size:16}),_.jsx("span",{className:"text-sm",children:c})]}),h&&_.jsxs("div",{className:"flex items-center gap-2 p-3 mb-4 bg-green-500/10 border border-green-500/30 rounded-lg text-green-400",children:[_.jsx(Qc,{size:16}),_.jsx("span",{className:"text-sm",children:h})]}),_.jsx("div",{className:"flex-1 overflow-y-auto pr-2",children:_.jsx("div",{className:"bg-bg-card border border-border rounded-lg p-6",children:M()})})]})]})}function GJe({feed:e}){const t=e.is_loaded?e.consecutive_errors>0?"bg-amber-500":"bg-green-500":"bg-red-500",r=e.is_loaded?e.consecutive_errors>0?`${e.consecutive_errors} errors`:"Healthy":"Not loaded",n=e.last_fetch?new Date(e.last_fetch*1e3).toLocaleTimeString():"Never";return _.jsxs("div",{className:"bg-bg-hover rounded-lg p-4",children:[_.jsxs("div",{className:"flex items-center justify-between mb-2",children:[_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx("div",{className:`w-2 h-2 rounded-full ${t}`}),_.jsx("span",{className:"text-sm font-medium text-slate-200 uppercase",children:e.source})]}),_.jsx("span",{className:"text-xs text-slate-400",children:r})]}),_.jsxs("div",{className:"text-xs text-slate-500 space-y-1",children:[_.jsxs("div",{children:["Events: ",e.event_count]}),_.jsxs("div",{children:["Last fetch: ",n]}),e.last_error&&_.jsx("div",{className:"text-amber-500 truncate",children:e.last_error})]})]})}function WJe({event:e}){const t=e.severity.toLowerCase(),r=t==="extreme"||t==="severe"||t==="immediate"?{bg:"bg-red-500/10",border:"border-red-500",Icon:xd,color:"text-red-500"}:t==="moderate"||t==="warning"||t==="priority"?{bg:"bg-amber-500/10",border:"border-amber-500",Icon:iu,color:"text-amber-500"}:{bg:"bg-blue-500/10",border:"border-blue-500",Icon:CS,color:"text-blue-500"},n=r.Icon;return _.jsx("div",{className:`p-3 rounded-lg ${r.bg} border-l-2 ${r.border}`,children:_.jsxs("div",{className:"flex items-start gap-3",children:[_.jsx(n,{size:16,className:r.color}),_.jsxs("div",{className:"flex-1 min-w-0",children:[_.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[_.jsx("span",{className:"text-sm font-medium text-slate-200",children:e.event_type}),_.jsx("span",{className:`text-xs px-1.5 py-0.5 rounded ${r.bg} ${r.color}`,children:e.severity})]}),_.jsx("div",{className:"text-sm text-slate-300",children:e.headline})]})]})})}function Zie({value:e,onChange:t,disabled:r,centralDisabled:n}){const i="px-2 py-1 text-xs transition-colors";return _.jsxs("div",{className:`flex rounded border border-[#1e2a3a] overflow-hidden ${r?"opacity-40":""}`,children:[_.jsx("button",{type:"button",disabled:r,onClick:()=>t("native"),className:`${i} ${e==="native"?"bg-accent text-white":"text-slate-400 hover:text-slate-200"}`,children:"native"}),_.jsx("button",{type:"button",disabled:r||n,title:n?"Central not available for this adapter":"",onClick:()=>{n||t("central")},className:`${i} ${n?"text-slate-600 cursor-not-allowed":e==="central"?"bg-accent text-white":"text-slate-400 hover:text-slate-200"}`,children:"central"})]})}function HJe({title:e,subtitle:t,enabled:r,onEnabled:n,feedSource:i,onFeedSource:a,hasCentral:o,nativeOnly:s,hasKey:l,health:u,events:c,children:f}){const h=s||!o;return _.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg p-4 space-y-3",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("div",{children:[_.jsx("span",{className:"text-sm font-medium text-slate-300",children:e}),t&&_.jsx("p",{className:"text-xs text-slate-600",children:t})]}),_.jsxs("div",{className:"flex items-center gap-4",children:[_.jsxs("div",{className:"flex items-center gap-1",children:[_.jsx("span",{className:"text-[10px] uppercase tracking-wide text-slate-600",children:"source"}),_.jsx(Zie,{value:i,onChange:a,disabled:!r,centralDisabled:h})]}),_.jsx(yr,{label:"",checked:r,onChange:n})]})]}),!l&&_.jsx("div",{className:"text-xs text-amber-400 bg-amber-500/10 rounded p-2",children:"API key not configured — contact admin"}),s&&_.jsx("div",{className:"text-[11px] text-slate-600",children:"Central not available for this adapter — native only"}),_.jsx("div",{className:r?"space-y-3":"space-y-3 opacity-40 pointer-events-none select-none",children:f}),(u||c&&c.length>0)&&_.jsxs("div",{className:"pt-2 border-t border-[#1e2a3a] space-y-3",children:[_.jsx("div",{className:"text-[10px] uppercase tracking-wide text-slate-600",children:"Live status"}),u?_.jsx(GJe,{feed:u}):_.jsx("div",{className:"text-xs text-slate-600",children:"No status reported."}),c&&c.length>0&&_.jsx("div",{className:"space-y-2",children:c.slice(0,5).map((d,v)=>_.jsx(WJe,{event:d},v))})]})]})}const cl={nws:{label:"NWS Weather Alerts",subtitle:"National Weather Service alerts",health:"nws",hasCentral:!0,nativeOnly:!1,hasKey:!0},fires:{label:"NIFC Fire Perimeters",subtitle:"Active wildfires (National Interagency Fire Center)",health:"nifc",hasCentral:!0,nativeOnly:!1,hasKey:!0},firms:{label:"NASA FIRMS Hotspots",subtitle:"Satellite thermal-anomaly detections",health:"firms",hasCentral:!0,nativeOnly:!1,hasKey:!1},swpc:{label:"NOAA Space Weather (SWPC)",subtitle:"Solar indices, geomagnetic storms",health:"swpc",hasCentral:!0,nativeOnly:!1,hasKey:!0},ducting:{label:"Tropospheric Ducting",subtitle:"VHF/UHF extended-range conditions",health:"ducting",hasCentral:!1,nativeOnly:!0,hasKey:!0},traffic:{label:"TomTom Traffic",subtitle:"Traffic flow on monitored corridors",health:"traffic",hasCentral:!0,nativeOnly:!1,hasKey:!0},roads511:{label:"511 Road Conditions",subtitle:"State DOT road events and closures",health:"roads511",hasCentral:!0,nativeOnly:!1,hasKey:!1},usgs_quake:{label:"USGS Earthquakes",subtitle:"Seismic events from the USGS feed",health:"usgs_quake",hasCentral:!0,nativeOnly:!1,hasKey:!0},usgs:{label:"USGS Stream Gauges",subtitle:"River and stream water levels",health:"usgs",hasCentral:!0,nativeOnly:!1,hasKey:!0},avalanche:{label:"Avalanche Advisories",subtitle:"Backcountry avalanche danger ratings",health:"avalanche",hasCentral:!1,nativeOnly:!0,hasKey:!0}},dP=[{key:"weather",label:"Weather",icon:nu,adapters:["nws"]},{key:"fire",label:"Fire",icon:TS,adapters:["fires","firms"]},{key:"rf",label:"RF Propagation",icon:Za,adapters:["swpc","ducting"]},{key:"roads",label:"Roads",icon:SS,adapters:["traffic","roads511"]},{key:"geohazards",label:"Geohazards",icon:AS,adapters:["usgs_quake","usgs","avalanche"]},{key:"tracking",label:"Tracking",icon:PS,adapters:[]},{key:"mesh",label:"Mesh Health",icon:gv,adapters:[]}];function UJe(){var F,G;const[e,t]=U.useState(null),[r,n]=U.useState(""),[i,a]=U.useState(null),[o,s]=U.useState([]),[l,u]=U.useState(!0),[c,f]=U.useState(!1),[h,d]=U.useState(null),[v,g]=U.useState(null),[m,y]=U.useState(!1),[x,b]=U.useState("weather"),[S,T]=U.useState("nws");U.useEffect(()=>{document.title="Environment — MeshAI",(async()=>{try{const H=await(await fetch("/api/config/environmental")).json();t(H),n(JSON.stringify(H))}catch(V){d(V instanceof Error?V.message:"Failed to load config")}finally{u(!1)}})()},[]),U.useEffect(()=>{const V=async()=>{try{a(await jZ()),s(await RZ())}catch{}};V();const H=setInterval(V,3e4);return()=>clearInterval(H)},[]);const C=e!==null&&JSON.stringify(e)!==r,M=async()=>{if(e){f(!0),d(null),g(null);try{const V=await fetch("/api/config/environmental",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),H=await V.json();if(!V.ok)throw new Error(H.detail||"Save failed");n(JSON.stringify(e)),g("Environmental config saved"),H.restart_required&&y(!0),setTimeout(()=>g(null),3e3)}catch(V){d(V instanceof Error?V.message:"Save failed")}finally{f(!1)}}},P=()=>{e&&t(JSON.parse(r))},I=async()=>{try{await fetch("/api/restart",{method:"POST"}),y(!1),g("Restart initiated")}catch{d("Restart failed")}},k=V=>e&&t({...e,...V});if(l)return _.jsx("div",{className:"flex items-center justify-center h-64 text-slate-400",children:"Loading environmental config…"});if(!e)return _.jsx("div",{className:"flex items-center justify-center h-64 text-red-400",children:h||"No config"});const E=V=>i==null?void 0:i.feeds.find(H=>H.source===cl[V].health),D=V=>o.filter(H=>H.source===cl[V].health),j=dP.find(V=>V.key===x),N=j.adapters.length===0?null:S&&j.adapters.includes(S)?S:j.adapters[0],z=V=>{switch(V){case"nws":return _.jsxs(_.Fragment,{children:[_.jsx(Yh,{label:"NWS Zones",value:e.nws_zones,onChange:H=>k({nws_zones:H}),helper:"Zone IDs like IDZ016, IDZ030",infoLink:"https://www.weather.gov/pimar/PubZone"}),_.jsx(_t,{label:"User Agent",value:e.nws.user_agent,onChange:H=>k({nws:{...e.nws,user_agent:H}}),placeholder:"(MeshAI, you@email.com)",helper:"Format: (app_name, contact_email)"}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Tick Seconds",value:e.nws.tick_seconds,onChange:H=>k({nws:{...e.nws,tick_seconds:H}}),min:30}),_.jsx(Po,{label:"Min Severity",value:e.nws.severity_min,onChange:H=>k({nws:{...e.nws,severity_min:H}}),options:[{value:"minor",label:"Minor"},{value:"moderate",label:"Moderate"},{value:"severe",label:"Severe"},{value:"extreme",label:"Extreme"}]})]})]});case"swpc":return _.jsx("div",{className:"text-xs text-slate-500",children:"No additional settings."});case"ducting":return _.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[_.jsx(rt,{label:"Tick Seconds",value:e.ducting.tick_seconds,onChange:H=>k({ducting:{...e.ducting,tick_seconds:H}}),min:60}),_.jsx(rt,{label:"Latitude",value:e.ducting.latitude,onChange:H=>k({ducting:{...e.ducting,latitude:H}}),step:.01}),_.jsx(rt,{label:"Longitude",value:e.ducting.longitude,onChange:H=>k({ducting:{...e.ducting,longitude:H}}),step:.01})]});case"fires":return _.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(rt,{label:"Tick Seconds",value:e.fires.tick_seconds,onChange:H=>k({fires:{...e.fires,tick_seconds:H}}),min:60}),_.jsx(Po,{label:"State",value:e.fires.state,onChange:H=>k({fires:{...e.fires,state:H}}),options:TJe})]});case"avalanche":return _.jsxs(_.Fragment,{children:[_.jsx(rt,{label:"Tick Seconds",value:e.avalanche.tick_seconds,onChange:H=>k({avalanche:{...e.avalanche,tick_seconds:H}}),min:60}),_.jsx(Yh,{label:"Center IDs",value:e.avalanche.center_ids,onChange:H=>k({avalanche:{...e.avalanche,center_ids:H}}),helper:"e.g., SNFAC",infoLink:"https://avalanche.org/avalanche-centers/"}),_.jsx(AJe,{label:"Season Months",value:e.avalanche.season_months,onChange:H=>k({avalanche:{...e.avalanche,season_months:H}}),helper:"e.g., 12, 1, 2, 3, 4"})]});case"usgs":return _.jsxs(_.Fragment,{children:[_.jsx(rt,{label:"Tick Seconds",value:e.usgs.tick_seconds,onChange:H=>k({usgs:{...e.usgs,tick_seconds:H}}),min:900,helper:"Minimum 15 min (900s)"}),_.jsx(Yh,{label:"Site IDs",value:e.usgs.sites,onChange:H=>k({usgs:{...e.usgs,sites:H}}),helper:"USGS gauge site numbers",infoLink:"https://waterdata.usgs.gov/nwis"})]});case"usgs_quake":return _.jsxs(_.Fragment,{children:[_.jsx(rt,{label:"Tick Seconds",value:e.usgs_quake.tick_seconds,onChange:H=>k({usgs_quake:{...e.usgs_quake,tick_seconds:H}}),min:60}),_.jsx(rt,{label:"Min Magnitude",value:e.usgs_quake.min_magnitude,onChange:H=>k({usgs_quake:{...e.usgs_quake,min_magnitude:H}}),step:.1,min:0}),_.jsx(_t,{label:"Region Tag",value:e.usgs_quake.region,onChange:H=>k({usgs_quake:{...e.usgs_quake,region:H}})}),_.jsx("div",{className:"grid grid-cols-4 gap-2",children:["West","South","East","North"].map((H,Y)=>{var K;return _.jsx(rt,{label:H,value:((K=e.usgs_quake.bbox)==null?void 0:K[Y])??0,onChange:ee=>{const le=[...e.usgs_quake.bbox||[0,0,0,0]];le[Y]=ee,k({usgs_quake:{...e.usgs_quake,bbox:le}})},step:.01},H)})}),_.jsx("div",{className:"text-xs text-slate-500",children:"Bounding box [W,S,E,N] geographic filter"})]});case"traffic":return _.jsxs(_.Fragment,{children:[_.jsx(_t,{label:"API Key",value:e.traffic.api_key,onChange:H=>k({traffic:{...e.traffic,api_key:H}}),type:"password",helper:"developer.tomtom.com"}),_.jsx(rt,{label:"Tick Seconds",value:e.traffic.tick_seconds,onChange:H=>k({traffic:{...e.traffic,tick_seconds:H}}),min:60}),_.jsx("div",{className:"text-xs text-slate-500 mt-2",children:"Corridors:"}),(e.traffic.corridors||[]).map((H,Y)=>_.jsxs("div",{className:"grid grid-cols-4 gap-2 items-end",children:[_.jsx(_t,{label:"Name",value:H.name,onChange:K=>{const ee=[...e.traffic.corridors];ee[Y]={...H,name:K},k({traffic:{...e.traffic,corridors:ee}})}}),_.jsx(rt,{label:"Lat",value:H.lat,onChange:K=>{const ee=[...e.traffic.corridors];ee[Y]={...H,lat:K},k({traffic:{...e.traffic,corridors:ee}})},step:.01}),_.jsx(rt,{label:"Lon",value:H.lon,onChange:K=>{const ee=[...e.traffic.corridors];ee[Y]={...H,lon:K},k({traffic:{...e.traffic,corridors:ee}})},step:.01}),_.jsx("button",{onClick:()=>k({traffic:{...e.traffic,corridors:e.traffic.corridors.filter((K,ee)=>ee!==Y)}}),className:"px-2 py-2 text-xs text-red-400 hover:text-red-300 border border-red-400/30 rounded",children:"Remove"})]},Y)),_.jsx("button",{onClick:()=>k({traffic:{...e.traffic,corridors:[...e.traffic.corridors||[],{name:"",lat:0,lon:0}]}}),className:"text-xs text-accent hover:underline",children:"+ Add Corridor"})]});case"roads511":return _.jsxs(_.Fragment,{children:[_.jsx(_t,{label:"Base URL",value:e.roads511.base_url,onChange:H=>k({roads511:{...e.roads511,base_url:H}}),placeholder:"https://511.yourstate.gov/api/v2"}),_.jsx(_t,{label:"API Key",value:e.roads511.api_key,onChange:H=>k({roads511:{...e.roads511,api_key:H}}),type:"password",helper:"Leave empty if not required"}),_.jsx(rt,{label:"Tick Seconds",value:e.roads511.tick_seconds,onChange:H=>k({roads511:{...e.roads511,tick_seconds:H}}),min:60}),_.jsx(Yh,{label:"Endpoints",value:e.roads511.endpoints,onChange:H=>k({roads511:{...e.roads511,endpoints:H}}),helper:"e.g., /get/event"}),_.jsx("div",{className:"grid grid-cols-4 gap-2",children:["West","South","East","North"].map((H,Y)=>{var K;return _.jsx(rt,{label:H,value:((K=e.roads511.bbox)==null?void 0:K[Y])??0,onChange:ee=>{const le=[...e.roads511.bbox||[0,0,0,0]];le[Y]=ee,k({roads511:{...e.roads511,bbox:le}})},step:.01},H)})})]});case"firms":return _.jsxs(_.Fragment,{children:[_.jsx(_t,{label:"MAP Key",value:e.firms.map_key,onChange:H=>k({firms:{...e.firms,map_key:H}}),type:"password",helper:"firms.modaps.eosdis.nasa.gov/api/area/",infoLink:"https://firms.modaps.eosdis.nasa.gov/api/area/"}),_.jsx(rt,{label:"Tick Seconds",value:e.firms.tick_seconds,onChange:H=>k({firms:{...e.firms,tick_seconds:H}}),min:300}),_.jsx(Po,{label:"Satellite Source",value:e.firms.source,onChange:H=>k({firms:{...e.firms,source:H}}),options:[{value:"VIIRS_SNPP_NRT",label:"VIIRS SNPP (NRT)"},{value:"VIIRS_NOAA20_NRT",label:"VIIRS NOAA-20 (NRT)"},{value:"MODIS_NRT",label:"MODIS (NRT)"}]}),_.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[_.jsx(rt,{label:"Day Range",value:e.firms.day_range,onChange:H=>k({firms:{...e.firms,day_range:H}}),min:1,max:10}),_.jsx(Po,{label:"Min Confidence",value:e.firms.confidence_min,onChange:H=>k({firms:{...e.firms,confidence_min:H}}),options:[{value:"low",label:"Low"},{value:"nominal",label:"Nominal"},{value:"high",label:"High"}]}),_.jsx(rt,{label:"Proximity (km)",value:e.firms.proximity_km,onChange:H=>k({firms:{...e.firms,proximity_km:H}}),step:.5})]}),_.jsx("div",{className:"grid grid-cols-4 gap-2",children:["West","South","East","North"].map((H,Y)=>{var K;return _.jsx(rt,{label:H,value:((K=e.firms.bbox)==null?void 0:K[Y])??0,onChange:ee=>{const le=[...e.firms.bbox||[0,0,0,0]];le[Y]=ee,k({firms:{...e.firms,bbox:le}})},step:.01},H)})})]})}},$=e,Z=(V,H)=>{const Y=e[V]||{};k({[V]:{...Y,...H}})};return _.jsxs("div",{className:"space-y-6",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsx("h1",{className:"text-xl font-semibold text-slate-200",children:"Environment"}),_.jsxs("div",{className:"flex items-center gap-3",children:[_.jsx(yr,{label:"Feeds Enabled",checked:e.enabled,onChange:V=>k({enabled:V})}),C&&_.jsxs(_.Fragment,{children:[_.jsxs("button",{onClick:P,className:"flex items-center gap-1 px-3 py-1.5 text-sm text-slate-400 hover:text-slate-200 border border-border rounded",children:[_.jsx(QE,{size:14})," Discard"]}),_.jsxs("button",{onClick:M,disabled:c,className:"flex items-center gap-1 px-3 py-1.5 text-sm bg-accent text-white rounded disabled:opacity-50",children:[_.jsx(JE,{size:14})," ",c?"Saving…":"Save"]})]})]})]}),h&&_.jsx("div",{className:"text-sm text-red-400 bg-red-500/10 rounded p-3",children:h}),v&&_.jsx("div",{className:"text-sm text-green-400 bg-green-500/10 rounded p-3",children:v}),m&&_.jsxs("div",{className:"flex items-center justify-between text-sm text-amber-400 bg-amber-500/10 border border-amber-500/30 rounded p-3",children:[_.jsxs("span",{className:"flex items-center gap-2",children:[_.jsx(Am,{size:14})," A restart is required for some changes to take effect."]}),_.jsx("button",{onClick:I,className:"px-3 py-1 bg-amber-500/20 hover:bg-amber-500/30 rounded",children:"Restart now"})]}),e.central&&_.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg p-4 space-y-3",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("div",{children:[_.jsx("span",{className:"text-sm font-medium text-slate-300",children:"Central Connection"}),_.jsx("p",{className:"text-xs text-slate-600",children:'NATS JetStream source for any adapter set to "central"'})]}),_.jsx(yr,{label:"",checked:!!e.central.enabled,onChange:V=>k({central:{...e.central,enabled:V}})})]}),_.jsxs("div",{className:e.central.enabled?"space-y-3":"space-y-3 opacity-40 pointer-events-none select-none",children:[_.jsx(_t,{label:"URL",value:e.central.url||"",onChange:V=>k({central:{...e.central,url:V}}),placeholder:"nats://central.echo6.mesh:4222"}),_.jsx(_t,{label:"Durable",value:e.central.durable||"",onChange:V=>k({central:{...e.central,durable:V}}),placeholder:"meshai-v04"}),_.jsx(_t,{label:"Region",value:e.central.region||"",onChange:V=>k({central:{...e.central,region:V}}),placeholder:"us.id",helper:"Central v0.9.20 region token (dotted, e.g. 'us.id'). Empty = bare wildcards (all-US firehose)."})]})]}),_.jsx("div",{className:"flex gap-1 border-b border-border overflow-x-auto",children:dP.map(({key:V,label:H,icon:Y})=>_.jsxs("button",{onClick:()=>{b(V);const K=dP.find(ee=>ee.key===V);T(K.adapters[0]??null)},className:`flex items-center gap-2 px-4 py-2 text-sm whitespace-nowrap border-b-2 -mb-px transition-colors ${x===V?"border-accent text-accent":"border-transparent text-slate-400 hover:text-slate-200"}`,children:[_.jsx(Y,{size:15})," ",H]},V))}),x==="tracking"&&_.jsxs("div",{className:"flex flex-col items-center justify-center h-[40vh] text-center",children:[_.jsx(PS,{size:32,className:"text-slate-600 mb-4"}),_.jsx("p",{className:"text-slate-500 max-w-md",children:"No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5."})]}),x==="mesh"&&_.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg p-4 space-y-3",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("div",{children:[_.jsx("span",{className:"text-sm font-medium text-slate-300",children:"Mesh Health"}),_.jsx("p",{className:"text-xs text-slate-600",children:"Node/infra telemetry — sourced from the mesh, not an environmental feed."})]}),_.jsxs("div",{className:"flex items-center gap-1",children:[_.jsx("span",{className:"text-[10px] uppercase tracking-wide text-slate-600",children:"source"}),_.jsx(Zie,{value:"native",onChange:()=>{},disabled:!1,centralDisabled:!0})]})]}),_.jsx("div",{className:"text-[11px] text-slate-600",children:"Central not available — reserved for a future migration."})]}),j.adapters.length>0&&N&&_.jsxs(_.Fragment,{children:[j.adapters.length>1&&_.jsx("div",{className:"flex gap-1",children:j.adapters.map(V=>_.jsx("button",{onClick:()=>T(V),className:`px-3 py-1.5 text-sm rounded ${N===V?"bg-bg-hover text-slate-100":"text-slate-400 hover:text-slate-200"}`,children:cl[V].label},V))}),_.jsx(HJe,{title:cl[N].label,subtitle:cl[N].subtitle,enabled:((F=$[N])==null?void 0:F.enabled)??!1,onEnabled:V=>Z(N,{enabled:V}),feedSource:((G=$[N])==null?void 0:G.feed_source)??"native",onFeedSource:V=>Z(N,{feed_source:V}),hasCentral:cl[N].hasCentral,nativeOnly:cl[N].nativeOnly,hasKey:cl[N].hasKey,health:E(N),events:D(N),children:z(N)})]})]})}const ZU={infra_offline:DZ,infra_recovery:LS,battery_warning:uA,battery_critical:uA,battery_emergency:uA,hf_blackout:Mm,uhf_ducting:Za,weather_warning:nu,weather_watch:nu,new_router:Za,packet_flood:iu,sustained_high_util:iu,region_blackout:xd,default:Cm};function ZJe(e){return ZU[e]||ZU.default}function Yie(e){switch(e==null?void 0:e.toLowerCase()){case"immediate":return{bg:"bg-red-500/10",border:"border-red-500",badge:"bg-red-500/20 text-red-400",iconColor:"text-red-500"};case"priority":return{bg:"bg-amber-500/10",border:"border-amber-500",badge:"bg-amber-500/20 text-amber-400",iconColor:"text-amber-500"};case"routine":default:return{bg:"bg-blue-500/10",border:"border-blue-500",badge:"bg-blue-500/20 text-blue-400",iconColor:"text-blue-500"}}}function YJe(e){const t=typeof e=="number"?new Date(e*1e3):new Date(e),n=new Date().getTime()-t.getTime(),i=Math.floor(n/1e3),a=Math.floor(i/60),o=Math.floor(a/60),s=Math.floor(o/24);return i<60?"Just now":a<60?`${a}m ago`:o<24?`${o}h ago`:`${s}d ago`}function XJe(e){return(typeof e=="number"?new Date(e*1e3):new Date(e)).toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1})}function qJe(e){return e<60?`${e}s`:e<3600?`${Math.floor(e/60)}m`:e<86400?`${Math.floor(e/3600)}h ${Math.floor(e%3600/60)}m`:`${Math.floor(e/86400)}d`}function KJe({alert:e,onAcknowledge:t}){var i;const r=Yie(e.severity),n=ZJe(e.type);return _.jsx("div",{className:`p-4 rounded-lg ${r.bg} border-l-4 ${r.border}`,children:_.jsxs("div",{className:"flex items-start gap-3",children:[_.jsx(n,{size:20,className:r.iconColor}),_.jsxs("div",{className:"flex-1 min-w-0",children:[_.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[_.jsx("span",{className:`text-xs px-2 py-0.5 rounded-full ${r.badge}`,children:(i=e.severity)==null?void 0:i.toUpperCase()}),_.jsx("span",{className:"text-xs text-slate-500",children:e.type})]}),_.jsx("div",{className:"text-sm text-slate-200",children:e.message}),_.jsxs("div",{className:"flex items-center gap-4 mt-2 text-xs text-slate-500",children:[_.jsxs("span",{className:"flex items-center gap-1",children:[_.jsx(bd,{size:12}),e.timestamp?YJe(e.timestamp):"Just now"]}),e.scope_value&&_.jsxs("span",{children:[e.scope_type,": ",e.scope_value]})]})]}),_.jsx("button",{onClick:()=>t(e),className:"px-3 py-1 text-xs text-slate-400 hover:text-slate-200 border border-border rounded hover:bg-bg-hover transition-colors",children:"Acknowledge"})]})})}function QJe({history:e,typeFilter:t,severityFilter:r,onTypeFilterChange:n,onSeverityFilterChange:i,page:a,totalPages:o,onPageChange:s}){const l=["all","infra_offline","infra_recovery","battery_warning","battery_critical","hf_blackout","uhf_ducting","weather_warning","new_router","packet_flood"],u=["all","immediate","priority","routine"];return _.jsxs("div",{className:"bg-bg-card border border-border rounded-lg",children:[_.jsxs("div",{className:"p-4 border-b border-border flex items-center gap-4",children:[_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx(qE,{size:14,className:"text-slate-400"}),_.jsx("span",{className:"text-sm text-slate-400",children:"Filter:"})]}),_.jsx("select",{value:t,onChange:c=>n(c.target.value),className:"bg-bg border border-border rounded px-3 py-1.5 text-sm text-slate-200 focus:outline-none focus:border-blue-500",children:l.map(c=>_.jsx("option",{value:c,children:c==="all"?"All Types":c.replace(/_/g," ")},c))}),_.jsx("select",{value:r,onChange:c=>i(c.target.value),className:"bg-bg border border-border rounded px-3 py-1.5 text-sm text-slate-200 focus:outline-none focus:border-blue-500",children:u.map(c=>_.jsx("option",{value:c,children:c==="all"?"All Severities":c.charAt(0).toUpperCase()+c.slice(1)},c))})]}),_.jsx("div",{className:"overflow-x-auto",children:_.jsxs("table",{className:"w-full",children:[_.jsx("thead",{children:_.jsxs("tr",{className:"border-b border-border",children:[_.jsx("th",{className:"text-left text-xs font-medium text-slate-400 p-4",children:"Time"}),_.jsx("th",{className:"text-left text-xs font-medium text-slate-400 p-4",children:"Type"}),_.jsx("th",{className:"text-left text-xs font-medium text-slate-400 p-4",children:"Severity"}),_.jsx("th",{className:"text-left text-xs font-medium text-slate-400 p-4",children:"Message"}),_.jsx("th",{className:"text-left text-xs font-medium text-slate-400 p-4",children:"Duration"})]})}),_.jsx("tbody",{children:e.length>0?e.map((c,f)=>{const h=Yie(c.severity);return _.jsxs("tr",{className:"border-b border-border hover:bg-bg-hover",children:[_.jsx("td",{className:"p-4 text-sm text-slate-400 font-mono whitespace-nowrap",children:XJe(c.timestamp)}),_.jsx("td",{className:"p-4 text-sm text-slate-300",children:c.type.replace(/_/g," ")}),_.jsx("td",{className:"p-4",children:_.jsx("span",{className:`text-xs px-2 py-0.5 rounded-full ${h.badge}`,children:c.severity})}),_.jsx("td",{className:"p-4 text-sm text-slate-200 max-w-md truncate",children:c.message}),_.jsx("td",{className:"p-4 text-sm text-slate-400 font-mono",children:c.duration?qJe(c.duration):"-"})]},c.id||f)}):_.jsx("tr",{children:_.jsx("td",{colSpan:5,className:"p-8 text-center text-slate-500",children:"No alert history available"})})})]})}),o>1&&_.jsxs("div",{className:"p-4 border-t border-border flex items-center justify-between",children:[_.jsxs("span",{className:"text-sm text-slate-400",children:["Page ",a," of ",o]}),_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx("button",{onClick:()=>s(a-1),disabled:a<=1,className:"p-2 text-slate-400 hover:text-slate-200 disabled:opacity-50 disabled:cursor-not-allowed",children:_.jsx(Jue,{size:16})}),_.jsx("button",{onClick:()=>s(a+1),disabled:a>=o,className:"p-2 text-slate-400 hover:text-slate-200 disabled:opacity-50 disabled:cursor-not-allowed",children:_.jsx(_d,{size:16})})]})]})]})}function JJe({subscription:e,nodes:t}){const r=o=>{const s=t.find(l=>l.node_id_hex===o||String(l.node_num)===o||l.short_name===o);return s?s.long_name&&s.long_name!==s.short_name?`${s.short_name} (${s.long_name})`:s.short_name:o},n=()=>{if(e.sub_type==="alerts")return"Real-time";const o=e.schedule_time||"0000",s=parseInt(o.slice(0,2)),l=o.slice(2),u=s>=12?"PM":"AM";let f=`${s%12||12}:${l} ${u}`;return e.sub_type==="weekly"&&e.schedule_day&&(f+=` ${e.schedule_day.charAt(0).toUpperCase()}${e.schedule_day.slice(1)}`),f},a=(()=>{switch(e.sub_type){case"alerts":return Cm;case"daily":return bd;case"weekly":return bd;default:return Cm}})();return _.jsx("div",{className:"p-4 rounded-lg bg-bg-hover border border-border",children:_.jsxs("div",{className:"flex items-center gap-3",children:[_.jsx("div",{className:"w-10 h-10 rounded-lg bg-blue-500/10 flex items-center justify-center",children:_.jsx(a,{size:18,className:"text-blue-400"})}),_.jsxs("div",{className:"flex-1",children:[_.jsxs("div",{className:"text-sm text-slate-200 font-medium",children:[e.sub_type.charAt(0).toUpperCase()+e.sub_type.slice(1),e.scope_type!=="mesh"&&e.scope_value&&_.jsxs("span",{className:"text-slate-400 font-normal ml-2",children:["(",e.scope_type,": ",e.scope_value,")"]})]}),_.jsxs("div",{className:"text-xs text-slate-500 mt-0.5",children:[n()," • ",r(e.user_id)]})]}),_.jsx("div",{className:`w-2 h-2 rounded-full ${e.enabled?"bg-green-500":"bg-slate-500"}`})]})})}function eet(){const[e,t]=U.useState([]),[r,n]=U.useState([]),[i,a]=U.useState([]),[o,s]=U.useState([]),[l,u]=U.useState(!0),[c,f]=U.useState(null),[h,d]=U.useState("all"),[v,g]=U.useState("all"),[m,y]=U.useState(1),[x,b]=U.useState(1),S=20,[T,C]=U.useState(new Set),{lastAlert:M}=rD();U.useEffect(()=>{document.title="Alerts — MeshAI"},[]),U.useEffect(()=>{Promise.all([NZ().catch(()=>[]),m3(S,0).catch(()=>({items:[],total:0})),gce().catch(()=>[]),fetch("/api/nodes").then(k=>k.json()).catch(()=>[])]).then(([k,E,D,j])=>{t(k),Array.isArray(E)?(n(E),b(1)):(n(E.items||[]),b(Math.ceil((E.total||0)/S))),a(D),s(j),u(!1)}).catch(k=>{f(k.message),u(!1)})},[]),U.useEffect(()=>{M&&t(k=>k.some(D=>D.type===M.type&&D.message===M.message)?k:[M,...k])},[M]),U.useEffect(()=>{const k=(m-1)*S;m3(S,k,h,v).then(E=>{Array.isArray(E)?(n(E),b(1)):(n(E.items||[]),b(Math.ceil((E.total||0)/S)))}).catch(()=>{})},[m,h,v]);const P=U.useCallback(k=>{const E=`${k.type}-${k.message}-${k.timestamp}`;C(D=>new Set([...D,E]))},[]),I=e.filter(k=>{const E=`${k.type}-${k.message}-${k.timestamp}`;return!T.has(E)});return l?_.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsx("div",{className:"text-slate-400",children:"Loading alerts..."})}):c?_.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsxs("div",{className:"text-red-400",children:["Error: ",c]})}):_.jsxs("div",{className:"space-y-6",children:[_.jsxs("div",{className:"bg-bg-card border border-border rounded-lg p-6",children:[_.jsxs("h2",{className:"text-sm font-medium text-slate-400 mb-4 flex items-center gap-2",children:[_.jsx(iu,{size:14}),"Active Alerts (",I.length,")"]}),I.length>0?_.jsx("div",{className:"space-y-3",children:I.map((k,E)=>_.jsx(KJe,{alert:k,onAcknowledge:P},`${k.type}-${k.timestamp}-${E}`))}):_.jsxs("div",{className:"flex items-center gap-2 text-slate-500 py-8",children:[_.jsx(YE,{size:20,className:"text-green-500"}),_.jsx("span",{children:"No active alerts — all systems nominal"})]})]}),_.jsxs("div",{children:[_.jsxs("h2",{className:"text-sm font-medium text-slate-400 mb-4 flex items-center gap-2",children:[_.jsx(bd,{size:14}),"Alert History"]}),_.jsx(QJe,{history:r,typeFilter:h,severityFilter:v,onTypeFilterChange:k=>{d(k),y(1)},onSeverityFilterChange:k=>{g(k),y(1)},page:m,totalPages:x,onPageChange:y})]}),_.jsxs("div",{className:"bg-bg-card border border-border rounded-lg p-6",children:[_.jsxs("h2",{className:"text-sm font-medium text-slate-400 mb-4 flex items-center gap-2",children:[_.jsx(fce,{size:14}),"Mesh Subscriptions (",i.length,")"]}),i.length>0?_.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3",children:i.map(k=>_.jsx(JJe,{subscription:k,nodes:o},k.id))}):_.jsxs("div",{className:"text-slate-500 py-4",children:[_.jsx("p",{children:"No active subscriptions."}),_.jsxs("p",{className:"text-xs mt-2",children:["Manage subscriptions via ",_.jsx("code",{className:"text-blue-400",children:"!subscribe"})," on mesh"]})]})]})]})}const yb=[{value:"routine",label:"Routine",description:"Informational, no time pressure (ducting, new node, weather advisory, battery declining)"},{value:"priority",label:"Priority",description:"Needs attention soon (severe weather, fire nearby, node offline, HF blackout)"},{value:"immediate",label:"Immediate",description:"Act now, drop everything (fire at infrastructure, extreme weather, region blackout)"}],YU=[{id:"mesh_health",name:"Mesh Health Monitoring",description:"Infrastructure problems - offline nodes, low battery, channel congestion",rule:{name:"Mesh Health Monitoring",enabled:!0,trigger_type:"condition",categories:["infra_offline","critical_node_down","infra_recovery","battery_warning","battery_critical","battery_emergency","high_utilization","packet_flood","mesh_score_low"],min_severity:"routine",delivery_type:"mesh_broadcast",broadcast_channel:0,cooldown_minutes:30,override_quiet:!1,schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"",schedule_days:[],message_type:"",custom_message:"",node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{}}},{id:"weather_fire",name:"Weather & Fire Alerts",description:"Environmental threats - severe weather, nearby wildfires, new ignitions, flooding",rule:{name:"Weather & Fire Alerts",enabled:!0,trigger_type:"condition",categories:["weather_warning","fire_proximity","new_ignition","stream_flood_warning"],min_severity:"priority",delivery_type:"mesh_broadcast",broadcast_channel:0,cooldown_minutes:15,override_quiet:!1,schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"",schedule_days:[],message_type:"",custom_message:"",node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{}}},{id:"rf_conditions",name:"RF Conditions",description:"Propagation changes - solar events, HF blackouts, tropospheric ducting",rule:{name:"RF Conditions",enabled:!0,trigger_type:"condition",categories:["hf_blackout","tropospheric_ducting","geomagnetic_storm"],min_severity:"routine",delivery_type:"mesh_broadcast",broadcast_channel:0,cooldown_minutes:60,override_quiet:!1,schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"",schedule_days:[],message_type:"",custom_message:"",node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{}}},{id:"road_traffic",name:"Road & Traffic",description:"Road closures and severe congestion",rule:{name:"Road & Traffic",enabled:!0,trigger_type:"condition",categories:["road_closure","traffic_congestion"],min_severity:"routine",delivery_type:"mesh_broadcast",broadcast_channel:0,cooldown_minutes:30,override_quiet:!1,schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"",schedule_days:[],message_type:"",custom_message:"",node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{}}},{id:"everything_critical",name:"Everything Critical",description:"All emergency-level events regardless of type",rule:{name:"Everything Critical",enabled:!0,trigger_type:"condition",categories:[],min_severity:"immediate",delivery_type:"mesh_broadcast",broadcast_channel:0,cooldown_minutes:5,override_quiet:!0,schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"",schedule_days:[],message_type:"",custom_message:"",node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{}}},{id:"morning_briefing",name:"Morning Briefing",description:"Daily health and conditions summary at 7am",rule:{name:"Morning Briefing",enabled:!0,trigger_type:"schedule",categories:[],min_severity:"routine",schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"",schedule_days:[],message_type:"mesh_health_summary",custom_message:"",delivery_type:"mesh_broadcast",broadcast_channel:0,cooldown_minutes:0,override_quiet:!1,node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{}}}];function vP(e){if(!e)return"Never";const r=Date.now()/1e3-e;return r<60?"Just now":r<3600?`${Math.floor(r/60)}m ago`:r<86400?`${Math.floor(r/3600)}h ago`:r<604800?`${Math.floor(r/86400)}d ago`:new Date(e*1e3).toLocaleDateString()}function Ha({info:e}){const[t,r]=U.useState(!1);return _.jsxs("div",{className:"relative inline-block",children:[_.jsx("button",{type:"button",onClick:n=>{n.stopPropagation(),r(!t)},className:"ml-1.5 w-4 h-4 rounded-full bg-slate-700 hover:bg-slate-600 text-slate-400 hover:text-slate-200 inline-flex items-center justify-center text-xs transition-colors",title:"More info",children:"?"}),t&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"fixed inset-0 z-40",onClick:()=>r(!1)}),_.jsx("div",{className:"absolute left-0 top-6 z-50 w-72 p-3 bg-[#1a2332] border border-[#2a3a4a] rounded-lg shadow-xl text-xs text-slate-300 leading-relaxed",children:e})]})]})}function bl({label:e,value:t,onChange:r,type:n="text",placeholder:i="",helper:a="",info:o=""}){const[s,l]=U.useState(!1),u=n==="password";return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,o&&_.jsx(Ha,{info:o})]}),_.jsxs("div",{className:"relative",children:[_.jsx("input",{type:u&&!s?"password":"text",value:t,onChange:c=>r(c.target.value),placeholder:i,className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent placeholder-slate-600"}),u&&_.jsx("button",{type:"button",onClick:()=>l(!s),className:"absolute right-2 top-1/2 -translate-y-1/2 text-slate-500 hover:text-slate-300",children:s?_.jsx(MZ,{size:16}):_.jsx(XE,{size:16})})]}),a&&_.jsx("p",{className:"text-xs text-slate-600",children:a})]})}function iS({label:e,value:t,onChange:r,min:n,max:i,step:a=1,helper:o="",info:s=""}){return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,s&&_.jsx(Ha,{info:s})]}),_.jsx("input",{type:"number",value:t,onChange:l=>r(Number(l.target.value)),min:n,max:i,step:a,className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent"}),o&&_.jsx("p",{className:"text-xs text-slate-600",children:o})]})}function cv({label:e,checked:t,onChange:r,helper:n="",info:i=""}){return _.jsxs("div",{className:"flex items-center justify-between py-2",children:[_.jsxs("div",{children:[_.jsxs("span",{className:"flex items-center text-sm text-slate-300",children:[e,i&&_.jsx(Ha,{info:i})]}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]}),_.jsx("button",{type:"button",onClick:()=>r(!t),className:`relative w-11 h-6 rounded-full transition-colors ${t?"bg-accent":"bg-[#1e2a3a]"}`,children:_.jsx("span",{className:`absolute top-1 left-1 w-4 h-4 rounded-full bg-white transition-transform ${t?"translate-x-5":""}`})})]})}function aS({label:e,value:t,onChange:r,helper:n="",info:i=""}){return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,i&&_.jsx(Ha,{info:i})]}),_.jsx("input",{type:"time",value:t,onChange:a=>r(a.target.value),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent"}),n&&_.jsx("p",{className:"text-xs text-slate-600",children:n})]})}function _b({label:e,value:t,onChange:r,placeholder:n="Add item...",helper:i="",info:a=""}){const[o,s]=U.useState(""),l=()=>{o.trim()&&!t.includes(o.trim())&&(r([...t,o.trim()]),s(""))},u=c=>{r(t.filter((f,h)=>h!==c))};return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:[e,a&&_.jsx(Ha,{info:a})]}),_.jsxs("div",{className:"flex gap-2",children:[_.jsx("input",{type:"text",value:o,onChange:c=>s(c.target.value),onKeyDown:c=>c.key==="Enter"&&(c.preventDefault(),l()),className:"flex-1 px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent",placeholder:n}),_.jsx("button",{type:"button",onClick:l,className:"px-3 py-2 bg-accent hover:bg-accent/80 rounded text-sm text-white transition-colors",children:_.jsx(MS,{size:16})})]}),t.length>0&&_.jsx("div",{className:"flex flex-wrap gap-2 mt-2",children:t.map((c,f)=>_.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-1 bg-[#1e2a3a] rounded text-sm text-slate-300",children:[c,_.jsx("button",{type:"button",onClick:()=>u(f),className:"text-slate-500 hover:text-red-400",children:_.jsx(au,{size:14})})]},f))}),i&&_.jsx("p",{className:"text-xs text-slate-600",children:i})]})}function Xie({value:e,onChange:t}){const[r,n]=U.useState(!1),i=yb.find(a=>a.value===e)||yb[0];return _.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Severity Threshold",_.jsx(Ha,{info:"Only alerts at or above this severity trigger this rule. ROUTINE = informational, PRIORITY = needs attention, IMMEDIATE = act now."})]}),_.jsxs("div",{className:"relative",children:[_.jsxs("button",{type:"button",onClick:()=>n(!r),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-left flex items-center justify-between hover:border-accent transition-colors",children:[_.jsxs("div",{children:[_.jsx("span",{className:"text-slate-200",children:i.label}),_.jsxs("span",{className:"text-slate-500 ml-2",children:["- ",i.description]})]}),_.jsx(mv,{size:16,className:`text-slate-500 transition-transform ${r?"rotate-180":""}`})]}),r&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"fixed inset-0 z-40",onClick:()=>n(!1)}),_.jsx("div",{className:"absolute left-0 right-0 top-full mt-1 z-50 bg-[#0a0e17] border border-[#1e2a3a] rounded-lg shadow-xl overflow-hidden",children:yb.map(a=>_.jsxs("button",{type:"button",onClick:()=>{t(a.value),n(!1)},className:`w-full px-3 py-2.5 text-left text-sm hover:bg-[#1e2a3a] transition-colors ${e===a.value?"bg-accent/10":""}`,children:[_.jsx("div",{className:"font-medium text-slate-200",children:a.label}),_.jsx("div",{className:"text-xs text-slate-500",children:a.description})]},a.value))})]})]}),_.jsx("p",{className:"text-xs text-slate-600",children:'Lower = more notifications. "Warning" recommended for most rules.'})]})}function Bx({rule:e}){const[t,r]=U.useState(!1),[n,i]=U.useState(null),a=async()=>{r(!0),i(null);try{let s={type:e.delivery_type};e.delivery_type==="mesh_broadcast"?s.channel_index=e.broadcast_channel:e.delivery_type==="mesh_dm"?s.node_ids=e.node_ids:e.delivery_type==="email"?s={type:"email",smtp_host:e.smtp_host,smtp_port:e.smtp_port,smtp_user:e.smtp_user,smtp_password:e.smtp_password,smtp_tls:e.smtp_tls,from_address:e.from_address,recipients:e.recipients}:e.delivery_type==="webhook"&&(s={type:"webhook",url:e.webhook_url,headers:e.webhook_headers});const u=await(await fetch("/api/notifications/channels/test",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)})).json();i(u)}catch(s){i({success:!1,message:"Test failed",error:s instanceof Error?s.message:"Unknown error",details:{}})}finally{r(!1)}};if(!e.delivery_type)return null;const o={mesh_broadcast:_.jsx(Za,{size:14}),mesh_dm:_.jsx(kZ,{size:14}),email:_.jsx(oce,{size:14}),webhook:_.jsx(ace,{size:14})}[e.delivery_type]||_.jsx(LS,{size:14});return _.jsxs("div",{className:"space-y-2",children:[_.jsx("button",{type:"button",onClick:a,disabled:t,className:"flex items-center gap-2 px-3 py-1.5 bg-slate-700 hover:bg-slate-600 rounded text-sm disabled:opacity-50",children:t?_.jsxs(_.Fragment,{children:[_.jsx(Am,{size:14,className:"animate-spin"}),"Testing..."]}):_.jsxs(_.Fragment,{children:[o,"Test Channel"]})}),n&&_.jsx("div",{className:`p-2 rounded text-xs ${n.success?"bg-green-500/10 border border-green-500/30 text-green-400":"bg-red-500/10 border border-red-500/30 text-red-400"}`,children:_.jsxs("div",{className:"flex items-start gap-2",children:[n.success?_.jsx(Qc,{size:14,className:"mt-0.5 flex-shrink-0"}):_.jsx(au,{size:14,className:"mt-0.5 flex-shrink-0"}),_.jsxs("div",{children:[_.jsx("div",{className:"font-medium",children:n.message}),n.error&&_.jsx("div",{className:"mt-1 text-red-300",children:n.error})]})]})})]})}function tet({rule:e,ruleIndex:t,categories:r,quietHoursEnabled:n,onChange:i,onDelete:a,onDuplicate:o,onTest:s}){var k,E,D,j;const[l,u]=U.useState(!e.name),[c,f]=U.useState(!1),[h,d]=U.useState(null),[v,g]=U.useState(null);U.useEffect(()=>{var N;e.name&&t>=0&&(fetch(`/api/notifications/rules/${t}/stats`).then(z=>z.json()).then(z=>d(z)).catch(()=>{}),(N=e.categories)!=null&&N.length&&fetch("/api/notifications/rules/sources",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({categories:e.categories})}).then(z=>z.json()).then(z=>g(z)).catch(()=>{}))},[e.name,t,e.categories]);const m=[{value:"",label:"(None)",description:"Rule matches but does not deliver"},{value:"mesh_broadcast",label:"Mesh Broadcast",description:"Send to a mesh radio channel"},{value:"mesh_dm",label:"Mesh DM",description:"Direct message to specific nodes"},{value:"email",label:"Email",description:"Send via SMTP"},{value:"webhook",label:"Webhook",description:"POST to any URL"}],y=[{value:"daily",label:"Daily"},{value:"twice_daily",label:"Twice Daily"},{value:"weekly",label:"Weekly"}],x=[{value:"mesh_health_summary",label:"Mesh Health Summary",description:"Current health score, pillar breakdown, problem nodes"},{value:"rf_propagation_report",label:"RF Propagation Report",description:"Solar indices, Kp, ducting conditions"},{value:"alerts_digest",label:"Active Alerts Digest",description:"Summary of all active environmental alerts"},{value:"environmental_conditions",label:"Environmental Conditions",description:"Full conditions: weather, fire, streams, roads"},{value:"custom",label:"Custom Message",description:"Write your own with template tokens"}],b=["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],S=N=>{const z=e.categories||[];z.includes(N)?i({...e,categories:z.filter($=>$!==N)}):i({...e,categories:[...z,N]})},T=N=>{const z=e.schedule_days||[];z.includes(N)?i({...e,schedule_days:z.filter($=>$!==N)}):i({...e,schedule_days:[...z,N]})},C=async()=>{f(!0),await s(),f(!1)},M=()=>{if(e.trigger_type==="schedule")return"[Scheduled report preview would appear here]";const N=e.categories||[];if(N.length===0&&r.length>0)return r[0].example_message||"Alert notification";const z=r.find($=>N.includes($.id));return(z==null?void 0:z.example_message)||"Alert notification"},P=()=>{var z,$,Z,F,G,V,H,Y;const N=[];if(e.trigger_type==="schedule"){const K=((z=y.find(le=>le.value===e.schedule_frequency))==null?void 0:z.label)||e.schedule_frequency,ee=(($=x.find(le=>le.value===e.message_type))==null?void 0:$.label)||e.message_type;N.push(`${K} at ${e.schedule_time||"??:??"}`),N.push(ee)}else{const K=((Z=e.categories)==null?void 0:Z.length)||0,ee=K===0?"All":r.filter(fe=>{var Be;return(Be=e.categories)==null?void 0:Be.includes(fe.id)}).map(fe=>fe.name).slice(0,2).join(", ")+(K>2?` +${K-2}`:""),le=((F=yb.find(fe=>fe.value===e.min_severity))==null?void 0:F.label)||e.min_severity;N.push(`${ee} at ${le}+`)}if(!e.delivery_type)N.push("No delivery");else{const K=((G=m.find(le=>le.value===e.delivery_type))==null?void 0:G.label)||e.delivery_type;let ee="";if(e.delivery_type==="mesh_broadcast")ee=`Ch ${e.broadcast_channel}`;else if(e.delivery_type==="mesh_dm")ee=`${((V=e.node_ids)==null?void 0:V.length)||0} nodes`;else if(e.delivery_type==="email")ee=(H=e.recipients)!=null&&H.length?e.recipients[0]+(e.recipients.length>1?` +${e.recipients.length-1}`:""):"no recipients";else if(e.delivery_type==="webhook")try{ee=new URL(e.webhook_url).hostname}catch{ee=((Y=e.webhook_url)==null?void 0:Y.slice(0,20))||"no URL"}N.push(`${K}${ee?` (${ee})`:""}`)}return N.join(" -> ")},I=()=>{var z;if(!v||!((z=e.categories)!=null&&z.length))return null;const N=new Map;for(const[,$]of Object.entries(v)){const Z=N.get($.source);Z?(Z.events+=$.active_events,Z.enabled=Z.enabled&&$.enabled):N.set($.source,{enabled:$.enabled,events:$.active_events})}return Array.from(N.entries()).map(([$,{enabled:Z,events:F}])=>_.jsxs("span",{className:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-xs ${Z?"bg-green-500/10 text-green-400":"bg-red-500/10 text-red-400"}`,title:Z?`${F} active`:"Not enabled",children:[Z?_.jsx(LS,{size:10}):_.jsx(DZ,{size:10}),$.toUpperCase(),Z&&F>0&&` (${F})`]},$))};return _.jsxs("div",{className:`border rounded-lg overflow-hidden ${e.enabled?"border-[#1e2a3a]":"border-slate-700 opacity-60"}`,children:[_.jsxs("div",{className:"flex items-center justify-between p-3 bg-[#0a0e17] cursor-pointer",onClick:()=>u(!l),children:[_.jsxs("div",{className:"flex items-center gap-3 min-w-0 flex-1",children:[l?_.jsx(mv,{size:16,className:"text-slate-500 flex-shrink-0"}):_.jsx(_d,{size:16,className:"text-slate-500 flex-shrink-0"}),_.jsx("button",{onClick:N=>{N.stopPropagation(),i({...e,enabled:!e.enabled})},className:`w-2 h-2 rounded-full flex-shrink-0 ${e.enabled?"bg-green-500":"bg-slate-500"}`,title:e.enabled?"Enabled":"Disabled"}),e.trigger_type==="schedule"?_.jsx(bd,{size:14,className:"text-blue-400 flex-shrink-0"}):_.jsx(Mm,{size:14,className:"text-yellow-400 flex-shrink-0"}),_.jsx("span",{className:"font-medium text-slate-200 truncate",children:e.name||"New Rule"}),!l&&_.jsx("span",{className:`text-xs truncate hidden sm:block ${e.delivery_type?"text-slate-500":"text-amber-400"}`,children:P()})]}),_.jsxs("div",{className:"flex items-center gap-1 flex-shrink-0",children:[h&&!l&&_.jsx("span",{className:"hidden sm:inline-flex items-center gap-1 px-2 py-0.5 bg-slate-800 rounded text-xs text-slate-400 mr-2",children:h.last_fired?vP(h.last_fired):"Never fired"}),!l&&_.jsx("div",{className:"hidden md:flex items-center gap-1 mr-2",children:I()}),_.jsx("button",{onClick:N=>{N.stopPropagation(),C()},disabled:c||!e.name,className:"p-1.5 text-blue-400 hover:text-blue-300 hover:bg-blue-500/10 rounded disabled:opacity-50",title:"Test rule",children:_.jsx(p3,{size:14})}),_.jsx("button",{onClick:N=>{N.stopPropagation(),o()},className:"p-1.5 text-slate-400 hover:text-slate-200 hover:bg-slate-500/10 rounded",title:"Duplicate",children:_.jsx(nce,{size:14})}),_.jsx("button",{onClick:N=>{N.stopPropagation(),a()},className:"p-1.5 text-red-400 hover:text-red-300 hover:bg-red-500/10 rounded",title:"Delete",children:_.jsx(tD,{size:14})})]})]}),!l&&e.name&&_.jsxs("div",{className:"px-3 pb-2 pt-0 bg-[#0a0e17] flex items-center gap-2 flex-wrap text-xs",children:[!e.delivery_type&&_.jsxs("span",{className:"inline-flex items-center gap-1 px-1.5 py-0.5 bg-amber-500/10 text-amber-400 rounded",children:[_.jsx(xd,{size:10}),"No delivery method"]}),(h==null?void 0:h.fire_count)!==void 0&&h.fire_count>0&&_.jsxs("span",{className:"text-slate-500",children:["Fired ",h.fire_count,"x"]})]}),l&&_.jsxs("div",{className:"p-4 space-y-6 border-t border-[#1e2a3a]",children:[_.jsx(bl,{label:"Rule Name",value:e.name,onChange:N=>i({...e,name:N}),placeholder:"e.g., Emergency Broadcast, Daily Health Report",helper:"A descriptive name for this rule"}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Trigger Type"}),_.jsxs("div",{className:"flex gap-2",children:[_.jsxs("button",{type:"button",onClick:()=>i({...e,trigger_type:"condition"}),className:`flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg border transition-colors ${e.trigger_type!=="schedule"?"bg-accent/10 border-accent text-accent":"bg-[#0a0e17] border-[#1e2a3a] text-slate-400 hover:text-slate-200"}`,children:[_.jsx(Mm,{size:16}),_.jsx("span",{children:"Condition"})]}),_.jsxs("button",{type:"button",onClick:()=>i({...e,trigger_type:"schedule"}),className:`flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg border transition-colors ${e.trigger_type==="schedule"?"bg-accent/10 border-accent text-accent":"bg-[#0a0e17] border-[#1e2a3a] text-slate-400 hover:text-slate-200"}`,children:[_.jsx(bd,{size:16}),_.jsx("span",{children:"Schedule"})]})]}),_.jsx("p",{className:"text-xs text-slate-600",children:e.trigger_type==="schedule"?"Send reports on a schedule (daily briefings, weekly digests)":"React to alert conditions (fires, outages, weather warnings)"})]}),e.trigger_type!=="schedule"&&_.jsxs("div",{className:"space-y-4 p-4 bg-[#0a0e17] rounded-lg border border-[#1e2a3a]",children:[_.jsxs("div",{className:"flex items-center gap-2 text-sm font-medium text-slate-300",children:[_.jsx(iu,{size:14}),"WHEN (Condition)"]}),_.jsx(Xie,{value:e.min_severity,onChange:N=>i({...e,min_severity:N})}),_.jsxs("div",{className:"space-y-2",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Alert Categories",_.jsx(Ha,{info:"Select which types of alerts trigger this rule. Leave all unchecked to match ALL categories."})]}),_.jsx("div",{className:"text-xs text-slate-500 mb-2",children:(((k=e.categories)==null?void 0:k.length)||0)===0?"All categories (none selected)":`${(E=e.categories)==null?void 0:E.length} selected`}),_.jsx("div",{className:"max-h-48 overflow-y-auto border border-[#1e2a3a] rounded-lg p-2 space-y-1",children:r.map(N=>{var z,$;return _.jsxs("label",{onClick:()=>S(N.id),className:"flex items-start gap-2 p-2 rounded hover:bg-[#1e2a3a]/50 cursor-pointer",children:[_.jsx("div",{className:`w-4 h-4 mt-0.5 rounded border flex items-center justify-center flex-shrink-0 ${(z=e.categories)!=null&&z.includes(N.id)?"bg-accent border-accent":"border-slate-600"}`,children:(($=e.categories)==null?void 0:$.includes(N.id))&&_.jsx(Qc,{size:12,className:"text-white"})}),_.jsxs("div",{className:"flex-1 min-w-0",children:[_.jsx("div",{className:"text-sm text-slate-200",children:N.name}),_.jsx("div",{className:"text-xs text-slate-500",children:N.description})]})]},N.id)})})]}),v&&Object.keys(v).length>0&&_.jsxs("div",{className:"space-y-2",children:[_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Data Sources"}),_.jsx("div",{className:"flex flex-wrap gap-2",children:I()})]})]}),e.trigger_type==="schedule"&&_.jsxs("div",{className:"space-y-4 p-4 bg-[#0a0e17] rounded-lg border border-[#1e2a3a]",children:[_.jsxs("div",{className:"flex items-center gap-2 text-sm font-medium text-slate-300",children:[_.jsx(Que,{size:14}),"WHEN (Schedule)"]}),_.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Frequency"}),_.jsx("select",{value:e.schedule_frequency||"daily",onChange:N=>i({...e,schedule_frequency:N.target.value}),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent",children:y.map(N=>_.jsx("option",{value:N.value,children:N.label},N.value))})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(aS,{label:"Time",value:e.schedule_time||"07:00",onChange:N=>i({...e,schedule_time:N})}),e.schedule_frequency==="twice_daily"&&_.jsx(aS,{label:"Second Time",value:e.schedule_time_2||"19:00",onChange:N=>i({...e,schedule_time_2:N})})]}),e.schedule_frequency==="weekly"&&_.jsxs("div",{className:"space-y-2",children:[_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Days"}),_.jsx("div",{className:"flex flex-wrap gap-2",children:b.map(N=>{var z;return _.jsx("button",{type:"button",onClick:()=>T(N),className:`px-3 py-1.5 rounded text-sm capitalize transition-colors ${(z=e.schedule_days)!=null&&z.includes(N)?"bg-accent text-white":"bg-[#1e2a3a] text-slate-400 hover:text-slate-200"}`,children:N.slice(0,3)},N)})})]}),_.jsxs("div",{className:"space-y-1",children:[_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Report Type"}),_.jsx("select",{value:e.message_type||"mesh_health_summary",onChange:N=>i({...e,message_type:N.target.value}),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent",children:x.map(N=>_.jsx("option",{value:N.value,children:N.label},N.value))}),_.jsx("p",{className:"text-xs text-slate-600",children:(D=x.find(N=>N.value===e.message_type))==null?void 0:D.description})]}),e.message_type==="custom"&&_.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Custom Message",_.jsx(Ha,{info:"Available tokens: {MESH_SCORE}, {NODE_COUNT}, {NODES_ONLINE}, {ACTIVE_ALERTS}, {KP}, {SFI}, {DATE}, {TIME}"})]}),_.jsx("textarea",{value:e.custom_message||"",onChange:N=>i({...e,custom_message:N.target.value}),rows:4,placeholder:"Good morning! Mesh health: {MESH_SCORE}/100 with {NODE_COUNT} nodes online.",className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent placeholder-slate-600"})]})]}),_.jsxs("div",{className:"space-y-4 p-4 bg-[#0a0e17] rounded-lg border border-[#1e2a3a]",children:[_.jsxs("div",{className:"flex items-center gap-2 text-sm font-medium text-slate-300",children:[_.jsx(p3,{size:14}),"SEND VIA"]}),_.jsxs("div",{className:"space-y-1",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Delivery Method",_.jsx(Ha,{info:"Where this notification gets delivered. Select (None) to save the rule without delivery - it will match conditions but won't send until you configure a delivery method."})]}),_.jsx("select",{value:e.delivery_type||"",onChange:N=>i({...e,delivery_type:N.target.value}),className:"w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent",children:m.map(N=>_.jsx("option",{value:N.value,children:N.label},N.value))}),_.jsx("p",{className:"text-xs text-slate-600",children:(j=m.find(N=>N.value===(e.delivery_type||"")))==null?void 0:j.description})]}),!e.delivery_type&&_.jsxs("div",{className:"flex items-start gap-2 p-3 bg-amber-500/10 border border-amber-500/20 rounded-lg",children:[_.jsx(xd,{size:16,className:"text-amber-400 mt-0.5 flex-shrink-0"}),_.jsx("div",{className:"text-sm text-amber-300",children:"Rule will log matches but not deliver until a delivery method is configured."})]}),e.delivery_type==="mesh_broadcast"&&_.jsxs(_.Fragment,{children:[_.jsx(UR,{label:"Broadcast Channel",value:e.broadcast_channel??0,onChange:N=>i({...e,broadcast_channel:N}),helper:"Select the mesh radio channel",mode:"single"}),_.jsx(Bx,{rule:e})]}),e.delivery_type==="mesh_dm"&&_.jsxs(_.Fragment,{children:[_.jsx(HR,{label:"Recipient Nodes",value:e.node_ids||[],onChange:N=>i({...e,node_ids:N}),helper:"Nodes that receive direct messages",valueType:"node_id_hex"}),_.jsx(Bx,{rule:e})]}),e.delivery_type==="email"&&_.jsxs("div",{className:"space-y-4",children:[_.jsx(_b,{label:"Recipients",value:e.recipients||[],onChange:N=>i({...e,recipients:N}),placeholder:"email@example.com",helper:"Email addresses to receive alerts"}),_.jsxs("details",{className:"group",children:[_.jsxs("summary",{className:"flex items-center gap-2 cursor-pointer text-sm text-slate-400 hover:text-slate-200",children:[_.jsx(_d,{size:14,className:"group-open:rotate-90 transition-transform"}),"SMTP Configuration"]}),_.jsxs("div",{className:"mt-4 space-y-4 pl-6 border-l border-[#1e2a3a]",children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(bl,{label:"SMTP Host",value:e.smtp_host||"",onChange:N=>i({...e,smtp_host:N}),placeholder:"smtp.gmail.com"}),_.jsx(iS,{label:"SMTP Port",value:e.smtp_port??587,onChange:N=>i({...e,smtp_port:N}),min:1,max:65535})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(bl,{label:"Username",value:e.smtp_user||"",onChange:N=>i({...e,smtp_user:N})}),_.jsx(bl,{label:"Password",value:e.smtp_password||"",onChange:N=>i({...e,smtp_password:N}),type:"password",info:"Gmail users: use an App Password from myaccount.google.com/apppasswords"})]}),_.jsx(cv,{label:"Use TLS",checked:e.smtp_tls??!0,onChange:N=>i({...e,smtp_tls:N})}),_.jsx(bl,{label:"From Address",value:e.from_address||"",onChange:N=>i({...e,from_address:N}),placeholder:"alerts@yourdomain.com"})]})]}),_.jsx(Bx,{rule:e})]}),e.delivery_type==="webhook"&&_.jsxs(_.Fragment,{children:[_.jsx(bl,{label:"Webhook URL",value:e.webhook_url||"",onChange:N=>i({...e,webhook_url:N}),placeholder:"https://discord.com/api/webhooks/...",helper:"POST alert as JSON",info:"Works with Discord webhooks, ntfy.sh, Slack, Home Assistant, Pushover, or any HTTP POST endpoint."}),_.jsx(Bx,{rule:e})]})]}),_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(iS,{label:"Cooldown (minutes)",value:e.cooldown_minutes??10,onChange:N=>i({...e,cooldown_minutes:N}),min:0,helper:"Min time between repeat sends",info:"Prevents alert spam. Same condition won't re-trigger this rule within this window."}),n&&_.jsx("div",{className:"flex items-end pb-1",children:_.jsx(cv,{label:"Override Quiet Hours",checked:e.override_quiet??!1,onChange:N=>i({...e,override_quiet:N}),helper:"Deliver during quiet hours"})})]}),h&&_.jsxs("div",{className:"flex items-center gap-4 text-xs text-slate-500",children:[_.jsxs("span",{children:["Last fired: ",vP(h.last_fired)]}),_.jsxs("span",{children:["Last tested: ",vP(h.last_test)]}),_.jsxs("span",{children:["Total fires: ",h.fire_count]})]}),e.trigger_type!=="schedule"&&_.jsxs("div",{className:"space-y-2",children:[_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Example Message"}),_.jsx("div",{className:"p-3 bg-[#1e2a3a]/50 rounded-lg border border-[#1e2a3a]",children:_.jsx("p",{className:"text-sm text-slate-300 font-mono",children:M()})}),_.jsx("p",{className:"text-xs text-slate-600",children:"This is an example of what this rule would send."})]})]})]})}const ret=[{key:"mesh_health",label:"Mesh Health",Icon:gv},{key:"weather",label:"Weather",Icon:nu},{key:"fire",label:"Fire",Icon:TS},{key:"rf_propagation",label:"RF Propagation",Icon:Za},{key:"roads",label:"Roads",Icon:SS},{key:"avalanche",label:"Avalanche",Icon:cce},{key:"seismic",label:"Seismic",Icon:AS},{key:"tracking",label:"Tracking",Icon:KE}],XU=["digest","mesh_broadcast","mesh_dm","email","webhook"],net=["routine","priority","immediate"];function iet({toggles:e,onChange:t}){const[r,n]=U.useState(null),i=(a,o)=>t({...e,[a]:{...e[a]||{},name:a,...o}});return _.jsxs("div",{className:"space-y-3 mb-8",children:[_.jsxs("div",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Master Toggles",_.jsx(Ha,{info:"Per-family notification policy: enable a family, set its severity threshold, choose which channels fire at each severity, and scope to regions (PagerDuty/Grafana-style)."})]}),_.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-3",children:ret.map(({key:a,label:o,Icon:s})=>{const l=e[a]||{},u=r===a,c=Object.values(l.severity_channels||{}).reduce((h,d)=>h+((d==null?void 0:d.length)||0),0),f=(l.regions||[]).length;return _.jsxs("div",{className:"border border-[#1e2a3a] rounded-lg p-3",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("button",{type:"button",onClick:()=>n(u?null:a),className:"flex items-center gap-2 text-sm text-slate-200",children:[_.jsx(s,{size:15})," ",o,u?_.jsx(mv,{size:14}):_.jsx(_d,{size:14})]}),_.jsx(cv,{label:"",checked:!!l.enabled,onChange:h=>i(a,{enabled:h})})]}),!u&&_.jsx("div",{className:"text-xs text-slate-600 mt-1",children:l.enabled?`${f||"all"} region${f===1?"":"s"}, ${c} channel${c===1?"":"s"} at ${l.min_severity||"priority"}+`:"OFF"}),u&&_.jsxs("div",{className:`mt-3 space-y-3 ${l.enabled?"":"opacity-40 pointer-events-none select-none"}`,children:[_.jsx(Xie,{value:l.min_severity||"priority",onChange:h=>i(a,{min_severity:h})}),_.jsx("div",{className:"text-xs text-slate-500",children:"Severity → channels"}),_.jsxs("table",{className:"text-xs w-full",children:[_.jsx("thead",{children:_.jsxs("tr",{children:[_.jsx("th",{}),XU.map(h=>_.jsx("th",{className:"text-slate-500 font-normal px-1",children:h.replace("_"," ")},h))]})}),_.jsx("tbody",{children:net.map(h=>_.jsxs("tr",{children:[_.jsx("td",{className:"text-slate-400 pr-2",children:h}),XU.map(d=>{var g;const v=(((g=l.severity_channels)==null?void 0:g[h])||[]).includes(d);return _.jsx("td",{className:"text-center",children:_.jsx("input",{type:"checkbox",checked:v,onChange:m=>{const y={...l.severity_channels||{}},x=new Set(y[h]||[]);m.target.checked?x.add(d):x.delete(d),y[h]=Array.from(x),i(a,{severity_channels:y})}})},d)})]},h))})]}),_.jsx(_b,{label:"Regions (empty = all)",value:l.regions||[],onChange:h=>i(a,{regions:h}),placeholder:"Add region..."}),_.jsx(cv,{label:"Quiet-hours override (immediate only)",checked:!!l.quiet_hours_override,onChange:h=>i(a,{quiet_hours_override:h})}),_.jsx("div",{className:"text-xs text-slate-500 pt-1",children:"Channel config"}),_.jsx(iS,{label:"Broadcast channel",value:l.broadcast_channel??0,onChange:h=>i(a,{broadcast_channel:h})}),_.jsx(_b,{label:"DM node IDs",value:l.node_ids||[],onChange:h=>i(a,{node_ids:h}),placeholder:"!nodeid"}),_.jsx(_b,{label:"Email recipients",value:l.recipients||[],onChange:h=>i(a,{recipients:h}),placeholder:"ops@example.com"}),_.jsx(bl,{label:"SMTP host",value:l.smtp_host||"",onChange:h=>i(a,{smtp_host:h}),placeholder:"smtp.example.com"}),_.jsx(iS,{label:"SMTP port",value:l.smtp_port??587,onChange:h=>i(a,{smtp_port:h})}),_.jsx(bl,{label:"Webhook URL",value:l.webhook_url||"",onChange:h=>i(a,{webhook_url:h}),placeholder:"https://..."})]})]},a)})})]})}function aet(){var $,Z,F;const[e,t]=U.useState(null),[r,n]=U.useState(null),[i,a]=U.useState([]),[o,s]=U.useState(!0),[l,u]=U.useState(!1),[c,f]=U.useState(null),[h,d]=U.useState(null),[v,g]=U.useState(null),[m,y]=U.useState({open:!1,ruleIndex:-1,loading:!1,action:""}),[x,b]=U.useState(!1),[S,T]=U.useState(!1),C=U.useCallback(async()=>{try{const[G,V]=await Promise.all([fetch("/api/config/notifications"),fetch("/api/notifications/categories")]);if(!G.ok)throw new Error("Failed to fetch notifications config");const H=await G.json(),Y=await V.json();t(H),n(JSON.parse(JSON.stringify(H))),a(Y),T(!1),f(null)}catch(G){f(G instanceof Error?G.message:"Unknown error")}finally{s(!1)}},[]);U.useEffect(()=>{document.title="Notifications - MeshAI",C()},[C]),U.useEffect(()=>{e&&r&&T(JSON.stringify(e)!==JSON.stringify(r))},[e,r]);const M=async()=>{if(e){u(!0),f(null),d(null);try{const G=await fetch("/api/config/notifications",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),V=await G.json();if(!G.ok)throw new Error(V.detail||"Save failed");d("Notifications config saved successfully"),n(JSON.parse(JSON.stringify(e))),T(!1),setTimeout(()=>d(null),3e3)}catch(G){f(G instanceof Error?G.message:"Save failed")}finally{u(!1)}}},P=()=>{r&&(t(JSON.parse(JSON.stringify(r))),T(!1))},I=()=>({name:"",enabled:!0,trigger_type:"condition",categories:[],min_severity:"routine",schedule_frequency:"daily",schedule_time:"07:00",schedule_time_2:"19:00",schedule_days:["monday"],message_type:"mesh_health_summary",custom_message:"",delivery_type:"",broadcast_channel:0,node_ids:[],smtp_host:"",smtp_port:587,smtp_user:"",smtp_password:"",smtp_tls:!0,from_address:"",recipients:[],webhook_url:"",webhook_headers:{},cooldown_minutes:10,override_quiet:!1}),k=()=>{e&&t({...e,rules:[...e.rules||[],I()]})},E=G=>{if(!e)return;const V=YU.find(H=>H.id===G);V&&(t({...e,rules:[...e.rules||[],{...V.rule}]}),b(!1))},D=G=>{if(!e)return;const V=e.rules[G],H={...JSON.parse(JSON.stringify(V)),name:`${V.name} (copy)`},Y=[...e.rules];Y.splice(G+1,0,H),t({...e,rules:Y})},j=async G=>{y({open:!0,ruleIndex:G,loading:!0,action:""});try{const H=await(await fetch(`/api/notifications/rules/${G}/test`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"preview"})})).json();g(H),y(Y=>({...Y,loading:!1}))}catch{g({success:!1,message:"Failed to get preview"}),y(V=>({...V,loading:!1}))}},N=async G=>{const V=m.ruleIndex;y(H=>({...H,loading:!0,action:G}));try{const Y=await(await fetch(`/api/notifications/rules/${V}/test`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:G})})).json();g(Y),y(K=>({...K,loading:!1}))}catch{g({success:!1,message:`Failed to ${G}`}),y(H=>({...H,loading:!1}))}},z=()=>{y({open:!1,ruleIndex:-1,loading:!1,action:""}),g(null)};return o?_.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsx("div",{className:"text-slate-400",children:"Loading notifications config..."})}):e?_.jsxs("div",{className:"max-w-4xl mx-auto space-y-6",children:[m.open&&_.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/50",children:_.jsxs("div",{className:"bg-[#1a2332] border border-[#2a3a4a] rounded-lg shadow-xl max-w-2xl w-full mx-4 max-h-[85vh] overflow-auto",children:[_.jsxs("div",{className:"p-4 border-b border-[#2a3a4a] flex items-center justify-between sticky top-0 bg-[#1a2332]",children:[_.jsx("h3",{className:"text-lg font-semibold",children:"Test Notification Rule"}),_.jsx("button",{onClick:z,className:"text-slate-500 hover:text-slate-300",children:_.jsx(au,{size:20})})]}),_.jsx("div",{className:"p-4 space-y-4",children:m.loading?_.jsxs("div",{className:"flex items-center justify-center py-8",children:[_.jsx(Am,{size:20,className:"animate-spin text-slate-400 mr-2"}),_.jsx("div",{className:"text-slate-400",children:m.action?`${m.action.replace("_"," ").replace("send ","Sending ")}...`:"Loading current data..."})]}):v?_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"space-y-2",children:[_.jsx("div",{className:"text-sm font-medium text-slate-400 uppercase tracking-wide",children:"Current Data"}),v.live_data_summary&&v.live_data_summary.length>0?_.jsx("div",{className:"p-3 bg-slate-800/50 rounded space-y-1",children:v.live_data_summary.map((G,V)=>_.jsx("div",{className:`text-sm font-mono ${G.startsWith("[!]")?"text-amber-400":""}`,children:G},V))}):_.jsx("div",{className:"p-3 bg-slate-800/50 rounded text-sm text-slate-500",children:"No live data available for this rule's categories"})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("div",{className:"text-sm font-medium text-slate-400 uppercase tracking-wide",children:"Rule Matching"}),_.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[v.conditions_matched&&v.conditions_matched>0?_.jsxs("span",{className:"px-2 py-1 bg-green-500/20 text-green-400 rounded text-sm",children:[v.conditions_matched," condition",v.conditions_matched!==1?"s":""," match - this rule WOULD fire"]}):_.jsx("span",{className:"px-2 py-1 bg-slate-700 text-slate-400 rounded text-sm",children:"No conditions trigger this rule right now"}),v.conditions_below_threshold&&v.conditions_below_threshold>0&&_.jsxs("span",{className:"px-2 py-1 bg-yellow-500/20 text-yellow-400 rounded text-sm",children:[v.conditions_below_threshold," below threshold"]})]}),v.conditions_below_threshold&&v.conditions_below_threshold>0&&_.jsxs("div",{className:"p-3 bg-yellow-500/10 border border-yellow-500/30 rounded text-sm space-y-2",children:[_.jsx("div",{className:"text-yellow-300",children:v.below_threshold_summary}),v.below_threshold_events&&v.below_threshold_events.length>0&&_.jsx("div",{className:"space-y-1 text-yellow-200/80",children:v.below_threshold_events.slice(0,3).map((G,V)=>_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx("span",{className:"text-xs px-1.5 py-0.5 bg-yellow-500/20 rounded",children:G.severity}),_.jsx("span",{children:G.headline})]},V))}),v.suggestion&&_.jsxs("div",{className:"text-yellow-400 text-xs mt-2",children:["Tip: ",v.suggestion]})]})]}),_.jsxs("div",{className:"space-y-2",children:[_.jsx("div",{className:"text-sm font-medium text-slate-400 uppercase tracking-wide",children:v.is_example?"Example Messages":"Messages That Would Fire"}),($=v.preview_messages)==null?void 0:$.map((G,V)=>_.jsx("div",{className:"p-3 bg-slate-800 rounded text-sm font-mono break-words",children:G},V))]}),v.delivered!==void 0&&v.delivery_result&&_.jsx("div",{className:`p-3 rounded text-sm ${v.delivered?"bg-green-500/10 border border-green-500/30 text-green-400":"bg-red-500/10 border border-red-500/30 text-red-400"}`,children:_.jsxs("div",{className:"flex items-start gap-2",children:[v.delivered?_.jsx(Qc,{size:16,className:"mt-0.5"}):_.jsx(au,{size:16,className:"mt-0.5"}),_.jsxs("div",{children:[_.jsx("div",{children:v.delivery_result}),v.delivery_error&&_.jsx("div",{className:"mt-1 text-red-300",children:v.delivery_error})]})]})}),v.message&&!v.preview_messages&&_.jsx("div",{className:`p-3 rounded text-sm ${v.success?"bg-green-500/10 text-green-400":"bg-red-500/10 text-red-400"}`,children:v.message})]}):null}),_.jsxs("div",{className:"p-4 border-t border-[#2a3a4a] flex justify-between sticky bottom-0 bg-[#1a2332]",children:[_.jsx("button",{onClick:z,className:"px-4 py-2 text-slate-400 hover:text-slate-200",children:"Close"}),v&&!v.delivered&&_.jsx("div",{className:"flex gap-2",children:v.delivery_method?_.jsxs(_.Fragment,{children:[v.live_data_summary&&v.live_data_summary.length>0&&_.jsx("button",{onClick:()=>N("send_status"),disabled:m.loading,className:"px-3 py-2 bg-slate-700 hover:bg-slate-600 rounded text-sm disabled:opacity-50",title:"Send current conditions summary",children:"Send Current Conditions"}),_.jsx("button",{onClick:()=>N("send_test"),disabled:m.loading,className:"px-3 py-2 bg-slate-700 hover:bg-slate-600 rounded text-sm disabled:opacity-50",title:"Send example alert message",children:"Send Example Alert"}),v.can_send_live&&_.jsx("button",{onClick:()=>N("send_live"),disabled:m.loading,className:"px-3 py-2 bg-accent hover:bg-accent/80 rounded text-sm disabled:opacity-50",title:"Send actual live alert",children:"Send Live Alert"})]}):_.jsx("span",{className:"px-3 py-2 text-amber-400 text-sm",children:"Configure a delivery method to send test messages"})})]})]})}),_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsx("div",{children:_.jsx("p",{className:"text-sm text-slate-500",children:"Alert delivery and scheduled reports. Rules define what triggers a notification and where it gets sent."})}),_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx("button",{onClick:C,className:"p-2 text-slate-400 hover:text-slate-200 hover:bg-bg-hover rounded transition-colors",title:"Refresh",children:_.jsx(Am,{size:18})}),_.jsxs("button",{onClick:P,disabled:!S,className:"flex items-center gap-2 px-3 py-2 text-slate-400 hover:text-slate-200 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",children:[_.jsx(QE,{size:16}),"Discard"]}),_.jsxs("button",{onClick:M,disabled:l||!S,className:"flex items-center gap-2 px-4 py-2 bg-accent hover:bg-accent/80 disabled:bg-slate-700 disabled:cursor-not-allowed rounded text-white transition-colors",children:[_.jsx(JE,{size:16}),l?"Saving...":"Save"]})]})]}),c&&_.jsx("div",{className:"p-3 rounded-lg text-sm bg-red-500/10 text-red-400 border border-red-500/20",children:c}),h&&_.jsxs("div",{className:"p-3 rounded-lg text-sm bg-green-500/10 text-green-400 border border-green-500/20",children:[_.jsx(Qc,{size:14,className:"inline mr-2"}),h]}),_.jsxs("div",{className:"bg-bg-card border border-border rounded-lg p-6 space-y-6",children:[_.jsx(cv,{label:"Enable Notifications",checked:e.enabled,onChange:G=>t({...e,enabled:G}),helper:"Master switch for all notification delivery",info:"When disabled, no alerts or scheduled messages will be delivered. Alerts still get recorded to history."}),e.enabled&&_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"space-y-3 p-4 bg-[#0a0e17] rounded-lg border border-[#1e2a3a]",children:[_.jsxs("div",{className:"flex items-center gap-2",children:[_.jsx(lce,{size:14,className:"text-slate-400"}),_.jsx("label",{className:"text-xs text-slate-500 uppercase tracking-wide",children:"Quiet Hours"})]}),_.jsx(cv,{label:"Enable Quiet Hours",checked:e.quiet_hours_enabled??!0,onChange:G=>t({...e,quiet_hours_enabled:G}),helper:"Suppress non-emergency alerts during sleeping hours",info:"When enabled, ROUTINE alerts are suppressed during quiet hours. PRIORITY and IMMEDIATE always deliver."}),e.quiet_hours_enabled&&_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[_.jsx(aS,{label:"Start Time",value:e.quiet_hours_start||"22:00",onChange:G=>t({...e,quiet_hours_start:G}),helper:"When quiet hours begin"}),_.jsx(aS,{label:"End Time",value:e.quiet_hours_end||"06:00",onChange:G=>t({...e,quiet_hours_end:G}),helper:"When quiet hours end"})]}),_.jsx("p",{className:"text-xs text-slate-600",children:'Emergency alerts and rules with "Override Quiet Hours" enabled always deliver.'})]})]}),e.toggles&&_.jsx(iet,{toggles:e.toggles,onChange:G=>t({...e,toggles:G})}),_.jsxs("div",{className:"space-y-3",children:[_.jsxs("div",{className:"flex items-center justify-between",children:[_.jsxs("label",{className:"flex items-center text-xs text-slate-500 uppercase tracking-wide",children:["Notification Rules",_.jsx(Ha,{info:"Each rule is self-contained: define what triggers it (condition or schedule), where to send it (mesh, email, webhook), and behavior settings."})]}),_.jsxs("span",{className:"text-xs text-slate-500",children:[((Z=e.rules)==null?void 0:Z.length)||0," rule",(((F=e.rules)==null?void 0:F.length)||0)!==1?"s":""]})]}),(e.rules||[]).map((G,V)=>_.jsx(tet,{rule:G,ruleIndex:V,categories:i,quietHoursEnabled:e.quiet_hours_enabled??!0,onChange:H=>{const Y=[...e.rules||[]];Y[V]=H,t({...e,rules:Y})},onDelete:()=>{confirm(`Delete rule "${G.name||"New Rule"}"?`)&&t({...e,rules:(e.rules||[]).filter((H,Y)=>Y!==V)})},onDuplicate:()=>D(V),onTest:()=>j(V)},V)),_.jsxs("div",{className:"flex gap-2",children:[_.jsxs("button",{onClick:k,className:"flex-1 py-3 border border-dashed border-[#1e2a3a] rounded-lg text-slate-500 hover:text-slate-300 hover:border-accent flex items-center justify-center gap-2 transition-colors",children:[_.jsx(MS,{size:16})," Add Rule"]}),_.jsxs("div",{className:"relative",children:[_.jsxs("button",{onClick:()=>b(!x),className:"py-3 px-4 border border-dashed border-[#1e2a3a] rounded-lg text-slate-500 hover:text-slate-300 hover:border-accent flex items-center gap-2 transition-colors",children:[_.jsx(PZ,{size:16})," Add from Template"]}),x&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"fixed inset-0 z-40",onClick:()=>b(!1)}),_.jsxs("div",{className:"absolute right-0 top-full mt-2 z-50 w-80 bg-[#1a2332] border border-[#2a3a4a] rounded-lg shadow-xl overflow-hidden",children:[_.jsx("div",{className:"p-2 border-b border-[#2a3a4a] text-xs text-slate-500 uppercase",children:"Rule Templates"}),YU.map(G=>_.jsxs("button",{onClick:()=>E(G.id),className:"w-full p-3 text-left hover:bg-[#2a3a4a] transition-colors",children:[_.jsx("div",{className:"font-medium text-slate-200",children:G.name}),_.jsx("div",{className:"text-xs text-slate-500 mt-0.5",children:G.description})]},G.id))]})]})]})]})]})]})]})]}):_.jsx("div",{className:"flex items-center justify-center h-64",children:_.jsx("div",{className:"text-red-400",children:"Failed to load notifications config"})})}const qU=[{id:"stream-gauges",label:"Stream Gauges",icon:AZ},{id:"wildfire",label:"Wildfire",icon:TS},{id:"firms",label:"Satellite Fire Detection (FIRMS)",icon:PS},{id:"weather-alerts",label:"Weather Alerts",icon:tce},{id:"solar",label:"Solar & Geomagnetic",icon:OZ},{id:"ducting",label:"Tropospheric Ducting",icon:Za},{id:"avalanche",label:"Avalanche Danger",icon:AS},{id:"traffic",label:"Traffic Flow",icon:SS},{id:"roads-511",label:"Road Conditions (511)",icon:TZ},{id:"mesh-health",label:"Mesh Health",icon:gv},{id:"notifications",label:"Notifications",icon:Cm},{id:"commands",label:"Commands",icon:EZ},{id:"api",label:"API Reference",icon:rce}];function ir({color:e}){const t={green:"bg-green-500",yellow:"bg-yellow-500",orange:"bg-orange-500",red:"bg-red-500",black:"bg-slate-800 border border-slate-600"};return _.jsx("span",{className:`inline-block w-3 h-3 rounded-full ${t[e]}`})}function jt({headers:e,rows:t}){return _.jsx("div",{className:"overflow-x-auto my-4",children:_.jsxs("table",{className:"w-full text-sm",children:[_.jsx("thead",{children:_.jsx("tr",{className:"bg-[#1a2332] border-b border-[#2a3a4a]",children:e.map((r,n)=>_.jsx("th",{className:"px-4 py-2 text-left text-slate-400 font-medium",children:r},n))})}),_.jsx("tbody",{children:t.map((r,n)=>_.jsx("tr",{className:`border-b border-[#1e2a3a] ${n%2===0?"bg-[#0d1219]":"bg-[#0a0e17]"}`,children:r.map((i,a)=>_.jsx("td",{className:"px-4 py-2 text-slate-300",children:i},a))},n))})]})})}function Rt({href:e,children:t}){return _.jsxs("a",{href:e,target:"_blank",rel:"noopener noreferrer",className:"text-accent hover:underline inline-flex items-center gap-1",children:[t," ",_.jsx(wd,{size:12})]})}function Te({children:e}){return _.jsx("h3",{className:"text-lg font-semibold text-slate-200 mt-6 mb-3",children:e})}function fl({children:e}){return _.jsx("h4",{className:"text-base font-medium text-slate-300 mt-4 mb-2",children:e})}function $e({children:e}){return _.jsx("code",{className:"font-mono text-accent bg-[#1a2332] px-1 rounded",children:e})}function vi({id:e,title:t,children:r}){return _.jsxs("section",{id:e,className:"mb-12 scroll-mt-6",children:[_.jsx("h2",{className:"text-2xl font-bold text-slate-100 mb-4 pb-2 border-b border-[#2a3a4a]",children:t}),_.jsx("div",{className:"text-slate-300 leading-relaxed space-y-4",children:r})]})}function oet(){const e=pv(),[t,r]=U.useState(""),[n,i]=U.useState("stream-gauges"),a=U.useRef(null);U.useEffect(()=>{const l=e.hash.replace("#","");if(l&&qU.find(u=>u.id===l)){i(l);const u=document.getElementById(l);u&&u.scrollIntoView({behavior:"smooth"})}},[e.hash]);const o=qU.filter(l=>l.label.toLowerCase().includes(t.toLowerCase())),s=l=>{i(l);const u=document.getElementById(l);u&&u.scrollIntoView({behavior:"smooth"}),window.history.replaceState(null,"",`#${l}`)};return _.jsxs("div",{className:"flex h-full -m-6",children:[_.jsxs("aside",{className:"w-64 flex-shrink-0 bg-bg-card border-r border-border overflow-y-auto",children:[_.jsx("div",{className:"p-4 border-b border-border",children:_.jsxs("div",{className:"relative",children:[_.jsx(eD,{size:16,className:"absolute left-3 top-1/2 -translate-y-1/2 text-slate-500"}),_.jsx("input",{type:"text",value:t,onChange:l=>r(l.target.value),placeholder:"Search topics...",className:"w-full pl-9 pr-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 focus:outline-none focus:border-accent placeholder-slate-600"})]})}),_.jsx("nav",{className:"py-2",children:o.map(l=>{const u=l.icon,c=n===l.id;return _.jsxs("button",{onClick:()=>s(l.id),className:`w-full flex items-center gap-3 px-4 py-2.5 text-sm text-left transition-colors ${c?"text-accent bg-accent/10 border-l-2 border-accent":"text-slate-400 hover:text-slate-200 hover:bg-bg-hover border-l-2 border-transparent"}`,children:[_.jsx(u,{size:16}),l.label]},l.id)})})]}),_.jsx("div",{ref:a,className:"flex-1 overflow-y-auto p-6",children:_.jsxs("div",{className:"max-w-4xl",children:[_.jsx("p",{className:"text-slate-400 mb-8",children:"Everything you need to understand and configure MeshAI's monitoring and alerting systems."}),_.jsxs(vi,{id:"stream-gauges",title:"Stream Gauges",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"MeshAI watches river and stream levels at gauges you configure. Each gauge reports two things:"}),_.jsxs("p",{children:[_.jsx("strong",{children:"Water Level (Gage Height)"}),` — how high the water is, measured in feet. Important: this is NOT the depth of the river. It's the height above a fixed measuring point that's different at every gauge. A reading of "10 feet" at one gauge means something completely different than "10 feet" at another. You can only compare readings from the SAME gauge over time.`]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Flow (Discharge)"}),` — how much water is moving past the gauge, in cubic feet per second (CFS). Think of it as the river's "throughput." For scale:`]}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsx("li",{children:"A small creek: 50-200 CFS"}),_.jsx("li",{children:"A mid-size river: 1,000-5,000 CFS"}),_.jsx("li",{children:"A big river in spring runoff: 10,000+ CFS"})]}),_.jsx(Te,{children:"When Does It Flood?"}),_.jsxs("p",{children:["Flood levels are set by the ",_.jsx("strong",{children:"National Weather Service"}),', not USGS. NWS looks at each specific gauge location and decides "at what water level does the road flood? At what level do buildings get water?" Those levels are different everywhere.']}),_.jsxs("p",{children:[_.jsx("strong",{children:"Action Stage"})," — water is rising, time to start paying attention. Usually still inside the riverbanks."]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Minor Flood"})," — low-lying roads start getting water on them. NWS issues a Flood Advisory."]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Moderate Flood"})," — water in buildings near the river. Some people need to evacuate. NWS issues a Flood Warning."]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Major Flood"})," — widespread flooding. Many people evacuating. Serious property damage."]}),_.jsx("p",{children:"MeshAI automatically looks up the flood levels for your gauge from NWS when you add a site. Some remote gauges don't have flood levels assigned — for those, you set them manually if you know what water levels cause problems in your area."}),_.jsx(Te,{children:"Low Water / Drought"}),_.jsx("p",{children:`There's no official "drought stage" for most gauges. If you need to monitor low water (irrigation, fish habitat), set a manual low-water threshold based on what you know about your local river.`}),_.jsx(Te,{children:"Setting It Up"}),_.jsxs("ol",{className:"list-decimal list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:["Find your gauge at ",_.jsx(Rt,{href:"https://waterdata.usgs.gov/nwis",children:"waterdata.usgs.gov/nwis"})]}),_.jsxs("li",{children:["Copy the site number (like ",_.jsx($e,{children:"13090500"}),")"]}),_.jsx("li",{children:"Add it in Config → Environmental → USGS"}),_.jsx("li",{children:"MeshAI auto-fills the gauge name and flood levels from NWS"})]}),_.jsx("p",{children:"If NWS flood levels don't populate, your gauge may not have them. Set manual thresholds if you know your local conditions."}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://waterdata.usgs.gov/nwis",children:"USGS Water Data"})," — find gauges near you"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://water.noaa.gov",children:"NWS Water Prediction Service"})," — flood forecasts and thresholds"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.usgs.gov/special-topics/water-science-school/science/how-streamflow-measured",children:"Understanding Streamflow"})," — USGS explainer"]})]})]}),_.jsxs(vi,{id:"wildfire",title:"Wildfire",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"MeshAI tracks active wildfire perimeters from the National Interagency Fire Center (NIFC). For each fire, you see the name, size, how much is contained, and how far it is from your mesh nodes."}),_.jsx(Te,{children:"Fire Size — How Big Is It?"}),_.jsx(jt,{headers:["Size","What That Means"],rows:[["10 acres","Small fire. Usually handled quickly by initial crews."],["100 acres","Notable fire. Active firefighting effort."],["1,000 acres","Large fire. Major resources being deployed."],["10,000+ acres","Very large fire. Multiple teams, aircraft, heavy equipment."],["100,000+ acres","Mega-fire. These make the national news."]]}),_.jsx("p",{children:"For reference, 1,000 acres is about 1.5 square miles."}),_.jsx(Te,{children:"Containment — Is It Under Control?"}),_.jsx("p",{children:"Containment means the percentage of the fire's edge where firefighters have built a control line (a cleared strip to stop the fire from spreading further). It does NOT mean the fire is out inside that line."}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx("strong",{children:"0-30%"})," — Essentially uncontrolled. The fire goes where it wants."]}),_.jsxs("li",{children:[_.jsx("strong",{children:"50%"})," — Good progress, but half the edge can still grow."]}),_.jsxs("li",{children:[_.jsx("strong",{children:"80%+"})," — Well controlled. Major growth unlikely."]}),_.jsxs("li",{children:[_.jsx("strong",{children:"100%"}),' — The edge is fully controlled. But the fire may STILL be actively burning inside. "100% contained" does NOT mean "out."']})]}),_.jsx(Te,{children:"How Far Away Should I Worry?"}),_.jsx(jt,{headers:["Distance","What To Do"],rows:[[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"red"})," Under 5 km (3 miles)"]}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Immediate threat."})," This is evacuation-order range. Embers can fly this far in wind."]})],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"orange"})," 5-15 km (3-10 miles)"]}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Prepare."})," The fire could reach you in hours under bad conditions. Have a plan."]})],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"yellow"})," 15-30 km (10-20 miles)"]}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Watch."})," Smoke is likely. Wind shifts could change things fast."]})],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"green"})," Over 30 km (20 miles)"]}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Awareness."})," Keep an eye on it, but no immediate threat."]})]]}),_.jsx("p",{children:"How fast can a fire travel? In grass with wind: up to 14 mph. In heavy timber: 1-6 mph. A fire 10 miles away could theoretically reach you in 1-2 hours under worst-case conditions, but typical spread is much slower."}),_.jsx(Te,{children:"Which Matters More — Size or Distance?"}),_.jsxs("p",{children:[_.jsx("strong",{children:"Distance is the immediate concern."})," A small uncontained fire 10 km away is more dangerous right now than a huge fire 50 km away. But big fires have more energy and can grow fast under wind shifts — keep watching them."]}),_.jsx(Te,{children:"Setting It Up"}),_.jsxs("p",{children:["Just configure your state code (like ",_.jsx($e,{children:"US-ID"})," for Idaho) in Config → Environmental → Fires. MeshAI polls NIFC every 10 minutes for active fires in that state and computes the distance to your mesh nodes automatically."]}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://inciweb.nwcg.gov",children:"InciWeb"})," — detailed incident information"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://data-nifc.opendata.arcgis.com",children:"NIFC Fire Map"})," — raw perimeter data"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.ready.gov/wildfires",children:"Ready.gov Wildfires"})," — preparedness guide"]})]})]}),_.jsxs(vi,{id:"firms",title:"Satellite Fire Detection (FIRMS)",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:`NASA's VIIRS satellites orbit the Earth and look for heat signatures on the ground. When they see something hot — a fire, a factory, a sunlit building — they flag it as a "hotspot." MeshAI checks these detections for your area.`}),_.jsxs("p",{children:[_.jsx("strong",{children:"Why this matters"}),": satellite hotspots show up ",_.jsx("strong",{children:"hours before"})," official fire perimeters are mapped. If a new fire starts near your mesh, the satellite might see it before anyone on the ground reports it."]}),_.jsx(Te,{children:"Confidence — Is It Really a Fire?"}),_.jsx("p",{children:"Each detection gets a confidence rating:"}),_.jsx(jt,{headers:["Confidence","What It Means"],rows:[["High","Almost certainly a real fire. Strong heat signature."],["Nominal","Probably a real fire. Most actual fires get this rating."],["Low","Maybe a fire, maybe not. Could be a hot roof, sun reflecting off water, a factory, or a gas flare. Lots of false alarms."]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Recommendation"}),`: Set the filter to "Nominal + High." If you include "Low" you'll get alerts for every hot parking lot on a summer day.`]}),_.jsx(Te,{children:"FRP — How Intense Is It?"}),_.jsx("p",{children:'FRP (Fire Radiative Power) measures the heat output in megawatts. Think of it as "how hot is this thing":'}),_.jsx(jt,{headers:["FRP","What It Probably Is"],rows:[["Under 5 MW","Hot surface, small agricultural burn, gas flare, or warm ground"],["5-50 MW","An actual fire — brush fire, grass fire, typical wildfire"],["50-300 MW","Intense fire — trees fully burning, active fire front"],["Over 300 MW","Extreme fire — major wildfire in full force"]]}),_.jsx("p",{children:"Setting the minimum FRP to 5 MW filters out most industrial and agricultural false alarms."}),_.jsx(Te,{children:"New Ignition Detection"}),_.jsxs("p",{children:["MeshAI cross-references satellite hotspots against known NIFC fire perimeters. If a hotspot is NOT near any known fire, it gets flagged as a ",_.jsx("strong",{children:"potential new ignition"})," — maybe a new fire just started. These get elevated priority regardless of confidence level."]}),_.jsx(Te,{children:"Timing"}),_.jsxs("p",{children:["Satellite data arrives ",_.jsx("strong",{children:"1-3 hours"})," after the satellite passes overhead. Each location gets observed about ",_.jsx("strong",{children:"6 times per day"}),` across all satellites, so there are multi-hour gaps. This is not real-time — it's "pretty recent."`]}),_.jsx(Te,{children:"Getting an API Key"}),_.jsxs("ol",{className:"list-decimal list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:["Go to ",_.jsx(Rt,{href:"https://firms.modaps.eosdis.nasa.gov/api/area/",children:"FIRMS API page"})]}),_.jsx("li",{children:'Click "Get MAP_KEY"'}),_.jsx("li",{children:"Register for a free Earthdata account"}),_.jsx("li",{children:"Your key arrives by email"}),_.jsx("li",{children:"Enter it in Config → Environmental → FIRMS"})]}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://firms.modaps.eosdis.nasa.gov",children:"FIRMS Fire Map"})," — see hotspots on a map"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://earthdata.nasa.gov/data/tools/firms/faq",children:"FIRMS FAQ"})," — how it works"]})]})]}),_.jsxs(vi,{id:"weather-alerts",title:"Weather Alerts",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"MeshAI watches for NWS (National Weather Service) alerts affecting your area — warnings, watches, and advisories."}),_.jsx(Te,{children:"Alert Severity — How Serious Is It?"}),_.jsx(jt,{headers:["Severity","What It Means","Example"],rows:[["Extreme","Life-threatening. The most serious events.","Tornado Emergency, Hurricane Warning, Tsunami Warning"],["Severe","Dangerous. Take protective action.","Tornado Warning, Flash Flood Warning, Blizzard Warning, Red Flag Warning"],["Moderate","Be prepared. Could become dangerous.","Winter Weather Advisory, Wind Advisory, Flood Watch, Heat Advisory"],["Minor","Good to know. Probably won't hurt anyone.","Special Weather Statement, Air Quality Alert"]]}),_.jsx(Te,{children:"When Should I Act? (Urgency)"}),_.jsx(jt,{headers:["Urgency","What It Means"],rows:[["Immediate","Do something NOW"],["Expected","Do something within the hour"],["Future","Coming in the next several hours"],["Past","It's over — NWS is clearing the alert"]]}),_.jsx(Te,{children:"How Sure Are They? (Certainty)"}),_.jsx(jt,{headers:["Certainty","What It Means"],rows:[["Observed","It's happening right now. Verified."],["Likely","More than 50% chance"],["Possible","Could happen, but less than 50%"],["Unlikely","Probably won't, but mentioned for awareness"]]}),_.jsx(Te,{children:"These Are Separate Scales"}),_.jsx("p",{children:'A single alert has all three. A hurricane warning for next week is "Severe + Future + Likely." A tornado spotted on the ground is "Extreme + Immediate + Observed." An air quality advisory is "Minor + Expected + Possible."'}),_.jsx(Te,{children:"What Minimum Severity Should I Set?"}),_.jsx(jt,{headers:["Setting","What You Get","What You Miss"],rows:[["Minor","Everything — high volume","Nothing"],[_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Moderate"})," ✓"]}),"Watches, Advisories, and Warnings","Special Weather Statements"],["Severe","Only Warnings — things happening NOW","Watches (which give you hours of advance warning)"],["Extreme","Only the rarest events","Most Tornado and Severe Thunderstorm Warnings"]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Moderate is recommended."})," It catches Watches (advance warning that conditions may worsen) and Advisories (conditions exist but aren't severe) while filtering out the informational stuff."]}),_.jsx(Te,{children:"Finding Your NWS Zone"}),_.jsxs("ol",{className:"list-decimal list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:["Go to ",_.jsx(Rt,{href:"https://www.weather.gov",children:"weather.gov"})]}),_.jsx("li",{children:"Enter your location"}),_.jsxs("li",{children:["Find your zone code at ",_.jsx(Rt,{href:"https://www.weather.gov/pimar/PubZone",children:"NWS Zone Map"})]}),_.jsxs("li",{children:["Zone codes look like: ",_.jsx($e,{children:"IDZ016"}),", ",_.jsx($e,{children:"UTZ040"}),", etc."]})]}),_.jsx(Te,{children:"The User-Agent Field"}),_.jsx("p",{children:"NWS wants to know who's using their API — not for approval, just so they can contact you if something breaks. You make it up:"}),_.jsx("p",{children:_.jsx($e,{children:"(meshai, you@email.com)"})}),_.jsx("p",{children:"No registration. No waiting. Just type it in."}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://alerts.weather.gov",children:"NWS Active Alerts"})," — see current alerts"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.weather.gov/documentation/services-web-api",children:"NWS API Docs"})," — technical details"]})]})]}),_.jsxs(vi,{id:"solar",title:"Solar & Geomagnetic Conditions",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"MeshAI tracks space weather — solar activity and its effects on Earth's magnetic field. This matters for radio operators because the sun directly controls how well HF radio works, and major solar events can affect all radio communications."}),_.jsx(Te,{children:"Solar Flux Index (SFI)"}),_.jsx("p",{children:'Think of SFI as a "how active is the sun" number. Higher = better for HF radio, but also higher risk of solar flares.'}),_.jsx(jt,{headers:["SFI","What It Means for You"],rows:[["Below 70","Quiet sun. Higher HF bands (10m, 15m) are probably dead. Stick to lower bands."],["70-90","Getting better. Some openings on 15m and above, but inconsistent."],["90-120","Good. Most HF bands work. Reliable contacts on 20m and 15m."],["120-170","Great. All HF bands open. 10m works for worldwide contacts."],["Above 170","Excellent. Best HF conditions — but watch for flares."]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Quick rule"}),": SFI above 90 and Kp below 4 = good day for HF radio."]}),_.jsx(Te,{children:"Kp Index"}),_.jsx("p",{children:"Kp measures how disturbed Earth's magnetic field is, on a 0-9 scale. Higher = more disturbance = worse for HF radio but better for aurora viewing."}),_.jsx(jt,{headers:["Kp","What It Means for You"],rows:[["0-2","Quiet. Best HF conditions."],["3","Slightly unsettled. You probably won't notice."],["4","Active. Some noise and fading on HF, especially if you're at higher latitudes."],[_.jsx("strong",{children:"5"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Minor storm (G1)."})," HF noticeably degraded. Aurora visible at high latitudes (~60°N)."]})],[_.jsx("strong",{children:"6"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Moderate storm (G2)."})," HF getting rough. Aurora moving south (~55°N)."]})],[_.jsx("strong",{children:"7"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Strong storm (G3)."})," HF unreliable for 1-2 days. Aurora at mid-latitudes."]})],[_.jsx("strong",{children:"8-9"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Severe/Extreme storm."})," HF may black out completely. Aurora visible at very low latitudes. Power grid stress possible."]})]]}),_.jsx(Te,{children:"R / S / G Scales"}),_.jsx("p",{children:"NOAA's shorthand for three types of space weather events:"}),_.jsx(fl,{children:"R (Radio Blackouts) — from solar flares:"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsx("li",{children:"R1-R2: Brief HF disruption. You might not notice."}),_.jsx("li",{children:"R3: HF goes out for about an hour on the sunlit side of Earth."}),_.jsx("li",{children:"R4-R5: HF dead for hours. Serious."})]}),_.jsx(fl,{children:"S (Solar Radiation Storms) — from energetic particles:"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsx("li",{children:"Mostly affects polar regions and satellites"}),_.jsx("li",{children:"S3+: Polar HF goes out entirely"})]}),_.jsx(fl,{children:"G (Geomagnetic Storms) — from solar wind disturbances:"}),_.jsx("ul",{className:"list-disc list-inside ml-4 space-y-1",children:_.jsx("li",{children:"Same as the Kp scale: G1 = Kp 5, up to G5 = Kp 9"})}),_.jsx(Te,{children:"Bz — The Storm Predictor"}),_.jsx("p",{children:"Bz measures the direction of the solar wind's magnetic field. When it points south (negative values), the solar wind can dump energy into Earth's magnetic field, causing storms."}),_.jsx(jt,{headers:["Bz","What It Means"],rows:[["Positive","All good. Solar wind bouncing off."],["0 to -5","Slight coupling. Nothing dramatic."],["-5 to -10","Things starting to pick up. Storm possible."],["Below -10","Storm likely. Kp will start climbing."],["Below -20","Severe storm probable."]]}),_.jsx("p",{children:"Bz can change fast — minute to minute. What matters is whether it stays negative for hours, not brief dips."}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.swpc.noaa.gov",children:"SWPC Space Weather Dashboard"})," — live data"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.swpc.noaa.gov/noaa-scales-explanation",children:"NOAA Space Weather Scales"})," — what R/S/G mean"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.hamqsl.com/solar.html",children:"HamQSL Solar Page"})," — ham-friendly display"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.swpc.noaa.gov/products/planetary-k-index",children:"Planetary K-Index"})," — live Kp"]})]})]}),_.jsxs(vi,{id:"ducting",title:"Tropospheric Ducting",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:'Sometimes the atmosphere creates an invisible "pipe" that traps radio signals and carries them much farther than normal. This is called tropospheric ducting. It mostly affects VHF and UHF frequencies.'}),_.jsx("p",{children:"MeshAI watches for these conditions by analyzing weather data (temperature and humidity at different altitudes) over your mesh area."}),_.jsx(Te,{children:"How Do I Know If Ducting Is Happening?"}),_.jsx("p",{children:'MeshAI reports a "condition" based on the atmospheric profile:'}),_.jsx(jt,{headers:["Condition","What It Means"],rows:[["Normal","Standard propagation. Nothing unusual."],["Super-refraction","Slightly enhanced range. You might hear a few more distant stations than usual."],["Surface Duct","Radio signals trapped near the ground. You may hear stations hundreds of km away that you've never heard before."],["Elevated Duct",'Same effect but the "pipe" is up in the atmosphere. Affects signals passing through that altitude.']]}),_.jsx(Te,{children:"What You'll Actually Notice"}),_.jsx("p",{children:"When ducting happens on your mesh:"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsx("li",{children:"Distant repeaters you've never heard suddenly come in"}),_.jsx("li",{children:"Nodes appear from far outside your normal range"}),_.jsx("li",{children:"You hear FM radio stations from other cities"}),_.jsx("li",{children:"ADS-B flight tracking range gets much longer"}),_.jsx("li",{children:"There might be interference from distant stations on your frequency"})]}),_.jsx(Te,{children:"The dM/dz Number"}),_.jsx("p",{children:`The dashboard shows a "dM/dz" value in "M-units/km." You don't need to understand the math — just know:`}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx("strong",{children:"Around 118"})," = normal atmosphere"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Below 79"})," = enhanced propagation starting"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Below 0 (negative)"})," = ducting is happening"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Below -50"})," = strong ducting — classic VHF/UHF DX event"]})]}),_.jsx(Te,{children:"When Does Ducting Happen?"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsx("li",{children:"Under high-pressure weather systems (clear, stable air)"}),_.jsx("li",{children:"When warm air sits on top of cool air (temperature inversion)"}),_.jsx("li",{children:"Most common in late summer and early fall"}),_.jsx("li",{children:"Strongest along coastlines and over water"}),_.jsx("li",{children:"In mountain valleys: cold air pooling in fall/winter can create surface ducts"})]}),_.jsx(Te,{children:"Setting It Up"}),_.jsx("p",{children:"Just configure the latitude and longitude of the center of your mesh area in Config → Environmental → Ducting. MeshAI checks the atmospheric conditions there every 3 hours using free weather model data. No API key needed."}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://dxinfocentre.com/tropo.html",children:"Tropo Forecast Maps (Hepburn)"})," — 6-day tropo prediction"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://dxmaps.com",children:"DX Maps"})," — real-time VHF/UHF propagation reports"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://en.wikipedia.org/wiki/Tropospheric_propagation",children:"Wikipedia: Tropospheric Propagation"})," — background"]})]})]}),_.jsxs(vi,{id:"avalanche",title:"Avalanche Danger",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"MeshAI pulls avalanche forecasts from your regional avalanche center during winter months. The danger scale has 5 levels and it's the same across all of North America."}),_.jsx(Te,{children:"The Danger Scale"}),_.jsx(jt,{headers:["Level","Name","Color","What To Do"],rows:[["1","Low",_.jsx(ir,{color:"green"}),"Generally safe. Normal caution in steep terrain."],["2","Moderate",_.jsx(ir,{color:"yellow"}),"Be careful on specific terrain features. Evaluate conditions."],["3","Considerable",_.jsx(ir,{color:"orange"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"DANGEROUS."}),` This is where most people die in avalanches — they see "3 out of 5" and think it's fine. It's not. Use extreme caution.`]})],["4","High",_.jsx(ir,{color:"red"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Very dangerous."})," Stay off anything steep."]})],["5","Extreme",_.jsx(ir,{color:"black"}),_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Don't go out."})," Avalanches are happening on their own."]})]]}),_.jsx(Te,{children:"The Most Important Thing to Know"}),_.jsxs("p",{children:[_.jsx("strong",{children:"Level 3 (Considerable) kills more people than any other level."}),' People look at "3 out of 5" and think "middle of the road, probably okay." In reality, the risk roughly doubles at each step up the scale. Level 3 is where dangerous conditions overlap with people thinking they can handle it.']}),_.jsx(Te,{children:"Seasonal"}),_.jsx("p",{children:'MeshAI only checks avalanche conditions during winter months (configurable, default December through April). Outside season, it shows "off season" and saves API calls.'}),_.jsx(Te,{children:"Finding Your Avalanche Center"}),_.jsxs("p",{children:["Go to ",_.jsx(Rt,{href:"https://avalanche.org/avalanche-centers/",children:"avalanche.org/avalanche-centers/"})," for a map. Common center codes:"]}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx($e,{children:"SNFAC"})," — Sawtooth (central Idaho)"]}),_.jsxs("li",{children:[_.jsx($e,{children:"UAC"})," — Utah"]}),_.jsxs("li",{children:[_.jsx($e,{children:"NWAC"})," — Cascades/Olympics (WA/OR)"]}),_.jsxs("li",{children:[_.jsx($e,{children:"CAIC"})," — Colorado"]}),_.jsxs("li",{children:[_.jsx($e,{children:"SAC"})," — Sierra Nevada (CA)"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GNFAC"})," — Gallatin (SW Montana)"]})]}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://avalanche.org",children:"Avalanche.org"})," — US forecasts"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://avalanche.org/avalanche-encyclopedia/human/resources/north-american-public-avalanche-danger-scale/",children:"Avalanche Danger Scale"})," — full scale explanation"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://kbyg.org",children:"Know Before You Go"})," — avalanche awareness"]})]})]}),_.jsxs(vi,{id:"traffic",title:"Traffic Flow",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"MeshAI monitors traffic speed on road segments you configure, using data from TomTom (real vehicles with navigation apps reporting their speed)."}),_.jsx(Te,{children:"Speed Ratio — The Key Number"}),_.jsx("p",{children:'MeshAI compares current speed to "free-flow speed" (what traffic normally does when the road is empty). The ratio tells you how congested it is:'}),_.jsx(jt,{headers:["Ratio","What It Means"],rows:[[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"green"})," Above 85%"]}),"Normal. Traffic flowing fine."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"yellow"})," 65-85%"]}),"Slow. Heavier than usual but moving."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"orange"})," 40-65%"]}),"Congested. Significant delays."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"red"})," Below 40%"]}),"Gridlock. Barely moving."]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Note"}),`: "free-flow speed" is NOT the speed limit. It's what traffic actually does on that road when nobody's in the way. Drivers often exceed speed limits on open highways.`]}),_.jsx(Te,{children:"Confidence — Can You Trust the Data?"}),_.jsx("p",{children:"TomTom's confidence score tells you how much of the reading comes from real vehicles right now vs historical averages:"}),_.jsx(jt,{headers:["Confidence","What It Means"],rows:[["Above 0.9","Very reliable — lots of real-time probe data"],["0.7-0.9","Good — mix of real-time and historical"],["Below 0.7",_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Unreliable"})," — mostly guessing from historical patterns. Don't alert on this."]})]]}),_.jsx("p",{children:"Set minimum confidence to 0.7 to avoid false congestion alerts at night or on rural roads where few probe vehicles drive."}),_.jsx(Te,{children:"Setting Up Corridors"}),_.jsx("p",{children:'Each "corridor" is a point on a road you want to monitor. To add one:'}),_.jsxs("ol",{className:"list-decimal list-inside ml-4 space-y-1",children:[_.jsx("li",{children:"Go to Google Maps, find the road"}),_.jsx("li",{children:`Right-click the road → "What's here?" → copy the coordinates`}),_.jsx("li",{children:"Add the corridor in Config with a name and those coordinates"}),_.jsx("li",{children:"TomTom finds the nearest road segment automatically"})]}),_.jsx(Te,{children:"Getting an API Key"}),_.jsxs("ol",{className:"list-decimal list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:["Sign up at ",_.jsx(Rt,{href:"https://developer.tomtom.com",children:"developer.tomtom.com"})," (free)"]}),_.jsx("li",{children:"Create an app → get your API key"}),_.jsx("li",{children:"Free tier: 2,500 requests/day (plenty for 5-10 corridors)"})]}),_.jsx(Te,{children:"Learn More"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx(Rt,{href:"https://developer.tomtom.com",children:"TomTom Developer Portal"})," — API docs and key signup"]}),_.jsxs("li",{children:[_.jsx(Rt,{href:"https://www.tomtom.com/traffic-index/",children:"TomTom Traffic Index"})," — city congestion rankings"]})]})]}),_.jsxs(vi,{id:"roads-511",title:"Road Conditions (511)",children:[_.jsx(Te,{children:"What You're Looking At"}),_.jsx("p",{children:"511 systems report road closures, construction, weather events, mountain pass conditions, and incidents. Every state runs their own 511 system — there is no national API."}),_.jsx(Te,{children:"Setting It Up"}),_.jsx("p",{children:"You need to find YOUR state's 511 developer API. MeshAI does not include a default URL because every state is different. Some states have free public APIs, some require registration, and some don't have developer APIs at all."}),_.jsx("p",{children:"Configure in Config → Environmental → 511:"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx("strong",{children:"Base URL"})," — your state's API endpoint"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"API Key"})," — if required by your state"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Endpoints"})," — which data feeds to poll (varies by state)"]})]}),_.jsx(Te,{children:"Learn More"}),_.jsx("p",{children:"Check your state's 511 or DOT website for developer information."})]}),_.jsxs(vi,{id:"mesh-health",title:"Mesh Health",children:[_.jsx(Te,{children:"Health Score"}),_.jsx("p",{children:"MeshAI computes a 0-100 health score for your mesh network by looking at five areas, each weighted differently:"}),_.jsx(jt,{headers:["Pillar","Weight","What It Measures"],rows:[[_.jsx("strong",{children:"Infrastructure"}),"30%","Are your routers online?"],[_.jsx("strong",{children:"Utilization"}),"25%","Is the radio channel congested?"],[_.jsx("strong",{children:"Coverage"}),"20%","Do nodes have redundant paths to gateways?"],[_.jsx("strong",{children:"Behavior"}),"15%","Are any nodes flooding the channel?"],[_.jsx("strong",{children:"Power"}),"10%","Are battery-powered nodes running low?"]]}),_.jsx("p",{children:"The overall score is the weighted sum:"}),_.jsx("p",{className:"p-3 bg-slate-800 rounded font-mono text-sm",children:"Score = (Infrastructure × 30%) + (Utilization × 25%) + (Coverage × 20%) + (Behavior × 15%) + (Power × 10%)"}),_.jsx(Te,{children:"How Each Pillar Is Calculated"}),_.jsx(fl,{children:"Infrastructure (30%)"}),_.jsx("p",{children:"This is the simplest pillar — what percentage of your infrastructure nodes are currently online?"}),_.jsx("p",{className:"p-3 bg-slate-800 rounded font-mono text-sm",children:"(routers online ÷ total routers) × 100"}),_.jsxs("p",{children:["Only nodes with the ",_.jsx($e,{children:"ROUTER"}),", ",_.jsx($e,{children:"ROUTER_LATE"}),", or ",_.jsx($e,{children:"ROUTER_CLIENT"})," role count as infrastructure. Regular client nodes going offline doesn't affect this score. If you have 5 routers and 3 are online, infrastructure scores 60."]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Special case:"})," If you have no routers at all (all clients), this pillar scores 100. You're not penalized for not having infrastructure — you just don't have any to track."]}),_.jsx(fl,{children:"Utilization (25%)"}),_.jsxs("p",{children:["MeshAI reads the channel utilization that each router reports in its telemetry — this is the firmware's own measurement of how busy the radio channel is. MeshAI uses the ",_.jsx("strong",{children:"highest"})," value from any infrastructure node because the busiest router is the bottleneck for the whole mesh."]}),_.jsx("p",{children:_.jsx("strong",{children:"How it works:"})}),_.jsxs("ol",{className:"list-decimal list-inside space-y-1 ml-4",children:[_.jsxs("li",{children:["Collect ",_.jsx($e,{children:"channel_utilization"})," from all infrastructure nodes that report it"]}),_.jsx("li",{children:"If no infra nodes have telemetry, try all nodes"}),_.jsxs("li",{children:["Use the ",_.jsx("strong",{children:"maximum"})," value for scoring (busiest node = bottleneck)"]}),_.jsx("li",{children:"If no nodes report utilization (older firmware), fall back to packet count estimate"})]}),_.jsxs("p",{className:"mt-4",children:[_.jsx("strong",{children:"Fallback method"})," (when telemetry unavailable): estimates from packet counts using 200ms/packet airtime. This is less accurate — it assumes MediumFast preset and sums packets across all nodes."]}),_.jsx(jt,{headers:["Channel Utilization","Score","What It Means"],rows:[["Under 20%","100","Channel is clear — this is the goal"],["20-25%","75-100","Slight degradation, occasional collisions"],["25-35%","50-75","Severe degradation — firmware throttling active"],["35-45%","25-50","Mesh struggling badly — reliability dropping"],["Over 45%","0-25","Mesh is effectively unusable"]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Special case:"})," If no utilization data is available (no telemetry and no packet data), this pillar scores 100. You're not penalized for missing data."]}),_.jsx(fl,{children:"Coverage (20%)"}),_.jsx("p",{children:'Measures gateway redundancy — how many of your data sources can "see" each node. A node reported by all 3 of your gateways has full coverage. A node only seen by 1 gateway is a single point of failure.'}),_.jsxs("p",{className:"p-3 bg-slate-800 rounded font-mono text-sm",children:["coverage_ratio = average_gateways_per_node ÷ total_sources",_.jsx("br",{}),"single_gw_penalty = (single_gateway_nodes ÷ total_nodes) × 40"]}),_.jsx("p",{children:"If a node is seen by 2 out of 3 sources, its coverage ratio is 0.67. Infrastructure nodes with only single-gateway coverage get an extra penalty — they're critical but have no backup path."}),_.jsx(jt,{headers:["Coverage Ratio","Base Score","After Penalty"],rows:[["100% (all sources)","100","100 minus single-gw penalty"],["70-99%","90","Minus penalties"],["50-69%","70","Minus penalties"],["Under 50%","50 or less","Heavy penalty"]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Special case:"})," With only 1 data source, this pillar can't score well — there's no redundancy to measure. Coverage becomes meaningful when you have 2+ sources (MeshMonitor + MQTT, multiple gateways, etc.)."]}),_.jsx(fl,{children:"Behavior (15%)"}),_.jsx("p",{children:"Counts how many nodes are sending an unusually high number of non-text packets. This catches firmware bugs, stuck transmitters, and misconfigured nodes that are flooding the channel."}),_.jsxs("p",{children:[_.jsx("strong",{children:"What counts as flooding:"})," More than 500 non-text packets in 24 hours. Text messages don't count — the behavior pillar only flags telemetry, position, and routing packet floods."]}),_.jsx(jt,{headers:["Flagged Nodes","Score"],rows:[["0","100"],["1","80"],["2-3","60"],["4-5","40"],["6+","20"]]}),_.jsx("p",{children:"A single misbehaving node only drops the score to 80. It takes multiple problem nodes to seriously hurt the behavior pillar."}),_.jsx(fl,{children:"Power (10%)"}),_.jsx("p",{children:"Measures what fraction of battery-powered nodes are below the warning threshold (default 20%)."}),_.jsx("p",{className:"p-3 bg-slate-800 rounded font-mono text-sm",children:"100 × (1 − low_battery_nodes ÷ total_battery_nodes)"}),_.jsx("p",{children:"If 2 out of 10 battery nodes are below 20%, power scores 80."}),_.jsxs("p",{children:[_.jsx("strong",{children:"Important:"})," USB-powered nodes are excluded from this calculation. Many nodes report 100% battery even when running on wall power with no battery installed. Only nodes actually running on batteries affect this pillar."]}),_.jsx(Te,{children:"Health Tiers"}),_.jsx(jt,{headers:["Score","Tier","What It Means"],rows:[["90-100",_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"green"})," Healthy"]}),"Everything's working well."],["75-89",_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"yellow"})," Slight degradation"]}),"Some issues but the mesh is functional."],["50-74",_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"orange"})," Unhealthy"]}),"Multiple problems. Reliability is affected."],["25-49",_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"red"})," Warning"]}),"Significant issues. The mesh is struggling."],["0-24",_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"black"})," Critical"]}),"Major failures. Barely functional."]]}),_.jsx(Te,{children:"Channel Utilization — Is the Radio Channel Full?"}),_.jsx("p",{children:"Meshtastic radios share one LoRa channel. If too many nodes are transmitting too often, they step on each other and messages get lost."}),_.jsx(jt,{headers:["Utilization","What's Happening"],rows:[[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"green"})," Under 25%"]}),"Healthy. The firmware itself starts throttling above 25% to protect the channel — so under 25% is the target."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"yellow"})," 25-40%"]}),"Getting busy. Common on larger meshes. Worth watching."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"orange"})," 40-50%"]}),"Congested. The firmware throttles GPS updates above 40%. Messages are colliding and retrying."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"red"})," Over 50%"]}),"Serious problem. More time is spent retrying than communicating. Mesh reliability drops fast."],[_.jsxs(_.Fragment,{children:[_.jsx(ir,{color:"black"})," Over 65%"]}),"Documented failure point on busy LONG_FAST meshes. The mesh becomes unusable."]]}),_.jsx(Te,{children:"Packet Flooding"}),_.jsx("p",{className:"p-3 bg-yellow-500/10 border border-yellow-500/30 rounded text-yellow-200",children:_.jsx("strong",{children:'⚠️ "Packet flooding" means a node sending too many RADIO PACKETS. This has nothing to do with water flooding.'})}),_.jsx("p",{children:"A normal Meshtastic node sends a packet every few minutes (announcing itself, reporting telemetry, updating position). If a node starts blasting packets every few seconds, something is wrong — firmware bug, stuck transmitter, or misconfiguration."}),_.jsx(jt,{headers:["Packets per Minute","What It Means"],rows:[["1-5","Normal"],["5-10","Elevated — might be someone chatting a lot"],["10-20","Suspicious — worth investigating"],["Over 30","Something is broken. This node is actively hurting the mesh."]]}),_.jsx(Te,{children:"Battery Levels"}),_.jsx("p",{children:"Most Meshtastic radios (T-Beam, RAK4631, Heltec V3) use a single lithium battery cell. The voltage tells you how much charge is left:"}),_.jsx(jt,{headers:["Voltage","Charge","What To Do"],rows:[["4.20V","100%","Full"],["3.80V","~60%","Fine"],[_.jsx("strong",{children:"3.60V"}),_.jsx("strong",{children:"~30%"}),_.jsx(_.Fragment,{children:_.jsx("strong",{children:"⚠️ Warning — charge it soon"})})],[_.jsx("strong",{children:"3.50V"}),_.jsx("strong",{children:"~15%"}),_.jsx(_.Fragment,{children:_.jsx("strong",{children:"🔴 Low — charge it now"})})],[_.jsx("strong",{children:"3.40V"}),_.jsx("strong",{children:"~7%"}),_.jsx(_.Fragment,{children:_.jsx("strong",{children:"⚫ About to die"})})],["3.30V","~3%","Device shutting down"]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"USB-powered nodes"})," report 100% battery even if there's no battery installed. Battery alerts only matter for nodes actually running on battery power."]}),_.jsx(Te,{children:"Node Offline Detection"}),_.jsx("p",{children:`MeshAI marks a node as "offline" when it hasn't been heard for a configurable time period. Different node types need different thresholds:`}),_.jsx(jt,{headers:["Node Type","Recommended Threshold","Why"],rows:[["Fixed infrastructure (wall power)",_.jsx("strong",{children:"2 hours"}),"These should always be transmitting. 2 hours of silence means something is wrong."],["Fixed client (wall power)","2-4 hours","Same logic, slightly more lenient."],["Mobile / vehicle","4-8 hours","They go behind mountains, into garages, out of range. Normal."],["Solar-powered","12-24 hours","May shut down at night when solar stops charging."]]}),_.jsxs("p",{children:[_.jsx("strong",{children:"Rule of thumb"}),`: set the threshold to about 4× the node's beacon interval. Too tight and nodes will constantly flap "offline/online" from normal gaps. Too loose and real outages go unnoticed.`]})]}),_.jsxs(vi,{id:"notifications",title:"Notifications",children:[_.jsx(Te,{children:"How It Works"}),_.jsxs("ol",{className:"list-decimal list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx("strong",{children:"Something happens"})," — a fire is detected, weather warning issued, node goes offline, etc."]}),_.jsxs("li",{children:[_.jsx("strong",{children:"MeshAI checks your rules"})," — does this event match any of your notification rules? Is it severe enough? Are we in quiet hours?"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"If a rule matches"})," — MeshAI sends the notification through whatever delivery method that rule is configured for."]})]}),_.jsx(Te,{children:"Building Rules"}),_.jsx("p",{children:"Each rule answers three questions:"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx("strong",{children:"WHEN"})," does it trigger? (which categories, what severity)"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"WHERE"})," does it send? (mesh broadcast, email, webhook, etc.)"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"HOW OFTEN"})," at most? (cooldown period)"]})]}),_.jsx("p",{children:'Use "Add from Template" to start with a pre-built rule and customize it, or build from scratch with "Add Rule."'}),_.jsx(Te,{children:"Severity Levels — What Should I Set?"}),_.jsx(jt,{headers:["Level","When It's Used","Notification Volume"],rows:[["Info","Routine stuff (ducting detected, new router appeared)","High — lots of messages"],["Advisory","Worth knowing (weather advisory, slow traffic, battery declining)","Moderate"],["Watch","Pay attention (fire within 50km, weather watch, stream rising)","Low-moderate"],[_.jsxs(_.Fragment,{children:[_.jsx("strong",{children:"Warning"})," ✓"]}),"Take action (fire within 15km, severe weather, critical battery)","Low — recommended for most rules"],["Emergency","Life safety (extreme weather, fire at infrastructure, total blackout)","Very rare"]]}),_.jsxs("p",{children:[_.jsx("strong",{children:'"Warning" is the sweet spot for most rules.'})," You get alerted when something actually needs your attention without being overwhelmed by every minor event."]}),_.jsx(Te,{children:"Quiet Hours"}),_.jsx("p",{children:'When enabled, non-emergency notifications are held during sleeping hours (default 10pm-6am). Emergency alerts and rules marked "Override Quiet Hours" always get through.'}),_.jsx("p",{children:"You can turn quiet hours off entirely if you don't want them."}),_.jsx(Te,{children:"Webhook — The Swiss Army Knife"}),_.jsx("p",{children:"A webhook sends your alert as an HTTP POST to any URL. This one delivery method works with:"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx("strong",{children:"Discord"})," — use a Discord webhook URL"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Slack"})," — use a Slack incoming webhook URL"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"ntfy.sh"})," — POST to ",_.jsx($e,{children:"https://ntfy.sh/your-topic"})]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Pushover"})," — POST to the Pushover API"]}),_.jsxs("li",{children:[_.jsx("strong",{children:"Home Assistant"})," — POST to an automation webhook URL"]}),_.jsx("li",{children:"Anything else that accepts HTTP POST"})]}),_.jsx("p",{children:"MeshAI doesn't need to know what's on the other end. Give it the URL and it works."})]}),_.jsxs(vi,{id:"commands",title:"Commands",children:[_.jsxs("p",{children:["All commands use the ",_.jsx($e,{children:"!"})," prefix (configurable). Send these as a direct message to MeshAI on your mesh."]}),_.jsx(Te,{children:"Basic Commands"}),_.jsx(jt,{headers:["Command","What It Does"],rows:[[_.jsx($e,{children:"!help"}),"Shows all available commands"],[_.jsx($e,{children:"!ping"}),"Tests if the bot is alive"],[_.jsx($e,{children:"!status"}),"Quick mesh summary (nodes online, health score)"],[_.jsx($e,{children:"!health"}),"Detailed health report with pillar scores"],[_.jsx($e,{children:"!weather"}),"Current weather for your area"]]}),_.jsx(Te,{children:"Environmental Commands"}),_.jsx(jt,{headers:["Command","What It Does"],rows:[[_.jsx($e,{children:"!alerts"}),"Active NWS weather alerts for your area"],[_.jsxs(_.Fragment,{children:[_.jsx($e,{children:"!solar"})," (or ",_.jsx($e,{children:"!hf"}),")"]}),"Current solar indices and RF conditions"],[_.jsx($e,{children:"!fire"}),"Active wildfires near your mesh"],[_.jsx($e,{children:"!avy"}),'Avalanche advisory (seasonal — shows "off season" in summer)'],[_.jsxs(_.Fragment,{children:[_.jsx($e,{children:"!streams"})," (or ",_.jsx($e,{children:"!gauges"}),")"]}),"Stream gauge readings"],[_.jsxs(_.Fragment,{children:[_.jsx($e,{children:"!roads"})," (or ",_.jsx($e,{children:"!traffic"}),")"]}),"Road conditions and traffic flow"],[_.jsx($e,{children:"!hotspots"}),"Satellite fire detections"]]}),_.jsx(Te,{children:"Subscription Commands"}),_.jsx(jt,{headers:["Command","What It Does"],rows:[[_.jsx($e,{children:"!subscribe"}),"Lists all alert categories you can subscribe to"],[_.jsx($e,{children:"!subscribe fire_proximity"}),"Subscribe to a specific category"],[_.jsx($e,{children:"!subscribe all"}),"Subscribe to everything"],[_.jsx($e,{children:"!unsubscribe fire_proximity"}),"Unsubscribe from a category"],[_.jsx($e,{children:"!subscriptions"}),"Shows what you're currently subscribed to"]]}),_.jsx(Te,{children:"Conversational"}),_.jsx("p",{children:`MeshAI isn't just commands — you can ask it questions in plain English. "How's the mesh doing?" "Is there any ducting?" "What's the fire situation?" "How's traffic on I-84?" It uses the live environmental data and mesh health data to answer.`})]}),_.jsxs(vi,{id:"api",title:"API Reference",children:[_.jsxs("p",{children:["MeshAI's REST API is available at ",_.jsx($e,{children:"http://your-host:8080"}),". All endpoints return JSON."]}),_.jsx(Te,{children:"System"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/status"})," — version, uptime, node count"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/channels"})," — radio channel list"]}),_.jsxs("li",{children:[_.jsx($e,{children:"POST /api/restart"})," — restart the bot"]})]}),_.jsx(Te,{children:"Mesh Data"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/health"})," — health score and pillars"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/nodes"})," — all nodes with positions and telemetry"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/edges"})," — neighbor links with signal quality"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/regions"})," — region summaries"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/sources"})," — data source health"]})]}),_.jsx(Te,{children:"Configuration"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/config"})," — full config"]}),_.jsxs("li",{children:[_.jsxs($e,{children:["GET /api/config/","{section}"]})," — one section"]}),_.jsxs("li",{children:[_.jsxs($e,{children:["PUT /api/config/","{section}"]})," — update a section"]})]}),_.jsx(Te,{children:"Environmental"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/env/status"})," — per-feed health"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/env/active"})," — all active events"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/env/swpc"})," — solar/geomagnetic data"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/env/ducting"})," — atmospheric profile"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/env/fires"})," — wildfire perimeters"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/env/hotspots"})," — satellite fire detections"]})]}),_.jsx(Te,{children:"Alerts"}),_.jsxs("ul",{className:"list-disc list-inside ml-4 space-y-1",children:[_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/alerts/active"})," — current alerts"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/alerts/history"})," — past alerts"]}),_.jsxs("li",{children:[_.jsx($e,{children:"GET /api/notifications/categories"})," — available alert categories"]})]}),_.jsx(Te,{children:"Real-time"}),_.jsx("ul",{className:"list-disc list-inside ml-4 space-y-1",children:_.jsxs("li",{children:[_.jsx($e,{children:"ws://your-host:8080/ws/live"})," — WebSocket for live updates"]})})]})]})})]})}function set(){return _.jsx(Sce,{children:_.jsx(Ace,{children:_.jsxs(Due,{children:[_.jsx(hl,{path:"/",element:_.jsx(aNe,{})}),_.jsx(hl,{path:"/mesh",element:_.jsx(wJe,{})}),_.jsx(hl,{path:"/environment",element:_.jsx(UJe,{})}),_.jsx(hl,{path:"/config",element:_.jsx(VJe,{})}),_.jsx(hl,{path:"/alerts",element:_.jsx(eet,{})}),_.jsx(hl,{path:"/notifications",element:_.jsx(aet,{})}),_.jsx(hl,{path:"/reference",element:_.jsx(oet,{})})]})})})}pP.createRoot(document.getElementById("root")).render(_.jsx(J.StrictMode,{children:_.jsx(Fue,{children:_.jsx(set,{})})})); diff --git a/meshai/dashboard/static/index.html b/meshai/dashboard/static/index.html index 986fcb4..30f68c4 100644 --- a/meshai/dashboard/static/index.html +++ b/meshai/dashboard/static/index.html @@ -8,7 +8,7 @@ - + diff --git a/tests/test_central_consumer.py b/tests/test_central_consumer.py index 751bf0b..c28f93d 100644 --- a/tests/test_central_consumer.py +++ b/tests/test_central_consumer.py @@ -54,7 +54,10 @@ def test_no_subjects_when_all_native(): def test_subjects_when_central(): + # v0.5.4: assert the legacy bare-wildcard form by clearing region. + # Region-aware subject shapes are covered by test_central_region_routing.py. c, env, rec = make_consumer() + env.central.region = "" env.usgs_quake.feed_source = "central" assert "central.quake.>" in c.subjects() diff --git a/tests/test_central_region_routing.py b/tests/test_central_region_routing.py new file mode 100644 index 0000000..d29aaf7 --- /dev/null +++ b/tests/test_central_region_routing.py @@ -0,0 +1,88 @@ +"""v0.5.4: Central v0.9.20 region-aware subject building. + +Exercises `_subjects_for(adapter, region)` and the wiring through +`CentralConsumer._subject_owned()`. The spec is hard-coded in the test +strings on purpose so a future drift in the v0.9.20 subject scheme +fails noisily here instead of silently shipping wrong filters. +""" + +from meshai.central.consumer import ( + CentralConsumer, + _subjects_for, + _SUBJECTS_BARE, +) +from meshai.config import EnvironmentalConfig + + +# --------------------------------------------------------------------- per-adapter + +def test_subjects_for_nws_us_id(): + """NWS: region BEFORE wildcard (matches alert..<...>).""" + assert _subjects_for("nws", "us.id") == ["central.wx.alert.us.id.>"] + + +def test_subjects_for_usgs_quake_us_id(): + """USGS quake: region AFTER wildcard.""" + assert _subjects_for("usgs_quake", "us.id") == ["central.quake.event.>.us.id"] + + +def test_subjects_for_firms_us_id(): + """FIRMS hotspots: region AFTER wildcard, hotspot domain explicit.""" + assert _subjects_for("firms", "us.id") == ["central.fire.hotspot.>.us.id"] + + +def test_subjects_for_fires_us_id_uses_state_token(): + """NIFC fires: state-only token at depth-4 for both incident + perimeter.""" + assert _subjects_for("fires", "us.id") == [ + "central.fire.incident.id.>", + "central.fire.perimeter.id.>", + ] + + +def test_subjects_for_traffic_and_roads511_share_state_token(): + """Traffic family: bare-state suffix (no us. prefix), shared by both adapters.""" + assert _subjects_for("traffic", "us.id") == ["central.traffic.>.id"] + assert _subjects_for("roads511", "us.id") == ["central.traffic.>.id"] + + +def test_subjects_for_usgs_includes_unknown_workaround(): + """USGS hydro: subscribes to BOTH the region-tagged filter and the + ".unknown" filter to cover gauges whose state Central v0.9.20 can't + infer yet (workaround until v0.9.20.1 backfills the tag).""" + assert _subjects_for("usgs", "us.id") == [ + "central.hydro.>.us.id", + "central.hydro.>.unknown", + ] + + +def test_subjects_for_swpc_stays_global(): + """SWPC: space weather is planetary; region argument is ignored.""" + assert _subjects_for("swpc", "us.id") == ["central.space.>"] + assert _subjects_for("swpc", "us.mt") == ["central.space.>"] # same regardless + assert _subjects_for("swpc", "") == ["central.space.>"] + + +# --------------------------------------------------------------------- backward compat + +def test_subjects_for_empty_region_falls_back_to_bare_wildcards(): + """Empty/None region = pre-v0.9.20 behaviour for every adapter, byte-identical + to the legacy _SUBJECTS_BARE map. Adapters absent from the map return [].""" + for adapter, expected in _SUBJECTS_BARE.items(): + assert _subjects_for(adapter, "") == expected, f"empty region mismatch for {adapter}" + assert _subjects_for(adapter, None) == expected, f"None region mismatch for {adapter}" + # Unknown adapters return empty regardless of region. + assert _subjects_for("ducting", "us.id") == [] + assert _subjects_for("avalanche", "") == [] + + +# --------------------------------------------------------------------- integration + +def test_central_region_default_propagates_to_consumer_subjects(): + """Default region = 'us.id': flipping nws to central → consumer subscribes + to the region-aware subject, not the bare wildcard.""" + env = EnvironmentalConfig() + assert env.central.region == "us.id" # spec default + env.nws.feed_source = "central" + so = CentralConsumer(env, None)._subject_owned() + assert list(so.keys()) == ["central.wx.alert.us.id.>"] + assert so["central.wx.alert.us.id.>"] == {"nws"} diff --git a/tests/test_central_sub_adapter_routing.py b/tests/test_central_sub_adapter_routing.py index eb07f1b..e57272b 100644 --- a/tests/test_central_sub_adapter_routing.py +++ b/tests/test_central_sub_adapter_routing.py @@ -17,8 +17,15 @@ def _envelope(adapter, category="x.y", eid="e1"): def _route(central, adapter, subject, category="x.y"): """Simulate a message arriving on the subscription that matches `subject`, - with that subscription's owned-sources, and return the emitted Event (or None).""" + with that subscription's owned-sources, and return the emitted Event (or None). + + v0.5.4: this helper deliberately clears central.region so sub-adapter + routing is exercised against bare wildcards (its concern is the + owned-sources filter, not the region-aware subject shape — those are + tested in test_central_region_routing.py). + """ env = EnvironmentalConfig() + env.central.region = "" for a in central: getattr(env, a).feed_source = "central" rec = [] @@ -71,7 +78,11 @@ def test_tomtom_incidents_remaps_to_traffic(): def test_subject_owned_shares_traffic_subject(): + # v0.5.4: assert the legacy bare-wildcard shape by clearing region. + # Region-aware shared-subject behaviour ('central.traffic.>.id' for both + # traffic and roads511) is covered in test_central_region_routing.py. env = EnvironmentalConfig() + env.central.region = "" env.traffic.feed_source = "central" env.roads511.feed_source = "central" so = CentralConsumer(env, None)._subject_owned()