Refactor code structure for improved readability and maintainability

This commit is contained in:
Lloyd committed 2025-11-22 22:07:46 +00:00
1 parent 2495f08ab9
commit 2137e6a1c6
8 files changed
+147 -94

No files matched your search

+6
View File
@@ -169,3 +169,9 @@ logging:
# Log format
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
# Web interface configuration
web:
# Enable Cross-Origin Resource Sharing (CORS) headers
# Allows web frontends from different origins to access the API
cors_enabled: false
+1 -31
View File
@@ -74,7 +74,6 @@ class RRDToolHandler:
def update_packet_metrics(self, record: dict, cumulative_counts: dict):
if not self.available or not self.rrd_path.exists():
logger.debug("RRD not available or doesn't exist for packet metrics")
return
try:
@@ -83,9 +82,7 @@ class RRDToolHandler:
try:
info = rrdtool.info(str(self.rrd_path))
last_update = int(info.get("last_update", timestamp - 60))
logger.debug(f"RRD packet update: timestamp={timestamp}, last_update={last_update}")
if timestamp <= last_update:
logger.debug(f"Skipping RRD packet update: timestamp {timestamp} <= last_update {last_update}")
return
except Exception as e:
logger.debug(f"Failed to get RRD info for packet update: {e}")
@@ -108,9 +105,7 @@ class RRDToolHandler:
type_values_str = ":".join(type_values)
values = f"{basic_values}:{type_values_str}"
logger.debug(f"Updating RRD with packet values: {values}")
rrdtool.update(str(self.rrd_path), values)
logger.debug(f"RRD packet update successful")
except Exception as e:
logger.error(f"Failed to update RRD packet metrics: {e}")
@@ -128,8 +123,6 @@ class RRDToolHandler:
if start_time is None:
start_time = end_time - (24 * 3600)
logger.debug(f"RRD fetch: start={start_time}, end={end_time}, resolution={resolution}")
fetch_result = rrdtool.fetch(
str(self.rrd_path),
resolution.upper(),
@@ -142,13 +135,8 @@ class RRDToolHandler:
return None
(start, end, step), data_sources, data_points = fetch_result
logger.debug(f"RRD fetch result: start={start}, end={end}, step={step}, sources={len(data_sources)}, points={len(data_points)}")
logger.debug(f"Data sources: {data_sources}")
if data_points:
logger.debug(f"First data point: {data_points[0]}")
logger.debug(f"Last data point: {data_points[-1]}")
else:
if not data_points:
logger.warning("No data points returned from RRD fetch")
result = {
@@ -184,13 +172,6 @@ class RRDToolHandler:
current_time += step
result['timestamps'] = timestamps
logger.debug(f"RRD data processed successfully: {len(timestamps)} timestamps, packet_types keys: {list(result['packet_types'].keys())}")
for type_key in ['type_2', 'type_4', 'type_5']:
if type_key in result['packet_types']:
values = result['packet_types'][type_key]
non_none_values = [v for v in values if v is not None]
logger.debug(f"{type_key} values: count={len(values)}, non-none={len(non_none_values)}, sample={values[:3] if values else 'empty'}")
return result
@@ -203,15 +184,11 @@ class RRDToolHandler:
end_time = int(time.time())
start_time = end_time - (hours * 3600)
logger.debug(f"Getting packet type stats for {hours} hours from {start_time} to {end_time}")
rrd_data = self.get_data(start_time, end_time)
if not rrd_data or 'packet_types' not in rrd_data:
logger.warning(f"No RRD data available")
return None
logger.debug(f"RRD packet_types keys: {list(rrd_data['packet_types'].keys())}")
type_totals = {}
packet_type_names = {
'type_0': 'Request (REQ)',
@@ -244,23 +221,17 @@ class RRDToolHandler:
for type_key, data_points in rrd_data['packet_types'].items():
valid_points = [p for p in data_points if p is not None]
logger.debug(f"{type_key}: total_points={len(data_points)}, valid_points={len(valid_points)}")
if len(valid_points) >= 2:
total = max(valid_points) - min(valid_points)
logger.debug(f"{type_key}: min={min(valid_points)}, max={max(valid_points)}, total={total}")
elif len(valid_points) == 1:
total = valid_points[0]
logger.debug(f"{type_key}: single value={total}")
else:
total = 0
logger.debug(f"{type_key}: no valid values, total=0")
type_name = packet_type_names.get(type_key, type_key)
type_totals[type_name] = max(0, total or 0)
logger.debug(f"Final type_totals: {type_totals}")
result = {
"hours": hours,
"packet_type_totals": type_totals,
@@ -269,7 +240,6 @@ class RRDToolHandler:
"data_source": "rrd"
}
logger.debug(f"Returning packet type stats: {result}")
return result
except Exception as e:
+21 -2
View File
@@ -478,6 +478,7 @@ class RepeaterHandler(BaseHandler):
for key_record in transport_keys:
transport_key_encoded = key_record.get("transport_key")
key_name = key_record.get("name", "unknown")
flood_policy = key_record.get("flood_policy", "deny")
if not transport_key_encoded:
continue
@@ -487,8 +488,26 @@ class RepeaterHandler(BaseHandler):
transport_key = base64.b64decode(transport_key_encoded)
expected_code = calc_transport_code(transport_key, packet)
if transport_code_0 == expected_code:
logger.debug(f"Transport code validated for key '{key_name}'")
return True, ""
logger.debug(f"Transport code validated for key '{key_name}' with policy '{flood_policy}'")
# Update last_used timestamp for this key
try:
key_id = key_record.get("id")
if key_id:
self.storage.update_transport_key(
key_id=key_id,
last_used=time.time()
)
logger.debug(f"Updated last_used timestamp for transport key '{key_name}'")
except Exception as e:
logger.warning(f"Failed to update last_used for transport key '{key_name}': {e}")
# Check flood policy for this key
if flood_policy == "allow":
return True, ""
else:
return False, f"Transport key '{key_name}' flood policy denied"
except Exception as e:
logger.warning(f"Error checking transport key '{key_name}': {e}")
-2
View File
@@ -351,7 +351,6 @@ class RepeaterDaemon:
http_port = self.config.get("http", {}).get("port", 8000)
http_host = self.config.get("http", {}).get("host", "0.0.0.0")
template_dir = os.path.join(os.path.dirname(__file__), "templates")
node_name = self.config.get("repeater", {}).get("node_name", "Repeater")
# Format public key for display
@@ -370,7 +369,6 @@ class RepeaterDaemon:
host=http_host,
port=http_port,
stats_getter=self.get_stats,
template_dir=template_dir,
node_name=node_name,
pub_key=pub_key_formatted,
send_advert_func=self.send_advert,
+61 -33
View File
@@ -1,5 +1,6 @@
import json
import logging
import os
import time
from datetime import datetime
from typing import Callable, Optional
@@ -11,6 +12,11 @@ from .cad_calibration_engine import CADCalibrationEngine
logger = logging.getLogger("HTTPServer")
def is_cors_enabled(config: dict) -> bool:
"""Check if CORS is enabled in the configuration"""
return config.get("web", {}).get("cors_enabled", False)
def add_cors_headers():
"""Add CORS headers to allow cross-origin requests"""
cherrypy.response.headers['Access-Control-Allow-Origin'] = '*'
@@ -18,14 +24,6 @@ def add_cors_headers():
cherrypy.response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
def cors_enabled(func):
"""Decorator to enable CORS for API endpoints"""
def wrapper(*args, **kwargs):
add_cors_headers()
return func(*args, **kwargs)
return wrapper
# system systems
# GET /api/stats
# GET /api/logs
@@ -77,12 +75,25 @@ class APIEndpoints:
self.daemon_instance = daemon_instance
self._config_path = config_path or '/etc/pymc_repeater/config.yaml'
self.cad_calibration = CADCalibrationEngine(daemon_instance, event_loop)
# CORS check from config
self._cors_enabled = is_cors_enabled(self.config)
# Log the CORS status
logger.info(f"CORS {'enabled' if self._cors_enabled else 'disabled'} (config: web.cors_enabled={self._cors_enabled})")
# Set up automatic CORS for all responses if enabled
if self._cors_enabled:
cherrypy.engine.subscribe('before_finalize', self._add_cors_headers)
def _add_cors_headers(self):
"""Automatically add CORS headers to all responses"""
add_cors_headers()
@cherrypy.expose
def default(self, *args, **kwargs):
"""Handle OPTIONS requests for CORS preflight"""
if cherrypy.request.method == "OPTIONS":
add_cors_headers()
return ""
# For non-OPTIONS requests, return 404
raise cherrypy.HTTPError(404)
@@ -122,7 +133,9 @@ class APIEndpoints:
def _require_post(self):
if cherrypy.request.method != "POST":
raise Exception("Method not allowed")
cherrypy.response.status = 405 # Method Not Allowed
cherrypy.response.headers['Allow'] = 'POST'
raise cherrypy.HTTPError(405, "Method not allowed. This endpoint requires POST.")
def _get_time_range(self, hours):
end_time = int(time.time())
@@ -147,7 +160,6 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def stats(self):
try:
stats = self.stats_getter() if self.stats_getter else {}
@@ -164,7 +176,6 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def send_advert(self):
try:
self._require_post()
@@ -176,6 +187,9 @@ class APIEndpoints:
future = asyncio.run_coroutine_threadsafe(self.send_advert_func(), self.event_loop)
result = future.result(timeout=10)
return self._success("Advert sent successfully") if result else self._error("Failed to send advert")
except cherrypy.HTTPError:
# Re-raise HTTP errors (like 405 Method Not Allowed) without logging
raise
except Exception as e:
logger.error(f"Error sending advert: {e}", exc_info=True)
return self._error(e)
@@ -183,7 +197,6 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
@cors_enabled
def set_mode(self):
try:
self._require_post()
@@ -196,6 +209,9 @@ class APIEndpoints:
self.config["repeater"]["mode"] = new_mode
logger.info(f"Mode changed to: {new_mode}")
return {"success": True, "mode": new_mode}
except cherrypy.HTTPError:
# Re-raise HTTP errors (like 405 Method Not Allowed) without logging
raise
except Exception as e:
logger.error(f"Error setting mode: {e}", exc_info=True)
return self._error(e)
@@ -203,7 +219,6 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
@cors_enabled
def set_duty_cycle(self):
try:
self._require_post()
@@ -214,13 +229,15 @@ class APIEndpoints:
self.config["duty_cycle"]["enforcement_enabled"] = enabled
logger.info(f"Duty cycle enforcement {'enabled' if enabled else 'disabled'}")
return {"success": True, "enabled": enabled}
except cherrypy.HTTPError:
# Re-raise HTTP errors (like 405 Method Not Allowed) without logging
raise
except Exception as e:
logger.error(f"Error setting duty cycle: {e}", exc_info=True)
return self._error(e)
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def logs(self):
from .http_server import _log_buffer
try:
@@ -244,8 +261,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def packet_stats(self, hours=24):
try:
hours = int(hours)
stats = self._get_storage().get_packet_stats(hours=hours)
@@ -256,8 +273,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def packet_type_stats(self, hours=24):
try:
hours = int(hours)
stats = self._get_storage().get_packet_type_stats(hours=hours)
@@ -268,8 +285,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def route_stats(self, hours=24):
try:
hours = int(hours)
stats = self._get_storage().get_route_stats(hours=hours)
@@ -280,8 +297,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def recent_packets(self, limit=100):
try:
limit = int(limit)
packets = self._get_storage().get_recent_packets(limit=limit)
@@ -351,8 +368,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def packet_type_graph_data(self, hours=24, resolution='average', types='all'):
try:
hours = int(hours)
start_time, end_time = self._get_time_range(hours)
@@ -398,8 +415,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def metrics_graph_data(self, hours=24, resolution='average', metrics='all'):
try:
hours = int(hours)
start_time, end_time = self._get_time_range(hours)
@@ -460,8 +477,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
@cors_enabled
def cad_calibration_start(self):
try:
self._require_post()
data = cherrypy.request.json or {}
@@ -471,18 +488,24 @@ class APIEndpoints:
return self._success("Calibration started")
else:
return self._error("Calibration already running")
except cherrypy.HTTPError:
# Re-raise HTTP errors (like 405 Method Not Allowed) without logging
raise
except Exception as e:
logger.error(f"Error starting CAD calibration: {e}")
return self._error(e)
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def cad_calibration_stop(self):
try:
self._require_post()
self.cad_calibration.stop_calibration()
return self._success("Calibration stopped")
except cherrypy.HTTPError:
# Re-raise HTTP errors (like 405 Method Not Allowed) without logging
raise
except Exception as e:
logger.error(f"Error stopping CAD calibration: {e}")
return self._error(e)
@@ -490,8 +513,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
@cors_enabled
def save_cad_settings(self):
try:
self._require_post()
data = cherrypy.request.json or {}
@@ -524,6 +547,9 @@ class APIEndpoints:
"message": f"CAD settings saved: peak={peak}, min={min_val}",
"settings": {"peak": peak, "min_val": min_val, "detection_rate": detection_rate}
}
except cherrypy.HTTPError:
# Re-raise HTTP errors (like 405 Method Not Allowed) without logging
raise
except Exception as e:
logger.error(f"Error saving CAD settings: {e}")
return self._error(e)
@@ -542,8 +568,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def noise_floor_history(self, hours: int = 24):
try:
storage = self._get_storage()
hours = int(hours)
@@ -560,8 +586,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def noise_floor_stats(self, hours: int = 24):
try:
storage = self._get_storage()
hours = int(hours)
@@ -577,8 +603,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def noise_floor_chart_data(self, hours: int = 24):
try:
storage = self._get_storage()
hours = int(hours)
@@ -597,7 +623,10 @@ class APIEndpoints:
cherrypy.response.headers['Content-Type'] = 'text/event-stream'
cherrypy.response.headers['Cache-Control'] = 'no-cache'
cherrypy.response.headers['Connection'] = 'keep-alive'
cherrypy.response.headers['Access-Control-Allow-Origin'] = '*'
# Add CORS headers conditionally for SSE endpoint
if self._cors_enabled:
cherrypy.response.headers['Access-Control-Allow-Origin'] = '*'
if not hasattr(self.cad_calibration, 'message_queue'):
self.cad_calibration.message_queue = []
@@ -651,9 +680,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cors_enabled
def adverts_by_contact_type(self, contact_type=None, limit=None, hours=None):
try:
if not contact_type:
return self._error("contact_type parameter is required")
@@ -686,8 +714,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
@cors_enabled
def transport_keys(self):
if cherrypy.request.method == "GET":
try:
storage = self._get_storage()
@@ -738,8 +766,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
@cors_enabled
def transport_key(self, key_id):
if cherrypy.request.method == "GET":
try:
key_id = int(key_id)
@@ -810,8 +838,8 @@ class APIEndpoints:
@cherrypy.expose
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
@cors_enabled
def global_flood_policy(self):
"""
Update global flood policy configuration
@@ -31,7 +31,7 @@
`)}getSetCookie(){return this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(l){return l instanceof this?l:new this(l)}static concat(l,...z){const j=new this(l);return z.forEach(J=>j.set(J)),j}static accessor(l){const j=(this[EL]=this[EL]={accessors:{}}).accessors,J=this.prototype;function mt(kt){const Dt=Qb(kt);j[Dt]||(OJ(J,kt),j[Dt]=!0)}return to.isArray(l)?l.forEach(mt):mt(l),this}};E0.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);to.reduceDescriptors(E0.prototype,({value:d},l)=>{let z=l[0].toUpperCase()+l.slice(1);return{get:()=>d,set(j){this[z]=j}}});to.freezeMethods(E0);function E8(d,l){const z=this||ew,j=l||z,J=E0.from(j.headers);let mt=j.data;return to.forEach(d,function(Dt){mt=Dt.call(z,mt,J.normalize(),l?l.status:void 0)}),J.normalize(),mt}function mO(d){return!!(d&&d.__CANCEL__)}function x_(d,l,z){Qu.call(this,d??"canceled",Qu.ERR_CANCELED,l,z),this.name="CanceledError"}to.inherits(x_,Qu,{__CANCEL__:!0});function gO(d,l,z){const j=z.config.validateStatus;!z.status||!j||j(z.status)?d(z):l(new Qu("Request failed with status code "+z.status,[Qu.ERR_BAD_REQUEST,Qu.ERR_BAD_RESPONSE][Math.floor(z.status/100)-4],z.config,z.request,z))}function DJ(d){const l=/^([-+\w]{1,25})(:?\/\/|:)/.exec(d);return l&&l[1]||""}function FJ(d,l){d=d||10;const z=new Array(d),j=new Array(d);let J=0,mt=0,kt;return l=l!==void 0?l:1e3,function(Gt){const re=Date.now(),pe=j[mt];kt||(kt=re),z[J]=Gt,j[J]=re;let Ne=mt,or=0;for(;Ne!==J;)or+=z[Ne++],Ne=Ne%d;if(J=(J+1)%d,J===mt&&(mt=(mt+1)%d),re-kt<l)return;const _r=pe&&re-pe;return _r?Math.round(or*1e3/_r):void 0}}function RJ(d,l){let z=0,j=1e3/l,J,mt;const kt=(re,pe=Date.now())=>{z=pe,J=null,mt&&(clearTimeout(mt),mt=null),d(...re)};return[(...re)=>{const pe=Date.now(),Ne=pe-z;Ne>=j?kt(re,pe):(J=re,mt||(mt=setTimeout(()=>{mt=null,kt(J)},j-Ne)))},()=>J&&kt(J)]}const c4=(d,l,z=3)=>{let j=0;const J=FJ(50,250);return RJ(mt=>{const kt=mt.loaded,Dt=mt.lengthComputable?mt.total:void 0,Gt=kt-j,re=J(Gt),pe=kt<=Dt;j=kt;const Ne={loaded:kt,total:Dt,progress:Dt?kt/Dt:void 0,bytes:Gt,rate:re||void 0,estimated:re&&Dt&&pe?(Dt-kt)/re:void 0,event:mt,lengthComputable:Dt!=null,[l?"download":"upload"]:!0};d(Ne)},z)},CL=(d,l)=>{const z=d!=null;return[j=>l[0]({lengthComputable:z,total:d,loaded:j}),l[1]]},LL=d=>(...l)=>to.asap(()=>d(...l)),BJ=Kp.hasStandardBrowserEnv?((d,l)=>z=>(z=new URL(z,Kp.origin),d.protocol===z.protocol&&d.host===z.host&&(l||d.port===z.port)))(new URL(Kp.origin),Kp.navigator&&/(msie|trident)/i.test(Kp.navigator.userAgent)):()=>!0,NJ=Kp.hasStandardBrowserEnv?{write(d,l,z,j,J,mt,kt){if(typeof document>"u")return;const Dt=[`${d}=${encodeURIComponent(l)}`];to.isNumber(z)&&Dt.push(`expires=${new Date(z).toUTCString()}`),to.isString(j)&&Dt.push(`path=${j}`),to.isString(J)&&Dt.push(`domain=${J}`),mt===!0&&Dt.push("secure"),to.isString(kt)&&Dt.push(`SameSite=${kt}`),document.cookie=Dt.join("; ")},read(d){if(typeof document>"u")return null;const l=document.cookie.match(new RegExp("(?:^|; )"+d+"=([^;]*)"));return l?decodeURIComponent(l[1]):null},remove(d){this.write(d,"",Date.now()-864e5,"/")}}:{write(){},read(){return null},remove(){}};function jJ(d){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(d)}function UJ(d,l){return l?d.replace(/\/?\/$/,"")+"/"+l.replace(/^\/+/,""):d}function vO(d,l,z){let j=!jJ(l);return d&&(j||z==!1)?UJ(d,l):l}const PL=d=>d instanceof E0?{...d}:d;function Ay(d,l){l=l||{};const z={};function j(re,pe,Ne,or){return to.isPlainObject(re)&&to.isPlainObject(pe)?to.merge.call({caseless:or},re,pe):to.isPlainObject(pe)?to.merge({},pe):to.isArray(pe)?pe.slice():pe}function J(re,pe,Ne,or){if(to.isUndefined(pe)){if(!to.isUndefined(re))return j(void 0,re,Ne,or)}else return j(re,pe,Ne,or)}function mt(re,pe){if(!to.isUndefined(pe))return j(void 0,pe)}function kt(re,pe){if(to.isUndefined(pe)){if(!to.isUndefined(re))return j(void 0,re)}else return j(void 0,pe)}function Dt(re,pe,Ne){if(Ne in l)return j(re,pe);if(Ne in d)return j(void 0,re)}const Gt={url:mt,method:mt,data:mt,baseURL:kt,transformRequest:kt,transformResponse:kt,paramsSerializer:kt,timeout:kt,timeoutMessage:kt,withCredentials:kt,withXSRFToken:kt,adapter:kt,responseType:kt,xsrfCookieName:kt,xsrfHeaderName:kt,onUploadProgress:kt,onDownloadProgress:kt,decompress:kt,maxContentLength:kt,maxBodyLength:kt,beforeRedirect:kt,transport:kt,httpAgent:kt,httpsAgent:kt,cancelToken:kt,socketPath:kt,responseEncoding:kt,validateStatus:Dt,headers:(re,pe,Ne)=>J(PL(re),PL(pe),Ne,!0)};return to.forEach(Object.keys({...d,...l}),function(pe){const Ne=Gt[pe]||J,or=Ne(d[pe],l[pe],pe);to.isUndefined(or)&&Ne!==Dt||(z[pe]=or)}),z}const yO=d=>{const l=Ay({},d);let{data:z,withXSRFToken:j,xsrfHeaderName:J,xsrfCookieName:mt,headers:kt,auth:Dt}=l;if(l.headers=kt=E0.from(kt),l.url=fO(vO(l.baseURL,l.url,l.allowAbsoluteUrls),d.params,d.paramsSerializer),Dt&&kt.set("Authorization","Basic "+btoa((Dt.username||"")+":"+(Dt.password?unescape(encodeURIComponent(Dt.password)):""))), Line truncated
`+kt.map(RL).join(`
`):" "+RL(kt[0]):"as no adapter specified";throw new Qu("There is no suitable adapter to dispatch the request "+Dt,"ERR_NOT_SUPPORT")}return J}const _O={getAdapter:JJ,adapters:tM};function C8(d){if(d.cancelToken&&d.cancelToken.throwIfRequested(),d.signal&&d.signal.aborted)throw new x_(null,d)}function BL(d){return C8(d),d.headers=E0.from(d.headers),d.data=E8.call(d,d.transformRequest),["post","put","patch"].indexOf(d.method)!==-1&&d.headers.setContentType("application/x-www-form-urlencoded",!1),_O.getAdapter(d.adapter||ew.adapter,d)(d).then(function(j){return C8(d),j.data=E8.call(d,d.transformResponse,j),j.headers=E0.from(j.headers),j},function(j){return mO(j)||(C8(d),j&&j.response&&(j.response.data=E8.call(d,d.transformResponse,j.response),j.response.headers=E0.from(j.response.headers))),Promise.reject(j)})}const bO="1.13.2",j4={};["object","boolean","number","function","string","symbol"].forEach((d,l)=>{j4[d]=function(j){return typeof j===d||"a"+(l<1?"n ":" ")+d}});const NL={};j4.transitional=function(l,z,j){function J(mt,kt){return"[Axios v"+bO+"] Transitional option '"+mt+"'"+kt+(j?". "+j:"")}return(mt,kt,Dt)=>{if(l===!1)throw new Qu(J(kt," has been removed"+(z?" in "+z:"")),Qu.ERR_DEPRECATED);return z&&!NL[kt]&&(NL[kt]=!0,console.warn(J(kt," has been deprecated since v"+z+" and will be removed in the near future"))),l?l(mt,kt,Dt):!0}};j4.spelling=function(l){return(z,j)=>(console.warn(`${j} is likely a misspelling of ${l}`),!0)};function QJ(d,l,z){if(typeof d!="object")throw new Qu("options must be an object",Qu.ERR_BAD_OPTION_VALUE);const j=Object.keys(d);let J=j.length;for(;J-- >0;){const mt=j[J],kt=l[mt];if(kt){const Dt=d[mt],Gt=Dt===void 0||kt(Dt,mt,d);if(Gt!==!0)throw new Qu("option "+mt+" must be "+Gt,Qu.ERR_BAD_OPTION_VALUE);continue}if(z!==!0)throw new Qu("Unknown option "+mt,Qu.ERR_BAD_OPTION)}}const Y5={assertOptions:QJ,validators:j4},tg=Y5.validators;let by=class{constructor(l){this.defaults=l||{},this.interceptors={request:new SL,response:new SL}}async request(l,z){try{return await this._request(l,z)}catch(j){if(j instanceof Error){let J={};Error.captureStackTrace?Error.captureStackTrace(J):J=new Error;const mt=J.stack?J.stack.replace(/^.+\n/,""):"";try{j.stack?mt&&!String(j.stack).endsWith(mt.replace(/^.+\n.+\n/,""))&&(j.stack+=`
`+mt):j.stack=mt}catch{}}throw j}}_request(l,z){typeof l=="string"?(z=z||{},z.url=l):z=l||{},z=Ay(this.defaults,z);const{transitional:j,paramsSerializer:J,headers:mt}=z;j!==void 0&&Y5.assertOptions(j,{silentJSONParsing:tg.transitional(tg.boolean),forcedJSONParsing:tg.transitional(tg.boolean),clarifyTimeoutError:tg.transitional(tg.boolean)},!1),J!=null&&(to.isFunction(J)?z.paramsSerializer={serialize:J}:Y5.assertOptions(J,{encode:tg.function,serialize:tg.function},!0)),z.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls!==void 0?z.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:z.allowAbsoluteUrls=!0),Y5.assertOptions(z,{baseUrl:tg.spelling("baseURL"),withXsrfToken:tg.spelling("withXSRFToken")},!0),z.method=(z.method||this.defaults.method||"get").toLowerCase();let kt=mt&&to.merge(mt.common,mt[z.method]);mt&&to.forEach(["delete","get","head","post","put","patch","common"],Fr=>{delete mt[Fr]}),z.headers=E0.concat(kt,mt);const Dt=[];let Gt=!0;this.interceptors.request.forEach(function(zr){typeof zr.runWhen=="function"&&zr.runWhen(z)===!1||(Gt=Gt&&zr.synchronous,Dt.unshift(zr.fulfilled,zr.rejected))});const re=[];this.interceptors.response.forEach(function(zr){re.push(zr.fulfilled,zr.rejected)});let pe,Ne=0,or;if(!Gt){const Fr=[BL.bind(this),void 0];for(Fr.unshift(...Dt),Fr.push(...re),or=Fr.length,pe=Promise.resolve(z);Ne<or;)pe=pe.then(Fr[Ne++],Fr[Ne++]);return pe}or=Dt.length;let _r=z;for(;Ne<or;){const Fr=Dt[Ne++],zr=Dt[Ne++];try{_r=Fr(_r)}catch(Wr){zr.call(this,Wr);break}}try{pe=BL.call(this,_r)}catch(Fr){return Promise.reject(Fr)}for(Ne=0,or=re.length;Ne<or;)pe=pe.then(re[Ne++],re[Ne++]);return pe}getUri(l){l=Ay(this.defaults,l);const z=vO(l.baseURL,l.url,l.allowAbsoluteUrls);return fO(z,l.params,l.paramsSerializer)}};to.forEach(["delete","get","head","options"],function(l){by.prototype[l]=function(z,j){return this.request(Ay(j||{},{method:l,url:z,data:(j||{}).data}))}});to.forEach(["post","put","patch"],function(l){function z(j){return function(mt,kt,Dt){return this.request(Ay(Dt||{},{method:l,headers:j?{"Content-Type":"multipart/form-data"}:{},url:mt,data:kt}))}}by.prototype[l]=z(),by.prototype[l+"Form"]=z(!0)});let tQ=class wO{constructor(l){if(typeof l!="function")throw new TypeError("executor must be a function.");let z;this.promise=new Promise(function(mt){z=mt});const j=this;this.promise.then(J=>{if(!j._listeners)return;let mt=j._listeners.length;for(;mt-- >0;)j._listeners[mt](J);j._listeners=null}),this.promise.then=J=>{let mt;const kt=new Promise(Dt=>{j.subscribe(Dt),mt=Dt}).then(J);return kt.cancel=function(){j.unsubscribe(mt)},kt},l(function(mt,kt,Dt){j.reason||(j.reason=new x_(mt,kt,Dt),z(j.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(l){if(this.reason){l(this.reason);return}this._listeners?this._listeners.push(l):this._listeners=[l]}unsubscribe(l){if(!this._listeners)return;const z=this._listeners.indexOf(l);z!==-1&&this._listeners.splice(z,1)}toAbortSignal(){const l=new AbortController,z=j=>{l.abort(j)};return this.subscribe(z),l.signal.unsubscribe=()=>this.unsubscribe(z),l.signal}static source(){let l;return{token:new wO(function(J){l=J}),cancel:l}}};function eQ(d){return function(z){return d.apply(null,z)}}function rQ(d){return to.isObject(d)&&d.isAxiosError===!0}const pA={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(pA).forEach(([d,l])=>{pA[l]=d});function kO(d){const l=new by(d),z=tO(by.prototype.request,l);return to.extend(z,by.prototype,l,{allOwnKeys:!0}),to.extend(z,l,null,{allOwnKeys:!0}),z.create=function(J){return kO(Ay(d,J))},z}const _d=kO(ew);_d.Axios=by;_d.CanceledError=x_;_d.CancelToken=tQ;_d.isCancel=mO;_d.VERSION=bO;_d.toFormData=N4;_d.AxiosError=Qu;_d.Cancel=_ Line truncated
`+mt):j.stack=mt}catch{}}throw j}}_request(l,z){typeof l=="string"?(z=z||{},z.url=l):z=l||{},z=Ay(this.defaults,z);const{transitional:j,paramsSerializer:J,headers:mt}=z;j!==void 0&&Y5.assertOptions(j,{silentJSONParsing:tg.transitional(tg.boolean),forcedJSONParsing:tg.transitional(tg.boolean),clarifyTimeoutError:tg.transitional(tg.boolean)},!1),J!=null&&(to.isFunction(J)?z.paramsSerializer={serialize:J}:Y5.assertOptions(J,{encode:tg.function,serialize:tg.function},!0)),z.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls!==void 0?z.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:z.allowAbsoluteUrls=!0),Y5.assertOptions(z,{baseUrl:tg.spelling("baseURL"),withXsrfToken:tg.spelling("withXSRFToken")},!0),z.method=(z.method||this.defaults.method||"get").toLowerCase();let kt=mt&&to.merge(mt.common,mt[z.method]);mt&&to.forEach(["delete","get","head","post","put","patch","common"],Fr=>{delete mt[Fr]}),z.headers=E0.concat(kt,mt);const Dt=[];let Gt=!0;this.interceptors.request.forEach(function(zr){typeof zr.runWhen=="function"&&zr.runWhen(z)===!1||(Gt=Gt&&zr.synchronous,Dt.unshift(zr.fulfilled,zr.rejected))});const re=[];this.interceptors.response.forEach(function(zr){re.push(zr.fulfilled,zr.rejected)});let pe,Ne=0,or;if(!Gt){const Fr=[BL.bind(this),void 0];for(Fr.unshift(...Dt),Fr.push(...re),or=Fr.length,pe=Promise.resolve(z);Ne<or;)pe=pe.then(Fr[Ne++],Fr[Ne++]);return pe}or=Dt.length;let _r=z;for(;Ne<or;){const Fr=Dt[Ne++],zr=Dt[Ne++];try{_r=Fr(_r)}catch(Wr){zr.call(this,Wr);break}}try{pe=BL.call(this,_r)}catch(Fr){return Promise.reject(Fr)}for(Ne=0,or=re.length;Ne<or;)pe=pe.then(re[Ne++],re[Ne++]);return pe}getUri(l){l=Ay(this.defaults,l);const z=vO(l.baseURL,l.url,l.allowAbsoluteUrls);return fO(z,l.params,l.paramsSerializer)}};to.forEach(["delete","get","head","options"],function(l){by.prototype[l]=function(z,j){return this.request(Ay(j||{},{method:l,url:z,data:(j||{}).data}))}});to.forEach(["post","put","patch"],function(l){function z(j){return function(mt,kt,Dt){return this.request(Ay(Dt||{},{method:l,headers:j?{"Content-Type":"multipart/form-data"}:{},url:mt,data:kt}))}}by.prototype[l]=z(),by.prototype[l+"Form"]=z(!0)});let tQ=class wO{constructor(l){if(typeof l!="function")throw new TypeError("executor must be a function.");let z;this.promise=new Promise(function(mt){z=mt});const j=this;this.promise.then(J=>{if(!j._listeners)return;let mt=j._listeners.length;for(;mt-- >0;)j._listeners[mt](J);j._listeners=null}),this.promise.then=J=>{let mt;const kt=new Promise(Dt=>{j.subscribe(Dt),mt=Dt}).then(J);return kt.cancel=function(){j.unsubscribe(mt)},kt},l(function(mt,kt,Dt){j.reason||(j.reason=new x_(mt,kt,Dt),z(j.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(l){if(this.reason){l(this.reason);return}this._listeners?this._listeners.push(l):this._listeners=[l]}unsubscribe(l){if(!this._listeners)return;const z=this._listeners.indexOf(l);z!==-1&&this._listeners.splice(z,1)}toAbortSignal(){const l=new AbortController,z=j=>{l.abort(j)};return this.subscribe(z),l.signal.unsubscribe=()=>this.unsubscribe(z),l.signal}static source(){let l;return{token:new wO(function(J){l=J}),cancel:l}}};function eQ(d){return function(z){return d.apply(null,z)}}function rQ(d){return to.isObject(d)&&d.isAxiosError===!0}const pA={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(pA).forEach(([d,l])=>{pA[l]=d});function kO(d){const l=new by(d),z=tO(by.prototype.request,l);return to.extend(z,by.prototype,l,{allOwnKeys:!0}),to.extend(z,l,null,{allOwnKeys:!0}),z.create=function(J){return kO(Ay(d,J))},z}const _d=kO(ew);_d.Axios=by;_d.CanceledError=x_;_d.CancelToken=tQ;_d.isCancel=mO;_d.VERSION=bO;_d.toFormData=N4;_d.AxiosError=Qu;_d.Cancel=_ Line truncated
${pe.color} 0%,
${pe.color.replace("0.8","0.6")} 30%,
${pe.color.replace("0.8","0.4")} 70%,
@@ -41,11 +41,11 @@
0 4px 15px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.3),
inset 0 -1px 0 rgba(0, 0, 0, 0.2)
`})},[Re("span",krt,aa(pe.count),1)],4)]),Re("div",Trt,[Re("div",Art,aa(pe.name.replace(/\([^)]*\)/g,"").trim()),1)])]))),128))]))]),l.value.length>0?(zi(),Vi("div",Mrt," Total packet types: "+aa(l.value.length)+" | Total packets: "+aa(l.value.reduce((pe,Ne)=>pe+Ne.count,0)),1)):bs("",!0)]))}}),Ert=ld(Srt,[["__scopeId","data-v-dc58fd68"]]),Crt={class:"glass-card rounded-[10px] p-6"},Lrt={class:"relative h-48"},Prt={class:"mt-4 grid grid-cols-2 gap-4"},zrt={class:"text-center"},Irt={class:"text-2xl font-bold text-white"},Ort={class:"text-center"},Drt={class:"text-2xl font-bold text-white"},Frt={class:"mt-3 grid grid-cols-3 gap-3 text-center"},Rrt={class:"text-sm font-semibold text-accent-purple"},Brt={class:"text-sm font-semibold text-accent-red"},Nrt={class:"text-sm font-semibold text-white"},jrt=Th({name:"PerformanceChart",__name:"PerformanceChart",setup(d){const l=rw(),z=lo(null),j=lo([]),J=lo(null),mt=lo(!0),kt=async()=>{try{mt.value=!0;const Gt=await Xh.get("/recent_packets",{limit:50});if(!Gt.success){j.value=[],mt.value=!1,Z0(()=>{Dt()});return}const re=Gt.data||[],pe=Date.now(),Ne=24,or=12,_r=Ne*60*60*1e3/or,Fr=[];for(let zr=0;zr<or;zr++){const Wr=pe-Ne*60*60*1e3+zr*_r,An=Wr+_r,Ft=re.filter(jn=>{const ai=jn.timestamp*1e3;return ai>=Wr&&ai<An}),kn=Ft.filter(jn=>!jn.transmitted).length,ei=Ft.filter(jn=>jn.transmitted).length;Fr.push({time:new Date(Wr+_r/2).toISOString(),rxPackets:kn,txPackets:ei})}j.value=Fr,mt.value=!1,Z0(()=>{Dt()})}catch{j.value=[],mt.value=!1,Z0(()=>{Dt()})}},Dt=()=>{if(!z.value)return;const Gt=z.value,re=Gt.getContext("2d");if(!re)return;const pe=Gt.parentElement;if(!pe)return;const Ne=pe.getBoundingClientRect(),or=Ne.width,_r=Ne.height;Gt.width=or*window.devicePixelRatio,Gt.height=_r*window.devicePixelRatio,Gt.style.width=or+"px",Gt.style.height=_r+"px",re.scale(window.devicePixelRatio,window.devicePixelRatio);const Fr=20;if(re.clearRect(0,0,or,_r),mt.value){re.fillStyle="#666",re.font="16px sans-serif",re.textAlign="center",re.fillText("Loading chart data...",or/2,_r/2);return}if(j.value.length===0){re.fillStyle="#666",re.font="16px sans-serif",re.textAlign="center",re.fillText("No data available",or/2,_r/2);return}const zr=j.value.every(Gi=>Gi.rxPackets===0&&Gi.txPackets===0),Wr=or-Fr*2,An=_r-Fr*2,Ft=j.value.flatMap(Gi=>[Gi.rxPackets,Gi.txPackets]),kn=Math.min(...Ft),ei=Math.max(...Ft),jn=kn,ai=ei,Qi=Math.max(ai-jn,1);if(re.strokeStyle="rgba(255, 255, 255, 0.1)",re.lineWidth=1,jn<=0&&ai>=0){re.strokeStyle="rgba(255, 255, 255, 0.3)",re.lineWidth=2;const Gi=_r-Fr-(0-jn)/Qi*An;re.beginPath(),re.moveTo(Fr,Gi),re.lineTo(or-Fr,Gi),re.stroke(),Gi>20&&Gi<_r-20&&(re.fillStyle="rgba(255, 255, 255, 0.7)",re.font="10px system-ui",re.textAlign="left",re.fillText("0",Fr+2,Gi-2)),re.strokeStyle="rgba(255, 255, 255, 0.1)",re.lineWidth=1}for(let Gi=0;Gi<=5;Gi++){const un=Fr+An*Gi/5;re.beginPath(),re.moveTo(Fr,un),re.lineTo(or-Fr,un),re.stroke()}for(let Gi=0;Gi<=6;Gi++){const un=Fr+Wr*Gi/6;re.beginPath(),re.moveTo(un,Fr),re.lineTo(un,_r-Fr),re.stroke()}j.value.length>1&&(re.strokeStyle="#EBA0FC",re.lineWidth=2,re.beginPath(),j.value.forEach((Gi,un)=>{const ia=Fr+Wr*un/(j.value.length-1),fa=_r-Fr-(Gi.rxPackets-jn)/Qi*An;un===0?re.moveTo(ia,fa):re.lineTo(ia,fa)}),re.stroke(),re.fillStyle="#EBA0FC",j.value.forEach((Gi,un)=>{const ia=Fr+Wr*un/(j.value.length-1),fa=_r-Fr-(Gi.rxPackets-jn)/Qi*An;re.beginPath(),re.arc(ia,fa,2,0,2*Math.PI),re.fill()})),j.value.length>1&&(re.strokeStyle="#FB787B",re.lineWidth=2,re.beginPath(),j.value.forEach((Gi,un)=>{const ia=Fr+Wr*un/(j.value.length-1),fa=_r-Fr-(Gi.txPackets-jn)/Qi*An;un===0?re.moveTo(ia,fa):re.lineTo(ia,fa)}),re.stroke(),re.fillStyle="#FB787B",j.value.forEach((Gi,un)=>{const ia=Fr+Wr*un/(j.value.length-1),fa=_r-Fr-(Gi.txPackets-jn)/Qi*An;re.beginPath(),re.arc(ia,fa,2,0,2*Math.PI),re.fill()})),re.fillStyle="rgba(255, 255, 255, 0.6)",re.font="12px system-ui",re.textAlign="center",zr&&(re.fillStyle="rgba(255, 255, 255, 0.6)",re.font="14px system-ui",re.textAlign="center",re.fillText("No packet activity in last 24 hours",or/2,_r-15))};return t0(()=>{kt(),J.value=window.setInterval(kt,3e4),Z0(()=>{Dt(),setTimeout(()=>{Dt()},100)}),window.addEventListener("resize",Dt)}),dg(()=>{J.value&&clearInterval(J.value),window.removeEventListener("resize",Dt)}),(Gt,re)=>(zi(),Vi("div",Crt,[re[5]||(re[5]=Ff('<h3 class="text-white text-xl font-semibold mb-4" data-v-2ece57e8>Performance Metrics</h3><p class="text-white text-sm uppercase mb-4" data-v-2ece57e8>Packet Activity (Last 24 Hours)</p><div class="flex items-center gap-6 mb-4" data-v-2ece57e8><div class="flex items-center gap-2" data-v-2ece57e8><div class="w-7 h-2 rounded bg-accent-purple" data-v-2ece57e8></div><span class="text-white text-sm" data-v-2ece57e8>Received</span></div><div class="flex items-center gap-2" data-v-2ece57e8><div class="w-7 h-2 rounded bg-accent-red" data-v-2ece57e8></div><span class="text-white text-sm" data-v-2ece57e8>Transmitted</span></div></div>',3)),Re("div",Lrt,[Re(" Line truncated
`)||Ft},pe=(Ft,kn,ei)=>{try{let jn=0;const ai=kn.length/2;if(ai>=100){if(kn.length>=jn+64){const Qi=kn.slice(jn,jn+64);Ft.push({name:"Public Key",byteRange:`${(ei+jn)/2}-${(ei+jn+63)/2}`,hexData:Qi.match(/.{8}/g)?.join(" ")||Qi,description:"Ed25519 public key of the node (32 bytes)",fields:[{bits:"0-255",name:"Ed25519 Public Key",value:`${Qi.slice(0,16)}...${Qi.slice(-16)}`,binary:"32 bytes (256 bits)"}]}),jn+=64}if(kn.length>=jn+8){const Qi=kn.slice(jn,jn+8),Gi=parseInt(Qi,16),un=new Date(Gi*1e3);Ft.push({name:"Timestamp",byteRange:`${(ei+jn)/2}-${(ei+jn+7)/2}`,hexData:Qi.match(/.{2}/g)?.join(" ")||Qi,description:"Unix timestamp of advertisement",fields:[{bits:"0-31",name:"Unix Timestamp",value:`${Gi} (${un.toLocaleString()})`,binary:Gi.toString(2).padStart(32,"0")}]}),jn+=8}if(kn.length>=jn+128){const Qi=kn.slice(jn,jn+128);Ft.push({name:"Signature",byteRange:`${(ei+jn)/2}-${(ei+jn+127)/2}`,hexData:Qi.match(/.{8}/g)?.join(" ")||Qi,description:"Ed25519 signature of public key, timestamp, and appdata",fields:[{bits:"0-511",name:"Ed25519 Signature",value:`${Qi.slice(0,16)}...${Qi.slice(-16)}`,binary:"64 bytes (512 bits)"}]}),jn+=128}if(kn.length>jn){const Qi=kn.slice(jn);Ne(Ft,Qi,ei+jn)}}else Ft.push({name:"ADVERT AppData (Partial)",byteRange:`${ei/2}-${ei/2+ai-1}`,hexData:kn.match(/.{2}/g)?.join(" ")||kn,description:`Partial ADVERT data - appears to be just AppData portion (${ai} bytes)`,fields:[{bits:`0-${ai*8-1}`,name:"Partial Data",value:`${ai} bytes - attempting to decode as AppData`,binary:`${ai} bytes (${ai*8} bits)`}]}),Ne(Ft,kn,ei)}catch(jn){Ft.push({name:"ADVERT Parse Error",byteRange:"N/A",hexData:kn.slice(0,32)+"...",description:"Failed to parse ADVERT payload structure",fields:[{bits:"N/A",name:"Error",value:`Parse error: ${jn instanceof Error?jn.message:"Unknown error"}`,binary:"Invalid"}]})}},Ne=(Ft,kn,ei)=>{try{const jn=kn.length/2;Ft.push({name:"AppData",byteRange:`${ei/2}-${ei/2+jn-1}`,hexData:kn.match(/.{2}/g)?.join(" ")||kn,description:`Node advertisement application data (${jn} bytes)`,fields:[{bits:`0-${jn*8-1}`,name:"Application Data",value:`${jn} bytes (contains flags, location, name, etc.)`,binary:`${jn} bytes (${jn*8} bits)`}]});let ai=0;if(kn.length>=2){const Qi=parseInt(kn.slice(ai,ai+2),16),Gi=[],un=!!(Qi&16),ia=!!(Qi&32),fa=!!(Qi&64),Li=!!(Qi&128);if(Qi&1&&Gi.push("is chat node"),Qi&2&&Gi.push("is repeater"),Qi&4&&Gi.push("is room server"),Qi&8&&Gi.push("is sensor"),un&&Gi.push("has location"),ia&&Gi.push("has feature 1"),fa&&Gi.push("has feature 2"),Li&&Gi.push("has name"),Ft.push({name:"AppData Flags",byteRange:`${(ei+ai)/2}`,hexData:`0x${kn.slice(ai,ai+2)}`,description:"Flags indicating which optional fields are present",fields:[{bits:"0-7",name:"Flags",value:Gi.join(", ")||"none",binary:Qi.toString(2).padStart(8,"0")}]}),ai+=2,un&&kn.length>=ai+16){const yi=kn.slice(ai,ai+8),ra=[];for(let fu=6;fu>=0;fu-=2)ra.push(yi.slice(fu,fu+2));const Da=parseInt(ra.join(""),16),Ni=Da>2147483647?Da-4294967296:Da,Ei=Ni/1e6,Va=kn.slice(ai+8,ai+16),ss=[];for(let fu=6;fu>=0;fu-=2)ss.push(Va.slice(fu,fu+2));const mo=parseInt(ss.join(""),16),ko=mo>2147483647?mo-4294967296:mo,pl=ko/1e6;Ft.push({name:"Location Data",byteRange:`${(ei+ai)/2}-${(ei+ai+15)/2}`,hexData:`${yi.match(/.{2}/g)?.join(" ")||yi} ${Va.match(/.{2}/g)?.join(" ")||Va}`,description:"GPS coordinates (latitude and longitude)",fields:[{bits:"0-31",name:"Latitude",value:`${Ei.toFixed(6)}° (raw: ${Ni})`,binary:Ni.toString(2).padStart(32,"0")},{bits:"32-63",name:"Longitude",value:`${pl.toFixed(6)}° (raw: ${ko})`,binary:ko.toString(2).padStart(32,"0")}]}),ai+=16}if(ia&&kn.length>=ai+4){const yi=kn.slice(ai,ai+4),ra=parseInt(yi,16);Ft.push({name:"Feature 1",byteRange:`${(ei+ai)/2}-${(ei+ai+3)/2}`,hexData:yi.match(/.{2}/g)?.join(" ")||yi,description:"Reserved feature 1 (2 bytes)",fields:[{bits:"0-15",name:"Feature 1 Value",value:`${ra}`,binary:ra.toString(2).padStart(16,"0")}]}),ai+=4}if(fa&&kn.length>=ai+4){const yi=kn.slice(ai,ai+4),ra=parseInt(yi,16);Ft.push({name:"Feature 2",byteRange:`${(ei+ai)/2}-${(ei+ai+3)/2}`,hexData:yi.match(/.{2}/g)?.join(" ")||yi,description:"Reserved feature 2 (2 bytes)",fields:[{bits:"0-15",name:"Feature 2 Value",value:`${ra}`,binary:ra.toString(2).padStart(16,"0")}]}),ai+=4}if(Li&&kn.length>ai){const yi=kn.slice(ai),ra=yi.match(/.{2}/g)||[],Da=ra.map(Ni=>{const Ei=parseInt(Ni,16);return Ei>=32&&Ei<=126?String.fromCharCode(Ei):"."}).join("").replace(/\.+$/,"");Ft.push({name:"Node Name",byteRange:`${(ei+ai)/2}-${(ei+kn.length-1)/2}`,hexData:yi.match(/.{2}/g)?.join(" ")||yi,description:`Node name string (${ra.length} bytes)`,fields:[{bits:`0-${ra.length*8-1}`,name:"Node Name",value:`"${Da}"`,binary:`ASCII text (${ra.length} bytes)`}]})}}}catch(jn){Ft.push({name:"AppData Parse Error",byteRange:"N/A",hexData:kn.slice(0,Math.min(32,kn.length)),description:"Failed to parse AppData structure",fields:[{bits:"N/A",name:"Error",value:`Parse error: ${jn instanceof Error?jn.message:"Unknown error"}`,binary:"Invalid"} Line truncated
`})},[Re("span",Art,aa(pe.count),1)],4)]),Re("div",Mrt,[Re("div",Srt,aa(pe.name.replace(/\([^)]*\)/g,"").trim()),1)])]))),128))]))]),l.value.length>0?(zi(),Vi("div",Ert," Total packet types: "+aa(l.value.length)+" | Total packets: "+aa(l.value.reduce((pe,Ne)=>pe+Ne.count,0)),1)):bs("",!0)]))}}),Lrt=ld(Crt,[["__scopeId","data-v-dc58fd68"]]),Prt={class:"glass-card rounded-[10px] p-6"},zrt={class:"relative h-48"},Irt={class:"mt-4 grid grid-cols-2 gap-4"},Ort={class:"text-center"},Drt={class:"text-2xl font-bold text-white"},Frt={class:"text-center"},Rrt={class:"text-2xl font-bold text-white"},Brt={class:"mt-3 grid grid-cols-3 gap-3 text-center"},Nrt={class:"text-sm font-semibold text-accent-purple"},jrt={class:"text-sm font-semibold text-accent-red"},Urt={class:"text-sm font-semibold text-white"},Vrt=Th({name:"PerformanceChart",__name:"PerformanceChart",setup(d){const l=rw(),z=lo(null),j=lo([]),J=lo(null),mt=lo(!0),kt=async()=>{try{mt.value=!0;const Gt=await Xh.get("/recent_packets",{limit:50});if(!Gt.success){j.value=[],mt.value=!1,Z0(()=>{Dt()});return}const re=Gt.data||[],pe=Date.now(),Ne=24,or=12,_r=Ne*60*60*1e3/or,Fr=[];for(let zr=0;zr<or;zr++){const Wr=pe-Ne*60*60*1e3+zr*_r,An=Wr+_r,Ft=re.filter(jn=>{const ai=jn.timestamp*1e3;return ai>=Wr&&ai<An}),kn=Ft.filter(jn=>!jn.transmitted).length,ei=Ft.filter(jn=>jn.transmitted).length;Fr.push({time:new Date(Wr+_r/2).toISOString(),rxPackets:kn,txPackets:ei})}j.value=Fr,mt.value=!1,Z0(()=>{Dt()})}catch{j.value=[],mt.value=!1,Z0(()=>{Dt()})}},Dt=()=>{if(!z.value)return;const Gt=z.value,re=Gt.getContext("2d");if(!re)return;const pe=Gt.parentElement;if(!pe)return;const Ne=pe.getBoundingClientRect(),or=Ne.width,_r=Ne.height;Gt.width=or*window.devicePixelRatio,Gt.height=_r*window.devicePixelRatio,Gt.style.width=or+"px",Gt.style.height=_r+"px",re.scale(window.devicePixelRatio,window.devicePixelRatio);const Fr=20;if(re.clearRect(0,0,or,_r),mt.value){re.fillStyle="#666",re.font="16px sans-serif",re.textAlign="center",re.fillText("Loading chart data...",or/2,_r/2);return}if(j.value.length===0){re.fillStyle="#666",re.font="16px sans-serif",re.textAlign="center",re.fillText("No data available",or/2,_r/2);return}const zr=j.value.every(Gi=>Gi.rxPackets===0&&Gi.txPackets===0),Wr=or-Fr*2,An=_r-Fr*2,Ft=j.value.flatMap(Gi=>[Gi.rxPackets,Gi.txPackets]),kn=Math.min(...Ft),ei=Math.max(...Ft),jn=kn,ai=ei,Qi=Math.max(ai-jn,1);if(re.strokeStyle="rgba(255, 255, 255, 0.1)",re.lineWidth=1,jn<=0&&ai>=0){re.strokeStyle="rgba(255, 255, 255, 0.3)",re.lineWidth=2;const Gi=_r-Fr-(0-jn)/Qi*An;re.beginPath(),re.moveTo(Fr,Gi),re.lineTo(or-Fr,Gi),re.stroke(),Gi>20&&Gi<_r-20&&(re.fillStyle="rgba(255, 255, 255, 0.7)",re.font="10px system-ui",re.textAlign="left",re.fillText("0",Fr+2,Gi-2)),re.strokeStyle="rgba(255, 255, 255, 0.1)",re.lineWidth=1}for(let Gi=0;Gi<=5;Gi++){const un=Fr+An*Gi/5;re.beginPath(),re.moveTo(Fr,un),re.lineTo(or-Fr,un),re.stroke()}for(let Gi=0;Gi<=6;Gi++){const un=Fr+Wr*Gi/6;re.beginPath(),re.moveTo(un,Fr),re.lineTo(un,_r-Fr),re.stroke()}j.value.length>1&&(re.strokeStyle="#EBA0FC",re.lineWidth=2,re.beginPath(),j.value.forEach((Gi,un)=>{const ia=Fr+Wr*un/(j.value.length-1),fa=_r-Fr-(Gi.rxPackets-jn)/Qi*An;un===0?re.moveTo(ia,fa):re.lineTo(ia,fa)}),re.stroke(),re.fillStyle="#EBA0FC",j.value.forEach((Gi,un)=>{const ia=Fr+Wr*un/(j.value.length-1),fa=_r-Fr-(Gi.rxPackets-jn)/Qi*An;re.beginPath(),re.arc(ia,fa,2,0,2*Math.PI),re.fill()})),j.value.length>1&&(re.strokeStyle="#FB787B",re.lineWidth=2,re.beginPath(),j.value.forEach((Gi,un)=>{const ia=Fr+Wr*un/(j.value.length-1),fa=_r-Fr-(Gi.txPackets-jn)/Qi*An;un===0?re.moveTo(ia,fa):re.lineTo(ia,fa)}),re.stroke(),re.fillStyle="#FB787B",j.value.forEach((Gi,un)=>{const ia=Fr+Wr*un/(j.value.length-1),fa=_r-Fr-(Gi.txPackets-jn)/Qi*An;re.beginPath(),re.arc(ia,fa,2,0,2*Math.PI),re.fill()})),re.fillStyle="rgba(255, 255, 255, 0.6)",re.font="12px system-ui",re.textAlign="center",zr&&(re.fillStyle="rgba(255, 255, 255, 0.6)",re.font="14px system-ui",re.textAlign="center",re.fillText("No packet activity in last 24 hours",or/2,_r-15))};return t0(()=>{kt(),J.value=window.setInterval(kt,3e4),Z0(()=>{Dt(),setTimeout(()=>{Dt()},100)}),window.addEventListener("resize",Dt)}),dg(()=>{J.value&&clearInterval(J.value),window.removeEventListener("resize",Dt)}),(Gt,re)=>(zi(),Vi("div",Prt,[re[5]||(re[5]=Ff('<h3 class="text-white text-xl font-semibold mb-4" data-v-2ece57e8>Performance Metrics</h3><p class="text-white text-sm uppercase mb-4" data-v-2ece57e8>Packet Activity (Last 24 Hours)</p><div class="flex items-center gap-6 mb-4" data-v-2ece57e8><div class="flex items-center gap-2" data-v-2ece57e8><div class="w-7 h-2 rounded bg-accent-purple" data-v-2ece57e8></div><span class="text-white text-sm" data-v-2ece57e8>Received</span></div><div class="flex items-center gap-2" data-v-2ece57e8><div class="w-7 h-2 rounded bg-accent-red" data-v-2ece57e8></div><span class="text-white text-sm" data-v-2ece57e8>Transmitted</span></div></div>',3)),Re("div",zrt,[Re(" Line truncated
`)||Ft},pe=(Ft,kn,ei)=>{try{let jn=0;const ai=kn.length/2;if(ai>=100){if(kn.length>=jn+64){const Qi=kn.slice(jn,jn+64);Ft.push({name:"Public Key",byteRange:`${(ei+jn)/2}-${(ei+jn+63)/2}`,hexData:Qi.match(/.{8}/g)?.join(" ")||Qi,description:"Ed25519 public key of the node (32 bytes)",fields:[{bits:"0-255",name:"Ed25519 Public Key",value:`${Qi.slice(0,16)}...${Qi.slice(-16)}`,binary:"32 bytes (256 bits)"}]}),jn+=64}if(kn.length>=jn+8){const Qi=kn.slice(jn,jn+8),Gi=parseInt(Qi,16),un=new Date(Gi*1e3);Ft.push({name:"Timestamp",byteRange:`${(ei+jn)/2}-${(ei+jn+7)/2}`,hexData:Qi.match(/.{2}/g)?.join(" ")||Qi,description:"Unix timestamp of advertisement",fields:[{bits:"0-31",name:"Unix Timestamp",value:`${Gi} (${un.toLocaleString()})`,binary:Gi.toString(2).padStart(32,"0")}]}),jn+=8}if(kn.length>=jn+128){const Qi=kn.slice(jn,jn+128);Ft.push({name:"Signature",byteRange:`${(ei+jn)/2}-${(ei+jn+127)/2}`,hexData:Qi.match(/.{8}/g)?.join(" ")||Qi,description:"Ed25519 signature of public key, timestamp, and appdata",fields:[{bits:"0-511",name:"Ed25519 Signature",value:`${Qi.slice(0,16)}...${Qi.slice(-16)}`,binary:"64 bytes (512 bits)"}]}),jn+=128}if(kn.length>jn){const Qi=kn.slice(jn);Ne(Ft,Qi,ei+jn)}}else Ft.push({name:"ADVERT AppData (Partial)",byteRange:`${ei/2}-${ei/2+ai-1}`,hexData:kn.match(/.{2}/g)?.join(" ")||kn,description:`Partial ADVERT data - appears to be just AppData portion (${ai} bytes)`,fields:[{bits:`0-${ai*8-1}`,name:"Partial Data",value:`${ai} bytes - attempting to decode as AppData`,binary:`${ai} bytes (${ai*8} bits)`}]}),Ne(Ft,kn,ei)}catch(jn){Ft.push({name:"ADVERT Parse Error",byteRange:"N/A",hexData:kn.slice(0,32)+"...",description:"Failed to parse ADVERT payload structure",fields:[{bits:"N/A",name:"Error",value:`Parse error: ${jn instanceof Error?jn.message:"Unknown error"}`,binary:"Invalid"}]})}},Ne=(Ft,kn,ei)=>{try{const jn=kn.length/2;Ft.push({name:"AppData",byteRange:`${ei/2}-${ei/2+jn-1}`,hexData:kn.match(/.{2}/g)?.join(" ")||kn,description:`Node advertisement application data (${jn} bytes)`,fields:[{bits:`0-${jn*8-1}`,name:"Application Data",value:`${jn} bytes (contains flags, location, name, etc.)`,binary:`${jn} bytes (${jn*8} bits)`}]});let ai=0;if(kn.length>=2){const Qi=parseInt(kn.slice(ai,ai+2),16),Gi=[],un=!!(Qi&16),ia=!!(Qi&32),fa=!!(Qi&64),Li=!!(Qi&128);if(Qi&1&&Gi.push("is chat node"),Qi&2&&Gi.push("is repeater"),Qi&4&&Gi.push("is room server"),Qi&8&&Gi.push("is sensor"),un&&Gi.push("has location"),ia&&Gi.push("has feature 1"),fa&&Gi.push("has feature 2"),Li&&Gi.push("has name"),Ft.push({name:"AppData Flags",byteRange:`${(ei+ai)/2}`,hexData:`0x${kn.slice(ai,ai+2)}`,description:"Flags indicating which optional fields are present",fields:[{bits:"0-7",name:"Flags",value:Gi.join(", ")||"none",binary:Qi.toString(2).padStart(8,"0")}]}),ai+=2,un&&kn.length>=ai+16){const yi=kn.slice(ai,ai+8),ra=[];for(let fu=6;fu>=0;fu-=2)ra.push(yi.slice(fu,fu+2));const Da=parseInt(ra.join(""),16),Ni=Da>2147483647?Da-4294967296:Da,Ei=Ni/1e6,Va=kn.slice(ai+8,ai+16),ss=[];for(let fu=6;fu>=0;fu-=2)ss.push(Va.slice(fu,fu+2));const mo=parseInt(ss.join(""),16),ko=mo>2147483647?mo-4294967296:mo,pl=ko/1e6;Ft.push({name:"Location Data",byteRange:`${(ei+ai)/2}-${(ei+ai+15)/2}`,hexData:`${yi.match(/.{2}/g)?.join(" ")||yi} ${Va.match(/.{2}/g)?.join(" ")||Va}`,description:"GPS coordinates (latitude and longitude)",fields:[{bits:"0-31",name:"Latitude",value:`${Ei.toFixed(6)}° (raw: ${Ni})`,binary:Ni.toString(2).padStart(32,"0")},{bits:"32-63",name:"Longitude",value:`${pl.toFixed(6)}° (raw: ${ko})`,binary:ko.toString(2).padStart(32,"0")}]}),ai+=16}if(ia&&kn.length>=ai+4){const yi=kn.slice(ai,ai+4),ra=parseInt(yi,16);Ft.push({name:"Feature 1",byteRange:`${(ei+ai)/2}-${(ei+ai+3)/2}`,hexData:yi.match(/.{2}/g)?.join(" ")||yi,description:"Reserved feature 1 (2 bytes)",fields:[{bits:"0-15",name:"Feature 1 Value",value:`${ra}`,binary:ra.toString(2).padStart(16,"0")}]}),ai+=4}if(fa&&kn.length>=ai+4){const yi=kn.slice(ai,ai+4),ra=parseInt(yi,16);Ft.push({name:"Feature 2",byteRange:`${(ei+ai)/2}-${(ei+ai+3)/2}`,hexData:yi.match(/.{2}/g)?.join(" ")||yi,description:"Reserved feature 2 (2 bytes)",fields:[{bits:"0-15",name:"Feature 2 Value",value:`${ra}`,binary:ra.toString(2).padStart(16,"0")}]}),ai+=4}if(Li&&kn.length>ai){const yi=kn.slice(ai),ra=yi.match(/.{2}/g)||[],Da=ra.map(Ni=>{const Ei=parseInt(Ni,16);return Ei>=32&&Ei<=126?String.fromCharCode(Ei):"."}).join("").replace(/\.+$/,"");Ft.push({name:"Node Name",byteRange:`${(ei+ai)/2}-${(ei+kn.length-1)/2}`,hexData:yi.match(/.{2}/g)?.join(" ")||yi,description:`Node name string (${ra.length} bytes)`,fields:[{bits:`0-${ra.length*8-1}`,name:"Node Name",value:`"${Da}"`,binary:`ASCII text (${ra.length} bytes)`}]})}}}catch(jn){Ft.push({name:"AppData Parse Error",byteRange:"N/A",hexData:kn.slice(0,Math.min(32,kn.length)),description:"Failed to parse AppData structure",fields:[{bits:"N/A",name:"Error",value:`Parse error: ${jn instanceof Error?jn.message:"Unknown error"}`,binary:"Invalid"} Line truncated
* Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com
* (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade
*/var Cat=c2.exports,jL;function Lat(){return jL||(jL=1,function(d,l){(function(z,j){j(l)})(Cat,function(z){var j="1.9.4";function J(ct){var Bt,me,Qe,Pr;for(me=1,Qe=arguments.length;me<Qe;me++){Pr=arguments[me];for(Bt in Pr)ct[Bt]=Pr[Bt]}return ct}var mt=Object.create||function(){function ct(){}return function(Bt){return ct.prototype=Bt,new ct}}();function kt(ct,Bt){var me=Array.prototype.slice;if(ct.bind)return ct.bind.apply(ct,me.call(arguments,1));var Qe=me.call(arguments,2);return function(){return ct.apply(Bt,Qe.length?Qe.concat(me.call(arguments)):arguments)}}var Dt=0;function Gt(ct){return"_leaflet_id"in ct||(ct._leaflet_id=++Dt),ct._leaflet_id}function re(ct,Bt,me){var Qe,Pr,Tn,ji;return ji=function(){Qe=!1,Pr&&(Tn.apply(me,Pr),Pr=!1)},Tn=function(){Qe?Pr=arguments:(ct.apply(me,arguments),setTimeout(ji,Bt),Qe=!0)},Tn}function pe(ct,Bt,me){var Qe=Bt[1],Pr=Bt[0],Tn=Qe-Pr;return ct===Qe&&me?ct:((ct-Pr)%Tn+Tn)%Tn+Pr}function Ne(){return!1}function or(ct,Bt){if(Bt===!1)return ct;var me=Math.pow(10,Bt===void 0?6:Bt);return Math.round(ct*me)/me}function _r(ct){return ct.trim?ct.trim():ct.replace(/^\s+|\s+$/g,"")}function Fr(ct){return _r(ct).split(/\s+/)}function zr(ct,Bt){Object.prototype.hasOwnProperty.call(ct,"options")||(ct.options=ct.options?mt(ct.options):{});for(var me in Bt)ct.options[me]=Bt[me];return ct.options}function Wr(ct,Bt,me){var Qe=[];for(var Pr in ct)Qe.push(encodeURIComponent(me?Pr.toUpperCase():Pr)+"="+encodeURIComponent(ct[Pr]));return(!Bt||Bt.indexOf("?")===-1?"?":"&")+Qe.join("&")}var An=/\{ *([\w_ -]+) *\}/g;function Ft(ct,Bt){return ct.replace(An,function(me,Qe){var Pr=Bt[Qe];if(Pr===void 0)throw new Error("No value provided for variable "+me);return typeof Pr=="function"&&(Pr=Pr(Bt)),Pr})}var kn=Array.isArray||function(ct){return Object.prototype.toString.call(ct)==="[object Array]"};function ei(ct,Bt){for(var me=0;me<ct.length;me++)if(ct[me]===Bt)return me;return-1}var jn="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";function ai(ct){return window["webkit"+ct]||window["moz"+ct]||window["ms"+ct]}var Qi=0;function Gi(ct){var Bt=+new Date,me=Math.max(0,16-(Bt-Qi));return Qi=Bt+me,window.setTimeout(ct,me)}var un=window.requestAnimationFrame||ai("RequestAnimationFrame")||Gi,ia=window.cancelAnimationFrame||ai("CancelAnimationFrame")||ai("CancelRequestAnimationFrame")||function(ct){window.clearTimeout(ct)};function fa(ct,Bt,me){if(me&&un===Gi)ct.call(Bt);else return un.call(window,kt(ct,Bt))}function Li(ct){ct&&ia.call(window,ct)}var yi={__proto__:null,extend:J,create:mt,bind:kt,get lastId(){return Dt},stamp:Gt,throttle:re,wrapNum:pe,falseFn:Ne,formatNum:or,trim:_r,splitWords:Fr,setOptions:zr,getParamString:Wr,template:Ft,isArray:kn,indexOf:ei,emptyImageUrl:jn,requestFn:un,cancelFn:ia,requestAnimFrame:fa,cancelAnimFrame:Li};function ra(){}ra.extend=function(ct){var Bt=function(){zr(this),this.initialize&&this.initialize.apply(this,arguments),this.callInitHooks()},me=Bt.__super__=this.prototype,Qe=mt(me);Qe.constructor=Bt,Bt.prototype=Qe;for(var Pr in this)Object.prototype.hasOwnProperty.call(this,Pr)&&Pr!=="prototype"&&Pr!=="__super__"&&(Bt[Pr]=this[Pr]);return ct.statics&&J(Bt,ct.statics),ct.includes&&(Da(ct.includes),J.apply(null,[Qe].concat(ct.includes))),J(Qe,ct),delete Qe.statics,delete Qe.includes,Qe.options&&(Qe.options=me.options?mt(me.options):{},J(Qe.options,ct.options)),Qe._initHooks=[],Qe.callInitHooks=function(){if(!this._initHooksCalled){me.callInitHooks&&me.callInitHooks.call(this),this._initHooksCalled=!0;for(var Tn=0,ji=Qe._initHooks.length;Tn<ji;Tn++)Qe._initHooks[Tn].call(this)}},Bt},ra.include=function(ct){var Bt=this.prototype.options;return J(this.prototype,ct),ct.options&&(this.prototype.options=Bt,this.mergeOptions(ct.options)),this},ra.mergeOptions=function(ct){return J(this.prototype.options,ct),this},ra.addInitHook=function(ct){var Bt=Array.prototype.slice.call(arguments,1),me=typeof ct=="function"?ct:function(){this[ct].apply(this,Bt)};return this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(me),this};function Da(ct){if(!(typeof L>"u"||!L||!L.Mixin)){ct=kn(ct)?ct:[ct];for(var Bt=0;Bt<ct.length;Bt++)ct[Bt]===L.Mixin.Events&&console.warn("Deprecated include of L.Mixin.Events: this property will be removed in future releases, please inherit from L.Evented instead.",new Error().stack)}}var Ni={on:function(ct,Bt,me){if(typeof ct=="object")for(var Qe in ct)this._on(Qe,ct[Qe],Bt);else{ct=Fr(ct);for(var Pr=0,Tn=ct.length;Pr<Tn;Pr++)this._on(ct[Pr],Bt,me)}return this},off:function(ct,Bt,me){if(!arguments.length)delete this._events;else if(typeof ct=="object")for(var Qe in ct)this._off(Qe,ct[Qe],Bt);else{ct=Fr(ct);for(var Pr=arguments.length===1,Tn=0,ji=ct.length;Tn<ji;Tn++)Pr?this._off(ct[Tn]):this._off(ct[Tn],Bt,me)}return this},_on:function(ct,Bt,me,Qe){if(typeof Bt!="function"){console.warn("wrong listener type: "+typeof Bt);return}if(this._listens(ct,Bt,me)===!1){me===this&&(me=void 0);var Pr={fn:Bt,ctx:me};Qe&Line truncated
*/var Pat=c2.exports,jL;function zat(){return jL||(jL=1,function(d,l){(function(z,j){j(l)})(Pat,function(z){var j="1.9.4";function J(ct){var Bt,me,Qe,Pr;for(me=1,Qe=arguments.length;me<Qe;me++){Pr=arguments[me];for(Bt in Pr)ct[Bt]=Pr[Bt]}return ct}var mt=Object.create||function(){function ct(){}return function(Bt){return ct.prototype=Bt,new ct}}();function kt(ct,Bt){var me=Array.prototype.slice;if(ct.bind)return ct.bind.apply(ct,me.call(arguments,1));var Qe=me.call(arguments,2);return function(){return ct.apply(Bt,Qe.length?Qe.concat(me.call(arguments)):arguments)}}var Dt=0;function Gt(ct){return"_leaflet_id"in ct||(ct._leaflet_id=++Dt),ct._leaflet_id}function re(ct,Bt,me){var Qe,Pr,Tn,ji;return ji=function(){Qe=!1,Pr&&(Tn.apply(me,Pr),Pr=!1)},Tn=function(){Qe?Pr=arguments:(ct.apply(me,arguments),setTimeout(ji,Bt),Qe=!0)},Tn}function pe(ct,Bt,me){var Qe=Bt[1],Pr=Bt[0],Tn=Qe-Pr;return ct===Qe&&me?ct:((ct-Pr)%Tn+Tn)%Tn+Pr}function Ne(){return!1}function or(ct,Bt){if(Bt===!1)return ct;var me=Math.pow(10,Bt===void 0?6:Bt);return Math.round(ct*me)/me}function _r(ct){return ct.trim?ct.trim():ct.replace(/^\s+|\s+$/g,"")}function Fr(ct){return _r(ct).split(/\s+/)}function zr(ct,Bt){Object.prototype.hasOwnProperty.call(ct,"options")||(ct.options=ct.options?mt(ct.options):{});for(var me in Bt)ct.options[me]=Bt[me];return ct.options}function Wr(ct,Bt,me){var Qe=[];for(var Pr in ct)Qe.push(encodeURIComponent(me?Pr.toUpperCase():Pr)+"="+encodeURIComponent(ct[Pr]));return(!Bt||Bt.indexOf("?")===-1?"?":"&")+Qe.join("&")}var An=/\{ *([\w_ -]+) *\}/g;function Ft(ct,Bt){return ct.replace(An,function(me,Qe){var Pr=Bt[Qe];if(Pr===void 0)throw new Error("No value provided for variable "+me);return typeof Pr=="function"&&(Pr=Pr(Bt)),Pr})}var kn=Array.isArray||function(ct){return Object.prototype.toString.call(ct)==="[object Array]"};function ei(ct,Bt){for(var me=0;me<ct.length;me++)if(ct[me]===Bt)return me;return-1}var jn="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";function ai(ct){return window["webkit"+ct]||window["moz"+ct]||window["ms"+ct]}var Qi=0;function Gi(ct){var Bt=+new Date,me=Math.max(0,16-(Bt-Qi));return Qi=Bt+me,window.setTimeout(ct,me)}var un=window.requestAnimationFrame||ai("RequestAnimationFrame")||Gi,ia=window.cancelAnimationFrame||ai("CancelAnimationFrame")||ai("CancelRequestAnimationFrame")||function(ct){window.clearTimeout(ct)};function fa(ct,Bt,me){if(me&&un===Gi)ct.call(Bt);else return un.call(window,kt(ct,Bt))}function Li(ct){ct&&ia.call(window,ct)}var yi={__proto__:null,extend:J,create:mt,bind:kt,get lastId(){return Dt},stamp:Gt,throttle:re,wrapNum:pe,falseFn:Ne,formatNum:or,trim:_r,splitWords:Fr,setOptions:zr,getParamString:Wr,template:Ft,isArray:kn,indexOf:ei,emptyImageUrl:jn,requestFn:un,cancelFn:ia,requestAnimFrame:fa,cancelAnimFrame:Li};function ra(){}ra.extend=function(ct){var Bt=function(){zr(this),this.initialize&&this.initialize.apply(this,arguments),this.callInitHooks()},me=Bt.__super__=this.prototype,Qe=mt(me);Qe.constructor=Bt,Bt.prototype=Qe;for(var Pr in this)Object.prototype.hasOwnProperty.call(this,Pr)&&Pr!=="prototype"&&Pr!=="__super__"&&(Bt[Pr]=this[Pr]);return ct.statics&&J(Bt,ct.statics),ct.includes&&(Da(ct.includes),J.apply(null,[Qe].concat(ct.includes))),J(Qe,ct),delete Qe.statics,delete Qe.includes,Qe.options&&(Qe.options=me.options?mt(me.options):{},J(Qe.options,ct.options)),Qe._initHooks=[],Qe.callInitHooks=function(){if(!this._initHooksCalled){me.callInitHooks&&me.callInitHooks.call(this),this._initHooksCalled=!0;for(var Tn=0,ji=Qe._initHooks.length;Tn<ji;Tn++)Qe._initHooks[Tn].call(this)}},Bt},ra.include=function(ct){var Bt=this.prototype.options;return J(this.prototype,ct),ct.options&&(this.prototype.options=Bt,this.mergeOptions(ct.options)),this},ra.mergeOptions=function(ct){return J(this.prototype.options,ct),this},ra.addInitHook=function(ct){var Bt=Array.prototype.slice.call(arguments,1),me=typeof ct=="function"?ct:function(){this[ct].apply(this,Bt)};return this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(me),this};function Da(ct){if(!(typeof L>"u"||!L||!L.Mixin)){ct=kn(ct)?ct:[ct];for(var Bt=0;Bt<ct.length;Bt++)ct[Bt]===L.Mixin.Events&&console.warn("Deprecated include of L.Mixin.Events: this property will be removed in future releases, please inherit from L.Evented instead.",new Error().stack)}}var Ni={on:function(ct,Bt,me){if(typeof ct=="object")for(var Qe in ct)this._on(Qe,ct[Qe],Bt);else{ct=Fr(ct);for(var Pr=0,Tn=ct.length;Pr<Tn;Pr++)this._on(ct[Pr],Bt,me)}return this},off:function(ct,Bt,me){if(!arguments.length)delete this._events;else if(typeof ct=="object")for(var Qe in ct)this._off(Qe,ct[Qe],Bt);else{ct=Fr(ct);for(var Pr=arguments.length===1,Tn=0,ji=ct.length;Tn<ji;Tn++)Pr?this._off(ct[Tn]):this._off(ct[Tn],Bt,me)}return this},_on:function(ct,Bt,me,Qe){if(typeof Bt!="function"){console.warn("wrong listener type: "+typeof Bt);return}if(this._listens(ct,Bt,me)===!1){me===this&&(me=void 0);var Pr={fn:Bt,ctx:me};Qe&Line truncated
<div class="p-2">
<strong style="color: #AAE8E8;">This Node</strong><br>
<span class="text-sm" style="color: #9CA3AF;">Base Station</span><br>
@@ -68,29 +68,29 @@
<span class="text-sm" style="color: #E5E7EB;">Route: ${zr(Ta.route_type)}</span><br>
<span class="text-sm" style="color: #E5E7EB;">Signal: ${_r(Ta.rssi)} / ${Fr(Ta.snr)}</span>
</div>
`),ko.push(Oo),fu++,fu===qo&&fo())};Fc()},pl)})(),pl+=300}})};setTimeout(()=>{Dt&&Dt.invalidateSize()},2e3)}catch(Li){console.error("Error initializing map:",Li)}},200)},ai=()=>{Dt&&(Dt.remove(),Dt=null),Gt.value.clear()},Qi=un=>{const ia=Gt.value.get(un);if(ia){const fa=ia.getElement();fa&&fa.classList.add("marker-highlight")}},Gi=un=>{const ia=Gt.value.get(un);if(ia){const fa=ia.getElement();fa&&fa.classList.remove("marker-highlight")}};return t0(async()=>{await ei()}),K2(()=>{ai()}),um(pe,async un=>{un.length>0&&!J.value&&(await Z0(),setTimeout(async()=>{await jn()},300))},{immediate:!1}),um(J,async un=>{!un&&pe.value.length>0&&(await Z0(),setTimeout(async()=>{await jn()},300))}),(un,ia)=>(zi(),Vi("div",zat,[J.value?(zi(),Vi("div",Iat,ia[0]||(ia[0]=[Re("div",{class:"text-center"},[Re("div",{class:"animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto mb-4"}),Re("p",{class:"text-dark-text"},"Loading neighbor data...")],-1)]))):mt.value?(zi(),Vi("div",Oat,[Re("div",Dat,[ia[2]||(ia[2]=Re("svg",{class:"w-5 h-5 text-accent-red",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[Re("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"})],-1)),Re("div",null,[ia[1]||(ia[1]=Re("h3",{class:"text-accent-red font-medium"},"Error Loading Neighbors",-1)),Re("p",Fat,aa(mt.value),1)])])])):(zi(),Vi(Ou,{key:2},[pe.value.length>0?(zi(),Vi("div",Rat,[Re("div",{class:"flex items-center justify-between mb-4"},[ia[3]||(ia[3]=Re("h2",{class:"text-white text-lg font-semibold"},"Network Map",-1)),Re("button",{onClick:jn,class:"px-3 py-1 text-xs bg-primary/20 text-primary border border-primary/30 rounded-lg hover:bg-primary/30 transition-colors"}," Reload Map ")]),Re("div",{ref_key:"mapContainer",ref:kt,class:"w-full h-96 bg-gray-900 rounded-lg border border-gray-700/50 overflow-hidden leaflet-container shadow-xl",style:{"min-height":"384px",position:"relative",outline:"none","box-shadow":"inset 0 0 20px rgba(0,0,0,0.5)"}},null,512),ia[4]||(ia[4]=Ff('<div class="mt-4 grid grid-cols-1 md:grid-cols-2 gap-4 text-sm" data-v-eb494437><div data-v-eb494437><h4 class="text-white font-medium mb-2" data-v-eb494437>Node Types</h4><div class="space-y-1" data-v-eb494437><div class="flex items-center gap-2" data-v-eb494437><div class="w-3 h-3 bg-primary rounded-full" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>This Node</span></div><div class="flex items-center gap-2" data-v-eb494437><div class="w-3 h-3 bg-accent-green rounded-full" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>Repeater</span></div><div class="flex items-center gap-2" data-v-eb494437><div class="w-3 h-3 bg-blue-400 rounded-full" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>Chat Node</span></div><div class="flex items-center gap-2" data-v-eb494437><div class="w-3 h-3 bg-accent-purple rounded-full" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>Room Server</span></div><div class="flex items-center gap-2" data-v-eb494437><div class="w-3 h-3 bg-secondary rounded-full" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>Hybrid Node</span></div></div></div><div data-v-eb494437><h4 class="text-white font-medium mb-2" data-v-eb494437>Connection Types</h4><div class="space-y-1" data-v-eb494437><div class="flex items-center gap-2" data-v-eb494437><div class="w-8 h-0.5 bg-accent-green" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>Direct Routing</span></div><div class="flex items-center gap-2" data-v-eb494437><div class="w-8 h-0.5 bg-secondary" style="background-image:repeating-linear-gradient(to right, #FFC246 0, #FFC246 6px, transparent 6px, transparent 10px);" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>Flood Routing</span></div></div></div></div>',1))])):bs("",!0),(zi(!0),Vi(Ou,null,sf(re.value,([fa,Li])=>(zi(),Vi("div",{key:fa,class:"bg-dark-card/30 backdrop-blur border border-white/10 rounded-[15px] overflow-hidden"},[Re("div",Bat,[Re("h2",Nat,[nc(aa(Li)+" ",1),Re("span",jat,"("+aa(j.value[fa]?.length||0)+")",1)]),Re("div",{class:Xs(["flex items-center gap-2 text-sm",z[parseInt(fa)]])},[Re("div",{class:"w-2 h-2 rounded-full",style:av(`background-color: ${Ft(parseInt(fa))}`)},null,4),nc(" "+aa(Li),1)],2)]),Re("div",Uat,[Re("table",Vat,[ia[5]||(ia[5]=Re("thead",null,[Re("tr",{class:"bg-dark-bg/50"},[Re("th",{class:"text-left text-dark-text text-xs font-medium py-3 px-6 border-b border-white/5"},"Node Name"),Re("th",{class:"text-left text-dark-text text-xs font-medium py-3 px-6 border-b border-white/5"},"Public Key"),Re("th",{class:"text-left text-dark-text text-xs font-medium py-3 px-6 border-b border-white/5"},"Location"),Re("th",{class:"text-left text-dark-text text-xs font-medium py-3 px-6 border-b border-white/5"},"Distance"),Re("th",{ Line truncated
`),ko.push(Oo),fu++,fu===qo&&fo())};Fc()},pl)})(),pl+=300}})};setTimeout(()=>{Dt&&Dt.invalidateSize()},2e3)}catch(Li){console.error("Error initializing map:",Li)}},200)},ai=()=>{Dt&&(Dt.remove(),Dt=null),Gt.value.clear()},Qi=un=>{const ia=Gt.value.get(un);if(ia){const fa=ia.getElement();fa&&fa.classList.add("marker-highlight")}},Gi=un=>{const ia=Gt.value.get(un);if(ia){const fa=ia.getElement();fa&&fa.classList.remove("marker-highlight")}};return t0(async()=>{await ei()}),K2(()=>{ai()}),um(pe,async un=>{un.length>0&&!J.value&&(await Z0(),setTimeout(async()=>{await jn()},300))},{immediate:!1}),um(J,async un=>{!un&&pe.value.length>0&&(await Z0(),setTimeout(async()=>{await jn()},300))}),(un,ia)=>(zi(),Vi("div",Oat,[J.value?(zi(),Vi("div",Dat,ia[0]||(ia[0]=[Re("div",{class:"text-center"},[Re("div",{class:"animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto mb-4"}),Re("p",{class:"text-dark-text"},"Loading neighbor data...")],-1)]))):mt.value?(zi(),Vi("div",Fat,[Re("div",Rat,[ia[2]||(ia[2]=Re("svg",{class:"w-5 h-5 text-accent-red",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[Re("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"})],-1)),Re("div",null,[ia[1]||(ia[1]=Re("h3",{class:"text-accent-red font-medium"},"Error Loading Neighbors",-1)),Re("p",Bat,aa(mt.value),1)])])])):(zi(),Vi(Ou,{key:2},[pe.value.length>0?(zi(),Vi("div",Nat,[Re("div",{class:"flex items-center justify-between mb-4"},[ia[3]||(ia[3]=Re("h2",{class:"text-white text-lg font-semibold"},"Network Map",-1)),Re("button",{onClick:jn,class:"px-3 py-1 text-xs bg-primary/20 text-primary border border-primary/30 rounded-lg hover:bg-primary/30 transition-colors"}," Reload Map ")]),Re("div",{ref_key:"mapContainer",ref:kt,class:"w-full h-96 bg-gray-900 rounded-lg border border-gray-700/50 overflow-hidden leaflet-container shadow-xl",style:{"min-height":"384px",position:"relative",outline:"none","box-shadow":"inset 0 0 20px rgba(0,0,0,0.5)"}},null,512),ia[4]||(ia[4]=Ff('<div class="mt-4 grid grid-cols-1 md:grid-cols-2 gap-4 text-sm" data-v-eb494437><div data-v-eb494437><h4 class="text-white font-medium mb-2" data-v-eb494437>Node Types</h4><div class="space-y-1" data-v-eb494437><div class="flex items-center gap-2" data-v-eb494437><div class="w-3 h-3 bg-primary rounded-full" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>This Node</span></div><div class="flex items-center gap-2" data-v-eb494437><div class="w-3 h-3 bg-accent-green rounded-full" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>Repeater</span></div><div class="flex items-center gap-2" data-v-eb494437><div class="w-3 h-3 bg-blue-400 rounded-full" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>Chat Node</span></div><div class="flex items-center gap-2" data-v-eb494437><div class="w-3 h-3 bg-accent-purple rounded-full" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>Room Server</span></div><div class="flex items-center gap-2" data-v-eb494437><div class="w-3 h-3 bg-secondary rounded-full" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>Hybrid Node</span></div></div></div><div data-v-eb494437><h4 class="text-white font-medium mb-2" data-v-eb494437>Connection Types</h4><div class="space-y-1" data-v-eb494437><div class="flex items-center gap-2" data-v-eb494437><div class="w-8 h-0.5 bg-accent-green" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>Direct Routing</span></div><div class="flex items-center gap-2" data-v-eb494437><div class="w-8 h-0.5 bg-secondary" style="background-image:repeating-linear-gradient(to right, #FFC246 0, #FFC246 6px, transparent 6px, transparent 10px);" data-v-eb494437></div><span class="text-dark-text" data-v-eb494437>Flood Routing</span></div></div></div></div>',1))])):bs("",!0),(zi(!0),Vi(Ou,null,sf(re.value,([fa,Li])=>(zi(),Vi("div",{key:fa,class:"bg-dark-card/30 backdrop-blur border border-white/10 rounded-[15px] overflow-hidden"},[Re("div",jat,[Re("h2",Uat,[nc(aa(Li)+" ",1),Re("span",Vat,"("+aa(j.value[fa]?.length||0)+")",1)]),Re("div",{class:Xs(["flex items-center gap-2 text-sm",z[parseInt(fa)]])},[Re("div",{class:"w-2 h-2 rounded-full",style:av(`background-color: ${Ft(parseInt(fa))}`)},null,4),nc(" "+aa(Li),1)],2)]),Re("div",Hat,[Re("table",Wat,[ia[5]||(ia[5]=Re("thead",null,[Re("tr",{class:"bg-dark-bg/50"},[Re("th",{class:"text-left text-dark-text text-xs font-medium py-3 px-6 border-b border-white/5"},"Node Name"),Re("th",{class:"text-left text-dark-text text-xs font-medium py-3 px-6 border-b border-white/5"},"Public Key"),Re("th",{class:"text-left text-dark-text text-xs font-medium py-3 px-6 border-b border-white/5"},"Location"),Re("th",{class:"text-left text-dark-text text-xs font-medium py-3 px-6 border-b border-white/5"},"Distance"),Re("th",{ Line truncated
* @kurkle/color v0.3.4
* https://github.com/kurkle/color#readme
* (c) 2024 Jukka Kurkela
* Released under the MIT License
*/function nw(d){return d+.5|0}const u1=(d,l,z)=>Math.max(Math.min(d,z),l);function h2(d){return u1(nw(d*2.55),0,255)}function p1(d){return u1(nw(d*255),0,255)}function Xg(d){return u1(nw(d/2.55)/100,0,1)}function UL(d){return u1(nw(d*100),0,100)}const am={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},mA=[..."0123456789ABCDEF"],oot=d=>mA[d&15],sot=d=>mA[(d&240)>>4]+mA[d&15],S5=d=>(d&240)>>4===(d&15),lot=d=>S5(d.r)&&S5(d.g)&&S5(d.b)&&S5(d.a);function uot(d){var l=d.length,z;return d[0]==="#"&&(l===4||l===5?z={r:255&am[d[1]]*17,g:255&am[d[2]]*17,b:255&am[d[3]]*17,a:l===5?am[d[4]]*17:255}:(l===7||l===9)&&(z={r:am[d[1]]<<4|am[d[2]],g:am[d[3]]<<4|am[d[4]],b:am[d[5]]<<4|am[d[6]],a:l===9?am[d[7]]<<4|am[d[8]]:255})),z}const cot=(d,l)=>d<255?l(d):"";function hot(d){var l=lot(d)?oot:sot;return d?"#"+l(d.r)+l(d.g)+l(d.b)+cot(d.a,l):void 0}const fot=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function PO(d,l,z){const j=l*Math.min(z,1-z),J=(mt,kt=(mt+d/30)%12)=>z-j*Math.max(Math.min(kt-3,9-kt,1),-1);return[J(0),J(8),J(4)]}function dot(d,l,z){const j=(J,mt=(J+d/60)%6)=>z-z*l*Math.max(Math.min(mt,4-mt,1),0);return[j(5),j(3),j(1)]}function pot(d,l,z){const j=PO(d,1,.5);let J;for(l+z>1&&(J=1/(l+z),l*=J,z*=J),J=0;J<3;J++)j[J]*=1-l-z,j[J]+=l;return j}function mot(d,l,z,j,J){return d===J?(l-z)/j+(l<z?6:0):l===J?(z-d)/j+2:(d-l)/j+4}function eM(d){const z=d.r/255,j=d.g/255,J=d.b/255,mt=Math.max(z,j,J),kt=Math.min(z,j,J),Dt=(mt+kt)/2;let Gt,re,pe;return mt!==kt&&(pe=mt-kt,re=Dt>.5?pe/(2-mt-kt):pe/(mt+kt),Gt=mot(z,j,J,pe,mt),Gt=Gt*60+.5),[Gt|0,re||0,Dt]}function rM(d,l,z,j){return(Array.isArray(l)?d(l[0],l[1],l[2]):d(l,z,j)).map(p1)}function nM(d,l,z){return rM(PO,d,l,z)}function got(d,l,z){return rM(pot,d,l,z)}function vot(d,l,z){return rM(dot,d,l,z)}function zO(d){return(d%360+360)%360}function yot(d){const l=fot.exec(d);let z=255,j;if(!l)return;l[5]!==j&&(z=l[6]?h2(+l[5]):p1(+l[5]));const J=zO(+l[2]),mt=+l[3]/100,kt=+l[4]/100;return l[1]==="hwb"?j=got(J,mt,kt):l[1]==="hsv"?j=vot(J,mt,kt):j=nM(J,mt,kt),{r:j[0],g:j[1],b:j[2],a:z}}function xot(d,l){var z=eM(d);z[0]=zO(z[0]+l),z=nM(z),d.r=z[0],d.g=z[1],d.b=z[2]}function _ot(d){if(!d)return;const l=eM(d),z=l[0],j=UL(l[1]),J=UL(l[2]);return d.a<255?`hsla(${z}, ${j}%, ${J}%, ${Xg(d.a)})`:`hsl(${z}, ${j}%, ${J}%)`}const VL={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},HL={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function bot(){const d={},l=Object.keys(HL),z=Object.keys(VL);let jLine truncated
*/function nw(d){return d+.5|0}const u1=(d,l,z)=>Math.max(Math.min(d,z),l);function h2(d){return u1(nw(d*2.55),0,255)}function p1(d){return u1(nw(d*255),0,255)}function Xg(d){return u1(nw(d/2.55)/100,0,1)}function UL(d){return u1(nw(d*100),0,100)}const am={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},mA=[..."0123456789ABCDEF"],lot=d=>mA[d&15],uot=d=>mA[(d&240)>>4]+mA[d&15],S5=d=>(d&240)>>4===(d&15),cot=d=>S5(d.r)&&S5(d.g)&&S5(d.b)&&S5(d.a);function hot(d){var l=d.length,z;return d[0]==="#"&&(l===4||l===5?z={r:255&am[d[1]]*17,g:255&am[d[2]]*17,b:255&am[d[3]]*17,a:l===5?am[d[4]]*17:255}:(l===7||l===9)&&(z={r:am[d[1]]<<4|am[d[2]],g:am[d[3]]<<4|am[d[4]],b:am[d[5]]<<4|am[d[6]],a:l===9?am[d[7]]<<4|am[d[8]]:255})),z}const fot=(d,l)=>d<255?l(d):"";function dot(d){var l=cot(d)?lot:uot;return d?"#"+l(d.r)+l(d.g)+l(d.b)+fot(d.a,l):void 0}const pot=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function PO(d,l,z){const j=l*Math.min(z,1-z),J=(mt,kt=(mt+d/30)%12)=>z-j*Math.max(Math.min(kt-3,9-kt,1),-1);return[J(0),J(8),J(4)]}function mot(d,l,z){const j=(J,mt=(J+d/60)%6)=>z-z*l*Math.max(Math.min(mt,4-mt,1),0);return[j(5),j(3),j(1)]}function got(d,l,z){const j=PO(d,1,.5);let J;for(l+z>1&&(J=1/(l+z),l*=J,z*=J),J=0;J<3;J++)j[J]*=1-l-z,j[J]+=l;return j}function vot(d,l,z,j,J){return d===J?(l-z)/j+(l<z?6:0):l===J?(z-d)/j+2:(d-l)/j+4}function eM(d){const z=d.r/255,j=d.g/255,J=d.b/255,mt=Math.max(z,j,J),kt=Math.min(z,j,J),Dt=(mt+kt)/2;let Gt,re,pe;return mt!==kt&&(pe=mt-kt,re=Dt>.5?pe/(2-mt-kt):pe/(mt+kt),Gt=vot(z,j,J,pe,mt),Gt=Gt*60+.5),[Gt|0,re||0,Dt]}function rM(d,l,z,j){return(Array.isArray(l)?d(l[0],l[1],l[2]):d(l,z,j)).map(p1)}function nM(d,l,z){return rM(PO,d,l,z)}function yot(d,l,z){return rM(got,d,l,z)}function xot(d,l,z){return rM(mot,d,l,z)}function zO(d){return(d%360+360)%360}function _ot(d){const l=pot.exec(d);let z=255,j;if(!l)return;l[5]!==j&&(z=l[6]?h2(+l[5]):p1(+l[5]));const J=zO(+l[2]),mt=+l[3]/100,kt=+l[4]/100;return l[1]==="hwb"?j=yot(J,mt,kt):l[1]==="hsv"?j=xot(J,mt,kt):j=nM(J,mt,kt),{r:j[0],g:j[1],b:j[2],a:z}}function bot(d,l){var z=eM(d);z[0]=zO(z[0]+l),z=nM(z),d.r=z[0],d.g=z[1],d.b=z[2]}function wot(d){if(!d)return;const l=eM(d),z=l[0],j=UL(l[1]),J=UL(l[2]);return d.a<255?`hsla(${z}, ${j}%, ${J}%, ${Xg(d.a)})`:`hsl(${z}, ${j}%, ${J}%)`}const VL={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},HL={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function kot(){const d={},l=Object.keys(HL),z=Object.keys(VL);let jLine truncated
* Chart.js v4.5.1
* https://www.chartjs.org
* (c) 2025 Chart.js Contributors
* Released under the MIT License
*/function $g(){}const Eot=(()=>{let d=0;return()=>d++})();function Rh(d){return d==null}function Xd(d){if(Array.isArray&&Array.isArray(d))return!0;const l=Object.prototype.toString.call(d);return l.slice(0,7)==="[object"&&l.slice(-6)==="Array]"}function Ec(d){return d!==null&&Object.prototype.toString.call(d)==="[object Object]"}function Qp(d){return(typeof d=="number"||d instanceof Number)&&isFinite(+d)}function eg(d,l){return Qp(d)?d:l}function cc(d,l){return typeof d>"u"?l:d}const Cot=(d,l)=>typeof d=="string"&&d.endsWith("%")?parseFloat(d)/100:+d/l,OO=(d,l)=>typeof d=="string"&&d.endsWith("%")?parseFloat(d)/100*l:+d;function Df(d,l,z){if(d&&typeof d.call=="function")return d.apply(z,l)}function Kh(d,l,z,j){let J,mt,kt;if(Xd(d))for(mt=d.length,J=0;J<mt;J++)l.call(z,d[J],J);else if(Ec(d))for(kt=Object.keys(d),mt=kt.length,J=0;J<mt;J++)l.call(z,d[kt[J]],kt[J])}function h4(d,l){let z,j,J,mt;if(!d||!l||d.length!==l.length)return!1;for(z=0,j=d.length;z<j;++z)if(J=d[z],mt=l[z],J.datasetIndex!==mt.datasetIndex||J.index!==mt.index)return!1;return!0}function f4(d){if(Xd(d))return d.map(f4);if(Ec(d)){const l=Object.create(null),z=Object.keys(d),j=z.length;let J=0;for(;J<j;++J)l[z[J]]=f4(d[z[J]]);return l}return d}function DO(d){return["__proto__","prototype","constructor"].indexOf(d)===-1}function Lot(d,l,z,j){if(!DO(d))return;const J=l[d],mt=z[d];Ec(J)&&Ec(mt)?j2(J,mt,j):l[d]=f4(mt)}function j2(d,l,z){const j=Xd(l)?l:[l],J=j.length;if(!Ec(d))return d;z=z||{};const mt=z.merger||Lot;let kt;for(let Dt=0;Dt<J;++Dt){if(kt=j[Dt],!Ec(kt))continue;const Gt=Object.keys(kt);for(let re=0,pe=Gt.length;re<pe;++re)mt(Gt[re],d,kt,z)}return d}function T2(d,l){return j2(d,l,{merger:Pot})}function Pot(d,l,z){if(!DO(d))return;const j=l[d],J=z[d];Ec(j)&&Ec(J)?T2(j,J):Object.prototype.hasOwnProperty.call(l,d)||(l[d]=f4(J))}const qL={"":d=>d,x:d=>d.x,y:d=>d.y};function zot(d){const l=d.split("."),z=[];let j="";for(const J of l)j+=J,j.endsWith("\\")?j=j.slice(0,-1)+".":(z.push(j),j="");return z}function Iot(d){const l=zot(d);return z=>{for(const j of l){if(j==="")break;z=z&&z[j]}return z}}function My(d,l){return(qL[l]||(qL[l]=Iot(l)))(d)}function iM(d){return d.charAt(0).toUpperCase()+d.slice(1)}const U2=d=>typeof d<"u",v1=d=>typeof d=="function",ZL=(d,l)=>{if(d.size!==l.size)return!1;for(const z of d)if(!l.has(z))return!1;return!0};function Oot(d){return d.type==="mouseup"||d.type==="click"||d.type==="contextmenu"}const Jh=Math.PI,od=2*Jh,Dot=od+Jh,d4=Number.POSITIVE_INFINITY,Fot=Jh/180,ap=Jh/2,sy=Jh/4,$L=Jh*2/3,FO=Math.log10,cg=Math.sign;function A2(d,l,z){return Math.abs(d-l)<z}function GL(d){const l=Math.round(d);d=A2(d,l,d/1e3)?l:d;const z=Math.pow(10,Math.floor(FO(d))),j=d/z;return(j<=1?1:j<=2?2:j<=5?5:10)*z}function Rot(d){const l=[],z=Math.sqrt(d);let j;for(j=1;j<z;j++)d%j===0&&(l.push(j),l.push(d/j));return z===(z|0)&&l.push(z),l.sort((J,mt)=>J-mt).pop(),l}function Bot(d){return typeof d=="symbol"||typeof d=="object"&&d!==null&&!(Symbol.toPrimitive in d||"toString"in d||"valueOf"in d)}function V2(d){return!Bot(d)&&!isNaN(parseFloat(d))&&isFinite(d)}function Not(d,l){const z=Math.round(d);return z-l<=d&&z+l>=d}function jot(d,l,z){let j,J,mt;for(j=0,J=d.length;j<J;j++)mt=d[j][z],isNaN(mt)||(l.min=Math.min(l.min,mt),l.max=Math.max(l.max,mt))}function tv(d){return d*(Jh/180)}function Uot(d){return d*(180/Jh)}function YL(d){if(!Qp(d))return;let l=1,z=0;for(;Math.round(d*l)/l!==d;)l*=10,z++;return z}function RO(d,l){const z=l.x-d.x,j=l.y-d.y,J=Math.sqrt(z*z+j*j);let mt=Math.atan2(j,z);return mt<-.5*Jh&&(mt+=od),{angle:mt,distance:J}}function gA(d,l){return Math.sqrt(Math.pow(l.x-d.x,2)+Math.pow(l.y-d.y,2))}function Vot(d,l){return(d-l+Dot)%od-Jh}function q0(d){return(d%od+od)%od}function H2(d,l,z,j){const J=q0(d),mt=q0(l),kt=q0(z),Dt=q0(mt-J),Gt=q0(kt-J),re=q0(J-mt),pe=q0(J-kt);return J===mt||J===kt||j&&mt===kt||Dt>Gt&&re<pe}function Xp(d,l,z){return Math.max(l,Math.min(z,d))}function Hot(d){return Xp(d,-32768,32767)}function ev(d,l,z,j=1e-6){return d>=Math.min(l,z)-j&&d<=Math.max(l,z)+j}function aM(d,l,z){z=z||(kt=>d[kt]<l);let j=d.length-1,J=0,mt;for(;j-J>1;)mt=J+j>>1,z(mt)?J=mt:j=mt;return{lo:J,hi:j}}const yy=(d,l,z,j)=>aM(d,z,j?J=>{const mt=d[J][l];return mt<z||mt===z&&d[J+1][l]===z}:J=>d[J][l]<z),Wot=(d,l,z)=>aM(d,z,j=>d[j][l]>=z);function qot(d,l,z){let j=0,J=d.length;for(;j<J&&d[j]<l;)j++;for(;J>j&&d[J-1]>z;)J--;return j>0||J<d.length?d.slice(j,J):d}const BO=["push","pop","shift","splice","unshift"];function Zot(d,l){if(d._chartjs){d._chartjs.listeners.push(l);return}Object.defineProperty(d,"_chartjs",{configurable:!0,enumerable:!1,value:{listeners:[l]}}),BO.forEach(z=>{const j="_onData"+iM(z),J=d[z];Object.defineProperty(d,z,{configurable:!0,enumerable:!1,value(...mt){const kt=J.apply(this,mt);return d._chartjs.listeners.forEach(Dt=>{typeof Dt[j]=="function"&&Dt[j](...mt)}),kt}})})}function KL(d,l){const z=d._chartjs;if(!z)return;const j=z.listeners,J=j.indexOf(l);J!==-1&&j.splice(J,1),!(j.length>0)&&(BO.forEach(mt=>{deleLine truncated
*/function $g(){}const Lot=(()=>{let d=0;return()=>d++})();function Rh(d){return d==null}function Xd(d){if(Array.isArray&&Array.isArray(d))return!0;const l=Object.prototype.toString.call(d);return l.slice(0,7)==="[object"&&l.slice(-6)==="Array]"}function Ec(d){return d!==null&&Object.prototype.toString.call(d)==="[object Object]"}function Qp(d){return(typeof d=="number"||d instanceof Number)&&isFinite(+d)}function eg(d,l){return Qp(d)?d:l}function cc(d,l){return typeof d>"u"?l:d}const Pot=(d,l)=>typeof d=="string"&&d.endsWith("%")?parseFloat(d)/100:+d/l,OO=(d,l)=>typeof d=="string"&&d.endsWith("%")?parseFloat(d)/100*l:+d;function Df(d,l,z){if(d&&typeof d.call=="function")return d.apply(z,l)}function Kh(d,l,z,j){let J,mt,kt;if(Xd(d))for(mt=d.length,J=0;J<mt;J++)l.call(z,d[J],J);else if(Ec(d))for(kt=Object.keys(d),mt=kt.length,J=0;J<mt;J++)l.call(z,d[kt[J]],kt[J])}function h4(d,l){let z,j,J,mt;if(!d||!l||d.length!==l.length)return!1;for(z=0,j=d.length;z<j;++z)if(J=d[z],mt=l[z],J.datasetIndex!==mt.datasetIndex||J.index!==mt.index)return!1;return!0}function f4(d){if(Xd(d))return d.map(f4);if(Ec(d)){const l=Object.create(null),z=Object.keys(d),j=z.length;let J=0;for(;J<j;++J)l[z[J]]=f4(d[z[J]]);return l}return d}function DO(d){return["__proto__","prototype","constructor"].indexOf(d)===-1}function zot(d,l,z,j){if(!DO(d))return;const J=l[d],mt=z[d];Ec(J)&&Ec(mt)?j2(J,mt,j):l[d]=f4(mt)}function j2(d,l,z){const j=Xd(l)?l:[l],J=j.length;if(!Ec(d))return d;z=z||{};const mt=z.merger||zot;let kt;for(let Dt=0;Dt<J;++Dt){if(kt=j[Dt],!Ec(kt))continue;const Gt=Object.keys(kt);for(let re=0,pe=Gt.length;re<pe;++re)mt(Gt[re],d,kt,z)}return d}function T2(d,l){return j2(d,l,{merger:Iot})}function Iot(d,l,z){if(!DO(d))return;const j=l[d],J=z[d];Ec(j)&&Ec(J)?T2(j,J):Object.prototype.hasOwnProperty.call(l,d)||(l[d]=f4(J))}const qL={"":d=>d,x:d=>d.x,y:d=>d.y};function Oot(d){const l=d.split("."),z=[];let j="";for(const J of l)j+=J,j.endsWith("\\")?j=j.slice(0,-1)+".":(z.push(j),j="");return z}function Dot(d){const l=Oot(d);return z=>{for(const j of l){if(j==="")break;z=z&&z[j]}return z}}function My(d,l){return(qL[l]||(qL[l]=Dot(l)))(d)}function iM(d){return d.charAt(0).toUpperCase()+d.slice(1)}const U2=d=>typeof d<"u",v1=d=>typeof d=="function",ZL=(d,l)=>{if(d.size!==l.size)return!1;for(const z of d)if(!l.has(z))return!1;return!0};function Fot(d){return d.type==="mouseup"||d.type==="click"||d.type==="contextmenu"}const Jh=Math.PI,od=2*Jh,Rot=od+Jh,d4=Number.POSITIVE_INFINITY,Bot=Jh/180,ap=Jh/2,sy=Jh/4,$L=Jh*2/3,FO=Math.log10,cg=Math.sign;function A2(d,l,z){return Math.abs(d-l)<z}function GL(d){const l=Math.round(d);d=A2(d,l,d/1e3)?l:d;const z=Math.pow(10,Math.floor(FO(d))),j=d/z;return(j<=1?1:j<=2?2:j<=5?5:10)*z}function Not(d){const l=[],z=Math.sqrt(d);let j;for(j=1;j<z;j++)d%j===0&&(l.push(j),l.push(d/j));return z===(z|0)&&l.push(z),l.sort((J,mt)=>J-mt).pop(),l}function jot(d){return typeof d=="symbol"||typeof d=="object"&&d!==null&&!(Symbol.toPrimitive in d||"toString"in d||"valueOf"in d)}function V2(d){return!jot(d)&&!isNaN(parseFloat(d))&&isFinite(d)}function Uot(d,l){const z=Math.round(d);return z-l<=d&&z+l>=d}function Vot(d,l,z){let j,J,mt;for(j=0,J=d.length;j<J;j++)mt=d[j][z],isNaN(mt)||(l.min=Math.min(l.min,mt),l.max=Math.max(l.max,mt))}function tv(d){return d*(Jh/180)}function Hot(d){return d*(180/Jh)}function YL(d){if(!Qp(d))return;let l=1,z=0;for(;Math.round(d*l)/l!==d;)l*=10,z++;return z}function RO(d,l){const z=l.x-d.x,j=l.y-d.y,J=Math.sqrt(z*z+j*j);let mt=Math.atan2(j,z);return mt<-.5*Jh&&(mt+=od),{angle:mt,distance:J}}function gA(d,l){return Math.sqrt(Math.pow(l.x-d.x,2)+Math.pow(l.y-d.y,2))}function Wot(d,l){return(d-l+Rot)%od-Jh}function q0(d){return(d%od+od)%od}function H2(d,l,z,j){const J=q0(d),mt=q0(l),kt=q0(z),Dt=q0(mt-J),Gt=q0(kt-J),re=q0(J-mt),pe=q0(J-kt);return J===mt||J===kt||j&&mt===kt||Dt>Gt&&re<pe}function Xp(d,l,z){return Math.max(l,Math.min(z,d))}function qot(d){return Xp(d,-32768,32767)}function ev(d,l,z,j=1e-6){return d>=Math.min(l,z)-j&&d<=Math.max(l,z)+j}function aM(d,l,z){z=z||(kt=>d[kt]<l);let j=d.length-1,J=0,mt;for(;j-J>1;)mt=J+j>>1,z(mt)?J=mt:j=mt;return{lo:J,hi:j}}const yy=(d,l,z,j)=>aM(d,z,j?J=>{const mt=d[J][l];return mt<z||mt===z&&d[J+1][l]===z}:J=>d[J][l]<z),Zot=(d,l,z)=>aM(d,z,j=>d[j][l]>=z);function $ot(d,l,z){let j=0,J=d.length;for(;j<J&&d[j]<l;)j++;for(;J>j&&d[J-1]>z;)J--;return j>0||J<d.length?d.slice(j,J):d}const BO=["push","pop","shift","splice","unshift"];function Got(d,l){if(d._chartjs){d._chartjs.listeners.push(l);return}Object.defineProperty(d,"_chartjs",{configurable:!0,enumerable:!1,value:{listeners:[l]}}),BO.forEach(z=>{const j="_onData"+iM(z),J=d[z];Object.defineProperty(d,z,{configurable:!0,enumerable:!1,value(...mt){const kt=J.apply(this,mt);return d._chartjs.listeners.forEach(Dt=>{typeof Dt[j]=="function"&&Dt[j](...mt)}),kt}})})}function KL(d,l){const z=d._chartjs;if(!z)return;const j=z.listeners,J=j.indexOf(l);J!==-1&&j.splice(J,1),!(j.length>0)&&(BO.forEach(mt=>{deleLine truncated
* Chart.js v4.5.1
* https://www.chartjs.org
* (c) 2025 Chart.js Contributors
* Released under the MIT License
*/class Xst{constructor(){this._request=null,this._charts=new Map,this._running=!1,this._lastDate=void 0}_notify(l,z,j,J){const mt=z.listeners[J],kt=z.duration;mt.forEach(Dt=>Dt({chart:l,initial:z.initial,numSteps:kt,currentStep:Math.min(j-z.start,kt)}))}_refresh(){this._request||(this._running=!0,this._request=jO.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(l=Date.now()){let z=0;this._charts.forEach((j,J)=>{if(!j.running||!j.items.length)return;const mt=j.items;let kt=mt.length-1,Dt=!1,Gt;for(;kt>=0;--kt)Gt=mt[kt],Gt._active?(Gt._total>j.duration&&(j.duration=Gt._total),Gt.tick(l),Dt=!0):(mt[kt]=mt[mt.length-1],mt.pop());Dt&&(J.draw(),this._notify(J,j,l,"progress")),mt.length||(j.running=!1,this._notify(J,j,l,"complete"),j.initial=!1),z+=mt.length}),this._lastDate=l,z===0&&(this._running=!1)}_getAnims(l){const z=this._charts;let j=z.get(l);return j||(j={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},z.set(l,j)),j}listen(l,z,j){this._getAnims(l).listeners[z].push(j)}add(l,z){!z||!z.length||this._getAnims(l).items.push(...z)}has(l){return this._getAnims(l).items.length>0}start(l){const z=this._charts.get(l);z&&(z.running=!0,z.start=Date.now(),z.duration=z.items.reduce((j,J)=>Math.max(j,J._duration),0),this._refresh())}running(l){if(!this._running)return!1;const z=this._charts.get(l);return!(!z||!z.running||!z.items.length)}stop(l){const z=this._charts.get(l);if(!z||!z.items.length)return;const j=z.items;let J=j.length-1;for(;J>=0;--J)j[J].cancel();z.items=[],this._notify(l,z,Date.now(),"complete")}remove(l){return this._charts.delete(l)}}var Gg=new Xst;const cP="transparent",Jst={boolean(d,l,z){return z>.5?l:d},color(d,l,z){const j=QL(d||cP),J=j.valid&&QL(l||cP);return J&&J.valid?J.mix(j,z).hexString():l},number(d,l,z){return d+(l-d)*z}};class Qst{constructor(l,z,j,J){const mt=z[j];J=P5([l.to,J,mt,l.from]);const kt=P5([l.from,mt,J]);this._active=!0,this._fn=l.fn||Jst[l.type||typeof kt],this._easing=M2[l.easing]||M2.linear,this._start=Math.floor(Date.now()+(l.delay||0)),this._duration=this._total=Math.floor(l.duration),this._loop=!!l.loop,this._target=z,this._prop=j,this._from=kt,this._to=J,this._promises=void 0}active(){return this._active}update(l,z,j){if(this._active){this._notify(!1);const J=this._target[this._prop],mt=j-this._start,kt=this._duration-mt;this._start=j,this._duration=Math.floor(Math.max(kt,l.duration)),this._total+=mt,this._loop=!!l.loop,this._to=P5([l.to,z,J,l.from]),this._from=P5([l.from,J,z])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(l){const z=l-this._start,j=this._duration,J=this._prop,mt=this._from,kt=this._loop,Dt=this._to;let Gt;if(this._active=mt!==Dt&&(kt||z<j),!this._active){this._target[J]=Dt,this._notify(!0);return}if(z<0){this._target[J]=mt;return}Gt=z/j%2,Gt=kt&&Gt>1?2-Gt:Gt,Gt=this._easing(Math.min(1,Math.max(0,Gt))),this._target[J]=this._fn(mt,Dt,Gt)}wait(){const l=this._promises||(this._promises=[]);return new Promise((z,j)=>{l.push({res:z,rej:j})})}_notify(l){const z=l?"res":"rej",j=this._promises||[];for(let J=0;J<j.length;J++)j[J][z]()}}class rD{constructor(l,z){this._chart=l,this._properties=new Map,this.configure(z)}configure(l){if(!Ec(l))return;const z=Object.keys(Bd.animation),j=this._properties;Object.getOwnPropertyNames(l).forEach(J=>{const mt=l[J];if(!Ec(mt))return;const kt={};for(const Dt of z)kt[Dt]=mt[Dt];(Xd(mt.properties)&&mt.properties||[J]).forEach(Dt=>{(Dt===J||!j.has(Dt))&&j.set(Dt,kt)})})}_animateOptions(l,z){const j=z.options,J=elt(l,j);if(!J)return[];const mt=this._createAnimations(J,j);return j.$shared&&tlt(l.options.$animations,j).then(()=>{l.options=j},()=>{}),mt}_createAnimations(l,z){const j=this._properties,J=[],mt=l.$animations||(l.$animations={}),kt=Object.keys(z),Dt=Date.now();let Gt;for(Gt=kt.length-1;Gt>=0;--Gt){const re=kt[Gt];if(re.charAt(0)==="$")continue;if(re==="options"){J.push(...this._animateOptions(l,z));continue}const pe=z[re];let Ne=mt[re];const or=j.get(re);if(Ne)if(or&&Ne.active()){Ne.update(or,pe,Dt);continue}else Ne.cancel();if(!or||!or.duration){l[re]=pe;continue}mt[re]=Ne=new Qst(or,l,re,pe),J.push(Ne)}return J}update(l,z){if(this._properties.size===0){Object.assign(l,z);return}const j=this._createAnimations(l,z);if(j.length)return Gg.add(this._chart,j),!0}}function tlt(d,l){const z=[],j=Object.keys(l);for(let J=0;J<j.length;J++){const mt=d[j[J]];mt&&mt.active()&&z.push(mt.wait())}return Promise.all(z)}function elt(d,l){if(!l)return;let z=d.options;if(!z){d.options=l;return}return z.$shared&&(d.options=z=Object.assign({},z,{$shared:!1,$animations:{}})),z}function hP(d,l){const z=d&&d.options||{},j=z.reverse,J=z.min===void 0?l:0,mt=z.max===void 0?l:0;return{start:j?mt:J,end:j?J:mt}}function rlt(d,l,z){if(z===!1)return!1;const j=hP(d,z),J=hP(l,z);return{top:J.end,right:j.end,bottom:J.start,left:j.start}}function nlt(d){let l,z,j,J;return Ec(d)?(l=d.top,z=d.right,j=d.bottom,J=d.left):l=z=j=J=d,{top:l,right:z,bottoLine truncated
*/class Qst{constructor(){this._request=null,this._charts=new Map,this._running=!1,this._lastDate=void 0}_notify(l,z,j,J){const mt=z.listeners[J],kt=z.duration;mt.forEach(Dt=>Dt({chart:l,initial:z.initial,numSteps:kt,currentStep:Math.min(j-z.start,kt)}))}_refresh(){this._request||(this._running=!0,this._request=jO.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(l=Date.now()){let z=0;this._charts.forEach((j,J)=>{if(!j.running||!j.items.length)return;const mt=j.items;let kt=mt.length-1,Dt=!1,Gt;for(;kt>=0;--kt)Gt=mt[kt],Gt._active?(Gt._total>j.duration&&(j.duration=Gt._total),Gt.tick(l),Dt=!0):(mt[kt]=mt[mt.length-1],mt.pop());Dt&&(J.draw(),this._notify(J,j,l,"progress")),mt.length||(j.running=!1,this._notify(J,j,l,"complete"),j.initial=!1),z+=mt.length}),this._lastDate=l,z===0&&(this._running=!1)}_getAnims(l){const z=this._charts;let j=z.get(l);return j||(j={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},z.set(l,j)),j}listen(l,z,j){this._getAnims(l).listeners[z].push(j)}add(l,z){!z||!z.length||this._getAnims(l).items.push(...z)}has(l){return this._getAnims(l).items.length>0}start(l){const z=this._charts.get(l);z&&(z.running=!0,z.start=Date.now(),z.duration=z.items.reduce((j,J)=>Math.max(j,J._duration),0),this._refresh())}running(l){if(!this._running)return!1;const z=this._charts.get(l);return!(!z||!z.running||!z.items.length)}stop(l){const z=this._charts.get(l);if(!z||!z.items.length)return;const j=z.items;let J=j.length-1;for(;J>=0;--J)j[J].cancel();z.items=[],this._notify(l,z,Date.now(),"complete")}remove(l){return this._charts.delete(l)}}var Gg=new Qst;const cP="transparent",tlt={boolean(d,l,z){return z>.5?l:d},color(d,l,z){const j=QL(d||cP),J=j.valid&&QL(l||cP);return J&&J.valid?J.mix(j,z).hexString():l},number(d,l,z){return d+(l-d)*z}};class elt{constructor(l,z,j,J){const mt=z[j];J=P5([l.to,J,mt,l.from]);const kt=P5([l.from,mt,J]);this._active=!0,this._fn=l.fn||tlt[l.type||typeof kt],this._easing=M2[l.easing]||M2.linear,this._start=Math.floor(Date.now()+(l.delay||0)),this._duration=this._total=Math.floor(l.duration),this._loop=!!l.loop,this._target=z,this._prop=j,this._from=kt,this._to=J,this._promises=void 0}active(){return this._active}update(l,z,j){if(this._active){this._notify(!1);const J=this._target[this._prop],mt=j-this._start,kt=this._duration-mt;this._start=j,this._duration=Math.floor(Math.max(kt,l.duration)),this._total+=mt,this._loop=!!l.loop,this._to=P5([l.to,z,J,l.from]),this._from=P5([l.from,J,z])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(l){const z=l-this._start,j=this._duration,J=this._prop,mt=this._from,kt=this._loop,Dt=this._to;let Gt;if(this._active=mt!==Dt&&(kt||z<j),!this._active){this._target[J]=Dt,this._notify(!0);return}if(z<0){this._target[J]=mt;return}Gt=z/j%2,Gt=kt&&Gt>1?2-Gt:Gt,Gt=this._easing(Math.min(1,Math.max(0,Gt))),this._target[J]=this._fn(mt,Dt,Gt)}wait(){const l=this._promises||(this._promises=[]);return new Promise((z,j)=>{l.push({res:z,rej:j})})}_notify(l){const z=l?"res":"rej",j=this._promises||[];for(let J=0;J<j.length;J++)j[J][z]()}}class rD{constructor(l,z){this._chart=l,this._properties=new Map,this.configure(z)}configure(l){if(!Ec(l))return;const z=Object.keys(Bd.animation),j=this._properties;Object.getOwnPropertyNames(l).forEach(J=>{const mt=l[J];if(!Ec(mt))return;const kt={};for(const Dt of z)kt[Dt]=mt[Dt];(Xd(mt.properties)&&mt.properties||[J]).forEach(Dt=>{(Dt===J||!j.has(Dt))&&j.set(Dt,kt)})})}_animateOptions(l,z){const j=z.options,J=nlt(l,j);if(!J)return[];const mt=this._createAnimations(J,j);return j.$shared&&rlt(l.options.$animations,j).then(()=>{l.options=j},()=>{}),mt}_createAnimations(l,z){const j=this._properties,J=[],mt=l.$animations||(l.$animations={}),kt=Object.keys(z),Dt=Date.now();let Gt;for(Gt=kt.length-1;Gt>=0;--Gt){const re=kt[Gt];if(re.charAt(0)==="$")continue;if(re==="options"){J.push(...this._animateOptions(l,z));continue}const pe=z[re];let Ne=mt[re];const or=j.get(re);if(Ne)if(or&&Ne.active()){Ne.update(or,pe,Dt);continue}else Ne.cancel();if(!or||!or.duration){l[re]=pe;continue}mt[re]=Ne=new elt(or,l,re,pe),J.push(Ne)}return J}update(l,z){if(this._properties.size===0){Object.assign(l,z);return}const j=this._createAnimations(l,z);if(j.length)return Gg.add(this._chart,j),!0}}function rlt(d,l){const z=[],j=Object.keys(l);for(let J=0;J<j.length;J++){const mt=d[j[J]];mt&&mt.active()&&z.push(mt.wait())}return Promise.all(z)}function nlt(d,l){if(!l)return;let z=d.options;if(!z){d.options=l;return}return z.$shared&&(d.options=z=Object.assign({},z,{$shared:!1,$animations:{}})),z}function hP(d,l){const z=d&&d.options||{},j=z.reverse,J=z.min===void 0?l:0,mt=z.max===void 0?l:0;return{start:j?mt:J,end:j?J:mt}}function ilt(d,l,z){if(z===!1)return!1;const j=hP(d,z),J=hP(l,z);return{top:J.end,right:j.end,bottom:J.start,left:j.start}}function alt(d){let l,z,j,J;return Ec(d)?(l=d.top,z=d.right,j=d.bottom,J=d.left):l=z=j=J=d,{top:l,right:z,bottoLine truncated
`)>-1?d.split(`
`):d}function Lct(d,l){const{element:z,datasetIndex:j,index:J}=l,mt=d.getDatasetMeta(j).controller,{label:kt,value:Dt}=mt.getLabelAndValue(J);return{chart:d,label:kt,parsed:mt.getParsed(J),raw:d.data.datasets[j].data[J],formattedValue:Dt,dataset:mt.getDataset(),dataIndex:J,datasetIndex:j,element:z}}function ZP(d,l){const z=d.chart.ctx,{body:j,footer:J,title:mt}=d,{boxWidth:kt,boxHeight:Dt}=l,Gt=Jp(l.bodyFont),re=Jp(l.titleFont),pe=Jp(l.footerFont),Ne=mt.length,or=J.length,_r=j.length,Fr=fm(l.padding);let zr=Fr.height,Wr=0,An=j.reduce((ei,jn)=>ei+jn.before.length+jn.lines.length+jn.after.length,0);if(An+=d.beforeBody.length+d.afterBody.length,Ne&&(zr+=Ne*re.lineHeight+(Ne-1)*l.titleSpacing+l.titleMarginBottom),An){const ei=l.displayColors?Math.max(Dt,Gt.lineHeight):Gt.lineHeight;zr+=_r*ei+(An-_r)*Gt.lineHeight+(An-1)*l.bodySpacing}or&&(zr+=l.footerMarginTop+or*pe.lineHeight+(or-1)*l.footerSpacing);let Ft=0;const kn=function(ei){Wr=Math.max(Wr,z.measureText(ei).width+Ft)};return z.save(),z.font=re.string,Kh(d.title,kn),z.font=Gt.string,Kh(d.beforeBody.concat(d.afterBody),kn),Ft=l.displayColors?kt+2+l.boxPadding:0,Kh(j,ei=>{Kh(ei.before,kn),Kh(ei.lines,kn),Kh(ei.after,kn)}),Ft=0,z.font=pe.string,Kh(d.footer,kn),z.restore(),Wr+=Fr.width,{width:Wr,height:zr}}function Pct(d,l){const{y:z,height:j}=l;return z<j/2?"top":z>d.height-j/2?"bottom":"center"}function zct(d,l,z,j){const{x:J,width:mt}=j,kt=z.caretSize+z.caretPadding;if(d==="left"&&J+mt+kt>l.width||d==="right"&&J-mt-kt<0)return!0}function Ict(d,l,z,j){const{x:J,width:mt}=z,{width:kt,chartArea:{left:Dt,right:Gt}}=d;let re="center";return j==="center"?re=J<=(Dt+Gt)/2?"left":"right":J<=mt/2?re="left":J>=kt-mt/2&&(re="right"),zct(re,d,l,z)&&(re="center"),re}function $P(d,l,z){const j=z.yAlign||l.yAlign||Pct(d,z);return{xAlign:z.xAlign||l.xAlign||Ict(d,l,z,j),yAlign:j}}function Oct(d,l){let{x:z,width:j}=d;return l==="right"?z-=j:l==="center"&&(z-=j/2),z}function Dct(d,l,z){let{y:j,height:J}=d;return l==="top"?j+=z:l==="bottom"?j-=J+z:j-=J/2,j}function GP(d,l,z,j){const{caretSize:J,caretPadding:mt,cornerRadius:kt}=d,{xAlign:Dt,yAlign:Gt}=z,re=J+mt,{topLeft:pe,topRight:Ne,bottomLeft:or,bottomRight:_r}=s_(kt);let Fr=Oct(l,Dt);const zr=Dct(l,Gt,re);return Gt==="center"?Dt==="left"?Fr+=re:Dt==="right"&&(Fr-=re):Dt==="left"?Fr-=Math.max(pe,or)+J:Dt==="right"&&(Fr+=Math.max(Ne,_r)+J),{x:Xp(Fr,0,j.width-l.width),y:Xp(zr,0,j.height-l.height)}}function B5(d,l,z){const j=fm(z.padding);return l==="center"?d.x+d.width/2:l==="right"?d.x+d.width-j.right:d.x+j.left}function YP(d){return ng([],Yg(d))}function Fct(d,l,z){return Cy(d,{tooltip:l,tooltipItems:z,type:"tooltip"})}function KP(d,l){const z=l&&l.dataset&&l.dataset.tooltip&&l.dataset.tooltip.callbacks;return z?d.override(z):d}const wD={beforeTitle:$g,title(d){if(d.length>0){const l=d[0],z=l.chart.data.labels,j=z?z.length:0;if(this&&this.options&&this.options.mode==="dataset")return l.dataset.label||"";if(l.label)return l.label;if(j>0&&l.dataIndex<j)return z[l.dataIndex]}return""},afterTitle:$g,beforeBody:$g,beforeLabel:$g,label(d){if(this&&this.options&&this.options.mode==="dataset")return d.label+": "+d.formattedValue||d.formattedValue;let l=d.dataset.label||"";l&&(l+=": ");const z=d.formattedValue;return Rh(z)||(l+=z),l},labelColor(d){const z=d.chart.getDatasetMeta(d.datasetIndex).controller.getStyle(d.dataIndex);return{borderColor:z.borderColor,backgroundColor:z.backgroundColor,borderWidth:z.borderWidth,borderDash:z.borderDash,borderDashOffset:z.borderDashOffset,borderRadius:0}},labelTextColor(){return this.options.bodyColor},labelPointStyle(d){const z=d.chart.getDatasetMeta(d.datasetIndex).controller.getStyle(d.dataIndex);return{pointStyle:z.pointStyle,rotation:z.rotation}},afterLabel:$g,afterBody:$g,beforeFooter:$g,footer:$g,afterFooter:$g};function A0(d,l,z,j){const J=d[l].call(z,j);return typeof J>"u"?wD[l].call(z,j):J}class XP extends lv{static positioners=p2;constructor(l){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=l.chart,this.options=l.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(l){this.options=l,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const l=this._cachedAnimations;if(l)return l;const z=this.chart,j=this.options.setContext(this.getContext()),J=j.enabled&&z.options.animation&&j.animations,mt=new rD(this.chart,J);return J._cacheable&&(this._cachedAnimations=Object.freeze(mt)),mt}getContext(){return this.$context||(this.$context=Fct(this.chart.getContext(),this,this._tooltipItems))}getTitle(l,z){const{callbacks:j}=z,J=A0(j Line truncated
`):d}function zct(d,l){const{element:z,datasetIndex:j,index:J}=l,mt=d.getDatasetMeta(j).controller,{label:kt,value:Dt}=mt.getLabelAndValue(J);return{chart:d,label:kt,parsed:mt.getParsed(J),raw:d.data.datasets[j].data[J],formattedValue:Dt,dataset:mt.getDataset(),dataIndex:J,datasetIndex:j,element:z}}function ZP(d,l){const z=d.chart.ctx,{body:j,footer:J,title:mt}=d,{boxWidth:kt,boxHeight:Dt}=l,Gt=Jp(l.bodyFont),re=Jp(l.titleFont),pe=Jp(l.footerFont),Ne=mt.length,or=J.length,_r=j.length,Fr=fm(l.padding);let zr=Fr.height,Wr=0,An=j.reduce((ei,jn)=>ei+jn.before.length+jn.lines.length+jn.after.length,0);if(An+=d.beforeBody.length+d.afterBody.length,Ne&&(zr+=Ne*re.lineHeight+(Ne-1)*l.titleSpacing+l.titleMarginBottom),An){const ei=l.displayColors?Math.max(Dt,Gt.lineHeight):Gt.lineHeight;zr+=_r*ei+(An-_r)*Gt.lineHeight+(An-1)*l.bodySpacing}or&&(zr+=l.footerMarginTop+or*pe.lineHeight+(or-1)*l.footerSpacing);let Ft=0;const kn=function(ei){Wr=Math.max(Wr,z.measureText(ei).width+Ft)};return z.save(),z.font=re.string,Kh(d.title,kn),z.font=Gt.string,Kh(d.beforeBody.concat(d.afterBody),kn),Ft=l.displayColors?kt+2+l.boxPadding:0,Kh(j,ei=>{Kh(ei.before,kn),Kh(ei.lines,kn),Kh(ei.after,kn)}),Ft=0,z.font=pe.string,Kh(d.footer,kn),z.restore(),Wr+=Fr.width,{width:Wr,height:zr}}function Ict(d,l){const{y:z,height:j}=l;return z<j/2?"top":z>d.height-j/2?"bottom":"center"}function Oct(d,l,z,j){const{x:J,width:mt}=j,kt=z.caretSize+z.caretPadding;if(d==="left"&&J+mt+kt>l.width||d==="right"&&J-mt-kt<0)return!0}function Dct(d,l,z,j){const{x:J,width:mt}=z,{width:kt,chartArea:{left:Dt,right:Gt}}=d;let re="center";return j==="center"?re=J<=(Dt+Gt)/2?"left":"right":J<=mt/2?re="left":J>=kt-mt/2&&(re="right"),Oct(re,d,l,z)&&(re="center"),re}function $P(d,l,z){const j=z.yAlign||l.yAlign||Ict(d,z);return{xAlign:z.xAlign||l.xAlign||Dct(d,l,z,j),yAlign:j}}function Fct(d,l){let{x:z,width:j}=d;return l==="right"?z-=j:l==="center"&&(z-=j/2),z}function Rct(d,l,z){let{y:j,height:J}=d;return l==="top"?j+=z:l==="bottom"?j-=J+z:j-=J/2,j}function GP(d,l,z,j){const{caretSize:J,caretPadding:mt,cornerRadius:kt}=d,{xAlign:Dt,yAlign:Gt}=z,re=J+mt,{topLeft:pe,topRight:Ne,bottomLeft:or,bottomRight:_r}=s_(kt);let Fr=Fct(l,Dt);const zr=Rct(l,Gt,re);return Gt==="center"?Dt==="left"?Fr+=re:Dt==="right"&&(Fr-=re):Dt==="left"?Fr-=Math.max(pe,or)+J:Dt==="right"&&(Fr+=Math.max(Ne,_r)+J),{x:Xp(Fr,0,j.width-l.width),y:Xp(zr,0,j.height-l.height)}}function B5(d,l,z){const j=fm(z.padding);return l==="center"?d.x+d.width/2:l==="right"?d.x+d.width-j.right:d.x+j.left}function YP(d){return ng([],Yg(d))}function Bct(d,l,z){return Cy(d,{tooltip:l,tooltipItems:z,type:"tooltip"})}function KP(d,l){const z=l&&l.dataset&&l.dataset.tooltip&&l.dataset.tooltip.callbacks;return z?d.override(z):d}const wD={beforeTitle:$g,title(d){if(d.length>0){const l=d[0],z=l.chart.data.labels,j=z?z.length:0;if(this&&this.options&&this.options.mode==="dataset")return l.dataset.label||"";if(l.label)return l.label;if(j>0&&l.dataIndex<j)return z[l.dataIndex]}return""},afterTitle:$g,beforeBody:$g,beforeLabel:$g,label(d){if(this&&this.options&&this.options.mode==="dataset")return d.label+": "+d.formattedValue||d.formattedValue;let l=d.dataset.label||"";l&&(l+=": ");const z=d.formattedValue;return Rh(z)||(l+=z),l},labelColor(d){const z=d.chart.getDatasetMeta(d.datasetIndex).controller.getStyle(d.dataIndex);return{borderColor:z.borderColor,backgroundColor:z.backgroundColor,borderWidth:z.borderWidth,borderDash:z.borderDash,borderDashOffset:z.borderDashOffset,borderRadius:0}},labelTextColor(){return this.options.bodyColor},labelPointStyle(d){const z=d.chart.getDatasetMeta(d.datasetIndex).controller.getStyle(d.dataIndex);return{pointStyle:z.pointStyle,rotation:z.rotation}},afterLabel:$g,afterBody:$g,beforeFooter:$g,footer:$g,afterFooter:$g};function A0(d,l,z,j){const J=d[l].call(z,j);return typeof J>"u"?wD[l].call(z,j):J}class XP extends lv{static positioners=p2;constructor(l){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=l.chart,this.options=l.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(l){this.options=l,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const l=this._cachedAnimations;if(l)return l;const z=this.chart,j=this.options.setContext(this.getContext()),J=j.enabled&&z.options.animation&&j.animations,mt=new rD(this.chart,J);return J._cacheable&&(this._cachedAnimations=Object.freeze(mt)),mt}getContext(){return this.$context||(this.$context=Bct(this.chart.getContext(),this,this._tooltipItems))}getTitle(l,z){const{callbacks:j}=z,J=A0(j Line truncated
* chartjs-adapter-date-fns v3.0.0
* https://www.chartjs.org
* (c) 2022 chartjs-adapter-date-fns Contributors
* Released under the MIT license
*/const Pdt={datetime:"MMM d, yyyy, h:mm:ss aaaa",millisecond:"h:mm:ss.SSS aaaa",second:"h:mm:ss aaaa",minute:"h:mm aaaa",hour:"ha",day:"MMM d",week:"PP",month:"MMM yyyy",quarter:"qqq - yyyy",year:"yyyy"};aD._date.override({_id:"date-fns",formats:function(){return Pdt},parse:function(d,l){if(d===null||typeof d>"u")return null;const z=typeof d;return z==="number"||d instanceof Date?d=Vu(d):z==="string"&&(typeof l=="string"?d=cdt(d,l,new Date,this.options):d=mdt(d,this.options)),MD(d)?d.getTime():null},format:function(d,l){return fft(d,l,this.options)},add:function(d,l,z){switch(z){case"millisecond":return vM(d,l);case"second":return eht(d,l);case"minute":return Qct(d,l);case"hour":return Kct(d,l);case"day":return Y4(d,l);case"week":return rht(d,l);case"month":return gM(d,l);case"quarter":return tht(d,l);case"year":return nht(d,l);default:return d}},diff:function(d,l,z){switch(z){case"millisecond":return yM(d,l);case"second":return hht(d,l);case"minute":return lht(d,l);case"hour":return sht(d,l);case"day":return SD(d,l);case"week":return fht(d,l);case"month":return LD(d,l);case"quarter":return cht(d,l);case"year":return dht(d,l);default:return 0}},startOf:function(d,l,z){switch(l){case"second":return pdt(d);case"minute":return ddt(d);case"hour":return fdt(d);case"day":return TA(d);case"week":return fg(d);case"isoWeek":return fg(d,{weekStartsOn:+z});case"month":return mht(d);case"quarter":return pht(d);case"year":return PD(d);default:return d}},endOf:function(d,l){switch(l){case"second":return bht(d);case"minute":return xht(d);case"hour":return vht(d);case"day":return ED(d);case"week":return yht(d);case"month":return CD(d);case"quarter":return _ht(d);case"year":return ght(d);default:return d}}});var J5={exports:{}},zdt=J5.exports,hz;function Idt(){return hz||(hz=1,function(d){var l={};(function(z,j){d.exports?d.exports=j():z.moduleName=j()})(typeof self<"u"?self:zdt,()=>{var z=(()=>{var j=Object.create,J=Object.defineProperty,mt=Object.defineProperties,kt=Object.getOwnPropertyDescriptor,Dt=Object.getOwnPropertyDescriptors,Gt=Object.getOwnPropertyNames,re=Object.getOwnPropertySymbols,pe=Object.getPrototypeOf,Ne=Object.prototype.hasOwnProperty,or=Object.prototype.propertyIsEnumerable,_r=(Q,$,c)=>$ in Q?J(Q,$,{enumerable:!0,configurable:!0,writable:!0,value:c}):Q[$]=c,Fr=(Q,$)=>{for(var c in $||($={}))Ne.call($,c)&&_r(Q,c,$[c]);if(re)for(var c of re($))or.call($,c)&&_r(Q,c,$[c]);return Q},zr=(Q,$)=>mt(Q,Dt($)),Wr=(Q,$)=>{var c={};for(var g in Q)Ne.call(Q,g)&&$.indexOf(g)<0&&(c[g]=Q[g]);if(Q!=null&&re)for(var g of re(Q))$.indexOf(g)<0&&or.call(Q,g)&&(c[g]=Q[g]);return c},An=(Q,$)=>()=>(Q&&($=Q(Q=0)),$),Ft=(Q,$)=>()=>($||Q(($={exports:{}}).exports,$),$.exports),kn=(Q,$)=>{for(var c in $)J(Q,c,{get:$[c],enumerable:!0})},ei=(Q,$,c,g)=>{if($&&typeof $=="object"||typeof $=="function")for(let P of Gt($))!Ne.call(Q,P)&&P!==c&&J(Q,P,{get:()=>$[P],enumerable:!(g=kt($,P))||g.enumerable});return Q},jn=(Q,$,c)=>(c=Q!=null?j(pe(Q)):{},ei(J(c,"default",{value:Q,enumerable:!0}),Q)),ai=Q=>ei(J({},"__esModule",{value:!0}),Q),Qi=Ft(Q=>{Q.version="3.2.0"}),Gi=Ft((Q,$)=>{(function(c,g,P){g[c]=g[c]||P(),typeof $<"u"&&$.exports&&($.exports=g[c])})("Promise",typeof window<"u"?window:Q,function(){var c,g,P,S=Object.prototype.toString,t=typeof setImmediate<"u"?function(T){return setImmediate(T)}:setTimeout;try{Object.defineProperty({},"x",{}),c=function(T,u,b,_){return Object.defineProperty(T,u,{value:b,writable:!0,configurable:_!==!1})}}catch{c=function(u,b,_){return u[b]=_,u}}P=function(){var T,u,b;function _(C,M){this.fn=C,this.self=M,this.next=void 0}return{add:function(C,M){b=new _(C,M),u?u.next=b:T=b,u=b,b=void 0},drain:function(){var C=T;for(T=u=g=void 0;C;)C.fn.call(C.self),C=C.next}}}();function e(T,u){P.add(T,u),g||(g=t(P.drain))}function r(T){var u,b=typeof T;return T!=null&&(b=="object"||b=="function")&&(u=T.then),typeof u=="function"?u:!1}function a(){for(var T=0;T<this.chain.length;T++)n(this,this.state===1?this.chain[T].success:this.chain[T].failure,this.chain[T]);this.chain.length=0}function n(T,u,b){var _,C;try{u===!1?b.reject(T.msg):(u===!0?_=T.msg:_=u.call(void 0,T.msg),_===b.promise?b.reject(TypeError("Promise-chain cycle")):(C=r(_))?C.call(_,b.resolve,b.reject):b.resolve(_))}catch(M){b.reject(M)}}function o(T){var u,b=this;if(!b.triggered){b.triggered=!0,b.def&&(b=b.def);try{(u=r(T))?e(function(){var _=new f(b);try{u.call(T,function(){o.apply(_,arguments)},function(){i.apply(_,arguments)})}catch(C){i.call(_,C)}}):(b.msg=T,b.state=1,b.chain.length>0&&e(a,b))}catch(_){i.call(new f(b),_)}}}function i(T){var u=this;u.triggered||(u.triggered=!0,u.def&&(u=u.def),u.msg=T,u.state=2,u.chain.length>0&&e(a,u))}function s(T,u,b,_){for(var C=0;C<u.length;C++)(function(M){T.resolve(u[M]).then(function(E){b(M,E)},_)})(C)}function f(T){this.def=T,this.triggered=!1}function x(T){this.promise=T,this.state=0,this.triggered=!1,this.chain=[],this.msg=void 0}function y(T){if(typeof T!="function")throw TypeError("Not a function");if(this.Line truncated
*/const Idt={datetime:"MMM d, yyyy, h:mm:ss aaaa",millisecond:"h:mm:ss.SSS aaaa",second:"h:mm:ss aaaa",minute:"h:mm aaaa",hour:"ha",day:"MMM d",week:"PP",month:"MMM yyyy",quarter:"qqq - yyyy",year:"yyyy"};aD._date.override({_id:"date-fns",formats:function(){return Idt},parse:function(d,l){if(d===null||typeof d>"u")return null;const z=typeof d;return z==="number"||d instanceof Date?d=Vu(d):z==="string"&&(typeof l=="string"?d=fdt(d,l,new Date,this.options):d=vdt(d,this.options)),MD(d)?d.getTime():null},format:function(d,l){return pft(d,l,this.options)},add:function(d,l,z){switch(z){case"millisecond":return vM(d,l);case"second":return nht(d,l);case"minute":return eht(d,l);case"hour":return Jct(d,l);case"day":return Y4(d,l);case"week":return iht(d,l);case"month":return gM(d,l);case"quarter":return rht(d,l);case"year":return aht(d,l);default:return d}},diff:function(d,l,z){switch(z){case"millisecond":return yM(d,l);case"second":return dht(d,l);case"minute":return cht(d,l);case"hour":return uht(d,l);case"day":return SD(d,l);case"week":return pht(d,l);case"month":return LD(d,l);case"quarter":return fht(d,l);case"year":return mht(d,l);default:return 0}},startOf:function(d,l,z){switch(l){case"second":return gdt(d);case"minute":return mdt(d);case"hour":return pdt(d);case"day":return TA(d);case"week":return fg(d);case"isoWeek":return fg(d,{weekStartsOn:+z});case"month":return vht(d);case"quarter":return ght(d);case"year":return PD(d);default:return d}},endOf:function(d,l){switch(l){case"second":return kht(d);case"minute":return bht(d);case"hour":return xht(d);case"day":return ED(d);case"week":return _ht(d);case"month":return CD(d);case"quarter":return wht(d);case"year":return yht(d);default:return d}}});var J5={exports:{}},Odt=J5.exports,hz;function Ddt(){return hz||(hz=1,function(d){var l={};(function(z,j){d.exports?d.exports=j():z.moduleName=j()})(typeof self<"u"?self:Odt,()=>{var z=(()=>{var j=Object.create,J=Object.defineProperty,mt=Object.defineProperties,kt=Object.getOwnPropertyDescriptor,Dt=Object.getOwnPropertyDescriptors,Gt=Object.getOwnPropertyNames,re=Object.getOwnPropertySymbols,pe=Object.getPrototypeOf,Ne=Object.prototype.hasOwnProperty,or=Object.prototype.propertyIsEnumerable,_r=(Q,$,c)=>$ in Q?J(Q,$,{enumerable:!0,configurable:!0,writable:!0,value:c}):Q[$]=c,Fr=(Q,$)=>{for(var c in $||($={}))Ne.call($,c)&&_r(Q,c,$[c]);if(re)for(var c of re($))or.call($,c)&&_r(Q,c,$[c]);return Q},zr=(Q,$)=>mt(Q,Dt($)),Wr=(Q,$)=>{var c={};for(var g in Q)Ne.call(Q,g)&&$.indexOf(g)<0&&(c[g]=Q[g]);if(Q!=null&&re)for(var g of re(Q))$.indexOf(g)<0&&or.call(Q,g)&&(c[g]=Q[g]);return c},An=(Q,$)=>()=>(Q&&($=Q(Q=0)),$),Ft=(Q,$)=>()=>($||Q(($={exports:{}}).exports,$),$.exports),kn=(Q,$)=>{for(var c in $)J(Q,c,{get:$[c],enumerable:!0})},ei=(Q,$,c,g)=>{if($&&typeof $=="object"||typeof $=="function")for(let P of Gt($))!Ne.call(Q,P)&&P!==c&&J(Q,P,{get:()=>$[P],enumerable:!(g=kt($,P))||g.enumerable});return Q},jn=(Q,$,c)=>(c=Q!=null?j(pe(Q)):{},ei(J(c,"default",{value:Q,enumerable:!0}),Q)),ai=Q=>ei(J({},"__esModule",{value:!0}),Q),Qi=Ft(Q=>{Q.version="3.2.0"}),Gi=Ft((Q,$)=>{(function(c,g,P){g[c]=g[c]||P(),typeof $<"u"&&$.exports&&($.exports=g[c])})("Promise",typeof window<"u"?window:Q,function(){var c,g,P,S=Object.prototype.toString,t=typeof setImmediate<"u"?function(T){return setImmediate(T)}:setTimeout;try{Object.defineProperty({},"x",{}),c=function(T,u,b,_){return Object.defineProperty(T,u,{value:b,writable:!0,configurable:_!==!1})}}catch{c=function(u,b,_){return u[b]=_,u}}P=function(){var T,u,b;function _(C,M){this.fn=C,this.self=M,this.next=void 0}return{add:function(C,M){b=new _(C,M),u?u.next=b:T=b,u=b,b=void 0},drain:function(){var C=T;for(T=u=g=void 0;C;)C.fn.call(C.self),C=C.next}}}();function e(T,u){P.add(T,u),g||(g=t(P.drain))}function r(T){var u,b=typeof T;return T!=null&&(b=="object"||b=="function")&&(u=T.then),typeof u=="function"?u:!1}function a(){for(var T=0;T<this.chain.length;T++)n(this,this.state===1?this.chain[T].success:this.chain[T].failure,this.chain[T]);this.chain.length=0}function n(T,u,b){var _,C;try{u===!1?b.reject(T.msg):(u===!0?_=T.msg:_=u.call(void 0,T.msg),_===b.promise?b.reject(TypeError("Promise-chain cycle")):(C=r(_))?C.call(_,b.resolve,b.reject):b.resolve(_))}catch(M){b.reject(M)}}function o(T){var u,b=this;if(!b.triggered){b.triggered=!0,b.def&&(b=b.def);try{(u=r(T))?e(function(){var _=new f(b);try{u.call(T,function(){o.apply(_,arguments)},function(){i.apply(_,arguments)})}catch(C){i.call(_,C)}}):(b.msg=T,b.state=1,b.chain.length>0&&e(a,b))}catch(_){i.call(new f(b),_)}}}function i(T){var u=this;u.triggered||(u.triggered=!0,u.def&&(u=u.def),u.msg=T,u.state=2,u.chain.length>0&&e(a,u))}function s(T,u,b,_){for(var C=0;C<u.length;C++)(function(M){T.resolve(u[M]).then(function(E){b(M,E)},_)})(C)}function f(T){this.def=T,this.triggered=!1}function x(T){this.promise=T,this.state=0,this.triggered=!1,this.chain=[],this.msg=void 0}function y(T){if(typeof T!="function")throw TypeError("Not a function");if(this.Line truncated
]`),Me=At.charCodeAt(0);function Le(en,Xr,vn){arguments.length<3&&(vn=Xr,Xr=null);var In=Vr(en,jt,Xr==null?Be:sr(Xr),vn);return In.row=function(On){return arguments.length?In.response((Xr=On)==null?Be:sr(On)):Xr},In}function Be(en){return Le.parse(en.responseText)}function sr(en){return function(Xr){return Le.parse(Xr.responseText,en)}}Le.parse=function(en,Xr){var vn;return Le.parseRows(en,function(In,On){if(vn)return vn(In,On-1);var Bi=function(Un){for(var mi={},Ti=In.length,Ii=0;Ii<Ti;++Ii)mi[In[Ii]]=Un[Ii];return mi};vn=Xr?function(Un,mi){return Xr(Bi(Un),mi)}:Bi})},Le.parseRows=function(en,Xr){var vn={},In={},On=[],Bi=en.length,Un=0,mi=0,Ti,Ii;function Wi(){if(Un>=Bi)return In;if(Ii)return Ii=!1,vn;var ja=Un;if(en.charCodeAt(ja)===34){for(var Ha=ja;Ha++<Bi;)if(en.charCodeAt(Ha)===34){if(en.charCodeAt(Ha+1)!==34)break;++Ha}Un=Ha+2;var ro=en.charCodeAt(Ha+1);return ro===13?(Ii=!0,en.charCodeAt(Ha+2)===10&&++Un):ro===10&&(Ii=!0),en.slice(ja+1,Ha).replace(/""/g,'"')}for(;Un<Bi;){var ro=en.charCodeAt(Un++),Lo=1;if(ro===10)Ii=!0;else if(ro===13)Ii=!0,en.charCodeAt(Un)===10&&(++Un,++Lo);else if(ro!==Me)continue;return en.slice(ja,Un-Lo)}return en.slice(ja)}for(;(Ti=Wi())!==In;){for(var Yn=[];Ti!==vn&&Ti!==In;)Yn.push(Ti),Ti=Wi();Xr&&(Yn=Xr(Yn,mi++))==null||On.push(Yn)}return On},Le.format=function(en){if(Array.isArray(en[0]))return Le.formatRows(en);var Xr=new N,vn=[];return en.forEach(function(In){for(var On in In)Xr.has(On)||vn.push(Xr.add(On))}),[vn.map(Mr).join(At)].concat(en.map(function(In){return vn.map(function(On){return Mr(In[On])}).join(At)})).join(`
`)},Le.formatRows=function(en){return en.map(ir).join(`
`)};function ir(en){return en.map(Mr).join(At)}function Mr(en){return ue.test(en)?'"'+en.replace(/\"/g,'""')+'"':en}return Le},c.csv=c.dsv(",","text/csv"),c.tsv=c.dsv(" ","text/tab-separated-values");var Xn,Pn,En,Zn,Ca=this[F(this,"requestAnimationFrame")]||function(At){setTimeout(At,17)};c.timer=function(){Ri.apply(this,arguments)};function Ri(At,jt,ue){var Me=arguments.length;Me<2&&(jt=0),Me<3&&(ue=Date.now());var Le=ue+jt,Be={c:At,t:Le,n:null};return Pn?Pn.n=Be:Xn=Be,Pn=Be,En||(Zn=clearTimeout(Zn),En=1,Ca(Ja)),Be}function Ja(){var At=Xa(),jt=Io()-At;jt>24?(isFinite(jt)&&(clearTimeout(Zn),Zn=setTimeout(Ja,jt)),En=0):(En=1,Ca(Ja))}c.timer.flush=function(){Xa(),Io()};function Xa(){for(var At=Date.now(),jt=Xn;jt;)At>=jt.t&&jt.c(At-jt.t)&&(jt.c=null),jt=jt.n;return At}function Io(){for(var At,jt=Xn,ue=1/0;jt;)jt.c?(jt.t<ue&&(ue=jt.t),jt=(At=jt).n):jt=At?At.n=jt.n:Xn=jt.n;return Pn=At,ue}c.round=function(At,jt){return jt?Math.round(At*(jt=Math.pow(10,jt)))/jt:Math.round(At)},c.geom={};function po(At){return At[0]}function Do(At){return At[1]}c.geom.hull=function(At){var jt=po,ue=Do;if(arguments.length)return Me(At);function Me(Le){if(Le.length<3)return[];var Be=Ee(jt),sr=Ee(ue),ir,Mr=Le.length,en=[],Xr=[];for(ir=0;ir<Mr;ir++)en.push([+Be.call(this,Le[ir],ir),+sr.call(this,Le[ir],ir),ir]);for(en.sort(gs),ir=0;ir<Mr;ir++)Xr.push([en[ir][0],-en[ir][1]]);var vn=Ia(en),In=Ia(Xr),On=In[0]===vn[0],Bi=In[In.length-1]===vn[vn.length-1],Un=[];for(ir=vn.length-1;ir>=0;--ir)Un.push(Le[en[vn[ir]][2]]);for(ir=+On;ir<In.length-Bi;++ir)Un.push(Le[en[In[ir]][2]]);return Un}return Me.x=function(Le){return arguments.length?(jt=Le,Me):jt},Me.y=function(Le){return arguments.length?(ue=Le,Me):ue},Me};function Ia(At){for(var jt=At.length,ue=[0,1],Me=2,Le=2;Le<jt;Le++){for(;Me>1&&Ue(At[ue[Me-2]],At[ue[Me-1]],At[Le])<=0;)--Me;ue[Me++]=Le}return ue.slice(0,Me)}function gs(At,jt){return At[0]-jt[0]||At[1]-jt[1]}c.geom.polygon=function(At){return tt(At,is),At};var is=c.geom.polygon.prototype=[];is.area=function(){for(var At=-1,jt=this.length,ue,Me=this[jt-1],Le=0;++At<jt;)ue=Me,Me=this[At],Le+=ue[1]*Me[0]-ue[0]*Me[1];return Le*.5},is.centroid=function(At){var jt=-1,ue=this.length,Me=0,Le=0,Be,sr=this[ue-1],ir;for(arguments.length||(At=-1/(6*this.area()));++jt<ue;)Be=sr,sr=this[jt],ir=Be[0]*sr[1]-sr[0]*Be[1],Me+=(Be[0]+sr[0])*ir,Le+=(Be[1]+sr[1])*ir;return[Me*At,Le*At]},is.clip=function(At){for(var jt,ue=Gs(At),Me=-1,Le=this.length-Gs(this),Be,sr,ir=this[Le-1],Mr,en,Xr;++Me<Le;){for(jt=At.slice(),At.length=0,Mr=this[Me],en=jt[(sr=jt.length-ue)-1],Be=-1;++Be<sr;)Xr=jt[Be],ll(Xr,ir,Mr)?(ll(en,ir,Mr)||At.push(Ho(en,Xr,ir,Mr)),At.push(Xr)):ll(en,ir,Mr)&&At.push(Ho(en,Xr,ir,Mr)),en=Xr;ue&&At.push(At[0]),ir=Mr}return At};function ll(At,jt,ue){return(ue[0]-jt[0])*(At[1]-jt[1])<(ue[1]-jt[1])*(At[0]-jt[0])}function Ho(At,jt,ue,Me){var Le=At[0],Be=ue[0],sr=jt[0]-Le,ir=Me[0]-Be,Mr=At[1],en=ue[1],Xr=jt[1]-Mr,vn=Me[1]-en,In=(ir*(Mr-en)-vn*(Le-Be))/(vn*sr-ir*Xr);return[Le+In*sr,Mr+In*Xr]}function Gs(At){var jt=At[0],ue=At[At.length-1];return!(jt[0]-ue[0]||jt[1]-ue[1])}var as,ul,Js,Rl=[],ls,Cs,Co=[];function ks(){_l(this),this.edge=this.site=this.circle=null}function kl(At){var jt=Rl.pop()||new ks;return jt.site=At,jt}function Vl(At){Cl(At),Js.remove(At),Rl.push(At),_l(At)}function Yl(At){var jt=At.circle,ue=jt.x,Me=jt.cy,Le={x:ue,y:Me},Be=At.P,sr=At.N,ir=[At];Vl(At);for(var Mr=Be;Mr.circle&&u(ue-Mr.circle.x)<ne&&u(Me-Mr.circle.cy)<ne;)Be=Mr.P,ir.unshift(Mr),Vl(Mr),Mr=Be;ir.unshift(Mr),Cl(Mr);for(var en=sr;en.circle&&u(ue-en.circle.x)<ne&&u(Me-en.circle.cy)<ne;)sr=en.N,ir.push(en),Vl(en),en=sr;ir.push(en),Cl(en);var Xr=ir.length,vn;for(vn=1;vn<Xr;++vn)en=ir[vn],Mr=ir[vn-1],Qo(en.edge,Mr.site,en.site,Le);Mr=ir[0],en=ir[Xr-1],en.edge=nu(Mr.site,en.site,null,Le),il(Mr),il(en)}function Ns(At){for(var jt=At.x,ue=At.y,Me,Le,Be,sr,ir=Js._;ir;)if(Be=La(ir,ue)-jt,Be>ne)ir=ir.L;else if(sr=jt-uo(ir,ue),sr>ne){if(!ir.R){Me=ir;break}ir=ir.R}else{Be>-ne?(Me=ir.P,Le=ir):sr>-ne?(Me=ir,Le=ir.N):Me=Le=ir;break}var Mr=kl(At);if(Js.insert(Me,Mr),!(!Me&&!Le)){if(Me===Le){Cl(Me),Le=kl(Me.site),Js.insert(Mr,Le),Mr.edge=Le.edge=nu(Me.site,Mr.site),il(Me),il(Le);return}if(!Le){Mr.edge=nu(Me.site,Mr.site);return}Cl(Me),Cl(Le);var en=Me.site,Xr=en.x,vn=en.y,In=At.x-Xr,On=At.y-vn,Bi=Le.site,Un=Bi.x-Xr,mi=Bi.y-vn,Ti=2*(In*mi-On*Un),Ii=In*In+On*On,Wi=Un*Un+mi*mi,Yn={x:(mi*Ii-On*Wi)/Ti+Xr,y:(In*Wi-Un*Ii)/Ti+vn};Qo(Le.edge,en,Bi,Yn),Mr.edge=nu(en,At,null,Yn),Le.edge=nu(At,Bi,null,Yn),il(Me),il(Le)}}function La(At,jt){var ue=At.site,Me=ue.x,Le=ue.y,Be=Le-jt;if(!Be)return Me;var sr=At.P;if(!sr)return-1/0;ue=sr.site;var ir=ue.x,Mr=ue.y,en=Mr-jt;if(!en)return ir;var Xr=ir-Me,vn=1/Be-1/en,In=Xr/en;return vn?(-In+Math.sqrt(In*In-2*vn*(Xr*Xr/(-2*en)-Mr+en/2+Le-Be/2)))/vn+Me:(Me+ir)/2}function uo(At,jt){var ue=At.N;if(ue)return La(ue,jt);var Me=At.site;return Me.y===jt?Me.x:1/0}function Hs(At){this.site=At,this.edges=[]}Hs.prototype.prepare=function(){for(var At=this.edges,jt=At.length,ue;jt Line truncated
@@ -3947,4 +3947,4 @@ maplibre-gl/dist/maplibre-gl.js:
* MapLibre GL JS
* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v4.7.1/LICENSE.txt
*)
*/return window.Plotly=z,z})}(J5)),J5.exports}var Odt=Idt();const l1=LO(Odt),Ddt={class:"p-6 space-y-6"},Fdt={class:"flex justify-between items-center"},Rdt={class:"flex items-center gap-3"},Bdt=["value"],Ndt={class:"grid grid-cols-1 sm:grid-cols-2 gap-4"},jdt={class:"glass-card rounded-[15px] p-6"},Udt={class:"mb-6"},Vdt={class:"relative h-48 bg-white/5 rounded-lg p-4"},Hdt={key:0,class:"absolute inset-0 flex items-center justify-center bg-white/5 backdrop-blur-sm z-20"},Wdt={key:1,class:"absolute inset-0 flex items-center justify-center bg-white/5 z-20"},qdt={class:"mb-6"},Zdt={class:"relative h-48 bg-white/5 rounded-lg p-4"},$dt={key:0,class:"absolute inset-0 flex items-center justify-center bg-white/5 backdrop-blur-sm z-20"},Gdt={key:1,class:"absolute inset-0 flex items-center justify-center bg-white/5 z-20"},Ydt={class:"glass-card rounded-[15px] p-6"},Kdt={class:"grid grid-cols-1 lg:grid-cols-3 gap-6"},Xdt={class:"lg:col-span-2"},Jdt={class:"relative h-64 bg-white/5 rounded-lg p-4"},Qdt={class:"flex flex-col items-center justify-center"},tpt={class:"relative w-48 h-48"},ept={key:0,class:"absolute inset-0 flex items-center justify-center bg-white/5 backdrop-blur-sm rounded-full z-20"},rpt={key:1,class:"absolute inset-0 flex items-center justify-center bg-white/5 rounded-full z-20"},npt={key:0,class:"glass-card rounded-[15px] p-8 text-center"},ipt={key:1,class:"glass-card rounded-[15px] p-8 text-center"},apt={class:"text-white/60 text-sm"},opt=Th({name:"StatisticsView",__name:"Statistics",setup(d){d2.register(Uct,Wct,Yut,Z4,Tlt,blt,klt,Cct,Rct,Sct,Nut,ect,bct,kA);const l=rw(),z=lo(null),j=lo(!1),J=lo(24),mt=[{value:1,label:"1 Hour"},{value:6,label:"6 Hours"},{value:12,label:"12 Hours"},{value:24,label:"24 Hours"},{value:48,label:"2 Days"},{value:168,label:"1 Week"}],kt=lo(null),Dt=lo(null),Gt=lo([]),re=lo(null),pe=lo([]),Ne=lo(!0),or=lo(null),_r=lo({packetRate:!0,packetType:!0,noiseFloor:!1,routePie:!0}),Fr=lo(!1),zr=lo(!1),Wr=lo(!1),An=lo(null),Ft=lo(null),kn=lo(null),ei=lo(null),jn=lo(null),ai=lo(null),Qi=lo(null),Gi=Yo(()=>{const qo=l.packetStats;return qo?{totalRx:qo.total_packets||0,totalTx:qo.transmitted_packets||0}:{totalRx:0,totalTx:0}}),un=Yo(()=>{let qo=[],fo=[];if(kt.value?.series){const Ta=kt.value.series.find(go=>go.type==="rx_count"),Ea=kt.value.series.find(go=>go.type==="tx_count");Ta?.data&&(qo=Ta.data.map(([,go])=>go)),Ea?.data&&(fo=Ea.data.map(([,go])=>go))}return{totalPackets:qo,transmittedPackets:fo,droppedPackets:[]}}),ia=async()=>{try{Ne.value=!0,or.value=null,await Promise.all([l.fetchPacketStats({hours:J.value}),l.fetchSystemStats()]),Ne.value=!1,fa()}catch(qo){or.value=qo instanceof Error?qo.message:"Failed to fetch data",Ne.value=!1}},fa=async()=>{_r.value={packetRate:!0,packetType:!0,noiseFloor:!1,routePie:!0};const qo=[Li(),yi(),ra(),Da()];try{await Promise.allSettled(qo),await Z0(),!ei.value||!jn.value?setTimeout(()=>{Va()},100):Va()}catch(fo){console.error("Error loading chart data:",fo)}},Li=async()=>{_r.value.packetRate=!0;try{const qo=await Xh.get("/metrics_graph_data",{hours:J.value,resolution:"average",metrics:"rx_count,tx_count"});qo?.success&&(kt.value=qo.data)}catch{kt.value=null}},yi=async()=>{_r.value.packetType=!0;try{const qo=await Xh.get("/packet_type_graph_data",{hours:J.value,resolution:"average",types:"all"});if(qo?.success&&qo.data){const fo=qo.data;Gt.value=fo.series||[]}}catch{Gt.value=[]}},ra=async()=>{_r.value.routePie=!0;try{const qo=await Xh.get("/route_stats",{hours:J.value});qo?.success&&qo.data&&(re.value=qo.data)}catch{re.value=null}},Da=async()=>{try{const qo=await Xh.get("/noise_floor_history",{hours:J.value});if(qo.success&&qo.data){const Ta=qo.data.history||[];Array.isArray(Ta)&&Ta.length>0&&(Dt.value={chart_data:Ta.map(Ea=>({timestamp:Ea.timestamp||Date.now()/1e3,noise_floor_dbm:Ea.noise_floor_dbm||Ea.noise_floor||-120}))},Ei())}}catch{Dt.value={chart_data:[]}}},Ni=()=>{ss(),Fr.value=!1,zr.value=!1,Wr.value=!1,ia()},Ei=()=>{if(pe.value=[],Dt.value?.chart_data&&Dt.value.chart_data.length>0){const qo=Dt.value.chart_data,fo=Math.max(1,Math.floor(qo.length/100));pe.value=qo.filter((Ta,Ea)=>Ea%fo===0).map(Ta=>({timestamp:Ta.timestamp*1e3,snr:null,rssi:null,noiseFloor:Ta.noise_floor_dbm}))}},Va=()=>{if(!j.value){j.value=!0;try{mo(),ko(),pl(),fu(),setTimeout(()=>{_r.value.packetRate&&An.value&&(_r.value.packetRate=!1),_r.value.packetType&&Ft.value&&(_r.value.packetType=!1),_r.value.routePie&&Qi.value&&(_r.value.routePie=!1),_r.value.routePie&&Qi.value&&(_r.value.routePie=!1),setTimeout(()=>{const qo=ju(An.value),fo=ju(Ft.value),Ta=ju(kn.value);qo&&qo.update("none"),fo&&fo.update("none"),Ta&&Ta.update("none")},50)},100)}catch(qo){console.error("Error creating/updating charts:",qo),ss()}finally{j.value=!1}}},ss=()=>{try{An.value&&(An.value.destroy(),An.value=null),Ft.value&&(Ft.value.destroy(),Ft.value=null),kn.value&&(kn.value.destroy(),kn.value=null),Qi.value&&l1.purge(Qi.value)}catch(qo){console.error("Error destroying charts:",qo)}},mo=()=>{if(!eiLine truncated
*/return window.Plotly=z,z})}(J5)),J5.exports}var Fdt=Ddt();const l1=LO(Fdt),Rdt={class:"p-6 space-y-6"},Bdt={class:"flex justify-between items-center"},Ndt={class:"flex items-center gap-3"},jdt=["value"],Udt={class:"grid grid-cols-1 sm:grid-cols-2 gap-4"},Vdt={class:"glass-card rounded-[15px] p-6"},Hdt={class:"mb-6"},Wdt={class:"relative h-48 bg-white/5 rounded-lg p-4"},qdt={key:0,class:"absolute inset-0 flex items-center justify-center bg-white/5 backdrop-blur-sm z-20"},Zdt={key:1,class:"absolute inset-0 flex items-center justify-center bg-white/5 z-20"},$dt={class:"mb-6"},Gdt={class:"relative h-48 bg-white/5 rounded-lg p-4"},Ydt={key:0,class:"absolute inset-0 flex items-center justify-center bg-white/5 backdrop-blur-sm z-20"},Kdt={key:1,class:"absolute inset-0 flex items-center justify-center bg-white/5 z-20"},Xdt={class:"glass-card rounded-[15px] p-6"},Jdt={class:"grid grid-cols-1 lg:grid-cols-3 gap-6"},Qdt={class:"lg:col-span-2"},tpt={class:"relative h-64 bg-white/5 rounded-lg p-4"},ept={class:"flex flex-col items-center justify-center"},rpt={class:"relative w-48 h-48"},npt={key:0,class:"absolute inset-0 flex items-center justify-center bg-white/5 backdrop-blur-sm rounded-full z-20"},ipt={key:1,class:"absolute inset-0 flex items-center justify-center bg-white/5 rounded-full z-20"},apt={key:0,class:"glass-card rounded-[15px] p-8 text-center"},opt={key:1,class:"glass-card rounded-[15px] p-8 text-center"},spt={class:"text-white/60 text-sm"},lpt=Th({name:"StatisticsView",__name:"Statistics",setup(d){d2.register(Hct,Zct,Xut,Z4,Mlt,klt,Alt,Pct,Nct,Cct,Uut,nct,kct,kA);const l=rw(),z=lo(null),j=lo(!1),J=lo(24),mt=[{value:1,label:"1 Hour"},{value:6,label:"6 Hours"},{value:12,label:"12 Hours"},{value:24,label:"24 Hours"},{value:48,label:"2 Days"},{value:168,label:"1 Week"}],kt=lo(null),Dt=lo(null),Gt=lo([]),re=lo(null),pe=lo([]),Ne=lo(!0),or=lo(null),_r=lo({packetRate:!0,packetType:!0,noiseFloor:!1,routePie:!0}),Fr=lo(!1),zr=lo(!1),Wr=lo(!1),An=lo(null),Ft=lo(null),kn=lo(null),ei=lo(null),jn=lo(null),ai=lo(null),Qi=lo(null),Gi=Yo(()=>{const qo=l.packetStats;return qo?{totalRx:qo.total_packets||0,totalTx:qo.transmitted_packets||0}:{totalRx:0,totalTx:0}}),un=Yo(()=>{let qo=[],fo=[];if(kt.value?.series){const Ta=kt.value.series.find(go=>go.type==="rx_count"),Ea=kt.value.series.find(go=>go.type==="tx_count");Ta?.data&&(qo=Ta.data.map(([,go])=>go)),Ea?.data&&(fo=Ea.data.map(([,go])=>go))}return{totalPackets:qo,transmittedPackets:fo,droppedPackets:[]}}),ia=async()=>{try{Ne.value=!0,or.value=null,await Promise.all([l.fetchPacketStats({hours:J.value}),l.fetchSystemStats()]),Ne.value=!1,fa()}catch(qo){or.value=qo instanceof Error?qo.message:"Failed to fetch data",Ne.value=!1}},fa=async()=>{_r.value={packetRate:!0,packetType:!0,noiseFloor:!1,routePie:!0};const qo=[Li(),yi(),ra(),Da()];try{await Promise.allSettled(qo),await Z0(),!ei.value||!jn.value?setTimeout(()=>{Va()},100):Va()}catch(fo){console.error("Error loading chart data:",fo)}},Li=async()=>{_r.value.packetRate=!0;try{const qo=await Xh.get("/metrics_graph_data",{hours:J.value,resolution:"average",metrics:"rx_count,tx_count"});qo?.success&&(kt.value=qo.data)}catch{kt.value=null}},yi=async()=>{_r.value.packetType=!0;try{const qo=await Xh.get("/packet_type_graph_data",{hours:J.value,resolution:"average",types:"all"});if(qo?.success&&qo.data){const fo=qo.data;Gt.value=fo.series||[]}}catch{Gt.value=[]}},ra=async()=>{_r.value.routePie=!0;try{const qo=await Xh.get("/route_stats",{hours:J.value});qo?.success&&qo.data&&(re.value=qo.data)}catch{re.value=null}},Da=async()=>{try{const qo=await Xh.get("/noise_floor_history",{hours:J.value});if(qo.success&&qo.data){const Ta=qo.data.history||[];Array.isArray(Ta)&&Ta.length>0&&(Dt.value={chart_data:Ta.map(Ea=>({timestamp:Ea.timestamp||Date.now()/1e3,noise_floor_dbm:Ea.noise_floor_dbm||Ea.noise_floor||-120}))},Ei())}}catch{Dt.value={chart_data:[]}}},Ni=()=>{ss(),Fr.value=!1,zr.value=!1,Wr.value=!1,ia()},Ei=()=>{if(pe.value=[],Dt.value?.chart_data&&Dt.value.chart_data.length>0){const qo=Dt.value.chart_data,fo=Math.max(1,Math.floor(qo.length/100));pe.value=qo.filter((Ta,Ea)=>Ea%fo===0).map(Ta=>({timestamp:Ta.timestamp*1e3,snr:null,rssi:null,noiseFloor:Ta.noise_floor_dbm}))}},Va=()=>{if(!j.value){j.value=!0;try{mo(),ko(),pl(),fu(),setTimeout(()=>{_r.value.packetRate&&An.value&&(_r.value.packetRate=!1),_r.value.packetType&&Ft.value&&(_r.value.packetType=!1),_r.value.routePie&&Qi.value&&(_r.value.routePie=!1),_r.value.routePie&&Qi.value&&(_r.value.routePie=!1),setTimeout(()=>{const qo=ju(An.value),fo=ju(Ft.value),Ta=ju(kn.value);qo&&qo.update("none"),fo&&fo.update("none"),Ta&&Ta.update("none")},50)},100)}catch(qo){console.error("Error creating/updating charts:",qo),ss()}finally{j.value=!1}}},ss=()=>{try{An.value&&(An.value.destroy(),An.value=null),Ft.value&&(Ft.value.destroy(),Ft.value=null),kn.value&&(kn.value.destroy(),kn.value=null),Qi.value&&l1.purge(Qi.value)}catch(qo){console.error("Error destroying charts:",qo)}},mo=()=>{if(!eiLine truncated
+1 -1
View File
@@ -8,7 +8,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
<script type="module" crossorigin src="/assets/index-BJnLrjMq.js"></script>
<script type="module" crossorigin src="/assets/index-DJUYEmmJ.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-wgCsqUA2.css">
</head>
<body>
+46 -14
View File
@@ -46,7 +46,6 @@ class StatsApp:
def __init__(
self,
stats_getter: Optional[Callable] = None,
template_dir: Optional[str] = None,
node_name: str = "Repeater",
pub_key: str = "",
send_advert_func: Optional[Callable] = None,
@@ -57,20 +56,39 @@ class StatsApp:
):
self.stats_getter = stats_getter
self.template_dir = template_dir
self.node_name = node_name
self.pub_key = pub_key
self.dashboard_template = None
self.config = config or {}
# Path to the compiled Vue.js application
self.html_dir = os.path.join(os.path.dirname(__file__), "html")
# Create nested API object for routing
self.api = APIEndpoints(stats_getter, send_advert_func, self.config, event_loop, daemon_instance, config_path)
@cherrypy.expose
def index(self):
"""Serve the Vue.js application index.html."""
index_path = os.path.join(self.html_dir, "index.html")
try:
with open(index_path, 'r', encoding='utf-8') as f:
return f.read()
except FileNotFoundError:
raise cherrypy.HTTPError(404, "Application not found. Please build the frontend first.")
except Exception as e:
logger.error(f"Error serving index.html: {e}")
raise cherrypy.HTTPError(500, "Internal server error")
# @cherrypy.expose
# def index(self):
# """Serve dashboard HTML."""
# return self._serve_template("dashboard.html")
@cherrypy.expose
def default(self, *args, **kwargs):
"""Handle client-side routing - serve index.html for all non-API routes."""
# Let API routes pass through
if args and args[0] == 'api':
raise cherrypy.NotFound()
# For all other routes, serve the Vue.js app (client-side routing)
return self.index()
class HTTPStatsServer:
@@ -80,7 +98,6 @@ class HTTPStatsServer:
host: str = "0.0.0.0",
port: int = 8000,
stats_getter: Optional[Callable] = None,
template_dir: Optional[str] = None,
node_name: str = "Repeater",
pub_key: str = "",
send_advert_func: Optional[Callable] = None,
@@ -93,24 +110,39 @@ class HTTPStatsServer:
self.host = host
self.port = port
self.app = StatsApp(
stats_getter, template_dir, node_name, pub_key, send_advert_func, config, event_loop, daemon_instance, config_path
stats_getter, node_name, pub_key, send_advert_func, config, event_loop, daemon_instance, config_path
)
def start(self):
try:
# Serve static files from templates directory
static_dir = (
self.app.template_dir if self.app.template_dir else os.path.dirname(__file__)
)
# Serve static files from the html directory (compiled Vue.js app)
html_dir = os.path.join(os.path.dirname(__file__), "html")
assets_dir = os.path.join(html_dir, "assets")
config = {
"/": {
"tools.sessions.on": False,
# Ensure proper content types for Vue.js files
"tools.staticfile.content_types": {
'js': 'application/javascript',
'css': 'text/css',
'html': 'text/html; charset=utf-8'
},
},
"/static": {
"/assets": {
"tools.staticdir.on": True,
"tools.staticdir.dir": static_dir,
"tools.staticdir.dir": assets_dir,
# Set proper content types for assets
"tools.staticdir.content_types": {
'js': 'application/javascript',
'css': 'text/css',
'map': 'application/json'
},
},
"/favicon.ico": {
"tools.staticfile.on": True,
"tools.staticfile.filename": os.path.join(html_dir, "favicon.ico"),
},
}