diff --git a/.env.example b/.env.example index 4b41c25..2bb3c22 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,8 @@ # Copy this file to .env and fill in real values - never commit .env +# +# Docker Compose reads this file to fill in the ${...} placeholders in docker-compose.yml, which +# passes each service only the settings it needs. A variable that is not listed there reaches no +# container, so adding your own name here has no effect unless you map it in the compose file too. # ── SAIC / MG iSMART ──────────────────────────────────────────────────────── # Must be the vehicle owner account. Shared/secondary accounts lack the write @@ -175,15 +179,15 @@ OPENCHARGEMAP_API_KEY= # OVERPASS__BASEURL=https://overpass-api.de/api/interpreter # ── API rate limit ───────────────────────────────────────────────────────────── -# Requests per minute the API accepts per client IP. The default suits a handful -# of browser tabs; raise it when several people reach GarageStack through one -# public address, or when a dashboard polls it often. -RATE_LIMIT_GLOBAL_PER_MINUTE=120 +# Requests per minute the API accepts per client IP. The default of 120 suits a +# handful of browser tabs; raise it when several people reach GarageStack through +# one public address, or when a dashboard polls it often. +# RATE_LIMIT_GLOBAL_PER_MINUTE=120 # ── Tyre pressure thresholds ─────────────────────────────────────────────────── # Colour-code thresholds (bar) for the Overview diagram's tyre dots and for # low/high tyre-pressure notifications. Defaults suit ~2.5 bar passenger cars -- # override with your vehicle's placarded pressure, e.g. TYRE_PRESSURE_GOOD_BAR=2.55. -TYRE_PRESSURE_LOW_BAR=2.2 -TYRE_PRESSURE_GOOD_BAR=2.6 -TYRE_PRESSURE_HIGH_BAR=3.2 +# TYRE_PRESSURE_LOW_BAR=2.2 +# TYRE_PRESSURE_GOOD_BAR=2.6 +# TYRE_PRESSURE_HIGH_BAR=3.2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5115876..98b6a91 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,8 @@ jobs: - uses: pnpm/action-setup@v6 with: - version: 11.1.3 + # Version comes from frontend/package.json's packageManager field. + package_json_file: frontend/package.json - uses: actions/setup-node@v7.0.0 with: @@ -56,7 +57,8 @@ jobs: - uses: pnpm/action-setup@v6 with: - version: 11.1.3 + # Version comes from frontend/package.json's packageManager field. + package_json_file: frontend/package.json - uses: actions/setup-node@v7.0.0 with: @@ -82,7 +84,8 @@ jobs: - uses: pnpm/action-setup@v6 with: - version: 11.1.3 + # Version comes from frontend/package.json's packageManager field. + package_json_file: frontend/package.json - uses: actions/setup-node@v7.0.0 with: @@ -111,7 +114,8 @@ jobs: - uses: pnpm/action-setup@v6 with: - version: 11.1.3 + # Version comes from frontend/package.json's packageManager field. + package_json_file: frontend/package.json - uses: actions/setup-node@v7.0.0 with: diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 8030213..1a8194f 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -94,7 +94,14 @@ jobs: id: minver shell: bash run: | - dotnet tool install --global minver-cli --version 7.0.0 + # Same MinVer the build uses to stamp the assemblies, read from the central pin so + # the image tag and the version inside it can never come from different majors. + MINVER_VERSION=$(sed -n 's/.*Include="MinVer" Version="\([^"]*\)".*/\1/p' Directory.Packages.props) + if [ -z "$MINVER_VERSION" ]; then + echo "Could not read the MinVer version from Directory.Packages.props" >&2 + exit 1 + fi + dotnet tool install --global minver-cli --version "$MINVER_VERSION" VERSION=$(minver . -t v -p preview.0 | tail -n 1 | tr -d '\r') if [ -z "$VERSION" ]; then echo "MinVer produced an empty version" >&2 diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 7e44538..e79e2b2 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -117,11 +117,28 @@ dotnet ef migrations has-pending-model-changes --project src/GarageStack.Data -- CI runs the second command, and the pending-model-changes warning is not suppressed anywhere, so a model change without a migration fails before it reaches a real database. +### Adding a telemetry field + +A new value arriving from the car passes through several layers, in this order: + +1. `TelemetrySnapshot` (the model) plus a migration, as above. +2. `TelemetryMapper` in the Worker, which maps the MQTT topic onto the new property. +3. The frontend's `TelemetrySnapshot` interface in `services/vehicleApi.ts`, mirrored by hand. +4. Wherever it should show up: a card in `frontend/src/cards/registry.ts`, and the demo data in + `src/GarageStack.Data/Demo/` so the field is visible without a car. + +What it does *not* need is a mention in the repository's merge loops or its "does this row carry +anything" filters, or in `WidgetStatusDto`: the first two are derived from the model itself, and +the widget deliberately serves a curated subset. Charts are the same story in reverse: add the +field to `TelemetryHistoryPoint` and the history query starts returning rows that carry it. + ## Frontend REST calls go through `frontend/src/services/` - `apiCore.ts` centralizes the `fetch` wrapper (cookie-based auth, shared 401 handling, the JSON request helpers), and each domain area (`vehicleApi.ts`, `maintenanceApi.ts`, `notificationsApi.ts`, `mapApi.ts`, etc.) builds on it. Real-time updates use `useSignalR.ts` as described above, not polling. -The vehicle store owns `effectiveVehicleType` (the user's manual override, else the type detected from the gateway's `hw_version`); views derive their `isHev`/`isBev` style flags from it rather than repeating the override logic. The TypeScript interfaces in `services/` mirror the API's DTOs by hand; the history endpoint returns `TelemetryHistoryPoint` (the chart fields only), not full snapshots. +The vehicle store owns `activeVehicle`/`activeVin` (the one car this instance follows) and `effectiveVehicleType` (the user's manual override, else the drivetrain the API detected from the gateway's `hw_version`); views read those rather than indexing into the vehicle list or repeating the override logic. The TypeScript interfaces in `services/` mirror the API's DTOs by hand; the history endpoint returns `TelemetryHistoryPoint` (the chart fields only), not full snapshots. + +Dashboard cards are described once, in `frontend/src/cards/registry.ts`: each entry carries the card's icon, whether it is visible by default for a given drivetrain, and whether the current telemetry has anything to show. The card ids, the default layout and the "does this card have data" checks are all derived from that list, so a new card is one entry plus its markup in `DashboardCardContent.vue`. The map's point-of-interest layers work the same way: `composables/poiTileLayer.ts` holds the fetch-by-tile, cache and cluster logic, and charging stations, fuel stations and service areas are three configurations of it. ## Tests @@ -138,4 +155,6 @@ rotated map marker. CI runs them in the `e2e` job; see CONTRIBUTING.md for runni ## Build conventions -Backend projects share `Directory.Build.props` (analyzers on, warnings are errors, `.editorconfig` style rules enforced in the build) and `Directory.Packages.props` (central package versions). `dotnet format GarageStack.slnx` applies the formatting rules; CI verifies them. The frontend is linted by oxlint and ESLint and formatted by Prettier, also verified in CI. +Backend projects share `Directory.Build.props` (analyzers on, warnings are errors, `.editorconfig` style rules enforced in the build) and `Directory.Packages.props` (central package versions). `dotnet format GarageStack.slnx` applies the formatting rules; CI verifies them. The frontend is linted by oxlint and ESLint, its stylesheets by Stylelint, and formatted by Prettier, all verified in CI. + +Versions are pinned once: package versions in `Directory.Packages.props` (the release workflow reads MinVer's version from there too, so the tag and the stamped assemblies cannot come from different MinVer majors), and pnpm in `frontend/package.json`'s `packageManager` field, which corepack and the CI action both read. Runtime defaults belong to the app rather than to the deployment files: the compose files and the all-in-one entrypoint pass a setting through only when the operator sets one, so numbers like the tyre pressure thresholds exist in exactly one place. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c47f8d..e0a2c17 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,7 +34,7 @@ To work against the full stack (real or self-provided credentials): - Make sure linting and formatting pass: - Backend: `dotnet build` (analyzers run as part of the build, warnings are errors) and `dotnet format GarageStack.slnx` - - Frontend: `pnpm lint` and `pnpm format` + - Frontend: `pnpm lint` (oxlint, ESLint and Stylelint, each with `--fix`) and `pnpm format` - Changed an EF Core entity? Add a migration from the repository root (no Api configuration needed): `dotnet ef migrations add --project src/GarageStack.Data --startup-project src/GarageStack.Data` diff --git a/Directory.Packages.props b/Directory.Packages.props index a8f4a68..21565c1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,6 +18,7 @@ Microsoft.AspNetCore.OpenApi at both compile time (XmlCommentGenerator) and runtime. --> + diff --git a/docker-compose.demo.yml b/docker-compose.demo.yml index 9be0bbf..2911b41 100644 --- a/docker-compose.demo.yml +++ b/docker-compose.demo.yml @@ -5,11 +5,12 @@ services: dockerfile: src/GarageStack.Api/Dockerfile container_name: garagestack-api-demo restart: unless-stopped - env_file: - - .env.demo environment: + # Everything the container gets is listed here; compose reads .env.demo for the ${...} + # placeholders on its own, as the production compose file does. ASPNETCORE_ENVIRONMENT: "Production" DEMO_MODE: "true" + DEBUG_LOGS: "${DEBUG_LOGS:-false}" Cors__Origins__0: "${CORS_ORIGIN:-http://localhost:8080}" Auth__Username: "${AUTH_USERNAME:-demo}" Auth__Password: "${AUTH_PASSWORD:-demo}" diff --git a/docker-compose.yml b/docker-compose.yml index d76d548..94ccb82 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,25 @@ +# Settings both .NET services need. Compose reads .env for ${...} interpolation on its own, so +# every value a container gets is listed explicitly here: no env_file, and no secret reaching a +# container that has no use for it (the Worker never sees the OIDC or widget credentials, the API +# never sees the notification language). +x-dotnet-environment: &dotnet-environment + ASPNETCORE_ENVIRONMENT: "Production" + DEBUG_LOGS: "${DEBUG_LOGS:-false}" + ConnectionStrings__DefaultConnection: "Host=${POSTGRES_HOST:-postgres};Port=${POSTGRES_PORT:-5432};Database=${POSTGRES_DB:-garagestack};Username=${POSTGRES_USER:-garagestack};Password=${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in your .env file, see .env.example}" + Mqtt__Host: "${MQTT_HOST:-mosquitto}" + Mqtt__Port: "${MQTT_PORT:-1883}" + Mqtt__Username: "${MQTT_BROKER_USERNAME:-garagestack}" + Mqtt__Password: "${MQTT_BROKER_PASSWORD:?Set MQTT_BROKER_PASSWORD in your .env file, see .env.example}" + Vapid__PublicKey: "${VAPID_PUBLIC_KEY}" + Vapid__PrivateKey: "${VAPID_PRIVATE_KEY}" + Vapid__Subject: "mailto:${SAIC_USER}" + # Colour-coding and notification thresholds (bar); the API paints them, the Worker alerts on + # them. Left unset here on purpose: the app holds the defaults, so they are not restated (and + # cannot drift) in the deployment files. + TyrePressure__LowBar: "${TYRE_PRESSURE_LOW_BAR:-}" + TyrePressure__GoodBar: "${TYRE_PRESSURE_GOOD_BAR:-}" + TyrePressure__HighBar: "${TYRE_PRESSURE_HIGH_BAR:-}" + services: mosquitto: image: eclipse-mosquitto:2 @@ -83,23 +105,10 @@ services: condition: service_started api: condition: service_healthy - env_file: - - .env environment: - ASPNETCORE_ENVIRONMENT: "Production" - ConnectionStrings__DefaultConnection: "Host=${POSTGRES_HOST:-postgres};Port=${POSTGRES_PORT:-5432};Database=${POSTGRES_DB:-garagestack};Username=${POSTGRES_USER:-garagestack};Password=${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in your .env file, see .env.example}" - Mqtt__Host: "${MQTT_HOST:-mosquitto}" - Mqtt__Port: "${MQTT_PORT:-1883}" - Mqtt__Username: "${MQTT_BROKER_USERNAME:-garagestack}" - Mqtt__Password: "${MQTT_BROKER_PASSWORD:?Set MQTT_BROKER_PASSWORD in your .env file, see .env.example}" - Vapid__PublicKey: "${VAPID_PUBLIC_KEY}" - Vapid__PrivateKey: "${VAPID_PRIVATE_KEY}" - Vapid__Subject: "mailto:${SAIC_USER}" + <<: *dotnet-environment # Language of push notification texts (en or nl); the Worker has no browser to ask. Notifications__Culture: "${NOTIFICATION_LANGUAGE:-en}" - TyrePressure__LowBar: "${TYRE_PRESSURE_LOW_BAR:-2.2}" - TyrePressure__GoodBar: "${TYRE_PRESSURE_GOOD_BAR:-2.6}" - TyrePressure__HighBar: "${TYRE_PRESSURE_HIGH_BAR:-3.2}" volumes: - worker_logs:/app/logs @@ -121,11 +130,8 @@ services: required: false mosquitto: condition: service_started - env_file: - - .env environment: - ASPNETCORE_ENVIRONMENT: "Production" - ConnectionStrings__DefaultConnection: "Host=${POSTGRES_HOST:-postgres};Port=${POSTGRES_PORT:-5432};Database=${POSTGRES_DB:-garagestack};Username=${POSTGRES_USER:-garagestack};Password=${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in your .env file, see .env.example}" + <<: *dotnet-environment Cors__Origins__0: "${CORS_ORIGIN:-http://localhost:8080}" SAIC_USER: "${SAIC_USER}" SAIC_PASSWORD: "${SAIC_PASSWORD}" @@ -149,21 +155,13 @@ services: Oidc__GroupsClaim: "${OIDC_GROUPS_CLAIM:-groups}" Oidc__RedirectUri: "${OIDC_REDIRECT_URI:-}" Oidc__RequireHttpsMetadata: "${OIDC_REQUIRE_HTTPS_METADATA:-true}" - Mqtt__Host: "${MQTT_HOST:-mosquitto}" - Mqtt__Port: "${MQTT_PORT:-1883}" - Mqtt__Username: "${MQTT_BROKER_USERNAME:-garagestack}" - Mqtt__Password: "${MQTT_BROKER_PASSWORD:?Set MQTT_BROKER_PASSWORD in your .env file, see .env.example}" - Vapid__PublicKey: "${VAPID_PUBLIC_KEY}" - Vapid__PrivateKey: "${VAPID_PRIVATE_KEY}" - Vapid__Subject: "mailto:${SAIC_USER}" Widget__ApiKey: "${WIDGET_API_KEY:-}" OpenChargeMap__ApiKey: "${OPENCHARGEMAP_API_KEY:-}" + # Only worth setting when you self-host Overpass; the public endpoint needs no key. + Overpass__BaseUrl: "${OVERPASS__BASEURL:-https://overpass-api.de/api/interpreter}" Auth__CookieSecure: "${AUTH_COOKIE_SECURE:-false}" Auth__SessionLifetimeHours: "${AUTH_SESSION_LIFETIME_HOURS:-168}" - RateLimits__GlobalPerMinute: "${RATE_LIMIT_GLOBAL_PER_MINUTE:-120}" - TyrePressure__LowBar: "${TYRE_PRESSURE_LOW_BAR:-2.2}" - TyrePressure__GoodBar: "${TYRE_PRESSURE_GOOD_BAR:-2.6}" - TyrePressure__HighBar: "${TYRE_PRESSURE_HIGH_BAR:-3.2}" + RateLimits__GlobalPerMinute: "${RATE_LIMIT_GLOBAL_PER_MINUTE:-}" ports: - "127.0.0.1:${API_PORT:-5000}:8080" volumes: diff --git a/docker/all-in-one/Dockerfile b/docker/all-in-one/Dockerfile index caa6566..8bdf32b 100644 --- a/docker/all-in-one/Dockerfile +++ b/docker/all-in-one/Dockerfile @@ -6,8 +6,11 @@ WORKDIR /app # OS_PATCH_DATE busts the layer cache so apk upgrade actually re-runs instead of # reusing a stale cached layer from before an OS package fix was published. ARG OS_PATCH_DATE=0 -RUN echo "os patch date: ${OS_PATCH_DATE}" && apk upgrade --no-cache && corepack enable && corepack prepare pnpm@11.1.3 --activate +RUN echo "os patch date: ${OS_PATCH_DATE}" && apk upgrade --no-cache && corepack enable COPY frontend/package.json frontend/pnpm-lock.yaml frontend/pnpm-workspace.yaml ./ +# No version here: corepack takes it from package.json's packageManager field, so the pnpm +# version is pinned in one place for local installs, CI and every image. +RUN corepack prepare --activate RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \ pnpm config set store-dir /pnpm/store && \ pnpm config set minimum-release-age 0 && \ diff --git a/docker/all-in-one/entrypoint.sh b/docker/all-in-one/entrypoint.sh index 5ada563..557d820 100644 --- a/docker/all-in-one/entrypoint.sh +++ b/docker/all-in-one/entrypoint.sh @@ -87,13 +87,13 @@ export Widget__ApiKey="${WIDGET_API_KEY:-}" # API requests per minute per client IP. Optional -- raise it when several people share one # public address, lower it to tighten the budget. -export RateLimits__GlobalPerMinute="${RATE_LIMIT_GLOBAL_PER_MINUTE:-120}" +export RateLimits__GlobalPerMinute="${RATE_LIMIT_GLOBAL_PER_MINUTE:-}" -# Tyre pressure colour-coding / notification thresholds (bar). Optional -- defaults to -# 2.2 / 2.6 / 3.2 (the app's built-in values) when unset. -export TyrePressure__LowBar="${TYRE_PRESSURE_LOW_BAR:-2.2}" -export TyrePressure__GoodBar="${TYRE_PRESSURE_GOOD_BAR:-2.6}" -export TyrePressure__HighBar="${TYRE_PRESSURE_HIGH_BAR:-3.2}" +# Tyre pressure colour-coding / notification thresholds (bar). Optional. Empty means "use the +# app's own defaults", so those numbers live in one place instead of being restated here. +export TyrePressure__LowBar="${TYRE_PRESSURE_LOW_BAR:-}" +export TyrePressure__GoodBar="${TYRE_PRESSURE_GOOD_BAR:-}" +export TyrePressure__HighBar="${TYRE_PRESSURE_HIGH_BAR:-}" # .NET API listens on an internal port; nginx proxies port 80 to it export ASPNETCORE_URLS="http://127.0.0.1:9000" diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 072f338..0927f93 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -5,8 +5,11 @@ ENV VITE_DEMO_MODE=$VITE_DEMO_MODE # OS_PATCH_DATE busts the layer cache so apk upgrade actually re-runs instead of # reusing a stale cached layer from before an OS package fix was published. ARG OS_PATCH_DATE=0 -RUN echo "os patch date: ${OS_PATCH_DATE}" && apk upgrade --no-cache && corepack enable && corepack prepare pnpm@11.1.3 --activate +RUN echo "os patch date: ${OS_PATCH_DATE}" && apk upgrade --no-cache && corepack enable COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +# No version here: corepack takes it from package.json's packageManager field, so the pnpm +# version is pinned in one place for local installs, CI and every image. +RUN corepack prepare --activate RUN pnpm config set minimum-release-age 0 && pnpm install --frozen-lockfile --ignore-scripts COPY . . # Declared after install so a new version does not bust the dependency layer cache. diff --git a/frontend/package.json b/frontend/package.json index 469297f..3199c63 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,9 +11,10 @@ "test:e2e": "playwright test", "build-only": "vite build", "type-check": "vue-tsc --build", - "lint": "pnpm run lint:oxlint && pnpm run lint:eslint", + "lint": "pnpm run lint:oxlint && pnpm run lint:eslint && pnpm run lint:css", "lint:oxlint": "oxlint . --fix", "lint:eslint": "eslint . --fix --cache", + "lint:css": "stylelint \"src/**/*.{css,vue}\" --fix", "format": "prettier --write --experimental-cli src/ e2e/" }, "dependencies": { @@ -62,7 +63,10 @@ "jsdom": "^30.0.1", "npm-run-all2": "^9.0.3", "oxlint": "~1.82.0", + "postcss-html": "^2.0.0", "prettier": "3.9.6", + "stylelint": "^17.15.0", + "stylelint-config-standard": "^40.0.0", "typescript": "~6.0.3", "vite": "^8.3.0", "vite-plugin-vue-devtools": "^8.2.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index f930fdd..ccaf3bd 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -141,9 +141,18 @@ importers: oxlint: specifier: ~1.82.0 version: 1.82.0 + postcss-html: + specifier: ^2.0.0 + version: 2.0.0(postcss@8.5.28) prettier: specifier: 3.9.6 version: 3.9.6 + stylelint: + specifier: ^17.15.0 + version: 17.15.0(typescript@6.0.3) + stylelint-config-standard: + specifier: ^40.0.0 + version: 40.0.0(stylelint@17.15.0(typescript@6.0.3)) typescript: specifier: ~6.0.3 version: 6.0.3 @@ -746,6 +755,14 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 + '@csstools/css-syntax-patches-for-csstree@1.1.14': + resolution: {integrity: sha512-HpbVXyrofRXpHpgkNIjU/3EWR4WJvOkO3emNK/L6X/mTJU7bGUI3AkkpoTNXznQLp0KRjLHELTGeKI5dIkI9JQ==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + '@csstools/css-syntax-patches-for-csstree@1.1.7': resolution: {integrity: sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==} peerDependencies: @@ -758,6 +775,25 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} + '@csstools/media-query-list-parser@5.0.0': + resolution: {integrity: sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/selector-resolve-nested@4.0.1': + resolution: {integrity: sha512-j3vdQu0XwLME5qOTWxm8cnmvsf423R2YL6DbKklCHZwkDm7UdKNu6RPlw4REIJhSlKBICY3B70/7QZdicLqZgg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss-selector-parser: ^7.1.1 + + '@csstools/selector-specificity@6.0.0': + resolution: {integrity: sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss-selector-parser: ^7.1.1 + '@eslint-community/eslint-utils@4.10.1': resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1410,6 +1446,10 @@ packages: cpu: [x64] os: [win32] + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1': resolution: {integrity: sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q==} engines: {node: '>=12'} @@ -1748,6 +1788,18 @@ packages: alien-signals@3.2.1: resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.3.0: + resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + ansi-styles@7.0.0: resolution: {integrity: sha512-kKvt3m4uwzqL0wlkPd09CmljPJGOZZ4D0fP65sqFSvPkMRKhNi+74MgIJ5QxE6SxqB4t4KyUFGg8+n5zjo6hew==} engines: {node: '>=22'} @@ -1756,6 +1808,9 @@ packages: resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} engines: {node: '>=14'} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + array-buffer-byte-length@1.0.2: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} @@ -1776,6 +1831,10 @@ packages: resolution: {integrity: sha512-IJdzo2vLiElBxKzwS36VsCue/62d6IdWjnPB2v3nuPKeWGynp6FF/CYoLa5i/3jXH/z97ZDdsXz6abpgM6w07A==} engines: {node: '>=20.19.0'} + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -1878,6 +1937,10 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + caniuse-lite@1.0.30001806: resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} @@ -1896,6 +1959,16 @@ packages: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colord@2.10.0: + resolution: {integrity: sha512-AidJptpBJmjTclAp9BkLwJi0T93fo5epJnbaZslpg6QVzpHjAiveF55mE9AcUJiGMqRHgMDY8soMsQtuNYMHfw==} + commander@14.0.3: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} @@ -1926,6 +1999,15 @@ packages: resolution: {integrity: sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==} engines: {node: '>=6.4.0'} + cosmiconfig@9.0.2: + resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1934,6 +2016,10 @@ packages: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} + css-functions-list@3.3.3: + resolution: {integrity: sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==} + engines: {node: '>=12'} + css-tree@3.2.1: resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} @@ -2005,6 +2091,19 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -2025,6 +2124,13 @@ packages: electron-to-chromium@1.5.427: resolution: {integrity: sha512-n14zb3FdsChZ2BNobqNHAJMcP3ifFv4paox2LvCrfVAQcqGiSURgbJl+PfMpHVCNFkStnNc+RRVtPBTVW5PDgw==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + entities@7.0.1: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} @@ -2033,6 +2139,13 @@ packages: resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} engines: {node: '>=20.19.0'} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + error-stack-parser-es@1.0.5: resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} @@ -2183,6 +2296,10 @@ packages: fast-uri@3.1.7: resolution: {integrity: sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==} + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -2256,6 +2373,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -2289,10 +2410,25 @@ packages: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} + globby@16.2.4: + resolution: {integrity: sha512-c8B/VNLmxRcmqqenRA9t+9IyOjf9+V6lTxPaUJLqOCONdQkWZ0ETYgX0qbtJqPsgCNusT9MZ5Jeidw8Eb9tn2g==} + engines: {node: '>=20'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -2304,6 +2440,10 @@ packages: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} + has-flag@5.0.1: + resolution: {integrity: sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==} + engines: {node: '>=12'} + has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} @@ -2340,6 +2480,13 @@ packages: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + html-tags@5.1.0: + resolution: {integrity: sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==} + engines: {node: '>=20.10'} + + htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + idb@7.1.1: resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} @@ -2351,6 +2498,13 @@ packages: resolution: {integrity: sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw==} engines: {node: '>= 4'} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -2366,6 +2520,9 @@ packages: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-async-function@2.1.1: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} @@ -2411,6 +2568,10 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -2451,6 +2612,10 @@ packages: resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} engines: {node: '>=0.10.0'} + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -2536,6 +2701,13 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} + hasBin: true + jsdom@30.0.1: resolution: {integrity: sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} @@ -2550,6 +2722,9 @@ packages: engines: {node: '>=6'} hasBin: true + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-parse-even-better-errors@6.0.0: resolution: {integrity: sha512-2/8adwnK1/+Fdjyts4r6wSpfANWw8zdNhU9U/Llk59c6O+DjSisPWPykwoL8gZmocP9Dy64S7oie2g+Mia123A==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} @@ -2581,6 +2756,10 @@ packages: keyv@5.6.0: resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} @@ -2677,6 +2856,9 @@ packages: resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + local-pkg@1.2.1: resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} engines: {node: '>=14'} @@ -2688,6 +2870,9 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + lru-cache@11.5.2: resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} @@ -2709,6 +2894,9 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mathml-tag-names@4.0.0: + resolution: {integrity: sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==} + mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} @@ -2716,6 +2904,10 @@ packages: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} engines: {node: '>= 0.10.0'} + meow@14.1.0: + resolution: {integrity: sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==} + engines: {node: '>=20'} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2779,6 +2971,10 @@ packages: engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} hasBin: true + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + nostics@1.2.0: resolution: {integrity: sha512-FGqEfhQjrvo1lL8KFifdTQiNwwQHJxC1jtYE1Rc54qF/jxONUNL+kC9gS1krX8Q65PgrQ5fCqH/I4NhWBvdSqg==} @@ -2849,6 +3045,14 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + parse5@8.0.1: resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} @@ -2926,10 +3130,25 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} + postcss-html@2.0.0: + resolution: {integrity: sha512-f2Rvw5FCollEfVj3wfN7JdQb7n2rNIthW+epw2EByio7M6P7RH0BTj8a/ODHrUXd0cmO7ychb6YniymV93182Q==} + engines: {node: ^22.12 || >=24} + peerDependencies: + postcss: ^8.5.0 + + postcss-safe-parser@7.1.0: + resolution: {integrity: sha512-1WzZxRLaAFwEh6Do+zyGpjWV3nGJNxxhuh7Ubu/q1ICImMgZJnLwhoaEpRbG4pJppp2Y1ncL9ffA2f+LhrefQg==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + postcss-selector-parser@7.1.6: resolution: {integrity: sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==} engines: {node: '>=4'} + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.5.28: resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} @@ -3019,6 +3238,10 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + resolve@1.22.12: resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} @@ -3131,6 +3354,14 @@ packages: resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + smob@1.6.2: resolution: {integrity: sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==} engines: {node: '>=20.0.0'} @@ -3160,6 +3391,14 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@8.2.2: + resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} + engines: {node: '>=20'} + string.prototype.matchall@4.1.0: resolution: {integrity: sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==} engines: {node: '>= 0.4'} @@ -3180,17 +3419,57 @@ packages: resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} engines: {node: '>=4'} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-comments@2.0.1: resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} engines: {node: '>=10'} + stylelint-config-recommended@18.0.0: + resolution: {integrity: sha512-mxgT2XY6YZ3HWWe3Di8umG6aBmWmHTblTgu/f10rqFXnyWxjKWwNdjSWkgkwCtxIKnqjSJzvFmPT5yabVIRxZg==} + engines: {node: '>=20.19.0'} + peerDependencies: + stylelint: ^17.0.0 + + stylelint-config-standard@40.0.0: + resolution: {integrity: sha512-EznGJxOUhtWck2r6dJpbgAdPATIzvpLdK9+i5qPd4Lx70es66TkBPljSg4wN3Qnc6c4h2n+WbUrUynQ3fanjHw==} + engines: {node: '>=20.19.0'} + peerDependencies: + stylelint: ^17.0.0 + + stylelint@17.15.0: + resolution: {integrity: sha512-mWIkesYQvQjf4Kvdeu9ns0IL8/K/wtjaGxPqsPd6DlLZvaQxGysJsocxUDrBcyHQ5VleAk4uSMat4yMrVED7PA==} + engines: {node: '>=20.19.0'} + hasBin: true + + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + supports-hyperlinks@4.5.0: + resolution: {integrity: sha512-ZW2OvfeCXrNTbLakPUzjQG922EeGCOteFSVoek5DKStTh898wf7zgtuFlzQN8HfZCxC3Eh02yJVrRW51hADf+w==} + engines: {node: '>=20'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} + temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} @@ -3325,6 +3604,10 @@ packages: resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} engines: {node: '>=4'} + unicorn-magic@0.4.0: + resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} + engines: {node: '>=20'} + unique-string@2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} @@ -3636,6 +3919,10 @@ packages: resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} engines: {node: '>= 0.4'} + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3704,6 +3991,10 @@ packages: workbox-window@7.4.1: resolution: {integrity: sha512-notZDH2u8VXaqyuD7xaqIfEFi6SRM4SUSd7ewe9PDsVqADuepxX2ZMY3uvuZGxzY5ZOsGC/vD3A/3smFtJt4/A==} + write-file-atomic@7.0.1: + resolution: {integrity: sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==} + engines: {node: ^20.17.0 || >=22.9.0} + ws@7.5.13: resolution: {integrity: sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==} engines: {node: '>=8.3.0'} @@ -4494,12 +4785,29 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 + '@csstools/css-syntax-patches-for-csstree@1.1.14(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + '@csstools/css-syntax-patches-for-csstree@1.1.7(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 '@csstools/css-tokenizer@4.0.0': {} + '@csstools/media-query-list-parser@5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/selector-resolve-nested@4.0.1(postcss-selector-parser@7.1.6)': + dependencies: + postcss-selector-parser: 7.1.6 + + '@csstools/selector-specificity@6.0.0(postcss-selector-parser@7.1.6)': + dependencies: + postcss-selector-parser: 7.1.6 + '@eslint-community/eslint-utils@4.10.1(eslint@10.10.0(jiti@2.7.0))': dependencies: eslint: 10.10.0(jiti@2.7.0) @@ -4927,6 +5235,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.63.2': optional: true + '@sindresorhus/merge-streams@4.0.0': {} + '@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1': dependencies: ejs: 3.1.10 @@ -5353,10 +5663,20 @@ snapshots: alien-signals@3.2.1: {} + ansi-regex@5.0.1: {} + + ansi-regex@6.3.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + ansi-styles@7.0.0: {} ansis@4.3.1: {} + argparse@2.0.1: {} + array-buffer-byte-length@1.0.2: dependencies: call-bound: 1.0.4 @@ -5385,6 +5705,8 @@ snapshots: '@babel/types': 7.29.8 ast-kit: 2.2.0 + astral-regex@2.0.0: {} + async-function@1.0.0: {} async@3.2.6: {} @@ -5496,6 +5818,8 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 + callsites@3.1.0: {} + caniuse-lite@1.0.30001806: {} caniuse-lite@1.0.30001810: {} @@ -5510,6 +5834,14 @@ snapshots: dependencies: readdirp: 5.1.1 + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colord@2.10.0: {} + commander@14.0.3: {} commander@2.20.3: {} @@ -5533,6 +5865,15 @@ snapshots: dependencies: browserslist: 4.28.9 + cosmiconfig@9.0.2(typescript@6.0.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.3.2 + parse-json: 5.2.0 + optionalDependencies: + typescript: 6.0.3 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -5541,6 +5882,8 @@ snapshots: crypto-random-string@2.0.0: {} + css-functions-list@3.3.3: {} + css-tree@3.2.1: dependencies: mdn-data: 2.27.1 @@ -5608,6 +5951,24 @@ snapshots: detect-libc@2.1.2: {} + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -5629,10 +5990,20 @@ snapshots: electron-to-chromium@1.5.427: {} + emoji-regex@8.0.0: {} + + entities@4.5.0: {} + entities@7.0.1: {} entities@8.0.0: {} + env-paths@2.2.1: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + error-stack-parser-es@1.0.5: {} es-abstract-get@1.0.0: @@ -5849,6 +6220,8 @@ snapshots: fast-uri@3.1.7: {} + fastest-levenshtein@1.0.16: {} + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -5928,6 +6301,8 @@ snapshots: gensync@1.0.0-beta.2: {} + get-east-asian-width@1.6.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -5977,17 +6352,41 @@ snapshots: minipass: 7.1.3 path-scurry: 2.0.2 + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + globalthis@1.0.4: dependencies: define-properties: 1.2.1 gopd: 1.2.0 + globby@16.2.4: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + fast-glob: 3.3.3 + ignore: 7.0.9 + is-path-inside: 4.0.0 + micromatch: 4.0.8 + slash: 5.1.0 + unicorn-magic: 0.4.0 + + globjoin@0.1.4: {} + gopd@1.2.0: {} graceful-fs@4.2.11: {} has-bigints@1.1.0: {} + has-flag@5.0.1: {} + has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 @@ -6022,12 +6421,28 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' + html-tags@5.1.0: {} + + htmlparser2@9.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + idb@7.1.1: {} ignore@5.3.2: {} ignore@7.0.9: {} + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-meta-resolve@4.2.0: {} + imurmurhash@0.1.4: {} ini@1.3.8: {} @@ -6044,6 +6459,8 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-arrayish@0.2.1: {} + is-async-function@2.1.1: dependencies: async-function: 1.0.0 @@ -6090,6 +6507,8 @@ snapshots: dependencies: call-bound: 1.0.4 + is-fullwidth-code-point@3.0.0: {} + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -6123,6 +6542,8 @@ snapshots: is-obj@1.0.1: {} + is-path-inside@4.0.0: {} + is-potential-custom-element-name@1.0.1: {} is-regex@1.2.1: @@ -6202,6 +6623,12 @@ snapshots: js-tokens@4.0.0: {} + js-tokens@9.0.1: {} + + js-yaml@4.3.2: + dependencies: + argparse: 2.0.1 + jsdom@30.0.1: dependencies: '@asamuzakjp/css-color': 6.0.5 @@ -6230,6 +6657,8 @@ snapshots: jsesc@3.1.0: {} + json-parse-even-better-errors@2.3.1: {} + json-parse-even-better-errors@6.0.0: {} json-schema-traverse@0.4.1: {} @@ -6254,6 +6683,8 @@ snapshots: dependencies: '@keyv/serialize': 1.1.1 + kind-of@6.0.3: {} + kolorist@1.8.0: {} leaflet.heat@0.2.0: {} @@ -6320,6 +6751,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.33.0 lightningcss-win32-x64-msvc: 1.33.0 + lines-and-columns@1.2.4: {} + local-pkg@1.2.1: dependencies: mlly: 1.8.2 @@ -6332,6 +6765,8 @@ snapshots: lodash.debounce@4.0.8: {} + lodash.truncate@4.4.2: {} + lru-cache@11.5.2: {} lru-cache@5.1.1: @@ -6352,10 +6787,14 @@ snapshots: math-intrinsics@1.1.0: {} + mathml-tag-names@4.0.0: {} + mdn-data@2.27.1: {} memorystream@0.3.1: {} + meow@14.1.0: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -6402,6 +6841,8 @@ snapshots: dependencies: abbrev: 5.0.0 + normalize-path@3.0.0: {} + nostics@1.2.0: {} npm-normalize-package-bin@6.0.0: {} @@ -6495,6 +6936,17 @@ snapshots: package-json-from-dist@1.0.1: {} + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.7 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + parse5@8.0.1: dependencies: entities: 8.0.0 @@ -6554,11 +7006,24 @@ snapshots: possible-typed-array-names@1.1.0: {} + postcss-html@2.0.0(postcss@8.5.28): + dependencies: + htmlparser2: 9.1.0 + js-tokens: 9.0.1 + postcss: 8.5.28 + postcss-safe-parser: 7.1.0(postcss@8.5.28) + + postcss-safe-parser@7.1.0(postcss@8.5.28): + dependencies: + postcss: 8.5.28 + postcss-selector-parser@7.1.6: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 + postcss-value-parser@4.2.0: {} + postcss@8.5.28: dependencies: nanoid: 3.3.19 @@ -6645,6 +7110,8 @@ snapshots: requires-port@1.0.0: {} + resolve-from@4.0.0: {} + resolve@1.22.12: dependencies: es-errors: 1.3.0 @@ -6814,6 +7281,14 @@ snapshots: mrmime: 2.0.1 totalist: 3.0.1 + slash@5.1.0: {} + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + smob@1.6.2: {} source-map-js@1.2.1: {} @@ -6836,6 +7311,17 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@8.2.2: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + string.prototype.matchall@4.1.0: dependencies: call-bind: 1.0.9 @@ -6882,12 +7368,87 @@ snapshots: is-obj: 1.0.1 is-regexp: 1.0.0 + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.3.0 + strip-comments@2.0.1: {} + stylelint-config-recommended@18.0.0(stylelint@17.15.0(typescript@6.0.3)): + dependencies: + stylelint: 17.15.0(typescript@6.0.3) + + stylelint-config-standard@40.0.0(stylelint@17.15.0(typescript@6.0.3)): + dependencies: + stylelint: 17.15.0(typescript@6.0.3) + stylelint-config-recommended: 18.0.0(stylelint@17.15.0(typescript@6.0.3)) + + stylelint@17.15.0(typescript@6.0.3): + dependencies: + '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-syntax-patches-for-csstree': 1.1.14(css-tree@3.2.1) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/selector-resolve-nested': 4.0.1(postcss-selector-parser@7.1.6) + '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.6) + colord: 2.10.0 + cosmiconfig: 9.0.2(typescript@6.0.3) + css-functions-list: 3.3.3 + css-tree: 3.2.1 + debug: 4.4.3 + fast-glob: 3.3.3 + fastest-levenshtein: 1.0.16 + file-entry-cache: 11.1.5 + global-modules: 2.0.0 + globby: 16.2.4 + globjoin: 0.1.4 + html-tags: 5.1.0 + ignore: 7.0.9 + import-meta-resolve: 4.2.0 + mathml-tag-names: 4.0.0 + meow: 14.1.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.28 + postcss-safe-parser: 7.1.0(postcss@8.5.28) + postcss-selector-parser: 7.1.6 + postcss-value-parser: 4.2.0 + string-width: 8.2.2 + supports-hyperlinks: 4.5.0 + svg-tags: 1.0.0 + table: 6.9.0 + write-file-atomic: 7.0.1 + transitivePeerDependencies: + - supports-color + - typescript + + supports-color@10.2.2: {} + + supports-hyperlinks@4.5.0: + dependencies: + has-flag: 5.0.1 + supports-color: 10.2.2 + supports-preserve-symlinks-flag@1.0.0: {} + svg-tags@1.0.0: {} + symbol-tree@3.2.4: {} + table@6.9.0: + dependencies: + ajv: 8.20.0 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + temp-dir@2.0.0: {} tempy@0.6.0: @@ -7029,6 +7590,8 @@ snapshots: unicode-property-aliases-ecmascript@2.2.0: {} + unicorn-magic@0.4.0: {} + unique-string@2.0.0: dependencies: crypto-random-string: 2.0.0 @@ -7331,6 +7894,10 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 + which@1.3.1: + dependencies: + isexe: 2.0.0 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -7459,6 +8026,10 @@ snapshots: '@types/trusted-types': 2.0.7 workbox-core: 7.4.1 + write-file-atomic@7.0.1: + dependencies: + signal-exit: 4.1.0 + ws@7.5.13: {} wsl-utils@0.3.1: diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 72c71b6..fec6ed1 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -59,8 +59,8 @@ const { deleteAllNotifications, } = useNotifications() -const carModel = computed(() => vehicleStore.vehicles[0]?.model ?? null) -const vehicleId = computed(() => vehicleStore.vehicles[0]?.id ?? null) +const carModel = computed(() => vehicleStore.activeVehicle?.model ?? null) +const vehicleId = computed(() => vehicleStore.activeVehicle?.id ?? null) const availabilityToast = ref<'online' | 'offline' | null>(null) let toastTimer: ReturnType | null = null diff --git a/frontend/src/assets/login.css b/frontend/src/assets/login.css index 53255b0..69c3c93 100644 --- a/frontend/src/assets/login.css +++ b/frontend/src/assets/login.css @@ -105,7 +105,7 @@ .login-field__input:-webkit-autofill, .login-field__input:-webkit-autofill:hover, .login-field__input:-webkit-autofill:focus { - -webkit-box-shadow: 0 0 0 1000px var(--color-surface-2) inset !important; + box-shadow: 0 0 0 1000px var(--color-surface-2) inset !important; -webkit-text-fill-color: var(--color-text) !important; caret-color: var(--color-text); border-color: var(--color-border) !important; @@ -132,8 +132,8 @@ display: flex; align-items: center; gap: 0.5rem; - background: rgba(239, 68, 68, 0.12); - border: 1px solid rgba(239, 68, 68, 0.3); + background: rgb(239 68 68 / 12%); + border: 1px solid rgb(239 68 68 / 30%); border-radius: 6px; color: var(--color-danger-text); font-size: 0.83rem; @@ -175,8 +175,8 @@ } .login-note--warning { - background: rgba(239, 68, 68, 0.12); - border-color: rgba(239, 68, 68, 0.3); + background: rgb(239 68 68 / 12%); + border-color: rgb(239 68 68 / 30%); color: var(--color-danger-text); } diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index d67e2ef..2721d5a 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -1,4 +1,4 @@ -@import '@vueform/multiselect/themes/default.css'; +@import url('@vueform/multiselect/themes/default.css'); :root { --color-bg: #0f1117; @@ -24,7 +24,7 @@ [data-theme='light'] { --color-bg: #f0f4f8; - --color-surface: #ffffff; + --color-surface: #fff; --color-surface-2: #e8edf3; --color-border: #cdd5df; --color-text: #1a202c; @@ -93,7 +93,7 @@ html, body, #app { height: 100%; - font-family: 'Inter', system-ui, sans-serif; + font-family: Inter, system-ui, sans-serif; background: var(--color-bg); color: var(--color-text); } @@ -169,7 +169,7 @@ body, display: none; position: fixed; inset: 0; - background: rgba(0, 0, 0, 0.6); + background: rgb(0 0 0 / 60%); z-index: 1000; } @@ -265,9 +265,11 @@ body, .text-sm { font-size: 0.85rem; } + .text-success { color: var(--color-success); } + .text-danger { color: var(--color-danger); } @@ -277,18 +279,23 @@ body, .mt-2 { margin-top: 0.5rem; } + .mt-3 { margin-top: 1rem; } + .mt-4 { margin-top: 1.5rem; } + .mb-4 { margin-bottom: 1.5rem; } + .ms-2 { margin-left: 0.5rem; } + .text-xs { font-size: 0.75rem; } @@ -336,12 +343,15 @@ body, .status-card--success { border-left: 3px solid var(--color-success); } + .status-card--warning { border-left: 3px solid var(--color-warning); } + .status-card--danger { border-left: 3px solid var(--color-danger); } + .status-card--info { border-left: 3px solid var(--color-info); } @@ -415,7 +425,7 @@ body, position: fixed; inset: 0; z-index: 1100; - background: rgba(0, 0, 0, 0.6); + background: rgb(0 0 0 / 60%); display: flex; align-items: flex-end; justify-content: center; @@ -568,7 +578,7 @@ body, transform: translateY(100%); } -@media (min-width: 768px) { +@media (width >= 768px) { .detail-modal-backdrop { align-items: center; } @@ -629,29 +639,30 @@ body, } .road-edge { - stroke: rgba(255, 255, 255, 0.35); + stroke: rgb(255 255 255 / 35%); stroke-width: 1.5; } [data-theme='light'] .road-edge { - stroke: rgba(255, 255, 255, 0.7); + stroke: rgb(255 255 255 / 70%); } .road-center-dash { - stroke: rgba(255, 255, 255, 0.6); + stroke: rgb(255 255 255 / 60%); stroke-width: 2.5; stroke-dasharray: 30 15; animation: road-scroll 1s linear infinite; } [data-theme='light'] .road-center-dash { - stroke: rgba(255, 255, 255, 0.85); + stroke: rgb(255 255 255 / 85%); } @keyframes road-scroll { from { stroke-dashoffset: 0; } + to { stroke-dashoffset: -45; } @@ -693,7 +704,7 @@ body, flex-direction: column; } -@media (max-width: 767px) { +@media (width <= 767px) { .overview-row { flex-direction: column; } @@ -791,7 +802,7 @@ body, border-radius: 6px; color: var(--color-text); cursor: pointer; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); + box-shadow: 0 1px 4px rgb(0 0 0 / 20%); transition: background 0.15s; } @@ -902,43 +913,50 @@ body, .car-svg-body { fill: var(--car-primary, #f9b233); } + .car-svg-accent { fill: var(--car-secondary, #f39200); } + .car-svg-detail { fill: #3a3020; } + .car-svg-dark { fill: #1d1d1b; } + .car-svg-red { fill: #e6332a; } + .car-svg-highlight { fill: #ededed; opacity: 0.7; } + .car-svg-gloss { - fill: #ffffff; + fill: #fff; opacity: 0.18; } [data-theme='light'] .car-svg-detail { fill: #4a3c20; } + [data-theme='light'] .car-svg-dark { fill: #2d2d2d; } /* Live car marker (buildCarMarkerIcon in utils/mapCarIcon.ts) - used on both MapView and the - dashboard's location preview, so it lives here rather than in either view's own +