Show OpenWeather API call usage in the app

OpenWeather reports no remaining quota (only 429 when over), so add an
in-app gauge that counts the calls PilotVault itself makes and shows them
against the plan's per-minute limit.

- Plugin: process-wide usage store bucketed per API key (hashed) into the
  current minute and UTC day; a do() wrapper counts each request that
  reaches OpenWeather (transport errors consume no quota, so uncounted).
  Health and Invoke both route through it.
- New callsPerMinute config field (default 60) that cascades global -> org
  -> user; plugin contract gains plugins.HealthUsage on Health.
- Web App: a "Calls per minute limit" field and an "API call usage" meter
  in the OpenWeather card, shown after Test connection and cached.
- Counts reset on restart and cover only PilotVault's own calls (noted in
  the UI); the account dashboard stays authoritative.
- Tests for per-key counting and minute/day rollover; rebuilt frontend.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tajniak81andClaude Opus 4.8 committed 2026-07-14 11:49:19 +02:00
1 parent 285551fe8a
commit 6d73c89cc8
9 files changed
+289 -41

No files matched your search

@@ -29,7 +29,7 @@ const openWeatherPlugin = "openweather"
// owFields are the plugin's ConfigField keys, in display order. Kept in sync with
// the plugin descriptor so the cascade covers every setting.
var owFields = []string{"apiKey", "units", "lat", "lon", "lang"}
var owFields = []string{"apiKey", "units", "lat", "lon", "lang", "callsPerMinute"}
// owSecretKeys are masked in every view and preserved on save when left at the mask.
var owSecretKeys = map[string]bool{"apiKey": true}
@@ -37,11 +37,12 @@ var owSecretKeys = map[string]bool{"apiKey": true}
// owConfig is one layer's openweather settings. Values are strings to match the
// plugin's ConfigField keys 1:1 (they are handed straight to plugins.Init).
type owConfig struct {
APIKey string `json:"apiKey"`
Units string `json:"units"`
Lat string `json:"lat"`
Lon string `json:"lon"`
Lang string `json:"lang"`
APIKey string `json:"apiKey"`
Units string `json:"units"`
Lat string `json:"lat"`
Lon string `json:"lon"`
Lang string `json:"lang"`
CallsPerMinute string `json:"callsPerMinute"`
}
// owStored is what we persist per user/org under pluginSettings.openweather.
@@ -74,7 +75,7 @@ type owResolution struct {
// owConfigFromMap builds an owConfig from a flat string map (global plugin config).
func owConfigFromMap(m map[string]string) owConfig {
return owConfig{APIKey: m["apiKey"], Units: m["units"], Lat: m["lat"], Lon: m["lon"], Lang: m["lang"]}
return owConfig{APIKey: m["apiKey"], Units: m["units"], Lat: m["lat"], Lon: m["lon"], Lang: m["lang"], CallsPerMinute: m["callsPerMinute"]}
}
// owGet returns a config field by the plugin's key name.
@@ -90,6 +91,8 @@ func owGet(c owConfig, key string) string {
return c.Lon
case "lang":
return c.Lang
case "callsPerMinute":
return c.CallsPerMinute
}
return ""
}
@@ -107,6 +110,8 @@ func owSet(c *owConfig, key, v string) {
c.Lon = v
case "lang":
c.Lang = v
case "callsPerMinute":
c.CallsPerMinute = v
}
}
@@ -8,6 +8,8 @@ package openweather
import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
@@ -22,6 +24,77 @@ import (
"pilotvault/apiserver/internal/plugins"
)
// defaultCallsPerMinute is OpenWeather's free-tier per-minute limit, used to gauge
// usage when no plan limit is configured.
const defaultCallsPerMinute = 60
// callUsage tracks how many upstream OpenWeather calls this process has made for
// one API key, bucketed into the current minute and the current UTC day.
//
// OpenWeather does not report remaining quota, so this counts only the calls
// PilotVault itself makes (not any made outside it) and resets when the server
// restarts — an approximate gauge, not an authoritative balance.
type callUsage struct {
mu sync.Mutex
minuteStart time.Time
minuteCount int
dayStart time.Time
dayCount int
}
// roll resets the minute/day buckets that now has moved past. Caller holds u.mu.
func (u *callUsage) roll(now time.Time) {
if m := now.Truncate(time.Minute); !m.Equal(u.minuteStart) {
u.minuteStart, u.minuteCount = m, 0
}
if d := now.UTC().Truncate(24 * time.Hour); !d.Equal(u.dayStart) {
u.dayStart, u.dayCount = d, 0
}
}
func (u *callUsage) record(now time.Time) {
u.mu.Lock()
defer u.mu.Unlock()
u.roll(now)
u.minuteCount++
u.dayCount++
}
func (u *callUsage) snapshot(now time.Time) (minute, day int) {
u.mu.Lock()
defer u.mu.Unlock()
u.roll(now)
return u.minuteCount, u.dayCount
}
// usageStore holds per-API-key call counters for the whole process, so counts
// survive the transient plugin instances the settings cascade builds per request.
type usageStore struct {
mu sync.Mutex
m map[string]*callUsage
}
var usage = &usageStore{m: map[string]*callUsage{}}
func (s *usageStore) get(keyHash string) *callUsage {
s.mu.Lock()
defer s.mu.Unlock()
u := s.m[keyHash]
if u == nil {
u = &callUsage{}
s.m[keyHash] = u
}
return u
}
// keyFingerprint derives a short, non-reversible bucket id from an API key, so
// distinct keys (e.g. per-organization) get independent usage counters without
// the key itself ever being stored in the counter map.
func keyFingerprint(apiKey string) string {
sum := sha256.Sum256([]byte(apiKey))
return hex.EncodeToString(sum[:8])
}
// apiBase is the OpenWeather REST root. It is copied into the plugin instance in
// Init so tests can point the connector at an httptest server.
const apiBase = "https://api.openweathermap.org"
@@ -39,12 +112,14 @@ func init() {
// Plugin is the OpenWeather connector.
type Plugin struct {
mu sync.Mutex
apiKey string
units string
lang string
lat string
lon string
mu sync.Mutex
apiKey string
units string
lang string
lat string
lon string
callsPerMinute int
keyHash string // fingerprint of apiKey, keys this plugin's usage counter
base string
client *http.Client
@@ -85,10 +160,20 @@ func (p *Plugin) Descriptor() plugins.Descriptor {
Help: "Longitude used by the health probe and by calls that supply no location (−180…180)."},
{Key: "lang", Label: "Language", Type: "text",
Help: "Optional ISO language code for human-readable weather descriptions (e.g. en, pl, de). Leave blank for the API default."},
{Key: "callsPerMinute", Label: "Calls per minute limit", Type: "number", Default: "60",
Help: "Your OpenWeather plan's per-minute call limit (the free tier is 60). Used only to gauge usage in the app — OpenWeather does not report remaining quota, so the app counts the calls it makes against this number."},
},
}
}
// atoiDefault parses s as an int, returning def when it is blank or malformed.
func atoiDefault(s string, def int) int {
if n, err := strconv.Atoi(strings.TrimSpace(s)); err == nil && n > 0 {
return n
}
return def
}
func (p *Plugin) Init(_ context.Context, config map[string]string) error {
p.mu.Lock()
defer p.mu.Unlock()
@@ -106,11 +191,27 @@ func (p *Plugin) Init(_ context.Context, config map[string]string) error {
if p.lon == "" {
p.lon = defaultLon
}
p.callsPerMinute = atoiDefault(config["callsPerMinute"], defaultCallsPerMinute)
p.keyHash = keyFingerprint(p.apiKey)
p.base = apiBase
p.client = &http.Client{Timeout: 10 * time.Second}
return nil
}
// do performs an upstream request and records it against this key's usage gauge
// when the request actually reached OpenWeather (any HTTP response — a transport
// failure consumes no quota, so it is not counted).
func (p *Plugin) do(req *http.Request) (*http.Response, error) {
resp, err := p.client.Do(req)
if err == nil {
p.mu.Lock()
kh := p.keyHash
p.mu.Unlock()
usage.get(kh).record(time.Now())
}
return resp, err
}
// requestURL builds an absolute OpenWeather request URL for the given path,
// merging the caller's query with the configured units, language and API key.
// The API key is never included when unset (callers must guard on errNoKey).
@@ -164,7 +265,7 @@ func (p *Plugin) HealthCheck(ctx context.Context) plugins.Health {
target := p.requestURL("/data/2.5/weather", p.pointQuery("", ""))
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, target, nil)
resp, err := p.client.Do(req)
resp, err := p.do(req)
lat := time.Since(start).Milliseconds()
if err != nil {
return plugins.Health{Status: plugins.StatusDown, LatencyMs: lat, Detail: err.Error()}
@@ -186,9 +287,27 @@ func (p *Plugin) HealthCheck(ctx context.Context) plugins.Health {
default:
h.Status, h.Detail = plugins.StatusDown, "HTTP "+resp.Status
}
// Attach the app-side usage gauge (this probe is included in the count) so the
// UI can show consumption against the plan's per-minute limit.
p.mu.Lock()
kh, limit := p.keyHash, p.callsPerMinute
p.mu.Unlock()
minute, day := usage.get(kh).snapshot(time.Now())
h.Usage = &plugins.HealthUsage{MinuteUsed: minute, MinuteLimit: limit, DayUsed: day}
h.Detail += " · " + usageText(minute, limit, day)
return h
}
// usageText renders a compact app-usage summary, e.g. "3/60 calls this min · 27 today".
func usageText(minute, limit, day int) string {
m := strconv.Itoa(minute)
if limit > 0 {
m += "/" + strconv.Itoa(limit)
}
return m + " calls this min · " + strconv.Itoa(day) + " today"
}
// currentSummary renders a short "place: 12°C, clear sky" line from a current
// weather payload. Returns "" when the body can't be parsed.
func currentSummary(data []byte) string {
@@ -253,7 +372,7 @@ func (p *Plugin) Invoke(ctx context.Context, action string, params json.RawMessa
target := p.requestURL(path, p.pointQuery(in.Lat, in.Lon))
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, target, nil)
resp, err := p.client.Do(req)
resp, err := p.do(req)
if err != nil {
return nil, err
}
@@ -7,6 +7,7 @@ import (
"net/http/httptest"
"strings"
"testing"
"time"
"pilotvault/apiserver/internal/plugins"
)
@@ -69,6 +70,9 @@ func TestInitDefaults(t *testing.T) {
if p.lat != defaultLat || p.lon != defaultLon {
t.Errorf("default location = %q,%q, want %q,%q", p.lat, p.lon, defaultLat, defaultLon)
}
if p.callsPerMinute != defaultCallsPerMinute {
t.Errorf("default callsPerMinute = %d, want %d", p.callsPerMinute, defaultCallsPerMinute)
}
if p.client == nil {
t.Error("Init must build an http client")
}
@@ -205,3 +209,56 @@ func TestInvokeNoKey(t *testing.T) {
t.Error("expected errNoKey when no API key is configured")
}
}
// TestUsageCounter confirms each upstream call is counted and surfaced on Health,
// bucketed per API key against the configured per-minute limit.
func TestUsageCounter(t *testing.T) {
srv := newFakeOW(t)
defer srv.Close()
p := &Plugin{}
// A key unique to this test so its counter is isolated from other tests that
// share the process-wide usage store. The fake returns 401 for a non-"good-key",
// but a call that reaches the server is still counted (only transport errors are not).
_ = p.Init(context.Background(), map[string]string{"apiKey": "usage-key-" + t.Name(), "callsPerMinute": "42"})
p.base = srv.URL
ctx := context.Background()
h := p.HealthCheck(ctx)
if h.Usage == nil {
t.Fatal("expected Usage on the health result")
}
if h.Usage.MinuteLimit != 42 {
t.Errorf("MinuteLimit = %d, want the configured 42", h.Usage.MinuteLimit)
}
if h.Usage.MinuteUsed != 1 || h.Usage.DayUsed != 1 {
t.Errorf("after 1 probe usage = %d/min %d/day, want 1/1", h.Usage.MinuteUsed, h.Usage.DayUsed)
}
// A second probe and an Invoke both add to the same bucket.
_ = p.HealthCheck(ctx)
_, _ = p.Invoke(ctx, "weather.current", nil)
h3 := p.HealthCheck(ctx)
if h3.Usage.MinuteUsed != 4 || h3.Usage.DayUsed != 4 {
t.Errorf("after 4 calls usage = %d/min %d/day, want 4/4", h3.Usage.MinuteUsed, h3.Usage.DayUsed)
}
}
// TestUsageRollover confirms the minute bucket resets when the clock moves on.
func TestUsageRollover(t *testing.T) {
u := &callUsage{}
base := time.Date(2026, 7, 14, 10, 30, 0, 0, time.UTC)
u.record(base)
u.record(base.Add(30 * time.Second)) // same minute
if m, d := u.snapshot(base.Add(45 * time.Second)); m != 2 || d != 2 {
t.Fatalf("same-minute snapshot = %d/%d, want 2/2", m, d)
}
// Next minute: minute count resets, day count persists.
if m, d := u.snapshot(base.Add(90 * time.Second)); m != 0 || d != 2 {
t.Errorf("next-minute snapshot = %d/%d, want 0/2", m, d)
}
// Next day: both reset.
if m, d := u.snapshot(base.Add(25 * time.Hour)); m != 0 || d != 0 {
t.Errorf("next-day snapshot = %d/%d, want 0/0", m, d)
}
}
+12
View File
@@ -113,6 +113,18 @@ type Health struct {
LatencyMs int64 `json:"latencyMs,omitempty"`
Detail string `json:"detail,omitempty"`
Credits *HealthCredits `json:"credits,omitempty"`
Usage *HealthUsage `json:"usage,omitempty"`
}
// HealthUsage is optional call-usage accounting a plugin may report when its
// upstream does NOT expose remaining quota (e.g. OpenWeather). Unlike
// HealthCredits — which reflects a balance the upstream reports — these are
// process-local counts of the calls this server has made, bucketed into the
// current minute and day, so the UI can render an approximate usage gauge.
type HealthUsage struct {
MinuteUsed int `json:"minuteUsed"` // calls made in the current minute
MinuteLimit int `json:"minuteLimit,omitempty"` // the plan's per-minute limit
DayUsed int `json:"dayUsed"` // calls made so far today (UTC)
}
// HealthCredits is optional structured rate-limit/credit accounting a plugin may
+20
View File
@@ -0,0 +1,20 @@
(function(){const i=document.createElement("link").relList;if(i&&i.supports&&i.supports("modulepreload"))return;for(const u of document.querySelectorAll('link[rel="modulepreload"]'))l(u);new MutationObserver(u=>{for(const f of u)if(f.type==="childList")for(const h of f.addedNodes)h.tagName==="LINK"&&h.rel==="modulepreload"&&l(h)}).observe(document,{childList:!0,subtree:!0});function s(u){const f={};return u.integrity&&(f.integrity=u.integrity),u.referrerPolicy&&(f.referrerPolicy=u.referrerPolicy),u.crossOrigin==="use-credentials"?f.credentials="include":u.crossOrigin==="anonymous"?f.credentials="omit":f.credentials="same-origin",f}function l(u){if(u.ep)return;u.ep=!0;const f=s(u);fetch(u.href,f)}})();/**
* @vue/shared v3.5.39
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
**/function Xr(t){const i=Object.create(null);for(const s of t.split(","))i[s]=1;return s=>s in i}const yt={},qo=[],ni=()=>{},Ou=()=>!1,Ha=t=>t.charCodeAt(0)===111&&t.charCodeAt(1)===110&&(t.charCodeAt(2)>122||t.charCodeAt(2)<97),ja=t=>t.startsWith("onUpdate:"),Bt=Object.assign,Qr=(t,i)=>{const s=t.indexOf(i);s>-1&&t.splice(s,1)},pd=Object.prototype.hasOwnProperty,mt=(t,i)=>pd.call(t,i),Fe=Array.isArray,Yo=t=>Xs(t)==="[object Map]",is=t=>Xs(t)==="[object Set]",Ll=t=>Xs(t)==="[object Date]",Ye=t=>typeof t=="function",Ct=t=>typeof t=="string",Wn=t=>typeof t=="symbol",gt=t=>t!==null&&typeof t=="object",zu=t=>(gt(t)||Ye(t))&&Ye(t.then)&&Ye(t.catch),Iu=Object.prototype.toString,Xs=t=>Iu.call(t),md=t=>Xs(t).slice(8,-1),$u=t=>Xs(t)==="[object Object]",el=t=>Ct(t)&&t!=="NaN"&&t[0]!=="-"&&""+parseInt(t,10)===t,Ds=Xr(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Wa=t=>{const i=Object.create(null);return(s=>i[s]||(i[s]=t(s)))},gd=/-\w/g,Hn=Wa(t=>t.replace(gd,i=>i.slice(1).toUpperCase())),vd=/\B([A-Z])/g,ji=Wa(t=>t.replace(vd,"-$1").toLowerCase()),Nu=Wa(t=>t.charAt(0).toUpperCase()+t.slice(1)),xr=Wa(t=>t?`on${Nu(t)}`:""),ti=(t,i)=>!Object.is(t,i),Aa=(t,...i)=>{for(let s=0;s<t.length;s++)t[s](...i)},Du=(t,i,s,l=!1)=>{Object.defineProperty(t,i,{configurable:!0,enumerable:!1,writable:l,value:s})},Ka=t=>{const i=parseFloat(t);return isNaN(i)?t:i},_d=t=>{const i=Ct(t)?Number(t):NaN;return isNaN(i)?t:i};let Ml;const Ga=()=>Ml||(Ml=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function wo(t){if(Fe(t)){const i={};for(let s=0;s<t.length;s++){const l=t[s],u=Ct(l)?wd(l):wo(l);if(u)for(const f in u)i[f]=u[f]}return i}else if(Ct(t)||gt(t))return t}const bd=/;(?![^(]*\))/g,yd=/:([^]+)/,xd=/\/\*[^]*?\*\//g;function wd(t){const i={};return t.replace(xd,"").split(bd).forEach(s=>{if(s){const l=s.split(yd);l.length>1&&(i[l[0].trim()]=l[1].trim())}}),i}function Ae(t){let i="";if(Ct(t))i=t;else if(Fe(t))for(let s=0;s<t.length;s++){const l=Ae(t[s]);l&&(i+=l+" ")}else if(gt(t))for(const s in t)t[s]&&(i+=s+" ");return i.trim()}const kd="itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly",Sd=Xr(kd);function Fu(t){return!!t||t===""}function Td(t,i){if(t.length!==i.length)return!1;let s=!0;for(let l=0;s&&l<t.length;l++)s=Vi(t[l],i[l]);return s}function Vi(t,i){if(t===i)return!0;let s=Ll(t),l=Ll(i);if(s||l)return s&&l?t.getTime()===i.getTime():!1;if(s=Wn(t),l=Wn(i),s||l)return t===i;if(s=Fe(t),l=Fe(i),s||l)return s&&l?Td(t,i):!1;if(s=gt(t),l=gt(i),s||l){if(!s||!l)return!1;const u=Object.keys(t).length,f=Object.keys(i).length;if(u!==f)return!1;for(const h in t){const _=t.hasOwnProperty(h),y=i.hasOwnProperty(h);if(_&&!y||!_&&y||!Vi(t[h],i[h]))return!1}}return String(t)===String(i)}function tl(t,i){return t.findIndex(s=>Vi(s,i))}const Ru=t=>!!(t&&t.__v_isRef===!0),k=t=>Ct(t)?t:t==null?"":Fe(t)||gt(t)&&(t.toString===Iu||!Ye(t.toString))?Ru(t)?k(t.value):JSON.stringify(t,Bu,2):String(t),Bu=(t,i)=>Ru(i)?Bu(t,i.value):Yo(i)?{[`Map(${i.size})`]:[...i.entries()].reduce((s,[l,u],f)=>(s[wr(l,f)+" =>"]=u,s),{})}:is(i)?{[`Set(${i.size})`]:[...i.values()].map(s=>wr(s))}:Wn(i)?wr(i):gt(i)&&!Fe(i)&&!$u(i)?String(i):i,wr=(t,i="")=>{var s;return Wn(t)?`Symbol(${(s=t.description)!=null?s:i})`:t};/**
* @vue/reactivity v3.5.39
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
**/let Vt;class Pd{constructor(i=!1){this.detached=i,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this._warnOnRun=!0,this.__v_skip=!0,!i&&Vt&&(Vt.active?(this.parent=Vt,this.index=(Vt.scopes||(Vt.scopes=[])).push(this)-1):(this._active=!1,this._warnOnRun=!1))}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let i,s;if(this.scopes)for(i=0,s=this.scopes.length;i<s;i++)this.scopes[i].pause();for(i=0,s=this.effects.length;i<s;i++)this.effects[i].pause()}}resume(){if(this._active&&this._isPaused){this._isPaused=!1;let i,s;if(this.scopes)for(i=0,s=this.scopes.length;i<s;i++)this.scopes[i].resume();for(i=0,s=this.effects.length;i<s;i++)this.effects[i].resume()}}run(i){if(this._active){const s=Vt;try{return Vt=this,i()}finally{Vt=s}}}on(){++this._on===1&&(this.prevScope=Vt,Vt=this)}off(){if(this._on>0&&--this._on===0){if(Vt===this)Vt=this.prevScope;else{let i=Vt;for(;i;){if(i.prevScope===this){i.prevScope=this.prevScope;break}i=i.prevScope}}this.prevScope=void 0}}stop(i){if(this._active){this._active=!1;let s,l;for(s=0,l=this.effects.length;s<l;s++)this.effects[s].stop();for(this.effects.length=0,s=0,l=this.cleanups.length;s<l;s++)this.cleanups[s]();if(this.cleanups.length=0,this.scopes){for(s=0,l=this.scopes.length;s<l;s++)this.scopes[s].stop(!0);this.scopes.length=0}if(!this.detached&&this.parent&&!i){const u=this.parent.scopes.pop();u&&u!==this&&(this.parent.scopes[this.index]=u,u.index=this.index)}this.parent=void 0}}}function Cd(){return Vt}let kt;const kr=new WeakSet;class Uu{constructor(i){this.fn=i,this.deps=void 0,this.depsTail=void 0,this.flags=5,this.next=void 0,this.cleanup=void 0,this.scheduler=void 0,Vt&&(Vt.active?Vt.effects.push(this):this.flags&=-2)}pause(){this.flags|=64}resume(){this.flags&64&&(this.flags&=-65,kr.has(this)&&(kr.delete(this),this.trigger()))}notify(){this.flags&2&&!(this.flags&32)||this.flags&8||Zu(this)}run(){if(!(this.flags&1))return this.fn();this.flags|=2,Al(this),Hu(this);const i=kt,s=jn;kt=this,jn=!0;try{return this.fn()}finally{ju(this),kt=i,jn=s,this.flags&=-3}}stop(){if(this.flags&1){for(let i=this.deps;i;i=i.nextDep)ol(i);this.deps=this.depsTail=void 0,Al(this),this.onStop&&this.onStop(),this.flags&=-2}}trigger(){this.flags&64?kr.add(this):this.scheduler?this.scheduler():this.runIfDirty()}runIfDirty(){$r(this)&&this.run()}get dirty(){return $r(this)}}let Vu=0,Fs,Rs;function Zu(t,i=!1){if(t.flags|=8,i){t.next=Rs,Rs=t;return}t.next=Fs,Fs=t}function nl(){Vu++}function il(){if(--Vu>0)return;if(Rs){let i=Rs;for(Rs=void 0;i;){const s=i.next;i.next=void 0,i.flags&=-9,i=s}}let t;for(;Fs;){let i=Fs;for(Fs=void 0;i;){const s=i.next;if(i.next=void 0,i.flags&=-9,i.flags&1)try{i.trigger()}catch(l){t||(t=l)}i=s}}if(t)throw t}function Hu(t){for(let i=t.deps;i;i=i.nextDep)i.version=-1,i.prevActiveLink=i.dep.activeLink,i.dep.activeLink=i}function ju(t){let i,s=t.depsTail,l=s;for(;l;){const u=l.prevDep;l.version===-1?(l===s&&(s=u),ol(l),Ld(l)):i=l,l.dep.activeLink=l.prevActiveLink,l.prevActiveLink=void 0,l=u}t.deps=i,t.depsTail=s}function $r(t){for(let i=t.deps;i;i=i.nextDep)if(i.dep.version!==i.version||i.dep.computed&&(Wu(i.dep.computed)||i.dep.version!==i.version))return!0;return!!t._dirty}function Wu(t){if(t.flags&4&&!(t.flags&16)||(t.flags&=-17,t.globalVersion===Hs)||(t.globalVersion=Hs,!t.isSSR&&t.flags&128&&(!t.deps&&!t._dirty||!$r(t))))return;t.flags|=2;const i=t.dep,s=kt,l=jn;kt=t,jn=!0;try{Hu(t);const u=t.fn(t._value);(i.version===0||ti(u,t._value))&&(t.flags|=128,t._value=u,i.version++)}catch(u){throw i.version++,u}finally{kt=s,jn=l,ju(t),t.flags&=-3}}function ol(t,i=!1){const{dep:s,prevSub:l,nextSub:u}=t;if(l&&(l.nextSub=u,t.prevSub=void 0),u&&(u.prevSub=l,t.nextSub=void 0),s.subs===t&&(s.subs=l,!l&&s.computed)){s.computed.flags&=-5;for(let f=s.computed.deps;f;f=f.nextDep)ol(f,!0)}!i&&!--s.sc&&s.map&&s.map.delete(s.key)}function Ld(t){const{prevDep:i,nextDep:s}=t;i&&(i.nextDep=s,t.prevDep=void 0),s&&(s.prevDep=i,t.nextDep=void 0)}let jn=!0;const Ku=[];function ii(){Ku.push(jn),jn=!1}function oi(){const t=Ku.pop();jn=t===void 0?!0:t}function Al(t){const{cleanup:i}=t;if(t.cleanup=void 0,i){const s=kt;kt=void 0;try{i()}finally{kt=s}}}let Hs=0;class Md{constructor(i,s){this.sub=i,this.dep=s,this.version=s.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class sl{constructor(i){this.computed=i,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(i){if(!kt||!jn||kt===this.computed)return;let s=this.activeLink;if(s===void 0||s.sub!==kt)s=this.activeLink=new Md(kt,this),kt.deps?(s.prevDep=kt.depsTail,kt.depsTail.nextDep=s,kt.depsTail=s):kt.deps=kt.depsTail=s,Gu(s);else if(s.version===-1&&(s.version=this.version,s.nextDep)){const l=s.nextDep;l.prevDep=s.prevDep,s.prevDep&&(s.prevDep.nextDep=l),s.prevDep=kt.depsTail,s.nextDep=void 0,kt.depsTail.nextDep=s,kt.depsTail=s,kt.deps===s&&(kt.deps=l)}return s}trigger(i){this.versiLine truncated
* @vue/runtime-core v3.5.39
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
**/function Qs(t,i,s,l){try{return l?t(...l):t()}catch(u){Ya(u,i,s)}}function Dn(t,i,s,l){if(Ye(t)){const u=Qs(t,i,s,l);return u&&zu(u)&&u.catch(f=>{Ya(f,i,s)}),u}if(Fe(t)){const u=[];for(let f=0;f<t.length;f++)u.push(Dn(t[f],i,s,l));return u}}function Ya(t,i,s,l=!0){const u=i?i.vnode:null,{errorHandler:f,throwUnhandledErrorInProduction:h}=i&&i.appContext.config||yt;if(i){let _=i.parent;const y=i.proxy,C=`https://vuejs.org/error-reference/#runtime-${s}`;for(;_;){const T=_.ec;if(T){for(let A=0;A<T.length;A++)if(T[A](t,y,C)===!1)return}_=_.parent}if(f){ii(),Qs(f,null,10,[t,y,C]),oi();return}}ef(t,s,u,l,h)}function ef(t,i,s,l=!0,u=!1){if(u)throw t;console.error(t)}const sn=[];let Xn=-1;const Jo=[];let Ui=null,Go=0;const nc=Promise.resolve();let za=null;function ic(t){const i=za||nc;return t?i.then(this?t.bind(this):t):i}function tf(t){let i=Xn+1,s=sn.length;for(;i<s;){const l=i+s>>>1,u=sn[l],f=Ws(u);f<t||f===t&&u.flags&2?i=l+1:s=l}return i}function ul(t){if(!(t.flags&1)){const i=Ws(t),s=sn[sn.length-1];!s||!(t.flags&2)&&i>=Ws(s)?sn.push(t):sn.splice(tf(i),0,t),t.flags|=1,oc()}}function oc(){za||(za=nc.then(ac))}function nf(t){Fe(t)?Jo.push(...t):Ui&&t.id===-1?Ui.splice(Go+1,0,t):t.flags&1||(Jo.push(t),t.flags|=1),oc()}function Ol(t,i,s=Xn+1){for(;s<sn.length;s++){const l=sn[s];if(l&&l.flags&2){if(t&&l.id!==t.uid)continue;sn.splice(s,1),s--,l.flags&4&&(l.flags&=-2),l(),l.flags&4||(l.flags&=-2)}}}function sc(t){if(Jo.length){const i=[...new Set(Jo)].sort((s,l)=>Ws(s)-Ws(l));if(Jo.length=0,Ui){Ui.push(...i);return}for(Ui=i,Go=0;Go<Ui.length;Go++){const s=Ui[Go];s.flags&4&&(s.flags&=-2),s.flags&8||s(),s.flags&=-2}Ui=null,Go=0}}const Ws=t=>t.id==null?t.flags&2?-1:1/0:t.id;function ac(t){try{for(Xn=0;Xn<sn.length;Xn++){const i=sn[Xn];i&&!(i.flags&8)&&(i.flags&4&&(i.flags&=-2),Qs(i,i.i,i.i?15:14),i.flags&4||(i.flags&=-2))}}finally{for(;Xn<sn.length;Xn++){const i=sn[Xn];i&&(i.flags&=-2)}Xn=-1,sn.length=0,sc(),za=null,(sn.length||Jo.length)&&ac()}}let Yt=null,rc=null;function Ia(t){const i=Yt;return Yt=t,rc=t&&t.type.__scopeId||null,i}function xe(t,i=Yt,s){if(!i||t._n)return t;const l=(...u)=>{l._d&&Da(-1);const f=Ia(i);let h;try{h=t(...u)}finally{Ia(f),l._d&&Da(1)}return h};return l._n=!0,l._c=!0,l._d=!0,l}function ee(t,i){if(Yt===null)return t;const s=tr(Yt),l=t.dirs||(t.dirs=[]);for(let u=0;u<i.length;u++){let[f,h,_,y=yt]=i[u];f&&(Ye(f)&&(f={mounted:f,updated:f}),f.deep&&yi(h),l.push({dir:f,instance:s,value:h,oldValue:void 0,arg:_,modifiers:y}))}return t}function ho(t,i,s,l){const u=t.dirs,f=i&&i.dirs;for(let h=0;h<u.length;h++){const _=u[h];f&&(_.oldValue=f[h].value);let y=_.dir[l];y&&(ii(),Dn(y,s,8,[t.el,_,t,i]),oi())}}function lc(t,i){if(an){let s=an.provides;const l=an.parent&&an.parent.provides;l===s&&(s=an.provides=Object.create(l)),s[t]=i}}function Bs(t,i,s=!1){const l=Bc();if(l||Qo){let u=Qo?Qo._context.provides:l?l.parent==null||l.ce?l.vnode.appContext&&l.vnode.appContext.provides:l.parent.provides:void 0;if(u&&t in u)return u[t];if(arguments.length>1)return s&&Ye(i)?i.call(l&&l.proxy):i}}const of=Symbol.for("v-scx"),sf=()=>Bs(of);function Nt(t,i,s){return uc(t,i,s)}function uc(t,i,s=yt){const{immediate:l,deep:u,flush:f,once:h}=s,_=Bt({},s),y=i&&l||!i&&f!=="post";let C;if(Ys){if(f==="sync"){const B=sf();C=B.__watcherHandles||(B.__watcherHandles=[])}else if(!y){const B=()=>{};return B.stop=ni,B.resume=ni,B.pause=ni,B}}const T=an;_.call=(B,j,F)=>Dn(B,T,j,F);let A=!1;f==="post"?_.scheduler=B=>{on(B,T&&T.suspense)}:f!=="sync"&&(A=!0,_.scheduler=(B,j)=>{j?B():ul(B)}),_.augmentJob=B=>{i&&(B.flags|=4),A&&(B.flags|=2,T&&(B.id=T.uid,B.i=T))};const R=Qd(t,i,_);return Ys&&(C?C.push(R):y&&R()),R}function af(t,i,s){const l=this.proxy,u=Ct(t)?t.includes(".")?cc(l,t):()=>l[t]:t.bind(l,l);let f;Ye(i)?f=i:(f=i.handler,s=i);const h=ea(this),_=uc(u,f.bind(l),s);return h(),_}function cc(t,i){const s=i.split(".");return()=>{let l=t;for(let u=0;u<s.length&&l;u++)l=l[s[u]];return l}}const Bi=new WeakMap,dc=Symbol("_vte"),fc=t=>t.__isTeleport,_o=t=>t&&(t.disabled||t.disabled===""),rf=t=>t&&(t.defer||t.defer===""),zl=t=>typeof SVGElement<"u"&&t instanceof SVGElement,Il=t=>typeof MathMLElement=="function"&&t instanceof MathMLElement,Br=(t,i)=>{const s=t&&t.to;return Ct(s)?i?i(s):null:s},lf={name:"Teleport",__isTeleport:!0,process(t,i,s,l,u,f,h,_,y,C){const{mc:T,pc:A,pbc:R,o:{insert:B,querySelector:j,createText:F,createComment:pe,parentNode:me}}=C,Y=_o(i.props);let{dynamicChildren:Le}=i;const fe=(Ie,Ge,we)=>{Ie.shapeFlag&16&&T(Ie.children,Ge,we,u,f,h,_,y)},Ue=(Ie=i)=>{const Ge=_o(Ie.props),we=Ie.target=Br(Ie.props,j),Te=Ur(we,Ie,F,B);we&&(h!=="svg"&&zl(we)?h="svg":h!=="mathml"&&Il(we)&&(h="mathml"),u&&u.isCE&&(u.ce._teleportTargets||(u.ce._teleportTargets=new Set)).add(we),Ge||(fe(Ie,we,Te),zs(Ie,!1)))},Ne=Ie=>{const Ge=()=>{if(Bi.get(Ie)===Ge){if(Bi.delete(Ie),_o(Ie.props)){const we=me(Ie.el)||s;fe(Ie,we,Ie.anchor),zs(Ie,!0)}Ue(Ie)}};Bi.set(Ie,Ge),on(Ge,f)};if(t==null){const Ie=i.el=F(""),Ge=i.anchor=F("");if(B(Ie,s,l),B(Ge,s,l),rf Line truncated
* @vue/runtime-dom v3.5.39
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
**/let Gr;const Gl=typeof window<"u"&&window.trustedTypes;if(Gl)try{Gr=Gl.createPolicy("vue",{createHTML:t=>t})}catch{}const Zc=Gr?t=>Gr.createHTML(t):t=>t,fh="http://www.w3.org/2000/svg",hh="http://www.w3.org/1998/Math/MathML",vi=typeof document<"u"?document:null,ql=vi&&vi.createElement("template"),ph={insert:(t,i,s)=>{i.insertBefore(t,s||null)},remove:t=>{const i=t.parentNode;i&&i.removeChild(t)},createElement:(t,i,s,l)=>{const u=i==="svg"?vi.createElementNS(fh,t):i==="mathml"?vi.createElementNS(hh,t):s?vi.createElement(t,{is:s}):vi.createElement(t);return t==="select"&&l&&l.multiple!=null&&u.setAttribute("multiple",l.multiple),u},createText:t=>vi.createTextNode(t),createComment:t=>vi.createComment(t),setText:(t,i)=>{t.nodeValue=i},setElementText:(t,i)=>{t.textContent=i},parentNode:t=>t.parentNode,nextSibling:t=>t.nextSibling,querySelector:t=>vi.querySelector(t),setScopeId(t,i){t.setAttribute(i,"")},insertStaticContent(t,i,s,l,u,f){const h=s?s.previousSibling:i.lastChild;if(u&&(u===f||u.nextSibling))for(;i.insertBefore(u.cloneNode(!0),s),!(u===f||!(u=u.nextSibling)););else{ql.innerHTML=Zc(l==="svg"?`<svg>${t}</svg>`:l==="mathml"?`<math>${t}</math>`:t);const _=ql.content;if(l==="svg"||l==="mathml"){const y=_.firstChild;for(;y.firstChild;)_.appendChild(y.firstChild);_.removeChild(y)}i.insertBefore(_,s)}return[h?h.nextSibling:i.firstChild,s?s.previousSibling:i.lastChild]}},Fi="transition",Es="animation",Js=Symbol("_vtc"),Hc={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},mh=Bt({},hc,Hc),gh=t=>(t.displayName="Transition",t.props=mh,t),vh=gh((t,{slots:i})=>ch(hf,_h(t),i)),mo=(t,i=[])=>{Fe(t)?t.forEach(s=>s(...i)):t&&t(...i)},Yl=t=>t?Fe(t)?t.some(i=>i.length>1):t.length>1:!1;function _h(t){const i={};for(const ie in t)ie in Hc||(i[ie]=t[ie]);if(t.css===!1)return i;const{name:s="v",type:l,duration:u,enterFromClass:f=`${s}-enter-from`,enterActiveClass:h=`${s}-enter-active`,enterToClass:_=`${s}-enter-to`,appearFromClass:y=f,appearActiveClass:C=h,appearToClass:T=_,leaveFromClass:A=`${s}-leave-from`,leaveActiveClass:R=`${s}-leave-active`,leaveToClass:B=`${s}-leave-to`}=t,j=bh(u),F=j&&j[0],pe=j&&j[1],{onBeforeEnter:me,onEnter:Y,onEnterCancelled:Le,onLeave:fe,onLeaveCancelled:Ue,onBeforeAppear:Ne=me,onAppear:Ie=Y,onAppearCancelled:Ge=Le}=i,we=(ie,je,oe,We)=>{ie._enterCancelled=We,go(ie,je?T:_),go(ie,je?C:h),oe&&oe()},Te=(ie,je)=>{ie._isLeaving=!1,go(ie,A),go(ie,B),go(ie,R),je&&je()},ze=ie=>(je,oe)=>{const We=ie?Ie:Y,ue=()=>we(je,ie,oe);mo(We,[je,ue]),Jl(()=>{go(je,ie?y:f),gi(je,ie?T:_),Yl(We)||Xl(je,l,F,ue)})};return Bt(i,{onBeforeEnter(ie){mo(me,[ie]),gi(ie,f),gi(ie,h)},onBeforeAppear(ie){mo(Ne,[ie]),gi(ie,y),gi(ie,C)},onEnter:ze(!1),onAppear:ze(!0),onLeave(ie,je){ie._isLeaving=!0;const oe=()=>Te(ie,je);gi(ie,A),ie._enterCancelled?(gi(ie,R),tu(ie)):(tu(ie),gi(ie,R)),Jl(()=>{ie._isLeaving&&(go(ie,A),gi(ie,B),Yl(fe)||Xl(ie,l,pe,oe))}),mo(fe,[ie,oe])},onEnterCancelled(ie){we(ie,!1,void 0,!0),mo(Le,[ie])},onAppearCancelled(ie){we(ie,!0,void 0,!0),mo(Ge,[ie])},onLeaveCancelled(ie){Te(ie),mo(Ue,[ie])}})}function bh(t){if(t==null)return null;if(gt(t))return[Ar(t.enter),Ar(t.leave)];{const i=Ar(t);return[i,i]}}function Ar(t){return _d(t)}function gi(t,i){i.split(/\s+/).forEach(s=>s&&t.classList.add(s)),(t[Js]||(t[Js]=new Set)).add(i)}function go(t,i){i.split(/\s+/).forEach(l=>l&&t.classList.remove(l));const s=t[Js];s&&(s.delete(i),s.size||(t[Js]=void 0))}function Jl(t){requestAnimationFrame(()=>{requestAnimationFrame(t)})}let yh=0;function Xl(t,i,s,l){const u=t._endId=++yh,f=()=>{u===t._endId&&l()};if(s!=null)return setTimeout(f,s);const{type:h,timeout:_,propCount:y}=xh(t,i);if(!h)return l();const C=h+"end";let T=0;const A=()=>{t.removeEventListener(C,R),f()},R=B=>{B.target===t&&++T>=y&&A()};setTimeout(()=>{T<y&&A()},_+1),t.addEventListener(C,R)}function xh(t,i){const s=window.getComputedStyle(t),l=j=>(s[j]||"").split(", "),u=l(`${Fi}Delay`),f=l(`${Fi}Duration`),h=Ql(u,f),_=l(`${Es}Delay`),y=l(`${Es}Duration`),C=Ql(_,y);let T=null,A=0,R=0;i===Fi?h>0&&(T=Fi,A=h,R=f.length):i===Es?C>0&&(T=Es,A=C,R=y.length):(A=Math.max(h,C),T=A>0?h>C?Fi:Es:null,R=T?T===Fi?f.length:y.length:0);const B=T===Fi&&/\b(?:transform|all)(?:,|$)/.test(l(`${Fi}Property`).toString());return{type:T,timeout:A,propCount:R,hasTransform:B}}function Ql(t,i){for(;t.length<i.length;)t=t.concat(t);return Math.max(...i.map((s,l)=>eu(s)+eu(t[l])))}function eu(t){return t==="auto"?0:Number(t.slice(0,-1).replace(",","."))*1e3}function tu(t){return(t?t.ownerDocument:document).body.offsetHeight}function wh(t,i,s){const l=t[Js];l&&(i=(i?[i,...l]:[...l]).join(" ")),i==null?t.removeAttribute("class"):s?t.setAttribute("class",i):t.className=i}const Ba=Symbol("_vod"),jc=Symbol("_vsh"),kh={name:"show",beforeMount(t,{value:i},{transition:s 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 rm=Ns.exports,Tu;function lm(){return Tu||(Tu=1,(function(t,i){(function(s,l){l(i)})(rm,(function(s){var l="1.9.4";function u(e){var n,o,r,d;for(o=1,r=arguments.length;o<r;o++){d=arguments[o];for(n in d)e[n]=d[n]}return e}var f=Object.create||(function(){function e(){}return function(n){return e.prototype=n,new e}})();function h(e,n){var o=Array.prototype.slice;if(e.bind)return e.bind.apply(e,o.call(arguments,1));var r=o.call(arguments,2);return function(){return e.apply(n,r.length?r.concat(o.call(arguments)):arguments)}}var _=0;function y(e){return"_leaflet_id"in e||(e._leaflet_id=++_),e._leaflet_id}function C(e,n,o){var r,d,v,P;return P=function(){r=!1,d&&(v.apply(o,d),d=!1)},v=function(){r?d=arguments:(e.apply(o,arguments),setTimeout(P,n),r=!0)},v}function T(e,n,o){var r=n[1],d=n[0],v=r-d;return e===r&&o?e:((e-d)%v+v)%v+d}function A(){return!1}function R(e,n){if(n===!1)return e;var o=Math.pow(10,n===void 0?6:n);return Math.round(e*o)/o}function B(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}function j(e){return B(e).split(/\s+/)}function F(e,n){Object.prototype.hasOwnProperty.call(e,"options")||(e.options=e.options?f(e.options):{});for(var o in n)e.options[o]=n[o];return e.options}function pe(e,n,o){var r=[];for(var d in e)r.push(encodeURIComponent(o?d.toUpperCase():d)+"="+encodeURIComponent(e[d]));return(!n||n.indexOf("?")===-1?"?":"&")+r.join("&")}var me=/\{ *([\w_ -]+) *\}/g;function Y(e,n){return e.replace(me,function(o,r){var d=n[r];if(d===void 0)throw new Error("No value provided for variable "+o);return typeof d=="function"&&(d=d(n)),d})}var Le=Array.isArray||function(e){return Object.prototype.toString.call(e)==="[object Array]"};function fe(e,n){for(var o=0;o<e.length;o++)if(e[o]===n)return o;return-1}var Ue="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";function Ne(e){return window["webkit"+e]||window["moz"+e]||window["ms"+e]}var Ie=0;function Ge(e){var n=+new Date,o=Math.max(0,16-(n-Ie));return Ie=n+o,window.setTimeout(e,o)}var we=window.requestAnimationFrame||Ne("RequestAnimationFrame")||Ge,Te=window.cancelAnimationFrame||Ne("CancelAnimationFrame")||Ne("CancelRequestAnimationFrame")||function(e){window.clearTimeout(e)};function ze(e,n,o){if(o&&we===Ge)e.call(n);else return we.call(window,h(e,n))}function ie(e){e&&Te.call(window,e)}var je={__proto__:null,extend:u,create:f,bind:h,get lastId(){return _},stamp:y,throttle:C,wrapNum:T,falseFn:A,formatNum:R,trim:B,splitWords:j,setOptions:F,getParamString:pe,template:Y,isArray:Le,indexOf:fe,emptyImageUrl:Ue,requestFn:we,cancelFn:Te,requestAnimFrame:ze,cancelAnimFrame:ie};function oe(){}oe.extend=function(e){var n=function(){F(this),this.initialize&&this.initialize.apply(this,arguments),this.callInitHooks()},o=n.__super__=this.prototype,r=f(o);r.constructor=n,n.prototype=r;for(var d in this)Object.prototype.hasOwnProperty.call(this,d)&&d!=="prototype"&&d!=="__super__"&&(n[d]=this[d]);return e.statics&&u(n,e.statics),e.includes&&(We(e.includes),u.apply(null,[r].concat(e.includes))),u(r,e),delete r.statics,delete r.includes,r.options&&(r.options=o.options?f(o.options):{},u(r.options,e.options)),r._initHooks=[],r.callInitHooks=function(){if(!this._initHooksCalled){o.callInitHooks&&o.callInitHooks.call(this),this._initHooksCalled=!0;for(var v=0,P=r._initHooks.length;v<P;v++)r._initHooks[v].call(this)}},n},oe.include=function(e){var n=this.prototype.options;return u(this.prototype,e),e.options&&(this.prototype.options=n,this.mergeOptions(e.options)),this},oe.mergeOptions=function(e){return u(this.prototype.options,e),this},oe.addInitHook=function(e){var n=Array.prototype.slice.call(arguments,1),o=typeof e=="function"?e:function(){this[e].apply(this,n)};return this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(o),this};function We(e){if(!(typeof L>"u"||!L||!L.Mixin)){e=Le(e)?e:[e];for(var n=0;n<e.length;n++)e[n]===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 ue={on:function(e,n,o){if(typeof e=="object")for(var r in e)this._on(r,e[r],n);else{e=j(e);for(var d=0,v=e.length;d<v;d++)this._on(e[d],n,o)}return this},off:function(e,n,o){if(!arguments.length)delete this._events;else if(typeof e=="object")for(var r in e)this._off(r,e[r],n);else{e=j(e);for(var d=arguments.length===1,v=0,P=e.length;v<P;v++)d?this._off(e[v]):this._off(e[v],n,o)}return this},_on:function(e,n,o,r){if(typeof n!="function"){console.warn("wrong listener type: "+typeof n);return}if(this._listens(e,n,o)===!1){o===this&&(o=void 0);var d={fn:n,ctx:o};r&&(d.once=!0),this._events=this._events||{},this._events[e]=this._events[e]||[],this._events[e].push(d)}},_off:function(e,n,o){var r,d,v;if(this._events&&(r=this._events[e],!!r)){if(arguments.length===1){if(this._firingCount)for(d=0,v=r.length;d<v;d++)r[d].fn=A;delete this._events[e];return}if(typeof n!="function"){console.warn("wrong listener type:Line truncated
-20
View File
@@ -1,20 +0,0 @@
(function(){const i=document.createElement("link").relList;if(i&&i.supports&&i.supports("modulepreload"))return;for(const u of document.querySelectorAll('link[rel="modulepreload"]'))l(u);new MutationObserver(u=>{for(const f of u)if(f.type==="childList")for(const h of f.addedNodes)h.tagName==="LINK"&&h.rel==="modulepreload"&&l(h)}).observe(document,{childList:!0,subtree:!0});function s(u){const f={};return u.integrity&&(f.integrity=u.integrity),u.referrerPolicy&&(f.referrerPolicy=u.referrerPolicy),u.crossOrigin==="use-credentials"?f.credentials="include":u.crossOrigin==="anonymous"?f.credentials="omit":f.credentials="same-origin",f}function l(u){if(u.ep)return;u.ep=!0;const f=s(u);fetch(u.href,f)}})();/**
* @vue/shared v3.5.39
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
**/function Yr(t){const i=Object.create(null);for(const s of t.split(","))i[s]=1;return s=>s in i}const bt={},qo=[],ni=()=>{},Ou=()=>!1,Ka=t=>t.charCodeAt(0)===111&&t.charCodeAt(1)===110&&(t.charCodeAt(2)>122||t.charCodeAt(2)<97),Ga=t=>t.startsWith("onUpdate:"),Dt=Object.assign,Jr=(t,i)=>{const s=t.indexOf(i);s>-1&&t.splice(s,1)},pd=Object.prototype.hasOwnProperty,mt=(t,i)=>pd.call(t,i),Fe=Array.isArray,Yo=t=>Js(t)==="[object Map]",os=t=>Js(t)==="[object Set]",Ll=t=>Js(t)==="[object Date]",Je=t=>typeof t=="function",Pt=t=>typeof t=="string",Zn=t=>typeof t=="symbol",gt=t=>t!==null&&typeof t=="object",zu=t=>(gt(t)||Je(t))&&Je(t.then)&&Je(t.catch),Iu=Object.prototype.toString,Js=t=>Iu.call(t),md=t=>Js(t).slice(8,-1),$u=t=>Js(t)==="[object Object]",Xr=t=>Pt(t)&&t!=="NaN"&&t[0]!=="-"&&""+parseInt(t,10)===t,Ns=Yr(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),qa=t=>{const i=Object.create(null);return(s=>i[s]||(i[s]=t(s)))},gd=/-\w/g,Un=qa(t=>t.replace(gd,i=>i.slice(1).toUpperCase())),vd=/\B([A-Z])/g,Wi=qa(t=>t.replace(vd,"-$1").toLowerCase()),Nu=qa(t=>t.charAt(0).toUpperCase()+t.slice(1)),br=qa(t=>t?`on${Nu(t)}`:""),ti=(t,i)=>!Object.is(t,i),za=(t,...i)=>{for(let s=0;s<t.length;s++)t[s](...i)},Du=(t,i,s,l=!1)=>{Object.defineProperty(t,i,{configurable:!0,enumerable:!1,writable:l,value:s})},Ya=t=>{const i=parseFloat(t);return isNaN(i)?t:i},_d=t=>{const i=Pt(t)?Number(t):NaN;return isNaN(i)?t:i};let Al;const Ja=()=>Al||(Al=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function ts(t){if(Fe(t)){const i={};for(let s=0;s<t.length;s++){const l=t[s],u=Pt(l)?wd(l):ts(l);if(u)for(const f in u)i[f]=u[f]}return i}else if(Pt(t)||gt(t))return t}const bd=/;(?![^(]*\))/g,yd=/:([^]+)/,xd=/\/\*[^]*?\*\//g;function wd(t){const i={};return t.replace(xd,"").split(bd).forEach(s=>{if(s){const l=s.split(yd);l.length>1&&(i[l[0].trim()]=l[1].trim())}}),i}function Me(t){let i="";if(Pt(t))i=t;else if(Fe(t))for(let s=0;s<t.length;s++){const l=Me(t[s]);l&&(i+=l+" ")}else if(gt(t))for(const s in t)t[s]&&(i+=s+" ");return i.trim()}const kd="itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly",Sd=Yr(kd);function Fu(t){return!!t||t===""}function Td(t,i){if(t.length!==i.length)return!1;let s=!0;for(let l=0;s&&l<t.length;l++)s=Zi(t[l],i[l]);return s}function Zi(t,i){if(t===i)return!0;let s=Ll(t),l=Ll(i);if(s||l)return s&&l?t.getTime()===i.getTime():!1;if(s=Zn(t),l=Zn(i),s||l)return t===i;if(s=Fe(t),l=Fe(i),s||l)return s&&l?Td(t,i):!1;if(s=gt(t),l=gt(i),s||l){if(!s||!l)return!1;const u=Object.keys(t).length,f=Object.keys(i).length;if(u!==f)return!1;for(const h in t){const _=t.hasOwnProperty(h),y=i.hasOwnProperty(h);if(_&&!y||!_&&y||!Zi(t[h],i[h]))return!1}}return String(t)===String(i)}function Qr(t,i){return t.findIndex(s=>Zi(s,i))}const Ru=t=>!!(t&&t.__v_isRef===!0),k=t=>Pt(t)?t:t==null?"":Fe(t)||gt(t)&&(t.toString===Iu||!Je(t.toString))?Ru(t)?k(t.value):JSON.stringify(t,Bu,2):String(t),Bu=(t,i)=>Ru(i)?Bu(t,i.value):Yo(i)?{[`Map(${i.size})`]:[...i.entries()].reduce((s,[l,u],f)=>(s[yr(l,f)+" =>"]=u,s),{})}:os(i)?{[`Set(${i.size})`]:[...i.values()].map(s=>yr(s))}:Zn(i)?yr(i):gt(i)&&!Fe(i)&&!$u(i)?String(i):i,yr=(t,i="")=>{var s;return Zn(t)?`Symbol(${(s=t.description)!=null?s:i})`:t};/**
* @vue/reactivity v3.5.39
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
**/let Vt;class Pd{constructor(i=!1){this.detached=i,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this._warnOnRun=!0,this.__v_skip=!0,!i&&Vt&&(Vt.active?(this.parent=Vt,this.index=(Vt.scopes||(Vt.scopes=[])).push(this)-1):(this._active=!1,this._warnOnRun=!1))}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let i,s;if(this.scopes)for(i=0,s=this.scopes.length;i<s;i++)this.scopes[i].pause();for(i=0,s=this.effects.length;i<s;i++)this.effects[i].pause()}}resume(){if(this._active&&this._isPaused){this._isPaused=!1;let i,s;if(this.scopes)for(i=0,s=this.scopes.length;i<s;i++)this.scopes[i].resume();for(i=0,s=this.effects.length;i<s;i++)this.effects[i].resume()}}run(i){if(this._active){const s=Vt;try{return Vt=this,i()}finally{Vt=s}}}on(){++this._on===1&&(this.prevScope=Vt,Vt=this)}off(){if(this._on>0&&--this._on===0){if(Vt===this)Vt=this.prevScope;else{let i=Vt;for(;i;){if(i.prevScope===this){i.prevScope=this.prevScope;break}i=i.prevScope}}this.prevScope=void 0}}stop(i){if(this._active){this._active=!1;let s,l;for(s=0,l=this.effects.length;s<l;s++)this.effects[s].stop();for(this.effects.length=0,s=0,l=this.cleanups.length;s<l;s++)this.cleanups[s]();if(this.cleanups.length=0,this.scopes){for(s=0,l=this.scopes.length;s<l;s++)this.scopes[s].stop(!0);this.scopes.length=0}if(!this.detached&&this.parent&&!i){const u=this.parent.scopes.pop();u&&u!==this&&(this.parent.scopes[this.index]=u,u.index=this.index)}this.parent=void 0}}}function Cd(){return Vt}let wt;const xr=new WeakSet;class Uu{constructor(i){this.fn=i,this.deps=void 0,this.depsTail=void 0,this.flags=5,this.next=void 0,this.cleanup=void 0,this.scheduler=void 0,Vt&&(Vt.active?Vt.effects.push(this):this.flags&=-2)}pause(){this.flags|=64}resume(){this.flags&64&&(this.flags&=-65,xr.has(this)&&(xr.delete(this),this.trigger()))}notify(){this.flags&2&&!(this.flags&32)||this.flags&8||Zu(this)}run(){if(!(this.flags&1))return this.fn();this.flags|=2,Ml(this),Hu(this);const i=wt,s=Vn;wt=this,Vn=!0;try{return this.fn()}finally{ju(this),wt=i,Vn=s,this.flags&=-3}}stop(){if(this.flags&1){for(let i=this.deps;i;i=i.nextDep)nl(i);this.deps=this.depsTail=void 0,Ml(this),this.onStop&&this.onStop(),this.flags&=-2}}trigger(){this.flags&64?xr.add(this):this.scheduler?this.scheduler():this.runIfDirty()}runIfDirty(){zr(this)&&this.run()}get dirty(){return zr(this)}}let Vu=0,Ds,Fs;function Zu(t,i=!1){if(t.flags|=8,i){t.next=Fs,Fs=t;return}t.next=Ds,Ds=t}function el(){Vu++}function tl(){if(--Vu>0)return;if(Fs){let i=Fs;for(Fs=void 0;i;){const s=i.next;i.next=void 0,i.flags&=-9,i=s}}let t;for(;Ds;){let i=Ds;for(Ds=void 0;i;){const s=i.next;if(i.next=void 0,i.flags&=-9,i.flags&1)try{i.trigger()}catch(l){t||(t=l)}i=s}}if(t)throw t}function Hu(t){for(let i=t.deps;i;i=i.nextDep)i.version=-1,i.prevActiveLink=i.dep.activeLink,i.dep.activeLink=i}function ju(t){let i,s=t.depsTail,l=s;for(;l;){const u=l.prevDep;l.version===-1?(l===s&&(s=u),nl(l),Ld(l)):i=l,l.dep.activeLink=l.prevActiveLink,l.prevActiveLink=void 0,l=u}t.deps=i,t.depsTail=s}function zr(t){for(let i=t.deps;i;i=i.nextDep)if(i.dep.version!==i.version||i.dep.computed&&(Wu(i.dep.computed)||i.dep.version!==i.version))return!0;return!!t._dirty}function Wu(t){if(t.flags&4&&!(t.flags&16)||(t.flags&=-17,t.globalVersion===Zs)||(t.globalVersion=Zs,!t.isSSR&&t.flags&128&&(!t.deps&&!t._dirty||!zr(t))))return;t.flags|=2;const i=t.dep,s=wt,l=Vn;wt=t,Vn=!0;try{Hu(t);const u=t.fn(t._value);(i.version===0||ti(u,t._value))&&(t.flags|=128,t._value=u,i.version++)}catch(u){throw i.version++,u}finally{wt=s,Vn=l,ju(t),t.flags&=-3}}function nl(t,i=!1){const{dep:s,prevSub:l,nextSub:u}=t;if(l&&(l.nextSub=u,t.prevSub=void 0),u&&(u.prevSub=l,t.nextSub=void 0),s.subs===t&&(s.subs=l,!l&&s.computed)){s.computed.flags&=-5;for(let f=s.computed.deps;f;f=f.nextDep)nl(f,!0)}!i&&!--s.sc&&s.map&&s.map.delete(s.key)}function Ld(t){const{prevDep:i,nextDep:s}=t;i&&(i.nextDep=s,t.prevDep=void 0),s&&(s.prevDep=i,t.nextDep=void 0)}let Vn=!0;const Ku=[];function ii(){Ku.push(Vn),Vn=!1}function oi(){const t=Ku.pop();Vn=t===void 0?!0:t}function Ml(t){const{cleanup:i}=t;if(t.cleanup=void 0,i){const s=wt;wt=void 0;try{i()}finally{wt=s}}}let Zs=0;class Ad{constructor(i,s){this.sub=i,this.dep=s,this.version=s.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class il{constructor(i){this.computed=i,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(i){if(!wt||!Vn||wt===this.computed)return;let s=this.activeLink;if(s===void 0||s.sub!==wt)s=this.activeLink=new Ad(wt,this),wt.deps?(s.prevDep=wt.depsTail,wt.depsTail.nextDep=s,wt.depsTail=s):wt.deps=wt.depsTail=s,Gu(s);else if(s.version===-1&&(s.version=this.version,s.nextDep)){const l=s.nextDep;l.prevDep=s.prevDep,s.prevDep&&(s.prevDep.nextDep=l),s.prevDep=wt.depsTail,s.nextDep=void 0,wt.depsTail.nextDep=s,wt.depsTail=s,wt.deps===s&&(wt.deps=l)}return s}trigger(i){this.versiLine truncated
* @vue/runtime-core v3.5.39
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
**/function Xs(t,i,s,l){try{return l?t(...l):t()}catch(u){Qa(u,i,s)}}function In(t,i,s,l){if(Je(t)){const u=Xs(t,i,s,l);return u&&zu(u)&&u.catch(f=>{Qa(f,i,s)}),u}if(Fe(t)){const u=[];for(let f=0;f<t.length;f++)u.push(In(t[f],i,s,l));return u}}function Qa(t,i,s,l=!0){const u=i?i.vnode:null,{errorHandler:f,throwUnhandledErrorInProduction:h}=i&&i.appContext.config||bt;if(i){let _=i.parent;const y=i.proxy,C=`https://vuejs.org/error-reference/#runtime-${s}`;for(;_;){const T=_.ec;if(T){for(let M=0;M<T.length;M++)if(T[M](t,y,C)===!1)return}_=_.parent}if(f){ii(),Xs(f,null,10,[t,y,C]),oi();return}}ef(t,s,u,l,h)}function ef(t,i,s,l=!0,u=!1){if(u)throw t;console.error(t)}const sn=[];let Xn=-1;const Jo=[];let Vi=null,Go=0;const nc=Promise.resolve();let Na=null;function ic(t){const i=Na||nc;return t?i.then(this?t.bind(this):t):i}function tf(t){let i=Xn+1,s=sn.length;for(;i<s;){const l=i+s>>>1,u=sn[l],f=js(u);f<t||f===t&&u.flags&2?i=l+1:s=l}return i}function rl(t){if(!(t.flags&1)){const i=js(t),s=sn[sn.length-1];!s||!(t.flags&2)&&i>=js(s)?sn.push(t):sn.splice(tf(i),0,t),t.flags|=1,oc()}}function oc(){Na||(Na=nc.then(ac))}function nf(t){Fe(t)?Jo.push(...t):Vi&&t.id===-1?Vi.splice(Go+1,0,t):t.flags&1||(Jo.push(t),t.flags|=1),oc()}function Ol(t,i,s=Xn+1){for(;s<sn.length;s++){const l=sn[s];if(l&&l.flags&2){if(t&&l.id!==t.uid)continue;sn.splice(s,1),s--,l.flags&4&&(l.flags&=-2),l(),l.flags&4||(l.flags&=-2)}}}function sc(t){if(Jo.length){const i=[...new Set(Jo)].sort((s,l)=>js(s)-js(l));if(Jo.length=0,Vi){Vi.push(...i);return}for(Vi=i,Go=0;Go<Vi.length;Go++){const s=Vi[Go];s.flags&4&&(s.flags&=-2),s.flags&8||s(),s.flags&=-2}Vi=null,Go=0}}const js=t=>t.id==null?t.flags&2?-1:1/0:t.id;function ac(t){try{for(Xn=0;Xn<sn.length;Xn++){const i=sn[Xn];i&&!(i.flags&8)&&(i.flags&4&&(i.flags&=-2),Xs(i,i.i,i.i?15:14),i.flags&4||(i.flags&=-2))}}finally{for(;Xn<sn.length;Xn++){const i=sn[Xn];i&&(i.flags&=-2)}Xn=-1,sn.length=0,sc(),Na=null,(sn.length||Jo.length)&&ac()}}let Jt=null,rc=null;function Da(t){const i=Jt;return Jt=t,rc=t&&t.type.__scopeId||null,i}function we(t,i=Jt,s){if(!i||t._n)return t;const l=(...u)=>{l._d&&Ba(-1);const f=Da(i);let h;try{h=t(...u)}finally{Da(f),l._d&&Ba(1)}return h};return l._n=!0,l._c=!0,l._d=!0,l}function ee(t,i){if(Jt===null)return t;const s=or(Jt),l=t.dirs||(t.dirs=[]);for(let u=0;u<i.length;u++){let[f,h,_,y=bt]=i[u];f&&(Je(f)&&(f={mounted:f,updated:f}),f.deep&&yi(h),l.push({dir:f,instance:s,value:h,oldValue:void 0,arg:_,modifiers:y}))}return t}function co(t,i,s,l){const u=t.dirs,f=i&&i.dirs;for(let h=0;h<u.length;h++){const _=u[h];f&&(_.oldValue=f[h].value);let y=_.dir[l];y&&(ii(),In(y,s,8,[t.el,_,t,i]),oi())}}function lc(t,i){if(an){let s=an.provides;const l=an.parent&&an.parent.provides;l===s&&(s=an.provides=Object.create(l)),s[t]=i}}function Rs(t,i,s=!1){const l=Bc();if(l||Qo){let u=Qo?Qo._context.provides:l?l.parent==null||l.ce?l.vnode.appContext&&l.vnode.appContext.provides:l.parent.provides:void 0;if(u&&t in u)return u[t];if(arguments.length>1)return s&&Je(i)?i.call(l&&l.proxy):i}}const of=Symbol.for("v-scx"),sf=()=>Rs(of);function $t(t,i,s){return uc(t,i,s)}function uc(t,i,s=bt){const{immediate:l,deep:u,flush:f,once:h}=s,_=Dt({},s),y=i&&l||!i&&f!=="post";let C;if(qs){if(f==="sync"){const B=sf();C=B.__watcherHandles||(B.__watcherHandles=[])}else if(!y){const B=()=>{};return B.stop=ni,B.resume=ni,B.pause=ni,B}}const T=an;_.call=(B,j,F)=>In(B,T,j,F);let M=!1;f==="post"?_.scheduler=B=>{on(B,T&&T.suspense)}:f!=="sync"&&(M=!0,_.scheduler=(B,j)=>{j?B():rl(B)}),_.augmentJob=B=>{i&&(B.flags|=4),M&&(B.flags|=2,T&&(B.id=T.uid,B.i=T))};const R=Qd(t,i,_);return qs&&(C?C.push(R):y&&R()),R}function af(t,i,s){const l=this.proxy,u=Pt(t)?t.includes(".")?cc(l,t):()=>l[t]:t.bind(l,l);let f;Je(i)?f=i:(f=i.handler,s=i);const h=Qs(this),_=uc(u,f.bind(l),s);return h(),_}function cc(t,i){const s=i.split(".");return()=>{let l=t;for(let u=0;u<s.length&&l;u++)l=l[s[u]];return l}}const Ui=new WeakMap,dc=Symbol("_vte"),fc=t=>t.__isTeleport,go=t=>t&&(t.disabled||t.disabled===""),rf=t=>t&&(t.defer||t.defer===""),zl=t=>typeof SVGElement<"u"&&t instanceof SVGElement,Il=t=>typeof MathMLElement=="function"&&t instanceof MathMLElement,Fr=(t,i)=>{const s=t&&t.to;return Pt(s)?i?i(s):null:s},lf={name:"Teleport",__isTeleport:!0,process(t,i,s,l,u,f,h,_,y,C){const{mc:T,pc:M,pbc:R,o:{insert:B,querySelector:j,createText:F,createComment:he,parentNode:pe}}=C,Y=go(i.props);let{dynamicChildren:Le}=i;const fe=(Ie,qe,xe)=>{Ie.shapeFlag&16&&T(Ie.children,qe,xe,u,f,h,_,y)},Ue=(Ie=i)=>{const qe=go(Ie.props),xe=Ie.target=Fr(Ie.props,j),Se=Rr(xe,Ie,F,B);xe&&(h!=="svg"&&zl(xe)?h="svg":h!=="mathml"&&Il(xe)&&(h="mathml"),u&&u.isCE&&(u.ce._teleportTargets||(u.ce._teleportTargets=new Set)).add(xe),qe||(fe(Ie,xe,Se),Os(Ie,!1)))},$e=Ie=>{const qe=()=>{if(Ui.get(Ie)===qe){if(Ui.delete(Ie),go(Ie.props)){const xe=pe(Ie.el)||s;fe(Ie,xe,Ie.anchor),Os(Ie,!0)}Ue(Ie)}};Ui.set(Ie,qe),on(qe,f)};if(t==null){const Ie=i.el=F(""),qe=i.anchor=F("");if(B(Ie,s,l),B(qe,s,l),rf Line truncated
* @vue/runtime-dom v3.5.39
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
**/let Wr;const Gl=typeof window<"u"&&window.trustedTypes;if(Gl)try{Wr=Gl.createPolicy("vue",{createHTML:t=>t})}catch{}const Zc=Wr?t=>Wr.createHTML(t):t=>t,fh="http://www.w3.org/2000/svg",hh="http://www.w3.org/1998/Math/MathML",vi=typeof document<"u"?document:null,ql=vi&&vi.createElement("template"),ph={insert:(t,i,s)=>{i.insertBefore(t,s||null)},remove:t=>{const i=t.parentNode;i&&i.removeChild(t)},createElement:(t,i,s,l)=>{const u=i==="svg"?vi.createElementNS(fh,t):i==="mathml"?vi.createElementNS(hh,t):s?vi.createElement(t,{is:s}):vi.createElement(t);return t==="select"&&l&&l.multiple!=null&&u.setAttribute("multiple",l.multiple),u},createText:t=>vi.createTextNode(t),createComment:t=>vi.createComment(t),setText:(t,i)=>{t.nodeValue=i},setElementText:(t,i)=>{t.textContent=i},parentNode:t=>t.parentNode,nextSibling:t=>t.nextSibling,querySelector:t=>vi.querySelector(t),setScopeId(t,i){t.setAttribute(i,"")},insertStaticContent(t,i,s,l,u,f){const h=s?s.previousSibling:i.lastChild;if(u&&(u===f||u.nextSibling))for(;i.insertBefore(u.cloneNode(!0),s),!(u===f||!(u=u.nextSibling)););else{ql.innerHTML=Zc(l==="svg"?`<svg>${t}</svg>`:l==="mathml"?`<math>${t}</math>`:t);const _=ql.content;if(l==="svg"||l==="mathml"){const y=_.firstChild;for(;y.firstChild;)_.appendChild(y.firstChild);_.removeChild(y)}i.insertBefore(_,s)}return[h?h.nextSibling:i.firstChild,s?s.previousSibling:i.lastChild]}},Ri="transition",Ms="animation",Ys=Symbol("_vtc"),Hc={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},mh=Dt({},hc,Hc),gh=t=>(t.displayName="Transition",t.props=mh,t),vh=gh((t,{slots:i})=>ch(hf,_h(t),i)),ho=(t,i=[])=>{Fe(t)?t.forEach(s=>s(...i)):t&&t(...i)},Yl=t=>t?Fe(t)?t.some(i=>i.length>1):t.length>1:!1;function _h(t){const i={};for(const ie in t)ie in Hc||(i[ie]=t[ie]);if(t.css===!1)return i;const{name:s="v",type:l,duration:u,enterFromClass:f=`${s}-enter-from`,enterActiveClass:h=`${s}-enter-active`,enterToClass:_=`${s}-enter-to`,appearFromClass:y=f,appearActiveClass:C=h,appearToClass:T=_,leaveFromClass:M=`${s}-leave-from`,leaveActiveClass:R=`${s}-leave-active`,leaveToClass:B=`${s}-leave-to`}=t,j=bh(u),F=j&&j[0],he=j&&j[1],{onBeforeEnter:pe,onEnter:Y,onEnterCancelled:Le,onLeave:fe,onLeaveCancelled:Ue,onBeforeAppear:$e=pe,onAppear:Ie=Y,onAppearCancelled:qe=Le}=i,xe=(ie,je,oe,We)=>{ie._enterCancelled=We,po(ie,je?T:_),po(ie,je?C:h),oe&&oe()},Se=(ie,je)=>{ie._isLeaving=!1,po(ie,M),po(ie,B),po(ie,R),je&&je()},ze=ie=>(je,oe)=>{const We=ie?Ie:Y,le=()=>xe(je,ie,oe);ho(We,[je,le]),Jl(()=>{po(je,ie?y:f),gi(je,ie?T:_),Yl(We)||Xl(je,l,F,le)})};return Dt(i,{onBeforeEnter(ie){ho(pe,[ie]),gi(ie,f),gi(ie,h)},onBeforeAppear(ie){ho($e,[ie]),gi(ie,y),gi(ie,C)},onEnter:ze(!1),onAppear:ze(!0),onLeave(ie,je){ie._isLeaving=!0;const oe=()=>Se(ie,je);gi(ie,M),ie._enterCancelled?(gi(ie,R),tu(ie)):(tu(ie),gi(ie,R)),Jl(()=>{ie._isLeaving&&(po(ie,M),gi(ie,B),Yl(fe)||Xl(ie,l,he,oe))}),ho(fe,[ie,oe])},onEnterCancelled(ie){xe(ie,!1,void 0,!0),ho(Le,[ie])},onAppearCancelled(ie){xe(ie,!0,void 0,!0),ho(qe,[ie])},onLeaveCancelled(ie){Se(ie),ho(Ue,[ie])}})}function bh(t){if(t==null)return null;if(gt(t))return[Lr(t.enter),Lr(t.leave)];{const i=Lr(t);return[i,i]}}function Lr(t){return _d(t)}function gi(t,i){i.split(/\s+/).forEach(s=>s&&t.classList.add(s)),(t[Ys]||(t[Ys]=new Set)).add(i)}function po(t,i){i.split(/\s+/).forEach(l=>l&&t.classList.remove(l));const s=t[Ys];s&&(s.delete(i),s.size||(t[Ys]=void 0))}function Jl(t){requestAnimationFrame(()=>{requestAnimationFrame(t)})}let yh=0;function Xl(t,i,s,l){const u=t._endId=++yh,f=()=>{u===t._endId&&l()};if(s!=null)return setTimeout(f,s);const{type:h,timeout:_,propCount:y}=xh(t,i);if(!h)return l();const C=h+"end";let T=0;const M=()=>{t.removeEventListener(C,R),f()},R=B=>{B.target===t&&++T>=y&&M()};setTimeout(()=>{T<y&&M()},_+1),t.addEventListener(C,R)}function xh(t,i){const s=window.getComputedStyle(t),l=j=>(s[j]||"").split(", "),u=l(`${Ri}Delay`),f=l(`${Ri}Duration`),h=Ql(u,f),_=l(`${Ms}Delay`),y=l(`${Ms}Duration`),C=Ql(_,y);let T=null,M=0,R=0;i===Ri?h>0&&(T=Ri,M=h,R=f.length):i===Ms?C>0&&(T=Ms,M=C,R=y.length):(M=Math.max(h,C),T=M>0?h>C?Ri:Ms:null,R=T?T===Ri?f.length:y.length:0);const B=T===Ri&&/\b(?:transform|all)(?:,|$)/.test(l(`${Ri}Property`).toString());return{type:T,timeout:M,propCount:R,hasTransform:B}}function Ql(t,i){for(;t.length<i.length;)t=t.concat(t);return Math.max(...i.map((s,l)=>eu(s)+eu(t[l])))}function eu(t){return t==="auto"?0:Number(t.slice(0,-1).replace(",","."))*1e3}function tu(t){return(t?t.ownerDocument:document).body.offsetHeight}function wh(t,i,s){const l=t[Ys];l&&(i=(i?[i,...l]:[...l]).join(" ")),i==null?t.removeAttribute("class"):s?t.setAttribute("class",i):t.className=i}const Za=Symbol("_vod"),jc=Symbol("_vsh"),kh={name:"show",beforeMount(t,{value:i},{transition:s 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 rm=$s.exports,Tu;function lm(){return Tu||(Tu=1,(function(t,i){(function(s,l){l(i)})(rm,(function(s){var l="1.9.4";function u(e){var n,o,a,c;for(o=1,a=arguments.length;o<a;o++){c=arguments[o];for(n in c)e[n]=c[n]}return e}var f=Object.create||(function(){function e(){}return function(n){return e.prototype=n,new e}})();function h(e,n){var o=Array.prototype.slice;if(e.bind)return e.bind.apply(e,o.call(arguments,1));var a=o.call(arguments,2);return function(){return e.apply(n,a.length?a.concat(o.call(arguments)):arguments)}}var _=0;function y(e){return"_leaflet_id"in e||(e._leaflet_id=++_),e._leaflet_id}function C(e,n,o){var a,c,g,P;return P=function(){a=!1,c&&(g.apply(o,c),c=!1)},g=function(){a?c=arguments:(e.apply(o,arguments),setTimeout(P,n),a=!0)},g}function T(e,n,o){var a=n[1],c=n[0],g=a-c;return e===a&&o?e:((e-c)%g+g)%g+c}function M(){return!1}function R(e,n){if(n===!1)return e;var o=Math.pow(10,n===void 0?6:n);return Math.round(e*o)/o}function B(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}function j(e){return B(e).split(/\s+/)}function F(e,n){Object.prototype.hasOwnProperty.call(e,"options")||(e.options=e.options?f(e.options):{});for(var o in n)e.options[o]=n[o];return e.options}function he(e,n,o){var a=[];for(var c in e)a.push(encodeURIComponent(o?c.toUpperCase():c)+"="+encodeURIComponent(e[c]));return(!n||n.indexOf("?")===-1?"?":"&")+a.join("&")}var pe=/\{ *([\w_ -]+) *\}/g;function Y(e,n){return e.replace(pe,function(o,a){var c=n[a];if(c===void 0)throw new Error("No value provided for variable "+o);return typeof c=="function"&&(c=c(n)),c})}var Le=Array.isArray||function(e){return Object.prototype.toString.call(e)==="[object Array]"};function fe(e,n){for(var o=0;o<e.length;o++)if(e[o]===n)return o;return-1}var Ue="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";function $e(e){return window["webkit"+e]||window["moz"+e]||window["ms"+e]}var Ie=0;function qe(e){var n=+new Date,o=Math.max(0,16-(n-Ie));return Ie=n+o,window.setTimeout(e,o)}var xe=window.requestAnimationFrame||$e("RequestAnimationFrame")||qe,Se=window.cancelAnimationFrame||$e("CancelAnimationFrame")||$e("CancelRequestAnimationFrame")||function(e){window.clearTimeout(e)};function ze(e,n,o){if(o&&xe===qe)e.call(n);else return xe.call(window,h(e,n))}function ie(e){e&&Se.call(window,e)}var je={__proto__:null,extend:u,create:f,bind:h,get lastId(){return _},stamp:y,throttle:C,wrapNum:T,falseFn:M,formatNum:R,trim:B,splitWords:j,setOptions:F,getParamString:he,template:Y,isArray:Le,indexOf:fe,emptyImageUrl:Ue,requestFn:xe,cancelFn:Se,requestAnimFrame:ze,cancelAnimFrame:ie};function oe(){}oe.extend=function(e){var n=function(){F(this),this.initialize&&this.initialize.apply(this,arguments),this.callInitHooks()},o=n.__super__=this.prototype,a=f(o);a.constructor=n,n.prototype=a;for(var c in this)Object.prototype.hasOwnProperty.call(this,c)&&c!=="prototype"&&c!=="__super__"&&(n[c]=this[c]);return e.statics&&u(n,e.statics),e.includes&&(We(e.includes),u.apply(null,[a].concat(e.includes))),u(a,e),delete a.statics,delete a.includes,a.options&&(a.options=o.options?f(o.options):{},u(a.options,e.options)),a._initHooks=[],a.callInitHooks=function(){if(!this._initHooksCalled){o.callInitHooks&&o.callInitHooks.call(this),this._initHooksCalled=!0;for(var g=0,P=a._initHooks.length;g<P;g++)a._initHooks[g].call(this)}},n},oe.include=function(e){var n=this.prototype.options;return u(this.prototype,e),e.options&&(this.prototype.options=n,this.mergeOptions(e.options)),this},oe.mergeOptions=function(e){return u(this.prototype.options,e),this},oe.addInitHook=function(e){var n=Array.prototype.slice.call(arguments,1),o=typeof e=="function"?e:function(){this[e].apply(this,n)};return this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(o),this};function We(e){if(!(typeof L>"u"||!L||!L.Mixin)){e=Le(e)?e:[e];for(var n=0;n<e.length;n++)e[n]===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 le={on:function(e,n,o){if(typeof e=="object")for(var a in e)this._on(a,e[a],n);else{e=j(e);for(var c=0,g=e.length;c<g;c++)this._on(e[c],n,o)}return this},off:function(e,n,o){if(!arguments.length)delete this._events;else if(typeof e=="object")for(var a in e)this._off(a,e[a],n);else{e=j(e);for(var c=arguments.length===1,g=0,P=e.length;g<P;g++)c?this._off(e[g]):this._off(e[g],n,o)}return this},_on:function(e,n,o,a){if(typeof n!="function"){console.warn("wrong listener type: "+typeof n);return}if(this._listens(e,n,o)===!1){o===this&&(o=void 0);var c={fn:n,ctx:o};a&&(c.once=!0),this._events=this._events||{},this._events[e]=this._events[e]||[],this._events[e].push(c)}},_off:function(e,n,o){var a,c,g;if(this._events&&(a=this._events[e],!!a)){if(arguments.length===1){if(this._firingCount)for(c=0,g=a.length;c<g;c++)a[c].fn=M;delete this._events[e];return}if(typeof n!="function"){console.warn("wrong listener type:Line truncated
@@ -1 +1 @@
:root,[data-theme=light]{--navy-950: #0B1730;--navy-900: #0F1E3D;--navy-800: #1B2E52;--navy-700: #26406E;--blue-50: #EAF1FE;--blue-100: #D6E3FD;--blue-200: #B4CDFA;--blue-300: #8FB4F6;--blue-400: #5B93F5;--blue-500: #3D7BF0;--blue-600: #2B62CC;--blue-700: #1F4CA0;--slate-0: #FFFFFF;--slate-50: #F6F7F9;--slate-100: #EEF0F3;--slate-150: #E6E9EE;--slate-200: #DCE0E7;--slate-300: #C5CCD7;--slate-400: #97A1B0;--slate-500: #6B7688;--slate-600: #4C5566;--slate-700: #333B4A;--slate-800: #1E2635;--slate-900: #131A28;--slate-950: #0B111C;--steel: #5A6B85;--green-500: #1F8A5B;--green-100: #DCF1E7;--green-600:#177049;--amber-500: #D9852B;--amber-100: #FBEBD5;--amber-600:#B86C1B;--red-500: #D64545;--red-100: #FBE0E0;--red-600: #B83232;--bg-app: var(--slate-100);--bg-subtle: var(--slate-50);--surface: var(--slate-0);--surface-2: var(--slate-50);--surface-inset: var(--slate-100);--border: var(--slate-200);--border-strong: var(--slate-300);--border-subtle: var(--slate-150);--text-primary: var(--navy-900);--text-secondary:var(--steel);--text-tertiary: var(--slate-400);--text-inverse: var(--slate-0);--text-on-accent:#FFFFFF;--accent: var(--blue-500);--accent-hover: var(--blue-600);--accent-active: var(--blue-700);--accent-soft: var(--blue-50);--accent-soft-fg:var(--blue-700);--focus-ring: color-mix(in srgb, var(--blue-500) 45%, transparent);--success: var(--green-500);--success-soft: var(--green-100);--success-fg: var(--green-600);--warning: var(--amber-500);--warning-soft: var(--amber-100);--warning-fg: var(--amber-600);--danger: var(--red-500);--danger-soft: var(--red-100);--danger-fg: var(--red-600);--overlay: color-mix(in srgb, var(--navy-950) 55%, transparent);--font-sans: "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;--font-mono: "Space Mono", ui-monospace, "SF Mono", "JetBrains Mono", monospace;--radius-xs: 4px;--radius-sm: 6px;--radius-md: 10px;--radius-lg: 14px;--radius-xl: 20px;--radius-pill: 999px;--shadow-xs: 0 1px 2px rgba(15,30,61,.06);--shadow-sm: 0 1px 2px rgba(15,30,61,.06), 0 1px 3px rgba(15,30,61,.04);--shadow-md: 0 2px 4px rgba(15,30,61,.06), 0 6px 16px rgba(15,30,61,.08);--shadow-lg: 0 8px 24px rgba(15,30,61,.1), 0 2px 6px rgba(15,30,61,.06);--ease-standard: cubic-bezier(.4, 0, .2, 1);--ease-out: cubic-bezier(.16, 1, .3, 1);--dur-fast: .12s;--dur-base: .2s;color-scheme:light}[data-theme=dark]{--bg-app: var(--navy-950);--bg-subtle: var(--slate-950);--surface: #10203F;--surface-2: #142748;--surface-inset: var(--navy-950);--border: color-mix(in srgb, #FFFFFF 10%, transparent);--border-strong: color-mix(in srgb, #FFFFFF 18%, transparent);--border-subtle: color-mix(in srgb, #FFFFFF 6%, transparent);--text-primary: #F4F7FC;--text-secondary:#8FA0BE;--text-tertiary: #5E6E8C;--text-inverse: var(--navy-900);--text-on-accent:#FFFFFF;--accent: var(--blue-400);--accent-hover: var(--blue-300);--accent-active: var(--blue-200);--accent-soft: color-mix(in srgb, var(--blue-500) 18%, transparent);--accent-soft-fg:var(--blue-300);--focus-ring: color-mix(in srgb, var(--blue-400) 55%, transparent);--success:var(--green-500);--success-soft: color-mix(in srgb, var(--green-500) 22%, transparent);--success-fg:#5FD3A0;--warning:var(--amber-500);--warning-soft: color-mix(in srgb, var(--amber-500) 22%, transparent);--warning-fg:#F0B26A;--danger: var(--red-500);--danger-soft: color-mix(in srgb, var(--red-500) 22%, transparent);--danger-fg: #F08A8A;--overlay: color-mix(in srgb, #000000 62%, transparent);--shadow-xs: 0 1px 2px rgba(0,0,0,.35);--shadow-sm: 0 1px 3px rgba(0,0,0,.4);--shadow-md: 0 4px 12px rgba(0,0,0,.45);--shadow-lg: 0 12px 32px rgba(0,0,0,.5);color-scheme:dark}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-Line truncated
:root,[data-theme=light]{--navy-950: #0B1730;--navy-900: #0F1E3D;--navy-800: #1B2E52;--navy-700: #26406E;--blue-50: #EAF1FE;--blue-100: #D6E3FD;--blue-200: #B4CDFA;--blue-300: #8FB4F6;--blue-400: #5B93F5;--blue-500: #3D7BF0;--blue-600: #2B62CC;--blue-700: #1F4CA0;--slate-0: #FFFFFF;--slate-50: #F6F7F9;--slate-100: #EEF0F3;--slate-150: #E6E9EE;--slate-200: #DCE0E7;--slate-300: #C5CCD7;--slate-400: #97A1B0;--slate-500: #6B7688;--slate-600: #4C5566;--slate-700: #333B4A;--slate-800: #1E2635;--slate-900: #131A28;--slate-950: #0B111C;--steel: #5A6B85;--green-500: #1F8A5B;--green-100: #DCF1E7;--green-600:#177049;--amber-500: #D9852B;--amber-100: #FBEBD5;--amber-600:#B86C1B;--red-500: #D64545;--red-100: #FBE0E0;--red-600: #B83232;--bg-app: var(--slate-100);--bg-subtle: var(--slate-50);--surface: var(--slate-0);--surface-2: var(--slate-50);--surface-inset: var(--slate-100);--border: var(--slate-200);--border-strong: var(--slate-300);--border-subtle: var(--slate-150);--text-primary: var(--navy-900);--text-secondary:var(--steel);--text-tertiary: var(--slate-400);--text-inverse: var(--slate-0);--text-on-accent:#FFFFFF;--accent: var(--blue-500);--accent-hover: var(--blue-600);--accent-active: var(--blue-700);--accent-soft: var(--blue-50);--accent-soft-fg:var(--blue-700);--focus-ring: color-mix(in srgb, var(--blue-500) 45%, transparent);--success: var(--green-500);--success-soft: var(--green-100);--success-fg: var(--green-600);--warning: var(--amber-500);--warning-soft: var(--amber-100);--warning-fg: var(--amber-600);--danger: var(--red-500);--danger-soft: var(--red-100);--danger-fg: var(--red-600);--overlay: color-mix(in srgb, var(--navy-950) 55%, transparent);--font-sans: "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;--font-mono: "Space Mono", ui-monospace, "SF Mono", "JetBrains Mono", monospace;--radius-xs: 4px;--radius-sm: 6px;--radius-md: 10px;--radius-lg: 14px;--radius-xl: 20px;--radius-pill: 999px;--shadow-xs: 0 1px 2px rgba(15,30,61,.06);--shadow-sm: 0 1px 2px rgba(15,30,61,.06), 0 1px 3px rgba(15,30,61,.04);--shadow-md: 0 2px 4px rgba(15,30,61,.06), 0 6px 16px rgba(15,30,61,.08);--shadow-lg: 0 8px 24px rgba(15,30,61,.1), 0 2px 6px rgba(15,30,61,.06);--ease-standard: cubic-bezier(.4, 0, .2, 1);--ease-out: cubic-bezier(.16, 1, .3, 1);--dur-fast: .12s;--dur-base: .2s;color-scheme:light}[data-theme=dark]{--bg-app: var(--navy-950);--bg-subtle: var(--slate-950);--surface: #10203F;--surface-2: #142748;--surface-inset: var(--navy-950);--border: color-mix(in srgb, #FFFFFF 10%, transparent);--border-strong: color-mix(in srgb, #FFFFFF 18%, transparent);--border-subtle: color-mix(in srgb, #FFFFFF 6%, transparent);--text-primary: #F4F7FC;--text-secondary:#8FA0BE;--text-tertiary: #5E6E8C;--text-inverse: var(--navy-900);--text-on-accent:#FFFFFF;--accent: var(--blue-400);--accent-hover: var(--blue-300);--accent-active: var(--blue-200);--accent-soft: color-mix(in srgb, var(--blue-500) 18%, transparent);--accent-soft-fg:var(--blue-300);--focus-ring: color-mix(in srgb, var(--blue-400) 55%, transparent);--success:var(--green-500);--success-soft: color-mix(in srgb, var(--green-500) 22%, transparent);--success-fg:#5FD3A0;--warning:var(--amber-500);--warning-soft: color-mix(in srgb, var(--amber-500) 22%, transparent);--warning-fg:#F0B26A;--danger: var(--red-500);--danger-soft: color-mix(in srgb, var(--red-500) 22%, transparent);--danger-fg: #F08A8A;--overlay: color-mix(in srgb, #000000 62%, transparent);--shadow-xs: 0 1px 2px rgba(0,0,0,.35);--shadow-sm: 0 1px 3px rgba(0,0,0,.4);--shadow-md: 0 4px 12px rgba(0,0,0,.45);--shadow-lg: 0 12px 32px rgba(0,0,0,.5);color-scheme:dark}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-Line truncated
+2 -2
View File
@@ -35,8 +35,8 @@
})()
</script>
<title>PilotVault — Control Panel</title>
<script type="module" crossorigin src="./assets/index-CpDOSPL4.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-DIbxnHR0.css">
<script type="module" crossorigin src="./assets/index-B-DwId4e.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-Dr5W1RO_.css">
</head>
<body>
<div id="app"></div>
+58 -3
View File
@@ -51,7 +51,7 @@ const SECTIONS = computed(() => {
const list = [
{ id: 'account', label: 'Account', icon: 'user', kw: 'name username email password verification login credentials role' },
{ id: 'appearance', label: 'Appearance', icon: 'sliders', kw: 'theme light dark system language region font size accessibility date time format motion' },
{ id: 'integrations', label: 'Integrations', icon: 'radio', kw: 'opensky flights adsb aircraft plugin oauth credentials bounding box plan connection ftp sftp ftps file transfer server host upload download local storage folder drive isolated private read only webdav nextcloud owncloud dav url https openweather weather forecast temperature api key units' },
{ id: 'integrations', label: 'Integrations', icon: 'radio', kw: 'opensky flights adsb aircraft plugin oauth credentials bounding box plan connection ftp sftp ftps file transfer server host upload download local storage folder drive isolated private read only webdav nextcloud owncloud dav url https openweather weather forecast temperature api key units calls per minute usage limit quota' },
{ id: 'profile', label: 'Profile', icon: 'image', kw: 'avatar photo display name bio public' },
{ id: 'security', label: 'Privacy & Security', icon: 'shield', kw: 'two factor authentication 2fa sessions devices logout security privacy' },
]
@@ -80,7 +80,7 @@ function sectionMatches(s) {
const ROW_KW = {
account: ['full name', 'username', 'email address verification verify', 'password change current new'],
appearance: ['theme light dark system', 'language', 'region', 'font size accessibility', 'reduce motion', 'date format', 'time format clock'],
integrations: ['opensky live flights', 'enable plugin', 'oauth client id secret', 'plan credits', 'bounding box', 'test connection', 'file transfer ftp sftp ftps', 'server host port username password', 'private key passphrase', 'base path directory', 'local storage folder drive', 'private isolated folder', 'read only access mode', 'webdav nextcloud owncloud dav', 'server url username password tls', 'base path directory folder', 'openweather weather forecast', 'api key units metric imperial', 'default latitude longitude language'],
integrations: ['opensky live flights', 'enable plugin', 'oauth client id secret', 'plan credits', 'bounding box', 'test connection', 'file transfer ftp sftp ftps', 'server host port username password', 'private key passphrase', 'base path directory', 'local storage folder drive', 'private isolated folder', 'read only access mode', 'webdav nextcloud owncloud dav', 'server url username password tls', 'base path directory folder', 'openweather weather forecast', 'api key units metric imperial', 'default latitude longitude language', 'calls per minute limit usage quota', 'api call usage today rate limit'],
profile: ['profile photo avatar', 'display name', 'bio about', 'show email public'],
security: ['two factor authentication', 'active sessions devices', 'sign out'],
team: ['add user create account', 'members list role admin remove delete', 'organization org assign'],
@@ -731,7 +731,7 @@ const ow = reactive({
})
const owScope = ref('user')
// All editable fields; the API key is prefilled with the mask and preserved on save.
const OW_KEYS = ['apiKey', 'units', 'lat', 'lon', 'lang']
const OW_KEYS = ['apiKey', 'units', 'lat', 'lon', 'lang', 'callsPerMinute']
const owForm = reactive(Object.fromEntries(OW_KEYS.map((k) => [k, ''])))
const owMsg = ref('')
const owSaving = ref(false)
@@ -861,6 +861,24 @@ function owHealthClass(status) {
return status === 'ok' ? badgeClass.success : status === 'degraded' ? badgeClass.warning : badgeClass.danger
}
// App-side call usage from the last probe (null until one runs). OpenWeather does
// not report remaining quota, so these are the calls PilotVault itself has made.
const owUsage = computed(() => (owHealth.value && owHealth.value.usage) || null)
// Fraction of the per-minute limit consumed (null when no limit is known).
const owUsagePct = computed(() => {
const u = owUsage.value
if (!u || !u.minuteLimit) return null
return Math.max(0, Math.min(100, Math.round((u.minuteUsed / u.minuteLimit) * 100)))
})
// Bar colour tracks how close to the per-minute limit we are (fills as usage grows).
const owUsageBarClass = computed(() => {
const p = owUsagePct.value
if (p == null) return 'bg-accent'
if (p >= 90) return 'bg-danger'
if (p >= 70) return 'bg-amber'
return 'bg-success'
})
/* ---------- integrations: Local storage (host filesystem) ---------- */
// Same three-layer cascade as OpenSky (global → org → user), but the folder is not
// editable: it is derived from identity so each org-less user gets a private folder
@@ -1872,6 +1890,43 @@ onBeforeUnmount(() => {
<input v-else v-model="owForm.lang" class="field w-24 font-mono" placeholder="en" />
</Row>
<!-- calls-per-minute limit (used only to gauge usage) -->
<Row title="Calls per minute limit" desc="Your plan's per-minute limit (free tier is 60). Only used to gauge app usage below." keywords="calls per minute limit rate quota plan usage">
<template v-if="owLocked('callsPerMinute')">
<span class="inline-flex items-center gap-2 font-mono text-sm text-ink">
{{ owField('callsPerMinute').effective || '60' }}
<span v-if="owSourceLabel('callsPerMinute')" class="inline-flex items-center gap-1 rounded-full bg-surface-2 px-2 py-0.5 text-[10px] font-semibold text-ink-secondary"><Icon name="lock" :size="10" />{{ owSourceLabel('callsPerMinute') }}</span>
</span>
</template>
<input v-else v-model="owForm.callsPerMinute" inputmode="numeric" class="field w-24 font-mono" placeholder="60" />
</Row>
<!-- app-side call usage gauge (OpenWeather does not report remaining quota) -->
<div v-if="ow.available && !owEditingOrg" class="my-4 rounded-lg border border-line bg-surface-2 p-4" data-keywords="usage quota rate limit calls per minute remaining left api">
<div class="flex items-center justify-between gap-3">
<div class="flex items-center gap-2 text-sm font-semibold text-ink">
<Icon name="signal" :size="15" />API call usage
</div>
<span v-if="owHealthTs" class="text-[11px] text-ink-muted">Checked {{ owHealthAgo() }}</span>
</div>
<template v-if="owUsage">
<div class="mt-2 flex items-baseline gap-1.5">
<span class="font-mono text-2xl font-semibold text-ink">{{ owUsage.minuteUsed }}</span>
<span class="text-sm text-ink-muted">/ {{ owUsage.minuteLimit || '—' }} calls this minute</span>
</div>
<div v-if="owUsagePct != null" class="mt-2 h-2 w-full overflow-hidden rounded-full bg-line">
<div class="h-full rounded-full transition-all" :class="owUsageBarClass" :style="{ width: owUsagePct + '%' }"></div>
</div>
<div class="mt-2 text-xs text-ink-muted">
{{ owUsage.dayUsed }} calls today · counts only requests PilotVault makes with this key, since server start. OpenWeather does not report remaining quota — check your account dashboard for the authoritative total.
</div>
</template>
<div v-else class="mt-2 text-xs text-ink-muted">
Run <span class="font-semibold text-ink-secondary">Test connection</span> below to record and show call usage.
</div>
</div>
<!-- actions -->
<div class="mt-4 flex flex-wrap items-center gap-3">
<button v-if="!owReadOnly" class="btn-accent" :disabled="owSaving || !ow.available" @click="saveOpenWeatherSettings">