From 3d6bac15e73856e13bd230c0bdea41e8e9ed3812 Mon Sep 17 00:00:00 2001 From: dude2k <7114997+dude2k@users.noreply.github.com> Date: Mon, 17 Aug 2026 21:47:56 +0000 Subject: [PATCH] Release MultiSpeed 1.1.0 --- .dockerignore | 4 +- .env.example | 3 + .github/workflows/ci.yml | 17 + .../workflows/dependabot-frontend-assets.yml | 108 + CHANGELOG.md | 16 +- Dockerfile | 1 + README.md | 3 +- cmd/multispeed/main.go | 3 +- compose.yaml | 1 + docs/deployment.md | 4 + docs/development.md | 1 + go.mod | 8 + go.sum | 34 + internal/api/host_policy.go | 1 + internal/api/metrics.go | 102 + internal/api/metrics_test.go | 76 + internal/api/server.go | 13 +- internal/config/config.go | 28 +- internal/config/config_test.go | 23 + ...Cp7FUxCh.js => ComparisonPage-CZJO9UdP.js} | 2 +- ...-BfyYS-Fi.js => DashboardPage-DEmE5jHs.js} | 2 +- ...{EChart-C9A5j2N8.js => EChart-A2OaIcPj.js} | 2 +- ...s-DZDjvrcw.js => EntityBadges-CM7WeMOS.js} | 2 +- ...ard-GfGMWPS-.js => MetricCard-CMX2GS_z.js} | 2 +- ...ge-EmZpqL4-.js => NetworkPage-CZ9yIsUy.js} | 2 +- ...e-DK5iVdeX.js => NotFoundPage-YfFq8HYe.js} | 2 +- ...WrJ.js => OoklaEulaAcceptance-DkJ6glh5.js} | 2 +- ...2B-oxr.js => ResultDetailPage-DbQ93uA2.js} | 2 +- ...ge-CwUSS1pP.js => ResultsPage-DR4hm0dc.js} | 2 +- .../dist/assets/SettingsPage-BK6TpFP7.js | 1 - .../dist/assets/SettingsPage-BUWz04bL.js | 1 + ...D4N2MQUQ.js => StatisticsPage-C5F5n78a.js} | 2 +- ...age-DV8hzwFf.js => SystemPage-Cwt4SToT.js} | 2 +- .../dist/assets/TaskEditorPage-BwxKINaO.js | 1 - .../dist/assets/TaskEditorPage-Bx-TQ0Uc.js | 1 + ...Page-BEXsLscb.js => TasksPage-CxX5SuNf.js} | 2 +- ...eft-CkoT9SwQ.js => arrow-left-DZUEJBho.js} | 2 +- ...ow-up-Cm-GZvKO.js => arrow-up-DVV_1I7Y.js} | 2 +- .../{badge-D4YKBNKr.js => badge-BWAHg20h.js} | 2 +- .../{card-DEXft0Cm.js => card-BtXBd7n8.js} | 2 +- ...cle-x-DYIKQ94H.js => circle-x-CJ7LJdu2.js} | 2 +- ...lock-3-BeKI5fxN.js => clock-3-DhdKy9KD.js} | 2 +- ...abase-BRZrCBcR.js => database-DuA3AOvM.js} | 2 +- ...{dialog-DXkCUNSe.js => dialog-CT4xHTSq.js} | 2 +- ...nload-Dj5PHFJw.js => download-CUwygqUa.js} | 2 +- ...-DfzRK7DJ.js => external-link-4Ueuyx8f.js} | 2 +- ...{fields-PJGT-Jp7.js => fields-DSX3DSQR.js} | 2 +- ...nel-x-BSCtvBiK.js => funnel-x-DRKN7t7O.js} | 2 +- .../{index-B84OiuS0.js => index-DleN6wGz.js} | 4 +- ...{index--iC0sacy.css => index-sN3vPLoH.css} | 2 +- ...-line-BMm6ehcb.js => pen-line-UL8dX1Od.js} | 2 +- .../{plus-C7xMvwvj.js => plus-BbAjJ_nn.js} | 2 +- ...er-C96Q3yet.js => radio-tower-D9Z4YmgD.js} | 2 +- .../{route-C77_DY7l.js => route-DtX_GSbk.js} | 2 +- ...{search-B8uA6mdW.js => search-b0EiqX0q.js} | 2 +- ...{server-BOAQDuBN.js => server-BWQHmE7V.js} | 2 +- ...t-ma0N2JAc.js => shield-alert-CznEbFrD.js} | 2 +- ...k-Dm-p0u_h.js => shield-check-DrVussKH.js} | 2 +- ...rash-2-DJb0gq__.js => trash-2-DyGHVVq4.js} | 2 +- ...CeA9vkHH.js => useAppSettings-vnP5f96R.js} | 2 +- internal/frontend/dist/index.html | 4 +- openapi.yaml | 2 +- web/e2e/README.md | 2 + web/e2e/accessibility.spec.ts | 29 + web/package-lock.json | 598 ++++- web/package.json | 5 +- web/playwright.config.ts | 3 +- web/src/components/layout/AppShell.tsx | 2 +- web/src/components/ui/fields.tsx | 29 +- web/src/index.css | 4 +- web/src/lib/openapi/index.ts | 3 + web/src/lib/openapi/types.gen.ts | 2019 +++++++++++++++++ web/src/lib/types.ts | 235 +- web/src/pages/SettingsPage.tsx | 2 +- web/src/pages/TaskEditorPage.tsx | 2 +- 75 files changed, 3132 insertions(+), 333 deletions(-) create mode 100644 .github/workflows/dependabot-frontend-assets.yml create mode 100644 internal/api/metrics.go create mode 100644 internal/api/metrics_test.go rename internal/frontend/dist/assets/{ComparisonPage-Cp7FUxCh.js => ComparisonPage-CZJO9UdP.js} (95%) rename internal/frontend/dist/assets/{DashboardPage-BfyYS-Fi.js => DashboardPage-DEmE5jHs.js} (95%) rename internal/frontend/dist/assets/{EChart-C9A5j2N8.js => EChart-A2OaIcPj.js} (86%) rename internal/frontend/dist/assets/{EntityBadges-DZDjvrcw.js => EntityBadges-CM7WeMOS.js} (74%) rename internal/frontend/dist/assets/{MetricCard-GfGMWPS-.js => MetricCard-CMX2GS_z.js} (96%) rename internal/frontend/dist/assets/{NetworkPage-EmZpqL4-.js => NetworkPage-CZ9yIsUy.js} (96%) rename internal/frontend/dist/assets/{NotFoundPage-DK5iVdeX.js => NotFoundPage-YfFq8HYe.js} (84%) rename internal/frontend/dist/assets/{OoklaEulaAcceptance-B4WQoWrJ.js => OoklaEulaAcceptance-DkJ6glh5.js} (97%) rename internal/frontend/dist/assets/{ResultDetailPage-Dj2B-oxr.js => ResultDetailPage-DbQ93uA2.js} (91%) rename internal/frontend/dist/assets/{ResultsPage-CwUSS1pP.js => ResultsPage-DR4hm0dc.js} (96%) delete mode 100644 internal/frontend/dist/assets/SettingsPage-BK6TpFP7.js create mode 100644 internal/frontend/dist/assets/SettingsPage-BUWz04bL.js rename internal/frontend/dist/assets/{StatisticsPage-D4N2MQUQ.js => StatisticsPage-C5F5n78a.js} (97%) rename internal/frontend/dist/assets/{SystemPage-DV8hzwFf.js => SystemPage-Cwt4SToT.js} (94%) delete mode 100644 internal/frontend/dist/assets/TaskEditorPage-BwxKINaO.js create mode 100644 internal/frontend/dist/assets/TaskEditorPage-Bx-TQ0Uc.js rename internal/frontend/dist/assets/{TasksPage-BEXsLscb.js => TasksPage-CxX5SuNf.js} (98%) rename internal/frontend/dist/assets/{arrow-left-CkoT9SwQ.js => arrow-left-DZUEJBho.js} (58%) rename internal/frontend/dist/assets/{arrow-up-Cm-GZvKO.js => arrow-up-DVV_1I7Y.js} (75%) rename internal/frontend/dist/assets/{badge-D4YKBNKr.js => badge-BWAHg20h.js} (93%) rename internal/frontend/dist/assets/{card-DEXft0Cm.js => card-BtXBd7n8.js} (90%) rename internal/frontend/dist/assets/{circle-x-DYIKQ94H.js => circle-x-CJ7LJdu2.js} (67%) rename internal/frontend/dist/assets/{clock-3-BeKI5fxN.js => clock-3-DhdKy9KD.js} (59%) rename internal/frontend/dist/assets/{database-BRZrCBcR.js => database-DuA3AOvM.js} (72%) rename internal/frontend/dist/assets/{dialog-DXkCUNSe.js => dialog-CT4xHTSq.js} (98%) rename internal/frontend/dist/assets/{download-Dj5PHFJw.js => download-CUwygqUa.js} (71%) rename internal/frontend/dist/assets/{external-link-DfzRK7DJ.js => external-link-4Ueuyx8f.js} (73%) rename internal/frontend/dist/assets/{fields-PJGT-Jp7.js => fields-DSX3DSQR.js} (73%) rename internal/frontend/dist/assets/{funnel-x-BSCtvBiK.js => funnel-x-DRKN7t7O.js} (79%) rename internal/frontend/dist/assets/{index-B84OiuS0.js => index-DleN6wGz.js} (90%) rename internal/frontend/dist/assets/{index--iC0sacy.css => index-sN3vPLoH.css} (67%) rename internal/frontend/dist/assets/{pen-line-BMm6ehcb.js => pen-line-UL8dX1Od.js} (75%) rename internal/frontend/dist/assets/{plus-C7xMvwvj.js => plus-BbAjJ_nn.js} (54%) rename internal/frontend/dist/assets/{radio-tower-C96Q3yet.js => radio-tower-D9Z4YmgD.js} (85%) rename internal/frontend/dist/assets/{route-C77_DY7l.js => route-DtX_GSbk.js} (85%) rename internal/frontend/dist/assets/{search-B8uA6mdW.js => search-b0EiqX0q.js} (60%) rename internal/frontend/dist/assets/{server-BOAQDuBN.js => server-BWQHmE7V.js} (80%) rename internal/frontend/dist/assets/{shield-alert-ma0N2JAc.js => shield-alert-CznEbFrD.js} (81%) rename internal/frontend/dist/assets/{shield-check-Dm-p0u_h.js => shield-check-DrVussKH.js} (79%) rename internal/frontend/dist/assets/{trash-2-DJb0gq__.js => trash-2-DyGHVVq4.js} (79%) rename internal/frontend/dist/assets/{useAppSettings-CeA9vkHH.js => useAppSettings-vnP5f96R.js} (93%) create mode 100644 web/e2e/accessibility.spec.ts create mode 100644 web/src/lib/openapi/index.ts create mode 100644 web/src/lib/openapi/types.gen.ts diff --git a/.dockerignore b/.dockerignore index 96d3f75..35c2349 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,8 +15,8 @@ scripts/* *.log bin coverage -playwright-report -test-results +**/playwright-report +**/test-results web/node_modules web/dist multispeed diff --git a/.env.example b/.env.example index 8961de3..e47a9f4 100644 --- a/.env.example +++ b/.env.example @@ -13,6 +13,9 @@ APP_ALLOWED_CUSTOM_SERVER_URLS= # upload one separately obtained Linux amd64 Ookla executable into the writable # /data mount. Never expose this unauthenticated endpoint to an untrusted network. APP_ALLOW_OOKLA_BINARY_UPLOAD=false +# Optional Prometheus/OpenMetrics endpoint at /metrics. It follows the same +# trusted-host policy and has no separate authentication. +APP_METRICS_ENABLED=false # compose.yaml deliberately fixes APP_DATA_DIR=/data so the database and # managed provider files always use the persistent ./data mount. APP_LOG_LEVEL=INFO diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc18ec6..27d8c3e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,6 +92,14 @@ jobs: - name: Install locked dependencies run: npm ci --no-audit --no-fund + - name: Verify generated OpenAPI types + working-directory: . + shell: bash + run: | + npm --prefix web run generate:api-types + git diff --exit-code -- web/src/lib/openapi + test -z "$(git status --porcelain --untracked-files=all -- web/src/lib/openapi)" + - name: Lint run: npm run lint @@ -104,6 +112,15 @@ jobs: - name: Production build run: npm run build + - name: Upload Dependabot frontend candidate + if: github.event_name == 'pull_request' && github.actor == 'dependabot[bot]' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: dependabot-frontend-dist-${{ github.event.pull_request.number }} + path: internal/frontend/dist + if-no-files-found: error + retention-days: 1 + - name: Verify committed embedded frontend is current working-directory: . shell: bash diff --git a/.github/workflows/dependabot-frontend-assets.yml b/.github/workflows/dependabot-frontend-assets.yml new file mode 100644 index 0000000..9e0d400 --- /dev/null +++ b/.github/workflows/dependabot-frontend-assets.yml @@ -0,0 +1,108 @@ +name: Refresh Dependabot frontend assets + +on: + workflow_run: + workflows: + - CI + types: + - completed + +permissions: + actions: read + contents: write + pull-requests: read + +concurrency: + group: dependabot-frontend-assets-${{ github.event.workflow_run.id }} + cancel-in-progress: false + +jobs: + refresh: + if: >- + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.actor.login == 'dependabot[bot]' + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - name: Validate originating pull request + id: pull-request + uses: actions/github-script@d746ffe35508b1917358783b479e04febd2b8f71 # v9.0.0 + with: + script: | + const run = context.payload.workflow_run + const reference = run.pull_requests?.[0] + if (!reference?.number) { + core.setFailed('The CI run is not associated with a pull request.') + return + } + const { data: pull } = await github.rest.pulls.get({ + ...context.repo, + pull_number: reference.number, + }) + const valid = pull.state === 'open' && + pull.base.ref === 'main' && + pull.head.repo?.full_name === `${context.repo.owner}/${context.repo.repo}` && + pull.user?.login === 'dependabot[bot]' && + pull.head.ref.startsWith('dependabot/npm_and_yarn/web/') && + pull.head.sha === run.head_sha + if (!valid) { + core.setFailed('The workflow run is not an eligible same-repository Dependabot npm pull request.') + return + } + const files = await github.paginate(github.rest.pulls.listFiles, { + ...context.repo, + pull_number: pull.number, + per_page: 100, + }) + const allowed = files.every(({ filename }) => + filename === 'web/package.json' || + filename === 'web/package-lock.json' || + filename.startsWith('internal/frontend/dist/')) + if (!allowed) { + core.setFailed('The Dependabot pull request changes files outside the approved frontend dependency and generated-asset paths.') + return + } + core.setOutput('number', String(pull.number)) + core.setOutput('head-ref', pull.head.ref) + core.setOutput('head-sha', pull.head.sha) + + - name: Check out the validated Dependabot commit + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ steps.pull-request.outputs.head-ref }} + fetch-depth: 1 + + - name: Download unprivileged frontend build + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + name: dependabot-frontend-dist-${{ steps.pull-request.outputs.number }} + path: .dependabot-frontend-dist + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ github.token }} + + - name: Replace only embedded frontend assets + shell: bash + env: + HEAD_REF: ${{ steps.pull-request.outputs.head-ref }} + HEAD_SHA: ${{ steps.pull-request.outputs.head-sha }} + run: | + set -euo pipefail + test "$(git rev-parse HEAD)" = "${HEAD_SHA}" + test -f .dependabot-frontend-dist/index.html + test -z "$(find .dependabot-frontend-dist -type l -print -quit)" + rsync --archive --delete .dependabot-frontend-dist/ internal/frontend/dist/ + mapfile -t changed < <( + { git diff --name-only; git ls-files --others --exclude-standard; } | sort -u + ) + test "${#changed[@]}" -gt 0 + for path in "${changed[@]}"; do + case "${path}" in + internal/frontend/dist/*) ;; + *) printf 'Refusing unexpected changed path: %s\n' "${path}" >&2; exit 1 ;; + esac + done + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add -- internal/frontend/dist + git commit -m 'chore(web): refresh embedded frontend assets' + git push origin "HEAD:${HEAD_REF}" diff --git a/CHANGELOG.md b/CHANGELOG.md index e265d0e..7a77ca3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ All notable changes to MultiSpeed are documented here. The project follows [Sema ## [Unreleased] +## [1.1.0] - 2026-08-17 + +### Added + +- Add opt-in Prometheus metrics for HTTP traffic and persisted application state at `/metrics`. +- Generate frontend API models from OpenAPI and enforce generated-contract drift in CI. +- Run automated WCAG A/AA accessibility scans across the primary operator pages. + +### Changed + +- Reject malformed boolean environment values instead of silently treating them as false. +- Automatically refresh committed frontend assets for eligible same-repository Dependabot npm pull requests through a privilege-separated workflow. + ### Security - Upgrade the build toolchain to Go 1.26.6 and the patched LibreSpeed overlay to `golang.org/x/net` v0.56.0. @@ -87,7 +100,8 @@ All notable changes to MultiSpeed are documented here. The project follows [Sema - Non-root read-only container with all capabilities dropped and `no-new-privileges`. - Ookla Speedtest CLI is never downloaded or redistributed by the project image; in-app acceptance remains separate from installation and licensing permission. -[Unreleased]: https://github.com/dude2k/MultiSpeed/compare/v1.0.4...HEAD +[Unreleased]: https://github.com/dude2k/MultiSpeed/compare/v1.1.0...HEAD +[1.1.0]: https://github.com/dude2k/MultiSpeed/compare/v1.0.4...v1.1.0 [1.0.4]: https://github.com/dude2k/MultiSpeed/compare/v1.0.3...v1.0.4 [1.0.3]: https://github.com/dude2k/MultiSpeed/compare/v1.0.2...v1.0.3 [1.0.2]: https://github.com/dude2k/MultiSpeed/compare/v1.0.1...v1.0.2 diff --git a/Dockerfile b/Dockerfile index 6fb6f91..24dbbf1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -172,6 +172,7 @@ ENV APP_LISTEN_ADDR=127.0.0.1:8787 \ LIBRESPEED_BINARY=/usr/local/bin/librespeed-cli \ OOKLA_BINARY=/data/providers/ookla/speedtest \ APP_ALLOW_OOKLA_BINARY_UPLOAD=false \ + APP_METRICS_ENABLED=false \ ACCEPT_OOKLA_EULA=false USER 10001:10001 diff --git a/README.md b/README.md index 361e60f..7124ea8 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ Process configuration is intentionally small; persisted runtime settings are man | `APP_TRUSTED_HOSTS` | empty | empty | Comma-separated exact proxy/LAN DNS names or IPs; wildcard listeners already accept unicast IP literals but require explicit DNS names to prevent DNS rebinding | | `APP_ALLOWED_CUSTOM_SERVER_URLS` | empty | empty | Comma-separated exact LibreSpeed custom backend base URLs; empty disables custom URLs, and plain HTTP additionally requires the task's explicit insecure opt-in | | `APP_ALLOW_OOKLA_BINARY_UPLOAD` | `false` | `false` | Enables bounded upload and execution validation of one operator-supplied Linux amd64 Ookla executable; no authentication, trusted private networks only | +| `APP_METRICS_ENABLED` | `false` | `false` | Enables the Prometheus/OpenMetrics endpoint at `/metrics`; protected by the listener's trusted-host policy but not by separate authentication | | `APP_DATA_DIR` | `/data` | `/data` (fixed by supplied Compose) | Database and managed-provider directory; keep the `/data` mount writable | | `APP_LOG_LEVEL` | `INFO` | `INFO` | `DEBUG`, `INFO`, `WARN`, or `ERROR` | | `APP_SHUTDOWN_TIMEOUT` | `20s` | `20s` | Graceful shutdown deadline | @@ -186,7 +187,7 @@ Process configuration is intentionally small; persisted runtime settings are man | `OOKLA_BINARY` | `/data/providers/ookla/speedtest` | `/data/providers/ookla/speedtest` | Managed or externally supplied Ookla executable path | | `ACCEPT_OOKLA_EULA` | `false` | `false` | Legacy-named headless override authorizing non-interactive `--accept-license`/`--accept-gdpr`; technical gate only, never a license grant | -No environment-variable values are exposed by the system API. +Boolean variables reject malformed values instead of silently falling back. No environment-variable values are exposed by the system API. ## Development diff --git a/cmd/multispeed/main.go b/cmd/multispeed/main.go index 6b0118b..f69c3f8 100644 --- a/cmd/multispeed/main.go +++ b/cmd/multispeed/main.go @@ -136,7 +136,8 @@ func run() error { api.BuildInfo{Version: version, GitCommit: gitCommit, BuildTime: buildTime}, api.HTTPPolicy{ListenAddress: configuration.ListenAddress, TrustedHosts: configuration.TrustedHosts, OoklaEULAEnvironmentAccepted: configuration.AcceptOoklaEULA, DataDirectory: configuration.DataDirectory, - OoklaBinaryPath: configuration.OoklaBinary, AllowOoklaBinaryUpload: configuration.AllowOoklaBinaryUpload}) + OoklaBinaryPath: configuration.OoklaBinary, AllowOoklaBinaryUpload: configuration.AllowOoklaBinaryUpload, + MetricsEnabled: configuration.MetricsEnabled}) httpServer := &http.Server{Addr: configuration.ListenAddress, Handler: apiServer.Handler(), ReadHeaderTimeout: 10 * time.Second, ReadTimeout: 30 * time.Second, IdleTimeout: 2 * time.Minute, MaxHeaderBytes: 32 << 10} serveErrors := make(chan error, 1) go func() { diff --git a/compose.yaml b/compose.yaml index bf4828f..9438392 100644 --- a/compose.yaml +++ b/compose.yaml @@ -32,6 +32,7 @@ services: APP_TRUSTED_HOSTS: ${APP_TRUSTED_HOSTS:-} APP_ALLOWED_CUSTOM_SERVER_URLS: ${APP_ALLOWED_CUSTOM_SERVER_URLS:-} APP_ALLOW_OOKLA_BINARY_UPLOAD: ${APP_ALLOW_OOKLA_BINARY_UPLOAD:-false} + APP_METRICS_ENABLED: ${APP_METRICS_ENABLED:-false} APP_DATA_DIR: /data APP_LOG_LEVEL: ${APP_LOG_LEVEL:-INFO} APP_SHUTDOWN_TIMEOUT: ${APP_SHUTDOWN_TIMEOUT:-20s} diff --git a/docs/deployment.md b/docs/deployment.md index b157ee8..fe3ba5d 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -107,6 +107,10 @@ curl --fail http://127.0.0.1:8787/api/v1/readyz `healthz` indicates that the process is alive. `readyz` additionally reflects startup dependencies such as migrations/database access. The Docker healthcheck targets the configured local listener. +## Metrics + +Set `APP_METRICS_ENABLED=true` to expose Prometheus/OpenMetrics data at `/metrics`. The endpoint includes bounded route-template HTTP labels, request duration and in-flight counts, Go/process metrics, and current task, result, and active-run gauges. It follows the same trusted-host checks as the UI, but MultiSpeed does not add separate metrics authentication; restrict scraping to a trusted network or an authenticating reverse proxy. Leave it disabled when it is not needed. + ## Reverse proxy MultiSpeed does not implement authentication. For remote access, use a separate operator-managed reverse proxy with TLS and authentication, bind MultiSpeed to loopback, and preserve streaming for `/api/v1/events`. Add the public DNS name to `APP_TRUSTED_HOSTS` and forward that hostname consistently so same-origin mutation validation remains effective. The backend rejects an explicit Host port different from `APP_LISTEN_ADDR`; configure the proxy's upstream `Host` header without the external port (for example, an Nginx `$host`) or with the backend listen port. Do not add permissive CORS headers. diff --git a/docs/development.md b/docs/development.md index ec5e638..18ae262 100644 --- a/docs/development.md +++ b/docs/development.md @@ -40,6 +40,7 @@ npm ci npm run lint npm run typecheck npm run test +npm run generate:api-types npm run build ``` diff --git a/go.mod b/go.mod index 345bd1a..92e55c6 100644 --- a/go.mod +++ b/go.mod @@ -5,16 +5,24 @@ go 1.26.0 require ( github.com/go-chi/chi/v5 v5.3.1 github.com/google/uuid v1.6.0 + github.com/prometheus/client_golang v1.24.0 github.com/robfig/cron/v3 v3.0.1 modernc.org/sqlite v1.56.0 ) require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/mattn/go-isatty v0.0.24 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.70.0 // indirect + github.com/prometheus/procfs v0.21.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect golang.org/x/sys v0.47.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect modernc.org/libc v1.74.4 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect diff --git a/go.sum b/go.sum index aec9730..ba59df4 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,51 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/go-chi/chi/v5 v5.3.1 h1:3j4HZLGZQ3JpMCrPJF/Jl3mYJfWLKBfNJ6quurUGCf8= github.com/go-chi/chi/v5 v5.3.1/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 h1:LMLX+LgTNWpfvCBdFebv6EsYotImrt/Ppc5cXIriCSo= github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ= +github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI= github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.24.0 h1:5XStIklKuAtJSNpdD3s8XJj/Yv78IQmE1kbNk87JrAI= +github.com/prometheus/client_golang v1.24.0/go.mod h1:QcsNdotprC2nS4BTM2ucbcqxd2CeXTEa9jW7zHO9iDE= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.70.0 h1:bcpru3tWPVnxGnETLgOV5jbp/JRXgYEyv65CuBLAMMI= +github.com/prometheus/common v0.70.0/go.mod h1:S/SFasQmgGiYH6C81LKCtYa8QACgthGg5zxL2udV7SY= +github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= +github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= @@ -24,6 +54,10 @@ golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= modernc.org/cc/v4 v4.29.1 h1:MKgdCV3WykTSPqpVrnxdEDS0HEd2FHpKZDzxzU5LyeI= modernc.org/cc/v4 v4.29.1/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU= diff --git a/internal/api/host_policy.go b/internal/api/host_policy.go index a3c19d9..8c2fbda 100644 --- a/internal/api/host_policy.go +++ b/internal/api/host_policy.go @@ -18,6 +18,7 @@ type HTTPPolicy struct { DataDirectory string OoklaBinaryPath string AllowOoklaBinaryUpload bool + MetricsEnabled bool } type hostPolicy struct { diff --git a/internal/api/metrics.go b/internal/api/metrics.go new file mode 100644 index 0000000..dcd4a2f --- /dev/null +++ b/internal/api/metrics.go @@ -0,0 +1,102 @@ +package api + +import ( + "context" + "net/http" + "strconv" + "time" + + "github.com/dude2k/MultiSpeed/internal/database" + "github.com/go-chi/chi/v5" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +type httpMetrics struct { + handler http.Handler + inFlight prometheus.Gauge + requests *prometheus.CounterVec + duration *prometheus.HistogramVec +} + +func newHTTPMetrics(store *database.Store) *httpMetrics { + registry := prometheus.NewRegistry() + metrics := &httpMetrics{ + inFlight: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "multispeed", + Subsystem: "http", + Name: "requests_in_flight", + Help: "Current number of HTTP requests being served.", + }), + requests: prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: "multispeed", + Subsystem: "http", + Name: "requests_total", + Help: "Total number of HTTP requests by method, route, and status.", + }, []string{"method", "route", "status"}), + duration: prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: "multispeed", + Subsystem: "http", + Name: "request_duration_seconds", + Help: "HTTP request duration in seconds by method and route.", + Buckets: prometheus.DefBuckets, + }, []string{"method", "route"}), + } + registry.MustRegister(metrics.inFlight, metrics.requests, metrics.duration) + registry.MustRegister(newApplicationStateCollector(store)) + registry.MustRegister(collectors.NewGoCollector(), collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})) + metrics.handler = promhttp.HandlerFor(registry, promhttp.HandlerOpts{EnableOpenMetrics: true}) + return metrics +} + +func (m *httpMetrics) observe(next http.Handler) http.Handler { + return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + started := time.Now() + m.inFlight.Inc() + defer m.inFlight.Dec() + recorder := &responseRecorder{ResponseWriter: writer, status: http.StatusOK} + next.ServeHTTP(recorder, request) + route := chi.RouteContext(request.Context()).RoutePattern() + if route == "" { + route = "unmatched" + } + m.requests.WithLabelValues(request.Method, route, strconv.Itoa(recorder.status)).Inc() + m.duration.WithLabelValues(request.Method, route).Observe(time.Since(started).Seconds()) + }) +} + +type applicationStateCollector struct { + store *database.Store + tasks *prometheus.Desc + results *prometheus.Desc + activeRuns *prometheus.Desc +} + +func newApplicationStateCollector(store *database.Store) *applicationStateCollector { + return &applicationStateCollector{ + store: store, + tasks: prometheus.NewDesc("multispeed_tasks", "Current number of persisted speed-test tasks.", nil, nil), + results: prometheus.NewDesc("multispeed_results", "Current number of persisted result records.", nil, nil), + activeRuns: prometheus.NewDesc("multispeed_active_runs", "Current number of queued, validating, or running tests.", nil, nil), + } +} + +func (c *applicationStateCollector) Describe(descriptions chan<- *prometheus.Desc) { + descriptions <- c.tasks + descriptions <- c.results + descriptions <- c.activeRuns +} + +func (c *applicationStateCollector) Collect(metrics chan<- prometheus.Metric) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + tasks, results, activeRuns, err := c.store.Counts(ctx) + if err != nil { + metrics <- prometheus.NewInvalidMetric(c.tasks, err) + return + } + metrics <- prometheus.MustNewConstMetric(c.tasks, prometheus.GaugeValue, float64(tasks)) + metrics <- prometheus.MustNewConstMetric(c.results, prometheus.GaugeValue, float64(results)) + metrics <- prometheus.MustNewConstMetric(c.activeRuns, prometheus.GaugeValue, float64(activeRuns)) +} diff --git a/internal/api/metrics_test.go b/internal/api/metrics_test.go new file mode 100644 index 0000000..f7aa8f8 --- /dev/null +++ b/internal/api/metrics_test.go @@ -0,0 +1,76 @@ +package api + +import ( + "context" + "io" + "log/slog" + "net/http" + "net/http/httptest" + "path/filepath" + "strings" + "testing" + + "github.com/dude2k/MultiSpeed/internal/database" + "github.com/dude2k/MultiSpeed/internal/events" + "github.com/dude2k/MultiSpeed/internal/network" + "github.com/dude2k/MultiSpeed/internal/providers" +) + +func TestPrometheusMetricsAreExplicitlyOptIn(t *testing.T) { + for _, enabled := range []bool{false, true} { + t.Run(map[bool]string{false: "disabled", true: "enabled"}[enabled], func(t *testing.T) { + handler := metricsTestHandler(t, enabled) + if enabled { + healthRequest := httptest.NewRequest(http.MethodGet, "http://multispeed.local/api/v1/healthz", nil) + healthResponse := httptest.NewRecorder() + handler.ServeHTTP(healthResponse, healthRequest) + if healthResponse.Code != http.StatusOK { + t.Fatalf("health status=%d", healthResponse.Code) + } + } + + request := httptest.NewRequest(http.MethodGet, "http://multispeed.local/metrics", nil) + response := httptest.NewRecorder() + handler.ServeHTTP(response, request) + if !enabled { + if response.Code != http.StatusNotFound { + t.Fatalf("disabled metrics status=%d body=%s", response.Code, response.Body.String()) + } + return + } + if response.Code != http.StatusOK { + t.Fatalf("enabled metrics status=%d body=%s", response.Code, response.Body.String()) + } + body := response.Body.String() + for _, metric := range []string{ + "multispeed_tasks 0", + "multispeed_results 0", + "multispeed_active_runs 0", + `multispeed_http_requests_total{method="GET",route="/api/v1/healthz",status="200"} 1`, + } { + if !strings.Contains(body, metric) { + t.Errorf("metrics response does not contain %q", metric) + } + } + }) + } +} + +func metricsTestHandler(t *testing.T, enabled bool) http.Handler { + t.Helper() + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) + store, err := database.Open(context.Background(), filepath.Join(t.TempDir(), "metrics.db"), logger) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = store.Close() }) + broker := events.New() + t.Cleanup(broker.Close) + interfaces := network.NewInterfaceService(broker) + _, _ = interfaces.Refresh(context.Background()) + return New(store, nil, nil, interfaces, network.NewRouteValidator(interfaces), providers.NewRegistry(), broker, logger, BuildInfo{Version: "test"}, HTTPPolicy{ + ListenAddress: "127.0.0.1:8787", + TrustedHosts: []string{"multispeed.local"}, + MetricsEnabled: enabled, + }).Handler() +} diff --git a/internal/api/server.go b/internal/api/server.go index 378fcc4..7a11d47 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -58,6 +58,7 @@ type Server struct { ooklaBinary *ooklaprovider.BinaryManager ooklaEULAEnvironmentAccepted bool configurationMu sync.Mutex + metrics *httpMetrics } func New(store *database.Store, schedule *scheduler.Scheduler, executionManager *execution.Manager, interfaces *network.InterfaceService, @@ -66,18 +67,28 @@ func New(store *database.Store, schedule *scheduler.Scheduler, executionManager if err != nil { panic(err) } - return &Server{store: store, scheduler: schedule, execution: executionManager, interfaces: interfaces, routes: routes, providers: registry, broker: broker, logger: logger, + server := &Server{store: store, scheduler: schedule, execution: executionManager, interfaces: interfaces, routes: routes, providers: registry, broker: broker, logger: logger, build: build, hosts: newHostPolicy(httpPolicy, interfaces), statsService: statistics.New(store), retentionCleaner: cleaner, startedAt: time.Now().UTC(), ready: func(ctx context.Context) error { _, err := store.SchemaVersion(ctx) return err }, manualLimit: newRateGate(4, time.Minute), discoveryLimit: newRateGate(12, time.Minute), ooklaUploadLimit: newRateGate(2, time.Hour), ooklaBinary: ooklaprovider.NewBinaryManager(httpPolicy.DataDirectory, httpPolicy.OoklaBinaryPath, httpPolicy.AllowOoklaBinaryUpload, nil), ooklaEULAEnvironmentAccepted: httpPolicy.OoklaEULAEnvironmentAccepted} + if httpPolicy.MetricsEnabled { + server.metrics = newHTTPMetrics(store) + } + return server } func (s *Server) Handler() http.Handler { router := chi.NewRouter() router.Use(s.requestID, s.recoverer, s.securityHeaders, s.requestLog, s.trustedHost, s.sameOrigin, s.contentType) + if s.metrics != nil { + router.Use(s.metrics.observe) + router.Handle("/metrics", s.metrics.handler) + } else { + router.HandleFunc("/metrics", http.NotFound) + } router.Route("/api/v1", func(api chi.Router) { api.Get("/healthz", s.health) api.Get("/readyz", s.readyz) diff --git a/internal/config/config.go b/internal/config/config.go index cc7c905..1d6575b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -30,6 +30,7 @@ type Config struct { LogLevel slog.Level AcceptOoklaEULA bool AllowOoklaBinaryUpload bool + MetricsEnabled bool OoklaBinary string LibreSpeedBinary string ShutdownTimeout time.Duration @@ -66,6 +67,18 @@ func Load(version, commit, buildTime string) (Config, error) { if err != nil || shutdown <= 0 || shutdown > 5*time.Minute { return Config{}, errors.New("invalid APP_SHUTDOWN_TIMEOUT: value must be between 1ns and 5m") } + acceptOoklaEULA, err := envBool("ACCEPT_OOKLA_EULA", false) + if err != nil { + return Config{}, err + } + allowOoklaBinaryUpload, err := envBool("APP_ALLOW_OOKLA_BINARY_UPLOAD", false) + if err != nil { + return Config{}, err + } + metricsEnabled, err := envBool("APP_METRICS_ENABLED", false) + if err != nil { + return Config{}, err + } return Config{ ListenAddress: listen, @@ -74,8 +87,9 @@ func Load(version, commit, buildTime string) (Config, error) { DataDirectory: dataDir, DatabasePath: filepath.Join(dataDir, "multispeed.db"), LogLevel: level, - AcceptOoklaEULA: envBool("ACCEPT_OOKLA_EULA", false), - AllowOoklaBinaryUpload: envBool("APP_ALLOW_OOKLA_BINARY_UPLOAD", false), + AcceptOoklaEULA: acceptOoklaEULA, + AllowOoklaBinaryUpload: allowOoklaBinaryUpload, + MetricsEnabled: metricsEnabled, OoklaBinary: env("OOKLA_BINARY", filepath.Join(dataDir, "providers", "ookla", "speedtest")), LibreSpeedBinary: env("LIBRESPEED_BINARY", "librespeed-cli"), ShutdownTimeout: shutdown, @@ -167,14 +181,14 @@ func env(key, fallback string) string { return fallback } -func envBool(key string, fallback bool) bool { +func envBool(key string, fallback bool) (bool, error) { raw, ok := os.LookupEnv(key) if !ok { - return fallback + return fallback, nil } - value, err := strconv.ParseBool(raw) + value, err := strconv.ParseBool(strings.TrimSpace(raw)) if err != nil { - return fallback + return false, fmt.Errorf("invalid %s: value must be a boolean", key) } - return value + return value, nil } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 1efa8c7..ddbc60d 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -20,6 +20,29 @@ func TestLoadUsesManagedOoklaBinaryPathAndExplicitUploadOptIn(t *testing.T) { } } +func TestLoadRejectsInvalidBooleanEnvironmentValues(t *testing.T) { + for _, key := range []string{"ACCEPT_OOKLA_EULA", "APP_ALLOW_OOKLA_BINARY_UPLOAD", "APP_METRICS_ENABLED"} { + t.Run(key, func(t *testing.T) { + t.Setenv(key, "flase") + if _, err := Load("test", "commit", "time"); err == nil || !strings.Contains(err.Error(), key) { + t.Fatalf("Load() error=%v", err) + } + }) + } +} + +func TestLoadTrimsBooleanEnvironmentValues(t *testing.T) { + t.Setenv("ACCEPT_OOKLA_EULA", " true ") + t.Setenv("APP_ALLOW_OOKLA_BINARY_UPLOAD", " false ") + configuration, err := Load("test", "commit", "time") + if err != nil { + t.Fatal(err) + } + if !configuration.AcceptOoklaEULA || configuration.AllowOoklaBinaryUpload { + t.Fatalf("unexpected booleans: accept=%v upload=%v", configuration.AcceptOoklaEULA, configuration.AllowOoklaBinaryUpload) + } +} + func TestLoadRejectsRelativeOrFilesystemRootDataDirectory(t *testing.T) { for _, dataDirectory := range []string{"relative-data", string(filepath.Separator)} { t.Run(dataDirectory, func(t *testing.T) { diff --git a/internal/frontend/dist/assets/ComparisonPage-Cp7FUxCh.js b/internal/frontend/dist/assets/ComparisonPage-CZJO9UdP.js similarity index 95% rename from internal/frontend/dist/assets/ComparisonPage-Cp7FUxCh.js rename to internal/frontend/dist/assets/ComparisonPage-CZJO9UdP.js index b03524b..8c830a3 100644 --- a/internal/frontend/dist/assets/ComparisonPage-Cp7FUxCh.js +++ b/internal/frontend/dist/assets/ComparisonPage-CZJO9UdP.js @@ -1 +1 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r}from"./query-DWNqGEie.js";import{n as i,t as a}from"./arrow-up-Cm-GZvKO.js";import{i as o}from"./fields-PJGT-Jp7.js";import{I as s,M as c,_ as l,a as u,h as d,i as f,j as p,m,n as h,r as g,w as _}from"./index-B84OiuS0.js";import{n as v,r as y}from"./useAppSettings-CeA9vkHH.js";import{t as b}from"./PageHeader-DGX-y_Bi.js";import{n as x,r as S,t as C}from"./EChart-C9A5j2N8.js";import{t as w}from"./badge-D4YKBNKr.js";import{i as T,n as E,r as D,t as O}from"./card-DEXft0Cm.js";var k=s(`trophy`,[[`path`,{d:`M10 14.66V17a1 1 0 0 1-1 1 2 2 0 0 0-2 2v2`,key:`pwuv1l`}],[`path`,{d:`M14 14.66V17a1 1 0 0 0 1 1 2 2 0 0 1 2 2v2`,key:`1y54w1`}],[`path`,{d:`M17.916 10H19.5A2.5 2.5 0 0 0 22 7.5V5a1 1 0 0 0-1-1h-3`,key:`e30mpu`}],[`path`,{d:`M4 22h16`,key:`57wxv0`}],[`path`,{d:`M6 9a6 6 0 0 0 12 0V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1z`,key:`1mhfuq`}],[`path`,{d:`M6.084 10H4.5A2.5 2.5 0 0 1 2 7.5V5a1 1 0 0 1 1-1h3`,key:`i0yafy`}]]),A=e(r(),1),j=n(),M=[l.ookla,l.librespeed,l.cloudflare,`#34d399`,`#f472b6`,`#facc15`],N=[{days:1,label:`Last 24 hours`},{days:7,label:`Last 7 days`},{days:30,label:`Last 30 days`},{days:90,label:`Last 90 days`},{days:365,label:`Last year`}];function P(){let e=v(),{bitrate:n,dateTime:r}=y(),[s,l]=(0,A.useState)(null),[x,S]=(0,A.useState)([]),[P,z]=(0,A.useState)(()=>Date.now()),V=s??B(e.settings.defaultChartRange),H=new Date(P-V*864e5).toISOString(),U=new Date(P).toISOString(),W=t({queryKey:[...h.interfaces,`comparison`,!0],queryFn:()=>_.interfaces({includeDown:!0})}),G=W.data?.map(e=>e.name)??[],K=x.length?x:G.slice(0,4),q={granularity:`day`,from:H,to:U,timezone:e.settings.defaultTimezone,interfaceName:K,groupBy:`interface`},J=t({queryKey:h.statistics({comparison:q}),queryFn:()=>_.statistics(q),enabled:K.length>0});if(W.isLoading||e.isLoading||K.length>0&&J.isLoading)return(0,j.jsx)(u,{label:`Comparing independent WAN paths…`});let Y=W.error??e.error??J.error;if(Y)return(0,j.jsx)(f,{error:Y,onRetry:()=>{W.refetch(),e.refetch(),J.refetch()}});let X=(J.data?.series??[]).flatMap(e=>{let t=I(e);return t&&t.samples>0?[t]:[]}),Z=[...X].sort((e,t)=>L(t)-L(e))[0],Q=R(J.data?.series,n,r);return(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(b,{title:`Compare paths, not anecdotes.`,description:`Use exact full-range statistics across successful, failed, skipped, and cancelled attempts to compare independent WAN paths without page truncation.`,actions:(0,j.jsx)(o,{className:`w-44`,value:V,onChange:e=>{l(Number(e.target.value)),z(Date.now())},children:N.map(e=>(0,j.jsx)(`option`,{value:e.days,children:e.label},e.days))})}),(0,j.jsxs)(O,{className:`mb-5 p-4`,children:[(0,j.jsx)(`p`,{className:`mb-3 text-xs font-semibold`,children:`WAN paths to compare`}),(0,j.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:G.map(e=>{let t=K.includes(e);return(0,j.jsxs)(`button`,{type:`button`,onClick:()=>S(t=>{let n=t.length?t:G.slice(0,4);return n.includes(e)?n.filter(t=>t!==e):[...n,e]}),className:`inline-flex items-center gap-2 rounded-lg border px-3 py-2 text-xs font-semibold transition ${t?`border-primary/40 bg-primary/10 text-primary`:`border-border bg-background text-muted-foreground hover:text-foreground`}`,"aria-pressed":t,children:[t?(0,j.jsx)(c,{className:`h-3.5 w-3.5`}):(0,j.jsx)(`span`,{className:`h-3.5 w-3.5 rounded-full border border-current`}),e]},e)})})]}),X.length===0?(0,j.jsx)(O,{children:(0,j.jsx)(g,{title:`No comparable WAN samples`,description:`Select at least one interface with measurement attempts in this reporting range.`})}):(0,j.jsxs)(j.Fragment,{children:[Z?(0,j.jsxs)(`div`,{className:`mb-5 flex items-center gap-3 rounded-xl border border-emerald-500/25 bg-emerald-500/[.06] p-4`,children:[(0,j.jsx)(`span`,{className:`grid h-9 w-9 place-items-center rounded-lg bg-emerald-500/10 text-emerald-500`,children:(0,j.jsx)(k,{className:`h-4 w-4`})}),(0,j.jsxs)(`div`,{children:[(0,j.jsxs)(`p`,{className:`text-sm font-semibold`,children:[`Best balanced path: `,Z.name]}),(0,j.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Highest normalized blend of exact download, upload, latency, and all-attempt success rate in this period.`})]})]}):null,(0,j.jsx)(`div`,{className:`grid gap-4 md:grid-cols-2 2xl:grid-cols-4`,children:X.map((e,t)=>(0,j.jsxs)(O,{className:`overflow-hidden`,children:[(0,j.jsx)(`div`,{className:`h-1`,style:{backgroundColor:M[t%M.length]}}),(0,j.jsxs)(`div`,{className:`p-4`,children:[(0,j.jsxs)(`div`,{className:`flex items-start justify-between`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{className:`text-sm font-bold`,children:e.name}),(0,j.jsxs)(`p`,{className:`mt-0.5 text-[10px] text-muted-foreground`,children:[e.succeeded,` succeeded · `,e.failed,` failed · `,e.skipped,` skipped`]})]}),(0,j.jsxs)(w,{tone:Z?.id===e.id?`success`:`neutral`,children:[e.samples,` attempt`,e.samples===1?``:`s`]})]}),(0,j.jsxs)(`dl`,{className:`mt-4 grid grid-cols-2 gap-3`,children:[(0,j.jsx)(F,{icon:i,label:`Download`,value:n(e.download)}),(0,j.jsx)(F,{icon:a,label:`Upload`,value:n(e.upload)}),(0,j.jsx)(F,{icon:p,label:`Latency`,value:m(e.latency)}),(0,j.jsx)(F,{icon:c,label:`Success`,value:d(e.successRate)})]})]})]},e.id))}),(0,j.jsxs)(O,{className:`mt-5`,children:[(0,j.jsxs)(D,{children:[(0,j.jsx)(T,{children:`WAN performance timeline`}),(0,j.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Sparse interface series are aligned by reporting-bucket start; missing buckets remain gaps`})]}),(0,j.jsx)(E,{children:(0,j.jsx)(C,{option:Q,className:`h-[28rem]`,ariaLabel:`WAN comparison for throughput and latency`})})]}),(0,j.jsxs)(O,{className:`mt-5 overflow-hidden`,children:[(0,j.jsx)(D,{children:(0,j.jsx)(T,{children:`Comparison matrix`})}),(0,j.jsx)(`div`,{className:`overflow-x-auto`,children:(0,j.jsxs)(`table`,{className:`data-table w-full min-w-[930px]`,children:[(0,j.jsx)(`thead`,{children:(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`th`,{children:`WAN`}),(0,j.jsx)(`th`,{children:`Attempts`}),(0,j.jsx)(`th`,{children:`Succeeded`}),(0,j.jsx)(`th`,{children:`Failed`}),(0,j.jsx)(`th`,{children:`Average download`}),(0,j.jsx)(`th`,{children:`Average upload`}),(0,j.jsx)(`th`,{children:`Average latency`}),(0,j.jsx)(`th`,{children:`Average jitter`}),(0,j.jsx)(`th`,{children:`Success rate`})]})}),(0,j.jsx)(`tbody`,{children:X.map(e=>(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`td`,{className:`font-semibold`,children:e.name}),(0,j.jsx)(`td`,{children:e.samples}),(0,j.jsx)(`td`,{children:e.succeeded}),(0,j.jsx)(`td`,{children:e.failed}),(0,j.jsx)(`td`,{children:n(e.download)}),(0,j.jsx)(`td`,{children:n(e.upload)}),(0,j.jsx)(`td`,{children:m(e.latency)}),(0,j.jsx)(`td`,{children:m(e.jitter)}),(0,j.jsx)(`td`,{children:d(e.successRate)})]},e.id))})]})})]})]})]})}function F({icon:e,label:t,value:n}){return(0,j.jsxs)(`div`,{children:[(0,j.jsxs)(`p`,{className:`flex items-center gap-1 text-[10px] uppercase tracking-wider text-muted-foreground`,children:[(0,j.jsx)(e,{className:`h-3 w-3`}),t]}),(0,j.jsx)(`p`,{className:`metric-number mt-1 text-sm font-semibold`,children:n})]})}function I(e){let t=e.overall;return t?{id:e.id,name:e.name,samples:t.sampleCount,succeeded:t.successfulCount,failed:t.failedCount,skipped:t.skippedCount,download:t.download?.average??null,upload:t.upload?.average??null,latency:t.latency?.average??null,jitter:t.jitter?.average??null,successRate:t.successRate??(t.sampleCount?t.successfulCount/t.sampleCount*100:0)}:null}function L(e){return(e.download??0)/1e8+(e.upload??0)/1e8-(e.latency??100)/50+e.successRate/100}function R(e,t,n){let r=S(),i=x(),a=e??[],o=[...new Set(a.flatMap(e=>e.buckets.map(z)).filter(Boolean))].sort(),s=a.flatMap((e,t)=>{let n=new Map(e.buckets.map(e=>[z(e),e])),r=M[t%M.length]??`#67e8f9`;return[{name:`${e.name} download`,type:`line`,yAxisIndex:0,smooth:.18,showSymbol:!1,connectNulls:!1,data:o.map(e=>n.get(e)?.download?.average??null),lineStyle:{width:2,color:r},itemStyle:{color:r}},{name:`${e.name} upload`,type:`line`,yAxisIndex:0,smooth:.18,showSymbol:!1,connectNulls:!1,data:o.map(e=>n.get(e)?.upload?.average??null),lineStyle:{width:1,type:`dashed`,color:r},itemStyle:{color:r}},{name:`${e.name} latency`,type:`line`,yAxisIndex:1,smooth:.18,showSymbol:!1,connectNulls:!1,data:o.map(e=>n.get(e)?.latency?.average??null),lineStyle:{width:1,type:`dotted`,color:r},itemStyle:{color:r}}]});return{tooltip:{trigger:`axis`,backgroundColor:`rgba(8,17,31,.95)`,borderColor:`rgba(148,163,184,.22)`,textStyle:{color:`#e2e8f0`,fontSize:10}},legend:{top:0,type:`scroll`,textStyle:{color:r,fontSize:10}},grid:{left:8,right:12,top:50,bottom:8,containLabel:!0},xAxis:{type:`category`,boundaryGap:!1,data:o.map(e=>n(e)),axisLabel:{color:r,fontSize:10,hideOverlap:!0},axisLine:{lineStyle:{color:i}}},yAxis:[{type:`value`,axisLabel:{color:r,fontSize:10,formatter:e=>t(e,!0)},splitLine:{lineStyle:{color:i}}},{type:`value`,axisLabel:{color:r,fontSize:10,formatter:e=>`${e}ms`},splitLine:{show:!1}}],series:s}}function z(e){return e.bucket||e.timestamp||``}function B(e){return e===`24h`?1:e===`7d`?7:e===`90d`?90:30}export{R as comparisonOption,P as default,I as summarizeWan}; \ No newline at end of file +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r}from"./query-DWNqGEie.js";import{n as i,t as a}from"./arrow-up-DVV_1I7Y.js";import{i as o}from"./fields-DSX3DSQR.js";import{I as s,M as c,_ as l,a as u,h as d,i as f,j as p,m,n as h,r as g,w as _}from"./index-DleN6wGz.js";import{n as v,r as y}from"./useAppSettings-vnP5f96R.js";import{t as b}from"./PageHeader-DGX-y_Bi.js";import{n as x,r as S,t as C}from"./EChart-A2OaIcPj.js";import{t as w}from"./badge-BWAHg20h.js";import{i as T,n as E,r as D,t as O}from"./card-BtXBd7n8.js";var k=s(`trophy`,[[`path`,{d:`M10 14.66V17a1 1 0 0 1-1 1 2 2 0 0 0-2 2v2`,key:`pwuv1l`}],[`path`,{d:`M14 14.66V17a1 1 0 0 0 1 1 2 2 0 0 1 2 2v2`,key:`1y54w1`}],[`path`,{d:`M17.916 10H19.5A2.5 2.5 0 0 0 22 7.5V5a1 1 0 0 0-1-1h-3`,key:`e30mpu`}],[`path`,{d:`M4 22h16`,key:`57wxv0`}],[`path`,{d:`M6 9a6 6 0 0 0 12 0V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1z`,key:`1mhfuq`}],[`path`,{d:`M6.084 10H4.5A2.5 2.5 0 0 1 2 7.5V5a1 1 0 0 1 1-1h3`,key:`i0yafy`}]]),A=e(r(),1),j=n(),M=[l.ookla,l.librespeed,l.cloudflare,`#34d399`,`#f472b6`,`#facc15`],N=[{days:1,label:`Last 24 hours`},{days:7,label:`Last 7 days`},{days:30,label:`Last 30 days`},{days:90,label:`Last 90 days`},{days:365,label:`Last year`}];function P(){let e=v(),{bitrate:n,dateTime:r}=y(),[s,l]=(0,A.useState)(null),[x,S]=(0,A.useState)([]),[P,z]=(0,A.useState)(()=>Date.now()),V=s??B(e.settings.defaultChartRange),H=new Date(P-V*864e5).toISOString(),U=new Date(P).toISOString(),W=t({queryKey:[...h.interfaces,`comparison`,!0],queryFn:()=>_.interfaces({includeDown:!0})}),G=W.data?.map(e=>e.name)??[],K=x.length?x:G.slice(0,4),q={granularity:`day`,from:H,to:U,timezone:e.settings.defaultTimezone,interfaceName:K,groupBy:`interface`},J=t({queryKey:h.statistics({comparison:q}),queryFn:()=>_.statistics(q),enabled:K.length>0});if(W.isLoading||e.isLoading||K.length>0&&J.isLoading)return(0,j.jsx)(u,{label:`Comparing independent WAN paths…`});let Y=W.error??e.error??J.error;if(Y)return(0,j.jsx)(f,{error:Y,onRetry:()=>{W.refetch(),e.refetch(),J.refetch()}});let X=(J.data?.series??[]).flatMap(e=>{let t=I(e);return t&&t.samples>0?[t]:[]}),Z=[...X].sort((e,t)=>L(t)-L(e))[0],Q=R(J.data?.series,n,r);return(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(b,{title:`Compare paths, not anecdotes.`,description:`Use exact full-range statistics across successful, failed, skipped, and cancelled attempts to compare independent WAN paths without page truncation.`,actions:(0,j.jsx)(o,{className:`w-44`,value:V,onChange:e=>{l(Number(e.target.value)),z(Date.now())},children:N.map(e=>(0,j.jsx)(`option`,{value:e.days,children:e.label},e.days))})}),(0,j.jsxs)(O,{className:`mb-5 p-4`,children:[(0,j.jsx)(`p`,{className:`mb-3 text-xs font-semibold`,children:`WAN paths to compare`}),(0,j.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:G.map(e=>{let t=K.includes(e);return(0,j.jsxs)(`button`,{type:`button`,onClick:()=>S(t=>{let n=t.length?t:G.slice(0,4);return n.includes(e)?n.filter(t=>t!==e):[...n,e]}),className:`inline-flex items-center gap-2 rounded-lg border px-3 py-2 text-xs font-semibold transition ${t?`border-primary/40 bg-primary/10 text-primary`:`border-border bg-background text-muted-foreground hover:text-foreground`}`,"aria-pressed":t,children:[t?(0,j.jsx)(c,{className:`h-3.5 w-3.5`}):(0,j.jsx)(`span`,{className:`h-3.5 w-3.5 rounded-full border border-current`}),e]},e)})})]}),X.length===0?(0,j.jsx)(O,{children:(0,j.jsx)(g,{title:`No comparable WAN samples`,description:`Select at least one interface with measurement attempts in this reporting range.`})}):(0,j.jsxs)(j.Fragment,{children:[Z?(0,j.jsxs)(`div`,{className:`mb-5 flex items-center gap-3 rounded-xl border border-emerald-500/25 bg-emerald-500/[.06] p-4`,children:[(0,j.jsx)(`span`,{className:`grid h-9 w-9 place-items-center rounded-lg bg-emerald-500/10 text-emerald-500`,children:(0,j.jsx)(k,{className:`h-4 w-4`})}),(0,j.jsxs)(`div`,{children:[(0,j.jsxs)(`p`,{className:`text-sm font-semibold`,children:[`Best balanced path: `,Z.name]}),(0,j.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Highest normalized blend of exact download, upload, latency, and all-attempt success rate in this period.`})]})]}):null,(0,j.jsx)(`div`,{className:`grid gap-4 md:grid-cols-2 2xl:grid-cols-4`,children:X.map((e,t)=>(0,j.jsxs)(O,{className:`overflow-hidden`,children:[(0,j.jsx)(`div`,{className:`h-1`,style:{backgroundColor:M[t%M.length]}}),(0,j.jsxs)(`div`,{className:`p-4`,children:[(0,j.jsxs)(`div`,{className:`flex items-start justify-between`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{className:`text-sm font-bold`,children:e.name}),(0,j.jsxs)(`p`,{className:`mt-0.5 text-[10px] text-muted-foreground`,children:[e.succeeded,` succeeded · `,e.failed,` failed · `,e.skipped,` skipped`]})]}),(0,j.jsxs)(w,{tone:Z?.id===e.id?`success`:`neutral`,children:[e.samples,` attempt`,e.samples===1?``:`s`]})]}),(0,j.jsxs)(`dl`,{className:`mt-4 grid grid-cols-2 gap-3`,children:[(0,j.jsx)(F,{icon:i,label:`Download`,value:n(e.download)}),(0,j.jsx)(F,{icon:a,label:`Upload`,value:n(e.upload)}),(0,j.jsx)(F,{icon:p,label:`Latency`,value:m(e.latency)}),(0,j.jsx)(F,{icon:c,label:`Success`,value:d(e.successRate)})]})]})]},e.id))}),(0,j.jsxs)(O,{className:`mt-5`,children:[(0,j.jsxs)(D,{children:[(0,j.jsx)(T,{children:`WAN performance timeline`}),(0,j.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Sparse interface series are aligned by reporting-bucket start; missing buckets remain gaps`})]}),(0,j.jsx)(E,{children:(0,j.jsx)(C,{option:Q,className:`h-[28rem]`,ariaLabel:`WAN comparison for throughput and latency`})})]}),(0,j.jsxs)(O,{className:`mt-5 overflow-hidden`,children:[(0,j.jsx)(D,{children:(0,j.jsx)(T,{children:`Comparison matrix`})}),(0,j.jsx)(`div`,{className:`overflow-x-auto`,children:(0,j.jsxs)(`table`,{className:`data-table w-full min-w-[930px]`,children:[(0,j.jsx)(`thead`,{children:(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`th`,{children:`WAN`}),(0,j.jsx)(`th`,{children:`Attempts`}),(0,j.jsx)(`th`,{children:`Succeeded`}),(0,j.jsx)(`th`,{children:`Failed`}),(0,j.jsx)(`th`,{children:`Average download`}),(0,j.jsx)(`th`,{children:`Average upload`}),(0,j.jsx)(`th`,{children:`Average latency`}),(0,j.jsx)(`th`,{children:`Average jitter`}),(0,j.jsx)(`th`,{children:`Success rate`})]})}),(0,j.jsx)(`tbody`,{children:X.map(e=>(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`td`,{className:`font-semibold`,children:e.name}),(0,j.jsx)(`td`,{children:e.samples}),(0,j.jsx)(`td`,{children:e.succeeded}),(0,j.jsx)(`td`,{children:e.failed}),(0,j.jsx)(`td`,{children:n(e.download)}),(0,j.jsx)(`td`,{children:n(e.upload)}),(0,j.jsx)(`td`,{children:m(e.latency)}),(0,j.jsx)(`td`,{children:m(e.jitter)}),(0,j.jsx)(`td`,{children:d(e.successRate)})]},e.id))})]})})]})]})]})}function F({icon:e,label:t,value:n}){return(0,j.jsxs)(`div`,{children:[(0,j.jsxs)(`p`,{className:`flex items-center gap-1 text-[10px] uppercase tracking-wider text-muted-foreground`,children:[(0,j.jsx)(e,{className:`h-3 w-3`}),t]}),(0,j.jsx)(`p`,{className:`metric-number mt-1 text-sm font-semibold`,children:n})]})}function I(e){let t=e.overall;return t?{id:e.id,name:e.name,samples:t.sampleCount,succeeded:t.successfulCount,failed:t.failedCount,skipped:t.skippedCount,download:t.download?.average??null,upload:t.upload?.average??null,latency:t.latency?.average??null,jitter:t.jitter?.average??null,successRate:t.successRate??(t.sampleCount?t.successfulCount/t.sampleCount*100:0)}:null}function L(e){return(e.download??0)/1e8+(e.upload??0)/1e8-(e.latency??100)/50+e.successRate/100}function R(e,t,n){let r=S(),i=x(),a=e??[],o=[...new Set(a.flatMap(e=>e.buckets.map(z)).filter(Boolean))].sort(),s=a.flatMap((e,t)=>{let n=new Map(e.buckets.map(e=>[z(e),e])),r=M[t%M.length]??`#67e8f9`;return[{name:`${e.name} download`,type:`line`,yAxisIndex:0,smooth:.18,showSymbol:!1,connectNulls:!1,data:o.map(e=>n.get(e)?.download?.average??null),lineStyle:{width:2,color:r},itemStyle:{color:r}},{name:`${e.name} upload`,type:`line`,yAxisIndex:0,smooth:.18,showSymbol:!1,connectNulls:!1,data:o.map(e=>n.get(e)?.upload?.average??null),lineStyle:{width:1,type:`dashed`,color:r},itemStyle:{color:r}},{name:`${e.name} latency`,type:`line`,yAxisIndex:1,smooth:.18,showSymbol:!1,connectNulls:!1,data:o.map(e=>n.get(e)?.latency?.average??null),lineStyle:{width:1,type:`dotted`,color:r},itemStyle:{color:r}}]});return{tooltip:{trigger:`axis`,backgroundColor:`rgba(8,17,31,.95)`,borderColor:`rgba(148,163,184,.22)`,textStyle:{color:`#e2e8f0`,fontSize:10}},legend:{top:0,type:`scroll`,textStyle:{color:r,fontSize:10}},grid:{left:8,right:12,top:50,bottom:8,containLabel:!0},xAxis:{type:`category`,boundaryGap:!1,data:o.map(e=>n(e)),axisLabel:{color:r,fontSize:10,hideOverlap:!0},axisLine:{lineStyle:{color:i}}},yAxis:[{type:`value`,axisLabel:{color:r,fontSize:10,formatter:e=>t(e,!0)},splitLine:{lineStyle:{color:i}}},{type:`value`,axisLabel:{color:r,fontSize:10,formatter:e=>`${e}ms`},splitLine:{show:!1}}],series:s}}function z(e){return e.bucket||e.timestamp||``}function B(e){return e===`24h`?1:e===`7d`?7:e===`90d`?90:30}export{R as comparisonOption,P as default,I as summarizeWan}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/DashboardPage-BfyYS-Fi.js b/internal/frontend/dist/assets/DashboardPage-DEmE5jHs.js similarity index 95% rename from internal/frontend/dist/assets/DashboardPage-BfyYS-Fi.js rename to internal/frontend/dist/assets/DashboardPage-DEmE5jHs.js index b4306a1..9e6f847 100644 --- a/internal/frontend/dist/assets/DashboardPage-BfyYS-Fi.js +++ b/internal/frontend/dist/assets/DashboardPage-DEmE5jHs.js @@ -1 +1 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r}from"./query-DWNqGEie.js";import{d as i}from"./react-DsKPNe9r.js";import{t as a}from"./MetricCard-GfGMWPS-.js";import{n as o,t as s}from"./arrow-up-Cm-GZvKO.js";import{t as c}from"./plus-C7xMvwvj.js";import{F as ee,I as l,N as te,_ as u,a as ne,g as d,i as f,j as re,k as p,m,n as h,r as g,s as _,w as v}from"./index-B84OiuS0.js";import{n as ie,r as ae}from"./useAppSettings-CeA9vkHH.js";import{n as y,t as oe}from"./PageHeader-DGX-y_Bi.js";import{n as b,r as x,t as se}from"./EChart-C9A5j2N8.js";import{n as S,t as C}from"./badge-D4YKBNKr.js";import{i as w,n as T,r as E,t as D}from"./card-DEXft0Cm.js";import{n as O,t as k}from"./EntityBadges-DZDjvrcw.js";var ce=l(`calendar-clock`,[[`path`,{d:`M16 14v2.2l1.6 1`,key:`fo4ql5`}],[`path`,{d:`M16 2v3`,key:`otl347`}],[`path`,{d:`M21 7.338V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h2.338`,key:`7hb8p4`}],[`path`,{d:`M3 9h5.859`,key:`numkqi`}],[`path`,{d:`M8 2v3`,key:`1ioesn`}],[`circle`,{cx:`16`,cy:`16`,r:`6`,key:`qoo3c4`}]]),A=l(`radio`,[[`path`,{d:`M16.247 7.761a6 6 0 0 1 0 8.478`,key:`1fwjs5`}],[`path`,{d:`M19.075 4.933a10 10 0 0 1 0 14.134`,key:`ehdyv1`}],[`path`,{d:`M4.925 19.067a10 10 0 0 1 0-14.134`,key:`1q22gi`}],[`path`,{d:`M7.753 16.239a6 6 0 0 1 0-8.478`,key:`r2q7qm`}],[`circle`,{cx:`12`,cy:`12`,r:`2`,key:`1c9p78`}]]),j=l(`router`,[[`rect`,{width:`20`,height:`8`,x:`2`,y:`14`,rx:`2`,key:`w68u3i`}],[`path`,{d:`M6.01 18H6`,key:`19vcac`}],[`path`,{d:`M10.01 18H10`,key:`uamcmx`}],[`path`,{d:`M15 10v4`,key:`qjz1xs`}],[`path`,{d:`M17.84 7.17a4 4 0 0 0-5.66 0`,key:`1rif40`}],[`path`,{d:`M20.66 4.34a8 8 0 0 0-11.31 0`,key:`6a5xfq`}]]),M=e(r(),1),N=n(),P=[{label:`24h`,hours:24},{label:`7d`,hours:168},{label:`30d`,hours:720},{label:`90d`,hours:2160}];function F(){let e=ie(),{bitrate:n,dateTime:r}=ae(),[l,u]=(0,M.useState)(null),[b,x]=(0,M.useState)(()=>Date.now()),O=l??P.find(t=>t.label===e.settings.defaultChartRange)??P[2],A=new Date(b-O.hours*36e5).toISOString(),j=new Date(b).toISOString(),F=t({queryKey:h.tasks,queryFn:v.tasks}),R=t({queryKey:h.dashboardSummary,queryFn:v.dashboardSummary}),z=t({queryKey:[...h.interfaces,`dashboard`,!0],queryFn:()=>v.interfaces({includeDown:!0})}),B={granularity:`raw`,from:A,to:j,timezone:e.settings.defaultTimezone,groupBy:`task`},V=t({queryKey:h.statistics({dashboard:B}),queryFn:()=>v.statistics(B)});if(F.isLoading||R.isLoading||z.isLoading||V.isLoading||e.isLoading)return(0,N.jsx)(ne,{label:`Assembling the live operations view…`});let H=F.error??R.error??z.error??e.error;if(H)return(0,N.jsx)(f,{error:H,onRetry:()=>{F.refetch(),R.refetch(),z.refetch(),V.refetch(),e.refetch()}});let U=F.data??[],W=R.data,G=z.data??[],K=W?.activeRuns??[],q=W?.recentFailures??[],J=W?.failedTaskCount??0,Y=U.filter(e=>e.enabled),X=W?.latestByTask??[],Z=[...Y].filter(e=>e.nextScheduledAt).sort((e,t)=>String(e.nextScheduledAt).localeCompare(String(t.nextScheduledAt))).slice(0,5),fe=I(V.data?.series,U,n,r),pe=V.data?.series?.some(e=>e.buckets.length>0)??!1,Q=de(V.data?.series),$=U.find(e=>e.id===Q?.taskId);return(0,N.jsxs)(N.Fragment,{children:[(0,N.jsx)(oe,{title:`Every WAN, one operational picture.`,description:`Live throughput, route integrity, and scheduled measurements across every configured network path.`,actions:(0,N.jsx)(_,{asChild:!0,children:(0,N.jsxs)(i,{to:`/tasks/new`,children:[(0,N.jsx)(c,{className:`h-4 w-4`}),`New task`]})})}),(0,N.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2 xl:grid-cols-4`,children:[(0,N.jsx)(a,{label:`Latest download`,value:n(Q?.bucket.download?.average),detail:Q?`${$?.interfaceName??Q.name} · ${d(L(Q.bucket))}`:`Waiting for a successful test`,icon:o,tone:`cyan`}),(0,N.jsx)(a,{label:`Latest upload`,value:n(Q?.bucket.upload?.average),detail:$?.name??`No measurement available`,icon:s,tone:`violet`}),(0,N.jsx)(a,{label:`Current latency`,value:m(Q?.bucket.latency?.average),detail:$?`${$.provider} methodology`:`No measurement available`,icon:re,tone:`orange`}),(0,N.jsx)(a,{label:`Task health`,value:`${Y.length} enabled`,detail:J?`${J} task${J===1?``:`s`} currently have a failed latest result`:`No task has a failed latest result`,icon:J?te:ee,tone:J?`rose`:`emerald`})]}),K.length>0?(0,N.jsx)(le,{results:K}):null,(0,N.jsxs)(`div`,{className:`mt-6 grid gap-5 xl:grid-cols-[minmax(0,1.65fr)_minmax(320px,.75fr)]`,children:[(0,N.jsxs)(D,{children:[(0,N.jsxs)(E,{className:`flex-row items-center justify-between gap-3`,children:[(0,N.jsxs)(`div`,{children:[(0,N.jsx)(w,{children:`Throughput timeline`}),(0,N.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Successful measurements by provider and path`})]}),(0,N.jsx)(`div`,{className:`flex rounded-lg border border-border bg-muted/50 p-1`,"aria-label":`Chart range`,children:P.map(e=>(0,N.jsx)(`button`,{type:`button`,onClick:()=>{u(e),x(Date.now())},className:`rounded-md px-2.5 py-1 text-[11px] font-semibold transition ${O.label===e.label?`bg-card text-foreground shadow-sm`:`text-muted-foreground hover:text-foreground`}`,"aria-pressed":O.label===e.label,children:e.label},e.label))})]}),(0,N.jsx)(T,{children:V.error?(0,N.jsx)(f,{compact:!0,title:`Throughput range is too large`,error:V.error,onRetry:()=>{V.refetch()}}):pe?(0,N.jsx)(se,{option:fe,ariaLabel:`Download and upload throughput over ${O.label}`,className:`h-80`}):(0,N.jsx)(g,{compact:!0,title:`No throughput in this range`,description:`Successful tests will appear here as soon as a task completes.`})})]}),(0,N.jsxs)(D,{children:[(0,N.jsxs)(E,{children:[(0,N.jsx)(w,{children:`Network readiness`}),(0,N.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Interfaces visible in the active Linux namespace`})]}),(0,N.jsxs)(T,{className:`space-y-2`,children:[G.length===0?(0,N.jsx)(g,{compact:!0,title:`No interfaces detected`,description:`Refresh interface discovery from Network & routes.`}):G.slice(0,6).map(e=>(0,N.jsxs)(`div`,{className:`flex items-center gap-3 rounded-lg border border-border bg-background p-3`,children:[(0,N.jsx)(`span`,{className:`grid h-8 w-8 shrink-0 place-items-center rounded-lg ${e.operational?`bg-emerald-500/10 text-emerald-500`:`bg-muted text-muted-foreground`}`,children:(0,N.jsx)(p,{className:`h-4 w-4`})}),(0,N.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,N.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,N.jsx)(`p`,{className:`truncate text-sm font-semibold`,children:e.name}),(0,N.jsxs)(C,{tone:e.operational?`success`:`neutral`,className:`gap-1`,children:[(0,N.jsx)(S,{active:e.operational}),e.operationalState||(e.operational?`Up`:`Down`)]})]}),(0,N.jsx)(`p`,{className:`mt-0.5 truncate text-[11px] text-muted-foreground`,children:e.addresses.filter(e=>!e.linkLocal).map(e=>e.address).join(` · `)||`No routable address`})]})]},e.name)),(0,N.jsx)(_,{asChild:!0,variant:`ghost`,size:`sm`,className:`mt-2 w-full`,children:(0,N.jsx)(i,{to:`/network`,children:`Inspect network paths`})})]})]})]}),(0,N.jsxs)(`div`,{className:`mt-6 grid gap-5 xl:grid-cols-2`,children:[(0,N.jsxs)(`section`,{children:[(0,N.jsx)(y,{title:`Latest by task and WAN`,description:`The newest state for every configured path, including tasks that have never run`}),(0,N.jsx)(D,{className:`overflow-hidden`,children:X.length===0?(0,N.jsx)(g,{title:`No configured measurement paths`,description:`Create a task, validate its path, and run it to establish the first baseline.`,action:(0,N.jsx)(_,{asChild:!0,size:`sm`,children:(0,N.jsx)(i,{to:`/tasks/new`,children:`Create a task`})})}):(0,N.jsx)(`div`,{className:`max-h-[42rem] divide-y divide-border overflow-y-auto scrollbar-thin`,children:X.map(e=>(0,N.jsx)(ue,{entry:e,task:U.find(t=>t.id===e.taskId),formatBitrate:n},e.taskId))})})]}),(0,N.jsxs)(`div`,{className:`grid gap-5 sm:grid-cols-2 xl:grid-cols-1 2xl:grid-cols-2`,children:[(0,N.jsxs)(`section`,{children:[(0,N.jsx)(y,{title:`Next scheduled`,description:`Upcoming persisted schedules`}),(0,N.jsx)(D,{className:`overflow-hidden`,children:Z.length===0?(0,N.jsx)(g,{compact:!0,title:`Nothing scheduled`,description:`Enable a task to calculate its next run.`}):(0,N.jsx)(`div`,{className:`divide-y divide-border`,children:Z.map(e=>(0,N.jsxs)(i,{to:`/tasks/${e.id}/edit`,className:`flex items-center gap-3 p-3.5 transition hover:bg-muted/50`,children:[(0,N.jsx)(ce,{className:`h-4 w-4 shrink-0 text-primary`}),(0,N.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,N.jsx)(`p`,{className:`truncate text-xs font-semibold`,children:e.name}),(0,N.jsxs)(`p`,{className:`mt-0.5 truncate text-[11px] text-muted-foreground`,children:[r(e.nextScheduledAt),` · `,e.timezone]})]}),(0,N.jsx)(k,{provider:e.provider})]},e.id))})})]}),(0,N.jsxs)(`section`,{children:[(0,N.jsx)(y,{title:`Recent failures`,description:`Actionable path and provider errors`}),(0,N.jsx)(D,{className:`overflow-hidden`,children:q.length===0?(0,N.jsx)(g,{compact:!0,title:`No recent failures`,description:`All tests in this range completed without a hard failure.`}):(0,N.jsx)(`div`,{className:`divide-y divide-border`,children:q.slice(0,5).map(e=>(0,N.jsxs)(i,{to:`/results/${e.id}`,className:`block p-3.5 transition hover:bg-muted/50`,children:[(0,N.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,N.jsx)(`p`,{className:`truncate text-xs font-semibold`,children:U.find(t=>t.id===e.taskId)?.name??`Deleted task`}),(0,N.jsx)(`span`,{className:`text-[10px] text-muted-foreground`,children:d(e.finishedAt)})]}),(0,N.jsx)(`p`,{className:`mt-1 line-clamp-2 text-[11px] leading-4 text-rose-600 dark:text-rose-400`,children:e.sanitizedError||`The provider did not complete the measurement.`})]},e.id))})})]})]})]})]})}function le({results:e}){return(0,N.jsxs)(`div`,{className:`mt-5 flex flex-col gap-3 rounded-xl border border-cyan-500/25 bg-cyan-500/[.06] p-4 sm:flex-row sm:items-center`,children:[(0,N.jsx)(`div`,{className:`flex h-9 items-center gap-1 rounded-lg bg-cyan-500/10 px-3`,"aria-hidden":`true`,children:[0,1,2,3].map(e=>(0,N.jsx)(`span`,{className:`h-5 w-1 animate-pulsebar rounded-full bg-cyan-500`,style:{animationDelay:`${e*120}ms`}},e))}),(0,N.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,N.jsxs)(`p`,{className:`text-sm font-semibold text-foreground`,children:[e.length,` test`,e.length===1?``:`s`,` active`]}),(0,N.jsx)(`p`,{className:`truncate text-xs text-muted-foreground`,children:e.map(e=>`${e.selectedInterface||`pending interface`} · ${e.status}`).join(` / `)})]}),(0,N.jsx)(_,{asChild:!0,variant:`outline`,size:`sm`,children:(0,N.jsxs)(i,{to:`/results?status=running`,children:[(0,N.jsx)(A,{className:`h-3.5 w-3.5`}),`Follow live`]})})]})}function ue({entry:e,task:t,formatBitrate:n}){let r=e.latestResult,a=r?`/results/${r.id}`:`/tasks/${e.taskId}/edit`;return(0,N.jsxs)(i,{to:a,className:`grid gap-3 p-4 transition hover:bg-muted/45 sm:grid-cols-[minmax(0,1.2fr)_repeat(3,minmax(90px,.55fr))] sm:items-center`,children:[(0,N.jsxs)(`div`,{className:`min-w-0`,children:[(0,N.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,N.jsx)(j,{className:`h-4 w-4 shrink-0 text-primary`}),(0,N.jsx)(`p`,{className:`truncate text-sm font-semibold`,children:e.taskName}),t?(0,N.jsx)(k,{provider:t.provider}):null]}),(0,N.jsxs)(`p`,{className:`mt-1 truncate pl-6 text-[11px] text-muted-foreground`,children:[e.interfaceName,` · `,e.sourceIp,r?.detectedPublicIp?` · ${r.detectedPublicIp}`:``]})]}),(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`p`,{className:`text-[10px] uppercase tracking-wider text-muted-foreground`,children:`Download`}),(0,N.jsx)(`p`,{className:`metric-number mt-0.5 text-sm font-semibold`,children:n(r?.downloadBitsPerSecond)})]}),(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`p`,{className:`text-[10px] uppercase tracking-wider text-muted-foreground`,children:`Upload`}),(0,N.jsx)(`p`,{className:`metric-number mt-0.5 text-sm font-semibold`,children:n(r?.uploadBitsPerSecond)})]}),(0,N.jsxs)(`div`,{className:`flex items-center justify-between gap-2 sm:block`,children:[(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`p`,{className:`text-[10px] uppercase tracking-wider text-muted-foreground`,children:`Latency`}),(0,N.jsx)(`p`,{className:`metric-number mt-0.5 text-sm font-semibold`,children:m(r?.latencyMilliseconds)})]}),r?(0,N.jsx)(O,{status:r.status}):(0,N.jsx)(C,{tone:`neutral`,children:`Never run`})]})]})}function de(e){return(e??[]).flatMap(e=>e.buckets.filter(e=>e.successfulCount>0&&e.download?.average!=null).map(t=>({taskId:e.id,name:e.name,bucket:t}))).sort((e,t)=>L(t.bucket).localeCompare(L(e.bucket)))[0]??null}function I(e,t,n,r){let i=x(),a=b(),o=e??[],s=[u.ookla,u.librespeed,u.cloudflare,`#34d399`,`#f472b6`,`#facc15`],c=o.flatMap((e,n)=>{let r=t.find(t=>t.id===e.id),i=r?`${r.name} · ${r.interfaceName}`:e.name,a=s[n%s.length]??u.ookla,o=t=>e.buckets.flatMap(e=>{let n=L(e),r=e[t]?.average;return n&&r!=null?[[n,r]]:[]}).sort(([e],[t])=>e.localeCompare(t));return[{name:`${i} download`,type:`line`,smooth:!1,showSymbol:e.buckets.length<25,symbolSize:4,data:o(`download`),connectNulls:!1,lineStyle:{width:2,color:a},itemStyle:{color:a}},{name:`${i} upload`,type:`line`,smooth:!1,showSymbol:!1,data:o(`upload`),connectNulls:!1,lineStyle:{width:1,type:`dashed`,color:a},itemStyle:{color:a}}]});return{tooltip:{trigger:`axis`,backgroundColor:`rgba(8,17,31,.94)`,borderColor:`rgba(148,163,184,.22)`,textStyle:{color:`#e2e8f0`,fontSize:11},valueFormatter:e=>n(Number(Array.isArray(e)?e[1]:e))},legend:{right:0,top:0,type:`scroll`,textStyle:{color:i,fontSize:10}},grid:{left:8,right:12,top:38,bottom:8,containLabel:!0},xAxis:{type:`time`,axisLabel:{color:i,fontSize:10,hideOverlap:!0,formatter:e=>r(new Date(e).toISOString())},axisLine:{lineStyle:{color:a}},axisTick:{show:!1}},yAxis:{type:`value`,axisLabel:{color:i,fontSize:10,formatter:e=>n(e,!0)},splitLine:{lineStyle:{color:a}}},series:c}}function L(e){return e.bucket||e.timestamp||``}export{F as default,I as throughputOption}; \ No newline at end of file +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r}from"./query-DWNqGEie.js";import{d as i}from"./react-DsKPNe9r.js";import{t as a}from"./MetricCard-CMX2GS_z.js";import{n as o,t as s}from"./arrow-up-DVV_1I7Y.js";import{t as c}from"./plus-BbAjJ_nn.js";import{F as ee,I as l,N as te,_ as u,a as ne,g as d,i as f,j as re,k as p,m,n as h,r as g,s as _,w as v}from"./index-DleN6wGz.js";import{n as ie,r as ae}from"./useAppSettings-vnP5f96R.js";import{n as y,t as oe}from"./PageHeader-DGX-y_Bi.js";import{n as b,r as x,t as se}from"./EChart-A2OaIcPj.js";import{n as S,t as C}from"./badge-BWAHg20h.js";import{i as w,n as T,r as E,t as D}from"./card-BtXBd7n8.js";import{n as O,t as k}from"./EntityBadges-CM7WeMOS.js";var ce=l(`calendar-clock`,[[`path`,{d:`M16 14v2.2l1.6 1`,key:`fo4ql5`}],[`path`,{d:`M16 2v3`,key:`otl347`}],[`path`,{d:`M21 7.338V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h2.338`,key:`7hb8p4`}],[`path`,{d:`M3 9h5.859`,key:`numkqi`}],[`path`,{d:`M8 2v3`,key:`1ioesn`}],[`circle`,{cx:`16`,cy:`16`,r:`6`,key:`qoo3c4`}]]),A=l(`radio`,[[`path`,{d:`M16.247 7.761a6 6 0 0 1 0 8.478`,key:`1fwjs5`}],[`path`,{d:`M19.075 4.933a10 10 0 0 1 0 14.134`,key:`ehdyv1`}],[`path`,{d:`M4.925 19.067a10 10 0 0 1 0-14.134`,key:`1q22gi`}],[`path`,{d:`M7.753 16.239a6 6 0 0 1 0-8.478`,key:`r2q7qm`}],[`circle`,{cx:`12`,cy:`12`,r:`2`,key:`1c9p78`}]]),j=l(`router`,[[`rect`,{width:`20`,height:`8`,x:`2`,y:`14`,rx:`2`,key:`w68u3i`}],[`path`,{d:`M6.01 18H6`,key:`19vcac`}],[`path`,{d:`M10.01 18H10`,key:`uamcmx`}],[`path`,{d:`M15 10v4`,key:`qjz1xs`}],[`path`,{d:`M17.84 7.17a4 4 0 0 0-5.66 0`,key:`1rif40`}],[`path`,{d:`M20.66 4.34a8 8 0 0 0-11.31 0`,key:`6a5xfq`}]]),M=e(r(),1),N=n(),P=[{label:`24h`,hours:24},{label:`7d`,hours:168},{label:`30d`,hours:720},{label:`90d`,hours:2160}];function F(){let e=ie(),{bitrate:n,dateTime:r}=ae(),[l,u]=(0,M.useState)(null),[b,x]=(0,M.useState)(()=>Date.now()),O=l??P.find(t=>t.label===e.settings.defaultChartRange)??P[2],A=new Date(b-O.hours*36e5).toISOString(),j=new Date(b).toISOString(),F=t({queryKey:h.tasks,queryFn:v.tasks}),R=t({queryKey:h.dashboardSummary,queryFn:v.dashboardSummary}),z=t({queryKey:[...h.interfaces,`dashboard`,!0],queryFn:()=>v.interfaces({includeDown:!0})}),B={granularity:`raw`,from:A,to:j,timezone:e.settings.defaultTimezone,groupBy:`task`},V=t({queryKey:h.statistics({dashboard:B}),queryFn:()=>v.statistics(B)});if(F.isLoading||R.isLoading||z.isLoading||V.isLoading||e.isLoading)return(0,N.jsx)(ne,{label:`Assembling the live operations view…`});let H=F.error??R.error??z.error??e.error;if(H)return(0,N.jsx)(f,{error:H,onRetry:()=>{F.refetch(),R.refetch(),z.refetch(),V.refetch(),e.refetch()}});let U=F.data??[],W=R.data,G=z.data??[],K=W?.activeRuns??[],q=W?.recentFailures??[],J=W?.failedTaskCount??0,Y=U.filter(e=>e.enabled),X=W?.latestByTask??[],Z=[...Y].filter(e=>e.nextScheduledAt).sort((e,t)=>String(e.nextScheduledAt).localeCompare(String(t.nextScheduledAt))).slice(0,5),fe=I(V.data?.series,U,n,r),pe=V.data?.series?.some(e=>e.buckets.length>0)??!1,Q=de(V.data?.series),$=U.find(e=>e.id===Q?.taskId);return(0,N.jsxs)(N.Fragment,{children:[(0,N.jsx)(oe,{title:`Every WAN, one operational picture.`,description:`Live throughput, route integrity, and scheduled measurements across every configured network path.`,actions:(0,N.jsx)(_,{asChild:!0,children:(0,N.jsxs)(i,{to:`/tasks/new`,children:[(0,N.jsx)(c,{className:`h-4 w-4`}),`New task`]})})}),(0,N.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2 xl:grid-cols-4`,children:[(0,N.jsx)(a,{label:`Latest download`,value:n(Q?.bucket.download?.average),detail:Q?`${$?.interfaceName??Q.name} · ${d(L(Q.bucket))}`:`Waiting for a successful test`,icon:o,tone:`cyan`}),(0,N.jsx)(a,{label:`Latest upload`,value:n(Q?.bucket.upload?.average),detail:$?.name??`No measurement available`,icon:s,tone:`violet`}),(0,N.jsx)(a,{label:`Current latency`,value:m(Q?.bucket.latency?.average),detail:$?`${$.provider} methodology`:`No measurement available`,icon:re,tone:`orange`}),(0,N.jsx)(a,{label:`Task health`,value:`${Y.length} enabled`,detail:J?`${J} task${J===1?``:`s`} currently have a failed latest result`:`No task has a failed latest result`,icon:J?te:ee,tone:J?`rose`:`emerald`})]}),K.length>0?(0,N.jsx)(le,{results:K}):null,(0,N.jsxs)(`div`,{className:`mt-6 grid gap-5 xl:grid-cols-[minmax(0,1.65fr)_minmax(320px,.75fr)]`,children:[(0,N.jsxs)(D,{children:[(0,N.jsxs)(E,{className:`flex-row items-center justify-between gap-3`,children:[(0,N.jsxs)(`div`,{children:[(0,N.jsx)(w,{children:`Throughput timeline`}),(0,N.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Successful measurements by provider and path`})]}),(0,N.jsx)(`div`,{className:`flex rounded-lg border border-border bg-muted/50 p-1`,"aria-label":`Chart range`,children:P.map(e=>(0,N.jsx)(`button`,{type:`button`,onClick:()=>{u(e),x(Date.now())},className:`rounded-md px-2.5 py-1 text-[11px] font-semibold transition ${O.label===e.label?`bg-card text-foreground shadow-sm`:`text-muted-foreground hover:text-foreground`}`,"aria-pressed":O.label===e.label,children:e.label},e.label))})]}),(0,N.jsx)(T,{children:V.error?(0,N.jsx)(f,{compact:!0,title:`Throughput range is too large`,error:V.error,onRetry:()=>{V.refetch()}}):pe?(0,N.jsx)(se,{option:fe,ariaLabel:`Download and upload throughput over ${O.label}`,className:`h-80`}):(0,N.jsx)(g,{compact:!0,title:`No throughput in this range`,description:`Successful tests will appear here as soon as a task completes.`})})]}),(0,N.jsxs)(D,{children:[(0,N.jsxs)(E,{children:[(0,N.jsx)(w,{children:`Network readiness`}),(0,N.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Interfaces visible in the active Linux namespace`})]}),(0,N.jsxs)(T,{className:`space-y-2`,children:[G.length===0?(0,N.jsx)(g,{compact:!0,title:`No interfaces detected`,description:`Refresh interface discovery from Network & routes.`}):G.slice(0,6).map(e=>(0,N.jsxs)(`div`,{className:`flex items-center gap-3 rounded-lg border border-border bg-background p-3`,children:[(0,N.jsx)(`span`,{className:`grid h-8 w-8 shrink-0 place-items-center rounded-lg ${e.operational?`bg-emerald-500/10 text-emerald-500`:`bg-muted text-muted-foreground`}`,children:(0,N.jsx)(p,{className:`h-4 w-4`})}),(0,N.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,N.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,N.jsx)(`p`,{className:`truncate text-sm font-semibold`,children:e.name}),(0,N.jsxs)(C,{tone:e.operational?`success`:`neutral`,className:`gap-1`,children:[(0,N.jsx)(S,{active:e.operational}),e.operationalState||(e.operational?`Up`:`Down`)]})]}),(0,N.jsx)(`p`,{className:`mt-0.5 truncate text-[11px] text-muted-foreground`,children:e.addresses.filter(e=>!e.linkLocal).map(e=>e.address).join(` · `)||`No routable address`})]})]},e.name)),(0,N.jsx)(_,{asChild:!0,variant:`ghost`,size:`sm`,className:`mt-2 w-full`,children:(0,N.jsx)(i,{to:`/network`,children:`Inspect network paths`})})]})]})]}),(0,N.jsxs)(`div`,{className:`mt-6 grid gap-5 xl:grid-cols-2`,children:[(0,N.jsxs)(`section`,{children:[(0,N.jsx)(y,{title:`Latest by task and WAN`,description:`The newest state for every configured path, including tasks that have never run`}),(0,N.jsx)(D,{className:`overflow-hidden`,children:X.length===0?(0,N.jsx)(g,{title:`No configured measurement paths`,description:`Create a task, validate its path, and run it to establish the first baseline.`,action:(0,N.jsx)(_,{asChild:!0,size:`sm`,children:(0,N.jsx)(i,{to:`/tasks/new`,children:`Create a task`})})}):(0,N.jsx)(`div`,{className:`max-h-[42rem] divide-y divide-border overflow-y-auto scrollbar-thin`,children:X.map(e=>(0,N.jsx)(ue,{entry:e,task:U.find(t=>t.id===e.taskId),formatBitrate:n},e.taskId))})})]}),(0,N.jsxs)(`div`,{className:`grid gap-5 sm:grid-cols-2 xl:grid-cols-1 2xl:grid-cols-2`,children:[(0,N.jsxs)(`section`,{children:[(0,N.jsx)(y,{title:`Next scheduled`,description:`Upcoming persisted schedules`}),(0,N.jsx)(D,{className:`overflow-hidden`,children:Z.length===0?(0,N.jsx)(g,{compact:!0,title:`Nothing scheduled`,description:`Enable a task to calculate its next run.`}):(0,N.jsx)(`div`,{className:`divide-y divide-border`,children:Z.map(e=>(0,N.jsxs)(i,{to:`/tasks/${e.id}/edit`,className:`flex items-center gap-3 p-3.5 transition hover:bg-muted/50`,children:[(0,N.jsx)(ce,{className:`h-4 w-4 shrink-0 text-primary`}),(0,N.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,N.jsx)(`p`,{className:`truncate text-xs font-semibold`,children:e.name}),(0,N.jsxs)(`p`,{className:`mt-0.5 truncate text-[11px] text-muted-foreground`,children:[r(e.nextScheduledAt),` · `,e.timezone]})]}),(0,N.jsx)(k,{provider:e.provider})]},e.id))})})]}),(0,N.jsxs)(`section`,{children:[(0,N.jsx)(y,{title:`Recent failures`,description:`Actionable path and provider errors`}),(0,N.jsx)(D,{className:`overflow-hidden`,children:q.length===0?(0,N.jsx)(g,{compact:!0,title:`No recent failures`,description:`All tests in this range completed without a hard failure.`}):(0,N.jsx)(`div`,{className:`divide-y divide-border`,children:q.slice(0,5).map(e=>(0,N.jsxs)(i,{to:`/results/${e.id}`,className:`block p-3.5 transition hover:bg-muted/50`,children:[(0,N.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,N.jsx)(`p`,{className:`truncate text-xs font-semibold`,children:U.find(t=>t.id===e.taskId)?.name??`Deleted task`}),(0,N.jsx)(`span`,{className:`text-[10px] text-muted-foreground`,children:d(e.finishedAt)})]}),(0,N.jsx)(`p`,{className:`mt-1 line-clamp-2 text-[11px] leading-4 text-rose-600 dark:text-rose-400`,children:e.sanitizedError||`The provider did not complete the measurement.`})]},e.id))})})]})]})]})]})}function le({results:e}){return(0,N.jsxs)(`div`,{className:`mt-5 flex flex-col gap-3 rounded-xl border border-cyan-500/25 bg-cyan-500/[.06] p-4 sm:flex-row sm:items-center`,children:[(0,N.jsx)(`div`,{className:`flex h-9 items-center gap-1 rounded-lg bg-cyan-500/10 px-3`,"aria-hidden":`true`,children:[0,1,2,3].map(e=>(0,N.jsx)(`span`,{className:`h-5 w-1 animate-pulsebar rounded-full bg-cyan-500`,style:{animationDelay:`${e*120}ms`}},e))}),(0,N.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,N.jsxs)(`p`,{className:`text-sm font-semibold text-foreground`,children:[e.length,` test`,e.length===1?``:`s`,` active`]}),(0,N.jsx)(`p`,{className:`truncate text-xs text-muted-foreground`,children:e.map(e=>`${e.selectedInterface||`pending interface`} · ${e.status}`).join(` / `)})]}),(0,N.jsx)(_,{asChild:!0,variant:`outline`,size:`sm`,children:(0,N.jsxs)(i,{to:`/results?status=running`,children:[(0,N.jsx)(A,{className:`h-3.5 w-3.5`}),`Follow live`]})})]})}function ue({entry:e,task:t,formatBitrate:n}){let r=e.latestResult,a=r?`/results/${r.id}`:`/tasks/${e.taskId}/edit`;return(0,N.jsxs)(i,{to:a,className:`grid gap-3 p-4 transition hover:bg-muted/45 sm:grid-cols-[minmax(0,1.2fr)_repeat(3,minmax(90px,.55fr))] sm:items-center`,children:[(0,N.jsxs)(`div`,{className:`min-w-0`,children:[(0,N.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,N.jsx)(j,{className:`h-4 w-4 shrink-0 text-primary`}),(0,N.jsx)(`p`,{className:`truncate text-sm font-semibold`,children:e.taskName}),t?(0,N.jsx)(k,{provider:t.provider}):null]}),(0,N.jsxs)(`p`,{className:`mt-1 truncate pl-6 text-[11px] text-muted-foreground`,children:[e.interfaceName,` · `,e.sourceIp,r?.detectedPublicIp?` · ${r.detectedPublicIp}`:``]})]}),(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`p`,{className:`text-[10px] uppercase tracking-wider text-muted-foreground`,children:`Download`}),(0,N.jsx)(`p`,{className:`metric-number mt-0.5 text-sm font-semibold`,children:n(r?.downloadBitsPerSecond)})]}),(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`p`,{className:`text-[10px] uppercase tracking-wider text-muted-foreground`,children:`Upload`}),(0,N.jsx)(`p`,{className:`metric-number mt-0.5 text-sm font-semibold`,children:n(r?.uploadBitsPerSecond)})]}),(0,N.jsxs)(`div`,{className:`flex items-center justify-between gap-2 sm:block`,children:[(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`p`,{className:`text-[10px] uppercase tracking-wider text-muted-foreground`,children:`Latency`}),(0,N.jsx)(`p`,{className:`metric-number mt-0.5 text-sm font-semibold`,children:m(r?.latencyMilliseconds)})]}),r?(0,N.jsx)(O,{status:r.status}):(0,N.jsx)(C,{tone:`neutral`,children:`Never run`})]})]})}function de(e){return(e??[]).flatMap(e=>e.buckets.filter(e=>e.successfulCount>0&&e.download?.average!=null).map(t=>({taskId:e.id,name:e.name,bucket:t}))).sort((e,t)=>L(t.bucket).localeCompare(L(e.bucket)))[0]??null}function I(e,t,n,r){let i=x(),a=b(),o=e??[],s=[u.ookla,u.librespeed,u.cloudflare,`#34d399`,`#f472b6`,`#facc15`],c=o.flatMap((e,n)=>{let r=t.find(t=>t.id===e.id),i=r?`${r.name} · ${r.interfaceName}`:e.name,a=s[n%s.length]??u.ookla,o=t=>e.buckets.flatMap(e=>{let n=L(e),r=e[t]?.average;return n&&r!=null?[[n,r]]:[]}).sort(([e],[t])=>e.localeCompare(t));return[{name:`${i} download`,type:`line`,smooth:!1,showSymbol:e.buckets.length<25,symbolSize:4,data:o(`download`),connectNulls:!1,lineStyle:{width:2,color:a},itemStyle:{color:a}},{name:`${i} upload`,type:`line`,smooth:!1,showSymbol:!1,data:o(`upload`),connectNulls:!1,lineStyle:{width:1,type:`dashed`,color:a},itemStyle:{color:a}}]});return{tooltip:{trigger:`axis`,backgroundColor:`rgba(8,17,31,.94)`,borderColor:`rgba(148,163,184,.22)`,textStyle:{color:`#e2e8f0`,fontSize:11},valueFormatter:e=>n(Number(Array.isArray(e)?e[1]:e))},legend:{right:0,top:0,type:`scroll`,textStyle:{color:i,fontSize:10}},grid:{left:8,right:12,top:38,bottom:8,containLabel:!0},xAxis:{type:`time`,axisLabel:{color:i,fontSize:10,hideOverlap:!0,formatter:e=>r(new Date(e).toISOString())},axisLine:{lineStyle:{color:a}},axisTick:{show:!1}},yAxis:{type:`value`,axisLabel:{color:i,fontSize:10,formatter:e=>n(e,!0)},splitLine:{lineStyle:{color:a}}},series:c}}function L(e){return e.bucket||e.timestamp||``}export{F as default,I as throughputOption}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/EChart-C9A5j2N8.js b/internal/frontend/dist/assets/EChart-A2OaIcPj.js similarity index 86% rename from internal/frontend/dist/assets/EChart-C9A5j2N8.js rename to internal/frontend/dist/assets/EChart-A2OaIcPj.js index 918ae37..721d41a 100644 --- a/internal/frontend/dist/assets/EChart-C9A5j2N8.js +++ b/internal/frontend/dist/assets/EChart-A2OaIcPj.js @@ -1 +1 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{T as t,V as n}from"./query-DWNqGEie.js";import{c as r}from"./index-B84OiuS0.js";import{a as i,c as a,i as o,l as s,n as c,o as l,r as u,s as d,t as f}from"./charts-BiiqPz-H.js";var p=e(n(),1),m=t();a([d,l,i,o,c,u,f]);function h({option:e,className:t,ariaLabel:n}){let i=(0,p.useRef)(null);return(0,p.useEffect)(()=>{if(!i.current)return;let t=s(i.current,void 0,{renderer:`canvas`});t.setOption({animationDuration:450,...e});let n=new ResizeObserver(()=>t.resize());return n.observe(i.current),()=>{n.disconnect(),t.dispose()}},[e]),(0,m.jsx)(`div`,{ref:i,className:r(`h-72 w-full`,t),role:`img`,"aria-label":n})}function g(){return getComputedStyle(document.documentElement).getPropertyValue(`--chart-text`).trim()||`#94a3b8`}function _(){return getComputedStyle(document.documentElement).getPropertyValue(`--chart-grid`).trim()||`rgba(148,163,184,.12)`}export{_ as n,g as r,h as t}; \ No newline at end of file +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{T as t,V as n}from"./query-DWNqGEie.js";import{c as r}from"./index-DleN6wGz.js";import{a as i,c as a,i as o,l as s,n as c,o as l,r as u,s as d,t as f}from"./charts-BiiqPz-H.js";var p=e(n(),1),m=t();a([d,l,i,o,c,u,f]);function h({option:e,className:t,ariaLabel:n}){let i=(0,p.useRef)(null);return(0,p.useEffect)(()=>{if(!i.current)return;let t=s(i.current,void 0,{renderer:`canvas`});t.setOption({animationDuration:450,...e});let n=new ResizeObserver(()=>t.resize());return n.observe(i.current),()=>{n.disconnect(),t.dispose()}},[e]),(0,m.jsx)(`div`,{ref:i,className:r(`h-72 w-full`,t),role:`img`,"aria-label":n})}function g(){return getComputedStyle(document.documentElement).getPropertyValue(`--chart-text`).trim()||`#94a3b8`}function _(){return getComputedStyle(document.documentElement).getPropertyValue(`--chart-grid`).trim()||`rgba(148,163,184,.12)`}export{_ as n,g as r,h as t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/EntityBadges-DZDjvrcw.js b/internal/frontend/dist/assets/EntityBadges-CM7WeMOS.js similarity index 74% rename from internal/frontend/dist/assets/EntityBadges-DZDjvrcw.js rename to internal/frontend/dist/assets/EntityBadges-CM7WeMOS.js index 950f53f..900925f 100644 --- a/internal/frontend/dist/assets/EntityBadges-DZDjvrcw.js +++ b/internal/frontend/dist/assets/EntityBadges-CM7WeMOS.js @@ -1 +1 @@ -import{T as e}from"./query-DWNqGEie.js";import{C as t,F as n,v as r}from"./index-B84OiuS0.js";import{t as i}from"./badge-D4YKBNKr.js";var a=e();function o({provider:e}){return(0,a.jsx)(i,{tone:e===`ookla`?`info`:e===`librespeed`?`violet`:`warning`,children:r(e)})}function s({status:e}){let r=e===`running`||e===`validating`||e===`queued`;return(0,a.jsxs)(i,{tone:t[e],className:`gap-1.5 capitalize`,children:[r?(0,a.jsx)(n,{className:`h-3 w-3 animate-pulse`}):null,e]})}export{s as n,o as t}; \ No newline at end of file +import{T as e}from"./query-DWNqGEie.js";import{C as t,F as n,v as r}from"./index-DleN6wGz.js";import{t as i}from"./badge-BWAHg20h.js";var a=e();function o({provider:e}){return(0,a.jsx)(i,{tone:e===`ookla`?`info`:e===`librespeed`?`violet`:`warning`,children:r(e)})}function s({status:e}){let r=e===`running`||e===`validating`||e===`queued`;return(0,a.jsxs)(i,{tone:t[e],className:`gap-1.5 capitalize`,children:[r?(0,a.jsx)(n,{className:`h-3 w-3 animate-pulse`}):null,e]})}export{s as n,o as t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/MetricCard-GfGMWPS-.js b/internal/frontend/dist/assets/MetricCard-CMX2GS_z.js similarity index 96% rename from internal/frontend/dist/assets/MetricCard-GfGMWPS-.js rename to internal/frontend/dist/assets/MetricCard-CMX2GS_z.js index 9ef9874..5904632 100644 --- a/internal/frontend/dist/assets/MetricCard-GfGMWPS-.js +++ b/internal/frontend/dist/assets/MetricCard-CMX2GS_z.js @@ -1 +1 @@ -import{T as e}from"./query-DWNqGEie.js";import{I as t,c as n}from"./index-B84OiuS0.js";import{t as r}from"./card-DEXft0Cm.js";var i=t(`arrow-down-right`,[[`path`,{d:`m7 7 10 10`,key:`1fmybs`}],[`path`,{d:`M17 7v10H7`,key:`6fjiku`}]]),a=t(`arrow-up-right`,[[`path`,{d:`M7 7h10v10`,key:`1tivn9`}],[`path`,{d:`M7 17 17 7`,key:`1vkiza`}]]),o=t(`minus`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}]]),s=e();function c({label:e,value:t,detail:c,icon:l,tone:u=`cyan`,trend:d}){let f={cyan:`bg-cyan-500/10 text-cyan-600 dark:text-cyan-300`,violet:`bg-violet-500/10 text-violet-600 dark:text-violet-300`,orange:`bg-orange-500/10 text-orange-600 dark:text-orange-300`,emerald:`bg-emerald-500/10 text-emerald-600 dark:text-emerald-300`,rose:`bg-rose-500/10 text-rose-600 dark:text-rose-300`}[u],p=d?.direction===`up`?a:d?.direction===`down`?i:o;return(0,s.jsxs)(r,{className:`relative overflow-hidden p-4 sm:p-5`,children:[(0,s.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,s.jsxs)(`div`,{className:`min-w-0`,children:[(0,s.jsx)(`p`,{className:`text-[11px] font-semibold uppercase tracking-[.1em] text-muted-foreground`,children:e}),(0,s.jsx)(`p`,{className:`metric-number mt-2 truncate text-2xl font-bold text-foreground`,children:t})]}),(0,s.jsx)(`span`,{className:n(`grid h-9 w-9 shrink-0 place-items-center rounded-lg`,f),children:(0,s.jsx)(l,{className:`h-4 w-4`})})]}),(0,s.jsxs)(`div`,{className:`mt-3 flex min-h-5 items-center gap-2 text-[11px] text-muted-foreground`,children:[d?(0,s.jsxs)(`span`,{className:n(`inline-flex items-center gap-0.5 font-semibold`,d.positive===!0?`text-emerald-600 dark:text-emerald-400`:d.positive===!1?`text-rose-600 dark:text-rose-400`:`text-muted-foreground`),children:[(0,s.jsx)(p,{className:`h-3 w-3`}),d.value]}):null,(0,s.jsx)(`span`,{className:`truncate`,children:c})]})]})}export{c as t}; \ No newline at end of file +import{T as e}from"./query-DWNqGEie.js";import{I as t,c as n}from"./index-DleN6wGz.js";import{t as r}from"./card-BtXBd7n8.js";var i=t(`arrow-down-right`,[[`path`,{d:`m7 7 10 10`,key:`1fmybs`}],[`path`,{d:`M17 7v10H7`,key:`6fjiku`}]]),a=t(`arrow-up-right`,[[`path`,{d:`M7 7h10v10`,key:`1tivn9`}],[`path`,{d:`M7 17 17 7`,key:`1vkiza`}]]),o=t(`minus`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}]]),s=e();function c({label:e,value:t,detail:c,icon:l,tone:u=`cyan`,trend:d}){let f={cyan:`bg-cyan-500/10 text-cyan-600 dark:text-cyan-300`,violet:`bg-violet-500/10 text-violet-600 dark:text-violet-300`,orange:`bg-orange-500/10 text-orange-600 dark:text-orange-300`,emerald:`bg-emerald-500/10 text-emerald-600 dark:text-emerald-300`,rose:`bg-rose-500/10 text-rose-600 dark:text-rose-300`}[u],p=d?.direction===`up`?a:d?.direction===`down`?i:o;return(0,s.jsxs)(r,{className:`relative overflow-hidden p-4 sm:p-5`,children:[(0,s.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,s.jsxs)(`div`,{className:`min-w-0`,children:[(0,s.jsx)(`p`,{className:`text-[11px] font-semibold uppercase tracking-[.1em] text-muted-foreground`,children:e}),(0,s.jsx)(`p`,{className:`metric-number mt-2 truncate text-2xl font-bold text-foreground`,children:t})]}),(0,s.jsx)(`span`,{className:n(`grid h-9 w-9 shrink-0 place-items-center rounded-lg`,f),children:(0,s.jsx)(l,{className:`h-4 w-4`})})]}),(0,s.jsxs)(`div`,{className:`mt-3 flex min-h-5 items-center gap-2 text-[11px] text-muted-foreground`,children:[d?(0,s.jsxs)(`span`,{className:n(`inline-flex items-center gap-0.5 font-semibold`,d.positive===!0?`text-emerald-600 dark:text-emerald-400`:d.positive===!1?`text-rose-600 dark:text-rose-400`:`text-muted-foreground`),children:[(0,s.jsx)(p,{className:`h-3 w-3`}),d.value]}):null,(0,s.jsx)(`span`,{className:`truncate`,children:c})]})]})}export{c as t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/NetworkPage-EmZpqL4-.js b/internal/frontend/dist/assets/NetworkPage-CZ9yIsUy.js similarity index 96% rename from internal/frontend/dist/assets/NetworkPage-EmZpqL4-.js rename to internal/frontend/dist/assets/NetworkPage-CZ9yIsUy.js index 5049e92..8d0f363 100644 --- a/internal/frontend/dist/assets/NetworkPage-EmZpqL4-.js +++ b/internal/frontend/dist/assets/NetworkPage-CZ9yIsUy.js @@ -1 +1 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,w as i,x as a}from"./query-DWNqGEie.js";import{i as o,n as s,o as c,r as l,t as u}from"./fields-PJGT-Jp7.js";import{t as d}from"./circle-x-DYIKQ94H.js";import{n as f,t as p}from"./route-C77_DY7l.js";import{t as m}from"./pen-line-BMm6ehcb.js";import{t as h}from"./plus-C7xMvwvj.js";import{t as ee}from"./shield-alert-ma0N2JAc.js";import{t as g}from"./trash-2-DJb0gq__.js";import{I as _,M as v,O as te,T as y,a as ne,c as b,i as x,k as S,l as C,m as w,n as T,o as E,r as D,s as O,t as k,w as A}from"./index-B84OiuS0.js";import{r as j}from"./useAppSettings-CeA9vkHH.js";import{n as M,t as N}from"./PageHeader-DGX-y_Bi.js";import{n as P,t as F}from"./badge-D4YKBNKr.js";import{i as I,n as L,r as R,t as z}from"./card-DEXft0Cm.js";import{a as B,i as V,l as H,s as U,u as W}from"./schemas-DaFWBMkt.js";import{n as G,r as K,t as q}from"./dialog-DXkCUNSe.js";var J=_(`cable`,[[`path`,{d:`M17 19a1 1 0 0 1-1-1v-2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2a1 1 0 0 1-1 1z`,key:`trhst0`}],[`path`,{d:`M17 21v-2`,key:`ds4u3f`}],[`path`,{d:`M19 14V6.5a1 1 0 0 0-7 0v11a1 1 0 0 1-7 0V10`,key:`1mo9zo`}],[`path`,{d:`M21 21v-2`,key:`eo0ou`}],[`path`,{d:`M3 5V3`,key:`1k5hjh`}],[`path`,{d:`M4 10a2 2 0 0 1-2-2V6a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2a2 2 0 0 1-2 2z`,key:`1dd30t`}],[`path`,{d:`M7 5V3`,key:`1t1388`}]]),Y=_(`eye`,[[`path`,{d:`M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0`,key:`1nclc0`}],[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}]]),X=e(r(),1),Z=n(),re=V({name:B().trim().min(2,`Enter at least 2 characters.`).max(100),description:B().trim().max(500),interfaceName:B().min(1,`Select an interface.`),sourceIp:B().min(1,`Select a concrete source address.`),expectedGateway:B().trim().max(128),expectedRoutingTable:B().trim().max(64).refine(e=>/^(?:|[0-9]{1,10}|[A-Za-z][A-Za-z0-9_.-]{0,63})$/.test(e),`Use a numeric table ID or a safe Linux table name.`),validationTarget:B().trim().min(1,`Enter a hostname or IP to validate.`).max(255),notes:B().trim().max(1e3)}),Q={name:``,description:``,interfaceName:``,sourceIp:``,expectedGateway:``,expectedRoutingTable:``,validationTarget:`one.one.one.one`,notes:``};function ie(){let e=i(),{toast:n}=k(),[r,o]=(0,X.useState)(!1),[s,c]=(0,X.useState)(!1),[l,d]=(0,X.useState)(null),[f,p]=(0,X.useState)(null),[m,g]=(0,X.useState)(null),_=t({queryKey:[...T.interfaces,r,s],queryFn:()=>A.interfaces({includeDown:r,includeVirtual:s})}),v=t({queryKey:T.routes,queryFn:A.routes}),b=a({mutationFn:A.refreshInterfaces,onSuccess:()=>{e.invalidateQueries({queryKey:T.interfaces}),e.invalidateQueries({queryKey:T.tasks}),n({tone:`success`,title:`Interface snapshot refreshed`,description:`Task bindings were re-evaluated against the active namespace.`})},onError:e=>n({tone:`error`,title:`Refresh failed`,description:y(e)})}),S=a({mutationFn:A.validateRoute,onSuccess:t=>{g(e=>e?{...e,pending:!1}:null),e.invalidateQueries({queryKey:T.routes}),n({tone:t.success?`success`:`error`,title:t.success?`Route matches expectations`:`Route mismatch detected`,description:t.message})},onError:e=>{g(e=>e?{...e,pending:!1}:null),n({tone:`error`,title:`Validation failed`,description:y(e)})}}),C=a({mutationFn:A.deleteRoute,onSuccess:()=>{p(null),e.invalidateQueries({queryKey:T.routes}),n({tone:`success`,title:`Route profile deleted`})},onError:e=>n({tone:`error`,title:`Unable to delete profile`,description:y(e)})}),w=_.error??v.error;if(_.isLoading||v.isLoading)return(0,Z.jsx)(ne,{label:`Inspecting Linux interfaces and route expectations…`});if(w)return(0,Z.jsx)(x,{error:w,onRetry:()=>{_.refetch(),v.refetch()}});let j=_.data??[],P=v.data??[],F=e=>{g({profile:e,pending:!0}),S.mutate(e.id)};return(0,Z.jsxs)(Z.Fragment,{children:[(0,Z.jsx)(N,{title:`Observe routes. Never rewrite them.`,description:`Discover source addresses in the active Linux namespace and persist read-only expectations for source-based policy routing.`,actions:(0,Z.jsxs)(O,{variant:`outline`,onClick:()=>b.mutate(),disabled:b.isPending,children:[b.isPending?(0,Z.jsx)(E,{}):(0,Z.jsx)(te,{className:`h-4 w-4`}),`Refresh interfaces`]})}),(0,Z.jsxs)(`div`,{className:`mb-5 flex gap-3 rounded-xl border border-amber-500/25 bg-amber-500/[.055] p-4`,children:[(0,Z.jsx)(ee,{className:`mt-0.5 h-5 w-5 shrink-0 text-amber-500`}),(0,Z.jsxs)(`div`,{children:[(0,Z.jsx)(`p`,{className:`text-sm font-semibold`,children:`MultiSpeed does not modify host networking`}),(0,Z.jsx)(`p`,{className:`mt-1 text-xs leading-5 text-muted-foreground`,children:`Route profiles only validate route lookup, effective source, gateway, reachability, and public IP. Configure policy rules and gateways on the Docker host.`})]})]}),(0,Z.jsxs)(`section`,{children:[(0,Z.jsx)(M,{title:`Detected interfaces`,description:`${j.length} interface${j.length===1?``:`s`} shown from the active namespace`,action:(0,Z.jsxs)(`div`,{className:`flex gap-2`,children:[(0,Z.jsx)(u,{checked:r,onChange:o,label:`Down`}),(0,Z.jsx)(u,{checked:s,onChange:c,label:`Virtual`})]})}),j.length===0?(0,Z.jsx)(z,{children:(0,Z.jsx)(D,{title:`No matching interfaces`,description:`Show down or virtual interfaces, or refresh discovery.`})}):(0,Z.jsx)(`div`,{className:`grid gap-4 md:grid-cols-2 xl:grid-cols-3`,children:j.map(e=>(0,Z.jsx)(ae,{item:e},e.name))})]}),(0,Z.jsxs)(`section`,{className:`mt-8`,children:[(0,Z.jsx)(M,{title:`Route profiles`,description:`Persisted expected paths used before speed tests`,action:(0,Z.jsxs)(O,{size:`sm`,onClick:()=>d(`new`),children:[(0,Z.jsx)(h,{className:`h-3.5 w-3.5`}),`New route profile`]})}),P.length===0?(0,Z.jsx)(z,{children:(0,Z.jsx)(D,{title:`No route expectations yet`,description:`Create a read-only profile to verify interface, source, gateway, routing table, reachability, and public IP.`,action:(0,Z.jsxs)(O,{size:`sm`,onClick:()=>d(`new`),children:[(0,Z.jsx)(h,{className:`h-3.5 w-3.5`}),`Create profile`]})})}):(0,Z.jsx)(`div`,{className:`grid gap-4 lg:grid-cols-2`,children:P.map(e=>(0,Z.jsx)(oe,{profile:e,onEdit:()=>d(e),onDelete:()=>p(e),onValidate:()=>F(e),validating:m?.profile.id===e.id&&m.pending},e.id))})]}),(0,Z.jsx)(ce,{routes:P}),(0,Z.jsx)(se,{open:l!==null,profile:l===`new`?null:l,interfaces:j,onOpenChange:e=>!e&&d(null),onSaved:()=>{d(null),e.invalidateQueries({queryKey:T.routes})}}),(0,Z.jsx)(q,{open:f!==null,onOpenChange:e=>!e&&p(null),title:`Delete route profile?`,description:(0,Z.jsxs)(Z.Fragment,{children:[(0,Z.jsx)(`strong`,{className:`font-semibold text-foreground`,children:f?.name}),` will no longer be available for task validation. Tasks that reference it must be updated first.`]}),confirmLabel:`Delete profile`,destructive:!0,busy:C.isPending,onConfirm:()=>f&&C.mutate(f.id)})]})}function ae({item:e}){let t=e.addresses.filter(e=>!e.linkLocal);return(0,Z.jsxs)(z,{className:b(`overflow-hidden`,!e.operational&&`opacity-75`),children:[(0,Z.jsx)(`div`,{className:b(`h-1`,e.operational?`bg-emerald-500`:`bg-slate-500`)}),(0,Z.jsxs)(`div`,{className:`p-4`,children:[(0,Z.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,Z.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,Z.jsx)(`span`,{className:b(`grid h-9 w-9 place-items-center rounded-lg`,e.operational?`bg-emerald-500/10 text-emerald-500`:`bg-muted text-muted-foreground`),children:(0,Z.jsx)(J,{className:`h-4 w-4`})}),(0,Z.jsxs)(`div`,{children:[(0,Z.jsx)(`h3`,{className:`text-sm font-bold`,children:e.name}),(0,Z.jsx)(`p`,{className:`mt-0.5 font-mono text-[10px] text-muted-foreground`,children:e.macAddress||`MAC unavailable`})]})]}),(0,Z.jsxs)(F,{tone:e.operational?`success`:`neutral`,className:`gap-1`,children:[(0,Z.jsx)(P,{active:e.operational}),e.operationalState||(e.operational?`Up`:`Down`)]})]}),(0,Z.jsxs)(`div`,{className:`mt-4 flex flex-wrap gap-1.5`,children:[(0,Z.jsxs)(F,{tone:`neutral`,children:[`MTU `,e.mtu]}),e.virtual?(0,Z.jsx)(F,{tone:`violet`,children:`Virtual`}):null,e.loopback?(0,Z.jsx)(F,{tone:`warning`,children:`Loopback`}):null]}),(0,Z.jsx)(`div`,{className:`mt-4 space-y-2`,children:t.length?t.map(e=>(0,Z.jsxs)(`div`,{className:`flex items-center justify-between gap-2 rounded-md border border-border bg-background px-2.5 py-2`,children:[(0,Z.jsx)(`code`,{className:`min-w-0 truncate text-[11px]`,children:e.address}),(0,Z.jsx)(F,{tone:e.family===`ipv4`?`info`:`violet`,children:e.family.toUpperCase()})]},e.address)):(0,Z.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No routable source addresses detected.`})})]})]})}function oe({profile:e,onEdit:t,onDelete:n,onValidate:r,validating:i}){let{dateTime:a}=j(),o=e.lastValidationSnapshot,s=e.lastValidationSucceeded;return(0,Z.jsxs)(z,{children:[(0,Z.jsxs)(R,{className:`flex-row items-start justify-between gap-3`,children:[(0,Z.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,Z.jsx)(`span`,{className:b(`grid h-9 w-9 shrink-0 place-items-center rounded-lg`,s===!0?`bg-emerald-500/10 text-emerald-500`:s===!1?`bg-rose-500/10 text-rose-500`:`bg-muted text-muted-foreground`),children:(0,Z.jsx)(p,{className:`h-4 w-4`})}),(0,Z.jsxs)(`div`,{children:[(0,Z.jsx)(I,{children:e.name}),(0,Z.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:e.description||`No description`})]})]}),(0,Z.jsxs)(F,{tone:s===!0?`success`:s===!1?`danger`:`neutral`,className:`gap-1`,children:[s===!0?(0,Z.jsx)(v,{className:`h-3 w-3`}):s===!1?(0,Z.jsx)(d,{className:`h-3 w-3`}):(0,Z.jsx)(Y,{className:`h-3 w-3`}),s===!0?`Valid`:s===!1?`Mismatch`:`Not validated`]})]}),(0,Z.jsxs)(L,{children:[(0,Z.jsxs)(`dl`,{className:`grid grid-cols-[125px_1fr] gap-x-3 gap-y-2 text-xs`,children:[(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Expected path`}),(0,Z.jsxs)(`dd`,{className:`font-medium`,children:[e.interfaceName,` · `,(0,Z.jsx)(`span`,{className:`font-mono`,children:e.sourceIp})]}),(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Expected gateway`}),(0,Z.jsx)(`dd`,{children:e.expectedGateway||`Any`}),(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Detected gateway`}),(0,Z.jsx)(`dd`,{children:typeof o.gateway==`string`&&o.gateway?o.gateway:`—`}),(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Expected table`}),(0,Z.jsx)(`dd`,{children:e.expectedRoutingTable||`Any`}),(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Detected table`}),(0,Z.jsx)(`dd`,{children:typeof o.routingTable==`string`&&o.routingTable?o.routingTable:`—`}),(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Target`}),(0,Z.jsx)(`dd`,{children:e.validationTarget}),e.lastValidationAt?(0,Z.jsxs)(Z.Fragment,{children:[(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Last validation`}),(0,Z.jsxs)(`dd`,{children:[a(e.lastValidationAt),` · `,w(typeof o.durationMs==`number`?o.durationMs:null)]}),(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Detected public IP`}),(0,Z.jsx)(`dd`,{className:`font-mono`,children:typeof o.detectedPublicIp==`string`?o.detectedPublicIp:`—`})]}):null]}),s===!1&&typeof o.message==`string`?(0,Z.jsx)(`p`,{className:`mt-4 rounded-lg border border-rose-500/20 bg-rose-500/[.055] p-3 text-xs leading-5 text-rose-600 dark:text-rose-400`,children:o.message}):null,(0,Z.jsxs)(`div`,{className:`mt-4 flex flex-wrap gap-2`,children:[(0,Z.jsxs)(O,{size:`sm`,onClick:r,disabled:i,children:[i?(0,Z.jsx)(E,{}):(0,Z.jsx)(Y,{className:`h-3.5 w-3.5`}),`Validate now`]}),(0,Z.jsxs)(O,{size:`sm`,variant:`outline`,onClick:t,children:[(0,Z.jsx)(m,{className:`h-3.5 w-3.5`}),`Edit`]}),(0,Z.jsxs)(O,{size:`sm`,variant:`ghost`,onClick:n,className:`ml-auto text-rose-600 dark:text-rose-400`,children:[(0,Z.jsx)(g,{className:`h-3.5 w-3.5`}),`Delete`]})]})]})]})}function se({open:e,profile:t,interfaces:n,onOpenChange:r,onSaved:i}){let{toast:u}=k(),d=H({resolver:U(re),defaultValues:Q}),f=W({control:d.control,name:`interfaceName`});(0,X.useEffect)(()=>{d.reset(t?{name:t.name,description:t.description,interfaceName:t.interfaceName,sourceIp:t.sourceIp,expectedGateway:t.expectedGateway,expectedRoutingTable:t.expectedRoutingTable,validationTarget:t.validationTarget,notes:t.notes}:Q)},[d,t,e]);let p=a({mutationFn:e=>t?A.updateRoute(t.id,e):A.createRoute(e),onSuccess:e=>{u({tone:`success`,title:t?`Route profile updated`:`Route profile created`,description:`${e.name} is ready for read-only validation.`}),i()},onError:e=>u({tone:`error`,title:`Unable to save route profile`,description:y(e)})}),m=n.find(e=>e.name===f)?.addresses.filter(e=>!e.linkLocal)??[];return(0,Z.jsx)(G,{open:e,onOpenChange:r,children:(0,Z.jsx)(K,{className:`max-w-2xl`,title:t?`Edit route profile`:`Create route profile`,description:`Persist expected routing facts. MultiSpeed never executes route changes.`,children:(0,Z.jsxs)(`form`,{onSubmit:d.handleSubmit(e=>p.mutate(e)),className:`space-y-4`,children:[(0,Z.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,Z.jsx)(s,{label:`Profile name`,required:!0,error:d.formState.errors.name?.message,children:(0,Z.jsx)(l,{...d.register(`name`),placeholder:`Fiber policy route`})}),(0,Z.jsx)(s,{label:`Validation target`,required:!0,error:d.formState.errors.validationTarget?.message,children:(0,Z.jsx)(l,{...d.register(`validationTarget`),placeholder:`one.one.one.one`})})]}),(0,Z.jsx)(s,{label:`Description`,error:d.formState.errors.description?.message,children:(0,Z.jsx)(l,{...d.register(`description`),placeholder:`Expected path for the primary fiber uplink`})}),(0,Z.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,Z.jsx)(s,{label:`Interface`,required:!0,error:d.formState.errors.interfaceName?.message,children:(0,Z.jsxs)(o,{...d.register(`interfaceName`),onChange:e=>{d.register(`interfaceName`).onChange(e),d.setValue(`sourceIp`,``)},children:[(0,Z.jsx)(`option`,{value:``,children:`Select interface…`}),n.map(e=>(0,Z.jsxs)(`option`,{value:e.name,children:[e.name,` · `,e.operationalState||(e.operational?`up`:`down`)]},e.name))]})}),(0,Z.jsx)(s,{label:`Source address`,required:!0,error:d.formState.errors.sourceIp?.message,children:(0,Z.jsxs)(o,{...d.register(`sourceIp`),disabled:!f,children:[(0,Z.jsx)(`option`,{value:``,children:`Select concrete address…`}),m.map(e=>(0,Z.jsxs)(`option`,{value:e.address,children:[e.address,` · `,e.family.toUpperCase()]},e.address))]})})]}),(0,Z.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,Z.jsx)(s,{label:`Expected gateway`,hint:`Optional`,error:d.formState.errors.expectedGateway?.message,children:(0,Z.jsx)(l,{...d.register(`expectedGateway`),placeholder:`192.0.2.1`})}),(0,Z.jsx)(s,{label:`Expected routing table`,hint:`ID or name`,error:d.formState.errors.expectedRoutingTable?.message,children:(0,Z.jsx)(l,{...d.register(`expectedRoutingTable`),placeholder:`100 or wan_fiber`})})]}),(0,Z.jsx)(s,{label:`Operator notes`,hint:`Optional`,error:d.formState.errors.notes?.message,children:(0,Z.jsx)(c,{...d.register(`notes`),placeholder:`Host policy-routing prerequisites and change record.`})}),(0,Z.jsxs)(`div`,{className:`flex justify-end gap-2 pt-2`,children:[(0,Z.jsx)(O,{type:`button`,variant:`outline`,onClick:()=>r(!1),disabled:p.isPending,children:`Cancel`}),(0,Z.jsxs)(O,{type:`submit`,disabled:p.isPending,children:[p.isPending?(0,Z.jsx)(E,{}):null,t?`Save changes`:`Create profile`]})]})]})})})}function ce({routes:e}){let{toast:t}=k(),n=e.find(e=>e.expectedGateway)??e[0],r=n?.sourceIp||`192.0.2.10`,i=n?.expectedGateway||`192.0.2.1`,a=n?.interfaceName||`eth1`,o=n?.expectedRoutingTable||`100`,s=`ip rule add from ${r.includes(`:`)?`${r}/128`:`${r}/32`} table ${$(o)}\nip route add default via ${i} dev ${$(a)} table ${$(o)}`;return(0,Z.jsxs)(`section`,{className:`mt-8`,children:[(0,Z.jsx)(M,{title:`Host policy-routing example`,description:`Copy-only examples derived from the selected route profile; never executed by MultiSpeed`}),(0,Z.jsx)(z,{children:(0,Z.jsx)(L,{className:`pt-5`,children:(0,Z.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,Z.jsx)(S,{className:`mt-1 h-4 w-4 shrink-0 text-primary`}),(0,Z.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,Z.jsx)(`pre`,{className:`overflow-x-auto rounded-lg border border-border bg-slate-950 p-4 text-xs leading-6 text-slate-200 scrollbar-thin`,children:(0,Z.jsx)(`code`,{children:s})}),(0,Z.jsx)(`p`,{className:`mt-3 text-xs leading-5 text-muted-foreground`,children:`Review interface names, prefixes, gateways, and table IDs on the Docker host. These examples are informational and may not match your network design.`})]}),(0,Z.jsxs)(O,{variant:`outline`,size:`sm`,onClick:()=>void C(s).then(()=>t({tone:`success`,title:`Example commands copied`})),children:[(0,Z.jsx)(f,{className:`h-3.5 w-3.5`}),`Copy`]})]})})})]})}function $(e){return`'${e.replaceAll(`'`,`'"'"'`)}'`}export{ie as default}; \ No newline at end of file +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,w as i,x as a}from"./query-DWNqGEie.js";import{i as o,n as s,o as c,r as l,t as u}from"./fields-DSX3DSQR.js";import{t as d}from"./circle-x-CJ7LJdu2.js";import{n as f,t as p}from"./route-DtX_GSbk.js";import{t as m}from"./pen-line-UL8dX1Od.js";import{t as h}from"./plus-BbAjJ_nn.js";import{t as ee}from"./shield-alert-CznEbFrD.js";import{t as g}from"./trash-2-DyGHVVq4.js";import{I as _,M as v,O as te,T as y,a as ne,c as b,i as x,k as S,l as C,m as w,n as T,o as E,r as D,s as O,t as k,w as A}from"./index-DleN6wGz.js";import{r as j}from"./useAppSettings-vnP5f96R.js";import{n as M,t as N}from"./PageHeader-DGX-y_Bi.js";import{n as P,t as F}from"./badge-BWAHg20h.js";import{i as I,n as L,r as R,t as z}from"./card-BtXBd7n8.js";import{a as B,i as V,l as H,s as U,u as W}from"./schemas-DaFWBMkt.js";import{n as G,r as K,t as q}from"./dialog-CT4xHTSq.js";var J=_(`cable`,[[`path`,{d:`M17 19a1 1 0 0 1-1-1v-2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2a1 1 0 0 1-1 1z`,key:`trhst0`}],[`path`,{d:`M17 21v-2`,key:`ds4u3f`}],[`path`,{d:`M19 14V6.5a1 1 0 0 0-7 0v11a1 1 0 0 1-7 0V10`,key:`1mo9zo`}],[`path`,{d:`M21 21v-2`,key:`eo0ou`}],[`path`,{d:`M3 5V3`,key:`1k5hjh`}],[`path`,{d:`M4 10a2 2 0 0 1-2-2V6a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2a2 2 0 0 1-2 2z`,key:`1dd30t`}],[`path`,{d:`M7 5V3`,key:`1t1388`}]]),Y=_(`eye`,[[`path`,{d:`M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0`,key:`1nclc0`}],[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}]]),X=e(r(),1),Z=n(),re=V({name:B().trim().min(2,`Enter at least 2 characters.`).max(100),description:B().trim().max(500),interfaceName:B().min(1,`Select an interface.`),sourceIp:B().min(1,`Select a concrete source address.`),expectedGateway:B().trim().max(128),expectedRoutingTable:B().trim().max(64).refine(e=>/^(?:|[0-9]{1,10}|[A-Za-z][A-Za-z0-9_.-]{0,63})$/.test(e),`Use a numeric table ID or a safe Linux table name.`),validationTarget:B().trim().min(1,`Enter a hostname or IP to validate.`).max(255),notes:B().trim().max(1e3)}),Q={name:``,description:``,interfaceName:``,sourceIp:``,expectedGateway:``,expectedRoutingTable:``,validationTarget:`one.one.one.one`,notes:``};function ie(){let e=i(),{toast:n}=k(),[r,o]=(0,X.useState)(!1),[s,c]=(0,X.useState)(!1),[l,d]=(0,X.useState)(null),[f,p]=(0,X.useState)(null),[m,g]=(0,X.useState)(null),_=t({queryKey:[...T.interfaces,r,s],queryFn:()=>A.interfaces({includeDown:r,includeVirtual:s})}),v=t({queryKey:T.routes,queryFn:A.routes}),b=a({mutationFn:A.refreshInterfaces,onSuccess:()=>{e.invalidateQueries({queryKey:T.interfaces}),e.invalidateQueries({queryKey:T.tasks}),n({tone:`success`,title:`Interface snapshot refreshed`,description:`Task bindings were re-evaluated against the active namespace.`})},onError:e=>n({tone:`error`,title:`Refresh failed`,description:y(e)})}),S=a({mutationFn:A.validateRoute,onSuccess:t=>{g(e=>e?{...e,pending:!1}:null),e.invalidateQueries({queryKey:T.routes}),n({tone:t.success?`success`:`error`,title:t.success?`Route matches expectations`:`Route mismatch detected`,description:t.message})},onError:e=>{g(e=>e?{...e,pending:!1}:null),n({tone:`error`,title:`Validation failed`,description:y(e)})}}),C=a({mutationFn:A.deleteRoute,onSuccess:()=>{p(null),e.invalidateQueries({queryKey:T.routes}),n({tone:`success`,title:`Route profile deleted`})},onError:e=>n({tone:`error`,title:`Unable to delete profile`,description:y(e)})}),w=_.error??v.error;if(_.isLoading||v.isLoading)return(0,Z.jsx)(ne,{label:`Inspecting Linux interfaces and route expectations…`});if(w)return(0,Z.jsx)(x,{error:w,onRetry:()=>{_.refetch(),v.refetch()}});let j=_.data??[],P=v.data??[],F=e=>{g({profile:e,pending:!0}),S.mutate(e.id)};return(0,Z.jsxs)(Z.Fragment,{children:[(0,Z.jsx)(N,{title:`Observe routes. Never rewrite them.`,description:`Discover source addresses in the active Linux namespace and persist read-only expectations for source-based policy routing.`,actions:(0,Z.jsxs)(O,{variant:`outline`,onClick:()=>b.mutate(),disabled:b.isPending,children:[b.isPending?(0,Z.jsx)(E,{}):(0,Z.jsx)(te,{className:`h-4 w-4`}),`Refresh interfaces`]})}),(0,Z.jsxs)(`div`,{className:`mb-5 flex gap-3 rounded-xl border border-amber-500/25 bg-amber-500/[.055] p-4`,children:[(0,Z.jsx)(ee,{className:`mt-0.5 h-5 w-5 shrink-0 text-amber-500`}),(0,Z.jsxs)(`div`,{children:[(0,Z.jsx)(`p`,{className:`text-sm font-semibold`,children:`MultiSpeed does not modify host networking`}),(0,Z.jsx)(`p`,{className:`mt-1 text-xs leading-5 text-muted-foreground`,children:`Route profiles only validate route lookup, effective source, gateway, reachability, and public IP. Configure policy rules and gateways on the Docker host.`})]})]}),(0,Z.jsxs)(`section`,{children:[(0,Z.jsx)(M,{title:`Detected interfaces`,description:`${j.length} interface${j.length===1?``:`s`} shown from the active namespace`,action:(0,Z.jsxs)(`div`,{className:`flex gap-2`,children:[(0,Z.jsx)(u,{checked:r,onChange:o,label:`Down`}),(0,Z.jsx)(u,{checked:s,onChange:c,label:`Virtual`})]})}),j.length===0?(0,Z.jsx)(z,{children:(0,Z.jsx)(D,{title:`No matching interfaces`,description:`Show down or virtual interfaces, or refresh discovery.`})}):(0,Z.jsx)(`div`,{className:`grid gap-4 md:grid-cols-2 xl:grid-cols-3`,children:j.map(e=>(0,Z.jsx)(ae,{item:e},e.name))})]}),(0,Z.jsxs)(`section`,{className:`mt-8`,children:[(0,Z.jsx)(M,{title:`Route profiles`,description:`Persisted expected paths used before speed tests`,action:(0,Z.jsxs)(O,{size:`sm`,onClick:()=>d(`new`),children:[(0,Z.jsx)(h,{className:`h-3.5 w-3.5`}),`New route profile`]})}),P.length===0?(0,Z.jsx)(z,{children:(0,Z.jsx)(D,{title:`No route expectations yet`,description:`Create a read-only profile to verify interface, source, gateway, routing table, reachability, and public IP.`,action:(0,Z.jsxs)(O,{size:`sm`,onClick:()=>d(`new`),children:[(0,Z.jsx)(h,{className:`h-3.5 w-3.5`}),`Create profile`]})})}):(0,Z.jsx)(`div`,{className:`grid gap-4 lg:grid-cols-2`,children:P.map(e=>(0,Z.jsx)(oe,{profile:e,onEdit:()=>d(e),onDelete:()=>p(e),onValidate:()=>F(e),validating:m?.profile.id===e.id&&m.pending},e.id))})]}),(0,Z.jsx)(ce,{routes:P}),(0,Z.jsx)(se,{open:l!==null,profile:l===`new`?null:l,interfaces:j,onOpenChange:e=>!e&&d(null),onSaved:()=>{d(null),e.invalidateQueries({queryKey:T.routes})}}),(0,Z.jsx)(q,{open:f!==null,onOpenChange:e=>!e&&p(null),title:`Delete route profile?`,description:(0,Z.jsxs)(Z.Fragment,{children:[(0,Z.jsx)(`strong`,{className:`font-semibold text-foreground`,children:f?.name}),` will no longer be available for task validation. Tasks that reference it must be updated first.`]}),confirmLabel:`Delete profile`,destructive:!0,busy:C.isPending,onConfirm:()=>f&&C.mutate(f.id)})]})}function ae({item:e}){let t=e.addresses.filter(e=>!e.linkLocal);return(0,Z.jsxs)(z,{className:b(`overflow-hidden`,!e.operational&&`opacity-75`),children:[(0,Z.jsx)(`div`,{className:b(`h-1`,e.operational?`bg-emerald-500`:`bg-slate-500`)}),(0,Z.jsxs)(`div`,{className:`p-4`,children:[(0,Z.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,Z.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,Z.jsx)(`span`,{className:b(`grid h-9 w-9 place-items-center rounded-lg`,e.operational?`bg-emerald-500/10 text-emerald-500`:`bg-muted text-muted-foreground`),children:(0,Z.jsx)(J,{className:`h-4 w-4`})}),(0,Z.jsxs)(`div`,{children:[(0,Z.jsx)(`h3`,{className:`text-sm font-bold`,children:e.name}),(0,Z.jsx)(`p`,{className:`mt-0.5 font-mono text-[10px] text-muted-foreground`,children:e.macAddress||`MAC unavailable`})]})]}),(0,Z.jsxs)(F,{tone:e.operational?`success`:`neutral`,className:`gap-1`,children:[(0,Z.jsx)(P,{active:e.operational}),e.operationalState||(e.operational?`Up`:`Down`)]})]}),(0,Z.jsxs)(`div`,{className:`mt-4 flex flex-wrap gap-1.5`,children:[(0,Z.jsxs)(F,{tone:`neutral`,children:[`MTU `,e.mtu]}),e.virtual?(0,Z.jsx)(F,{tone:`violet`,children:`Virtual`}):null,e.loopback?(0,Z.jsx)(F,{tone:`warning`,children:`Loopback`}):null]}),(0,Z.jsx)(`div`,{className:`mt-4 space-y-2`,children:t.length?t.map(e=>(0,Z.jsxs)(`div`,{className:`flex items-center justify-between gap-2 rounded-md border border-border bg-background px-2.5 py-2`,children:[(0,Z.jsx)(`code`,{className:`min-w-0 truncate text-[11px]`,children:e.address}),(0,Z.jsx)(F,{tone:e.family===`ipv4`?`info`:`violet`,children:e.family.toUpperCase()})]},e.address)):(0,Z.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No routable source addresses detected.`})})]})]})}function oe({profile:e,onEdit:t,onDelete:n,onValidate:r,validating:i}){let{dateTime:a}=j(),o=e.lastValidationSnapshot,s=e.lastValidationSucceeded;return(0,Z.jsxs)(z,{children:[(0,Z.jsxs)(R,{className:`flex-row items-start justify-between gap-3`,children:[(0,Z.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,Z.jsx)(`span`,{className:b(`grid h-9 w-9 shrink-0 place-items-center rounded-lg`,s===!0?`bg-emerald-500/10 text-emerald-500`:s===!1?`bg-rose-500/10 text-rose-500`:`bg-muted text-muted-foreground`),children:(0,Z.jsx)(p,{className:`h-4 w-4`})}),(0,Z.jsxs)(`div`,{children:[(0,Z.jsx)(I,{children:e.name}),(0,Z.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:e.description||`No description`})]})]}),(0,Z.jsxs)(F,{tone:s===!0?`success`:s===!1?`danger`:`neutral`,className:`gap-1`,children:[s===!0?(0,Z.jsx)(v,{className:`h-3 w-3`}):s===!1?(0,Z.jsx)(d,{className:`h-3 w-3`}):(0,Z.jsx)(Y,{className:`h-3 w-3`}),s===!0?`Valid`:s===!1?`Mismatch`:`Not validated`]})]}),(0,Z.jsxs)(L,{children:[(0,Z.jsxs)(`dl`,{className:`grid grid-cols-[125px_1fr] gap-x-3 gap-y-2 text-xs`,children:[(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Expected path`}),(0,Z.jsxs)(`dd`,{className:`font-medium`,children:[e.interfaceName,` · `,(0,Z.jsx)(`span`,{className:`font-mono`,children:e.sourceIp})]}),(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Expected gateway`}),(0,Z.jsx)(`dd`,{children:e.expectedGateway||`Any`}),(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Detected gateway`}),(0,Z.jsx)(`dd`,{children:typeof o.gateway==`string`&&o.gateway?o.gateway:`—`}),(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Expected table`}),(0,Z.jsx)(`dd`,{children:e.expectedRoutingTable||`Any`}),(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Detected table`}),(0,Z.jsx)(`dd`,{children:typeof o.routingTable==`string`&&o.routingTable?o.routingTable:`—`}),(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Target`}),(0,Z.jsx)(`dd`,{children:e.validationTarget}),e.lastValidationAt?(0,Z.jsxs)(Z.Fragment,{children:[(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Last validation`}),(0,Z.jsxs)(`dd`,{children:[a(e.lastValidationAt),` · `,w(typeof o.durationMs==`number`?o.durationMs:null)]}),(0,Z.jsx)(`dt`,{className:`text-muted-foreground`,children:`Detected public IP`}),(0,Z.jsx)(`dd`,{className:`font-mono`,children:typeof o.detectedPublicIp==`string`?o.detectedPublicIp:`—`})]}):null]}),s===!1&&typeof o.message==`string`?(0,Z.jsx)(`p`,{className:`mt-4 rounded-lg border border-rose-500/20 bg-rose-500/[.055] p-3 text-xs leading-5 text-rose-600 dark:text-rose-400`,children:o.message}):null,(0,Z.jsxs)(`div`,{className:`mt-4 flex flex-wrap gap-2`,children:[(0,Z.jsxs)(O,{size:`sm`,onClick:r,disabled:i,children:[i?(0,Z.jsx)(E,{}):(0,Z.jsx)(Y,{className:`h-3.5 w-3.5`}),`Validate now`]}),(0,Z.jsxs)(O,{size:`sm`,variant:`outline`,onClick:t,children:[(0,Z.jsx)(m,{className:`h-3.5 w-3.5`}),`Edit`]}),(0,Z.jsxs)(O,{size:`sm`,variant:`ghost`,onClick:n,className:`ml-auto text-rose-600 dark:text-rose-400`,children:[(0,Z.jsx)(g,{className:`h-3.5 w-3.5`}),`Delete`]})]})]})]})}function se({open:e,profile:t,interfaces:n,onOpenChange:r,onSaved:i}){let{toast:u}=k(),d=H({resolver:U(re),defaultValues:Q}),f=W({control:d.control,name:`interfaceName`});(0,X.useEffect)(()=>{d.reset(t?{name:t.name,description:t.description,interfaceName:t.interfaceName,sourceIp:t.sourceIp,expectedGateway:t.expectedGateway,expectedRoutingTable:t.expectedRoutingTable,validationTarget:t.validationTarget,notes:t.notes}:Q)},[d,t,e]);let p=a({mutationFn:e=>t?A.updateRoute(t.id,e):A.createRoute(e),onSuccess:e=>{u({tone:`success`,title:t?`Route profile updated`:`Route profile created`,description:`${e.name} is ready for read-only validation.`}),i()},onError:e=>u({tone:`error`,title:`Unable to save route profile`,description:y(e)})}),m=n.find(e=>e.name===f)?.addresses.filter(e=>!e.linkLocal)??[];return(0,Z.jsx)(G,{open:e,onOpenChange:r,children:(0,Z.jsx)(K,{className:`max-w-2xl`,title:t?`Edit route profile`:`Create route profile`,description:`Persist expected routing facts. MultiSpeed never executes route changes.`,children:(0,Z.jsxs)(`form`,{onSubmit:d.handleSubmit(e=>p.mutate(e)),className:`space-y-4`,children:[(0,Z.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,Z.jsx)(s,{label:`Profile name`,required:!0,error:d.formState.errors.name?.message,children:(0,Z.jsx)(l,{...d.register(`name`),placeholder:`Fiber policy route`})}),(0,Z.jsx)(s,{label:`Validation target`,required:!0,error:d.formState.errors.validationTarget?.message,children:(0,Z.jsx)(l,{...d.register(`validationTarget`),placeholder:`one.one.one.one`})})]}),(0,Z.jsx)(s,{label:`Description`,error:d.formState.errors.description?.message,children:(0,Z.jsx)(l,{...d.register(`description`),placeholder:`Expected path for the primary fiber uplink`})}),(0,Z.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,Z.jsx)(s,{label:`Interface`,required:!0,error:d.formState.errors.interfaceName?.message,children:(0,Z.jsxs)(o,{...d.register(`interfaceName`),onChange:e=>{d.register(`interfaceName`).onChange(e),d.setValue(`sourceIp`,``)},children:[(0,Z.jsx)(`option`,{value:``,children:`Select interface…`}),n.map(e=>(0,Z.jsxs)(`option`,{value:e.name,children:[e.name,` · `,e.operationalState||(e.operational?`up`:`down`)]},e.name))]})}),(0,Z.jsx)(s,{label:`Source address`,required:!0,error:d.formState.errors.sourceIp?.message,children:(0,Z.jsxs)(o,{...d.register(`sourceIp`),disabled:!f,children:[(0,Z.jsx)(`option`,{value:``,children:`Select concrete address…`}),m.map(e=>(0,Z.jsxs)(`option`,{value:e.address,children:[e.address,` · `,e.family.toUpperCase()]},e.address))]})})]}),(0,Z.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,Z.jsx)(s,{label:`Expected gateway`,hint:`Optional`,error:d.formState.errors.expectedGateway?.message,children:(0,Z.jsx)(l,{...d.register(`expectedGateway`),placeholder:`192.0.2.1`})}),(0,Z.jsx)(s,{label:`Expected routing table`,hint:`ID or name`,error:d.formState.errors.expectedRoutingTable?.message,children:(0,Z.jsx)(l,{...d.register(`expectedRoutingTable`),placeholder:`100 or wan_fiber`})})]}),(0,Z.jsx)(s,{label:`Operator notes`,hint:`Optional`,error:d.formState.errors.notes?.message,children:(0,Z.jsx)(c,{...d.register(`notes`),placeholder:`Host policy-routing prerequisites and change record.`})}),(0,Z.jsxs)(`div`,{className:`flex justify-end gap-2 pt-2`,children:[(0,Z.jsx)(O,{type:`button`,variant:`outline`,onClick:()=>r(!1),disabled:p.isPending,children:`Cancel`}),(0,Z.jsxs)(O,{type:`submit`,disabled:p.isPending,children:[p.isPending?(0,Z.jsx)(E,{}):null,t?`Save changes`:`Create profile`]})]})]})})})}function ce({routes:e}){let{toast:t}=k(),n=e.find(e=>e.expectedGateway)??e[0],r=n?.sourceIp||`192.0.2.10`,i=n?.expectedGateway||`192.0.2.1`,a=n?.interfaceName||`eth1`,o=n?.expectedRoutingTable||`100`,s=`ip rule add from ${r.includes(`:`)?`${r}/128`:`${r}/32`} table ${$(o)}\nip route add default via ${i} dev ${$(a)} table ${$(o)}`;return(0,Z.jsxs)(`section`,{className:`mt-8`,children:[(0,Z.jsx)(M,{title:`Host policy-routing example`,description:`Copy-only examples derived from the selected route profile; never executed by MultiSpeed`}),(0,Z.jsx)(z,{children:(0,Z.jsx)(L,{className:`pt-5`,children:(0,Z.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,Z.jsx)(S,{className:`mt-1 h-4 w-4 shrink-0 text-primary`}),(0,Z.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,Z.jsx)(`pre`,{className:`overflow-x-auto rounded-lg border border-border bg-slate-950 p-4 text-xs leading-6 text-slate-200 scrollbar-thin`,children:(0,Z.jsx)(`code`,{children:s})}),(0,Z.jsx)(`p`,{className:`mt-3 text-xs leading-5 text-muted-foreground`,children:`Review interface names, prefixes, gateways, and table IDs on the Docker host. These examples are informational and may not match your network design.`})]}),(0,Z.jsxs)(O,{variant:`outline`,size:`sm`,onClick:()=>void C(s).then(()=>t({tone:`success`,title:`Example commands copied`})),children:[(0,Z.jsx)(f,{className:`h-3.5 w-3.5`}),`Copy`]})]})})})]})}function $(e){return`'${e.replaceAll(`'`,`'"'"'`)}'`}export{ie as default}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/NotFoundPage-DK5iVdeX.js b/internal/frontend/dist/assets/NotFoundPage-YfFq8HYe.js similarity index 84% rename from internal/frontend/dist/assets/NotFoundPage-DK5iVdeX.js rename to internal/frontend/dist/assets/NotFoundPage-YfFq8HYe.js index fa5a850..97dd1a8 100644 --- a/internal/frontend/dist/assets/NotFoundPage-DK5iVdeX.js +++ b/internal/frontend/dist/assets/NotFoundPage-YfFq8HYe.js @@ -1 +1 @@ -import{T as e}from"./query-DWNqGEie.js";import{d as t}from"./react-DsKPNe9r.js";import{t as n}from"./arrow-left-CkoT9SwQ.js";import{I as r,s as i}from"./index-B84OiuS0.js";import{t as a}from"./card-DEXft0Cm.js";var o=r(`compass`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z`,key:`9ktpf1`}]]),s=e();function c(){return(0,s.jsxs)(a,{className:`mx-auto mt-12 max-w-lg p-8 text-center`,children:[(0,s.jsx)(`span`,{className:`mx-auto grid h-12 w-12 place-items-center rounded-xl border border-border bg-muted text-muted-foreground`,children:(0,s.jsx)(o,{className:`h-5 w-5`})}),(0,s.jsx)(`p`,{className:`mt-5 text-[11px] font-bold uppercase tracking-[.15em] text-primary`,children:`404 · Unknown route`}),(0,s.jsx)(`h2`,{className:`mt-2 text-2xl font-bold tracking-tight`,children:`This path is not monitored.`}),(0,s.jsx)(`p`,{className:`mt-2 text-sm leading-6 text-muted-foreground`,children:`The requested console page does not exist or was moved.`}),(0,s.jsx)(i,{asChild:!0,className:`mt-6`,children:(0,s.jsxs)(t,{to:`/`,children:[(0,s.jsx)(n,{className:`h-4 w-4`}),`Return to dashboard`]})})]})}export{c as default}; \ No newline at end of file +import{T as e}from"./query-DWNqGEie.js";import{d as t}from"./react-DsKPNe9r.js";import{t as n}from"./arrow-left-DZUEJBho.js";import{I as r,s as i}from"./index-DleN6wGz.js";import{t as a}from"./card-BtXBd7n8.js";var o=r(`compass`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z`,key:`9ktpf1`}]]),s=e();function c(){return(0,s.jsxs)(a,{className:`mx-auto mt-12 max-w-lg p-8 text-center`,children:[(0,s.jsx)(`span`,{className:`mx-auto grid h-12 w-12 place-items-center rounded-xl border border-border bg-muted text-muted-foreground`,children:(0,s.jsx)(o,{className:`h-5 w-5`})}),(0,s.jsx)(`p`,{className:`mt-5 text-[11px] font-bold uppercase tracking-[.15em] text-primary`,children:`404 · Unknown route`}),(0,s.jsx)(`h2`,{className:`mt-2 text-2xl font-bold tracking-tight`,children:`This path is not monitored.`}),(0,s.jsx)(`p`,{className:`mt-2 text-sm leading-6 text-muted-foreground`,children:`The requested console page does not exist or was moved.`}),(0,s.jsx)(i,{asChild:!0,className:`mt-6`,children:(0,s.jsxs)(t,{to:`/`,children:[(0,s.jsx)(n,{className:`h-4 w-4`}),`Return to dashboard`]})})]})}export{c as default}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/OoklaEulaAcceptance-B4WQoWrJ.js b/internal/frontend/dist/assets/OoklaEulaAcceptance-DkJ6glh5.js similarity index 97% rename from internal/frontend/dist/assets/OoklaEulaAcceptance-B4WQoWrJ.js rename to internal/frontend/dist/assets/OoklaEulaAcceptance-DkJ6glh5.js index bfe3f42..cf4eb83 100644 --- a/internal/frontend/dist/assets/OoklaEulaAcceptance-B4WQoWrJ.js +++ b/internal/frontend/dist/assets/OoklaEulaAcceptance-DkJ6glh5.js @@ -1,2 +1,2 @@ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/dist-BiRd6Yua.js","assets/rolldown-runtime-hePW80VL.js"])))=>i.map(i=>d[i]); -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,w as i,x as a}from"./query-DWNqGEie.js";import{m as o}from"./react-DsKPNe9r.js";import{t as s}from"./fields-PJGT-Jp7.js";import{t as c}from"./external-link-DfzRK7DJ.js";import{t as l}from"./shield-check-Dm-p0u_h.js";import{I as u,T as d,n as f,o as p,p as m,s as h,t as g,w as _}from"./index-B84OiuS0.js";import{n as v}from"./useAppSettings-CeA9vkHH.js";import{t as y}from"./badge-D4YKBNKr.js";import{t as b}from"./dialog-DXkCUNSe.js";var x=u(`file-check-corner`,[[`path`,{d:`M10.5 22H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v6`,key:`g5mvt7`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`m14 20 2 2 4-4`,key:`15kota`}]]),S=u(`save`,[[`path`,{d:`M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z`,key:`1c8476`}],[`path`,{d:`M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7`,key:`1ydtos`}],[`path`,{d:`M7 3v4a1 1 0 0 0 1 1h7`,key:`t51u73`}]]),C=u(`shield-off`,[[`path`,{d:`m2 2 20 20`,key:`1ooewy`}],[`path`,{d:`M5 5a1 1 0 0 0-1 1v7c0 5 3.5 7.5 7.67 8.94a1 1 0 0 0 .67.01c2.35-.82 4.48-1.97 5.9-3.71`,key:`1jlk70`}],[`path`,{d:`M9.309 3.652A12.252 12.252 0 0 0 11.24 2.28a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v7a9.784 9.784 0 0 1-.08 1.264`,key:`18rp1v`}]]),w=u(`upload`,[[`path`,{d:`M12 3v12`,key:`1x0j5s`}],[`path`,{d:`m17 8-5-5-5 5`,key:`7q97r8`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}]]),T=[{id:`15m`,label:`Every 15 minutes`,description:`Four checks per hour`,expression:`*/15 * * * *`},{id:`hourly`,label:`Hourly`,description:`At the start of every hour`,expression:`0 * * * *`},{id:`six-hours`,label:`Every 6 hours`,description:`At 00:00, 06:00, 12:00, and 18:00`,expression:`0 */6 * * *`},{id:`daily`,label:`Daily`,description:`Every day at 06:00`,expression:`0 6 * * *`},{id:`weekly`,label:`Weekly`,description:`Every Monday at 06:00`,expression:`0 6 * * 1`}],E=/^(?:\*|(?:\d+)(?:-\d+)?(?:\/\d+)?|\*\/\d+)(?:,(?:\d+)(?:-\d+)?(?:\/\d+)?)*$/;function D(e){let t=e.trim().split(/\s+/);if(t.length!==5||!t.every(e=>E.test(e)))return!1;let n=t.map(e=>/^\d+$/.test(e)?Number(e):null);return(n[0]==null||n[0]<=59)&&(n[1]==null||n[1]<=23)&&(n[2]==null||n[2]>=1&&n[2]<=31)&&(n[3]==null||n[3]>=1&&n[3]<=12)&&(n[4]==null||n[4]<=7)}function O(e){let t=T.find(t=>t.expression===e);if(t)return t.description;let n=e.trim().split(/\s+/);if(n.length!==5)return`Invalid five-field cron expression`;let[r,i,a,o,s]=n;return r?.startsWith(`*/`)&&i===`*`&&a===`*`&&o===`*`&&s===`*`?`Every ${r.slice(2)} minutes`:r===`0`&&i?.startsWith(`*/`)&&a===`*`&&o===`*`&&s===`*`?`Every ${i.slice(2)} hours`:/^\d+$/.test(r??``)&&/^\d+$/.test(i??``)&&a===`*`&&o===`*`&&s===`*`?`Daily at ${String(i).padStart(2,`0`)}:${String(r).padStart(2,`0`)}`:D(e)?`Custom schedule: ${e}`:`Invalid five-field cron expression`}var k=[`UTC`,`Europe/Berlin`,`Europe/London`,`America/New_York`,`America/Chicago`,`America/Denver`,`America/Los_Angeles`,`Asia/Kolkata`,`Asia/Singapore`,`Asia/Tokyo`,`Australia/Sydney`],A=(()=>{try{let e=Intl.supportedValuesOf,t=e?.(`timeZone`)??[];return[...new Set([...k,...t])].sort((e,t)=>e.localeCompare(t))}catch{return k}})();function j(e){try{return new Intl.DateTimeFormat(`en`,{timeZone:e}).format(),!0}catch{return!1}}async function M(t,n,r=5){if(!D(t))return[];try{let{CronExpressionParser:i}=await o(async()=>{let{CronExpressionParser:t}=await import(`./dist-BiRd6Yua.js`).then(t=>e(t.default,1));return{CronExpressionParser:t}},__vite__mapDeps([0,1])),a=i.parse(t,{currentDate:new Date,tz:n});return Array.from({length:r},()=>a.next().toDate())}catch{return[]}}var N=e(r(),1),P=n(),F=`https://www.speedtest.net/about/eula`,I=`https://www.speedtest.net/about/terms`,L=`https://www.speedtest.net/about/privacy`,R=`https://www.speedtest.net/apps/cli`;function z({compact:e=!1}){let t=i(),{toast:n}=g(),{settings:r}=v(),[o,c]=(0,N.useState)(!1),[u,x]=(0,N.useState)(!1),S=a({mutationFn:({accepted:e,explicitConfirmation:t})=>_.updateOoklaEula(e,t),onSuccess:(e,r)=>{t.setQueryData(f.settings,e),t.invalidateQueries({queryKey:f.providers}),t.invalidateQueries({queryKey:f.system}),c(!1),x(!1),n({tone:`success`,title:r.accepted?`Ookla terms acknowledgement recorded`:`Ookla terms acknowledgement revoked`,description:r.accepted?`Provider checks now continue to the separately installed Ookla CLI.`:e.ooklaEulaEffectiveAccepted?`The persisted record was removed; ACCEPT_OOKLA_EULA still keeps the technical gate open.`:`New Ookla discovery and test runs are blocked.`})},onError:e=>n({tone:`error`,title:`Unable to update Ookla acknowledgement`,description:d(e)})});if(r.ooklaEulaEffectiveAccepted){let t=r.ooklaEulaAcceptanceSource===`environment`;return(0,P.jsxs)(`div`,{className:e?`mt-3 space-y-3`:`space-y-4`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,P.jsxs)(y,{tone:`success`,className:`gap-1`,children:[(0,P.jsx)(l,{className:`h-3 w-3`}),t?`Enabled by environment`:`Acknowledgement recorded`]}),(0,P.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:t?`ACCEPT_OOKLA_EULA=true`:r.ooklaEulaAcceptedAt?m(r.ooklaEulaAcceptedAt,!0,r.defaultTimezone):`Timestamp unavailable`})]}),(0,P.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:t?`The environment override authorizes MultiSpeed to pass --accept-license and --accept-gdpr non-interactively and opens only its technical gate. It does not grant a license. Use only when the deployment fits the current documents or you have separate written Ookla authorization; clear ACCEPT_OOKLA_EULA and restart to block runs.`:`This record authorizes MultiSpeed to pass --accept-license and --accept-gdpr non-interactively and opens only its technical gate. It does not grant a license, install the proprietary CLI, or replace separate written authorization when the deployment falls outside Ookla's express grant.`}),(0,P.jsx)(`p`,{className:`text-xs leading-5 text-amber-700 dark:text-amber-300`,children:`Ookla's public EULA describes personal, non-commercial CLI use on one personal computer, excludes routers, modems, and other non-PC devices, and restricts making the CLI available on a network where more than one device can access it. The binding current documents or individual written authorization govern your deployment.`}),(0,P.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`MultiSpeed review marker (not an Ookla version identifier): `,(0,P.jsx)(`code`,{children:r.ooklaEulaCurrentVersion})]}),(0,P.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,P.jsx)(V,{}),t?null:(0,P.jsxs)(h,{size:`sm`,variant:`ghost`,onClick:()=>x(!0),disabled:S.isPending,children:[(0,P.jsx)(C,{className:`h-3.5 w-3.5`}),`Revoke acknowledgement`]})]}),(0,P.jsx)(H,{}),(0,P.jsx)(B,{}),t?null:(0,P.jsx)(b,{open:u,onOpenChange:x,title:`Revoke Ookla terms acknowledgement?`,description:`New Ookla provider discovery and test runs will be blocked. Existing result history is preserved.`,confirmLabel:`Revoke acknowledgement`,destructive:!0,busy:S.isPending,onConfirm:()=>S.mutate({accepted:!1,explicitConfirmation:!1})})]})}return(0,P.jsxs)(`div`,{className:e?`mt-3 space-y-3`:`space-y-4`,children:[r.ooklaEulaVersion&&r.ooklaEulaVersion!==r.ooklaEulaCurrentVersion?(0,P.jsxs)(`p`,{className:`text-xs leading-5 text-amber-500`,children:[`The previous acknowledgement covered `,(0,P.jsx)(`code`,{children:r.ooklaEulaVersion}),`. Review and agree to the currently required documents again.`]}):null,(0,P.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`Review all current Ookla documents. Its public EULA describes personal, non-commercial CLI use on one personal computer, excludes routers, modems, and other non-PC devices, and restricts making the CLI available on a network where more than one device can access it. Use only when this deployment fits the binding current documents or you have separate written Ookla authorization. MultiSpeed never downloads or bundles the proprietary CLI.`}),(0,P.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`Required MultiSpeed review marker (not an Ookla version identifier): `,(0,P.jsx)(`code`,{children:r.ooklaEulaCurrentVersion})]}),(0,P.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:(0,P.jsx)(V,{})}),(0,P.jsx)(s,{checked:o,onChange:c,label:`I agree to the current EULA and Terms of Use and reviewed the Privacy Policy`,description:`I authorize MultiSpeed to pass --accept-license and --accept-gdpr non-interactively, and confirm this personal-computer/non-commercial deployment, device type, and any network access from multiple devices fit the current documents or I have separate written authorization.`}),(0,P.jsxs)(h,{size:`sm`,onClick:()=>S.mutate({accepted:!0,explicitConfirmation:!0}),disabled:!o||S.isPending,children:[S.isPending?(0,P.jsx)(p,{}):(0,P.jsx)(l,{className:`h-3.5 w-3.5`}),`Record acknowledgement`]}),(0,P.jsx)(B,{})]})}function B(){return(0,P.jsx)(`p`,{className:`text-[11px] leading-5 text-muted-foreground`,children:`Ookla® and Speedtest® are registered trademarks of Ookla, LLC. MultiSpeed is independent and is not affiliated with, endorsed by, or sponsored by Ookla.`})}function V(){return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(h,{asChild:!0,size:`sm`,variant:`outline`,children:(0,P.jsxs)(`a`,{href:F,target:`_blank`,rel:`noopener noreferrer`,children:[(0,P.jsx)(c,{className:`h-3.5 w-3.5`}),`EULA`]})}),(0,P.jsx)(h,{asChild:!0,size:`sm`,variant:`outline`,children:(0,P.jsxs)(`a`,{href:I,target:`_blank`,rel:`noopener noreferrer`,children:[(0,P.jsx)(c,{className:`h-3.5 w-3.5`}),`Terms of Use`]})}),(0,P.jsx)(h,{asChild:!0,size:`sm`,variant:`outline`,children:(0,P.jsxs)(`a`,{href:L,target:`_blank`,rel:`noopener noreferrer`,children:[(0,P.jsx)(c,{className:`h-3.5 w-3.5`}),`Privacy Policy`]})})]})}function H(){let e=i(),{toast:n}=g(),r=(0,N.useRef)(null),[o,l]=(0,N.useState)(null),[u,m]=(0,N.useState)(!1),v=t({queryKey:f.ooklaBinary,queryFn:_.ooklaBinaryStatus}),b=a({mutationFn:e=>_.uploadOoklaBinary(e),onSuccess:t=>{e.setQueryData(f.ooklaBinary,t),e.invalidateQueries({queryKey:f.providers}),e.invalidateQueries({queryKey:f.system}),l(null),m(!1),r.current&&(r.current.value=``),n({tone:`success`,title:`Ookla executable installed`,description:`${t.version} is ready for provider validation.`})},onError:e=>n({tone:`error`,title:`Ookla executable rejected`,description:d(e)})}),S=v.data,C=o!==null&&S!==void 0&&o.size>S.maxUploadBytes;return(0,P.jsxs)(`div`,{className:`space-y-3 rounded-lg border border-border bg-background/70 p-3`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-start justify-between gap-2`,children:[(0,P.jsxs)(`div`,{children:[(0,P.jsx)(`p`,{className:`text-xs font-semibold text-foreground`,children:`Ookla executable`}),(0,P.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:`MultiSpeed accepts one separately obtained Linux amd64 executable and stores it persistently outside the image.`})]}),S?.installed?(0,P.jsxs)(y,{tone:`success`,className:`gap-1`,children:[(0,P.jsx)(x,{className:`h-3 w-3`}),`Installed`]}):null]}),v.isLoading?(0,P.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Checking manual upload support…`}):null,v.error?(0,P.jsx)(`p`,{className:`text-xs text-rose-600 dark:text-rose-400`,children:d(v.error)}):null,S&&!S.uploadEnabled?(0,P.jsxs)(`p`,{className:`text-xs leading-5 text-amber-700 dark:text-amber-300`,children:[S.message,` Set `,(0,P.jsx)(`code`,{children:`APP_ALLOW_OOKLA_BINARY_UPLOAD=true`}),` and use the managed binary path to enable it.`]}):null,S?.uploadEnabled?(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(`input`,{ref:r,className:`sr-only`,type:`file`,"aria-label":`Ookla Speedtest executable`,onChange:e=>{l(e.target.files?.[0]??null),m(!1)}}),(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,P.jsxs)(h,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>r.current?.click(),disabled:b.isPending,children:[(0,P.jsx)(w,{className:`h-3.5 w-3.5`}),`Choose executable`]}),(0,P.jsx)(h,{asChild:!0,size:`sm`,variant:`outline`,children:(0,P.jsxs)(`a`,{href:R,target:`_blank`,rel:`noopener noreferrer`,children:[(0,P.jsx)(c,{className:`h-3.5 w-3.5`}),`Get official Speedtest CLI`]})}),(0,P.jsx)(`span`,{className:`min-w-0 truncate text-xs text-muted-foreground`,children:o?`${o.name} · ${(o.size/1048576).toFixed(1)} MiB`:`Maximum ${(S.maxUploadBytes/1048576).toFixed(0)} MiB`})]}),(0,P.jsxs)(`p`,{className:`text-[11px] leading-5 text-muted-foreground`,children:[`On Ookla's download page, choose the Linux x86_64 archive, extract it, and select the contained `,(0,P.jsx)(`code`,{children:`speedtest`}),` executable here.`]}),C?(0,P.jsxs)(`p`,{role:`alert`,className:`text-xs text-rose-600 dark:text-rose-400`,children:[`The selected file exceeds the `,Math.round(S.maxUploadBytes/1048576),` MiB upload limit.`]}):null,(0,P.jsx)(s,{checked:u,onChange:m,disabled:!o||C||b.isPending,label:`This is an authorized Speedtest by Ookla executable`,description:`I obtained this Linux amd64 file separately, agreed to the current EULA/Terms, reviewed the Privacy Policy, and confirm this device and any network access from multiple devices fit those documents or have separate written Ookla authorization.`}),(0,P.jsxs)(h,{type:`button`,size:`sm`,onClick:()=>o&&b.mutate(o),disabled:!o||!u||C||b.isPending,children:[b.isPending?(0,P.jsx)(p,{}):(0,P.jsx)(w,{className:`h-3.5 w-3.5`}),`Install executable`]}),(0,P.jsx)(`p`,{className:`text-[11px] leading-5 text-muted-foreground`,children:`Because MultiSpeed has no authentication, keep this opt-in upload endpoint limited to a trusted private network.`})]}):null]})}export{M as a,w as c,j as i,S as l,O as n,T as o,A as r,D as s,z as t}; \ No newline at end of file +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,w as i,x as a}from"./query-DWNqGEie.js";import{m as o}from"./react-DsKPNe9r.js";import{t as s}from"./fields-DSX3DSQR.js";import{t as c}from"./external-link-4Ueuyx8f.js";import{t as l}from"./shield-check-DrVussKH.js";import{I as u,T as d,n as f,o as p,p as m,s as h,t as g,w as _}from"./index-DleN6wGz.js";import{n as v}from"./useAppSettings-vnP5f96R.js";import{t as y}from"./badge-BWAHg20h.js";import{t as b}from"./dialog-CT4xHTSq.js";var x=u(`file-check-corner`,[[`path`,{d:`M10.5 22H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v6`,key:`g5mvt7`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`m14 20 2 2 4-4`,key:`15kota`}]]),S=u(`save`,[[`path`,{d:`M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z`,key:`1c8476`}],[`path`,{d:`M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7`,key:`1ydtos`}],[`path`,{d:`M7 3v4a1 1 0 0 0 1 1h7`,key:`t51u73`}]]),C=u(`shield-off`,[[`path`,{d:`m2 2 20 20`,key:`1ooewy`}],[`path`,{d:`M5 5a1 1 0 0 0-1 1v7c0 5 3.5 7.5 7.67 8.94a1 1 0 0 0 .67.01c2.35-.82 4.48-1.97 5.9-3.71`,key:`1jlk70`}],[`path`,{d:`M9.309 3.652A12.252 12.252 0 0 0 11.24 2.28a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v7a9.784 9.784 0 0 1-.08 1.264`,key:`18rp1v`}]]),w=u(`upload`,[[`path`,{d:`M12 3v12`,key:`1x0j5s`}],[`path`,{d:`m17 8-5-5-5 5`,key:`7q97r8`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}]]),T=[{id:`15m`,label:`Every 15 minutes`,description:`Four checks per hour`,expression:`*/15 * * * *`},{id:`hourly`,label:`Hourly`,description:`At the start of every hour`,expression:`0 * * * *`},{id:`six-hours`,label:`Every 6 hours`,description:`At 00:00, 06:00, 12:00, and 18:00`,expression:`0 */6 * * *`},{id:`daily`,label:`Daily`,description:`Every day at 06:00`,expression:`0 6 * * *`},{id:`weekly`,label:`Weekly`,description:`Every Monday at 06:00`,expression:`0 6 * * 1`}],E=/^(?:\*|(?:\d+)(?:-\d+)?(?:\/\d+)?|\*\/\d+)(?:,(?:\d+)(?:-\d+)?(?:\/\d+)?)*$/;function D(e){let t=e.trim().split(/\s+/);if(t.length!==5||!t.every(e=>E.test(e)))return!1;let n=t.map(e=>/^\d+$/.test(e)?Number(e):null);return(n[0]==null||n[0]<=59)&&(n[1]==null||n[1]<=23)&&(n[2]==null||n[2]>=1&&n[2]<=31)&&(n[3]==null||n[3]>=1&&n[3]<=12)&&(n[4]==null||n[4]<=7)}function O(e){let t=T.find(t=>t.expression===e);if(t)return t.description;let n=e.trim().split(/\s+/);if(n.length!==5)return`Invalid five-field cron expression`;let[r,i,a,o,s]=n;return r?.startsWith(`*/`)&&i===`*`&&a===`*`&&o===`*`&&s===`*`?`Every ${r.slice(2)} minutes`:r===`0`&&i?.startsWith(`*/`)&&a===`*`&&o===`*`&&s===`*`?`Every ${i.slice(2)} hours`:/^\d+$/.test(r??``)&&/^\d+$/.test(i??``)&&a===`*`&&o===`*`&&s===`*`?`Daily at ${String(i).padStart(2,`0`)}:${String(r).padStart(2,`0`)}`:D(e)?`Custom schedule: ${e}`:`Invalid five-field cron expression`}var k=[`UTC`,`Europe/Berlin`,`Europe/London`,`America/New_York`,`America/Chicago`,`America/Denver`,`America/Los_Angeles`,`Asia/Kolkata`,`Asia/Singapore`,`Asia/Tokyo`,`Australia/Sydney`],A=(()=>{try{let e=Intl.supportedValuesOf,t=e?.(`timeZone`)??[];return[...new Set([...k,...t])].sort((e,t)=>e.localeCompare(t))}catch{return k}})();function j(e){try{return new Intl.DateTimeFormat(`en`,{timeZone:e}).format(),!0}catch{return!1}}async function M(t,n,r=5){if(!D(t))return[];try{let{CronExpressionParser:i}=await o(async()=>{let{CronExpressionParser:t}=await import(`./dist-BiRd6Yua.js`).then(t=>e(t.default,1));return{CronExpressionParser:t}},__vite__mapDeps([0,1])),a=i.parse(t,{currentDate:new Date,tz:n});return Array.from({length:r},()=>a.next().toDate())}catch{return[]}}var N=e(r(),1),P=n(),F=`https://www.speedtest.net/about/eula`,I=`https://www.speedtest.net/about/terms`,L=`https://www.speedtest.net/about/privacy`,R=`https://www.speedtest.net/apps/cli`;function z({compact:e=!1}){let t=i(),{toast:n}=g(),{settings:r}=v(),[o,c]=(0,N.useState)(!1),[u,x]=(0,N.useState)(!1),S=a({mutationFn:({accepted:e,explicitConfirmation:t})=>_.updateOoklaEula(e,t),onSuccess:(e,r)=>{t.setQueryData(f.settings,e),t.invalidateQueries({queryKey:f.providers}),t.invalidateQueries({queryKey:f.system}),c(!1),x(!1),n({tone:`success`,title:r.accepted?`Ookla terms acknowledgement recorded`:`Ookla terms acknowledgement revoked`,description:r.accepted?`Provider checks now continue to the separately installed Ookla CLI.`:e.ooklaEulaEffectiveAccepted?`The persisted record was removed; ACCEPT_OOKLA_EULA still keeps the technical gate open.`:`New Ookla discovery and test runs are blocked.`})},onError:e=>n({tone:`error`,title:`Unable to update Ookla acknowledgement`,description:d(e)})});if(r.ooklaEulaEffectiveAccepted){let t=r.ooklaEulaAcceptanceSource===`environment`;return(0,P.jsxs)(`div`,{className:e?`mt-3 space-y-3`:`space-y-4`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,P.jsxs)(y,{tone:`success`,className:`gap-1`,children:[(0,P.jsx)(l,{className:`h-3 w-3`}),t?`Enabled by environment`:`Acknowledgement recorded`]}),(0,P.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:t?`ACCEPT_OOKLA_EULA=true`:r.ooklaEulaAcceptedAt?m(r.ooklaEulaAcceptedAt,!0,r.defaultTimezone):`Timestamp unavailable`})]}),(0,P.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:t?`The environment override authorizes MultiSpeed to pass --accept-license and --accept-gdpr non-interactively and opens only its technical gate. It does not grant a license. Use only when the deployment fits the current documents or you have separate written Ookla authorization; clear ACCEPT_OOKLA_EULA and restart to block runs.`:`This record authorizes MultiSpeed to pass --accept-license and --accept-gdpr non-interactively and opens only its technical gate. It does not grant a license, install the proprietary CLI, or replace separate written authorization when the deployment falls outside Ookla's express grant.`}),(0,P.jsx)(`p`,{className:`text-xs leading-5 text-amber-700 dark:text-amber-300`,children:`Ookla's public EULA describes personal, non-commercial CLI use on one personal computer, excludes routers, modems, and other non-PC devices, and restricts making the CLI available on a network where more than one device can access it. The binding current documents or individual written authorization govern your deployment.`}),(0,P.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`MultiSpeed review marker (not an Ookla version identifier): `,(0,P.jsx)(`code`,{children:r.ooklaEulaCurrentVersion})]}),(0,P.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,P.jsx)(V,{}),t?null:(0,P.jsxs)(h,{size:`sm`,variant:`ghost`,onClick:()=>x(!0),disabled:S.isPending,children:[(0,P.jsx)(C,{className:`h-3.5 w-3.5`}),`Revoke acknowledgement`]})]}),(0,P.jsx)(H,{}),(0,P.jsx)(B,{}),t?null:(0,P.jsx)(b,{open:u,onOpenChange:x,title:`Revoke Ookla terms acknowledgement?`,description:`New Ookla provider discovery and test runs will be blocked. Existing result history is preserved.`,confirmLabel:`Revoke acknowledgement`,destructive:!0,busy:S.isPending,onConfirm:()=>S.mutate({accepted:!1,explicitConfirmation:!1})})]})}return(0,P.jsxs)(`div`,{className:e?`mt-3 space-y-3`:`space-y-4`,children:[r.ooklaEulaVersion&&r.ooklaEulaVersion!==r.ooklaEulaCurrentVersion?(0,P.jsxs)(`p`,{className:`text-xs leading-5 text-amber-500`,children:[`The previous acknowledgement covered `,(0,P.jsx)(`code`,{children:r.ooklaEulaVersion}),`. Review and agree to the currently required documents again.`]}):null,(0,P.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`Review all current Ookla documents. Its public EULA describes personal, non-commercial CLI use on one personal computer, excludes routers, modems, and other non-PC devices, and restricts making the CLI available on a network where more than one device can access it. Use only when this deployment fits the binding current documents or you have separate written Ookla authorization. MultiSpeed never downloads or bundles the proprietary CLI.`}),(0,P.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`Required MultiSpeed review marker (not an Ookla version identifier): `,(0,P.jsx)(`code`,{children:r.ooklaEulaCurrentVersion})]}),(0,P.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:(0,P.jsx)(V,{})}),(0,P.jsx)(s,{checked:o,onChange:c,label:`I agree to the current EULA and Terms of Use and reviewed the Privacy Policy`,description:`I authorize MultiSpeed to pass --accept-license and --accept-gdpr non-interactively, and confirm this personal-computer/non-commercial deployment, device type, and any network access from multiple devices fit the current documents or I have separate written authorization.`}),(0,P.jsxs)(h,{size:`sm`,onClick:()=>S.mutate({accepted:!0,explicitConfirmation:!0}),disabled:!o||S.isPending,children:[S.isPending?(0,P.jsx)(p,{}):(0,P.jsx)(l,{className:`h-3.5 w-3.5`}),`Record acknowledgement`]}),(0,P.jsx)(B,{})]})}function B(){return(0,P.jsx)(`p`,{className:`text-[11px] leading-5 text-muted-foreground`,children:`Ookla® and Speedtest® are registered trademarks of Ookla, LLC. MultiSpeed is independent and is not affiliated with, endorsed by, or sponsored by Ookla.`})}function V(){return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(h,{asChild:!0,size:`sm`,variant:`outline`,children:(0,P.jsxs)(`a`,{href:F,target:`_blank`,rel:`noopener noreferrer`,children:[(0,P.jsx)(c,{className:`h-3.5 w-3.5`}),`EULA`]})}),(0,P.jsx)(h,{asChild:!0,size:`sm`,variant:`outline`,children:(0,P.jsxs)(`a`,{href:I,target:`_blank`,rel:`noopener noreferrer`,children:[(0,P.jsx)(c,{className:`h-3.5 w-3.5`}),`Terms of Use`]})}),(0,P.jsx)(h,{asChild:!0,size:`sm`,variant:`outline`,children:(0,P.jsxs)(`a`,{href:L,target:`_blank`,rel:`noopener noreferrer`,children:[(0,P.jsx)(c,{className:`h-3.5 w-3.5`}),`Privacy Policy`]})})]})}function H(){let e=i(),{toast:n}=g(),r=(0,N.useRef)(null),[o,l]=(0,N.useState)(null),[u,m]=(0,N.useState)(!1),v=t({queryKey:f.ooklaBinary,queryFn:_.ooklaBinaryStatus}),b=a({mutationFn:e=>_.uploadOoklaBinary(e),onSuccess:t=>{e.setQueryData(f.ooklaBinary,t),e.invalidateQueries({queryKey:f.providers}),e.invalidateQueries({queryKey:f.system}),l(null),m(!1),r.current&&(r.current.value=``),n({tone:`success`,title:`Ookla executable installed`,description:`${t.version} is ready for provider validation.`})},onError:e=>n({tone:`error`,title:`Ookla executable rejected`,description:d(e)})}),S=v.data,C=o!==null&&S!==void 0&&o.size>S.maxUploadBytes;return(0,P.jsxs)(`div`,{className:`space-y-3 rounded-lg border border-border bg-background/70 p-3`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-start justify-between gap-2`,children:[(0,P.jsxs)(`div`,{children:[(0,P.jsx)(`p`,{className:`text-xs font-semibold text-foreground`,children:`Ookla executable`}),(0,P.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:`MultiSpeed accepts one separately obtained Linux amd64 executable and stores it persistently outside the image.`})]}),S?.installed?(0,P.jsxs)(y,{tone:`success`,className:`gap-1`,children:[(0,P.jsx)(x,{className:`h-3 w-3`}),`Installed`]}):null]}),v.isLoading?(0,P.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Checking manual upload support…`}):null,v.error?(0,P.jsx)(`p`,{className:`text-xs text-rose-600 dark:text-rose-400`,children:d(v.error)}):null,S&&!S.uploadEnabled?(0,P.jsxs)(`p`,{className:`text-xs leading-5 text-amber-700 dark:text-amber-300`,children:[S.message,` Set `,(0,P.jsx)(`code`,{children:`APP_ALLOW_OOKLA_BINARY_UPLOAD=true`}),` and use the managed binary path to enable it.`]}):null,S?.uploadEnabled?(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(`input`,{ref:r,className:`sr-only`,type:`file`,"aria-label":`Ookla Speedtest executable`,onChange:e=>{l(e.target.files?.[0]??null),m(!1)}}),(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,P.jsxs)(h,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>r.current?.click(),disabled:b.isPending,children:[(0,P.jsx)(w,{className:`h-3.5 w-3.5`}),`Choose executable`]}),(0,P.jsx)(h,{asChild:!0,size:`sm`,variant:`outline`,children:(0,P.jsxs)(`a`,{href:R,target:`_blank`,rel:`noopener noreferrer`,children:[(0,P.jsx)(c,{className:`h-3.5 w-3.5`}),`Get official Speedtest CLI`]})}),(0,P.jsx)(`span`,{className:`min-w-0 truncate text-xs text-muted-foreground`,children:o?`${o.name} · ${(o.size/1048576).toFixed(1)} MiB`:`Maximum ${(S.maxUploadBytes/1048576).toFixed(0)} MiB`})]}),(0,P.jsxs)(`p`,{className:`text-[11px] leading-5 text-muted-foreground`,children:[`On Ookla's download page, choose the Linux x86_64 archive, extract it, and select the contained `,(0,P.jsx)(`code`,{children:`speedtest`}),` executable here.`]}),C?(0,P.jsxs)(`p`,{role:`alert`,className:`text-xs text-rose-600 dark:text-rose-400`,children:[`The selected file exceeds the `,Math.round(S.maxUploadBytes/1048576),` MiB upload limit.`]}):null,(0,P.jsx)(s,{checked:u,onChange:m,disabled:!o||C||b.isPending,label:`This is an authorized Speedtest by Ookla executable`,description:`I obtained this Linux amd64 file separately, agreed to the current EULA/Terms, reviewed the Privacy Policy, and confirm this device and any network access from multiple devices fit those documents or have separate written Ookla authorization.`}),(0,P.jsxs)(h,{type:`button`,size:`sm`,onClick:()=>o&&b.mutate(o),disabled:!o||!u||C||b.isPending,children:[b.isPending?(0,P.jsx)(p,{}):(0,P.jsx)(w,{className:`h-3.5 w-3.5`}),`Install executable`]}),(0,P.jsx)(`p`,{className:`text-[11px] leading-5 text-muted-foreground`,children:`Because MultiSpeed has no authentication, keep this opt-in upload endpoint limited to a trusted private network.`})]}):null]})}export{M as a,w as c,j as i,S as l,O as n,T as o,A as r,D as s,z as t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/ResultDetailPage-Dj2B-oxr.js b/internal/frontend/dist/assets/ResultDetailPage-DbQ93uA2.js similarity index 91% rename from internal/frontend/dist/assets/ResultDetailPage-Dj2B-oxr.js rename to internal/frontend/dist/assets/ResultDetailPage-DbQ93uA2.js index 7884615..a37b310 100644 --- a/internal/frontend/dist/assets/ResultDetailPage-Dj2B-oxr.js +++ b/internal/frontend/dist/assets/ResultDetailPage-DbQ93uA2.js @@ -1 +1 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,w as i,x as a}from"./query-DWNqGEie.js";import{b as o,d as s,y as c}from"./react-DsKPNe9r.js";import{t as l}from"./MetricCard-GfGMWPS-.js";import{n as u,t as ee}from"./arrow-up-Cm-GZvKO.js";import{t as d}from"./arrow-left-CkoT9SwQ.js";import{n as f,t as te}from"./route-C77_DY7l.js";import{t as p}from"./clock-3-BeKI5fxN.js";import{t as m}from"./external-link-DfzRK7DJ.js";import{t as h}from"./radio-tower-C96Q3yet.js";import{t as ne}from"./trash-2-DJb0gq__.js";import{S as re,T as g,a as ie,f as _,h as v,i as y,j as b,k as x,l as S,m as C,n as w,s as T,t as ae,w as E}from"./index-B84OiuS0.js";import{r as oe}from"./useAppSettings-CeA9vkHH.js";import{t as D}from"./PageHeader-DGX-y_Bi.js";import{i as O,n as k,r as A,t as j}from"./card-DEXft0Cm.js";import{n as M,t as N}from"./EntityBadges-DZDjvrcw.js";import{t as P}from"./dialog-DXkCUNSe.js";var F=e(r(),1),I=n();function L(){let{resultId:e=``}=o(),n=c(),r=i(),{toast:L}=ae(),{bitrate:H,dateTime:U}=oe(),[W,G]=(0,F.useState)(!1),K=t({queryKey:w.result(e),queryFn:()=>E.result(e)}),q=t({queryKey:w.tasks,queryFn:E.tasks}),J=a({mutationFn:()=>E.deleteResult(e),onSuccess:()=>{r.invalidateQueries({queryKey:[`results`]}),L({tone:`success`,title:`Result deleted`}),n(`/results`)},onError:e=>L({tone:`error`,title:`Delete failed`,description:g(e)})});if(K.isLoading||q.isLoading)return(0,I.jsx)(ie,{label:`Loading full result diagnostics…`});let Y=K.error??q.error;if(Y||!K.data)return(0,I.jsx)(y,{error:Y??Error(`Result not found.`),onRetry:()=>void K.refetch()});let X=K.data,Z=q.data?.find(e=>e.id===X.taskId),Q=X.routeValidationSnapshot??{},$=re(X.providerResultUrl),se=(e,t)=>void S(e).then(()=>L({tone:`success`,title:`${t} copied`})).catch(e=>L({tone:`error`,title:`Copy failed`,description:g(e)}));return(0,I.jsxs)(I.Fragment,{children:[(0,I.jsx)(D,{title:Z?.name??`Measurement diagnostics`,description:`${U(X.startedAt,!0)} · ${X.trigger} execution · result ${X.id}`,actions:(0,I.jsxs)(I.Fragment,{children:[(0,I.jsx)(T,{asChild:!0,variant:`outline`,children:(0,I.jsxs)(s,{to:`/results`,children:[(0,I.jsx)(d,{className:`h-4 w-4`}),`Results`]})}),(0,I.jsxs)(T,{variant:`danger`,onClick:()=>G(!0),children:[(0,I.jsx)(ne,{className:`h-4 w-4`}),`Delete`]})]})}),(0,I.jsxs)(`div`,{className:`mb-5 flex flex-wrap items-center gap-2`,children:[(0,I.jsx)(M,{status:X.status}),(0,I.jsx)(N,{provider:X.provider}),Z?(0,I.jsx)(T,{asChild:!0,variant:`ghost`,size:`sm`,children:(0,I.jsxs)(s,{to:`/tasks/${Z.id}/edit`,children:[`Open task`,(0,I.jsx)(m,{className:`h-3.5 w-3.5`})]})}):null,$?(0,I.jsx)(T,{asChild:!0,variant:`ghost`,size:`sm`,children:(0,I.jsxs)(`a`,{href:$,target:`_blank`,rel:`noreferrer`,children:[`Provider result`,(0,I.jsx)(m,{className:`h-3.5 w-3.5`})]})}):null]}),(0,I.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2 xl:grid-cols-4`,children:[(0,I.jsx)(l,{label:`Download`,value:H(X.downloadBitsPerSecond),detail:_(X.downloadBytes),icon:u,tone:`cyan`}),(0,I.jsx)(l,{label:`Upload`,value:H(X.uploadBitsPerSecond),detail:_(X.uploadBytes),icon:ee,tone:`violet`}),(0,I.jsx)(l,{label:`Latency`,value:C(X.latencyMilliseconds),detail:`Jitter ${C(X.jitterMilliseconds)}`,icon:b,tone:`orange`}),(0,I.jsx)(l,{label:`Packet loss`,value:v(X.packetLossPercent),detail:`Duration ${C(X.executionDurationMs)}`,icon:h,tone:X.packetLossPercent&&X.packetLossPercent>1?`rose`:`emerald`})]}),X.sanitizedError?(0,I.jsxs)(j,{className:`mt-5 border-rose-500/25 bg-rose-500/[.055] p-4`,children:[(0,I.jsx)(`p`,{className:`text-xs font-semibold uppercase tracking-wider text-rose-600 dark:text-rose-400`,children:`Failure explanation`}),(0,I.jsx)(`p`,{className:`mt-2 whitespace-pre-wrap text-sm leading-6 text-foreground`,children:X.sanitizedError})]}):null,X.tlsVerificationDisabled?(0,I.jsxs)(j,{className:`mt-5 border-rose-500/25 bg-rose-500/[.055] p-4`,children:[(0,I.jsx)(`p`,{className:`text-xs font-semibold uppercase tracking-wider text-rose-600 dark:text-rose-400`,children:`TLS verification was disabled`}),(0,I.jsx)(`p`,{className:`mt-2 text-sm leading-6 text-foreground`,children:`This measurement trusted an unverified LibreSpeed endpoint certificate. Treat its throughput, latency, and public-IP metadata as security-sensitive diagnostics.`})]}):null,(0,I.jsxs)(`div`,{className:`mt-5 grid gap-5 xl:grid-cols-2`,children:[(0,I.jsxs)(j,{children:[(0,I.jsx)(A,{children:(0,I.jsxs)(O,{className:`flex items-center gap-2`,children:[(0,I.jsx)(x,{className:`h-4 w-4 text-primary`}),`Network path`]})}),(0,I.jsx)(k,{children:(0,I.jsx)(R,{items:[[`Selected interface`,X.selectedInterface],[`Selected source IP`,X.selectedSourceIp],[`Detected public IP`,X.detectedPublicIp],[`IP route profile`,X.routeProfileId??`None`],[`Cloudflare colo`,X.cloudflareColo||`Not applicable`]]})})]}),(0,I.jsxs)(j,{children:[(0,I.jsx)(A,{children:(0,I.jsxs)(O,{className:`flex items-center gap-2`,children:[(0,I.jsx)(h,{className:`h-4 w-4 text-primary`}),`Provider target`]})}),(0,I.jsx)(k,{children:(0,I.jsx)(R,{items:[[`Server ID`,X.serverId],[`Server name`,X.serverName],[`Server host`,X.serverHost],[`Sponsor`,X.serverSponsor],[`Location`,[X.serverLocation,X.serverCountry].filter(Boolean).join(`, `)],[`Provider version`,X.providerVersion]]})})]}),(0,I.jsxs)(j,{children:[(0,I.jsx)(A,{children:(0,I.jsxs)(O,{className:`flex items-center gap-2`,children:[(0,I.jsx)(te,{className:`h-4 w-4 text-primary`}),`Route-validation snapshot`]})}),(0,I.jsx)(k,{children:Object.keys(Q).length?(0,I.jsx)(R,{items:Object.entries(Q).map(([e,t])=>[z(e),B(t)])}):(0,I.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No persisted route-validation snapshot was returned for this result.`})})]}),(0,I.jsxs)(j,{children:[(0,I.jsx)(A,{children:(0,I.jsxs)(O,{className:`flex items-center gap-2`,children:[(0,I.jsx)(p,{className:`h-4 w-4 text-primary`}),`Execution metadata`]})}),(0,I.jsx)(k,{children:(0,I.jsx)(R,{items:[[`Scheduled`,U(X.scheduledAt,!0)],[`Started`,U(X.startedAt,!0)],[`Finished`,U(X.finishedAt,!0)],[`Duration`,C(X.executionDurationMs)],[`Process exit code`,X.processExitCode==null?`Not applicable`:String(X.processExitCode)],[`TLS verification`,X.tlsVerificationDisabled?`Disabled (unsafe)`:`Enabled`],[`Application version`,X.applicationVersion]]})})]})]}),(0,I.jsxs)(j,{className:`mt-5`,children:[(0,I.jsxs)(A,{className:`flex-row items-center justify-between`,children:[(0,I.jsxs)(`div`,{children:[(0,I.jsx)(O,{children:`Sanitized provider response`}),(0,I.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Size-limited diagnostic payload stored with the result`})]}),(0,I.jsxs)(T,{variant:`outline`,size:`sm`,onClick:()=>se(X.rawProviderResponse||`{}`,`Raw provider response`),children:[(0,I.jsx)(f,{className:`h-3.5 w-3.5`}),`Copy`]})]}),(0,I.jsx)(k,{children:(0,I.jsx)(`pre`,{className:`max-h-[32rem] overflow-auto whitespace-pre-wrap break-all rounded-lg border border-border bg-slate-950 p-4 text-xs leading-5 text-slate-200 scrollbar-thin`,children:V(X.rawProviderResponse||`{}`)})})]}),(0,I.jsx)(P,{open:W,onOpenChange:G,title:`Delete this result?`,description:`The measurement, route snapshot, and sanitized provider diagnostics will be permanently removed. The task remains unchanged.`,confirmLabel:`Delete result`,destructive:!0,busy:J.isPending,onConfirm:()=>J.mutate()})]})}function R({items:e}){return(0,I.jsx)(`dl`,{className:`divide-y divide-border`,children:e.map(([e,t])=>(0,I.jsxs)(`div`,{className:`grid gap-1 py-2.5 first:pt-0 last:pb-0 sm:grid-cols-[150px_1fr]`,children:[(0,I.jsx)(`dt`,{className:`text-xs text-muted-foreground`,children:e}),(0,I.jsx)(`dd`,{className:`break-all text-xs font-medium text-foreground`,children:t||`—`})]},e))})}function z(e){return e.replace(/([a-z])([A-Z])/g,`$1 $2`).replace(/_/g,` `).replace(/^./,e=>e.toUpperCase())}function B(e){return e==null?`—`:typeof e==`string`?e:typeof e==`number`||typeof e==`boolean`||typeof e==`bigint`?e.toString():JSON.stringify(e)}function V(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e||`{}`}}export{L as default}; \ No newline at end of file +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,w as i,x as a}from"./query-DWNqGEie.js";import{b as o,d as s,y as c}from"./react-DsKPNe9r.js";import{t as l}from"./MetricCard-CMX2GS_z.js";import{n as u,t as ee}from"./arrow-up-DVV_1I7Y.js";import{t as d}from"./arrow-left-DZUEJBho.js";import{n as f,t as te}from"./route-DtX_GSbk.js";import{t as p}from"./clock-3-DhdKy9KD.js";import{t as m}from"./external-link-4Ueuyx8f.js";import{t as h}from"./radio-tower-D9Z4YmgD.js";import{t as ne}from"./trash-2-DyGHVVq4.js";import{S as re,T as g,a as ie,f as _,h as v,i as y,j as b,k as x,l as S,m as C,n as w,s as T,t as ae,w as E}from"./index-DleN6wGz.js";import{r as oe}from"./useAppSettings-vnP5f96R.js";import{t as D}from"./PageHeader-DGX-y_Bi.js";import{i as O,n as k,r as A,t as j}from"./card-BtXBd7n8.js";import{n as M,t as N}from"./EntityBadges-CM7WeMOS.js";import{t as P}from"./dialog-CT4xHTSq.js";var F=e(r(),1),I=n();function L(){let{resultId:e=``}=o(),n=c(),r=i(),{toast:L}=ae(),{bitrate:H,dateTime:U}=oe(),[W,G]=(0,F.useState)(!1),K=t({queryKey:w.result(e),queryFn:()=>E.result(e)}),q=t({queryKey:w.tasks,queryFn:E.tasks}),J=a({mutationFn:()=>E.deleteResult(e),onSuccess:()=>{r.invalidateQueries({queryKey:[`results`]}),L({tone:`success`,title:`Result deleted`}),n(`/results`)},onError:e=>L({tone:`error`,title:`Delete failed`,description:g(e)})});if(K.isLoading||q.isLoading)return(0,I.jsx)(ie,{label:`Loading full result diagnostics…`});let Y=K.error??q.error;if(Y||!K.data)return(0,I.jsx)(y,{error:Y??Error(`Result not found.`),onRetry:()=>void K.refetch()});let X=K.data,Z=q.data?.find(e=>e.id===X.taskId),Q=X.routeValidationSnapshot??{},$=re(X.providerResultUrl),se=(e,t)=>void S(e).then(()=>L({tone:`success`,title:`${t} copied`})).catch(e=>L({tone:`error`,title:`Copy failed`,description:g(e)}));return(0,I.jsxs)(I.Fragment,{children:[(0,I.jsx)(D,{title:Z?.name??`Measurement diagnostics`,description:`${U(X.startedAt,!0)} · ${X.trigger} execution · result ${X.id}`,actions:(0,I.jsxs)(I.Fragment,{children:[(0,I.jsx)(T,{asChild:!0,variant:`outline`,children:(0,I.jsxs)(s,{to:`/results`,children:[(0,I.jsx)(d,{className:`h-4 w-4`}),`Results`]})}),(0,I.jsxs)(T,{variant:`danger`,onClick:()=>G(!0),children:[(0,I.jsx)(ne,{className:`h-4 w-4`}),`Delete`]})]})}),(0,I.jsxs)(`div`,{className:`mb-5 flex flex-wrap items-center gap-2`,children:[(0,I.jsx)(M,{status:X.status}),(0,I.jsx)(N,{provider:X.provider}),Z?(0,I.jsx)(T,{asChild:!0,variant:`ghost`,size:`sm`,children:(0,I.jsxs)(s,{to:`/tasks/${Z.id}/edit`,children:[`Open task`,(0,I.jsx)(m,{className:`h-3.5 w-3.5`})]})}):null,$?(0,I.jsx)(T,{asChild:!0,variant:`ghost`,size:`sm`,children:(0,I.jsxs)(`a`,{href:$,target:`_blank`,rel:`noreferrer`,children:[`Provider result`,(0,I.jsx)(m,{className:`h-3.5 w-3.5`})]})}):null]}),(0,I.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2 xl:grid-cols-4`,children:[(0,I.jsx)(l,{label:`Download`,value:H(X.downloadBitsPerSecond),detail:_(X.downloadBytes),icon:u,tone:`cyan`}),(0,I.jsx)(l,{label:`Upload`,value:H(X.uploadBitsPerSecond),detail:_(X.uploadBytes),icon:ee,tone:`violet`}),(0,I.jsx)(l,{label:`Latency`,value:C(X.latencyMilliseconds),detail:`Jitter ${C(X.jitterMilliseconds)}`,icon:b,tone:`orange`}),(0,I.jsx)(l,{label:`Packet loss`,value:v(X.packetLossPercent),detail:`Duration ${C(X.executionDurationMs)}`,icon:h,tone:X.packetLossPercent&&X.packetLossPercent>1?`rose`:`emerald`})]}),X.sanitizedError?(0,I.jsxs)(j,{className:`mt-5 border-rose-500/25 bg-rose-500/[.055] p-4`,children:[(0,I.jsx)(`p`,{className:`text-xs font-semibold uppercase tracking-wider text-rose-600 dark:text-rose-400`,children:`Failure explanation`}),(0,I.jsx)(`p`,{className:`mt-2 whitespace-pre-wrap text-sm leading-6 text-foreground`,children:X.sanitizedError})]}):null,X.tlsVerificationDisabled?(0,I.jsxs)(j,{className:`mt-5 border-rose-500/25 bg-rose-500/[.055] p-4`,children:[(0,I.jsx)(`p`,{className:`text-xs font-semibold uppercase tracking-wider text-rose-600 dark:text-rose-400`,children:`TLS verification was disabled`}),(0,I.jsx)(`p`,{className:`mt-2 text-sm leading-6 text-foreground`,children:`This measurement trusted an unverified LibreSpeed endpoint certificate. Treat its throughput, latency, and public-IP metadata as security-sensitive diagnostics.`})]}):null,(0,I.jsxs)(`div`,{className:`mt-5 grid gap-5 xl:grid-cols-2`,children:[(0,I.jsxs)(j,{children:[(0,I.jsx)(A,{children:(0,I.jsxs)(O,{className:`flex items-center gap-2`,children:[(0,I.jsx)(x,{className:`h-4 w-4 text-primary`}),`Network path`]})}),(0,I.jsx)(k,{children:(0,I.jsx)(R,{items:[[`Selected interface`,X.selectedInterface],[`Selected source IP`,X.selectedSourceIp],[`Detected public IP`,X.detectedPublicIp],[`IP route profile`,X.routeProfileId??`None`],[`Cloudflare colo`,X.cloudflareColo||`Not applicable`]]})})]}),(0,I.jsxs)(j,{children:[(0,I.jsx)(A,{children:(0,I.jsxs)(O,{className:`flex items-center gap-2`,children:[(0,I.jsx)(h,{className:`h-4 w-4 text-primary`}),`Provider target`]})}),(0,I.jsx)(k,{children:(0,I.jsx)(R,{items:[[`Server ID`,X.serverId],[`Server name`,X.serverName],[`Server host`,X.serverHost],[`Sponsor`,X.serverSponsor],[`Location`,[X.serverLocation,X.serverCountry].filter(Boolean).join(`, `)],[`Provider version`,X.providerVersion]]})})]}),(0,I.jsxs)(j,{children:[(0,I.jsx)(A,{children:(0,I.jsxs)(O,{className:`flex items-center gap-2`,children:[(0,I.jsx)(te,{className:`h-4 w-4 text-primary`}),`Route-validation snapshot`]})}),(0,I.jsx)(k,{children:Object.keys(Q).length?(0,I.jsx)(R,{items:Object.entries(Q).map(([e,t])=>[z(e),B(t)])}):(0,I.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No persisted route-validation snapshot was returned for this result.`})})]}),(0,I.jsxs)(j,{children:[(0,I.jsx)(A,{children:(0,I.jsxs)(O,{className:`flex items-center gap-2`,children:[(0,I.jsx)(p,{className:`h-4 w-4 text-primary`}),`Execution metadata`]})}),(0,I.jsx)(k,{children:(0,I.jsx)(R,{items:[[`Scheduled`,U(X.scheduledAt,!0)],[`Started`,U(X.startedAt,!0)],[`Finished`,U(X.finishedAt,!0)],[`Duration`,C(X.executionDurationMs)],[`Process exit code`,X.processExitCode==null?`Not applicable`:String(X.processExitCode)],[`TLS verification`,X.tlsVerificationDisabled?`Disabled (unsafe)`:`Enabled`],[`Application version`,X.applicationVersion]]})})]})]}),(0,I.jsxs)(j,{className:`mt-5`,children:[(0,I.jsxs)(A,{className:`flex-row items-center justify-between`,children:[(0,I.jsxs)(`div`,{children:[(0,I.jsx)(O,{children:`Sanitized provider response`}),(0,I.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Size-limited diagnostic payload stored with the result`})]}),(0,I.jsxs)(T,{variant:`outline`,size:`sm`,onClick:()=>se(X.rawProviderResponse||`{}`,`Raw provider response`),children:[(0,I.jsx)(f,{className:`h-3.5 w-3.5`}),`Copy`]})]}),(0,I.jsx)(k,{children:(0,I.jsx)(`pre`,{className:`max-h-[32rem] overflow-auto whitespace-pre-wrap break-all rounded-lg border border-border bg-slate-950 p-4 text-xs leading-5 text-slate-200 scrollbar-thin`,children:V(X.rawProviderResponse||`{}`)})})]}),(0,I.jsx)(P,{open:W,onOpenChange:G,title:`Delete this result?`,description:`The measurement, route snapshot, and sanitized provider diagnostics will be permanently removed. The task remains unchanged.`,confirmLabel:`Delete result`,destructive:!0,busy:J.isPending,onConfirm:()=>J.mutate()})]})}function R({items:e}){return(0,I.jsx)(`dl`,{className:`divide-y divide-border`,children:e.map(([e,t])=>(0,I.jsxs)(`div`,{className:`grid gap-1 py-2.5 first:pt-0 last:pb-0 sm:grid-cols-[150px_1fr]`,children:[(0,I.jsx)(`dt`,{className:`text-xs text-muted-foreground`,children:e}),(0,I.jsx)(`dd`,{className:`break-all text-xs font-medium text-foreground`,children:t||`—`})]},e))})}function z(e){return e.replace(/([a-z])([A-Z])/g,`$1 $2`).replace(/_/g,` `).replace(/^./,e=>e.toUpperCase())}function B(e){return e==null?`—`:typeof e==`string`?e:typeof e==`number`||typeof e==`boolean`||typeof e==`bigint`?e.toString():JSON.stringify(e)}function V(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e||`{}`}}export{L as default}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/ResultsPage-CwUSS1pP.js b/internal/frontend/dist/assets/ResultsPage-DR4hm0dc.js similarity index 96% rename from internal/frontend/dist/assets/ResultsPage-CwUSS1pP.js rename to internal/frontend/dist/assets/ResultsPage-DR4hm0dc.js index 78764a8..d7515b2 100644 --- a/internal/frontend/dist/assets/ResultsPage-CwUSS1pP.js +++ b/internal/frontend/dist/assets/ResultsPage-DR4hm0dc.js @@ -1 +1 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,b as i,t as ee,w as te,x as ne}from"./query-DWNqGEie.js";import{d as a,p as re}from"./react-DsKPNe9r.js";import{c as o,i as s,r as c}from"./fields-PJGT-Jp7.js";import{t as ie}from"./download-Dj5PHFJw.js";import{t as l}from"./external-link-DfzRK7DJ.js";import{t as ae}from"./funnel-x-BSCtvBiK.js";import{t as oe}from"./trash-2-DJb0gq__.js";import{I as u,T as se,a as ce,h as d,i as le,m as f,n as p,o as ue,r as de,s as m,t as fe,u as pe,v as me,w as h,x as he,y as ge}from"./index-B84OiuS0.js";import{n as _e,r as ve}from"./useAppSettings-CeA9vkHH.js";import{t as ye}from"./PageHeader-DGX-y_Bi.js";import{t as be}from"./card-DEXft0Cm.js";import{n as xe,t as Se}from"./EntityBadges-DZDjvrcw.js";import{t as Ce}from"./dialog-DXkCUNSe.js";import{n as g,r as _,t as v}from"./columnVisibilityFeature-CB2cjdCS.js";var we=u(`chevron-left`,[[`path`,{d:`m15 18-6-6 6-6`,key:`1wnfg3`}]]),Te=u(`chevron-right`,[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]]),y=u(`chevron-up`,[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]),b=e(r(),1),x=n(),Ee=g({columnVisibilityFeature:v}),S=_();function C(){let[e,n]=re(),r=te(),{toast:l}=fe(),{settings:u}=_e(),{bitrate:d,dateTime:g}=ve(),[_,v]=(0,b.useState)(1),[C,Oe]=(0,b.useState)(25),[T,ke]=(0,b.useState)(`startedAt`),[E,D]=(0,b.useState)(`desc`),[O,k]=(0,b.useState)(e.get(`taskId`)??``),[A,j]=(0,b.useState)(e.get(`provider`)??``),[M,N]=(0,b.useState)(e.get(`interfaceName`)??``),[P,F]=(0,b.useState)(e.get(`status`)??``),[I,Ae]=(0,b.useState)(e.get(`from`)??``),[L,R]=(0,b.useState)(e.get(`to`)??``),[z,B]=(0,b.useState)(new Set),[V,je]=(0,b.useState)(new Set),[Me,H]=(0,b.useState)(!1),U={page:_,pageSize:C,sort:T,direction:E,taskId:O,provider:A,interfaceName:M,status:P,from:I?he(I,u.defaultTimezone):``,to:L?ge(L,u.defaultTimezone):``},Ne=JSON.stringify(U),W=t({queryKey:p.results(U),queryFn:()=>h.results(U),placeholderData:e=>e}),G=t({queryKey:p.tasks,queryFn:h.tasks}),K=t({queryKey:p.interfaces,queryFn:()=>h.interfaces()}),q=ne({mutationFn:h.deleteResults,onSuccess:e=>{B(new Set),H(!1),r.invalidateQueries({queryKey:[`results`]}),r.invalidateQueries({queryKey:[`statistics`]}),l({tone:`success`,title:`Results deleted`,description:`${e.deleted} diagnostic record${e.deleted===1?``:`s`} removed.`})},onError:e=>l({tone:`error`,title:`Unable to delete results`,description:se(e)})}),J=(0,b.useMemo)(()=>G.data??[],[G.data]),Y=(0,b.useMemo)(()=>W.data?.items??[],[W.data?.items]),X=Y.length>0&&Y.every(e=>z.has(e.id)),Pe=(0,b.useCallback)(()=>B(e=>{let t=new Set(e);return X?Y.forEach(e=>t.delete(e.id)):Y.forEach(e=>t.add(e.id)),t}),[X,Y]),Fe=(0,b.useCallback)(e=>je(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n}),[]),Z=(0,b.useCallback)(e=>{T===e?D(e=>e===`asc`?`desc`:`asc`):(ke(e),D(`desc`)),v(1)},[T]),Ie=(0,b.useMemo)(()=>S.columns([S.display({id:`select`,header:()=>(0,x.jsx)(`input`,{type:`checkbox`,checked:X,onChange:Pe,"aria-label":`Select all visible results`,className:`h-4 w-4 rounded border-input accent-cyan-500`}),cell:({row:e})=>(0,x.jsx)(`input`,{type:`checkbox`,checked:z.has(e.original.id),onChange:()=>B(t=>{let n=new Set(t);return n.has(e.original.id)?n.delete(e.original.id):n.add(e.original.id),n}),"aria-label":`Select result ${e.original.id}`,className:`h-4 w-4 rounded border-input accent-cyan-500`})}),S.accessor(`startedAt`,{header:()=>(0,x.jsx)(w,{label:`Started`,column:`startedAt`,sort:T,direction:E,onSort:Z}),cell:({getValue:e})=>(0,x.jsxs)(`div`,{className:`whitespace-nowrap`,children:[(0,x.jsx)(`p`,{className:`text-xs font-medium`,children:g(e())}),(0,x.jsxs)(`p`,{className:`mt-0.5 text-[10px] capitalize text-muted-foreground`,children:[g(e(),!0),` · `,Y.find(t=>t.startedAt===e())?.trigger]})]})}),S.accessor(`taskId`,{header:`Task`,cell:({row:e})=>(0,x.jsxs)(`div`,{className:`min-w-40`,children:[(0,x.jsx)(a,{to:`/results/${e.original.id}`,className:`font-semibold text-foreground hover:text-primary hover:underline`,children:J.find(t=>t.id===e.original.taskId)?.name??`Deleted task`}),(0,x.jsx)(`p`,{className:`mt-0.5 text-[10px] text-muted-foreground`,children:e.original.serverName||e.original.cloudflareColo||`Automatic target`})]})}),S.accessor(`status`,{header:`Status`,cell:({getValue:e})=>(0,x.jsx)(xe,{status:e()})}),S.accessor(`provider`,{header:`Provider`,cell:({getValue:e})=>(0,x.jsx)(Se,{provider:e()})}),S.accessor(`selectedInterface`,{header:`WAN path`,cell:({row:e})=>(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`text-xs font-semibold`,children:e.original.selectedInterface||`Pending`}),(0,x.jsx)(`p`,{className:`mt-0.5 max-w-40 truncate font-mono text-[10px] text-muted-foreground`,children:e.original.selectedSourceIp||`Source pending`})]})}),S.accessor(`downloadBitsPerSecond`,{header:()=>(0,x.jsx)(w,{label:`Download`,column:`download`,sort:T,direction:E,onSort:Z}),cell:({getValue:e})=>(0,x.jsx)(`span`,{className:`metric-number whitespace-nowrap text-xs font-semibold`,children:d(e())})}),S.accessor(`uploadBitsPerSecond`,{header:()=>(0,x.jsx)(w,{label:`Upload`,column:`upload`,sort:T,direction:E,onSort:Z}),cell:({getValue:e})=>(0,x.jsx)(`span`,{className:`metric-number whitespace-nowrap text-xs font-semibold`,children:d(e())})}),S.accessor(`latencyMilliseconds`,{header:()=>(0,x.jsx)(w,{label:`Latency`,column:`latency`,sort:T,direction:E,onSort:Z}),cell:({getValue:e})=>(0,x.jsx)(`span`,{className:`metric-number whitespace-nowrap text-xs font-semibold`,children:f(e())})}),S.display({id:`expand`,header:``,cell:({row:e})=>(0,x.jsx)(m,{size:`icon`,variant:`ghost`,"aria-label":`${V.has(e.original.id)?`Hide`:`Show`} diagnostics`,onClick:()=>Fe(e.original.id),children:V.has(e.original.id)?(0,x.jsx)(y,{className:`h-4 w-4`}):(0,x.jsx)(o,{className:`h-4 w-4`})})})]),[X,Z,d,g,E,V,Y,z,T,J,Pe,Fe]),Le=ee({features:Ee,data:Y,columns:Ie});if(W.isLoading||G.isLoading||K.isLoading)return(0,x.jsx)(ce,{label:`Querying persisted measurements…`});let Re=W.error??G.error??K.error;if(Re)return(0,x.jsx)(le,{error:Re,onRetry:()=>{W.refetch(),G.refetch(),K.refetch()}});let ze=()=>{k(``),j(``),N(``),F(``),Ae(``),R(``),v(1),n({})},Q=!!(O||A||M||P||I||L),$=e=>pe(h.exportUrl(e,U),`multispeed-results.${e}`);return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(ye,{title:`Every sample, fully explainable.`,description:`Filter measurement history, inspect route and provider diagnostics, or export exactly the selected result set.`,actions:(0,x.jsxs)(x.Fragment,{children:[(0,x.jsxs)(m,{variant:`outline`,onClick:()=>$(`csv`),children:[(0,x.jsx)(ie,{className:`h-4 w-4`}),`CSV`]}),(0,x.jsxs)(m,{variant:`outline`,onClick:()=>$(`json`),children:[(0,x.jsx)(ie,{className:`h-4 w-4`}),`JSON`]})]})}),(0,x.jsxs)(be,{className:`overflow-hidden`,children:[(0,x.jsxs)(`div`,{className:`grid gap-3 border-b border-border p-4 sm:grid-cols-2 xl:grid-cols-6`,children:[(0,x.jsxs)(s,{value:O,onChange:e=>{k(e.target.value),v(1)},"aria-label":`Filter results by task`,children:[(0,x.jsx)(`option`,{value:``,children:`All tasks`}),J.map(e=>(0,x.jsx)(`option`,{value:e.id,children:e.name},e.id))]}),(0,x.jsxs)(s,{value:A,onChange:e=>{j(e.target.value),v(1)},"aria-label":`Filter results by provider`,children:[(0,x.jsx)(`option`,{value:``,children:`All providers`}),[`ookla`,`librespeed`,`cloudflare`].map(e=>(0,x.jsx)(`option`,{value:e,children:me(e)},e))]}),(0,x.jsxs)(s,{value:M,onChange:e=>{N(e.target.value),v(1)},"aria-label":`Filter results by WAN`,children:[(0,x.jsx)(`option`,{value:``,children:`All WAN paths`}),K.data?.map(e=>(0,x.jsx)(`option`,{value:e.name,children:e.name},e.name))]}),(0,x.jsxs)(s,{value:P,onChange:e=>{F(e.target.value),v(1)},"aria-label":`Filter results by status`,children:[(0,x.jsx)(`option`,{value:``,children:`Any status`}),[`queued`,`validating`,`running`,`succeeded`,`failed`,`skipped`,`cancelled`].map(e=>(0,x.jsxs)(`option`,{value:e,children:[e[0]?.toUpperCase(),e.slice(1)]},e))]}),(0,x.jsx)(c,{type:`date`,value:I,onChange:e=>{Ae(e.target.value),v(1)},"aria-label":`Results from date`}),(0,x.jsxs)(`div`,{className:`flex gap-2`,children:[(0,x.jsx)(c,{type:`date`,value:L,onChange:e=>{R(e.target.value),v(1)},"aria-label":`Results to date`}),(0,x.jsx)(m,{size:`icon`,variant:`ghost`,disabled:!Q,onClick:ze,"aria-label":`Clear result filters`,children:(0,x.jsx)(ae,{className:`h-4 w-4`})})]})]}),z.size>0?(0,x.jsxs)(`div`,{className:`flex items-center justify-between gap-3 border-b border-border bg-primary/[.055] px-4 py-2.5`,children:[(0,x.jsxs)(`p`,{className:`text-xs font-semibold`,children:[z.size,` selected`]}),(0,x.jsxs)(m,{size:`sm`,variant:`danger`,onClick:()=>H(!0),children:[(0,x.jsx)(oe,{className:`h-3.5 w-3.5`}),`Delete selected`]})]}):null,Y.length===0?(0,x.jsx)(de,{title:Q?`No results match these filters`:`No measurements yet`,description:Q?`Expand the date range or clear one or more filters.`:`Completed and failed task runs will appear here with full diagnostics.`,action:Q?(0,x.jsxs)(m,{variant:`outline`,size:`sm`,onClick:ze,children:[(0,x.jsx)(ae,{className:`h-3.5 w-3.5`}),`Clear filters`]}):null}):(0,x.jsx)(`div`,{className:`overflow-x-auto scrollbar-thin`,children:(0,x.jsxs)(`table`,{className:`data-table w-full min-w-[1160px]`,children:[(0,x.jsx)(`thead`,{children:Le.getHeaderGroups().map(e=>(0,x.jsx)(`tr`,{children:e.headers.map(e=>(0,x.jsx)(`th`,{children:e.isPlaceholder?null:i(e.column.columnDef.header,e.getContext())},e.id))},e.id))}),(0,x.jsx)(`tbody`,{children:Le.getRowModel().rows.map(e=>(0,x.jsx)(De,{row:e,expanded:V.has(e.original.id),columnCount:Ie.length},e.id))})]})}),(0,x.jsxs)(`div`,{className:`flex flex-col items-center justify-between gap-3 border-t border-border px-4 py-3 text-xs text-muted-foreground sm:flex-row`,children:[(0,x.jsxs)(`span`,{children:[W.data?.totalItems??0,` results · page `,W.data?.page??_,` of `,Math.max(W.data?.totalPages??1,1)]}),(0,x.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,x.jsxs)(s,{className:`h-8 w-24`,value:C,onChange:e=>{Oe(Number(e.target.value)),v(1)},"aria-label":`Results per page`,children:[(0,x.jsx)(`option`,{value:10,children:`10 / page`}),(0,x.jsx)(`option`,{value:25,children:`25 / page`}),(0,x.jsx)(`option`,{value:50,children:`50 / page`}),(0,x.jsx)(`option`,{value:100,children:`100 / page`})]}),(0,x.jsx)(m,{size:`icon`,variant:`outline`,disabled:_<=1||W.isFetching,onClick:()=>v(e=>e-1),"aria-label":`Previous page`,children:(0,x.jsx)(we,{className:`h-4 w-4`})}),(0,x.jsx)(m,{size:`icon`,variant:`outline`,disabled:_>=(W.data?.totalPages??1)||W.isFetching,onClick:()=>v(e=>e+1),"aria-label":`Next page`,children:(0,x.jsx)(Te,{className:`h-4 w-4`})}),W.isFetching?(0,x.jsx)(ue,{}):null]})]})]}),(0,x.jsx)(`span`,{className:`sr-only`,children:Ne}),(0,x.jsx)(Ce,{open:Me,onOpenChange:H,title:`Delete selected results?`,description:`${z.size} persisted result records and their diagnostics will be permanently deleted. Task definitions are not affected.`,confirmLabel:`Delete results`,destructive:!0,busy:q.isPending,onConfirm:()=>q.mutate([...z])})]})}function w({label:e,column:t,sort:n,direction:r,onSort:i}){return(0,x.jsxs)(`button`,{type:`button`,onClick:()=>i(t),className:`inline-flex items-center gap-1 hover:text-foreground`,children:[e,n===t?r===`asc`?(0,x.jsx)(y,{className:`h-3 w-3`}):(0,x.jsx)(o,{className:`h-3 w-3`}):null]})}function De({row:e,expanded:t,columnCount:n}){let r=e.original;return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(`tr`,{children:e.getVisibleCells().map(e=>(0,x.jsx)(`td`,{children:i(e.column.columnDef.cell,e.getContext())},e.id))}),t?(0,x.jsx)(`tr`,{children:(0,x.jsx)(`td`,{colSpan:n,className:`!bg-muted/25 !p-4`,children:(0,x.jsxs)(`div`,{className:`grid gap-4 lg:grid-cols-[1fr_1fr_auto]`,children:[(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`text-[10px] font-semibold uppercase tracking-wider text-muted-foreground`,children:`Route & execution`}),(0,x.jsxs)(`dl`,{className:`mt-2 grid grid-cols-2 gap-x-4 gap-y-2 text-xs`,children:[(0,x.jsx)(`dt`,{className:`text-muted-foreground`,children:`Public IP`}),(0,x.jsx)(`dd`,{className:`font-mono`,children:r.detectedPublicIp||`—`}),(0,x.jsx)(`dt`,{className:`text-muted-foreground`,children:`Jitter / loss`}),(0,x.jsxs)(`dd`,{children:[f(r.jitterMilliseconds),` / `,d(r.packetLossPercent)]}),(0,x.jsx)(`dt`,{className:`text-muted-foreground`,children:`Duration`}),(0,x.jsx)(`dd`,{children:f(r.executionDurationMs)}),(0,x.jsx)(`dt`,{className:`text-muted-foreground`,children:`Provider version`}),(0,x.jsx)(`dd`,{children:r.providerVersion||`—`}),(0,x.jsx)(`dt`,{className:`text-muted-foreground`,children:`TLS verification`}),(0,x.jsx)(`dd`,{className:r.tlsVerificationDisabled?`font-semibold text-rose-600 dark:text-rose-400`:``,children:r.tlsVerificationDisabled?`Disabled`:`Enabled`})]})]}),(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`text-[10px] font-semibold uppercase tracking-wider text-muted-foreground`,children:`Diagnostic message`}),(0,x.jsx)(`p`,{className:`mt-2 text-xs leading-5 ${r.sanitizedError?`text-rose-600 dark:text-rose-400`:`text-muted-foreground`}`,children:r.sanitizedError||`No provider or route error was recorded.`})]}),(0,x.jsx)(`div`,{children:(0,x.jsx)(m,{size:`sm`,asChild:!0,children:(0,x.jsxs)(a,{to:`/results/${r.id}`,children:[`Full diagnostics`,(0,x.jsx)(l,{className:`h-3.5 w-3.5`})]})})})]})})}):null]})}export{C as default}; \ No newline at end of file +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,b as i,t as ee,w as te,x as ne}from"./query-DWNqGEie.js";import{d as a,p as re}from"./react-DsKPNe9r.js";import{c as o,i as s,r as c}from"./fields-DSX3DSQR.js";import{t as ie}from"./download-CUwygqUa.js";import{t as l}from"./external-link-4Ueuyx8f.js";import{t as ae}from"./funnel-x-DRKN7t7O.js";import{t as oe}from"./trash-2-DyGHVVq4.js";import{I as u,T as se,a as ce,h as d,i as le,m as f,n as p,o as ue,r as de,s as m,t as fe,u as pe,v as me,w as h,x as he,y as ge}from"./index-DleN6wGz.js";import{n as _e,r as ve}from"./useAppSettings-vnP5f96R.js";import{t as ye}from"./PageHeader-DGX-y_Bi.js";import{t as be}from"./card-BtXBd7n8.js";import{n as xe,t as Se}from"./EntityBadges-CM7WeMOS.js";import{t as Ce}from"./dialog-CT4xHTSq.js";import{n as g,r as _,t as v}from"./columnVisibilityFeature-CB2cjdCS.js";var we=u(`chevron-left`,[[`path`,{d:`m15 18-6-6 6-6`,key:`1wnfg3`}]]),Te=u(`chevron-right`,[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]]),y=u(`chevron-up`,[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]),b=e(r(),1),x=n(),Ee=g({columnVisibilityFeature:v}),S=_();function C(){let[e,n]=re(),r=te(),{toast:l}=fe(),{settings:u}=_e(),{bitrate:d,dateTime:g}=ve(),[_,v]=(0,b.useState)(1),[C,Oe]=(0,b.useState)(25),[T,ke]=(0,b.useState)(`startedAt`),[E,D]=(0,b.useState)(`desc`),[O,k]=(0,b.useState)(e.get(`taskId`)??``),[A,j]=(0,b.useState)(e.get(`provider`)??``),[M,N]=(0,b.useState)(e.get(`interfaceName`)??``),[P,F]=(0,b.useState)(e.get(`status`)??``),[I,Ae]=(0,b.useState)(e.get(`from`)??``),[L,R]=(0,b.useState)(e.get(`to`)??``),[z,B]=(0,b.useState)(new Set),[V,je]=(0,b.useState)(new Set),[Me,H]=(0,b.useState)(!1),U={page:_,pageSize:C,sort:T,direction:E,taskId:O,provider:A,interfaceName:M,status:P,from:I?he(I,u.defaultTimezone):``,to:L?ge(L,u.defaultTimezone):``},Ne=JSON.stringify(U),W=t({queryKey:p.results(U),queryFn:()=>h.results(U),placeholderData:e=>e}),G=t({queryKey:p.tasks,queryFn:h.tasks}),K=t({queryKey:p.interfaces,queryFn:()=>h.interfaces()}),q=ne({mutationFn:h.deleteResults,onSuccess:e=>{B(new Set),H(!1),r.invalidateQueries({queryKey:[`results`]}),r.invalidateQueries({queryKey:[`statistics`]}),l({tone:`success`,title:`Results deleted`,description:`${e.deleted} diagnostic record${e.deleted===1?``:`s`} removed.`})},onError:e=>l({tone:`error`,title:`Unable to delete results`,description:se(e)})}),J=(0,b.useMemo)(()=>G.data??[],[G.data]),Y=(0,b.useMemo)(()=>W.data?.items??[],[W.data?.items]),X=Y.length>0&&Y.every(e=>z.has(e.id)),Pe=(0,b.useCallback)(()=>B(e=>{let t=new Set(e);return X?Y.forEach(e=>t.delete(e.id)):Y.forEach(e=>t.add(e.id)),t}),[X,Y]),Fe=(0,b.useCallback)(e=>je(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n}),[]),Z=(0,b.useCallback)(e=>{T===e?D(e=>e===`asc`?`desc`:`asc`):(ke(e),D(`desc`)),v(1)},[T]),Ie=(0,b.useMemo)(()=>S.columns([S.display({id:`select`,header:()=>(0,x.jsx)(`input`,{type:`checkbox`,checked:X,onChange:Pe,"aria-label":`Select all visible results`,className:`h-4 w-4 rounded border-input accent-cyan-500`}),cell:({row:e})=>(0,x.jsx)(`input`,{type:`checkbox`,checked:z.has(e.original.id),onChange:()=>B(t=>{let n=new Set(t);return n.has(e.original.id)?n.delete(e.original.id):n.add(e.original.id),n}),"aria-label":`Select result ${e.original.id}`,className:`h-4 w-4 rounded border-input accent-cyan-500`})}),S.accessor(`startedAt`,{header:()=>(0,x.jsx)(w,{label:`Started`,column:`startedAt`,sort:T,direction:E,onSort:Z}),cell:({getValue:e})=>(0,x.jsxs)(`div`,{className:`whitespace-nowrap`,children:[(0,x.jsx)(`p`,{className:`text-xs font-medium`,children:g(e())}),(0,x.jsxs)(`p`,{className:`mt-0.5 text-[10px] capitalize text-muted-foreground`,children:[g(e(),!0),` · `,Y.find(t=>t.startedAt===e())?.trigger]})]})}),S.accessor(`taskId`,{header:`Task`,cell:({row:e})=>(0,x.jsxs)(`div`,{className:`min-w-40`,children:[(0,x.jsx)(a,{to:`/results/${e.original.id}`,className:`font-semibold text-foreground hover:text-primary hover:underline`,children:J.find(t=>t.id===e.original.taskId)?.name??`Deleted task`}),(0,x.jsx)(`p`,{className:`mt-0.5 text-[10px] text-muted-foreground`,children:e.original.serverName||e.original.cloudflareColo||`Automatic target`})]})}),S.accessor(`status`,{header:`Status`,cell:({getValue:e})=>(0,x.jsx)(xe,{status:e()})}),S.accessor(`provider`,{header:`Provider`,cell:({getValue:e})=>(0,x.jsx)(Se,{provider:e()})}),S.accessor(`selectedInterface`,{header:`WAN path`,cell:({row:e})=>(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`text-xs font-semibold`,children:e.original.selectedInterface||`Pending`}),(0,x.jsx)(`p`,{className:`mt-0.5 max-w-40 truncate font-mono text-[10px] text-muted-foreground`,children:e.original.selectedSourceIp||`Source pending`})]})}),S.accessor(`downloadBitsPerSecond`,{header:()=>(0,x.jsx)(w,{label:`Download`,column:`download`,sort:T,direction:E,onSort:Z}),cell:({getValue:e})=>(0,x.jsx)(`span`,{className:`metric-number whitespace-nowrap text-xs font-semibold`,children:d(e())})}),S.accessor(`uploadBitsPerSecond`,{header:()=>(0,x.jsx)(w,{label:`Upload`,column:`upload`,sort:T,direction:E,onSort:Z}),cell:({getValue:e})=>(0,x.jsx)(`span`,{className:`metric-number whitespace-nowrap text-xs font-semibold`,children:d(e())})}),S.accessor(`latencyMilliseconds`,{header:()=>(0,x.jsx)(w,{label:`Latency`,column:`latency`,sort:T,direction:E,onSort:Z}),cell:({getValue:e})=>(0,x.jsx)(`span`,{className:`metric-number whitespace-nowrap text-xs font-semibold`,children:f(e())})}),S.display({id:`expand`,header:``,cell:({row:e})=>(0,x.jsx)(m,{size:`icon`,variant:`ghost`,"aria-label":`${V.has(e.original.id)?`Hide`:`Show`} diagnostics`,onClick:()=>Fe(e.original.id),children:V.has(e.original.id)?(0,x.jsx)(y,{className:`h-4 w-4`}):(0,x.jsx)(o,{className:`h-4 w-4`})})})]),[X,Z,d,g,E,V,Y,z,T,J,Pe,Fe]),Le=ee({features:Ee,data:Y,columns:Ie});if(W.isLoading||G.isLoading||K.isLoading)return(0,x.jsx)(ce,{label:`Querying persisted measurements…`});let Re=W.error??G.error??K.error;if(Re)return(0,x.jsx)(le,{error:Re,onRetry:()=>{W.refetch(),G.refetch(),K.refetch()}});let ze=()=>{k(``),j(``),N(``),F(``),Ae(``),R(``),v(1),n({})},Q=!!(O||A||M||P||I||L),$=e=>pe(h.exportUrl(e,U),`multispeed-results.${e}`);return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(ye,{title:`Every sample, fully explainable.`,description:`Filter measurement history, inspect route and provider diagnostics, or export exactly the selected result set.`,actions:(0,x.jsxs)(x.Fragment,{children:[(0,x.jsxs)(m,{variant:`outline`,onClick:()=>$(`csv`),children:[(0,x.jsx)(ie,{className:`h-4 w-4`}),`CSV`]}),(0,x.jsxs)(m,{variant:`outline`,onClick:()=>$(`json`),children:[(0,x.jsx)(ie,{className:`h-4 w-4`}),`JSON`]})]})}),(0,x.jsxs)(be,{className:`overflow-hidden`,children:[(0,x.jsxs)(`div`,{className:`grid gap-3 border-b border-border p-4 sm:grid-cols-2 xl:grid-cols-6`,children:[(0,x.jsxs)(s,{value:O,onChange:e=>{k(e.target.value),v(1)},"aria-label":`Filter results by task`,children:[(0,x.jsx)(`option`,{value:``,children:`All tasks`}),J.map(e=>(0,x.jsx)(`option`,{value:e.id,children:e.name},e.id))]}),(0,x.jsxs)(s,{value:A,onChange:e=>{j(e.target.value),v(1)},"aria-label":`Filter results by provider`,children:[(0,x.jsx)(`option`,{value:``,children:`All providers`}),[`ookla`,`librespeed`,`cloudflare`].map(e=>(0,x.jsx)(`option`,{value:e,children:me(e)},e))]}),(0,x.jsxs)(s,{value:M,onChange:e=>{N(e.target.value),v(1)},"aria-label":`Filter results by WAN`,children:[(0,x.jsx)(`option`,{value:``,children:`All WAN paths`}),K.data?.map(e=>(0,x.jsx)(`option`,{value:e.name,children:e.name},e.name))]}),(0,x.jsxs)(s,{value:P,onChange:e=>{F(e.target.value),v(1)},"aria-label":`Filter results by status`,children:[(0,x.jsx)(`option`,{value:``,children:`Any status`}),[`queued`,`validating`,`running`,`succeeded`,`failed`,`skipped`,`cancelled`].map(e=>(0,x.jsxs)(`option`,{value:e,children:[e[0]?.toUpperCase(),e.slice(1)]},e))]}),(0,x.jsx)(c,{type:`date`,value:I,onChange:e=>{Ae(e.target.value),v(1)},"aria-label":`Results from date`}),(0,x.jsxs)(`div`,{className:`flex gap-2`,children:[(0,x.jsx)(c,{type:`date`,value:L,onChange:e=>{R(e.target.value),v(1)},"aria-label":`Results to date`}),(0,x.jsx)(m,{size:`icon`,variant:`ghost`,disabled:!Q,onClick:ze,"aria-label":`Clear result filters`,children:(0,x.jsx)(ae,{className:`h-4 w-4`})})]})]}),z.size>0?(0,x.jsxs)(`div`,{className:`flex items-center justify-between gap-3 border-b border-border bg-primary/[.055] px-4 py-2.5`,children:[(0,x.jsxs)(`p`,{className:`text-xs font-semibold`,children:[z.size,` selected`]}),(0,x.jsxs)(m,{size:`sm`,variant:`danger`,onClick:()=>H(!0),children:[(0,x.jsx)(oe,{className:`h-3.5 w-3.5`}),`Delete selected`]})]}):null,Y.length===0?(0,x.jsx)(de,{title:Q?`No results match these filters`:`No measurements yet`,description:Q?`Expand the date range or clear one or more filters.`:`Completed and failed task runs will appear here with full diagnostics.`,action:Q?(0,x.jsxs)(m,{variant:`outline`,size:`sm`,onClick:ze,children:[(0,x.jsx)(ae,{className:`h-3.5 w-3.5`}),`Clear filters`]}):null}):(0,x.jsx)(`div`,{className:`overflow-x-auto scrollbar-thin`,children:(0,x.jsxs)(`table`,{className:`data-table w-full min-w-[1160px]`,children:[(0,x.jsx)(`thead`,{children:Le.getHeaderGroups().map(e=>(0,x.jsx)(`tr`,{children:e.headers.map(e=>(0,x.jsx)(`th`,{children:e.isPlaceholder?null:i(e.column.columnDef.header,e.getContext())},e.id))},e.id))}),(0,x.jsx)(`tbody`,{children:Le.getRowModel().rows.map(e=>(0,x.jsx)(De,{row:e,expanded:V.has(e.original.id),columnCount:Ie.length},e.id))})]})}),(0,x.jsxs)(`div`,{className:`flex flex-col items-center justify-between gap-3 border-t border-border px-4 py-3 text-xs text-muted-foreground sm:flex-row`,children:[(0,x.jsxs)(`span`,{children:[W.data?.totalItems??0,` results · page `,W.data?.page??_,` of `,Math.max(W.data?.totalPages??1,1)]}),(0,x.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,x.jsxs)(s,{className:`h-8 w-24`,value:C,onChange:e=>{Oe(Number(e.target.value)),v(1)},"aria-label":`Results per page`,children:[(0,x.jsx)(`option`,{value:10,children:`10 / page`}),(0,x.jsx)(`option`,{value:25,children:`25 / page`}),(0,x.jsx)(`option`,{value:50,children:`50 / page`}),(0,x.jsx)(`option`,{value:100,children:`100 / page`})]}),(0,x.jsx)(m,{size:`icon`,variant:`outline`,disabled:_<=1||W.isFetching,onClick:()=>v(e=>e-1),"aria-label":`Previous page`,children:(0,x.jsx)(we,{className:`h-4 w-4`})}),(0,x.jsx)(m,{size:`icon`,variant:`outline`,disabled:_>=(W.data?.totalPages??1)||W.isFetching,onClick:()=>v(e=>e+1),"aria-label":`Next page`,children:(0,x.jsx)(Te,{className:`h-4 w-4`})}),W.isFetching?(0,x.jsx)(ue,{}):null]})]})]}),(0,x.jsx)(`span`,{className:`sr-only`,children:Ne}),(0,x.jsx)(Ce,{open:Me,onOpenChange:H,title:`Delete selected results?`,description:`${z.size} persisted result records and their diagnostics will be permanently deleted. Task definitions are not affected.`,confirmLabel:`Delete results`,destructive:!0,busy:q.isPending,onConfirm:()=>q.mutate([...z])})]})}function w({label:e,column:t,sort:n,direction:r,onSort:i}){return(0,x.jsxs)(`button`,{type:`button`,onClick:()=>i(t),className:`inline-flex items-center gap-1 hover:text-foreground`,children:[e,n===t?r===`asc`?(0,x.jsx)(y,{className:`h-3 w-3`}):(0,x.jsx)(o,{className:`h-3 w-3`}):null]})}function De({row:e,expanded:t,columnCount:n}){let r=e.original;return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(`tr`,{children:e.getVisibleCells().map(e=>(0,x.jsx)(`td`,{children:i(e.column.columnDef.cell,e.getContext())},e.id))}),t?(0,x.jsx)(`tr`,{children:(0,x.jsx)(`td`,{colSpan:n,className:`!bg-muted/25 !p-4`,children:(0,x.jsxs)(`div`,{className:`grid gap-4 lg:grid-cols-[1fr_1fr_auto]`,children:[(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`text-[10px] font-semibold uppercase tracking-wider text-muted-foreground`,children:`Route & execution`}),(0,x.jsxs)(`dl`,{className:`mt-2 grid grid-cols-2 gap-x-4 gap-y-2 text-xs`,children:[(0,x.jsx)(`dt`,{className:`text-muted-foreground`,children:`Public IP`}),(0,x.jsx)(`dd`,{className:`font-mono`,children:r.detectedPublicIp||`—`}),(0,x.jsx)(`dt`,{className:`text-muted-foreground`,children:`Jitter / loss`}),(0,x.jsxs)(`dd`,{children:[f(r.jitterMilliseconds),` / `,d(r.packetLossPercent)]}),(0,x.jsx)(`dt`,{className:`text-muted-foreground`,children:`Duration`}),(0,x.jsx)(`dd`,{children:f(r.executionDurationMs)}),(0,x.jsx)(`dt`,{className:`text-muted-foreground`,children:`Provider version`}),(0,x.jsx)(`dd`,{children:r.providerVersion||`—`}),(0,x.jsx)(`dt`,{className:`text-muted-foreground`,children:`TLS verification`}),(0,x.jsx)(`dd`,{className:r.tlsVerificationDisabled?`font-semibold text-rose-600 dark:text-rose-400`:``,children:r.tlsVerificationDisabled?`Disabled`:`Enabled`})]})]}),(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`text-[10px] font-semibold uppercase tracking-wider text-muted-foreground`,children:`Diagnostic message`}),(0,x.jsx)(`p`,{className:`mt-2 text-xs leading-5 ${r.sanitizedError?`text-rose-600 dark:text-rose-400`:`text-muted-foreground`}`,children:r.sanitizedError||`No provider or route error was recorded.`})]}),(0,x.jsx)(`div`,{children:(0,x.jsx)(m,{size:`sm`,asChild:!0,children:(0,x.jsxs)(a,{to:`/results/${r.id}`,children:[`Full diagnostics`,(0,x.jsx)(l,{className:`h-3.5 w-3.5`})]})})})]})})}):null]})}export{C as default}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/SettingsPage-BK6TpFP7.js b/internal/frontend/dist/assets/SettingsPage-BK6TpFP7.js deleted file mode 100644 index 93362bb..0000000 --- a/internal/frontend/dist/assets/SettingsPage-BK6TpFP7.js +++ /dev/null @@ -1 +0,0 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,w as ee,x as i}from"./query-DWNqGEie.js";import{d as te}from"./react-DsKPNe9r.js";import{i as a,n as o,r as s,t as c}from"./fields-PJGT-Jp7.js";import{t as l}from"./database-BRZrCBcR.js";import{t as u}from"./download-Dj5PHFJw.js";import{c as ne,i as d,l as f,r as p,s as m,t as h}from"./OoklaEulaAcceptance-B4WQoWrJ.js";import{t as g}from"./shield-alert-ma0N2JAc.js";import{t as _}from"./trash-2-DJb0gq__.js";import{I as v,T as y,a as b,i as x,j as re,k as ie,n as S,o as C,s as w,t as ae,w as T}from"./index-B84OiuS0.js";import{t as oe}from"./useAppSettings-CeA9vkHH.js";import{t as se}from"./PageHeader-DGX-y_Bi.js";import{i as E,n as D,r as O,t as k}from"./card-DEXft0Cm.js";import{a as A,c as j,i as M,l as N,n as P,r as F,s as I,t as L,u as R}from"./schemas-DaFWBMkt.js";import{t as z}from"./dialog-DXkCUNSe.js";var B=v(`archive`,[[`rect`,{width:`20`,height:`5`,x:`2`,y:`3`,rx:`1`,key:`1wp1u1`}],[`path`,{d:`M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8`,key:`1s80jp`}],[`path`,{d:`M10 12h4`,key:`a56b0p`}]]),ce=v(`file-braces`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1`,key:`1oajmo`}],[`path`,{d:`M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1`,key:`mpwhp6`}]]),le=v(`scale`,[[`path`,{d:`M12 3v18`,key:`108xh3`}],[`path`,{d:`m19 8 3 8a5 5 0 0 1-6 0zV7`,key:`zcdpyk`}],[`path`,{d:`M3 7h1a17 17 0 0 0 8-2 17 17 0 0 0 8 2h1`,key:`1yorad`}],[`path`,{d:`m5 8 3 8a5 5 0 0 1-6 0zV7`,key:`eua70x`}],[`path`,{d:`M7 21h10`,key:`1b0cd5`}]]),ue=v(`timer-reset`,[[`path`,{d:`M10 2h4`,key:`n1abiw`}],[`path`,{d:`M12 14v-4`,key:`1evpnu`}],[`path`,{d:`M4 13a8 8 0 0 1 8-7 8 8 0 1 1-5.3 14L4 17.6`,key:`1ts96g`}],[`path`,{d:`M9 17H4v5`,key:`8t5av`}]]),V=e(r(),1),H=n(),de=M({displayUnits:L([`bits`,`bytes`]),defaultTimezone:A().min(1).refine(d,`Select a valid IANA timezone.`),globalConcurrency:F().int().min(1).max(16),allowSeparateWanConcurrency:P(),retentionMode:L([`forever`,`days`,`months`]),retentionValue:F().int().min(0).max(3650),defaultChartRange:A().min(1),interfaceRefreshIntervalSeconds:F().int().min(5).max(3600),defaultTaskTimeoutSeconds:F().int().min(5).max(3600),databaseMaintenanceSchedule:A().refine(m,`Enter a valid five-field cron expression.`),ooklaEulaAccepted:P(),ooklaEulaAcceptedAt:A().nullable(),ooklaEulaVersion:A(),ooklaEulaCurrentVersion:A(),ooklaEulaEffectiveAccepted:P(),ooklaEulaAcceptanceSource:L([`none`,`persisted`,`environment`])}).superRefine((e,t)=>{e.retentionMode!==`forever`&&e.retentionValue<1&&t.addIssue({code:`custom`,path:[`retentionValue`],message:`Enter a positive retention period.`})}),fe=1<<20;function U(e){let t;try{t=JSON.parse(e)}catch{throw Error(`The selected file is not valid JSON.`)}if(typeof t!=`object`||!t||Array.isArray(t))throw Error(`The selected file does not contain a configuration object.`);let n=t;if(n.format!==`multispeed-config`||n.version!==1)throw Error(`The selected file is not a supported MultiSpeed configuration export.`);if(typeof n.exportedAt!=`string`||typeof n.settings!=`object`||n.settings===null||!Array.isArray(n.tasks)||!Array.isArray(n.routeProfiles))throw Error(`The selected configuration is incomplete.`);return t}function W(e,t){let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,document.body.append(r),r.click(),r.remove(),URL.revokeObjectURL(n)}function G(){let e=ee(),{toast:n}=ae(),[r,d]=(0,V.useState)(!1),[m,v]=(0,V.useState)(null),A=(0,V.useRef)(null),M=t({queryKey:S.settings,queryFn:T.settings}),P=N({resolver:I(de),defaultValues:oe,mode:`onBlur`});(0,V.useEffect)(()=>{M.data&&P.reset(M.data)},[P,M.data]);let F=i({mutationFn:T.updateSettings,onSuccess:t=>{P.reset(t),e.invalidateQueries({queryKey:S.settings}),n({tone:`success`,title:`Settings saved`,description:`Scheduler and maintenance defaults were updated.`})},onError:e=>n({tone:`error`,title:`Unable to save settings`,description:y(e)})}),L=i({mutationFn:T.backup,onSuccess:e=>{W(e.blob,e.filename),n({tone:`success`,title:`Consistent backup created`,description:`${e.filename} includes committed WAL data.`})},onError:e=>n({tone:`error`,title:`Backup failed`,description:y(e)})}),G=i({mutationFn:T.exportConfiguration,onSuccess:e=>{W(e.blob,e.filename),n({tone:`success`,title:`Configuration exported`,description:`${e.filename} contains settings, tasks, and route profiles.`})},onError:e=>n({tone:`error`,title:`Export failed`,description:y(e)})}),K=i({mutationFn:e=>T.importConfiguration(e),onSuccess:async t=>{v(null),await Promise.all([e.invalidateQueries({queryKey:S.settings}),e.invalidateQueries({queryKey:S.tasks}),e.invalidateQueries({queryKey:S.routes}),e.invalidateQueries({queryKey:S.system})]),n({tone:`success`,title:`Configuration imported`,description:`${t.taskCount} tasks and ${t.routeProfileCount} route profiles restored. Results and the Ookla terms acknowledgement were preserved.`})},onError:e=>n({tone:`error`,title:`Import failed`,description:y(e)})}),q=i({mutationFn:()=>T.cleanupResults(),onSuccess:t=>{d(!1),e.invalidateQueries({queryKey:[`results`]}),e.invalidateQueries({queryKey:[`statistics`]}),n({tone:`success`,title:`Retention cleanup complete`,description:`${t.deletedResults??t.deleted??0} expired results removed in bounded batches.`})},onError:e=>n({tone:`error`,title:`Cleanup failed`,description:y(e)})}),{register:J,control:Y,formState:{errors:X,isDirty:Z}}=P,Q=R({control:Y,name:`retentionMode`}),$=R({control:Y,name:`defaultTimezone`}),pe=async e=>{let t=e.currentTarget,r=t.files?.[0];if(r)try{if(r.size>fe)throw Error(`Configuration files must not exceed 1 MiB.`);v({document:U(await r.text()),filename:r.name})}catch(e){n({tone:`error`,title:`Configuration file rejected`,description:y(e)})}finally{t.value=``}};return M.isLoading?(0,H.jsx)(b,{label:`Loading runtime defaults…`}):M.error?(0,H.jsx)(x,{error:M.error,onRetry:()=>void M.refetch()}):(0,H.jsxs)(`form`,{onSubmit:P.handleSubmit(e=>F.mutate(e)),children:[(0,H.jsx)(se,{title:`Safe operational defaults.`,description:`Tune new-task defaults, concurrency, retention, interface discovery, and database maintenance.`,actions:(0,H.jsxs)(w,{type:`submit`,disabled:F.isPending||!Z,children:[F.isPending?(0,H.jsx)(C,{}):(0,H.jsx)(f,{className:`h-4 w-4`}),`Save settings`]})}),(0,H.jsxs)(`div`,{className:`mb-5 flex gap-3 rounded-xl border border-rose-500/25 bg-rose-500/[.055] p-4`,children:[(0,H.jsx)(g,{className:`mt-0.5 h-5 w-5 shrink-0 text-rose-500`}),(0,H.jsxs)(`div`,{children:[(0,H.jsx)(`p`,{className:`text-sm font-semibold`,children:`Trusted network only`}),(0,H.jsx)(`p`,{className:`mt-1 text-xs leading-5 text-muted-foreground`,children:`MultiSpeed does not include authentication and must only be exposed to trusted networks unless protected by an authenticating reverse proxy.`})]})]}),(0,H.jsxs)(`div`,{className:`grid gap-5 xl:grid-cols-2`,children:[(0,H.jsxs)(k,{children:[(0,H.jsxs)(O,{children:[(0,H.jsxs)(E,{className:`flex items-center gap-2`,children:[(0,H.jsx)(re,{className:`h-4 w-4 text-primary`}),`Display & task defaults`]}),(0,H.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Applied to new tasks and reporting views`})]}),(0,H.jsxs)(D,{className:`space-y-4`,children:[(0,H.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,H.jsx)(o,{label:`Display units`,error:X.displayUnits?.message,children:(0,H.jsxs)(a,{...J(`displayUnits`),children:[(0,H.jsx)(`option`,{value:`bits`,children:`Bits per second`}),(0,H.jsx)(`option`,{value:`bytes`,children:`Bytes per second`})]})}),(0,H.jsx)(o,{label:`Default chart range`,error:X.defaultChartRange?.message,children:(0,H.jsxs)(a,{...J(`defaultChartRange`),children:[(0,H.jsx)(`option`,{value:`24h`,children:`Last 24 hours`}),(0,H.jsx)(`option`,{value:`7d`,children:`Last 7 days`}),(0,H.jsx)(`option`,{value:`30d`,children:`Last 30 days`}),(0,H.jsx)(`option`,{value:`90d`,children:`Last 90 days`})]})})]}),(0,H.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,H.jsx)(o,{label:`Default timezone`,error:X.defaultTimezone?.message,children:(0,H.jsxs)(a,{...J(`defaultTimezone`),children:[p.includes($)?null:(0,H.jsx)(`option`,{value:$,children:$}),p.map(e=>(0,H.jsx)(`option`,{value:e,children:e},e))]})}),(0,H.jsx)(o,{label:`Default task timeout`,hint:`Seconds`,error:X.defaultTaskTimeoutSeconds?.message,children:(0,H.jsx)(s,{type:`number`,min:5,max:3600,...J(`defaultTaskTimeoutSeconds`,{valueAsNumber:!0})})})]})]})]}),(0,H.jsxs)(k,{children:[(0,H.jsxs)(O,{children:[(0,H.jsxs)(E,{className:`flex items-center gap-2`,children:[(0,H.jsx)(ie,{className:`h-4 w-4 text-primary`}),`Concurrency & discovery`]}),(0,H.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`The global concurrency default is one; same-WAN tests never overlap`})]}),(0,H.jsxs)(D,{className:`space-y-4`,children:[(0,H.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,H.jsx)(o,{label:`Global concurrency`,hint:`1–16`,error:X.globalConcurrency?.message,children:(0,H.jsx)(s,{type:`number`,min:1,max:16,...J(`globalConcurrency`,{valueAsNumber:!0})})}),(0,H.jsx)(o,{label:`Interface refresh interval`,hint:`Seconds`,error:X.interfaceRefreshIntervalSeconds?.message,children:(0,H.jsx)(s,{type:`number`,min:5,max:3600,...J(`interfaceRefreshIntervalSeconds`,{valueAsNumber:!0})})})]}),(0,H.jsx)(j,{name:`allowSeparateWanConcurrency`,control:Y,render:({field:e})=>(0,H.jsx)(c,{checked:e.value,onChange:e.onChange,label:`Allow concurrency on separate WANs`,description:`Only tasks with distinct interface/source locks may execute together, bounded by global concurrency.`})})]})]}),(0,H.jsxs)(k,{id:`ookla-eula`,className:`xl:col-span-2`,children:[(0,H.jsxs)(O,{children:[(0,H.jsxs)(E,{className:`flex items-center gap-2`,children:[(0,H.jsx)(le,{className:`h-4 w-4 text-primary`}),`Ookla provider terms & authorization`]}),(0,H.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Technical acknowledgement and CLI flag authorization, independent from installation and any separate license permission`})]}),(0,H.jsx)(D,{children:(0,H.jsx)(h,{})})]}),(0,H.jsxs)(k,{className:`xl:col-span-2`,children:[(0,H.jsxs)(O,{children:[(0,H.jsxs)(E,{className:`flex items-center gap-2`,children:[(0,H.jsx)(ce,{className:`h-4 w-4 text-primary`}),`Configuration import & export`]}),(0,H.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Portable JSON for operational settings, tasks, and route profiles`})]}),(0,H.jsxs)(D,{className:`grid gap-3 md:grid-cols-2`,children:[(0,H.jsxs)(`div`,{className:`flex items-start justify-between gap-4 rounded-lg border border-border bg-muted/30 p-3`,children:[(0,H.jsxs)(`div`,{children:[(0,H.jsx)(`p`,{className:`text-xs font-semibold`,children:`Export current configuration`}),(0,H.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:`Downloads a versioned JSON file. Results and the Ookla terms acknowledgement are excluded.`})]}),(0,H.jsxs)(w,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>G.mutate(),disabled:G.isPending,children:[G.isPending?(0,H.jsx)(C,{}):(0,H.jsx)(u,{className:`h-3.5 w-3.5`}),`Export`]})]}),(0,H.jsxs)(`div`,{className:`flex items-start justify-between gap-4 rounded-lg border border-border bg-muted/30 p-3`,children:[(0,H.jsxs)(`div`,{children:[(0,H.jsx)(`p`,{className:`text-xs font-semibold`,children:`Import configuration`}),(0,H.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:`Validates the complete file before atomically replacing saved configuration.`})]}),(0,H.jsx)(`input`,{ref:A,className:`sr-only`,type:`file`,accept:`application/json,.json`,"aria-label":`Configuration file`,onChange:e=>void pe(e)}),(0,H.jsxs)(w,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>A.current?.click(),disabled:K.isPending,children:[(0,H.jsx)(ne,{className:`h-3.5 w-3.5`}),`Choose file`]})]})]})]}),(0,H.jsxs)(k,{children:[(0,H.jsxs)(O,{children:[(0,H.jsxs)(E,{className:`flex items-center gap-2`,children:[(0,H.jsx)(B,{className:`h-4 w-4 text-primary`}),`Retention`]}),(0,H.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Cleanup deletes old results in bounded batches and preserves task definitions`})]}),(0,H.jsxs)(D,{className:`space-y-4`,children:[(0,H.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,H.jsx)(o,{label:`Retention policy`,error:X.retentionMode?.message,children:(0,H.jsxs)(a,{...J(`retentionMode`),children:[(0,H.jsx)(`option`,{value:`forever`,children:`Keep forever`}),(0,H.jsx)(`option`,{value:`days`,children:`Fixed number of days`}),(0,H.jsx)(`option`,{value:`months`,children:`Fixed number of months`})]})}),(0,H.jsx)(o,{label:Q===`months`?`Months to retain`:`Days to retain`,error:X.retentionValue?.message,children:(0,H.jsx)(s,{type:`number`,min:Q===`forever`?0:1,max:3650,disabled:Q===`forever`,...J(`retentionValue`,{valueAsNumber:!0})})})]}),(0,H.jsxs)(`div`,{className:`flex items-start justify-between gap-4 rounded-lg border border-border bg-muted/30 p-3`,children:[(0,H.jsxs)(`div`,{children:[(0,H.jsx)(`p`,{className:`text-xs font-semibold`,children:`Run configured cleanup now`}),(0,H.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:`Applies the saved retention policy in bounded batches. Review individual records separately.`})]}),(0,H.jsxs)(`div`,{className:`flex gap-2`,children:[(0,H.jsx)(w,{asChild:!0,size:`sm`,variant:`ghost`,children:(0,H.jsx)(te,{to:`/results`,children:`Review`})}),(0,H.jsxs)(w,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>d(!0),disabled:Q===`forever`,children:[(0,H.jsx)(_,{className:`h-3.5 w-3.5`}),`Clean up`]})]})]})]})]}),(0,H.jsxs)(k,{children:[(0,H.jsxs)(O,{children:[(0,H.jsxs)(E,{className:`flex items-center gap-2`,children:[(0,H.jsx)(l,{className:`h-4 w-4 text-primary`}),`Database maintenance`]}),(0,H.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`WAL checkpointing and bounded upkeep`})]}),(0,H.jsxs)(D,{className:`space-y-4`,children:[(0,H.jsx)(o,{label:`Maintenance schedule`,hint:`Five-field cron`,error:X.databaseMaintenanceSchedule?.message,children:(0,H.jsx)(s,{className:`font-mono`,...J(`databaseMaintenanceSchedule`),placeholder:`30 3 * * *`})}),(0,H.jsxs)(`div`,{className:`flex items-start justify-between gap-4 rounded-lg border border-border bg-muted/30 p-3`,children:[(0,H.jsxs)(`div`,{children:[(0,H.jsx)(`p`,{className:`text-xs font-semibold`,children:`Consistent SQLite backup`}),(0,H.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:`Creates a transactional backup that includes active WAL data.`})]}),(0,H.jsxs)(w,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>L.mutate(),disabled:L.isPending,children:[L.isPending?(0,H.jsx)(C,{}):(0,H.jsx)(u,{className:`h-3.5 w-3.5`}),`Create backup`]})]})]})]})]}),(0,H.jsxs)(`div`,{className:`mt-5 flex items-center justify-between rounded-xl border border-border bg-card p-4`,children:[(0,H.jsxs)(`p`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,H.jsx)(ue,{className:`h-4 w-4`}),`Changes affect future scheduling and maintenance cycles; active tests are not interrupted.`]}),(0,H.jsxs)(w,{type:`submit`,disabled:F.isPending||!Z,children:[F.isPending?(0,H.jsx)(C,{}):(0,H.jsx)(f,{className:`h-4 w-4`}),`Save settings`]})]}),(0,H.jsx)(z,{open:r,onOpenChange:d,title:`Delete expired results now?`,description:`The currently saved retention policy will be applied immediately. Matching results and their diagnostics cannot be recovered unless you created a backup.`,confirmLabel:`Run cleanup`,destructive:!0,busy:q.isPending,onConfirm:()=>q.mutate()}),(0,H.jsx)(z,{open:m!==null,onOpenChange:e=>{!e&&!K.isPending&&v(null)},title:`Replace the saved configuration?`,description:m?(0,H.jsxs)(`div`,{className:`space-y-2`,children:[(0,H.jsxs)(`p`,{children:[(0,H.jsx)(`span`,{className:`font-medium text-foreground`,children:m.filename}),` contains `,m.document.tasks.length,` tasks and `,m.document.routeProfiles.length,` route profiles.`]}),(0,H.jsx)(`p`,{children:`Saved settings, tasks, and route profiles will be replaced. Measurement results and the Ookla terms acknowledgement remain unchanged. Import is blocked while a test is active.`}),Z?(0,H.jsx)(`p`,{className:`text-amber-500`,children:`Unsaved edits on this page will be discarded.`}):null]}):``,confirmLabel:`Import configuration`,destructive:!0,busy:K.isPending,onConfirm:()=>{m&&K.mutate(m.document)}})]})}export{G as default,U as parseConfigurationPreview}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/SettingsPage-BUWz04bL.js b/internal/frontend/dist/assets/SettingsPage-BUWz04bL.js new file mode 100644 index 0000000..7e8ccf8 --- /dev/null +++ b/internal/frontend/dist/assets/SettingsPage-BUWz04bL.js @@ -0,0 +1 @@ +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,w as ee,x as i}from"./query-DWNqGEie.js";import{d as te}from"./react-DsKPNe9r.js";import{i as a,n as o,r as s,t as c}from"./fields-DSX3DSQR.js";import{t as l}from"./database-DuA3AOvM.js";import{t as u}from"./download-CUwygqUa.js";import{c as ne,i as d,l as f,r as p,s as m,t as h}from"./OoklaEulaAcceptance-DkJ6glh5.js";import{t as g}from"./shield-alert-CznEbFrD.js";import{t as _}from"./trash-2-DyGHVVq4.js";import{I as v,T as y,a as b,i as x,j as re,k as ie,n as S,o as C,s as w,t as ae,w as T}from"./index-DleN6wGz.js";import{t as oe}from"./useAppSettings-vnP5f96R.js";import{t as se}from"./PageHeader-DGX-y_Bi.js";import{i as E,n as D,r as O,t as k}from"./card-BtXBd7n8.js";import{a as A,c as j,i as M,l as N,n as P,r as F,s as I,t as L,u as R}from"./schemas-DaFWBMkt.js";import{t as z}from"./dialog-CT4xHTSq.js";var B=v(`archive`,[[`rect`,{width:`20`,height:`5`,x:`2`,y:`3`,rx:`1`,key:`1wp1u1`}],[`path`,{d:`M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8`,key:`1s80jp`}],[`path`,{d:`M10 12h4`,key:`a56b0p`}]]),ce=v(`file-braces`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1`,key:`1oajmo`}],[`path`,{d:`M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1`,key:`mpwhp6`}]]),le=v(`scale`,[[`path`,{d:`M12 3v18`,key:`108xh3`}],[`path`,{d:`m19 8 3 8a5 5 0 0 1-6 0zV7`,key:`zcdpyk`}],[`path`,{d:`M3 7h1a17 17 0 0 0 8-2 17 17 0 0 0 8 2h1`,key:`1yorad`}],[`path`,{d:`m5 8 3 8a5 5 0 0 1-6 0zV7`,key:`eua70x`}],[`path`,{d:`M7 21h10`,key:`1b0cd5`}]]),ue=v(`timer-reset`,[[`path`,{d:`M10 2h4`,key:`n1abiw`}],[`path`,{d:`M12 14v-4`,key:`1evpnu`}],[`path`,{d:`M4 13a8 8 0 0 1 8-7 8 8 0 1 1-5.3 14L4 17.6`,key:`1ts96g`}],[`path`,{d:`M9 17H4v5`,key:`8t5av`}]]),V=e(r(),1),H=n(),de=M({displayUnits:L([`bits`,`bytes`]),defaultTimezone:A().min(1).refine(d,`Select a valid IANA timezone.`),globalConcurrency:F().int().min(1).max(16),allowSeparateWanConcurrency:P(),retentionMode:L([`forever`,`days`,`months`]),retentionValue:F().int().min(0).max(3650),defaultChartRange:L([`24h`,`7d`,`30d`,`90d`]),interfaceRefreshIntervalSeconds:F().int().min(5).max(3600),defaultTaskTimeoutSeconds:F().int().min(5).max(3600),databaseMaintenanceSchedule:A().refine(m,`Enter a valid five-field cron expression.`),ooklaEulaAccepted:P(),ooklaEulaAcceptedAt:A().nullable(),ooklaEulaVersion:A(),ooklaEulaCurrentVersion:A(),ooklaEulaEffectiveAccepted:P(),ooklaEulaAcceptanceSource:L([`none`,`persisted`,`environment`])}).superRefine((e,t)=>{e.retentionMode!==`forever`&&e.retentionValue<1&&t.addIssue({code:`custom`,path:[`retentionValue`],message:`Enter a positive retention period.`})}),fe=1<<20;function U(e){let t;try{t=JSON.parse(e)}catch{throw Error(`The selected file is not valid JSON.`)}if(typeof t!=`object`||!t||Array.isArray(t))throw Error(`The selected file does not contain a configuration object.`);let n=t;if(n.format!==`multispeed-config`||n.version!==1)throw Error(`The selected file is not a supported MultiSpeed configuration export.`);if(typeof n.exportedAt!=`string`||typeof n.settings!=`object`||n.settings===null||!Array.isArray(n.tasks)||!Array.isArray(n.routeProfiles))throw Error(`The selected configuration is incomplete.`);return t}function W(e,t){let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,document.body.append(r),r.click(),r.remove(),URL.revokeObjectURL(n)}function G(){let e=ee(),{toast:n}=ae(),[r,d]=(0,V.useState)(!1),[m,v]=(0,V.useState)(null),A=(0,V.useRef)(null),M=t({queryKey:S.settings,queryFn:T.settings}),P=N({resolver:I(de),defaultValues:oe,mode:`onBlur`});(0,V.useEffect)(()=>{M.data&&P.reset(M.data)},[P,M.data]);let F=i({mutationFn:T.updateSettings,onSuccess:t=>{P.reset(t),e.invalidateQueries({queryKey:S.settings}),n({tone:`success`,title:`Settings saved`,description:`Scheduler and maintenance defaults were updated.`})},onError:e=>n({tone:`error`,title:`Unable to save settings`,description:y(e)})}),L=i({mutationFn:T.backup,onSuccess:e=>{W(e.blob,e.filename),n({tone:`success`,title:`Consistent backup created`,description:`${e.filename} includes committed WAL data.`})},onError:e=>n({tone:`error`,title:`Backup failed`,description:y(e)})}),G=i({mutationFn:T.exportConfiguration,onSuccess:e=>{W(e.blob,e.filename),n({tone:`success`,title:`Configuration exported`,description:`${e.filename} contains settings, tasks, and route profiles.`})},onError:e=>n({tone:`error`,title:`Export failed`,description:y(e)})}),K=i({mutationFn:e=>T.importConfiguration(e),onSuccess:async t=>{v(null),await Promise.all([e.invalidateQueries({queryKey:S.settings}),e.invalidateQueries({queryKey:S.tasks}),e.invalidateQueries({queryKey:S.routes}),e.invalidateQueries({queryKey:S.system})]),n({tone:`success`,title:`Configuration imported`,description:`${t.taskCount} tasks and ${t.routeProfileCount} route profiles restored. Results and the Ookla terms acknowledgement were preserved.`})},onError:e=>n({tone:`error`,title:`Import failed`,description:y(e)})}),q=i({mutationFn:()=>T.cleanupResults(),onSuccess:t=>{d(!1),e.invalidateQueries({queryKey:[`results`]}),e.invalidateQueries({queryKey:[`statistics`]}),n({tone:`success`,title:`Retention cleanup complete`,description:`${t.deletedResults??t.deleted??0} expired results removed in bounded batches.`})},onError:e=>n({tone:`error`,title:`Cleanup failed`,description:y(e)})}),{register:J,control:Y,formState:{errors:X,isDirty:Z}}=P,Q=R({control:Y,name:`retentionMode`}),$=R({control:Y,name:`defaultTimezone`}),pe=async e=>{let t=e.currentTarget,r=t.files?.[0];if(r)try{if(r.size>fe)throw Error(`Configuration files must not exceed 1 MiB.`);v({document:U(await r.text()),filename:r.name})}catch(e){n({tone:`error`,title:`Configuration file rejected`,description:y(e)})}finally{t.value=``}};return M.isLoading?(0,H.jsx)(b,{label:`Loading runtime defaults…`}):M.error?(0,H.jsx)(x,{error:M.error,onRetry:()=>void M.refetch()}):(0,H.jsxs)(`form`,{onSubmit:P.handleSubmit(e=>F.mutate(e)),children:[(0,H.jsx)(se,{title:`Safe operational defaults.`,description:`Tune new-task defaults, concurrency, retention, interface discovery, and database maintenance.`,actions:(0,H.jsxs)(w,{type:`submit`,disabled:F.isPending||!Z,children:[F.isPending?(0,H.jsx)(C,{}):(0,H.jsx)(f,{className:`h-4 w-4`}),`Save settings`]})}),(0,H.jsxs)(`div`,{className:`mb-5 flex gap-3 rounded-xl border border-rose-500/25 bg-rose-500/[.055] p-4`,children:[(0,H.jsx)(g,{className:`mt-0.5 h-5 w-5 shrink-0 text-rose-500`}),(0,H.jsxs)(`div`,{children:[(0,H.jsx)(`p`,{className:`text-sm font-semibold`,children:`Trusted network only`}),(0,H.jsx)(`p`,{className:`mt-1 text-xs leading-5 text-muted-foreground`,children:`MultiSpeed does not include authentication and must only be exposed to trusted networks unless protected by an authenticating reverse proxy.`})]})]}),(0,H.jsxs)(`div`,{className:`grid gap-5 xl:grid-cols-2`,children:[(0,H.jsxs)(k,{children:[(0,H.jsxs)(O,{children:[(0,H.jsxs)(E,{className:`flex items-center gap-2`,children:[(0,H.jsx)(re,{className:`h-4 w-4 text-primary`}),`Display & task defaults`]}),(0,H.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Applied to new tasks and reporting views`})]}),(0,H.jsxs)(D,{className:`space-y-4`,children:[(0,H.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,H.jsx)(o,{label:`Display units`,error:X.displayUnits?.message,children:(0,H.jsxs)(a,{...J(`displayUnits`),children:[(0,H.jsx)(`option`,{value:`bits`,children:`Bits per second`}),(0,H.jsx)(`option`,{value:`bytes`,children:`Bytes per second`})]})}),(0,H.jsx)(o,{label:`Default chart range`,error:X.defaultChartRange?.message,children:(0,H.jsxs)(a,{...J(`defaultChartRange`),children:[(0,H.jsx)(`option`,{value:`24h`,children:`Last 24 hours`}),(0,H.jsx)(`option`,{value:`7d`,children:`Last 7 days`}),(0,H.jsx)(`option`,{value:`30d`,children:`Last 30 days`}),(0,H.jsx)(`option`,{value:`90d`,children:`Last 90 days`})]})})]}),(0,H.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,H.jsx)(o,{label:`Default timezone`,error:X.defaultTimezone?.message,children:(0,H.jsxs)(a,{...J(`defaultTimezone`),children:[p.includes($)?null:(0,H.jsx)(`option`,{value:$,children:$}),p.map(e=>(0,H.jsx)(`option`,{value:e,children:e},e))]})}),(0,H.jsx)(o,{label:`Default task timeout`,hint:`Seconds`,error:X.defaultTaskTimeoutSeconds?.message,children:(0,H.jsx)(s,{type:`number`,min:5,max:3600,...J(`defaultTaskTimeoutSeconds`,{valueAsNumber:!0})})})]})]})]}),(0,H.jsxs)(k,{children:[(0,H.jsxs)(O,{children:[(0,H.jsxs)(E,{className:`flex items-center gap-2`,children:[(0,H.jsx)(ie,{className:`h-4 w-4 text-primary`}),`Concurrency & discovery`]}),(0,H.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`The global concurrency default is one; same-WAN tests never overlap`})]}),(0,H.jsxs)(D,{className:`space-y-4`,children:[(0,H.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,H.jsx)(o,{label:`Global concurrency`,hint:`1–16`,error:X.globalConcurrency?.message,children:(0,H.jsx)(s,{type:`number`,min:1,max:16,...J(`globalConcurrency`,{valueAsNumber:!0})})}),(0,H.jsx)(o,{label:`Interface refresh interval`,hint:`Seconds`,error:X.interfaceRefreshIntervalSeconds?.message,children:(0,H.jsx)(s,{type:`number`,min:5,max:3600,...J(`interfaceRefreshIntervalSeconds`,{valueAsNumber:!0})})})]}),(0,H.jsx)(j,{name:`allowSeparateWanConcurrency`,control:Y,render:({field:e})=>(0,H.jsx)(c,{checked:e.value,onChange:e.onChange,label:`Allow concurrency on separate WANs`,description:`Only tasks with distinct interface/source locks may execute together, bounded by global concurrency.`})})]})]}),(0,H.jsxs)(k,{id:`ookla-eula`,className:`xl:col-span-2`,children:[(0,H.jsxs)(O,{children:[(0,H.jsxs)(E,{className:`flex items-center gap-2`,children:[(0,H.jsx)(le,{className:`h-4 w-4 text-primary`}),`Ookla provider terms & authorization`]}),(0,H.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Technical acknowledgement and CLI flag authorization, independent from installation and any separate license permission`})]}),(0,H.jsx)(D,{children:(0,H.jsx)(h,{})})]}),(0,H.jsxs)(k,{className:`xl:col-span-2`,children:[(0,H.jsxs)(O,{children:[(0,H.jsxs)(E,{className:`flex items-center gap-2`,children:[(0,H.jsx)(ce,{className:`h-4 w-4 text-primary`}),`Configuration import & export`]}),(0,H.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Portable JSON for operational settings, tasks, and route profiles`})]}),(0,H.jsxs)(D,{className:`grid gap-3 md:grid-cols-2`,children:[(0,H.jsxs)(`div`,{className:`flex items-start justify-between gap-4 rounded-lg border border-border bg-muted/30 p-3`,children:[(0,H.jsxs)(`div`,{children:[(0,H.jsx)(`p`,{className:`text-xs font-semibold`,children:`Export current configuration`}),(0,H.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:`Downloads a versioned JSON file. Results and the Ookla terms acknowledgement are excluded.`})]}),(0,H.jsxs)(w,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>G.mutate(),disabled:G.isPending,children:[G.isPending?(0,H.jsx)(C,{}):(0,H.jsx)(u,{className:`h-3.5 w-3.5`}),`Export`]})]}),(0,H.jsxs)(`div`,{className:`flex items-start justify-between gap-4 rounded-lg border border-border bg-muted/30 p-3`,children:[(0,H.jsxs)(`div`,{children:[(0,H.jsx)(`p`,{className:`text-xs font-semibold`,children:`Import configuration`}),(0,H.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:`Validates the complete file before atomically replacing saved configuration.`})]}),(0,H.jsx)(`input`,{ref:A,className:`sr-only`,type:`file`,accept:`application/json,.json`,"aria-label":`Configuration file`,onChange:e=>void pe(e)}),(0,H.jsxs)(w,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>A.current?.click(),disabled:K.isPending,children:[(0,H.jsx)(ne,{className:`h-3.5 w-3.5`}),`Choose file`]})]})]})]}),(0,H.jsxs)(k,{children:[(0,H.jsxs)(O,{children:[(0,H.jsxs)(E,{className:`flex items-center gap-2`,children:[(0,H.jsx)(B,{className:`h-4 w-4 text-primary`}),`Retention`]}),(0,H.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Cleanup deletes old results in bounded batches and preserves task definitions`})]}),(0,H.jsxs)(D,{className:`space-y-4`,children:[(0,H.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,H.jsx)(o,{label:`Retention policy`,error:X.retentionMode?.message,children:(0,H.jsxs)(a,{...J(`retentionMode`),children:[(0,H.jsx)(`option`,{value:`forever`,children:`Keep forever`}),(0,H.jsx)(`option`,{value:`days`,children:`Fixed number of days`}),(0,H.jsx)(`option`,{value:`months`,children:`Fixed number of months`})]})}),(0,H.jsx)(o,{label:Q===`months`?`Months to retain`:`Days to retain`,error:X.retentionValue?.message,children:(0,H.jsx)(s,{type:`number`,min:Q===`forever`?0:1,max:3650,disabled:Q===`forever`,...J(`retentionValue`,{valueAsNumber:!0})})})]}),(0,H.jsxs)(`div`,{className:`flex items-start justify-between gap-4 rounded-lg border border-border bg-muted/30 p-3`,children:[(0,H.jsxs)(`div`,{children:[(0,H.jsx)(`p`,{className:`text-xs font-semibold`,children:`Run configured cleanup now`}),(0,H.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:`Applies the saved retention policy in bounded batches. Review individual records separately.`})]}),(0,H.jsxs)(`div`,{className:`flex gap-2`,children:[(0,H.jsx)(w,{asChild:!0,size:`sm`,variant:`ghost`,children:(0,H.jsx)(te,{to:`/results`,children:`Review`})}),(0,H.jsxs)(w,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>d(!0),disabled:Q===`forever`,children:[(0,H.jsx)(_,{className:`h-3.5 w-3.5`}),`Clean up`]})]})]})]})]}),(0,H.jsxs)(k,{children:[(0,H.jsxs)(O,{children:[(0,H.jsxs)(E,{className:`flex items-center gap-2`,children:[(0,H.jsx)(l,{className:`h-4 w-4 text-primary`}),`Database maintenance`]}),(0,H.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`WAL checkpointing and bounded upkeep`})]}),(0,H.jsxs)(D,{className:`space-y-4`,children:[(0,H.jsx)(o,{label:`Maintenance schedule`,hint:`Five-field cron`,error:X.databaseMaintenanceSchedule?.message,children:(0,H.jsx)(s,{className:`font-mono`,...J(`databaseMaintenanceSchedule`),placeholder:`30 3 * * *`})}),(0,H.jsxs)(`div`,{className:`flex items-start justify-between gap-4 rounded-lg border border-border bg-muted/30 p-3`,children:[(0,H.jsxs)(`div`,{children:[(0,H.jsx)(`p`,{className:`text-xs font-semibold`,children:`Consistent SQLite backup`}),(0,H.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:`Creates a transactional backup that includes active WAL data.`})]}),(0,H.jsxs)(w,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>L.mutate(),disabled:L.isPending,children:[L.isPending?(0,H.jsx)(C,{}):(0,H.jsx)(u,{className:`h-3.5 w-3.5`}),`Create backup`]})]})]})]})]}),(0,H.jsxs)(`div`,{className:`mt-5 flex items-center justify-between rounded-xl border border-border bg-card p-4`,children:[(0,H.jsxs)(`p`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,H.jsx)(ue,{className:`h-4 w-4`}),`Changes affect future scheduling and maintenance cycles; active tests are not interrupted.`]}),(0,H.jsxs)(w,{type:`submit`,disabled:F.isPending||!Z,children:[F.isPending?(0,H.jsx)(C,{}):(0,H.jsx)(f,{className:`h-4 w-4`}),`Save settings`]})]}),(0,H.jsx)(z,{open:r,onOpenChange:d,title:`Delete expired results now?`,description:`The currently saved retention policy will be applied immediately. Matching results and their diagnostics cannot be recovered unless you created a backup.`,confirmLabel:`Run cleanup`,destructive:!0,busy:q.isPending,onConfirm:()=>q.mutate()}),(0,H.jsx)(z,{open:m!==null,onOpenChange:e=>{!e&&!K.isPending&&v(null)},title:`Replace the saved configuration?`,description:m?(0,H.jsxs)(`div`,{className:`space-y-2`,children:[(0,H.jsxs)(`p`,{children:[(0,H.jsx)(`span`,{className:`font-medium text-foreground`,children:m.filename}),` contains `,m.document.tasks.length,` tasks and `,m.document.routeProfiles.length,` route profiles.`]}),(0,H.jsx)(`p`,{children:`Saved settings, tasks, and route profiles will be replaced. Measurement results and the Ookla terms acknowledgement remain unchanged. Import is blocked while a test is active.`}),Z?(0,H.jsx)(`p`,{className:`text-amber-500`,children:`Unsaved edits on this page will be discarded.`}):null]}):``,confirmLabel:`Import configuration`,destructive:!0,busy:K.isPending,onConfirm:()=>{m&&K.mutate(m.document)}})]})}export{G as default,U as parseConfigurationPreview}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/StatisticsPage-D4N2MQUQ.js b/internal/frontend/dist/assets/StatisticsPage-C5F5n78a.js similarity index 97% rename from internal/frontend/dist/assets/StatisticsPage-D4N2MQUQ.js rename to internal/frontend/dist/assets/StatisticsPage-C5F5n78a.js index 0db87b8..e94bd80 100644 --- a/internal/frontend/dist/assets/StatisticsPage-D4N2MQUQ.js +++ b/internal/frontend/dist/assets/StatisticsPage-C5F5n78a.js @@ -1 +1 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r}from"./query-DWNqGEie.js";import{t as i}from"./MetricCard-GfGMWPS-.js";import{i as a,r as o}from"./fields-PJGT-Jp7.js";import{t as s}from"./funnel-x-BSCtvBiK.js";import{I as c,P as ee,_ as l,a as u,b as te,h as d,i as ne,j as re,m as ie,n as f,o as ae,r as p,s as oe,v as se,w as m}from"./index-B84OiuS0.js";import{n as ce,r as le}from"./useAppSettings-CeA9vkHH.js";import{t as ue}from"./PageHeader-DGX-y_Bi.js";import{n as h,r as g,t as de}from"./EChart-C9A5j2N8.js";import{i as _,n as fe,r as v,t as y}from"./card-DEXft0Cm.js";var pe=c(`calendar-range`,[[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}],[`path`,{d:`M16 2v3`,key:`otl347`}],[`path`,{d:`M3 9h18`,key:`1pudct`}],[`path`,{d:`M8 2v3`,key:`1ioesn`}],[`path`,{d:`M17 13h-6`,key:`1qbiup`}],[`path`,{d:`M13 17H7`,key:`1x38vv`}],[`path`,{d:`M7 13h.01`,key:`1vezk1`}],[`path`,{d:`M17 17h.01`,key:`1sd3ek`}]]),me=c(`sigma`,[[`path`,{d:`M18 7V5a1 1 0 0 0-1-1H6.5a.5.5 0 0 0-.4.8l4.5 6a2 2 0 0 1 0 2.4l-4.5 6a.5.5 0 0 0 .4.8H17a1 1 0 0 0 1-1v-2`,key:`wuwx1p`}]]),he=c(`target`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`circle`,{cx:`12`,cy:`12`,r:`6`,key:`1vlfrh`}],[`circle`,{cx:`12`,cy:`12`,r:`2`,key:`1c9p78`}]]),b=e(r(),1),x=n(),S=[{id:`download`,label:`Download throughput`},{id:`upload`,label:`Upload throughput`},{id:`latency`,label:`Latency`},{id:`jitter`,label:`Jitter`},{id:`packetLoss`,label:`Packet loss`},{id:`duration`,label:`Test duration`},{id:`successRate`,label:`Success rate`}];function C(){let e=ce(),{bitrate:n}=le(),[r]=(0,b.useState)(()=>Date.now()),[c,l]=(0,b.useState)(`day`),[d,ie]=(0,b.useState)(`download`),[h,g]=(0,b.useState)(null),C=ve(r,e.settings.defaultChartRange),E=h?.from??C.from,D=h?.to??C.to,[O,k]=(0,b.useState)([]),[A,j]=(0,b.useState)([]),[M,N]=(0,b.useState)([]),[P,F]=(0,b.useState)([]),[I,L]=(0,b.useState)([]),[R,ye]=(0,b.useState)([]),[z,be]=(0,b.useState)([]),[B,xe]=(0,b.useState)(`none`),V=te(E,D,e.settings.defaultTimezone),H=t({queryKey:f.tasks,queryFn:m.tasks}),U=t({queryKey:f.interfaces,queryFn:()=>m.interfaces()}),W=t({queryKey:f.routes,queryFn:m.routes}),G=t({queryKey:f.results({dimensions:!0,...V}),queryFn:()=>m.results({page:1,pageSize:200,...V})}),K={granularity:c===`week`?`iso-week`:c,metric:d,...V,timezone:e.settings.defaultTimezone,taskId:O,interfaceName:A,sourceIp:I,provider:M,serverId:P,routeProfileId:R,publicIp:z,...B===`none`?{}:{groupBy:B}},q=t({queryKey:f.statistics(K),queryFn:()=>m.statistics(K)});if(H.isLoading||U.isLoading||W.isLoading||G.isLoading||e.isLoading)return(0,x.jsx)(u,{label:`Loading statistics controls…`});let Se=H.error??U.error??W.error??G.error??e.error;if(Se)return(0,x.jsx)(ne,{error:Se,onRetry:()=>{H.refetch(),U.refetch(),W.refetch(),G.refetch(),e.refetch()}});let J=q.data,Y=J?.buckets??[],X=J?.overall??J?.summary??J?.totals,Z=T(X,d),Ce=_e(J?.series,Y,d,n),Q=G.data?.items??[],we=[...new Map(Q.filter(e=>e.serverId).map(e=>[e.serverId,e.serverName||e.serverId])).entries()],Te=[...new Set(Q.map(e=>e.selectedSourceIp).filter(Boolean))].sort(),Ee=[...new Set(Q.map(e=>e.detectedPublicIp).filter(Boolean))].sort(),De=O.length+A.length+M.length+P.length+I.length+R.length+z.length>0,Oe=()=>{k([]),j([]),N([]),F([]),L([]),ye([]),be([]),xe(`none`)},$=ge(d,n);return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(ue,{title:`Patterns beyond a single test.`,description:`Aggregate in the reporting timezone, compare dimensions, and inspect min, average, median, p95, and variance without failed sentinel values.`}),(0,x.jsxs)(y,{className:`mb-5`,children:[(0,x.jsxs)(`div`,{className:`grid gap-4 p-4 lg:grid-cols-[1fr_1fr_1fr_1fr_auto]`,children:[(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`mb-1.5 text-xs font-semibold`,children:`Aggregation`}),(0,x.jsx)(`div`,{className:`flex flex-wrap rounded-lg border border-border bg-muted/40 p-1`,children:[`raw`,`day`,`week`,`month`,`year`].map(e=>(0,x.jsx)(`button`,{type:`button`,onClick:()=>l(e),className:`flex-1 rounded-md px-2 py-1.5 text-[11px] font-semibold capitalize transition ${c===e?`bg-card text-foreground shadow-sm`:`text-muted-foreground hover:text-foreground`}`,"aria-pressed":c===e,children:e===`week`?`ISO week`:e},e))})]}),(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`mb-1.5 text-xs font-semibold`,children:`Metric`}),(0,x.jsx)(a,{value:d,onChange:e=>ie(e.target.value),children:S.map(e=>(0,x.jsx)(`option`,{value:e.id,children:e.label},e.id))})]}),(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`mb-1.5 text-xs font-semibold`,children:`From`}),(0,x.jsx)(o,{type:`date`,value:E,max:D,onChange:e=>g({from:e.target.value,to:D})})]}),(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`mb-1.5 text-xs font-semibold`,children:`To`}),(0,x.jsx)(o,{type:`date`,value:D,min:E,onChange:e=>g({from:E,to:e.target.value})})]}),(0,x.jsx)(`div`,{className:`flex items-end`,children:(0,x.jsxs)(oe,{variant:`outline`,onClick:()=>void q.refetch(),disabled:q.isFetching,children:[q.isFetching?(0,x.jsx)(ae,{}):(0,x.jsx)(pe,{className:`h-4 w-4`}),`Refresh`]})})]}),(0,x.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 border-t border-border p-4`,children:[(0,x.jsx)(w,{label:`Tasks`,options:(H.data??[]).map(e=>({value:e.id,label:e.name})),selected:O,onChange:k}),(0,x.jsx)(w,{label:`WAN interfaces`,options:(U.data??[]).map(e=>({value:e.name,label:e.name})),selected:A,onChange:j}),(0,x.jsx)(w,{label:`Providers`,options:[`ookla`,`librespeed`,`cloudflare`].map(e=>({value:e,label:se(e)})),selected:M,onChange:N}),(0,x.jsx)(w,{label:`Servers`,options:we.map(([e,t])=>({value:e,label:t})),selected:P,onChange:F}),(0,x.jsx)(w,{label:`Source IPs`,options:Te.map(e=>({value:e,label:e})),selected:I,onChange:L}),(0,x.jsx)(w,{label:`Route profiles`,options:(W.data??[]).map(e=>({value:e.id,label:e.name})),selected:R,onChange:ye}),(0,x.jsx)(w,{label:`Public IPs`,options:Ee.map(e=>({value:e,label:e})),selected:z,onChange:be}),(0,x.jsxs)(a,{className:`h-9 w-52`,value:B,onChange:e=>xe(e.target.value),"aria-label":`Group series by`,children:[(0,x.jsx)(`option`,{value:`none`,children:`Single series`}),(0,x.jsx)(`option`,{value:`task`,children:`Group by task`}),(0,x.jsx)(`option`,{value:`provider`,children:`Group by provider`}),(0,x.jsx)(`option`,{value:`interface`,children:`Group by interface`}),(0,x.jsx)(`option`,{value:`source-ip`,children:`Group by source IP`}),(0,x.jsx)(`option`,{value:`server`,children:`Group by server`}),(0,x.jsx)(`option`,{value:`route-profile`,children:`Group by route profile`}),(0,x.jsx)(`option`,{value:`public-ip`,children:`Group by public IP`})]}),(0,x.jsxs)(oe,{size:`sm`,variant:`ghost`,onClick:Oe,disabled:!De&&B===`none`,children:[(0,x.jsx)(s,{className:`h-3.5 w-3.5`}),`Clear dimensions`]})]})]}),q.error?(0,x.jsx)(y,{className:`mt-5`,children:(0,x.jsx)(ne,{compact:!0,title:`Unable to calculate statistics`,error:q.error,onRetry:()=>{q.refetch()}})}):q.isLoading?(0,x.jsx)(y,{className:`mt-5`,children:(0,x.jsx)(u,{compact:!0,label:`Calculating statistical buckets…`})}):(0,x.jsxs)(x.Fragment,{children:[(0,x.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2 xl:grid-cols-4`,children:[(0,x.jsx)(i,{label:`Average`,value:$(Z.average),detail:`${X?.successfulCount??0} successful samples`,icon:ee,tone:`cyan`}),(0,x.jsx)(i,{label:`Median`,value:$(Z.median),detail:`50th percentile`,icon:he,tone:`violet`}),(0,x.jsx)(i,{label:`P95`,value:$(Z.p95),detail:`95th percentile`,icon:re,tone:`orange`}),(0,x.jsx)(i,{label:`Standard deviation`,value:$(Z.standardDeviation),detail:`${X?.failedCount??0} failed · ${X?.skippedCount??0} skipped`,icon:me,tone:`emerald`})]}),(0,x.jsxs)(y,{className:`mt-5`,children:[(0,x.jsxs)(v,{className:`flex-row items-start justify-between`,children:[(0,x.jsxs)(`div`,{children:[(0,x.jsxs)(_,{children:[S.find(e=>e.id===d)?.label,` trend`]}),(0,x.jsxs)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:[`Min / average / max band with median and p95 · `,J?.timezone??K.timezone]})]}),q.isFetching?(0,x.jsx)(ae,{}):null]}),(0,x.jsx)(fe,{children:Y.length||(J?.series?.length??0)?(0,x.jsx)(de,{option:Ce,ariaLabel:`${S.find(e=>e.id===d)?.label} statistics by ${c}`,className:`h-[25rem]`}):(0,x.jsx)(p,{title:`No statistical samples`,description:`No completed results match this date range and dimension filter.`})})]}),(0,x.jsxs)(y,{className:`mt-5 overflow-hidden`,children:[(0,x.jsxs)(v,{children:[(0,x.jsx)(_,{children:`Accessible statistical table`}),(0,x.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Exact aggregate values for every reporting bucket`})]}),Y.length?(0,x.jsx)(`div`,{className:`overflow-x-auto scrollbar-thin`,children:(0,x.jsxs)(`table`,{className:`data-table w-full min-w-[900px]`,children:[(0,x.jsx)(`thead`,{children:(0,x.jsxs)(`tr`,{children:[(0,x.jsx)(`th`,{children:`Bucket`}),(0,x.jsx)(`th`,{children:`Samples`}),(0,x.jsx)(`th`,{children:`Succeeded`}),(0,x.jsx)(`th`,{children:`Failed`}),(0,x.jsx)(`th`,{children:`Minimum`}),(0,x.jsx)(`th`,{children:`Average`}),(0,x.jsx)(`th`,{children:`Median`}),(0,x.jsx)(`th`,{children:`P95`}),(0,x.jsx)(`th`,{children:`Maximum`}),(0,x.jsx)(`th`,{children:`Std. deviation`})]})}),(0,x.jsx)(`tbody`,{children:Y.map((e,t)=>{let n=T(e,d);return(0,x.jsxs)(`tr`,{children:[(0,x.jsx)(`td`,{className:`font-semibold`,children:e.label??e.bucket??e.timestamp}),(0,x.jsx)(`td`,{children:e.sampleCount}),(0,x.jsx)(`td`,{children:e.successfulCount}),(0,x.jsx)(`td`,{children:e.failedCount}),(0,x.jsx)(`td`,{children:$(n.min)}),(0,x.jsx)(`td`,{children:$(n.average)}),(0,x.jsx)(`td`,{children:$(n.median)}),(0,x.jsx)(`td`,{children:$(n.p95)}),(0,x.jsx)(`td`,{children:$(n.max)}),(0,x.jsx)(`td`,{children:$(n.standardDeviation)})]},`${e.bucket}-${t}`)})})]})}):(0,x.jsx)(p,{compact:!0,title:`No rows to display`,description:`Adjust the reporting period or dimension filters.`})]})]})]})}function w({label:e,options:t,selected:n,onChange:r}){return(0,x.jsxs)(`details`,{className:`group relative`,children:[(0,x.jsxs)(`summary`,{className:`flex h-9 cursor-pointer list-none items-center gap-2 rounded-lg border border-border bg-background px-3 text-xs font-semibold transition hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring`,children:[e,n.length?(0,x.jsx)(`span`,{className:`grid min-w-5 place-items-center rounded-full bg-primary px-1.5 py-0.5 text-[10px] text-primary-foreground`,children:n.length}):null]}),(0,x.jsx)(`div`,{className:`absolute left-0 top-11 z-40 max-h-64 min-w-52 overflow-y-auto rounded-lg border border-border bg-card p-2 shadow-xl scrollbar-thin`,children:t.length?t.map(e=>(0,x.jsxs)(`label`,{className:`flex cursor-pointer items-center gap-2 rounded-md px-2 py-2 text-xs hover:bg-muted`,children:[(0,x.jsx)(`input`,{type:`checkbox`,checked:n.includes(e.value),onChange:()=>r(n.includes(e.value)?n.filter(t=>t!==e.value):[...n,e.value]),className:`h-4 w-4 accent-cyan-500`}),e.label]},e.value)):(0,x.jsx)(`p`,{className:`p-2 text-xs text-muted-foreground`,children:`No options available`})})]})}function T(e,t){let n={min:null,max:null,average:null,median:null,p95:null,standardDeviation:null};if(!e)return n;if(t===`successRate`){let t=typeof e.successRate==`number`?e.successRate:e.sampleCount?e.successfulCount/e.sampleCount*100:null;return{min:t,max:t,average:t,median:t,p95:t,standardDeviation:null}}let r=e[t];if(typeof r==`object`&&r)return{...n,...r};let i={min:[`${t}Min`,`min`],max:[`${t}Max`,`max`],average:[`${t}Average`,`average`,`avg`],median:[`${t}Median`,`median`],p95:[`${t}P95`,`p95`],standardDeviation:[`${t}StandardDeviation`,`standardDeviation`,`stdDev`]};return Object.fromEntries(Object.entries(i).map(([t,n])=>[t,n.map(t=>e[t]).find(e=>typeof e==`number`)??null]))}function ge(e,t){return e===`download`||e===`upload`?t:e===`latency`||e===`jitter`||e===`duration`?ie:e===`packetLoss`||e===`successRate`?d:e=>e==null?`—`:String(e)}function _e(e,t,n,r){let i=g(),a=h(),o=ge(n,r),s=[l.ookla,l.librespeed,l.cloudflare,`#34d399`,`#f472b6`,`#facc15`],c=e?.length?[...new Set(e.flatMap(e=>e.buckets.map(E)).filter(Boolean))].sort():t.map(E),ee=new Map((e?.flatMap(e=>e.buckets)??t).map(e=>[E(e),e.label??E(e)])),u=c.map(e=>ee.get(e)??e),te=e?.length?e.flatMap((e,t)=>{let r=s[t%s.length]??`#67e8f9`,i=new Map(e.buckets.map(e=>[E(e),e]));return[{name:e.name,type:`line`,smooth:.18,showSymbol:e.buckets.length<30,symbolSize:5,connectNulls:!1,data:c.map(e=>T(i.get(e),n).average),lineStyle:{width:2,color:r},itemStyle:{color:r}}]}):[{name:`Minimum`,type:`line`,smooth:.18,showSymbol:!1,data:t.map(e=>T(e,n).min),lineStyle:{width:1,color:`rgba(103,232,249,.38)`},itemStyle:{color:l.ookla}},{name:`Average`,type:`line`,smooth:.18,showSymbol:t.length<30,symbolSize:5,data:t.map(e=>T(e,n).average),lineStyle:{width:2.5,color:l.ookla},itemStyle:{color:l.ookla},areaStyle:{color:`rgba(34,211,238,.08)`}},{name:`P95`,type:`line`,smooth:.18,showSymbol:!1,data:t.map(e=>T(e,n).p95),lineStyle:{width:1.5,type:`dashed`,color:l.librespeed},itemStyle:{color:l.librespeed}},{name:`Maximum`,type:`line`,smooth:.18,showSymbol:!1,data:t.map(e=>T(e,n).max),lineStyle:{width:1,color:`rgba(167,139,250,.4)`},itemStyle:{color:l.librespeed}}];return{tooltip:{trigger:`axis`,backgroundColor:`rgba(8,17,31,.95)`,borderColor:`rgba(148,163,184,.22)`,textStyle:{color:`#e2e8f0`,fontSize:11},valueFormatter:e=>o(Number(e))},legend:{top:0,right:0,textStyle:{color:i,fontSize:10}},grid:{left:8,right:12,top:38,bottom:8,containLabel:!0},xAxis:{type:`category`,boundaryGap:!1,data:u,axisLabel:{color:i,fontSize:10,hideOverlap:!0},axisLine:{lineStyle:{color:a}},axisTick:{show:!1}},yAxis:{type:`value`,axisLabel:{color:i,fontSize:10,formatter:e=>o(e)},splitLine:{lineStyle:{color:a}}},series:te}}function E(e){return e.bucket||e.timestamp||``}function ve(e,t){return{from:new Date(e-(t===`24h`?1:t===`7d`?7:t===`90d`?90:30)*864e5).toISOString().slice(0,10),to:new Date(e).toISOString().slice(0,10)}}export{C as default,T as metricAggregate,_e as statisticsOption}; \ No newline at end of file +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r}from"./query-DWNqGEie.js";import{t as i}from"./MetricCard-CMX2GS_z.js";import{i as a,r as o}from"./fields-DSX3DSQR.js";import{t as s}from"./funnel-x-DRKN7t7O.js";import{I as c,P as ee,_ as l,a as u,b as te,h as d,i as ne,j as re,m as ie,n as f,o as ae,r as p,s as oe,v as se,w as m}from"./index-DleN6wGz.js";import{n as ce,r as le}from"./useAppSettings-vnP5f96R.js";import{t as ue}from"./PageHeader-DGX-y_Bi.js";import{n as h,r as g,t as de}from"./EChart-A2OaIcPj.js";import{i as _,n as fe,r as v,t as y}from"./card-BtXBd7n8.js";var pe=c(`calendar-range`,[[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}],[`path`,{d:`M16 2v3`,key:`otl347`}],[`path`,{d:`M3 9h18`,key:`1pudct`}],[`path`,{d:`M8 2v3`,key:`1ioesn`}],[`path`,{d:`M17 13h-6`,key:`1qbiup`}],[`path`,{d:`M13 17H7`,key:`1x38vv`}],[`path`,{d:`M7 13h.01`,key:`1vezk1`}],[`path`,{d:`M17 17h.01`,key:`1sd3ek`}]]),me=c(`sigma`,[[`path`,{d:`M18 7V5a1 1 0 0 0-1-1H6.5a.5.5 0 0 0-.4.8l4.5 6a2 2 0 0 1 0 2.4l-4.5 6a.5.5 0 0 0 .4.8H17a1 1 0 0 0 1-1v-2`,key:`wuwx1p`}]]),he=c(`target`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`circle`,{cx:`12`,cy:`12`,r:`6`,key:`1vlfrh`}],[`circle`,{cx:`12`,cy:`12`,r:`2`,key:`1c9p78`}]]),b=e(r(),1),x=n(),S=[{id:`download`,label:`Download throughput`},{id:`upload`,label:`Upload throughput`},{id:`latency`,label:`Latency`},{id:`jitter`,label:`Jitter`},{id:`packetLoss`,label:`Packet loss`},{id:`duration`,label:`Test duration`},{id:`successRate`,label:`Success rate`}];function C(){let e=ce(),{bitrate:n}=le(),[r]=(0,b.useState)(()=>Date.now()),[c,l]=(0,b.useState)(`day`),[d,ie]=(0,b.useState)(`download`),[h,g]=(0,b.useState)(null),C=ve(r,e.settings.defaultChartRange),E=h?.from??C.from,D=h?.to??C.to,[O,k]=(0,b.useState)([]),[A,j]=(0,b.useState)([]),[M,N]=(0,b.useState)([]),[P,F]=(0,b.useState)([]),[I,L]=(0,b.useState)([]),[R,ye]=(0,b.useState)([]),[z,be]=(0,b.useState)([]),[B,xe]=(0,b.useState)(`none`),V=te(E,D,e.settings.defaultTimezone),H=t({queryKey:f.tasks,queryFn:m.tasks}),U=t({queryKey:f.interfaces,queryFn:()=>m.interfaces()}),W=t({queryKey:f.routes,queryFn:m.routes}),G=t({queryKey:f.results({dimensions:!0,...V}),queryFn:()=>m.results({page:1,pageSize:200,...V})}),K={granularity:c===`week`?`iso-week`:c,metric:d,...V,timezone:e.settings.defaultTimezone,taskId:O,interfaceName:A,sourceIp:I,provider:M,serverId:P,routeProfileId:R,publicIp:z,...B===`none`?{}:{groupBy:B}},q=t({queryKey:f.statistics(K),queryFn:()=>m.statistics(K)});if(H.isLoading||U.isLoading||W.isLoading||G.isLoading||e.isLoading)return(0,x.jsx)(u,{label:`Loading statistics controls…`});let Se=H.error??U.error??W.error??G.error??e.error;if(Se)return(0,x.jsx)(ne,{error:Se,onRetry:()=>{H.refetch(),U.refetch(),W.refetch(),G.refetch(),e.refetch()}});let J=q.data,Y=J?.buckets??[],X=J?.overall??J?.summary??J?.totals,Z=T(X,d),Ce=_e(J?.series,Y,d,n),Q=G.data?.items??[],we=[...new Map(Q.filter(e=>e.serverId).map(e=>[e.serverId,e.serverName||e.serverId])).entries()],Te=[...new Set(Q.map(e=>e.selectedSourceIp).filter(Boolean))].sort(),Ee=[...new Set(Q.map(e=>e.detectedPublicIp).filter(Boolean))].sort(),De=O.length+A.length+M.length+P.length+I.length+R.length+z.length>0,Oe=()=>{k([]),j([]),N([]),F([]),L([]),ye([]),be([]),xe(`none`)},$=ge(d,n);return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(ue,{title:`Patterns beyond a single test.`,description:`Aggregate in the reporting timezone, compare dimensions, and inspect min, average, median, p95, and variance without failed sentinel values.`}),(0,x.jsxs)(y,{className:`mb-5`,children:[(0,x.jsxs)(`div`,{className:`grid gap-4 p-4 lg:grid-cols-[1fr_1fr_1fr_1fr_auto]`,children:[(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`mb-1.5 text-xs font-semibold`,children:`Aggregation`}),(0,x.jsx)(`div`,{className:`flex flex-wrap rounded-lg border border-border bg-muted/40 p-1`,children:[`raw`,`day`,`week`,`month`,`year`].map(e=>(0,x.jsx)(`button`,{type:`button`,onClick:()=>l(e),className:`flex-1 rounded-md px-2 py-1.5 text-[11px] font-semibold capitalize transition ${c===e?`bg-card text-foreground shadow-sm`:`text-muted-foreground hover:text-foreground`}`,"aria-pressed":c===e,children:e===`week`?`ISO week`:e},e))})]}),(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`mb-1.5 text-xs font-semibold`,children:`Metric`}),(0,x.jsx)(a,{value:d,onChange:e=>ie(e.target.value),children:S.map(e=>(0,x.jsx)(`option`,{value:e.id,children:e.label},e.id))})]}),(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`mb-1.5 text-xs font-semibold`,children:`From`}),(0,x.jsx)(o,{type:`date`,value:E,max:D,onChange:e=>g({from:e.target.value,to:D})})]}),(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`p`,{className:`mb-1.5 text-xs font-semibold`,children:`To`}),(0,x.jsx)(o,{type:`date`,value:D,min:E,onChange:e=>g({from:E,to:e.target.value})})]}),(0,x.jsx)(`div`,{className:`flex items-end`,children:(0,x.jsxs)(oe,{variant:`outline`,onClick:()=>void q.refetch(),disabled:q.isFetching,children:[q.isFetching?(0,x.jsx)(ae,{}):(0,x.jsx)(pe,{className:`h-4 w-4`}),`Refresh`]})})]}),(0,x.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 border-t border-border p-4`,children:[(0,x.jsx)(w,{label:`Tasks`,options:(H.data??[]).map(e=>({value:e.id,label:e.name})),selected:O,onChange:k}),(0,x.jsx)(w,{label:`WAN interfaces`,options:(U.data??[]).map(e=>({value:e.name,label:e.name})),selected:A,onChange:j}),(0,x.jsx)(w,{label:`Providers`,options:[`ookla`,`librespeed`,`cloudflare`].map(e=>({value:e,label:se(e)})),selected:M,onChange:N}),(0,x.jsx)(w,{label:`Servers`,options:we.map(([e,t])=>({value:e,label:t})),selected:P,onChange:F}),(0,x.jsx)(w,{label:`Source IPs`,options:Te.map(e=>({value:e,label:e})),selected:I,onChange:L}),(0,x.jsx)(w,{label:`Route profiles`,options:(W.data??[]).map(e=>({value:e.id,label:e.name})),selected:R,onChange:ye}),(0,x.jsx)(w,{label:`Public IPs`,options:Ee.map(e=>({value:e,label:e})),selected:z,onChange:be}),(0,x.jsxs)(a,{className:`h-9 w-52`,value:B,onChange:e=>xe(e.target.value),"aria-label":`Group series by`,children:[(0,x.jsx)(`option`,{value:`none`,children:`Single series`}),(0,x.jsx)(`option`,{value:`task`,children:`Group by task`}),(0,x.jsx)(`option`,{value:`provider`,children:`Group by provider`}),(0,x.jsx)(`option`,{value:`interface`,children:`Group by interface`}),(0,x.jsx)(`option`,{value:`source-ip`,children:`Group by source IP`}),(0,x.jsx)(`option`,{value:`server`,children:`Group by server`}),(0,x.jsx)(`option`,{value:`route-profile`,children:`Group by route profile`}),(0,x.jsx)(`option`,{value:`public-ip`,children:`Group by public IP`})]}),(0,x.jsxs)(oe,{size:`sm`,variant:`ghost`,onClick:Oe,disabled:!De&&B===`none`,children:[(0,x.jsx)(s,{className:`h-3.5 w-3.5`}),`Clear dimensions`]})]})]}),q.error?(0,x.jsx)(y,{className:`mt-5`,children:(0,x.jsx)(ne,{compact:!0,title:`Unable to calculate statistics`,error:q.error,onRetry:()=>{q.refetch()}})}):q.isLoading?(0,x.jsx)(y,{className:`mt-5`,children:(0,x.jsx)(u,{compact:!0,label:`Calculating statistical buckets…`})}):(0,x.jsxs)(x.Fragment,{children:[(0,x.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2 xl:grid-cols-4`,children:[(0,x.jsx)(i,{label:`Average`,value:$(Z.average),detail:`${X?.successfulCount??0} successful samples`,icon:ee,tone:`cyan`}),(0,x.jsx)(i,{label:`Median`,value:$(Z.median),detail:`50th percentile`,icon:he,tone:`violet`}),(0,x.jsx)(i,{label:`P95`,value:$(Z.p95),detail:`95th percentile`,icon:re,tone:`orange`}),(0,x.jsx)(i,{label:`Standard deviation`,value:$(Z.standardDeviation),detail:`${X?.failedCount??0} failed · ${X?.skippedCount??0} skipped`,icon:me,tone:`emerald`})]}),(0,x.jsxs)(y,{className:`mt-5`,children:[(0,x.jsxs)(v,{className:`flex-row items-start justify-between`,children:[(0,x.jsxs)(`div`,{children:[(0,x.jsxs)(_,{children:[S.find(e=>e.id===d)?.label,` trend`]}),(0,x.jsxs)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:[`Min / average / max band with median and p95 · `,J?.timezone??K.timezone]})]}),q.isFetching?(0,x.jsx)(ae,{}):null]}),(0,x.jsx)(fe,{children:Y.length||(J?.series?.length??0)?(0,x.jsx)(de,{option:Ce,ariaLabel:`${S.find(e=>e.id===d)?.label} statistics by ${c}`,className:`h-[25rem]`}):(0,x.jsx)(p,{title:`No statistical samples`,description:`No completed results match this date range and dimension filter.`})})]}),(0,x.jsxs)(y,{className:`mt-5 overflow-hidden`,children:[(0,x.jsxs)(v,{children:[(0,x.jsx)(_,{children:`Accessible statistical table`}),(0,x.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Exact aggregate values for every reporting bucket`})]}),Y.length?(0,x.jsx)(`div`,{className:`overflow-x-auto scrollbar-thin`,children:(0,x.jsxs)(`table`,{className:`data-table w-full min-w-[900px]`,children:[(0,x.jsx)(`thead`,{children:(0,x.jsxs)(`tr`,{children:[(0,x.jsx)(`th`,{children:`Bucket`}),(0,x.jsx)(`th`,{children:`Samples`}),(0,x.jsx)(`th`,{children:`Succeeded`}),(0,x.jsx)(`th`,{children:`Failed`}),(0,x.jsx)(`th`,{children:`Minimum`}),(0,x.jsx)(`th`,{children:`Average`}),(0,x.jsx)(`th`,{children:`Median`}),(0,x.jsx)(`th`,{children:`P95`}),(0,x.jsx)(`th`,{children:`Maximum`}),(0,x.jsx)(`th`,{children:`Std. deviation`})]})}),(0,x.jsx)(`tbody`,{children:Y.map((e,t)=>{let n=T(e,d);return(0,x.jsxs)(`tr`,{children:[(0,x.jsx)(`td`,{className:`font-semibold`,children:e.label??e.bucket??e.timestamp}),(0,x.jsx)(`td`,{children:e.sampleCount}),(0,x.jsx)(`td`,{children:e.successfulCount}),(0,x.jsx)(`td`,{children:e.failedCount}),(0,x.jsx)(`td`,{children:$(n.min)}),(0,x.jsx)(`td`,{children:$(n.average)}),(0,x.jsx)(`td`,{children:$(n.median)}),(0,x.jsx)(`td`,{children:$(n.p95)}),(0,x.jsx)(`td`,{children:$(n.max)}),(0,x.jsx)(`td`,{children:$(n.standardDeviation)})]},`${e.bucket}-${t}`)})})]})}):(0,x.jsx)(p,{compact:!0,title:`No rows to display`,description:`Adjust the reporting period or dimension filters.`})]})]})]})}function w({label:e,options:t,selected:n,onChange:r}){return(0,x.jsxs)(`details`,{className:`group relative`,children:[(0,x.jsxs)(`summary`,{className:`flex h-9 cursor-pointer list-none items-center gap-2 rounded-lg border border-border bg-background px-3 text-xs font-semibold transition hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring`,children:[e,n.length?(0,x.jsx)(`span`,{className:`grid min-w-5 place-items-center rounded-full bg-primary px-1.5 py-0.5 text-[10px] text-primary-foreground`,children:n.length}):null]}),(0,x.jsx)(`div`,{className:`absolute left-0 top-11 z-40 max-h-64 min-w-52 overflow-y-auto rounded-lg border border-border bg-card p-2 shadow-xl scrollbar-thin`,children:t.length?t.map(e=>(0,x.jsxs)(`label`,{className:`flex cursor-pointer items-center gap-2 rounded-md px-2 py-2 text-xs hover:bg-muted`,children:[(0,x.jsx)(`input`,{type:`checkbox`,checked:n.includes(e.value),onChange:()=>r(n.includes(e.value)?n.filter(t=>t!==e.value):[...n,e.value]),className:`h-4 w-4 accent-cyan-500`}),e.label]},e.value)):(0,x.jsx)(`p`,{className:`p-2 text-xs text-muted-foreground`,children:`No options available`})})]})}function T(e,t){let n={min:null,max:null,average:null,median:null,p95:null,standardDeviation:null};if(!e)return n;if(t===`successRate`){let t=typeof e.successRate==`number`?e.successRate:e.sampleCount?e.successfulCount/e.sampleCount*100:null;return{min:t,max:t,average:t,median:t,p95:t,standardDeviation:null}}let r=e[t];if(typeof r==`object`&&r)return{...n,...r};let i={min:[`${t}Min`,`min`],max:[`${t}Max`,`max`],average:[`${t}Average`,`average`,`avg`],median:[`${t}Median`,`median`],p95:[`${t}P95`,`p95`],standardDeviation:[`${t}StandardDeviation`,`standardDeviation`,`stdDev`]};return Object.fromEntries(Object.entries(i).map(([t,n])=>[t,n.map(t=>e[t]).find(e=>typeof e==`number`)??null]))}function ge(e,t){return e===`download`||e===`upload`?t:e===`latency`||e===`jitter`||e===`duration`?ie:e===`packetLoss`||e===`successRate`?d:e=>e==null?`—`:String(e)}function _e(e,t,n,r){let i=g(),a=h(),o=ge(n,r),s=[l.ookla,l.librespeed,l.cloudflare,`#34d399`,`#f472b6`,`#facc15`],c=e?.length?[...new Set(e.flatMap(e=>e.buckets.map(E)).filter(Boolean))].sort():t.map(E),ee=new Map((e?.flatMap(e=>e.buckets)??t).map(e=>[E(e),e.label??E(e)])),u=c.map(e=>ee.get(e)??e),te=e?.length?e.flatMap((e,t)=>{let r=s[t%s.length]??`#67e8f9`,i=new Map(e.buckets.map(e=>[E(e),e]));return[{name:e.name,type:`line`,smooth:.18,showSymbol:e.buckets.length<30,symbolSize:5,connectNulls:!1,data:c.map(e=>T(i.get(e),n).average),lineStyle:{width:2,color:r},itemStyle:{color:r}}]}):[{name:`Minimum`,type:`line`,smooth:.18,showSymbol:!1,data:t.map(e=>T(e,n).min),lineStyle:{width:1,color:`rgba(103,232,249,.38)`},itemStyle:{color:l.ookla}},{name:`Average`,type:`line`,smooth:.18,showSymbol:t.length<30,symbolSize:5,data:t.map(e=>T(e,n).average),lineStyle:{width:2.5,color:l.ookla},itemStyle:{color:l.ookla},areaStyle:{color:`rgba(34,211,238,.08)`}},{name:`P95`,type:`line`,smooth:.18,showSymbol:!1,data:t.map(e=>T(e,n).p95),lineStyle:{width:1.5,type:`dashed`,color:l.librespeed},itemStyle:{color:l.librespeed}},{name:`Maximum`,type:`line`,smooth:.18,showSymbol:!1,data:t.map(e=>T(e,n).max),lineStyle:{width:1,color:`rgba(167,139,250,.4)`},itemStyle:{color:l.librespeed}}];return{tooltip:{trigger:`axis`,backgroundColor:`rgba(8,17,31,.95)`,borderColor:`rgba(148,163,184,.22)`,textStyle:{color:`#e2e8f0`,fontSize:11},valueFormatter:e=>o(Number(e))},legend:{top:0,right:0,textStyle:{color:i,fontSize:10}},grid:{left:8,right:12,top:38,bottom:8,containLabel:!0},xAxis:{type:`category`,boundaryGap:!1,data:u,axisLabel:{color:i,fontSize:10,hideOverlap:!0},axisLine:{lineStyle:{color:a}},axisTick:{show:!1}},yAxis:{type:`value`,axisLabel:{color:i,fontSize:10,formatter:e=>o(e)},splitLine:{lineStyle:{color:a}}},series:te}}function E(e){return e.bucket||e.timestamp||``}function ve(e,t){return{from:new Date(e-(t===`24h`?1:t===`7d`?7:t===`90d`?90:30)*864e5).toISOString().slice(0,10),to:new Date(e).toISOString().slice(0,10)}}export{C as default,T as metricAggregate,_e as statisticsOption}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/SystemPage-DV8hzwFf.js b/internal/frontend/dist/assets/SystemPage-Cwt4SToT.js similarity index 94% rename from internal/frontend/dist/assets/SystemPage-DV8hzwFf.js rename to internal/frontend/dist/assets/SystemPage-Cwt4SToT.js index c2c7cd5..27a8a20 100644 --- a/internal/frontend/dist/assets/SystemPage-DV8hzwFf.js +++ b/internal/frontend/dist/assets/SystemPage-Cwt4SToT.js @@ -1 +1 @@ -import{S as e,T as t}from"./query-DWNqGEie.js";import{t as n}from"./MetricCard-GfGMWPS-.js";import{t as r}from"./circle-x-DYIKQ94H.js";import{t as i}from"./clock-3-BeKI5fxN.js";import{t as a}from"./database-BRZrCBcR.js";import{t as o}from"./server-BOAQDuBN.js";import{t as s}from"./shield-check-Dm-p0u_h.js";import{F as c,I as l,M as u,O as d,a as f,f as p,i as m,k as h,n as g,o as _,s as v,w as y}from"./index-B84OiuS0.js";import{n as b,t as x}from"./PageHeader-DGX-y_Bi.js";import{n as S,t as C}from"./badge-D4YKBNKr.js";import{i as w,n as T,r as E,t as D}from"./card-DEXft0Cm.js";var O=l(`box`,[[`path`,{d:`M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z`,key:`hh9hay`}],[`path`,{d:`m3.3 7 8.7 5 8.7-5`,key:`g66t2b`}],[`path`,{d:`M12 22V12`,key:`d0xqtd`}]]),k=l(`code-xml`,[[`path`,{d:`m18 16 4-4-4-4`,key:`1inbqp`}],[`path`,{d:`m6 8-4 4 4 4`,key:`15zrgr`}],[`path`,{d:`m14.5 4-5 16`,key:`e7oirm`}]]),A=l(`cpu`,[[`path`,{d:`M12 20v2`,key:`1lh1kg`}],[`path`,{d:`M12 2v2`,key:`tus03m`}],[`path`,{d:`M17 20v2`,key:`1rnc9c`}],[`path`,{d:`M17 2v2`,key:`11trls`}],[`path`,{d:`M2 12h2`,key:`1t8f8n`}],[`path`,{d:`M2 17h2`,key:`7oei6x`}],[`path`,{d:`M2 7h2`,key:`asdhe0`}],[`path`,{d:`M20 12h2`,key:`1q8mjw`}],[`path`,{d:`M20 17h2`,key:`1fpfkl`}],[`path`,{d:`M20 7h2`,key:`1o8tra`}],[`path`,{d:`M7 20v2`,key:`4gnj0m`}],[`path`,{d:`M7 2v2`,key:`1i4yhu`}],[`rect`,{x:`4`,y:`4`,width:`16`,height:`16`,rx:`2`,key:`1vbyd7`}],[`rect`,{x:`8`,y:`8`,width:`8`,height:`8`,rx:`1`,key:`z9xiuo`}]]),j=l(`hard-drive`,[[`path`,{d:`M10 16h.01`,key:`1bzywj`}],[`path`,{d:`M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z`,key:`18tbho`}],[`path`,{d:`M21.946 12.013H2.054`,key:`zqlbp7`}],[`path`,{d:`M6 16h.01`,key:`1pmjb7`}]]),M=t();function N(){let t=e({queryKey:g.system,queryFn:y.system,refetchInterval:6e4}),l=e({queryKey:[`health`],queryFn:y.health,refetchInterval:3e4});if(t.isLoading)return(0,M.jsx)(f,{label:`Inspecting runtime health and versions…`});if(t.error||!t.data)return(0,M.jsx)(m,{error:t.error??Error(`System information is unavailable.`),onRetry:()=>void t.refetch()});let N=t.data,L=Array.isArray(N.providers)?N.providers:[],R=Array.isArray(N.interfaces)?N.interfaces:[];return(0,M.jsxs)(M.Fragment,{children:[(0,M.jsx)(x,{title:`Runtime facts, without secrets.`,description:`Build identity, database health, provider availability, interface state, and application uptime.`,actions:(0,M.jsxs)(v,{variant:`outline`,onClick:()=>{t.refetch(),l.refetch()},disabled:t.isFetching,children:[t.isFetching?(0,M.jsx)(_,{}):(0,M.jsx)(d,{className:`h-4 w-4`}),`Refresh`]})}),(0,M.jsxs)(`div`,{className:`mb-5 flex items-center gap-3 rounded-xl border border-emerald-500/25 bg-emerald-500/[.055] p-4`,children:[(0,M.jsx)(`span`,{className:`grid h-9 w-9 place-items-center rounded-lg bg-emerald-500/10 text-emerald-500`,children:(0,M.jsx)(s,{className:`h-4 w-4`})}),(0,M.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,M.jsx)(`p`,{className:`text-sm font-semibold`,children:l.isError?`Health check unavailable`:`Application ${l.data?.status??`healthy`}`}),(0,M.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Sensitive environment variables and arbitrary filesystem content are never exposed here.`})]}),(0,M.jsxs)(C,{tone:l.isError?`danger`:`success`,className:`gap-1`,children:[(0,M.jsx)(S,{active:!l.isError}),l.isError?`Degraded`:`Healthy`]})]}),(0,M.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2 xl:grid-cols-4`,children:[(0,M.jsx)(n,{label:`Uptime`,value:I(N.uptimeSeconds),detail:`MultiSpeed ${N.version||`development`}`,icon:i,tone:`emerald`}),(0,M.jsx)(n,{label:`Database`,value:p(N.databaseSizeBytes),detail:`Schema ${String(N.schemaVersion??`unknown`)}`,icon:a,tone:`cyan`}),(0,M.jsx)(n,{label:`Persisted tasks`,value:String(N.taskCount??0),detail:`${N.runningTaskCount??0} currently active`,icon:c,tone:`violet`}),(0,M.jsx)(n,{label:`Results`,value:String(N.resultCount??0),detail:`${R.filter(e=>e.operational).length} interfaces up`,icon:j,tone:`orange`})]}),(0,M.jsxs)(`div`,{className:`mt-5 grid gap-5 xl:grid-cols-2`,children:[(0,M.jsxs)(D,{children:[(0,M.jsx)(E,{children:(0,M.jsxs)(w,{className:`flex items-center gap-2`,children:[(0,M.jsx)(O,{className:`h-4 w-4 text-primary`}),`Build & runtime`]})}),(0,M.jsx)(T,{children:(0,M.jsx)(P,{items:[[`MultiSpeed version`,N.version],[`Git commit`,N.gitCommit],[`Build date`,N.buildDate||N.buildTime],[`Go version`,N.goVersion],[`Operating system`,N.operatingSystem],[`Architecture`,N.architecture]]})})]}),(0,M.jsxs)(D,{children:[(0,M.jsx)(E,{children:(0,M.jsxs)(w,{className:`flex items-center gap-2`,children:[(0,M.jsx)(a,{className:`h-4 w-4 text-primary`}),`Persistence`]})}),(0,M.jsx)(T,{children:(0,M.jsx)(P,{items:[[`Database path`,N.databasePath],[`Database size`,p(N.databaseSizeBytes)],[`Schema version`,String(N.schemaVersion??`—`)],[`Task count`,String(N.taskCount??0)],[`Result count`,String(N.resultCount??0)],[`Running tasks`,String(N.runningTaskCount??0)]]})})]})]}),(0,M.jsxs)(`section`,{className:`mt-7`,children:[(0,M.jsx)(b,{title:`Provider runtime`,description:`Executables and native adapter availability`}),(0,M.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-3`,children:[L.map(e=>(0,M.jsxs)(D,{className:`p-4`,children:[(0,M.jsxs)(`div`,{className:`flex items-start justify-between`,children:[(0,M.jsx)(`span`,{className:`grid h-9 w-9 place-items-center rounded-lg ${e.available?`bg-emerald-500/10 text-emerald-500`:`bg-rose-500/10 text-rose-500`}`,children:e.available?(0,M.jsx)(u,{className:`h-4 w-4`}):(0,M.jsx)(r,{className:`h-4 w-4`})}),(0,M.jsx)(C,{tone:e.available?`success`:`danger`,children:e.available?`Available`:`Unavailable`})]}),(0,M.jsx)(`h3`,{className:`mt-3 text-sm font-bold`,children:e.displayName}),(0,M.jsx)(`p`,{className:`mt-1 font-mono text-[10px] text-muted-foreground`,children:e.version||`Version unavailable`}),e.message?(0,M.jsx)(`p`,{className:`mt-3 text-xs leading-5 text-muted-foreground`,children:e.message}):null]},e.id)),L.length===0?(0,M.jsx)(D,{className:`p-4 text-xs text-muted-foreground md:col-span-3`,children:`Provider runtime details were not included by this build.`}):null]})]}),(0,M.jsxs)(`section`,{className:`mt-7`,children:[(0,M.jsx)(b,{title:`Interface snapshot`,description:`${R.length} detected in the active Linux network namespace`}),(0,M.jsx)(D,{className:`overflow-hidden`,children:(0,M.jsx)(`div`,{className:`overflow-x-auto`,children:(0,M.jsxs)(`table`,{className:`data-table w-full min-w-[760px]`,children:[(0,M.jsx)(`thead`,{children:(0,M.jsxs)(`tr`,{children:[(0,M.jsx)(`th`,{children:`Interface`}),(0,M.jsx)(`th`,{children:`State`}),(0,M.jsx)(`th`,{children:`MAC address`}),(0,M.jsx)(`th`,{children:`MTU`}),(0,M.jsx)(`th`,{children:`Addresses`}),(0,M.jsx)(`th`,{children:`Kind`})]})}),(0,M.jsx)(`tbody`,{children:R.map(e=>(0,M.jsxs)(`tr`,{children:[(0,M.jsx)(`td`,{className:`font-semibold`,children:(0,M.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,M.jsx)(h,{className:`h-3.5 w-3.5 text-primary`}),e.name]})}),(0,M.jsx)(`td`,{children:(0,M.jsxs)(C,{tone:e.operational?`success`:`neutral`,className:`gap-1`,children:[(0,M.jsx)(S,{active:e.operational}),e.operational?`Up`:`Down`]})}),(0,M.jsx)(`td`,{className:`font-mono text-xs`,children:e.macAddress||`—`}),(0,M.jsx)(`td`,{children:e.mtu}),(0,M.jsx)(`td`,{className:`max-w-md font-mono text-[11px]`,children:(Array.isArray(e.addresses)?e.addresses:[]).map(e=>e.address).join(` · `)||`—`}),(0,M.jsx)(`td`,{children:e.loopback?`Loopback`:e.virtual?`Virtual`:`Physical`})]},e.name))})]})})})]}),(0,M.jsxs)(D,{className:`mt-7`,children:[(0,M.jsx)(E,{children:(0,M.jsxs)(w,{className:`flex items-center gap-2`,children:[(0,M.jsx)(k,{className:`h-4 w-4 text-primary`}),`Runtime identifiers`]})}),(0,M.jsx)(T,{children:(0,M.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-3`,children:[(0,M.jsx)(F,{icon:A,label:`Platform`,value:`${N.operatingSystem||`unknown`}/${N.architecture||`unknown`}`}),(0,M.jsx)(F,{icon:o,label:`Go runtime`,value:N.goVersion||`Unknown`}),(0,M.jsx)(F,{icon:a,label:`Schema`,value:String(N.schemaVersion??`Unknown`)})]})})]})]})}function P({items:e}){return(0,M.jsx)(`dl`,{className:`divide-y divide-border`,children:e.map(([e,t])=>(0,M.jsxs)(`div`,{className:`grid gap-1 py-2.5 first:pt-0 last:pb-0 sm:grid-cols-[145px_1fr]`,children:[(0,M.jsx)(`dt`,{className:`text-xs text-muted-foreground`,children:e}),(0,M.jsx)(`dd`,{className:`break-all font-mono text-xs font-medium text-foreground`,children:t||`—`})]},e))})}function F({icon:e,label:t,value:n}){return(0,M.jsxs)(`div`,{className:`flex items-center gap-3 rounded-lg border border-border bg-background p-3`,children:[(0,M.jsx)(`span`,{className:`grid h-8 w-8 place-items-center rounded-lg bg-primary/10 text-primary`,children:(0,M.jsx)(e,{className:`h-4 w-4`})}),(0,M.jsxs)(`div`,{children:[(0,M.jsx)(`p`,{className:`text-[10px] uppercase tracking-wider text-muted-foreground`,children:t}),(0,M.jsx)(`p`,{className:`mt-0.5 font-mono text-xs font-semibold`,children:n})]})]})}function I(e){if(!Number.isFinite(e))return`—`;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t?`${t}d ${n}h`:n?`${n}h ${r}m`:`${r}m`}export{N as default}; \ No newline at end of file +import{S as e,T as t}from"./query-DWNqGEie.js";import{t as n}from"./MetricCard-CMX2GS_z.js";import{t as r}from"./circle-x-CJ7LJdu2.js";import{t as i}from"./clock-3-DhdKy9KD.js";import{t as a}from"./database-DuA3AOvM.js";import{t as o}from"./server-BWQHmE7V.js";import{t as s}from"./shield-check-DrVussKH.js";import{F as c,I as l,M as u,O as d,a as f,f as p,i as m,k as h,n as g,o as _,s as v,w as y}from"./index-DleN6wGz.js";import{n as b,t as x}from"./PageHeader-DGX-y_Bi.js";import{n as S,t as C}from"./badge-BWAHg20h.js";import{i as w,n as T,r as E,t as D}from"./card-BtXBd7n8.js";var O=l(`box`,[[`path`,{d:`M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z`,key:`hh9hay`}],[`path`,{d:`m3.3 7 8.7 5 8.7-5`,key:`g66t2b`}],[`path`,{d:`M12 22V12`,key:`d0xqtd`}]]),k=l(`code-xml`,[[`path`,{d:`m18 16 4-4-4-4`,key:`1inbqp`}],[`path`,{d:`m6 8-4 4 4 4`,key:`15zrgr`}],[`path`,{d:`m14.5 4-5 16`,key:`e7oirm`}]]),A=l(`cpu`,[[`path`,{d:`M12 20v2`,key:`1lh1kg`}],[`path`,{d:`M12 2v2`,key:`tus03m`}],[`path`,{d:`M17 20v2`,key:`1rnc9c`}],[`path`,{d:`M17 2v2`,key:`11trls`}],[`path`,{d:`M2 12h2`,key:`1t8f8n`}],[`path`,{d:`M2 17h2`,key:`7oei6x`}],[`path`,{d:`M2 7h2`,key:`asdhe0`}],[`path`,{d:`M20 12h2`,key:`1q8mjw`}],[`path`,{d:`M20 17h2`,key:`1fpfkl`}],[`path`,{d:`M20 7h2`,key:`1o8tra`}],[`path`,{d:`M7 20v2`,key:`4gnj0m`}],[`path`,{d:`M7 2v2`,key:`1i4yhu`}],[`rect`,{x:`4`,y:`4`,width:`16`,height:`16`,rx:`2`,key:`1vbyd7`}],[`rect`,{x:`8`,y:`8`,width:`8`,height:`8`,rx:`1`,key:`z9xiuo`}]]),j=l(`hard-drive`,[[`path`,{d:`M10 16h.01`,key:`1bzywj`}],[`path`,{d:`M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z`,key:`18tbho`}],[`path`,{d:`M21.946 12.013H2.054`,key:`zqlbp7`}],[`path`,{d:`M6 16h.01`,key:`1pmjb7`}]]),M=t();function N(){let t=e({queryKey:g.system,queryFn:y.system,refetchInterval:6e4}),l=e({queryKey:[`health`],queryFn:y.health,refetchInterval:3e4});if(t.isLoading)return(0,M.jsx)(f,{label:`Inspecting runtime health and versions…`});if(t.error||!t.data)return(0,M.jsx)(m,{error:t.error??Error(`System information is unavailable.`),onRetry:()=>void t.refetch()});let N=t.data,L=Array.isArray(N.providers)?N.providers:[],R=Array.isArray(N.interfaces)?N.interfaces:[];return(0,M.jsxs)(M.Fragment,{children:[(0,M.jsx)(x,{title:`Runtime facts, without secrets.`,description:`Build identity, database health, provider availability, interface state, and application uptime.`,actions:(0,M.jsxs)(v,{variant:`outline`,onClick:()=>{t.refetch(),l.refetch()},disabled:t.isFetching,children:[t.isFetching?(0,M.jsx)(_,{}):(0,M.jsx)(d,{className:`h-4 w-4`}),`Refresh`]})}),(0,M.jsxs)(`div`,{className:`mb-5 flex items-center gap-3 rounded-xl border border-emerald-500/25 bg-emerald-500/[.055] p-4`,children:[(0,M.jsx)(`span`,{className:`grid h-9 w-9 place-items-center rounded-lg bg-emerald-500/10 text-emerald-500`,children:(0,M.jsx)(s,{className:`h-4 w-4`})}),(0,M.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,M.jsx)(`p`,{className:`text-sm font-semibold`,children:l.isError?`Health check unavailable`:`Application ${l.data?.status??`healthy`}`}),(0,M.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Sensitive environment variables and arbitrary filesystem content are never exposed here.`})]}),(0,M.jsxs)(C,{tone:l.isError?`danger`:`success`,className:`gap-1`,children:[(0,M.jsx)(S,{active:!l.isError}),l.isError?`Degraded`:`Healthy`]})]}),(0,M.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2 xl:grid-cols-4`,children:[(0,M.jsx)(n,{label:`Uptime`,value:I(N.uptimeSeconds),detail:`MultiSpeed ${N.version||`development`}`,icon:i,tone:`emerald`}),(0,M.jsx)(n,{label:`Database`,value:p(N.databaseSizeBytes),detail:`Schema ${String(N.schemaVersion??`unknown`)}`,icon:a,tone:`cyan`}),(0,M.jsx)(n,{label:`Persisted tasks`,value:String(N.taskCount??0),detail:`${N.runningTaskCount??0} currently active`,icon:c,tone:`violet`}),(0,M.jsx)(n,{label:`Results`,value:String(N.resultCount??0),detail:`${R.filter(e=>e.operational).length} interfaces up`,icon:j,tone:`orange`})]}),(0,M.jsxs)(`div`,{className:`mt-5 grid gap-5 xl:grid-cols-2`,children:[(0,M.jsxs)(D,{children:[(0,M.jsx)(E,{children:(0,M.jsxs)(w,{className:`flex items-center gap-2`,children:[(0,M.jsx)(O,{className:`h-4 w-4 text-primary`}),`Build & runtime`]})}),(0,M.jsx)(T,{children:(0,M.jsx)(P,{items:[[`MultiSpeed version`,N.version],[`Git commit`,N.gitCommit],[`Build date`,N.buildDate||N.buildTime],[`Go version`,N.goVersion],[`Operating system`,N.operatingSystem],[`Architecture`,N.architecture]]})})]}),(0,M.jsxs)(D,{children:[(0,M.jsx)(E,{children:(0,M.jsxs)(w,{className:`flex items-center gap-2`,children:[(0,M.jsx)(a,{className:`h-4 w-4 text-primary`}),`Persistence`]})}),(0,M.jsx)(T,{children:(0,M.jsx)(P,{items:[[`Database path`,N.databasePath],[`Database size`,p(N.databaseSizeBytes)],[`Schema version`,String(N.schemaVersion??`—`)],[`Task count`,String(N.taskCount??0)],[`Result count`,String(N.resultCount??0)],[`Running tasks`,String(N.runningTaskCount??0)]]})})]})]}),(0,M.jsxs)(`section`,{className:`mt-7`,children:[(0,M.jsx)(b,{title:`Provider runtime`,description:`Executables and native adapter availability`}),(0,M.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-3`,children:[L.map(e=>(0,M.jsxs)(D,{className:`p-4`,children:[(0,M.jsxs)(`div`,{className:`flex items-start justify-between`,children:[(0,M.jsx)(`span`,{className:`grid h-9 w-9 place-items-center rounded-lg ${e.available?`bg-emerald-500/10 text-emerald-500`:`bg-rose-500/10 text-rose-500`}`,children:e.available?(0,M.jsx)(u,{className:`h-4 w-4`}):(0,M.jsx)(r,{className:`h-4 w-4`})}),(0,M.jsx)(C,{tone:e.available?`success`:`danger`,children:e.available?`Available`:`Unavailable`})]}),(0,M.jsx)(`h3`,{className:`mt-3 text-sm font-bold`,children:e.displayName}),(0,M.jsx)(`p`,{className:`mt-1 font-mono text-[10px] text-muted-foreground`,children:e.version||`Version unavailable`}),e.message?(0,M.jsx)(`p`,{className:`mt-3 text-xs leading-5 text-muted-foreground`,children:e.message}):null]},e.id)),L.length===0?(0,M.jsx)(D,{className:`p-4 text-xs text-muted-foreground md:col-span-3`,children:`Provider runtime details were not included by this build.`}):null]})]}),(0,M.jsxs)(`section`,{className:`mt-7`,children:[(0,M.jsx)(b,{title:`Interface snapshot`,description:`${R.length} detected in the active Linux network namespace`}),(0,M.jsx)(D,{className:`overflow-hidden`,children:(0,M.jsx)(`div`,{className:`overflow-x-auto`,children:(0,M.jsxs)(`table`,{className:`data-table w-full min-w-[760px]`,children:[(0,M.jsx)(`thead`,{children:(0,M.jsxs)(`tr`,{children:[(0,M.jsx)(`th`,{children:`Interface`}),(0,M.jsx)(`th`,{children:`State`}),(0,M.jsx)(`th`,{children:`MAC address`}),(0,M.jsx)(`th`,{children:`MTU`}),(0,M.jsx)(`th`,{children:`Addresses`}),(0,M.jsx)(`th`,{children:`Kind`})]})}),(0,M.jsx)(`tbody`,{children:R.map(e=>(0,M.jsxs)(`tr`,{children:[(0,M.jsx)(`td`,{className:`font-semibold`,children:(0,M.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,M.jsx)(h,{className:`h-3.5 w-3.5 text-primary`}),e.name]})}),(0,M.jsx)(`td`,{children:(0,M.jsxs)(C,{tone:e.operational?`success`:`neutral`,className:`gap-1`,children:[(0,M.jsx)(S,{active:e.operational}),e.operational?`Up`:`Down`]})}),(0,M.jsx)(`td`,{className:`font-mono text-xs`,children:e.macAddress||`—`}),(0,M.jsx)(`td`,{children:e.mtu}),(0,M.jsx)(`td`,{className:`max-w-md font-mono text-[11px]`,children:(Array.isArray(e.addresses)?e.addresses:[]).map(e=>e.address).join(` · `)||`—`}),(0,M.jsx)(`td`,{children:e.loopback?`Loopback`:e.virtual?`Virtual`:`Physical`})]},e.name))})]})})})]}),(0,M.jsxs)(D,{className:`mt-7`,children:[(0,M.jsx)(E,{children:(0,M.jsxs)(w,{className:`flex items-center gap-2`,children:[(0,M.jsx)(k,{className:`h-4 w-4 text-primary`}),`Runtime identifiers`]})}),(0,M.jsx)(T,{children:(0,M.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-3`,children:[(0,M.jsx)(F,{icon:A,label:`Platform`,value:`${N.operatingSystem||`unknown`}/${N.architecture||`unknown`}`}),(0,M.jsx)(F,{icon:o,label:`Go runtime`,value:N.goVersion||`Unknown`}),(0,M.jsx)(F,{icon:a,label:`Schema`,value:String(N.schemaVersion??`Unknown`)})]})})]})]})}function P({items:e}){return(0,M.jsx)(`dl`,{className:`divide-y divide-border`,children:e.map(([e,t])=>(0,M.jsxs)(`div`,{className:`grid gap-1 py-2.5 first:pt-0 last:pb-0 sm:grid-cols-[145px_1fr]`,children:[(0,M.jsx)(`dt`,{className:`text-xs text-muted-foreground`,children:e}),(0,M.jsx)(`dd`,{className:`break-all font-mono text-xs font-medium text-foreground`,children:t||`—`})]},e))})}function F({icon:e,label:t,value:n}){return(0,M.jsxs)(`div`,{className:`flex items-center gap-3 rounded-lg border border-border bg-background p-3`,children:[(0,M.jsx)(`span`,{className:`grid h-8 w-8 place-items-center rounded-lg bg-primary/10 text-primary`,children:(0,M.jsx)(e,{className:`h-4 w-4`})}),(0,M.jsxs)(`div`,{children:[(0,M.jsx)(`p`,{className:`text-[10px] uppercase tracking-wider text-muted-foreground`,children:t}),(0,M.jsx)(`p`,{className:`mt-0.5 font-mono text-xs font-semibold`,children:n})]})]})}function I(e){if(!Number.isFinite(e))return`—`;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t?`${t}d ${n}h`:n?`${n}h ${r}m`:`${r}m`}export{N as default}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/TaskEditorPage-BwxKINaO.js b/internal/frontend/dist/assets/TaskEditorPage-BwxKINaO.js deleted file mode 100644 index e264241..0000000 --- a/internal/frontend/dist/assets/TaskEditorPage-BwxKINaO.js +++ /dev/null @@ -1 +0,0 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,w as i,x as a}from"./query-DWNqGEie.js";import{b as o,d as s,y as c}from"./react-DsKPNe9r.js";import{t as l}from"./arrow-left-CkoT9SwQ.js";import{i as u,n as d,o as f,r as p,t as m}from"./fields-PJGT-Jp7.js";import{t as h}from"./clock-3-BeKI5fxN.js";import{a as g,i as _,l as ee,n as v,o as y,r as b,s as x,t as S}from"./OoklaEulaAcceptance-B4WQoWrJ.js";import{t as C}from"./radio-tower-C96Q3yet.js";import{t as w}from"./search-B8uA6mdW.js";import{t as T}from"./server-BOAQDuBN.js";import{t as te}from"./shield-check-Dm-p0u_h.js";import{A as E,D,I as O,M as ne,T as re,a as ie,c as k,i as ae,j as A,k as oe,n as j,o as M,p as N,r as P,s as F,t as se,v as I,w as L}from"./index-B84OiuS0.js";import{n as ce}from"./useAppSettings-CeA9vkHH.js";import{t as le}from"./PageHeader-DGX-y_Bi.js";import{n as ue,t as de}from"./badge-D4YKBNKr.js";import{i as R,n as z,r as B,t as V}from"./card-DEXft0Cm.js";import{t as fe}from"./EntityBadges-DZDjvrcw.js";import{a as H,c as U,i as W,l as pe,n as G,o as me,r as K,s as he,t as ge,u as q}from"./schemas-DaFWBMkt.js";var _e=O(`arrow-right`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`m12 5 7 7-7 7`,key:`xquz4c`}]]),ve=O(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]),ye=O(`cloud`,[[`path`,{d:`M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z`,key:`p7xjir`}]]),J=e(r(),1),Y=n(),be=`Use a safe relative endpoint without a leading slash, traversal, backslash, encoded separator, control character, scheme, host, or fragment.`,X=H().max(2048).refine(e=>!Array.from(e).some(Oe),be).transform(e=>e.trim()).refine(De,be),xe=W({name:H().trim().min(2,`Enter at least 2 characters.`).max(100),description:H().trim().max(500),enabled:G(),provider:ge([`ookla`,`librespeed`,`cloudflare`]),cronExpression:H().trim().refine(x,`Enter a valid five-field cron expression.`),timezone:H().trim().min(1,`Select a timezone.`).refine(_,`Select a valid IANA timezone.`),randomJitterSeconds:K().int().min(0).max(3600),serverSelectionMode:ge([`automatic`,`fixed`,`custom`]),serverId:H().trim().max(200),serverUrl:H().trim().max(2048),customServerName:H().trim().max(200),customDownloadPath:X,customUploadPath:X,customPingPath:X,customIpPath:X,allowInsecureCustomServer:G(),interfaceName:H().trim().min(1,`Select a concrete interface.`),sourceIp:H().trim().min(1,`Select one concrete source address.`),ipFamily:ge([`auto`,`ipv4`,`ipv6`]),routeProfileId:H(),timeoutSeconds:K().int().min(5).max(3600),preventOverlap:G(),routeValidation:ge([`required`,`interface-only`]),skipTlsVerification:G()}).superRefine((e,t)=>{if(e.provider===`cloudflare`&&e.serverSelectionMode!==`automatic`&&t.addIssue({code:`custom`,path:[`serverSelectionMode`],message:`Cloudflare always uses automatic edge selection.`}),e.serverSelectionMode===`fixed`&&!e.serverId&&t.addIssue({code:`custom`,path:[`serverId`],message:`Select or enter a server ID.`}),e.serverSelectionMode===`custom`){e.provider!==`librespeed`&&t.addIssue({code:`custom`,path:[`serverSelectionMode`],message:`Custom URLs are supported by LibreSpeed only.`});let n=me({protocol:/^https?$/}).safeParse(e.serverUrl);n.success||t.addIssue({code:`custom`,path:[`serverUrl`],message:`Enter a valid HTTP or HTTPS URL.`}),n.success&&n.data.toLowerCase().startsWith(`http:`)&&!e.allowInsecureCustomServer&&t.addIssue({code:`custom`,path:[`allowInsecureCustomServer`],message:`Explicitly allow plain HTTP or use HTTPS.`})}}),Se={name:``,description:``,enabled:!0,provider:`cloudflare`,cronExpression:`0 */6 * * *`,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone||`UTC`,randomJitterSeconds:0,serverSelectionMode:`automatic`,serverId:``,serverUrl:``,customServerName:``,customDownloadPath:``,customUploadPath:``,customPingPath:``,customIpPath:``,allowInsecureCustomServer:!1,interfaceName:``,sourceIp:``,ipFamily:`auto`,routeProfileId:``,timeoutSeconds:120,preventOverlap:!0,routeValidation:`required`,skipTlsVerification:!1};function Ce(e){return{...Se,timezone:e.defaultTimezone||`UTC`,timeoutSeconds:e.defaultTaskTimeoutSeconds}}function we(e){return{name:e.name,description:e.description,enabled:e.enabled,provider:e.provider,cronExpression:e.cronExpression,timezone:e.timezone,randomJitterSeconds:e.randomJitterSeconds,serverSelectionMode:e.serverSelectionMode,serverId:e.serverId,serverUrl:e.serverUrl,customServerName:Z(e.customServerDefinition,`name`),customDownloadPath:Z(e.customServerDefinition,`dlURL`),customUploadPath:Z(e.customServerDefinition,`ulURL`),customPingPath:Z(e.customServerDefinition,`pingURL`),customIpPath:Z(e.customServerDefinition,`getIpURL`),allowInsecureCustomServer:e.customServerDefinition.allowInsecure===!0,interfaceName:e.interfaceName,sourceIp:e.sourceIp,ipFamily:e.ipFamily,routeProfileId:e.routeProfileId??``,timeoutSeconds:e.timeoutSeconds,preventOverlap:e.preventOverlap,routeValidation:e.routeValidation,skipTlsVerification:e.providerOptions.skipTlsVerification===!0}}function Te(e,t){let n=e.provider===`cloudflare`?`automatic`:e.serverSelectionMode,r={...t?.customServerDefinition??{}};e.provider===`librespeed`&&(Q(r,`name`,e.customServerName),Q(r,`dlURL`,e.customDownloadPath),Q(r,`ulURL`,e.customUploadPath),Q(r,`pingURL`,e.customPingPath),Q(r,`getIpURL`,e.customIpPath),e.allowInsecureCustomServer?r.allowInsecure=!0:delete r.allowInsecure);let i={...t?.providerOptions??{}};return e.provider===`librespeed`&&(i.telemetry=!1,i.skipTlsVerification=e.skipTlsVerification),{name:e.name,description:e.description,enabled:e.enabled,provider:e.provider,cronExpression:e.cronExpression,timezone:e.timezone,randomJitterSeconds:e.randomJitterSeconds,serverSelectionMode:n,serverId:n===`fixed`?e.serverId:``,serverUrl:n===`custom`?e.serverUrl:``,customServerDefinition:r,interfaceName:e.interfaceName,sourceIp:e.sourceIp,ipFamily:e.ipFamily,routeProfileId:e.routeProfileId||null,timeoutSeconds:e.timeoutSeconds,providerOptions:i,preventOverlap:e.preventOverlap,routeValidation:e.routeValidation}}function Z(e,t){return typeof e[t]==`string`?e[t]:``}function Q(e,t,n){e[t]=n||void 0}function Ee(e){return JSON.stringify(e)}function De(e){if(!e)return!0;let t=e.toLowerCase();if(e.startsWith(`/`)||e.includes(`\\`)||t.includes(`%2f`)||t.includes(`%5c`)||t.includes(`%00`)||t.includes(`%0a`)||t.includes(`%0d`)||e.includes(`#`)||/^[A-Za-z][A-Za-z0-9+.-]*:/.test(e))return!1;let n=e.split(`?`,1)[0];if(!n)return!1;for(let e of n.split(`/`)){if(!e)return!1;let t;try{t=decodeURIComponent(e)}catch{return!1}if(t===`.`||t===`..`)return!1}try{let t=new URL(e,`https://multispeed.invalid/`);return t.origin===`https://multispeed.invalid`&&!t.username&&!t.password&&!t.hash}catch{return!1}}function Oe(e){let t=e.charCodeAt(0);return t<=31||t===127}var $=[{title:`Identity & provider`,description:`Name the test and select its measurement methodology.`,icon:A},{title:`Target & network`,description:`Pin the server and concrete Linux source path.`,icon:oe},{title:`Schedule & safety`,description:`Control timing, timeout, jitter, and overlap.`,icon:h},{title:`Review & validate`,description:`Confirm the persisted task before activation.`,icon:te}],ke={0:[`name`,`description`,`provider`],1:[`serverSelectionMode`,`serverId`,`serverUrl`,`customServerName`,`customDownloadPath`,`customUploadPath`,`customPingPath`,`customIpPath`,`allowInsecureCustomServer`,`interfaceName`,`sourceIp`,`ipFamily`,`routeProfileId`],2:[`cronExpression`,`timezone`,`randomJitterSeconds`,`timeoutSeconds`,`preventOverlap`,`routeValidation`],3:[]};function Ae(){let{taskId:e}=o(),n=!!e,r=c(),u=i(),{toast:d}=se(),[f,p]=(0,J.useState)(0),[m,h]=(0,J.useState)(!1),[_,v]=(0,J.useState)([]),[y,b]=(0,J.useState)(null),x=ce(),S=t({queryKey:j.task(e??`new`),queryFn:()=>L.task(e??``),enabled:n}),C=t({queryKey:[...j.interfaces,`task-editor`,!0,!0],queryFn:()=>L.interfaces({includeDown:!0,includeVirtual:!0})}),w=t({queryKey:j.routes,queryFn:L.routes}),T=t({queryKey:j.providers,queryFn:L.providers}),te=(0,J.useMemo)(()=>S.data?we(S.data):Ce(x.settings),[x.settings,S.data]),E=pe({resolver:he(xe),defaultValues:te,values:te,resetOptions:{keepDirtyValues:!0},mode:`onBlur`}),O=q({control:E.control}),ne=O.provider??`cloudflare`,A=O.interfaceName??``,oe=O.cronExpression??``,N=O.timezone??`UTC`;(0,J.useEffect)(()=>{let e=!0;return g(oe,N).then(t=>e&&v(t)),()=>{e=!1}},[oe,N]);let P=a({mutationFn:t=>n&&e?L.updateTask(e,t):L.createTask(t),onSuccess:e=>{u.invalidateQueries({queryKey:j.tasks}),d({tone:`success`,title:n?`Task updated`:`Task created`,description:`${e.name} was persisted and its schedule recalculated.`}),r(`/tasks`)},onError:e=>d({tone:`error`,title:n?`Unable to update task`:`Unable to create task`,description:re(e)})}),I=a({mutationFn:async({input:e,signature:t})=>({signature:t,validation:await L.validateTaskInput(e)}),onMutate:()=>b(null),onSuccess:e=>{b(e),d({tone:e.validation.success?`success`:`error`,title:e.validation.success?`Current task path validated`:`Task preflight failed`,description:e.validation.message})},onError:e=>{b(null),d({tone:`error`,title:`Task preflight failed`,description:re(e)})}}),ue=e=>{let t=Te(e,S.data),n=Ee(t);if(e.enabled&&e.provider!==`ookla`&&(y?.signature!==n||!y.validation.success)){p($.length-1),d({tone:`error`,title:`Validate this enabled task first`,description:`Run a successful preflight for the current provider, target, and source path before saving.`});return}P.mutate(t)},de=async()=>{if(!await E.trigger()){d({tone:`error`,title:`Complete required task fields`,description:`Resolve the highlighted fields before route preflight.`});return}let e=Te(E.getValues(),S.data);I.mutate({input:e,signature:Ee(e)})},R=async()=>{await E.trigger(ke[f]??[])&&p(e=>Math.min(e+1,$.length-1))},z=S.error??C.error??w.error??T.error??x.error;if(n&&S.isLoading||C.isLoading||w.isLoading||T.isLoading||x.isLoading)return(0,Y.jsx)(ie,{label:`Loading provider and network capabilities…`});if(z)return(0,Y.jsx)(ae,{error:z,onRetry:()=>{S.refetch(),C.refetch(),w.refetch(),T.refetch(),x.refetch()}});let B=T.data??Re,fe=C.data??[],H=fe.filter(e=>!e.virtual||m||e.name===A),U=w.data??[],W=B.find(e=>e.id===ne),G=fe.find(e=>e.name===A)?.addresses.filter(e=>!e.linkLocal&&(O.ipFamily===`auto`||e.family===O.ipFamily))??[],me=Ee(Te(O,S.data)),K=y?.signature===me?y.validation:null;return(0,Y.jsxs)(`form`,{onSubmit:E.handleSubmit(ue),noValidate:!0,children:[(0,Y.jsx)(le,{title:n?`Edit ${S.data?.name??`task`}`:`Create an independent speed test`,description:`The selected interface and source address are mandatory. MultiSpeed never falls back to another WAN when binding or route validation fails.`,actions:(0,Y.jsx)(F,{asChild:!0,variant:`outline`,children:(0,Y.jsxs)(s,{to:`/tasks`,children:[(0,Y.jsx)(l,{className:`h-4 w-4`}),`Back to tasks`]})})}),n&&S.data?.networkPathValid===!1?(0,Y.jsxs)(`div`,{role:`alert`,className:`mb-5 flex gap-3 rounded-xl border border-rose-500/25 bg-rose-500/[.06] p-4 text-rose-700 dark:text-rose-300`,children:[(0,Y.jsx)(D,{className:`mt-0.5 h-5 w-5 shrink-0`}),(0,Y.jsxs)(`div`,{children:[(0,Y.jsx)(`p`,{className:`text-sm font-semibold`,children:`This task's network path is no longer valid`}),(0,Y.jsxs)(`p`,{className:`mt-1 text-xs leading-5`,children:[S.data.networkPathMessage||`The configured interface or source address is not available in the active Linux namespace.`,` Select a current interface and concrete source address before saving or running this task.`]})]})]}):null,(0,Y.jsxs)(`div`,{className:`grid gap-5 lg:grid-cols-[250px_minmax(0,1fr)]`,children:[(0,Y.jsx)(V,{className:`h-fit p-3 lg:sticky lg:top-24`,children:(0,Y.jsx)(`ol`,{className:`space-y-1`,"aria-label":`Task editor steps`,children:$.map((e,t)=>{let n=e.icon;return(0,Y.jsx)(`li`,{children:(0,Y.jsxs)(`button`,{type:`button`,onClick:()=>p(t),className:k(`flex w-full items-start gap-3 rounded-lg p-3 text-left transition focus-visible:ring-2 focus-visible:ring-ring`,f===t?`bg-accent text-accent-foreground`:`text-muted-foreground hover:bg-muted hover:text-foreground`),"aria-current":f===t?`step`:void 0,children:[(0,Y.jsx)(`span`,{className:k(`grid h-7 w-7 shrink-0 place-items-center rounded-lg border text-[11px] font-bold`,f===t?`border-primary/30 bg-primary/10 text-primary`:te.id===O.routeProfileId),cronRuns:_,preflight:K,configurationChanged:y!==null&&K===null,validating:I.isPending,onValidate:()=>void de()}):null,(0,Y.jsxs)(`div`,{className:`mt-5 flex items-center justify-between rounded-xl border border-border bg-card p-4 shadow-panel`,children:[(0,Y.jsxs)(F,{variant:`outline`,onClick:()=>p(e=>Math.max(e-1,0)),disabled:f===0||P.isPending,children:[(0,Y.jsx)(l,{className:`h-4 w-4`}),`Previous`]}),(0,Y.jsxs)(`div`,{className:`text-center text-[11px] text-muted-foreground`,children:[`Step `,f+1,` of `,$.length]}),f<$.length-1?(0,Y.jsxs)(F,{onClick:e=>{e.preventDefault(),R()},children:[`Continue`,(0,Y.jsx)(_e,{className:`h-4 w-4`})]}):(0,Y.jsxs)(F,{type:`submit`,disabled:P.isPending,children:[P.isPending?(0,Y.jsx)(M,{}):(0,Y.jsx)(ee,{className:`h-4 w-4`}),n?`Save changes`:`Create task`]})]})]})]})]})}function je({form:e,providers:t,activeProvider:n}){let{register:r,control:i,formState:{errors:a}}=e;return(0,Y.jsxs)(V,{children:[(0,Y.jsxs)(B,{children:[(0,Y.jsx)(R,{children:`Identity & measurement provider`}),(0,Y.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`Provider behavior remains isolated per task; changing this task cannot affect another schedule.`})]}),(0,Y.jsxs)(z,{className:`space-y-5`,children:[(0,Y.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,Y.jsx)(d,{label:`Task name`,required:!0,error:a.name?.message,children:(0,Y.jsx)(p,{...r(`name`),placeholder:`Fiber uplink · Frankfurt`,autoFocus:!0})}),(0,Y.jsx)(U,{control:i,name:`enabled`,render:({field:e})=>(0,Y.jsx)(m,{checked:e.value,onChange:e.onChange,label:`Task enabled`,description:`Schedule immediately after saving.`})})]}),(0,Y.jsx)(d,{label:`Description`,error:a.description?.message,hint:`Optional`,children:(0,Y.jsx)(f,{...r(`description`),placeholder:`Primary WAN baseline during business hours.`})}),(0,Y.jsxs)(`div`,{children:[(0,Y.jsxs)(`p`,{className:`mb-2 text-xs font-semibold text-foreground`,children:[`Provider `,(0,Y.jsx)(`span`,{className:`text-rose-500`,children:`*`})]}),(0,Y.jsx)(U,{control:i,name:`provider`,render:({field:n})=>(0,Y.jsx)(`div`,{className:`grid gap-3 md:grid-cols-3`,children:t.map(t=>{let r=t.id===`ookla`?A:t.id===`librespeed`?T:ye;return(0,Y.jsxs)(`button`,{type:`button`,onClick:()=>{n.onChange(t.id),(t.id===`cloudflare`||e.getValues(`serverSelectionMode`)===`custom`&&t.id===`ookla`)&&e.setValue(`serverSelectionMode`,`automatic`)},className:k(`rounded-xl border p-4 text-left transition focus-visible:ring-2 focus-visible:ring-ring`,n.value===t.id?`border-primary/50 bg-primary/[.07] shadow-sm`:`border-border bg-background hover:border-primary/25`,!t.available&&`opacity-65`),"aria-pressed":n.value===t.id,children:[(0,Y.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,Y.jsx)(`span`,{className:k(`grid h-9 w-9 place-items-center rounded-lg`,t.id===`ookla`?`bg-cyan-500/10 text-cyan-500`:t.id===`librespeed`?`bg-violet-500/10 text-violet-500`:`bg-orange-500/10 text-orange-500`),children:(0,Y.jsx)(r,{className:`h-4 w-4`})}),(0,Y.jsxs)(de,{tone:t.available?`success`:`danger`,className:`gap-1`,children:[(0,Y.jsx)(ue,{active:t.available}),t.available?`Available`:`Unavailable`]})]}),(0,Y.jsx)(`p`,{className:`mt-3 text-sm font-semibold`,children:t.displayName}),(0,Y.jsx)(`p`,{className:`mt-1 text-[11px] leading-4 text-muted-foreground`,children:t.id===`cloudflare`?`Native edge methodology with automatic colo selection.`:t.id===`ookla`?`Official CLI with broad fixed-server discovery.`:`Open-source CLI with public and custom backends.`})]},t.id)})})})]}),n&&!n.available?(0,Y.jsxs)(`div`,{className:`flex gap-3 rounded-lg border border-amber-500/25 bg-amber-500/[.07] p-4 text-sm`,children:[(0,Y.jsx)(D,{className:`mt-0.5 h-4 w-4 shrink-0 text-amber-500`}),(0,Y.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,Y.jsxs)(`p`,{className:`font-semibold text-foreground`,children:[n.displayName,` is not available`]}),(0,Y.jsx)(`p`,{className:`mt-1 text-xs leading-5 text-muted-foreground`,children:n.message||`Check provider installation details on System information.`}),n.id===`ookla`?(0,Y.jsx)(S,{compact:!0}):null]})]}):null]})]})}function Me({form:e,provider:t,interfaces:n,routes:r,addressOptions:i,showVirtualInterfaces:a,onShowVirtualInterfacesChange:o}){let{register:s,control:c,setValue:l,formState:{errors:f}}=e,h=q({control:c,name:`serverSelectionMode`}),g=q({control:c,name:`provider`}),_=q({control:c,name:`interfaceName`}),ee=q({control:c,name:`sourceIp`}),v=q({control:c,name:`ipFamily`}),y=q({control:c,name:`serverId`}),b=q({control:c,name:`skipTlsVerification`}),x=q({control:c,name:`allowInsecureCustomServer`});return(0,Y.jsxs)(`div`,{className:`space-y-5`,children:[(0,Y.jsxs)(V,{children:[(0,Y.jsxs)(B,{children:[(0,Y.jsx)(R,{children:`Target selection`}),(0,Y.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`Discovery uses the same network binding as the final measurement.`})]}),(0,Y.jsx)(z,{className:`space-y-5`,children:g===`cloudflare`?(0,Y.jsxs)(`div`,{className:`flex items-start gap-3 rounded-xl border border-orange-500/25 bg-orange-500/[.06] p-4`,children:[(0,Y.jsx)(ye,{className:`mt-0.5 h-5 w-5 shrink-0 text-orange-500`}),(0,Y.jsxs)(`div`,{children:[(0,Y.jsx)(`p`,{className:`text-sm font-semibold`,children:`Automatic edge selection`}),(0,Y.jsx)(`p`,{className:`mt-1 text-xs leading-5 text-muted-foreground`,children:`Cloudflare selects the reachable edge colo for this source path. Its native multi-sample methodology differs from Ookla and LibreSpeed.`})]})]}):(0,Y.jsxs)(Y.Fragment,{children:[(0,Y.jsx)(U,{control:c,name:`serverSelectionMode`,render:({field:e})=>(0,Y.jsx)(`div`,{className:`grid gap-2 sm:grid-cols-3`,children:[{value:`automatic`,label:`Automatic`,description:`Provider selects a target`},{value:`fixed`,label:`Fixed server`,description:`Persist a discovered ID`},...g===`librespeed`&&t?.capabilities.customServerUrls?[{value:`custom`,label:`Custom backend`,description:`Use your own HTTPS URL`}]:[]].map(t=>(0,Y.jsxs)(`button`,{type:`button`,onClick:()=>e.onChange(t.value),className:k(`rounded-lg border p-3 text-left transition`,e.value===t.value?`border-primary/50 bg-primary/[.07]`:`border-border bg-background hover:border-primary/25`),children:[(0,Y.jsx)(`span`,{className:`block text-xs font-semibold`,children:t.label}),(0,Y.jsx)(`span`,{className:`mt-0.5 block text-[10px] text-muted-foreground`,children:t.description})]},t.value))})}),g===`librespeed`&&!t?.capabilities.customServerUrls?(0,Y.jsxs)(`div`,{className:`flex gap-2 rounded-lg border border-amber-500/25 bg-amber-500/[.06] p-3 text-xs leading-5 text-amber-700 dark:text-amber-300`,children:[(0,Y.jsx)(D,{className:`mt-0.5 h-4 w-4 shrink-0`}),`Custom backends are disabled by this deployment. Add the exact base URL to APP_ALLOWED_CUSTOM_SERVER_URLS and restart MultiSpeed.`]}):null,h===`fixed`?(0,Y.jsx)(Ne,{provider:g,interfaceName:_,sourceIp:ee,ipFamily:v,value:y,onChange:e=>l(`serverId`,e,{shouldValidate:!0,shouldDirty:!0}),error:f.serverId?.message}):null,h===`custom`?(0,Y.jsxs)(`div`,{className:`space-y-4`,children:[(0,Y.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-2`,children:[(0,Y.jsx)(d,{label:`LibreSpeed backend URL`,required:!0,error:f.serverUrl?.message,children:(0,Y.jsx)(p,{type:`url`,...s(`serverUrl`),placeholder:`https://speed.example.net`})}),(0,Y.jsx)(d,{label:`Display name`,hint:`Optional`,error:f.customServerName?.message,children:(0,Y.jsx)(p,{...s(`customServerName`),placeholder:`Office LibreSpeed`})}),(0,Y.jsx)(d,{label:`Download endpoint`,hint:`Relative to backend URL`,error:f.customDownloadPath?.message,children:(0,Y.jsx)(p,{...s(`customDownloadPath`),placeholder:`garbage.php`})}),(0,Y.jsx)(d,{label:`Upload endpoint`,hint:`Relative to backend URL`,error:f.customUploadPath?.message,children:(0,Y.jsx)(p,{...s(`customUploadPath`),placeholder:`empty.php`})}),(0,Y.jsx)(d,{label:`Ping endpoint`,hint:`Relative to backend URL`,error:f.customPingPath?.message,children:(0,Y.jsx)(p,{...s(`customPingPath`),placeholder:`empty.php`})}),(0,Y.jsx)(d,{label:`Public-IP endpoint`,hint:`Relative to backend URL`,error:f.customIpPath?.message,children:(0,Y.jsx)(p,{...s(`customIpPath`),placeholder:`getIP.php`})})]}),(0,Y.jsxs)(`div`,{className:`grid gap-3 md:grid-cols-2`,children:[(0,Y.jsx)(U,{control:c,name:`allowInsecureCustomServer`,render:({field:e})=>(0,Y.jsx)(m,{checked:e.value,onChange:e.onChange,label:`Allow plain HTTP backend`,description:`Unsafe. Required only when the custom URL starts with http://.`})}),(0,Y.jsx)(U,{control:c,name:`skipTlsVerification`,render:({field:e})=>(0,Y.jsx)(m,{checked:e.value,onChange:e.onChange,label:`Skip TLS certificate verification`,description:`Unsafe. Enable only for a backend you control; every result records this setting.`})})]}),f.allowInsecureCustomServer?.message?(0,Y.jsx)(`p`,{className:`text-xs text-rose-600 dark:text-rose-400`,children:f.allowInsecureCustomServer.message}):null,x?(0,Y.jsxs)(`div`,{className:`flex gap-2 rounded-lg border border-rose-500/25 bg-rose-500/[.06] p-3 text-xs leading-5 text-rose-700 dark:text-rose-300`,children:[(0,Y.jsx)(D,{className:`mt-0.5 h-4 w-4 shrink-0`}),`Plain HTTP permits interception and response tampering. Use this only on an isolated network you control.`]}):null,b?(0,Y.jsxs)(`div`,{className:`flex gap-2 rounded-lg border border-rose-500/25 bg-rose-500/[.06] p-3 text-xs leading-5 text-rose-700 dark:text-rose-300`,children:[(0,Y.jsx)(D,{className:`mt-0.5 h-4 w-4 shrink-0`}),`TLS verification is disabled for this task. Traffic can be intercepted or redirected.`]}):null]}):null]})})]}),(0,Y.jsxs)(V,{children:[(0,Y.jsxs)(B,{children:[(0,Y.jsx)(R,{children:`Concrete source binding`}),(0,Y.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`No arbitrary address is selected and there is no fallback to the default route.`})]}),(0,Y.jsxs)(z,{className:`space-y-4`,children:[(0,Y.jsxs)(`div`,{className:`flex items-start justify-between gap-4 rounded-lg border border-border bg-muted/30 p-3`,children:[(0,Y.jsxs)(`div`,{children:[(0,Y.jsx)(`p`,{className:`text-xs font-semibold`,children:`Virtual network paths`}),(0,Y.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:`Show VPN, tunnel, bridge, and other virtual interfaces when they are intentional WAN paths.`})]}),(0,Y.jsx)(m,{checked:a,onChange:o,label:`Show virtual paths`})]}),(0,Y.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-3`,children:[(0,Y.jsx)(d,{label:`IP family`,required:!0,error:f.ipFamily?.message,children:(0,Y.jsxs)(u,{...s(`ipFamily`),onChange:e=>{s(`ipFamily`).onChange(e),l(`sourceIp`,``),l(`routeProfileId`,``)},children:[(0,Y.jsx)(`option`,{value:`auto`,children:`IPv4 or IPv6`}),(0,Y.jsx)(`option`,{value:`ipv4`,children:`IPv4 only`}),(0,Y.jsx)(`option`,{value:`ipv6`,children:`IPv6 only`})]})}),(0,Y.jsx)(d,{label:`Linux interface`,required:!0,error:f.interfaceName?.message,children:(0,Y.jsxs)(u,{...s(`interfaceName`),onChange:e=>{s(`interfaceName`).onChange(e),l(`sourceIp`,``,{shouldValidate:!0}),l(`routeProfileId`,``)},children:[(0,Y.jsx)(`option`,{value:``,children:`Select interface…`}),n.map(e=>(0,Y.jsxs)(`option`,{value:e.name,children:[e.name,` · `,e.operationalState||(e.operational?`up`:`down`),e.virtual?` · virtual`:``]},e.name))]})}),(0,Y.jsx)(d,{label:`Source address`,required:!0,error:f.sourceIp?.message,children:(0,Y.jsxs)(u,{...s(`sourceIp`),onChange:e=>{s(`sourceIp`).onChange(e),l(`routeProfileId`,``)},disabled:!_,children:[(0,Y.jsx)(`option`,{value:``,children:`Select concrete address…`}),i.map(e=>(0,Y.jsxs)(`option`,{value:e.address,children:[e.address,` · `,e.family.toUpperCase()]},e.address))]})})]}),_&&i.length===0?(0,Y.jsxs)(`p`,{className:`rounded-lg border border-amber-500/25 bg-amber-500/[.06] p-3 text-xs text-amber-700 dark:text-amber-300`,children:[`This interface has no non-link-local `,v===`auto`?``:v.toUpperCase(),` address. Choose another interface or family.`]}):null,(0,Y.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-2`,children:[(0,Y.jsx)(d,{label:`Route profile`,hint:`Recommended`,error:f.routeProfileId?.message,children:(0,Y.jsxs)(u,{...s(`routeProfileId`),children:[(0,Y.jsx)(`option`,{value:``,children:`No persisted route expectation`}),r.map(e=>{let t=e.interfaceName===_&&e.sourceIp===ee;return(0,Y.jsxs)(`option`,{value:e.id,disabled:!t,children:[e.name,` · `,e.interfaceName,` · `,e.sourceIp,t?``:` · path mismatch`]},e.id)})]})}),(0,Y.jsxs)(`div`,{className:`rounded-lg border border-border bg-muted/35 p-3`,children:[(0,Y.jsx)(`p`,{className:`text-xs font-semibold`,children:`Binding capability`}),(0,Y.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:t?.capabilities.sourceAddressBinding?`Provider supports source-address binding.`:t?.capabilities.interfaceBinding?`Provider binds to the selected interface.`:`Review provider availability before enabling this task.`})]})]})]})]})]})}function Ne({provider:e,interfaceName:n,sourceIp:r,ipFamily:i,value:a,onChange:o,error:s}){let[c,l]=(0,J.useState)(``),u=t({queryKey:[`provider-servers`,e,c,n,r,i],queryFn:()=>L.providerServers(e,{search:c,interfaceName:n,sourceIp:r,ipFamily:i}),enabled:!1}),f=()=>void u.refetch();return(0,Y.jsxs)(`div`,{className:`space-y-3`,children:[(0,Y.jsx)(d,{label:`${I(e)} server`,required:!0,error:s,hint:`Discovery follows selected WAN`,children:(0,Y.jsxs)(`div`,{className:`flex gap-2`,children:[(0,Y.jsxs)(`div`,{className:`relative flex-1`,children:[(0,Y.jsx)(w,{className:`absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground`}),(0,Y.jsx)(p,{className:`pl-9`,value:c,onChange:e=>l(e.target.value),placeholder:`City, sponsor, host, or server ID`,onKeyDown:e=>{e.key===`Enter`&&(e.preventDefault(),f())}})]}),(0,Y.jsxs)(F,{variant:`outline`,onClick:f,disabled:!n||!r||u.isFetching,children:[u.isFetching?(0,Y.jsx)(M,{}):(0,Y.jsx)(C,{className:`h-4 w-4`}),`Discover`]})]})}),!n||!r?(0,Y.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Select an interface and concrete source address before discovery.`}):null,u.error?(0,Y.jsx)(`p`,{className:`rounded-lg border border-rose-500/20 bg-rose-500/[.06] p-3 text-xs text-rose-600 dark:text-rose-400`,children:re(u.error)}):null,(u.data?.length??0)>0?(0,Y.jsx)(`div`,{className:`max-h-64 overflow-y-auto rounded-lg border border-border scrollbar-thin`,role:`listbox`,"aria-label":`Discovered servers`,children:u.data?.map(e=>(0,Y.jsx)(Pe,{server:e,selected:e.id===a,onSelect:()=>o(e.id)},e.id))}):u.isFetched&&!u.isFetching&&!u.error?(0,Y.jsx)(P,{compact:!0,title:`No servers found`,description:`Try a broader search or verify that discovery can use this source path.`}):null,a?(0,Y.jsxs)(`div`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,Y.jsx)(ne,{className:`h-4 w-4 text-emerald-500`}),`Persisted server ID: `,(0,Y.jsx)(`code`,{className:`rounded bg-muted px-1.5 py-0.5 text-foreground`,children:a})]}):null]})}function Pe({server:e,selected:t,onSelect:n}){return(0,Y.jsxs)(`button`,{type:`button`,role:`option`,"aria-selected":t,onClick:n,className:k(`flex w-full items-center gap-3 border-b border-border p-3 text-left transition last:border-b-0 hover:bg-muted/50`,t&&`bg-primary/[.07]`),children:[(0,Y.jsx)(`span`,{className:k(`grid h-8 w-8 shrink-0 place-items-center rounded-lg border`,t?`border-primary/30 bg-primary/10 text-primary`:`border-border bg-background text-muted-foreground`),children:(0,Y.jsx)(T,{className:`h-4 w-4`})}),(0,Y.jsxs)(`span`,{className:`min-w-0 flex-1`,children:[(0,Y.jsx)(`span`,{className:`block truncate text-xs font-semibold`,children:e.name||e.host}),(0,Y.jsxs)(`span`,{className:`mt-0.5 block truncate text-[10px] text-muted-foreground`,children:[e.sponsor,` · `,e.location,e.country?`, ${e.country}`:``]})]}),(0,Y.jsxs)(`span`,{className:`font-mono text-[10px] text-muted-foreground`,children:[`#`,e.id]})]})}function Fe({form:e,cronRuns:t}){let{register:n,control:r,setValue:i,formState:{errors:a}}=e,o=q({control:r,name:`cronExpression`}),s=q({control:r,name:`timezone`}),c=e=>`${N(e,!1,s)} (${s})`;return(0,Y.jsxs)(`div`,{className:`space-y-5`,children:[(0,Y.jsxs)(V,{children:[(0,Y.jsxs)(B,{children:[(0,Y.jsx)(R,{children:`Independent schedule`}),(0,Y.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`Standard five-field cron syntax is evaluated in this task’s IANA timezone.`})]}),(0,Y.jsxs)(z,{className:`space-y-5`,children:[(0,Y.jsx)(`div`,{className:`grid gap-2 sm:grid-cols-2 xl:grid-cols-5`,children:y.map(e=>(0,Y.jsxs)(`button`,{type:`button`,onClick:()=>i(`cronExpression`,e.expression,{shouldValidate:!0,shouldDirty:!0}),className:k(`rounded-lg border p-3 text-left transition`,o===e.expression?`border-primary/50 bg-primary/[.07]`:`border-border bg-background hover:border-primary/25`),children:[(0,Y.jsx)(`span`,{className:`block text-xs font-semibold`,children:e.label}),(0,Y.jsx)(`span`,{className:`mt-0.5 block text-[10px] leading-4 text-muted-foreground`,children:e.description})]},e.id))}),(0,Y.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-2`,children:[(0,Y.jsx)(d,{label:`Advanced cron expression`,required:!0,error:a.cronExpression?.message,hint:v(o),children:(0,Y.jsx)(p,{className:`font-mono`,...n(`cronExpression`),placeholder:`0 */6 * * *`,spellCheck:!1})}),(0,Y.jsx)(d,{label:`Timezone`,required:!0,error:a.timezone?.message,children:(0,Y.jsxs)(u,{...n(`timezone`),children:[b.includes(s)?null:(0,Y.jsx)(`option`,{value:s,children:s}),b.map(e=>(0,Y.jsx)(`option`,{value:e,children:e},e))]})})]}),(0,Y.jsxs)(`div`,{className:`rounded-xl border border-border bg-muted/30 p-4`,children:[(0,Y.jsx)(`p`,{className:`mb-3 text-[11px] font-semibold uppercase tracking-[.1em] text-muted-foreground`,children:`Next five executions`}),t.length?(0,Y.jsx)(`ol`,{className:`grid gap-2 sm:grid-cols-2 xl:grid-cols-5`,children:t.map((e,t)=>(0,Y.jsxs)(`li`,{className:`rounded-lg border border-border bg-background p-2.5`,children:[(0,Y.jsxs)(`span`,{className:`block text-[10px] text-muted-foreground`,children:[`Run `,t+1]}),(0,Y.jsx)(`span`,{className:`mt-0.5 block text-xs font-semibold tabular-nums`,children:c(e.toISOString())})]},e.toISOString()))}):(0,Y.jsx)(`p`,{className:`text-xs text-rose-600 dark:text-rose-400`,children:`A preview is unavailable until the expression and timezone are valid.`})]})]})]}),(0,Y.jsxs)(V,{children:[(0,Y.jsxs)(B,{children:[(0,Y.jsx)(R,{children:`Execution guardrails`}),(0,Y.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`Bound run duration and avoid competing tests on the same path.`})]}),(0,Y.jsxs)(z,{className:`space-y-4`,children:[(0,Y.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,Y.jsx)(d,{label:`Timeout`,hint:`5–3600 seconds`,error:a.timeoutSeconds?.message,children:(0,Y.jsx)(p,{type:`number`,min:5,max:3600,...n(`timeoutSeconds`,{valueAsNumber:!0})})}),(0,Y.jsx)(d,{label:`Random start jitter`,hint:`0–3600 seconds`,error:a.randomJitterSeconds?.message,children:(0,Y.jsx)(p,{type:`number`,min:0,max:3600,...n(`randomJitterSeconds`,{valueAsNumber:!0})})})]}),(0,Y.jsxs)(`div`,{className:`grid gap-3 md:grid-cols-2`,children:[(0,Y.jsx)(U,{control:r,name:`preventOverlap`,render:({field:e})=>(0,Y.jsx)(m,{checked:e.value,onChange:e.onChange,label:`Prevent task overlap`,description:`Skip a new execution while this task is active.`})}),(0,Y.jsx)(d,{label:`Route-validation behavior`,required:!0,error:a.routeValidation?.message,children:(0,Y.jsxs)(u,{...n(`routeValidation`),children:[(0,Y.jsx)(`option`,{value:`required`,children:`Full route validation · fail closed`}),(0,Y.jsx)(`option`,{value:`interface-only`,children:`Interface and source binding · fail closed`})]})})]})]})]})]})}function Ie({values:e,provider:t,route:n,cronRuns:r,preflight:i,configurationChanged:o,validating:s,onValidate:c}){let{toast:l}=se(),u=t=>`${N(t,!1,e.timezone)} (${e.timezone})`,d=a({mutationFn:()=>n?L.validateRoute(n.id):Promise.reject(Error(`No route profile selected.`)),onSuccess:e=>l({tone:e.success?`success`:`error`,title:e.success?`Route profile validated`:`Route mismatch detected`,description:e.message}),onError:e=>l({tone:`error`,title:`Route validation failed`,description:re(e)})}),f=[{label:`Provider`,value:I(e.provider),extra:t?.available?`Available`:`Unavailable`},{label:`Target`,value:e.provider===`cloudflare`?`Automatic edge selection`:e.serverSelectionMode===`fixed`?`Server #${e.serverId}`:e.serverSelectionMode===`custom`?e.serverUrl:`Automatic provider selection`},{label:`Source path`,value:`${e.interfaceName||`No interface`} · ${e.sourceIp||`No address`}`,extra:e.ipFamily.toUpperCase()},{label:`Route profile`,value:n?.name??`No persisted profile`,extra:e.routeValidation},{label:`Schedule`,value:`${e.cronExpression} · ${e.timezone}`,extra:v(e.cronExpression)},{label:`Guardrails`,value:`${e.timeoutSeconds}s timeout · ${e.randomJitterSeconds}s jitter`,extra:e.preventOverlap?`Overlap prevented`:`Overlap allowed`}];return(0,Y.jsxs)(V,{children:[(0,Y.jsxs)(B,{children:[(0,Y.jsx)(R,{children:`Review this independent path`}),(0,Y.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`Saving recalculates the persisted schedule immediately. Enabling it does not trigger an unscheduled speed test.`})]}),(0,Y.jsxs)(z,{className:`space-y-5`,children:[(0,Y.jsxs)(`div`,{className:`flex flex-col justify-between gap-3 rounded-xl border border-primary/25 bg-primary/[.055] p-4 sm:flex-row sm:items-center`,children:[(0,Y.jsxs)(`div`,{children:[(0,Y.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Y.jsx)(`h3`,{className:`text-base font-semibold`,children:e.name||`Unnamed task`}),(0,Y.jsx)(fe,{provider:e.provider}),(0,Y.jsx)(de,{tone:e.enabled?`success`:`neutral`,children:e.enabled?`Enabled`:`Disabled`})]}),(0,Y.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:e.description||`No description provided.`})]}),(0,Y.jsx)(E,{className:`h-7 w-7 shrink-0 text-primary`})]}),(0,Y.jsx)(`dl`,{className:`grid gap-3 md:grid-cols-2`,children:f.map(e=>(0,Y.jsxs)(`div`,{className:`rounded-lg border border-border bg-background p-3`,children:[(0,Y.jsx)(`dt`,{className:`text-[10px] font-semibold uppercase tracking-[.1em] text-muted-foreground`,children:e.label}),(0,Y.jsx)(`dd`,{className:`mt-1 break-words text-xs font-semibold text-foreground`,children:e.value}),e.extra?(0,Y.jsx)(`dd`,{className:`mt-0.5 text-[10px] capitalize text-muted-foreground`,children:e.extra}):null]},e.label))}),e.provider===`librespeed`&&e.skipTlsVerification?(0,Y.jsxs)(`div`,{className:`flex gap-2 rounded-lg border border-rose-500/25 bg-rose-500/[.06] p-3 text-xs text-rose-700 dark:text-rose-300`,children:[(0,Y.jsx)(D,{className:`h-4 w-4 shrink-0`}),`TLS certificate verification is disabled and will be recorded in result metadata.`]}):null,(0,Y.jsxs)(`div`,{role:i?.success?`status`:`alert`,className:k(`flex gap-3 rounded-lg border p-3 text-xs leading-5`,i?.success?`border-emerald-500/25 bg-emerald-500/[.06] text-emerald-700 dark:text-emerald-300`:i?`border-rose-500/25 bg-rose-500/[.06] text-rose-700 dark:text-rose-300`:`border-amber-500/25 bg-amber-500/[.06] text-amber-700 dark:text-amber-300`),children:[i?.success?(0,Y.jsx)(ne,{className:`mt-0.5 h-4 w-4 shrink-0`}):(0,Y.jsx)(D,{className:`mt-0.5 h-4 w-4 shrink-0`}),(0,Y.jsxs)(`div`,{children:[(0,Y.jsx)(`p`,{className:`font-semibold`,children:i?.success?`Current configuration passed preflight`:i?`Current configuration failed preflight`:o?`Configuration changed since preflight`:`Current configuration has not been validated`}),(0,Y.jsx)(`p`,{className:`mt-0.5`,children:i?.message||(e.provider===`ookla`?`Ookla tasks may be saved before the operator-installed CLI is available. Validation and execution remain blocked until the provider is ready.`:e.enabled?`A successful preflight is required before this enabled task can be saved.`:`Disabled tasks may be saved without preflight, but validation is recommended before enabling.`)})]})]}),(0,Y.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,Y.jsxs)(F,{onClick:c,disabled:s,children:[s?(0,Y.jsx)(M,{}):(0,Y.jsx)(C,{className:`h-4 w-4`}),`Validate current configuration`]}),(0,Y.jsxs)(F,{variant:`outline`,onClick:()=>d.mutate(),disabled:!n||d.isPending,children:[d.isPending?(0,Y.jsx)(M,{}):(0,Y.jsx)(te,{className:`h-4 w-4`}),`Validate route profile only`]})]}),(0,Y.jsxs)(`div`,{className:`rounded-lg border border-border bg-muted/30 p-3 text-xs text-muted-foreground`,children:[`First scheduled execution: `,(0,Y.jsx)(`strong`,{className:`font-semibold text-foreground`,children:r[0]?u(r[0].toISOString()):`Unavailable`})]})]})]})}var Le={serverDiscovery:!0,fixedServerIds:!0,customServerUrls:!1,interfaceBinding:!0,sourceAddressBinding:!0,ipv4:!0,ipv6:!0,jitter:!0,packetLoss:!1,resultUrls:!1},Re=[{id:`ookla`,displayName:`Ookla Speedtest`,available:!0,version:``,message:``,capabilities:{...Le,packetLoss:!0,resultUrls:!0}},{id:`librespeed`,displayName:`LibreSpeed`,available:!0,version:``,message:``,capabilities:{...Le,customServerUrls:!0}},{id:`cloudflare`,displayName:`Cloudflare`,available:!0,version:`native`,message:``,capabilities:{...Le,serverDiscovery:!1,fixedServerIds:!1}}];export{Te as buildTaskInput,X as customEndpointSchema,Ae as default,xe as taskSchema}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/TaskEditorPage-Bx-TQ0Uc.js b/internal/frontend/dist/assets/TaskEditorPage-Bx-TQ0Uc.js new file mode 100644 index 0000000..faf9c29 --- /dev/null +++ b/internal/frontend/dist/assets/TaskEditorPage-Bx-TQ0Uc.js @@ -0,0 +1 @@ +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,w as i,x as a}from"./query-DWNqGEie.js";import{b as o,d as s,y as c}from"./react-DsKPNe9r.js";import{t as l}from"./arrow-left-DZUEJBho.js";import{i as u,n as d,o as f,r as p,t as m}from"./fields-DSX3DSQR.js";import{t as h}from"./clock-3-DhdKy9KD.js";import{a as g,i as _,l as ee,n as v,o as y,r as b,s as x,t as S}from"./OoklaEulaAcceptance-DkJ6glh5.js";import{t as C}from"./radio-tower-D9Z4YmgD.js";import{t as w}from"./search-b0EiqX0q.js";import{t as T}from"./server-BWQHmE7V.js";import{t as E}from"./shield-check-DrVussKH.js";import{A as D,D as O,I as k,M as te,T as A,a as ne,c as j,i as re,j as ie,k as ae,n as M,o as N,p as oe,r as P,s as F,t as se,v as I,w as L}from"./index-DleN6wGz.js";import{n as ce}from"./useAppSettings-vnP5f96R.js";import{t as le}from"./PageHeader-DGX-y_Bi.js";import{n as ue,t as de}from"./badge-BWAHg20h.js";import{i as R,n as z,r as B,t as V}from"./card-BtXBd7n8.js";import{t as fe}from"./EntityBadges-CM7WeMOS.js";import{a as H,c as U,i as W,l as pe,n as G,o as me,r as K,s as he,t as ge,u as q}from"./schemas-DaFWBMkt.js";var _e=k(`arrow-right`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`m12 5 7 7-7 7`,key:`xquz4c`}]]),ve=k(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]),ye=k(`cloud`,[[`path`,{d:`M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z`,key:`p7xjir`}]]),J=e(r(),1),Y=n(),be=`Use a safe relative endpoint without a leading slash, traversal, backslash, encoded separator, control character, scheme, host, or fragment.`,X=H().max(2048).refine(e=>!Array.from(e).some(Oe),be).transform(e=>e.trim()).refine(De,be),xe=W({name:H().trim().min(2,`Enter at least 2 characters.`).max(100),description:H().trim().max(500),enabled:G(),provider:ge([`ookla`,`librespeed`,`cloudflare`]),cronExpression:H().trim().refine(x,`Enter a valid five-field cron expression.`),timezone:H().trim().min(1,`Select a timezone.`).refine(_,`Select a valid IANA timezone.`),randomJitterSeconds:K().int().min(0).max(3600),serverSelectionMode:ge([`automatic`,`fixed`,`custom`]),serverId:H().trim().max(200),serverUrl:H().trim().max(2048),customServerName:H().trim().max(200),customDownloadPath:X,customUploadPath:X,customPingPath:X,customIpPath:X,allowInsecureCustomServer:G(),interfaceName:H().trim().min(1,`Select a concrete interface.`),sourceIp:H().trim().min(1,`Select one concrete source address.`),ipFamily:ge([`auto`,`ipv4`,`ipv6`]),routeProfileId:H(),timeoutSeconds:K().int().min(5).max(3600),preventOverlap:G(),routeValidation:ge([`required`,`interface-only`]),skipTlsVerification:G()}).superRefine((e,t)=>{if(e.provider===`cloudflare`&&e.serverSelectionMode!==`automatic`&&t.addIssue({code:`custom`,path:[`serverSelectionMode`],message:`Cloudflare always uses automatic edge selection.`}),e.serverSelectionMode===`fixed`&&!e.serverId&&t.addIssue({code:`custom`,path:[`serverId`],message:`Select or enter a server ID.`}),e.serverSelectionMode===`custom`){e.provider!==`librespeed`&&t.addIssue({code:`custom`,path:[`serverSelectionMode`],message:`Custom URLs are supported by LibreSpeed only.`});let n=me({protocol:/^https?$/}).safeParse(e.serverUrl);n.success||t.addIssue({code:`custom`,path:[`serverUrl`],message:`Enter a valid HTTP or HTTPS URL.`}),n.success&&n.data.toLowerCase().startsWith(`http:`)&&!e.allowInsecureCustomServer&&t.addIssue({code:`custom`,path:[`allowInsecureCustomServer`],message:`Explicitly allow plain HTTP or use HTTPS.`})}}),Se={name:``,description:``,enabled:!0,provider:`cloudflare`,cronExpression:`0 */6 * * *`,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone||`UTC`,randomJitterSeconds:0,serverSelectionMode:`automatic`,serverId:``,serverUrl:``,customServerName:``,customDownloadPath:``,customUploadPath:``,customPingPath:``,customIpPath:``,allowInsecureCustomServer:!1,interfaceName:``,sourceIp:``,ipFamily:`auto`,routeProfileId:``,timeoutSeconds:120,preventOverlap:!0,routeValidation:`required`,skipTlsVerification:!1};function Ce(e){return{...Se,timezone:e.defaultTimezone||`UTC`,timeoutSeconds:e.defaultTaskTimeoutSeconds}}function we(e){return{name:e.name,description:e.description,enabled:e.enabled,provider:e.provider,cronExpression:e.cronExpression,timezone:e.timezone,randomJitterSeconds:e.randomJitterSeconds,serverSelectionMode:e.serverSelectionMode,serverId:e.serverId,serverUrl:e.serverUrl,customServerName:Z(e.customServerDefinition,`name`),customDownloadPath:Z(e.customServerDefinition,`dlURL`),customUploadPath:Z(e.customServerDefinition,`ulURL`),customPingPath:Z(e.customServerDefinition,`pingURL`),customIpPath:Z(e.customServerDefinition,`getIpURL`),allowInsecureCustomServer:e.customServerDefinition.allowInsecure===!0,interfaceName:e.interfaceName,sourceIp:e.sourceIp,ipFamily:e.ipFamily,routeProfileId:e.routeProfileId??``,timeoutSeconds:e.timeoutSeconds,preventOverlap:e.preventOverlap,routeValidation:e.routeValidation,skipTlsVerification:e.providerOptions.skipTlsVerification===!0}}function Te(e,t){let n=e.provider===`cloudflare`?`automatic`:e.serverSelectionMode,r={...t?.customServerDefinition??{}};e.provider===`librespeed`&&(Q(r,`name`,e.customServerName),Q(r,`dlURL`,e.customDownloadPath),Q(r,`ulURL`,e.customUploadPath),Q(r,`pingURL`,e.customPingPath),Q(r,`getIpURL`,e.customIpPath),e.allowInsecureCustomServer?r.allowInsecure=!0:delete r.allowInsecure);let i={...t?.providerOptions??{}};return e.provider===`librespeed`&&(i.telemetry=!1,i.skipTlsVerification=e.skipTlsVerification),{name:e.name,description:e.description,enabled:e.enabled,provider:e.provider,cronExpression:e.cronExpression,timezone:e.timezone,randomJitterSeconds:e.randomJitterSeconds,serverSelectionMode:n,serverId:n===`fixed`?e.serverId:``,serverUrl:n===`custom`?e.serverUrl:``,customServerDefinition:r,interfaceName:e.interfaceName,sourceIp:e.sourceIp,ipFamily:e.ipFamily,routeProfileId:e.routeProfileId||null,timeoutSeconds:e.timeoutSeconds,providerOptions:i,preventOverlap:e.preventOverlap,routeValidation:e.routeValidation}}function Z(e,t){return typeof e[t]==`string`?e[t]:``}function Q(e,t,n){e[t]=n||void 0}function Ee(e){return JSON.stringify(e)}function De(e){if(!e)return!0;let t=e.toLowerCase();if(e.startsWith(`/`)||e.includes(`\\`)||t.includes(`%2f`)||t.includes(`%5c`)||t.includes(`%00`)||t.includes(`%0a`)||t.includes(`%0d`)||e.includes(`#`)||/^[A-Za-z][A-Za-z0-9+.-]*:/.test(e))return!1;let n=e.split(`?`,1)[0];if(!n)return!1;for(let e of n.split(`/`)){if(!e)return!1;let t;try{t=decodeURIComponent(e)}catch{return!1}if(t===`.`||t===`..`)return!1}try{let t=new URL(e,`https://multispeed.invalid/`);return t.origin===`https://multispeed.invalid`&&!t.username&&!t.password&&!t.hash}catch{return!1}}function Oe(e){let t=e.charCodeAt(0);return t<=31||t===127}var $=[{title:`Identity & provider`,description:`Name the test and select its measurement methodology.`,icon:ie},{title:`Target & network`,description:`Pin the server and concrete Linux source path.`,icon:ae},{title:`Schedule & safety`,description:`Control timing, timeout, jitter, and overlap.`,icon:h},{title:`Review & validate`,description:`Confirm the persisted task before activation.`,icon:E}],ke={0:[`name`,`description`,`provider`],1:[`serverSelectionMode`,`serverId`,`serverUrl`,`customServerName`,`customDownloadPath`,`customUploadPath`,`customPingPath`,`customIpPath`,`allowInsecureCustomServer`,`interfaceName`,`sourceIp`,`ipFamily`,`routeProfileId`],2:[`cronExpression`,`timezone`,`randomJitterSeconds`,`timeoutSeconds`,`preventOverlap`,`routeValidation`],3:[]};function Ae(){let{taskId:e}=o(),n=!!e,r=c(),u=i(),{toast:d}=se(),[f,p]=(0,J.useState)(0),[m,h]=(0,J.useState)(!1),[_,v]=(0,J.useState)([]),[y,b]=(0,J.useState)(null),x=ce(),S=t({queryKey:M.task(e??`new`),queryFn:()=>L.task(e??``),enabled:n}),C=t({queryKey:[...M.interfaces,`task-editor`,!0,!0],queryFn:()=>L.interfaces({includeDown:!0,includeVirtual:!0})}),w=t({queryKey:M.routes,queryFn:L.routes}),T=t({queryKey:M.providers,queryFn:L.providers}),E=(0,J.useMemo)(()=>S.data?we(S.data):Ce(x.settings),[x.settings,S.data]),D=pe({resolver:he(xe),defaultValues:E,values:E,resetOptions:{keepDirtyValues:!0},mode:`onBlur`}),k=q({control:D.control}),te=k.provider??`cloudflare`,ie=k.interfaceName??``,ae=k.cronExpression??``,oe=k.timezone??`UTC`;(0,J.useEffect)(()=>{let e=!0;return g(ae,oe).then(t=>e&&v(t)),()=>{e=!1}},[ae,oe]);let P=a({mutationFn:t=>n&&e?L.updateTask(e,t):L.createTask(t),onSuccess:e=>{u.invalidateQueries({queryKey:M.tasks}),d({tone:`success`,title:n?`Task updated`:`Task created`,description:`${e.name} was persisted and its schedule recalculated.`}),r(`/tasks`)},onError:e=>d({tone:`error`,title:n?`Unable to update task`:`Unable to create task`,description:A(e)})}),I=a({mutationFn:async({input:e,signature:t})=>({signature:t,validation:await L.validateTaskInput(e)}),onMutate:()=>b(null),onSuccess:e=>{b(e),d({tone:e.validation.success?`success`:`error`,title:e.validation.success?`Current task path validated`:`Task preflight failed`,description:e.validation.message})},onError:e=>{b(null),d({tone:`error`,title:`Task preflight failed`,description:A(e)})}}),ue=e=>{let t=Te(e,S.data),n=Ee(t);if(e.enabled&&e.provider!==`ookla`&&(y?.signature!==n||!y.validation.success)){p($.length-1),d({tone:`error`,title:`Validate this enabled task first`,description:`Run a successful preflight for the current provider, target, and source path before saving.`});return}P.mutate(t)},de=async()=>{if(!await D.trigger()){d({tone:`error`,title:`Complete required task fields`,description:`Resolve the highlighted fields before route preflight.`});return}let e=Te(D.getValues(),S.data);I.mutate({input:e,signature:Ee(e)})},R=async()=>{await D.trigger(ke[f]??[])&&p(e=>Math.min(e+1,$.length-1))},z=S.error??C.error??w.error??T.error??x.error;if(n&&S.isLoading||C.isLoading||w.isLoading||T.isLoading||x.isLoading)return(0,Y.jsx)(ne,{label:`Loading provider and network capabilities…`});if(z)return(0,Y.jsx)(re,{error:z,onRetry:()=>{S.refetch(),C.refetch(),w.refetch(),T.refetch(),x.refetch()}});let B=T.data??Re,fe=C.data??[],H=fe.filter(e=>!e.virtual||m||e.name===ie),U=w.data??[],W=B.find(e=>e.id===te),G=fe.find(e=>e.name===ie)?.addresses.filter(e=>!e.linkLocal&&(k.ipFamily===`auto`||e.family===k.ipFamily))??[],me=Ee(Te(k,S.data)),K=y?.signature===me?y.validation:null;return(0,Y.jsxs)(`form`,{onSubmit:D.handleSubmit(ue),noValidate:!0,children:[(0,Y.jsx)(le,{title:n?`Edit ${S.data?.name??`task`}`:`Create an independent speed test`,description:`The selected interface and source address are mandatory. MultiSpeed never falls back to another WAN when binding or route validation fails.`,actions:(0,Y.jsx)(F,{asChild:!0,variant:`outline`,children:(0,Y.jsxs)(s,{to:`/tasks`,children:[(0,Y.jsx)(l,{className:`h-4 w-4`}),`Back to tasks`]})})}),n&&S.data?.networkPathValid===!1?(0,Y.jsxs)(`div`,{role:`alert`,className:`mb-5 flex gap-3 rounded-xl border border-rose-500/25 bg-rose-500/[.06] p-4 text-rose-700 dark:text-rose-300`,children:[(0,Y.jsx)(O,{className:`mt-0.5 h-5 w-5 shrink-0`}),(0,Y.jsxs)(`div`,{children:[(0,Y.jsx)(`p`,{className:`text-sm font-semibold`,children:`This task's network path is no longer valid`}),(0,Y.jsxs)(`p`,{className:`mt-1 text-xs leading-5`,children:[S.data.networkPathMessage||`The configured interface or source address is not available in the active Linux namespace.`,` Select a current interface and concrete source address before saving or running this task.`]})]})]}):null,(0,Y.jsxs)(`div`,{className:`grid gap-5 lg:grid-cols-[250px_minmax(0,1fr)]`,children:[(0,Y.jsx)(V,{className:`h-fit p-3 lg:sticky lg:top-24`,children:(0,Y.jsx)(`ol`,{className:`space-y-1`,"aria-label":`Task editor steps`,children:$.map((e,t)=>{let n=e.icon;return(0,Y.jsx)(`li`,{children:(0,Y.jsxs)(`button`,{type:`button`,onClick:()=>p(t),className:j(`flex w-full items-start gap-3 rounded-lg p-3 text-left transition focus-visible:ring-2 focus-visible:ring-ring`,f===t?`bg-accent text-accent-foreground`:`text-muted-foreground hover:bg-muted hover:text-foreground`),"aria-current":f===t?`step`:void 0,children:[(0,Y.jsx)(`span`,{className:j(`grid h-7 w-7 shrink-0 place-items-center rounded-lg border text-[11px] font-bold`,f===t?`border-primary/30 bg-primary/10 text-primary`:te.id===k.routeProfileId),cronRuns:_,preflight:K,configurationChanged:y!==null&&K===null,validating:I.isPending,onValidate:()=>void de()}):null,(0,Y.jsxs)(`div`,{className:`mt-5 flex items-center justify-between rounded-xl border border-border bg-card p-4 shadow-panel`,children:[(0,Y.jsxs)(F,{variant:`outline`,onClick:()=>p(e=>Math.max(e-1,0)),disabled:f===0||P.isPending,children:[(0,Y.jsx)(l,{className:`h-4 w-4`}),`Previous`]}),(0,Y.jsxs)(`div`,{className:`text-center text-[11px] text-muted-foreground`,children:[`Step `,f+1,` of `,$.length]}),f<$.length-1?(0,Y.jsxs)(F,{onClick:e=>{e.preventDefault(),R()},children:[`Continue`,(0,Y.jsx)(_e,{className:`h-4 w-4`})]}):(0,Y.jsxs)(F,{type:`submit`,disabled:P.isPending,children:[P.isPending?(0,Y.jsx)(N,{}):(0,Y.jsx)(ee,{className:`h-4 w-4`}),n?`Save changes`:`Create task`]})]})]})]})]})}function je({form:e,providers:t,activeProvider:n}){let{register:r,control:i,formState:{errors:a}}=e;return(0,Y.jsxs)(V,{children:[(0,Y.jsxs)(B,{children:[(0,Y.jsx)(R,{children:`Identity & measurement provider`}),(0,Y.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`Provider behavior remains isolated per task; changing this task cannot affect another schedule.`})]}),(0,Y.jsxs)(z,{className:`space-y-5`,children:[(0,Y.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,Y.jsx)(d,{label:`Task name`,required:!0,error:a.name?.message,children:(0,Y.jsx)(p,{...r(`name`),placeholder:`Fiber uplink · Frankfurt`,autoFocus:!0})}),(0,Y.jsx)(U,{control:i,name:`enabled`,render:({field:e})=>(0,Y.jsx)(m,{checked:e.value,onChange:e.onChange,label:`Task enabled`,description:`Schedule immediately after saving.`})})]}),(0,Y.jsx)(d,{label:`Description`,error:a.description?.message,hint:`Optional`,children:(0,Y.jsx)(f,{...r(`description`),placeholder:`Primary WAN baseline during business hours.`})}),(0,Y.jsxs)(`div`,{children:[(0,Y.jsxs)(`p`,{className:`mb-2 text-xs font-semibold text-foreground`,children:[`Provider `,(0,Y.jsx)(`span`,{className:`text-rose-500`,children:`*`})]}),(0,Y.jsx)(U,{control:i,name:`provider`,render:({field:n})=>(0,Y.jsx)(`div`,{className:`grid gap-3 md:grid-cols-3`,children:t.map(t=>{let r=t.id===`ookla`?ie:t.id===`librespeed`?T:ye;return(0,Y.jsxs)(`button`,{type:`button`,onClick:()=>{n.onChange(t.id),(t.id===`cloudflare`||e.getValues(`serverSelectionMode`)===`custom`&&t.id===`ookla`)&&e.setValue(`serverSelectionMode`,`automatic`)},className:j(`rounded-xl border p-4 text-left transition focus-visible:ring-2 focus-visible:ring-ring`,n.value===t.id?`border-primary/50 bg-primary/[.07] shadow-sm`:`border-border bg-background hover:border-primary/25`,!t.available&&`opacity-65`),"aria-pressed":n.value===t.id,children:[(0,Y.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,Y.jsx)(`span`,{className:j(`grid h-9 w-9 place-items-center rounded-lg`,t.id===`ookla`?`bg-cyan-500/10 text-cyan-500`:t.id===`librespeed`?`bg-violet-500/10 text-violet-500`:`bg-orange-500/10 text-orange-500`),children:(0,Y.jsx)(r,{className:`h-4 w-4`})}),(0,Y.jsxs)(de,{tone:t.available?`success`:`danger`,className:`gap-1`,children:[(0,Y.jsx)(ue,{active:t.available}),t.available?`Available`:`Unavailable`]})]}),(0,Y.jsx)(`p`,{className:`mt-3 text-sm font-semibold`,children:t.displayName}),(0,Y.jsx)(`p`,{className:`mt-1 text-[11px] leading-4 text-muted-foreground`,children:t.id===`cloudflare`?`Native edge methodology with automatic colo selection.`:t.id===`ookla`?`Official CLI with broad fixed-server discovery.`:`Open-source CLI with public and custom backends.`})]},t.id)})})})]}),n&&!n.available?(0,Y.jsxs)(`div`,{className:`flex gap-3 rounded-lg border border-amber-500/25 bg-amber-500/[.07] p-4 text-sm`,children:[(0,Y.jsx)(O,{className:`mt-0.5 h-4 w-4 shrink-0 text-amber-500`}),(0,Y.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,Y.jsxs)(`p`,{className:`font-semibold text-foreground`,children:[n.displayName,` is not available`]}),(0,Y.jsx)(`p`,{className:`mt-1 text-xs leading-5 text-muted-foreground`,children:n.message||`Check provider installation details on System information.`}),n.id===`ookla`?(0,Y.jsx)(S,{compact:!0}):null]})]}):null]})]})}function Me({form:e,provider:t,interfaces:n,routes:r,addressOptions:i,showVirtualInterfaces:a,onShowVirtualInterfacesChange:o}){let{register:s,control:c,setValue:l,formState:{errors:f}}=e,h=q({control:c,name:`serverSelectionMode`}),g=q({control:c,name:`provider`}),_=q({control:c,name:`interfaceName`}),ee=q({control:c,name:`sourceIp`}),v=q({control:c,name:`ipFamily`}),y=q({control:c,name:`serverId`}),b=q({control:c,name:`skipTlsVerification`}),x=q({control:c,name:`allowInsecureCustomServer`});return(0,Y.jsxs)(`div`,{className:`space-y-5`,children:[(0,Y.jsxs)(V,{children:[(0,Y.jsxs)(B,{children:[(0,Y.jsx)(R,{children:`Target selection`}),(0,Y.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`Discovery uses the same network binding as the final measurement.`})]}),(0,Y.jsx)(z,{className:`space-y-5`,children:g===`cloudflare`?(0,Y.jsxs)(`div`,{className:`flex items-start gap-3 rounded-xl border border-orange-500/25 bg-orange-500/[.06] p-4`,children:[(0,Y.jsx)(ye,{className:`mt-0.5 h-5 w-5 shrink-0 text-orange-500`}),(0,Y.jsxs)(`div`,{children:[(0,Y.jsx)(`p`,{className:`text-sm font-semibold`,children:`Automatic edge selection`}),(0,Y.jsx)(`p`,{className:`mt-1 text-xs leading-5 text-muted-foreground`,children:`Cloudflare selects the reachable edge colo for this source path. Its native multi-sample methodology differs from Ookla and LibreSpeed.`})]})]}):(0,Y.jsxs)(Y.Fragment,{children:[(0,Y.jsx)(U,{control:c,name:`serverSelectionMode`,render:({field:e})=>(0,Y.jsx)(`div`,{className:`grid gap-2 sm:grid-cols-3`,children:[{value:`automatic`,label:`Automatic`,description:`Provider selects a target`},{value:`fixed`,label:`Fixed server`,description:`Persist a discovered ID`},...g===`librespeed`&&t?.capabilities.customServerUrls?[{value:`custom`,label:`Custom backend`,description:`Use your own HTTPS URL`}]:[]].map(t=>(0,Y.jsxs)(`button`,{type:`button`,onClick:()=>e.onChange(t.value),className:j(`rounded-lg border p-3 text-left transition`,e.value===t.value?`border-primary/50 bg-primary/[.07]`:`border-border bg-background hover:border-primary/25`),children:[(0,Y.jsx)(`span`,{className:`block text-xs font-semibold`,children:t.label}),(0,Y.jsx)(`span`,{className:`mt-0.5 block text-[10px] text-muted-foreground`,children:t.description})]},t.value))})}),g===`librespeed`&&!t?.capabilities.customServerUrls?(0,Y.jsxs)(`div`,{className:`flex gap-2 rounded-lg border border-amber-500/25 bg-amber-500/[.06] p-3 text-xs leading-5 text-amber-700 dark:text-amber-300`,children:[(0,Y.jsx)(O,{className:`mt-0.5 h-4 w-4 shrink-0`}),`Custom backends are disabled by this deployment. Add the exact base URL to APP_ALLOWED_CUSTOM_SERVER_URLS and restart MultiSpeed.`]}):null,h===`fixed`?(0,Y.jsx)(Ne,{provider:g,interfaceName:_,sourceIp:ee,ipFamily:v,value:y,onChange:e=>l(`serverId`,e,{shouldValidate:!0,shouldDirty:!0}),error:f.serverId?.message}):null,h===`custom`?(0,Y.jsxs)(`div`,{className:`space-y-4`,children:[(0,Y.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-2`,children:[(0,Y.jsx)(d,{label:`LibreSpeed backend URL`,required:!0,error:f.serverUrl?.message,children:(0,Y.jsx)(p,{type:`url`,...s(`serverUrl`),placeholder:`https://speed.example.net`})}),(0,Y.jsx)(d,{label:`Display name`,hint:`Optional`,error:f.customServerName?.message,children:(0,Y.jsx)(p,{...s(`customServerName`),placeholder:`Office LibreSpeed`})}),(0,Y.jsx)(d,{label:`Download endpoint`,hint:`Relative to backend URL`,error:f.customDownloadPath?.message,children:(0,Y.jsx)(p,{...s(`customDownloadPath`),placeholder:`garbage.php`})}),(0,Y.jsx)(d,{label:`Upload endpoint`,hint:`Relative to backend URL`,error:f.customUploadPath?.message,children:(0,Y.jsx)(p,{...s(`customUploadPath`),placeholder:`empty.php`})}),(0,Y.jsx)(d,{label:`Ping endpoint`,hint:`Relative to backend URL`,error:f.customPingPath?.message,children:(0,Y.jsx)(p,{...s(`customPingPath`),placeholder:`empty.php`})}),(0,Y.jsx)(d,{label:`Public-IP endpoint`,hint:`Relative to backend URL`,error:f.customIpPath?.message,children:(0,Y.jsx)(p,{...s(`customIpPath`),placeholder:`getIP.php`})})]}),(0,Y.jsxs)(`div`,{className:`grid gap-3 md:grid-cols-2`,children:[(0,Y.jsx)(U,{control:c,name:`allowInsecureCustomServer`,render:({field:e})=>(0,Y.jsx)(m,{checked:e.value,onChange:e.onChange,label:`Allow plain HTTP backend`,description:`Unsafe. Required only when the custom URL starts with http://.`})}),(0,Y.jsx)(U,{control:c,name:`skipTlsVerification`,render:({field:e})=>(0,Y.jsx)(m,{checked:e.value,onChange:e.onChange,label:`Skip TLS certificate verification`,description:`Unsafe. Enable only for a backend you control; every result records this setting.`})})]}),f.allowInsecureCustomServer?.message?(0,Y.jsx)(`p`,{className:`text-xs text-rose-600 dark:text-rose-400`,children:f.allowInsecureCustomServer.message}):null,x?(0,Y.jsxs)(`div`,{className:`flex gap-2 rounded-lg border border-rose-500/25 bg-rose-500/[.06] p-3 text-xs leading-5 text-rose-700 dark:text-rose-300`,children:[(0,Y.jsx)(O,{className:`mt-0.5 h-4 w-4 shrink-0`}),`Plain HTTP permits interception and response tampering. Use this only on an isolated network you control.`]}):null,b?(0,Y.jsxs)(`div`,{className:`flex gap-2 rounded-lg border border-rose-500/25 bg-rose-500/[.06] p-3 text-xs leading-5 text-rose-700 dark:text-rose-300`,children:[(0,Y.jsx)(O,{className:`mt-0.5 h-4 w-4 shrink-0`}),`TLS verification is disabled for this task. Traffic can be intercepted or redirected.`]}):null]}):null]})})]}),(0,Y.jsxs)(V,{children:[(0,Y.jsxs)(B,{children:[(0,Y.jsx)(R,{children:`Concrete source binding`}),(0,Y.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`No arbitrary address is selected and there is no fallback to the default route.`})]}),(0,Y.jsxs)(z,{className:`space-y-4`,children:[(0,Y.jsxs)(`div`,{className:`flex items-start justify-between gap-4 rounded-lg border border-border bg-muted/30 p-3`,children:[(0,Y.jsxs)(`div`,{children:[(0,Y.jsx)(`p`,{className:`text-xs font-semibold`,children:`Virtual network paths`}),(0,Y.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:`Show VPN, tunnel, bridge, and other virtual interfaces when they are intentional WAN paths.`})]}),(0,Y.jsx)(m,{checked:a,onChange:o,label:`Show virtual paths`})]}),(0,Y.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-3`,children:[(0,Y.jsx)(d,{label:`IP family`,required:!0,error:f.ipFamily?.message,children:(0,Y.jsxs)(u,{...s(`ipFamily`),onChange:e=>{s(`ipFamily`).onChange(e),l(`sourceIp`,``),l(`routeProfileId`,``)},children:[(0,Y.jsx)(`option`,{value:`auto`,children:`IPv4 or IPv6`}),(0,Y.jsx)(`option`,{value:`ipv4`,children:`IPv4 only`}),(0,Y.jsx)(`option`,{value:`ipv6`,children:`IPv6 only`})]})}),(0,Y.jsx)(d,{label:`Linux interface`,required:!0,error:f.interfaceName?.message,children:(0,Y.jsxs)(u,{...s(`interfaceName`),onChange:e=>{s(`interfaceName`).onChange(e),l(`sourceIp`,``,{shouldValidate:!0}),l(`routeProfileId`,``)},children:[(0,Y.jsx)(`option`,{value:``,children:`Select interface…`}),n.map(e=>(0,Y.jsxs)(`option`,{value:e.name,children:[e.name,` · `,e.operationalState||(e.operational?`up`:`down`),e.virtual?` · virtual`:``]},e.name))]})}),(0,Y.jsx)(d,{label:`Source address`,required:!0,error:f.sourceIp?.message,children:(0,Y.jsxs)(u,{...s(`sourceIp`),onChange:e=>{s(`sourceIp`).onChange(e),l(`routeProfileId`,``)},disabled:!_,children:[(0,Y.jsx)(`option`,{value:``,children:`Select concrete address…`}),i.map(e=>(0,Y.jsxs)(`option`,{value:e.address,children:[e.address,` · `,e.family.toUpperCase()]},e.address))]})})]}),_&&i.length===0?(0,Y.jsxs)(`p`,{className:`rounded-lg border border-amber-500/25 bg-amber-500/[.06] p-3 text-xs text-amber-700 dark:text-amber-300`,children:[`This interface has no non-link-local `,v===`auto`?``:v.toUpperCase(),` address. Choose another interface or family.`]}):null,(0,Y.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-2`,children:[(0,Y.jsx)(d,{label:`Route profile`,hint:`Recommended`,error:f.routeProfileId?.message,children:(0,Y.jsxs)(u,{...s(`routeProfileId`),children:[(0,Y.jsx)(`option`,{value:``,children:`No persisted route expectation`}),r.map(e=>{let t=e.interfaceName===_&&e.sourceIp===ee;return(0,Y.jsxs)(`option`,{value:e.id,disabled:!t,children:[e.name,` · `,e.interfaceName,` · `,e.sourceIp,t?``:` · path mismatch`]},e.id)})]})}),(0,Y.jsxs)(`div`,{className:`rounded-lg border border-border bg-muted/35 p-3`,children:[(0,Y.jsx)(`p`,{className:`text-xs font-semibold`,children:`Binding capability`}),(0,Y.jsx)(`p`,{className:`mt-1 text-[11px] leading-5 text-muted-foreground`,children:t?.capabilities.sourceAddressBinding?`Provider supports source-address binding.`:t?.capabilities.interfaceBinding?`Provider binds to the selected interface.`:`Review provider availability before enabling this task.`})]})]})]})]})]})}function Ne({provider:e,interfaceName:n,sourceIp:r,ipFamily:i,value:a,onChange:o,error:s}){let[c,l]=(0,J.useState)(``),u=t({queryKey:[`provider-servers`,e,c,n,r,i],queryFn:()=>L.providerServers(e,{search:c,interfaceName:n,sourceIp:r,ipFamily:i}),enabled:!1}),f=()=>void u.refetch();return(0,Y.jsxs)(`div`,{className:`space-y-3`,children:[(0,Y.jsx)(d,{label:`${I(e)} server`,required:!0,error:s,hint:`Discovery follows selected WAN`,children:(0,Y.jsxs)(`div`,{className:`flex gap-2`,children:[(0,Y.jsxs)(`div`,{className:`relative flex-1`,children:[(0,Y.jsx)(w,{className:`absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground`}),(0,Y.jsx)(p,{className:`pl-9`,value:c,onChange:e=>l(e.target.value),placeholder:`City, sponsor, host, or server ID`,"aria-label":`${I(e)} server search`,onKeyDown:e=>{e.key===`Enter`&&(e.preventDefault(),f())}})]}),(0,Y.jsxs)(F,{variant:`outline`,onClick:f,disabled:!n||!r||u.isFetching,children:[u.isFetching?(0,Y.jsx)(N,{}):(0,Y.jsx)(C,{className:`h-4 w-4`}),`Discover`]})]})}),!n||!r?(0,Y.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Select an interface and concrete source address before discovery.`}):null,u.error?(0,Y.jsx)(`p`,{className:`rounded-lg border border-rose-500/20 bg-rose-500/[.06] p-3 text-xs text-rose-600 dark:text-rose-400`,children:A(u.error)}):null,(u.data?.length??0)>0?(0,Y.jsx)(`div`,{className:`max-h-64 overflow-y-auto rounded-lg border border-border scrollbar-thin`,role:`listbox`,"aria-label":`Discovered servers`,children:u.data?.map(e=>(0,Y.jsx)(Pe,{server:e,selected:e.id===a,onSelect:()=>o(e.id)},e.id))}):u.isFetched&&!u.isFetching&&!u.error?(0,Y.jsx)(P,{compact:!0,title:`No servers found`,description:`Try a broader search or verify that discovery can use this source path.`}):null,a?(0,Y.jsxs)(`div`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,Y.jsx)(te,{className:`h-4 w-4 text-emerald-500`}),`Persisted server ID: `,(0,Y.jsx)(`code`,{className:`rounded bg-muted px-1.5 py-0.5 text-foreground`,children:a})]}):null]})}function Pe({server:e,selected:t,onSelect:n}){return(0,Y.jsxs)(`button`,{type:`button`,role:`option`,"aria-selected":t,onClick:n,className:j(`flex w-full items-center gap-3 border-b border-border p-3 text-left transition last:border-b-0 hover:bg-muted/50`,t&&`bg-primary/[.07]`),children:[(0,Y.jsx)(`span`,{className:j(`grid h-8 w-8 shrink-0 place-items-center rounded-lg border`,t?`border-primary/30 bg-primary/10 text-primary`:`border-border bg-background text-muted-foreground`),children:(0,Y.jsx)(T,{className:`h-4 w-4`})}),(0,Y.jsxs)(`span`,{className:`min-w-0 flex-1`,children:[(0,Y.jsx)(`span`,{className:`block truncate text-xs font-semibold`,children:e.name||e.host}),(0,Y.jsxs)(`span`,{className:`mt-0.5 block truncate text-[10px] text-muted-foreground`,children:[e.sponsor,` · `,e.location,e.country?`, ${e.country}`:``]})]}),(0,Y.jsxs)(`span`,{className:`font-mono text-[10px] text-muted-foreground`,children:[`#`,e.id]})]})}function Fe({form:e,cronRuns:t}){let{register:n,control:r,setValue:i,formState:{errors:a}}=e,o=q({control:r,name:`cronExpression`}),s=q({control:r,name:`timezone`}),c=e=>`${oe(e,!1,s)} (${s})`;return(0,Y.jsxs)(`div`,{className:`space-y-5`,children:[(0,Y.jsxs)(V,{children:[(0,Y.jsxs)(B,{children:[(0,Y.jsx)(R,{children:`Independent schedule`}),(0,Y.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`Standard five-field cron syntax is evaluated in this task’s IANA timezone.`})]}),(0,Y.jsxs)(z,{className:`space-y-5`,children:[(0,Y.jsx)(`div`,{className:`grid gap-2 sm:grid-cols-2 xl:grid-cols-5`,children:y.map(e=>(0,Y.jsxs)(`button`,{type:`button`,onClick:()=>i(`cronExpression`,e.expression,{shouldValidate:!0,shouldDirty:!0}),className:j(`rounded-lg border p-3 text-left transition`,o===e.expression?`border-primary/50 bg-primary/[.07]`:`border-border bg-background hover:border-primary/25`),children:[(0,Y.jsx)(`span`,{className:`block text-xs font-semibold`,children:e.label}),(0,Y.jsx)(`span`,{className:`mt-0.5 block text-[10px] leading-4 text-muted-foreground`,children:e.description})]},e.id))}),(0,Y.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-2`,children:[(0,Y.jsx)(d,{label:`Advanced cron expression`,required:!0,error:a.cronExpression?.message,hint:v(o),children:(0,Y.jsx)(p,{className:`font-mono`,...n(`cronExpression`),placeholder:`0 */6 * * *`,spellCheck:!1})}),(0,Y.jsx)(d,{label:`Timezone`,required:!0,error:a.timezone?.message,children:(0,Y.jsxs)(u,{...n(`timezone`),children:[b.includes(s)?null:(0,Y.jsx)(`option`,{value:s,children:s}),b.map(e=>(0,Y.jsx)(`option`,{value:e,children:e},e))]})})]}),(0,Y.jsxs)(`div`,{className:`rounded-xl border border-border bg-muted/30 p-4`,children:[(0,Y.jsx)(`p`,{className:`mb-3 text-[11px] font-semibold uppercase tracking-[.1em] text-muted-foreground`,children:`Next five executions`}),t.length?(0,Y.jsx)(`ol`,{className:`grid gap-2 sm:grid-cols-2 xl:grid-cols-5`,children:t.map((e,t)=>(0,Y.jsxs)(`li`,{className:`rounded-lg border border-border bg-background p-2.5`,children:[(0,Y.jsxs)(`span`,{className:`block text-[10px] text-muted-foreground`,children:[`Run `,t+1]}),(0,Y.jsx)(`span`,{className:`mt-0.5 block text-xs font-semibold tabular-nums`,children:c(e.toISOString())})]},e.toISOString()))}):(0,Y.jsx)(`p`,{className:`text-xs text-rose-600 dark:text-rose-400`,children:`A preview is unavailable until the expression and timezone are valid.`})]})]})]}),(0,Y.jsxs)(V,{children:[(0,Y.jsxs)(B,{children:[(0,Y.jsx)(R,{children:`Execution guardrails`}),(0,Y.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`Bound run duration and avoid competing tests on the same path.`})]}),(0,Y.jsxs)(z,{className:`space-y-4`,children:[(0,Y.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,Y.jsx)(d,{label:`Timeout`,hint:`5–3600 seconds`,error:a.timeoutSeconds?.message,children:(0,Y.jsx)(p,{type:`number`,min:5,max:3600,...n(`timeoutSeconds`,{valueAsNumber:!0})})}),(0,Y.jsx)(d,{label:`Random start jitter`,hint:`0–3600 seconds`,error:a.randomJitterSeconds?.message,children:(0,Y.jsx)(p,{type:`number`,min:0,max:3600,...n(`randomJitterSeconds`,{valueAsNumber:!0})})})]}),(0,Y.jsxs)(`div`,{className:`grid gap-3 md:grid-cols-2`,children:[(0,Y.jsx)(U,{control:r,name:`preventOverlap`,render:({field:e})=>(0,Y.jsx)(m,{checked:e.value,onChange:e.onChange,label:`Prevent task overlap`,description:`Skip a new execution while this task is active.`})}),(0,Y.jsx)(d,{label:`Route-validation behavior`,required:!0,error:a.routeValidation?.message,children:(0,Y.jsxs)(u,{...n(`routeValidation`),children:[(0,Y.jsx)(`option`,{value:`required`,children:`Full route validation · fail closed`}),(0,Y.jsx)(`option`,{value:`interface-only`,children:`Interface and source binding · fail closed`})]})})]})]})]})]})}function Ie({values:e,provider:t,route:n,cronRuns:r,preflight:i,configurationChanged:o,validating:s,onValidate:c}){let{toast:l}=se(),u=t=>`${oe(t,!1,e.timezone)} (${e.timezone})`,d=a({mutationFn:()=>n?L.validateRoute(n.id):Promise.reject(Error(`No route profile selected.`)),onSuccess:e=>l({tone:e.success?`success`:`error`,title:e.success?`Route profile validated`:`Route mismatch detected`,description:e.message}),onError:e=>l({tone:`error`,title:`Route validation failed`,description:A(e)})}),f=[{label:`Provider`,value:I(e.provider),extra:t?.available?`Available`:`Unavailable`},{label:`Target`,value:e.provider===`cloudflare`?`Automatic edge selection`:e.serverSelectionMode===`fixed`?`Server #${e.serverId}`:e.serverSelectionMode===`custom`?e.serverUrl:`Automatic provider selection`},{label:`Source path`,value:`${e.interfaceName||`No interface`} · ${e.sourceIp||`No address`}`,extra:e.ipFamily.toUpperCase()},{label:`Route profile`,value:n?.name??`No persisted profile`,extra:e.routeValidation},{label:`Schedule`,value:`${e.cronExpression} · ${e.timezone}`,extra:v(e.cronExpression)},{label:`Guardrails`,value:`${e.timeoutSeconds}s timeout · ${e.randomJitterSeconds}s jitter`,extra:e.preventOverlap?`Overlap prevented`:`Overlap allowed`}];return(0,Y.jsxs)(V,{children:[(0,Y.jsxs)(B,{children:[(0,Y.jsx)(R,{children:`Review this independent path`}),(0,Y.jsx)(`p`,{className:`text-xs leading-5 text-muted-foreground`,children:`Saving recalculates the persisted schedule immediately. Enabling it does not trigger an unscheduled speed test.`})]}),(0,Y.jsxs)(z,{className:`space-y-5`,children:[(0,Y.jsxs)(`div`,{className:`flex flex-col justify-between gap-3 rounded-xl border border-primary/25 bg-primary/[.055] p-4 sm:flex-row sm:items-center`,children:[(0,Y.jsxs)(`div`,{children:[(0,Y.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Y.jsx)(`h3`,{className:`text-base font-semibold`,children:e.name||`Unnamed task`}),(0,Y.jsx)(fe,{provider:e.provider}),(0,Y.jsx)(de,{tone:e.enabled?`success`:`neutral`,children:e.enabled?`Enabled`:`Disabled`})]}),(0,Y.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:e.description||`No description provided.`})]}),(0,Y.jsx)(D,{className:`h-7 w-7 shrink-0 text-primary`})]}),(0,Y.jsx)(`dl`,{className:`grid gap-3 md:grid-cols-2`,children:f.map(e=>(0,Y.jsxs)(`div`,{className:`rounded-lg border border-border bg-background p-3`,children:[(0,Y.jsx)(`dt`,{className:`text-[10px] font-semibold uppercase tracking-[.1em] text-muted-foreground`,children:e.label}),(0,Y.jsx)(`dd`,{className:`mt-1 break-words text-xs font-semibold text-foreground`,children:e.value}),e.extra?(0,Y.jsx)(`dd`,{className:`mt-0.5 text-[10px] capitalize text-muted-foreground`,children:e.extra}):null]},e.label))}),e.provider===`librespeed`&&e.skipTlsVerification?(0,Y.jsxs)(`div`,{className:`flex gap-2 rounded-lg border border-rose-500/25 bg-rose-500/[.06] p-3 text-xs text-rose-700 dark:text-rose-300`,children:[(0,Y.jsx)(O,{className:`h-4 w-4 shrink-0`}),`TLS certificate verification is disabled and will be recorded in result metadata.`]}):null,(0,Y.jsxs)(`div`,{role:i?.success?`status`:`alert`,className:j(`flex gap-3 rounded-lg border p-3 text-xs leading-5`,i?.success?`border-emerald-500/25 bg-emerald-500/[.06] text-emerald-700 dark:text-emerald-300`:i?`border-rose-500/25 bg-rose-500/[.06] text-rose-700 dark:text-rose-300`:`border-amber-500/25 bg-amber-500/[.06] text-amber-700 dark:text-amber-300`),children:[i?.success?(0,Y.jsx)(te,{className:`mt-0.5 h-4 w-4 shrink-0`}):(0,Y.jsx)(O,{className:`mt-0.5 h-4 w-4 shrink-0`}),(0,Y.jsxs)(`div`,{children:[(0,Y.jsx)(`p`,{className:`font-semibold`,children:i?.success?`Current configuration passed preflight`:i?`Current configuration failed preflight`:o?`Configuration changed since preflight`:`Current configuration has not been validated`}),(0,Y.jsx)(`p`,{className:`mt-0.5`,children:i?.message||(e.provider===`ookla`?`Ookla tasks may be saved before the operator-installed CLI is available. Validation and execution remain blocked until the provider is ready.`:e.enabled?`A successful preflight is required before this enabled task can be saved.`:`Disabled tasks may be saved without preflight, but validation is recommended before enabling.`)})]})]}),(0,Y.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,Y.jsxs)(F,{onClick:c,disabled:s,children:[s?(0,Y.jsx)(N,{}):(0,Y.jsx)(C,{className:`h-4 w-4`}),`Validate current configuration`]}),(0,Y.jsxs)(F,{variant:`outline`,onClick:()=>d.mutate(),disabled:!n||d.isPending,children:[d.isPending?(0,Y.jsx)(N,{}):(0,Y.jsx)(E,{className:`h-4 w-4`}),`Validate route profile only`]})]}),(0,Y.jsxs)(`div`,{className:`rounded-lg border border-border bg-muted/30 p-3 text-xs text-muted-foreground`,children:[`First scheduled execution: `,(0,Y.jsx)(`strong`,{className:`font-semibold text-foreground`,children:r[0]?u(r[0].toISOString()):`Unavailable`})]})]})]})}var Le={serverDiscovery:!0,fixedServerIds:!0,customServerUrls:!1,interfaceBinding:!0,sourceAddressBinding:!0,ipv4:!0,ipv6:!0,jitter:!0,packetLoss:!1,resultUrls:!1},Re=[{id:`ookla`,displayName:`Ookla Speedtest`,available:!0,version:``,message:``,capabilities:{...Le,packetLoss:!0,resultUrls:!0}},{id:`librespeed`,displayName:`LibreSpeed`,available:!0,version:``,message:``,capabilities:{...Le,customServerUrls:!0}},{id:`cloudflare`,displayName:`Cloudflare`,available:!0,version:`native`,message:``,capabilities:{...Le,serverDiscovery:!1,fixedServerIds:!1}}];export{Te as buildTaskInput,X as customEndpointSchema,Ae as default,xe as taskSchema}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/TasksPage-BEXsLscb.js b/internal/frontend/dist/assets/TasksPage-CxX5SuNf.js similarity index 98% rename from internal/frontend/dist/assets/TasksPage-BEXsLscb.js rename to internal/frontend/dist/assets/TasksPage-CxX5SuNf.js index 9ec397a..4470335 100644 --- a/internal/frontend/dist/assets/TasksPage-BEXsLscb.js +++ b/internal/frontend/dist/assets/TasksPage-CxX5SuNf.js @@ -1 +1 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,b as i,t as a,w as o,x as s}from"./query-DWNqGEie.js";import{a as c,c as l,d as u,i as d,l as f,n as p,o as m,r as h,s as g,t as _,y as v}from"./react-DsKPNe9r.js";import{a as y,i as b,r as x,s as S}from"./fields-PJGT-Jp7.js";import{t as C}from"./funnel-x-BSCtvBiK.js";import{t as ee}from"./pen-line-BMm6ehcb.js";import{t as w}from"./plus-C7xMvwvj.js";import{t as T}from"./search-B8uA6mdW.js";import{t as te}from"./trash-2-DJb0gq__.js";import{$ as E,D as ne,I as D,J as O,K as k,Q as A,T as j,X as M,Y as N,Z as re,a as ie,at as P,c as ae,ct as F,et as I,g as oe,i as se,it as L,n as R,nt as z,o as ce,ot as B,q as le,r as ue,rt as de,s as V,st as H,t as fe,tt as pe,v as me,w as he}from"./index-B84OiuS0.js";import{t as ge}from"./PageHeader-DGX-y_Bi.js";import{n as _e,t as ve}from"./badge-D4YKBNKr.js";import{t as ye}from"./card-DEXft0Cm.js";import{t as be}from"./EntityBadges-DZDjvrcw.js";import{t as xe}from"./dialog-DXkCUNSe.js";import{n as Se,r as Ce,t as we}from"./columnVisibilityFeature-CB2cjdCS.js";var Te=D(`copy`,[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]),Ee=D(`ellipsis`,[[`circle`,{cx:`12`,cy:`12`,r:`1`,key:`41hilf`}],[`circle`,{cx:`19`,cy:`12`,r:`1`,key:`1wjl8i`}],[`circle`,{cx:`5`,cy:`12`,r:`1`,key:`1pcz8c`}]]),De=D(`play`,[[`path`,{d:`M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z`,key:`10ikf1`}]]),U=e(r(),1);function Oe(e){return{name:e.name,description:e.description,enabled:e.enabled,provider:e.provider,cronExpression:e.cronExpression,timezone:e.timezone,randomJitterSeconds:e.randomJitterSeconds,serverSelectionMode:e.serverSelectionMode,serverId:e.serverId,serverUrl:e.serverUrl,customServerDefinition:e.customServerDefinition??{},interfaceName:e.interfaceName,sourceIp:e.sourceIp,ipFamily:e.ipFamily,routeProfileId:e.routeProfileId,timeoutSeconds:e.timeoutSeconds,providerOptions:e.providerOptions??{},preventOverlap:e.preventOverlap,routeValidation:e.routeValidation}}var W=n(),ke=Object.defineProperty,G=(e,t)=>ke(e,`name`,{value:t,configurable:!0});function Ae(e){let t=e+`CollectionProvider`,[n,r]=B(t),[i,a]=n(t,{collectionRef:{current:null},itemMap:new Map}),o=G(e=>{let{scope:t,children:n}=e,r=U.useRef(null),a=U.useRef(new Map).current;return(0,W.jsx)(i,{scope:t,itemMap:a,collectionRef:r,children:n})},`CollectionProvider`);o.displayName=t;let s=e+`CollectionSlot`,c=z(s),l=U.forwardRef((e,t)=>{let{scope:n,children:r}=e,i=a(s,n),o=H(t,i.collectionRef);return(0,W.jsx)(c,{ref:o,children:r})});l.displayName=s;let u=e+`CollectionItemSlot`,d=`data-radix-collection-item`,f=z(u),p=U.forwardRef((e,t)=>{let{scope:n,children:r,...i}=e,o=U.useRef(null),s=H(t,o),c=a(u,n);return U.useEffect(()=>(c.itemMap.set(o,{ref:o,...i}),()=>void c.itemMap.delete(o))),(0,W.jsx)(f,{[d]:``,ref:s,children:r})});p.displayName=u;function m(t){let n=a(e+`CollectionConsumer`,t);return U.useCallback(()=>{let e=n.collectionRef.current;if(!e)return[];let t=Array.from(e.querySelectorAll(`[${d}]`));return Array.from(n.itemMap.values()).sort((e,n)=>t.indexOf(e.ref.current)-t.indexOf(n.ref.current))},[n.collectionRef,n.itemMap])}return G(m,`useCollection`),[{Provider:o,Slot:l,ItemSlot:p},m,r]}G(Ae,`createCollection`);var je=new WeakMap,Me=class e extends Map{static{G(this,`OrderedDict`)}#e;constructor(e){super(e),this.#e=[...super.keys()],je.set(this,!0)}set(e,t){return je.get(this)&&(this.has(e)?this.#e[this.#e.indexOf(e)]=e:this.#e.push(e)),super.set(e,t),this}insert(e,t,n){let r=this.has(t),i=this.#e.length,a=Fe(e),o=a>=0?a:i+a,s=o<0||o>=i?-1:o;if(s===this.size||r&&s===this.size-1||s===-1)return this.set(t,n),this;let c=this.size+ +!r;a<0&&o++;let l=[...this.#e],u,d=!1;for(let e=o;e=this.size&&(r=this.size-1),this.at(r)}keyFrom(e,t){let n=this.indexOf(e);if(n===-1)return;let r=n+t;return r<0&&(r=0),r>=this.size&&(r=this.size-1),this.keyAt(r)}find(e,t){let n=0;for(let r of this){if(Reflect.apply(e,t,[r,n,this]))return r;n++}}findIndex(e,t){let n=0;for(let r of this){if(Reflect.apply(e,t,[r,n,this]))return n;n++}return-1}filter(t,n){let r=[],i=0;for(let e of this)Reflect.apply(t,n,[e,i,this])&&r.push(e),i++;return new e(r)}map(t,n){let r=[],i=0;for(let e of this)r.push([e[0],Reflect.apply(t,n,[e,i,this])]),i++;return new e(r)}reduce(...e){let[t,n]=e,r=0,i=n??this.at(0);for(let n of this)i=r===0&&e.length===1?n:Reflect.apply(t,this,[i,n,r,this]),r++;return i}reduceRight(...e){let[t,n]=e,r=n??this.at(-1);for(let n=this.size-1;n>=0;n--){let i=this.at(n);r=n===this.size-1&&e.length===1?i:Reflect.apply(t,this,[r,i,n,this])}return r}toSorted(t){let n=[...this.entries()].sort(t);return new e(n)}toReversed(){let t=new e;for(let e=this.size-1;e>=0;e--){let n=this.keyAt(e),r=this.get(n);t.set(n,r)}return t}toSpliced(...t){let n=[...this.entries()];return n.splice(...t),new e(n)}slice(t,n){let r=new e,i=this.size-1;if(t===void 0)return r;t<0&&(t+=this.size),n!==void 0&&n>0&&(i=n-1);for(let e=t;e<=i;e++){let t=this.keyAt(e),n=this.get(t);r.set(t,n)}return r}every(e,t){let n=0;for(let r of this){if(!Reflect.apply(e,t,[r,n,this]))return!1;n++}return!0}some(e,t){let n=0;for(let r of this){if(Reflect.apply(e,t,[r,n,this]))return!0;n++}return!1}};function Ne(e,t){if(`at`in Array.prototype)return Array.prototype.at.call(e,t);let n=Pe(e,t);return n===-1?void 0:e[n]}G(Ne,`at`);function Pe(e,t){let n=e.length,r=Fe(t),i=r>=0?r:n+r;return i<0||i>=n?-1:i}G(Pe,`toSafeIndex`);function Fe(e){return e!==e||e===0?0:Math.trunc(e)}G(Fe,`toSafeInteger`);function Ie(e){let t=e+`CollectionProvider`,[n,r]=B(t),[i,a]=n(t,{collectionElement:null,collectionRef:{current:null},collectionRefObject:{current:null},itemMap:new Me,setItemMap:G(()=>void 0,`setItemMap`)}),o=G(({state:e,...t})=>e?(0,W.jsx)(c,{...t,state:e}):(0,W.jsx)(s,{...t}),`CollectionProvider`);o.displayName=t;let s=G(e=>{let t=h();return(0,W.jsx)(c,{...e,state:t})},`CollectionInit`);s.displayName=t+`Init`;let c=G(e=>{let{scope:t,children:n,state:r}=e,a=U.useRef(null),[o,s]=U.useState(null),c=H(a,s),[l,u]=r;return U.useEffect(()=>{if(!o)return;let e=Be(()=>{});return e.observe(o,{childList:!0,subtree:!0}),()=>{e.disconnect()}},[o]),(0,W.jsx)(i,{scope:t,itemMap:l,setItemMap:u,collectionRef:c,collectionRefObject:a,collectionElement:o,children:n})},`CollectionProviderImpl`);c.displayName=t+`Impl`;let l=e+`CollectionSlot`,u=z(l),d=U.forwardRef((e,t)=>{let{scope:n,children:r}=e,i=a(l,n),o=H(t,i.collectionRef);return(0,W.jsx)(u,{ref:o,children:r})});d.displayName=l;let f=e+`CollectionItemSlot`,p=z(f),m=U.forwardRef((e,t)=>{let{scope:n,children:r,...i}=e,o=U.useRef(null),[s,c]=U.useState(null),l=H(t,o,c),{setItemMap:u}=a(f,n),d=U.useRef(i);Le(d.current,i)||(d.current=i);let m=d.current;return U.useEffect(()=>{let e=m;return u(t=>s?t.has(s)?t.set(s,{...e,element:s}).toSorted(ze):(t.set(s,{...e,element:s}),t.toSorted(ze)):t),()=>{u(e=>!s||!e.has(s)?e:(e.delete(s),new Me(e)))}},[s,m,u]),(0,W.jsx)(p,{"data-radix-collection-item":``,ref:l,children:r})});m.displayName=f;function h(){return U.useState(new Me)}G(h,`useInitCollection`);function g(t){let{itemMap:n}=a(e+`CollectionConsumer`,t);return n}return G(g,`useCollection`),[{Provider:o,Slot:d,ItemSlot:m},{createCollectionScope:r,useCollection:g,useInitCollection:h}]}G(Ie,`createCollection`);function Le(e,t){if(e===t)return!0;if(typeof e!=`object`||typeof t!=`object`||e==null||t==null)return!1;let n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(let r of n)if(!Object.prototype.hasOwnProperty.call(t,r)||e[r]!==t[r])return!1;return!0}G(Le,`shallowEqual`);function Re(e,t){return!!(t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_PRECEDING)}G(Re,`isElementPreceding`);function ze(e,t){return!e[1].element||!t[1].element?0:Re(e[1].element,t[1].element)?-1:1}G(ze,`sortByDocumentPosition`);function Be(e){return new MutationObserver(t=>{for(let n of t)if(n.type===`childList`){e();return}})}G(Be,`getChildListObserver`);var Ve=Object.defineProperty,He=(e,t)=>Ve(e,`name`,{value:t,configurable:!0}),Ue=U.createContext(void 0);function We(e){let t=U.useContext(Ue);return e||t||`ltr`}He(We,`useDirection`);var Ge=Object.defineProperty,K=(e,t)=>Ge(e,`name`,{value:t,configurable:!0}),Ke=`Popper`,[qe,Je]=B(Ke),[Ye,Xe]=qe(Ke),Ze=K(e=>{let{__scopePopper:t,children:n}=e,[r,i]=U.useState(null),[a,o]=U.useState(void 0);return(0,W.jsx)(Ye,{scope:t,anchor:r,onAnchorChange:i,placementState:a,setPlacementState:o,children:n})},`Popper`),Qe=`PopperAnchor`,$e=U.forwardRef(K(function(e,t){let{__scopePopper:n,virtualRef:r,...i}=e,a=Xe(Qe,n),o=U.useRef(null),s=a.onAnchorChange,c=U.useCallback(e=>{o.current=e,e&&s(e)},[s]),l=H(t,c),u=U.useRef(null);U.useEffect(()=>{if(!r)return;let e=u.current;u.current=r.current,e!==u.current&&s(u.current)});let d=a.placementState&&ot(a.placementState),f=d?.[0],p=d?.[1];return r?null:(0,W.jsx)(I.div,{"data-radix-popper-side":f,"data-radix-popper-align":p,...i,ref:l})},`PopperAnchor`)),et=`PopperContent`,[tt,nt]=qe(et),rt=U.forwardRef(K(function(e,t){let{__scopePopper:n,side:r=`bottom`,sideOffset:i=0,align:a=`center`,alignOffset:o=0,arrowPadding:s=0,avoidCollisions:u=!0,collisionBoundary:v=[],collisionPadding:y=0,sticky:b=`partial`,hideWhenDetached:x=!1,updatePositionStrategy:C=`optimized`,onPlaced:ee,...w}=e,T=Xe(et,n),[te,ne]=U.useState(null),D=H(t,ne),[O,k]=U.useState(null),A=S(O),j=A?.width??0,M=A?.height??0,N=r+(a===`center`?``:`-`+a),re=typeof y==`number`?y:{top:0,right:0,bottom:0,left:0,...y},ie=Array.isArray(v)?v:[v],ae=ie.length>0,F={padding:re,boundary:ie.filter(it),altBoundary:ae},{refs:oe,floatingStyles:se,placement:L,isPositioned:R,middlewareData:z}=l({strategy:`fixed`,placement:N,whileElementsMounted:K((...e)=>f(...e,{animationFrame:C===`always`}),`whileElementsMounted`),elements:{reference:T.anchor},middleware:[c({mainAxis:i+M,alignmentAxis:o}),u&&m({mainAxis:!0,crossAxis:!1,limiter:b===`partial`?d():void 0,...F}),u&&p({...F}),g({...F,apply:K(({elements:e,rects:t,availableWidth:n,availableHeight:r})=>{let{width:i,height:a}=t.reference,o=e.floating.style;o.setProperty(`--radix-popper-available-width`,`${n}px`),o.setProperty(`--radix-popper-available-height`,`${r}px`),o.setProperty(`--radix-popper-anchor-width`,`${i}px`),o.setProperty(`--radix-popper-anchor-height`,`${a}px`)},`apply`)}),O&&_({element:O,padding:s}),at({arrowWidth:j,arrowHeight:M}),x&&h({strategy:`referenceHidden`,...F,boundary:ae?F.boundary:void 0})]}),ce=T.setPlacementState;P(()=>(ce(L),()=>{ce(void 0)}),[L,ce]);let[B,le]=ot(L),ue=E(ee);P(()=>{R&&ue?.()},[R,ue]);let de=z.arrow?.x,V=z.arrow?.y,fe=z.arrow?.centerOffset!==0,[pe,me]=U.useState();return P(()=>{te&&me(window.getComputedStyle(te).zIndex)},[te]),(0,W.jsx)(`div`,{ref:oe.setFloating,"data-radix-popper-content-wrapper":``,style:{...se,transform:R?se.transform:`translate(0, -200%)`,minWidth:`max-content`,zIndex:pe,"--radix-popper-transform-origin":[z.transformOrigin?.x,z.transformOrigin?.y].join(` `),...z.hide?.referenceHidden&&{visibility:`hidden`,pointerEvents:`none`}},dir:e.dir,children:(0,W.jsx)(tt,{scope:n,placedSide:B,placedAlign:le,onArrowChange:k,arrowX:de,arrowY:V,shouldHideArrow:fe,children:(0,W.jsx)(I.div,{"data-side":B,"data-align":le,...w,ref:D,style:{...w.style,animation:R?w.style?.animation:`none`}})})})},`PopperContent`));function it(e){return e!==null}K(it,`isNotNull`);var at=K(e=>({name:`transformOrigin`,options:e,fn(t){let{placement:n,rects:r,middlewareData:i}=t,a=i.arrow?.centerOffset!==0,o=a?0:e.arrowWidth,s=a?0:e.arrowHeight,[c,l]=ot(n),u={start:`0%`,center:`50%`,end:`100%`}[l],d=(i.arrow?.x??0)+o/2,f=(i.arrow?.y??0)+s/2,p=``,m=``;return c===`bottom`?(p=a?u:`${d}px`,m=`${-s}px`):c===`top`?(p=a?u:`${d}px`,m=`${r.floating.height+s}px`):c===`right`?(p=`${-s}px`,m=a?u:`${f}px`):c===`left`&&(p=`${r.floating.width+s}px`,m=a?u:`${f}px`),{data:{x:p,y:m}}}}),`transformOrigin`);function ot(e){let[t,n=`center`]=e.split(`-`);return[t,n]}K(ot,`getSideAndAlignFromPlacement`);var st=Ze,ct=$e,lt=rt,ut=Object.defineProperty,dt=(e,t)=>ut(e,`name`,{value:t,configurable:!0}),ft=!1;function pt(){let[e,t]=U.useState(ft);return U.useEffect(()=>{ft||(ft=!0,t(!0))},[]),e}dt(pt,`useIsHydrated`);var mt=U.useSyncExternalStore;function ht(){return()=>{}}dt(ht,`subscribe`);function gt(){return mt(ht,()=>!0,()=>!1)}dt(gt,`useIsHydratedModern`);var _t=typeof mt==`function`?gt:pt,vt=Object.defineProperty,q=(e,t)=>vt(e,`name`,{value:t,configurable:!0}),yt=`rovingFocusGroup.onEntryFocus`,bt={bubbles:!1,cancelable:!0},xt=`RovingFocusGroup`,[St,Ct,wt]=Ae(xt),[Tt,Et]=B(xt,[wt]),[Dt,Ot]=Tt(xt),kt=U.forwardRef(q(function(e,t){return(0,W.jsx)(St.Provider,{scope:e.__scopeRovingFocusGroup,children:(0,W.jsx)(St.Slot,{scope:e.__scopeRovingFocusGroup,children:(0,W.jsx)(At,{...e,ref:t})})})},`RovingFocusGroup`)),At=U.forwardRef(q(function(e,t){let{__scopeRovingFocusGroup:n,orientation:r,loop:i=!1,dir:a,currentTabStopId:o,defaultCurrentTabStopId:s,onCurrentTabStopIdChange:c,onEntryFocus:l,preventScrollOnEntryFocus:u=!1,...d}=e,f=U.useRef(null),p=H(t,f),m=We(a),[h,g]=de({prop:o,defaultProp:s??null,onChange:c,caller:xt}),[_,v]=U.useState(!1),y=E(l),b=Ct(n),x=U.useRef(!1),[S,C]=U.useState(0);return U.useEffect(()=>{let e=f.current;if(e)return e.addEventListener(yt,y),()=>e.removeEventListener(yt,y)},[y]),(0,W.jsx)(Dt,{scope:n,orientation:r,dir:m,loop:i,currentTabStopId:h,onItemFocus:U.useCallback(e=>g(e),[g]),onItemShiftTab:U.useCallback(()=>v(!0),[]),onFocusableItemAdd:U.useCallback(()=>C(e=>e+1),[]),onFocusableItemRemove:U.useCallback(()=>C(e=>e-1),[]),children:(0,W.jsx)(I.div,{tabIndex:_||S===0?-1:0,"data-orientation":r,...d,ref:p,style:{outline:`none`,...e.style},onMouseDown:F(e.onMouseDown,()=>{x.current=!0}),onFocus:F(e.onFocus,e=>{let t=!x.current;if(e.target===e.currentTarget&&t&&!_){let t=new CustomEvent(yt,bt);if(e.currentTarget.dispatchEvent(t),!t.defaultPrevented){let e=b().filter(e=>e.focusable);It([e.find(e=>e.active),e.find(e=>e.id===h),...e].filter(Boolean).map(e=>e.ref.current),u)}}x.current=!1}),onBlur:F(e.onBlur,()=>v(!1))})})},`RovingFocusGroupImpl`)),jt=`RovingFocusGroupItem`,Mt=U.forwardRef(q(function(e,t){let{__scopeRovingFocusGroup:n,focusable:r=!0,active:i=!1,tabStopId:a,children:o,...s}=e,c=L(),l=a||c,u=Ot(jt,n),d=u.currentTabStopId===l,f=Ct(n),{onFocusableItemAdd:p,onFocusableItemRemove:m,currentTabStopId:h}=u,g=_t();return P(()=>{if(!(!g||!r))return p(),()=>m()},[g,r,p,m]),U.useEffect(()=>{if(!(g||!r))return p(),()=>m()},[g,r,p,m]),(0,W.jsx)(St.ItemSlot,{scope:n,id:l,focusable:r,active:i,children:(0,W.jsx)(I.span,{tabIndex:d?0:-1,"data-orientation":u.orientation,...s,ref:t,onMouseDown:F(e.onMouseDown,e=>{r?u.onItemFocus(l):e.preventDefault()}),onFocus:F(e.onFocus,()=>u.onItemFocus(l)),onKeyDown:F(e.onKeyDown,e=>{if(e.key===`Tab`&&e.shiftKey){u.onItemShiftTab();return}if(e.target!==e.currentTarget)return;let t=Ft(e,u.orientation,u.dir);if(t!==void 0){if(e.metaKey||e.ctrlKey||e.altKey||e.shiftKey)return;e.preventDefault();let n=f().filter(e=>e.focusable).map(e=>e.ref.current);if(t===`last`)n.reverse();else if(t===`prev`||t===`next`){t===`prev`&&n.reverse();let r=n.indexOf(e.currentTarget);n=u.loop?Lt(n,r+1):n.slice(r+1)}setTimeout(()=>It(n))}}),children:typeof o==`function`?o({isCurrentTabStop:d,hasTabStop:h!=null}):o})})},`RovingFocusGroupItem`)),Nt={ArrowLeft:`prev`,ArrowUp:`prev`,ArrowRight:`next`,ArrowDown:`next`,PageUp:`first`,Home:`first`,PageDown:`last`,End:`last`};function Pt(e,t){return t===`rtl`?e===`ArrowLeft`?`ArrowRight`:e===`ArrowRight`?`ArrowLeft`:e:e}q(Pt,`getDirectionAwareKey`);function Ft(e,t,n){let r=Pt(e.key,n);if(!(t===`vertical`&&[`ArrowLeft`,`ArrowRight`].includes(r))&&!(t===`horizontal`&&[`ArrowUp`,`ArrowDown`].includes(r)))return Nt[r]}q(Ft,`getFocusIntent`);function It(e,t=!1){let n=document.activeElement;for(let r of e)if(r===n||(r.focus({preventScroll:t}),document.activeElement!==n))return}q(It,`focusFirst`);function Lt(e,t){return e.map((n,r)=>e[(t+r)%e.length])}q(Lt,`wrapArray`);var Rt=kt,zt=Mt,Bt=Object.defineProperty,J=(e,t)=>Bt(e,`name`,{value:t,configurable:!0}),Vt=[`Enter`,` `],Ht=[`ArrowDown`,`PageUp`,`Home`],Ut=[`ArrowUp`,`PageDown`,`End`],Wt=[...Ht,...Ut];[...Vt],[...Vt];var Gt=`Menu`,[Kt,qt,Jt]=Ae(Gt),[Y,Yt]=B(Gt,[Jt,Je,Et]),Xt=Je(),Zt=Et(),[Qt,$t]=Y(Gt),[en,tn]=Y(Gt),nn=J(e=>{let{__scopeMenu:t,open:n=!1,children:r,dir:i,onOpenChange:a,modal:o=!0}=e,s=Xt(t),[c,l]=U.useState(null),u=U.useRef(!1),d=E(a),f=We(i);return U.useEffect(()=>{let e=J(()=>{u.current=!0,document.addEventListener(`pointerdown`,t,{capture:!0,once:!0}),document.addEventListener(`pointermove`,t,{capture:!0,once:!0})},`handleKeyDown`),t=J(()=>u.current=!1,`handlePointer`);return document.addEventListener(`keydown`,e,{capture:!0}),()=>{document.removeEventListener(`keydown`,e,{capture:!0}),document.removeEventListener(`pointerdown`,t,{capture:!0}),document.removeEventListener(`pointermove`,t,{capture:!0})}},[]),U.useEffect(()=>{if(!n)return;let e=J(()=>d(!1),`handleBlur`);return window.addEventListener(`blur`,e),()=>window.removeEventListener(`blur`,e)},[n,d]),(0,W.jsx)(st,{...s,children:(0,W.jsx)(Qt,{scope:t,open:n,onOpenChange:d,content:c,onContentChange:l,children:(0,W.jsx)(en,{scope:t,onClose:U.useCallback(()=>d(!1),[d]),isUsingKeyboardRef:u,dir:f,modal:o,children:r})})})},`Menu`),rn=U.forwardRef(J(function(e,t){let{__scopeMenu:n,...r}=e,i=Xt(n);return(0,W.jsx)(ct,{...i,...r,ref:t})},`MenuAnchor`)),an=`MenuPortal`,[on,sn]=Y(an,{forceMount:void 0}),cn=J(e=>{let{__scopeMenu:t,forceMount:n,children:r,container:i}=e,a=$t(an,t);return(0,W.jsx)(on,{scope:t,forceMount:n,children:(0,W.jsx)(N,{present:n||a.open,children:(0,W.jsx)(M,{asChild:!0,container:i,children:r})})})},`MenuPortal`),X=`MenuContent`,[ln,un]=Y(X),dn=U.forwardRef(J(function(e,t){let n=sn(X,e.__scopeMenu),{forceMount:r=n.forceMount,...i}=e,a=$t(X,e.__scopeMenu),o=tn(X,e.__scopeMenu);return(0,W.jsx)(Kt.Provider,{scope:e.__scopeMenu,children:(0,W.jsx)(N,{present:r||a.open,children:(0,W.jsx)(Kt.Slot,{scope:e.__scopeMenu,children:o.modal?(0,W.jsx)(fn,{...i,ref:t}):(0,W.jsx)(pn,{...i,ref:t})})})})},`MenuContent`)),fn=U.forwardRef(J(function(e,t){let n=$t(X,e.__scopeMenu),r=U.useRef(null),i=H(t,r);return U.useEffect(()=>{let e=r.current;if(e)return k(e)},[]),(0,W.jsx)(hn,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,disableOutsideScroll:!0,onFocusOutside:F(e.onFocusOutside,e=>e.preventDefault(),{checkForDefaultPrevented:!1}),onDismiss:()=>n.onOpenChange(!1)})},`MenuRootContentModal`)),pn=U.forwardRef(J(function(e,t){let n=$t(X,e.__scopeMenu);return(0,W.jsx)(hn,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,disableOutsideScroll:!1,onDismiss:()=>n.onOpenChange(!1)})},`MenuRootContentNonModal`)),mn=z(`MenuContent.ScrollLock`),hn=U.forwardRef(J(function(e,t){let{__scopeMenu:n,loop:r=!1,trapFocus:i,onOpenAutoFocus:a,onCloseAutoFocus:o,disableOutsidePointerEvents:s,onEntryFocus:c,onEscapeKeyDown:l,onPointerDownOutside:u,onFocusOutside:d,onInteractOutside:f,onDismiss:p,disableOutsideScroll:m,...h}=e,g=$t(X,n),_=tn(X,n),v=Xt(n),y=Zt(n),b=qt(n),[x,S]=U.useState(null),C=U.useRef(null),ee=H(t,C,g.onContentChange),w=U.useRef(0),T=U.useRef(``),te=U.useRef(0),E=U.useRef(null),ne=U.useRef(`right`),D=U.useRef(0),k=m?le:U.Fragment,j=m?{as:mn,allowPinchZoom:!0}:void 0,M=J(e=>{let t=T.current+e,n=b().filter(e=>!e.disabled),r=document.activeElement,i=n.find(e=>e.ref.current===r)?.textValue,a=Mn(n.map(e=>e.textValue),t,i),o=n.find(e=>e.textValue===a)?.ref.current;J((function e(t){T.current=t,window.clearTimeout(w.current),t!==``&&(w.current=window.setTimeout(()=>e(``),1e3))}),`updateSearch`)(t),o&&setTimeout(()=>o.focus())},`handleTypeaheadSearch`);U.useEffect(()=>()=>window.clearTimeout(w.current),[]),O();let N=U.useCallback(e=>ne.current===E.current?.side&&Pn(e,E.current?.area),[]);return(0,W.jsx)(ln,{scope:n,searchRef:T,onItemEnter:U.useCallback(e=>{N(e)&&e.preventDefault()},[N]),onItemLeave:U.useCallback(e=>{N(e)||(C.current?.focus(),S(null))},[N]),onTriggerLeave:U.useCallback(e=>{N(e)&&e.preventDefault()},[N]),pointerGraceTimerRef:te,onPointerGraceIntentChange:U.useCallback(e=>{E.current=e},[]),children:(0,W.jsx)(k,{...j,children:(0,W.jsx)(re,{asChild:!0,trapped:i,onMountAutoFocus:F(a,e=>{e.preventDefault(),C.current?.focus({preventScroll:!0})}),onUnmountAutoFocus:o,children:(0,W.jsx)(A,{asChild:!0,disableOutsidePointerEvents:s,onEscapeKeyDown:l,onPointerDownOutside:u,onFocusOutside:d,onInteractOutside:f,onDismiss:p,children:(0,W.jsx)(Rt,{asChild:!0,...y,dir:_.dir,orientation:`vertical`,loop:r,currentTabStopId:x,onCurrentTabStopIdChange:S,onEntryFocus:F(c,e=>{_.isUsingKeyboardRef.current||e.preventDefault()}),preventScrollOnEntryFocus:!0,children:(0,W.jsx)(lt,{role:`menu`,"aria-orientation":`vertical`,"data-state":Dn(g.open),"data-radix-menu-content":``,dir:_.dir,...v,...h,ref:ee,style:{outline:`none`,...h.style},onKeyDown:F(h.onKeyDown,e=>{let t=e.target.closest(`[data-radix-menu-content]`)===e.currentTarget,n=e.ctrlKey||e.altKey||e.metaKey,r=e.key.length===1;t&&(e.key===`Tab`&&e.preventDefault(),!n&&r&&M(e.key));let i=C.current;if(e.target!==i||!Wt.includes(e.key))return;e.preventDefault();let a=b().filter(e=>!e.disabled).map(e=>e.ref.current);Ut.includes(e.key)&&a.reverse(),An(a)}),onBlur:F(e.onBlur,e=>{e.currentTarget.contains(e.target)||(window.clearTimeout(w.current),T.current=``)}),onPointerMove:F(e.onPointerMove,Fn(e=>{let t=e.target,n=D.current!==e.clientX;if(e.currentTarget.contains(t)&&n){let t=e.clientX>D.current?`right`:`left`;ne.current=t,D.current=e.clientX}}))})})})})})})},`MenuContentImpl`)),gn=`MenuItem`,_n=`menu.itemSelect`,vn=U.forwardRef(J(function(e,t){let{disabled:n=!1,onSelect:r,...i}=e,a=U.useRef(null),o=tn(gn,e.__scopeMenu),s=un(gn,e.__scopeMenu),c=H(t,a),l=U.useRef(!1),u=J(()=>{let e=a.current;if(!n&&e){let t=new CustomEvent(_n,{bubbles:!0,cancelable:!0});e.addEventListener(_n,e=>r?.(e),{once:!0}),pe(e,t),t.defaultPrevented?l.current=!1:o.onClose()}},`handleSelect`);return(0,W.jsx)(yn,{...i,ref:c,disabled:n,onClick:F(e.onClick,u),onPointerDown:t=>{e.onPointerDown?.(t),l.current=!0},onPointerUp:F(e.onPointerUp,e=>{l.current||e.currentTarget?.click()}),onKeyDown:F(e.onKeyDown,e=>{n||e.target!==e.currentTarget||(s.searchRef.current===``||e.key!==` `)&&Vt.includes(e.key)&&(e.currentTarget.click(),e.preventDefault())})})},`MenuItem`)),yn=U.forwardRef(J(function(e,t){let{__scopeMenu:n,disabled:r=!1,textValue:i,...a}=e,o=un(gn,n),s=Zt(n),c=U.useRef(null),l=H(t,c),[u,d]=U.useState(!1),[f,p]=U.useState(``);return U.useEffect(()=>{let e=c.current;e&&p((e.textContent??``).trim())},[a.children]),(0,W.jsx)(Kt.ItemSlot,{scope:n,disabled:r,textValue:i??f,children:(0,W.jsx)(zt,{asChild:!0,...s,focusable:!r,children:(0,W.jsx)(I.div,{role:`menuitem`,"data-highlighted":u?``:void 0,"aria-disabled":r||void 0,"data-disabled":r?``:void 0,...a,ref:l,onPointerMove:F(e.onPointerMove,Fn(e=>{r?o.onItemLeave(e):(o.onItemEnter(e),e.defaultPrevented||e.currentTarget.focus({preventScroll:!0}))})),onPointerLeave:F(e.onPointerLeave,Fn(e=>o.onItemLeave(e))),onFocus:F(e.onFocus,()=>d(!0)),onBlur:F(e.onBlur,()=>d(!1))})})})},`MenuItemImpl`)),[bn,xn]=Y(`MenuRadioGroup`,{value:void 0,onValueChange:J(()=>{},`onValueChange`)}),[Sn,Cn]=Y(`MenuItemIndicator`,{checked:!1}),wn=U.forwardRef(J(function(e,t){let{__scopeMenu:n,...r}=e;return(0,W.jsx)(I.div,{role:`separator`,"aria-orientation":`horizontal`,...r,ref:t})},`MenuSeparator`)),[Tn,En]=Y(`MenuSub`);function Dn(e){return e?`open`:`closed`}J(Dn,`getOpenState`);function On(e){return e===`indeterminate`}J(On,`isIndeterminate`);function kn(e){return On(e)?`indeterminate`:e?`checked`:`unchecked`}J(kn,`getCheckedState`);function An(e){let t=document.activeElement;for(let n of e)if(n===t||(n.focus(),document.activeElement!==t))return}J(An,`focusFirst`);function jn(e,t){return e.map((n,r)=>e[(t+r)%e.length])}J(jn,`wrapArray`);function Mn(e,t,n){let r=t.length>1&&Array.from(t).every(e=>e===t[0])?t[0]:t,i=n?e.indexOf(n):-1,a=jn(e,Math.max(i,0));r.length===1&&(a=a.filter(e=>e!==n));let o=a.find(e=>e.toLowerCase().startsWith(r.toLowerCase()));return o===n?void 0:o}J(Mn,`getNextMatch`);function Nn(e,t){let{x:n,y:r}=e,i=!1;for(let e=0,a=t.length-1;er!=d>r&&n<(u-c)*(r-l)/(d-l)+c&&(i=!i)}return i}J(Nn,`isPointInPolygon`);function Pn(e,t){return t?Nn({x:e.clientX,y:e.clientY},t):!1}J(Pn,`isPointerInGraceArea`);function Fn(e){return t=>t.pointerType===`mouse`?e(t):void 0}J(Fn,`whenMouse`);var In=nn,Ln=rn,Rn=cn,zn=dn,Bn=vn,Vn=wn,Hn=Object.defineProperty,Z=(e,t)=>Hn(e,`name`,{value:t,configurable:!0}),Un=`DropdownMenu`,[Wn,Gn]=B(Un,[Yt]),Q=Yt(),[Kn,qn]=Wn(Un),Jn=Z(e=>{let{__scopeDropdownMenu:t,children:n,dir:r,open:i,defaultOpen:a,onOpenChange:o,modal:s=!0}=e,c=Q(t),l=U.useRef(null),[u,d]=de({prop:i,defaultProp:a??!1,onChange:o,caller:Un});return(0,W.jsx)(Kn,{scope:t,triggerId:L(),triggerRef:l,contentId:L(),open:u,onOpenChange:d,onOpenToggle:U.useCallback(()=>d(e=>!e),[d]),modal:s,children:(0,W.jsx)(In,{...c,open:u,onOpenChange:d,dir:r,modal:s,children:n})})},`DropdownMenu`),Yn=`DropdownMenuTrigger`,Xn=U.forwardRef(Z(function(e,t){let{__scopeDropdownMenu:n,disabled:r=!1,...i}=e,a=qn(Yn,n),o=Q(n),s=H(t,a.triggerRef);return(0,W.jsx)(Ln,{asChild:!0,...o,children:(0,W.jsx)(I.button,{type:`button`,id:a.triggerId,"aria-haspopup":`menu`,"aria-expanded":a.open,"aria-controls":a.open?a.contentId:void 0,"data-state":a.open?`open`:`closed`,"data-disabled":r?``:void 0,disabled:r,...i,ref:s,onPointerDown:F(e.onPointerDown,e=>{!r&&e.button===0&&e.ctrlKey===!1&&(a.onOpenToggle(),a.open||e.preventDefault())}),onKeyDown:F(e.onKeyDown,e=>{r||([`Enter`,` `].includes(e.key)&&a.onOpenToggle(),e.key===`ArrowDown`&&a.onOpenChange(!0),[`Enter`,` `,`ArrowDown`].includes(e.key)&&e.preventDefault())})})})},`DropdownMenuTrigger`)),Zn=Z(e=>{let{__scopeDropdownMenu:t,...n}=e,r=Q(t);return(0,W.jsx)(Rn,{...r,...n})},`DropdownMenuPortal`),Qn=`DropdownMenuContent`,$n=U.forwardRef(Z(function(e,t){let{__scopeDropdownMenu:n,...r}=e,i=qn(Qn,n),a=Q(n),o=U.useRef(!1);return(0,W.jsx)(zn,{id:i.contentId,"aria-labelledby":i.triggerId,...a,...r,ref:t,onCloseAutoFocus:F(e.onCloseAutoFocus,e=>{o.current||i.triggerRef.current?.focus(),o.current=!1,e.preventDefault()}),onInteractOutside:F(e.onInteractOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0,r=t.button===2||n;(!i.modal||r)&&(o.current=!0)}),style:{...e.style,"--radix-dropdown-menu-content-transform-origin":`var(--radix-popper-transform-origin)`,"--radix-dropdown-menu-content-available-width":`var(--radix-popper-available-width)`,"--radix-dropdown-menu-content-available-height":`var(--radix-popper-available-height)`,"--radix-dropdown-menu-trigger-width":`var(--radix-popper-anchor-width)`,"--radix-dropdown-menu-trigger-height":`var(--radix-popper-anchor-height)`}})},`DropdownMenuContent`)),er=U.forwardRef(Z(function(e,t){let{__scopeDropdownMenu:n,...r}=e,i=Q(n);return(0,W.jsx)(Bn,{...i,...r,ref:t})},`DropdownMenuItem`)),tr=U.forwardRef(Z(function(e,t){let{__scopeDropdownMenu:n,...r}=e,i=Q(n);return(0,W.jsx)(Vn,{...i,...r,ref:t})},`DropdownMenuSeparator`)),nr=Jn,rr=Xn,ir=Zn,ar=$n,or=er,sr=tr;function cr({label:e=`Open actions`,children:t}){return(0,W.jsxs)(nr,{children:[(0,W.jsx)(rr,{asChild:!0,children:(0,W.jsx)(V,{size:`icon`,variant:`ghost`,"aria-label":e,children:(0,W.jsx)(Ee,{className:`h-4 w-4`})})}),(0,W.jsx)(ir,{children:(0,W.jsx)(ar,{align:`end`,sideOffset:6,className:`z-50 min-w-44 rounded-lg border border-border bg-card p-1 text-card-foreground shadow-xl`,children:t})})]})}function lr({className:e,danger:t,...n}){return(0,W.jsx)(or,{className:ae(`flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-xs font-medium outline-none transition focus:bg-accent`,t&&`text-rose-600 focus:bg-rose-500/10 dark:text-rose-400`,e),...n})}var ur=()=>(0,W.jsx)(sr,{className:`my-1 h-px bg-border`}),dr=Se({columnVisibilityFeature:we}),$=Ce();function fr(){let e=o(),n=v(),{toast:r}=fe(),[c,l]=(0,U.useState)(``),[d,f]=(0,U.useState)(`all`),[p,m]=(0,U.useState)(`all`),[h,g]=(0,U.useState)(`all`),[_,S]=(0,U.useState)(null),E=t({queryKey:R.tasks,queryFn:he.tasks}),D=()=>e.invalidateQueries({queryKey:R.tasks}),O=s({mutationFn:({task:e,nextEnabled:t})=>he.updateTask(e.id,{...Oe(e),enabled:t}),onSuccess:e=>{D(),r({tone:`success`,title:e.enabled?`Task enabled`:`Task disabled`,description:`${e.name} was updated immediately.`})},onError:e=>r({tone:`error`,title:`Task update failed`,description:j(e)})}),k=s({mutationFn:he.runTask,onSuccess:()=>{e.invalidateQueries({queryKey:[`results`]}),r({tone:`success`,title:`Test queued`,description:`Route validation will run before the measurement starts.`})},onError:e=>r({tone:`error`,title:`Unable to queue test`,description:j(e)})}),A=s({mutationFn:he.duplicateTask,onSuccess:e=>{D(),r({tone:`success`,title:`Task duplicated`,description:`${e.name} is ready to review.`}),n(`/tasks/${e.id}/edit`)},onError:e=>r({tone:`error`,title:`Duplicate failed`,description:j(e)})}),M=s({mutationFn:he.deleteTask,onSuccess:()=>{S(null),D(),r({tone:`success`,title:`Task deleted`,description:`Its historical results were preserved.`})},onError:e=>r({tone:`error`,title:`Delete failed`,description:j(e)})}),N=(0,U.useMemo)(()=>E.data??[],[E.data]),re=(0,U.useMemo)(()=>[...new Set(N.map(e=>e.interfaceName))].sort(),[N]),P=(0,U.useMemo)(()=>N.filter(e=>{let t=c.trim().toLowerCase();return(!t||`${e.name} ${e.description} ${e.interfaceName} ${e.sourceIp}`.toLowerCase().includes(t))&&(d===`all`||e.provider===d)&&(p===`all`||String(e.enabled)===p)&&(h===`all`||e.interfaceName===h)}),[N,c,d,p,h]),ae=(0,U.useMemo)(()=>$.columns([$.accessor(`name`,{header:`Task`,cell:({row:e})=>(0,W.jsxs)(`div`,{className:`min-w-52`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(u,{to:`/tasks/${e.original.id}/edit`,className:`font-semibold text-foreground hover:text-primary hover:underline`,children:e.original.name}),e.original.networkPathValid===!1?(0,W.jsxs)(ve,{tone:`danger`,className:`gap-1`,children:[(0,W.jsx)(ne,{className:`h-3 w-3`}),`Invalid path`]}):null]}),(0,W.jsx)(`p`,{className:`mt-0.5 max-w-xs truncate text-[11px] text-muted-foreground`,children:e.original.description||`No description`})]})}),$.accessor(`provider`,{header:`Provider`,cell:({getValue:e})=>(0,W.jsx)(be,{provider:e()})}),$.accessor(`interfaceName`,{header:`Network path`,cell:({row:e})=>(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`p`,{className:`font-medium text-foreground`,children:e.original.interfaceName}),(0,W.jsx)(`p`,{className:`mt-0.5 font-mono text-[11px] text-muted-foreground`,children:e.original.sourceIp}),e.original.networkPathValid===!1?(0,W.jsx)(`p`,{className:`mt-1 max-w-56 text-[10px] leading-4 text-rose-600 dark:text-rose-400`,children:e.original.networkPathMessage||`The configured interface or source address is unavailable.`}):null]})}),$.accessor(`cronExpression`,{header:`Schedule`,cell:({row:e})=>(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`p`,{className:`font-mono text-xs font-semibold text-foreground`,children:e.original.cronExpression}),(0,W.jsx)(`p`,{className:`mt-0.5 text-[11px] text-muted-foreground`,children:e.original.timezone})]})}),$.accessor(`nextScheduledAt`,{header:`Next run`,cell:({row:e})=>(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`p`,{className:`text-xs font-medium text-foreground`,children:e.original.enabled?oe(e.original.nextScheduledAt):`Paused`}),(0,W.jsx)(`p`,{className:`mt-0.5 text-[11px] text-muted-foreground`,children:e.original.lastScheduledAt?`Last ${oe(e.original.lastScheduledAt)}`:`Never run`})]})}),$.accessor(`enabled`,{header:`Enabled`,cell:({row:e})=>(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(y,{checked:e.original.enabled,onCheckedChange:t=>{if(t&&!e.original.enabled){r({tone:`info`,title:`Preflight required before enabling`,description:`Review this task and validate its current provider, target, and source path.`}),n(`/tasks/${e.original.id}/edit`);return}O.mutate({task:e.original,nextEnabled:t})},"aria-label":`${e.original.enabled?`Disable`:`Enable`} ${e.original.name}`}),(0,W.jsxs)(ve,{tone:e.original.enabled?`success`:`neutral`,className:`gap-1`,children:[(0,W.jsx)(_e,{active:e.original.enabled}),e.original.enabled?`On`:`Off`]})]})}),$.display({id:`actions`,header:``,cell:({row:e})=>(0,W.jsxs)(cr,{label:`Actions for ${e.original.name}`,children:[(0,W.jsxs)(lr,{onSelect:()=>void n(`/tasks/${e.original.id}/edit`),children:[(0,W.jsx)(ee,{className:`h-3.5 w-3.5`}),`Edit task`]}),(0,W.jsxs)(lr,{disabled:k.isPending||e.original.networkPathValid===!1,onSelect:()=>k.mutate(e.original.id),children:[(0,W.jsx)(De,{className:`h-3.5 w-3.5`}),`Run now`]}),(0,W.jsxs)(lr,{disabled:A.isPending,onSelect:()=>A.mutate(e.original.id),children:[(0,W.jsx)(Te,{className:`h-3.5 w-3.5`}),`Duplicate`]}),(0,W.jsx)(ur,{}),(0,W.jsxs)(lr,{danger:!0,onSelect:()=>S(e.original),children:[(0,W.jsx)(te,{className:`h-3.5 w-3.5`}),`Delete`]})]})})]),[A,n,k,r,O]),F=a({features:dr,data:P,columns:ae});if(E.isLoading)return(0,W.jsx)(ie,{label:`Loading independent schedules…`});if(E.error)return(0,W.jsx)(se,{error:E.error,onRetry:()=>void E.refetch()});let I=c||d!==`all`||p!==`all`||h!==`all`,L=()=>{l(``),f(`all`),m(`all`),g(`all`)};return(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(ge,{title:`Independent tests, precisely routed.`,description:`Each task owns its provider, target, schedule, source address, and route-validation policy.`,actions:(0,W.jsx)(V,{asChild:!0,children:(0,W.jsxs)(u,{to:`/tasks/new`,children:[(0,W.jsx)(w,{className:`h-4 w-4`}),`Create task`]})})}),(0,W.jsxs)(ye,{className:`overflow-hidden`,children:[(0,W.jsxs)(`div`,{className:`grid gap-3 border-b border-border p-4 md:grid-cols-[minmax(220px,1fr)_repeat(3,minmax(140px,.36fr))_auto]`,children:[(0,W.jsxs)(`div`,{className:`relative`,children:[(0,W.jsx)(T,{className:`pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground`}),(0,W.jsx)(x,{className:`pl-9`,value:c,onChange:e=>l(e.target.value),placeholder:`Search name, WAN, or source IP…`,"aria-label":`Search tasks`})]}),(0,W.jsxs)(b,{value:d,onChange:e=>f(e.target.value),"aria-label":`Filter by provider`,children:[(0,W.jsx)(`option`,{value:`all`,children:`All providers`}),[`ookla`,`librespeed`,`cloudflare`].map(e=>(0,W.jsx)(`option`,{value:e,children:me(e)},e))]}),(0,W.jsxs)(b,{value:p,onChange:e=>m(e.target.value),"aria-label":`Filter by enabled state`,children:[(0,W.jsx)(`option`,{value:`all`,children:`Any state`}),(0,W.jsx)(`option`,{value:`true`,children:`Enabled`}),(0,W.jsx)(`option`,{value:`false`,children:`Disabled`})]}),(0,W.jsxs)(b,{value:h,onChange:e=>g(e.target.value),"aria-label":`Filter by network interface`,children:[(0,W.jsx)(`option`,{value:`all`,children:`All interfaces`}),re.map(e=>(0,W.jsx)(`option`,{value:e,children:e},e))]}),(0,W.jsx)(V,{variant:`ghost`,size:`icon`,onClick:L,disabled:!I,"aria-label":`Clear filters`,children:(0,W.jsx)(C,{className:`h-4 w-4`})})]}),P.length===0?(0,W.jsx)(ue,{title:N.length?`No tasks match these filters`:`Create your first measurement path`,description:N.length?`Clear or adjust filters to see more tasks.`:`Configure a provider, concrete source address, and independent schedule.`,action:N.length?(0,W.jsxs)(V,{size:`sm`,variant:`outline`,onClick:L,children:[(0,W.jsx)(C,{className:`h-3.5 w-3.5`}),`Clear filters`]}):(0,W.jsx)(V,{asChild:!0,size:`sm`,children:(0,W.jsxs)(u,{to:`/tasks/new`,children:[(0,W.jsx)(w,{className:`h-3.5 w-3.5`}),`Create task`]})})}):(0,W.jsx)(`div`,{className:`overflow-x-auto scrollbar-thin`,children:(0,W.jsxs)(`table`,{className:`data-table w-full min-w-[1000px]`,children:[(0,W.jsx)(`thead`,{children:F.getHeaderGroups().map(e=>(0,W.jsx)(`tr`,{children:e.headers.map(e=>(0,W.jsx)(`th`,{children:e.isPlaceholder?null:i(e.column.columnDef.header,e.getContext())},e.id))},e.id))}),(0,W.jsx)(`tbody`,{children:F.getRowModel().rows.map(e=>(0,W.jsx)(`tr`,{children:e.getVisibleCells().map(e=>(0,W.jsx)(`td`,{children:i(e.column.columnDef.cell,e.getContext())},e.id))},e.id))})]})}),(0,W.jsxs)(`div`,{className:`flex items-center justify-between border-t border-border px-4 py-3 text-xs text-muted-foreground`,children:[(0,W.jsxs)(`span`,{children:[P.length,` of `,N.length,` task`,N.length===1?``:`s`]}),O.isPending||k.isPending||A.isPending?(0,W.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,W.jsx)(ce,{}),`Applying change…`]}):(0,W.jsx)(`span`,{children:`Schedules update immediately`})]})]}),(0,W.jsx)(xe,{open:_!==null,onOpenChange:e=>!e&&S(null),title:`Delete this task?`,description:(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`strong`,{className:`font-semibold text-foreground`,children:_?.name}),` and its future schedule will be removed. Existing results remain available for analysis.`]}),confirmLabel:`Delete task`,destructive:!0,busy:M.isPending,onConfirm:()=>_&&M.mutate(_.id)})]})}export{fr as default}; \ No newline at end of file +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,T as n,V as r,b as i,t as a,w as o,x as s}from"./query-DWNqGEie.js";import{a as c,c as l,d as u,i as d,l as f,n as p,o as m,r as h,s as g,t as _,y as v}from"./react-DsKPNe9r.js";import{a as y,i as b,r as x,s as S}from"./fields-DSX3DSQR.js";import{t as C}from"./funnel-x-DRKN7t7O.js";import{t as ee}from"./pen-line-UL8dX1Od.js";import{t as w}from"./plus-BbAjJ_nn.js";import{t as T}from"./search-b0EiqX0q.js";import{t as te}from"./trash-2-DyGHVVq4.js";import{$ as E,D as ne,I as D,J as O,K as k,Q as A,T as j,X as M,Y as N,Z as re,a as ie,at as P,c as ae,ct as F,et as I,g as oe,i as se,it as L,n as R,nt as z,o as ce,ot as B,q as le,r as ue,rt as de,s as V,st as H,t as fe,tt as pe,v as me,w as he}from"./index-DleN6wGz.js";import{t as ge}from"./PageHeader-DGX-y_Bi.js";import{n as _e,t as ve}from"./badge-BWAHg20h.js";import{t as ye}from"./card-BtXBd7n8.js";import{t as be}from"./EntityBadges-CM7WeMOS.js";import{t as xe}from"./dialog-CT4xHTSq.js";import{n as Se,r as Ce,t as we}from"./columnVisibilityFeature-CB2cjdCS.js";var Te=D(`copy`,[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]),Ee=D(`ellipsis`,[[`circle`,{cx:`12`,cy:`12`,r:`1`,key:`41hilf`}],[`circle`,{cx:`19`,cy:`12`,r:`1`,key:`1wjl8i`}],[`circle`,{cx:`5`,cy:`12`,r:`1`,key:`1pcz8c`}]]),De=D(`play`,[[`path`,{d:`M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z`,key:`10ikf1`}]]),U=e(r(),1);function Oe(e){return{name:e.name,description:e.description,enabled:e.enabled,provider:e.provider,cronExpression:e.cronExpression,timezone:e.timezone,randomJitterSeconds:e.randomJitterSeconds,serverSelectionMode:e.serverSelectionMode,serverId:e.serverId,serverUrl:e.serverUrl,customServerDefinition:e.customServerDefinition??{},interfaceName:e.interfaceName,sourceIp:e.sourceIp,ipFamily:e.ipFamily,routeProfileId:e.routeProfileId,timeoutSeconds:e.timeoutSeconds,providerOptions:e.providerOptions??{},preventOverlap:e.preventOverlap,routeValidation:e.routeValidation}}var W=n(),ke=Object.defineProperty,G=(e,t)=>ke(e,`name`,{value:t,configurable:!0});function Ae(e){let t=e+`CollectionProvider`,[n,r]=B(t),[i,a]=n(t,{collectionRef:{current:null},itemMap:new Map}),o=G(e=>{let{scope:t,children:n}=e,r=U.useRef(null),a=U.useRef(new Map).current;return(0,W.jsx)(i,{scope:t,itemMap:a,collectionRef:r,children:n})},`CollectionProvider`);o.displayName=t;let s=e+`CollectionSlot`,c=z(s),l=U.forwardRef((e,t)=>{let{scope:n,children:r}=e,i=a(s,n),o=H(t,i.collectionRef);return(0,W.jsx)(c,{ref:o,children:r})});l.displayName=s;let u=e+`CollectionItemSlot`,d=`data-radix-collection-item`,f=z(u),p=U.forwardRef((e,t)=>{let{scope:n,children:r,...i}=e,o=U.useRef(null),s=H(t,o),c=a(u,n);return U.useEffect(()=>(c.itemMap.set(o,{ref:o,...i}),()=>void c.itemMap.delete(o))),(0,W.jsx)(f,{[d]:``,ref:s,children:r})});p.displayName=u;function m(t){let n=a(e+`CollectionConsumer`,t);return U.useCallback(()=>{let e=n.collectionRef.current;if(!e)return[];let t=Array.from(e.querySelectorAll(`[${d}]`));return Array.from(n.itemMap.values()).sort((e,n)=>t.indexOf(e.ref.current)-t.indexOf(n.ref.current))},[n.collectionRef,n.itemMap])}return G(m,`useCollection`),[{Provider:o,Slot:l,ItemSlot:p},m,r]}G(Ae,`createCollection`);var je=new WeakMap,Me=class e extends Map{static{G(this,`OrderedDict`)}#e;constructor(e){super(e),this.#e=[...super.keys()],je.set(this,!0)}set(e,t){return je.get(this)&&(this.has(e)?this.#e[this.#e.indexOf(e)]=e:this.#e.push(e)),super.set(e,t),this}insert(e,t,n){let r=this.has(t),i=this.#e.length,a=Fe(e),o=a>=0?a:i+a,s=o<0||o>=i?-1:o;if(s===this.size||r&&s===this.size-1||s===-1)return this.set(t,n),this;let c=this.size+ +!r;a<0&&o++;let l=[...this.#e],u,d=!1;for(let e=o;e=this.size&&(r=this.size-1),this.at(r)}keyFrom(e,t){let n=this.indexOf(e);if(n===-1)return;let r=n+t;return r<0&&(r=0),r>=this.size&&(r=this.size-1),this.keyAt(r)}find(e,t){let n=0;for(let r of this){if(Reflect.apply(e,t,[r,n,this]))return r;n++}}findIndex(e,t){let n=0;for(let r of this){if(Reflect.apply(e,t,[r,n,this]))return n;n++}return-1}filter(t,n){let r=[],i=0;for(let e of this)Reflect.apply(t,n,[e,i,this])&&r.push(e),i++;return new e(r)}map(t,n){let r=[],i=0;for(let e of this)r.push([e[0],Reflect.apply(t,n,[e,i,this])]),i++;return new e(r)}reduce(...e){let[t,n]=e,r=0,i=n??this.at(0);for(let n of this)i=r===0&&e.length===1?n:Reflect.apply(t,this,[i,n,r,this]),r++;return i}reduceRight(...e){let[t,n]=e,r=n??this.at(-1);for(let n=this.size-1;n>=0;n--){let i=this.at(n);r=n===this.size-1&&e.length===1?i:Reflect.apply(t,this,[r,i,n,this])}return r}toSorted(t){let n=[...this.entries()].sort(t);return new e(n)}toReversed(){let t=new e;for(let e=this.size-1;e>=0;e--){let n=this.keyAt(e),r=this.get(n);t.set(n,r)}return t}toSpliced(...t){let n=[...this.entries()];return n.splice(...t),new e(n)}slice(t,n){let r=new e,i=this.size-1;if(t===void 0)return r;t<0&&(t+=this.size),n!==void 0&&n>0&&(i=n-1);for(let e=t;e<=i;e++){let t=this.keyAt(e),n=this.get(t);r.set(t,n)}return r}every(e,t){let n=0;for(let r of this){if(!Reflect.apply(e,t,[r,n,this]))return!1;n++}return!0}some(e,t){let n=0;for(let r of this){if(Reflect.apply(e,t,[r,n,this]))return!0;n++}return!1}};function Ne(e,t){if(`at`in Array.prototype)return Array.prototype.at.call(e,t);let n=Pe(e,t);return n===-1?void 0:e[n]}G(Ne,`at`);function Pe(e,t){let n=e.length,r=Fe(t),i=r>=0?r:n+r;return i<0||i>=n?-1:i}G(Pe,`toSafeIndex`);function Fe(e){return e!==e||e===0?0:Math.trunc(e)}G(Fe,`toSafeInteger`);function Ie(e){let t=e+`CollectionProvider`,[n,r]=B(t),[i,a]=n(t,{collectionElement:null,collectionRef:{current:null},collectionRefObject:{current:null},itemMap:new Me,setItemMap:G(()=>void 0,`setItemMap`)}),o=G(({state:e,...t})=>e?(0,W.jsx)(c,{...t,state:e}):(0,W.jsx)(s,{...t}),`CollectionProvider`);o.displayName=t;let s=G(e=>{let t=h();return(0,W.jsx)(c,{...e,state:t})},`CollectionInit`);s.displayName=t+`Init`;let c=G(e=>{let{scope:t,children:n,state:r}=e,a=U.useRef(null),[o,s]=U.useState(null),c=H(a,s),[l,u]=r;return U.useEffect(()=>{if(!o)return;let e=Be(()=>{});return e.observe(o,{childList:!0,subtree:!0}),()=>{e.disconnect()}},[o]),(0,W.jsx)(i,{scope:t,itemMap:l,setItemMap:u,collectionRef:c,collectionRefObject:a,collectionElement:o,children:n})},`CollectionProviderImpl`);c.displayName=t+`Impl`;let l=e+`CollectionSlot`,u=z(l),d=U.forwardRef((e,t)=>{let{scope:n,children:r}=e,i=a(l,n),o=H(t,i.collectionRef);return(0,W.jsx)(u,{ref:o,children:r})});d.displayName=l;let f=e+`CollectionItemSlot`,p=z(f),m=U.forwardRef((e,t)=>{let{scope:n,children:r,...i}=e,o=U.useRef(null),[s,c]=U.useState(null),l=H(t,o,c),{setItemMap:u}=a(f,n),d=U.useRef(i);Le(d.current,i)||(d.current=i);let m=d.current;return U.useEffect(()=>{let e=m;return u(t=>s?t.has(s)?t.set(s,{...e,element:s}).toSorted(ze):(t.set(s,{...e,element:s}),t.toSorted(ze)):t),()=>{u(e=>!s||!e.has(s)?e:(e.delete(s),new Me(e)))}},[s,m,u]),(0,W.jsx)(p,{"data-radix-collection-item":``,ref:l,children:r})});m.displayName=f;function h(){return U.useState(new Me)}G(h,`useInitCollection`);function g(t){let{itemMap:n}=a(e+`CollectionConsumer`,t);return n}return G(g,`useCollection`),[{Provider:o,Slot:d,ItemSlot:m},{createCollectionScope:r,useCollection:g,useInitCollection:h}]}G(Ie,`createCollection`);function Le(e,t){if(e===t)return!0;if(typeof e!=`object`||typeof t!=`object`||e==null||t==null)return!1;let n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(let r of n)if(!Object.prototype.hasOwnProperty.call(t,r)||e[r]!==t[r])return!1;return!0}G(Le,`shallowEqual`);function Re(e,t){return!!(t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_PRECEDING)}G(Re,`isElementPreceding`);function ze(e,t){return!e[1].element||!t[1].element?0:Re(e[1].element,t[1].element)?-1:1}G(ze,`sortByDocumentPosition`);function Be(e){return new MutationObserver(t=>{for(let n of t)if(n.type===`childList`){e();return}})}G(Be,`getChildListObserver`);var Ve=Object.defineProperty,He=(e,t)=>Ve(e,`name`,{value:t,configurable:!0}),Ue=U.createContext(void 0);function We(e){let t=U.useContext(Ue);return e||t||`ltr`}He(We,`useDirection`);var Ge=Object.defineProperty,K=(e,t)=>Ge(e,`name`,{value:t,configurable:!0}),Ke=`Popper`,[qe,Je]=B(Ke),[Ye,Xe]=qe(Ke),Ze=K(e=>{let{__scopePopper:t,children:n}=e,[r,i]=U.useState(null),[a,o]=U.useState(void 0);return(0,W.jsx)(Ye,{scope:t,anchor:r,onAnchorChange:i,placementState:a,setPlacementState:o,children:n})},`Popper`),Qe=`PopperAnchor`,$e=U.forwardRef(K(function(e,t){let{__scopePopper:n,virtualRef:r,...i}=e,a=Xe(Qe,n),o=U.useRef(null),s=a.onAnchorChange,c=U.useCallback(e=>{o.current=e,e&&s(e)},[s]),l=H(t,c),u=U.useRef(null);U.useEffect(()=>{if(!r)return;let e=u.current;u.current=r.current,e!==u.current&&s(u.current)});let d=a.placementState&&ot(a.placementState),f=d?.[0],p=d?.[1];return r?null:(0,W.jsx)(I.div,{"data-radix-popper-side":f,"data-radix-popper-align":p,...i,ref:l})},`PopperAnchor`)),et=`PopperContent`,[tt,nt]=qe(et),rt=U.forwardRef(K(function(e,t){let{__scopePopper:n,side:r=`bottom`,sideOffset:i=0,align:a=`center`,alignOffset:o=0,arrowPadding:s=0,avoidCollisions:u=!0,collisionBoundary:v=[],collisionPadding:y=0,sticky:b=`partial`,hideWhenDetached:x=!1,updatePositionStrategy:C=`optimized`,onPlaced:ee,...w}=e,T=Xe(et,n),[te,ne]=U.useState(null),D=H(t,ne),[O,k]=U.useState(null),A=S(O),j=A?.width??0,M=A?.height??0,N=r+(a===`center`?``:`-`+a),re=typeof y==`number`?y:{top:0,right:0,bottom:0,left:0,...y},ie=Array.isArray(v)?v:[v],ae=ie.length>0,F={padding:re,boundary:ie.filter(it),altBoundary:ae},{refs:oe,floatingStyles:se,placement:L,isPositioned:R,middlewareData:z}=l({strategy:`fixed`,placement:N,whileElementsMounted:K((...e)=>f(...e,{animationFrame:C===`always`}),`whileElementsMounted`),elements:{reference:T.anchor},middleware:[c({mainAxis:i+M,alignmentAxis:o}),u&&m({mainAxis:!0,crossAxis:!1,limiter:b===`partial`?d():void 0,...F}),u&&p({...F}),g({...F,apply:K(({elements:e,rects:t,availableWidth:n,availableHeight:r})=>{let{width:i,height:a}=t.reference,o=e.floating.style;o.setProperty(`--radix-popper-available-width`,`${n}px`),o.setProperty(`--radix-popper-available-height`,`${r}px`),o.setProperty(`--radix-popper-anchor-width`,`${i}px`),o.setProperty(`--radix-popper-anchor-height`,`${a}px`)},`apply`)}),O&&_({element:O,padding:s}),at({arrowWidth:j,arrowHeight:M}),x&&h({strategy:`referenceHidden`,...F,boundary:ae?F.boundary:void 0})]}),ce=T.setPlacementState;P(()=>(ce(L),()=>{ce(void 0)}),[L,ce]);let[B,le]=ot(L),ue=E(ee);P(()=>{R&&ue?.()},[R,ue]);let de=z.arrow?.x,V=z.arrow?.y,fe=z.arrow?.centerOffset!==0,[pe,me]=U.useState();return P(()=>{te&&me(window.getComputedStyle(te).zIndex)},[te]),(0,W.jsx)(`div`,{ref:oe.setFloating,"data-radix-popper-content-wrapper":``,style:{...se,transform:R?se.transform:`translate(0, -200%)`,minWidth:`max-content`,zIndex:pe,"--radix-popper-transform-origin":[z.transformOrigin?.x,z.transformOrigin?.y].join(` `),...z.hide?.referenceHidden&&{visibility:`hidden`,pointerEvents:`none`}},dir:e.dir,children:(0,W.jsx)(tt,{scope:n,placedSide:B,placedAlign:le,onArrowChange:k,arrowX:de,arrowY:V,shouldHideArrow:fe,children:(0,W.jsx)(I.div,{"data-side":B,"data-align":le,...w,ref:D,style:{...w.style,animation:R?w.style?.animation:`none`}})})})},`PopperContent`));function it(e){return e!==null}K(it,`isNotNull`);var at=K(e=>({name:`transformOrigin`,options:e,fn(t){let{placement:n,rects:r,middlewareData:i}=t,a=i.arrow?.centerOffset!==0,o=a?0:e.arrowWidth,s=a?0:e.arrowHeight,[c,l]=ot(n),u={start:`0%`,center:`50%`,end:`100%`}[l],d=(i.arrow?.x??0)+o/2,f=(i.arrow?.y??0)+s/2,p=``,m=``;return c===`bottom`?(p=a?u:`${d}px`,m=`${-s}px`):c===`top`?(p=a?u:`${d}px`,m=`${r.floating.height+s}px`):c===`right`?(p=`${-s}px`,m=a?u:`${f}px`):c===`left`&&(p=`${r.floating.width+s}px`,m=a?u:`${f}px`),{data:{x:p,y:m}}}}),`transformOrigin`);function ot(e){let[t,n=`center`]=e.split(`-`);return[t,n]}K(ot,`getSideAndAlignFromPlacement`);var st=Ze,ct=$e,lt=rt,ut=Object.defineProperty,dt=(e,t)=>ut(e,`name`,{value:t,configurable:!0}),ft=!1;function pt(){let[e,t]=U.useState(ft);return U.useEffect(()=>{ft||(ft=!0,t(!0))},[]),e}dt(pt,`useIsHydrated`);var mt=U.useSyncExternalStore;function ht(){return()=>{}}dt(ht,`subscribe`);function gt(){return mt(ht,()=>!0,()=>!1)}dt(gt,`useIsHydratedModern`);var _t=typeof mt==`function`?gt:pt,vt=Object.defineProperty,q=(e,t)=>vt(e,`name`,{value:t,configurable:!0}),yt=`rovingFocusGroup.onEntryFocus`,bt={bubbles:!1,cancelable:!0},xt=`RovingFocusGroup`,[St,Ct,wt]=Ae(xt),[Tt,Et]=B(xt,[wt]),[Dt,Ot]=Tt(xt),kt=U.forwardRef(q(function(e,t){return(0,W.jsx)(St.Provider,{scope:e.__scopeRovingFocusGroup,children:(0,W.jsx)(St.Slot,{scope:e.__scopeRovingFocusGroup,children:(0,W.jsx)(At,{...e,ref:t})})})},`RovingFocusGroup`)),At=U.forwardRef(q(function(e,t){let{__scopeRovingFocusGroup:n,orientation:r,loop:i=!1,dir:a,currentTabStopId:o,defaultCurrentTabStopId:s,onCurrentTabStopIdChange:c,onEntryFocus:l,preventScrollOnEntryFocus:u=!1,...d}=e,f=U.useRef(null),p=H(t,f),m=We(a),[h,g]=de({prop:o,defaultProp:s??null,onChange:c,caller:xt}),[_,v]=U.useState(!1),y=E(l),b=Ct(n),x=U.useRef(!1),[S,C]=U.useState(0);return U.useEffect(()=>{let e=f.current;if(e)return e.addEventListener(yt,y),()=>e.removeEventListener(yt,y)},[y]),(0,W.jsx)(Dt,{scope:n,orientation:r,dir:m,loop:i,currentTabStopId:h,onItemFocus:U.useCallback(e=>g(e),[g]),onItemShiftTab:U.useCallback(()=>v(!0),[]),onFocusableItemAdd:U.useCallback(()=>C(e=>e+1),[]),onFocusableItemRemove:U.useCallback(()=>C(e=>e-1),[]),children:(0,W.jsx)(I.div,{tabIndex:_||S===0?-1:0,"data-orientation":r,...d,ref:p,style:{outline:`none`,...e.style},onMouseDown:F(e.onMouseDown,()=>{x.current=!0}),onFocus:F(e.onFocus,e=>{let t=!x.current;if(e.target===e.currentTarget&&t&&!_){let t=new CustomEvent(yt,bt);if(e.currentTarget.dispatchEvent(t),!t.defaultPrevented){let e=b().filter(e=>e.focusable);It([e.find(e=>e.active),e.find(e=>e.id===h),...e].filter(Boolean).map(e=>e.ref.current),u)}}x.current=!1}),onBlur:F(e.onBlur,()=>v(!1))})})},`RovingFocusGroupImpl`)),jt=`RovingFocusGroupItem`,Mt=U.forwardRef(q(function(e,t){let{__scopeRovingFocusGroup:n,focusable:r=!0,active:i=!1,tabStopId:a,children:o,...s}=e,c=L(),l=a||c,u=Ot(jt,n),d=u.currentTabStopId===l,f=Ct(n),{onFocusableItemAdd:p,onFocusableItemRemove:m,currentTabStopId:h}=u,g=_t();return P(()=>{if(!(!g||!r))return p(),()=>m()},[g,r,p,m]),U.useEffect(()=>{if(!(g||!r))return p(),()=>m()},[g,r,p,m]),(0,W.jsx)(St.ItemSlot,{scope:n,id:l,focusable:r,active:i,children:(0,W.jsx)(I.span,{tabIndex:d?0:-1,"data-orientation":u.orientation,...s,ref:t,onMouseDown:F(e.onMouseDown,e=>{r?u.onItemFocus(l):e.preventDefault()}),onFocus:F(e.onFocus,()=>u.onItemFocus(l)),onKeyDown:F(e.onKeyDown,e=>{if(e.key===`Tab`&&e.shiftKey){u.onItemShiftTab();return}if(e.target!==e.currentTarget)return;let t=Ft(e,u.orientation,u.dir);if(t!==void 0){if(e.metaKey||e.ctrlKey||e.altKey||e.shiftKey)return;e.preventDefault();let n=f().filter(e=>e.focusable).map(e=>e.ref.current);if(t===`last`)n.reverse();else if(t===`prev`||t===`next`){t===`prev`&&n.reverse();let r=n.indexOf(e.currentTarget);n=u.loop?Lt(n,r+1):n.slice(r+1)}setTimeout(()=>It(n))}}),children:typeof o==`function`?o({isCurrentTabStop:d,hasTabStop:h!=null}):o})})},`RovingFocusGroupItem`)),Nt={ArrowLeft:`prev`,ArrowUp:`prev`,ArrowRight:`next`,ArrowDown:`next`,PageUp:`first`,Home:`first`,PageDown:`last`,End:`last`};function Pt(e,t){return t===`rtl`?e===`ArrowLeft`?`ArrowRight`:e===`ArrowRight`?`ArrowLeft`:e:e}q(Pt,`getDirectionAwareKey`);function Ft(e,t,n){let r=Pt(e.key,n);if(!(t===`vertical`&&[`ArrowLeft`,`ArrowRight`].includes(r))&&!(t===`horizontal`&&[`ArrowUp`,`ArrowDown`].includes(r)))return Nt[r]}q(Ft,`getFocusIntent`);function It(e,t=!1){let n=document.activeElement;for(let r of e)if(r===n||(r.focus({preventScroll:t}),document.activeElement!==n))return}q(It,`focusFirst`);function Lt(e,t){return e.map((n,r)=>e[(t+r)%e.length])}q(Lt,`wrapArray`);var Rt=kt,zt=Mt,Bt=Object.defineProperty,J=(e,t)=>Bt(e,`name`,{value:t,configurable:!0}),Vt=[`Enter`,` `],Ht=[`ArrowDown`,`PageUp`,`Home`],Ut=[`ArrowUp`,`PageDown`,`End`],Wt=[...Ht,...Ut];[...Vt],[...Vt];var Gt=`Menu`,[Kt,qt,Jt]=Ae(Gt),[Y,Yt]=B(Gt,[Jt,Je,Et]),Xt=Je(),Zt=Et(),[Qt,$t]=Y(Gt),[en,tn]=Y(Gt),nn=J(e=>{let{__scopeMenu:t,open:n=!1,children:r,dir:i,onOpenChange:a,modal:o=!0}=e,s=Xt(t),[c,l]=U.useState(null),u=U.useRef(!1),d=E(a),f=We(i);return U.useEffect(()=>{let e=J(()=>{u.current=!0,document.addEventListener(`pointerdown`,t,{capture:!0,once:!0}),document.addEventListener(`pointermove`,t,{capture:!0,once:!0})},`handleKeyDown`),t=J(()=>u.current=!1,`handlePointer`);return document.addEventListener(`keydown`,e,{capture:!0}),()=>{document.removeEventListener(`keydown`,e,{capture:!0}),document.removeEventListener(`pointerdown`,t,{capture:!0}),document.removeEventListener(`pointermove`,t,{capture:!0})}},[]),U.useEffect(()=>{if(!n)return;let e=J(()=>d(!1),`handleBlur`);return window.addEventListener(`blur`,e),()=>window.removeEventListener(`blur`,e)},[n,d]),(0,W.jsx)(st,{...s,children:(0,W.jsx)(Qt,{scope:t,open:n,onOpenChange:d,content:c,onContentChange:l,children:(0,W.jsx)(en,{scope:t,onClose:U.useCallback(()=>d(!1),[d]),isUsingKeyboardRef:u,dir:f,modal:o,children:r})})})},`Menu`),rn=U.forwardRef(J(function(e,t){let{__scopeMenu:n,...r}=e,i=Xt(n);return(0,W.jsx)(ct,{...i,...r,ref:t})},`MenuAnchor`)),an=`MenuPortal`,[on,sn]=Y(an,{forceMount:void 0}),cn=J(e=>{let{__scopeMenu:t,forceMount:n,children:r,container:i}=e,a=$t(an,t);return(0,W.jsx)(on,{scope:t,forceMount:n,children:(0,W.jsx)(N,{present:n||a.open,children:(0,W.jsx)(M,{asChild:!0,container:i,children:r})})})},`MenuPortal`),X=`MenuContent`,[ln,un]=Y(X),dn=U.forwardRef(J(function(e,t){let n=sn(X,e.__scopeMenu),{forceMount:r=n.forceMount,...i}=e,a=$t(X,e.__scopeMenu),o=tn(X,e.__scopeMenu);return(0,W.jsx)(Kt.Provider,{scope:e.__scopeMenu,children:(0,W.jsx)(N,{present:r||a.open,children:(0,W.jsx)(Kt.Slot,{scope:e.__scopeMenu,children:o.modal?(0,W.jsx)(fn,{...i,ref:t}):(0,W.jsx)(pn,{...i,ref:t})})})})},`MenuContent`)),fn=U.forwardRef(J(function(e,t){let n=$t(X,e.__scopeMenu),r=U.useRef(null),i=H(t,r);return U.useEffect(()=>{let e=r.current;if(e)return k(e)},[]),(0,W.jsx)(hn,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,disableOutsideScroll:!0,onFocusOutside:F(e.onFocusOutside,e=>e.preventDefault(),{checkForDefaultPrevented:!1}),onDismiss:()=>n.onOpenChange(!1)})},`MenuRootContentModal`)),pn=U.forwardRef(J(function(e,t){let n=$t(X,e.__scopeMenu);return(0,W.jsx)(hn,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,disableOutsideScroll:!1,onDismiss:()=>n.onOpenChange(!1)})},`MenuRootContentNonModal`)),mn=z(`MenuContent.ScrollLock`),hn=U.forwardRef(J(function(e,t){let{__scopeMenu:n,loop:r=!1,trapFocus:i,onOpenAutoFocus:a,onCloseAutoFocus:o,disableOutsidePointerEvents:s,onEntryFocus:c,onEscapeKeyDown:l,onPointerDownOutside:u,onFocusOutside:d,onInteractOutside:f,onDismiss:p,disableOutsideScroll:m,...h}=e,g=$t(X,n),_=tn(X,n),v=Xt(n),y=Zt(n),b=qt(n),[x,S]=U.useState(null),C=U.useRef(null),ee=H(t,C,g.onContentChange),w=U.useRef(0),T=U.useRef(``),te=U.useRef(0),E=U.useRef(null),ne=U.useRef(`right`),D=U.useRef(0),k=m?le:U.Fragment,j=m?{as:mn,allowPinchZoom:!0}:void 0,M=J(e=>{let t=T.current+e,n=b().filter(e=>!e.disabled),r=document.activeElement,i=n.find(e=>e.ref.current===r)?.textValue,a=Mn(n.map(e=>e.textValue),t,i),o=n.find(e=>e.textValue===a)?.ref.current;J((function e(t){T.current=t,window.clearTimeout(w.current),t!==``&&(w.current=window.setTimeout(()=>e(``),1e3))}),`updateSearch`)(t),o&&setTimeout(()=>o.focus())},`handleTypeaheadSearch`);U.useEffect(()=>()=>window.clearTimeout(w.current),[]),O();let N=U.useCallback(e=>ne.current===E.current?.side&&Pn(e,E.current?.area),[]);return(0,W.jsx)(ln,{scope:n,searchRef:T,onItemEnter:U.useCallback(e=>{N(e)&&e.preventDefault()},[N]),onItemLeave:U.useCallback(e=>{N(e)||(C.current?.focus(),S(null))},[N]),onTriggerLeave:U.useCallback(e=>{N(e)&&e.preventDefault()},[N]),pointerGraceTimerRef:te,onPointerGraceIntentChange:U.useCallback(e=>{E.current=e},[]),children:(0,W.jsx)(k,{...j,children:(0,W.jsx)(re,{asChild:!0,trapped:i,onMountAutoFocus:F(a,e=>{e.preventDefault(),C.current?.focus({preventScroll:!0})}),onUnmountAutoFocus:o,children:(0,W.jsx)(A,{asChild:!0,disableOutsidePointerEvents:s,onEscapeKeyDown:l,onPointerDownOutside:u,onFocusOutside:d,onInteractOutside:f,onDismiss:p,children:(0,W.jsx)(Rt,{asChild:!0,...y,dir:_.dir,orientation:`vertical`,loop:r,currentTabStopId:x,onCurrentTabStopIdChange:S,onEntryFocus:F(c,e=>{_.isUsingKeyboardRef.current||e.preventDefault()}),preventScrollOnEntryFocus:!0,children:(0,W.jsx)(lt,{role:`menu`,"aria-orientation":`vertical`,"data-state":Dn(g.open),"data-radix-menu-content":``,dir:_.dir,...v,...h,ref:ee,style:{outline:`none`,...h.style},onKeyDown:F(h.onKeyDown,e=>{let t=e.target.closest(`[data-radix-menu-content]`)===e.currentTarget,n=e.ctrlKey||e.altKey||e.metaKey,r=e.key.length===1;t&&(e.key===`Tab`&&e.preventDefault(),!n&&r&&M(e.key));let i=C.current;if(e.target!==i||!Wt.includes(e.key))return;e.preventDefault();let a=b().filter(e=>!e.disabled).map(e=>e.ref.current);Ut.includes(e.key)&&a.reverse(),An(a)}),onBlur:F(e.onBlur,e=>{e.currentTarget.contains(e.target)||(window.clearTimeout(w.current),T.current=``)}),onPointerMove:F(e.onPointerMove,Fn(e=>{let t=e.target,n=D.current!==e.clientX;if(e.currentTarget.contains(t)&&n){let t=e.clientX>D.current?`right`:`left`;ne.current=t,D.current=e.clientX}}))})})})})})})},`MenuContentImpl`)),gn=`MenuItem`,_n=`menu.itemSelect`,vn=U.forwardRef(J(function(e,t){let{disabled:n=!1,onSelect:r,...i}=e,a=U.useRef(null),o=tn(gn,e.__scopeMenu),s=un(gn,e.__scopeMenu),c=H(t,a),l=U.useRef(!1),u=J(()=>{let e=a.current;if(!n&&e){let t=new CustomEvent(_n,{bubbles:!0,cancelable:!0});e.addEventListener(_n,e=>r?.(e),{once:!0}),pe(e,t),t.defaultPrevented?l.current=!1:o.onClose()}},`handleSelect`);return(0,W.jsx)(yn,{...i,ref:c,disabled:n,onClick:F(e.onClick,u),onPointerDown:t=>{e.onPointerDown?.(t),l.current=!0},onPointerUp:F(e.onPointerUp,e=>{l.current||e.currentTarget?.click()}),onKeyDown:F(e.onKeyDown,e=>{n||e.target!==e.currentTarget||(s.searchRef.current===``||e.key!==` `)&&Vt.includes(e.key)&&(e.currentTarget.click(),e.preventDefault())})})},`MenuItem`)),yn=U.forwardRef(J(function(e,t){let{__scopeMenu:n,disabled:r=!1,textValue:i,...a}=e,o=un(gn,n),s=Zt(n),c=U.useRef(null),l=H(t,c),[u,d]=U.useState(!1),[f,p]=U.useState(``);return U.useEffect(()=>{let e=c.current;e&&p((e.textContent??``).trim())},[a.children]),(0,W.jsx)(Kt.ItemSlot,{scope:n,disabled:r,textValue:i??f,children:(0,W.jsx)(zt,{asChild:!0,...s,focusable:!r,children:(0,W.jsx)(I.div,{role:`menuitem`,"data-highlighted":u?``:void 0,"aria-disabled":r||void 0,"data-disabled":r?``:void 0,...a,ref:l,onPointerMove:F(e.onPointerMove,Fn(e=>{r?o.onItemLeave(e):(o.onItemEnter(e),e.defaultPrevented||e.currentTarget.focus({preventScroll:!0}))})),onPointerLeave:F(e.onPointerLeave,Fn(e=>o.onItemLeave(e))),onFocus:F(e.onFocus,()=>d(!0)),onBlur:F(e.onBlur,()=>d(!1))})})})},`MenuItemImpl`)),[bn,xn]=Y(`MenuRadioGroup`,{value:void 0,onValueChange:J(()=>{},`onValueChange`)}),[Sn,Cn]=Y(`MenuItemIndicator`,{checked:!1}),wn=U.forwardRef(J(function(e,t){let{__scopeMenu:n,...r}=e;return(0,W.jsx)(I.div,{role:`separator`,"aria-orientation":`horizontal`,...r,ref:t})},`MenuSeparator`)),[Tn,En]=Y(`MenuSub`);function Dn(e){return e?`open`:`closed`}J(Dn,`getOpenState`);function On(e){return e===`indeterminate`}J(On,`isIndeterminate`);function kn(e){return On(e)?`indeterminate`:e?`checked`:`unchecked`}J(kn,`getCheckedState`);function An(e){let t=document.activeElement;for(let n of e)if(n===t||(n.focus(),document.activeElement!==t))return}J(An,`focusFirst`);function jn(e,t){return e.map((n,r)=>e[(t+r)%e.length])}J(jn,`wrapArray`);function Mn(e,t,n){let r=t.length>1&&Array.from(t).every(e=>e===t[0])?t[0]:t,i=n?e.indexOf(n):-1,a=jn(e,Math.max(i,0));r.length===1&&(a=a.filter(e=>e!==n));let o=a.find(e=>e.toLowerCase().startsWith(r.toLowerCase()));return o===n?void 0:o}J(Mn,`getNextMatch`);function Nn(e,t){let{x:n,y:r}=e,i=!1;for(let e=0,a=t.length-1;er!=d>r&&n<(u-c)*(r-l)/(d-l)+c&&(i=!i)}return i}J(Nn,`isPointInPolygon`);function Pn(e,t){return t?Nn({x:e.clientX,y:e.clientY},t):!1}J(Pn,`isPointerInGraceArea`);function Fn(e){return t=>t.pointerType===`mouse`?e(t):void 0}J(Fn,`whenMouse`);var In=nn,Ln=rn,Rn=cn,zn=dn,Bn=vn,Vn=wn,Hn=Object.defineProperty,Z=(e,t)=>Hn(e,`name`,{value:t,configurable:!0}),Un=`DropdownMenu`,[Wn,Gn]=B(Un,[Yt]),Q=Yt(),[Kn,qn]=Wn(Un),Jn=Z(e=>{let{__scopeDropdownMenu:t,children:n,dir:r,open:i,defaultOpen:a,onOpenChange:o,modal:s=!0}=e,c=Q(t),l=U.useRef(null),[u,d]=de({prop:i,defaultProp:a??!1,onChange:o,caller:Un});return(0,W.jsx)(Kn,{scope:t,triggerId:L(),triggerRef:l,contentId:L(),open:u,onOpenChange:d,onOpenToggle:U.useCallback(()=>d(e=>!e),[d]),modal:s,children:(0,W.jsx)(In,{...c,open:u,onOpenChange:d,dir:r,modal:s,children:n})})},`DropdownMenu`),Yn=`DropdownMenuTrigger`,Xn=U.forwardRef(Z(function(e,t){let{__scopeDropdownMenu:n,disabled:r=!1,...i}=e,a=qn(Yn,n),o=Q(n),s=H(t,a.triggerRef);return(0,W.jsx)(Ln,{asChild:!0,...o,children:(0,W.jsx)(I.button,{type:`button`,id:a.triggerId,"aria-haspopup":`menu`,"aria-expanded":a.open,"aria-controls":a.open?a.contentId:void 0,"data-state":a.open?`open`:`closed`,"data-disabled":r?``:void 0,disabled:r,...i,ref:s,onPointerDown:F(e.onPointerDown,e=>{!r&&e.button===0&&e.ctrlKey===!1&&(a.onOpenToggle(),a.open||e.preventDefault())}),onKeyDown:F(e.onKeyDown,e=>{r||([`Enter`,` `].includes(e.key)&&a.onOpenToggle(),e.key===`ArrowDown`&&a.onOpenChange(!0),[`Enter`,` `,`ArrowDown`].includes(e.key)&&e.preventDefault())})})})},`DropdownMenuTrigger`)),Zn=Z(e=>{let{__scopeDropdownMenu:t,...n}=e,r=Q(t);return(0,W.jsx)(Rn,{...r,...n})},`DropdownMenuPortal`),Qn=`DropdownMenuContent`,$n=U.forwardRef(Z(function(e,t){let{__scopeDropdownMenu:n,...r}=e,i=qn(Qn,n),a=Q(n),o=U.useRef(!1);return(0,W.jsx)(zn,{id:i.contentId,"aria-labelledby":i.triggerId,...a,...r,ref:t,onCloseAutoFocus:F(e.onCloseAutoFocus,e=>{o.current||i.triggerRef.current?.focus(),o.current=!1,e.preventDefault()}),onInteractOutside:F(e.onInteractOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0,r=t.button===2||n;(!i.modal||r)&&(o.current=!0)}),style:{...e.style,"--radix-dropdown-menu-content-transform-origin":`var(--radix-popper-transform-origin)`,"--radix-dropdown-menu-content-available-width":`var(--radix-popper-available-width)`,"--radix-dropdown-menu-content-available-height":`var(--radix-popper-available-height)`,"--radix-dropdown-menu-trigger-width":`var(--radix-popper-anchor-width)`,"--radix-dropdown-menu-trigger-height":`var(--radix-popper-anchor-height)`}})},`DropdownMenuContent`)),er=U.forwardRef(Z(function(e,t){let{__scopeDropdownMenu:n,...r}=e,i=Q(n);return(0,W.jsx)(Bn,{...i,...r,ref:t})},`DropdownMenuItem`)),tr=U.forwardRef(Z(function(e,t){let{__scopeDropdownMenu:n,...r}=e,i=Q(n);return(0,W.jsx)(Vn,{...i,...r,ref:t})},`DropdownMenuSeparator`)),nr=Jn,rr=Xn,ir=Zn,ar=$n,or=er,sr=tr;function cr({label:e=`Open actions`,children:t}){return(0,W.jsxs)(nr,{children:[(0,W.jsx)(rr,{asChild:!0,children:(0,W.jsx)(V,{size:`icon`,variant:`ghost`,"aria-label":e,children:(0,W.jsx)(Ee,{className:`h-4 w-4`})})}),(0,W.jsx)(ir,{children:(0,W.jsx)(ar,{align:`end`,sideOffset:6,className:`z-50 min-w-44 rounded-lg border border-border bg-card p-1 text-card-foreground shadow-xl`,children:t})})]})}function lr({className:e,danger:t,...n}){return(0,W.jsx)(or,{className:ae(`flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-xs font-medium outline-none transition focus:bg-accent`,t&&`text-rose-600 focus:bg-rose-500/10 dark:text-rose-400`,e),...n})}var ur=()=>(0,W.jsx)(sr,{className:`my-1 h-px bg-border`}),dr=Se({columnVisibilityFeature:we}),$=Ce();function fr(){let e=o(),n=v(),{toast:r}=fe(),[c,l]=(0,U.useState)(``),[d,f]=(0,U.useState)(`all`),[p,m]=(0,U.useState)(`all`),[h,g]=(0,U.useState)(`all`),[_,S]=(0,U.useState)(null),E=t({queryKey:R.tasks,queryFn:he.tasks}),D=()=>e.invalidateQueries({queryKey:R.tasks}),O=s({mutationFn:({task:e,nextEnabled:t})=>he.updateTask(e.id,{...Oe(e),enabled:t}),onSuccess:e=>{D(),r({tone:`success`,title:e.enabled?`Task enabled`:`Task disabled`,description:`${e.name} was updated immediately.`})},onError:e=>r({tone:`error`,title:`Task update failed`,description:j(e)})}),k=s({mutationFn:he.runTask,onSuccess:()=>{e.invalidateQueries({queryKey:[`results`]}),r({tone:`success`,title:`Test queued`,description:`Route validation will run before the measurement starts.`})},onError:e=>r({tone:`error`,title:`Unable to queue test`,description:j(e)})}),A=s({mutationFn:he.duplicateTask,onSuccess:e=>{D(),r({tone:`success`,title:`Task duplicated`,description:`${e.name} is ready to review.`}),n(`/tasks/${e.id}/edit`)},onError:e=>r({tone:`error`,title:`Duplicate failed`,description:j(e)})}),M=s({mutationFn:he.deleteTask,onSuccess:()=>{S(null),D(),r({tone:`success`,title:`Task deleted`,description:`Its historical results were preserved.`})},onError:e=>r({tone:`error`,title:`Delete failed`,description:j(e)})}),N=(0,U.useMemo)(()=>E.data??[],[E.data]),re=(0,U.useMemo)(()=>[...new Set(N.map(e=>e.interfaceName))].sort(),[N]),P=(0,U.useMemo)(()=>N.filter(e=>{let t=c.trim().toLowerCase();return(!t||`${e.name} ${e.description} ${e.interfaceName} ${e.sourceIp}`.toLowerCase().includes(t))&&(d===`all`||e.provider===d)&&(p===`all`||String(e.enabled)===p)&&(h===`all`||e.interfaceName===h)}),[N,c,d,p,h]),ae=(0,U.useMemo)(()=>$.columns([$.accessor(`name`,{header:`Task`,cell:({row:e})=>(0,W.jsxs)(`div`,{className:`min-w-52`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(u,{to:`/tasks/${e.original.id}/edit`,className:`font-semibold text-foreground hover:text-primary hover:underline`,children:e.original.name}),e.original.networkPathValid===!1?(0,W.jsxs)(ve,{tone:`danger`,className:`gap-1`,children:[(0,W.jsx)(ne,{className:`h-3 w-3`}),`Invalid path`]}):null]}),(0,W.jsx)(`p`,{className:`mt-0.5 max-w-xs truncate text-[11px] text-muted-foreground`,children:e.original.description||`No description`})]})}),$.accessor(`provider`,{header:`Provider`,cell:({getValue:e})=>(0,W.jsx)(be,{provider:e()})}),$.accessor(`interfaceName`,{header:`Network path`,cell:({row:e})=>(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`p`,{className:`font-medium text-foreground`,children:e.original.interfaceName}),(0,W.jsx)(`p`,{className:`mt-0.5 font-mono text-[11px] text-muted-foreground`,children:e.original.sourceIp}),e.original.networkPathValid===!1?(0,W.jsx)(`p`,{className:`mt-1 max-w-56 text-[10px] leading-4 text-rose-600 dark:text-rose-400`,children:e.original.networkPathMessage||`The configured interface or source address is unavailable.`}):null]})}),$.accessor(`cronExpression`,{header:`Schedule`,cell:({row:e})=>(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`p`,{className:`font-mono text-xs font-semibold text-foreground`,children:e.original.cronExpression}),(0,W.jsx)(`p`,{className:`mt-0.5 text-[11px] text-muted-foreground`,children:e.original.timezone})]})}),$.accessor(`nextScheduledAt`,{header:`Next run`,cell:({row:e})=>(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`p`,{className:`text-xs font-medium text-foreground`,children:e.original.enabled?oe(e.original.nextScheduledAt):`Paused`}),(0,W.jsx)(`p`,{className:`mt-0.5 text-[11px] text-muted-foreground`,children:e.original.lastScheduledAt?`Last ${oe(e.original.lastScheduledAt)}`:`Never run`})]})}),$.accessor(`enabled`,{header:`Enabled`,cell:({row:e})=>(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(y,{checked:e.original.enabled,onCheckedChange:t=>{if(t&&!e.original.enabled){r({tone:`info`,title:`Preflight required before enabling`,description:`Review this task and validate its current provider, target, and source path.`}),n(`/tasks/${e.original.id}/edit`);return}O.mutate({task:e.original,nextEnabled:t})},"aria-label":`${e.original.enabled?`Disable`:`Enable`} ${e.original.name}`}),(0,W.jsxs)(ve,{tone:e.original.enabled?`success`:`neutral`,className:`gap-1`,children:[(0,W.jsx)(_e,{active:e.original.enabled}),e.original.enabled?`On`:`Off`]})]})}),$.display({id:`actions`,header:``,cell:({row:e})=>(0,W.jsxs)(cr,{label:`Actions for ${e.original.name}`,children:[(0,W.jsxs)(lr,{onSelect:()=>void n(`/tasks/${e.original.id}/edit`),children:[(0,W.jsx)(ee,{className:`h-3.5 w-3.5`}),`Edit task`]}),(0,W.jsxs)(lr,{disabled:k.isPending||e.original.networkPathValid===!1,onSelect:()=>k.mutate(e.original.id),children:[(0,W.jsx)(De,{className:`h-3.5 w-3.5`}),`Run now`]}),(0,W.jsxs)(lr,{disabled:A.isPending,onSelect:()=>A.mutate(e.original.id),children:[(0,W.jsx)(Te,{className:`h-3.5 w-3.5`}),`Duplicate`]}),(0,W.jsx)(ur,{}),(0,W.jsxs)(lr,{danger:!0,onSelect:()=>S(e.original),children:[(0,W.jsx)(te,{className:`h-3.5 w-3.5`}),`Delete`]})]})})]),[A,n,k,r,O]),F=a({features:dr,data:P,columns:ae});if(E.isLoading)return(0,W.jsx)(ie,{label:`Loading independent schedules…`});if(E.error)return(0,W.jsx)(se,{error:E.error,onRetry:()=>void E.refetch()});let I=c||d!==`all`||p!==`all`||h!==`all`,L=()=>{l(``),f(`all`),m(`all`),g(`all`)};return(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(ge,{title:`Independent tests, precisely routed.`,description:`Each task owns its provider, target, schedule, source address, and route-validation policy.`,actions:(0,W.jsx)(V,{asChild:!0,children:(0,W.jsxs)(u,{to:`/tasks/new`,children:[(0,W.jsx)(w,{className:`h-4 w-4`}),`Create task`]})})}),(0,W.jsxs)(ye,{className:`overflow-hidden`,children:[(0,W.jsxs)(`div`,{className:`grid gap-3 border-b border-border p-4 md:grid-cols-[minmax(220px,1fr)_repeat(3,minmax(140px,.36fr))_auto]`,children:[(0,W.jsxs)(`div`,{className:`relative`,children:[(0,W.jsx)(T,{className:`pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground`}),(0,W.jsx)(x,{className:`pl-9`,value:c,onChange:e=>l(e.target.value),placeholder:`Search name, WAN, or source IP…`,"aria-label":`Search tasks`})]}),(0,W.jsxs)(b,{value:d,onChange:e=>f(e.target.value),"aria-label":`Filter by provider`,children:[(0,W.jsx)(`option`,{value:`all`,children:`All providers`}),[`ookla`,`librespeed`,`cloudflare`].map(e=>(0,W.jsx)(`option`,{value:e,children:me(e)},e))]}),(0,W.jsxs)(b,{value:p,onChange:e=>m(e.target.value),"aria-label":`Filter by enabled state`,children:[(0,W.jsx)(`option`,{value:`all`,children:`Any state`}),(0,W.jsx)(`option`,{value:`true`,children:`Enabled`}),(0,W.jsx)(`option`,{value:`false`,children:`Disabled`})]}),(0,W.jsxs)(b,{value:h,onChange:e=>g(e.target.value),"aria-label":`Filter by network interface`,children:[(0,W.jsx)(`option`,{value:`all`,children:`All interfaces`}),re.map(e=>(0,W.jsx)(`option`,{value:e,children:e},e))]}),(0,W.jsx)(V,{variant:`ghost`,size:`icon`,onClick:L,disabled:!I,"aria-label":`Clear filters`,children:(0,W.jsx)(C,{className:`h-4 w-4`})})]}),P.length===0?(0,W.jsx)(ue,{title:N.length?`No tasks match these filters`:`Create your first measurement path`,description:N.length?`Clear or adjust filters to see more tasks.`:`Configure a provider, concrete source address, and independent schedule.`,action:N.length?(0,W.jsxs)(V,{size:`sm`,variant:`outline`,onClick:L,children:[(0,W.jsx)(C,{className:`h-3.5 w-3.5`}),`Clear filters`]}):(0,W.jsx)(V,{asChild:!0,size:`sm`,children:(0,W.jsxs)(u,{to:`/tasks/new`,children:[(0,W.jsx)(w,{className:`h-3.5 w-3.5`}),`Create task`]})})}):(0,W.jsx)(`div`,{className:`overflow-x-auto scrollbar-thin`,children:(0,W.jsxs)(`table`,{className:`data-table w-full min-w-[1000px]`,children:[(0,W.jsx)(`thead`,{children:F.getHeaderGroups().map(e=>(0,W.jsx)(`tr`,{children:e.headers.map(e=>(0,W.jsx)(`th`,{children:e.isPlaceholder?null:i(e.column.columnDef.header,e.getContext())},e.id))},e.id))}),(0,W.jsx)(`tbody`,{children:F.getRowModel().rows.map(e=>(0,W.jsx)(`tr`,{children:e.getVisibleCells().map(e=>(0,W.jsx)(`td`,{children:i(e.column.columnDef.cell,e.getContext())},e.id))},e.id))})]})}),(0,W.jsxs)(`div`,{className:`flex items-center justify-between border-t border-border px-4 py-3 text-xs text-muted-foreground`,children:[(0,W.jsxs)(`span`,{children:[P.length,` of `,N.length,` task`,N.length===1?``:`s`]}),O.isPending||k.isPending||A.isPending?(0,W.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,W.jsx)(ce,{}),`Applying change…`]}):(0,W.jsx)(`span`,{children:`Schedules update immediately`})]})]}),(0,W.jsx)(xe,{open:_!==null,onOpenChange:e=>!e&&S(null),title:`Delete this task?`,description:(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`strong`,{className:`font-semibold text-foreground`,children:_?.name}),` and its future schedule will be removed. Existing results remain available for analysis.`]}),confirmLabel:`Delete task`,destructive:!0,busy:M.isPending,onConfirm:()=>_&&M.mutate(_.id)})]})}export{fr as default}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/arrow-left-CkoT9SwQ.js b/internal/frontend/dist/assets/arrow-left-DZUEJBho.js similarity index 58% rename from internal/frontend/dist/assets/arrow-left-CkoT9SwQ.js rename to internal/frontend/dist/assets/arrow-left-DZUEJBho.js index 894a405..21a2631 100644 --- a/internal/frontend/dist/assets/arrow-left-CkoT9SwQ.js +++ b/internal/frontend/dist/assets/arrow-left-DZUEJBho.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`arrow-left`,[[`path`,{d:`m12 19-7-7 7-7`,key:`1l729n`}],[`path`,{d:`M19 12H5`,key:`x3x0zl`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`arrow-left`,[[`path`,{d:`m12 19-7-7 7-7`,key:`1l729n`}],[`path`,{d:`M19 12H5`,key:`x3x0zl`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/arrow-up-Cm-GZvKO.js b/internal/frontend/dist/assets/arrow-up-DVV_1I7Y.js similarity index 75% rename from internal/frontend/dist/assets/arrow-up-Cm-GZvKO.js rename to internal/frontend/dist/assets/arrow-up-DVV_1I7Y.js index e2ef664..3d6fe58 100644 --- a/internal/frontend/dist/assets/arrow-up-Cm-GZvKO.js +++ b/internal/frontend/dist/assets/arrow-up-DVV_1I7Y.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`arrow-down`,[[`path`,{d:`M12 5v14`,key:`s699le`}],[`path`,{d:`m19 12-7 7-7-7`,key:`1idqje`}]]),n=e(`arrow-up`,[[`path`,{d:`m5 12 7-7 7 7`,key:`hav0vg`}],[`path`,{d:`M12 19V5`,key:`x0mq9r`}]]);export{t as n,n as t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`arrow-down`,[[`path`,{d:`M12 5v14`,key:`s699le`}],[`path`,{d:`m19 12-7 7-7-7`,key:`1idqje`}]]),n=e(`arrow-up`,[[`path`,{d:`m5 12 7-7 7 7`,key:`hav0vg`}],[`path`,{d:`M12 19V5`,key:`x0mq9r`}]]);export{t as n,n as t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/badge-D4YKBNKr.js b/internal/frontend/dist/assets/badge-BWAHg20h.js similarity index 93% rename from internal/frontend/dist/assets/badge-D4YKBNKr.js rename to internal/frontend/dist/assets/badge-BWAHg20h.js index 1f01938..ec1af42 100644 --- a/internal/frontend/dist/assets/badge-D4YKBNKr.js +++ b/internal/frontend/dist/assets/badge-BWAHg20h.js @@ -1 +1 @@ -import{T as e}from"./query-DWNqGEie.js";import{c as t}from"./index-B84OiuS0.js";var n=e(),r={neutral:`border-border bg-muted text-muted-foreground`,info:`border-cyan-500/25 bg-cyan-500/10 text-cyan-700 dark:text-cyan-300`,success:`border-emerald-500/25 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300`,warning:`border-amber-500/25 bg-amber-500/10 text-amber-700 dark:text-amber-300`,danger:`border-rose-500/25 bg-rose-500/10 text-rose-700 dark:text-rose-300`,violet:`border-violet-500/25 bg-violet-500/10 text-violet-700 dark:text-violet-300`};function i({className:e,tone:i=`neutral`,...a}){return(0,n.jsx)(`span`,{className:t(`inline-flex min-h-5 items-center rounded-full border px-2 py-0.5 text-[11px] font-semibold leading-4`,r[i],e),...a})}function a({active:e,className:r}){return(0,n.jsx)(`span`,{className:t(`inline-block h-1.5 w-1.5 rounded-full`,e?`bg-emerald-500`:`bg-muted-foreground/50`,r),"aria-hidden":`true`})}export{a as n,i as t}; \ No newline at end of file +import{T as e}from"./query-DWNqGEie.js";import{c as t}from"./index-DleN6wGz.js";var n=e(),r={neutral:`border-border bg-muted text-muted-foreground`,info:`border-cyan-500/25 bg-cyan-500/10 text-cyan-700 dark:text-cyan-300`,success:`border-emerald-500/25 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300`,warning:`border-amber-500/25 bg-amber-500/10 text-amber-700 dark:text-amber-300`,danger:`border-rose-500/25 bg-rose-500/10 text-rose-700 dark:text-rose-300`,violet:`border-violet-500/25 bg-violet-500/10 text-violet-700 dark:text-violet-300`};function i({className:e,tone:i=`neutral`,...a}){return(0,n.jsx)(`span`,{className:t(`inline-flex min-h-5 items-center rounded-full border px-2 py-0.5 text-[11px] font-semibold leading-4`,r[i],e),...a})}function a({active:e,className:r}){return(0,n.jsx)(`span`,{className:t(`inline-block h-1.5 w-1.5 rounded-full`,e?`bg-emerald-500`:`bg-muted-foreground/50`,r),"aria-hidden":`true`})}export{a as n,i as t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/card-DEXft0Cm.js b/internal/frontend/dist/assets/card-BtXBd7n8.js similarity index 90% rename from internal/frontend/dist/assets/card-DEXft0Cm.js rename to internal/frontend/dist/assets/card-BtXBd7n8.js index 28a9634..095e720 100644 --- a/internal/frontend/dist/assets/card-DEXft0Cm.js +++ b/internal/frontend/dist/assets/card-BtXBd7n8.js @@ -1 +1 @@ -import{T as e}from"./query-DWNqGEie.js";import{c as t}from"./index-B84OiuS0.js";var n=e();function r({className:e,...r}){return(0,n.jsx)(`div`,{className:t(`rounded-xl border border-border bg-card text-card-foreground shadow-panel`,e),...r})}function i({className:e,...r}){return(0,n.jsx)(`div`,{className:t(`flex flex-col gap-1 px-5 pb-3 pt-5 sm:px-6 sm:pt-6`,e),...r})}function a({className:e,...r}){return(0,n.jsx)(`h2`,{className:t(`text-sm font-semibold tracking-tight text-foreground`,e),...r})}function o({className:e,...r}){return(0,n.jsx)(`div`,{className:t(`px-5 pb-5 sm:px-6 sm:pb-6`,e),...r})}export{a as i,o as n,i as r,r as t}; \ No newline at end of file +import{T as e}from"./query-DWNqGEie.js";import{c as t}from"./index-DleN6wGz.js";var n=e();function r({className:e,...r}){return(0,n.jsx)(`div`,{className:t(`rounded-xl border border-border bg-card text-card-foreground shadow-panel`,e),...r})}function i({className:e,...r}){return(0,n.jsx)(`div`,{className:t(`flex flex-col gap-1 px-5 pb-3 pt-5 sm:px-6 sm:pt-6`,e),...r})}function a({className:e,...r}){return(0,n.jsx)(`h2`,{className:t(`text-sm font-semibold tracking-tight text-foreground`,e),...r})}function o({className:e,...r}){return(0,n.jsx)(`div`,{className:t(`px-5 pb-5 sm:px-6 sm:pb-6`,e),...r})}export{a as i,o as n,i as r,r as t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/circle-x-DYIKQ94H.js b/internal/frontend/dist/assets/circle-x-CJ7LJdu2.js similarity index 67% rename from internal/frontend/dist/assets/circle-x-DYIKQ94H.js rename to internal/frontend/dist/assets/circle-x-CJ7LJdu2.js index f152dd3..3fb9958 100644 --- a/internal/frontend/dist/assets/circle-x-DYIKQ94H.js +++ b/internal/frontend/dist/assets/circle-x-CJ7LJdu2.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`circle-x`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`circle-x`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/clock-3-BeKI5fxN.js b/internal/frontend/dist/assets/clock-3-DhdKy9KD.js similarity index 59% rename from internal/frontend/dist/assets/clock-3-BeKI5fxN.js rename to internal/frontend/dist/assets/clock-3-DhdKy9KD.js index bd88d56..30ebca2 100644 --- a/internal/frontend/dist/assets/clock-3-BeKI5fxN.js +++ b/internal/frontend/dist/assets/clock-3-DhdKy9KD.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`clock-3`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 6v6h4`,key:`135r8i`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`clock-3`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 6v6h4`,key:`135r8i`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/database-BRZrCBcR.js b/internal/frontend/dist/assets/database-DuA3AOvM.js similarity index 72% rename from internal/frontend/dist/assets/database-BRZrCBcR.js rename to internal/frontend/dist/assets/database-DuA3AOvM.js index 7b35568..2f1c720 100644 --- a/internal/frontend/dist/assets/database-BRZrCBcR.js +++ b/internal/frontend/dist/assets/database-DuA3AOvM.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`database`,[[`ellipse`,{cx:`12`,cy:`5`,rx:`9`,ry:`3`,key:`msslwz`}],[`path`,{d:`M3 5V19A9 3 0 0 0 21 19V5`,key:`1wlel7`}],[`path`,{d:`M3 12A9 3 0 0 0 21 12`,key:`mv7ke4`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`database`,[[`ellipse`,{cx:`12`,cy:`5`,rx:`9`,ry:`3`,key:`msslwz`}],[`path`,{d:`M3 5V19A9 3 0 0 0 21 19V5`,key:`1wlel7`}],[`path`,{d:`M3 12A9 3 0 0 0 21 12`,key:`mv7ke4`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/dialog-DXkCUNSe.js b/internal/frontend/dist/assets/dialog-CT4xHTSq.js similarity index 98% rename from internal/frontend/dist/assets/dialog-DXkCUNSe.js rename to internal/frontend/dist/assets/dialog-CT4xHTSq.js index 68a5644..59086a3 100644 --- a/internal/frontend/dist/assets/dialog-DXkCUNSe.js +++ b/internal/frontend/dist/assets/dialog-CT4xHTSq.js @@ -1 +1 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{T as t,V as n}from"./query-DWNqGEie.js";import{B as r,E as i,G as a,H as o,L as s,R as c,U as l,V as u,W as d,c as f,ct as p,ot as m,s as h,st as g,z as _}from"./index-B84OiuS0.js";var v=e(n(),1),y=t(),b=Object.defineProperty,x=(e,t)=>b(e,`name`,{value:t,configurable:!0}),[S,C]=m(`AlertDialog`,[a]),w=a(),T=x(e=>{let{__scopeAlertDialog:t,...n}=e,r=w(t);return(0,y.jsx)(s,{...r,...n,modal:!0})},`AlertDialog`);v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,...r}=e,i=w(n);return(0,y.jsx)(d,{...i,...r,ref:t})},`AlertDialogTrigger`));var E=x(e=>{let{__scopeAlertDialog:t,...n}=e,r=w(t);return(0,y.jsx)(o,{...r,...n})},`AlertDialogPortal`),D=v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,...r}=e,i=w(n);return(0,y.jsx)(u,{...i,...r,ref:t})},`AlertDialogOverlay`)),[O,k]=S(`AlertDialogContent`),A=v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,children:r,...i}=e,a=w(n),o=v.useRef(null),s=g(t,o),c=v.useRef(null);return(0,y.jsx)(O,{scope:n,cancelRef:c,children:(0,y.jsx)(_,{role:`alertdialog`,...a,...i,ref:s,onOpenAutoFocus:p(i.onOpenAutoFocus,e=>{e.preventDefault(),c.current?.focus({preventScroll:!0})}),onPointerDownOutside:e=>e.preventDefault(),onInteractOutside:e=>e.preventDefault(),children:r})})},`AlertDialogContent`)),j=v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,...r}=e,i=w(n);return(0,y.jsx)(l,{...i,...r,ref:t})},`AlertDialogTitle`)),M=v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,...i}=e,a=w(n);return(0,y.jsx)(r,{...a,...i,ref:t})},`AlertDialogDescription`)),N=v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,...r}=e,i=w(n);return(0,y.jsx)(c,{...i,...r,ref:t})},`AlertDialogAction`)),P=`AlertDialogCancel`,F=v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,...r}=e,{cancelRef:i}=k(P,n),a=w(n),o=g(t,i);return(0,y.jsx)(c,{...a,...r,ref:o})},`AlertDialogCancel`)),I=T,L=E,R=D,z=A,B=N,V=F,H=j,U=M,W=s;function G({children:e,className:t,title:n,description:a}){return(0,y.jsxs)(o,{children:[(0,y.jsx)(u,{className:`fixed inset-0 z-50 bg-slate-950/65 backdrop-blur-[2px] data-[state=closed]:animate-out`}),(0,y.jsxs)(_,{className:f(`fixed left-1/2 top-1/2 z-50 max-h-[92vh] w-[calc(100%-2rem)] max-w-xl -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-xl border border-border bg-card p-6 text-card-foreground shadow-2xl outline-none`,t),children:[(0,y.jsxs)(`div`,{className:`pr-8`,children:[(0,y.jsx)(l,{className:`text-lg font-semibold tracking-tight`,children:n}),a?(0,y.jsx)(r,{className:`mt-1 text-sm leading-5 text-muted-foreground`,children:a}):null]}),(0,y.jsx)(c,{className:`absolute right-4 top-4 rounded-md p-1.5 text-muted-foreground transition hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring`,"aria-label":`Close`,children:(0,y.jsx)(i,{className:`h-4 w-4`})}),(0,y.jsx)(`div`,{className:`mt-5`,children:e})]})]})}function K({open:e,onOpenChange:t,title:n,description:r,confirmLabel:i,onConfirm:a,destructive:o=!1,busy:s=!1}){return(0,y.jsx)(I,{open:e,onOpenChange:t,children:(0,y.jsxs)(L,{children:[(0,y.jsx)(R,{className:`fixed inset-0 z-50 bg-slate-950/65 backdrop-blur-[2px]`}),(0,y.jsxs)(z,{className:`fixed left-1/2 top-1/2 z-50 w-[calc(100%-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-card p-6 shadow-2xl outline-none`,children:[(0,y.jsx)(H,{className:`text-lg font-semibold text-foreground`,children:n}),(0,y.jsx)(U,{asChild:!0,children:(0,y.jsx)(`div`,{className:`mt-2 text-sm leading-6 text-muted-foreground`,children:r})}),(0,y.jsxs)(`div`,{className:`mt-6 flex justify-end gap-2`,children:[(0,y.jsx)(V,{asChild:!0,children:(0,y.jsx)(h,{variant:`outline`,disabled:s,children:`Cancel`})}),(0,y.jsx)(B,{asChild:!0,children:(0,y.jsx)(h,{variant:o?`danger`:`primary`,disabled:s,onClick:a,children:s?`Working…`:i})})]})]})]})})}export{W as n,G as r,K as t}; \ No newline at end of file +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{T as t,V as n}from"./query-DWNqGEie.js";import{B as r,E as i,G as a,H as o,L as s,R as c,U as l,V as u,W as d,c as f,ct as p,ot as m,s as h,st as g,z as _}from"./index-DleN6wGz.js";var v=e(n(),1),y=t(),b=Object.defineProperty,x=(e,t)=>b(e,`name`,{value:t,configurable:!0}),[S,C]=m(`AlertDialog`,[a]),w=a(),T=x(e=>{let{__scopeAlertDialog:t,...n}=e,r=w(t);return(0,y.jsx)(s,{...r,...n,modal:!0})},`AlertDialog`);v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,...r}=e,i=w(n);return(0,y.jsx)(d,{...i,...r,ref:t})},`AlertDialogTrigger`));var E=x(e=>{let{__scopeAlertDialog:t,...n}=e,r=w(t);return(0,y.jsx)(o,{...r,...n})},`AlertDialogPortal`),D=v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,...r}=e,i=w(n);return(0,y.jsx)(u,{...i,...r,ref:t})},`AlertDialogOverlay`)),[O,k]=S(`AlertDialogContent`),A=v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,children:r,...i}=e,a=w(n),o=v.useRef(null),s=g(t,o),c=v.useRef(null);return(0,y.jsx)(O,{scope:n,cancelRef:c,children:(0,y.jsx)(_,{role:`alertdialog`,...a,...i,ref:s,onOpenAutoFocus:p(i.onOpenAutoFocus,e=>{e.preventDefault(),c.current?.focus({preventScroll:!0})}),onPointerDownOutside:e=>e.preventDefault(),onInteractOutside:e=>e.preventDefault(),children:r})})},`AlertDialogContent`)),j=v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,...r}=e,i=w(n);return(0,y.jsx)(l,{...i,...r,ref:t})},`AlertDialogTitle`)),M=v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,...i}=e,a=w(n);return(0,y.jsx)(r,{...a,...i,ref:t})},`AlertDialogDescription`)),N=v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,...r}=e,i=w(n);return(0,y.jsx)(c,{...i,...r,ref:t})},`AlertDialogAction`)),P=`AlertDialogCancel`,F=v.forwardRef(x(function(e,t){let{__scopeAlertDialog:n,...r}=e,{cancelRef:i}=k(P,n),a=w(n),o=g(t,i);return(0,y.jsx)(c,{...a,...r,ref:o})},`AlertDialogCancel`)),I=T,L=E,R=D,z=A,B=N,V=F,H=j,U=M,W=s;function G({children:e,className:t,title:n,description:a}){return(0,y.jsxs)(o,{children:[(0,y.jsx)(u,{className:`fixed inset-0 z-50 bg-slate-950/65 backdrop-blur-[2px] data-[state=closed]:animate-out`}),(0,y.jsxs)(_,{className:f(`fixed left-1/2 top-1/2 z-50 max-h-[92vh] w-[calc(100%-2rem)] max-w-xl -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-xl border border-border bg-card p-6 text-card-foreground shadow-2xl outline-none`,t),children:[(0,y.jsxs)(`div`,{className:`pr-8`,children:[(0,y.jsx)(l,{className:`text-lg font-semibold tracking-tight`,children:n}),a?(0,y.jsx)(r,{className:`mt-1 text-sm leading-5 text-muted-foreground`,children:a}):null]}),(0,y.jsx)(c,{className:`absolute right-4 top-4 rounded-md p-1.5 text-muted-foreground transition hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring`,"aria-label":`Close`,children:(0,y.jsx)(i,{className:`h-4 w-4`})}),(0,y.jsx)(`div`,{className:`mt-5`,children:e})]})]})}function K({open:e,onOpenChange:t,title:n,description:r,confirmLabel:i,onConfirm:a,destructive:o=!1,busy:s=!1}){return(0,y.jsx)(I,{open:e,onOpenChange:t,children:(0,y.jsxs)(L,{children:[(0,y.jsx)(R,{className:`fixed inset-0 z-50 bg-slate-950/65 backdrop-blur-[2px]`}),(0,y.jsxs)(z,{className:`fixed left-1/2 top-1/2 z-50 w-[calc(100%-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-card p-6 shadow-2xl outline-none`,children:[(0,y.jsx)(H,{className:`text-lg font-semibold text-foreground`,children:n}),(0,y.jsx)(U,{asChild:!0,children:(0,y.jsx)(`div`,{className:`mt-2 text-sm leading-6 text-muted-foreground`,children:r})}),(0,y.jsxs)(`div`,{className:`mt-6 flex justify-end gap-2`,children:[(0,y.jsx)(V,{asChild:!0,children:(0,y.jsx)(h,{variant:`outline`,disabled:s,children:`Cancel`})}),(0,y.jsx)(B,{asChild:!0,children:(0,y.jsx)(h,{variant:o?`danger`:`primary`,disabled:s,onClick:a,children:s?`Working…`:i})})]})]})]})})}export{W as n,G as r,K as t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/download-Dj5PHFJw.js b/internal/frontend/dist/assets/download-CUwygqUa.js similarity index 71% rename from internal/frontend/dist/assets/download-Dj5PHFJw.js rename to internal/frontend/dist/assets/download-CUwygqUa.js index 3072e8f..f9fa0b4 100644 --- a/internal/frontend/dist/assets/download-Dj5PHFJw.js +++ b/internal/frontend/dist/assets/download-CUwygqUa.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/external-link-DfzRK7DJ.js b/internal/frontend/dist/assets/external-link-4Ueuyx8f.js similarity index 73% rename from internal/frontend/dist/assets/external-link-DfzRK7DJ.js rename to internal/frontend/dist/assets/external-link-4Ueuyx8f.js index 5475f3a..19a3200 100644 --- a/internal/frontend/dist/assets/external-link-DfzRK7DJ.js +++ b/internal/frontend/dist/assets/external-link-4Ueuyx8f.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`external-link`,[[`path`,{d:`M15 3h6v6`,key:`1q9fwt`}],[`path`,{d:`M10 14 21 3`,key:`gplh6r`}],[`path`,{d:`M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6`,key:`a6xqqp`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`external-link`,[[`path`,{d:`M15 3h6v6`,key:`1q9fwt`}],[`path`,{d:`M10 14 21 3`,key:`gplh6r`}],[`path`,{d:`M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6`,key:`a6xqqp`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/fields-PJGT-Jp7.js b/internal/frontend/dist/assets/fields-DSX3DSQR.js similarity index 73% rename from internal/frontend/dist/assets/fields-PJGT-Jp7.js rename to internal/frontend/dist/assets/fields-DSX3DSQR.js index 4292ead..11dd267 100644 --- a/internal/frontend/dist/assets/fields-PJGT-Jp7.js +++ b/internal/frontend/dist/assets/fields-DSX3DSQR.js @@ -1 +1 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{T as t,V as n}from"./query-DWNqGEie.js";import{I as r,at as i,c as a,ct as o,et as s,ot as c,rt as l,st as u}from"./index-B84OiuS0.js";var d=r(`chevron-down`,[[`path`,{d:`m6 9 6 6 6-6`,key:`qrunsl`}]]),f=e(n(),1),p=t(),m=Object.defineProperty,h=f.forwardRef(((e,t)=>m(e,`name`,{value:t,configurable:!0}))(function(e,t){return(0,p.jsx)(s.label,{...e,ref:t,onMouseDown:t=>{t.target.closest(`button, input, select, textarea`)||(e.onMouseDown?.(t),!t.defaultPrevented&&t.detail>1&&t.preventDefault())}})},`Label`)),g=Object.defineProperty,_=(e,t)=>g(e,`name`,{value:t,configurable:!0});function v(e){let[t,n]=f.useState(void 0);return i(()=>{if(e){n({width:e.offsetWidth,height:e.offsetHeight});let t=new ResizeObserver(t=>{if(!Array.isArray(t)||!t.length)return;let r=t[0],i,a;if(`borderBoxSize`in r){let e=r.borderBoxSize,t=Array.isArray(e)?e[0]:e;i=t.inlineSize,a=t.blockSize}else i=e.offsetWidth,a=e.offsetHeight;n({width:i,height:a})});return t.observe(e,{box:`border-box`}),()=>t.unobserve(e)}n(void 0)},[e]),t}_(v,`useSize`);var y=Object.defineProperty,b=(e,t)=>y(e,`name`,{value:t,configurable:!0}),x=`Switch`,[S,C]=c(x),[w,T]=S(x);function E(e){let{__scopeSwitch:t,checked:n,children:r,defaultChecked:i,disabled:a,form:o,name:s,onCheckedChange:c,required:u,value:d=`on`,internal_do_not_use_render:m}=e,[h,g]=l({prop:n,defaultProp:i??!1,onChange:c,caller:x}),[_,v]=f.useState(null),[y,b]=f.useState(null),S=f.useRef(!1),[C,T]=f.useReducer(e=>e+1,0),E={checked:h,setChecked:g,disabled:a,control:_,setControl:v,name:s,form:o,value:d,hasConsumerStoppedPropagationRef:S,userInteractionCount:C,onUserInteraction:T,required:u,defaultChecked:i,isFormControl:!_||!!o||!!_.closest(`form`),bubbleInput:y,setBubbleInput:b};return(0,p.jsx)(w,{scope:t,...E,children:P(m)?m(E):r})}b(E,`SwitchProvider`);var D=`SwitchTrigger`,O=f.forwardRef(b(function({__scopeSwitch:e,onClick:t,...n},r){let{control:i,form:a,value:c,disabled:l,checked:d,required:m,setControl:h,setChecked:g,hasConsumerStoppedPropagationRef:_,onUserInteraction:v,isFormControl:y,bubbleInput:x}=T(D,e),S=u(r,h),C=f.useRef(d);return f.useEffect(()=>{let e=a?i?.ownerDocument.getElementById(a):i?.form;if(e instanceof HTMLFormElement){let t=b(()=>g(C.current),`reset`);return e.addEventListener(`reset`,t),()=>e.removeEventListener(`reset`,t)}},[i,a,g]),(0,p.jsx)(s.button,{type:`button`,role:`switch`,"aria-checked":d,"aria-required":m,"data-state":F(d),"data-disabled":l?``:void 0,disabled:l,value:c,...n,ref:S,onClick:o(t,e=>{v(),g(e=>!e),x&&y&&(_.current=e.isPropagationStopped(),_.current||e.stopPropagation())})})},`SwitchTrigger`)),k=f.forwardRef(b(function(e,t){let{__scopeSwitch:n,name:r,checked:i,defaultChecked:a,required:o,disabled:s,value:c,onCheckedChange:l,form:u,...d}=e;return(0,p.jsx)(E,{__scopeSwitch:n,checked:i,defaultChecked:a,disabled:s,required:o,onCheckedChange:l,name:r,form:u,value:c,internal_do_not_use_render:({isFormControl:e})=>(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(O,{...d,ref:t,__scopeSwitch:n}),e&&(0,p.jsx)(N,{__scopeSwitch:n})]})})},`Switch`)),A=`SwitchThumb`,j=f.forwardRef(b(function(e,t){let{__scopeSwitch:n,...r}=e,i=T(A,n);return(0,p.jsx)(s.span,{"data-state":F(i.checked),"data-disabled":i.disabled?``:void 0,...r,ref:t})},`SwitchThumb`)),M=`SwitchBubbleInput`,N=f.forwardRef(b(function({__scopeSwitch:e,onClick:t,...n},r){let{control:i,hasConsumerStoppedPropagationRef:a,userInteractionCount:c,checked:l,defaultChecked:d,required:m,disabled:h,name:g,value:_,form:y,bubbleInput:b,setBubbleInput:x}=T(M,e),S=u(r,x),C=v(i),w=f.useRef(!1),E=f.useRef(l),D=f.useRef(c);f.useEffect(()=>{let e=b;if(!e)return;let t=window.HTMLInputElement.prototype,n=Object.getOwnPropertyDescriptor(t,`checked`).set,r=c!==D.current;D.current=c;let i=E.current!==l;E.current=l;let o=!(r&&a.current);if(i&&n){w.current=!r;let t=new Event(`click`,{bubbles:o});n.call(e,l),e.dispatchEvent(t),w.current=!1}},[b,l,a,c]);let O=f.useRef(l);return(0,p.jsx)(s.input,{type:`checkbox`,"aria-hidden":!0,defaultChecked:d??O.current,required:m,disabled:h,name:g,value:_,form:y,...n,tabIndex:-1,ref:S,onClick:o(t,e=>{w.current&&e.stopPropagation()}),style:{...n.style,...C,position:`absolute`,pointerEvents:`none`,opacity:0,margin:0,transform:`translateX(-100%)`}})},`SwitchBubbleInput`));function P(e){return typeof e==`function`}b(P,`isFunction`);function F(e){return e?`checked`:`unchecked`}b(F,`getState`);var I=(0,f.forwardRef)(function({className:e,type:t,...n},r){return(0,p.jsx)(`input`,{ref:r,type:t,className:a(`flex h-10 w-full rounded-lg border border-input bg-background px-3 py-2 text-sm text-foreground shadow-sm outline-none transition placeholder:text-muted-foreground/70 focus:border-primary/70 focus:ring-2 focus:ring-primary/15 disabled:cursor-not-allowed disabled:opacity-60`,e),...n})}),L=(0,f.forwardRef)(function({className:e,...t},n){return(0,p.jsx)(`textarea`,{ref:n,className:a(`flex min-h-24 w-full resize-y rounded-lg border border-input bg-background px-3 py-2 text-sm text-foreground shadow-sm outline-none transition placeholder:text-muted-foreground/70 focus:border-primary/70 focus:ring-2 focus:ring-primary/15 disabled:cursor-not-allowed disabled:opacity-60`,e),...t})}),R=(0,f.forwardRef)(function({className:e,children:t,...n},r){return(0,p.jsxs)(`div`,{className:`relative`,children:[(0,p.jsx)(`select`,{ref:r,className:a(`flex h-10 w-full appearance-none rounded-lg border border-input bg-background py-2 pl-3 pr-9 text-sm text-foreground shadow-sm outline-none transition focus:border-primary/70 focus:ring-2 focus:ring-primary/15 disabled:cursor-not-allowed disabled:opacity-60`,e),...n,children:t}),(0,p.jsx)(d,{className:`pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground`,"aria-hidden":`true`})]})});function z({className:e,...t}){return(0,p.jsx)(h,{className:a(`text-xs font-semibold leading-5 text-foreground`,e),...t})}function B({label:e,hint:t,error:n,required:r,children:i,className:o}){return(0,p.jsxs)(`div`,{className:a(`space-y-1.5`,o),children:[(0,p.jsxs)(`div`,{className:`flex items-baseline justify-between gap-2`,children:[(0,p.jsxs)(z,{children:[e,r?(0,p.jsx)(`span`,{className:`ml-1 text-rose-500`,"aria-hidden":`true`,children:`*`}):null]}),t?(0,p.jsx)(`span`,{className:`text-[11px] text-muted-foreground`,children:t}):null]}),i,n?(0,p.jsx)(`p`,{className:`text-xs font-medium text-rose-600 dark:text-rose-400`,role:`alert`,children:n}):null]})}function V({checked:e,onCheckedChange:t,disabled:n,id:r,"aria-label":i}){return(0,p.jsx)(k,{id:r,checked:e,onCheckedChange:t,disabled:n,"aria-label":i,className:`relative h-6 w-11 shrink-0 cursor-pointer rounded-full border border-transparent bg-muted-foreground/25 outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring data-[state=checked]:bg-primary disabled:cursor-not-allowed disabled:opacity-50`,children:(0,p.jsx)(j,{className:`block h-5 w-5 translate-x-0 rounded-full bg-white shadow transition-transform data-[state=checked]:translate-x-5`})})}function H({checked:e,onChange:t,label:n,description:r,disabled:i=!1}){return(0,p.jsxs)(`label`,{className:a(`flex cursor-pointer items-start justify-between gap-4 rounded-lg border border-border bg-background p-3`,i&&`cursor-not-allowed opacity-60`),children:[(0,p.jsxs)(`span`,{children:[(0,p.jsx)(`span`,{className:`block text-sm font-medium text-foreground`,children:n}),r?(0,p.jsx)(`span`,{className:`mt-0.5 block text-xs leading-5 text-muted-foreground`,children:r}):null]}),(0,p.jsx)(V,{checked:e,onCheckedChange:t,disabled:i,"aria-label":n})]})}export{V as a,d as c,R as i,B as n,L as o,I as r,v as s,H as t}; \ No newline at end of file +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{T as t,V as n}from"./query-DWNqGEie.js";import{I as r,at as i,c as a,ct as o,et as s,ot as c,rt as l,st as u}from"./index-DleN6wGz.js";var d=r(`chevron-down`,[[`path`,{d:`m6 9 6 6 6-6`,key:`qrunsl`}]]),f=e(n(),1),p=t(),m=Object.defineProperty,h=f.forwardRef(((e,t)=>m(e,`name`,{value:t,configurable:!0}))(function(e,t){return(0,p.jsx)(s.label,{...e,ref:t,onMouseDown:t=>{t.target.closest(`button, input, select, textarea`)||(e.onMouseDown?.(t),!t.defaultPrevented&&t.detail>1&&t.preventDefault())}})},`Label`)),g=Object.defineProperty,_=(e,t)=>g(e,`name`,{value:t,configurable:!0});function v(e){let[t,n]=f.useState(void 0);return i(()=>{if(e){n({width:e.offsetWidth,height:e.offsetHeight});let t=new ResizeObserver(t=>{if(!Array.isArray(t)||!t.length)return;let r=t[0],i,a;if(`borderBoxSize`in r){let e=r.borderBoxSize,t=Array.isArray(e)?e[0]:e;i=t.inlineSize,a=t.blockSize}else i=e.offsetWidth,a=e.offsetHeight;n({width:i,height:a})});return t.observe(e,{box:`border-box`}),()=>t.unobserve(e)}n(void 0)},[e]),t}_(v,`useSize`);var y=Object.defineProperty,b=(e,t)=>y(e,`name`,{value:t,configurable:!0}),x=`Switch`,[S,C]=c(x),[w,T]=S(x);function E(e){let{__scopeSwitch:t,checked:n,children:r,defaultChecked:i,disabled:a,form:o,name:s,onCheckedChange:c,required:u,value:d=`on`,internal_do_not_use_render:m}=e,[h,g]=l({prop:n,defaultProp:i??!1,onChange:c,caller:x}),[_,v]=f.useState(null),[y,b]=f.useState(null),S=f.useRef(!1),[C,T]=f.useReducer(e=>e+1,0),E={checked:h,setChecked:g,disabled:a,control:_,setControl:v,name:s,form:o,value:d,hasConsumerStoppedPropagationRef:S,userInteractionCount:C,onUserInteraction:T,required:u,defaultChecked:i,isFormControl:!_||!!o||!!_.closest(`form`),bubbleInput:y,setBubbleInput:b};return(0,p.jsx)(w,{scope:t,...E,children:P(m)?m(E):r})}b(E,`SwitchProvider`);var D=`SwitchTrigger`,O=f.forwardRef(b(function({__scopeSwitch:e,onClick:t,...n},r){let{control:i,form:a,value:c,disabled:l,checked:d,required:m,setControl:h,setChecked:g,hasConsumerStoppedPropagationRef:_,onUserInteraction:v,isFormControl:y,bubbleInput:x}=T(D,e),S=u(r,h),C=f.useRef(d);return f.useEffect(()=>{let e=a?i?.ownerDocument.getElementById(a):i?.form;if(e instanceof HTMLFormElement){let t=b(()=>g(C.current),`reset`);return e.addEventListener(`reset`,t),()=>e.removeEventListener(`reset`,t)}},[i,a,g]),(0,p.jsx)(s.button,{type:`button`,role:`switch`,"aria-checked":d,"aria-required":m,"data-state":F(d),"data-disabled":l?``:void 0,disabled:l,value:c,...n,ref:S,onClick:o(t,e=>{v(),g(e=>!e),x&&y&&(_.current=e.isPropagationStopped(),_.current||e.stopPropagation())})})},`SwitchTrigger`)),k=f.forwardRef(b(function(e,t){let{__scopeSwitch:n,name:r,checked:i,defaultChecked:a,required:o,disabled:s,value:c,onCheckedChange:l,form:u,...d}=e;return(0,p.jsx)(E,{__scopeSwitch:n,checked:i,defaultChecked:a,disabled:s,required:o,onCheckedChange:l,name:r,form:u,value:c,internal_do_not_use_render:({isFormControl:e})=>(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(O,{...d,ref:t,__scopeSwitch:n}),e&&(0,p.jsx)(N,{__scopeSwitch:n})]})})},`Switch`)),A=`SwitchThumb`,j=f.forwardRef(b(function(e,t){let{__scopeSwitch:n,...r}=e,i=T(A,n);return(0,p.jsx)(s.span,{"data-state":F(i.checked),"data-disabled":i.disabled?``:void 0,...r,ref:t})},`SwitchThumb`)),M=`SwitchBubbleInput`,N=f.forwardRef(b(function({__scopeSwitch:e,onClick:t,...n},r){let{control:i,hasConsumerStoppedPropagationRef:a,userInteractionCount:c,checked:l,defaultChecked:d,required:m,disabled:h,name:g,value:_,form:y,bubbleInput:b,setBubbleInput:x}=T(M,e),S=u(r,x),C=v(i),w=f.useRef(!1),E=f.useRef(l),D=f.useRef(c);f.useEffect(()=>{let e=b;if(!e)return;let t=window.HTMLInputElement.prototype,n=Object.getOwnPropertyDescriptor(t,`checked`).set,r=c!==D.current;D.current=c;let i=E.current!==l;E.current=l;let o=!(r&&a.current);if(i&&n){w.current=!r;let t=new Event(`click`,{bubbles:o});n.call(e,l),e.dispatchEvent(t),w.current=!1}},[b,l,a,c]);let O=f.useRef(l);return(0,p.jsx)(s.input,{type:`checkbox`,"aria-hidden":!0,defaultChecked:d??O.current,required:m,disabled:h,name:g,value:_,form:y,...n,tabIndex:-1,ref:S,onClick:o(t,e=>{w.current&&e.stopPropagation()}),style:{...n.style,...C,position:`absolute`,pointerEvents:`none`,opacity:0,margin:0,transform:`translateX(-100%)`}})},`SwitchBubbleInput`));function P(e){return typeof e==`function`}b(P,`isFunction`);function F(e){return e?`checked`:`unchecked`}b(F,`getState`);var I=(0,f.forwardRef)(function({className:e,type:t,...n},r){return(0,p.jsx)(`input`,{ref:r,type:t,className:a(`flex h-10 w-full rounded-lg border border-input bg-background px-3 py-2 text-sm text-foreground shadow-sm outline-none transition placeholder:text-muted-foreground/70 focus:border-primary/70 focus:ring-2 focus:ring-primary/15 disabled:cursor-not-allowed disabled:opacity-60`,e),...n})}),L=(0,f.forwardRef)(function({className:e,...t},n){return(0,p.jsx)(`textarea`,{ref:n,className:a(`flex min-h-24 w-full resize-y rounded-lg border border-input bg-background px-3 py-2 text-sm text-foreground shadow-sm outline-none transition placeholder:text-muted-foreground/70 focus:border-primary/70 focus:ring-2 focus:ring-primary/15 disabled:cursor-not-allowed disabled:opacity-60`,e),...t})}),R=(0,f.forwardRef)(function({className:e,children:t,...n},r){return(0,p.jsxs)(`div`,{className:`relative`,children:[(0,p.jsx)(`select`,{ref:r,className:a(`flex h-10 w-full appearance-none rounded-lg border border-input bg-background py-2 pl-3 pr-9 text-sm text-foreground shadow-sm outline-none transition focus:border-primary/70 focus:ring-2 focus:ring-primary/15 disabled:cursor-not-allowed disabled:opacity-60`,e),...n,children:t}),(0,p.jsx)(d,{className:`pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground`,"aria-hidden":`true`})]})});function z({className:e,...t}){return(0,p.jsx)(h,{className:a(`text-xs font-semibold leading-5 text-foreground`,e),...t})}function B({label:e,hint:t,error:n,required:r,children:i,className:o}){let s=(0,f.useId)(),c=(0,f.isValidElement)(i)&&i.props.id?i.props.id:s,l=t?`${c}-hint`:void 0,u=n?`${c}-error`:void 0,d=[l,u].filter(Boolean).join(` `)||void 0,m={id:c};d&&(m[`aria-describedby`]=d),n&&(m[`aria-invalid`]=!0),r&&(m[`aria-required`]=!0);let h=(0,f.isValidElement)(i)?(0,f.cloneElement)(i,m):i;return(0,p.jsxs)(`div`,{className:a(`space-y-1.5`,o),children:[(0,p.jsxs)(`div`,{className:`flex items-baseline justify-between gap-2`,children:[(0,p.jsxs)(z,{htmlFor:c,children:[e,r?(0,p.jsx)(`span`,{className:`ml-1 text-rose-500`,"aria-hidden":`true`,children:`*`}):null]}),t?(0,p.jsx)(`span`,{id:l,className:`text-[11px] text-muted-foreground`,children:t}):null]}),h,n?(0,p.jsx)(`p`,{id:u,className:`text-xs font-medium text-rose-600 dark:text-rose-400`,role:`alert`,children:n}):null]})}function V({checked:e,onCheckedChange:t,disabled:n,id:r,"aria-label":i}){return(0,p.jsx)(k,{id:r,checked:e,onCheckedChange:t,disabled:n,"aria-label":i,className:`relative h-6 w-11 shrink-0 cursor-pointer rounded-full border border-transparent bg-muted-foreground/25 outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring data-[state=checked]:bg-primary disabled:cursor-not-allowed disabled:opacity-50`,children:(0,p.jsx)(j,{className:`block h-5 w-5 translate-x-0 rounded-full bg-white shadow transition-transform data-[state=checked]:translate-x-5`})})}function H({checked:e,onChange:t,label:n,description:r,disabled:i=!1}){return(0,p.jsxs)(`label`,{className:a(`flex cursor-pointer items-start justify-between gap-4 rounded-lg border border-border bg-background p-3`,i&&`cursor-not-allowed opacity-60`),children:[(0,p.jsxs)(`span`,{children:[(0,p.jsx)(`span`,{className:`block text-sm font-medium text-foreground`,children:n}),r?(0,p.jsx)(`span`,{className:`mt-0.5 block text-xs leading-5 text-muted-foreground`,children:r}):null]}),(0,p.jsx)(V,{checked:e,onCheckedChange:t,disabled:i,"aria-label":n})]})}export{V as a,d as c,R as i,B as n,L as o,I as r,v as s,H as t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/funnel-x-BSCtvBiK.js b/internal/frontend/dist/assets/funnel-x-DRKN7t7O.js similarity index 79% rename from internal/frontend/dist/assets/funnel-x-BSCtvBiK.js rename to internal/frontend/dist/assets/funnel-x-DRKN7t7O.js index 7c82b55..3b9151e 100644 --- a/internal/frontend/dist/assets/funnel-x-BSCtvBiK.js +++ b/internal/frontend/dist/assets/funnel-x-DRKN7t7O.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`funnel-x`,[[`path`,{d:`M12.531 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14v6a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341l.427-.473`,key:`ol2ft2`}],[`path`,{d:`m16.5 3.5 5 5`,key:`15e6fa`}],[`path`,{d:`m21.5 3.5-5 5`,key:`m0lwru`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`funnel-x`,[[`path`,{d:`M12.531 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14v6a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341l.427-.473`,key:`ol2ft2`}],[`path`,{d:`m16.5 3.5 5 5`,key:`15e6fa`}],[`path`,{d:`m21.5 3.5-5 5`,key:`m0lwru`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/index-B84OiuS0.js b/internal/frontend/dist/assets/index-DleN6wGz.js similarity index 90% rename from internal/frontend/dist/assets/index-B84OiuS0.js rename to internal/frontend/dist/assets/index-DleN6wGz.js index bf7ecfb..36d7e10 100644 --- a/internal/frontend/dist/assets/index-B84OiuS0.js +++ b/internal/frontend/dist/assets/index-DleN6wGz.js @@ -1,4 +1,4 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/DashboardPage-BfyYS-Fi.js","assets/rolldown-runtime-hePW80VL.js","assets/query-DWNqGEie.js","assets/react-DsKPNe9r.js","assets/MetricCard-GfGMWPS-.js","assets/card-DEXft0Cm.js","assets/arrow-up-Cm-GZvKO.js","assets/plus-C7xMvwvj.js","assets/useAppSettings-CeA9vkHH.js","assets/PageHeader-DGX-y_Bi.js","assets/EChart-C9A5j2N8.js","assets/charts-BiiqPz-H.js","assets/badge-D4YKBNKr.js","assets/EntityBadges-DZDjvrcw.js","assets/TasksPage-BEXsLscb.js","assets/fields-PJGT-Jp7.js","assets/funnel-x-BSCtvBiK.js","assets/pen-line-BMm6ehcb.js","assets/search-B8uA6mdW.js","assets/trash-2-DJb0gq__.js","assets/dialog-DXkCUNSe.js","assets/columnVisibilityFeature-CB2cjdCS.js","assets/TaskEditorPage-BwxKINaO.js","assets/arrow-left-CkoT9SwQ.js","assets/clock-3-BeKI5fxN.js","assets/OoklaEulaAcceptance-B4WQoWrJ.js","assets/external-link-DfzRK7DJ.js","assets/shield-check-Dm-p0u_h.js","assets/radio-tower-C96Q3yet.js","assets/server-BOAQDuBN.js","assets/schemas-DaFWBMkt.js","assets/ResultsPage-CwUSS1pP.js","assets/download-Dj5PHFJw.js","assets/ResultDetailPage-Dj2B-oxr.js","assets/route-C77_DY7l.js","assets/StatisticsPage-D4N2MQUQ.js","assets/ComparisonPage-Cp7FUxCh.js","assets/NetworkPage-EmZpqL4-.js","assets/circle-x-DYIKQ94H.js","assets/shield-alert-ma0N2JAc.js","assets/SettingsPage-BK6TpFP7.js","assets/database-BRZrCBcR.js","assets/SystemPage-DV8hzwFf.js","assets/NotFoundPage-DK5iVdeX.js"])))=>i.map(i=>d[i]); +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/DashboardPage-DEmE5jHs.js","assets/rolldown-runtime-hePW80VL.js","assets/query-DWNqGEie.js","assets/react-DsKPNe9r.js","assets/MetricCard-CMX2GS_z.js","assets/card-BtXBd7n8.js","assets/arrow-up-DVV_1I7Y.js","assets/plus-BbAjJ_nn.js","assets/useAppSettings-vnP5f96R.js","assets/PageHeader-DGX-y_Bi.js","assets/EChart-A2OaIcPj.js","assets/charts-BiiqPz-H.js","assets/badge-BWAHg20h.js","assets/EntityBadges-CM7WeMOS.js","assets/TasksPage-CxX5SuNf.js","assets/fields-DSX3DSQR.js","assets/funnel-x-DRKN7t7O.js","assets/pen-line-UL8dX1Od.js","assets/search-b0EiqX0q.js","assets/trash-2-DyGHVVq4.js","assets/dialog-CT4xHTSq.js","assets/columnVisibilityFeature-CB2cjdCS.js","assets/TaskEditorPage-Bx-TQ0Uc.js","assets/arrow-left-DZUEJBho.js","assets/clock-3-DhdKy9KD.js","assets/OoklaEulaAcceptance-DkJ6glh5.js","assets/external-link-4Ueuyx8f.js","assets/shield-check-DrVussKH.js","assets/radio-tower-D9Z4YmgD.js","assets/server-BWQHmE7V.js","assets/schemas-DaFWBMkt.js","assets/ResultsPage-DR4hm0dc.js","assets/download-CUwygqUa.js","assets/ResultDetailPage-DbQ93uA2.js","assets/route-DtX_GSbk.js","assets/StatisticsPage-C5F5n78a.js","assets/ComparisonPage-CZJO9UdP.js","assets/NetworkPage-CZ9yIsUy.js","assets/circle-x-CJ7LJdu2.js","assets/shield-alert-CznEbFrD.js","assets/SettingsPage-BUWz04bL.js","assets/database-DuA3AOvM.js","assets/SystemPage-Cwt4SToT.js","assets/NotFoundPage-YfFq8HYe.js"])))=>i.map(i=>d[i]); import{r as e}from"./rolldown-runtime-hePW80VL.js";import{A as t,B as n,C as r,D as i,E as a,F as o,I as s,L as c,M as l,N as u,O as d,P as f,R as p,T as m,V as h,j as g,k as _,w as ee,z as te}from"./query-DWNqGEie.js";import{S as ne,_ as re,f as v,g as y,h as b,m as x,u as ie,v as ae,x as oe}from"./react-DsKPNe9r.js";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var se=class extends n{constructor(e={}){super(),this.config=e,this.#e=new Set,this.#t=new Map,this.#n=0}#e;#t;#n;build(e,t,n){let r=new a({client:e,mutationCache:this,mutationId:++this.#n,options:e.defaultMutationOptions(t),state:n});return this.add(r),r}add(e){this.#e.add(e);let t=ce(e);if(typeof t==`string`){let n=this.#t.get(t);n?n.push(e):this.#t.set(t,[e])}this.notify({type:`added`,mutation:e})}remove(e){if(this.#e.delete(e)){let t=ce(e);if(typeof t==`string`){let n=this.#t.get(t);if(n){if(n.length>1){let t=n.indexOf(e);t!==-1&&n.splice(t,1)}else n[0]===e&&this.#t.delete(t)}}}this.notify({type:`removed`,mutation:e})}canRun(e){let t=ce(e);if(typeof t==`string`){let n=this.#t.get(t)?.find(e=>e.state.status===`pending`);return!n||n===e}return!0}runNext(e){let t=ce(e);return typeof t==`string`?(this.#t.get(t)?.find(t=>t!==e&&t.state.isPaused))?.continue()??Promise.resolve():Promise.resolve()}clear(){_.batch(()=>{this.#e.forEach(e=>{this.notify({type:`removed`,mutation:e})}),this.#e.clear(),this.#t.clear()})}getAll(){return Array.from(this.#e)}find(e){let t={exact:!0,...e};return this.getAll().find(e=>u(t,e))}findAll(e={}){return this.getAll().filter(t=>u(e,t))}notify(e){_.batch(()=>{this.listeners.forEach(t=>{t(e)})})}resumePausedMutations(){let e=this.getAll().filter(e=>e.state.isPaused);return _.batch(()=>Promise.all(e.map(e=>e.continue().catch(o))))}};function ce(e){return e.options.scope?.id}var le=class extends n{constructor(e={}){super(),this.config=e,this.#e=new Map}#e;build(e,t,n){let r=t.queryKey,a=t.queryHash??l(r,t),o=this.get(a);return o||(o=new i({client:e,queryKey:r,queryHash:a,options:e.defaultQueryOptions(t),state:n,defaultOptions:e.getQueryDefaults(r)}),this.add(o)),o}add(e){this.#e.has(e.queryHash)||(this.#e.set(e.queryHash,e),this.notify({type:`added`,query:e}))}remove(e){let t=this.#e.get(e.queryHash);t&&(e.destroy(),t===e&&this.#e.delete(e.queryHash),this.notify({type:`removed`,query:e}))}clear(){_.batch(()=>{this.getAll().forEach(e=>{this.remove(e)})})}get(e){return this.#e.get(e)}getAll(){return[...this.#e.values()]}find(e){let t={exact:!0,...e};return this.getAll().find(e=>f(t,e))}findAll(e={}){let t=this.getAll();return Object.keys(e).length>0?t.filter(t=>f(e,t)):t}notify(e){_.batch(()=>{this.listeners.forEach(t=>{t(e)})})}onFocus(){_.batch(()=>{this.getAll().forEach(e=>{e.onFocus()})})}onOnline(){_.batch(()=>{this.getAll().forEach(e=>{e.onOnline()})})}},S=class{#e;#t;#n;#r;#i;#a;#o;#s;constructor(e={}){this.#e=e.queryCache||new le,this.#t=e.mutationCache||new se,this.#n=e.defaultOptions||{},this.#r=new Map,this.#i=new Map,this.#a=0}mount(){this.#a++,this.#a===1&&(this.#o=te.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onFocus())}),this.#s=d.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onOnline())}))}unmount(){this.#a--,this.#a===0&&(this.#o?.(),this.#o=void 0,this.#s?.(),this.#s=void 0)}isFetching(e){return this.#e.findAll({...e,fetchStatus:`fetching`}).length}isMutating(e){return this.#t.findAll({...e,status:`pending`}).length}getQueryData(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state.data}ensureQueryData(e){let t=this.defaultQueryOptions(e),n=this.#e.build(this,t),r=n.state.data;return r===void 0?this.fetchQuery(e):(e.revalidateIfStale&&n.isStaleByTime(c(t.staleTime,n))&&this.prefetchQuery(t),Promise.resolve(r))}getQueriesData(e){return this.#e.findAll(e).map(({queryKey:e,state:t})=>[e,t.data])}setQueryData(e,n,r){let i=this.defaultQueryOptions({queryKey:e}),a=this.#e.get(i.queryHash)?.state.data,o=t(n,a);if(o!==void 0)return this.#e.build(this,i).setData(o,{...r,manual:!0})}setQueriesData(e,t,n){return _.batch(()=>this.#e.findAll(e).map(({queryKey:e})=>[e,this.setQueryData(e,t,n)]))}getQueryState(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state}removeQueries(e){let t=this.#e;_.batch(()=>{t.findAll(e).forEach(e=>{t.remove(e)})})}resetQueries(e,t){let n=this.#e;return _.batch(()=>(n.findAll(e).forEach(e=>{e.reset()}),this.refetchQueries({type:`active`,...e},t)))}cancelQueries(e,t={}){let n={revert:!0,...t},r=_.batch(()=>this.#e.findAll(e).map(e=>e.cancel(n)));return Promise.all(r).then(o).catch(o)}invalidateQueries(e,t={}){return _.batch(()=>(this.#e.findAll(e).forEach(e=>{e.invalidate()}),e?.refetchType===`none`?Promise.resolve():this.refetchQueries({...e,type:e?.refetchType??e?.type??`active`},t)))}refetchQueries(e,t={}){let n={...t,cancelRefetch:t.cancelRefetch??!0},r=_.batch(()=>this.#e.findAll(e).filter(e=>!e.isDisabled()&&!e.isStatic()).map(e=>{let t=e.fetch(void 0,n);return n.throwOnError||(t=t.catch(o)),e.state.fetchStatus===`paused`?Promise.resolve():t}));return Promise.all(r).then(o)}fetchQuery(e){let t=this.defaultQueryOptions(e);t.retry===void 0&&(t.retry=!1);let n=this.#e.build(this,t);return n.isStaleByTime(c(t.staleTime,n))?n.fetch(t):Promise.resolve(n.state.data)}prefetchQuery(e){return this.fetchQuery(e).then(o).catch(o)}fetchInfiniteQuery(e){return e._type=`infinite`,this.fetchQuery(e)}prefetchInfiniteQuery(e){return this.fetchInfiniteQuery(e).then(o).catch(o)}ensureInfiniteQueryData(e){return e._type=`infinite`,this.ensureQueryData(e)}resumePausedMutations(){return d.isOnline()?this.#t.resumePausedMutations():Promise.resolve()}getQueryCache(){return this.#e}getMutationCache(){return this.#t}getDefaultOptions(){return this.#n}setDefaultOptions(e){this.#n=e}setQueryDefaults(e,t){this.#r.set(g(e),{queryKey:e,defaultOptions:t})}getQueryDefaults(e){let t=[...this.#r.values()],n={};return t.forEach(t=>{s(e,t.queryKey)&&Object.assign(n,t.defaultOptions)}),n}setMutationDefaults(e,t){this.#i.set(g(e),{mutationKey:e,defaultOptions:t})}getMutationDefaults(e){let t=[...this.#i.values()],n={};return t.forEach(t=>{s(e,t.mutationKey)&&Object.assign(n,t.defaultOptions)}),n}defaultQueryOptions(e){if(e._defaulted)return e;let t={...this.#n.queries,...this.getQueryDefaults(e.queryKey),...e,_defaulted:!0};return t.queryHash||=l(t.queryKey,t),t.refetchOnReconnect===void 0&&(t.refetchOnReconnect=t.networkMode!==`always`),t.throwOnError===void 0&&(t.throwOnError=!!t.suspense),!t.networkMode&&t.persister&&(t.networkMode=`offlineFirst`),t.queryFn===p&&(t.enabled=!1),t}defaultMutationOptions(e){return e?._defaulted?e:{...this.#n.mutations,...e?.mutationKey&&this.getMutationDefaults(e.mutationKey),...e,_defaulted:!0}}clear(){this.#e.clear(),this.#t.clear()}},C=e(h(),1),ue=oe(),de=Object.defineProperty,w=(e,t)=>de(e,`name`,{value:t,configurable:!0}),fe=!!(typeof window<`u`&&window.document&&window.document.createElement);function T(e,t,{checkForDefaultPrevented:n=!0}={}){return w(function(r){if(e?.(r),n===!1||!r||!r.defaultPrevented)return t?.(r)},`handleEvent`)}w(T,`composeEventHandlers`);function pe(e){if(!fe)throw Error(`Cannot access window outside of the DOM`);return e?.ownerDocument?.defaultView??window}w(pe,`getOwnerWindow`);function me(e){if(!fe)throw Error(`Cannot access document outside of the DOM`);return e?.ownerDocument??document}w(me,`getOwnerDocument`);function E(e,t=!1){let{activeElement:n}=me(e);if(!n?.nodeName)return null;if(D(n)&&n.contentDocument)return E(n.contentDocument.body,t);if(t){let e=n.getAttribute(`aria-activedescendant`);if(e){let t=me(n).getElementById(e);if(t)return t}}return n}w(E,`getActiveElement`);function D(e){return e.tagName===`IFRAME`}w(D,`isFrame`);var he=Object.defineProperty,ge=(e,t)=>he(e,`name`,{value:t,configurable:!0});function O(e,t){if(typeof e==`function`)return e(t);e!=null&&(e.current=t)}ge(O,`setRef`);function _e(...e){return t=>{let n=!1,r=e.map(e=>{let r=O(e,t);return!n&&typeof r==`function`&&(n=!0),r});if(n)return()=>{for(let t=0;tve(e,`name`,{value:t,configurable:!0});function ye(e,t){let n=C.createContext(t);n.displayName=e+`Context`;let r=j(e=>{let{children:t,...r}=e,i=C.useMemo(()=>r,Object.values(r));return(0,A.jsx)(n.Provider,{value:i,children:t})},`Provider`);r.displayName=e+`Provider`;function i(r,i={}){let{optional:a=!1}=i,o=C.useContext(n);if(o)return o;if(t!==void 0)return t;if(!a)throw Error(`\`${r}\` must be used within \`${e}\``)}return j(i,`useContext`),[r,i]}j(ye,`createContext`);function be(e,t=[]){let n=[];function r(t,r){let i=C.createContext(r);i.displayName=t+`Context`;let a=n.length;n=[...n,r];let o=j(t=>{let{scope:n,children:r,...o}=t,s=n?.[e]?.[a]||i,c=C.useMemo(()=>o,Object.values(o));return(0,A.jsx)(s.Provider,{value:c,children:r})},`Provider`);o.displayName=t+`Provider`;function s(n,o,s={}){let{optional:c=!1}=s,l=o?.[e]?.[a]||i,u=C.useContext(l);if(u)return u;if(r!==void 0)return r;if(!c)throw Error(`\`${n}\` must be used within \`${t}\``)}return j(s,`useContext`),[o,s]}j(r,`createContext`);let i=j(()=>{let t=n.map(e=>C.createContext(e));return j(function(n){let r=n?.[e]||t;return C.useMemo(()=>({[`__scope${e}`]:{...n,[e]:r}}),[n,r])},`useScope`)},`createScope`);return i.scopeName=e,[r,xe(i,...t)]}j(be,`createContextScope`);function xe(...e){let t=e[0];if(e.length===1)return t;let n=j(()=>{let n=e.map(e=>({useScope:e(),scopeName:e.scopeName}));return j(function(e){let r=n.reduce((t,{useScope:n,scopeName:r})=>{let i=n(e)[`__scope${r}`];return{...t,...i}},{});return C.useMemo(()=>({[`__scope${t.scopeName}`]:r}),[r])},`useComposedScopes`)},`createScope`);return n.scopeName=t.scopeName,n}j(xe,`composeContextScopes`);var M=globalThis?.document?C.useLayoutEffect:()=>{},Se=Object.defineProperty,Ce=(e,t)=>Se(e,`name`,{value:t,configurable:!0}),we=C.useId||(()=>void 0),Te=0;function Ee(e){let[t,n]=C.useState(we());return M(()=>{e||n(e=>e??String(Te++))},[e]),e||(t?`radix-${t}`:``)}Ce(Ee,`useId`);var De=Object.defineProperty,Oe=(e,t)=>De(e,`name`,{value:t,configurable:!0}),ke=C.useEffectEvent,Ae=C.useInsertionEffect;function je(e){if(typeof ke==`function`)return ke(e);let t=C.useRef(()=>{throw Error(`Cannot call an event handler while rendering.`)});return typeof Ae==`function`?Ae(()=>{t.current=e}):M(()=>{t.current=e}),C.useMemo(()=>((...e)=>t.current?.(...e)),[])}Oe(je,`useEffectEvent`);var Me=Object.defineProperty,Ne=(e,t)=>Me(e,`name`,{value:t,configurable:!0}),Pe=C.useInsertionEffect||M;function Fe({prop:e,defaultProp:t,onChange:n=Ne(()=>{},`onChange`),caller:r}){let[i,a,o]=Ie({defaultProp:t,onChange:n}),s=e!==void 0;return[s?e:i,C.useCallback(t=>{if(s){let n=Le(t)?t(e):t;n!==e&&o.current?.(n)}else a(t)},[s,e,a,o])]}Ne(Fe,`useControllableState`);function Ie({defaultProp:e,onChange:t}){let[n,r]=C.useState(e),i=C.useRef(n),a=C.useRef(t);return Pe(()=>{a.current=t},[t]),C.useEffect(()=>{i.current!==n&&(a.current?.(n),i.current=n)},[n,i]),[n,r,a]}Ne(Ie,`useUncontrolledState`);function Le(e){return typeof e==`function`}Ne(Le,`isFunction`);var Re=Symbol(`RADIX:SYNC_STATE`);function ze(e,t,n,r){let{prop:i,defaultProp:a,onChange:o,caller:s}=t,c=i!==void 0,l=je(o),u=[{...n,state:a}];r&&u.push(r);let[d,f]=C.useReducer((t,n)=>{if(n.type===Re)return{...t,state:n.state};let r=e(t,n);return c&&!Object.is(r.state,t.state)&&l(r.state),r},...u),p=d.state,m=C.useRef(p);C.useEffect(()=>{m.current!==p&&(m.current=p,c||l(p))},[p,m,c]);let h=C.useMemo(()=>i===void 0?d:{...d,state:i},[d,i]);return C.useEffect(()=>{c&&!Object.is(i,d.state)&&f({type:Re,state:i})},[i,d.state,c]),[h,f]}Ne(ze,`useControllableStateReducer`);var Be=e(ne(),1),Ve=Object.defineProperty,N=(e,t)=>Ve(e,`name`,{value:t,configurable:!0});function He(e){let t=C.forwardRef((t,n)=>{let{children:r,...i}=t,a=null,o=!1,s=[];Ze(r)&&typeof tt==`function`&&(r=tt(r._payload)),C.Children.forEach(r,e=>{if(Ye(e)){o=!0;let t=e,n=`child`in t.props?t.props.child:t.props.children;Ze(n)&&typeof tt==`function`&&(n=tt(n._payload)),a=Ke(t,n),s.push(a?.props?.children)}else s.push(e)}),a?a=C.cloneElement(a,void 0,s):!o&&C.Children.count(r)===1&&C.isValidElement(r)&&(a=r);let c=a?Je(a):void 0,l=k(n,c);if(!a){if(r||r===0)throw Error(o?et(e):$e(e));return r}let u=qe(i,a.props??{});return a.type!==C.Fragment&&(u.ref=n?l:c),C.cloneElement(a,u)});return t.displayName=`${e}.Slot`,t}N(He,`createSlot`);var Ue=He(`Slot`),We=Symbol.for(`radix.slottable`);function Ge(e){let t=N(e=>`child`in e?e.children(e.child):e.children,`Slottable`);return t.displayName=`${e}.Slottable`,t.__radixId=We,t}N(Ge,`createSlottable`);var Ke=N((e,t)=>{if(`child`in e.props){let t=e.props.child;return C.isValidElement(t)?C.cloneElement(t,void 0,e.props.children(t.props.children)):null}return C.isValidElement(t)?t:null},`getSlottableElementFromSlottable`);function qe(e,t){let n={...t};for(let r in t){let i=e[r],a=t[r];/^on[A-Z]/.test(r)?i&&a?n[r]=(...e)=>{let t=a(...e);return i(...e),t}:i&&(n[r]=i):r===`style`?n[r]={...i,...a}:r===`className`&&(n[r]=[i,a].filter(Boolean).join(` `))}return{...e,...n}}N(qe,`mergeProps`);function Je(e){let t=Object.getOwnPropertyDescriptor(e.props,`ref`)?.get,n=t&&`isReactWarning`in t&&t.isReactWarning;return n?e.ref:(t=Object.getOwnPropertyDescriptor(e,`ref`)?.get,n=t&&`isReactWarning`in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}N(Je,`getElementRef`);function Ye(e){return C.isValidElement(e)&&typeof e.type==`function`&&`__radixId`in e.type&&e.type.__radixId===We}N(Ye,`isSlottable`);var Xe=Symbol.for(`react.lazy`);function Ze(e){return typeof e==`object`&&!!e&&`$$typeof`in e&&e.$$typeof===Xe&&`_payload`in e&&Qe(e._payload)}N(Ze,`isLazyComponent`);function Qe(e){return typeof e==`object`&&!!e&&`then`in e}N(Qe,`isPromiseLike`);var $e=N(e=>`${e} failed to slot onto its children. Expected a single React element child or \`Slottable\`.`,`createSlotError`),et=N(e=>`${e} failed to slot onto its \`Slottable\`. Expected \`Slottable\` to receive a single React element child.`,`createSlottableError`),tt=C.use,nt=Object.defineProperty,rt=(e,t)=>nt(e,`name`,{value:t,configurable:!0}),P=[`a`,`button`,`div`,`form`,`h2`,`h3`,`img`,`input`,`label`,`li`,`nav`,`ol`,`p`,`select`,`span`,`svg`,`ul`].reduce((e,t)=>{let n=He(`Primitive.${t}`),r=C.forwardRef((e,r)=>{let{asChild:i,...a}=e,o=i?n:t;return typeof window<`u`&&(window[Symbol.for(`radix-ui`)]=!0),(0,A.jsx)(o,{...a,ref:r})});return r.displayName=`Primitive.${t}`,{...e,[t]:r}},{});function it(e,t){e&&Be.flushSync(()=>e.dispatchEvent(t))}rt(it,`dispatchDiscreteCustomEvent`);var at=Object.defineProperty,ot=(e,t)=>at(e,`name`,{value:t,configurable:!0});function st(e){let t=C.useRef(e);return C.useEffect(()=>{t.current=e}),C.useMemo(()=>((...e)=>t.current?.(...e)),[])}ot(st,`useCallbackRef`);var ct=Object.defineProperty,F=(e,t)=>ct(e,`name`,{value:t,configurable:!0}),lt=`dismissableLayer.update`,ut=`dismissableLayer.pointerDownOutside`,dt=`dismissableLayer.focusOutside`,ft,pt=C.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set,dismissableSurfaces:new Set}),mt=C.forwardRef(F(function(e,t){let{disableOutsidePointerEvents:n=!1,deferPointerDownOutside:r=!1,onEscapeKeyDown:i,onPointerDownOutside:a,onFocusOutside:o,onInteractOutside:s,onDismiss:c,...l}=e,u=C.useContext(pt),[d,f]=C.useState(null),p=d?.ownerDocument??globalThis?.document,[,m]=C.useState({}),h=k(t,f),g=Array.from(u.layers),[_]=[...u.layersWithOutsidePointerEventsDisabled].slice(-1),ee=_?g.indexOf(_):-1,te=d?g.indexOf(d):-1,ne=u.layersWithOutsidePointerEventsDisabled.size>0,re=te>=ee,v=C.useRef(!1),y=_t(e=>{a?.(e),s?.(e),e.defaultPrevented||c?.()},{ownerDocument:p,deferPointerDownOutside:r,isDeferredPointerDownOutsideRef:v,dismissableSurfaces:u.dismissableSurfaces,shouldHandlePointerDownOutside:C.useCallback(e=>{if(!(e instanceof Node))return!1;let t=[...u.branches].some(t=>t.contains(e));return re&&!t},[u.branches,re])}),b=vt(e=>{if(r&&v.current)return;let t=e.target;[...u.branches].some(e=>e.contains(t))||(o?.(e),s?.(e),e.defaultPrevented||c?.())},p),x=d?te===g.length-1:!1,ie=st(e=>{e.key===`Escape`&&(i?.(e),!e.defaultPrevented&&c&&(e.preventDefault(),c()))});return C.useEffect(()=>{if(x)return p.addEventListener(`keydown`,ie,{capture:!0}),()=>p.removeEventListener(`keydown`,ie,{capture:!0})},[p,x,ie]),C.useEffect(()=>{if(d)return n&&(u.layersWithOutsidePointerEventsDisabled.size===0&&(ft=p.body.style.pointerEvents,p.body.style.pointerEvents=`none`),u.layersWithOutsidePointerEventsDisabled.add(d)),u.layers.add(d),yt(),()=>{n&&(u.layersWithOutsidePointerEventsDisabled.delete(d),u.layersWithOutsidePointerEventsDisabled.size===0&&(p.body.style.pointerEvents=ft))}},[d,p,n,u]),C.useEffect(()=>()=>{d&&(u.layers.delete(d),u.layersWithOutsidePointerEventsDisabled.delete(d),yt())},[d,u]),C.useEffect(()=>{let e=F(()=>m({}),`handleUpdate`);return document.addEventListener(lt,e),()=>document.removeEventListener(lt,e)},[]),(0,A.jsx)(P.div,{...l,ref:h,style:{pointerEvents:ne?re?`auto`:`none`:void 0,...e.style},onFocusCapture:T(e.onFocusCapture,b.onFocusCapture),onBlurCapture:T(e.onBlurCapture,b.onBlurCapture),onPointerDownCapture:T(e.onPointerDownCapture,y.onPointerDownCapture)})},`DismissableLayer`));function ht(){let e=C.useContext(pt),[t,n]=C.useState(null);return C.useEffect(()=>{if(t)return e.dismissableSurfaces.add(t),()=>{e.dismissableSurfaces.delete(t)}},[t,e.dismissableSurfaces]),n}F(ht,`useDismissableLayerSurface`);var gt=F(()=>!0,`IS_TRUE`);function _t(e,t){let{ownerDocument:n=globalThis?.document,deferPointerDownOutside:r=!1,isDeferredPointerDownOutsideRef:i,dismissableSurfaces:a,shouldHandlePointerDownOutside:o=gt}=t,s=st(e),c=C.useRef(!1),l=C.useRef(!1),u=C.useRef(new Map),d=C.useRef(()=>{});return C.useEffect(()=>{function e(){l.current=!1,i.current=!1,u.current.clear()}F(e,`resetOutsideInteraction`);function t(){return Array.from(u.current.values()).some(Boolean)}F(t,`isOutsideInteractionIntercepted`);function f(e){if(!l.current)return;let t=e.target;t instanceof Node&&[...a].some(e=>e.contains(t))||u.current.set(e.type,!0),e.type===`click`&&window.setTimeout(()=>{l.current&&d.current()},0)}F(f,`handleInteractionCapture`);function p(e){l.current&&u.current.set(e.type,!1)}F(p,`handleInteractionBubble`);let m=F(a=>{if(a.target&&!c.current){let f=function(){n.removeEventListener(`click`,d.current);let r=t();e(),r||bt(ut,s,p,{discrete:!0})};if(F(f,`handleAndDispatchPointerDownOutsideEvent`),!o(a.target)){n.removeEventListener(`click`,d.current),e(),c.current=!1;return}let p={originalEvent:a};l.current=!0,i.current=r&&a.button===0,u.current.clear(),!r||a.button!==0?f():(n.removeEventListener(`click`,d.current),d.current=f,n.addEventListener(`click`,d.current,{once:!0}))}else n.removeEventListener(`click`,d.current),e();c.current=!1},`handlePointerDown`),h=[`pointerup`,`mousedown`,`mouseup`,`touchstart`,`touchend`,`click`];for(let e of h)n.addEventListener(e,f,!0),n.addEventListener(e,p);let g=window.setTimeout(()=>{n.addEventListener(`pointerdown`,m)},0);return()=>{window.clearTimeout(g),n.removeEventListener(`pointerdown`,m),n.removeEventListener(`click`,d.current);for(let e of h)n.removeEventListener(e,f,!0),n.removeEventListener(e,p)}},[n,s,r,i,a,o]),{onPointerDownCapture:F(()=>c.current=!0,`onPointerDownCapture`)}}F(_t,`usePointerDownOutside`);function vt(e,t=globalThis?.document){let n=st(e),r=C.useRef(!1);return C.useEffect(()=>{let e=F(e=>{e.target&&!r.current&&bt(dt,n,{originalEvent:e},{discrete:!1})},`handleFocus`);return t.addEventListener(`focusin`,e),()=>t.removeEventListener(`focusin`,e)},[t,n]),{onFocusCapture:F(()=>r.current=!0,`onFocusCapture`),onBlurCapture:F(()=>r.current=!1,`onBlurCapture`)}}F(vt,`useFocusOutside`);function yt(){let e=new CustomEvent(lt);document.dispatchEvent(e)}F(yt,`dispatchUpdate`);function bt(e,t,n,{discrete:r}){let i=n.originalEvent.target,a=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:n});t&&i.addEventListener(e,t,{once:!0}),r?it(i,a):i.dispatchEvent(a)}F(bt,`handleAndDispatchCustomEvent`);var xt=Object.defineProperty,I=(e,t)=>xt(e,`name`,{value:t,configurable:!0}),St=`focusScope.autoFocusOnMount`,Ct=`focusScope.autoFocusOnUnmount`,wt={bubbles:!1,cancelable:!0},Tt=C.forwardRef(I(function(e,t){let{loop:n=!1,trapped:r=!1,onMountAutoFocus:i,onUnmountAutoFocus:a,...o}=e,[s,c]=C.useState(null),l=st(i),u=st(a),d=C.useRef(null),f=k(t,c),p=C.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;C.useEffect(()=>{if(r){let e=function(e){if(p.paused||!s)return;let t=e.target;s.contains(t)?d.current=t:L(d.current,{select:!0})},t=function(e){if(p.paused||!s)return;let t=e.relatedTarget;t!==null&&(s.contains(t)||L(d.current,{select:!0}))},n=function(e){if(document.activeElement===document.body)for(let t of e)t.removedNodes.length>0&&L(s)};I(e,`handleFocusIn`),I(t,`handleFocusOut`),I(n,`handleMutations`),document.addEventListener(`focusin`,e),document.addEventListener(`focusout`,t);let r=new MutationObserver(n);return s&&r.observe(s,{childList:!0,subtree:!0}),()=>{document.removeEventListener(`focusin`,e),document.removeEventListener(`focusout`,t),r.disconnect()}}},[r,s,p.paused]),C.useEffect(()=>{if(s){Mt.add(p);let e=document.activeElement;if(!s.contains(e)){let t=new CustomEvent(St,wt);s.addEventListener(St,l),s.dispatchEvent(t),t.defaultPrevented||(Et(Ft(Ot(s)),{select:!0}),document.activeElement===e&&L(s))}return()=>{s.removeEventListener(St,l),setTimeout(()=>{let t=new CustomEvent(Ct,wt);s.addEventListener(Ct,u),s.dispatchEvent(t),t.defaultPrevented||L(e??document.body,{select:!0}),s.removeEventListener(Ct,u),Mt.remove(p)},0)}}},[s,l,u,p]);let m=C.useCallback(e=>{if(!n&&!r||p.paused)return;let t=e.key===`Tab`&&!e.altKey&&!e.ctrlKey&&!e.metaKey,i=document.activeElement;if(t&&i){let t=e.currentTarget,[r,a]=Dt(t);r&&a?!e.shiftKey&&i===a?(e.preventDefault(),n&&L(r,{select:!0})):e.shiftKey&&i===r&&(e.preventDefault(),n&&L(a,{select:!0})):i===t&&e.preventDefault()}},[n,r,p.paused]);return(0,A.jsx)(P.div,{tabIndex:-1,...o,ref:f,onKeyDown:m})},`FocusScope`));function Et(e,{select:t=!1}={}){let n=document.activeElement;for(let r of e)if(L(r,{select:t}),document.activeElement!==n)return}I(Et,`focusFirst`);function Dt(e){let t=Ot(e);return[kt(t,e),kt(t.reverse(),e)]}I(Dt,`getTabbableEdges`);function Ot(e){let t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:I(e=>{let t=e.tagName===`INPUT`&&e.type===`hidden`;return e.disabled||e.hidden||t?NodeFilter.FILTER_SKIP:e.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP},`acceptNode`)});for(;n.nextNode();)t.push(n.currentNode);return t}I(Ot,`getTabbableCandidates`);function kt(e,t){let n=typeof t.checkVisibility==`function`&&t.checkVisibility({checkVisibilityCSS:!0});for(let r of e)if(!(n?!r.checkVisibility({checkVisibilityCSS:!0}):At(r,{upTo:t})))return r}I(kt,`findVisible`);function At(e,{upTo:t}){if(getComputedStyle(e).visibility===`hidden`)return!0;for(;e;){if(t!==void 0&&e===t)return!1;if(getComputedStyle(e).display===`none`)return!0;e=e.parentElement}return!1}I(At,`isHidden`);function jt(e){return e instanceof HTMLInputElement&&`select`in e}I(jt,`isSelectableInput`);function L(e,{select:t=!1}={}){if(e&&e.focus){let n=document.activeElement;e.focus({preventScroll:!0}),e!==n&&jt(e)&&t&&e.select()}}I(L,`focus`);var Mt=Nt();function Nt(){let e=[];return{add(t){let n=e[0];t!==n&&n?.pause(),e=Pt(e,t),e.unshift(t)},remove(t){e=Pt(e,t),e[0]?.resume()}}}I(Nt,`createFocusScopesStack`);function Pt(e,t){let n=[...e],r=n.indexOf(t);return r!==-1&&n.splice(r,1),n}I(Pt,`arrayRemove`);function Ft(e){return e.filter(e=>e.tagName!==`A`)}I(Ft,`removeLinks`);var It=Object.defineProperty,Lt=C.forwardRef(((e,t)=>It(e,`name`,{value:t,configurable:!0}))(function(e,t){let{container:n,...r}=e,[i,a]=C.useState(!1);M(()=>a(!0),[]);let o=n||i&&globalThis?.document?.body;return o?Be.createPortal((0,A.jsx)(P.div,{...r,ref:t}),o):null},`Portal`)),Rt=Object.defineProperty,R=(e,t)=>Rt(e,`name`,{value:t,configurable:!0});function zt(e,t){return C.useReducer((e,n)=>t[e][n]??e,e)}R(zt,`useStateMachine`);var Bt=R(e=>{let{present:t,children:n}=e,r=Vt(t),i=typeof n==`function`?n({present:r.isPresent}):C.Children.only(n),a=Ut(r.ref,Gt(i));return typeof n==`function`||r.isPresent?C.cloneElement(i,{ref:a}):null},`Presence`);function Vt(e){let[t,n]=C.useState(),r=C.useRef(null),i=C.useRef(e),a=C.useRef(`none`),o=C.useRef(void 0),[s,c]=zt(e?`mounted`:`unmounted`,{mounted:{UNMOUNT:`unmounted`,ANIMATION_OUT:`unmountSuspended`},unmountSuspended:{MOUNT:`mounted`,ANIMATION_END:`unmounted`},unmounted:{MOUNT:`mounted`}});return C.useEffect(()=>{s===`mounted`?(a.current=o.current??Wt(r.current),o.current=void 0):a.current=`none`},[s]),M(()=>{let t=r.current,n=i.current;if(n!==e){let r=a.current,s=Wt(t);e?(o.current=s,c(`MOUNT`)):s===`none`||t?.display===`none`?c(`UNMOUNT`):c(n&&r!==s?`ANIMATION_OUT`:`UNMOUNT`),i.current=e}},[e,c]),M(()=>{if(t){let e,n=t.ownerDocument.defaultView??window,o=R(a=>{let o=Wt(r.current).includes(CSS.escape(a.animationName));if(a.target===t&&o&&(c(`ANIMATION_END`),!i.current)){let r=t.style.animationFillMode;t.style.animationFillMode=`forwards`,e=n.setTimeout(()=>{t.style.animationFillMode===`forwards`&&(t.style.animationFillMode=r)})}},`handleAnimationEnd`),s=R(e=>{e.target===t&&(a.current=Wt(r.current))},`handleAnimationStart`);return t.addEventListener(`animationstart`,s),t.addEventListener(`animationcancel`,o),t.addEventListener(`animationend`,o),()=>{n.clearTimeout(e),t.removeEventListener(`animationstart`,s),t.removeEventListener(`animationcancel`,o),t.removeEventListener(`animationend`,o)}}c(`ANIMATION_END`)},[t,c]),{isPresent:[`mounted`,`unmountSuspended`].includes(s),ref:C.useCallback(e=>{if(e){let t=getComputedStyle(e);r.current=t,o.current=Wt(t)}else r.current=null;n(e)},[])}}R(Vt,`usePresence`);function Ht(e,t){if(typeof e==`function`)return e(t);e!=null&&(e.current=t)}R(Ht,`setRef`);function Ut(...e){let t=C.useRef(e);return t.current=e,C.useCallback(e=>{let n=t.current,r=!1,i=n.map(t=>{let n=Ht(t,e);return!r&&typeof n==`function`&&(r=!0),n});if(r)return()=>{for(let e=0;eKt(e,`name`,{value:t,configurable:!0}),Jt=0,Yt=null;function Xt(e){return Zt(),e.children}qt(Xt,`FocusGuards`);function Zt(){C.useEffect(()=>{Yt||={start:Qt(),end:Qt()};let{start:e,end:t}=Yt;return document.body.firstElementChild!==e&&document.body.insertAdjacentElement(`afterbegin`,e),document.body.lastElementChild!==t&&document.body.insertAdjacentElement(`beforeend`,t),Jt++,()=>{Jt===1&&(Yt?.start.remove(),Yt?.end.remove(),Yt=null),Jt=Math.max(0,Jt-1)}},[])}qt(Zt,`useFocusGuards`);function Qt(){let e=document.createElement(`span`);return e.setAttribute(`data-radix-focus-guard`,``),e.tabIndex=0,e.style.outline=`none`,e.style.opacity=`0`,e.style.position=`fixed`,e.style.pointerEvents=`none`,e}qt(Qt,`createFocusGuard`);var z=function(){return z=Object.assign||function(e){for(var t,n=1,r=arguments.length;n`u`)return Dn;var t=kn(e),n=document.documentElement.clientWidth,r=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,r-n+t[2]-t[0])}},jn=En(),Mn=`data-scroll-locked`,Nn=function(e,t,n,r){var i=e.left,a=e.top,o=e.right,s=e.gap;return n===void 0&&(n=`margin`),` .${rn} { overflow: hidden ${r}; @@ -39,4 +39,4 @@ import{r as e}from"./rolldown-runtime-hePW80VL.js";import{A as t,B as n,C as r,D `},Pn=function(){var e=parseInt(document.body.getAttribute(`data-scroll-locked`)||`0`,10);return isFinite(e)?e:0},Fn=function(){C.useEffect(function(){return document.body.setAttribute(Mn,(Pn()+1).toString()),function(){var e=Pn()-1;e<=0?document.body.removeAttribute(Mn):document.body.setAttribute(Mn,e.toString())}},[])},In=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,i=r===void 0?`margin`:r;Fn();var a=C.useMemo(function(){return An(i)},[i]);return C.createElement(jn,{styles:Nn(a,!t,i,n?``:`!important`)})},Ln=!1;if(typeof window<`u`)try{var Rn=Object.defineProperty({},"passive",{get:function(){return Ln=!0,!0}});window.addEventListener(`test`,Rn,Rn),window.removeEventListener(`test`,Rn,Rn)}catch{Ln=!1}var zn=Ln?{passive:!1}:!1,Bn=function(e){return e.tagName===`TEXTAREA`},Vn=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return n[t]!==`hidden`&&!(n.overflowY===n.overflowX&&!Bn(e)&&n[t]===`visible`)},Hn=function(e){return Vn(e,`overflowY`)},Un=function(e){return Vn(e,`overflowX`)},Wn=function(e,t){var n=t.ownerDocument,r=t;do{if(typeof ShadowRoot<`u`&&r instanceof ShadowRoot&&(r=r.host),qn(e,r)){var i=Jn(e,r);if(i[1]>i[2])return!0}r=r.parentNode}while(r&&r!==n.body);return!1},Gn=function(e){return[e.scrollTop,e.scrollHeight,e.clientHeight]},Kn=function(e){return[e.scrollLeft,e.scrollWidth,e.clientWidth]},qn=function(e,t){return e===`v`?Hn(t):Un(t)},Jn=function(e,t){return e===`v`?Gn(t):Kn(t)},Yn=function(e,t){return e===`h`&&t===`rtl`?-1:1},Xn=function(e,t,n,r,i){var a=Yn(e,window.getComputedStyle(t).direction),o=a*r,s=n.target,c=t.contains(s),l=!1,u=o>0,d=0,f=0;do{if(!s)break;var p=Jn(e,s),m=p[0],h=p[1]-p[2]-a*m;(m||h)&&qn(e,s)&&(d+=h,f+=m);var g=s.parentNode;s=g&&g.nodeType===Node.DOCUMENT_FRAGMENT_NODE?g.host:g}while(!c&&s!==document.body||c&&(t.contains(s)||t===s));return(u&&(i&&Math.abs(d)<1||!i&&o>d)||!u&&(i&&Math.abs(f)<1||!i&&-o>f))&&(l=!0),l},Zn=function(e){return`changedTouches`in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},Qn=function(e){return[e.deltaX,e.deltaY]},$n=function(e){return e&&`current`in e?e.current:e},er=function(e,t){return e[0]===t[0]&&e[1]===t[1]},tr=function(e){return` .block-interactivity-${e} {pointer-events: none;} .allow-interactivity-${e} {pointer-events: all;} -`},nr=0,rr=[];function ir(e){var t=C.useRef([]),n=C.useRef([0,0]),r=C.useRef(),i=C.useState(nr++)[0],a=C.useState(En)[0],o=C.useRef(e);C.useEffect(function(){o.current=e},[e]),C.useEffect(function(){if(e.inert){document.body.classList.add(`block-interactivity-${i}`);var t=en([e.lockRef.current],(e.shards||[]).map($n),!0).filter(Boolean);return t.forEach(function(e){return e.classList.add(`allow-interactivity-${i}`)}),function(){document.body.classList.remove(`block-interactivity-${i}`),t.forEach(function(e){return e.classList.remove(`allow-interactivity-${i}`)})}}},[e.inert,e.lockRef.current,e.shards]);var s=C.useCallback(function(e,t){if(`touches`in e&&e.touches.length===2||e.type===`wheel`&&e.ctrlKey)return!o.current.allowPinchZoom;var i=Zn(e),a=n.current,s=`deltaX`in e?e.deltaX:a[0]-i[0],c=`deltaY`in e?e.deltaY:a[1]-i[1],l,u=e.target,d=Math.abs(s)>Math.abs(c)?`h`:`v`;if(`touches`in e&&d===`h`&&u.type===`range`)return!1;var f=window.getSelection(),p=f&&f.anchorNode;if(p&&(p===u||p.contains(u)))return!1;var m=Wn(d,u);if(!m)return!0;if(m?l=d:(l=d===`v`?`h`:`v`,m=Wn(d,u)),!m)return!1;if(!r.current&&`changedTouches`in e&&(s||c)&&(r.current=l),!l)return!0;var h=r.current||l;return Xn(h,t,e,h===`h`?s:c,!0)},[]),c=C.useCallback(function(e){var n=e;if(!(!rr.length||rr[rr.length-1]!==a)){var r=`deltaY`in n?Qn(n):Zn(n),i=t.current.filter(function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&er(e.delta,r)})[0];if(i&&i.should){n.cancelable&&n.preventDefault();return}if(!i){var c=(o.current.shards||[]).map($n).filter(Boolean).filter(function(e){return e.contains(n.target)});(c.length>0?s(n,c[0]):!o.current.noIsolation)&&n.cancelable&&n.preventDefault()}}},[]),l=C.useCallback(function(e,n,r,i){var a={name:e,delta:n,target:r,should:i,shadowParent:ar(r)};t.current.push(a),setTimeout(function(){t.current=t.current.filter(function(e){return e!==a})},1)},[]),u=C.useCallback(function(e){n.current=Zn(e),r.current=void 0},[]),d=C.useCallback(function(t){l(t.type,Qn(t),t.target,s(t,e.lockRef.current))},[]),f=C.useCallback(function(t){l(t.type,Zn(t),t.target,s(t,e.lockRef.current))},[]);C.useEffect(function(){return rr.push(a),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener(`wheel`,c,zn),document.addEventListener(`touchmove`,c,zn),document.addEventListener(`touchstart`,u,zn),function(){rr=rr.filter(function(e){return e!==a}),document.removeEventListener(`wheel`,c,zn),document.removeEventListener(`touchmove`,c,zn),document.removeEventListener(`touchstart`,u,zn)}},[]);var p=e.removeScrollBar,m=e.inert;return C.createElement(C.Fragment,null,m?C.createElement(a,{styles:tr(i)}):null,p?C.createElement(In,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function ar(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}var or=hn(gn,ir),sr=C.forwardRef(function(e,t){return C.createElement(vn,z({},e,{ref:t,sideCar:or}))});sr.classNames=vn.classNames;var cr=function(e){return typeof document>`u`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},lr=new WeakMap,ur=new WeakMap,dr={},fr=0,pr=function(e){return e&&(e.host||pr(e.parentNode))},mr=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=pr(t);return n&&e.contains(n)?n:(console.error(`aria-hidden`,t,`in not contained inside`,e,`. Doing nothing`),null)}).filter(function(e){return!!e})},hr=function(e,t,n,r){var i=mr(t,Array.isArray(e)?e:[e]);dr[n]||(dr[n]=new WeakMap);var a=dr[n],o=[],s=new Set,c=new Set(i),l=function(e){!e||s.has(e)||(s.add(e),l(e.parentNode))};i.forEach(l);var u=function(e){!e||c.has(e)||Array.prototype.forEach.call(e.children,function(e){if(s.has(e))u(e);else try{var t=e.getAttribute(r),i=t!==null&&t!==`false`,c=(lr.get(e)||0)+1,l=(a.get(e)||0)+1;lr.set(e,c),a.set(e,l),o.push(e),c===1&&i&&ur.set(e,!0),l===1&&e.setAttribute(n,`true`),i||e.setAttribute(r,`true`)}catch(t){console.error(`aria-hidden: cannot operate on `,e,t)}})};return u(t),s.clear(),fr++,function(){o.forEach(function(e){var t=lr.get(e)-1,i=a.get(e)-1;lr.set(e,t),a.set(e,i),t||(ur.has(e)||e.removeAttribute(r),ur.delete(e)),i||e.removeAttribute(n)}),fr--,fr||(lr=new WeakMap,lr=new WeakMap,ur=new WeakMap,dr={})}},gr=function(e,t,n){n===void 0&&(n=`data-aria-hidden`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||cr(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(`[aria-live], script`))),hr(r,i,n,`aria-hidden`)):function(){return null}},_r=Object.defineProperty,B=(e,t)=>_r(e,`name`,{value:t,configurable:!0}),vr=`Dialog`,[yr,br]=be(vr),[xr,V]=yr(vr),Sr=B(e=>{let{__scopeDialog:t,children:n,open:r,defaultOpen:i,onOpenChange:a,modal:o=!0}=e,s=C.useRef(null),c=C.useRef(null),[l,u]=Fe({prop:r,defaultProp:i??!1,onChange:a,caller:vr}),[d,f]=C.useState(0),[p,m]=C.useState(0);return(0,A.jsx)(xr,{scope:t,triggerRef:s,contentRef:c,contentId:Ee(),titleId:Ee(),descriptionId:Ee(),titlePresent:d>0,descriptionPresent:p>0,setTitleCount:f,setDescriptionCount:m,open:l,onOpenChange:u,onOpenToggle:C.useCallback(()=>u(e=>!e),[u]),modal:o,children:n})},`Dialog`),Cr=`DialogTrigger`,wr=C.forwardRef(B(function(e,t){let{__scopeDialog:n,...r}=e,i=V(Cr,n),a=k(t,i.triggerRef);return(0,A.jsx)(P.button,{type:`button`,"aria-haspopup":`dialog`,"aria-expanded":i.open,"aria-controls":i.open?i.contentId:void 0,"data-state":Wr(i.open),...r,ref:a,onClick:T(e.onClick,i.onOpenToggle)})},`DialogTrigger`)),Tr=`DialogPortal`,[Er,Dr]=yr(Tr,{forceMount:void 0}),Or=B(e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=V(Tr,t);return(0,A.jsx)(Er,{scope:t,forceMount:n,children:C.Children.map(r,e=>(0,A.jsx)(Bt,{present:n||a.open,children:(0,A.jsx)(Lt,{asChild:!0,container:i,children:e})}))})},`DialogPortal`),kr=`DialogOverlay`,Ar=C.forwardRef(B(function(e,t){let n=Dr(kr,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=V(kr,e.__scopeDialog);return a.modal?(0,A.jsx)(Bt,{present:r||a.open,children:(0,A.jsx)(Mr,{...i,ref:t})}):null},`DialogOverlay`)),jr=He(`DialogOverlay.RemoveScroll`),Mr=C.forwardRef(B(function(e,t){let{__scopeDialog:n,...r}=e,i=V(kr,n),a=k(t,ht());return(0,A.jsx)(sr,{as:jr,allowPinchZoom:!0,shards:[i.contentRef],children:(0,A.jsx)(P.div,{"data-state":Wr(i.open),...r,ref:a,style:{pointerEvents:`auto`,...r.style}})})},`DialogOverlayImpl`)),Nr=`DialogContent`,Pr=C.forwardRef(B(function(e,t){let n=Dr(Nr,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=V(Nr,e.__scopeDialog);return(0,A.jsx)(Bt,{present:r||a.open,children:a.modal?(0,A.jsx)(Fr,{...i,ref:t}):(0,A.jsx)(Ir,{...i,ref:t})})},`DialogContent`)),Fr=C.forwardRef(B(function(e,t){let n=V(Nr,e.__scopeDialog),r=C.useRef(null),i=k(t,n.contentRef,r);return C.useEffect(()=>{let e=r.current;if(e)return gr(e)},[]),(0,A.jsx)(Lr,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:T(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:T(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:T(e.onFocusOutside,e=>e.preventDefault())})},`DialogContentModal`)),Ir=C.forwardRef(B(function(e,t){let n=V(Nr,e.__scopeDialog),r=C.useRef(!1),i=C.useRef(!1);return(0,A.jsx)(Lr,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===`pointerdown`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===`focusin`&&i.current&&t.preventDefault()}})},`DialogContentNonModal`)),Lr=C.forwardRef(B(function(e,t){let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,s=V(Nr,n);return Zt(),(0,A.jsx)(A.Fragment,{children:(0,A.jsx)(Tt,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,A.jsx)(mt,{role:`dialog`,id:s.contentId,"aria-describedby":s.descriptionPresent?s.descriptionId:void 0,"aria-labelledby":s.titlePresent?s.titleId:void 0,"data-state":Wr(s.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>s.onOpenChange(!1)})})})},`DialogContentImpl`)),Rr=`DialogTitle`,zr=C.forwardRef(B(function(e,t){let{__scopeDialog:n,...r}=e,i=V(Rr,n),{setTitleCount:a}=i;return M(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,A.jsx)(P.h2,{id:i.titleId,...r,ref:t})},`DialogTitle`)),Br=`DialogDescription`,Vr=C.forwardRef(B(function(e,t){let{__scopeDialog:n,...r}=e,i=V(Br,n),{setDescriptionCount:a}=i;return M(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,A.jsx)(P.p,{id:i.descriptionId,...r,ref:t})},`DialogDescription`)),Hr=`DialogClose`,Ur=C.forwardRef(B(function(e,t){let{__scopeDialog:n,...r}=e,i=V(Hr,n);return(0,A.jsx)(P.button,{type:`button`,...r,ref:t,onClick:T(e.onClick,()=>i.onOpenChange(!1))})},`DialogClose`));function Wr(e){return e?`open`:`closed`}B(Wr,`getState`);var Gr=(...e)=>e.filter((e,t,n)=>!!e&&e.trim()!==``&&n.indexOf(e)===t).join(` `).trim(),Kr=e=>e.replace(/([a-z0-9])([A-Z])/g,`$1-$2`).toLowerCase(),qr=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,n)=>n?n.toUpperCase():t.toLowerCase()),Jr=e=>{let t=qr(e);return t.charAt(0).toUpperCase()+t.slice(1)},Yr={xmlns:`http://www.w3.org/2000/svg`,width:24,height:24,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:2,strokeLinecap:`round`,strokeLinejoin:`round`},Xr=e=>{for(let t in e)if(t.startsWith(`aria-`)||t===`role`||t===`title`)return!0;return!1},Zr=(0,C.createContext)({}),Qr=()=>(0,C.useContext)(Zr),$r=(0,C.forwardRef)(({color:e,size:t,strokeWidth:n,absoluteStrokeWidth:r,className:i=``,children:a,iconNode:o,...s},c)=>{let{size:l=24,strokeWidth:u=2,absoluteStrokeWidth:d=!1,color:f=`currentColor`,className:p=``}=Qr()??{},m=r??d?Number(n??u)*24/Number(t??l):n??u;return(0,C.createElement)(`svg`,{ref:c,...Yr,width:t??l??Yr.width,height:t??l??Yr.height,stroke:e??f,strokeWidth:m,className:Gr(`lucide`,p,i),...!a&&!Xr(s)&&{"aria-hidden":`true`},...s},[...o.map(([e,t])=>(0,C.createElement)(e,t)),...Array.isArray(a)?a:[a]])}),H=(e,t)=>{let n=(0,C.forwardRef)(({className:n,...r},i)=>(0,C.createElement)($r,{ref:i,iconNode:t,className:Gr(`lucide-${Kr(Jr(e))}`,`lucide-${e}`,n),...r}));return n.displayName=Jr(e),n},ei=H(`activity`,[[`path`,{d:`M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2`,key:`169zse`}]]),ti=H(`chart-column`,[[`path`,{d:`M3 3v16a2 2 0 0 0 2 2h16`,key:`c24i48`}],[`path`,{d:`M18 17V9`,key:`2bz60n`}],[`path`,{d:`M13 17V5`,key:`1frdt8`}],[`path`,{d:`M8 17v-3`,key:`17ska0`}]]),ni=H(`circle-alert`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`line`,{x1:`12`,x2:`12`,y1:`8`,y2:`12`,key:`1pkeuh`}],[`line`,{x1:`12`,x2:`12.01`,y1:`16`,y2:`16`,key:`4dfq90`}]]),ri=H(`circle-check`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]),ii=H(`columns-3`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M9 3v18`,key:`fh3hqa`}],[`path`,{d:`M15 3v18`,key:`14nvp0`}]]),ai=H(`gauge`,[[`path`,{d:`m12 14 4-4`,key:`9kzdfg`}],[`path`,{d:`M3.34 19a10 10 0 1 1 17.32 0`,key:`19p75a`}]]),oi=H(`inbox`,[[`polyline`,{points:`22 12 16 12 14 15 10 15 8 12 2 12`,key:`o97t9d`}],[`path`,{d:`M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z`,key:`oot6mr`}]]),si=H(`info`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 16v-4`,key:`1dtifu`}],[`path`,{d:`M12 8h.01`,key:`e9boi3`}]]),ci=H(`list-checks`,[[`path`,{d:`M13 5h8`,key:`a7qcls`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}],[`path`,{d:`m3 17 2 2 4-4`,key:`1jhpwq`}],[`path`,{d:`m3 7 2 2 4-4`,key:`1obspn`}]]),li=H(`loader-circle`,[[`path`,{d:`M21 12a9 9 0 1 1-6.219-8.56`,key:`13zald`}]]),ui=H(`menu`,[[`path`,{d:`M4 5h16`,key:`1tepv9`}],[`path`,{d:`M4 12h16`,key:`1lakjw`}],[`path`,{d:`M4 19h16`,key:`1djgab`}]]),di=H(`moon`,[[`path`,{d:`M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401`,key:`kfwtm`}]]),fi=H(`network`,[[`rect`,{x:`16`,y:`16`,width:`6`,height:`6`,rx:`1`,key:`4q2zg0`}],[`rect`,{x:`2`,y:`16`,width:`6`,height:`6`,rx:`1`,key:`8cvhb9`}],[`rect`,{x:`9`,y:`2`,width:`6`,height:`6`,rx:`1`,key:`1egb70`}],[`path`,{d:`M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3`,key:`1jsf9p`}],[`path`,{d:`M12 12V8`,key:`2874zd`}]]),pi=H(`refresh-cw`,[[`path`,{d:`M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8`,key:`v9h5vc`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}],[`path`,{d:`M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16`,key:`3uifl3`}],[`path`,{d:`M8 16H3v5`,key:`1cv678`}]]),mi=H(`settings`,[[`path`,{d:`M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915`,key:`1i5ecw`}],[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}]]),hi=H(`sun`,[[`circle`,{cx:`12`,cy:`12`,r:`4`,key:`4exip2`}],[`path`,{d:`M12 2v2`,key:`tus03m`}],[`path`,{d:`M12 20v2`,key:`1lh1kg`}],[`path`,{d:`m4.93 4.93 1.41 1.41`,key:`149t6j`}],[`path`,{d:`m17.66 17.66 1.41 1.41`,key:`ptbguv`}],[`path`,{d:`M2 12h2`,key:`1t8f8n`}],[`path`,{d:`M20 12h2`,key:`1q8mjw`}],[`path`,{d:`m6.34 17.66-1.41 1.41`,key:`1m8zz5`}],[`path`,{d:`m19.07 4.93-1.41 1.41`,key:`1shlcs`}]]),gi=H(`triangle-alert`,[[`path`,{d:`m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3`,key:`wmoenq`}],[`path`,{d:`M12 9v4`,key:`juzpu7`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]),_i=H(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]),vi=`/api/v1`,yi=class extends Error{code;status;requestId;details;constructor(e,t,n=`REQUEST_FAILED`,r,i){super(e),this.name=`ApiError`,this.code=n,this.status=t,r!==void 0&&(this.requestId=r),i!==void 0&&(this.details=i)}};async function bi(e){try{let t=await e.json();if(t.error?.message)return new yi(t.error.message,e.status,t.error.code,t.error.requestId,t.error.details)}catch{}return new yi(e.statusText||`The request could not be completed.`,e.status)}async function U(e,t={}){let n=new Headers(t.headers);n.set(`Accept`,`application/json`),t.body!==void 0&&!(t.body instanceof FormData)&&n.set(`Content-Type`,`application/json`);let r=await fetch(`${vi}${e}`,{...t,headers:n,credentials:`same-origin`});if(!r.ok)throw await bi(r);if(r.status!==204)return await r.json()}function xi(e){let t=new URLSearchParams;Object.entries(e).forEach(([e,n])=>{n==null||n===``||Array.isArray(n)&&n.length===0||(Array.isArray(n)?n.forEach(n=>t.append(e,n)):t.set(e,String(n)))});let n=t.toString();return n?`?${n}`:``}function Si(e){return Array.isArray(e)?e:`items`in e&&Array.isArray(e.items)?e.items:`data`in e&&Array.isArray(e.data)?e.data:[]}async function Ci(e={}){let{interfaceName:t,...n}=e,r=await U(`/results${xi({...n,interface:t})}`);return Array.isArray(r)?{items:r,page:1,pageSize:r.length,totalItems:r.length,totalPages:1}:r}var wi={health:()=>U(`/healthz`),tasks:async()=>Si(await U(`/tasks`)),task:e=>U(`/tasks/${encodeURIComponent(e)}`),createTask:e=>U(`/tasks`,{method:`POST`,body:JSON.stringify(e)}),updateTask:(e,t)=>U(`/tasks/${encodeURIComponent(e)}`,{method:`PUT`,body:JSON.stringify(t)}),deleteTask:e=>U(`/tasks/${encodeURIComponent(e)}`,{method:`DELETE`}),runTask:e=>U(`/tasks/${encodeURIComponent(e)}/run`,{method:`POST`}),validateTask:e=>ki(`/tasks/${encodeURIComponent(e)}/validate`),validateTaskInput:e=>ki(`/tasks/validate`,e),duplicateTask:e=>U(`/tasks/${encodeURIComponent(e)}/duplicate`,{method:`POST`}),nextRuns:async e=>{let t=await U(`/tasks/${encodeURIComponent(e)}/next-runs`);return Array.isArray(t)?t:t.nextRuns??t.items??[]},results:Ci,dashboardSummary:()=>U(`/results/dashboard-summary`),result:e=>U(`/results/${encodeURIComponent(e)}`),deleteResult:e=>U(`/results/${encodeURIComponent(e)}`,{method:`DELETE`}),deleteResults:e=>U(`/results/delete-batch`,{method:`POST`,body:JSON.stringify({ids:e})}),statistics:e=>{let{interfaceName:t,...n}=e;return U(`/statistics${xi({...n,metric:void 0,interface:t})}`).then(Ei)},interfaces:async e=>Si(await U(`/interfaces${xi(e??{})}`)),refreshInterfaces:()=>U(`/interfaces/refresh`,{method:`POST`}),routes:async()=>Si(await U(`/route-profiles`)),route:e=>U(`/route-profiles/${encodeURIComponent(e)}`),createRoute:e=>U(`/route-profiles`,{method:`POST`,body:JSON.stringify(e)}),updateRoute:(e,t)=>U(`/route-profiles/${encodeURIComponent(e)}`,{method:`PUT`,body:JSON.stringify(t)}),deleteRoute:e=>U(`/route-profiles/${encodeURIComponent(e)}`,{method:`DELETE`}),validateRoute:e=>ki(`/route-profiles/${encodeURIComponent(e)}/validate`),providers:async()=>Si(await U(`/providers`)).map(Oi),ooklaBinaryStatus:()=>U(`/providers/ookla/binary`),uploadOoklaBinary:e=>Mi(e),providerServers:async(e,t)=>{let{interfaceName:n,...r}=t;return Si(await U(`/providers/${encodeURIComponent(e)}/servers${xi({...r,interface:n})}`))},validateServer:(e,t)=>U(`/providers/${encodeURIComponent(e)}/validate-server`,{method:`POST`,body:JSON.stringify(t)}),settings:()=>U(`/settings`),updateSettings:e=>U(`/settings`,{method:`PUT`,body:JSON.stringify(e)}),updateOoklaEula:(e,t)=>U(`/settings/ookla-eula`,{method:`PUT`,body:JSON.stringify({accepted:e,confirmed:t})}),exportConfiguration:Ni,importConfiguration:e=>U(`/config/import`,{method:`POST`,body:JSON.stringify(e)}),backup:ji,cleanupResults:e=>U(`/retention/cleanup`,{method:`POST`,body:JSON.stringify(e?{before:e}:{})}),system:async()=>{let e=await U(`/system`);return{...e,providers:Array.isArray(e.providers)?e.providers.map(Oi):[],interfaces:Array.isArray(e.interfaces)?e.interfaces.map(Ti):[],buildDate:e.buildDate||e.buildTime||``}},exportUrl:(e,t={})=>{let{interfaceName:n,...r}=t;return`${vi}/exports/results.${e}${xi({...r,interface:n})}`},eventsUrl:`${vi}/events`};function Ti(e){return{...e,addresses:Array.isArray(e.addresses)?e.addresses:[]}}function Ei(e){if(`groups`in e&&Array.isArray(e.groups)){let t=e.groups.map(e=>({id:e.key||`all`,name:e.label||`All results`,overall:Di(e.overall),buckets:e.buckets.map(e=>Di(e))})),n=Di(e.overall),r=!!e.groupBy;return{buckets:r?t.flatMap(e=>e.buckets.map(t=>({...t,label:`${e.name} / ${t.label??t.bucket}`}))):t[0]?.buckets??[],overall:n,summary:n,...r?{series:t}:{},timezone:e.reportingTimezone,...e.groupBy===void 0?{}:{groupBy:e.groupBy}}}let t=e;return{buckets:t.buckets??t.items??t.data??[],...t.overall===void 0?{}:{overall:t.overall},...t.totals===void 0?{}:{totals:t.totals},...t.summary===void 0?{}:{summary:t.summary},...t.series===void 0?{}:{series:t.series},...t.timezone===void 0?{}:{timezone:t.timezone}}}function Di(e){let t=e=>({min:e.minimum,max:e.maximum,average:e.average,median:e.median,p95:e.p95,standardDeviation:e.standardDeviation});return{bucket:e.start,...e.resultId===void 0?{}:{resultId:e.resultId},timestamp:e.start,label:e.label,sampleCount:e.counts.total,successfulCount:e.counts.successful,failedCount:e.counts.failed,skippedCount:e.counts.skipped,...e.successRatePercent===null?{}:{successRate:e.successRatePercent},download:t(e.metrics.downloadBitsPerSecond),upload:t(e.metrics.uploadBitsPerSecond),latency:t(e.metrics.latencyMilliseconds),jitter:t(e.metrics.jitterMilliseconds),packetLoss:t(e.metrics.packetLossPercent),duration:t(e.metrics.executionDurationMilliseconds)}}function Oi(e){let t=`availability`in e?e.availability:void 0;return{id:e.id,displayName:e.displayName,capabilities:e.capabilities,available:`available`in e&&typeof e.available==`boolean`?e.available:t?.available??!1,version:`version`in e&&typeof e.version==`string`?e.version:t?.version??``,message:`message`in e&&typeof e.message==`string`?e.message:t?.message??``}}async function ki(e,t){let n=new Headers({Accept:`application/json`});t!==void 0&&n.set(`Content-Type`,`application/json`);let r=await fetch(`${vi}${e}`,{method:`POST`,headers:n,...t===void 0?{}:{body:JSON.stringify(t)},credentials:`same-origin`});if(r.ok||r.status===422){let e=await r.json();if(`success`in e)return e;throw Ai(r,e)}throw await bi(r)}function Ai(e,t){return`error`in t?new yi(t.error.message,e.status,t.error.code,t.error.requestId,t.error.details):new yi(e.statusText||`Validation failed.`,e.status)}async function ji(){let e=await fetch(`${vi}/backup`,{method:`POST`,headers:{Accept:`application/vnd.sqlite3`},credentials:`same-origin`});if(!e.ok)throw await bi(e);let t=e.headers.get(`Content-Disposition`)??``,n=/filename="?([^";]+)"?/i.exec(t);return{blob:await e.blob(),filename:n?.[1]??`multispeed-backup.db`}}async function Mi(e){let t=await fetch(`${vi}/providers/ookla/binary`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/octet-stream`},body:e,credentials:`same-origin`});if(!t.ok)throw await bi(t);return await t.json()}async function Ni(){let e=await fetch(`${vi}/config/export`,{headers:{Accept:`application/json`},credentials:`same-origin`});if(!e.ok)throw await bi(e);let t=e.headers.get(`Content-Disposition`)??``,n=/filename="?([^";]+)"?/i.exec(t);return{blob:await e.blob(),filename:n?.[1]??`multispeed-config.json`}}function Pi(e){return e instanceof yi||e instanceof Error?e.message:`An unexpected error occurred.`}var Fi=new Set([`task.created`,`task.updated`,`task.enabled`,`task.disabled`,`task.deleted`,`task_created`,`task_updated`,`task_enabled`,`task_disabled`,`task_deleted`]),Ii=new Set([`run.queued`,`route.validation.started`,`route.validation.completed`,`test.started`,`test.completed`,`test.failed`,`test.skipped`,`test.cancelled`,`result.stored`,`run_queued`,`route_validation_started`,`route_validation_completed`,`test_started`,`test_completed`,`test_failed`,`test_skipped`,`new_result_stored`]),Li=[[`tasks`],[`results`],[`statistics`],[`interfaces`],[`route-profiles`],[`providers`],[`settings`],[`system`]];function Ri(){let e=ee(),[t,n]=(0,C.useState)(()=>typeof EventSource>`u`?`unavailable`:`connecting`),[r,i]=(0,C.useState)(null);return(0,C.useEffect)(()=>{if(typeof EventSource>`u`)return;let t=new EventSource(wi.eventsUrl,{withCredentials:!0});t.onopen=()=>{n(`connected`);for(let t of Li)e.invalidateQueries({queryKey:t})},t.onerror=()=>n(t.readyState===EventSource.CLOSED?`unavailable`:`reconnecting`);let r=t=>{try{let n=JSON.parse(t.data);!n.type&&t.type!==`message`&&(n.type=t.type),i(n),Fi.has(n.type)&&e.invalidateQueries({queryKey:[`tasks`]}),Ii.has(n.type)&&(e.invalidateQueries({queryKey:[`results`]}),e.invalidateQueries({queryKey:[`statistics`]}),e.invalidateQueries({queryKey:[`system`]})),(n.type===`interface.state.changed`||n.type===`interface_state_changed`)&&e.invalidateQueries({queryKey:[`interfaces`]}),(n.type.includes(`route.validation`)||n.type.includes(`route_validation`))&&e.invalidateQueries({queryKey:[`route-profiles`]})}catch{}};return t.onmessage=r,[...Fi,...Ii,`interface.state.changed`,`interface_state_changed`].forEach(e=>t.addEventListener(e,r)),()=>t.close()},[e]),{state:t,latestEvent:r}}var zi=`multispeed-theme`;function Bi(){return window.matchMedia(`(prefers-color-scheme: dark)`).matches}function Vi(e){let t=e===`dark`||e===`system`&&Bi();document.documentElement.classList.toggle(`dark`,t),document.documentElement.dataset.theme=e,document.querySelector(`meta[name="theme-color"]`)?.setAttribute(`content`,t?`#08111f`:`#f7f9fc`)}function Hi(){let[e,t]=(0,C.useState)(()=>{let e=localStorage.getItem(zi);return e===`light`||e===`dark`||e===`system`?e:`system`});return(0,C.useEffect)(()=>{Vi(e);let t=window.matchMedia(`(prefers-color-scheme: dark)`),n=()=>e===`system`&&Vi(e);return t.addEventListener(`change`,n),()=>t.removeEventListener(`change`,n)},[e]),{theme:e,setTheme:e=>{localStorage.setItem(zi,e),t(e)}}}function Ui(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=a.getTime()?r+1:n.getTime()>=s.getTime()?r:r-1}function oa(e){let t=G(e),n=new Date(Date.UTC(t.getFullYear(),t.getMonth(),t.getDate(),t.getHours(),t.getMinutes(),t.getSeconds(),t.getMilliseconds()));return n.setUTCFullYear(t.getFullYear()),e-+n}function sa(e,...t){let n=W.bind(null,e||t.find(e=>typeof e==`object`));return t.map(n)}function ca(e,t){let n=G(e,t?.in);return n.setHours(0,0,0,0),n}function la(e,t,n){let[r,i]=sa(n?.in,e,t),a=ca(r),o=ca(i),s=+a-oa(a),c=+o-oa(o);return Math.round((s-c)/qi)}function ua(e,t){let n=aa(e,t),r=W(t?.in||e,0);return r.setFullYear(n,0,4),r.setHours(0,0,0,0),ia(r)}function da(e,t){let n=G(e)-+G(t);return n<0?-1:n>0?1:n}function fa(e){return W(e,Date.now())}function pa(e){return e instanceof Date||typeof e==`object`&&Object.prototype.toString.call(e)===`[object Date]`}function ma(e){return!(!pa(e)&&typeof e!=`number`||isNaN(+G(e)))}function ha(e){return t=>{let n=(e?Math[e]:Math.trunc)(t);return n===0?0:n}}function ga(e,t){let n=G(e,t?.in);return n.setFullYear(n.getFullYear(),0,1),n.setHours(0,0,0,0),n}var _a={lessThanXSeconds:{one:`less than a second`,other:`less than {{count}} seconds`},xSeconds:{one:`1 second`,other:`{{count}} seconds`},halfAMinute:`half a minute`,lessThanXMinutes:{one:`less than a minute`,other:`less than {{count}} minutes`},xMinutes:{one:`1 minute`,other:`{{count}} minutes`},aboutXHours:{one:`about 1 hour`,other:`about {{count}} hours`},xHours:{one:`1 hour`,other:`{{count}} hours`},xDays:{one:`1 day`,other:`{{count}} days`},aboutXWeeks:{one:`about 1 week`,other:`about {{count}} weeks`},xWeeks:{one:`1 week`,other:`{{count}} weeks`},aboutXMonths:{one:`about 1 month`,other:`about {{count}} months`},xMonths:{one:`1 month`,other:`{{count}} months`},aboutXYears:{one:`about 1 year`,other:`about {{count}} years`},xYears:{one:`1 year`,other:`{{count}} years`},overXYears:{one:`over 1 year`,other:`over {{count}} years`},almostXYears:{one:`almost 1 year`,other:`almost {{count}} years`}},va=(e,t,n)=>{let r,i=_a[e];return r=typeof i==`string`?i:t===1?i.one:i.other.replace(`{{count}}`,t.toString()),n?.addSuffix?n.comparison&&n.comparison>0?`in `+r:r+` ago`:r};function ya(e){return(t={})=>{let n=t.width?String(t.width):e.defaultWidth;return e.formats[n]||e.formats[e.defaultWidth]}}var ba={date:ya({formats:{full:`EEEE, MMMM do, y`,long:`MMMM do, y`,medium:`MMM d, y`,short:`MM/dd/yyyy`},defaultWidth:`full`}),time:ya({formats:{full:`h:mm:ss a zzzz`,long:`h:mm:ss a z`,medium:`h:mm:ss a`,short:`h:mm a`},defaultWidth:`full`}),dateTime:ya({formats:{full:`{{date}} 'at' {{time}}`,long:`{{date}} 'at' {{time}}`,medium:`{{date}}, {{time}}`,short:`{{date}}, {{time}}`},defaultWidth:`full`})},xa={lastWeek:`'last' eeee 'at' p`,yesterday:`'yesterday at' p`,today:`'today at' p`,tomorrow:`'tomorrow at' p`,nextWeek:`eeee 'at' p`,other:`P`},Sa=(e,t,n,r)=>xa[e];function Ca(e){return(t,n)=>{let r=n?.context?String(n.context):`standalone`,i;if(r===`formatting`&&e.formattingValues){let t=e.defaultFormattingWidth||e.defaultWidth,r=n?.width?String(n.width):t;i=e.formattingValues[r]||e.formattingValues[t]}else{let t=e.defaultWidth,r=n?.width?String(n.width):e.defaultWidth;i=e.values[r]||e.values[t]}let a=e.argumentCallback?e.argumentCallback(t):t;return i[a]}}var wa={ordinalNumber:(e,t)=>{let n=Number(e),r=n%100;if(r>20||r<10)switch(r%10){case 1:return n+`st`;case 2:return n+`nd`;case 3:return n+`rd`}return n+`th`},era:Ca({values:{narrow:[`B`,`A`],abbreviated:[`BC`,`AD`],wide:[`Before Christ`,`Anno Domini`]},defaultWidth:`wide`}),quarter:Ca({values:{narrow:[`1`,`2`,`3`,`4`],abbreviated:[`Q1`,`Q2`,`Q3`,`Q4`],wide:[`1st quarter`,`2nd quarter`,`3rd quarter`,`4th quarter`]},defaultWidth:`wide`,argumentCallback:e=>e-1}),month:Ca({values:{narrow:[`J`,`F`,`M`,`A`,`M`,`J`,`J`,`A`,`S`,`O`,`N`,`D`],abbreviated:[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`],wide:[`January`,`February`,`March`,`April`,`May`,`June`,`July`,`August`,`September`,`October`,`November`,`December`]},defaultWidth:`wide`}),day:Ca({values:{narrow:[`S`,`M`,`T`,`W`,`T`,`F`,`S`],short:[`Su`,`Mo`,`Tu`,`We`,`Th`,`Fr`,`Sa`],abbreviated:[`Sun`,`Mon`,`Tue`,`Wed`,`Thu`,`Fri`,`Sat`],wide:[`Sunday`,`Monday`,`Tuesday`,`Wednesday`,`Thursday`,`Friday`,`Saturday`]},defaultWidth:`wide`}),dayPeriod:Ca({values:{narrow:{am:`a`,pm:`p`,midnight:`mi`,noon:`n`,morning:`morning`,afternoon:`afternoon`,evening:`evening`,night:`night`},abbreviated:{am:`AM`,pm:`PM`,midnight:`midnight`,noon:`noon`,morning:`morning`,afternoon:`afternoon`,evening:`evening`,night:`night`},wide:{am:`a.m.`,pm:`p.m.`,midnight:`midnight`,noon:`noon`,morning:`morning`,afternoon:`afternoon`,evening:`evening`,night:`night`}},defaultWidth:`wide`,formattingValues:{narrow:{am:`a`,pm:`p`,midnight:`mi`,noon:`n`,morning:`in the morning`,afternoon:`in the afternoon`,evening:`in the evening`,night:`at night`},abbreviated:{am:`AM`,pm:`PM`,midnight:`midnight`,noon:`noon`,morning:`in the morning`,afternoon:`in the afternoon`,evening:`in the evening`,night:`at night`},wide:{am:`a.m.`,pm:`p.m.`,midnight:`midnight`,noon:`noon`,morning:`in the morning`,afternoon:`in the afternoon`,evening:`in the evening`,night:`at night`}},defaultFormattingWidth:`wide`})};function Ta(e){return(t,n={})=>{let r=n.width,i=r&&e.matchPatterns[r]||e.matchPatterns[e.defaultMatchWidth],a=t.match(i);if(!a)return null;let o=a[0],s=r&&e.parsePatterns[r]||e.parsePatterns[e.defaultParseWidth],c=Array.isArray(s)?Da(s,e=>e.test(o)):Ea(s,e=>e.test(o)),l;l=e.valueCallback?e.valueCallback(c):c,l=n.valueCallback?n.valueCallback(l):l;let u=t.slice(o.length);return{value:l,rest:u}}}function Ea(e,t){for(let n in e)if(Object.prototype.hasOwnProperty.call(e,n)&&t(e[n]))return n}function Da(e,t){for(let n=0;n{let r=t.match(e.matchPattern);if(!r)return null;let i=r[0],a=t.match(e.parsePattern);if(!a)return null;let o=e.valueCallback?e.valueCallback(a[0]):a[0];o=n.valueCallback?n.valueCallback(o):o;let s=t.slice(i.length);return{value:o,rest:s}}}var ka={code:`en-US`,formatDistance:va,formatLong:ba,formatRelative:Sa,localize:wa,match:{ordinalNumber:Oa({matchPattern:/^(\d+)(th|st|nd|rd)?/i,parsePattern:/\d+/i,valueCallback:e=>parseInt(e,10)}),era:Ta({matchPatterns:{narrow:/^(b|a)/i,abbreviated:/^(b\.?\s?c\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?)/i,wide:/^(before christ|before common era|anno domini|common era)/i},defaultMatchWidth:`wide`,parsePatterns:{any:[/^b/i,/^(a|c)/i]},defaultParseWidth:`any`}),quarter:Ta({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^q[1234]/i,wide:/^[1234](th|st|nd|rd)? quarter/i},defaultMatchWidth:`wide`,parsePatterns:{any:[/1/i,/2/i,/3/i,/4/i]},defaultParseWidth:`any`,valueCallback:e=>e+1}),month:Ta({matchPatterns:{narrow:/^[jfmasond]/i,abbreviated:/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i,wide:/^(january|february|march|april|may|june|july|august|september|october|november|december)/i},defaultMatchWidth:`wide`,parsePatterns:{narrow:[/^j/i,/^f/i,/^m/i,/^a/i,/^m/i,/^j/i,/^j/i,/^a/i,/^s/i,/^o/i,/^n/i,/^d/i],any:[/^ja/i,/^f/i,/^mar/i,/^ap/i,/^may/i,/^jun/i,/^jul/i,/^au/i,/^s/i,/^o/i,/^n/i,/^d/i]},defaultParseWidth:`any`}),day:Ta({matchPatterns:{narrow:/^[smtwf]/i,short:/^(su|mo|tu|we|th|fr|sa)/i,abbreviated:/^(sun|mon|tue|wed|thu|fri|sat)/i,wide:/^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)/i},defaultMatchWidth:`wide`,parsePatterns:{narrow:[/^s/i,/^m/i,/^t/i,/^w/i,/^t/i,/^f/i,/^s/i],any:[/^su/i,/^m/i,/^tu/i,/^w/i,/^th/i,/^f/i,/^sa/i]},defaultParseWidth:`any`}),dayPeriod:Ta({matchPatterns:{narrow:/^(a|p|mi|n|(in the|at) (morning|afternoon|evening|night))/i,any:/^([ap]\.?\s?m\.?|midnight|noon|(in the|at) (morning|afternoon|evening|night))/i},defaultMatchWidth:`any`,parsePatterns:{any:{am:/^a/i,pm:/^p/i,midnight:/^mi/i,noon:/^no/i,morning:/morning/i,afternoon:/afternoon/i,evening:/evening/i,night:/night/i}},defaultParseWidth:`any`})},options:{weekStartsOn:0,firstWeekContainsDate:1}};function Aa(e,t){let n=G(e,t?.in);return la(n,ga(n))+1}function ja(e,t){let n=G(e,t?.in),r=ia(n)-+ua(n);return Math.round(r/Ki)+1}function Ma(e,t){let n=G(e,t?.in),r=n.getFullYear(),i=na(),a=t?.firstWeekContainsDate??t?.locale?.options?.firstWeekContainsDate??i.firstWeekContainsDate??i.locale?.options?.firstWeekContainsDate??1,o=W(t?.in||e,0);o.setFullYear(r+1,0,a),o.setHours(0,0,0,0);let s=ra(o,t),c=W(t?.in||e,0);c.setFullYear(r,0,a),c.setHours(0,0,0,0);let l=ra(c,t);return+n>=+s?r+1:+n>=+l?r:r-1}function Na(e,t){let n=na(),r=t?.firstWeekContainsDate??t?.locale?.options?.firstWeekContainsDate??n.firstWeekContainsDate??n.locale?.options?.firstWeekContainsDate??1,i=Ma(e,t),a=W(t?.in||e,0);return a.setFullYear(i,0,r),a.setHours(0,0,0,0),ra(a,t)}function Pa(e,t){let n=G(e,t?.in),r=ra(n,t)-+Na(n,t);return Math.round(r/Ki)+1}function K(e,t){return(e<0?`-`:``)+Math.abs(e).toString().padStart(t,`0`)}var Fa={y(e,t){let n=e.getFullYear(),r=n>0?n:1-n;return K(t===`yy`?r%100:r,t.length)},M(e,t){let n=e.getMonth();return t===`M`?String(n+1):K(n+1,2)},d(e,t){return K(e.getDate(),t.length)},a(e,t){let n=e.getHours()/12>=1?`pm`:`am`;switch(t){case`a`:case`aa`:return n.toUpperCase();case`aaa`:return n;case`aaaaa`:return n[0];default:return n===`am`?`a.m.`:`p.m.`}},h(e,t){return K(e.getHours()%12||12,t.length)},H(e,t){return K(e.getHours(),t.length)},m(e,t){return K(e.getMinutes(),t.length)},s(e,t){return K(e.getSeconds(),t.length)},S(e,t){let n=t.length,r=e.getMilliseconds();return K(Math.trunc(r*10**(n-3)),t.length)}},Ia={am:`am`,pm:`pm`,midnight:`midnight`,noon:`noon`,morning:`morning`,afternoon:`afternoon`,evening:`evening`,night:`night`},La={G:function(e,t,n){let r=+(e.getFullYear()>0);switch(t){case`G`:case`GG`:case`GGG`:return n.era(r,{width:`abbreviated`});case`GGGGG`:return n.era(r,{width:`narrow`});default:return n.era(r,{width:`wide`})}},y:function(e,t,n){if(t===`yo`){let t=e.getFullYear(),r=t>0?t:1-t;return n.ordinalNumber(r,{unit:`year`})}return Fa.y(e,t)},Y:function(e,t,n,r){let i=Ma(e,r),a=i>0?i:1-i;return t===`YY`?K(a%100,2):t===`Yo`?n.ordinalNumber(a,{unit:`year`}):K(a,t.length)},R:function(e,t){return K(aa(e),t.length)},u:function(e,t){return K(e.getFullYear(),t.length)},Q:function(e,t,n){let r=Math.ceil((e.getMonth()+1)/3);switch(t){case`Q`:return String(r);case`QQ`:return K(r,2);case`Qo`:return n.ordinalNumber(r,{unit:`quarter`});case`QQQ`:return n.quarter(r,{width:`abbreviated`,context:`formatting`});case`QQQQQ`:return n.quarter(r,{width:`narrow`,context:`formatting`});default:return n.quarter(r,{width:`wide`,context:`formatting`})}},q:function(e,t,n){let r=Math.ceil((e.getMonth()+1)/3);switch(t){case`q`:return String(r);case`qq`:return K(r,2);case`qo`:return n.ordinalNumber(r,{unit:`quarter`});case`qqq`:return n.quarter(r,{width:`abbreviated`,context:`standalone`});case`qqqqq`:return n.quarter(r,{width:`narrow`,context:`standalone`});default:return n.quarter(r,{width:`wide`,context:`standalone`})}},M:function(e,t,n){let r=e.getMonth();switch(t){case`M`:case`MM`:return Fa.M(e,t);case`Mo`:return n.ordinalNumber(r+1,{unit:`month`});case`MMM`:return n.month(r,{width:`abbreviated`,context:`formatting`});case`MMMMM`:return n.month(r,{width:`narrow`,context:`formatting`});default:return n.month(r,{width:`wide`,context:`formatting`})}},L:function(e,t,n){let r=e.getMonth();switch(t){case`L`:return String(r+1);case`LL`:return K(r+1,2);case`Lo`:return n.ordinalNumber(r+1,{unit:`month`});case`LLL`:return n.month(r,{width:`abbreviated`,context:`standalone`});case`LLLLL`:return n.month(r,{width:`narrow`,context:`standalone`});default:return n.month(r,{width:`wide`,context:`standalone`})}},w:function(e,t,n,r){let i=Pa(e,r);return t===`wo`?n.ordinalNumber(i,{unit:`week`}):K(i,t.length)},I:function(e,t,n){let r=ja(e);return t===`Io`?n.ordinalNumber(r,{unit:`week`}):K(r,t.length)},d:function(e,t,n){return t===`do`?n.ordinalNumber(e.getDate(),{unit:`date`}):Fa.d(e,t)},D:function(e,t,n){let r=Aa(e);return t===`Do`?n.ordinalNumber(r,{unit:`dayOfYear`}):K(r,t.length)},E:function(e,t,n){let r=e.getDay();switch(t){case`E`:case`EE`:case`EEE`:return n.day(r,{width:`abbreviated`,context:`formatting`});case`EEEEE`:return n.day(r,{width:`narrow`,context:`formatting`});case`EEEEEE`:return n.day(r,{width:`short`,context:`formatting`});default:return n.day(r,{width:`wide`,context:`formatting`})}},e:function(e,t,n,r){let i=e.getDay(),a=(i-r.weekStartsOn+8)%7||7;switch(t){case`e`:return String(a);case`ee`:return K(a,2);case`eo`:return n.ordinalNumber(a,{unit:`day`});case`eee`:return n.day(i,{width:`abbreviated`,context:`formatting`});case`eeeee`:return n.day(i,{width:`narrow`,context:`formatting`});case`eeeeee`:return n.day(i,{width:`short`,context:`formatting`});default:return n.day(i,{width:`wide`,context:`formatting`})}},c:function(e,t,n,r){let i=e.getDay(),a=(i-r.weekStartsOn+8)%7||7;switch(t){case`c`:return String(a);case`cc`:return K(a,t.length);case`co`:return n.ordinalNumber(a,{unit:`day`});case`ccc`:return n.day(i,{width:`abbreviated`,context:`standalone`});case`ccccc`:return n.day(i,{width:`narrow`,context:`standalone`});case`cccccc`:return n.day(i,{width:`short`,context:`standalone`});default:return n.day(i,{width:`wide`,context:`standalone`})}},i:function(e,t,n){let r=e.getDay(),i=r===0?7:r;switch(t){case`i`:return String(i);case`ii`:return K(i,t.length);case`io`:return n.ordinalNumber(i,{unit:`day`});case`iii`:return n.day(r,{width:`abbreviated`,context:`formatting`});case`iiiii`:return n.day(r,{width:`narrow`,context:`formatting`});case`iiiiii`:return n.day(r,{width:`short`,context:`formatting`});default:return n.day(r,{width:`wide`,context:`formatting`})}},a:function(e,t,n){let r=e.getHours()/12>=1?`pm`:`am`;switch(t){case`a`:case`aa`:return n.dayPeriod(r,{width:`abbreviated`,context:`formatting`});case`aaa`:return n.dayPeriod(r,{width:`abbreviated`,context:`formatting`}).toLowerCase();case`aaaaa`:return n.dayPeriod(r,{width:`narrow`,context:`formatting`});default:return n.dayPeriod(r,{width:`wide`,context:`formatting`})}},b:function(e,t,n){let r=e.getHours(),i;switch(i=r===12?Ia.noon:r===0?Ia.midnight:r/12>=1?`pm`:`am`,t){case`b`:case`bb`:return n.dayPeriod(i,{width:`abbreviated`,context:`formatting`});case`bbb`:return n.dayPeriod(i,{width:`abbreviated`,context:`formatting`}).toLowerCase();case`bbbbb`:return n.dayPeriod(i,{width:`narrow`,context:`formatting`});default:return n.dayPeriod(i,{width:`wide`,context:`formatting`})}},B:function(e,t,n){let r=e.getHours(),i;switch(i=r>=17?Ia.evening:r>=12?Ia.afternoon:r>=4?Ia.morning:Ia.night,t){case`B`:case`BB`:case`BBB`:return n.dayPeriod(i,{width:`abbreviated`,context:`formatting`});case`BBBBB`:return n.dayPeriod(i,{width:`narrow`,context:`formatting`});default:return n.dayPeriod(i,{width:`wide`,context:`formatting`})}},h:function(e,t,n){if(t===`ho`){let t=e.getHours()%12;return t===0&&(t=12),n.ordinalNumber(t,{unit:`hour`})}return Fa.h(e,t)},H:function(e,t,n){return t===`Ho`?n.ordinalNumber(e.getHours(),{unit:`hour`}):Fa.H(e,t)},K:function(e,t,n){let r=e.getHours()%12;return t===`Ko`?n.ordinalNumber(r,{unit:`hour`}):K(r,t.length)},k:function(e,t,n){let r=e.getHours();return r===0&&(r=24),t===`ko`?n.ordinalNumber(r,{unit:`hour`}):K(r,t.length)},m:function(e,t,n){return t===`mo`?n.ordinalNumber(e.getMinutes(),{unit:`minute`}):Fa.m(e,t)},s:function(e,t,n){return t===`so`?n.ordinalNumber(e.getSeconds(),{unit:`second`}):Fa.s(e,t)},S:function(e,t){return Fa.S(e,t)},X:function(e,t,n){let r=e.getTimezoneOffset();if(r===0)return`Z`;switch(t){case`X`:return za(r);case`XXXX`:case`XX`:return Ba(r);default:return Ba(r,`:`)}},x:function(e,t,n){let r=e.getTimezoneOffset();switch(t){case`x`:return za(r);case`xxxx`:case`xx`:return Ba(r);default:return Ba(r,`:`)}},O:function(e,t,n){let r=e.getTimezoneOffset();switch(t){case`O`:case`OO`:case`OOO`:return`GMT`+Ra(r,`:`);default:return`GMT`+Ba(r,`:`)}},z:function(e,t,n){let r=e.getTimezoneOffset();switch(t){case`z`:case`zz`:case`zzz`:return`GMT`+Ra(r,`:`);default:return`GMT`+Ba(r,`:`)}},t:function(e,t,n){return K(Math.trunc(e/1e3),t.length)},T:function(e,t,n){return K(+e,t.length)}};function Ra(e,t=``){let n=e>0?`-`:`+`,r=Math.abs(e),i=Math.trunc(r/60),a=r%60;return a===0?n+String(i):n+String(i)+t+K(a,2)}function za(e,t){return e%60==0?(e>0?`-`:`+`)+K(Math.abs(e)/60,2):Ba(e,t)}function Ba(e,t=``){let n=e>0?`-`:`+`,r=Math.abs(e),i=K(Math.trunc(r/60),2),a=K(r%60,2);return n+i+t+a}var Va=(e,t)=>{switch(e){case`P`:return t.date({width:`short`});case`PP`:return t.date({width:`medium`});case`PPP`:return t.date({width:`long`});default:return t.date({width:`full`})}},Ha=(e,t)=>{switch(e){case`p`:return t.time({width:`short`});case`pp`:return t.time({width:`medium`});case`ppp`:return t.time({width:`long`});default:return t.time({width:`full`})}},Ua={p:Ha,P:(e,t)=>{let n=e.match(/(P+)(p+)?/)||[],r=n[1],i=n[2];if(!i)return Va(e,t);let a;switch(r){case`P`:a=t.dateTime({width:`short`});break;case`PP`:a=t.dateTime({width:`medium`});break;case`PPP`:a=t.dateTime({width:`long`});break;default:a=t.dateTime({width:`full`})}return a.replace(`{{date}}`,Va(r,t)).replace(`{{time}}`,Ha(i,t))}},Wa=/^D+$/,Ga=/^Y+$/,Ka=[`D`,`DD`,`YY`,`YYYY`];function qa(e){return Wa.test(e)}function Ja(e){return Ga.test(e)}function Ya(e,t,n){let r=Xa(e,t,n);if(console.warn(r),Ka.includes(e))throw RangeError(r)}function Xa(e,t,n){let r=e[0]===`Y`?`years`:`days of the month`;return`Use \`${e.toLowerCase()}\` instead of \`${e}\` (in \`${t}\`) for formatting ${r} to the input \`${n}\`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md`}var Za=/[yYQqMLwIdDecihHKkms]o|(\w)\1*|''|'(''|[^'])+('|$)|./g,Qa=/P+p+|P+|p+|''|'(''|[^'])+('|$)|./g,$a=/^'([^]*?)'?$/,eo=/''/g,to=/[a-zA-Z]/;function no(e,t,n){let r=na(),i=n?.locale??r.locale??ka,a=n?.firstWeekContainsDate??n?.locale?.options?.firstWeekContainsDate??r.firstWeekContainsDate??r.locale?.options?.firstWeekContainsDate??1,o=n?.weekStartsOn??n?.locale?.options?.weekStartsOn??r.weekStartsOn??r.locale?.options?.weekStartsOn??0,s=G(e,n?.in);if(!ma(s))throw RangeError(`Invalid time value`);let c=t.match(Qa).map(e=>{let t=e[0];if(t===`p`||t===`P`){let n=Ua[t];return n(e,i.formatLong)}return e}).join(``).match(Za).map(e=>{if(e===`''`)return{isToken:!1,value:`'`};let t=e[0];if(t===`'`)return{isToken:!1,value:ro(e)};if(La[t])return{isToken:!0,value:e};if(t.match(to))throw RangeError("Format string contains an unescaped latin alphabet character `"+t+"`");return{isToken:!1,value:e}});i.localize.preprocessor&&(c=i.localize.preprocessor(s,c));let l={firstWeekContainsDate:a,weekStartsOn:o,locale:i};return c.map(r=>{if(!r.isToken)return r.value;let a=r.value;(!n?.useAdditionalWeekYearTokens&&Ja(a)||!n?.useAdditionalDayOfYearTokens&&qa(a))&&Ya(a,t,String(e));let o=La[a[0]];return o(s,a,i.localize,l)}).join(``)}function ro(e){let t=e.match($a);return t?t[1].replace(eo,`'`):e}function io(e,t,n){let r=na(),i=n?.locale??r.locale??ka,a=da(e,t);if(isNaN(a))throw RangeError(`Invalid time value`);let o=Object.assign({},n,{addSuffix:n?.addSuffix,comparison:a}),[s,c]=sa(n?.in,...a>0?[t,e]:[e,t]),l=ha(n?.roundingMethod??`round`),u=c.getTime()-s.getTime(),d=u/Ji,f=(u-(oa(c)-oa(s)))/Ji,p=n?.unit,m;if(m=p||(d<1?`second`:d<60?`minute`:d<1440?`hour`:f<43200?`day`:f<525600?`month`:`year`),m===`second`){let e=l(u/1e3);return i.formatDistance(`xSeconds`,e,o)}if(m===`minute`){let e=l(d);return i.formatDistance(`xMinutes`,e,o)}if(m===`hour`){let e=l(d/60);return i.formatDistance(`xHours`,e,o)}if(m===`day`){let e=l(f/Qi);return i.formatDistance(`xDays`,e,o)}if(m===`month`){let e=l(f/Zi);return e===12&&p!==`month`?i.formatDistance(`xYears`,1,o):i.formatDistance(`xMonths`,e,o)}{let e=l(f/Xi);return i.formatDistance(`xYears`,e,o)}}function ao(e,t){return io(e,fa(e),t)}function oo(e,t){let n=()=>W(t?.in,NaN),r=t?.additionalDigits??2,i=fo(e),a;if(i.date){let e=po(i.date,r);a=mo(e.restDateString,e.year)}if(!a||isNaN(+a))return n();let o=+a,s=0,c;if(i.time&&(s=go(i.time),isNaN(s)))return n();if(i.timezone){if(c=vo(i.timezone),isNaN(c))return n()}else{let e=new Date(o+s),n=G(0,t?.in);return n.setFullYear(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()),n.setHours(e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds()),n}return G(o+s+c,t?.in)}var so={dateTimeDelimiter:/[T ]/,timeZoneDelimiter:/[Z ]/i,timezone:/([Z+-].*)$/},co=/^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/,lo=/^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/,uo=/^([+-])(\d{2})(?::?(\d{2}))?$/;function fo(e){let t={},n=e.split(so.dateTimeDelimiter),r;if(n.length>2)return t;if(/:/.test(n[0])?r=n[0]:(t.date=n[0],r=n[1],so.timeZoneDelimiter.test(t.date)&&(t.date=e.split(so.timeZoneDelimiter)[0],r=e.substr(t.date.length,e.length))),r){let e=so.timezone.exec(r);e?(t.time=r.replace(e[1],``),t.timezone=e[1]):t.time=r}return t}function po(e,t){let n=RegExp(`^(?:(\\d{4}|[+-]\\d{`+(4+t)+`})|(\\d{2}|[+-]\\d{`+(2+t)+`})$)`),r=e.match(n);if(!r)return{year:NaN,restDateString:``};let i=r[1]?parseInt(r[1]):null,a=r[2]?parseInt(r[2]):null;return{year:a===null?i:a*100,restDateString:e.slice((r[1]||r[2]).length)}}function mo(e,t){if(t===null)return new Date(NaN);let n=e.match(co);if(!n)return new Date(NaN);let r=!!n[4],i=ho(n[1]),a=ho(n[2])-1,o=ho(n[3]),s=ho(n[4]),c=ho(n[5])-1;if(r)return wo(t,s,c)?yo(t,s,c):new Date(NaN);{let e=new Date(0);return!So(t,a,o)||!Co(t,i)?new Date(NaN):(e.setUTCFullYear(t,a,Math.max(i,o)),e)}}function ho(e){return e?parseInt(e):1}function go(e){let t=e.match(lo);if(!t)return NaN;let n=_o(t[1]),r=_o(t[2]),i=_o(t[3]);return To(n,r,i)?n*Yi+r*Ji+i*1e3:NaN}function _o(e){return e&&parseFloat(e.replace(`,`,`.`))||0}function vo(e){if(e===`Z`)return 0;let t=e.match(uo);if(!t)return 0;let n=t[1]===`+`?-1:1,r=parseInt(t[2]),i=t[3]&&parseInt(t[3])||0;return Eo(r,i)?n*(r*Yi+i*Ji):NaN}function yo(e,t,n){let r=new Date(0);r.setUTCFullYear(e,0,4);let i=r.getUTCDay()||7,a=(t-1)*7+n+1-i;return r.setUTCDate(r.getUTCDate()+a),r}var bo=[31,null,31,30,31,30,31,31,30,31,30,31];function xo(e){return e%400==0||e%4==0&&e%100!=0}function So(e,t,n){return t>=0&&t<=11&&n>=1&&n<=(bo[t]||(xo(e)?29:28))}function Co(e,t){return t>=1&&t<=(xo(e)?366:365)}function wo(e,t,n){return t>=1&&t<=53&&n>=0&&n<=6}function To(e,t,n){return e===24?t===0&&n===0:n>=0&&n<60&&t>=0&&t<60&&e>=0&&e<25}function Eo(e,t){return t>=0&&t<=59}var Do=(e,t)=>{let n=Array(e.length+t.length);for(let t=0;t({classGroupId:e,validator:t}),ko=(e=new Map,t=null,n)=>({nextPart:e,validators:t,classGroupId:n}),Ao=`-`,jo=[],Mo=`arbitrary..`,No=e=>{let t=Io(e),{conflictingClassGroups:n,conflictingClassGroupModifiers:r}=e;return{getClassGroupId:e=>{if(e.startsWith(`[`)&&e.endsWith(`]`))return Fo(e);let n=e.split(Ao);return Po(n,+(n[0]===``&&n.length>1),t)},getConflictingClassGroupIds:(e,t)=>{if(t){let t=r[e],i=n[e];return t?i?Do(i,t):t:i||jo}return n[e]||jo}}},Po=(e,t,n)=>{if(e.length-t===0)return n.classGroupId;let r=e[t],i=n.nextPart.get(r);if(i){let n=Po(e,t+1,i);if(n)return n}let a=n.validators;if(a===null)return;let o=t===0?e.join(Ao):e.slice(t).join(Ao),s=a.length;for(let e=0;ee.slice(1,-1).indexOf(`:`)===-1?void 0:(()=>{let t=e.slice(1,-1),n=t.indexOf(`:`),r=t.slice(0,n);return r?Mo+r:void 0})(),Io=e=>{let{theme:t,classGroups:n}=e;return Lo(n,t)},Lo=(e,t)=>{let n=ko();for(let r in e){let i=e[r];Ro(i,n,r,t)}return n},Ro=(e,t,n,r)=>{let i=e.length;for(let a=0;a{if(typeof e==`string`){Bo(e,t,n);return}if(typeof e==`function`){Vo(e,t,n,r);return}Ho(e,t,n,r)},Bo=(e,t,n)=>{let r=e===``?t:Uo(t,e);r.classGroupId=n},Vo=(e,t,n,r)=>{if(Wo(e)){Ro(e(r),t,n,r);return}t.validators===null&&(t.validators=[]),t.validators.push(Oo(n,e))},Ho=(e,t,n,r)=>{let i=Object.entries(e),a=i.length;for(let e=0;e{let n=e,r=t.split(Ao),i=r.length;for(let e=0;e`isThemeGetter`in e&&e.isThemeGetter===!0,Go=e=>{if(e<1)return{get:()=>void 0,set:()=>{}};let t=0,n=Object.create(null),r=Object.create(null),i=(i,a)=>{n[i]=a,t++,t>e&&(t=0,r=n,n=Object.create(null))};return{get(e){let t=n[e];if(t!==void 0)return t;if((t=r[e])!==void 0)return i(e,t),t},set(e,t){e in n?n[e]=t:i(e,t)}}},Ko=`!`,qo=`:`,Jo=[],Yo=(e,t,n,r,i)=>({modifiers:e,hasImportantModifier:t,baseClassName:n,maybePostfixModifierPosition:r,isExternal:i}),Xo=e=>{let{prefix:t,experimentalParseClassName:n}=e,r=e=>{let t=[],n=0,r=0,i=0,a,o=e.length;for(let s=0;si?a-i:void 0;return Yo(t,l,c,u)};if(t){let e=t+qo,n=r;r=t=>t.startsWith(e)?n(t.slice(e.length)):Yo(Jo,!1,t,void 0,!0)}if(n){let e=r;r=t=>n({className:t,parseClassName:e})}return r},Zo=e=>{let t=new Map;return e.orderSensitiveModifiers.forEach((e,n)=>{t.set(e,1e6+n)}),e=>{let n=[],r=[];for(let i=0;i0&&(r.sort(),n.push(...r),r=[]),n.push(a)):r.push(a)}return r.length>0&&(r.sort(),n.push(...r)),n}},Qo=e=>({cache:Go(e.cacheSize),parseClassName:Xo(e),sortModifiers:Zo(e),postfixLookupClassGroupIds:$o(e),...No(e)}),$o=e=>{let t=Object.create(null),n=e.postfixLookupClassGroups;if(n)for(let e=0;e{let{parseClassName:n,getClassGroupId:r,getConflictingClassGroupIds:i,sortModifiers:a,postfixLookupClassGroupIds:o}=t,s=[],c=e.trim().split(es),l=``;for(let e=c.length-1;e>=0;--e){let t=c[e],{isExternal:u,modifiers:d,hasImportantModifier:f,baseClassName:p,maybePostfixModifierPosition:m}=n(t);if(u){l=t+(l.length>0?` `+l:l);continue}let h=!!m,g;if(h){g=r(p.substring(0,m));let e=g&&o[g]?r(p):void 0;e&&e!==g&&(g=e,h=!1)}else g=r(p);if(!g){if(!h){l=t+(l.length>0?` `+l:l);continue}if(g=r(p),!g){l=t+(l.length>0?` `+l:l);continue}h=!1}let _=d.length===0?``:d.length===1?d[0]:a(d).join(`:`),ee=f?_+Ko:_,te=ee+g;if(s.indexOf(te)>-1)continue;s.push(te);let ne=i(g,h);for(let e=0;e0?` `+l:l)}return l},ns=(...e)=>{let t=0,n,r,i=``;for(;t{if(typeof e==`string`)return e;let t,n=``;for(let r=0;r{let n,r,i,a,o=o=>(n=Qo(t.reduce((e,t)=>t(e),e())),r=n.cache.get,i=n.cache.set,a=s,s(o)),s=e=>{let t=r(e);if(t)return t;let a=ts(e,n);return i(e,a),a};return a=o,(...e)=>a(ns(...e))},as=[],q=e=>{let t=t=>t[e]||as;return t.isThemeGetter=!0,t},os=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,ss=/^\((?:(\w[\w-]*):)?(.+)\)$/i,cs=/^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/,ls=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,us=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,ds=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,fs=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,ps=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,ms=e=>cs.test(e),J=e=>!!e&&!Number.isNaN(Number(e)),Y=e=>!!e&&Number.isInteger(Number(e)),hs=e=>e.endsWith(`%`)&&J(e.slice(0,-1)),X=e=>ls.test(e),gs=()=>!0,_s=e=>us.test(e)&&!ds.test(e),vs=()=>!1,ys=e=>fs.test(e),bs=e=>ps.test(e),xs=e=>!Z(e)&&!Q(e),Ss=e=>e.startsWith(`@container`)&&(e[10]===`/`&&e[11]!==void 0||e[11]===`s`&&e[16]!==void 0&&e.startsWith(`-size/`,10)||e[11]===`n`&&e[18]!==void 0&&e.startsWith(`-normal/`,10)),Cs=e=>Rs(e,Hs,vs),Z=e=>os.test(e),ws=e=>Rs(e,Us,_s),Ts=e=>Rs(e,Ws,J),Es=e=>Rs(e,Ks,gs),Ds=e=>Rs(e,Gs,vs),Os=e=>Rs(e,Bs,vs),ks=e=>Rs(e,Vs,bs),As=e=>Rs(e,qs,ys),Q=e=>ss.test(e),js=e=>zs(e,Us),Ms=e=>zs(e,Gs),Ns=e=>zs(e,Bs),Ps=e=>zs(e,Hs),Fs=e=>zs(e,Vs),Is=e=>zs(e,qs,!0),Ls=e=>zs(e,Ks,!0),Rs=(e,t,n)=>{let r=os.exec(e);return r?r[1]?t(r[1]):n(r[2]):!1},zs=(e,t,n=!1)=>{let r=ss.exec(e);return r?r[1]?t(r[1]):n:!1},Bs=e=>e===`position`||e===`percentage`,Vs=e=>e===`image`||e===`url`,Hs=e=>e===`length`||e===`size`||e===`bg-size`,Us=e=>e===`length`,Ws=e=>e===`number`,Gs=e=>e===`family-name`,Ks=e=>e===`number`||e===`weight`,qs=e=>e===`shadow`,Js=is(()=>{let e=q(`color`),t=q(`font`),n=q(`text`),r=q(`font-weight`),i=q(`tracking`),a=q(`leading`),o=q(`breakpoint`),s=q(`container`),c=q(`spacing`),l=q(`radius`),u=q(`shadow`),d=q(`inset-shadow`),f=q(`text-shadow`),p=q(`drop-shadow`),m=q(`blur`),h=q(`perspective`),g=q(`aspect`),_=q(`ease`),ee=q(`animate`),te=()=>[`auto`,`avoid`,`all`,`avoid-page`,`page`,`left`,`right`,`column`],ne=()=>[`center`,`top`,`bottom`,`left`,`right`,`top-left`,`left-top`,`top-right`,`right-top`,`bottom-right`,`right-bottom`,`bottom-left`,`left-bottom`],re=()=>[...ne(),Q,Z],v=()=>[`auto`,`hidden`,`clip`,`visible`,`scroll`],y=()=>[`auto`,`contain`,`none`],b=()=>[Q,Z,c],x=()=>[ms,`full`,`auto`,...b()],ie=()=>[Y,`none`,`subgrid`,Q,Z],ae=()=>[`auto`,{span:[`full`,Y,Q,Z]},Y,Q,Z],oe=()=>[Y,`auto`,Q,Z],se=()=>[`auto`,`min`,`max`,`fr`,Q,Z],ce=()=>[`start`,`end`,`center`,`between`,`around`,`evenly`,`stretch`,`baseline`,`center-safe`,`end-safe`],le=()=>[`start`,`end`,`center`,`stretch`,`center-safe`,`end-safe`],S=()=>[`auto`,...b()],C=()=>[ms,`auto`,`full`,`dvw`,`dvh`,`lvw`,`lvh`,`svw`,`svh`,`min`,`max`,`fit`,...b()],ue=()=>[ms,`screen`,`full`,`dvw`,`lvw`,`svw`,`min`,`max`,`fit`,...b()],de=()=>[ms,`screen`,`full`,`lh`,`dvh`,`lvh`,`svh`,`min`,`max`,`fit`,...b()],w=()=>[e,Q,Z],fe=()=>[...ne(),Ns,Os,{position:[Q,Z]}],T=()=>[`no-repeat`,{repeat:[``,`x`,`y`,`space`,`round`]}],pe=()=>[`auto`,`cover`,`contain`,Ps,Cs,{size:[Q,Z]}],me=()=>[hs,js,ws],E=()=>[``,`none`,`full`,l,Q,Z],D=()=>[``,J,js,ws],he=()=>[`solid`,`dashed`,`dotted`,`double`],ge=()=>[`normal`,`multiply`,`screen`,`overlay`,`darken`,`lighten`,`color-dodge`,`color-burn`,`hard-light`,`soft-light`,`difference`,`exclusion`,`hue`,`saturation`,`color`,`luminosity`],O=()=>[J,hs,Ns,Os],_e=()=>[``,`none`,m,Q,Z],k=()=>[`none`,J,Q,Z],A=()=>[`none`,J,Q,Z],ve=()=>[J,Q,Z],j=()=>[ms,`full`,...b()];return{cacheSize:500,theme:{animate:[`spin`,`ping`,`pulse`,`bounce`],aspect:[`video`],blur:[X],breakpoint:[X],color:[gs],container:[X],"drop-shadow":[X],ease:[`in`,`out`,`in-out`],font:[xs],"font-weight":[`thin`,`extralight`,`light`,`normal`,`medium`,`semibold`,`bold`,`extrabold`,`black`],"inset-shadow":[X],leading:[`none`,`tight`,`snug`,`normal`,`relaxed`,`loose`],perspective:[`dramatic`,`near`,`normal`,`midrange`,`distant`,`none`],radius:[X],shadow:[X],spacing:[`px`,J],text:[X],"text-shadow":[X],tracking:[`tighter`,`tight`,`normal`,`wide`,`wider`,`widest`]},classGroups:{aspect:[{aspect:[`auto`,`square`,ms,Z,Q,g]}],container:[`container`],"container-type":[{"@container":[``,`normal`,`size`,Q,Z]}],"container-named":[Ss],columns:[{columns:[J,Z,Q,s]}],"break-after":[{"break-after":te()}],"break-before":[{"break-before":te()}],"break-inside":[{"break-inside":[`auto`,`avoid`,`avoid-page`,`avoid-column`]}],"box-decoration":[{"box-decoration":[`slice`,`clone`]}],box:[{box:[`border`,`content`]}],display:[`block`,`inline-block`,`inline`,`flex`,`inline-flex`,`table`,`inline-table`,`table-caption`,`table-cell`,`table-column`,`table-column-group`,`table-footer-group`,`table-header-group`,`table-row-group`,`table-row`,`flow-root`,`grid`,`inline-grid`,`contents`,`list-item`,`hidden`],sr:[`sr-only`,`not-sr-only`],float:[{float:[`right`,`left`,`none`,`start`,`end`]}],clear:[{clear:[`left`,`right`,`both`,`none`,`start`,`end`]}],isolation:[`isolate`,`isolation-auto`],"object-fit":[{object:[`contain`,`cover`,`fill`,`none`,`scale-down`]}],"object-position":[{object:re()}],overflow:[{overflow:v()}],"overflow-x":[{"overflow-x":v()}],"overflow-y":[{"overflow-y":v()}],overscroll:[{overscroll:y()}],"overscroll-x":[{"overscroll-x":y()}],"overscroll-y":[{"overscroll-y":y()}],position:[`static`,`fixed`,`absolute`,`relative`,`sticky`],inset:[{inset:x()}],"inset-x":[{"inset-x":x()}],"inset-y":[{"inset-y":x()}],start:[{"inset-s":x(),start:x()}],end:[{"inset-e":x(),end:x()}],"inset-bs":[{"inset-bs":x()}],"inset-be":[{"inset-be":x()}],top:[{top:x()}],right:[{right:x()}],bottom:[{bottom:x()}],left:[{left:x()}],visibility:[`visible`,`invisible`,`collapse`],z:[{z:[Y,`auto`,Q,Z]}],basis:[{basis:[ms,`full`,`auto`,s,...b()]}],"flex-direction":[{flex:[`row`,`row-reverse`,`col`,`col-reverse`]}],"flex-wrap":[{flex:[`nowrap`,`wrap`,`wrap-reverse`]}],flex:[{flex:[J,ms,`auto`,`initial`,`none`,Z]}],grow:[{grow:[``,J,Q,Z]}],shrink:[{shrink:[``,J,Q,Z]}],order:[{order:[Y,`first`,`last`,`none`,Q,Z]}],"grid-cols":[{"grid-cols":ie()}],"col-start-end":[{col:ae()}],"col-start":[{"col-start":oe()}],"col-end":[{"col-end":oe()}],"grid-rows":[{"grid-rows":ie()}],"row-start-end":[{row:ae()}],"row-start":[{"row-start":oe()}],"row-end":[{"row-end":oe()}],"grid-flow":[{"grid-flow":[`row`,`col`,`dense`,`row-dense`,`col-dense`]}],"auto-cols":[{"auto-cols":se()}],"auto-rows":[{"auto-rows":se()}],gap:[{gap:b()}],"gap-x":[{"gap-x":b()}],"gap-y":[{"gap-y":b()}],"justify-content":[{justify:[...ce(),`normal`]}],"justify-items":[{"justify-items":[...le(),`normal`]}],"justify-self":[{"justify-self":[`auto`,...le()]}],"align-content":[{content:[`normal`,...ce()]}],"align-items":[{items:[...le(),{baseline:[``,`last`]}]}],"align-self":[{self:[`auto`,...le(),{baseline:[``,`last`]}]}],"place-content":[{"place-content":ce()}],"place-items":[{"place-items":[...le(),`baseline`]}],"place-self":[{"place-self":[`auto`,...le()]}],p:[{p:b()}],px:[{px:b()}],py:[{py:b()}],ps:[{ps:b()}],pe:[{pe:b()}],pbs:[{pbs:b()}],pbe:[{pbe:b()}],pt:[{pt:b()}],pr:[{pr:b()}],pb:[{pb:b()}],pl:[{pl:b()}],m:[{m:S()}],mx:[{mx:S()}],my:[{my:S()}],ms:[{ms:S()}],me:[{me:S()}],mbs:[{mbs:S()}],mbe:[{mbe:S()}],mt:[{mt:S()}],mr:[{mr:S()}],mb:[{mb:S()}],ml:[{ml:S()}],"space-x":[{"space-x":b()}],"space-x-reverse":[`space-x-reverse`],"space-y":[{"space-y":b()}],"space-y-reverse":[`space-y-reverse`],size:[{size:C()}],"inline-size":[{inline:[`auto`,...ue()]}],"min-inline-size":[{"min-inline":[`auto`,...ue()]}],"max-inline-size":[{"max-inline":[`none`,...ue()]}],"block-size":[{block:[`auto`,...de()]}],"min-block-size":[{"min-block":[`auto`,...de()]}],"max-block-size":[{"max-block":[`none`,...de()]}],w:[{w:[s,`screen`,...C()]}],"min-w":[{"min-w":[s,`screen`,`none`,...C()]}],"max-w":[{"max-w":[s,`screen`,`none`,`prose`,{screen:[o]},...C()]}],h:[{h:[`screen`,`lh`,...C()]}],"min-h":[{"min-h":[`screen`,`lh`,`none`,...C()]}],"max-h":[{"max-h":[`screen`,`lh`,...C()]}],"font-size":[{text:[`base`,n,js,ws]}],"font-smoothing":[`antialiased`,`subpixel-antialiased`],"font-style":[`italic`,`not-italic`],"font-weight":[{font:[r,Ls,Es]}],"font-stretch":[{"font-stretch":[`ultra-condensed`,`extra-condensed`,`condensed`,`semi-condensed`,`normal`,`semi-expanded`,`expanded`,`extra-expanded`,`ultra-expanded`,hs,Z]}],"font-family":[{font:[Ms,Ds,t]}],"font-features":[{"font-features":[Z]}],"fvn-normal":[`normal-nums`],"fvn-ordinal":[`ordinal`],"fvn-slashed-zero":[`slashed-zero`],"fvn-figure":[`lining-nums`,`oldstyle-nums`],"fvn-spacing":[`proportional-nums`,`tabular-nums`],"fvn-fraction":[`diagonal-fractions`,`stacked-fractions`],tracking:[{tracking:[i,Q,Z]}],"line-clamp":[{"line-clamp":[J,`none`,Q,Ts]}],leading:[{leading:[a,...b()]}],"list-image":[{"list-image":[`none`,Q,Z]}],"list-style-position":[{list:[`inside`,`outside`]}],"list-style-type":[{list:[`disc`,`decimal`,`none`,Q,Z]}],"text-alignment":[{text:[`left`,`center`,`right`,`justify`,`start`,`end`]}],"placeholder-color":[{placeholder:w()}],"text-color":[{text:w()}],"text-decoration":[`underline`,`overline`,`line-through`,`no-underline`],"text-decoration-style":[{decoration:[...he(),`wavy`]}],"text-decoration-thickness":[{decoration:[J,`from-font`,`auto`,Q,ws]}],"text-decoration-color":[{decoration:w()}],"underline-offset":[{"underline-offset":[J,`auto`,Q,Z]}],"text-transform":[`uppercase`,`lowercase`,`capitalize`,`normal-case`],"text-overflow":[`truncate`,`text-ellipsis`,`text-clip`],"text-wrap":[{text:[`wrap`,`nowrap`,`balance`,`pretty`]}],indent:[{indent:b()}],"tab-size":[{tab:[Y,Q,Z]}],"vertical-align":[{align:[`baseline`,`top`,`middle`,`bottom`,`text-top`,`text-bottom`,`sub`,`super`,Q,Z]}],whitespace:[{whitespace:[`normal`,`nowrap`,`pre`,`pre-line`,`pre-wrap`,`break-spaces`]}],break:[{break:[`normal`,`words`,`all`,`keep`]}],wrap:[{wrap:[`break-word`,`anywhere`,`normal`]}],hyphens:[{hyphens:[`none`,`manual`,`auto`]}],content:[{content:[`none`,Q,Z]}],"bg-attachment":[{bg:[`fixed`,`local`,`scroll`]}],"bg-clip":[{"bg-clip":[`border`,`padding`,`content`,`text`]}],"bg-origin":[{"bg-origin":[`border`,`padding`,`content`]}],"bg-position":[{bg:fe()}],"bg-repeat":[{bg:T()}],"bg-size":[{bg:pe()}],"bg-image":[{bg:[`none`,{linear:[{to:[`t`,`tr`,`r`,`br`,`b`,`bl`,`l`,`tl`]},Y,Q,Z],radial:[``,Q,Z],conic:[Y,Q,Z]},Fs,ks]}],"bg-color":[{bg:w()}],"gradient-from-pos":[{from:me()}],"gradient-via-pos":[{via:me()}],"gradient-to-pos":[{to:me()}],"gradient-from":[{from:w()}],"gradient-via":[{via:w()}],"gradient-to":[{to:w()}],rounded:[{rounded:E()}],"rounded-s":[{"rounded-s":E()}],"rounded-e":[{"rounded-e":E()}],"rounded-t":[{"rounded-t":E()}],"rounded-r":[{"rounded-r":E()}],"rounded-b":[{"rounded-b":E()}],"rounded-l":[{"rounded-l":E()}],"rounded-ss":[{"rounded-ss":E()}],"rounded-se":[{"rounded-se":E()}],"rounded-ee":[{"rounded-ee":E()}],"rounded-es":[{"rounded-es":E()}],"rounded-tl":[{"rounded-tl":E()}],"rounded-tr":[{"rounded-tr":E()}],"rounded-br":[{"rounded-br":E()}],"rounded-bl":[{"rounded-bl":E()}],"border-w":[{border:D()}],"border-w-x":[{"border-x":D()}],"border-w-y":[{"border-y":D()}],"border-w-s":[{"border-s":D()}],"border-w-e":[{"border-e":D()}],"border-w-bs":[{"border-bs":D()}],"border-w-be":[{"border-be":D()}],"border-w-t":[{"border-t":D()}],"border-w-r":[{"border-r":D()}],"border-w-b":[{"border-b":D()}],"border-w-l":[{"border-l":D()}],"divide-x":[{"divide-x":D()}],"divide-x-reverse":[`divide-x-reverse`],"divide-y":[{"divide-y":D()}],"divide-y-reverse":[`divide-y-reverse`],"border-style":[{border:[...he(),`hidden`,`none`]}],"divide-style":[{divide:[...he(),`hidden`,`none`]}],"border-color":[{border:w()}],"border-color-x":[{"border-x":w()}],"border-color-y":[{"border-y":w()}],"border-color-s":[{"border-s":w()}],"border-color-e":[{"border-e":w()}],"border-color-bs":[{"border-bs":w()}],"border-color-be":[{"border-be":w()}],"border-color-t":[{"border-t":w()}],"border-color-r":[{"border-r":w()}],"border-color-b":[{"border-b":w()}],"border-color-l":[{"border-l":w()}],"divide-color":[{divide:w()}],"outline-style":[{outline:[...he(),`none`,`hidden`]}],"outline-offset":[{"outline-offset":[J,Q,Z]}],"outline-w":[{outline:[``,J,js,ws]}],"outline-color":[{outline:w()}],shadow:[{shadow:[``,`none`,u,Is,As]}],"shadow-color":[{shadow:w()}],"inset-shadow":[{"inset-shadow":[`none`,d,Is,As]}],"inset-shadow-color":[{"inset-shadow":w()}],"ring-w":[{ring:D()}],"ring-w-inset":[`ring-inset`],"ring-color":[{ring:w()}],"ring-offset-w":[{"ring-offset":[J,ws]}],"ring-offset-color":[{"ring-offset":w()}],"inset-ring-w":[{"inset-ring":D()}],"inset-ring-color":[{"inset-ring":w()}],"text-shadow":[{"text-shadow":[`none`,f,Is,As]}],"text-shadow-color":[{"text-shadow":w()}],opacity:[{opacity:[J,Q,Z]}],"mix-blend":[{"mix-blend":[...ge(),`plus-darker`,`plus-lighter`]}],"bg-blend":[{"bg-blend":ge()}],"mask-clip":[{"mask-clip":[`border`,`padding`,`content`,`fill`,`stroke`,`view`]},`mask-no-clip`],"mask-composite":[{mask:[`add`,`subtract`,`intersect`,`exclude`]}],"mask-image-linear-pos":[{"mask-linear":[J]}],"mask-image-linear-from-pos":[{"mask-linear-from":O()}],"mask-image-linear-to-pos":[{"mask-linear-to":O()}],"mask-image-linear-from-color":[{"mask-linear-from":w()}],"mask-image-linear-to-color":[{"mask-linear-to":w()}],"mask-image-t-from-pos":[{"mask-t-from":O()}],"mask-image-t-to-pos":[{"mask-t-to":O()}],"mask-image-t-from-color":[{"mask-t-from":w()}],"mask-image-t-to-color":[{"mask-t-to":w()}],"mask-image-r-from-pos":[{"mask-r-from":O()}],"mask-image-r-to-pos":[{"mask-r-to":O()}],"mask-image-r-from-color":[{"mask-r-from":w()}],"mask-image-r-to-color":[{"mask-r-to":w()}],"mask-image-b-from-pos":[{"mask-b-from":O()}],"mask-image-b-to-pos":[{"mask-b-to":O()}],"mask-image-b-from-color":[{"mask-b-from":w()}],"mask-image-b-to-color":[{"mask-b-to":w()}],"mask-image-l-from-pos":[{"mask-l-from":O()}],"mask-image-l-to-pos":[{"mask-l-to":O()}],"mask-image-l-from-color":[{"mask-l-from":w()}],"mask-image-l-to-color":[{"mask-l-to":w()}],"mask-image-x-from-pos":[{"mask-x-from":O()}],"mask-image-x-to-pos":[{"mask-x-to":O()}],"mask-image-x-from-color":[{"mask-x-from":w()}],"mask-image-x-to-color":[{"mask-x-to":w()}],"mask-image-y-from-pos":[{"mask-y-from":O()}],"mask-image-y-to-pos":[{"mask-y-to":O()}],"mask-image-y-from-color":[{"mask-y-from":w()}],"mask-image-y-to-color":[{"mask-y-to":w()}],"mask-image-radial":[{"mask-radial":[Q,Z]}],"mask-image-radial-from-pos":[{"mask-radial-from":O()}],"mask-image-radial-to-pos":[{"mask-radial-to":O()}],"mask-image-radial-from-color":[{"mask-radial-from":w()}],"mask-image-radial-to-color":[{"mask-radial-to":w()}],"mask-image-radial-shape":[{"mask-radial":[`circle`,`ellipse`]}],"mask-image-radial-size":[{"mask-radial":[{closest:[`side`,`corner`],farthest:[`side`,`corner`]}]}],"mask-image-radial-pos":[{"mask-radial-at":ne()}],"mask-image-conic-pos":[{"mask-conic":[J]}],"mask-image-conic-from-pos":[{"mask-conic-from":O()}],"mask-image-conic-to-pos":[{"mask-conic-to":O()}],"mask-image-conic-from-color":[{"mask-conic-from":w()}],"mask-image-conic-to-color":[{"mask-conic-to":w()}],"mask-mode":[{mask:[`alpha`,`luminance`,`match`]}],"mask-origin":[{"mask-origin":[`border`,`padding`,`content`,`fill`,`stroke`,`view`]}],"mask-position":[{mask:fe()}],"mask-repeat":[{mask:T()}],"mask-size":[{mask:pe()}],"mask-type":[{"mask-type":[`alpha`,`luminance`]}],"mask-image":[{mask:[`none`,Q,Z]}],filter:[{filter:[``,`none`,Q,Z]}],blur:[{blur:_e()}],brightness:[{brightness:[J,Q,Z]}],contrast:[{contrast:[J,Q,Z]}],"drop-shadow":[{"drop-shadow":[``,`none`,p,Is,As]}],"drop-shadow-color":[{"drop-shadow":w()}],grayscale:[{grayscale:[``,J,Q,Z]}],"hue-rotate":[{"hue-rotate":[J,Q,Z]}],invert:[{invert:[``,J,Q,Z]}],saturate:[{saturate:[J,Q,Z]}],sepia:[{sepia:[``,J,Q,Z]}],"backdrop-filter":[{"backdrop-filter":[``,`none`,Q,Z]}],"backdrop-blur":[{"backdrop-blur":_e()}],"backdrop-brightness":[{"backdrop-brightness":[J,Q,Z]}],"backdrop-contrast":[{"backdrop-contrast":[J,Q,Z]}],"backdrop-grayscale":[{"backdrop-grayscale":[``,J,Q,Z]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[J,Q,Z]}],"backdrop-invert":[{"backdrop-invert":[``,J,Q,Z]}],"backdrop-opacity":[{"backdrop-opacity":[J,Q,Z]}],"backdrop-saturate":[{"backdrop-saturate":[J,Q,Z]}],"backdrop-sepia":[{"backdrop-sepia":[``,J,Q,Z]}],"border-collapse":[{border:[`collapse`,`separate`]}],"border-spacing":[{"border-spacing":b()}],"border-spacing-x":[{"border-spacing-x":b()}],"border-spacing-y":[{"border-spacing-y":b()}],"table-layout":[{table:[`auto`,`fixed`]}],caption:[{caption:[`top`,`bottom`]}],transition:[{transition:[``,`all`,`colors`,`opacity`,`shadow`,`transform`,`none`,Q,Z]}],"transition-behavior":[{transition:[`normal`,`discrete`]}],duration:[{duration:[J,`initial`,Q,Z]}],ease:[{ease:[`linear`,`initial`,_,Q,Z]}],delay:[{delay:[J,Q,Z]}],animate:[{animate:[`none`,ee,Q,Z]}],backface:[{backface:[`hidden`,`visible`]}],perspective:[{perspective:[h,Q,Z]}],"perspective-origin":[{"perspective-origin":re()}],rotate:[{rotate:k()}],"rotate-x":[{"rotate-x":k()}],"rotate-y":[{"rotate-y":k()}],"rotate-z":[{"rotate-z":k()}],scale:[{scale:A()}],"scale-x":[{"scale-x":A()}],"scale-y":[{"scale-y":A()}],"scale-z":[{"scale-z":A()}],"scale-3d":[`scale-3d`],skew:[{skew:ve()}],"skew-x":[{"skew-x":ve()}],"skew-y":[{"skew-y":ve()}],transform:[{transform:[Q,Z,``,`none`,`gpu`,`cpu`]}],"transform-origin":[{origin:re()}],"transform-style":[{transform:[`3d`,`flat`]}],translate:[{translate:j()}],"translate-x":[{"translate-x":j()}],"translate-y":[{"translate-y":j()}],"translate-z":[{"translate-z":j()}],"translate-none":[`translate-none`],zoom:[{zoom:[Y,Q,Z]}],accent:[{accent:w()}],appearance:[{appearance:[`none`,`auto`]}],"caret-color":[{caret:w()}],"color-scheme":[{scheme:[`normal`,`dark`,`light`,`light-dark`,`only-dark`,`only-light`]}],cursor:[{cursor:[`auto`,`default`,`pointer`,`wait`,`text`,`move`,`help`,`not-allowed`,`none`,`context-menu`,`progress`,`cell`,`crosshair`,`vertical-text`,`alias`,`copy`,`no-drop`,`grab`,`grabbing`,`all-scroll`,`col-resize`,`row-resize`,`n-resize`,`e-resize`,`s-resize`,`w-resize`,`ne-resize`,`nw-resize`,`se-resize`,`sw-resize`,`ew-resize`,`ns-resize`,`nesw-resize`,`nwse-resize`,`zoom-in`,`zoom-out`,Q,Z]}],"field-sizing":[{"field-sizing":[`fixed`,`content`]}],"pointer-events":[{"pointer-events":[`auto`,`none`]}],resize:[{resize:[`none`,``,`y`,`x`]}],"scroll-behavior":[{scroll:[`auto`,`smooth`]}],"scrollbar-thumb-color":[{"scrollbar-thumb":w()}],"scrollbar-track-color":[{"scrollbar-track":w()}],"scrollbar-gutter":[{"scrollbar-gutter":[`auto`,`stable`,`both`]}],"scrollbar-w":[{scrollbar:[`auto`,`thin`,`none`]}],"scroll-m":[{"scroll-m":b()}],"scroll-mx":[{"scroll-mx":b()}],"scroll-my":[{"scroll-my":b()}],"scroll-ms":[{"scroll-ms":b()}],"scroll-me":[{"scroll-me":b()}],"scroll-mbs":[{"scroll-mbs":b()}],"scroll-mbe":[{"scroll-mbe":b()}],"scroll-mt":[{"scroll-mt":b()}],"scroll-mr":[{"scroll-mr":b()}],"scroll-mb":[{"scroll-mb":b()}],"scroll-ml":[{"scroll-ml":b()}],"scroll-p":[{"scroll-p":b()}],"scroll-px":[{"scroll-px":b()}],"scroll-py":[{"scroll-py":b()}],"scroll-ps":[{"scroll-ps":b()}],"scroll-pe":[{"scroll-pe":b()}],"scroll-pbs":[{"scroll-pbs":b()}],"scroll-pbe":[{"scroll-pbe":b()}],"scroll-pt":[{"scroll-pt":b()}],"scroll-pr":[{"scroll-pr":b()}],"scroll-pb":[{"scroll-pb":b()}],"scroll-pl":[{"scroll-pl":b()}],"snap-align":[{snap:[`start`,`end`,`center`,`align-none`]}],"snap-stop":[{snap:[`normal`,`always`]}],"snap-type":[{snap:[`none`,`x`,`y`,`both`]}],"snap-strictness":[{snap:[`mandatory`,`proximity`]}],touch:[{touch:[`auto`,`none`,`manipulation`]}],"touch-x":[{"touch-pan":[`x`,`left`,`right`]}],"touch-y":[{"touch-pan":[`y`,`up`,`down`]}],"touch-pz":[`touch-pinch-zoom`],select:[{select:[`none`,`text`,`all`,`auto`]}],"will-change":[{"will-change":[`auto`,`scroll`,`contents`,`transform`,Q,Z]}],fill:[{fill:[`none`,...w()]}],"stroke-w":[{stroke:[J,js,ws,Ts]}],stroke:[{stroke:[`none`,...w()]}],"forced-color-adjust":[{"forced-color-adjust":[`auto`,`none`]}]},conflictingClassGroups:{"container-named":[`container-type`],overflow:[`overflow-x`,`overflow-y`],overscroll:[`overscroll-x`,`overscroll-y`],inset:[`inset-x`,`inset-y`,`inset-bs`,`inset-be`,`start`,`end`,`top`,`right`,`bottom`,`left`],"inset-x":[`right`,`left`],"inset-y":[`top`,`bottom`],flex:[`basis`,`grow`,`shrink`],gap:[`gap-x`,`gap-y`],p:[`px`,`py`,`ps`,`pe`,`pbs`,`pbe`,`pt`,`pr`,`pb`,`pl`],px:[`pr`,`pl`],py:[`pt`,`pb`],m:[`mx`,`my`,`ms`,`me`,`mbs`,`mbe`,`mt`,`mr`,`mb`,`ml`],mx:[`mr`,`ml`],my:[`mt`,`mb`],size:[`w`,`h`],"font-size":[`leading`],"fvn-normal":[`fvn-ordinal`,`fvn-slashed-zero`,`fvn-figure`,`fvn-spacing`,`fvn-fraction`],"fvn-ordinal":[`fvn-normal`],"fvn-slashed-zero":[`fvn-normal`],"fvn-figure":[`fvn-normal`],"fvn-spacing":[`fvn-normal`],"fvn-fraction":[`fvn-normal`],"line-clamp":[`display`,`overflow`],rounded:[`rounded-s`,`rounded-e`,`rounded-t`,`rounded-r`,`rounded-b`,`rounded-l`,`rounded-ss`,`rounded-se`,`rounded-ee`,`rounded-es`,`rounded-tl`,`rounded-tr`,`rounded-br`,`rounded-bl`],"rounded-s":[`rounded-ss`,`rounded-es`],"rounded-e":[`rounded-se`,`rounded-ee`],"rounded-t":[`rounded-tl`,`rounded-tr`],"rounded-r":[`rounded-tr`,`rounded-br`],"rounded-b":[`rounded-br`,`rounded-bl`],"rounded-l":[`rounded-tl`,`rounded-bl`],"border-spacing":[`border-spacing-x`,`border-spacing-y`],"border-w":[`border-w-x`,`border-w-y`,`border-w-s`,`border-w-e`,`border-w-bs`,`border-w-be`,`border-w-t`,`border-w-r`,`border-w-b`,`border-w-l`],"border-w-x":[`border-w-r`,`border-w-l`],"border-w-y":[`border-w-t`,`border-w-b`],"border-color":[`border-color-x`,`border-color-y`,`border-color-s`,`border-color-e`,`border-color-bs`,`border-color-be`,`border-color-t`,`border-color-r`,`border-color-b`,`border-color-l`],"border-color-x":[`border-color-r`,`border-color-l`],"border-color-y":[`border-color-t`,`border-color-b`],translate:[`translate-x`,`translate-y`,`translate-none`],"translate-none":[`translate`,`translate-x`,`translate-y`,`translate-z`],"scroll-m":[`scroll-mx`,`scroll-my`,`scroll-ms`,`scroll-me`,`scroll-mbs`,`scroll-mbe`,`scroll-mt`,`scroll-mr`,`scroll-mb`,`scroll-ml`],"scroll-mx":[`scroll-mr`,`scroll-ml`],"scroll-my":[`scroll-mt`,`scroll-mb`],"scroll-p":[`scroll-px`,`scroll-py`,`scroll-ps`,`scroll-pe`,`scroll-pbs`,`scroll-pbe`,`scroll-pt`,`scroll-pr`,`scroll-pb`,`scroll-pl`],"scroll-px":[`scroll-pr`,`scroll-pl`],"scroll-py":[`scroll-pt`,`scroll-pb`],touch:[`touch-x`,`touch-y`,`touch-pz`],"touch-x":[`touch`],"touch-y":[`touch`],"touch-pz":[`touch`]},conflictingClassGroupModifiers:{"font-size":[`leading`]},postfixLookupClassGroups:[`container-type`],orderSensitiveModifiers:[`*`,`**`,`after`,`backdrop`,`before`,`details-content`,`file`,`first-letter`,`first-line`,`marker`,`placeholder`,`selection`]}});function $(...e){return Js(Wi(e))}function Ys(e,t=!1,n=`bits`){if(e==null||!Number.isFinite(e))return`—`;let r=n===`bytes`?e/8:e,i=[{threshold:1e9,divisor:1e9,label:n===`bytes`?`GB/s`:`Gbps`},{threshold:1e6,divisor:1e6,label:n===`bytes`?`MB/s`:`Mbps`},{threshold:1e3,divisor:1e3,label:n===`bytes`?`kB/s`:`Kbps`}].find(e=>r>=e.threshold);if(!i)return`${Math.round(r)} ${n===`bytes`?`B/s`:`bps`}`;let a=r/i.divisor;return`${a.toFixed(a>=100||t?0:1)} ${i.label}`}function Xs(e){return e==null||!Number.isFinite(e)?`—`:`${e<10?e.toFixed(1):e.toFixed(0)} ms`}function Zs(e){return e==null||!Number.isFinite(e)?`—`:`${e.toFixed(+(e<10))}%`}function Qs(e){if(e==null||!Number.isFinite(e))return`—`;let t=[`B`,`KB`,`MB`,`GB`,`TB`],n=e,r=0;for(;n>=1e3&&r=100?0:1)} ${t[r]??`B`}`}function $s(e,t=!1,n){if(!e)return`—`;let r=oo(e);return ma(r)?n?new Intl.DateTimeFormat(`en-GB`,{day:`2-digit`,month:`short`,year:`numeric`,hour:`2-digit`,minute:`2-digit`,...t?{second:`2-digit`}:{},hourCycle:`h23`,timeZone:n}).format(r):no(r,t?`dd MMM yyyy, HH:mm:ss`:`dd MMM yyyy, HH:mm`):`—`}function ec(e){if(!e)return`Not scheduled`;let t=oo(e);return ma(t)?ao(t,{addSuffix:!0}):`Unknown`}function tc(e){return e===`ookla`?`Ookla`:e===`librespeed`?`LibreSpeed`:`Cloudflare`}var nc={ookla:`#67e8f9`,librespeed:`#a78bfa`,cloudflare:`#fb923c`},rc={queued:`neutral`,validating:`info`,running:`info`,succeeded:`success`,failed:`danger`,skipped:`warning`,cancelled:`neutral`};async function ic(e){await navigator.clipboard.writeText(e)}function ac(e,t){let n=document.createElement(`a`);n.href=e,n.download=t,n.rel=`noopener`,document.body.append(n),n.click(),n.remove()}function oc(e){if(!e)return null;try{let t=new URL(e);return t.protocol===`http:`||t.protocol===`https:`?t.href:null}catch{return null}}function sc(e,t,n){return{from:cc(e,n),to:lc(t,n)}}function cc(e,t){return uc(e,t)}function lc(e,t){return uc(dc(e,1),t)}function uc(e,t){let n=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!n)throw Error(`Invalid reporting date: ${e}`);let r=Date.UTC(Number(n[1]),Number(n[2])-1,Number(n[3])),i=r,a=new Intl.DateTimeFormat(`en-US`,{timeZone:t,year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hourCycle:`h23`});for(let e=0;e<4;e+=1){let e=Object.fromEntries(a.formatToParts(new Date(i)).filter(e=>e.type!==`literal`).map(e=>[e.type,Number(e.value)])),t=r-Date.UTC(e.year??0,(e.month??1)-1,e.day??1,e.hour??0,e.minute??0,e.second??0);if(i+=t,t===0)break}return new Date(i).toISOString()}function dc(e,t){let n=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!n)throw Error(`Invalid reporting date: ${e}`);return new Date(Date.UTC(Number(n[1]),Number(n[2])-1,Number(n[3])+t)).toISOString().slice(0,10)}var fc={primary:`bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm shadow-cyan-900/20`,secondary:`bg-secondary text-secondary-foreground hover:bg-secondary/80`,ghost:`bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground`,outline:`border border-border bg-background text-foreground hover:bg-accent`,danger:`bg-destructive text-destructive-foreground hover:bg-destructive/90`},pc={sm:`h-8 gap-1.5 rounded-md px-2.5 text-xs`,md:`h-10 gap-2 rounded-lg px-4 text-sm`,lg:`h-11 gap-2 rounded-lg px-5 text-sm`,icon:`h-9 w-9 rounded-lg`};function mc({asChild:e=!1,className:t,variant:n=`primary`,size:r=`md`,type:i=`button`,...a}){return(0,A.jsx)(e?Ue:`button`,{className:$(`inline-flex shrink-0 items-center justify-center whitespace-nowrap font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50`,fc[n],pc[r],t),type:e?void 0:i,...a})}var hc=[{href:`/`,label:`Dashboard`,icon:ai,subtitle:`Live overview`},{href:`/tasks`,label:`Tasks`,icon:ci,subtitle:`Schedules & targets`},{href:`/results`,label:`Results`,icon:ei,subtitle:`Test history`},{href:`/statistics`,label:`Statistics`,icon:ti,subtitle:`Trends & percentiles`},{href:`/comparison`,label:`WAN comparison`,icon:ii,subtitle:`Path performance`},{href:`/network`,label:`Network & routes`,icon:fi,subtitle:`Bindings & validation`},{href:`/settings`,label:`Settings`,icon:mi,subtitle:`Runtime defaults`},{href:`/system`,label:`System information`,icon:si,subtitle:`Health & versions`}],gc={"/":{title:`Operations overview`,eyebrow:`Multi-WAN observability`},"/tasks":{title:`Speed-test tasks`,eyebrow:`Independent schedules`},"/tasks/new":{title:`Create task`,eyebrow:`Measurement configuration`},"/results":{title:`Measurement results`,eyebrow:`History & diagnostics`},"/statistics":{title:`Performance statistics`,eyebrow:`Aggregates & trends`},"/comparison":{title:`WAN comparison`,eyebrow:`Side-by-side analysis`},"/network":{title:`Network & routes`,eyebrow:`Read-only path validation`},"/settings":{title:`Application settings`,eyebrow:`Defaults & retention`},"/system":{title:`System information`,eyebrow:`Runtime health`}};function _c(){return(0,A.jsxs)(v,{to:`/`,className:`group flex items-center gap-3 rounded-lg focus-visible:ring-2 focus-visible:ring-ring`,"aria-label":`MultiSpeed dashboard`,children:[(0,A.jsx)(`span`,{className:`relative grid h-10 w-10 shrink-0 place-items-center overflow-hidden rounded-xl border border-cyan-400/20 bg-slate-950 shadow-lg shadow-cyan-950/30`,children:(0,A.jsxs)(`span`,{className:`flex h-5 items-end gap-[3px]`,"aria-hidden":`true`,children:[(0,A.jsx)(`span`,{className:`h-2 w-1 rounded-full bg-cyan-300`}),(0,A.jsx)(`span`,{className:`h-4 w-1 rounded-full bg-cyan-400`}),(0,A.jsx)(`span`,{className:`h-3 w-1 rounded-full bg-violet-400`}),(0,A.jsx)(`span`,{className:`h-5 w-1 rounded-full bg-orange-400`})]})}),(0,A.jsxs)(`span`,{children:[(0,A.jsx)(`span`,{className:`block text-[17px] font-bold tracking-[-0.03em] text-foreground`,children:`MultiSpeed`}),(0,A.jsx)(`span`,{className:`block text-[10px] font-semibold uppercase tracking-[.15em] text-muted-foreground`,children:`Path telemetry`})]})]})}function vc({onNavigate:e}){return(0,A.jsx)(`nav`,{className:`space-y-1`,"aria-label":`Primary navigation`,children:hc.map(({href:t,label:n,icon:r,subtitle:i})=>(0,A.jsx)(v,{to:t,end:t===`/`,onClick:e,className:({isActive:e})=>$(`group flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm transition focus-visible:ring-2 focus-visible:ring-ring`,e?`bg-accent font-semibold text-accent-foreground`:`text-muted-foreground hover:bg-muted hover:text-foreground`),children:({isActive:e})=>(0,A.jsxs)(A.Fragment,{children:[(0,A.jsx)(r,{className:$(`h-4 w-4 shrink-0`,e?`text-primary`:`text-muted-foreground group-hover:text-foreground`)}),(0,A.jsxs)(`span`,{className:`min-w-0`,children:[(0,A.jsx)(`span`,{className:`block leading-4`,children:n}),(0,A.jsx)(`span`,{className:$(`mt-0.5 block text-[10px] font-normal leading-3`,e?`text-accent-foreground/65`:`text-muted-foreground/70`),children:i})]})]})},t))})}function yc({mode:e,setMode:t}){return(0,A.jsx)(`div`,{className:`flex rounded-lg border border-border bg-muted/50 p-1`,"aria-label":`Color theme`,children:[{value:`light`,label:`Light theme`,icon:hi},{value:`dark`,label:`Dark theme`,icon:di},{value:`system`,label:`System theme`,icon:ei}].map(({value:n,label:r,icon:i})=>(0,A.jsx)(`button`,{type:`button`,onClick:()=>t(n),className:$(`grid h-7 flex-1 place-items-center rounded-md transition focus-visible:ring-2 focus-visible:ring-ring`,e===n?`bg-card text-foreground shadow-sm`:`text-muted-foreground hover:text-foreground`),"aria-label":r,"aria-pressed":e===n,children:(0,A.jsx)(i,{className:`h-3.5 w-3.5`})},n))})}function bc({state:e}){let t=e===`connected`;return(0,A.jsxs)(`div`,{className:`flex items-center gap-2 rounded-lg border border-border bg-background px-2.5 py-2 text-[11px] font-medium text-muted-foreground`,title:`Server-sent events connection`,children:[(0,A.jsx)(`span`,{className:$(`relative h-2 w-2 rounded-full`,t?`bg-emerald-500`:e===`unavailable`?`bg-rose-500`:`bg-amber-500`),children:t?(0,A.jsx)(`span`,{className:`absolute inset-0 animate-ping rounded-full bg-emerald-400 opacity-50`}):null}),t?`Live`:e===`unavailable`?`Offline`:`Reconnecting`]})}function xc({children:e}){let{pathname:t}=ae(),{state:n}=Ri(),{theme:r,setTheme:i}=Hi(),[a,o]=(0,C.useState)(!1),s=t.startsWith(`/tasks/`)?{title:t.endsWith(`/new`)?`Create task`:`Edit task`,eyebrow:`Measurement configuration`}:t.startsWith(`/results/`)?{title:`Result diagnostics`,eyebrow:`Measurement detail`}:gc[t]??{title:`MultiSpeed`,eyebrow:`Infrastructure observability`};return(0,A.jsxs)(`div`,{className:`min-h-screen bg-background`,children:[(0,A.jsxs)(`aside`,{className:`fixed inset-y-0 left-0 z-40 hidden w-64 border-r border-border bg-card/95 px-4 py-5 lg:flex lg:flex-col`,children:[(0,A.jsx)(_c,{}),(0,A.jsx)(`div`,{className:`mt-7 flex-1 overflow-y-auto scrollbar-thin`,children:(0,A.jsx)(vc,{})}),(0,A.jsxs)(`div`,{className:`mt-4 space-y-3 border-t border-border pt-4`,children:[(0,A.jsx)(bc,{state:n}),(0,A.jsx)(yc,{mode:r,setMode:i}),(0,A.jsx)(`p`,{className:`px-1 text-[10px] leading-4 text-muted-foreground`,children:`No authentication. Keep this console on a trusted network.`})]})]}),(0,A.jsxs)(`div`,{className:`lg:pl-64`,children:[(0,A.jsx)(`header`,{className:`sticky top-0 z-30 border-b border-border bg-background/90 backdrop-blur-xl`,children:(0,A.jsxs)(`div`,{className:`flex h-16 items-center gap-3 px-4 sm:px-6 lg:px-8`,children:[(0,A.jsxs)(Sr,{open:a,onOpenChange:o,children:[(0,A.jsx)(wr,{asChild:!0,children:(0,A.jsx)(mc,{size:`icon`,variant:`ghost`,className:`lg:hidden`,"aria-label":`Open navigation`,children:(0,A.jsx)(ui,{className:`h-5 w-5`})})}),(0,A.jsxs)(Or,{children:[(0,A.jsx)(Ar,{className:`fixed inset-0 z-50 bg-slate-950/60 backdrop-blur-sm`}),(0,A.jsxs)(Pr,{className:`fixed inset-y-0 left-0 z-50 w-[min(88vw,19rem)] border-r border-border bg-card p-5 shadow-2xl outline-none`,children:[(0,A.jsx)(zr,{className:`sr-only`,children:`Navigation`}),(0,A.jsx)(Vr,{className:`sr-only`,children:`Navigate between MultiSpeed screens`}),(0,A.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,A.jsx)(_c,{}),(0,A.jsx)(Ur,{asChild:!0,children:(0,A.jsx)(mc,{size:`icon`,variant:`ghost`,"aria-label":`Close navigation`,children:(0,A.jsx)(_i,{className:`h-4 w-4`})})})]}),(0,A.jsx)(`div`,{className:`mt-7`,children:(0,A.jsx)(vc,{onNavigate:()=>o(!1)})}),(0,A.jsxs)(`div`,{className:`absolute inset-x-5 bottom-5 space-y-3`,children:[(0,A.jsx)(bc,{state:n}),(0,A.jsx)(yc,{mode:r,setMode:i})]})]})]})]}),(0,A.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,A.jsx)(`p`,{className:`truncate text-[10px] font-bold uppercase tracking-[.13em] text-primary`,children:s.eyebrow}),(0,A.jsx)(`h1`,{className:`truncate text-base font-semibold tracking-tight text-foreground`,children:s.title})]}),(0,A.jsx)(`div`,{className:`lg:hidden`,children:(0,A.jsx)(bc,{state:n})})]})}),(0,A.jsxs)(`main`,{className:`relative min-h-[calc(100vh-4rem)] overflow-hidden`,children:[(0,A.jsx)(`div`,{className:`page-grid pointer-events-none absolute inset-0`,"aria-hidden":`true`}),(0,A.jsx)(`div`,{className:`relative mx-auto w-full max-w-[1600px] px-4 py-5 sm:px-6 sm:py-7 lg:px-8 lg:py-8`,children:e})]}),(0,A.jsxs)(`footer`,{className:`border-t border-border px-4 py-3 text-center text-[10px] leading-5 text-muted-foreground sm:px-6 lg:px-8`,children:[`Cloudflare`,`®`,` is a trademark of Cloudflare, Inc. Ookla`,`®`,` and Speedtest`,`®`,` are registered trademarks of Ookla, LLC. MultiSpeed is an independent project and is not affiliated with, endorsed by, or sponsored by either company.`]})]})]})}function Sc({className:e}){return(0,A.jsx)(li,{className:$(`h-4 w-4 animate-spin`,e),"aria-hidden":`true`})}function Cc({label:e=`Loading data…`,compact:t=!1}){return(0,A.jsxs)(`div`,{className:$(`flex items-center justify-center gap-2 text-sm text-muted-foreground`,t?`py-6`:`min-h-52 py-12`),children:[(0,A.jsx)(Sc,{}),(0,A.jsx)(`span`,{children:e})]})}function wc({icon:e,title:t,description:n,action:r,compact:i=!1}){return(0,A.jsxs)(`div`,{className:$(`flex flex-col items-center justify-center px-5 text-center`,i?`py-8`:`min-h-56 py-12`),children:[(0,A.jsx)(`div`,{className:`mb-3 flex h-10 w-10 items-center justify-center rounded-xl border border-border bg-muted text-muted-foreground`,children:e??(0,A.jsx)(oi,{className:`h-5 w-5`})}),(0,A.jsx)(`h3`,{className:`text-sm font-semibold text-foreground`,children:t}),(0,A.jsx)(`p`,{className:`mt-1 max-w-md text-xs leading-5 text-muted-foreground`,children:n}),r?(0,A.jsx)(`div`,{className:`mt-4`,children:r}):null]})}function Tc({error:e,onRetry:t,title:n=`Unable to load data`,compact:r=!1}){return(0,A.jsxs)(`div`,{className:$(`flex flex-col items-center justify-center px-5 text-center`,r?`py-8`:`min-h-56 py-12`),role:`alert`,children:[(0,A.jsx)(`div`,{className:`mb-3 flex h-10 w-10 items-center justify-center rounded-xl border border-rose-500/20 bg-rose-500/10 text-rose-500`,children:(0,A.jsx)(gi,{className:`h-5 w-5`})}),(0,A.jsx)(`h3`,{className:`text-sm font-semibold text-foreground`,children:n}),(0,A.jsx)(`p`,{className:`mt-1 max-w-md text-xs leading-5 text-muted-foreground`,children:Pi(e)}),t?(0,A.jsxs)(mc,{className:`mt-4`,variant:`outline`,size:`sm`,onClick:t,children:[(0,A.jsx)(pi,{className:`h-3.5 w-3.5`}),`Try again`]}):null]})}var Ec=(0,C.lazy)(()=>x(()=>import(`./DashboardPage-BfyYS-Fi.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13]))),Dc=(0,C.lazy)(()=>x(()=>import(`./TasksPage-BEXsLscb.js`),__vite__mapDeps([14,1,2,3,15,16,17,7,18,19,9,12,5,13,20,21]))),Oc=(0,C.lazy)(()=>x(()=>import(`./TaskEditorPage-BwxKINaO.js`),__vite__mapDeps([22,1,2,3,23,15,24,25,26,27,8,12,20,28,18,29,9,5,13,30]))),kc=(0,C.lazy)(()=>x(()=>import(`./ResultsPage-CwUSS1pP.js`),__vite__mapDeps([31,1,2,3,15,32,26,16,19,8,9,5,13,12,20,21]))),Ac=(0,C.lazy)(()=>x(()=>import(`./ResultDetailPage-Dj2B-oxr.js`),__vite__mapDeps([33,1,2,3,4,5,6,23,34,24,26,28,19,8,9,13,12,20]))),jc=(0,C.lazy)(()=>x(()=>import(`./StatisticsPage-D4N2MQUQ.js`),__vite__mapDeps([35,1,2,4,5,15,16,8,9,10,11]))),Mc=(0,C.lazy)(()=>x(()=>import(`./ComparisonPage-Cp7FUxCh.js`),__vite__mapDeps([36,1,2,6,15,8,9,10,11,12,5]))),Nc=(0,C.lazy)(()=>x(()=>import(`./NetworkPage-EmZpqL4-.js`),__vite__mapDeps([37,1,2,15,38,34,17,7,39,19,8,9,12,5,30,20]))),Pc=(0,C.lazy)(()=>x(()=>import(`./SettingsPage-BK6TpFP7.js`),__vite__mapDeps([40,1,2,3,15,41,32,25,26,27,8,12,20,39,19,9,5,30]))),Fc=(0,C.lazy)(()=>x(()=>import(`./SystemPage-DV8hzwFf.js`),__vite__mapDeps([42,2,1,4,5,38,24,41,29,27,9,12]))),Ic=(0,C.lazy)(()=>x(()=>import(`./NotFoundPage-DK5iVdeX.js`),__vite__mapDeps([43,2,1,3,23,5])));function Lc(){return(0,A.jsx)(xc,{children:(0,A.jsx)(C.Suspense,{fallback:(0,A.jsx)(Cc,{label:`Opening workspace…`}),children:(0,A.jsxs)(re,{children:[(0,A.jsx)(y,{path:`/`,element:(0,A.jsx)(Ec,{})}),(0,A.jsx)(y,{path:`/tasks`,element:(0,A.jsx)(Dc,{})}),(0,A.jsx)(y,{path:`/tasks/new`,element:(0,A.jsx)(Oc,{})}),(0,A.jsx)(y,{path:`/tasks/:taskId/edit`,element:(0,A.jsx)(Oc,{})}),(0,A.jsx)(y,{path:`/results`,element:(0,A.jsx)(kc,{})}),(0,A.jsx)(y,{path:`/results/:resultId`,element:(0,A.jsx)(Ac,{})}),(0,A.jsx)(y,{path:`/statistics`,element:(0,A.jsx)(jc,{})}),(0,A.jsx)(y,{path:`/comparison`,element:(0,A.jsx)(Mc,{})}),(0,A.jsx)(y,{path:`/network`,element:(0,A.jsx)(Nc,{})}),(0,A.jsx)(y,{path:`/settings`,element:(0,A.jsx)(Pc,{})}),(0,A.jsx)(y,{path:`/system`,element:(0,A.jsx)(Fc,{})}),(0,A.jsx)(y,{path:`/dashboard`,element:(0,A.jsx)(b,{to:`/`,replace:!0})}),(0,A.jsx)(y,{path:`*`,element:(0,A.jsx)(Ic,{})})]})})})}var Rc=new S({defaultOptions:{queries:{staleTime:15e3,retry:(e,t)=>t instanceof Error&&`status`in t&&typeof t.status==`number`&&t.status<500?!1:e<2,refetchOnWindowFocus:!1},mutations:{retry:!1}}}),zc={tasks:[`tasks`],task:e=>[`tasks`,e],results:(e={})=>[`results`,e],dashboardSummary:[`results`,`dashboard-summary`],result:e=>[`results`,e],statistics:(e={})=>[`statistics`,e],interfaces:[`interfaces`],routes:[`route-profiles`],providers:[`providers`],ooklaBinary:[`providers`,`ookla`,`binary`],settings:[`settings`],system:[`system`]},Bc=(0,C.createContext)(null);function Vc({children:e}){let[t,n]=(0,C.useState)([]),r=(0,C.useRef)(1),i=(0,C.useCallback)(e=>n(t=>t.filter(t=>t.id!==e)),[]),a=(0,C.useCallback)(e=>{let t=r.current++;n(n=>[...n.slice(-3),{...e,id:t}]),window.setTimeout(()=>i(t),5e3)},[i]),o=(0,C.useMemo)(()=>({toast:a}),[a]);return(0,A.jsxs)(Bc.Provider,{value:o,children:[e,(0,A.jsx)(`div`,{className:`pointer-events-none fixed bottom-5 right-5 z-[100] flex w-[calc(100%-2.5rem)] max-w-sm flex-col gap-2`,"aria-live":`polite`,children:t.map(e=>{let t=e.tone===`success`?ri:e.tone===`error`?ni:si;return(0,A.jsxs)(`div`,{className:`pointer-events-auto flex items-start gap-3 rounded-xl border border-border bg-card p-4 text-card-foreground shadow-2xl`,children:[(0,A.jsx)(t,{className:$(`mt-0.5 h-4 w-4 shrink-0`,e.tone===`success`?`text-emerald-500`:e.tone===`error`?`text-rose-500`:`text-cyan-500`)}),(0,A.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,A.jsx)(`p`,{className:`text-sm font-semibold`,children:e.title}),e.description?(0,A.jsx)(`p`,{className:`mt-1 text-xs leading-5 text-muted-foreground`,children:e.description}):null]}),(0,A.jsx)(`button`,{type:`button`,onClick:()=>i(e.id),className:`rounded p-1 text-muted-foreground hover:bg-accent`,"aria-label":`Dismiss notification`,children:(0,A.jsx)(_i,{className:`h-3.5 w-3.5`})})]},e.id)})})]})}function Hc(){let e=(0,C.useContext)(Bc);if(!e)throw Error(`useToast must be used within ToastProvider`);return e}var Uc=localStorage.getItem(`multispeed-theme`),Wc=Uc===`dark`||(Uc===null||Uc===`system`)&&window.matchMedia(`(prefers-color-scheme: dark)`).matches;document.documentElement.classList.toggle(`dark`,Wc);var Gc=document.getElementById(`root`);if(!Gc)throw Error(`Application root is missing`);(0,ue.createRoot)(Gc).render((0,A.jsx)(C.StrictMode,{children:(0,A.jsx)(r,{client:Rc,children:(0,A.jsx)(ie,{children:(0,A.jsx)(Vc,{children:(0,A.jsx)(Lc,{})})})})}));export{st as $,ci as A,Vr as B,rc as C,gi as D,_i as E,ei as F,br as G,Or as H,H as I,Zt as J,gr as K,Sr as L,ri as M,ni as N,pi as O,ti as P,mt as Q,Ur as R,oc as S,Pi as T,zr as U,Ar as V,wr as W,Lt as X,Bt as Y,Tt as Z,nc as _,Cc as a,M as at,sc as b,$ as c,T as ct,Ys as d,P as et,Qs as f,ec as g,Zs as h,Tc as i,Ee as it,ai as j,fi as k,ic as l,Xs as m,zc as n,He as nt,Sc as o,be as ot,$s as p,sr as q,wc as r,Fe as rt,mc as s,k as st,Hc as t,it as tt,ac as u,tc as v,wi as w,cc as x,lc as y,Pr as z}; \ No newline at end of file +`},nr=0,rr=[];function ir(e){var t=C.useRef([]),n=C.useRef([0,0]),r=C.useRef(),i=C.useState(nr++)[0],a=C.useState(En)[0],o=C.useRef(e);C.useEffect(function(){o.current=e},[e]),C.useEffect(function(){if(e.inert){document.body.classList.add(`block-interactivity-${i}`);var t=en([e.lockRef.current],(e.shards||[]).map($n),!0).filter(Boolean);return t.forEach(function(e){return e.classList.add(`allow-interactivity-${i}`)}),function(){document.body.classList.remove(`block-interactivity-${i}`),t.forEach(function(e){return e.classList.remove(`allow-interactivity-${i}`)})}}},[e.inert,e.lockRef.current,e.shards]);var s=C.useCallback(function(e,t){if(`touches`in e&&e.touches.length===2||e.type===`wheel`&&e.ctrlKey)return!o.current.allowPinchZoom;var i=Zn(e),a=n.current,s=`deltaX`in e?e.deltaX:a[0]-i[0],c=`deltaY`in e?e.deltaY:a[1]-i[1],l,u=e.target,d=Math.abs(s)>Math.abs(c)?`h`:`v`;if(`touches`in e&&d===`h`&&u.type===`range`)return!1;var f=window.getSelection(),p=f&&f.anchorNode;if(p&&(p===u||p.contains(u)))return!1;var m=Wn(d,u);if(!m)return!0;if(m?l=d:(l=d===`v`?`h`:`v`,m=Wn(d,u)),!m)return!1;if(!r.current&&`changedTouches`in e&&(s||c)&&(r.current=l),!l)return!0;var h=r.current||l;return Xn(h,t,e,h===`h`?s:c,!0)},[]),c=C.useCallback(function(e){var n=e;if(!(!rr.length||rr[rr.length-1]!==a)){var r=`deltaY`in n?Qn(n):Zn(n),i=t.current.filter(function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&er(e.delta,r)})[0];if(i&&i.should){n.cancelable&&n.preventDefault();return}if(!i){var c=(o.current.shards||[]).map($n).filter(Boolean).filter(function(e){return e.contains(n.target)});(c.length>0?s(n,c[0]):!o.current.noIsolation)&&n.cancelable&&n.preventDefault()}}},[]),l=C.useCallback(function(e,n,r,i){var a={name:e,delta:n,target:r,should:i,shadowParent:ar(r)};t.current.push(a),setTimeout(function(){t.current=t.current.filter(function(e){return e!==a})},1)},[]),u=C.useCallback(function(e){n.current=Zn(e),r.current=void 0},[]),d=C.useCallback(function(t){l(t.type,Qn(t),t.target,s(t,e.lockRef.current))},[]),f=C.useCallback(function(t){l(t.type,Zn(t),t.target,s(t,e.lockRef.current))},[]);C.useEffect(function(){return rr.push(a),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener(`wheel`,c,zn),document.addEventListener(`touchmove`,c,zn),document.addEventListener(`touchstart`,u,zn),function(){rr=rr.filter(function(e){return e!==a}),document.removeEventListener(`wheel`,c,zn),document.removeEventListener(`touchmove`,c,zn),document.removeEventListener(`touchstart`,u,zn)}},[]);var p=e.removeScrollBar,m=e.inert;return C.createElement(C.Fragment,null,m?C.createElement(a,{styles:tr(i)}):null,p?C.createElement(In,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function ar(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}var or=hn(gn,ir),sr=C.forwardRef(function(e,t){return C.createElement(vn,z({},e,{ref:t,sideCar:or}))});sr.classNames=vn.classNames;var cr=function(e){return typeof document>`u`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},lr=new WeakMap,ur=new WeakMap,dr={},fr=0,pr=function(e){return e&&(e.host||pr(e.parentNode))},mr=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=pr(t);return n&&e.contains(n)?n:(console.error(`aria-hidden`,t,`in not contained inside`,e,`. Doing nothing`),null)}).filter(function(e){return!!e})},hr=function(e,t,n,r){var i=mr(t,Array.isArray(e)?e:[e]);dr[n]||(dr[n]=new WeakMap);var a=dr[n],o=[],s=new Set,c=new Set(i),l=function(e){!e||s.has(e)||(s.add(e),l(e.parentNode))};i.forEach(l);var u=function(e){!e||c.has(e)||Array.prototype.forEach.call(e.children,function(e){if(s.has(e))u(e);else try{var t=e.getAttribute(r),i=t!==null&&t!==`false`,c=(lr.get(e)||0)+1,l=(a.get(e)||0)+1;lr.set(e,c),a.set(e,l),o.push(e),c===1&&i&&ur.set(e,!0),l===1&&e.setAttribute(n,`true`),i||e.setAttribute(r,`true`)}catch(t){console.error(`aria-hidden: cannot operate on `,e,t)}})};return u(t),s.clear(),fr++,function(){o.forEach(function(e){var t=lr.get(e)-1,i=a.get(e)-1;lr.set(e,t),a.set(e,i),t||(ur.has(e)||e.removeAttribute(r),ur.delete(e)),i||e.removeAttribute(n)}),fr--,fr||(lr=new WeakMap,lr=new WeakMap,ur=new WeakMap,dr={})}},gr=function(e,t,n){n===void 0&&(n=`data-aria-hidden`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||cr(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(`[aria-live], script`))),hr(r,i,n,`aria-hidden`)):function(){return null}},_r=Object.defineProperty,B=(e,t)=>_r(e,`name`,{value:t,configurable:!0}),vr=`Dialog`,[yr,br]=be(vr),[xr,V]=yr(vr),Sr=B(e=>{let{__scopeDialog:t,children:n,open:r,defaultOpen:i,onOpenChange:a,modal:o=!0}=e,s=C.useRef(null),c=C.useRef(null),[l,u]=Fe({prop:r,defaultProp:i??!1,onChange:a,caller:vr}),[d,f]=C.useState(0),[p,m]=C.useState(0);return(0,A.jsx)(xr,{scope:t,triggerRef:s,contentRef:c,contentId:Ee(),titleId:Ee(),descriptionId:Ee(),titlePresent:d>0,descriptionPresent:p>0,setTitleCount:f,setDescriptionCount:m,open:l,onOpenChange:u,onOpenToggle:C.useCallback(()=>u(e=>!e),[u]),modal:o,children:n})},`Dialog`),Cr=`DialogTrigger`,wr=C.forwardRef(B(function(e,t){let{__scopeDialog:n,...r}=e,i=V(Cr,n),a=k(t,i.triggerRef);return(0,A.jsx)(P.button,{type:`button`,"aria-haspopup":`dialog`,"aria-expanded":i.open,"aria-controls":i.open?i.contentId:void 0,"data-state":Wr(i.open),...r,ref:a,onClick:T(e.onClick,i.onOpenToggle)})},`DialogTrigger`)),Tr=`DialogPortal`,[Er,Dr]=yr(Tr,{forceMount:void 0}),Or=B(e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=V(Tr,t);return(0,A.jsx)(Er,{scope:t,forceMount:n,children:C.Children.map(r,e=>(0,A.jsx)(Bt,{present:n||a.open,children:(0,A.jsx)(Lt,{asChild:!0,container:i,children:e})}))})},`DialogPortal`),kr=`DialogOverlay`,Ar=C.forwardRef(B(function(e,t){let n=Dr(kr,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=V(kr,e.__scopeDialog);return a.modal?(0,A.jsx)(Bt,{present:r||a.open,children:(0,A.jsx)(Mr,{...i,ref:t})}):null},`DialogOverlay`)),jr=He(`DialogOverlay.RemoveScroll`),Mr=C.forwardRef(B(function(e,t){let{__scopeDialog:n,...r}=e,i=V(kr,n),a=k(t,ht());return(0,A.jsx)(sr,{as:jr,allowPinchZoom:!0,shards:[i.contentRef],children:(0,A.jsx)(P.div,{"data-state":Wr(i.open),...r,ref:a,style:{pointerEvents:`auto`,...r.style}})})},`DialogOverlayImpl`)),Nr=`DialogContent`,Pr=C.forwardRef(B(function(e,t){let n=Dr(Nr,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=V(Nr,e.__scopeDialog);return(0,A.jsx)(Bt,{present:r||a.open,children:a.modal?(0,A.jsx)(Fr,{...i,ref:t}):(0,A.jsx)(Ir,{...i,ref:t})})},`DialogContent`)),Fr=C.forwardRef(B(function(e,t){let n=V(Nr,e.__scopeDialog),r=C.useRef(null),i=k(t,n.contentRef,r);return C.useEffect(()=>{let e=r.current;if(e)return gr(e)},[]),(0,A.jsx)(Lr,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:T(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:T(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:T(e.onFocusOutside,e=>e.preventDefault())})},`DialogContentModal`)),Ir=C.forwardRef(B(function(e,t){let n=V(Nr,e.__scopeDialog),r=C.useRef(!1),i=C.useRef(!1);return(0,A.jsx)(Lr,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===`pointerdown`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===`focusin`&&i.current&&t.preventDefault()}})},`DialogContentNonModal`)),Lr=C.forwardRef(B(function(e,t){let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,s=V(Nr,n);return Zt(),(0,A.jsx)(A.Fragment,{children:(0,A.jsx)(Tt,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,A.jsx)(mt,{role:`dialog`,id:s.contentId,"aria-describedby":s.descriptionPresent?s.descriptionId:void 0,"aria-labelledby":s.titlePresent?s.titleId:void 0,"data-state":Wr(s.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>s.onOpenChange(!1)})})})},`DialogContentImpl`)),Rr=`DialogTitle`,zr=C.forwardRef(B(function(e,t){let{__scopeDialog:n,...r}=e,i=V(Rr,n),{setTitleCount:a}=i;return M(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,A.jsx)(P.h2,{id:i.titleId,...r,ref:t})},`DialogTitle`)),Br=`DialogDescription`,Vr=C.forwardRef(B(function(e,t){let{__scopeDialog:n,...r}=e,i=V(Br,n),{setDescriptionCount:a}=i;return M(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,A.jsx)(P.p,{id:i.descriptionId,...r,ref:t})},`DialogDescription`)),Hr=`DialogClose`,Ur=C.forwardRef(B(function(e,t){let{__scopeDialog:n,...r}=e,i=V(Hr,n);return(0,A.jsx)(P.button,{type:`button`,...r,ref:t,onClick:T(e.onClick,()=>i.onOpenChange(!1))})},`DialogClose`));function Wr(e){return e?`open`:`closed`}B(Wr,`getState`);var Gr=(...e)=>e.filter((e,t,n)=>!!e&&e.trim()!==``&&n.indexOf(e)===t).join(` `).trim(),Kr=e=>e.replace(/([a-z0-9])([A-Z])/g,`$1-$2`).toLowerCase(),qr=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,n)=>n?n.toUpperCase():t.toLowerCase()),Jr=e=>{let t=qr(e);return t.charAt(0).toUpperCase()+t.slice(1)},Yr={xmlns:`http://www.w3.org/2000/svg`,width:24,height:24,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:2,strokeLinecap:`round`,strokeLinejoin:`round`},Xr=e=>{for(let t in e)if(t.startsWith(`aria-`)||t===`role`||t===`title`)return!0;return!1},Zr=(0,C.createContext)({}),Qr=()=>(0,C.useContext)(Zr),$r=(0,C.forwardRef)(({color:e,size:t,strokeWidth:n,absoluteStrokeWidth:r,className:i=``,children:a,iconNode:o,...s},c)=>{let{size:l=24,strokeWidth:u=2,absoluteStrokeWidth:d=!1,color:f=`currentColor`,className:p=``}=Qr()??{},m=r??d?Number(n??u)*24/Number(t??l):n??u;return(0,C.createElement)(`svg`,{ref:c,...Yr,width:t??l??Yr.width,height:t??l??Yr.height,stroke:e??f,strokeWidth:m,className:Gr(`lucide`,p,i),...!a&&!Xr(s)&&{"aria-hidden":`true`},...s},[...o.map(([e,t])=>(0,C.createElement)(e,t)),...Array.isArray(a)?a:[a]])}),H=(e,t)=>{let n=(0,C.forwardRef)(({className:n,...r},i)=>(0,C.createElement)($r,{ref:i,iconNode:t,className:Gr(`lucide-${Kr(Jr(e))}`,`lucide-${e}`,n),...r}));return n.displayName=Jr(e),n},ei=H(`activity`,[[`path`,{d:`M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2`,key:`169zse`}]]),ti=H(`chart-column`,[[`path`,{d:`M3 3v16a2 2 0 0 0 2 2h16`,key:`c24i48`}],[`path`,{d:`M18 17V9`,key:`2bz60n`}],[`path`,{d:`M13 17V5`,key:`1frdt8`}],[`path`,{d:`M8 17v-3`,key:`17ska0`}]]),ni=H(`circle-alert`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`line`,{x1:`12`,x2:`12`,y1:`8`,y2:`12`,key:`1pkeuh`}],[`line`,{x1:`12`,x2:`12.01`,y1:`16`,y2:`16`,key:`4dfq90`}]]),ri=H(`circle-check`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]),ii=H(`columns-3`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M9 3v18`,key:`fh3hqa`}],[`path`,{d:`M15 3v18`,key:`14nvp0`}]]),ai=H(`gauge`,[[`path`,{d:`m12 14 4-4`,key:`9kzdfg`}],[`path`,{d:`M3.34 19a10 10 0 1 1 17.32 0`,key:`19p75a`}]]),oi=H(`inbox`,[[`polyline`,{points:`22 12 16 12 14 15 10 15 8 12 2 12`,key:`o97t9d`}],[`path`,{d:`M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z`,key:`oot6mr`}]]),si=H(`info`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 16v-4`,key:`1dtifu`}],[`path`,{d:`M12 8h.01`,key:`e9boi3`}]]),ci=H(`list-checks`,[[`path`,{d:`M13 5h8`,key:`a7qcls`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}],[`path`,{d:`m3 17 2 2 4-4`,key:`1jhpwq`}],[`path`,{d:`m3 7 2 2 4-4`,key:`1obspn`}]]),li=H(`loader-circle`,[[`path`,{d:`M21 12a9 9 0 1 1-6.219-8.56`,key:`13zald`}]]),ui=H(`menu`,[[`path`,{d:`M4 5h16`,key:`1tepv9`}],[`path`,{d:`M4 12h16`,key:`1lakjw`}],[`path`,{d:`M4 19h16`,key:`1djgab`}]]),di=H(`moon`,[[`path`,{d:`M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401`,key:`kfwtm`}]]),fi=H(`network`,[[`rect`,{x:`16`,y:`16`,width:`6`,height:`6`,rx:`1`,key:`4q2zg0`}],[`rect`,{x:`2`,y:`16`,width:`6`,height:`6`,rx:`1`,key:`8cvhb9`}],[`rect`,{x:`9`,y:`2`,width:`6`,height:`6`,rx:`1`,key:`1egb70`}],[`path`,{d:`M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3`,key:`1jsf9p`}],[`path`,{d:`M12 12V8`,key:`2874zd`}]]),pi=H(`refresh-cw`,[[`path`,{d:`M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8`,key:`v9h5vc`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}],[`path`,{d:`M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16`,key:`3uifl3`}],[`path`,{d:`M8 16H3v5`,key:`1cv678`}]]),mi=H(`settings`,[[`path`,{d:`M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915`,key:`1i5ecw`}],[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}]]),hi=H(`sun`,[[`circle`,{cx:`12`,cy:`12`,r:`4`,key:`4exip2`}],[`path`,{d:`M12 2v2`,key:`tus03m`}],[`path`,{d:`M12 20v2`,key:`1lh1kg`}],[`path`,{d:`m4.93 4.93 1.41 1.41`,key:`149t6j`}],[`path`,{d:`m17.66 17.66 1.41 1.41`,key:`ptbguv`}],[`path`,{d:`M2 12h2`,key:`1t8f8n`}],[`path`,{d:`M20 12h2`,key:`1q8mjw`}],[`path`,{d:`m6.34 17.66-1.41 1.41`,key:`1m8zz5`}],[`path`,{d:`m19.07 4.93-1.41 1.41`,key:`1shlcs`}]]),gi=H(`triangle-alert`,[[`path`,{d:`m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3`,key:`wmoenq`}],[`path`,{d:`M12 9v4`,key:`juzpu7`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]),_i=H(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]),vi=`/api/v1`,yi=class extends Error{code;status;requestId;details;constructor(e,t,n=`REQUEST_FAILED`,r,i){super(e),this.name=`ApiError`,this.code=n,this.status=t,r!==void 0&&(this.requestId=r),i!==void 0&&(this.details=i)}};async function bi(e){try{let t=await e.json();if(t.error?.message)return new yi(t.error.message,e.status,t.error.code,t.error.requestId,t.error.details)}catch{}return new yi(e.statusText||`The request could not be completed.`,e.status)}async function U(e,t={}){let n=new Headers(t.headers);n.set(`Accept`,`application/json`),t.body!==void 0&&!(t.body instanceof FormData)&&n.set(`Content-Type`,`application/json`);let r=await fetch(`${vi}${e}`,{...t,headers:n,credentials:`same-origin`});if(!r.ok)throw await bi(r);if(r.status!==204)return await r.json()}function xi(e){let t=new URLSearchParams;Object.entries(e).forEach(([e,n])=>{n==null||n===``||Array.isArray(n)&&n.length===0||(Array.isArray(n)?n.forEach(n=>t.append(e,n)):t.set(e,String(n)))});let n=t.toString();return n?`?${n}`:``}function Si(e){return Array.isArray(e)?e:`items`in e&&Array.isArray(e.items)?e.items:`data`in e&&Array.isArray(e.data)?e.data:[]}async function Ci(e={}){let{interfaceName:t,...n}=e,r=await U(`/results${xi({...n,interface:t})}`);return Array.isArray(r)?{items:r,page:1,pageSize:r.length,totalItems:r.length,totalPages:1}:r}var wi={health:()=>U(`/healthz`),tasks:async()=>Si(await U(`/tasks`)),task:e=>U(`/tasks/${encodeURIComponent(e)}`),createTask:e=>U(`/tasks`,{method:`POST`,body:JSON.stringify(e)}),updateTask:(e,t)=>U(`/tasks/${encodeURIComponent(e)}`,{method:`PUT`,body:JSON.stringify(t)}),deleteTask:e=>U(`/tasks/${encodeURIComponent(e)}`,{method:`DELETE`}),runTask:e=>U(`/tasks/${encodeURIComponent(e)}/run`,{method:`POST`}),validateTask:e=>ki(`/tasks/${encodeURIComponent(e)}/validate`),validateTaskInput:e=>ki(`/tasks/validate`,e),duplicateTask:e=>U(`/tasks/${encodeURIComponent(e)}/duplicate`,{method:`POST`}),nextRuns:async e=>{let t=await U(`/tasks/${encodeURIComponent(e)}/next-runs`);return Array.isArray(t)?t:t.nextRuns??t.items??[]},results:Ci,dashboardSummary:()=>U(`/results/dashboard-summary`),result:e=>U(`/results/${encodeURIComponent(e)}`),deleteResult:e=>U(`/results/${encodeURIComponent(e)}`,{method:`DELETE`}),deleteResults:e=>U(`/results/delete-batch`,{method:`POST`,body:JSON.stringify({ids:e})}),statistics:e=>{let{interfaceName:t,...n}=e;return U(`/statistics${xi({...n,metric:void 0,interface:t})}`).then(Ei)},interfaces:async e=>Si(await U(`/interfaces${xi(e??{})}`)),refreshInterfaces:()=>U(`/interfaces/refresh`,{method:`POST`}),routes:async()=>Si(await U(`/route-profiles`)),route:e=>U(`/route-profiles/${encodeURIComponent(e)}`),createRoute:e=>U(`/route-profiles`,{method:`POST`,body:JSON.stringify(e)}),updateRoute:(e,t)=>U(`/route-profiles/${encodeURIComponent(e)}`,{method:`PUT`,body:JSON.stringify(t)}),deleteRoute:e=>U(`/route-profiles/${encodeURIComponent(e)}`,{method:`DELETE`}),validateRoute:e=>ki(`/route-profiles/${encodeURIComponent(e)}/validate`),providers:async()=>Si(await U(`/providers`)).map(Oi),ooklaBinaryStatus:()=>U(`/providers/ookla/binary`),uploadOoklaBinary:e=>Mi(e),providerServers:async(e,t)=>{let{interfaceName:n,...r}=t;return Si(await U(`/providers/${encodeURIComponent(e)}/servers${xi({...r,interface:n})}`))},validateServer:(e,t)=>U(`/providers/${encodeURIComponent(e)}/validate-server`,{method:`POST`,body:JSON.stringify(t)}),settings:()=>U(`/settings`),updateSettings:e=>U(`/settings`,{method:`PUT`,body:JSON.stringify(e)}),updateOoklaEula:(e,t)=>U(`/settings/ookla-eula`,{method:`PUT`,body:JSON.stringify({accepted:e,confirmed:t})}),exportConfiguration:Ni,importConfiguration:e=>U(`/config/import`,{method:`POST`,body:JSON.stringify(e)}),backup:ji,cleanupResults:e=>U(`/retention/cleanup`,{method:`POST`,body:JSON.stringify(e?{before:e}:{})}),system:async()=>{let e=await U(`/system`);return{...e,providers:Array.isArray(e.providers)?e.providers.map(Oi):[],interfaces:Array.isArray(e.interfaces)?e.interfaces.map(Ti):[],buildDate:e.buildDate||e.buildTime||``}},exportUrl:(e,t={})=>{let{interfaceName:n,...r}=t;return`${vi}/exports/results.${e}${xi({...r,interface:n})}`},eventsUrl:`${vi}/events`};function Ti(e){return{...e,addresses:Array.isArray(e.addresses)?e.addresses:[]}}function Ei(e){if(`groups`in e&&Array.isArray(e.groups)){let t=e.groups.map(e=>({id:e.key||`all`,name:e.label||`All results`,overall:Di(e.overall),buckets:e.buckets.map(e=>Di(e))})),n=Di(e.overall),r=!!e.groupBy;return{buckets:r?t.flatMap(e=>e.buckets.map(t=>({...t,label:`${e.name} / ${t.label??t.bucket}`}))):t[0]?.buckets??[],overall:n,summary:n,...r?{series:t}:{},timezone:e.reportingTimezone,...e.groupBy===void 0?{}:{groupBy:e.groupBy}}}let t=e;return{buckets:t.buckets??t.items??t.data??[],...t.overall===void 0?{}:{overall:t.overall},...t.totals===void 0?{}:{totals:t.totals},...t.summary===void 0?{}:{summary:t.summary},...t.series===void 0?{}:{series:t.series},...t.timezone===void 0?{}:{timezone:t.timezone}}}function Di(e){let t=e=>({min:e.minimum,max:e.maximum,average:e.average,median:e.median,p95:e.p95,standardDeviation:e.standardDeviation});return{bucket:e.start,...e.resultId===void 0?{}:{resultId:e.resultId},timestamp:e.start,label:e.label,sampleCount:e.counts.total,successfulCount:e.counts.successful,failedCount:e.counts.failed,skippedCount:e.counts.skipped,...e.successRatePercent===null?{}:{successRate:e.successRatePercent},download:t(e.metrics.downloadBitsPerSecond),upload:t(e.metrics.uploadBitsPerSecond),latency:t(e.metrics.latencyMilliseconds),jitter:t(e.metrics.jitterMilliseconds),packetLoss:t(e.metrics.packetLossPercent),duration:t(e.metrics.executionDurationMilliseconds)}}function Oi(e){let t=`availability`in e?e.availability:void 0;return{id:e.id,displayName:e.displayName,capabilities:e.capabilities,available:`available`in e&&typeof e.available==`boolean`?e.available:t?.available??!1,version:`version`in e&&typeof e.version==`string`?e.version:t?.version??``,message:`message`in e&&typeof e.message==`string`?e.message:t?.message??``}}async function ki(e,t){let n=new Headers({Accept:`application/json`});t!==void 0&&n.set(`Content-Type`,`application/json`);let r=await fetch(`${vi}${e}`,{method:`POST`,headers:n,...t===void 0?{}:{body:JSON.stringify(t)},credentials:`same-origin`});if(r.ok||r.status===422){let e=await r.json();if(`success`in e)return e;throw Ai(r,e)}throw await bi(r)}function Ai(e,t){return`error`in t?new yi(t.error.message,e.status,t.error.code,t.error.requestId,t.error.details):new yi(e.statusText||`Validation failed.`,e.status)}async function ji(){let e=await fetch(`${vi}/backup`,{method:`POST`,headers:{Accept:`application/vnd.sqlite3`},credentials:`same-origin`});if(!e.ok)throw await bi(e);let t=e.headers.get(`Content-Disposition`)??``,n=/filename="?([^";]+)"?/i.exec(t);return{blob:await e.blob(),filename:n?.[1]??`multispeed-backup.db`}}async function Mi(e){let t=await fetch(`${vi}/providers/ookla/binary`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/octet-stream`},body:e,credentials:`same-origin`});if(!t.ok)throw await bi(t);return await t.json()}async function Ni(){let e=await fetch(`${vi}/config/export`,{headers:{Accept:`application/json`},credentials:`same-origin`});if(!e.ok)throw await bi(e);let t=e.headers.get(`Content-Disposition`)??``,n=/filename="?([^";]+)"?/i.exec(t);return{blob:await e.blob(),filename:n?.[1]??`multispeed-config.json`}}function Pi(e){return e instanceof yi||e instanceof Error?e.message:`An unexpected error occurred.`}var Fi=new Set([`task.created`,`task.updated`,`task.enabled`,`task.disabled`,`task.deleted`,`task_created`,`task_updated`,`task_enabled`,`task_disabled`,`task_deleted`]),Ii=new Set([`run.queued`,`route.validation.started`,`route.validation.completed`,`test.started`,`test.completed`,`test.failed`,`test.skipped`,`test.cancelled`,`result.stored`,`run_queued`,`route_validation_started`,`route_validation_completed`,`test_started`,`test_completed`,`test_failed`,`test_skipped`,`new_result_stored`]),Li=[[`tasks`],[`results`],[`statistics`],[`interfaces`],[`route-profiles`],[`providers`],[`settings`],[`system`]];function Ri(){let e=ee(),[t,n]=(0,C.useState)(()=>typeof EventSource>`u`?`unavailable`:`connecting`),[r,i]=(0,C.useState)(null);return(0,C.useEffect)(()=>{if(typeof EventSource>`u`)return;let t=new EventSource(wi.eventsUrl,{withCredentials:!0});t.onopen=()=>{n(`connected`);for(let t of Li)e.invalidateQueries({queryKey:t})},t.onerror=()=>n(t.readyState===EventSource.CLOSED?`unavailable`:`reconnecting`);let r=t=>{try{let n=JSON.parse(t.data);!n.type&&t.type!==`message`&&(n.type=t.type),i(n),Fi.has(n.type)&&e.invalidateQueries({queryKey:[`tasks`]}),Ii.has(n.type)&&(e.invalidateQueries({queryKey:[`results`]}),e.invalidateQueries({queryKey:[`statistics`]}),e.invalidateQueries({queryKey:[`system`]})),(n.type===`interface.state.changed`||n.type===`interface_state_changed`)&&e.invalidateQueries({queryKey:[`interfaces`]}),(n.type.includes(`route.validation`)||n.type.includes(`route_validation`))&&e.invalidateQueries({queryKey:[`route-profiles`]})}catch{}};return t.onmessage=r,[...Fi,...Ii,`interface.state.changed`,`interface_state_changed`].forEach(e=>t.addEventListener(e,r)),()=>t.close()},[e]),{state:t,latestEvent:r}}var zi=`multispeed-theme`;function Bi(){return window.matchMedia(`(prefers-color-scheme: dark)`).matches}function Vi(e){let t=e===`dark`||e===`system`&&Bi();document.documentElement.classList.toggle(`dark`,t),document.documentElement.dataset.theme=e,document.querySelector(`meta[name="theme-color"]`)?.setAttribute(`content`,t?`#08111f`:`#f7f9fc`)}function Hi(){let[e,t]=(0,C.useState)(()=>{let e=localStorage.getItem(zi);return e===`light`||e===`dark`||e===`system`?e:`system`});return(0,C.useEffect)(()=>{Vi(e);let t=window.matchMedia(`(prefers-color-scheme: dark)`),n=()=>e===`system`&&Vi(e);return t.addEventListener(`change`,n),()=>t.removeEventListener(`change`,n)},[e]),{theme:e,setTheme:e=>{localStorage.setItem(zi,e),t(e)}}}function Ui(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=a.getTime()?r+1:n.getTime()>=s.getTime()?r:r-1}function oa(e){let t=G(e),n=new Date(Date.UTC(t.getFullYear(),t.getMonth(),t.getDate(),t.getHours(),t.getMinutes(),t.getSeconds(),t.getMilliseconds()));return n.setUTCFullYear(t.getFullYear()),e-+n}function sa(e,...t){let n=W.bind(null,e||t.find(e=>typeof e==`object`));return t.map(n)}function ca(e,t){let n=G(e,t?.in);return n.setHours(0,0,0,0),n}function la(e,t,n){let[r,i]=sa(n?.in,e,t),a=ca(r),o=ca(i),s=+a-oa(a),c=+o-oa(o);return Math.round((s-c)/qi)}function ua(e,t){let n=aa(e,t),r=W(t?.in||e,0);return r.setFullYear(n,0,4),r.setHours(0,0,0,0),ia(r)}function da(e,t){let n=G(e)-+G(t);return n<0?-1:n>0?1:n}function fa(e){return W(e,Date.now())}function pa(e){return e instanceof Date||typeof e==`object`&&Object.prototype.toString.call(e)===`[object Date]`}function ma(e){return!(!pa(e)&&typeof e!=`number`||isNaN(+G(e)))}function ha(e){return t=>{let n=(e?Math[e]:Math.trunc)(t);return n===0?0:n}}function ga(e,t){let n=G(e,t?.in);return n.setFullYear(n.getFullYear(),0,1),n.setHours(0,0,0,0),n}var _a={lessThanXSeconds:{one:`less than a second`,other:`less than {{count}} seconds`},xSeconds:{one:`1 second`,other:`{{count}} seconds`},halfAMinute:`half a minute`,lessThanXMinutes:{one:`less than a minute`,other:`less than {{count}} minutes`},xMinutes:{one:`1 minute`,other:`{{count}} minutes`},aboutXHours:{one:`about 1 hour`,other:`about {{count}} hours`},xHours:{one:`1 hour`,other:`{{count}} hours`},xDays:{one:`1 day`,other:`{{count}} days`},aboutXWeeks:{one:`about 1 week`,other:`about {{count}} weeks`},xWeeks:{one:`1 week`,other:`{{count}} weeks`},aboutXMonths:{one:`about 1 month`,other:`about {{count}} months`},xMonths:{one:`1 month`,other:`{{count}} months`},aboutXYears:{one:`about 1 year`,other:`about {{count}} years`},xYears:{one:`1 year`,other:`{{count}} years`},overXYears:{one:`over 1 year`,other:`over {{count}} years`},almostXYears:{one:`almost 1 year`,other:`almost {{count}} years`}},va=(e,t,n)=>{let r,i=_a[e];return r=typeof i==`string`?i:t===1?i.one:i.other.replace(`{{count}}`,t.toString()),n?.addSuffix?n.comparison&&n.comparison>0?`in `+r:r+` ago`:r};function ya(e){return(t={})=>{let n=t.width?String(t.width):e.defaultWidth;return e.formats[n]||e.formats[e.defaultWidth]}}var ba={date:ya({formats:{full:`EEEE, MMMM do, y`,long:`MMMM do, y`,medium:`MMM d, y`,short:`MM/dd/yyyy`},defaultWidth:`full`}),time:ya({formats:{full:`h:mm:ss a zzzz`,long:`h:mm:ss a z`,medium:`h:mm:ss a`,short:`h:mm a`},defaultWidth:`full`}),dateTime:ya({formats:{full:`{{date}} 'at' {{time}}`,long:`{{date}} 'at' {{time}}`,medium:`{{date}}, {{time}}`,short:`{{date}}, {{time}}`},defaultWidth:`full`})},xa={lastWeek:`'last' eeee 'at' p`,yesterday:`'yesterday at' p`,today:`'today at' p`,tomorrow:`'tomorrow at' p`,nextWeek:`eeee 'at' p`,other:`P`},Sa=(e,t,n,r)=>xa[e];function Ca(e){return(t,n)=>{let r=n?.context?String(n.context):`standalone`,i;if(r===`formatting`&&e.formattingValues){let t=e.defaultFormattingWidth||e.defaultWidth,r=n?.width?String(n.width):t;i=e.formattingValues[r]||e.formattingValues[t]}else{let t=e.defaultWidth,r=n?.width?String(n.width):e.defaultWidth;i=e.values[r]||e.values[t]}let a=e.argumentCallback?e.argumentCallback(t):t;return i[a]}}var wa={ordinalNumber:(e,t)=>{let n=Number(e),r=n%100;if(r>20||r<10)switch(r%10){case 1:return n+`st`;case 2:return n+`nd`;case 3:return n+`rd`}return n+`th`},era:Ca({values:{narrow:[`B`,`A`],abbreviated:[`BC`,`AD`],wide:[`Before Christ`,`Anno Domini`]},defaultWidth:`wide`}),quarter:Ca({values:{narrow:[`1`,`2`,`3`,`4`],abbreviated:[`Q1`,`Q2`,`Q3`,`Q4`],wide:[`1st quarter`,`2nd quarter`,`3rd quarter`,`4th quarter`]},defaultWidth:`wide`,argumentCallback:e=>e-1}),month:Ca({values:{narrow:[`J`,`F`,`M`,`A`,`M`,`J`,`J`,`A`,`S`,`O`,`N`,`D`],abbreviated:[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`],wide:[`January`,`February`,`March`,`April`,`May`,`June`,`July`,`August`,`September`,`October`,`November`,`December`]},defaultWidth:`wide`}),day:Ca({values:{narrow:[`S`,`M`,`T`,`W`,`T`,`F`,`S`],short:[`Su`,`Mo`,`Tu`,`We`,`Th`,`Fr`,`Sa`],abbreviated:[`Sun`,`Mon`,`Tue`,`Wed`,`Thu`,`Fri`,`Sat`],wide:[`Sunday`,`Monday`,`Tuesday`,`Wednesday`,`Thursday`,`Friday`,`Saturday`]},defaultWidth:`wide`}),dayPeriod:Ca({values:{narrow:{am:`a`,pm:`p`,midnight:`mi`,noon:`n`,morning:`morning`,afternoon:`afternoon`,evening:`evening`,night:`night`},abbreviated:{am:`AM`,pm:`PM`,midnight:`midnight`,noon:`noon`,morning:`morning`,afternoon:`afternoon`,evening:`evening`,night:`night`},wide:{am:`a.m.`,pm:`p.m.`,midnight:`midnight`,noon:`noon`,morning:`morning`,afternoon:`afternoon`,evening:`evening`,night:`night`}},defaultWidth:`wide`,formattingValues:{narrow:{am:`a`,pm:`p`,midnight:`mi`,noon:`n`,morning:`in the morning`,afternoon:`in the afternoon`,evening:`in the evening`,night:`at night`},abbreviated:{am:`AM`,pm:`PM`,midnight:`midnight`,noon:`noon`,morning:`in the morning`,afternoon:`in the afternoon`,evening:`in the evening`,night:`at night`},wide:{am:`a.m.`,pm:`p.m.`,midnight:`midnight`,noon:`noon`,morning:`in the morning`,afternoon:`in the afternoon`,evening:`in the evening`,night:`at night`}},defaultFormattingWidth:`wide`})};function Ta(e){return(t,n={})=>{let r=n.width,i=r&&e.matchPatterns[r]||e.matchPatterns[e.defaultMatchWidth],a=t.match(i);if(!a)return null;let o=a[0],s=r&&e.parsePatterns[r]||e.parsePatterns[e.defaultParseWidth],c=Array.isArray(s)?Da(s,e=>e.test(o)):Ea(s,e=>e.test(o)),l;l=e.valueCallback?e.valueCallback(c):c,l=n.valueCallback?n.valueCallback(l):l;let u=t.slice(o.length);return{value:l,rest:u}}}function Ea(e,t){for(let n in e)if(Object.prototype.hasOwnProperty.call(e,n)&&t(e[n]))return n}function Da(e,t){for(let n=0;n{let r=t.match(e.matchPattern);if(!r)return null;let i=r[0],a=t.match(e.parsePattern);if(!a)return null;let o=e.valueCallback?e.valueCallback(a[0]):a[0];o=n.valueCallback?n.valueCallback(o):o;let s=t.slice(i.length);return{value:o,rest:s}}}var ka={code:`en-US`,formatDistance:va,formatLong:ba,formatRelative:Sa,localize:wa,match:{ordinalNumber:Oa({matchPattern:/^(\d+)(th|st|nd|rd)?/i,parsePattern:/\d+/i,valueCallback:e=>parseInt(e,10)}),era:Ta({matchPatterns:{narrow:/^(b|a)/i,abbreviated:/^(b\.?\s?c\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?)/i,wide:/^(before christ|before common era|anno domini|common era)/i},defaultMatchWidth:`wide`,parsePatterns:{any:[/^b/i,/^(a|c)/i]},defaultParseWidth:`any`}),quarter:Ta({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^q[1234]/i,wide:/^[1234](th|st|nd|rd)? quarter/i},defaultMatchWidth:`wide`,parsePatterns:{any:[/1/i,/2/i,/3/i,/4/i]},defaultParseWidth:`any`,valueCallback:e=>e+1}),month:Ta({matchPatterns:{narrow:/^[jfmasond]/i,abbreviated:/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i,wide:/^(january|february|march|april|may|june|july|august|september|october|november|december)/i},defaultMatchWidth:`wide`,parsePatterns:{narrow:[/^j/i,/^f/i,/^m/i,/^a/i,/^m/i,/^j/i,/^j/i,/^a/i,/^s/i,/^o/i,/^n/i,/^d/i],any:[/^ja/i,/^f/i,/^mar/i,/^ap/i,/^may/i,/^jun/i,/^jul/i,/^au/i,/^s/i,/^o/i,/^n/i,/^d/i]},defaultParseWidth:`any`}),day:Ta({matchPatterns:{narrow:/^[smtwf]/i,short:/^(su|mo|tu|we|th|fr|sa)/i,abbreviated:/^(sun|mon|tue|wed|thu|fri|sat)/i,wide:/^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)/i},defaultMatchWidth:`wide`,parsePatterns:{narrow:[/^s/i,/^m/i,/^t/i,/^w/i,/^t/i,/^f/i,/^s/i],any:[/^su/i,/^m/i,/^tu/i,/^w/i,/^th/i,/^f/i,/^sa/i]},defaultParseWidth:`any`}),dayPeriod:Ta({matchPatterns:{narrow:/^(a|p|mi|n|(in the|at) (morning|afternoon|evening|night))/i,any:/^([ap]\.?\s?m\.?|midnight|noon|(in the|at) (morning|afternoon|evening|night))/i},defaultMatchWidth:`any`,parsePatterns:{any:{am:/^a/i,pm:/^p/i,midnight:/^mi/i,noon:/^no/i,morning:/morning/i,afternoon:/afternoon/i,evening:/evening/i,night:/night/i}},defaultParseWidth:`any`})},options:{weekStartsOn:0,firstWeekContainsDate:1}};function Aa(e,t){let n=G(e,t?.in);return la(n,ga(n))+1}function ja(e,t){let n=G(e,t?.in),r=ia(n)-+ua(n);return Math.round(r/Ki)+1}function Ma(e,t){let n=G(e,t?.in),r=n.getFullYear(),i=na(),a=t?.firstWeekContainsDate??t?.locale?.options?.firstWeekContainsDate??i.firstWeekContainsDate??i.locale?.options?.firstWeekContainsDate??1,o=W(t?.in||e,0);o.setFullYear(r+1,0,a),o.setHours(0,0,0,0);let s=ra(o,t),c=W(t?.in||e,0);c.setFullYear(r,0,a),c.setHours(0,0,0,0);let l=ra(c,t);return+n>=+s?r+1:+n>=+l?r:r-1}function Na(e,t){let n=na(),r=t?.firstWeekContainsDate??t?.locale?.options?.firstWeekContainsDate??n.firstWeekContainsDate??n.locale?.options?.firstWeekContainsDate??1,i=Ma(e,t),a=W(t?.in||e,0);return a.setFullYear(i,0,r),a.setHours(0,0,0,0),ra(a,t)}function Pa(e,t){let n=G(e,t?.in),r=ra(n,t)-+Na(n,t);return Math.round(r/Ki)+1}function K(e,t){return(e<0?`-`:``)+Math.abs(e).toString().padStart(t,`0`)}var Fa={y(e,t){let n=e.getFullYear(),r=n>0?n:1-n;return K(t===`yy`?r%100:r,t.length)},M(e,t){let n=e.getMonth();return t===`M`?String(n+1):K(n+1,2)},d(e,t){return K(e.getDate(),t.length)},a(e,t){let n=e.getHours()/12>=1?`pm`:`am`;switch(t){case`a`:case`aa`:return n.toUpperCase();case`aaa`:return n;case`aaaaa`:return n[0];default:return n===`am`?`a.m.`:`p.m.`}},h(e,t){return K(e.getHours()%12||12,t.length)},H(e,t){return K(e.getHours(),t.length)},m(e,t){return K(e.getMinutes(),t.length)},s(e,t){return K(e.getSeconds(),t.length)},S(e,t){let n=t.length,r=e.getMilliseconds();return K(Math.trunc(r*10**(n-3)),t.length)}},Ia={am:`am`,pm:`pm`,midnight:`midnight`,noon:`noon`,morning:`morning`,afternoon:`afternoon`,evening:`evening`,night:`night`},La={G:function(e,t,n){let r=+(e.getFullYear()>0);switch(t){case`G`:case`GG`:case`GGG`:return n.era(r,{width:`abbreviated`});case`GGGGG`:return n.era(r,{width:`narrow`});default:return n.era(r,{width:`wide`})}},y:function(e,t,n){if(t===`yo`){let t=e.getFullYear(),r=t>0?t:1-t;return n.ordinalNumber(r,{unit:`year`})}return Fa.y(e,t)},Y:function(e,t,n,r){let i=Ma(e,r),a=i>0?i:1-i;return t===`YY`?K(a%100,2):t===`Yo`?n.ordinalNumber(a,{unit:`year`}):K(a,t.length)},R:function(e,t){return K(aa(e),t.length)},u:function(e,t){return K(e.getFullYear(),t.length)},Q:function(e,t,n){let r=Math.ceil((e.getMonth()+1)/3);switch(t){case`Q`:return String(r);case`QQ`:return K(r,2);case`Qo`:return n.ordinalNumber(r,{unit:`quarter`});case`QQQ`:return n.quarter(r,{width:`abbreviated`,context:`formatting`});case`QQQQQ`:return n.quarter(r,{width:`narrow`,context:`formatting`});default:return n.quarter(r,{width:`wide`,context:`formatting`})}},q:function(e,t,n){let r=Math.ceil((e.getMonth()+1)/3);switch(t){case`q`:return String(r);case`qq`:return K(r,2);case`qo`:return n.ordinalNumber(r,{unit:`quarter`});case`qqq`:return n.quarter(r,{width:`abbreviated`,context:`standalone`});case`qqqqq`:return n.quarter(r,{width:`narrow`,context:`standalone`});default:return n.quarter(r,{width:`wide`,context:`standalone`})}},M:function(e,t,n){let r=e.getMonth();switch(t){case`M`:case`MM`:return Fa.M(e,t);case`Mo`:return n.ordinalNumber(r+1,{unit:`month`});case`MMM`:return n.month(r,{width:`abbreviated`,context:`formatting`});case`MMMMM`:return n.month(r,{width:`narrow`,context:`formatting`});default:return n.month(r,{width:`wide`,context:`formatting`})}},L:function(e,t,n){let r=e.getMonth();switch(t){case`L`:return String(r+1);case`LL`:return K(r+1,2);case`Lo`:return n.ordinalNumber(r+1,{unit:`month`});case`LLL`:return n.month(r,{width:`abbreviated`,context:`standalone`});case`LLLLL`:return n.month(r,{width:`narrow`,context:`standalone`});default:return n.month(r,{width:`wide`,context:`standalone`})}},w:function(e,t,n,r){let i=Pa(e,r);return t===`wo`?n.ordinalNumber(i,{unit:`week`}):K(i,t.length)},I:function(e,t,n){let r=ja(e);return t===`Io`?n.ordinalNumber(r,{unit:`week`}):K(r,t.length)},d:function(e,t,n){return t===`do`?n.ordinalNumber(e.getDate(),{unit:`date`}):Fa.d(e,t)},D:function(e,t,n){let r=Aa(e);return t===`Do`?n.ordinalNumber(r,{unit:`dayOfYear`}):K(r,t.length)},E:function(e,t,n){let r=e.getDay();switch(t){case`E`:case`EE`:case`EEE`:return n.day(r,{width:`abbreviated`,context:`formatting`});case`EEEEE`:return n.day(r,{width:`narrow`,context:`formatting`});case`EEEEEE`:return n.day(r,{width:`short`,context:`formatting`});default:return n.day(r,{width:`wide`,context:`formatting`})}},e:function(e,t,n,r){let i=e.getDay(),a=(i-r.weekStartsOn+8)%7||7;switch(t){case`e`:return String(a);case`ee`:return K(a,2);case`eo`:return n.ordinalNumber(a,{unit:`day`});case`eee`:return n.day(i,{width:`abbreviated`,context:`formatting`});case`eeeee`:return n.day(i,{width:`narrow`,context:`formatting`});case`eeeeee`:return n.day(i,{width:`short`,context:`formatting`});default:return n.day(i,{width:`wide`,context:`formatting`})}},c:function(e,t,n,r){let i=e.getDay(),a=(i-r.weekStartsOn+8)%7||7;switch(t){case`c`:return String(a);case`cc`:return K(a,t.length);case`co`:return n.ordinalNumber(a,{unit:`day`});case`ccc`:return n.day(i,{width:`abbreviated`,context:`standalone`});case`ccccc`:return n.day(i,{width:`narrow`,context:`standalone`});case`cccccc`:return n.day(i,{width:`short`,context:`standalone`});default:return n.day(i,{width:`wide`,context:`standalone`})}},i:function(e,t,n){let r=e.getDay(),i=r===0?7:r;switch(t){case`i`:return String(i);case`ii`:return K(i,t.length);case`io`:return n.ordinalNumber(i,{unit:`day`});case`iii`:return n.day(r,{width:`abbreviated`,context:`formatting`});case`iiiii`:return n.day(r,{width:`narrow`,context:`formatting`});case`iiiiii`:return n.day(r,{width:`short`,context:`formatting`});default:return n.day(r,{width:`wide`,context:`formatting`})}},a:function(e,t,n){let r=e.getHours()/12>=1?`pm`:`am`;switch(t){case`a`:case`aa`:return n.dayPeriod(r,{width:`abbreviated`,context:`formatting`});case`aaa`:return n.dayPeriod(r,{width:`abbreviated`,context:`formatting`}).toLowerCase();case`aaaaa`:return n.dayPeriod(r,{width:`narrow`,context:`formatting`});default:return n.dayPeriod(r,{width:`wide`,context:`formatting`})}},b:function(e,t,n){let r=e.getHours(),i;switch(i=r===12?Ia.noon:r===0?Ia.midnight:r/12>=1?`pm`:`am`,t){case`b`:case`bb`:return n.dayPeriod(i,{width:`abbreviated`,context:`formatting`});case`bbb`:return n.dayPeriod(i,{width:`abbreviated`,context:`formatting`}).toLowerCase();case`bbbbb`:return n.dayPeriod(i,{width:`narrow`,context:`formatting`});default:return n.dayPeriod(i,{width:`wide`,context:`formatting`})}},B:function(e,t,n){let r=e.getHours(),i;switch(i=r>=17?Ia.evening:r>=12?Ia.afternoon:r>=4?Ia.morning:Ia.night,t){case`B`:case`BB`:case`BBB`:return n.dayPeriod(i,{width:`abbreviated`,context:`formatting`});case`BBBBB`:return n.dayPeriod(i,{width:`narrow`,context:`formatting`});default:return n.dayPeriod(i,{width:`wide`,context:`formatting`})}},h:function(e,t,n){if(t===`ho`){let t=e.getHours()%12;return t===0&&(t=12),n.ordinalNumber(t,{unit:`hour`})}return Fa.h(e,t)},H:function(e,t,n){return t===`Ho`?n.ordinalNumber(e.getHours(),{unit:`hour`}):Fa.H(e,t)},K:function(e,t,n){let r=e.getHours()%12;return t===`Ko`?n.ordinalNumber(r,{unit:`hour`}):K(r,t.length)},k:function(e,t,n){let r=e.getHours();return r===0&&(r=24),t===`ko`?n.ordinalNumber(r,{unit:`hour`}):K(r,t.length)},m:function(e,t,n){return t===`mo`?n.ordinalNumber(e.getMinutes(),{unit:`minute`}):Fa.m(e,t)},s:function(e,t,n){return t===`so`?n.ordinalNumber(e.getSeconds(),{unit:`second`}):Fa.s(e,t)},S:function(e,t){return Fa.S(e,t)},X:function(e,t,n){let r=e.getTimezoneOffset();if(r===0)return`Z`;switch(t){case`X`:return za(r);case`XXXX`:case`XX`:return Ba(r);default:return Ba(r,`:`)}},x:function(e,t,n){let r=e.getTimezoneOffset();switch(t){case`x`:return za(r);case`xxxx`:case`xx`:return Ba(r);default:return Ba(r,`:`)}},O:function(e,t,n){let r=e.getTimezoneOffset();switch(t){case`O`:case`OO`:case`OOO`:return`GMT`+Ra(r,`:`);default:return`GMT`+Ba(r,`:`)}},z:function(e,t,n){let r=e.getTimezoneOffset();switch(t){case`z`:case`zz`:case`zzz`:return`GMT`+Ra(r,`:`);default:return`GMT`+Ba(r,`:`)}},t:function(e,t,n){return K(Math.trunc(e/1e3),t.length)},T:function(e,t,n){return K(+e,t.length)}};function Ra(e,t=``){let n=e>0?`-`:`+`,r=Math.abs(e),i=Math.trunc(r/60),a=r%60;return a===0?n+String(i):n+String(i)+t+K(a,2)}function za(e,t){return e%60==0?(e>0?`-`:`+`)+K(Math.abs(e)/60,2):Ba(e,t)}function Ba(e,t=``){let n=e>0?`-`:`+`,r=Math.abs(e),i=K(Math.trunc(r/60),2),a=K(r%60,2);return n+i+t+a}var Va=(e,t)=>{switch(e){case`P`:return t.date({width:`short`});case`PP`:return t.date({width:`medium`});case`PPP`:return t.date({width:`long`});default:return t.date({width:`full`})}},Ha=(e,t)=>{switch(e){case`p`:return t.time({width:`short`});case`pp`:return t.time({width:`medium`});case`ppp`:return t.time({width:`long`});default:return t.time({width:`full`})}},Ua={p:Ha,P:(e,t)=>{let n=e.match(/(P+)(p+)?/)||[],r=n[1],i=n[2];if(!i)return Va(e,t);let a;switch(r){case`P`:a=t.dateTime({width:`short`});break;case`PP`:a=t.dateTime({width:`medium`});break;case`PPP`:a=t.dateTime({width:`long`});break;default:a=t.dateTime({width:`full`})}return a.replace(`{{date}}`,Va(r,t)).replace(`{{time}}`,Ha(i,t))}},Wa=/^D+$/,Ga=/^Y+$/,Ka=[`D`,`DD`,`YY`,`YYYY`];function qa(e){return Wa.test(e)}function Ja(e){return Ga.test(e)}function Ya(e,t,n){let r=Xa(e,t,n);if(console.warn(r),Ka.includes(e))throw RangeError(r)}function Xa(e,t,n){let r=e[0]===`Y`?`years`:`days of the month`;return`Use \`${e.toLowerCase()}\` instead of \`${e}\` (in \`${t}\`) for formatting ${r} to the input \`${n}\`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md`}var Za=/[yYQqMLwIdDecihHKkms]o|(\w)\1*|''|'(''|[^'])+('|$)|./g,Qa=/P+p+|P+|p+|''|'(''|[^'])+('|$)|./g,$a=/^'([^]*?)'?$/,eo=/''/g,to=/[a-zA-Z]/;function no(e,t,n){let r=na(),i=n?.locale??r.locale??ka,a=n?.firstWeekContainsDate??n?.locale?.options?.firstWeekContainsDate??r.firstWeekContainsDate??r.locale?.options?.firstWeekContainsDate??1,o=n?.weekStartsOn??n?.locale?.options?.weekStartsOn??r.weekStartsOn??r.locale?.options?.weekStartsOn??0,s=G(e,n?.in);if(!ma(s))throw RangeError(`Invalid time value`);let c=t.match(Qa).map(e=>{let t=e[0];if(t===`p`||t===`P`){let n=Ua[t];return n(e,i.formatLong)}return e}).join(``).match(Za).map(e=>{if(e===`''`)return{isToken:!1,value:`'`};let t=e[0];if(t===`'`)return{isToken:!1,value:ro(e)};if(La[t])return{isToken:!0,value:e};if(t.match(to))throw RangeError("Format string contains an unescaped latin alphabet character `"+t+"`");return{isToken:!1,value:e}});i.localize.preprocessor&&(c=i.localize.preprocessor(s,c));let l={firstWeekContainsDate:a,weekStartsOn:o,locale:i};return c.map(r=>{if(!r.isToken)return r.value;let a=r.value;(!n?.useAdditionalWeekYearTokens&&Ja(a)||!n?.useAdditionalDayOfYearTokens&&qa(a))&&Ya(a,t,String(e));let o=La[a[0]];return o(s,a,i.localize,l)}).join(``)}function ro(e){let t=e.match($a);return t?t[1].replace(eo,`'`):e}function io(e,t,n){let r=na(),i=n?.locale??r.locale??ka,a=da(e,t);if(isNaN(a))throw RangeError(`Invalid time value`);let o=Object.assign({},n,{addSuffix:n?.addSuffix,comparison:a}),[s,c]=sa(n?.in,...a>0?[t,e]:[e,t]),l=ha(n?.roundingMethod??`round`),u=c.getTime()-s.getTime(),d=u/Ji,f=(u-(oa(c)-oa(s)))/Ji,p=n?.unit,m;if(m=p||(d<1?`second`:d<60?`minute`:d<1440?`hour`:f<43200?`day`:f<525600?`month`:`year`),m===`second`){let e=l(u/1e3);return i.formatDistance(`xSeconds`,e,o)}if(m===`minute`){let e=l(d);return i.formatDistance(`xMinutes`,e,o)}if(m===`hour`){let e=l(d/60);return i.formatDistance(`xHours`,e,o)}if(m===`day`){let e=l(f/Qi);return i.formatDistance(`xDays`,e,o)}if(m===`month`){let e=l(f/Zi);return e===12&&p!==`month`?i.formatDistance(`xYears`,1,o):i.formatDistance(`xMonths`,e,o)}{let e=l(f/Xi);return i.formatDistance(`xYears`,e,o)}}function ao(e,t){return io(e,fa(e),t)}function oo(e,t){let n=()=>W(t?.in,NaN),r=t?.additionalDigits??2,i=fo(e),a;if(i.date){let e=po(i.date,r);a=mo(e.restDateString,e.year)}if(!a||isNaN(+a))return n();let o=+a,s=0,c;if(i.time&&(s=go(i.time),isNaN(s)))return n();if(i.timezone){if(c=vo(i.timezone),isNaN(c))return n()}else{let e=new Date(o+s),n=G(0,t?.in);return n.setFullYear(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()),n.setHours(e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds()),n}return G(o+s+c,t?.in)}var so={dateTimeDelimiter:/[T ]/,timeZoneDelimiter:/[Z ]/i,timezone:/([Z+-].*)$/},co=/^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/,lo=/^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/,uo=/^([+-])(\d{2})(?::?(\d{2}))?$/;function fo(e){let t={},n=e.split(so.dateTimeDelimiter),r;if(n.length>2)return t;if(/:/.test(n[0])?r=n[0]:(t.date=n[0],r=n[1],so.timeZoneDelimiter.test(t.date)&&(t.date=e.split(so.timeZoneDelimiter)[0],r=e.substr(t.date.length,e.length))),r){let e=so.timezone.exec(r);e?(t.time=r.replace(e[1],``),t.timezone=e[1]):t.time=r}return t}function po(e,t){let n=RegExp(`^(?:(\\d{4}|[+-]\\d{`+(4+t)+`})|(\\d{2}|[+-]\\d{`+(2+t)+`})$)`),r=e.match(n);if(!r)return{year:NaN,restDateString:``};let i=r[1]?parseInt(r[1]):null,a=r[2]?parseInt(r[2]):null;return{year:a===null?i:a*100,restDateString:e.slice((r[1]||r[2]).length)}}function mo(e,t){if(t===null)return new Date(NaN);let n=e.match(co);if(!n)return new Date(NaN);let r=!!n[4],i=ho(n[1]),a=ho(n[2])-1,o=ho(n[3]),s=ho(n[4]),c=ho(n[5])-1;if(r)return wo(t,s,c)?yo(t,s,c):new Date(NaN);{let e=new Date(0);return!So(t,a,o)||!Co(t,i)?new Date(NaN):(e.setUTCFullYear(t,a,Math.max(i,o)),e)}}function ho(e){return e?parseInt(e):1}function go(e){let t=e.match(lo);if(!t)return NaN;let n=_o(t[1]),r=_o(t[2]),i=_o(t[3]);return To(n,r,i)?n*Yi+r*Ji+i*1e3:NaN}function _o(e){return e&&parseFloat(e.replace(`,`,`.`))||0}function vo(e){if(e===`Z`)return 0;let t=e.match(uo);if(!t)return 0;let n=t[1]===`+`?-1:1,r=parseInt(t[2]),i=t[3]&&parseInt(t[3])||0;return Eo(r,i)?n*(r*Yi+i*Ji):NaN}function yo(e,t,n){let r=new Date(0);r.setUTCFullYear(e,0,4);let i=r.getUTCDay()||7,a=(t-1)*7+n+1-i;return r.setUTCDate(r.getUTCDate()+a),r}var bo=[31,null,31,30,31,30,31,31,30,31,30,31];function xo(e){return e%400==0||e%4==0&&e%100!=0}function So(e,t,n){return t>=0&&t<=11&&n>=1&&n<=(bo[t]||(xo(e)?29:28))}function Co(e,t){return t>=1&&t<=(xo(e)?366:365)}function wo(e,t,n){return t>=1&&t<=53&&n>=0&&n<=6}function To(e,t,n){return e===24?t===0&&n===0:n>=0&&n<60&&t>=0&&t<60&&e>=0&&e<25}function Eo(e,t){return t>=0&&t<=59}var Do=(e,t)=>{let n=Array(e.length+t.length);for(let t=0;t({classGroupId:e,validator:t}),ko=(e=new Map,t=null,n)=>({nextPart:e,validators:t,classGroupId:n}),Ao=`-`,jo=[],Mo=`arbitrary..`,No=e=>{let t=Io(e),{conflictingClassGroups:n,conflictingClassGroupModifiers:r}=e;return{getClassGroupId:e=>{if(e.startsWith(`[`)&&e.endsWith(`]`))return Fo(e);let n=e.split(Ao);return Po(n,+(n[0]===``&&n.length>1),t)},getConflictingClassGroupIds:(e,t)=>{if(t){let t=r[e],i=n[e];return t?i?Do(i,t):t:i||jo}return n[e]||jo}}},Po=(e,t,n)=>{if(e.length-t===0)return n.classGroupId;let r=e[t],i=n.nextPart.get(r);if(i){let n=Po(e,t+1,i);if(n)return n}let a=n.validators;if(a===null)return;let o=t===0?e.join(Ao):e.slice(t).join(Ao),s=a.length;for(let e=0;ee.slice(1,-1).indexOf(`:`)===-1?void 0:(()=>{let t=e.slice(1,-1),n=t.indexOf(`:`),r=t.slice(0,n);return r?Mo+r:void 0})(),Io=e=>{let{theme:t,classGroups:n}=e;return Lo(n,t)},Lo=(e,t)=>{let n=ko();for(let r in e){let i=e[r];Ro(i,n,r,t)}return n},Ro=(e,t,n,r)=>{let i=e.length;for(let a=0;a{if(typeof e==`string`){Bo(e,t,n);return}if(typeof e==`function`){Vo(e,t,n,r);return}Ho(e,t,n,r)},Bo=(e,t,n)=>{let r=e===``?t:Uo(t,e);r.classGroupId=n},Vo=(e,t,n,r)=>{if(Wo(e)){Ro(e(r),t,n,r);return}t.validators===null&&(t.validators=[]),t.validators.push(Oo(n,e))},Ho=(e,t,n,r)=>{let i=Object.entries(e),a=i.length;for(let e=0;e{let n=e,r=t.split(Ao),i=r.length;for(let e=0;e`isThemeGetter`in e&&e.isThemeGetter===!0,Go=e=>{if(e<1)return{get:()=>void 0,set:()=>{}};let t=0,n=Object.create(null),r=Object.create(null),i=(i,a)=>{n[i]=a,t++,t>e&&(t=0,r=n,n=Object.create(null))};return{get(e){let t=n[e];if(t!==void 0)return t;if((t=r[e])!==void 0)return i(e,t),t},set(e,t){e in n?n[e]=t:i(e,t)}}},Ko=`!`,qo=`:`,Jo=[],Yo=(e,t,n,r,i)=>({modifiers:e,hasImportantModifier:t,baseClassName:n,maybePostfixModifierPosition:r,isExternal:i}),Xo=e=>{let{prefix:t,experimentalParseClassName:n}=e,r=e=>{let t=[],n=0,r=0,i=0,a,o=e.length;for(let s=0;si?a-i:void 0;return Yo(t,l,c,u)};if(t){let e=t+qo,n=r;r=t=>t.startsWith(e)?n(t.slice(e.length)):Yo(Jo,!1,t,void 0,!0)}if(n){let e=r;r=t=>n({className:t,parseClassName:e})}return r},Zo=e=>{let t=new Map;return e.orderSensitiveModifiers.forEach((e,n)=>{t.set(e,1e6+n)}),e=>{let n=[],r=[];for(let i=0;i0&&(r.sort(),n.push(...r),r=[]),n.push(a)):r.push(a)}return r.length>0&&(r.sort(),n.push(...r)),n}},Qo=e=>({cache:Go(e.cacheSize),parseClassName:Xo(e),sortModifiers:Zo(e),postfixLookupClassGroupIds:$o(e),...No(e)}),$o=e=>{let t=Object.create(null),n=e.postfixLookupClassGroups;if(n)for(let e=0;e{let{parseClassName:n,getClassGroupId:r,getConflictingClassGroupIds:i,sortModifiers:a,postfixLookupClassGroupIds:o}=t,s=[],c=e.trim().split(es),l=``;for(let e=c.length-1;e>=0;--e){let t=c[e],{isExternal:u,modifiers:d,hasImportantModifier:f,baseClassName:p,maybePostfixModifierPosition:m}=n(t);if(u){l=t+(l.length>0?` `+l:l);continue}let h=!!m,g;if(h){g=r(p.substring(0,m));let e=g&&o[g]?r(p):void 0;e&&e!==g&&(g=e,h=!1)}else g=r(p);if(!g){if(!h){l=t+(l.length>0?` `+l:l);continue}if(g=r(p),!g){l=t+(l.length>0?` `+l:l);continue}h=!1}let _=d.length===0?``:d.length===1?d[0]:a(d).join(`:`),ee=f?_+Ko:_,te=ee+g;if(s.indexOf(te)>-1)continue;s.push(te);let ne=i(g,h);for(let e=0;e0?` `+l:l)}return l},ns=(...e)=>{let t=0,n,r,i=``;for(;t{if(typeof e==`string`)return e;let t,n=``;for(let r=0;r{let n,r,i,a,o=o=>(n=Qo(t.reduce((e,t)=>t(e),e())),r=n.cache.get,i=n.cache.set,a=s,s(o)),s=e=>{let t=r(e);if(t)return t;let a=ts(e,n);return i(e,a),a};return a=o,(...e)=>a(ns(...e))},as=[],q=e=>{let t=t=>t[e]||as;return t.isThemeGetter=!0,t},os=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,ss=/^\((?:(\w[\w-]*):)?(.+)\)$/i,cs=/^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/,ls=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,us=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,ds=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,fs=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,ps=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,ms=e=>cs.test(e),J=e=>!!e&&!Number.isNaN(Number(e)),Y=e=>!!e&&Number.isInteger(Number(e)),hs=e=>e.endsWith(`%`)&&J(e.slice(0,-1)),X=e=>ls.test(e),gs=()=>!0,_s=e=>us.test(e)&&!ds.test(e),vs=()=>!1,ys=e=>fs.test(e),bs=e=>ps.test(e),xs=e=>!Z(e)&&!Q(e),Ss=e=>e.startsWith(`@container`)&&(e[10]===`/`&&e[11]!==void 0||e[11]===`s`&&e[16]!==void 0&&e.startsWith(`-size/`,10)||e[11]===`n`&&e[18]!==void 0&&e.startsWith(`-normal/`,10)),Cs=e=>Rs(e,Hs,vs),Z=e=>os.test(e),ws=e=>Rs(e,Us,_s),Ts=e=>Rs(e,Ws,J),Es=e=>Rs(e,Ks,gs),Ds=e=>Rs(e,Gs,vs),Os=e=>Rs(e,Bs,vs),ks=e=>Rs(e,Vs,bs),As=e=>Rs(e,qs,ys),Q=e=>ss.test(e),js=e=>zs(e,Us),Ms=e=>zs(e,Gs),Ns=e=>zs(e,Bs),Ps=e=>zs(e,Hs),Fs=e=>zs(e,Vs),Is=e=>zs(e,qs,!0),Ls=e=>zs(e,Ks,!0),Rs=(e,t,n)=>{let r=os.exec(e);return r?r[1]?t(r[1]):n(r[2]):!1},zs=(e,t,n=!1)=>{let r=ss.exec(e);return r?r[1]?t(r[1]):n:!1},Bs=e=>e===`position`||e===`percentage`,Vs=e=>e===`image`||e===`url`,Hs=e=>e===`length`||e===`size`||e===`bg-size`,Us=e=>e===`length`,Ws=e=>e===`number`,Gs=e=>e===`family-name`,Ks=e=>e===`number`||e===`weight`,qs=e=>e===`shadow`,Js=is(()=>{let e=q(`color`),t=q(`font`),n=q(`text`),r=q(`font-weight`),i=q(`tracking`),a=q(`leading`),o=q(`breakpoint`),s=q(`container`),c=q(`spacing`),l=q(`radius`),u=q(`shadow`),d=q(`inset-shadow`),f=q(`text-shadow`),p=q(`drop-shadow`),m=q(`blur`),h=q(`perspective`),g=q(`aspect`),_=q(`ease`),ee=q(`animate`),te=()=>[`auto`,`avoid`,`all`,`avoid-page`,`page`,`left`,`right`,`column`],ne=()=>[`center`,`top`,`bottom`,`left`,`right`,`top-left`,`left-top`,`top-right`,`right-top`,`bottom-right`,`right-bottom`,`bottom-left`,`left-bottom`],re=()=>[...ne(),Q,Z],v=()=>[`auto`,`hidden`,`clip`,`visible`,`scroll`],y=()=>[`auto`,`contain`,`none`],b=()=>[Q,Z,c],x=()=>[ms,`full`,`auto`,...b()],ie=()=>[Y,`none`,`subgrid`,Q,Z],ae=()=>[`auto`,{span:[`full`,Y,Q,Z]},Y,Q,Z],oe=()=>[Y,`auto`,Q,Z],se=()=>[`auto`,`min`,`max`,`fr`,Q,Z],ce=()=>[`start`,`end`,`center`,`between`,`around`,`evenly`,`stretch`,`baseline`,`center-safe`,`end-safe`],le=()=>[`start`,`end`,`center`,`stretch`,`center-safe`,`end-safe`],S=()=>[`auto`,...b()],C=()=>[ms,`auto`,`full`,`dvw`,`dvh`,`lvw`,`lvh`,`svw`,`svh`,`min`,`max`,`fit`,...b()],ue=()=>[ms,`screen`,`full`,`dvw`,`lvw`,`svw`,`min`,`max`,`fit`,...b()],de=()=>[ms,`screen`,`full`,`lh`,`dvh`,`lvh`,`svh`,`min`,`max`,`fit`,...b()],w=()=>[e,Q,Z],fe=()=>[...ne(),Ns,Os,{position:[Q,Z]}],T=()=>[`no-repeat`,{repeat:[``,`x`,`y`,`space`,`round`]}],pe=()=>[`auto`,`cover`,`contain`,Ps,Cs,{size:[Q,Z]}],me=()=>[hs,js,ws],E=()=>[``,`none`,`full`,l,Q,Z],D=()=>[``,J,js,ws],he=()=>[`solid`,`dashed`,`dotted`,`double`],ge=()=>[`normal`,`multiply`,`screen`,`overlay`,`darken`,`lighten`,`color-dodge`,`color-burn`,`hard-light`,`soft-light`,`difference`,`exclusion`,`hue`,`saturation`,`color`,`luminosity`],O=()=>[J,hs,Ns,Os],_e=()=>[``,`none`,m,Q,Z],k=()=>[`none`,J,Q,Z],A=()=>[`none`,J,Q,Z],ve=()=>[J,Q,Z],j=()=>[ms,`full`,...b()];return{cacheSize:500,theme:{animate:[`spin`,`ping`,`pulse`,`bounce`],aspect:[`video`],blur:[X],breakpoint:[X],color:[gs],container:[X],"drop-shadow":[X],ease:[`in`,`out`,`in-out`],font:[xs],"font-weight":[`thin`,`extralight`,`light`,`normal`,`medium`,`semibold`,`bold`,`extrabold`,`black`],"inset-shadow":[X],leading:[`none`,`tight`,`snug`,`normal`,`relaxed`,`loose`],perspective:[`dramatic`,`near`,`normal`,`midrange`,`distant`,`none`],radius:[X],shadow:[X],spacing:[`px`,J],text:[X],"text-shadow":[X],tracking:[`tighter`,`tight`,`normal`,`wide`,`wider`,`widest`]},classGroups:{aspect:[{aspect:[`auto`,`square`,ms,Z,Q,g]}],container:[`container`],"container-type":[{"@container":[``,`normal`,`size`,Q,Z]}],"container-named":[Ss],columns:[{columns:[J,Z,Q,s]}],"break-after":[{"break-after":te()}],"break-before":[{"break-before":te()}],"break-inside":[{"break-inside":[`auto`,`avoid`,`avoid-page`,`avoid-column`]}],"box-decoration":[{"box-decoration":[`slice`,`clone`]}],box:[{box:[`border`,`content`]}],display:[`block`,`inline-block`,`inline`,`flex`,`inline-flex`,`table`,`inline-table`,`table-caption`,`table-cell`,`table-column`,`table-column-group`,`table-footer-group`,`table-header-group`,`table-row-group`,`table-row`,`flow-root`,`grid`,`inline-grid`,`contents`,`list-item`,`hidden`],sr:[`sr-only`,`not-sr-only`],float:[{float:[`right`,`left`,`none`,`start`,`end`]}],clear:[{clear:[`left`,`right`,`both`,`none`,`start`,`end`]}],isolation:[`isolate`,`isolation-auto`],"object-fit":[{object:[`contain`,`cover`,`fill`,`none`,`scale-down`]}],"object-position":[{object:re()}],overflow:[{overflow:v()}],"overflow-x":[{"overflow-x":v()}],"overflow-y":[{"overflow-y":v()}],overscroll:[{overscroll:y()}],"overscroll-x":[{"overscroll-x":y()}],"overscroll-y":[{"overscroll-y":y()}],position:[`static`,`fixed`,`absolute`,`relative`,`sticky`],inset:[{inset:x()}],"inset-x":[{"inset-x":x()}],"inset-y":[{"inset-y":x()}],start:[{"inset-s":x(),start:x()}],end:[{"inset-e":x(),end:x()}],"inset-bs":[{"inset-bs":x()}],"inset-be":[{"inset-be":x()}],top:[{top:x()}],right:[{right:x()}],bottom:[{bottom:x()}],left:[{left:x()}],visibility:[`visible`,`invisible`,`collapse`],z:[{z:[Y,`auto`,Q,Z]}],basis:[{basis:[ms,`full`,`auto`,s,...b()]}],"flex-direction":[{flex:[`row`,`row-reverse`,`col`,`col-reverse`]}],"flex-wrap":[{flex:[`nowrap`,`wrap`,`wrap-reverse`]}],flex:[{flex:[J,ms,`auto`,`initial`,`none`,Z]}],grow:[{grow:[``,J,Q,Z]}],shrink:[{shrink:[``,J,Q,Z]}],order:[{order:[Y,`first`,`last`,`none`,Q,Z]}],"grid-cols":[{"grid-cols":ie()}],"col-start-end":[{col:ae()}],"col-start":[{"col-start":oe()}],"col-end":[{"col-end":oe()}],"grid-rows":[{"grid-rows":ie()}],"row-start-end":[{row:ae()}],"row-start":[{"row-start":oe()}],"row-end":[{"row-end":oe()}],"grid-flow":[{"grid-flow":[`row`,`col`,`dense`,`row-dense`,`col-dense`]}],"auto-cols":[{"auto-cols":se()}],"auto-rows":[{"auto-rows":se()}],gap:[{gap:b()}],"gap-x":[{"gap-x":b()}],"gap-y":[{"gap-y":b()}],"justify-content":[{justify:[...ce(),`normal`]}],"justify-items":[{"justify-items":[...le(),`normal`]}],"justify-self":[{"justify-self":[`auto`,...le()]}],"align-content":[{content:[`normal`,...ce()]}],"align-items":[{items:[...le(),{baseline:[``,`last`]}]}],"align-self":[{self:[`auto`,...le(),{baseline:[``,`last`]}]}],"place-content":[{"place-content":ce()}],"place-items":[{"place-items":[...le(),`baseline`]}],"place-self":[{"place-self":[`auto`,...le()]}],p:[{p:b()}],px:[{px:b()}],py:[{py:b()}],ps:[{ps:b()}],pe:[{pe:b()}],pbs:[{pbs:b()}],pbe:[{pbe:b()}],pt:[{pt:b()}],pr:[{pr:b()}],pb:[{pb:b()}],pl:[{pl:b()}],m:[{m:S()}],mx:[{mx:S()}],my:[{my:S()}],ms:[{ms:S()}],me:[{me:S()}],mbs:[{mbs:S()}],mbe:[{mbe:S()}],mt:[{mt:S()}],mr:[{mr:S()}],mb:[{mb:S()}],ml:[{ml:S()}],"space-x":[{"space-x":b()}],"space-x-reverse":[`space-x-reverse`],"space-y":[{"space-y":b()}],"space-y-reverse":[`space-y-reverse`],size:[{size:C()}],"inline-size":[{inline:[`auto`,...ue()]}],"min-inline-size":[{"min-inline":[`auto`,...ue()]}],"max-inline-size":[{"max-inline":[`none`,...ue()]}],"block-size":[{block:[`auto`,...de()]}],"min-block-size":[{"min-block":[`auto`,...de()]}],"max-block-size":[{"max-block":[`none`,...de()]}],w:[{w:[s,`screen`,...C()]}],"min-w":[{"min-w":[s,`screen`,`none`,...C()]}],"max-w":[{"max-w":[s,`screen`,`none`,`prose`,{screen:[o]},...C()]}],h:[{h:[`screen`,`lh`,...C()]}],"min-h":[{"min-h":[`screen`,`lh`,`none`,...C()]}],"max-h":[{"max-h":[`screen`,`lh`,...C()]}],"font-size":[{text:[`base`,n,js,ws]}],"font-smoothing":[`antialiased`,`subpixel-antialiased`],"font-style":[`italic`,`not-italic`],"font-weight":[{font:[r,Ls,Es]}],"font-stretch":[{"font-stretch":[`ultra-condensed`,`extra-condensed`,`condensed`,`semi-condensed`,`normal`,`semi-expanded`,`expanded`,`extra-expanded`,`ultra-expanded`,hs,Z]}],"font-family":[{font:[Ms,Ds,t]}],"font-features":[{"font-features":[Z]}],"fvn-normal":[`normal-nums`],"fvn-ordinal":[`ordinal`],"fvn-slashed-zero":[`slashed-zero`],"fvn-figure":[`lining-nums`,`oldstyle-nums`],"fvn-spacing":[`proportional-nums`,`tabular-nums`],"fvn-fraction":[`diagonal-fractions`,`stacked-fractions`],tracking:[{tracking:[i,Q,Z]}],"line-clamp":[{"line-clamp":[J,`none`,Q,Ts]}],leading:[{leading:[a,...b()]}],"list-image":[{"list-image":[`none`,Q,Z]}],"list-style-position":[{list:[`inside`,`outside`]}],"list-style-type":[{list:[`disc`,`decimal`,`none`,Q,Z]}],"text-alignment":[{text:[`left`,`center`,`right`,`justify`,`start`,`end`]}],"placeholder-color":[{placeholder:w()}],"text-color":[{text:w()}],"text-decoration":[`underline`,`overline`,`line-through`,`no-underline`],"text-decoration-style":[{decoration:[...he(),`wavy`]}],"text-decoration-thickness":[{decoration:[J,`from-font`,`auto`,Q,ws]}],"text-decoration-color":[{decoration:w()}],"underline-offset":[{"underline-offset":[J,`auto`,Q,Z]}],"text-transform":[`uppercase`,`lowercase`,`capitalize`,`normal-case`],"text-overflow":[`truncate`,`text-ellipsis`,`text-clip`],"text-wrap":[{text:[`wrap`,`nowrap`,`balance`,`pretty`]}],indent:[{indent:b()}],"tab-size":[{tab:[Y,Q,Z]}],"vertical-align":[{align:[`baseline`,`top`,`middle`,`bottom`,`text-top`,`text-bottom`,`sub`,`super`,Q,Z]}],whitespace:[{whitespace:[`normal`,`nowrap`,`pre`,`pre-line`,`pre-wrap`,`break-spaces`]}],break:[{break:[`normal`,`words`,`all`,`keep`]}],wrap:[{wrap:[`break-word`,`anywhere`,`normal`]}],hyphens:[{hyphens:[`none`,`manual`,`auto`]}],content:[{content:[`none`,Q,Z]}],"bg-attachment":[{bg:[`fixed`,`local`,`scroll`]}],"bg-clip":[{"bg-clip":[`border`,`padding`,`content`,`text`]}],"bg-origin":[{"bg-origin":[`border`,`padding`,`content`]}],"bg-position":[{bg:fe()}],"bg-repeat":[{bg:T()}],"bg-size":[{bg:pe()}],"bg-image":[{bg:[`none`,{linear:[{to:[`t`,`tr`,`r`,`br`,`b`,`bl`,`l`,`tl`]},Y,Q,Z],radial:[``,Q,Z],conic:[Y,Q,Z]},Fs,ks]}],"bg-color":[{bg:w()}],"gradient-from-pos":[{from:me()}],"gradient-via-pos":[{via:me()}],"gradient-to-pos":[{to:me()}],"gradient-from":[{from:w()}],"gradient-via":[{via:w()}],"gradient-to":[{to:w()}],rounded:[{rounded:E()}],"rounded-s":[{"rounded-s":E()}],"rounded-e":[{"rounded-e":E()}],"rounded-t":[{"rounded-t":E()}],"rounded-r":[{"rounded-r":E()}],"rounded-b":[{"rounded-b":E()}],"rounded-l":[{"rounded-l":E()}],"rounded-ss":[{"rounded-ss":E()}],"rounded-se":[{"rounded-se":E()}],"rounded-ee":[{"rounded-ee":E()}],"rounded-es":[{"rounded-es":E()}],"rounded-tl":[{"rounded-tl":E()}],"rounded-tr":[{"rounded-tr":E()}],"rounded-br":[{"rounded-br":E()}],"rounded-bl":[{"rounded-bl":E()}],"border-w":[{border:D()}],"border-w-x":[{"border-x":D()}],"border-w-y":[{"border-y":D()}],"border-w-s":[{"border-s":D()}],"border-w-e":[{"border-e":D()}],"border-w-bs":[{"border-bs":D()}],"border-w-be":[{"border-be":D()}],"border-w-t":[{"border-t":D()}],"border-w-r":[{"border-r":D()}],"border-w-b":[{"border-b":D()}],"border-w-l":[{"border-l":D()}],"divide-x":[{"divide-x":D()}],"divide-x-reverse":[`divide-x-reverse`],"divide-y":[{"divide-y":D()}],"divide-y-reverse":[`divide-y-reverse`],"border-style":[{border:[...he(),`hidden`,`none`]}],"divide-style":[{divide:[...he(),`hidden`,`none`]}],"border-color":[{border:w()}],"border-color-x":[{"border-x":w()}],"border-color-y":[{"border-y":w()}],"border-color-s":[{"border-s":w()}],"border-color-e":[{"border-e":w()}],"border-color-bs":[{"border-bs":w()}],"border-color-be":[{"border-be":w()}],"border-color-t":[{"border-t":w()}],"border-color-r":[{"border-r":w()}],"border-color-b":[{"border-b":w()}],"border-color-l":[{"border-l":w()}],"divide-color":[{divide:w()}],"outline-style":[{outline:[...he(),`none`,`hidden`]}],"outline-offset":[{"outline-offset":[J,Q,Z]}],"outline-w":[{outline:[``,J,js,ws]}],"outline-color":[{outline:w()}],shadow:[{shadow:[``,`none`,u,Is,As]}],"shadow-color":[{shadow:w()}],"inset-shadow":[{"inset-shadow":[`none`,d,Is,As]}],"inset-shadow-color":[{"inset-shadow":w()}],"ring-w":[{ring:D()}],"ring-w-inset":[`ring-inset`],"ring-color":[{ring:w()}],"ring-offset-w":[{"ring-offset":[J,ws]}],"ring-offset-color":[{"ring-offset":w()}],"inset-ring-w":[{"inset-ring":D()}],"inset-ring-color":[{"inset-ring":w()}],"text-shadow":[{"text-shadow":[`none`,f,Is,As]}],"text-shadow-color":[{"text-shadow":w()}],opacity:[{opacity:[J,Q,Z]}],"mix-blend":[{"mix-blend":[...ge(),`plus-darker`,`plus-lighter`]}],"bg-blend":[{"bg-blend":ge()}],"mask-clip":[{"mask-clip":[`border`,`padding`,`content`,`fill`,`stroke`,`view`]},`mask-no-clip`],"mask-composite":[{mask:[`add`,`subtract`,`intersect`,`exclude`]}],"mask-image-linear-pos":[{"mask-linear":[J]}],"mask-image-linear-from-pos":[{"mask-linear-from":O()}],"mask-image-linear-to-pos":[{"mask-linear-to":O()}],"mask-image-linear-from-color":[{"mask-linear-from":w()}],"mask-image-linear-to-color":[{"mask-linear-to":w()}],"mask-image-t-from-pos":[{"mask-t-from":O()}],"mask-image-t-to-pos":[{"mask-t-to":O()}],"mask-image-t-from-color":[{"mask-t-from":w()}],"mask-image-t-to-color":[{"mask-t-to":w()}],"mask-image-r-from-pos":[{"mask-r-from":O()}],"mask-image-r-to-pos":[{"mask-r-to":O()}],"mask-image-r-from-color":[{"mask-r-from":w()}],"mask-image-r-to-color":[{"mask-r-to":w()}],"mask-image-b-from-pos":[{"mask-b-from":O()}],"mask-image-b-to-pos":[{"mask-b-to":O()}],"mask-image-b-from-color":[{"mask-b-from":w()}],"mask-image-b-to-color":[{"mask-b-to":w()}],"mask-image-l-from-pos":[{"mask-l-from":O()}],"mask-image-l-to-pos":[{"mask-l-to":O()}],"mask-image-l-from-color":[{"mask-l-from":w()}],"mask-image-l-to-color":[{"mask-l-to":w()}],"mask-image-x-from-pos":[{"mask-x-from":O()}],"mask-image-x-to-pos":[{"mask-x-to":O()}],"mask-image-x-from-color":[{"mask-x-from":w()}],"mask-image-x-to-color":[{"mask-x-to":w()}],"mask-image-y-from-pos":[{"mask-y-from":O()}],"mask-image-y-to-pos":[{"mask-y-to":O()}],"mask-image-y-from-color":[{"mask-y-from":w()}],"mask-image-y-to-color":[{"mask-y-to":w()}],"mask-image-radial":[{"mask-radial":[Q,Z]}],"mask-image-radial-from-pos":[{"mask-radial-from":O()}],"mask-image-radial-to-pos":[{"mask-radial-to":O()}],"mask-image-radial-from-color":[{"mask-radial-from":w()}],"mask-image-radial-to-color":[{"mask-radial-to":w()}],"mask-image-radial-shape":[{"mask-radial":[`circle`,`ellipse`]}],"mask-image-radial-size":[{"mask-radial":[{closest:[`side`,`corner`],farthest:[`side`,`corner`]}]}],"mask-image-radial-pos":[{"mask-radial-at":ne()}],"mask-image-conic-pos":[{"mask-conic":[J]}],"mask-image-conic-from-pos":[{"mask-conic-from":O()}],"mask-image-conic-to-pos":[{"mask-conic-to":O()}],"mask-image-conic-from-color":[{"mask-conic-from":w()}],"mask-image-conic-to-color":[{"mask-conic-to":w()}],"mask-mode":[{mask:[`alpha`,`luminance`,`match`]}],"mask-origin":[{"mask-origin":[`border`,`padding`,`content`,`fill`,`stroke`,`view`]}],"mask-position":[{mask:fe()}],"mask-repeat":[{mask:T()}],"mask-size":[{mask:pe()}],"mask-type":[{"mask-type":[`alpha`,`luminance`]}],"mask-image":[{mask:[`none`,Q,Z]}],filter:[{filter:[``,`none`,Q,Z]}],blur:[{blur:_e()}],brightness:[{brightness:[J,Q,Z]}],contrast:[{contrast:[J,Q,Z]}],"drop-shadow":[{"drop-shadow":[``,`none`,p,Is,As]}],"drop-shadow-color":[{"drop-shadow":w()}],grayscale:[{grayscale:[``,J,Q,Z]}],"hue-rotate":[{"hue-rotate":[J,Q,Z]}],invert:[{invert:[``,J,Q,Z]}],saturate:[{saturate:[J,Q,Z]}],sepia:[{sepia:[``,J,Q,Z]}],"backdrop-filter":[{"backdrop-filter":[``,`none`,Q,Z]}],"backdrop-blur":[{"backdrop-blur":_e()}],"backdrop-brightness":[{"backdrop-brightness":[J,Q,Z]}],"backdrop-contrast":[{"backdrop-contrast":[J,Q,Z]}],"backdrop-grayscale":[{"backdrop-grayscale":[``,J,Q,Z]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[J,Q,Z]}],"backdrop-invert":[{"backdrop-invert":[``,J,Q,Z]}],"backdrop-opacity":[{"backdrop-opacity":[J,Q,Z]}],"backdrop-saturate":[{"backdrop-saturate":[J,Q,Z]}],"backdrop-sepia":[{"backdrop-sepia":[``,J,Q,Z]}],"border-collapse":[{border:[`collapse`,`separate`]}],"border-spacing":[{"border-spacing":b()}],"border-spacing-x":[{"border-spacing-x":b()}],"border-spacing-y":[{"border-spacing-y":b()}],"table-layout":[{table:[`auto`,`fixed`]}],caption:[{caption:[`top`,`bottom`]}],transition:[{transition:[``,`all`,`colors`,`opacity`,`shadow`,`transform`,`none`,Q,Z]}],"transition-behavior":[{transition:[`normal`,`discrete`]}],duration:[{duration:[J,`initial`,Q,Z]}],ease:[{ease:[`linear`,`initial`,_,Q,Z]}],delay:[{delay:[J,Q,Z]}],animate:[{animate:[`none`,ee,Q,Z]}],backface:[{backface:[`hidden`,`visible`]}],perspective:[{perspective:[h,Q,Z]}],"perspective-origin":[{"perspective-origin":re()}],rotate:[{rotate:k()}],"rotate-x":[{"rotate-x":k()}],"rotate-y":[{"rotate-y":k()}],"rotate-z":[{"rotate-z":k()}],scale:[{scale:A()}],"scale-x":[{"scale-x":A()}],"scale-y":[{"scale-y":A()}],"scale-z":[{"scale-z":A()}],"scale-3d":[`scale-3d`],skew:[{skew:ve()}],"skew-x":[{"skew-x":ve()}],"skew-y":[{"skew-y":ve()}],transform:[{transform:[Q,Z,``,`none`,`gpu`,`cpu`]}],"transform-origin":[{origin:re()}],"transform-style":[{transform:[`3d`,`flat`]}],translate:[{translate:j()}],"translate-x":[{"translate-x":j()}],"translate-y":[{"translate-y":j()}],"translate-z":[{"translate-z":j()}],"translate-none":[`translate-none`],zoom:[{zoom:[Y,Q,Z]}],accent:[{accent:w()}],appearance:[{appearance:[`none`,`auto`]}],"caret-color":[{caret:w()}],"color-scheme":[{scheme:[`normal`,`dark`,`light`,`light-dark`,`only-dark`,`only-light`]}],cursor:[{cursor:[`auto`,`default`,`pointer`,`wait`,`text`,`move`,`help`,`not-allowed`,`none`,`context-menu`,`progress`,`cell`,`crosshair`,`vertical-text`,`alias`,`copy`,`no-drop`,`grab`,`grabbing`,`all-scroll`,`col-resize`,`row-resize`,`n-resize`,`e-resize`,`s-resize`,`w-resize`,`ne-resize`,`nw-resize`,`se-resize`,`sw-resize`,`ew-resize`,`ns-resize`,`nesw-resize`,`nwse-resize`,`zoom-in`,`zoom-out`,Q,Z]}],"field-sizing":[{"field-sizing":[`fixed`,`content`]}],"pointer-events":[{"pointer-events":[`auto`,`none`]}],resize:[{resize:[`none`,``,`y`,`x`]}],"scroll-behavior":[{scroll:[`auto`,`smooth`]}],"scrollbar-thumb-color":[{"scrollbar-thumb":w()}],"scrollbar-track-color":[{"scrollbar-track":w()}],"scrollbar-gutter":[{"scrollbar-gutter":[`auto`,`stable`,`both`]}],"scrollbar-w":[{scrollbar:[`auto`,`thin`,`none`]}],"scroll-m":[{"scroll-m":b()}],"scroll-mx":[{"scroll-mx":b()}],"scroll-my":[{"scroll-my":b()}],"scroll-ms":[{"scroll-ms":b()}],"scroll-me":[{"scroll-me":b()}],"scroll-mbs":[{"scroll-mbs":b()}],"scroll-mbe":[{"scroll-mbe":b()}],"scroll-mt":[{"scroll-mt":b()}],"scroll-mr":[{"scroll-mr":b()}],"scroll-mb":[{"scroll-mb":b()}],"scroll-ml":[{"scroll-ml":b()}],"scroll-p":[{"scroll-p":b()}],"scroll-px":[{"scroll-px":b()}],"scroll-py":[{"scroll-py":b()}],"scroll-ps":[{"scroll-ps":b()}],"scroll-pe":[{"scroll-pe":b()}],"scroll-pbs":[{"scroll-pbs":b()}],"scroll-pbe":[{"scroll-pbe":b()}],"scroll-pt":[{"scroll-pt":b()}],"scroll-pr":[{"scroll-pr":b()}],"scroll-pb":[{"scroll-pb":b()}],"scroll-pl":[{"scroll-pl":b()}],"snap-align":[{snap:[`start`,`end`,`center`,`align-none`]}],"snap-stop":[{snap:[`normal`,`always`]}],"snap-type":[{snap:[`none`,`x`,`y`,`both`]}],"snap-strictness":[{snap:[`mandatory`,`proximity`]}],touch:[{touch:[`auto`,`none`,`manipulation`]}],"touch-x":[{"touch-pan":[`x`,`left`,`right`]}],"touch-y":[{"touch-pan":[`y`,`up`,`down`]}],"touch-pz":[`touch-pinch-zoom`],select:[{select:[`none`,`text`,`all`,`auto`]}],"will-change":[{"will-change":[`auto`,`scroll`,`contents`,`transform`,Q,Z]}],fill:[{fill:[`none`,...w()]}],"stroke-w":[{stroke:[J,js,ws,Ts]}],stroke:[{stroke:[`none`,...w()]}],"forced-color-adjust":[{"forced-color-adjust":[`auto`,`none`]}]},conflictingClassGroups:{"container-named":[`container-type`],overflow:[`overflow-x`,`overflow-y`],overscroll:[`overscroll-x`,`overscroll-y`],inset:[`inset-x`,`inset-y`,`inset-bs`,`inset-be`,`start`,`end`,`top`,`right`,`bottom`,`left`],"inset-x":[`right`,`left`],"inset-y":[`top`,`bottom`],flex:[`basis`,`grow`,`shrink`],gap:[`gap-x`,`gap-y`],p:[`px`,`py`,`ps`,`pe`,`pbs`,`pbe`,`pt`,`pr`,`pb`,`pl`],px:[`pr`,`pl`],py:[`pt`,`pb`],m:[`mx`,`my`,`ms`,`me`,`mbs`,`mbe`,`mt`,`mr`,`mb`,`ml`],mx:[`mr`,`ml`],my:[`mt`,`mb`],size:[`w`,`h`],"font-size":[`leading`],"fvn-normal":[`fvn-ordinal`,`fvn-slashed-zero`,`fvn-figure`,`fvn-spacing`,`fvn-fraction`],"fvn-ordinal":[`fvn-normal`],"fvn-slashed-zero":[`fvn-normal`],"fvn-figure":[`fvn-normal`],"fvn-spacing":[`fvn-normal`],"fvn-fraction":[`fvn-normal`],"line-clamp":[`display`,`overflow`],rounded:[`rounded-s`,`rounded-e`,`rounded-t`,`rounded-r`,`rounded-b`,`rounded-l`,`rounded-ss`,`rounded-se`,`rounded-ee`,`rounded-es`,`rounded-tl`,`rounded-tr`,`rounded-br`,`rounded-bl`],"rounded-s":[`rounded-ss`,`rounded-es`],"rounded-e":[`rounded-se`,`rounded-ee`],"rounded-t":[`rounded-tl`,`rounded-tr`],"rounded-r":[`rounded-tr`,`rounded-br`],"rounded-b":[`rounded-br`,`rounded-bl`],"rounded-l":[`rounded-tl`,`rounded-bl`],"border-spacing":[`border-spacing-x`,`border-spacing-y`],"border-w":[`border-w-x`,`border-w-y`,`border-w-s`,`border-w-e`,`border-w-bs`,`border-w-be`,`border-w-t`,`border-w-r`,`border-w-b`,`border-w-l`],"border-w-x":[`border-w-r`,`border-w-l`],"border-w-y":[`border-w-t`,`border-w-b`],"border-color":[`border-color-x`,`border-color-y`,`border-color-s`,`border-color-e`,`border-color-bs`,`border-color-be`,`border-color-t`,`border-color-r`,`border-color-b`,`border-color-l`],"border-color-x":[`border-color-r`,`border-color-l`],"border-color-y":[`border-color-t`,`border-color-b`],translate:[`translate-x`,`translate-y`,`translate-none`],"translate-none":[`translate`,`translate-x`,`translate-y`,`translate-z`],"scroll-m":[`scroll-mx`,`scroll-my`,`scroll-ms`,`scroll-me`,`scroll-mbs`,`scroll-mbe`,`scroll-mt`,`scroll-mr`,`scroll-mb`,`scroll-ml`],"scroll-mx":[`scroll-mr`,`scroll-ml`],"scroll-my":[`scroll-mt`,`scroll-mb`],"scroll-p":[`scroll-px`,`scroll-py`,`scroll-ps`,`scroll-pe`,`scroll-pbs`,`scroll-pbe`,`scroll-pt`,`scroll-pr`,`scroll-pb`,`scroll-pl`],"scroll-px":[`scroll-pr`,`scroll-pl`],"scroll-py":[`scroll-pt`,`scroll-pb`],touch:[`touch-x`,`touch-y`,`touch-pz`],"touch-x":[`touch`],"touch-y":[`touch`],"touch-pz":[`touch`]},conflictingClassGroupModifiers:{"font-size":[`leading`]},postfixLookupClassGroups:[`container-type`],orderSensitiveModifiers:[`*`,`**`,`after`,`backdrop`,`before`,`details-content`,`file`,`first-letter`,`first-line`,`marker`,`placeholder`,`selection`]}});function $(...e){return Js(Wi(e))}function Ys(e,t=!1,n=`bits`){if(e==null||!Number.isFinite(e))return`—`;let r=n===`bytes`?e/8:e,i=[{threshold:1e9,divisor:1e9,label:n===`bytes`?`GB/s`:`Gbps`},{threshold:1e6,divisor:1e6,label:n===`bytes`?`MB/s`:`Mbps`},{threshold:1e3,divisor:1e3,label:n===`bytes`?`kB/s`:`Kbps`}].find(e=>r>=e.threshold);if(!i)return`${Math.round(r)} ${n===`bytes`?`B/s`:`bps`}`;let a=r/i.divisor;return`${a.toFixed(a>=100||t?0:1)} ${i.label}`}function Xs(e){return e==null||!Number.isFinite(e)?`—`:`${e<10?e.toFixed(1):e.toFixed(0)} ms`}function Zs(e){return e==null||!Number.isFinite(e)?`—`:`${e.toFixed(+(e<10))}%`}function Qs(e){if(e==null||!Number.isFinite(e))return`—`;let t=[`B`,`KB`,`MB`,`GB`,`TB`],n=e,r=0;for(;n>=1e3&&r=100?0:1)} ${t[r]??`B`}`}function $s(e,t=!1,n){if(!e)return`—`;let r=oo(e);return ma(r)?n?new Intl.DateTimeFormat(`en-GB`,{day:`2-digit`,month:`short`,year:`numeric`,hour:`2-digit`,minute:`2-digit`,...t?{second:`2-digit`}:{},hourCycle:`h23`,timeZone:n}).format(r):no(r,t?`dd MMM yyyy, HH:mm:ss`:`dd MMM yyyy, HH:mm`):`—`}function ec(e){if(!e)return`Not scheduled`;let t=oo(e);return ma(t)?ao(t,{addSuffix:!0}):`Unknown`}function tc(e){return e===`ookla`?`Ookla`:e===`librespeed`?`LibreSpeed`:`Cloudflare`}var nc={ookla:`#67e8f9`,librespeed:`#a78bfa`,cloudflare:`#fb923c`},rc={queued:`neutral`,validating:`info`,running:`info`,succeeded:`success`,failed:`danger`,skipped:`warning`,cancelled:`neutral`};async function ic(e){await navigator.clipboard.writeText(e)}function ac(e,t){let n=document.createElement(`a`);n.href=e,n.download=t,n.rel=`noopener`,document.body.append(n),n.click(),n.remove()}function oc(e){if(!e)return null;try{let t=new URL(e);return t.protocol===`http:`||t.protocol===`https:`?t.href:null}catch{return null}}function sc(e,t,n){return{from:cc(e,n),to:lc(t,n)}}function cc(e,t){return uc(e,t)}function lc(e,t){return uc(dc(e,1),t)}function uc(e,t){let n=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!n)throw Error(`Invalid reporting date: ${e}`);let r=Date.UTC(Number(n[1]),Number(n[2])-1,Number(n[3])),i=r,a=new Intl.DateTimeFormat(`en-US`,{timeZone:t,year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hourCycle:`h23`});for(let e=0;e<4;e+=1){let e=Object.fromEntries(a.formatToParts(new Date(i)).filter(e=>e.type!==`literal`).map(e=>[e.type,Number(e.value)])),t=r-Date.UTC(e.year??0,(e.month??1)-1,e.day??1,e.hour??0,e.minute??0,e.second??0);if(i+=t,t===0)break}return new Date(i).toISOString()}function dc(e,t){let n=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!n)throw Error(`Invalid reporting date: ${e}`);return new Date(Date.UTC(Number(n[1]),Number(n[2])-1,Number(n[3])+t)).toISOString().slice(0,10)}var fc={primary:`bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm shadow-cyan-900/20`,secondary:`bg-secondary text-secondary-foreground hover:bg-secondary/80`,ghost:`bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground`,outline:`border border-border bg-background text-foreground hover:bg-accent`,danger:`bg-destructive text-destructive-foreground hover:bg-destructive/90`},pc={sm:`h-8 gap-1.5 rounded-md px-2.5 text-xs`,md:`h-10 gap-2 rounded-lg px-4 text-sm`,lg:`h-11 gap-2 rounded-lg px-5 text-sm`,icon:`h-9 w-9 rounded-lg`};function mc({asChild:e=!1,className:t,variant:n=`primary`,size:r=`md`,type:i=`button`,...a}){return(0,A.jsx)(e?Ue:`button`,{className:$(`inline-flex shrink-0 items-center justify-center whitespace-nowrap font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50`,fc[n],pc[r],t),type:e?void 0:i,...a})}var hc=[{href:`/`,label:`Dashboard`,icon:ai,subtitle:`Live overview`},{href:`/tasks`,label:`Tasks`,icon:ci,subtitle:`Schedules & targets`},{href:`/results`,label:`Results`,icon:ei,subtitle:`Test history`},{href:`/statistics`,label:`Statistics`,icon:ti,subtitle:`Trends & percentiles`},{href:`/comparison`,label:`WAN comparison`,icon:ii,subtitle:`Path performance`},{href:`/network`,label:`Network & routes`,icon:fi,subtitle:`Bindings & validation`},{href:`/settings`,label:`Settings`,icon:mi,subtitle:`Runtime defaults`},{href:`/system`,label:`System information`,icon:si,subtitle:`Health & versions`}],gc={"/":{title:`Operations overview`,eyebrow:`Multi-WAN observability`},"/tasks":{title:`Speed-test tasks`,eyebrow:`Independent schedules`},"/tasks/new":{title:`Create task`,eyebrow:`Measurement configuration`},"/results":{title:`Measurement results`,eyebrow:`History & diagnostics`},"/statistics":{title:`Performance statistics`,eyebrow:`Aggregates & trends`},"/comparison":{title:`WAN comparison`,eyebrow:`Side-by-side analysis`},"/network":{title:`Network & routes`,eyebrow:`Read-only path validation`},"/settings":{title:`Application settings`,eyebrow:`Defaults & retention`},"/system":{title:`System information`,eyebrow:`Runtime health`}};function _c(){return(0,A.jsxs)(v,{to:`/`,className:`group flex items-center gap-3 rounded-lg focus-visible:ring-2 focus-visible:ring-ring`,"aria-label":`MultiSpeed dashboard`,children:[(0,A.jsx)(`span`,{className:`relative grid h-10 w-10 shrink-0 place-items-center overflow-hidden rounded-xl border border-cyan-400/20 bg-slate-950 shadow-lg shadow-cyan-950/30`,children:(0,A.jsxs)(`span`,{className:`flex h-5 items-end gap-[3px]`,"aria-hidden":`true`,children:[(0,A.jsx)(`span`,{className:`h-2 w-1 rounded-full bg-cyan-300`}),(0,A.jsx)(`span`,{className:`h-4 w-1 rounded-full bg-cyan-400`}),(0,A.jsx)(`span`,{className:`h-3 w-1 rounded-full bg-violet-400`}),(0,A.jsx)(`span`,{className:`h-5 w-1 rounded-full bg-orange-400`})]})}),(0,A.jsxs)(`span`,{children:[(0,A.jsx)(`span`,{className:`block text-[17px] font-bold tracking-[-0.03em] text-foreground`,children:`MultiSpeed`}),(0,A.jsx)(`span`,{className:`block text-[10px] font-semibold uppercase tracking-[.15em] text-muted-foreground`,children:`Path telemetry`})]})]})}function vc({onNavigate:e}){return(0,A.jsx)(`nav`,{className:`space-y-1`,"aria-label":`Primary navigation`,children:hc.map(({href:t,label:n,icon:r,subtitle:i})=>(0,A.jsx)(v,{to:t,end:t===`/`,onClick:e,className:({isActive:e})=>$(`group flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm transition focus-visible:ring-2 focus-visible:ring-ring`,e?`bg-accent font-semibold text-accent-foreground`:`text-muted-foreground hover:bg-muted hover:text-foreground`),children:({isActive:e})=>(0,A.jsxs)(A.Fragment,{children:[(0,A.jsx)(r,{className:$(`h-4 w-4 shrink-0`,e?`text-primary`:`text-muted-foreground group-hover:text-foreground`)}),(0,A.jsxs)(`span`,{className:`min-w-0`,children:[(0,A.jsx)(`span`,{className:`block leading-4`,children:n}),(0,A.jsx)(`span`,{className:$(`mt-0.5 block text-[10px] font-normal leading-3`,e?`text-accent-foreground`:`text-muted-foreground`),children:i})]})]})},t))})}function yc({mode:e,setMode:t}){return(0,A.jsx)(`div`,{className:`flex rounded-lg border border-border bg-muted/50 p-1`,"aria-label":`Color theme`,children:[{value:`light`,label:`Light theme`,icon:hi},{value:`dark`,label:`Dark theme`,icon:di},{value:`system`,label:`System theme`,icon:ei}].map(({value:n,label:r,icon:i})=>(0,A.jsx)(`button`,{type:`button`,onClick:()=>t(n),className:$(`grid h-7 flex-1 place-items-center rounded-md transition focus-visible:ring-2 focus-visible:ring-ring`,e===n?`bg-card text-foreground shadow-sm`:`text-muted-foreground hover:text-foreground`),"aria-label":r,"aria-pressed":e===n,children:(0,A.jsx)(i,{className:`h-3.5 w-3.5`})},n))})}function bc({state:e}){let t=e===`connected`;return(0,A.jsxs)(`div`,{className:`flex items-center gap-2 rounded-lg border border-border bg-background px-2.5 py-2 text-[11px] font-medium text-muted-foreground`,title:`Server-sent events connection`,children:[(0,A.jsx)(`span`,{className:$(`relative h-2 w-2 rounded-full`,t?`bg-emerald-500`:e===`unavailable`?`bg-rose-500`:`bg-amber-500`),children:t?(0,A.jsx)(`span`,{className:`absolute inset-0 animate-ping rounded-full bg-emerald-400 opacity-50`}):null}),t?`Live`:e===`unavailable`?`Offline`:`Reconnecting`]})}function xc({children:e}){let{pathname:t}=ae(),{state:n}=Ri(),{theme:r,setTheme:i}=Hi(),[a,o]=(0,C.useState)(!1),s=t.startsWith(`/tasks/`)?{title:t.endsWith(`/new`)?`Create task`:`Edit task`,eyebrow:`Measurement configuration`}:t.startsWith(`/results/`)?{title:`Result diagnostics`,eyebrow:`Measurement detail`}:gc[t]??{title:`MultiSpeed`,eyebrow:`Infrastructure observability`};return(0,A.jsxs)(`div`,{className:`min-h-screen bg-background`,children:[(0,A.jsxs)(`aside`,{className:`fixed inset-y-0 left-0 z-40 hidden w-64 border-r border-border bg-card/95 px-4 py-5 lg:flex lg:flex-col`,children:[(0,A.jsx)(_c,{}),(0,A.jsx)(`div`,{className:`mt-7 flex-1 overflow-y-auto scrollbar-thin`,children:(0,A.jsx)(vc,{})}),(0,A.jsxs)(`div`,{className:`mt-4 space-y-3 border-t border-border pt-4`,children:[(0,A.jsx)(bc,{state:n}),(0,A.jsx)(yc,{mode:r,setMode:i}),(0,A.jsx)(`p`,{className:`px-1 text-[10px] leading-4 text-muted-foreground`,children:`No authentication. Keep this console on a trusted network.`})]})]}),(0,A.jsxs)(`div`,{className:`lg:pl-64`,children:[(0,A.jsx)(`header`,{className:`sticky top-0 z-30 border-b border-border bg-background/90 backdrop-blur-xl`,children:(0,A.jsxs)(`div`,{className:`flex h-16 items-center gap-3 px-4 sm:px-6 lg:px-8`,children:[(0,A.jsxs)(Sr,{open:a,onOpenChange:o,children:[(0,A.jsx)(wr,{asChild:!0,children:(0,A.jsx)(mc,{size:`icon`,variant:`ghost`,className:`lg:hidden`,"aria-label":`Open navigation`,children:(0,A.jsx)(ui,{className:`h-5 w-5`})})}),(0,A.jsxs)(Or,{children:[(0,A.jsx)(Ar,{className:`fixed inset-0 z-50 bg-slate-950/60 backdrop-blur-sm`}),(0,A.jsxs)(Pr,{className:`fixed inset-y-0 left-0 z-50 w-[min(88vw,19rem)] border-r border-border bg-card p-5 shadow-2xl outline-none`,children:[(0,A.jsx)(zr,{className:`sr-only`,children:`Navigation`}),(0,A.jsx)(Vr,{className:`sr-only`,children:`Navigate between MultiSpeed screens`}),(0,A.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,A.jsx)(_c,{}),(0,A.jsx)(Ur,{asChild:!0,children:(0,A.jsx)(mc,{size:`icon`,variant:`ghost`,"aria-label":`Close navigation`,children:(0,A.jsx)(_i,{className:`h-4 w-4`})})})]}),(0,A.jsx)(`div`,{className:`mt-7`,children:(0,A.jsx)(vc,{onNavigate:()=>o(!1)})}),(0,A.jsxs)(`div`,{className:`absolute inset-x-5 bottom-5 space-y-3`,children:[(0,A.jsx)(bc,{state:n}),(0,A.jsx)(yc,{mode:r,setMode:i})]})]})]})]}),(0,A.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,A.jsx)(`p`,{className:`truncate text-[10px] font-bold uppercase tracking-[.13em] text-primary`,children:s.eyebrow}),(0,A.jsx)(`h1`,{className:`truncate text-base font-semibold tracking-tight text-foreground`,children:s.title})]}),(0,A.jsx)(`div`,{className:`lg:hidden`,children:(0,A.jsx)(bc,{state:n})})]})}),(0,A.jsxs)(`main`,{className:`relative min-h-[calc(100vh-4rem)] overflow-hidden`,children:[(0,A.jsx)(`div`,{className:`page-grid pointer-events-none absolute inset-0`,"aria-hidden":`true`}),(0,A.jsx)(`div`,{className:`relative mx-auto w-full max-w-[1600px] px-4 py-5 sm:px-6 sm:py-7 lg:px-8 lg:py-8`,children:e})]}),(0,A.jsxs)(`footer`,{className:`border-t border-border px-4 py-3 text-center text-[10px] leading-5 text-muted-foreground sm:px-6 lg:px-8`,children:[`Cloudflare`,`®`,` is a trademark of Cloudflare, Inc. Ookla`,`®`,` and Speedtest`,`®`,` are registered trademarks of Ookla, LLC. MultiSpeed is an independent project and is not affiliated with, endorsed by, or sponsored by either company.`]})]})]})}function Sc({className:e}){return(0,A.jsx)(li,{className:$(`h-4 w-4 animate-spin`,e),"aria-hidden":`true`})}function Cc({label:e=`Loading data…`,compact:t=!1}){return(0,A.jsxs)(`div`,{className:$(`flex items-center justify-center gap-2 text-sm text-muted-foreground`,t?`py-6`:`min-h-52 py-12`),children:[(0,A.jsx)(Sc,{}),(0,A.jsx)(`span`,{children:e})]})}function wc({icon:e,title:t,description:n,action:r,compact:i=!1}){return(0,A.jsxs)(`div`,{className:$(`flex flex-col items-center justify-center px-5 text-center`,i?`py-8`:`min-h-56 py-12`),children:[(0,A.jsx)(`div`,{className:`mb-3 flex h-10 w-10 items-center justify-center rounded-xl border border-border bg-muted text-muted-foreground`,children:e??(0,A.jsx)(oi,{className:`h-5 w-5`})}),(0,A.jsx)(`h3`,{className:`text-sm font-semibold text-foreground`,children:t}),(0,A.jsx)(`p`,{className:`mt-1 max-w-md text-xs leading-5 text-muted-foreground`,children:n}),r?(0,A.jsx)(`div`,{className:`mt-4`,children:r}):null]})}function Tc({error:e,onRetry:t,title:n=`Unable to load data`,compact:r=!1}){return(0,A.jsxs)(`div`,{className:$(`flex flex-col items-center justify-center px-5 text-center`,r?`py-8`:`min-h-56 py-12`),role:`alert`,children:[(0,A.jsx)(`div`,{className:`mb-3 flex h-10 w-10 items-center justify-center rounded-xl border border-rose-500/20 bg-rose-500/10 text-rose-500`,children:(0,A.jsx)(gi,{className:`h-5 w-5`})}),(0,A.jsx)(`h3`,{className:`text-sm font-semibold text-foreground`,children:n}),(0,A.jsx)(`p`,{className:`mt-1 max-w-md text-xs leading-5 text-muted-foreground`,children:Pi(e)}),t?(0,A.jsxs)(mc,{className:`mt-4`,variant:`outline`,size:`sm`,onClick:t,children:[(0,A.jsx)(pi,{className:`h-3.5 w-3.5`}),`Try again`]}):null]})}var Ec=(0,C.lazy)(()=>x(()=>import(`./DashboardPage-DEmE5jHs.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13]))),Dc=(0,C.lazy)(()=>x(()=>import(`./TasksPage-CxX5SuNf.js`),__vite__mapDeps([14,1,2,3,15,16,17,7,18,19,9,12,5,13,20,21]))),Oc=(0,C.lazy)(()=>x(()=>import(`./TaskEditorPage-Bx-TQ0Uc.js`),__vite__mapDeps([22,1,2,3,23,15,24,25,26,27,8,12,20,28,18,29,9,5,13,30]))),kc=(0,C.lazy)(()=>x(()=>import(`./ResultsPage-DR4hm0dc.js`),__vite__mapDeps([31,1,2,3,15,32,26,16,19,8,9,5,13,12,20,21]))),Ac=(0,C.lazy)(()=>x(()=>import(`./ResultDetailPage-DbQ93uA2.js`),__vite__mapDeps([33,1,2,3,4,5,6,23,34,24,26,28,19,8,9,13,12,20]))),jc=(0,C.lazy)(()=>x(()=>import(`./StatisticsPage-C5F5n78a.js`),__vite__mapDeps([35,1,2,4,5,15,16,8,9,10,11]))),Mc=(0,C.lazy)(()=>x(()=>import(`./ComparisonPage-CZJO9UdP.js`),__vite__mapDeps([36,1,2,6,15,8,9,10,11,12,5]))),Nc=(0,C.lazy)(()=>x(()=>import(`./NetworkPage-CZ9yIsUy.js`),__vite__mapDeps([37,1,2,15,38,34,17,7,39,19,8,9,12,5,30,20]))),Pc=(0,C.lazy)(()=>x(()=>import(`./SettingsPage-BUWz04bL.js`),__vite__mapDeps([40,1,2,3,15,41,32,25,26,27,8,12,20,39,19,9,5,30]))),Fc=(0,C.lazy)(()=>x(()=>import(`./SystemPage-Cwt4SToT.js`),__vite__mapDeps([42,2,1,4,5,38,24,41,29,27,9,12]))),Ic=(0,C.lazy)(()=>x(()=>import(`./NotFoundPage-YfFq8HYe.js`),__vite__mapDeps([43,2,1,3,23,5])));function Lc(){return(0,A.jsx)(xc,{children:(0,A.jsx)(C.Suspense,{fallback:(0,A.jsx)(Cc,{label:`Opening workspace…`}),children:(0,A.jsxs)(re,{children:[(0,A.jsx)(y,{path:`/`,element:(0,A.jsx)(Ec,{})}),(0,A.jsx)(y,{path:`/tasks`,element:(0,A.jsx)(Dc,{})}),(0,A.jsx)(y,{path:`/tasks/new`,element:(0,A.jsx)(Oc,{})}),(0,A.jsx)(y,{path:`/tasks/:taskId/edit`,element:(0,A.jsx)(Oc,{})}),(0,A.jsx)(y,{path:`/results`,element:(0,A.jsx)(kc,{})}),(0,A.jsx)(y,{path:`/results/:resultId`,element:(0,A.jsx)(Ac,{})}),(0,A.jsx)(y,{path:`/statistics`,element:(0,A.jsx)(jc,{})}),(0,A.jsx)(y,{path:`/comparison`,element:(0,A.jsx)(Mc,{})}),(0,A.jsx)(y,{path:`/network`,element:(0,A.jsx)(Nc,{})}),(0,A.jsx)(y,{path:`/settings`,element:(0,A.jsx)(Pc,{})}),(0,A.jsx)(y,{path:`/system`,element:(0,A.jsx)(Fc,{})}),(0,A.jsx)(y,{path:`/dashboard`,element:(0,A.jsx)(b,{to:`/`,replace:!0})}),(0,A.jsx)(y,{path:`*`,element:(0,A.jsx)(Ic,{})})]})})})}var Rc=new S({defaultOptions:{queries:{staleTime:15e3,retry:(e,t)=>t instanceof Error&&`status`in t&&typeof t.status==`number`&&t.status<500?!1:e<2,refetchOnWindowFocus:!1},mutations:{retry:!1}}}),zc={tasks:[`tasks`],task:e=>[`tasks`,e],results:(e={})=>[`results`,e],dashboardSummary:[`results`,`dashboard-summary`],result:e=>[`results`,e],statistics:(e={})=>[`statistics`,e],interfaces:[`interfaces`],routes:[`route-profiles`],providers:[`providers`],ooklaBinary:[`providers`,`ookla`,`binary`],settings:[`settings`],system:[`system`]},Bc=(0,C.createContext)(null);function Vc({children:e}){let[t,n]=(0,C.useState)([]),r=(0,C.useRef)(1),i=(0,C.useCallback)(e=>n(t=>t.filter(t=>t.id!==e)),[]),a=(0,C.useCallback)(e=>{let t=r.current++;n(n=>[...n.slice(-3),{...e,id:t}]),window.setTimeout(()=>i(t),5e3)},[i]),o=(0,C.useMemo)(()=>({toast:a}),[a]);return(0,A.jsxs)(Bc.Provider,{value:o,children:[e,(0,A.jsx)(`div`,{className:`pointer-events-none fixed bottom-5 right-5 z-[100] flex w-[calc(100%-2.5rem)] max-w-sm flex-col gap-2`,"aria-live":`polite`,children:t.map(e=>{let t=e.tone===`success`?ri:e.tone===`error`?ni:si;return(0,A.jsxs)(`div`,{className:`pointer-events-auto flex items-start gap-3 rounded-xl border border-border bg-card p-4 text-card-foreground shadow-2xl`,children:[(0,A.jsx)(t,{className:$(`mt-0.5 h-4 w-4 shrink-0`,e.tone===`success`?`text-emerald-500`:e.tone===`error`?`text-rose-500`:`text-cyan-500`)}),(0,A.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,A.jsx)(`p`,{className:`text-sm font-semibold`,children:e.title}),e.description?(0,A.jsx)(`p`,{className:`mt-1 text-xs leading-5 text-muted-foreground`,children:e.description}):null]}),(0,A.jsx)(`button`,{type:`button`,onClick:()=>i(e.id),className:`rounded p-1 text-muted-foreground hover:bg-accent`,"aria-label":`Dismiss notification`,children:(0,A.jsx)(_i,{className:`h-3.5 w-3.5`})})]},e.id)})})]})}function Hc(){let e=(0,C.useContext)(Bc);if(!e)throw Error(`useToast must be used within ToastProvider`);return e}var Uc=localStorage.getItem(`multispeed-theme`),Wc=Uc===`dark`||(Uc===null||Uc===`system`)&&window.matchMedia(`(prefers-color-scheme: dark)`).matches;document.documentElement.classList.toggle(`dark`,Wc);var Gc=document.getElementById(`root`);if(!Gc)throw Error(`Application root is missing`);(0,ue.createRoot)(Gc).render((0,A.jsx)(C.StrictMode,{children:(0,A.jsx)(r,{client:Rc,children:(0,A.jsx)(ie,{children:(0,A.jsx)(Vc,{children:(0,A.jsx)(Lc,{})})})})}));export{st as $,ci as A,Vr as B,rc as C,gi as D,_i as E,ei as F,br as G,Or as H,H as I,Zt as J,gr as K,Sr as L,ri as M,ni as N,pi as O,ti as P,mt as Q,Ur as R,oc as S,Pi as T,zr as U,Ar as V,wr as W,Lt as X,Bt as Y,Tt as Z,nc as _,Cc as a,M as at,sc as b,$ as c,T as ct,Ys as d,P as et,Qs as f,ec as g,Zs as h,Tc as i,Ee as it,ai as j,fi as k,ic as l,Xs as m,zc as n,He as nt,Sc as o,be as ot,$s as p,sr as q,wc as r,Fe as rt,mc as s,k as st,Hc as t,it as tt,ac as u,tc as v,wi as w,cc as x,lc as y,Pr as z}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/index--iC0sacy.css b/internal/frontend/dist/assets/index-sN3vPLoH.css similarity index 67% rename from internal/frontend/dist/assets/index--iC0sacy.css rename to internal/frontend/dist/assets/index-sN3vPLoH.css index eb5a76c..a9b7629 100644 --- a/internal/frontend/dist/assets/index--iC0sacy.css +++ b/internal/frontend/dist/assets/index-sN3vPLoH.css @@ -1,2 +1,2 @@ /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-ease:initial}::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-orange-300:#ffb96d;--color-orange-400:#ff8b1a;--color-orange-500:#fe6e00;--color-orange-600:#f05100;--color-amber-300:#ffd236;--color-amber-500:#f99c00;--color-amber-700:#b75000;--color-emerald-300:#5ee9b5;--color-emerald-400:#00d294;--color-emerald-500:#00bb7f;--color-emerald-600:#009767;--color-emerald-700:#007956;--color-cyan-300:#53eafd;--color-cyan-400:#00d2ef;--color-cyan-500:#00b7d7;--color-cyan-600:#0092b5;--color-cyan-700:#007492;--color-cyan-900:#104e64;--color-cyan-950:#053345;--color-violet-300:#c4b4ff;--color-violet-400:#a685ff;--color-violet-500:#8d54ff;--color-violet-600:#7f22fe;--color-violet-700:#7008e7;--color-rose-300:#ffa2ae;--color-rose-400:#ff667f;--color-rose-500:#ff2357;--color-rose-600:#e70044;--color-rose-700:#c20039;--color-slate-200:#e2e8f0;--color-slate-500:#62748e;--color-slate-950:#020618;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0, 0, .2, 1) infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-sm:8px;--blur-xl:24px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}@supports (color:lab(0% 0 0)){:root,:host{--color-orange-300:lab(80.8059% 21.7313 50.4455);--color-orange-400:lab(70.0429% 42.5156 75.8207);--color-orange-500:lab(64.272% 57.1788 90.3583);--color-orange-600:lab(57.1026% 64.2584 89.8886);--color-amber-300:lab(86.4156% 6.13147 78.3961);--color-amber-500:lab(72.7183% 31.8672 97.9407);--color-amber-700:lab(47.2709% 42.9082 69.2966);--color-emerald-300:lab(83.9203% -48.7124 13.8849);--color-emerald-400:lab(75.0771% -60.7313 19.4147);--color-emerald-500:lab(66.9756% -58.27 19.5419);--color-emerald-600:lab(55.0481% -49.9246 15.93);--color-emerald-700:lab(44.4871% -41.0396 11.0361);--color-cyan-300:lab(85.3886% -36.7636 -21.5716);--color-cyan-400:lab(76.6045% -40.9406 -29.6231);--color-cyan-500:lab(67.805% -35.3952 -30.2018);--color-cyan-600:lab(55.1767% -26.7496 -30.5139);--color-cyan-700:lab(44.7267% -21.5987 -26.118);--color-cyan-900:lab(30.372% -13.1853 -18.7887);--color-cyan-950:lab(19.1528% -9.68757 -15.5267);--color-violet-300:lab(76.7419% 18.3911 -37.0706);--color-violet-400:lab(62.8239% 34.9159 -60.0512);--color-violet-500:lab(49.9355% 55.1776 -81.8963);--color-violet-600:lab(41.088% 68.9966 -91.995);--color-violet-700:lab(35.2783% 67.9912 -88.793);--color-rose-300:lab(76.6339% 38.3549 9.68835);--color-rose-400:lab(64.4125% 63.0291 19.2068);--color-rose-500:lab(56.101% 79.4328 31.4532);--color-rose-600:lab(49.1882% 81.577 36.0311);--color-rose-700:lab(41.1651% 71.6251 30.3087);--color-slate-200:lab(91.7353% -.998765 -4.76968);--color-slate-500:lab(48.0876% -2.03595 -16.5814);--color-slate-950:lab(1.76974% 1.32743 -9.28855)}}}@layer base{*,:after,:before{box-sizing:border-box;border:0 solid;margin:0;padding:0}::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--background:210 33% 98%;--foreground:222 47% 11%;--card:0 0% 100%;--card-foreground:222 47% 11%;--border:214 25% 89%;--input:214 24% 86%;--ring:190 90% 38%;--primary:190 90% 37%;--primary-foreground:0 0% 100%;--secondary:214 30% 94%;--secondary-foreground:222 35% 18%;--muted:214 28% 95%;--muted-foreground:215 16% 45%;--accent:190 60% 94%;--accent-foreground:190 80% 24%;--destructive:350 75% 50%;--destructive-foreground:0 0% 100%;--radius:.8rem;--chart-text:#64748b;--chart-grid:#64748b26}.dark{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--background:222 47% 7%;--foreground:210 40% 96%;--card:220 38% 10%;--card-foreground:210 40% 96%;--border:218 28% 18%;--input:218 28% 22%;--ring:187 92% 51%;--primary:187 92% 44%;--primary-foreground:222 60% 7%;--secondary:218 30% 16%;--secondary-foreground:210 40% 94%;--muted:219 29% 14%;--muted-foreground:215 17% 65%;--accent:190 50% 14%;--accent-foreground:186 85% 73%;--destructive:350 70% 50%;--destructive-foreground:0 0% 100%;--chart-text:#94a3b8;--chart-grid:#94a3b81f}*{border-color:hsl(var(--border))}html{background:hsl(var(--background));font-feature-settings:"cv02", "cv03", "cv04", "cv11";text-rendering:optimizelegibility;min-width:320px}body{background-color:hsl(var(--background));min-height:100vh;color:hsl(var(--foreground));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}::selection{background:hsl(var(--primary) / .24)}:focus-visible{outline:none}button,a,input,select,textarea{-webkit-tap-highlight-color:transparent}input[type=date]::-webkit-calendar-picker-indicator{opacity:.55}input[type=datetime-local]::-webkit-calendar-picker-indicator{opacity:.55}pre,code,.tabular-nums{font-variant-numeric:tabular-nums}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}}@layer components{.page-grid{background-image:linear-gradient(hsl(var(--border) / .17) 1px, transparent 1px), linear-gradient(90deg, hsl(var(--border) / .17) 1px, transparent 1px);background-size:32px 32px;-webkit-mask-image:linear-gradient(#000,#0000 540px);mask-image:linear-gradient(#000,#0000 540px)}.metric-number{letter-spacing:-.035em;font-variant-numeric:tabular-nums}.scrollbar-thin{scrollbar-color:hsl(var(--muted-foreground) / .25) transparent;scrollbar-width:thin}.data-table th{height:calc(var(--spacing) * 10);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:hsl(var(--border));padding-inline:calc(var(--spacing) * 3);text-align:left;--tw-font-weight:var(--font-weight-semibold);font-size:11px;font-weight:var(--font-weight-semibold);--tw-tracking:.08em;letter-spacing:.08em;white-space:nowrap;color:hsl(var(--muted-foreground));text-transform:uppercase}.data-table td{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:hsl(var(--border))}@supports (color:color-mix(in lab, red, red)){.data-table td{border-color:color-mix(in oklab, hsl(var(--border)) 70%, transparent)}}.data-table td{padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);vertical-align:middle;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.data-table tbody tr:last-child td{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.data-table tbody tr{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.data-table tbody tr:hover{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.data-table tbody tr:hover{background-color:color-mix(in oklab, hsl(var(--muted)) 45%, transparent)}}}}@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-x-5{inset-inline:calc(var(--spacing) * 5)}.inset-y-0{inset-block:0}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:calc(var(--spacing) * 4)}.top-11{top:calc(var(--spacing) * 11)}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.right-5{right:calc(var(--spacing) * 5)}.bottom-5{bottom:calc(var(--spacing) * 5)}.left-0{left:0}.left-1\/2{left:50%}.left-3{left:calc(var(--spacing) * 3)}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[100\]{z-index:100}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-1{margin-block:var(--spacing)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-7{margin-top:calc(var(--spacing) * 7)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-12{margin-top:calc(var(--spacing) * 12)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-1{margin-left:var(--spacing)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-1{height:var(--spacing)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-11{height:calc(var(--spacing) * 11)}.h-12{height:calc(var(--spacing) * 12)}.h-16{height:calc(var(--spacing) * 16)}.h-72{height:calc(var(--spacing) * 72)}.h-80{height:calc(var(--spacing) * 80)}.h-\[25rem\]{height:25rem}.h-\[28rem\]{height:28rem}.h-fit{height:-moz-fit-content;height:fit-content}.h-px{height:1px}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-\[32rem\]{max-height:32rem}.max-h-\[42rem\]{max-height:42rem}.max-h-\[92vh\]{max-height:92vh}.min-h-5{min-height:calc(var(--spacing) * 5)}.min-h-24{min-height:calc(var(--spacing) * 24)}.min-h-52{min-height:calc(var(--spacing) * 52)}.min-h-56{min-height:calc(var(--spacing) * 56)}.min-h-\[calc\(100vh-4rem\)\]{min-height:calc(100vh - 4rem)}.min-h-screen{min-height:100vh}.w-1{width:var(--spacing)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-10{width:calc(var(--spacing) * 10)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-24{width:calc(var(--spacing) * 24)}.w-44{width:calc(var(--spacing) * 44)}.w-52{width:calc(var(--spacing) * 52)}.w-64{width:calc(var(--spacing) * 64)}.w-\[calc\(100\%-2\.5rem\)\]{width:calc(100% - 2.5rem)}.w-\[calc\(100\%-2rem\)\]{width:calc(100% - 2rem)}.w-\[min\(88vw\,19rem\)\]{width:min(88vw,19rem)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-40{max-width:calc(var(--spacing) * 40)}.max-w-56{max-width:calc(var(--spacing) * 56)}.max-w-\[1600px\]{max-width:1600px}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:0}.min-w-5{min-width:calc(var(--spacing) * 5)}.min-w-40{min-width:calc(var(--spacing) * 40)}.min-w-44{min-width:calc(var(--spacing) * 44)}.min-w-52{min-width:calc(var(--spacing) * 52)}.min-w-\[760px\]{min-width:760px}.min-w-\[900px\]{min-width:900px}.min-w-\[930px\]{min-width:930px}.min-w-\[1000px\]{min-width:1000px}.min-w-\[1160px\]{min-width:1160px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:0px;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.animate-pulsebar{animation:1.1s ease-in-out infinite pulsebar}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize-y{resize:vertical}.scrollbar-thin{scrollbar-width:thin}.list-none{list-style-type:none}.appearance-none{appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\[125px_1fr\]{grid-template-columns:125px 1fr}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-\[3px\]{gap:3px}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-3{-moz-column-gap:calc(var(--spacing) * 3);column-gap:calc(var(--spacing) * 3)}.gap-x-4{-moz-column-gap:calc(var(--spacing) * 4);column-gap:calc(var(--spacing) * 4)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border>:not(:last-child)){border-color:hsl(var(--border))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:2147483647px}.rounded-lg{border-radius:calc(var(--radius) - 2px)}.rounded-md{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:var(--radius)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-amber-500\/25{border-color:#f99c0040}@supports (color:color-mix(in lab, red, red)){.border-amber-500\/25{border-color:color-mix(in oklab, var(--color-amber-500) 25%, transparent)}}.border-border{border-color:hsl(var(--border))}.border-current{border-color:currentColor}.border-cyan-400\/20{border-color:#00d2ef33}@supports (color:color-mix(in lab, red, red)){.border-cyan-400\/20{border-color:color-mix(in oklab, var(--color-cyan-400) 20%, transparent)}}.border-cyan-500\/25{border-color:#00b7d740}@supports (color:color-mix(in lab, red, red)){.border-cyan-500\/25{border-color:color-mix(in oklab, var(--color-cyan-500) 25%, transparent)}}.border-emerald-500\/25{border-color:#00bb7f40}@supports (color:color-mix(in lab, red, red)){.border-emerald-500\/25{border-color:color-mix(in oklab, var(--color-emerald-500) 25%, transparent)}}.border-input{border-color:hsl(var(--input))}.border-orange-500\/25{border-color:#fe6e0040}@supports (color:color-mix(in lab, red, red)){.border-orange-500\/25{border-color:color-mix(in oklab, var(--color-orange-500) 25%, transparent)}}.border-primary\/25{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.border-primary\/25{border-color:color-mix(in oklab, hsl(var(--primary)) 25%, transparent)}}.border-primary\/30{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.border-primary\/30{border-color:color-mix(in oklab, hsl(var(--primary)) 30%, transparent)}}.border-primary\/40{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.border-primary\/40{border-color:color-mix(in oklab, hsl(var(--primary)) 40%, transparent)}}.border-primary\/50{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.border-primary\/50{border-color:color-mix(in oklab, hsl(var(--primary)) 50%, transparent)}}.border-rose-500\/20{border-color:#ff235733}@supports (color:color-mix(in lab, red, red)){.border-rose-500\/20{border-color:color-mix(in oklab, var(--color-rose-500) 20%, transparent)}}.border-rose-500\/25{border-color:#ff235740}@supports (color:color-mix(in lab, red, red)){.border-rose-500\/25{border-color:color-mix(in oklab, var(--color-rose-500) 25%, transparent)}}.border-transparent{border-color:#0000}.border-violet-500\/25{border-color:#8d54ff40}@supports (color:color-mix(in lab, red, red)){.border-violet-500\/25{border-color:color-mix(in oklab, var(--color-violet-500) 25%, transparent)}}.\!bg-muted\/25{background-color:hsl(var(--muted))!important}@supports (color:color-mix(in lab, red, red)){.\!bg-muted\/25{background-color:color-mix(in oklab, hsl(var(--muted)) 25%, transparent)!important}}.bg-accent{background-color:hsl(var(--accent))}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab, red, red)){.bg-amber-500\/10{background-color:color-mix(in oklab, var(--color-amber-500) 10%, transparent)}}.bg-amber-500\/\[\.06\]{background-color:#f99c000f}@supports (color:color-mix(in lab, red, red)){.bg-amber-500\/\[\.06\]{background-color:color-mix(in oklab, var(--color-amber-500) 6%, transparent)}}.bg-amber-500\/\[\.07\]{background-color:#f99c0012}@supports (color:color-mix(in lab, red, red)){.bg-amber-500\/\[\.07\]{background-color:color-mix(in oklab, var(--color-amber-500) 7.0%, transparent)}}.bg-amber-500\/\[\.055\]{background-color:#f99c000e}@supports (color:color-mix(in lab, red, red)){.bg-amber-500\/\[\.055\]{background-color:color-mix(in oklab, var(--color-amber-500) 5.5%, transparent)}}.bg-background,.bg-background\/70{background-color:hsl(var(--background))}@supports (color:color-mix(in lab, red, red)){.bg-background\/70{background-color:color-mix(in oklab, hsl(var(--background)) 70%, transparent)}}.bg-background\/90{background-color:hsl(var(--background))}@supports (color:color-mix(in lab, red, red)){.bg-background\/90{background-color:color-mix(in oklab, hsl(var(--background)) 90%, transparent)}}.bg-border{background-color:hsl(var(--border))}.bg-card,.bg-card\/95{background-color:hsl(var(--card))}@supports (color:color-mix(in lab, red, red)){.bg-card\/95{background-color:color-mix(in oklab, hsl(var(--card)) 95%, transparent)}}.bg-cyan-300{background-color:var(--color-cyan-300)}.bg-cyan-400{background-color:var(--color-cyan-400)}.bg-cyan-500{background-color:var(--color-cyan-500)}.bg-cyan-500\/10{background-color:#00b7d71a}@supports (color:color-mix(in lab, red, red)){.bg-cyan-500\/10{background-color:color-mix(in oklab, var(--color-cyan-500) 10%, transparent)}}.bg-cyan-500\/\[\.06\]{background-color:#00b7d70f}@supports (color:color-mix(in lab, red, red)){.bg-cyan-500\/\[\.06\]{background-color:color-mix(in oklab, var(--color-cyan-500) 6%, transparent)}}.bg-destructive{background-color:hsl(var(--destructive))}.bg-emerald-400{background-color:var(--color-emerald-400)}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-emerald-500\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab, red, red)){.bg-emerald-500\/10{background-color:color-mix(in oklab, var(--color-emerald-500) 10%, transparent)}}.bg-emerald-500\/\[\.06\]{background-color:#00bb7f0f}@supports (color:color-mix(in lab, red, red)){.bg-emerald-500\/\[\.06\]{background-color:color-mix(in oklab, var(--color-emerald-500) 6%, transparent)}}.bg-emerald-500\/\[\.055\]{background-color:#00bb7f0e}@supports (color:color-mix(in lab, red, red)){.bg-emerald-500\/\[\.055\]{background-color:color-mix(in oklab, var(--color-emerald-500) 5.5%, transparent)}}.bg-muted{background-color:hsl(var(--muted))}.bg-muted-foreground\/25{background-color:hsl(var(--muted-foreground))}@supports (color:color-mix(in lab, red, red)){.bg-muted-foreground\/25{background-color:color-mix(in oklab, hsl(var(--muted-foreground)) 25%, transparent)}}.bg-muted-foreground\/50{background-color:hsl(var(--muted-foreground))}@supports (color:color-mix(in lab, red, red)){.bg-muted-foreground\/50{background-color:color-mix(in oklab, hsl(var(--muted-foreground)) 50%, transparent)}}.bg-muted\/30{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.bg-muted\/30{background-color:color-mix(in oklab, hsl(var(--muted)) 30%, transparent)}}.bg-muted\/35{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.bg-muted\/35{background-color:color-mix(in oklab, hsl(var(--muted)) 35%, transparent)}}.bg-muted\/40{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.bg-muted\/40{background-color:color-mix(in oklab, hsl(var(--muted)) 40%, transparent)}}.bg-muted\/50{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.bg-muted\/50{background-color:color-mix(in oklab, hsl(var(--muted)) 50%, transparent)}}.bg-orange-400{background-color:var(--color-orange-400)}.bg-orange-500\/10{background-color:#fe6e001a}@supports (color:color-mix(in lab, red, red)){.bg-orange-500\/10{background-color:color-mix(in oklab, var(--color-orange-500) 10%, transparent)}}.bg-orange-500\/\[\.06\]{background-color:#fe6e000f}@supports (color:color-mix(in lab, red, red)){.bg-orange-500\/\[\.06\]{background-color:color-mix(in oklab, var(--color-orange-500) 6%, transparent)}}.bg-primary,.bg-primary\/10{background-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.bg-primary\/10{background-color:color-mix(in oklab, hsl(var(--primary)) 10%, transparent)}}.bg-primary\/\[\.07\]{background-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.bg-primary\/\[\.07\]{background-color:color-mix(in oklab, hsl(var(--primary)) 7.0%, transparent)}}.bg-primary\/\[\.055\]{background-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.bg-primary\/\[\.055\]{background-color:color-mix(in oklab, hsl(var(--primary)) 5.5%, transparent)}}.bg-rose-500{background-color:var(--color-rose-500)}.bg-rose-500\/10{background-color:#ff23571a}@supports (color:color-mix(in lab, red, red)){.bg-rose-500\/10{background-color:color-mix(in oklab, var(--color-rose-500) 10%, transparent)}}.bg-rose-500\/\[\.06\]{background-color:#ff23570f}@supports (color:color-mix(in lab, red, red)){.bg-rose-500\/\[\.06\]{background-color:color-mix(in oklab, var(--color-rose-500) 6%, transparent)}}.bg-rose-500\/\[\.055\]{background-color:#ff23570e}@supports (color:color-mix(in lab, red, red)){.bg-rose-500\/\[\.055\]{background-color:color-mix(in oklab, var(--color-rose-500) 5.5%, transparent)}}.bg-secondary{background-color:hsl(var(--secondary))}.bg-slate-500{background-color:var(--color-slate-500)}.bg-slate-950{background-color:var(--color-slate-950)}.bg-slate-950\/60{background-color:#02061899}@supports (color:color-mix(in lab, red, red)){.bg-slate-950\/60{background-color:color-mix(in oklab, var(--color-slate-950) 60%, transparent)}}.bg-slate-950\/65{background-color:#020618a6}@supports (color:color-mix(in lab, red, red)){.bg-slate-950\/65{background-color:color-mix(in oklab, var(--color-slate-950) 65%, transparent)}}.bg-transparent{background-color:#0000}.bg-violet-400{background-color:var(--color-violet-400)}.bg-violet-500\/10{background-color:#8d54ff1a}@supports (color:color-mix(in lab, red, red)){.bg-violet-500\/10{background-color:color-mix(in oklab, var(--color-violet-500) 10%, transparent)}}.bg-white{background-color:var(--color-white)}.\!p-4{padding:calc(var(--spacing) * 4)!important}.p-1{padding:var(--spacing)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-3\.5{padding:calc(var(--spacing) * 3.5)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pr-9{padding-right:calc(var(--spacing) * 9)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-5{padding-bottom:calc(var(--spacing) * 5)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-6{padding-left:calc(var(--spacing) * 6)}.pl-9{padding-left:calc(var(--spacing) * 9)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[17px\]{font-size:17px}.leading-3{--tw-leading:calc(var(--spacing) * 3);line-height:calc(var(--spacing) * 3)}.leading-4{--tw-leading:calc(var(--spacing) * 4);line-height:calc(var(--spacing) * 4)}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[-0\.03em\]{--tw-tracking:-.03em;letter-spacing:-.03em}.tracking-\[-0\.035em\]{--tw-tracking:-.035em;letter-spacing:-.035em}.tracking-\[\.1em\]{--tw-tracking:.1em;letter-spacing:.1em}.tracking-\[\.13em\]{--tw-tracking:.13em;letter-spacing:.13em}.tracking-\[\.15em\]{--tw-tracking:.15em;letter-spacing:.15em}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent-foreground,.text-accent-foreground\/65{color:hsl(var(--accent-foreground))}@supports (color:color-mix(in lab, red, red)){.text-accent-foreground\/65{color:color-mix(in oklab, hsl(var(--accent-foreground)) 65%, transparent)}}.text-amber-500{color:var(--color-amber-500)}.text-amber-700{color:var(--color-amber-700)}.text-card-foreground{color:hsl(var(--card-foreground))}.text-cyan-500{color:var(--color-cyan-500)}.text-cyan-600{color:var(--color-cyan-600)}.text-cyan-700{color:var(--color-cyan-700)}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-emerald-500{color:var(--color-emerald-500)}.text-emerald-600{color:var(--color-emerald-600)}.text-emerald-700{color:var(--color-emerald-700)}.text-foreground{color:hsl(var(--foreground))}.text-muted-foreground,.text-muted-foreground\/70{color:hsl(var(--muted-foreground))}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/70{color:color-mix(in oklab, hsl(var(--muted-foreground)) 70%, transparent)}}.text-orange-500{color:var(--color-orange-500)}.text-orange-600{color:var(--color-orange-600)}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-rose-500{color:var(--color-rose-500)}.text-rose-600{color:var(--color-rose-600)}.text-rose-700{color:var(--color-rose-700)}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-slate-200{color:var(--color-slate-200)}.text-violet-500{color:var(--color-violet-500)}.text-violet-600{color:var(--color-violet-600)}.text-violet-700{color:var(--color-violet-700)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.accent-cyan-500{accent-color:var(--color-cyan-500)}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-65{opacity:.65}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-panel{--tw-shadow:0 18px 45px -32px var(--tw-shadow-color,#0208178c);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-cyan-900\/20{--tw-shadow-color:#104e6433}@supports (color:color-mix(in lab, red, red)){.shadow-cyan-900\/20{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-cyan-900) 20%, transparent) var(--tw-shadow-alpha), transparent)}}.shadow-cyan-950\/30{--tw-shadow-color:#0533454d}@supports (color:color-mix(in lab, red, red)){.shadow-cyan-950\/30{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-cyan-950) 30%, transparent) var(--tw-shadow-alpha), transparent)}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.backdrop-blur-\[2px\]{--tw-backdrop-blur:blur(2px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-xl{--tw-backdrop-blur:blur(var(--blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.group-hover\:text-foreground:is(:where(.group):hover *){color:hsl(var(--foreground))}}.placeholder\:text-muted-foreground\/70::placeholder{color:hsl(var(--muted-foreground))}@supports (color:color-mix(in lab, red, red)){.placeholder\:text-muted-foreground\/70::placeholder{color:color-mix(in oklab, hsl(var(--muted-foreground)) 70%, transparent)}}.first\:pt-0:first-child{padding-top:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.last\:pb-0:last-child{padding-bottom:0}@media (hover:hover){.hover\:border-primary\/25:hover{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/25:hover{border-color:color-mix(in oklab, hsl(var(--primary)) 25%, transparent)}}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive))}@supports (color:color-mix(in lab, red, red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab, hsl(var(--destructive)) 90%, transparent)}}.hover\:bg-muted:hover,.hover\:bg-muted\/45:hover{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/45:hover{background-color:color-mix(in oklab, hsl(var(--muted)) 45%, transparent)}}.hover\:bg-muted\/50:hover{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab, hsl(var(--muted)) 50%, transparent)}}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, hsl(var(--primary)) 90%, transparent)}}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary))}@supports (color:color-mix(in lab, red, red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, hsl(var(--secondary)) 80%, transparent)}}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:text-primary:hover{color:hsl(var(--primary))}.hover\:underline:hover{text-decoration-line:underline}}.focus\:border-primary\/70:focus{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.focus\:border-primary\/70:focus{border-color:color-mix(in oklab, hsl(var(--primary)) 70%, transparent)}}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:bg-rose-500\/10:focus{background-color:#ff23571a}@supports (color:color-mix(in lab, red, red)){.focus\:bg-rose-500\/10:focus{background-color:color-mix(in oklab, var(--color-rose-500) 10%, transparent)}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-primary\/15:focus{--tw-ring-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.focus\:ring-primary\/15:focus{--tw-ring-color:color-mix(in oklab, hsl(var(--primary)) 15%, transparent)}}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:hsl(var(--ring))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color:hsl(var(--background))}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.data-\[state\=checked\]\:translate-x-5[data-state=checked]{--tw-translate-x:calc(var(--spacing) * 5);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:hsl(var(--primary))}@media (min-width:40rem){.sm\:block{display:block}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-\[145px_1fr\]{grid-template-columns:145px 1fr}.sm\:grid-cols-\[150px_1fr\]{grid-template-columns:150px 1fr}.sm\:grid-cols-\[minmax\(0\,1\.2fr\)_repeat\(3\,minmax\(90px\,\.55fr\)\)\]{grid-template-columns:minmax(0,1.2fr) repeat(3,minmax(90px,.55fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:items-end{align-items:flex-end}.sm\:p-5{padding:calc(var(--spacing) * 5)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}.sm\:py-7{padding-block:calc(var(--spacing) * 7)}.sm\:pt-6{padding-top:calc(var(--spacing) * 6)}.sm\:pb-6{padding-bottom:calc(var(--spacing) * 6)}.sm\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}}@media (min-width:48rem){.md\:col-span-3{grid-column:span 3/span 3}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-\[minmax\(220px\,1fr\)_repeat\(3\,minmax\(140px\,\.36fr\)\)_auto\]{grid-template-columns:minmax(220px,1fr) repeat(3,minmax(140px,.36fr)) auto}}@media (min-width:64rem){.lg\:sticky{position:sticky}.lg\:top-24{top:calc(var(--spacing) * 24)}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-\[1fr_1fr_1fr_1fr_auto\]{grid-template-columns:1fr 1fr 1fr 1fr auto}.lg\:grid-cols-\[1fr_1fr_auto\]{grid-template-columns:1fr 1fr auto}.lg\:grid-cols-\[250px_minmax\(0\,1fr\)\]{grid-template-columns:250px minmax(0,1fr)}.lg\:flex-col{flex-direction:column}.lg\:px-8{padding-inline:calc(var(--spacing) * 8)}.lg\:py-8{padding-block:calc(var(--spacing) * 8)}.lg\:pl-64{padding-left:calc(var(--spacing) * 64)}}@media (min-width:80rem){.xl\:col-span-2{grid-column:span 2/span 2}.xl\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xl\:grid-cols-\[minmax\(0\,1\.65fr\)_minmax\(320px\,\.75fr\)\]{grid-template-columns:minmax(0,1.65fr) minmax(320px,.75fr)}}@media (min-width:96rem){.\32 xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.\32 xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}.dark\:text-amber-300:is(.dark *){color:var(--color-amber-300)}.dark\:text-cyan-300:is(.dark *){color:var(--color-cyan-300)}.dark\:text-emerald-300:is(.dark *){color:var(--color-emerald-300)}.dark\:text-emerald-400:is(.dark *){color:var(--color-emerald-400)}.dark\:text-orange-300:is(.dark *){color:var(--color-orange-300)}.dark\:text-rose-300:is(.dark *){color:var(--color-rose-300)}.dark\:text-rose-400:is(.dark *){color:var(--color-rose-400)}.dark\:text-violet-300:is(.dark *){color:var(--color-violet-300)}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}}@keyframes pulsebar{0%,to{opacity:.55;transform:scaleY(.45)}50%{opacity:1;transform:scaleY(1)}} +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-ease:initial}::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-orange-300:#ffb96d;--color-orange-400:#ff8b1a;--color-orange-500:#fe6e00;--color-orange-600:#f05100;--color-amber-300:#ffd236;--color-amber-500:#f99c00;--color-amber-700:#b75000;--color-emerald-300:#5ee9b5;--color-emerald-400:#00d294;--color-emerald-500:#00bb7f;--color-emerald-600:#009767;--color-emerald-700:#007956;--color-cyan-300:#53eafd;--color-cyan-400:#00d2ef;--color-cyan-500:#00b7d7;--color-cyan-600:#0092b5;--color-cyan-700:#007492;--color-cyan-900:#104e64;--color-cyan-950:#053345;--color-violet-300:#c4b4ff;--color-violet-400:#a685ff;--color-violet-500:#8d54ff;--color-violet-600:#7f22fe;--color-violet-700:#7008e7;--color-rose-300:#ffa2ae;--color-rose-400:#ff667f;--color-rose-500:#ff2357;--color-rose-600:#e70044;--color-rose-700:#c20039;--color-slate-200:#e2e8f0;--color-slate-500:#62748e;--color-slate-950:#020618;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0, 0, .2, 1) infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-sm:8px;--blur-xl:24px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}@supports (color:lab(0% 0 0)){:root,:host{--color-orange-300:lab(80.8059% 21.7313 50.4455);--color-orange-400:lab(70.0429% 42.5156 75.8207);--color-orange-500:lab(64.272% 57.1788 90.3583);--color-orange-600:lab(57.1026% 64.2584 89.8886);--color-amber-300:lab(86.4156% 6.13147 78.3961);--color-amber-500:lab(72.7183% 31.8672 97.9407);--color-amber-700:lab(47.2709% 42.9082 69.2966);--color-emerald-300:lab(83.9203% -48.7124 13.8849);--color-emerald-400:lab(75.0771% -60.7313 19.4147);--color-emerald-500:lab(66.9756% -58.27 19.5419);--color-emerald-600:lab(55.0481% -49.9246 15.93);--color-emerald-700:lab(44.4871% -41.0396 11.0361);--color-cyan-300:lab(85.3886% -36.7636 -21.5716);--color-cyan-400:lab(76.6045% -40.9406 -29.6231);--color-cyan-500:lab(67.805% -35.3952 -30.2018);--color-cyan-600:lab(55.1767% -26.7496 -30.5139);--color-cyan-700:lab(44.7267% -21.5987 -26.118);--color-cyan-900:lab(30.372% -13.1853 -18.7887);--color-cyan-950:lab(19.1528% -9.68757 -15.5267);--color-violet-300:lab(76.7419% 18.3911 -37.0706);--color-violet-400:lab(62.8239% 34.9159 -60.0512);--color-violet-500:lab(49.9355% 55.1776 -81.8963);--color-violet-600:lab(41.088% 68.9966 -91.995);--color-violet-700:lab(35.2783% 67.9912 -88.793);--color-rose-300:lab(76.6339% 38.3549 9.68835);--color-rose-400:lab(64.4125% 63.0291 19.2068);--color-rose-500:lab(56.101% 79.4328 31.4532);--color-rose-600:lab(49.1882% 81.577 36.0311);--color-rose-700:lab(41.1651% 71.6251 30.3087);--color-slate-200:lab(91.7353% -.998765 -4.76968);--color-slate-500:lab(48.0876% -2.03595 -16.5814);--color-slate-950:lab(1.76974% 1.32743 -9.28855)}}}@layer base{*,:after,:before{box-sizing:border-box;border:0 solid;margin:0;padding:0}::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--background:210 33% 98%;--foreground:222 47% 11%;--card:0 0% 100%;--card-foreground:222 47% 11%;--border:214 25% 89%;--input:214 24% 86%;--ring:190 90% 29%;--primary:190 90% 29%;--primary-foreground:0 0% 100%;--secondary:214 30% 94%;--secondary-foreground:222 35% 18%;--muted:214 28% 95%;--muted-foreground:215 16% 45%;--accent:190 60% 94%;--accent-foreground:190 80% 24%;--destructive:350 75% 50%;--destructive-foreground:0 0% 100%;--radius:.8rem;--chart-text:#64748b;--chart-grid:#64748b26}.dark{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--background:222 47% 7%;--foreground:210 40% 96%;--card:220 38% 10%;--card-foreground:210 40% 96%;--border:218 28% 18%;--input:218 28% 22%;--ring:187 92% 51%;--primary:187 92% 44%;--primary-foreground:222 60% 7%;--secondary:218 30% 16%;--secondary-foreground:210 40% 94%;--muted:219 29% 14%;--muted-foreground:215 17% 65%;--accent:190 50% 14%;--accent-foreground:186 85% 73%;--destructive:350 70% 50%;--destructive-foreground:0 0% 100%;--chart-text:#94a3b8;--chart-grid:#94a3b81f}*{border-color:hsl(var(--border))}html{background:hsl(var(--background));font-feature-settings:"cv02", "cv03", "cv04", "cv11";text-rendering:optimizelegibility;min-width:320px}body{background-color:hsl(var(--background));min-height:100vh;color:hsl(var(--foreground));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}::selection{background:hsl(var(--primary) / .24)}:focus-visible{outline:none}button,a,input,select,textarea{-webkit-tap-highlight-color:transparent}input[type=date]::-webkit-calendar-picker-indicator{opacity:.55}input[type=datetime-local]::-webkit-calendar-picker-indicator{opacity:.55}pre,code,.tabular-nums{font-variant-numeric:tabular-nums}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}}@layer components{.page-grid{background-image:linear-gradient(hsl(var(--border) / .17) 1px, transparent 1px), linear-gradient(90deg, hsl(var(--border) / .17) 1px, transparent 1px);background-size:32px 32px;-webkit-mask-image:linear-gradient(#000,#0000 540px);mask-image:linear-gradient(#000,#0000 540px)}.metric-number{letter-spacing:-.035em;font-variant-numeric:tabular-nums}.scrollbar-thin{scrollbar-color:hsl(var(--muted-foreground) / .25) transparent;scrollbar-width:thin}.data-table th{height:calc(var(--spacing) * 10);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:hsl(var(--border));padding-inline:calc(var(--spacing) * 3);text-align:left;--tw-font-weight:var(--font-weight-semibold);font-size:11px;font-weight:var(--font-weight-semibold);--tw-tracking:.08em;letter-spacing:.08em;white-space:nowrap;color:hsl(var(--muted-foreground));text-transform:uppercase}.data-table td{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:hsl(var(--border))}@supports (color:color-mix(in lab, red, red)){.data-table td{border-color:color-mix(in oklab, hsl(var(--border)) 70%, transparent)}}.data-table td{padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);vertical-align:middle;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.data-table tbody tr:last-child td{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.data-table tbody tr{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.data-table tbody tr:hover{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.data-table tbody tr:hover{background-color:color-mix(in oklab, hsl(var(--muted)) 45%, transparent)}}}}@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-x-5{inset-inline:calc(var(--spacing) * 5)}.inset-y-0{inset-block:0}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:calc(var(--spacing) * 4)}.top-11{top:calc(var(--spacing) * 11)}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.right-5{right:calc(var(--spacing) * 5)}.bottom-5{bottom:calc(var(--spacing) * 5)}.left-0{left:0}.left-1\/2{left:50%}.left-3{left:calc(var(--spacing) * 3)}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[100\]{z-index:100}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-1{margin-block:var(--spacing)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-7{margin-top:calc(var(--spacing) * 7)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-12{margin-top:calc(var(--spacing) * 12)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-1{margin-left:var(--spacing)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-1{height:var(--spacing)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-11{height:calc(var(--spacing) * 11)}.h-12{height:calc(var(--spacing) * 12)}.h-16{height:calc(var(--spacing) * 16)}.h-72{height:calc(var(--spacing) * 72)}.h-80{height:calc(var(--spacing) * 80)}.h-\[25rem\]{height:25rem}.h-\[28rem\]{height:28rem}.h-fit{height:-moz-fit-content;height:fit-content}.h-px{height:1px}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-\[32rem\]{max-height:32rem}.max-h-\[42rem\]{max-height:42rem}.max-h-\[92vh\]{max-height:92vh}.min-h-5{min-height:calc(var(--spacing) * 5)}.min-h-24{min-height:calc(var(--spacing) * 24)}.min-h-52{min-height:calc(var(--spacing) * 52)}.min-h-56{min-height:calc(var(--spacing) * 56)}.min-h-\[calc\(100vh-4rem\)\]{min-height:calc(100vh - 4rem)}.min-h-screen{min-height:100vh}.w-1{width:var(--spacing)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-10{width:calc(var(--spacing) * 10)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-24{width:calc(var(--spacing) * 24)}.w-44{width:calc(var(--spacing) * 44)}.w-52{width:calc(var(--spacing) * 52)}.w-64{width:calc(var(--spacing) * 64)}.w-\[calc\(100\%-2\.5rem\)\]{width:calc(100% - 2.5rem)}.w-\[calc\(100\%-2rem\)\]{width:calc(100% - 2rem)}.w-\[min\(88vw\,19rem\)\]{width:min(88vw,19rem)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-40{max-width:calc(var(--spacing) * 40)}.max-w-56{max-width:calc(var(--spacing) * 56)}.max-w-\[1600px\]{max-width:1600px}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:0}.min-w-5{min-width:calc(var(--spacing) * 5)}.min-w-40{min-width:calc(var(--spacing) * 40)}.min-w-44{min-width:calc(var(--spacing) * 44)}.min-w-52{min-width:calc(var(--spacing) * 52)}.min-w-\[760px\]{min-width:760px}.min-w-\[900px\]{min-width:900px}.min-w-\[930px\]{min-width:930px}.min-w-\[1000px\]{min-width:1000px}.min-w-\[1160px\]{min-width:1160px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:0px;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.animate-pulsebar{animation:1.1s ease-in-out infinite pulsebar}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize-y{resize:vertical}.scrollbar-thin{scrollbar-width:thin}.list-none{list-style-type:none}.appearance-none{appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\[125px_1fr\]{grid-template-columns:125px 1fr}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-\[3px\]{gap:3px}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-3{-moz-column-gap:calc(var(--spacing) * 3);column-gap:calc(var(--spacing) * 3)}.gap-x-4{-moz-column-gap:calc(var(--spacing) * 4);column-gap:calc(var(--spacing) * 4)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border>:not(:last-child)){border-color:hsl(var(--border))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:2147483647px}.rounded-lg{border-radius:calc(var(--radius) - 2px)}.rounded-md{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:var(--radius)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-amber-500\/25{border-color:#f99c0040}@supports (color:color-mix(in lab, red, red)){.border-amber-500\/25{border-color:color-mix(in oklab, var(--color-amber-500) 25%, transparent)}}.border-border{border-color:hsl(var(--border))}.border-current{border-color:currentColor}.border-cyan-400\/20{border-color:#00d2ef33}@supports (color:color-mix(in lab, red, red)){.border-cyan-400\/20{border-color:color-mix(in oklab, var(--color-cyan-400) 20%, transparent)}}.border-cyan-500\/25{border-color:#00b7d740}@supports (color:color-mix(in lab, red, red)){.border-cyan-500\/25{border-color:color-mix(in oklab, var(--color-cyan-500) 25%, transparent)}}.border-emerald-500\/25{border-color:#00bb7f40}@supports (color:color-mix(in lab, red, red)){.border-emerald-500\/25{border-color:color-mix(in oklab, var(--color-emerald-500) 25%, transparent)}}.border-input{border-color:hsl(var(--input))}.border-orange-500\/25{border-color:#fe6e0040}@supports (color:color-mix(in lab, red, red)){.border-orange-500\/25{border-color:color-mix(in oklab, var(--color-orange-500) 25%, transparent)}}.border-primary\/25{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.border-primary\/25{border-color:color-mix(in oklab, hsl(var(--primary)) 25%, transparent)}}.border-primary\/30{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.border-primary\/30{border-color:color-mix(in oklab, hsl(var(--primary)) 30%, transparent)}}.border-primary\/40{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.border-primary\/40{border-color:color-mix(in oklab, hsl(var(--primary)) 40%, transparent)}}.border-primary\/50{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.border-primary\/50{border-color:color-mix(in oklab, hsl(var(--primary)) 50%, transparent)}}.border-rose-500\/20{border-color:#ff235733}@supports (color:color-mix(in lab, red, red)){.border-rose-500\/20{border-color:color-mix(in oklab, var(--color-rose-500) 20%, transparent)}}.border-rose-500\/25{border-color:#ff235740}@supports (color:color-mix(in lab, red, red)){.border-rose-500\/25{border-color:color-mix(in oklab, var(--color-rose-500) 25%, transparent)}}.border-transparent{border-color:#0000}.border-violet-500\/25{border-color:#8d54ff40}@supports (color:color-mix(in lab, red, red)){.border-violet-500\/25{border-color:color-mix(in oklab, var(--color-violet-500) 25%, transparent)}}.\!bg-muted\/25{background-color:hsl(var(--muted))!important}@supports (color:color-mix(in lab, red, red)){.\!bg-muted\/25{background-color:color-mix(in oklab, hsl(var(--muted)) 25%, transparent)!important}}.bg-accent{background-color:hsl(var(--accent))}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab, red, red)){.bg-amber-500\/10{background-color:color-mix(in oklab, var(--color-amber-500) 10%, transparent)}}.bg-amber-500\/\[\.06\]{background-color:#f99c000f}@supports (color:color-mix(in lab, red, red)){.bg-amber-500\/\[\.06\]{background-color:color-mix(in oklab, var(--color-amber-500) 6%, transparent)}}.bg-amber-500\/\[\.07\]{background-color:#f99c0012}@supports (color:color-mix(in lab, red, red)){.bg-amber-500\/\[\.07\]{background-color:color-mix(in oklab, var(--color-amber-500) 7.0%, transparent)}}.bg-amber-500\/\[\.055\]{background-color:#f99c000e}@supports (color:color-mix(in lab, red, red)){.bg-amber-500\/\[\.055\]{background-color:color-mix(in oklab, var(--color-amber-500) 5.5%, transparent)}}.bg-background,.bg-background\/70{background-color:hsl(var(--background))}@supports (color:color-mix(in lab, red, red)){.bg-background\/70{background-color:color-mix(in oklab, hsl(var(--background)) 70%, transparent)}}.bg-background\/90{background-color:hsl(var(--background))}@supports (color:color-mix(in lab, red, red)){.bg-background\/90{background-color:color-mix(in oklab, hsl(var(--background)) 90%, transparent)}}.bg-border{background-color:hsl(var(--border))}.bg-card,.bg-card\/95{background-color:hsl(var(--card))}@supports (color:color-mix(in lab, red, red)){.bg-card\/95{background-color:color-mix(in oklab, hsl(var(--card)) 95%, transparent)}}.bg-cyan-300{background-color:var(--color-cyan-300)}.bg-cyan-400{background-color:var(--color-cyan-400)}.bg-cyan-500{background-color:var(--color-cyan-500)}.bg-cyan-500\/10{background-color:#00b7d71a}@supports (color:color-mix(in lab, red, red)){.bg-cyan-500\/10{background-color:color-mix(in oklab, var(--color-cyan-500) 10%, transparent)}}.bg-cyan-500\/\[\.06\]{background-color:#00b7d70f}@supports (color:color-mix(in lab, red, red)){.bg-cyan-500\/\[\.06\]{background-color:color-mix(in oklab, var(--color-cyan-500) 6%, transparent)}}.bg-destructive{background-color:hsl(var(--destructive))}.bg-emerald-400{background-color:var(--color-emerald-400)}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-emerald-500\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab, red, red)){.bg-emerald-500\/10{background-color:color-mix(in oklab, var(--color-emerald-500) 10%, transparent)}}.bg-emerald-500\/\[\.06\]{background-color:#00bb7f0f}@supports (color:color-mix(in lab, red, red)){.bg-emerald-500\/\[\.06\]{background-color:color-mix(in oklab, var(--color-emerald-500) 6%, transparent)}}.bg-emerald-500\/\[\.055\]{background-color:#00bb7f0e}@supports (color:color-mix(in lab, red, red)){.bg-emerald-500\/\[\.055\]{background-color:color-mix(in oklab, var(--color-emerald-500) 5.5%, transparent)}}.bg-muted{background-color:hsl(var(--muted))}.bg-muted-foreground\/25{background-color:hsl(var(--muted-foreground))}@supports (color:color-mix(in lab, red, red)){.bg-muted-foreground\/25{background-color:color-mix(in oklab, hsl(var(--muted-foreground)) 25%, transparent)}}.bg-muted-foreground\/50{background-color:hsl(var(--muted-foreground))}@supports (color:color-mix(in lab, red, red)){.bg-muted-foreground\/50{background-color:color-mix(in oklab, hsl(var(--muted-foreground)) 50%, transparent)}}.bg-muted\/30{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.bg-muted\/30{background-color:color-mix(in oklab, hsl(var(--muted)) 30%, transparent)}}.bg-muted\/35{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.bg-muted\/35{background-color:color-mix(in oklab, hsl(var(--muted)) 35%, transparent)}}.bg-muted\/40{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.bg-muted\/40{background-color:color-mix(in oklab, hsl(var(--muted)) 40%, transparent)}}.bg-muted\/50{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.bg-muted\/50{background-color:color-mix(in oklab, hsl(var(--muted)) 50%, transparent)}}.bg-orange-400{background-color:var(--color-orange-400)}.bg-orange-500\/10{background-color:#fe6e001a}@supports (color:color-mix(in lab, red, red)){.bg-orange-500\/10{background-color:color-mix(in oklab, var(--color-orange-500) 10%, transparent)}}.bg-orange-500\/\[\.06\]{background-color:#fe6e000f}@supports (color:color-mix(in lab, red, red)){.bg-orange-500\/\[\.06\]{background-color:color-mix(in oklab, var(--color-orange-500) 6%, transparent)}}.bg-primary,.bg-primary\/10{background-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.bg-primary\/10{background-color:color-mix(in oklab, hsl(var(--primary)) 10%, transparent)}}.bg-primary\/\[\.07\]{background-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.bg-primary\/\[\.07\]{background-color:color-mix(in oklab, hsl(var(--primary)) 7.0%, transparent)}}.bg-primary\/\[\.055\]{background-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.bg-primary\/\[\.055\]{background-color:color-mix(in oklab, hsl(var(--primary)) 5.5%, transparent)}}.bg-rose-500{background-color:var(--color-rose-500)}.bg-rose-500\/10{background-color:#ff23571a}@supports (color:color-mix(in lab, red, red)){.bg-rose-500\/10{background-color:color-mix(in oklab, var(--color-rose-500) 10%, transparent)}}.bg-rose-500\/\[\.06\]{background-color:#ff23570f}@supports (color:color-mix(in lab, red, red)){.bg-rose-500\/\[\.06\]{background-color:color-mix(in oklab, var(--color-rose-500) 6%, transparent)}}.bg-rose-500\/\[\.055\]{background-color:#ff23570e}@supports (color:color-mix(in lab, red, red)){.bg-rose-500\/\[\.055\]{background-color:color-mix(in oklab, var(--color-rose-500) 5.5%, transparent)}}.bg-secondary{background-color:hsl(var(--secondary))}.bg-slate-500{background-color:var(--color-slate-500)}.bg-slate-950{background-color:var(--color-slate-950)}.bg-slate-950\/60{background-color:#02061899}@supports (color:color-mix(in lab, red, red)){.bg-slate-950\/60{background-color:color-mix(in oklab, var(--color-slate-950) 60%, transparent)}}.bg-slate-950\/65{background-color:#020618a6}@supports (color:color-mix(in lab, red, red)){.bg-slate-950\/65{background-color:color-mix(in oklab, var(--color-slate-950) 65%, transparent)}}.bg-transparent{background-color:#0000}.bg-violet-400{background-color:var(--color-violet-400)}.bg-violet-500\/10{background-color:#8d54ff1a}@supports (color:color-mix(in lab, red, red)){.bg-violet-500\/10{background-color:color-mix(in oklab, var(--color-violet-500) 10%, transparent)}}.bg-white{background-color:var(--color-white)}.\!p-4{padding:calc(var(--spacing) * 4)!important}.p-1{padding:var(--spacing)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-3\.5{padding:calc(var(--spacing) * 3.5)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pr-9{padding-right:calc(var(--spacing) * 9)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-5{padding-bottom:calc(var(--spacing) * 5)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-6{padding-left:calc(var(--spacing) * 6)}.pl-9{padding-left:calc(var(--spacing) * 9)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[17px\]{font-size:17px}.leading-3{--tw-leading:calc(var(--spacing) * 3);line-height:calc(var(--spacing) * 3)}.leading-4{--tw-leading:calc(var(--spacing) * 4);line-height:calc(var(--spacing) * 4)}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[-0\.03em\]{--tw-tracking:-.03em;letter-spacing:-.03em}.tracking-\[-0\.035em\]{--tw-tracking:-.035em;letter-spacing:-.035em}.tracking-\[\.1em\]{--tw-tracking:.1em;letter-spacing:.1em}.tracking-\[\.13em\]{--tw-tracking:.13em;letter-spacing:.13em}.tracking-\[\.15em\]{--tw-tracking:.15em;letter-spacing:.15em}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent-foreground{color:hsl(var(--accent-foreground))}.text-amber-500{color:var(--color-amber-500)}.text-amber-700{color:var(--color-amber-700)}.text-card-foreground{color:hsl(var(--card-foreground))}.text-cyan-500{color:var(--color-cyan-500)}.text-cyan-600{color:var(--color-cyan-600)}.text-cyan-700{color:var(--color-cyan-700)}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-emerald-500{color:var(--color-emerald-500)}.text-emerald-600{color:var(--color-emerald-600)}.text-emerald-700{color:var(--color-emerald-700)}.text-foreground{color:hsl(var(--foreground))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-orange-500{color:var(--color-orange-500)}.text-orange-600{color:var(--color-orange-600)}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-rose-500{color:var(--color-rose-500)}.text-rose-600{color:var(--color-rose-600)}.text-rose-700{color:var(--color-rose-700)}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-slate-200{color:var(--color-slate-200)}.text-violet-500{color:var(--color-violet-500)}.text-violet-600{color:var(--color-violet-600)}.text-violet-700{color:var(--color-violet-700)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.accent-cyan-500{accent-color:var(--color-cyan-500)}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-65{opacity:.65}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-panel{--tw-shadow:0 18px 45px -32px var(--tw-shadow-color,#0208178c);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-cyan-900\/20{--tw-shadow-color:#104e6433}@supports (color:color-mix(in lab, red, red)){.shadow-cyan-900\/20{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-cyan-900) 20%, transparent) var(--tw-shadow-alpha), transparent)}}.shadow-cyan-950\/30{--tw-shadow-color:#0533454d}@supports (color:color-mix(in lab, red, red)){.shadow-cyan-950\/30{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-cyan-950) 30%, transparent) var(--tw-shadow-alpha), transparent)}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.backdrop-blur-\[2px\]{--tw-backdrop-blur:blur(2px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-xl{--tw-backdrop-blur:blur(var(--blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.group-hover\:text-foreground:is(:where(.group):hover *){color:hsl(var(--foreground))}}.placeholder\:text-muted-foreground\/70::placeholder{color:hsl(var(--muted-foreground))}@supports (color:color-mix(in lab, red, red)){.placeholder\:text-muted-foreground\/70::placeholder{color:color-mix(in oklab, hsl(var(--muted-foreground)) 70%, transparent)}}.first\:pt-0:first-child{padding-top:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.last\:pb-0:last-child{padding-bottom:0}@media (hover:hover){.hover\:border-primary\/25:hover{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/25:hover{border-color:color-mix(in oklab, hsl(var(--primary)) 25%, transparent)}}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive))}@supports (color:color-mix(in lab, red, red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab, hsl(var(--destructive)) 90%, transparent)}}.hover\:bg-muted:hover,.hover\:bg-muted\/45:hover{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/45:hover{background-color:color-mix(in oklab, hsl(var(--muted)) 45%, transparent)}}.hover\:bg-muted\/50:hover{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab, hsl(var(--muted)) 50%, transparent)}}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, hsl(var(--primary)) 90%, transparent)}}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary))}@supports (color:color-mix(in lab, red, red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, hsl(var(--secondary)) 80%, transparent)}}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:text-primary:hover{color:hsl(var(--primary))}.hover\:underline:hover{text-decoration-line:underline}}.focus\:border-primary\/70:focus{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.focus\:border-primary\/70:focus{border-color:color-mix(in oklab, hsl(var(--primary)) 70%, transparent)}}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:bg-rose-500\/10:focus{background-color:#ff23571a}@supports (color:color-mix(in lab, red, red)){.focus\:bg-rose-500\/10:focus{background-color:color-mix(in oklab, var(--color-rose-500) 10%, transparent)}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-primary\/15:focus{--tw-ring-color:hsl(var(--primary))}@supports (color:color-mix(in lab, red, red)){.focus\:ring-primary\/15:focus{--tw-ring-color:color-mix(in oklab, hsl(var(--primary)) 15%, transparent)}}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:hsl(var(--ring))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color:hsl(var(--background))}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.data-\[state\=checked\]\:translate-x-5[data-state=checked]{--tw-translate-x:calc(var(--spacing) * 5);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:hsl(var(--primary))}@media (min-width:40rem){.sm\:block{display:block}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-\[145px_1fr\]{grid-template-columns:145px 1fr}.sm\:grid-cols-\[150px_1fr\]{grid-template-columns:150px 1fr}.sm\:grid-cols-\[minmax\(0\,1\.2fr\)_repeat\(3\,minmax\(90px\,\.55fr\)\)\]{grid-template-columns:minmax(0,1.2fr) repeat(3,minmax(90px,.55fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:items-end{align-items:flex-end}.sm\:p-5{padding:calc(var(--spacing) * 5)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}.sm\:py-7{padding-block:calc(var(--spacing) * 7)}.sm\:pt-6{padding-top:calc(var(--spacing) * 6)}.sm\:pb-6{padding-bottom:calc(var(--spacing) * 6)}.sm\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}}@media (min-width:48rem){.md\:col-span-3{grid-column:span 3/span 3}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-\[minmax\(220px\,1fr\)_repeat\(3\,minmax\(140px\,\.36fr\)\)_auto\]{grid-template-columns:minmax(220px,1fr) repeat(3,minmax(140px,.36fr)) auto}}@media (min-width:64rem){.lg\:sticky{position:sticky}.lg\:top-24{top:calc(var(--spacing) * 24)}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-\[1fr_1fr_1fr_1fr_auto\]{grid-template-columns:1fr 1fr 1fr 1fr auto}.lg\:grid-cols-\[1fr_1fr_auto\]{grid-template-columns:1fr 1fr auto}.lg\:grid-cols-\[250px_minmax\(0\,1fr\)\]{grid-template-columns:250px minmax(0,1fr)}.lg\:flex-col{flex-direction:column}.lg\:px-8{padding-inline:calc(var(--spacing) * 8)}.lg\:py-8{padding-block:calc(var(--spacing) * 8)}.lg\:pl-64{padding-left:calc(var(--spacing) * 64)}}@media (min-width:80rem){.xl\:col-span-2{grid-column:span 2/span 2}.xl\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xl\:grid-cols-\[minmax\(0\,1\.65fr\)_minmax\(320px\,\.75fr\)\]{grid-template-columns:minmax(0,1.65fr) minmax(320px,.75fr)}}@media (min-width:96rem){.\32 xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.\32 xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}.dark\:text-amber-300:is(.dark *){color:var(--color-amber-300)}.dark\:text-cyan-300:is(.dark *){color:var(--color-cyan-300)}.dark\:text-emerald-300:is(.dark *){color:var(--color-emerald-300)}.dark\:text-emerald-400:is(.dark *){color:var(--color-emerald-400)}.dark\:text-orange-300:is(.dark *){color:var(--color-orange-300)}.dark\:text-rose-300:is(.dark *){color:var(--color-rose-300)}.dark\:text-rose-400:is(.dark *){color:var(--color-rose-400)}.dark\:text-violet-300:is(.dark *){color:var(--color-violet-300)}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}}@keyframes pulsebar{0%,to{opacity:.55;transform:scaleY(.45)}50%{opacity:1;transform:scaleY(1)}} diff --git a/internal/frontend/dist/assets/pen-line-BMm6ehcb.js b/internal/frontend/dist/assets/pen-line-UL8dX1Od.js similarity index 75% rename from internal/frontend/dist/assets/pen-line-BMm6ehcb.js rename to internal/frontend/dist/assets/pen-line-UL8dX1Od.js index 6275c61..0497546 100644 --- a/internal/frontend/dist/assets/pen-line-BMm6ehcb.js +++ b/internal/frontend/dist/assets/pen-line-UL8dX1Od.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`pen-line`,[[`path`,{d:`M13 21h8`,key:`1jsn5i`}],[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`pen-line`,[[`path`,{d:`M13 21h8`,key:`1jsn5i`}],[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/plus-C7xMvwvj.js b/internal/frontend/dist/assets/plus-BbAjJ_nn.js similarity index 54% rename from internal/frontend/dist/assets/plus-C7xMvwvj.js rename to internal/frontend/dist/assets/plus-BbAjJ_nn.js index b1bac44..f6295f2 100644 --- a/internal/frontend/dist/assets/plus-C7xMvwvj.js +++ b/internal/frontend/dist/assets/plus-BbAjJ_nn.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`plus`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`M12 5v14`,key:`s699le`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`plus`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`M12 5v14`,key:`s699le`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/radio-tower-C96Q3yet.js b/internal/frontend/dist/assets/radio-tower-D9Z4YmgD.js similarity index 85% rename from internal/frontend/dist/assets/radio-tower-C96Q3yet.js rename to internal/frontend/dist/assets/radio-tower-D9Z4YmgD.js index 8bb92bf..359e571 100644 --- a/internal/frontend/dist/assets/radio-tower-C96Q3yet.js +++ b/internal/frontend/dist/assets/radio-tower-D9Z4YmgD.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`radio-tower`,[[`path`,{d:`M4.9 16.1C1 12.2 1 5.8 4.9 1.9`,key:`s0qx1y`}],[`path`,{d:`M7.8 4.7a6.14 6.14 0 0 0-.8 7.5`,key:`1idnkw`}],[`circle`,{cx:`12`,cy:`9`,r:`2`,key:`1092wv`}],[`path`,{d:`M16.2 4.8c2 2 2.26 5.11.8 7.47`,key:`ojru2q`}],[`path`,{d:`M19.1 1.9a9.96 9.96 0 0 1 0 14.1`,key:`rhi7fg`}],[`path`,{d:`M9.5 18h5`,key:`mfy3pd`}],[`path`,{d:`m8 22 4-11 4 11`,key:`25yftu`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`radio-tower`,[[`path`,{d:`M4.9 16.1C1 12.2 1 5.8 4.9 1.9`,key:`s0qx1y`}],[`path`,{d:`M7.8 4.7a6.14 6.14 0 0 0-.8 7.5`,key:`1idnkw`}],[`circle`,{cx:`12`,cy:`9`,r:`2`,key:`1092wv`}],[`path`,{d:`M16.2 4.8c2 2 2.26 5.11.8 7.47`,key:`ojru2q`}],[`path`,{d:`M19.1 1.9a9.96 9.96 0 0 1 0 14.1`,key:`rhi7fg`}],[`path`,{d:`M9.5 18h5`,key:`mfy3pd`}],[`path`,{d:`m8 22 4-11 4 11`,key:`25yftu`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/route-C77_DY7l.js b/internal/frontend/dist/assets/route-DtX_GSbk.js similarity index 85% rename from internal/frontend/dist/assets/route-C77_DY7l.js rename to internal/frontend/dist/assets/route-DtX_GSbk.js index 4feb9c7..a38a30a 100644 --- a/internal/frontend/dist/assets/route-C77_DY7l.js +++ b/internal/frontend/dist/assets/route-DtX_GSbk.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`clipboard`,[[`rect`,{width:`8`,height:`4`,x:`8`,y:`2`,rx:`1`,ry:`1`,key:`tgr4d6`}],[`path`,{d:`M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2`,key:`116196`}]]),n=e(`route`,[[`circle`,{cx:`6`,cy:`19`,r:`3`,key:`1kj8tv`}],[`path`,{d:`M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15`,key:`1d8sl`}],[`circle`,{cx:`18`,cy:`5`,r:`3`,key:`gq8acd`}]]);export{t as n,n as t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`clipboard`,[[`rect`,{width:`8`,height:`4`,x:`8`,y:`2`,rx:`1`,ry:`1`,key:`tgr4d6`}],[`path`,{d:`M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2`,key:`116196`}]]),n=e(`route`,[[`circle`,{cx:`6`,cy:`19`,r:`3`,key:`1kj8tv`}],[`path`,{d:`M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15`,key:`1d8sl`}],[`circle`,{cx:`18`,cy:`5`,r:`3`,key:`gq8acd`}]]);export{t as n,n as t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/search-B8uA6mdW.js b/internal/frontend/dist/assets/search-b0EiqX0q.js similarity index 60% rename from internal/frontend/dist/assets/search-B8uA6mdW.js rename to internal/frontend/dist/assets/search-b0EiqX0q.js index bfab37d..0a64eb8 100644 --- a/internal/frontend/dist/assets/search-B8uA6mdW.js +++ b/internal/frontend/dist/assets/search-b0EiqX0q.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`search`,[[`path`,{d:`m21 21-4.34-4.34`,key:`14j7rj`}],[`circle`,{cx:`11`,cy:`11`,r:`8`,key:`4ej97u`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`search`,[[`path`,{d:`m21 21-4.34-4.34`,key:`14j7rj`}],[`circle`,{cx:`11`,cy:`11`,r:`8`,key:`4ej97u`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/server-BOAQDuBN.js b/internal/frontend/dist/assets/server-BWQHmE7V.js similarity index 80% rename from internal/frontend/dist/assets/server-BOAQDuBN.js rename to internal/frontend/dist/assets/server-BWQHmE7V.js index 48b0691..f75fd5b 100644 --- a/internal/frontend/dist/assets/server-BOAQDuBN.js +++ b/internal/frontend/dist/assets/server-BWQHmE7V.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`server`,[[`rect`,{width:`20`,height:`8`,x:`2`,y:`2`,rx:`2`,ry:`2`,key:`ngkwjq`}],[`rect`,{width:`20`,height:`8`,x:`2`,y:`14`,rx:`2`,ry:`2`,key:`iecqi9`}],[`line`,{x1:`6`,x2:`6.01`,y1:`6`,y2:`6`,key:`16zg32`}],[`line`,{x1:`6`,x2:`6.01`,y1:`18`,y2:`18`,key:`nzw8ys`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`server`,[[`rect`,{width:`20`,height:`8`,x:`2`,y:`2`,rx:`2`,ry:`2`,key:`ngkwjq`}],[`rect`,{width:`20`,height:`8`,x:`2`,y:`14`,rx:`2`,ry:`2`,key:`iecqi9`}],[`line`,{x1:`6`,x2:`6.01`,y1:`6`,y2:`6`,key:`16zg32`}],[`line`,{x1:`6`,x2:`6.01`,y1:`18`,y2:`18`,key:`nzw8ys`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/shield-alert-ma0N2JAc.js b/internal/frontend/dist/assets/shield-alert-CznEbFrD.js similarity index 81% rename from internal/frontend/dist/assets/shield-alert-ma0N2JAc.js rename to internal/frontend/dist/assets/shield-alert-CznEbFrD.js index 2f632be..ec7d7ab 100644 --- a/internal/frontend/dist/assets/shield-alert-ma0N2JAc.js +++ b/internal/frontend/dist/assets/shield-alert-CznEbFrD.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`shield-alert`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`M12 8v4`,key:`1got3b`}],[`path`,{d:`M12 16h.01`,key:`1drbdi`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`shield-alert`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`M12 8v4`,key:`1got3b`}],[`path`,{d:`M12 16h.01`,key:`1drbdi`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/shield-check-Dm-p0u_h.js b/internal/frontend/dist/assets/shield-check-DrVussKH.js similarity index 79% rename from internal/frontend/dist/assets/shield-check-Dm-p0u_h.js rename to internal/frontend/dist/assets/shield-check-DrVussKH.js index 7a0dac4..ad42f4f 100644 --- a/internal/frontend/dist/assets/shield-check-Dm-p0u_h.js +++ b/internal/frontend/dist/assets/shield-check-DrVussKH.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`shield-check`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`shield-check`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/trash-2-DJb0gq__.js b/internal/frontend/dist/assets/trash-2-DyGHVVq4.js similarity index 79% rename from internal/frontend/dist/assets/trash-2-DJb0gq__.js rename to internal/frontend/dist/assets/trash-2-DyGHVVq4.js index b6e0b30..86f2d5d 100644 --- a/internal/frontend/dist/assets/trash-2-DJb0gq__.js +++ b/internal/frontend/dist/assets/trash-2-DyGHVVq4.js @@ -1 +1 @@ -import{I as e}from"./index-B84OiuS0.js";var t=e(`trash-2`,[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]]);export{t}; \ No newline at end of file +import{I as e}from"./index-DleN6wGz.js";var t=e(`trash-2`,[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]]);export{t}; \ No newline at end of file diff --git a/internal/frontend/dist/assets/useAppSettings-CeA9vkHH.js b/internal/frontend/dist/assets/useAppSettings-vnP5f96R.js similarity index 93% rename from internal/frontend/dist/assets/useAppSettings-CeA9vkHH.js rename to internal/frontend/dist/assets/useAppSettings-vnP5f96R.js index 1d8b562..e28f6eb 100644 --- a/internal/frontend/dist/assets/useAppSettings-CeA9vkHH.js +++ b/internal/frontend/dist/assets/useAppSettings-vnP5f96R.js @@ -1 +1 @@ -import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,V as n}from"./query-DWNqGEie.js";import{d as r,n as i,p as a,w as o}from"./index-B84OiuS0.js";var s=e(n(),1),c={displayUnits:`bits`,defaultTimezone:`UTC`,globalConcurrency:1,allowSeparateWanConcurrency:!1,retentionMode:`forever`,retentionValue:0,defaultChartRange:`30d`,interfaceRefreshIntervalSeconds:60,defaultTaskTimeoutSeconds:120,databaseMaintenanceSchedule:`30 3 * * *`,ooklaEulaAccepted:!1,ooklaEulaAcceptedAt:null,ooklaEulaVersion:``,ooklaEulaCurrentVersion:``,ooklaEulaEffectiveAccepted:!1,ooklaEulaAcceptanceSource:`none`};function l(){let e=t({queryKey:i.settings,queryFn:o.settings,placeholderData:c});return{...e,settings:e.data??c}}function u(){let{settings:e}=l();return(0,s.useMemo)(()=>({bitrate:(t,n=!1)=>r(t,n,e.displayUnits),dateTime:(t,n=!1)=>a(t,n,e.defaultTimezone)}),[e.defaultTimezone,e.displayUnits])}export{l as n,u as r,c as t}; \ No newline at end of file +import{r as e}from"./rolldown-runtime-hePW80VL.js";import{S as t,V as n}from"./query-DWNqGEie.js";import{d as r,n as i,p as a,w as o}from"./index-DleN6wGz.js";var s=e(n(),1),c={displayUnits:`bits`,defaultTimezone:`UTC`,globalConcurrency:1,allowSeparateWanConcurrency:!1,retentionMode:`forever`,retentionValue:0,defaultChartRange:`30d`,interfaceRefreshIntervalSeconds:60,defaultTaskTimeoutSeconds:120,databaseMaintenanceSchedule:`30 3 * * *`,ooklaEulaAccepted:!1,ooklaEulaAcceptedAt:null,ooklaEulaVersion:``,ooklaEulaCurrentVersion:``,ooklaEulaEffectiveAccepted:!1,ooklaEulaAcceptanceSource:`none`};function l(){let e=t({queryKey:i.settings,queryFn:o.settings,placeholderData:c});return{...e,settings:e.data??c}}function u(){let{settings:e}=l();return(0,s.useMemo)(()=>({bitrate:(t,n=!1)=>r(t,n,e.displayUnits),dateTime:(t,n=!1)=>a(t,n,e.defaultTimezone)}),[e.defaultTimezone,e.displayUnits])}export{l as n,u as r,c as t}; \ No newline at end of file diff --git a/internal/frontend/dist/index.html b/internal/frontend/dist/index.html index ed6b057..0f10bdb 100644 --- a/internal/frontend/dist/index.html +++ b/internal/frontend/dist/index.html @@ -6,11 +6,11 @@ MultiSpeed - + - +
diff --git a/openapi.yaml b/openapi.yaml index fca4676..08ee150 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -2,7 +2,7 @@ openapi: 3.1.0 jsonSchemaDialect: https://json-schema.org/draft/2020-12/schema info: title: MultiSpeed API - version: 1.0.4 + version: 1.1.0 summary: Multi-WAN speed-test scheduling, execution, routing, and statistics API description: | Same-origin API for MultiSpeed. There is deliberately no authentication. diff --git a/web/e2e/README.md b/web/e2e/README.md index 356b249..b6e801a 100644 --- a/web/e2e/README.md +++ b/web/e2e/README.md @@ -23,6 +23,8 @@ Useful overrides: - `MULTISPEED_E2E_DOCKER_COMMAND`: Docker executable to use for fallback. - `MULTISPEED_E2E_DOCKER_IMAGE`: prebuilt compatible test image; skips build. - `MULTISPEED_E2E_BACKEND_PORT`: backend port, default `18787`. +- `MULTISPEED_E2E_REUSE_BACKEND=true`: reuse an already healthy backend on the + configured port without starting or stopping it. The command/image override must expose the same test-backend composition if the measurement assertions are expected to pass. Every run receives a fresh SQLite diff --git a/web/e2e/accessibility.spec.ts b/web/e2e/accessibility.spec.ts new file mode 100644 index 0000000..bbf5442 --- /dev/null +++ b/web/e2e/accessibility.spec.ts @@ -0,0 +1,29 @@ +import AxeBuilder from '@axe-core/playwright' +import { expect, test } from '@playwright/test' + +const corePages = [ + { name: 'dashboard', path: '/' }, + { name: 'tasks', path: '/tasks' }, + { name: 'network', path: '/network' }, + { name: 'results', path: '/results' }, + { name: 'settings', path: '/settings' }, +] + +for (const corePage of corePages) { + test(`${corePage.name} has no detectable WCAG A/AA violations`, async ({ page }) => { + await page.goto(corePage.path) + await expect(page.locator('main')).toBeVisible() + + const scan = await new AxeBuilder({ page }) + .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa']) + .analyze() + + if (scan.violations.length > 0) throw new Error(formatViolations(scan.violations)) + }) +} + +function formatViolations(violations: Array<{ id: string; impact?: string | null; help: string; nodes: Array<{ target: unknown }> }>): string { + return violations + .map((violation) => `${violation.impact ?? 'unknown'} ${violation.id}: ${violation.help}\n${violation.nodes.map((node) => ` ${JSON.stringify(node.target)}`).join('\n')}`) + .join('\n\n') +} diff --git a/web/package-lock.json b/web/package-lock.json index 1aa7629..fd525ce 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "multispeed-web", - "version": "1.0.4", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "multispeed-web", - "version": "1.0.4", + "version": "1.1.0", "dependencies": { "@hookform/resolvers": "5.8.0", "@radix-ui/react-alert-dialog": "1.1.23", @@ -31,7 +31,9 @@ "zod": "4.4.3" }, "devDependencies": { + "@axe-core/playwright": "4.13.0", "@eslint/js": "10.0.1", + "@hey-api/openapi-ts": "0.99.0", "@playwright/test": "1.62.1", "@tailwindcss/postcss": "4.3.3", "@testing-library/dom": "10.4.1", @@ -129,6 +131,18 @@ "node": "20 || >=22" } }, + "node_modules/@axe-core/playwright": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.13.0.tgz", + "integrity": "sha512-6YLx+kxXu5GJceG4ozFg+33a2EMTdjYwWGloJ3sb9Kta5pp+ZNS53uxGVog5JetIY8s++P5UrtX+cri+u0VAVg==", + "dev": true, + "dependencies": { + "axe-core": "~4.13.0" + }, + "peerDependencies": { + "playwright-core": ">= 1.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -716,6 +730,123 @@ "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", "license": "MIT" }, + "node_modules/@hey-api/codegen-core": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@hey-api/codegen-core/-/codegen-core-0.9.1.tgz", + "integrity": "sha512-s97jL1dgTMuiMHv2BZ1X4Tgd99Mf9GOvGdNqNcGwIMmnR+PgYNoraj4Zvp134MKsNCap/m7k0r0vKKnl56pj4w==", + "dev": true, + "dependencies": { + "@hey-api/types": "0.1.4", + "ansi-colors": "4.1.3", + "c12": "3.3.4", + "color-support": "1.1.3" + }, + "engines": { + "node": ">=22.18.0" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + } + }, + "node_modules/@hey-api/json-schema-ref-parser": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.4.4.tgz", + "integrity": "sha512-otmd+zCxbYVBIp/mlMTnGkvlNYLkVKgs3VOIq0kSnenhB1+fRwLPQIeSwyWM6E51oXhUedkYjVsVpkVexeuJOA==", + "dev": true, + "dependencies": { + "@jsdevtools/ono": "7.1.3", + "@types/json-schema": "7.0.15", + "js-yaml": "4.2.0" + }, + "engines": { + "node": ">=22.18.0" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + } + }, + "node_modules/@hey-api/openapi-ts": { + "version": "0.99.0", + "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.99.0.tgz", + "integrity": "sha512-SePU/5oEWWkvUBYmvzdYRctseoLuskyhs4ET0RvLIcmzc8yLQoA2R+KtBIQ8bPsoSUB0m4E5SmBnl6aGSA0szQ==", + "dev": true, + "dependencies": { + "@hey-api/codegen-core": "0.9.1", + "@hey-api/json-schema-ref-parser": "1.4.4", + "@hey-api/shared": "0.5.0", + "@hey-api/spec-types": "0.2.0", + "@hey-api/types": "0.1.4", + "@lukeed/ms": "2.0.2", + "ansi-colors": "4.1.3", + "color-support": "1.1.3", + "commander": "15.0.0", + "get-tsconfig": "4.14.0" + }, + "bin": { + "openapi-ts": "bin/run.js" + }, + "engines": { + "node": ">=22.18.0" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + }, + "peerDependencies": { + "typescript": ">=5.5.3 || >=6.0.0 || 6.0.1-rc" + } + }, + "node_modules/@hey-api/shared": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@hey-api/shared/-/shared-0.5.0.tgz", + "integrity": "sha512-JN/j4Ebh4cJGYIQ5cwWuqe7GeSUyQoz7oC51WqyhKOcrejK6DKZMDkshc5d1eKTRuRL+rjozuRcoUaZZn2DGPw==", + "dev": true, + "dependencies": { + "@hey-api/codegen-core": "0.9.1", + "@hey-api/json-schema-ref-parser": "1.4.4", + "@hey-api/spec-types": "0.2.0", + "@hey-api/types": "0.1.4", + "ansi-colors": "4.1.3", + "cross-spawn": "7.0.6", + "open": "11.0.0", + "semver": "7.8.4" + }, + "engines": { + "node": ">=22.18.0" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + } + }, + "node_modules/@hey-api/shared/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@hey-api/spec-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@hey-api/spec-types/-/spec-types-0.2.0.tgz", + "integrity": "sha512-ibQ8Is7evMavzr8GNyJCcTg975d8DpaMUyLmOrQ85UBdy1l6t1KuRAwgChAbesJsIlNV6gjmlXruWyegDX18Fg==", + "dev": true, + "dependencies": { + "@hey-api/types": "0.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + } + }, + "node_modules/@hey-api/types": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@hey-api/types/-/types-0.1.4.tgz", + "integrity": "sha512-thWfawrDIP7wSI9ioT13I5soaaqB5vAPIiZmgD8PbeEVKNrkonc0N/Sjj97ezl7oQgusZmaNphGdMKipPO6IBg==", + "dev": true + }, "node_modules/@hookform/resolvers": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.8.0.tgz", @@ -942,6 +1073,21 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "dev": true + }, + "node_modules/@lukeed/ms": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@oxc-project/types": { "version": "0.143.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.143.0.tgz", @@ -1796,9 +1942,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1816,9 +1959,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1836,9 +1976,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1856,9 +1993,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1876,9 +2010,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1896,9 +2027,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2112,9 +2240,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2132,9 +2257,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2152,9 +2274,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2172,9 +2291,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2997,6 +3113,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -3007,6 +3132,12 @@ "node": ">=8" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/aria-hidden": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", @@ -3076,6 +3207,15 @@ "postcss": "^8.1.0" } }, + "node_modules/axe-core": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.13.0.tgz", + "integrity": "sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -3156,6 +3296,49 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c12": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.3.4.tgz", + "integrity": "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==", + "dev": true, + "dependencies": { + "chokidar": "^5.0.0", + "confbox": "^0.2.4", + "defu": "^6.1.6", + "dotenv": "^17.3.1", + "exsolve": "^1.0.8", + "giget": "^3.2.0", + "jiti": "^2.6.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.1.0", + "pkg-types": "^2.3.0", + "rc9": "^3.0.1" + }, + "peerDependencies": { + "magicast": "*" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001806", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", @@ -3187,6 +3370,21 @@ "node": ">=18" } }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -3196,6 +3394,30 @@ "node": ">=6" } }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "dev": true, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "dev": true + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -3335,6 +3557,52 @@ "dev": true, "license": "MIT" }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "dev": true, + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "dev": true + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3345,6 +3613,12 @@ "node": ">=6" } }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "dev": true + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -3368,6 +3642,18 @@ "dev": true, "license": "MIT" }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/echarts": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.1.0.tgz", @@ -3677,6 +3963,12 @@ "node": ">=12.0.0" } }, + "node_modules/exsolve": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", + "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==", + "dev": true + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3797,6 +4089,27 @@ "node": ">=6" } }, + "node_modules/get-tsconfig": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/giget": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/giget/-/giget-3.3.1.tgz", + "integrity": "sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg==", + "dev": true, + "bin": { + "giget": "dist/cli.mjs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3890,6 +4203,21 @@ "node": ">=8" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3913,6 +4241,36 @@ "node": ">=0.10.0" } }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "dev": true, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -3920,6 +4278,21 @@ "dev": true, "license": "MIT" }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3944,6 +4317,28 @@ "dev": true, "license": "MIT" }, + "node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsdom": { "version": "30.0.1", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-30.0.1.tgz", @@ -4202,9 +4597,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -4226,9 +4618,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -4250,9 +4639,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -4274,9 +4660,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -4486,6 +4869,32 @@ "node": ">=12.20.0" } }, + "node_modules/ohash": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", + "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", + "dev": true + }, + "node_modules/open": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", + "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", + "dev": true, + "dependencies": { + "default-browser": "^5.4.0", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.1.0", + "wsl-utils": "^0.3.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -4576,6 +4985,12 @@ "dev": true, "license": "MIT" }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "dev": true + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -4596,6 +5011,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", + "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", + "dev": true, + "dependencies": { + "confbox": "^0.2.4", + "exsolve": "^1.0.8", + "pathe": "^2.0.3" + } + }, "node_modules/playwright": { "version": "1.62.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz", @@ -4664,6 +5090,18 @@ "dev": true, "license": "MIT" }, + "node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "dev": true, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4712,6 +5150,16 @@ "node": ">=6" } }, + "node_modules/rc9": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-3.0.1.tgz", + "integrity": "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==", + "dev": true, + "dependencies": { + "defu": "^6.1.6", + "destr": "^2.0.5" + } + }, "node_modules/react": { "version": "19.2.8", "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", @@ -4846,6 +5294,19 @@ } } }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "dev": true, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -4870,6 +5331,15 @@ "node": ">=0.10.0" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/rolldown": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.3.tgz", @@ -4903,6 +5373,18 @@ "@rolldown/binding-win32-x64-msvc": "1.2.3" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -5561,9 +6043,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5585,9 +6064,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5609,9 +6085,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5633,9 +6106,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5872,6 +6342,22 @@ "node": ">=0.10.0" } }, + "node_modules/wsl-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", + "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==", + "dev": true, + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", diff --git a/web/package.json b/web/package.json index 13ad4bb..c0d0f7a 100644 --- a/web/package.json +++ b/web/package.json @@ -1,12 +1,13 @@ { "name": "multispeed-web", - "version": "1.0.4", + "version": "1.1.0", "private": true, "type": "module", "scripts": { "dev": "vite", "build": "tsc -b && vite build && node ./scripts/sync-embedded-frontend.mjs", "check:embedded": "node ./scripts/sync-embedded-frontend.mjs --check", + "generate:api-types": "openapi-ts -i ../openapi.yaml -o src/lib/openapi -p @hey-api/typescript --no-log-file", "lint": "eslint . --max-warnings 0", "typecheck": "tsc -b --pretty false", "test": "vitest run", @@ -37,7 +38,9 @@ "zod": "4.4.3" }, "devDependencies": { + "@axe-core/playwright": "4.13.0", "@eslint/js": "10.0.1", + "@hey-api/openapi-ts": "0.99.0", "@playwright/test": "1.62.1", "@tailwindcss/postcss": "4.3.3", "@testing-library/dom": "10.4.1", diff --git a/web/playwright.config.ts b/web/playwright.config.ts index 111bc30..de09f69 100644 --- a/web/playwright.config.ts +++ b/web/playwright.config.ts @@ -3,6 +3,7 @@ import { defineConfig, devices } from '@playwright/test' const inheritedEnvironment = Object.fromEntries(Object.entries(process.env).filter((entry): entry is [string, string] => typeof entry[1] === 'string')) const backendPort = process.env.MULTISPEED_E2E_BACKEND_PORT ?? '18787' const backendURL = `http://127.0.0.1:${backendPort}` +const reuseBackend = process.env.MULTISPEED_E2E_REUSE_BACKEND === 'true' export default defineConfig({ testDir: './e2e', @@ -24,7 +25,7 @@ export default defineConfig({ command: 'node ./e2e/start-backend.mjs', url: `${backendURL}/api/v1/readyz`, env: { ...inheritedEnvironment, MULTISPEED_E2E_BACKEND_PORT: backendPort }, - reuseExistingServer: false, + reuseExistingServer: reuseBackend, timeout: 300_000, }, { diff --git a/web/src/components/layout/AppShell.tsx b/web/src/components/layout/AppShell.tsx index 68835c9..c24ac06 100644 --- a/web/src/components/layout/AppShell.tsx +++ b/web/src/components/layout/AppShell.tsx @@ -77,7 +77,7 @@ function Navigation({ onNavigate }: { onNavigate?: () => void }) { isActive ? 'bg-accent font-semibold text-accent-foreground' : 'text-muted-foreground hover:bg-muted hover:text-foreground', )} > - {({ isActive }) => <>{label}{subtitle}} + {({ isActive }) => <>{label}{subtitle}} ))} diff --git a/web/src/components/ui/fields.tsx b/web/src/components/ui/fields.tsx index ad522c0..0008702 100644 --- a/web/src/components/ui/fields.tsx +++ b/web/src/components/ui/fields.tsx @@ -1,7 +1,7 @@ import * as LabelPrimitive from '@radix-ui/react-label' import * as SwitchPrimitive from '@radix-ui/react-switch' import { ChevronDown } from 'lucide-react' -import { forwardRef, type InputHTMLAttributes, type SelectHTMLAttributes, type TextareaHTMLAttributes } from 'react' +import { cloneElement, forwardRef, isValidElement, useId, type InputHTMLAttributes, type SelectHTMLAttributes, type TextareaHTMLAttributes } from 'react' import { cn } from '../../lib/utils' export const Input = forwardRef>(function Input({ className, type, ...props }, ref) { @@ -54,21 +54,40 @@ export function Label({ className, ...props }: React.ComponentPropsWithoutRef(children) && children.props.id ? children.props.id : generatedId + const hintId = hint ? `${controlId}-hint` : undefined + const errorId = error ? `${controlId}-error` : undefined + const describedBy = [hintId, errorId].filter(Boolean).join(' ') || undefined + const accessibleProps: AccessibleControlProps = { id: controlId } + if (describedBy) accessibleProps['aria-describedby'] = describedBy + if (error) accessibleProps['aria-invalid'] = true + if (required) accessibleProps['aria-required'] = true + const control = isValidElement(children) + ? cloneElement(children, accessibleProps) + : children return (
-
- {children} - {error ?

{error}

: null} + {control} + {error ? : null}
) } +interface AccessibleControlProps { + id?: string + 'aria-describedby'?: string + 'aria-invalid'?: boolean + 'aria-required'?: boolean +} + export function Switch({ checked, onCheckedChange, disabled, id, 'aria-label': ariaLabel }: { checked: boolean; onCheckedChange: (checked: boolean) => void; disabled?: boolean; id?: string; 'aria-label'?: string }) { return ( ; + page: number; + pageSize: number; + totalItems: number; + totalPages: number; +}; + +export type DashboardTaskResult = { + taskId: Uuid; + taskName: string; + enabled: boolean; + interfaceName: InterfaceName; + sourceIp: IpAddress; + latestResult: ResultSummary | null; +}; + +export type DashboardPathResult = { + interfaceName: InterfaceName; + sourceIp: IpAddress; + taskIds: Array; + latestResult: ResultSummary | null; +}; + +export type DashboardResultSummary = { + latestByTask: Array; + latestByPath: Array; + activeRuns: Array; + recentFailures: Array; + failedTaskCount: number; +}; + +export type NetworkInterface = { + name: InterfaceName; + index: number; + operational: boolean; + operationalState: string; + loopback: boolean; + virtual: boolean; + macAddress: string; + mtu: number; + addresses: Array; +}; + +export type InterfaceAddress = { + address: IpAddress; + family: 'ipv4' | 'ipv6'; + linkLocal: boolean; +}; + +export type InterfaceSnapshot = { + items: Array; + /** + * Compatibility alias for `items` containing the same interface snapshot. + */ + interfaces?: Array; + refreshedAt: string; +}; + +export type RouteProfileFields = { + name: string; + description?: string; + interfaceName: InterfaceName; + sourceIp: IpAddress; + expectedGateway?: string; + /** + * Optional unsigned 32-bit numeric table ID or shell-safe Linux routing-table name. + */ + expectedRoutingTable?: string; + validationTarget?: string; + notes?: string; +}; + +export type RouteProfileInput = RouteProfileFields; + +export type RouteProfile = RouteProfileFields & { + id: Uuid; + createdAt: string; + updatedAt: string; + lastValidationAt: string | null; + lastValidationSucceeded: boolean | null; + lastValidationSnapshot: { + [key: string]: unknown; + }; +}; + +export type RouteValidation = { + success: boolean; + interfaceName: string; + sourceIp: string; + gateway: string; + routingTable: string; + destination: string; + detectedPublicIp: string; + reachable: boolean; + durationMs: number; + validatedAt: string; + message: string; +}; + +export type ProviderCapabilities = { + serverDiscovery: boolean; + fixedServerIds: boolean; + customServerUrls: boolean; + interfaceBinding: boolean; + sourceAddressBinding: boolean; + ipv4: boolean; + ipv6: boolean; + jitter: boolean; + packetLoss: boolean; + resultUrls: boolean; +}; + +export type ProviderDescriptor = { + id: ProviderId; + displayName: string; + capabilities: ProviderCapabilities; + available: boolean; + version: string; + message: string; +}; + +export type OoklaBinaryStatus = { + uploadEnabled: boolean; + installed: boolean; + maxUploadBytes: number; + message: string; +}; + +export type OoklaBinaryInstallResult = { + uploadEnabled: boolean; + installed: true; + maxUploadBytes: number; + message: string; + version: string; + sha256: string; +}; + +export type ProviderServer = { + id: string; + name: string; + host: string; + sponsor: string; + location: string; + country: string; + distanceKilometers?: number; + url?: string; +}; + +export type TestTarget = { + selectionMode: 'automatic' | 'fixed' | 'custom'; + serverId?: string; + serverUrl?: string; + customServerDefinition?: { + [key: string]: unknown; + }; +}; + +export type ProviderServerValidation = { + selectionMode: 'automatic' | 'fixed' | 'custom'; + serverId?: string; + serverUrl?: string; + customServerDefinition?: { + [key: string]: unknown; + }; + /** + * Provider-specific validation options. LibreSpeed accepts the explicit `skipTlsVerification` boolean. + */ + providerOptions?: { + [key: string]: unknown; + }; + interfaceName: InterfaceName; + sourceIp: IpAddress; + ipFamily?: IpFamily; +}; + +export type Settings = { + displayUnits: 'bits' | 'bytes'; + defaultTimezone: string; + globalConcurrency: number; + allowSeparateWanConcurrency: boolean; + retentionMode: 'forever' | 'days' | 'months'; + retentionValue: number; + defaultChartRange: '24h' | '7d' | '30d' | '90d'; + interfaceRefreshIntervalSeconds: number; + defaultTaskTimeoutSeconds: number; + databaseMaintenanceSchedule: string; + /** + * Whether an operator recorded the legacy-named technical terms acknowledgement and CLI flag authorization through the dedicated endpoint. + */ + readonly ooklaEulaAccepted?: boolean; + /** + * UTC timestamp of the persisted acknowledgement, or null while revoked. + */ + readonly ooklaEulaAcceptedAt?: string | null; + /** + * MultiSpeed internal review marker recorded with the persisted acknowledgement, or empty when none is stored; this is not an official Ookla document version. + */ + readonly ooklaEulaVersion?: string; + /** + * MultiSpeed internal review marker required by this release for reviewing the current Ookla EULA, Terms of Use, and Privacy Policy; this is not an official Ookla document version. + */ + readonly ooklaEulaCurrentVersion?: string; + /** + * Effective technical-gate state after applying the persisted acknowledgement and environment override. + */ + readonly ooklaEulaEffectiveAccepted?: boolean; + /** + * Source currently opening the technical gate. + */ + readonly ooklaEulaAcceptanceSource?: 'none' | 'persisted' | 'environment'; +}; + +export type OoklaEulaAcceptanceRequest = { + /** + * Legacy-named technical gate value; true records agreement to the current Ookla EULA and Terms of Use and acknowledgement that the Privacy Policy was reviewed. + */ + accepted: boolean; + /** + * Must be true when accepted is true and confirms authorization for MultiSpeed to pass --accept-license and --accept-gdpr non-interactively plus that the personal-computer/non-commercial deployment, device type, and any network access from multiple devices fit current documents or have separate written Ookla authorization; revocation does not require confirmation. + */ + confirmed: boolean; +}; + +export type ConfigurationSettings = { + displayUnits: 'bits' | 'bytes'; + defaultTimezone: string; + globalConcurrency: number; + allowSeparateWanConcurrency: boolean; + retentionMode: 'forever' | 'days' | 'months'; + retentionValue: number; + defaultChartRange: '24h' | '7d' | '30d' | '90d'; + interfaceRefreshIntervalSeconds: number; + defaultTaskTimeoutSeconds: number; + databaseMaintenanceSchedule: string; +}; + +export type ConfigurationRouteProfile = RouteProfileFields & { + id: Uuid; +}; + +export type ConfigurationTask = TaskFields & { + id: Uuid; +}; + +export type ConfigurationDocument = { + format: 'multispeed-config'; + version: 1; + exportedAt: string; + applicationVersion: string; + settings: ConfigurationSettings; + routeProfiles: Array; + tasks: Array; +}; + +export type ConfigurationImportResult = { + importedAt: string; + taskCount: number; + routeProfileCount: number; + settingsUpdated: boolean; +}; + +export type RetentionMetrics = { + mode: 'forever' | 'days' | 'months' | 'manual'; + cutoff?: string; + startedAt: string; + finishedAt: string; + /** + * Go duration encoded as nanoseconds. + */ + duration: number; + batches: number; + deletedResults: number; + limitReached: boolean; + maintenancePerformed: boolean; +}; + +export type StatisticsGranularity = 'raw' | 'day' | 'iso-week' | 'month' | 'year' | 'custom'; + +export type StatisticsDimension = '' | 'task' | 'interface' | 'source-ip' | 'provider' | 'server' | 'route-profile' | 'public-ip'; + +export type StatisticsReport = { + from: string; + to: string; + granularity: StatisticsGranularity; + reportingTimezone: string; + groupBy?: StatisticsDimension; + totalResults: number; + /** + * Exact full-range aggregate calculated from all matching raw results, never from averages of buckets. + */ + overall: StatisticsBucket; + groups: Array; +}; + +export type StatisticsGroup = { + key: string; + label: string; + /** + * Exact full-range aggregate for this comparison group. + */ + overall: StatisticsBucket; + buckets: Array; +}; + +export type StatisticsBucket = { + start: string; + end: string; + label: string; + resultId?: string; + counts: StatisticsCounts; + /** + * Successful results as a percentage of completed results (successful plus failed). + */ + successRatePercent: number | null; + /** + * Failed results as a percentage of completed results (successful plus failed). + */ + failureRatePercent: number | null; + metrics: StatisticsMetrics; +}; + +export type StatisticsCounts = { + total: number; + successful: number; + failed: number; + skipped: number; + cancelled: number; + other: number; +}; + +export type StatisticsMetrics = { + downloadBitsPerSecond: MetricSummary; + uploadBitsPerSecond: MetricSummary; + latencyMilliseconds: MetricSummary; + jitterMilliseconds: MetricSummary; + packetLossPercent: MetricSummary; + executionDurationMilliseconds: MetricSummary; +}; + +export type MetricSummary = { + count: number; + minimum: number | null; + maximum: number | null; + average: number | null; + median: number | null; + p95: number | null; + standardDeviation: number | null; +}; + +export type SystemInformation = { + product: 'MultiSpeed'; + version: string; + gitCommit: string; + buildTime: string; + /** + * Compatibility alias for `buildTime`. + */ + buildDate: string; + goVersion: string; + operatingSystem: 'linux'; + architecture: 'amd64'; + hostname: string; + databasePath: string; + databaseSizeBytes: number; + schemaVersion: number; + providers: Array; + interfaces: Array; + interfacesRefreshedAt: string; + taskCount: number; + resultCount: number; + runningTaskCount: number; + uptimeSeconds: number; + authenticationEnabled: false; + internetExposureWarning: string; +}; + +export type SettingsWritable = { + displayUnits: 'bits' | 'bytes'; + defaultTimezone: string; + globalConcurrency: number; + allowSeparateWanConcurrency: boolean; + retentionMode: 'forever' | 'days' | 'months'; + retentionValue: number; + defaultChartRange: '24h' | '7d' | '30d' | '90d'; + interfaceRefreshIntervalSeconds: number; + defaultTaskTimeoutSeconds: number; + databaseMaintenanceSchedule: string; +}; + +export type ResourceId = Uuid; + +export type ProviderPath = ProviderId; + +export type Page = number; + +export type PageSize = number; + +export type TaskIdFilter = Uuid; + +export type ProviderFilter = ProviderId; + +export type StatusFilter = ResultStatus; + +export type InterfaceFilter = InterfaceName; + +export type SourceIpFilter = IpAddress; + +export type ServerIdFilter = string; + +/** + * Inclusive RFC 3339 start instant. + */ +export type FromFilter = string; + +/** + * Exclusive RFC 3339 end instant. + */ +export type ToFilter = string; + +export type ResultSort = 'startedAt' | 'finishedAt' | 'download' | 'upload' | 'latency'; + +export type SortDirection = 'asc' | 'desc'; + +/** + * Repeat the parameter or provide comma-separated values. + */ +export type TaskIdListFilter = Array; + +/** + * Repeat the parameter or provide comma-separated values. + */ +export type ProviderListFilter = Array; + +/** + * Repeat the parameter or provide comma-separated values. The legacy interfaceName alias shares this combined 200-value limit. + */ +export type InterfaceListFilter = Array; + +/** + * Repeat the parameter or provide comma-separated values. + */ +export type SourceIpListFilter = Array; + +/** + * Repeat the parameter or provide comma-separated values. + */ +export type ServerIdListFilter = Array; + +/** + * Repeat the parameter or provide comma-separated values. + */ +export type RouteProfileIdListFilter = Array; + +/** + * Repeat the parameter or provide comma-separated values. + */ +export type PublicIpListFilter = Array; + +export type GetHealthData = { + body?: never; + path?: never; + query?: never; + url: '/healthz'; +}; + +export type GetHealthResponses = { + /** + * Process is alive. + */ + 200: Health; +}; + +export type GetHealthResponse = GetHealthResponses[keyof GetHealthResponses]; + +export type GetReadinessData = { + body?: never; + path?: never; + query?: never; + url: '/readyz'; +}; + +export type GetReadinessErrors = { + /** + * Application startup/integrity dependencies are not ready. + */ + 503: ErrorEnvelope; +}; + +export type GetReadinessError = GetReadinessErrors[keyof GetReadinessErrors]; + +export type GetReadinessResponses = { + /** + * Database, integrity checks, and migrations are ready. + */ + 200: Readiness; +}; + +export type GetReadinessResponse = GetReadinessResponses[keyof GetReadinessResponses]; + +export type ListTasksData = { + body?: never; + path?: never; + query?: never; + url: '/tasks'; +}; + +export type ListTasksErrors = { + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type ListTasksError = ListTasksErrors[keyof ListTasksErrors]; + +export type ListTasksResponses = { + /** + * Tasks ordered by name. + */ + 200: Array; +}; + +export type ListTasksResponse = ListTasksResponses[keyof ListTasksResponses]; + +export type CreateTaskData = { + body: TaskInput; + path?: never; + query?: never; + url: '/tasks'; +}; + +export type CreateTaskErrors = { + /** + * Malformed, oversized, multiple-value, or schema-incompatible JSON. + */ + 400: ErrorEnvelope; + /** + * A JSON body was sent without Content-Type: application/json. + */ + 415: ErrorEnvelope; + /** + * The resource, schedule, target, interface, source address, or route is invalid. + */ + 422: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type CreateTaskError = CreateTaskErrors[keyof CreateTaskErrors]; + +export type CreateTaskResponses = { + /** + * Task was persisted and its schedule was installed. + */ + 201: Task; +}; + +export type CreateTaskResponse = CreateTaskResponses[keyof CreateTaskResponses]; + +export type ValidateTransientTaskData = { + body: TaskInput; + path?: never; + query?: never; + url: '/tasks/validate'; +}; + +export type ValidateTransientTaskErrors = { + /** + * Malformed, oversized, multiple-value, or schema-incompatible JSON. + */ + 400: ErrorEnvelope; + /** + * A JSON body was sent without Content-Type: application/json. + */ + 415: ErrorEnvelope; + /** + * The task DTO, provider target, interface/source, route, or public path is invalid. + */ + 422: ErrorEnvelope | RouteValidation; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type ValidateTransientTaskError = ValidateTransientTaskErrors[keyof ValidateTransientTaskErrors]; + +export type ValidateTransientTaskResponses = { + /** + * The unsaved task and selected network path are valid. + */ + 200: RouteValidation; +}; + +export type ValidateTransientTaskResponse = ValidateTransientTaskResponses[keyof ValidateTransientTaskResponses]; + +export type DeleteTaskData = { + body?: never; + path: { + id: Uuid; + }; + query?: never; + url: '/tasks/{id}'; +}; + +export type DeleteTaskErrors = { + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * The operation conflicts with current resource, execution, or queue state. + */ + 409: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type DeleteTaskError = DeleteTaskErrors[keyof DeleteTaskErrors]; + +export type DeleteTaskResponses = { + /** + * Task deleted; historical results were preserved. + */ + 204: void; +}; + +export type DeleteTaskResponse = DeleteTaskResponses[keyof DeleteTaskResponses]; + +export type GetTaskData = { + body?: never; + path: { + id: Uuid; + }; + query?: never; + url: '/tasks/{id}'; +}; + +export type GetTaskErrors = { + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type GetTaskError = GetTaskErrors[keyof GetTaskErrors]; + +export type GetTaskResponses = { + /** + * Task details. + */ + 200: Task; +}; + +export type GetTaskResponse = GetTaskResponses[keyof GetTaskResponses]; + +export type UpdateTaskData = { + body: TaskInput; + path: { + id: Uuid; + }; + query?: never; + url: '/tasks/{id}'; +}; + +export type UpdateTaskErrors = { + /** + * Malformed, oversized, multiple-value, or schema-incompatible JSON. + */ + 400: ErrorEnvelope; + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * A JSON body was sent without Content-Type: application/json. + */ + 415: ErrorEnvelope; + /** + * The resource, schedule, target, interface, source address, or route is invalid. + */ + 422: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type UpdateTaskError = UpdateTaskErrors[keyof UpdateTaskErrors]; + +export type UpdateTaskResponses = { + /** + * Updated task. + */ + 200: Task; +}; + +export type UpdateTaskResponse = UpdateTaskResponses[keyof UpdateTaskResponses]; + +export type RunTaskData = { + body?: never; + path: { + id: Uuid; + }; + query?: never; + url: '/tasks/{id}/run'; +}; + +export type RunTaskErrors = { + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * The operation conflicts with current resource, execution, or queue state. + */ + 409: ErrorEnvelope; + /** + * The operation-specific per-client rate limit was exceeded. Current responses do not include a Retry-After header; clients must observe the documented fixed window. + */ + 429: ErrorEnvelope; +}; + +export type RunTaskError = RunTaskErrors[keyof RunTaskErrors]; + +export type RunTaskResponses = { + /** + * A queued result was created. + */ + 202: Result; +}; + +export type RunTaskResponse = RunTaskResponses[keyof RunTaskResponses]; + +export type ValidateTaskData = { + body?: never; + path: { + id: Uuid; + }; + query?: never; + url: '/tasks/{id}/validate'; +}; + +export type ValidateTaskErrors = { + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * Task or selected route is invalid. The response is either the standard error envelope or a failed route snapshot. + */ + 422: ErrorEnvelope | RouteValidation; +}; + +export type ValidateTaskError = ValidateTaskErrors[keyof ValidateTaskErrors]; + +export type ValidateTaskResponses = { + /** + * Task and route are valid. + */ + 200: RouteValidation; +}; + +export type ValidateTaskResponse = ValidateTaskResponses[keyof ValidateTaskResponses]; + +export type DuplicateTaskData = { + body?: never; + path: { + id: Uuid; + }; + query?: never; + url: '/tasks/{id}/duplicate'; +}; + +export type DuplicateTaskErrors = { + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type DuplicateTaskError = DuplicateTaskErrors[keyof DuplicateTaskErrors]; + +export type DuplicateTaskResponses = { + /** + * Disabled copy with a new ID. + */ + 201: Task; +}; + +export type DuplicateTaskResponse = DuplicateTaskResponses[keyof DuplicateTaskResponses]; + +export type GetTaskNextRunsData = { + body?: never; + path: { + id: Uuid; + }; + query?: never; + url: '/tasks/{id}/next-runs'; +}; + +export type GetTaskNextRunsErrors = { + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * The resource, schedule, target, interface, source address, or route is invalid. + */ + 422: ErrorEnvelope; +}; + +export type GetTaskNextRunsError = GetTaskNextRunsErrors[keyof GetTaskNextRunsErrors]; + +export type GetTaskNextRunsResponses = { + /** + * Next runs in UTC for the task's IANA timezone. + */ + 200: NextRuns; +}; + +export type GetTaskNextRunsResponse = GetTaskNextRunsResponses[keyof GetTaskNextRunsResponses]; + +export type ListResultsData = { + body?: never; + path?: never; + query?: { + page?: number; + pageSize?: number; + taskId?: Uuid; + provider?: ProviderId; + status?: ResultStatus; + interface?: InterfaceName; + sourceIp?: IpAddress; + serverId?: string; + /** + * Inclusive RFC 3339 start instant. + */ + from?: string; + /** + * Exclusive RFC 3339 end instant. + */ + to?: string; + sort?: 'startedAt' | 'finishedAt' | 'download' | 'upload' | 'latency'; + direction?: 'asc' | 'desc'; + }; + url: '/results'; +}; + +export type ListResultsErrors = { + /** + * One or more query parameters are invalid. + */ + 400: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type ListResultsError = ListResultsErrors[keyof ListResultsErrors]; + +export type ListResultsResponses = { + /** + * Paginated result set. + */ + 200: ResultPage; +}; + +export type ListResultsResponse = ListResultsResponses[keyof ListResultsResponses]; + +export type GetDashboardResultSummaryData = { + body?: never; + path?: never; + query?: never; + url: '/results/dashboard-summary'; +}; + +export type GetDashboardResultSummaryErrors = { + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type GetDashboardResultSummaryError = GetDashboardResultSummaryErrors[keyof GetDashboardResultSummaryErrors]; + +export type GetDashboardResultSummaryResponses = { + /** + * Bounded dashboard result snapshot. + */ + 200: DashboardResultSummary; +}; + +export type GetDashboardResultSummaryResponse = GetDashboardResultSummaryResponses[keyof GetDashboardResultSummaryResponses]; + +export type DeleteResultData = { + body?: never; + path: { + id: Uuid; + }; + query?: never; + url: '/results/{id}'; +}; + +export type DeleteResultErrors = { + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type DeleteResultError = DeleteResultErrors[keyof DeleteResultErrors]; + +export type DeleteResultResponses = { + /** + * Result deleted. + */ + 204: void; +}; + +export type DeleteResultResponse = DeleteResultResponses[keyof DeleteResultResponses]; + +export type GetResultData = { + body?: never; + path: { + id: Uuid; + }; + query?: never; + url: '/results/{id}'; +}; + +export type GetResultErrors = { + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type GetResultError = GetResultErrors[keyof GetResultErrors]; + +export type GetResultResponses = { + /** + * Result details. + */ + 200: Result; +}; + +export type GetResultResponse = GetResultResponses[keyof GetResultResponses]; + +export type DeleteResultsBatchData = { + body: { + ids: Array; + }; + path?: never; + query?: never; + url: '/results/delete-batch'; +}; + +export type DeleteResultsBatchErrors = { + /** + * Malformed, oversized, multiple-value, or schema-incompatible JSON. + */ + 400: ErrorEnvelope; + /** + * A JSON body was sent without Content-Type: application/json. + */ + 415: ErrorEnvelope; +}; + +export type DeleteResultsBatchError = DeleteResultsBatchErrors[keyof DeleteResultsBatchErrors]; + +export type DeleteResultsBatchResponses = { + /** + * Number of matching rows deleted. + */ + 200: { + deleted: number; + }; +}; + +export type DeleteResultsBatchResponse = DeleteResultsBatchResponses[keyof DeleteResultsBatchResponses]; + +export type GetStatisticsData = { + body?: never; + path?: never; + query?: { + /** + * Inclusive start. Defaults to 30 days before the request. + */ + from?: string; + /** + * Exclusive end. Defaults to the request time. Ranges are limited to ten years. + */ + to?: string; + granularity?: StatisticsGranularity; + /** + * IANA reporting timezone; defaults to persisted settings. + */ + timezone?: string; + groupBy?: StatisticsDimension; + /** + * Repeat the parameter or provide comma-separated values. + */ + taskId?: Array; + /** + * Repeat the parameter or provide comma-separated values. + */ + provider?: Array; + /** + * Repeat the parameter or provide comma-separated values. The legacy interfaceName alias shares this combined 200-value limit. + */ + interface?: Array; + /** + * Repeat the parameter or provide comma-separated values. + */ + sourceIp?: Array; + /** + * Repeat the parameter or provide comma-separated values. + */ + serverId?: Array; + /** + * Repeat the parameter or provide comma-separated values. + */ + routeProfileId?: Array; + /** + * Repeat the parameter or provide comma-separated values. + */ + publicIp?: Array; + }; + url: '/statistics'; +}; + +export type GetStatisticsErrors = { + /** + * One or more query parameters are invalid. + */ + 400: ErrorEnvelope; + /** + * The statistics query matched more than 50,000 results, would materialize more than 5,000 output points or 1,000 comparison groups, or contains a comparison-dimension value longer than 512 bytes. + */ + 413: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type GetStatisticsError = GetStatisticsErrors[keyof GetStatisticsErrors]; + +export type GetStatisticsResponses = { + /** + * Normalized statistics report. Raw queries are limited to 90 days; the materialized report contains at most 5,000 output points and 1,000 comparison groups. + */ + 200: StatisticsReport; +}; + +export type GetStatisticsResponse = GetStatisticsResponses[keyof GetStatisticsResponses]; + +export type ListInterfacesData = { + body?: never; + path?: never; + query?: { + includeLoopback?: boolean; + includeDown?: boolean; + includeVirtual?: boolean; + }; + url: '/interfaces'; +}; + +export type ListInterfacesErrors = { + /** + * One or more query parameters are invalid. + */ + 400: ErrorEnvelope; +}; + +export type ListInterfacesError = ListInterfacesErrors[keyof ListInterfacesErrors]; + +export type ListInterfacesResponses = { + /** + * Interface snapshot from the active Linux network namespace. + */ + 200: InterfaceSnapshot; +}; + +export type ListInterfacesResponse = ListInterfacesResponses[keyof ListInterfacesResponses]; + +export type RefreshInterfacesData = { + body?: never; + path?: never; + query?: never; + url: '/interfaces/refresh'; +}; + +export type RefreshInterfacesErrors = { + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type RefreshInterfacesError = RefreshInterfacesErrors[keyof RefreshInterfacesErrors]; + +export type RefreshInterfacesResponses = { + /** + * Refreshed interface snapshot. + */ + 200: InterfaceSnapshot; +}; + +export type RefreshInterfacesResponse = RefreshInterfacesResponses[keyof RefreshInterfacesResponses]; + +export type ListRouteProfilesData = { + body?: never; + path?: never; + query?: never; + url: '/route-profiles'; +}; + +export type ListRouteProfilesErrors = { + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type ListRouteProfilesError = ListRouteProfilesErrors[keyof ListRouteProfilesErrors]; + +export type ListRouteProfilesResponses = { + /** + * Route profiles. + */ + 200: Array; +}; + +export type ListRouteProfilesResponse = ListRouteProfilesResponses[keyof ListRouteProfilesResponses]; + +export type CreateRouteProfileData = { + body: RouteProfileInput; + path?: never; + query?: never; + url: '/route-profiles'; +}; + +export type CreateRouteProfileErrors = { + /** + * Malformed, oversized, multiple-value, or schema-incompatible JSON. + */ + 400: ErrorEnvelope; + /** + * A JSON body was sent without Content-Type: application/json. + */ + 415: ErrorEnvelope; + /** + * The resource, schedule, target, interface, source address, or route is invalid. + */ + 422: ErrorEnvelope; +}; + +export type CreateRouteProfileError = CreateRouteProfileErrors[keyof CreateRouteProfileErrors]; + +export type CreateRouteProfileResponses = { + /** + * Created profile. + */ + 201: RouteProfile; +}; + +export type CreateRouteProfileResponse = CreateRouteProfileResponses[keyof CreateRouteProfileResponses]; + +export type DeleteRouteProfileData = { + body?: never; + path: { + id: Uuid; + }; + query?: never; + url: '/route-profiles/{id}'; +}; + +export type DeleteRouteProfileErrors = { + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * The operation conflicts with current resource, execution, or queue state. + */ + 409: ErrorEnvelope; +}; + +export type DeleteRouteProfileError = DeleteRouteProfileErrors[keyof DeleteRouteProfileErrors]; + +export type DeleteRouteProfileResponses = { + /** + * Profile deleted. + */ + 204: void; +}; + +export type DeleteRouteProfileResponse = DeleteRouteProfileResponses[keyof DeleteRouteProfileResponses]; + +export type GetRouteProfileData = { + body?: never; + path: { + id: Uuid; + }; + query?: never; + url: '/route-profiles/{id}'; +}; + +export type GetRouteProfileErrors = { + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; +}; + +export type GetRouteProfileError = GetRouteProfileErrors[keyof GetRouteProfileErrors]; + +export type GetRouteProfileResponses = { + /** + * Route profile. + */ + 200: RouteProfile; +}; + +export type GetRouteProfileResponse = GetRouteProfileResponses[keyof GetRouteProfileResponses]; + +export type UpdateRouteProfileData = { + body: RouteProfileInput; + path: { + id: Uuid; + }; + query?: never; + url: '/route-profiles/{id}'; +}; + +export type UpdateRouteProfileErrors = { + /** + * Malformed, oversized, multiple-value, or schema-incompatible JSON. + */ + 400: ErrorEnvelope; + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * A JSON body was sent without Content-Type: application/json. + */ + 415: ErrorEnvelope; + /** + * The resource, schedule, target, interface, source address, or route is invalid. + */ + 422: ErrorEnvelope; +}; + +export type UpdateRouteProfileError = UpdateRouteProfileErrors[keyof UpdateRouteProfileErrors]; + +export type UpdateRouteProfileResponses = { + /** + * Updated profile. + */ + 200: RouteProfile; +}; + +export type UpdateRouteProfileResponse = UpdateRouteProfileResponses[keyof UpdateRouteProfileResponses]; + +export type ValidateRouteProfileData = { + body?: never; + path: { + id: Uuid; + }; + query?: never; + url: '/route-profiles/{id}/validate'; +}; + +export type ValidateRouteProfileErrors = { + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * Validation completed with a mismatch or reachability failure. + */ + 422: RouteValidation; +}; + +export type ValidateRouteProfileError = ValidateRouteProfileErrors[keyof ValidateRouteProfileErrors]; + +export type ValidateRouteProfileResponses = { + /** + * Route matched the profile. + */ + 200: RouteValidation; +}; + +export type ValidateRouteProfileResponse = ValidateRouteProfileResponses[keyof ValidateRouteProfileResponses]; + +export type ListProvidersData = { + body?: never; + path?: never; + query?: never; + url: '/providers'; +}; + +export type ListProvidersResponses = { + /** + * Provider descriptors. + */ + 200: Array; +}; + +export type ListProvidersResponse = ListProvidersResponses[keyof ListProvidersResponses]; + +export type GetOoklaBinaryStatusData = { + body?: never; + path?: never; + query?: never; + url: '/providers/ookla/binary'; +}; + +export type GetOoklaBinaryStatusResponses = { + /** + * Deployment-owned upload capability and installation state. + */ + 200: OoklaBinaryStatus; +}; + +export type GetOoklaBinaryStatusResponse = GetOoklaBinaryStatusResponses[keyof GetOoklaBinaryStatusResponses]; + +export type UploadOoklaBinaryData = { + body: Blob | File; + path?: never; + query?: never; + url: '/providers/ookla/binary'; +}; + +export type UploadOoklaBinaryErrors = { + /** + * Managed executable upload is not enabled by deployment policy. + */ + 403: ErrorEnvelope; + /** + * The managed executable target is a directory, symbolic link, or otherwise conflicts with the required regular-file path. + */ + 409: ErrorEnvelope; + /** + * The executable exceeds the 64 MiB upload limit. + */ + 413: ErrorEnvelope; + /** + * A JSON body was sent without Content-Type: application/json. + */ + 415: ErrorEnvelope; + /** + * The resource, schedule, target, interface, source address, or route is invalid. + */ + 422: ErrorEnvelope; + /** + * The operation-specific per-client rate limit was exceeded. Current responses do not include a Retry-After header; clients must observe the documented fixed window. + */ + 429: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type UploadOoklaBinaryError = UploadOoklaBinaryErrors[keyof UploadOoklaBinaryErrors]; + +export type UploadOoklaBinaryResponses = { + /** + * The validated executable was installed in managed persistent storage. + */ + 201: OoklaBinaryInstallResult; +}; + +export type UploadOoklaBinaryResponse = UploadOoklaBinaryResponses[keyof UploadOoklaBinaryResponses]; + +export type ListProviderServersData = { + body?: never; + path: { + provider: ProviderId; + }; + query: { + interface: InterfaceName; + sourceIp: IpAddress; + ipFamily?: IpFamily; + search?: string; + limit?: number; + }; + url: '/providers/{provider}/servers'; +}; + +export type ListProviderServersErrors = { + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * The resource, schedule, target, interface, source address, or route is invalid. + */ + 422: ErrorEnvelope; + /** + * The operation-specific per-client rate limit was exceeded. Current responses do not include a Retry-After header; clients must observe the documented fixed window. + */ + 429: ErrorEnvelope; + /** + * A bounded provider operation failed. + */ + 502: ErrorEnvelope; +}; + +export type ListProviderServersError = ListProviderServersErrors[keyof ListProviderServersErrors]; + +export type ListProviderServersResponses = { + /** + * Matching provider servers. + */ + 200: Array; +}; + +export type ListProviderServersResponse = ListProviderServersResponses[keyof ListProviderServersResponses]; + +export type ValidateProviderServerData = { + body: ProviderServerValidation; + path: { + provider: ProviderId; + }; + query?: never; + url: '/providers/{provider}/validate-server'; +}; + +export type ValidateProviderServerErrors = { + /** + * Malformed, oversized, multiple-value, or schema-incompatible JSON. + */ + 400: ErrorEnvelope; + /** + * Resource or provider was not found. + */ + 404: ErrorEnvelope; + /** + * A JSON body was sent without Content-Type: application/json. + */ + 415: ErrorEnvelope; + /** + * The resource, schedule, target, interface, source address, or route is invalid. + */ + 422: ErrorEnvelope; + /** + * The operation-specific per-client rate limit was exceeded. Current responses do not include a Retry-After header; clients must observe the documented fixed window. + */ + 429: ErrorEnvelope; +}; + +export type ValidateProviderServerError = ValidateProviderServerErrors[keyof ValidateProviderServerErrors]; + +export type ValidateProviderServerResponses = { + /** + * The target, route, and bounded provider reachability checks succeeded. + */ + 200: { + valid: true; + success: true; + message: string; + provider: ProviderId; + target: TestTarget; + }; +}; + +export type ValidateProviderServerResponse = ValidateProviderServerResponses[keyof ValidateProviderServerResponses]; + +export type GetSettingsData = { + body?: never; + path?: never; + query?: never; + url: '/settings'; +}; + +export type GetSettingsErrors = { + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type GetSettingsError = GetSettingsErrors[keyof GetSettingsErrors]; + +export type GetSettingsResponses = { + /** + * Current settings. + */ + 200: Settings; +}; + +export type GetSettingsResponse = GetSettingsResponses[keyof GetSettingsResponses]; + +export type UpdateSettingsData = { + body: SettingsWritable; + path?: never; + query?: never; + url: '/settings'; +}; + +export type UpdateSettingsErrors = { + /** + * Malformed, oversized, multiple-value, or schema-incompatible JSON. + */ + 400: ErrorEnvelope; + /** + * A JSON body was sent without Content-Type: application/json. + */ + 415: ErrorEnvelope; + /** + * The resource, schedule, target, interface, source address, or route is invalid. + */ + 422: ErrorEnvelope; +}; + +export type UpdateSettingsError = UpdateSettingsErrors[keyof UpdateSettingsErrors]; + +export type UpdateSettingsResponses = { + /** + * Updated settings. + */ + 200: Settings; +}; + +export type UpdateSettingsResponse = UpdateSettingsResponses[keyof UpdateSettingsResponses]; + +export type UpdateOoklaEulaAcceptanceData = { + body: OoklaEulaAcceptanceRequest; + path?: never; + query?: never; + url: '/settings/ookla-eula'; +}; + +export type UpdateOoklaEulaAcceptanceErrors = { + /** + * Malformed, oversized, multiple-value, or schema-incompatible JSON. + */ + 400: ErrorEnvelope; + /** + * A JSON body was sent without Content-Type: application/json. + */ + 415: ErrorEnvelope; + /** + * The resource, schedule, target, interface, source address, or route is invalid. + */ + 422: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type UpdateOoklaEulaAcceptanceError = UpdateOoklaEulaAcceptanceErrors[keyof UpdateOoklaEulaAcceptanceErrors]; + +export type UpdateOoklaEulaAcceptanceResponses = { + /** + * Updated settings and persisted acknowledgement timestamp. + */ + 200: Settings; +}; + +export type UpdateOoklaEulaAcceptanceResponse = UpdateOoklaEulaAcceptanceResponses[keyof UpdateOoklaEulaAcceptanceResponses]; + +export type CleanupResultsData = { + body?: { + /** + * Delete results strictly older than this past UTC instant. + */ + before?: string; + }; + path?: never; + query?: never; + url: '/retention/cleanup'; +}; + +export type CleanupResultsErrors = { + /** + * Malformed, oversized, multiple-value, or schema-incompatible JSON. + */ + 400: ErrorEnvelope; + /** + * A JSON body was sent without Content-Type: application/json. + */ + 415: ErrorEnvelope; + /** + * The resource, schedule, target, interface, source address, or route is invalid. + */ + 422: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type CleanupResultsError = CleanupResultsErrors[keyof CleanupResultsErrors]; + +export type CleanupResultsResponses = { + /** + * Cleanup metrics, including whether the safety batch limit was reached. + */ + 200: RetentionMetrics; +}; + +export type CleanupResultsResponse = CleanupResultsResponses[keyof CleanupResultsResponses]; + +export type ExportResultsCsvData = { + body?: never; + path?: never; + query?: { + taskId?: Uuid; + provider?: ProviderId; + status?: ResultStatus; + interface?: InterfaceName; + sourceIp?: IpAddress; + serverId?: string; + /** + * Inclusive RFC 3339 start instant. + */ + from?: string; + /** + * Exclusive RFC 3339 end instant. + */ + to?: string; + sort?: 'startedAt' | 'finishedAt' | 'download' | 'upload' | 'latency'; + direction?: 'asc' | 'desc'; + }; + url: '/exports/results.csv'; +}; + +export type ExportResultsCsvErrors = { + /** + * One or more query parameters are invalid. + */ + 400: ErrorEnvelope; +}; + +export type ExportResultsCsvError = ExportResultsCsvErrors[keyof ExportResultsCsvErrors]; + +export type ExportResultsCsvResponses = { + /** + * UTF-8 CSV attachment. + */ + 200: string; +}; + +export type ExportResultsCsvResponse = ExportResultsCsvResponses[keyof ExportResultsCsvResponses]; + +export type ExportResultsJsonData = { + body?: never; + path?: never; + query?: { + taskId?: Uuid; + provider?: ProviderId; + status?: ResultStatus; + interface?: InterfaceName; + sourceIp?: IpAddress; + serverId?: string; + /** + * Inclusive RFC 3339 start instant. + */ + from?: string; + /** + * Exclusive RFC 3339 end instant. + */ + to?: string; + sort?: 'startedAt' | 'finishedAt' | 'download' | 'upload' | 'latency'; + direction?: 'asc' | 'desc'; + }; + url: '/exports/results.json'; +}; + +export type ExportResultsJsonErrors = { + /** + * One or more query parameters are invalid. + */ + 400: ErrorEnvelope; +}; + +export type ExportResultsJsonError = ExportResultsJsonErrors[keyof ExportResultsJsonErrors]; + +export type ExportResultsJsonResponses = { + /** + * JSON array attachment. + */ + 200: Array; +}; + +export type ExportResultsJsonResponse = ExportResultsJsonResponses[keyof ExportResultsJsonResponses]; + +export type ExportConfigurationData = { + body?: never; + path?: never; + query?: never; + url: '/config/export'; +}; + +export type ExportConfigurationErrors = { + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type ExportConfigurationError = ExportConfigurationErrors[keyof ExportConfigurationErrors]; + +export type ExportConfigurationResponses = { + /** + * Versioned JSON configuration attachment from one consistent database snapshot. + */ + 200: ConfigurationDocument; +}; + +export type ExportConfigurationResponse = ExportConfigurationResponses[keyof ExportConfigurationResponses]; + +export type ImportConfigurationData = { + body: ConfigurationDocument; + path?: never; + query?: never; + url: '/config/import'; +}; + +export type ImportConfigurationErrors = { + /** + * Malformed, oversized, multiple-value, or schema-incompatible JSON. + */ + 400: ErrorEnvelope; + /** + * The operation conflicts with current resource, execution, or queue state. + */ + 409: ErrorEnvelope; + /** + * A JSON body was sent without Content-Type: application/json. + */ + 415: ErrorEnvelope; + /** + * The resource, schedule, target, interface, source address, or route is invalid. + */ + 422: ErrorEnvelope; + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type ImportConfigurationError = ImportConfigurationErrors[keyof ImportConfigurationErrors]; + +export type ImportConfigurationResponses = { + /** + * Configuration imported and schedules reconciled. + */ + 200: ConfigurationImportResult; +}; + +export type ImportConfigurationResponse = ImportConfigurationResponses[keyof ImportConfigurationResponses]; + +export type CreateBackupData = { + body?: never; + path?: never; + query?: never; + url: '/backup'; +}; + +export type CreateBackupErrors = { + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type CreateBackupError = CreateBackupErrors[keyof CreateBackupErrors]; + +export type CreateBackupResponses = { + /** + * Complete SQLite database snapshot. + */ + 200: Blob | File; +}; + +export type CreateBackupResponse = CreateBackupResponses[keyof CreateBackupResponses]; + +export type StreamEventsData = { + body?: never; + path?: never; + query?: never; + url: '/events'; +}; + +export type StreamEventsErrors = { + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; + /** + * The bounded live-event subscriber capacity has been reached. + */ + 503: ErrorEnvelope; +}; + +export type StreamEventsError = StreamEventsErrors[keyof StreamEventsErrors]; + +export type StreamEventsResponses = { + /** + * Server-Sent Event stream with heartbeat comments. + */ + 200: string; +}; + +export type StreamEventsResponse = StreamEventsResponses[keyof StreamEventsResponses]; + +export type GetSystemInformationData = { + body?: never; + path?: never; + query?: never; + url: '/system'; +}; + +export type GetSystemInformationErrors = { + /** + * Sanitized internal error. + */ + 500: ErrorEnvelope; +}; + +export type GetSystemInformationError = GetSystemInformationErrors[keyof GetSystemInformationErrors]; + +export type GetSystemInformationResponses = { + /** + * System information without environment-variable values. + */ + 200: SystemInformation; +}; + +export type GetSystemInformationResponse = GetSystemInformationResponses[keyof GetSystemInformationResponses]; diff --git a/web/src/lib/types.ts b/web/src/lib/types.ts index cf92768..76fbaa8 100644 --- a/web/src/lib/types.ts +++ b/web/src/lib/types.ts @@ -1,100 +1,29 @@ -export type ProviderId = 'ookla' | 'librespeed' | 'cloudflare' -export type ResultStatus = 'queued' | 'validating' | 'running' | 'succeeded' | 'failed' | 'skipped' | 'cancelled' +import type * as ApiContract from './openapi' + +export type ProviderId = ApiContract.ProviderId +export type ResultStatus = ApiContract.ResultStatus export type ThemeMode = 'light' | 'dark' | 'system' -export interface Task { - id: string - name: string - description: string - enabled: boolean - provider: ProviderId - cronExpression: string - timezone: string - randomJitterSeconds: number - serverSelectionMode: 'automatic' | 'fixed' | 'custom' - serverId: string - serverUrl: string - customServerDefinition: Record - interfaceName: string - sourceIp: string - ipFamily: 'auto' | 'ipv4' | 'ipv6' - routeProfileId: string | null - timeoutSeconds: number - providerOptions: Record - preventOverlap: boolean - routeValidation: 'required' | 'interface-only' - createdAt: string - updatedAt: string - lastScheduledAt: string | null - nextScheduledAt: string | null - networkPathValid?: boolean - networkPathMessage?: string -} +// Persisted task responses contain the server-applied defaults even where the +// OpenAPI input schema permits callers to omit them. +export type Task = Required> & + Partial> export type TaskInput = Omit -export interface Result { - id: string - taskId: string - routeProfileId: string | null - trigger: 'manual' | 'scheduled' - provider: ProviderId - scheduledAt: string | null - startedAt: string | null - finishedAt: string | null - status: ResultStatus - downloadBitsPerSecond: number | null - uploadBitsPerSecond: number | null - latencyMilliseconds: number | null - jitterMilliseconds: number | null - packetLossPercent: number | null - downloadBytes: number | null - uploadBytes: number | null - selectedInterface: string - selectedSourceIp: string - detectedPublicIp: string - serverId: string - serverName: string - serverHost: string - serverSponsor: string - serverLocation: string - serverCountry: string - providerResultUrl: string - cloudflareColo: string - routeValidationSnapshot?: Record - executionDurationMs: number - processExitCode: number | null - sanitizedError: string - rawProviderResponse?: string - providerVersion: string - applicationVersion: string - tlsVerificationDisabled: boolean -} +export type Result = Required> & + Partial> & + Partial> export type ResultSummary = Omit -export interface DashboardTaskSummary { - taskId: string - taskName: string - enabled: boolean - interfaceName: string - sourceIp: string - latestResult: ResultSummary | null -} - -export interface DashboardPathSummary { - interfaceName: string - sourceIp: string - taskIds: string[] - latestResult: ResultSummary | null -} - -export interface DashboardSummary { +export type DashboardTaskSummary = Omit & { latestResult: ResultSummary | null } +export type DashboardPathSummary = Omit & { latestResult: ResultSummary | null } +export type DashboardSummary = Omit & { latestByTask: DashboardTaskSummary[] latestByPath: DashboardPathSummary[] activeRuns: ResultSummary[] recentFailures: ResultSummary[] - failedTaskCount: number } export interface Page { @@ -105,119 +34,21 @@ export interface Page { totalPages: number } -export interface InterfaceAddress { - address: string - family: 'ipv4' | 'ipv6' - linkLocal: boolean -} - -export interface NetworkInterface { - name: string - index: number - operational: boolean - loopback: boolean - virtual: boolean - macAddress: string - mtu: number - addresses: InterfaceAddress[] - operationalState?: string -} - -export interface RouteValidation { - success: boolean - interfaceName: string - sourceIp: string - gateway: string - routingTable: string - destination: string - detectedPublicIp: string - reachable: boolean - durationMs: number - validatedAt: string - message: string -} - -export interface RouteProfile { - id: string - name: string - description: string - interfaceName: string - sourceIp: string - expectedGateway: string - expectedRoutingTable: string - validationTarget: string - notes: string - createdAt: string - updatedAt: string - lastValidationAt: string | null - lastValidationSucceeded: boolean | null +export type InterfaceAddress = ApiContract.InterfaceAddress +export type NetworkInterface = Omit & { operationalState?: string } +export type RouteValidation = ApiContract.RouteValidation +export type RouteProfile = Omit, 'lastValidationSnapshot'> & { lastValidationSnapshot: Partial & Record } export type RouteProfileInput = Omit -export interface ProviderCapabilities { - serverDiscovery: boolean - fixedServerIds: boolean - customServerUrls: boolean - interfaceBinding: boolean - sourceAddressBinding: boolean - ipv4: boolean - ipv6: boolean - jitter: boolean - packetLoss: boolean - resultUrls: boolean -} - -export interface Provider { - id: ProviderId - displayName: string - available: boolean - version: string - message: string - capabilities: ProviderCapabilities -} - -export interface OoklaBinaryStatus { - uploadEnabled: boolean - installed: boolean - maxUploadBytes: number - message: string -} - -export interface OoklaBinaryInstallResult extends OoklaBinaryStatus { - version: string - sha256: string -} - -export interface ProviderServer { - id: string - name: string - sponsor: string - host: string - location: string - country: string - distanceKilometers?: number -} - -export interface Settings { - displayUnits: 'bits' | 'bytes' - defaultTimezone: string - globalConcurrency: number - allowSeparateWanConcurrency: boolean - retentionMode: 'forever' | 'days' | 'months' - retentionValue: number - defaultChartRange: string - interfaceRefreshIntervalSeconds: number - defaultTaskTimeoutSeconds: number - databaseMaintenanceSchedule: string - ooklaEulaAccepted: boolean - ooklaEulaAcceptedAt: string | null - ooklaEulaVersion: string - ooklaEulaCurrentVersion: string - ooklaEulaEffectiveAccepted: boolean - ooklaEulaAcceptanceSource: 'none' | 'persisted' | 'environment' -} +export type ProviderCapabilities = ApiContract.ProviderCapabilities +export type Provider = ApiContract.ProviderDescriptor +export type OoklaBinaryStatus = ApiContract.OoklaBinaryStatus +export type OoklaBinaryInstallResult = ApiContract.OoklaBinaryInstallResult +export type ProviderServer = ApiContract.ProviderServer +export type Settings = Required export type ConfigurationSettings = Pick -
setSearch(event.target.value)} placeholder="City, sponsor, host, or server ID" onKeyDown={(event) => { if (event.key === 'Enter') { event.preventDefault(); discover() } }} />
+
setSearch(event.target.value)} placeholder="City, sponsor, host, or server ID" aria-label={`${providerLabel(provider)} server search`} onKeyDown={(event) => { if (event.key === 'Enter') { event.preventDefault(); discover() } }} />
{!interfaceName || !sourceIp ?

Select an interface and concrete source address before discovery.

: null} {serversQuery.error ?

{getApiErrorMessage(serversQuery.error)}

: null}