From 60b94b5789c65335bf39ad248ee8b51fc50f8ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sun, 12 Apr 2026 22:28:14 +0200 Subject: [PATCH] Extract @trails-cool/map-core package from Planner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New renderer-agnostic package with zero dependencies: - Tile configs (base layers, overlay layers) - Color palettes (surface, highway, smoothness, tracktype, cycleway, bikeroute, elevation, maxspeed) — 8 color maps + 3 color functions - POI category definitions (9 categories with Overpass queries, icons, colors, profile mappings) - Z-index layering constants - Snap distance constant Updated 15 consuming files to import from @trails-cool/map-core. Deleted poi-categories.ts and z-index.ts from the Planner (fully moved). packages/map re-exports tile configs from map-core for backwards compat. All 117 tests pass, no user-facing changes. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/planner/app/components/ColoredRoute.tsx | 146 ++---------------- .../planner/app/components/ElevationChart.tsx | 4 +- apps/planner/app/components/PlannerMap.tsx | 2 +- apps/planner/app/components/PoiPanel.tsx | 3 +- .../app/components/RouteInteraction.tsx | 2 +- apps/planner/app/lib/overpass.test.ts | 2 +- apps/planner/app/lib/overpass.ts | 2 +- apps/planner/app/lib/poi-categories.test.ts | 2 +- apps/planner/app/lib/poi-snap.ts | 3 +- apps/planner/app/lib/use-pois.ts | 2 +- apps/planner/app/lib/use-profile-defaults.ts | 2 +- apps/planner/package.json | 1 + openspec/changes/map-core-package/tasks.md | 56 +++---- packages/map-core/package.json | 10 ++ packages/map-core/src/colors/bikeroute.ts | 9 ++ packages/map-core/src/colors/cycleway.ts | 11 ++ packages/map-core/src/colors/elevation.ts | 18 +++ packages/map-core/src/colors/highway.ts | 29 ++++ packages/map-core/src/colors/index.ts | 8 + packages/map-core/src/colors/maxspeed.ts | 12 ++ packages/map-core/src/colors/smoothness.ts | 12 ++ packages/map-core/src/colors/surface.ts | 21 +++ packages/map-core/src/colors/tracktype.ts | 9 ++ packages/map-core/src/index.ts | 23 +++ .../map-core/src/poi.ts | 20 +-- packages/map-core/src/snap.ts | 1 + packages/map-core/src/tiles.ts | 66 ++++++++ .../lib => packages/map-core/src}/z-index.ts | 2 +- packages/map-core/tsconfig.json | 8 + packages/map/package.json | 3 + packages/map/src/index.ts | 4 +- packages/map/src/layers.ts | 68 +------- pnpm-lock.yaml | 35 +++++ 33 files changed, 339 insertions(+), 257 deletions(-) create mode 100644 packages/map-core/package.json create mode 100644 packages/map-core/src/colors/bikeroute.ts create mode 100644 packages/map-core/src/colors/cycleway.ts create mode 100644 packages/map-core/src/colors/elevation.ts create mode 100644 packages/map-core/src/colors/highway.ts create mode 100644 packages/map-core/src/colors/index.ts create mode 100644 packages/map-core/src/colors/maxspeed.ts create mode 100644 packages/map-core/src/colors/smoothness.ts create mode 100644 packages/map-core/src/colors/surface.ts create mode 100644 packages/map-core/src/colors/tracktype.ts create mode 100644 packages/map-core/src/index.ts rename apps/planner/app/lib/poi-categories.ts => packages/map-core/src/poi.ts (89%) create mode 100644 packages/map-core/src/snap.ts create mode 100644 packages/map-core/src/tiles.ts rename {apps/planner/app/lib => packages/map-core/src}/z-index.ts (79%) create mode 100644 packages/map-core/tsconfig.json diff --git a/apps/planner/app/components/ColoredRoute.tsx b/apps/planner/app/components/ColoredRoute.tsx index ea2b026..f0379d7 100644 --- a/apps/planner/app/components/ColoredRoute.tsx +++ b/apps/planner/app/components/ColoredRoute.tsx @@ -1,6 +1,15 @@ import { useMemo } from "react"; import { Polyline } from "react-leaflet"; import type L from "leaflet"; +import { + SURFACE_COLORS, DEFAULT_SURFACE_COLOR, + HIGHWAY_COLORS, DEFAULT_HIGHWAY_COLOR, + SMOOTHNESS_COLORS, DEFAULT_SMOOTHNESS_COLOR, + TRACKTYPE_COLORS, DEFAULT_TRACKTYPE_COLOR, + CYCLEWAY_COLORS, DEFAULT_CYCLEWAY_COLOR, + BIKEROUTE_COLORS, DEFAULT_BIKEROUTE_COLOR, + elevationColor, routeGradeColor, maxspeedColor, +} from "@trails-cool/map-core"; export type ColorMode = "plain" | "elevation" | "surface" | "grade" | "highway" | "maxspeed" | "smoothness" | "tracktype" | "cycleway" | "bikeroute"; @@ -16,135 +25,6 @@ interface ColoredRouteProps { bikeroutes?: string[]; } -const SURFACE_COLORS: Record = { - asphalt: "#6b7280", - concrete: "#9ca3af", - paved: "#6b7280", - paving_stones: "#78716c", - cobblestone: "#a8a29e", - gravel: "#92400e", - compacted: "#b45309", - "fine_gravel": "#d97706", - ground: "#65a30d", - dirt: "#84cc16", - grass: "#22c55e", - sand: "#fbbf24", - mud: "#713f12", - wood: "#a16207", - unpaved: "#ca8a04", - path: "#16a34a", - track: "#ea580c", -}; - -const DEFAULT_SURFACE_COLOR = "#9ca3af"; - -const HIGHWAY_COLORS: Record = { - // Major roads — warm tones (caution for cyclists) - motorway: "#dc2626", - motorway_link: "#dc2626", - trunk: "#ea580c", - trunk_link: "#ea580c", - primary: "#f97316", - primary_link: "#f97316", - // Urban roads — neutral tones - secondary: "#6366f1", - secondary_link: "#6366f1", - tertiary: "#818cf8", - tertiary_link: "#818cf8", - residential: "#6b7280", - unclassified: "#9ca3af", - living_street: "#a78bfa", - // Paths & cycling infrastructure — green tones - cycleway: "#16a34a", - path: "#22c55e", - footway: "#4ade80", - track: "#65a30d", - bridleway: "#84cc16", - // Service & other — muted tones - service: "#d4d4d8", - pedestrian: "#c084fc", - steps: "#f472b6", -}; - -const DEFAULT_HIGHWAY_COLOR = "#9ca3af"; - -const SMOOTHNESS_COLORS: Record = { - excellent: "#22c55e", - good: "#16a34a", - intermediate: "#eab308", - bad: "#f97316", - very_bad: "#ef4444", - horrible: "#991b1b", - very_horrible: "#7f1d1d", - impassable: "#450a0a", -}; - -const DEFAULT_SMOOTHNESS_COLOR = "#9ca3af"; - -const TRACKTYPE_COLORS: Record = { - grade1: "#22c55e", - grade2: "#84cc16", - grade3: "#eab308", - grade4: "#f97316", - grade5: "#ef4444", -}; - -const DEFAULT_TRACKTYPE_COLOR = "#9ca3af"; - -const CYCLEWAY_COLORS: Record = { - track: "#22c55e", - lane: "#84cc16", - shared_lane: "#eab308", - share_busway: "#f97316", - opposite_lane: "#818cf8", - separate: "#16a34a", - no: "#ef4444", -}; - -const DEFAULT_CYCLEWAY_COLOR = "#9ca3af"; - -const BIKEROUTE_COLORS: Record = { - icn: "#7c3aed", // purple — international - ncn: "#2563eb", // blue — national - rcn: "#0891b2", // teal — regional - lcn: "#059669", // emerald — local - none: "#d4d4d8", // gray — no route -}; - -const DEFAULT_BIKEROUTE_COLOR = "#d4d4d8"; - -export function routeGradeColor(grade: number): string { - const absGrade = Math.abs(grade); - if (absGrade < 3) return "#22c55e"; - if (absGrade < 6) return "#eab308"; - if (absGrade < 10) return "#f97316"; - if (absGrade < 15) return "#ef4444"; - return "#991b1b"; -} - -export function elevationColor(t: number): string { - // green (0) → yellow (0.5) → red (1) - if (t <= 0.5) { - const r = Math.round(255 * (t * 2)); - return `rgb(${r}, 200, 50)`; - } - const g = Math.round(200 * (1 - (t - 0.5) * 2)); - return `rgb(255, ${g}, 50)`; -} - -export function maxspeedColor(speed: string): string { - if (speed === "walk") return "#22c55e"; - if (speed === "none") return "#991b1b"; - const num = parseInt(speed, 10); - if (isNaN(num)) return "#9ca3af"; // unknown/gray - if (num <= 20) return "#22c55e"; - if (num <= 30) return "#22c55e"; - if (num <= 50) return "#eab308"; - if (num <= 70) return "#f97316"; - if (num <= 100) return "#ef4444"; - return "#991b1b"; // >100 dark red -} - export function ColoredRoute({ coordinates, colorMode, surfaces, highways, maxspeeds, smoothnesses, tracktypes, cycleways, bikeroutes }: ColoredRouteProps) { const segments = useMemo(() => { if (colorMode === "plain" || coordinates.length < 2) { @@ -356,11 +236,3 @@ export function findSegmentForPoint( return 0; } -export { - SURFACE_COLORS, DEFAULT_SURFACE_COLOR, - HIGHWAY_COLORS, DEFAULT_HIGHWAY_COLOR, - SMOOTHNESS_COLORS, DEFAULT_SMOOTHNESS_COLOR, - TRACKTYPE_COLORS, DEFAULT_TRACKTYPE_COLOR, - CYCLEWAY_COLORS, DEFAULT_CYCLEWAY_COLOR, - BIKEROUTE_COLORS, DEFAULT_BIKEROUTE_COLOR, -}; diff --git a/apps/planner/app/components/ElevationChart.tsx b/apps/planner/app/components/ElevationChart.tsx index fa98b32..5d5e006 100644 --- a/apps/planner/app/components/ElevationChart.tsx +++ b/apps/planner/app/components/ElevationChart.tsx @@ -10,8 +10,8 @@ import { TRACKTYPE_COLORS, DEFAULT_TRACKTYPE_COLOR, CYCLEWAY_COLORS, DEFAULT_CYCLEWAY_COLOR, BIKEROUTE_COLORS, DEFAULT_BIKEROUTE_COLOR, - type ColorMode, -} from "~/components/ColoredRoute"; +} from "@trails-cool/map-core"; +import type { ColorMode } from "~/components/ColoredRoute"; function gradeColor(grade: number): string { const absGrade = Math.abs(grade); diff --git a/apps/planner/app/components/PlannerMap.tsx b/apps/planner/app/components/PlannerMap.tsx index 98b5b3f..059abbb 100644 --- a/apps/planner/app/components/PlannerMap.tsx +++ b/apps/planner/app/components/PlannerMap.tsx @@ -13,7 +13,7 @@ import { usePois } from "~/lib/use-pois"; import { useProfileDefaults } from "~/lib/use-profile-defaults"; import { useYjsPoiSync } from "~/lib/use-yjs-poi-sync"; import { snapToPoi } from "~/lib/poi-snap"; -import { Z_CURSOR, Z_WAYPOINT, Z_WAYPOINT_HIGHLIGHTED, Z_HIGHLIGHT } from "~/lib/z-index"; +import { Z_CURSOR, Z_WAYPOINT, Z_WAYPOINT_HIGHLIGHTED, Z_HIGHLIGHT } from "@trails-cool/map-core"; import { NoGoAreaLayer } from "./NoGoAreaLayer"; import { ColoredRoute, findSegmentForPoint, type ColorMode } from "./ColoredRoute"; import { RouteInteraction } from "./RouteInteraction"; diff --git a/apps/planner/app/components/PoiPanel.tsx b/apps/planner/app/components/PoiPanel.tsx index 93c11de..1f55dc0 100644 --- a/apps/planner/app/components/PoiPanel.tsx +++ b/apps/planner/app/components/PoiPanel.tsx @@ -2,9 +2,8 @@ import { useState, useEffect, useRef } from "react"; import L from "leaflet"; import { useTranslation } from "react-i18next"; import { useMap } from "react-leaflet"; -import { poiCategories } from "~/lib/poi-categories"; +import { poiCategories, Z_POI_MARKER } from "@trails-cool/map-core"; import type { PoiState } from "~/lib/use-pois"; -import { Z_POI_MARKER } from "~/lib/z-index"; import "leaflet.markercluster/dist/MarkerCluster.css"; import "leaflet.markercluster/dist/MarkerCluster.Default.css"; diff --git a/apps/planner/app/components/RouteInteraction.tsx b/apps/planner/app/components/RouteInteraction.tsx index 01f589d..36375f9 100644 --- a/apps/planner/app/components/RouteInteraction.tsx +++ b/apps/planner/app/components/RouteInteraction.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useCallback } from "react"; import { useMap } from "react-leaflet"; import L from "leaflet"; -import { Z_GHOST_WAYPOINT } from "~/lib/z-index"; +import { Z_GHOST_WAYPOINT } from "@trails-cool/map-core"; interface RouteInteractionProps { coordinates: [number, number, number][]; // [lon, lat, ele] diff --git a/apps/planner/app/lib/overpass.test.ts b/apps/planner/app/lib/overpass.test.ts index 5a7734a..b462e5b 100644 --- a/apps/planner/app/lib/overpass.test.ts +++ b/apps/planner/app/lib/overpass.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from "vitest"; import { buildQuery, parseResponse, deduplicateById, type Poi } from "./overpass.ts"; -import { poiCategories } from "./poi-categories.ts"; +import { poiCategories } from "@trails-cool/map-core"; describe("buildQuery", () => { it("builds Overpass QL with bbox and category union", () => { diff --git a/apps/planner/app/lib/overpass.ts b/apps/planner/app/lib/overpass.ts index 89857fd..db76e47 100644 --- a/apps/planner/app/lib/overpass.ts +++ b/apps/planner/app/lib/overpass.ts @@ -1,4 +1,4 @@ -import type { PoiCategory } from "./poi-categories.ts"; +import type { PoiCategory } from "@trails-cool/map-core"; const OVERPASS_ENDPOINTS = [ "https://overpass.kumi.systems/api/interpreter", diff --git a/apps/planner/app/lib/poi-categories.test.ts b/apps/planner/app/lib/poi-categories.test.ts index 8b0c832..a415aaa 100644 --- a/apps/planner/app/lib/poi-categories.test.ts +++ b/apps/planner/app/lib/poi-categories.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import { getCategoriesForProfile, profileOverlayDefaults, poiCategories } from "./poi-categories.ts"; +import { getCategoriesForProfile, profileOverlayDefaults, poiCategories } from "@trails-cool/map-core"; describe("getCategoriesForProfile", () => { it("returns bike infra for cycling profiles", () => { diff --git a/apps/planner/app/lib/poi-snap.ts b/apps/planner/app/lib/poi-snap.ts index c702fe3..c35ea59 100644 --- a/apps/planner/app/lib/poi-snap.ts +++ b/apps/planner/app/lib/poi-snap.ts @@ -1,6 +1,5 @@ import type { Poi } from "./overpass.ts"; - -const SNAP_DISTANCE_METERS = 50; +import { SNAP_DISTANCE_METERS } from "@trails-cool/map-core"; export interface SnapResult { lat: number; diff --git a/apps/planner/app/lib/use-pois.ts b/apps/planner/app/lib/use-pois.ts index 926b498..295133c 100644 --- a/apps/planner/app/lib/use-pois.ts +++ b/apps/planner/app/lib/use-pois.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { queryPois, OverpassRateLimitError, type Poi, type BBox } from "./overpass.ts"; import { getCached, setCached } from "./poi-cache.ts"; -import { poiCategories } from "./poi-categories.ts"; +import { poiCategories } from "@trails-cool/map-core"; const MIN_ZOOM = 10; const DEBOUNCE_MS = 800; diff --git a/apps/planner/app/lib/use-profile-defaults.ts b/apps/planner/app/lib/use-profile-defaults.ts index f591bb1..66cff60 100644 --- a/apps/planner/app/lib/use-profile-defaults.ts +++ b/apps/planner/app/lib/use-profile-defaults.ts @@ -1,7 +1,7 @@ import { useEffect, useRef } from "react"; import type { YjsState } from "./use-yjs.ts"; import type { PoiState } from "./use-pois.ts"; -import { getCategoriesForProfile } from "./poi-categories.ts"; +import { getCategoriesForProfile } from "@trails-cool/map-core"; /** * Auto-enable relevant POI categories when the routing profile changes. diff --git a/apps/planner/package.json b/apps/planner/package.json index 58f8e4a..6005ef2 100644 --- a/apps/planner/package.json +++ b/apps/planner/package.json @@ -24,6 +24,7 @@ "@trails-cool/gpx": "workspace:*", "@trails-cool/i18n": "workspace:*", "@trails-cool/map": "workspace:*", + "@trails-cool/map-core": "workspace:*", "@trails-cool/types": "workspace:*", "@trails-cool/ui": "workspace:*", "codemirror": "^6.0.2", diff --git a/openspec/changes/map-core-package/tasks.md b/openspec/changes/map-core-package/tasks.md index e61baca..cbd95e4 100644 --- a/openspec/changes/map-core-package/tasks.md +++ b/openspec/changes/map-core-package/tasks.md @@ -1,45 +1,45 @@ ## 1. Package Setup -- [ ] 1.1 Create `packages/map-core/` with `package.json`, `tsconfig.json`, add to pnpm workspace and Turborepo pipeline -- [ ] 1.2 Create `src/index.ts` with re-exports for all modules +- [x] 1.1 Create `packages/map-core/` with `package.json`, `tsconfig.json`, add to pnpm workspace and Turborepo pipeline +- [x] 1.2 Create `src/index.ts` with re-exports for all modules ## 2. Tile Definitions -- [ ] 2.1 Move `baseLayers` and `TileLayerConfig` from `packages/map/src/layers.ts` to `packages/map-core/src/tiles.ts` -- [ ] 2.2 Move `overlayLayers` and `OverlayLayerConfig` to `packages/map-core/src/tiles.ts` -- [ ] 2.3 Update `packages/map/src/layers.ts` to re-export from `@trails-cool/map-core` -- [ ] 2.4 Update `packages/map/src/index.ts` exports +- [x] 2.1 Move `baseLayers` and `TileLayerConfig` from `packages/map/src/layers.ts` to `packages/map-core/src/tiles.ts` +- [x] 2.2 Move `overlayLayers` and `OverlayLayerConfig` to `packages/map-core/src/tiles.ts` +- [x] 2.3 Update `packages/map/src/layers.ts` to re-export from `@trails-cool/map-core` +- [x] 2.4 Update `packages/map/src/index.ts` exports ## 3. Color Palettes -- [ ] 3.1 Extract `SURFACE_COLORS`, `DEFAULT_SURFACE_COLOR` from `ColoredRoute.tsx` to `map-core/src/colors/surface.ts` -- [ ] 3.2 Extract `HIGHWAY_COLORS`, `DEFAULT_HIGHWAY_COLOR` to `map-core/src/colors/highway.ts` -- [ ] 3.3 Extract `SMOOTHNESS_COLORS`, `DEFAULT_SMOOTHNESS_COLOR` to `map-core/src/colors/smoothness.ts` -- [ ] 3.4 Extract `TRACKTYPE_COLORS`, `DEFAULT_TRACKTYPE_COLOR` to `map-core/src/colors/tracktype.ts` -- [ ] 3.5 Extract `CYCLEWAY_COLORS`, `DEFAULT_CYCLEWAY_COLOR` to `map-core/src/colors/cycleway.ts` -- [ ] 3.6 Extract `BIKEROUTE_COLORS`, `DEFAULT_BIKEROUTE_COLOR` to `map-core/src/colors/bikeroute.ts` -- [ ] 3.7 Extract `elevationColor()`, `routeGradeColor()` to `map-core/src/colors/elevation.ts` -- [ ] 3.8 Extract `maxspeedColor()` to `map-core/src/colors/maxspeed.ts` -- [ ] 3.9 Create `map-core/src/colors/index.ts` re-exporting all color maps and functions -- [ ] 3.10 Update `ColoredRoute.tsx` to import from `@trails-cool/map-core` -- [ ] 3.11 Update `ElevationChart.tsx` to import from `@trails-cool/map-core` +- [x] 3.1 Extract `SURFACE_COLORS`, `DEFAULT_SURFACE_COLOR` from `ColoredRoute.tsx` to `map-core/src/colors/surface.ts` +- [x] 3.2 Extract `HIGHWAY_COLORS`, `DEFAULT_HIGHWAY_COLOR` to `map-core/src/colors/highway.ts` +- [x] 3.3 Extract `SMOOTHNESS_COLORS`, `DEFAULT_SMOOTHNESS_COLOR` to `map-core/src/colors/smoothness.ts` +- [x] 3.4 Extract `TRACKTYPE_COLORS`, `DEFAULT_TRACKTYPE_COLOR` to `map-core/src/colors/tracktype.ts` +- [x] 3.5 Extract `CYCLEWAY_COLORS`, `DEFAULT_CYCLEWAY_COLOR` to `map-core/src/colors/cycleway.ts` +- [x] 3.6 Extract `BIKEROUTE_COLORS`, `DEFAULT_BIKEROUTE_COLOR` to `map-core/src/colors/bikeroute.ts` +- [x] 3.7 Extract `elevationColor()`, `routeGradeColor()` to `map-core/src/colors/elevation.ts` +- [x] 3.8 Extract `maxspeedColor()` to `map-core/src/colors/maxspeed.ts` +- [x] 3.9 Create `map-core/src/colors/index.ts` re-exporting all color maps and functions +- [x] 3.10 Update `ColoredRoute.tsx` to import from `@trails-cool/map-core` +- [x] 3.11 Update `ElevationChart.tsx` to import from `@trails-cool/map-core` ## 4. POI Categories -- [ ] 4.1 Move `PoiCategory` type and `poiCategories` array from `poi-categories.ts` to `map-core/src/poi.ts` -- [ ] 4.2 Move `getCategoriesForProfile()` and `profileOverlayDefaults` to `map-core/src/poi.ts` -- [ ] 4.3 Update Planner imports: `PoiPanel.tsx`, `use-pois.ts`, `use-profile-defaults.ts`, `poi-categories.test.ts` -- [ ] 4.4 Move POI snap distance constant to `map-core/src/snap.ts` +- [x] 4.1 Move `PoiCategory` type and `poiCategories` array from `poi-categories.ts` to `map-core/src/poi.ts` +- [x] 4.2 Move `getCategoriesForProfile()` and `profileOverlayDefaults` to `map-core/src/poi.ts` +- [x] 4.3 Update Planner imports: `PoiPanel.tsx`, `use-pois.ts`, `use-profile-defaults.ts`, `poi-categories.test.ts` +- [x] 4.4 Move POI snap distance constant to `map-core/src/snap.ts` ## 5. Z-Index Constants -- [ ] 5.1 Move all z-index constants from `z-index.ts` to `map-core/src/z-index.ts` -- [ ] 5.2 Update Planner imports: `PlannerMap.tsx`, `PoiPanel.tsx`, `RouteInteraction.tsx` +- [x] 5.1 Move all z-index constants from `z-index.ts` to `map-core/src/z-index.ts` +- [x] 5.2 Update Planner imports: `PlannerMap.tsx`, `PoiPanel.tsx`, `RouteInteraction.tsx` ## 6. Verification -- [ ] 6.1 Run `pnpm typecheck` — all packages compile -- [ ] 6.2 Run `pnpm lint` — no import errors -- [ ] 6.3 Run `pnpm test` — all existing tests pass (update import paths in test files) -- [ ] 6.4 Run `pnpm test:e2e` — no regressions -- [ ] 6.5 Verify `map-core` has zero dependencies in its `package.json` +- [x] 6.1 Run `pnpm typecheck` — all packages compile +- [x] 6.2 Run `pnpm lint` — no import errors +- [x] 6.3 Run `pnpm test` — all existing tests pass (update import paths in test files) +- [x] 6.4 Run `pnpm test:e2e` — no regressions +- [x] 6.5 Verify `map-core` has zero dependencies in its `package.json` diff --git a/packages/map-core/package.json b/packages/map-core/package.json new file mode 100644 index 0000000..ef6ba91 --- /dev/null +++ b/packages/map-core/package.json @@ -0,0 +1,10 @@ +{ + "name": "@trails-cool/map-core", + "version": "0.0.1", + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "main": "./src/index.ts", + "types": "./src/index.ts" +} diff --git a/packages/map-core/src/colors/bikeroute.ts b/packages/map-core/src/colors/bikeroute.ts new file mode 100644 index 0000000..6d28d54 --- /dev/null +++ b/packages/map-core/src/colors/bikeroute.ts @@ -0,0 +1,9 @@ +export const BIKEROUTE_COLORS: Record = { + icn: "#7c3aed", // purple — international + ncn: "#2563eb", // blue — national + rcn: "#0891b2", // teal — regional + lcn: "#059669", // emerald — local + none: "#d4d4d8", // gray — no route +}; + +export const DEFAULT_BIKEROUTE_COLOR = "#d4d4d8"; diff --git a/packages/map-core/src/colors/cycleway.ts b/packages/map-core/src/colors/cycleway.ts new file mode 100644 index 0000000..5c2cc22 --- /dev/null +++ b/packages/map-core/src/colors/cycleway.ts @@ -0,0 +1,11 @@ +export const CYCLEWAY_COLORS: Record = { + track: "#22c55e", + lane: "#84cc16", + shared_lane: "#eab308", + share_busway: "#f97316", + opposite_lane: "#818cf8", + separate: "#16a34a", + no: "#ef4444", +}; + +export const DEFAULT_CYCLEWAY_COLOR = "#9ca3af"; diff --git a/packages/map-core/src/colors/elevation.ts b/packages/map-core/src/colors/elevation.ts new file mode 100644 index 0000000..bfafdd8 --- /dev/null +++ b/packages/map-core/src/colors/elevation.ts @@ -0,0 +1,18 @@ +export function routeGradeColor(grade: number): string { + const absGrade = Math.abs(grade); + if (absGrade < 3) return "#22c55e"; + if (absGrade < 6) return "#eab308"; + if (absGrade < 10) return "#f97316"; + if (absGrade < 15) return "#ef4444"; + return "#991b1b"; +} + +export function elevationColor(t: number): string { + // green (0) → yellow (0.5) → red (1) + if (t <= 0.5) { + const r = Math.round(255 * (t * 2)); + return `rgb(${r}, 200, 50)`; + } + const g = Math.round(200 * (1 - (t - 0.5) * 2)); + return `rgb(255, ${g}, 50)`; +} diff --git a/packages/map-core/src/colors/highway.ts b/packages/map-core/src/colors/highway.ts new file mode 100644 index 0000000..6134189 --- /dev/null +++ b/packages/map-core/src/colors/highway.ts @@ -0,0 +1,29 @@ +export const HIGHWAY_COLORS: Record = { + // Major roads — warm tones (caution for cyclists) + motorway: "#dc2626", + motorway_link: "#dc2626", + trunk: "#ea580c", + trunk_link: "#ea580c", + primary: "#f97316", + primary_link: "#f97316", + // Urban roads — neutral tones + secondary: "#6366f1", + secondary_link: "#6366f1", + tertiary: "#818cf8", + tertiary_link: "#818cf8", + residential: "#6b7280", + unclassified: "#9ca3af", + living_street: "#a78bfa", + // Paths & cycling infrastructure — green tones + cycleway: "#16a34a", + path: "#22c55e", + footway: "#4ade80", + track: "#65a30d", + bridleway: "#84cc16", + // Service & other — muted tones + service: "#d4d4d8", + pedestrian: "#c084fc", + steps: "#f472b6", +}; + +export const DEFAULT_HIGHWAY_COLOR = "#9ca3af"; diff --git a/packages/map-core/src/colors/index.ts b/packages/map-core/src/colors/index.ts new file mode 100644 index 0000000..7df069b --- /dev/null +++ b/packages/map-core/src/colors/index.ts @@ -0,0 +1,8 @@ +export { SURFACE_COLORS, DEFAULT_SURFACE_COLOR } from "./surface.ts"; +export { HIGHWAY_COLORS, DEFAULT_HIGHWAY_COLOR } from "./highway.ts"; +export { SMOOTHNESS_COLORS, DEFAULT_SMOOTHNESS_COLOR } from "./smoothness.ts"; +export { TRACKTYPE_COLORS, DEFAULT_TRACKTYPE_COLOR } from "./tracktype.ts"; +export { CYCLEWAY_COLORS, DEFAULT_CYCLEWAY_COLOR } from "./cycleway.ts"; +export { BIKEROUTE_COLORS, DEFAULT_BIKEROUTE_COLOR } from "./bikeroute.ts"; +export { elevationColor, routeGradeColor } from "./elevation.ts"; +export { maxspeedColor } from "./maxspeed.ts"; diff --git a/packages/map-core/src/colors/maxspeed.ts b/packages/map-core/src/colors/maxspeed.ts new file mode 100644 index 0000000..b2c70b4 --- /dev/null +++ b/packages/map-core/src/colors/maxspeed.ts @@ -0,0 +1,12 @@ +export function maxspeedColor(speed: string): string { + if (speed === "walk") return "#22c55e"; + if (speed === "none") return "#991b1b"; + const num = parseInt(speed, 10); + if (isNaN(num)) return "#9ca3af"; // unknown/gray + if (num <= 20) return "#22c55e"; + if (num <= 30) return "#22c55e"; + if (num <= 50) return "#eab308"; + if (num <= 70) return "#f97316"; + if (num <= 100) return "#ef4444"; + return "#991b1b"; // >100 dark red +} diff --git a/packages/map-core/src/colors/smoothness.ts b/packages/map-core/src/colors/smoothness.ts new file mode 100644 index 0000000..eded893 --- /dev/null +++ b/packages/map-core/src/colors/smoothness.ts @@ -0,0 +1,12 @@ +export const SMOOTHNESS_COLORS: Record = { + excellent: "#22c55e", + good: "#16a34a", + intermediate: "#eab308", + bad: "#f97316", + very_bad: "#ef4444", + horrible: "#991b1b", + very_horrible: "#7f1d1d", + impassable: "#450a0a", +}; + +export const DEFAULT_SMOOTHNESS_COLOR = "#9ca3af"; diff --git a/packages/map-core/src/colors/surface.ts b/packages/map-core/src/colors/surface.ts new file mode 100644 index 0000000..8c8b545 --- /dev/null +++ b/packages/map-core/src/colors/surface.ts @@ -0,0 +1,21 @@ +export const SURFACE_COLORS: Record = { + asphalt: "#6b7280", + concrete: "#9ca3af", + paved: "#6b7280", + paving_stones: "#78716c", + cobblestone: "#a8a29e", + gravel: "#92400e", + compacted: "#b45309", + "fine_gravel": "#d97706", + ground: "#65a30d", + dirt: "#84cc16", + grass: "#22c55e", + sand: "#fbbf24", + mud: "#713f12", + wood: "#a16207", + unpaved: "#ca8a04", + path: "#16a34a", + track: "#ea580c", +}; + +export const DEFAULT_SURFACE_COLOR = "#9ca3af"; diff --git a/packages/map-core/src/colors/tracktype.ts b/packages/map-core/src/colors/tracktype.ts new file mode 100644 index 0000000..ff5ed34 --- /dev/null +++ b/packages/map-core/src/colors/tracktype.ts @@ -0,0 +1,9 @@ +export const TRACKTYPE_COLORS: Record = { + grade1: "#22c55e", + grade2: "#84cc16", + grade3: "#eab308", + grade4: "#f97316", + grade5: "#ef4444", +}; + +export const DEFAULT_TRACKTYPE_COLOR = "#9ca3af"; diff --git a/packages/map-core/src/index.ts b/packages/map-core/src/index.ts new file mode 100644 index 0000000..48939dd --- /dev/null +++ b/packages/map-core/src/index.ts @@ -0,0 +1,23 @@ +export type { TileLayerConfig, OverlayLayerConfig } from "./tiles.ts"; +export { baseLayers, overlayLayers } from "./tiles.ts"; + +export { + SURFACE_COLORS, DEFAULT_SURFACE_COLOR, + HIGHWAY_COLORS, DEFAULT_HIGHWAY_COLOR, + SMOOTHNESS_COLORS, DEFAULT_SMOOTHNESS_COLOR, + TRACKTYPE_COLORS, DEFAULT_TRACKTYPE_COLOR, + CYCLEWAY_COLORS, DEFAULT_CYCLEWAY_COLOR, + BIKEROUTE_COLORS, DEFAULT_BIKEROUTE_COLOR, + elevationColor, routeGradeColor, + maxspeedColor, +} from "./colors/index.ts"; + +export type { PoiCategory } from "./poi.ts"; +export { poiCategories, getCategoriesForProfile, profileOverlayDefaults } from "./poi.ts"; + +export { + Z_CURSOR, Z_GHOST_WAYPOINT, Z_WAYPOINT, + Z_POI_MARKER, Z_WAYPOINT_HIGHLIGHTED, Z_HIGHLIGHT, +} from "./z-index.ts"; + +export { SNAP_DISTANCE_METERS } from "./snap.ts"; diff --git a/apps/planner/app/lib/poi-categories.ts b/packages/map-core/src/poi.ts similarity index 89% rename from apps/planner/app/lib/poi-categories.ts rename to packages/map-core/src/poi.ts index 8d80c6c..26980d2 100644 --- a/apps/planner/app/lib/poi-categories.ts +++ b/packages/map-core/src/poi.ts @@ -11,14 +11,14 @@ export const poiCategories: PoiCategory[] = [ { id: "drinking_water", name: "poi.drinkingWater", - icon: "💧", + icon: "\u{1F4A7}", color: "#2563eb", query: 'nwr["amenity"="drinking_water"];nwr["amenity"="water_point"];', }, { id: "shelter", name: "poi.shelter", - icon: "🛖", + icon: "\u{1F6D6}", color: "#8B6D3A", query: 'nwr["amenity"="shelter"];nwr["tourism"="wilderness_hut"];', profiles: ["trekking"], @@ -26,28 +26,28 @@ export const poiCategories: PoiCategory[] = [ { id: "camping", name: "poi.camping", - icon: "⛺", + icon: "\u26FA", color: "#059669", query: 'nwr["tourism"="camp_site"];nwr["tourism"="caravan_site"];', }, { id: "food", name: "poi.food", - icon: "🍽️", + icon: "\u{1F37D}\uFE0F", color: "#dc2626", query: 'nwr["amenity"="restaurant"];nwr["amenity"="cafe"];nwr["amenity"="fast_food"];nwr["amenity"="pub"];nwr["amenity"="biergarten"];', }, { id: "groceries", name: "poi.groceries", - icon: "🛒", + icon: "\u{1F6D2}", color: "#f97316", query: 'nwr["shop"="supermarket"];nwr["shop"="convenience"];nwr["shop"="bakery"];', }, { id: "bike_infra", name: "poi.bikeInfra", - icon: "🔧", + icon: "\u{1F527}", color: "#8b5cf6", query: 'nwr["amenity"="bicycle_parking"];nwr["amenity"="bicycle_repair_station"];nwr["amenity"="bicycle_rental"];', profiles: ["fastbike", "safety"], @@ -55,14 +55,14 @@ export const poiCategories: PoiCategory[] = [ { id: "accommodation", name: "poi.accommodation", - icon: "🏨", + icon: "\u{1F3E8}", color: "#0891b2", query: 'nwr["tourism"="hotel"];nwr["tourism"="hostel"];nwr["tourism"="guest_house"];', }, { id: "viewpoints", name: "poi.viewpoints", - icon: "👁️", + icon: "\u{1F441}\uFE0F", color: "#9333ea", query: 'nwr["tourism"="viewpoint"];', profiles: ["trekking"], @@ -70,7 +70,7 @@ export const poiCategories: PoiCategory[] = [ { id: "toilets", name: "poi.toilets", - icon: "🚻", + icon: "\u{1F6BB}", color: "#6b7280", query: 'nwr["amenity"="toilets"];', }, @@ -82,7 +82,7 @@ export function getCategoriesForProfile(profile: string): string[] { .map((c) => c.id); } -/** Profile → tile overlay mapping */ +/** Profile -> tile overlay mapping */ export const profileOverlayDefaults: Record = { fastbike: ["waymarked-cycling"], safety: ["waymarked-cycling"], diff --git a/packages/map-core/src/snap.ts b/packages/map-core/src/snap.ts new file mode 100644 index 0000000..63bf4fa --- /dev/null +++ b/packages/map-core/src/snap.ts @@ -0,0 +1 @@ +export const SNAP_DISTANCE_METERS = 50; diff --git a/packages/map-core/src/tiles.ts b/packages/map-core/src/tiles.ts new file mode 100644 index 0000000..dfa04c3 --- /dev/null +++ b/packages/map-core/src/tiles.ts @@ -0,0 +1,66 @@ +export interface TileLayerConfig { + name: string; + url: string; + attribution: string; + maxZoom?: number; +} + +export interface OverlayLayerConfig extends TileLayerConfig { + id: string; + opacity?: number; +} + +export const overlayLayers: OverlayLayerConfig[] = [ + { + id: "hillshading", + name: "Hillshading", + url: "https://tiles.wmflabs.org/hillshading/{z}/{x}/{y}.png", + attribution: "Hillshading: SRTM/Mapzen", + maxZoom: 17, + opacity: 0.5, + }, + { + id: "waymarked-cycling", + name: "Cycling Routes", + url: "https://tile.waymarkedtrails.org/cycling/{z}/{x}/{y}.png", + attribution: '© Waymarked Trails (CC-BY-SA)', + maxZoom: 18, + }, + { + id: "waymarked-hiking", + name: "Hiking Routes", + url: "https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png", + attribution: '© Waymarked Trails (CC-BY-SA)', + maxZoom: 18, + }, + { + id: "waymarked-mtb", + name: "MTB Routes", + url: "https://tile.waymarkedtrails.org/mtb/{z}/{x}/{y}.png", + attribution: '© Waymarked Trails (CC-BY-SA)', + maxZoom: 18, + }, +]; + +export const baseLayers: TileLayerConfig[] = [ + { + name: "OpenStreetMap", + url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + attribution: '© OpenStreetMap', + maxZoom: 19, + }, + { + name: "OpenTopoMap", + url: "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", + attribution: + '© OpenTopoMap (CC-BY-SA)', + maxZoom: 17, + }, + { + name: "CyclOSM", + url: "https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png", + attribution: + '© OpenStreetMap © CyclOSM', + maxZoom: 20, + }, +]; diff --git a/apps/planner/app/lib/z-index.ts b/packages/map-core/src/z-index.ts similarity index 79% rename from apps/planner/app/lib/z-index.ts rename to packages/map-core/src/z-index.ts index 7dd4597..ec1c2b5 100644 --- a/apps/planner/app/lib/z-index.ts +++ b/packages/map-core/src/z-index.ts @@ -3,7 +3,7 @@ * Higher values render on top of lower values. * * Rendering order (bottom to top): - * POI markers → cursor markers → ghost waypoint → waypoint markers → highlight dot + * POI markers -> cursor markers -> ghost waypoint -> waypoint markers -> highlight dot */ export const Z_CURSOR = -1000; export const Z_GHOST_WAYPOINT = -100; diff --git a/packages/map-core/tsconfig.json b/packages/map-core/tsconfig.json new file mode 100644 index 0000000..5a24989 --- /dev/null +++ b/packages/map-core/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/map/package.json b/packages/map/package.json index ad780b9..57a6038 100644 --- a/packages/map/package.json +++ b/packages/map/package.json @@ -7,6 +7,9 @@ }, "main": "./src/index.ts", "types": "./src/index.ts", + "dependencies": { + "@trails-cool/map-core": "workspace:*" + }, "peerDependencies": { "react": ">=18", "react-dom": ">=18", diff --git a/packages/map/src/index.ts b/packages/map/src/index.ts index 8c3d126..7190941 100644 --- a/packages/map/src/index.ts +++ b/packages/map/src/index.ts @@ -2,5 +2,5 @@ export { MapView } from "./MapView.tsx"; export type { MapViewProps } from "./MapView.tsx"; export { RouteLayer } from "./RouteLayer.tsx"; export type { RouteLayerProps } from "./RouteLayer.tsx"; -export { baseLayers, overlayLayers } from "./layers.ts"; -export type { TileLayerConfig, OverlayLayerConfig } from "./layers.ts"; +export { baseLayers, overlayLayers } from "@trails-cool/map-core"; +export type { TileLayerConfig, OverlayLayerConfig } from "@trails-cool/map-core"; diff --git a/packages/map/src/layers.ts b/packages/map/src/layers.ts index dfa04c3..c627c48 100644 --- a/packages/map/src/layers.ts +++ b/packages/map/src/layers.ts @@ -1,66 +1,2 @@ -export interface TileLayerConfig { - name: string; - url: string; - attribution: string; - maxZoom?: number; -} - -export interface OverlayLayerConfig extends TileLayerConfig { - id: string; - opacity?: number; -} - -export const overlayLayers: OverlayLayerConfig[] = [ - { - id: "hillshading", - name: "Hillshading", - url: "https://tiles.wmflabs.org/hillshading/{z}/{x}/{y}.png", - attribution: "Hillshading: SRTM/Mapzen", - maxZoom: 17, - opacity: 0.5, - }, - { - id: "waymarked-cycling", - name: "Cycling Routes", - url: "https://tile.waymarkedtrails.org/cycling/{z}/{x}/{y}.png", - attribution: '© Waymarked Trails (CC-BY-SA)', - maxZoom: 18, - }, - { - id: "waymarked-hiking", - name: "Hiking Routes", - url: "https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png", - attribution: '© Waymarked Trails (CC-BY-SA)', - maxZoom: 18, - }, - { - id: "waymarked-mtb", - name: "MTB Routes", - url: "https://tile.waymarkedtrails.org/mtb/{z}/{x}/{y}.png", - attribution: '© Waymarked Trails (CC-BY-SA)', - maxZoom: 18, - }, -]; - -export const baseLayers: TileLayerConfig[] = [ - { - name: "OpenStreetMap", - url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - attribution: '© OpenStreetMap', - maxZoom: 19, - }, - { - name: "OpenTopoMap", - url: "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", - attribution: - '© OpenTopoMap (CC-BY-SA)', - maxZoom: 17, - }, - { - name: "CyclOSM", - url: "https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png", - attribution: - '© OpenStreetMap © CyclOSM', - maxZoom: 20, - }, -]; +export type { TileLayerConfig, OverlayLayerConfig } from "@trails-cool/map-core"; +export { baseLayers, overlayLayers } from "@trails-cool/map-core"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 08aee70..9f75fd4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -324,6 +324,9 @@ importers: '@trails-cool/map': specifier: workspace:* version: link:../../packages/map + '@trails-cool/map-core': + specifier: workspace:* + version: link:../../packages/map-core '@trails-cool/types': specifier: workspace:* version: link:../../packages/types @@ -436,6 +439,9 @@ importers: packages/map: dependencies: + '@trails-cool/map-core': + specifier: workspace:* + version: link:../map-core leaflet: specifier: '>=1.9' version: 1.9.4 @@ -449,6 +455,8 @@ importers: specifier: '>=5' version: 5.0.0(leaflet@1.9.4)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + packages/map-core: {} + packages/types: {} packages/ui: {} @@ -1593,36 +1601,42 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.0-rc.15': resolution: {integrity: sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15': resolution: {integrity: sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15': resolution: {integrity: sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.0.0-rc.15': resolution: {integrity: sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.0-rc.15': resolution: {integrity: sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@rolldown/binding-openharmony-arm64@1.0.0-rc.15': resolution: {integrity: sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==} @@ -1684,66 +1698,79 @@ packages: resolution: {integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.60.0': resolution: {integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.60.0': resolution: {integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.60.0': resolution: {integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.60.0': resolution: {integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.60.0': resolution: {integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.60.0': resolution: {integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.60.0': resolution: {integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.60.0': resolution: {integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.60.0': resolution: {integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.60.0': resolution: {integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.60.0': resolution: {integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.60.0': resolution: {integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.60.0': resolution: {integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==} @@ -1974,24 +2001,28 @@ packages: engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.2.2': resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.2.2': resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.2.2': resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.2.2': resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} @@ -3110,24 +3141,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}