From c5bdd5631a16f22d4a5202bdb8261ccd24fdc263 Mon Sep 17 00:00:00 2001 From: Anastasiia Ivanchenko Date: Sun, 27 Sep 2026 14:46:48 +0300 Subject: [PATCH 1/8] Remove accidentally committed worktree gitlink --- .worktrees/restore-year-grid | 1 - 1 file changed, 1 deletion(-) delete mode 160000 .worktrees/restore-year-grid diff --git a/.worktrees/restore-year-grid b/.worktrees/restore-year-grid deleted file mode 160000 index 18b7778..0000000 --- a/.worktrees/restore-year-grid +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 18b7778ac9ae4e218f4d46c4e6fd9dfaf59337ed From 55463dad94a25320d1f61a76fc06949067b0cabf Mon Sep 17 00:00:00 2001 From: Anastasiia Ivanchenko Date: Sun, 27 Sep 2026 15:02:13 +0300 Subject: [PATCH 2/8] Render the map alone on a free relief basemap Replace the OpenFreeMap Positron/Dark basemap with a style built from Mapterhorn elevation tiles: hypsometric color-relief, multidirectional hillshade, OpenFreeMap water, borders and labels, and a globe atmosphere. Light and dark palettes follow the theme toggle. For the refactor, /map now renders only the map with a Plan/Sphere switch, a locate button and the theme toggle. Clicks do nothing. EarthMap and the other components stay in the tree, unused. --- src/components/map/EarthMap.tsx | 21 +- src/components/map/MapExperience.tsx | 8 +- src/components/map/ReliefMap.tsx | 182 +++++++++++++++ src/components/map/UserPositionMarker.tsx | 20 ++ src/lib/map/reliefStyle.test.ts | 51 ++++ src/lib/map/reliefStyle.ts | 272 ++++++++++++++++++++++ 6 files changed, 530 insertions(+), 24 deletions(-) create mode 100644 src/components/map/ReliefMap.tsx create mode 100644 src/components/map/UserPositionMarker.tsx create mode 100644 src/lib/map/reliefStyle.test.ts create mode 100644 src/lib/map/reliefStyle.ts diff --git a/src/components/map/EarthMap.tsx b/src/components/map/EarthMap.tsx index fd1ca7b..0e09628 100644 --- a/src/components/map/EarthMap.tsx +++ b/src/components/map/EarthMap.tsx @@ -8,7 +8,6 @@ import { useSyncExternalStore, } from "react"; import Map, { - Marker, type MapMouseEvent, type MapRef, type ViewStateChangeEvent, @@ -17,7 +16,6 @@ import "maplibre-gl/dist/maplibre-gl.css"; import "@/lib/map/initMaplibre"; import { geoPointToZarrGrid } from "@/lib/map/geogrid"; import { - describeAccuracy, describeGeolocationError, readConnectionHint, requestPosition, @@ -73,6 +71,7 @@ import { MapSearch } from "@/components/map/MapSearch"; import { MapTour } from "@/components/map/MapTour"; import { MapReadout } from "@/components/map/MapReadout"; import { GlobeSelectionOverlay } from "@/components/map/GlobeSelectionOverlay"; +import { UserPositionMarker } from "@/components/map/UserPositionMarker"; function toMapViewState( viewState: { @@ -93,24 +92,6 @@ function toMapViewState( }; } -// A Marker rather than a deck layer: it needs no viewport maths and sits -// correctly on both the flat map and the globe. It ignores pointer events, so -// picking the cell underneath still works. -const UserPositionMarker = ({ position }: { position: UserPosition }) => ( - - - -); - const AUTO_ZOOM_STORAGE_KEY = "earthprints:auto_zoom"; const LOCATE_ERROR_VISIBLE_MS = 6000; diff --git a/src/components/map/MapExperience.tsx b/src/components/map/MapExperience.tsx index 67b4617..7ba11c3 100644 --- a/src/components/map/MapExperience.tsx +++ b/src/components/map/MapExperience.tsx @@ -2,12 +2,12 @@ import dynamic from "next/dynamic"; -const EarthMap = dynamic( - () => import("@/components/map/EarthMap").then((module) => module.EarthMap), +const ReliefMap = dynamic( + () => import("@/components/map/ReliefMap").then((module) => module.ReliefMap), { ssr: false, loading: () => ( -
+
Loading map…
), @@ -15,5 +15,5 @@ const EarthMap = dynamic( ); export function MapExperience() { - return ; + return ; } diff --git a/src/components/map/ReliefMap.tsx b/src/components/map/ReliefMap.tsx new file mode 100644 index 0000000..6b25541 --- /dev/null +++ b/src/components/map/ReliefMap.tsx @@ -0,0 +1,182 @@ +"use client"; + +import { useCallback, useEffect, useRef, useState } from "react"; +import Map, { type MapRef } from "react-map-gl/maplibre"; +import type { Map as MapLibreMap, MapLibreEvent } from "maplibre-gl"; +import "maplibre-gl/dist/maplibre-gl.css"; +import "@/lib/map/initMaplibre"; +import { + describeGeolocationError, + requestPosition, + type UserPosition, +} from "@/lib/map/geolocate"; +import { buildReliefStyle } from "@/lib/map/reliefStyle"; +import { + DEFAULT_MAP_VIEW, + VIEW_MODE_TRANSITION_MS, + viewStateForMode, +} from "@/lib/map/viewState"; +import type { MapViewMode } from "@/types/map"; +import { useTheme } from "@/providers/ThemeProvider"; +import { EditorViewTabs } from "@/components/layout/EditorViewTabs"; +import { IconButton } from "@/components/ui/IconButton"; +import { ThemeToggle } from "@/components/ui/ThemeToggle"; +import { LocateIcon } from "@/icons/LocateIcon"; +import { UserPositionMarker } from "@/components/map/UserPositionMarker"; + +const LOCATE_ZOOM = 8; +const LOCATE_FLY_MS = 1000; +const LOCATE_ERROR_VISIBLE_MS = 6000; + +export function ReliefMap() { + const { theme } = useTheme(); + const mapRef = useRef(null); + const [viewMode, setViewMode] = useState("sphere"); + const [userPosition, setUserPosition] = useState(null); + const [locating, setLocating] = useState(false); + const [locateError, setLocateError] = useState(null); + + const isSphere = viewMode === "sphere"; + + // `touchZoomRotate` covers both pinch-zoom and two-finger rotate, so it + // stays enabled and only its rotation half follows the projection. + const applyTouchRotation = useCallback( + (map: MapLibreMap) => { + if (isSphere) { + map.touchZoomRotate.enableRotation(); + } else { + map.touchZoomRotate.disableRotation(); + } + }, + [isSphere], + ); + + useEffect(() => { + const map = mapRef.current?.getMap(); + if (map) applyTouchRotation(map); + }, [applyTouchRotation]); + + const handleMapLoad = useCallback( + (event: MapLibreEvent) => applyTouchRotation(event.target), + [applyTouchRotation], + ); + + const handleViewModeChange = useCallback((mode: MapViewMode) => { + setViewMode(mode); + const map = mapRef.current; + if (!map) return; + const center = map.getCenter(); + const target = viewStateForMode( + { + longitude: center.lng, + latitude: center.lat, + zoom: map.getZoom(), + bearing: map.getBearing(), + pitch: map.getPitch(), + }, + mode, + ); + map.easeTo({ + center: [target.longitude, target.latitude], + zoom: target.zoom, + bearing: target.bearing ?? 0, + pitch: target.pitch ?? 0, + duration: VIEW_MODE_TRANSITION_MS, + }); + }, []); + + const handleLocate = useCallback(async () => { + setLocating(true); + try { + const position = await requestPosition(); + setUserPosition(position); + setLocateError(null); + const map = mapRef.current; + map?.flyTo({ + center: [position.lon, position.lat], + zoom: Math.max(map.getZoom(), LOCATE_ZOOM), + duration: LOCATE_FLY_MS, + }); + } catch (error) { + setLocateError(describeGeolocationError(error)); + } finally { + setLocating(false); + } + }, []); + + useEffect(() => { + if (!locateError) return; + const timer = window.setTimeout( + () => setLocateError(null), + LOCATE_ERROR_VISIBLE_MS, + ); + return () => window.clearTimeout(timer); + }, [locateError]); + + return ( +
+ + {userPosition ? : null} + + +
+
+ +
+ {locateError ? ( +
+ {locateError} + +
+ ) : null} +
+ +
+ + + + +
+
+ ); +} diff --git a/src/components/map/UserPositionMarker.tsx b/src/components/map/UserPositionMarker.tsx new file mode 100644 index 0000000..9b90048 --- /dev/null +++ b/src/components/map/UserPositionMarker.tsx @@ -0,0 +1,20 @@ +import { Marker } from "react-map-gl/maplibre"; +import { describeAccuracy, type UserPosition } from "@/lib/map/geolocate"; + +// A Marker rather than a deck layer: it needs no viewport maths and sits +// correctly on both the flat map and the globe. It ignores pointer events, so +// picking the cell underneath still works. +export const UserPositionMarker = ({ position }: { position: UserPosition }) => ( + + + +); diff --git a/src/lib/map/reliefStyle.test.ts b/src/lib/map/reliefStyle.test.ts new file mode 100644 index 0000000..cd2aab3 --- /dev/null +++ b/src/lib/map/reliefStyle.test.ts @@ -0,0 +1,51 @@ +import { describe, expect, it } from "vitest"; +import { validateStyleMin } from "@maplibre/maplibre-gl-style-spec"; +import { + buildReliefStyle, + DEM_SOURCE_ID, + RELIEF_PALETTES, +} from "@/lib/map/reliefStyle"; + +const THEMES = ["light", "dark"] as const; + +describe("buildReliefStyle", () => { + it.each(THEMES)("produces a valid %s style", (theme) => { + expect(validateStyleMin(buildReliefStyle(theme))).toEqual([]); + }); + + it("reads Mapterhorn as terrarium-encoded elevation", () => { + const dem = buildReliefStyle("light").sources[DEM_SOURCE_ID]; + expect(dem).toMatchObject({ type: "raster-dem", encoding: "terrarium" }); + }); + + it("draws tint and shading from the elevation source", () => { + const layers = buildReliefStyle("light").layers; + const relief = layers.filter( + (layer) => layer.type === "color-relief" || layer.type === "hillshade", + ); + expect(relief.map((layer) => layer.type)).toEqual([ + "color-relief", + "hillshade", + ]); + for (const layer of relief) { + expect("source" in layer && layer.source).toBe(DEM_SOURCE_ID); + } + }); + + it("keeps labels above the relief", () => { + const types = buildReliefStyle("dark").layers.map((layer) => layer.type); + expect(types.lastIndexOf("hillshade")).toBeLessThan(types.indexOf("symbol")); + }); + + it("caches one style per theme", () => { + expect(buildReliefStyle("dark")).toBe(buildReliefStyle("dark")); + expect(buildReliefStyle("dark")).not.toBe(buildReliefStyle("light")); + }); + + it("gives light and dark different palettes", () => { + expect(RELIEF_PALETTES.light.ocean).not.toBe(RELIEF_PALETTES.dark.ocean); + expect(RELIEF_PALETTES.light.elevation).not.toEqual( + RELIEF_PALETTES.dark.elevation, + ); + }); +}); diff --git a/src/lib/map/reliefStyle.ts b/src/lib/map/reliefStyle.ts new file mode 100644 index 0000000..e7e906c --- /dev/null +++ b/src/lib/map/reliefStyle.ts @@ -0,0 +1,272 @@ +import type { + ExpressionSpecification, + SkySpecification, + StyleSpecification, +} from "maplibre-gl"; +import type { Theme } from "@/lib/theme"; +import { SINGLE_LINE_LABEL_TEXT_FIELD } from "@/lib/map/mapLabels"; + +export const DEM_SOURCE_ID = "dem"; +export const VECTOR_SOURCE_ID = "openmaptiles"; + +const MAPTERHORN_TILES = "https://tiles.mapterhorn.com/{z}/{x}/{y}.webp"; +const OPENFREEMAP_TILEJSON = "https://tiles.openfreemap.org/planet"; +const OPENFREEMAP_GLYPHS = + "https://tiles.openfreemap.org/fonts/{fontstack}/{range}.pbf"; +const MAPTERHORN_ATTRIBUTION = + '© Mapterhorn'; + +type ReliefPalette = { + elevation: [number, string][]; + ocean: string; + water: string; + waterOpacity: number; + shadow: string; + highlight: string; + accent: string; + boundary: string; + label: string; + labelHalo: string; + waterLabel: string; + sky: SkySpecification; +}; + +// Hypsometric tints in the Natural Earth tradition: greens low, tans and +// browns through the uplands, pale near the summits so relief reads as light. +export const RELIEF_PALETTES: Record = { + light: { + elevation: [ + [-6000, "#a9c9da"], + [-200, "#c9dfe9"], + [0, "#b3cfa2"], + [150, "#c6d8a6"], + [500, "#dcdcae"], + [1000, "#e4d2a2"], + [1800, "#d7b78c"], + [2800, "#c4a283"], + [4000, "#cdbcb0"], + [5500, "#ece6e1"], + [7000, "#fbfaf8"], + ], + ocean: "#c4dce8", + water: "#bcd7e5", + waterOpacity: 0.82, + shadow: "rgba(72, 58, 44, 0.55)", + highlight: "rgba(255, 255, 255, 0.55)", + accent: "rgba(96, 80, 62, 0.35)", + boundary: "rgba(80, 70, 60, 0.35)", + label: "#3b3833", + labelHalo: "rgba(255, 255, 255, 0.85)", + waterLabel: "#5d7f93", + sky: { + "sky-color": "#dfeaf1", + "horizon-color": "#ffffff", + "fog-color": "#ffffff", + "sky-horizon-blend": 0.6, + "horizon-fog-blend": 0.7, + "fog-ground-blend": 0.4, + "atmosphere-blend": ["interpolate", ["linear"], ["zoom"], 0, 1, 5, 1, 7, 0], + }, + }, + dark: { + elevation: [ + [-6000, "#07111c"], + [-200, "#0d1b2a"], + [0, "#1f2b25"], + [150, "#25322a"], + [500, "#2d372c"], + [1000, "#37392d"], + [1800, "#423d32"], + [2800, "#4d463d"], + [4000, "#5e5953"], + [5500, "#7d7a76"], + [7000, "#a19f9c"], + ], + ocean: "#0a1622", + water: "#0b1826", + waterOpacity: 0.85, + shadow: "rgba(0, 0, 0, 0.75)", + highlight: "rgba(170, 196, 222, 0.22)", + accent: "rgba(0, 0, 0, 0.4)", + boundary: "rgba(210, 220, 230, 0.22)", + label: "#ffffff", + labelHalo: "rgba(0, 0, 0, 0.92)", + waterLabel: "#7f9bb3", + sky: { + "sky-color": "#03070d", + "horizon-color": "#1b3a5e", + "fog-color": "#0a1622", + "sky-horizon-blend": 0.5, + "horizon-fog-blend": 0.6, + "fog-ground-blend": 0.4, + "atmosphere-blend": ["interpolate", ["linear"], ["zoom"], 0, 1, 5, 1, 7, 0], + }, + }, +}; + +function elevationRamp(stops: [number, string][]): ExpressionSpecification { + return [ + "interpolate", + ["linear"], + ["elevation"], + ...stops.flat(), + ] as ExpressionSpecification; +} + +const LABEL_FONT = ["Noto Sans Regular"]; +const LABEL_FONT_BOLD = ["Noto Sans Bold"]; + +function buildStyle(theme: Theme): StyleSpecification { + const palette = RELIEF_PALETTES[theme]; + const labelPaint = { + "text-color": palette.label, + "text-halo-color": palette.labelHalo, + "text-halo-width": 1.4, + }; + + return { + version: 8, + sky: palette.sky, + glyphs: OPENFREEMAP_GLYPHS, + sources: { + [DEM_SOURCE_ID]: { + type: "raster-dem", + tiles: [MAPTERHORN_TILES], + encoding: "terrarium", + tileSize: 512, + attribution: MAPTERHORN_ATTRIBUTION, + }, + [VECTOR_SOURCE_ID]: { + type: "vector", + url: OPENFREEMAP_TILEJSON, + }, + }, + layers: [ + { + id: "ocean", + type: "background", + paint: { "background-color": palette.ocean }, + }, + { + id: "relief-tint", + type: "color-relief", + source: DEM_SOURCE_ID, + paint: { "color-relief-color": elevationRamp(palette.elevation) }, + }, + { + id: "relief-shade", + type: "hillshade", + source: DEM_SOURCE_ID, + paint: { + "hillshade-method": "multidirectional", + // Four lights around the north-west keep the classic cartographic + // lighting while filling in slopes a single sun would flatten. + "hillshade-illumination-direction": [270, 315, 338, 0], + "hillshade-illumination-altitude": [35, 45, 35, 60], + "hillshade-shadow-color": [ + palette.shadow, + palette.shadow, + palette.shadow, + palette.shadow, + ], + "hillshade-highlight-color": [ + palette.highlight, + palette.highlight, + palette.highlight, + palette.highlight, + ], + "hillshade-accent-color": palette.accent, + }, + }, + { + id: "water", + type: "fill", + source: VECTOR_SOURCE_ID, + "source-layer": "water", + filter: ["!=", ["get", "brunnel"], "tunnel"], + paint: { + "fill-color": palette.water, + "fill-opacity": palette.waterOpacity, + }, + }, + { + id: "boundary-country", + type: "line", + source: VECTOR_SOURCE_ID, + "source-layer": "boundary", + filter: [ + "all", + ["==", ["get", "admin_level"], 2], + ["!=", ["get", "maritime"], 1], + ], + layout: { "line-cap": "round", "line-join": "round" }, + paint: { + "line-color": palette.boundary, + "line-width": ["interpolate", ["linear"], ["zoom"], 1, 0.5, 8, 1.4], + }, + }, + { + id: "label-water", + type: "symbol", + source: VECTOR_SOURCE_ID, + "source-layer": "water_name", + filter: ["match", ["geometry-type"], ["MultiPoint", "Point"], true, false], + layout: { + "text-field": SINGLE_LINE_LABEL_TEXT_FIELD, + "text-font": ["Noto Sans Italic"], + "text-size": ["interpolate", ["linear"], ["zoom"], 0, 10, 8, 14], + "text-letter-spacing": 0.2, + "text-max-width": 5, + }, + paint: { ...labelPaint, "text-color": palette.waterLabel }, + }, + { + id: "label-city", + type: "symbol", + source: VECTOR_SOURCE_ID, + "source-layer": "place", + minzoom: 4, + filter: ["==", ["get", "class"], "city"], + layout: { + "text-field": SINGLE_LINE_LABEL_TEXT_FIELD, + "text-font": LABEL_FONT, + "text-size": ["interpolate", ["exponential", 1.2], ["zoom"], 4, 11, 7, 13, 11, 18], + "text-max-width": 8, + }, + paint: labelPaint, + }, + { + id: "label-country", + type: "symbol", + source: VECTOR_SOURCE_ID, + "source-layer": "place", + maxzoom: 9, + filter: [ + "all", + ["==", ["get", "class"], "country"], + ["<=", ["get", "rank"], 2], + ], + layout: { + "text-field": SINGLE_LINE_LABEL_TEXT_FIELD, + "text-font": LABEL_FONT_BOLD, + "text-size": ["interpolate", ["linear"], ["zoom"], 1, 9, 5, 16], + "text-max-width": 6.25, + "text-transform": "uppercase", + "text-letter-spacing": 0.08, + }, + paint: labelPaint, + }, + ], + }; +} + +const styleCache = new Map(); + +export function buildReliefStyle(theme: Theme): StyleSpecification { + let style = styleCache.get(theme); + if (!style) { + style = buildStyle(theme); + styleCache.set(theme, style); + } + return style; +} From 6ee15dc8bf7cd7155b05f3ead55bbb4518edfbf6 Mon Sep 17 00:00:00 2001 From: Anastasiia Ivanchenko Date: Sun, 27 Sep 2026 15:40:23 +0300 Subject: [PATCH 3/8] Enhance ReliefMap with smooth zoom and drag pan adjustments --- src/components/map/ReliefMap.tsx | 24 +++++- src/components/map/UserPositionMarker.tsx | 4 +- src/lib/map/initMaplibre.ts | 5 +- src/lib/map/reliefStyle.test.ts | 51 ------------- src/lib/map/reliefStyle.ts | 57 ++++++++------ src/lib/map/smoothWheelZoom.ts | 92 +++++++++++++++++++++++ 6 files changed, 152 insertions(+), 81 deletions(-) delete mode 100644 src/lib/map/reliefStyle.test.ts create mode 100644 src/lib/map/smoothWheelZoom.ts diff --git a/src/components/map/ReliefMap.tsx b/src/components/map/ReliefMap.tsx index 6b25541..95820a5 100644 --- a/src/components/map/ReliefMap.tsx +++ b/src/components/map/ReliefMap.tsx @@ -11,6 +11,7 @@ import { type UserPosition, } from "@/lib/map/geolocate"; import { buildReliefStyle } from "@/lib/map/reliefStyle"; +import { attachSmoothWheelZoom } from "@/lib/map/smoothWheelZoom"; import { DEFAULT_MAP_VIEW, VIEW_MODE_TRANSITION_MS, @@ -28,9 +29,19 @@ const LOCATE_ZOOM = 8; const LOCATE_FLY_MS = 1000; const LOCATE_ERROR_VISIBLE_MS = 6000; +// A longer, softer glide after release than MapLibre's defaults +// (linearity 0.3, deceleration 2500, maxSpeed 1400). +const DRAG_PAN = { + linearity: 0.4, + deceleration: 1600, + maxSpeed: 1800, + easing: (t: number) => 1 - (1 - t) ** 3, +}; + export function ReliefMap() { const { theme } = useTheme(); const mapRef = useRef(null); + const detachWheelZoomRef = useRef<(() => void) | null>(null); const [viewMode, setViewMode] = useState("sphere"); const [userPosition, setUserPosition] = useState(null); const [locating, setLocating] = useState(false); @@ -57,10 +68,16 @@ export function ReliefMap() { }, [applyTouchRotation]); const handleMapLoad = useCallback( - (event: MapLibreEvent) => applyTouchRotation(event.target), + (event: MapLibreEvent) => { + applyTouchRotation(event.target); + detachWheelZoomRef.current?.(); + detachWheelZoomRef.current = attachSmoothWheelZoom(event.target); + }, [applyTouchRotation], ); + useEffect(() => () => detachWheelZoomRef.current?.(), []); + const handleViewModeChange = useCallback((mode: MapViewMode) => { setViewMode(mode); const map = mapRef.current; @@ -122,6 +139,11 @@ export function ReliefMap() { initialViewState={DEFAULT_MAP_VIEW} minZoom={0} maxZoom={18} + // Let tiles for the levels passed on the way in keep loading, so + // detail fills in during the zoom instead of all at once after it. + cancelPendingTileRequestsWhileZooming={false} + scrollZoom={false} + dragPan={DRAG_PAN} dragRotate={isSphere} pitchWithRotate={isSphere} touchZoomRotate diff --git a/src/components/map/UserPositionMarker.tsx b/src/components/map/UserPositionMarker.tsx index 9b90048..ee3de85 100644 --- a/src/components/map/UserPositionMarker.tsx +++ b/src/components/map/UserPositionMarker.tsx @@ -1,9 +1,7 @@ import { Marker } from "react-map-gl/maplibre"; import { describeAccuracy, type UserPosition } from "@/lib/map/geolocate"; -// A Marker rather than a deck layer: it needs no viewport maths and sits -// correctly on both the flat map and the globe. It ignores pointer events, so -// picking the cell underneath still works. + export const UserPositionMarker = ({ position }: { position: UserPosition }) => ( { - it.each(THEMES)("produces a valid %s style", (theme) => { - expect(validateStyleMin(buildReliefStyle(theme))).toEqual([]); - }); - - it("reads Mapterhorn as terrarium-encoded elevation", () => { - const dem = buildReliefStyle("light").sources[DEM_SOURCE_ID]; - expect(dem).toMatchObject({ type: "raster-dem", encoding: "terrarium" }); - }); - - it("draws tint and shading from the elevation source", () => { - const layers = buildReliefStyle("light").layers; - const relief = layers.filter( - (layer) => layer.type === "color-relief" || layer.type === "hillshade", - ); - expect(relief.map((layer) => layer.type)).toEqual([ - "color-relief", - "hillshade", - ]); - for (const layer of relief) { - expect("source" in layer && layer.source).toBe(DEM_SOURCE_ID); - } - }); - - it("keeps labels above the relief", () => { - const types = buildReliefStyle("dark").layers.map((layer) => layer.type); - expect(types.lastIndexOf("hillshade")).toBeLessThan(types.indexOf("symbol")); - }); - - it("caches one style per theme", () => { - expect(buildReliefStyle("dark")).toBe(buildReliefStyle("dark")); - expect(buildReliefStyle("dark")).not.toBe(buildReliefStyle("light")); - }); - - it("gives light and dark different palettes", () => { - expect(RELIEF_PALETTES.light.ocean).not.toBe(RELIEF_PALETTES.dark.ocean); - expect(RELIEF_PALETTES.light.elevation).not.toEqual( - RELIEF_PALETTES.dark.elevation, - ); - }); -}); diff --git a/src/lib/map/reliefStyle.ts b/src/lib/map/reliefStyle.ts index e7e906c..f9fdf48 100644 --- a/src/lib/map/reliefStyle.ts +++ b/src/lib/map/reliefStyle.ts @@ -10,6 +10,10 @@ export const DEM_SOURCE_ID = "dem"; export const VECTOR_SOURCE_ID = "openmaptiles"; const MAPTERHORN_TILES = "https://tiles.mapterhorn.com/{z}/{x}/{y}.webp"; +// Mapterhorn is global only up to z12; above that it has tiles for some +// regions and 404s elsewhere, which MapLibre draws as holes. Capping the +// source makes it overzoom z12 everywhere instead. +export const MAPTERHORN_MAX_ZOOM = 12; const OPENFREEMAP_TILEJSON = "https://tiles.openfreemap.org/planet"; const OPENFREEMAP_GLYPHS = "https://tiles.openfreemap.org/fonts/{fontstack}/{range}.pbf"; @@ -24,6 +28,7 @@ type ReliefPalette = { shadow: string; highlight: string; accent: string; + exaggeration: number | ExpressionSpecification; boundary: string; label: string; labelHalo: string; @@ -31,37 +36,36 @@ type ReliefPalette = { sky: SkySpecification; }; -// Hypsometric tints in the Natural Earth tradition: greens low, tans and -// browns through the uplands, pale near the summits so relief reads as light. +// Light is a monochrome "clay" globe: near-white land over a cool gray +// ocean, with the terrain carried by blue-gray shading rather than hue. +// Dark keeps Natural Earth style hypsometric tints in night tones. export const RELIEF_PALETTES: Record = { light: { elevation: [ - [-6000, "#a9c9da"], - [-200, "#c9dfe9"], - [0, "#b3cfa2"], - [150, "#c6d8a6"], - [500, "#dcdcae"], - [1000, "#e4d2a2"], - [1800, "#d7b78c"], - [2800, "#c4a283"], - [4000, "#cdbcb0"], - [5500, "#ece6e1"], - [7000, "#fbfaf8"], + [-6000, "#c3c7d4"], + [-200, "#d3d6e0"], + [0, "#eceef3"], + [500, "#f0f1f5"], + [1500, "#f4f5f8"], + [3000, "#f8f9fb"], + [5000, "#ffffff"], ], - ocean: "#c4dce8", - water: "#bcd7e5", - waterOpacity: 0.82, - shadow: "rgba(72, 58, 44, 0.55)", - highlight: "rgba(255, 255, 255, 0.55)", - accent: "rgba(96, 80, 62, 0.35)", - boundary: "rgba(80, 70, 60, 0.35)", - label: "#3b3833", - labelHalo: "rgba(255, 255, 255, 0.85)", - waterLabel: "#5d7f93", + ocean: "#d5d8e2", + water: "#d1d5df", + waterOpacity: 0.92, + shadow: "rgba(84, 92, 124, 0.55)", + highlight: "rgba(255, 255, 255, 0.9)", + accent: "rgba(84, 92, 124, 0.28)", + // Full strength at globe scale, where slopes are only a few pixels wide. + exaggeration: ["interpolate", ["linear"], ["zoom"], 0, 1, 6, 0.7], + boundary: "rgba(92, 98, 122, 0.3)", + label: "#4a4f5e", + labelHalo: "rgba(246, 247, 250, 0.9)", + waterLabel: "#7d8399", sky: { - "sky-color": "#dfeaf1", + "sky-color": "#e9ebf1", "horizon-color": "#ffffff", - "fog-color": "#ffffff", + "fog-color": "#f3f4f7", "sky-horizon-blend": 0.6, "horizon-fog-blend": 0.7, "fog-ground-blend": 0.4, @@ -88,6 +92,7 @@ export const RELIEF_PALETTES: Record = { shadow: "rgba(0, 0, 0, 0.75)", highlight: "rgba(170, 196, 222, 0.22)", accent: "rgba(0, 0, 0, 0.4)", + exaggeration: 0.5, boundary: "rgba(210, 220, 230, 0.22)", label: "#ffffff", labelHalo: "rgba(0, 0, 0, 0.92)", @@ -134,6 +139,7 @@ function buildStyle(theme: Theme): StyleSpecification { tiles: [MAPTERHORN_TILES], encoding: "terrarium", tileSize: 512, + maxzoom: MAPTERHORN_MAX_ZOOM, attribution: MAPTERHORN_ATTRIBUTION, }, [VECTOR_SOURCE_ID]: { @@ -176,6 +182,7 @@ function buildStyle(theme: Theme): StyleSpecification { palette.highlight, ], "hillshade-accent-color": palette.accent, + "hillshade-exaggeration": palette.exaggeration, }, }, { diff --git a/src/lib/map/smoothWheelZoom.ts b/src/lib/map/smoothWheelZoom.ts new file mode 100644 index 0000000..6951312 --- /dev/null +++ b/src/lib/map/smoothWheelZoom.ts @@ -0,0 +1,92 @@ +import type { LngLat, Map as MapLibreMap, MapLibreEvent } from "maplibre-gl"; + +// Wheel pixels per zoom level. A mouse notch (~100px) moves half a level. +const PIXELS_PER_ZOOM_LEVEL = 200; +const PIXELS_PER_LINE = 33; +// Trackpad pinch arrives as ctrl+wheel with much smaller deltas. +const PINCH_MULTIPLIER = 8; +// Time constant of the glide toward the target zoom. +const GLIDE_TAU_MS = 90; +const SETTLE_EPSILON = 0.002; +const OWN_EVENT = { smoothWheelZoom: true }; + +type WheelLike = Pick; + +export function wheelZoomDelta(event: WheelLike, pageHeight: number): number { + let pixels = event.deltaY; + if (event.deltaMode === 1) pixels *= PIXELS_PER_LINE; + else if (event.deltaMode === 2) pixels *= pageHeight; + if (event.ctrlKey) pixels *= PINCH_MULTIPLIER; + return -pixels / PIXELS_PER_ZOOM_LEVEL; +} + +export function glideStep(current: number, target: number, dtMs: number) { + return current + (target - current) * (1 - Math.exp(-dtMs / GLIDE_TAU_MS)); +} + +/** + * Replaces MapLibre's scroll zoom, which applies trackpad-classified wheel + * events instantly and so steps on many mice. Every wheel event moves a + * target zoom, and the camera glides to it each frame around the cursor. + */ +export function attachSmoothWheelZoom(map: MapLibreMap): () => void { + map.scrollZoom.disable(); + const container = map.getCanvasContainer(); + let target: number | null = null; + let around: LngLat | null = null; + let frame = 0; + let lastTime = 0; + + const stop = () => { + cancelAnimationFrame(frame); + frame = 0; + target = null; + }; + + const tick = (time: number) => { + if (target === null || around === null) return stop(); + const current = map.getZoom(); + const done = Math.abs(target - current) < SETTLE_EPSILON; + const zoom = done ? target : glideStep(current, target, time - lastTime); + lastTime = time; + map.easeTo({ zoom, around, duration: 0 }, OWN_EVENT); + if (done) return stop(); + frame = requestAnimationFrame(tick); + }; + + const onWheel = (event: WheelEvent) => { + event.preventDefault(); + const rect = container.getBoundingClientRect(); + around = map.unproject([ + event.clientX - rect.left, + event.clientY - rect.top, + ]); + const base = target ?? map.getZoom(); + target = Math.min( + map.getMaxZoom(), + Math.max( + map.getMinZoom(), + base + wheelZoomDelta(event, window.innerHeight), + ), + ); + if (!frame) { + lastTime = performance.now(); + frame = requestAnimationFrame(tick); + } + }; + + // Any other camera move (drag, locate, view switch) takes over. + const onMoveStart = ( + event: MapLibreEvent & { smoothWheelZoom?: boolean }, + ) => { + if (!event.smoothWheelZoom) stop(); + }; + + container.addEventListener("wheel", onWheel, { passive: false }); + map.on("movestart", onMoveStart); + return () => { + stop(); + container.removeEventListener("wheel", onWheel); + map.off("movestart", onMoveStart); + }; +} From 745660cb6a8a31f348d48c70320e9e08c9fb064b Mon Sep 17 00:00:00 2001 From: Anastasiia Ivanchenko Date: Sun, 27 Sep 2026 20:01:39 +0300 Subject: [PATCH 4/8] Add pmtiles and playwright dependencies; enhance map initialization --- package-lock.json | 40 +++++ package.json | 5 +- scripts/relief-tiles/build.mjs | 150 +++++++++++++++++ scripts/relief-tiles/pmtiles.mjs | 197 +++++++++++++++++++++++ src/app/relief-render/ReliefRenderer.tsx | 79 +++++++++ src/app/relief-render/page.tsx | 8 + src/lib/map/initMaplibre.ts | 8 +- src/lib/map/reliefStyle.ts | 97 ++++++++++- 8 files changed, 576 insertions(+), 8 deletions(-) create mode 100644 scripts/relief-tiles/build.mjs create mode 100644 scripts/relief-tiles/pmtiles.mjs create mode 100644 src/app/relief-render/ReliefRenderer.tsx create mode 100644 src/app/relief-render/page.tsx diff --git a/package-lock.json b/package-lock.json index 540278a..ee08548 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "jspdf": "^4.2.1", "maplibre-gl": "^6.10.0", "next": "16.3.5", + "pmtiles": "^4.5.0", "react": "19.3.0", "react-dom": "19.3.0", "react-joyride": "^3.2.0", @@ -29,6 +30,7 @@ "@types/react-dom": "^19", "eslint": "^9.39.5", "eslint-config-next": "16.3.5", + "playwright": "^1.63.0", "tailwindcss": "^4", "typescript": "^6", "vitest": "^5.0.1" @@ -7222,6 +7224,44 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", + "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz", + "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/pmtiles": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/pmtiles/-/pmtiles-4.5.0.tgz", + "integrity": "sha512-CBeD4SoUluFziGdy/8k7FOjQxQy486n+929W/tWophauvMICpkZ26vGBWhDt/6b1FZQWNaf4jFdT/5+q0Wii5w==", + "license": "BSD-3-Clause", + "dependencies": { + "fflate": "^0.8.2" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", diff --git a/package.json b/package.json index f528add..925155f 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "lint": "eslint", "test": "vitest run", "test:watch": "vitest", - "postinstall": "cp node_modules/maplibre-gl/dist/maplibre-gl-worker.mjs node_modules/maplibre-gl/dist/maplibre-gl-shared.mjs public/" + "postinstall": "cp node_modules/maplibre-gl/dist/maplibre-gl-worker.mjs node_modules/maplibre-gl/dist/maplibre-gl-shared.mjs public/", + "relief-tiles": "node scripts/relief-tiles/build.mjs" }, "dependencies": { "@deck.gl/core": "^9.4.0", @@ -17,6 +18,7 @@ "jspdf": "^4.2.1", "maplibre-gl": "^6.10.0", "next": "16.3.5", + "pmtiles": "^4.5.0", "react": "19.3.0", "react-dom": "19.3.0", "react-joyride": "^3.2.0", @@ -32,6 +34,7 @@ "@types/react-dom": "^19", "eslint": "^9.39.5", "eslint-config-next": "16.3.5", + "playwright": "^1.63.0", "tailwindcss": "^4", "typescript": "^6", "vitest": "^5.0.1" diff --git a/scripts/relief-tiles/build.mjs b/scripts/relief-tiles/build.mjs new file mode 100644 index 0000000..9695059 --- /dev/null +++ b/scripts/relief-tiles/build.mjs @@ -0,0 +1,150 @@ +// Pre-renders the relief (ocean, tint, hillshade) into raster tiles and packs +// them as one PMTiles archive per theme. +// +// npm run dev # the renderer page runs on the dev server +// node scripts/relief-tiles/build.mjs [--themes light,dark] [--maxzoom 6] +// +// Rendered tiles are cached in out/relief-tiles//, so an interrupted +// run resumes where it stopped. Archives land in out/relief-tiles/ as +// relief--v.pmtiles, ready to upload. +import { mkdir, readFile, stat, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { parseArgs } from "node:util"; +import { chromium } from "playwright"; +import { TILE_TYPE_WEBP, writePmtiles } from "./pmtiles.mjs"; + +const { values: args } = parseArgs({ + options: { + themes: { type: "string", default: "light,dark" }, + maxzoom: { type: "string", default: "6" }, + tabs: { type: "string", default: "4" }, + quality: { type: "string", default: "0.9" }, + server: { type: "string", default: "http://localhost:3000" }, + out: { type: "string", default: "out/relief-tiles" }, + }, +}); + +// Archive names carry the version the map requests, read from the style so +// the two cannot drift apart. +const RELIEF_TILES_VERSION = ( + await readFile("src/lib/map/reliefStyle.ts", "utf8") +).match(/RELIEF_TILES_VERSION = (\d+)/)?.[1]; +if (!RELIEF_TILES_VERSION) throw new Error("RELIEF_TILES_VERSION not found"); + +const MAX_ZOOM = Number(args.maxzoom); +const TABS = Number(args.tabs); +const TILE_PX = 512; +// A tile request occasionally never settles, which would stall its tab. +const TILE_TIMEOUT_MS = 30_000; +const TILE_ATTEMPTS = 3; + +function* pyramid(maxZoom) { + for (let z = 0; z <= maxZoom; z++) { + for (let x = 0; x < 2 ** z; x++) { + for (let y = 0; y < 2 ** z; y++) yield { z, x, y }; + } + } +} + +const exists = (path) => + stat(path).then( + () => true, + () => false, + ); + +async function renderTheme(browser, theme) { + const dir = join(args.out, theme); + const tiles = [...pyramid(MAX_ZOOM)]; + const pending = []; + for (const tile of tiles) { + tile.path = join(dir, `${tile.z}/${tile.x}/${tile.y}.webp`); + if (!(await exists(tile.path))) pending.push(tile); + } + console.log(`${theme}: ${tiles.length} tiles, ${pending.length} to render`); + + let done = 0; + const started = Date.now(); + const worker = async () => { + const page = await browser.newPage({ + viewport: { width: TILE_PX, height: TILE_PX }, + }); + const open = async () => { + await page.goto( + `${args.server}/relief-render?theme=${theme}&quality=${args.quality}`, + ); + await page.waitForFunction(() => window.renderReliefTile, null, { + timeout: 120_000, + }); + }; + const render = async (tile) => { + for (let attempt = 1; ; attempt++) { + let timer; + const timeout = new Promise((_, reject) => { + timer = setTimeout( + () => reject(new Error("timed out")), + TILE_TIMEOUT_MS, + ); + }); + try { + return await Promise.race([ + page.evaluate( + ({ z, x, y }) => window.renderReliefTile(z, x, y), + tile, + ), + timeout, + ]); + } catch (error) { + if (attempt === TILE_ATTEMPTS) throw error; + console.warn(` retrying ${tile.z}/${tile.x}/${tile.y}: ${error}`); + await open(); + } finally { + clearTimeout(timer); + } + } + }; + await open(); + for (let tile = pending.shift(); tile; tile = pending.shift()) { + const dataUrl = await render(tile); + await mkdir(dirname(tile.path), { recursive: true }); + await writeFile(tile.path, Buffer.from(dataUrl.split(",")[1], "base64")); + if (++done % 250 === 0) { + const rate = done / ((Date.now() - started) / 1000); + console.log(` ${done} rendered, ${rate.toFixed(1)} tiles/s`); + } + } + await page.close(); + }; + await Promise.all(Array.from({ length: TABS }, worker)); + + const archive = writePmtiles( + await Promise.all( + tiles.map(async (tile) => ({ ...tile, data: await readFile(tile.path) })), + ), + { + tileType: TILE_TYPE_WEBP, + metadata: { + name: `EarthPrints relief (${theme})`, + attribution: "© Mapterhorn", + tileSize: TILE_PX, + }, + }, + ); + const archivePath = join( + args.out, + `relief-${theme}-v${RELIEF_TILES_VERSION}.pmtiles`, + ); + await writeFile(archivePath, archive); + console.log( + `${theme}: wrote ${archivePath} (${(archive.length / 1e6).toFixed(1)} MB)`, + ); +} + +const browser = await chromium.launch({ + channel: "chrome", + args: ["--use-angle=metal", "--enable-gpu", "--ignore-gpu-blocklist"], +}); +try { + for (const theme of args.themes.split(",")) await renderTheme(browser, theme); +} finally { + await browser.close(); +} diff --git a/scripts/relief-tiles/pmtiles.mjs b/scripts/relief-tiles/pmtiles.mjs new file mode 100644 index 0000000..53cd112 --- /dev/null +++ b/scripts/relief-tiles/pmtiles.mjs @@ -0,0 +1,197 @@ +// Minimal PMTiles v3 writer: https://github.com/protomaps/PMTiles/blob/main/spec/v3/spec.md +import { createHash } from "node:crypto"; +import { gzipSync } from "node:zlib"; + +const HEADER_BYTES = 127; +const ROOT_DIRECTORY_MAX_BYTES = 16384 - HEADER_BYTES; +const LEAF_ENTRIES = 4096; +const COMPRESSION_NONE = 1; +const COMPRESSION_GZIP = 2; +export const TILE_TYPE_WEBP = 4; + +export function zxyToTileId(z, x, y) { + let base = 0; + for (let level = 0; level < z; level++) base += 4 ** level; + let d = 0; + const n = 2 ** z; + let tx = x; + let ty = y; + for (let s = n / 2; s >= 1; s /= 2) { + const rx = (tx & s) > 0 ? 1 : 0; + const ry = (ty & s) > 0 ? 1 : 0; + d += s * s * ((3 * rx) ^ ry); + if (ry === 0) { + if (rx === 1) { + tx = s - 1 - tx; + ty = s - 1 - ty; + } + [tx, ty] = [ty, tx]; + } + } + return base + d; +} + +function varints(values) { + const bytes = []; + for (let value of values) { + while (value >= 0x80) { + bytes.push((value % 0x80) | 0x80); + value = Math.floor(value / 0x80); + } + bytes.push(value); + } + return Buffer.from(bytes); +} + +function serializeDirectory(entries) { + const values = [entries.length]; + let lastId = 0; + for (const entry of entries) { + values.push(entry.tileId - lastId); + lastId = entry.tileId; + } + for (const entry of entries) values.push(entry.runLength); + for (const entry of entries) values.push(entry.length); + entries.forEach((entry, i) => { + const previous = entries[i - 1]; + const contiguous = + previous && entry.offset === previous.offset + previous.length; + values.push(contiguous ? 0 : entry.offset + 1); + }); + return gzipSync(varints(values)); +} + +function buildDirectories(entries) { + const root = serializeDirectory(entries); + if (root.length <= ROOT_DIRECTORY_MAX_BYTES) { + return { root, leaves: Buffer.alloc(0) }; + } + const rootEntries = []; + const leafChunks = []; + let leafOffset = 0; + for (let i = 0; i < entries.length; i += LEAF_ENTRIES) { + const leaf = serializeDirectory(entries.slice(i, i + LEAF_ENTRIES)); + rootEntries.push({ + tileId: entries[i].tileId, + offset: leafOffset, + length: leaf.length, + runLength: 0, + }); + leafChunks.push(leaf); + leafOffset += leaf.length; + } + return { + root: serializeDirectory(rootEntries), + leaves: Buffer.concat(leafChunks), + }; +} + +function header(fields) { + const buf = Buffer.alloc(HEADER_BYTES); + buf.write("PMTiles", 0, "ascii"); + buf.writeUInt8(3, 7); + const u64 = [ + fields.rootOffset, + fields.rootLength, + fields.metadataOffset, + fields.metadataLength, + fields.leavesOffset, + fields.leavesLength, + fields.dataOffset, + fields.dataLength, + fields.addressedTiles, + fields.tileEntries, + fields.tileContents, + ]; + u64.forEach((value, i) => buf.writeBigUInt64LE(BigInt(value), 8 + i * 8)); + let at = 96; + for (const byte of [ + 1, + COMPRESSION_GZIP, + COMPRESSION_NONE, + fields.tileType, + fields.minZoom, + fields.maxZoom, + ]) { + buf.writeUInt8(byte, at++); + } + for (const degrees of [-180, -85.0511287, 180, 85.0511287]) { + buf.writeInt32LE(Math.round(degrees * 1e7), at); + at += 4; + } + buf.writeUInt8(0, at++); + buf.writeInt32LE(0, at); + buf.writeInt32LE(0, at + 4); + return buf; +} + +/** + * tiles: [{ z, x, y, data: Buffer }]. Identical tiles (open ocean) are + * stored once, and consecutive identical tiles collapse into one entry. + */ +export function writePmtiles(tiles, { tileType, metadata }) { + const sorted = tiles + .map((tile) => ({ ...tile, tileId: zxyToTileId(tile.z, tile.x, tile.y) })) + .sort((a, b) => a.tileId - b.tileId); + + const chunks = []; + const offsetsByHash = new Map(); + const entries = []; + let dataLength = 0; + for (const tile of sorted) { + const hash = createHash("sha256").update(tile.data).digest("hex"); + let offset = offsetsByHash.get(hash); + if (offset === undefined) { + offset = dataLength; + offsetsByHash.set(hash, offset); + chunks.push(tile.data); + dataLength += tile.data.length; + } + const last = entries.at(-1); + if ( + last && + last.offset === offset && + last.tileId + last.runLength === tile.tileId + ) { + last.runLength++; + } else { + entries.push({ + tileId: tile.tileId, + offset, + length: tile.data.length, + runLength: 1, + }); + } + } + + const { root, leaves } = buildDirectories(entries); + const meta = gzipSync(Buffer.from(JSON.stringify(metadata))); + const zooms = sorted.map((tile) => tile.z); + const rootOffset = HEADER_BYTES; + const metadataOffset = rootOffset + root.length; + const leavesOffset = metadataOffset + meta.length; + const dataOffset = leavesOffset + leaves.length; + + return Buffer.concat([ + header({ + rootOffset, + rootLength: root.length, + metadataOffset, + metadataLength: meta.length, + leavesOffset, + leavesLength: leaves.length, + dataOffset, + dataLength, + addressedTiles: sorted.length, + tileEntries: entries.length, + tileContents: chunks.length, + tileType, + minZoom: Math.min(...zooms), + maxZoom: Math.max(...zooms), + }), + root, + meta, + leaves, + ...chunks, + ]); +} diff --git a/src/app/relief-render/ReliefRenderer.tsx b/src/app/relief-render/ReliefRenderer.tsx new file mode 100644 index 0000000..b362054 --- /dev/null +++ b/src/app/relief-render/ReliefRenderer.tsx @@ -0,0 +1,79 @@ +"use client"; + +import { useEffect, useRef } from "react"; +import { Map as MapLibreMap } from "maplibre-gl"; +import "maplibre-gl/dist/maplibre-gl.css"; +import "@/lib/map/initMaplibre"; +import { buildBakedReliefStyle } from "@/lib/map/reliefStyle"; +import type { Theme } from "@/lib/theme"; + +const TILE_PX = 512; + +declare global { + interface Window { + renderReliefTile?: (z: number, x: number, y: number) => Promise; + } +} + +function tileCenter(z: number, x: number, y: number): [number, number] { + const n = 2 ** z; + const lon = ((x + 0.5) / n) * 360 - 180; + const lat = + (Math.atan(Math.sinh(Math.PI * (1 - (2 * (y + 0.5)) / n))) * 180) / Math.PI; + return [lon, lat]; +} + +function nextRender(map: MapLibreMap) { + return new Promise((resolve) => { + map.once("render", () => resolve()); + map.triggerRepaint(); + }); +} + +export function ReliefRenderer() { + const containerRef = useRef(null); + + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const theme: Theme = params.get("theme") === "dark" ? "dark" : "light"; + const quality = Number(params.get("quality") ?? 0.9); + const map = new MapLibreMap({ + container: containerRef.current!, + style: buildBakedReliefStyle(theme), + pixelRatio: 1, + interactive: false, + attributionControl: false, + fadeDuration: 0, + canvasContextAttributes: { preserveDrawingBuffer: true }, + }); + + const ready = new Promise((resolve) => + map.once("load", () => resolve()), + ); + + window.renderReliefTile = async (z, x, y) => { + await ready; + map.jumpTo({ center: tileCenter(z, x, y), zoom: z }); + // Not `idle`: it fires once per burst of activity, so a tile that is + // ready quickly can miss it. Poll the load state instead. + await nextRender(map); + while (!map.loaded() || !map.areTilesLoaded()) { + await new Promise((resolve) => setTimeout(resolve, 50)); + } + await nextRender(map); + return map.getCanvas().toDataURL("image/webp", quality); + }; + + return () => { + delete window.renderReliefTile; + map.remove(); + }; + }, []); + + return ( +
+ ); +} diff --git a/src/app/relief-render/page.tsx b/src/app/relief-render/page.tsx new file mode 100644 index 0000000..dbfa745 --- /dev/null +++ b/src/app/relief-render/page.tsx @@ -0,0 +1,8 @@ +import { notFound } from "next/navigation"; +import { ReliefRenderer } from "./ReliefRenderer"; + +// Used by scripts/relief-tiles to pre-render relief tiles. Dev only. +export default function ReliefRenderPage() { + if (process.env.NODE_ENV === "production") notFound(); + return ; +} diff --git a/src/lib/map/initMaplibre.ts b/src/lib/map/initMaplibre.ts index 2502a78..1c19f1c 100644 --- a/src/lib/map/initMaplibre.ts +++ b/src/lib/map/initMaplibre.ts @@ -1,8 +1,14 @@ -import { setMaxParallelImageRequests, setWorkerUrl } from "maplibre-gl"; +import { + addProtocol, + setMaxParallelImageRequests, + setWorkerUrl, +} from "maplibre-gl"; +import { Protocol } from "pmtiles"; const MAX_PARALLEL_IMAGE_REQUESTS = 32; if (typeof window !== "undefined") { setWorkerUrl("/maplibre-gl-worker.mjs"); setMaxParallelImageRequests(MAX_PARALLEL_IMAGE_REQUESTS); + addProtocol("pmtiles", new Protocol().tile); } diff --git a/src/lib/map/reliefStyle.ts b/src/lib/map/reliefStyle.ts index f9fdf48..e8ee6b5 100644 --- a/src/lib/map/reliefStyle.ts +++ b/src/lib/map/reliefStyle.ts @@ -14,6 +14,18 @@ const MAPTERHORN_TILES = "https://tiles.mapterhorn.com/{z}/{x}/{y}.webp"; // regions and 404s elsewhere, which MapLibre draws as holes. Capping the // source makes it overzoom z12 everywhere instead. export const MAPTERHORN_MAX_ZOOM = 12; +// Pre-rendered relief for zoom 0-6 (scripts/relief-tiles). Bump the version +// whenever the palette or shading changes and upload the new archives. +const RELIEF_TILES_URL = process.env.NEXT_PUBLIC_RELIEF_TILES_URL; +const RELIEF_TILES_VERSION = 1; +export const PRERENDERED_SOURCE_ID = "relief-prerendered"; +export const PRERENDERED_MAX_ZOOM = 6; +const PRERENDERED_FADE_MS = 300; + +export function prerenderedReliefUrl(baseUrl: string, theme: Theme) { + return `pmtiles://${baseUrl.replace(/\/$/, "")}/relief-${theme}-v${RELIEF_TILES_VERSION}.pmtiles`; +} + const OPENFREEMAP_TILEJSON = "https://tiles.openfreemap.org/planet"; const OPENFREEMAP_GLYPHS = "https://tiles.openfreemap.org/fonts/{fontstack}/{range}.pbf"; @@ -69,7 +81,17 @@ export const RELIEF_PALETTES: Record = { "sky-horizon-blend": 0.6, "horizon-fog-blend": 0.7, "fog-ground-blend": 0.4, - "atmosphere-blend": ["interpolate", ["linear"], ["zoom"], 0, 1, 5, 1, 7, 0], + "atmosphere-blend": [ + "interpolate", + ["linear"], + ["zoom"], + 0, + 1, + 5, + 1, + 7, + 0, + ], }, }, dark: { @@ -104,7 +126,17 @@ export const RELIEF_PALETTES: Record = { "sky-horizon-blend": 0.5, "horizon-fog-blend": 0.6, "fog-ground-blend": 0.4, - "atmosphere-blend": ["interpolate", ["linear"], ["zoom"], 0, 1, 5, 1, 7, 0], + "atmosphere-blend": [ + "interpolate", + ["linear"], + ["zoom"], + 0, + 1, + 5, + 1, + 7, + 0, + ], }, }, }; @@ -121,8 +153,13 @@ function elevationRamp(stops: [number, string][]): ExpressionSpecification { const LABEL_FONT = ["Noto Sans Regular"]; const LABEL_FONT_BOLD = ["Noto Sans Bold"]; -function buildStyle(theme: Theme): StyleSpecification { +function buildStyle( + theme: Theme, + prerenderedBaseUrl?: string, +): StyleSpecification { const palette = RELIEF_PALETTES[theme]; + // With pre-rendered tiles, live relief only draws where they run out. + const liveMinZoom = prerenderedBaseUrl ? PRERENDERED_MAX_ZOOM : 0; const labelPaint = { "text-color": palette.label, "text-halo-color": palette.labelHalo, @@ -146,6 +183,14 @@ function buildStyle(theme: Theme): StyleSpecification { type: "vector", url: OPENFREEMAP_TILEJSON, }, + ...(prerenderedBaseUrl && { + [PRERENDERED_SOURCE_ID]: { + type: "raster" as const, + url: prerenderedReliefUrl(prerenderedBaseUrl, theme), + tileSize: 512, + attribution: MAPTERHORN_ATTRIBUTION, + }, + }), }, layers: [ { @@ -153,16 +198,28 @@ function buildStyle(theme: Theme): StyleSpecification { type: "background", paint: { "background-color": palette.ocean }, }, + ...(prerenderedBaseUrl + ? [ + { + id: "relief-prerendered", + type: "raster" as const, + source: PRERENDERED_SOURCE_ID, + paint: { "raster-fade-duration": PRERENDERED_FADE_MS }, + }, + ] + : []), { id: "relief-tint", type: "color-relief", source: DEM_SOURCE_ID, + minzoom: liveMinZoom, paint: { "color-relief-color": elevationRamp(palette.elevation) }, }, { id: "relief-shade", type: "hillshade", source: DEM_SOURCE_ID, + minzoom: liveMinZoom, paint: { "hillshade-method": "multidirectional", // Four lights around the north-west keep the classic cartographic @@ -217,7 +274,13 @@ function buildStyle(theme: Theme): StyleSpecification { type: "symbol", source: VECTOR_SOURCE_ID, "source-layer": "water_name", - filter: ["match", ["geometry-type"], ["MultiPoint", "Point"], true, false], + filter: [ + "match", + ["geometry-type"], + ["MultiPoint", "Point"], + true, + false, + ], layout: { "text-field": SINGLE_LINE_LABEL_TEXT_FIELD, "text-font": ["Noto Sans Italic"], @@ -237,7 +300,17 @@ function buildStyle(theme: Theme): StyleSpecification { layout: { "text-field": SINGLE_LINE_LABEL_TEXT_FIELD, "text-font": LABEL_FONT, - "text-size": ["interpolate", ["exponential", 1.2], ["zoom"], 4, 11, 7, 13, 11, 18], + "text-size": [ + "interpolate", + ["exponential", 1.2], + ["zoom"], + 4, + 11, + 7, + 13, + 11, + 18, + ], "text-max-width": 8, }, paint: labelPaint, @@ -272,8 +345,20 @@ const styleCache = new Map(); export function buildReliefStyle(theme: Theme): StyleSpecification { let style = styleCache.get(theme); if (!style) { - style = buildStyle(theme); + style = buildStyle(theme, RELIEF_TILES_URL); styleCache.set(theme, style); } return style; } + +// The layers pre-rendered into raster tiles. Everything else stays live. +const BAKED_LAYER_IDS = new Set(["ocean", "relief-tint", "relief-shade"]); + +export function buildBakedReliefStyle(theme: Theme): StyleSpecification { + const style = buildStyle(theme); + return { + version: 8, + sources: { [DEM_SOURCE_ID]: style.sources[DEM_SOURCE_ID] }, + layers: style.layers.filter((layer) => BAKED_LAYER_IDS.has(layer.id)), + }; +} From 9f9fe91b8b5dfa208ed896e012152fb18710b414 Mon Sep 17 00:00:00 2001 From: Anastasiia Ivanchenko Date: Sun, 27 Sep 2026 21:04:07 +0300 Subject: [PATCH 5/8] Update dependencies and enhance styling across components --- components.json | 25 + package-lock.json | 3129 ++++++++++++++++++++- package.json | 6 + src/app/globals.css | 107 +- src/components/about/AboutContent.tsx | 8 +- src/components/layout/EditorViewTabs.tsx | 2 +- src/components/layout/MapToolbar.tsx | 72 + src/components/layout/SidebarResizer.tsx | 3 +- src/components/map/ColormapPicker.tsx | 2 +- src/components/map/DownloadButton.tsx | 4 +- src/components/map/MapControls.tsx | 57 + src/components/map/MapReadout.tsx | 8 +- src/components/map/MapSearch.tsx | 2 +- src/components/map/MapTour.tsx | 2 +- src/components/map/PatchMenu.tsx | 8 +- src/components/map/ReliefMap.tsx | 26 +- src/components/map/UserPositionMarker.tsx | 2 +- src/components/map/YearSelector.tsx | 4 +- src/components/ui/IconButton.tsx | 20 +- src/components/ui/ProgressBar.tsx | 4 +- src/components/ui/button-group.tsx | 87 + src/components/ui/button.tsx | 57 + src/components/ui/separator.tsx | 24 + src/components/ui/toggle-group.tsx | 89 + src/components/ui/toggle.tsx | 44 + src/lib/map/smoothWheelZoom.ts | 68 +- src/lib/theme.ts | 2 + src/lib/utils.ts | 1 + 28 files changed, 3680 insertions(+), 183 deletions(-) create mode 100644 components.json create mode 100644 src/components/layout/MapToolbar.tsx create mode 100644 src/components/map/MapControls.tsx create mode 100644 src/components/ui/button-group.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/separator.tsx create mode 100644 src/components/ui/toggle-group.tsx create mode 100644 src/components/ui/toggle.tsx create mode 100644 src/lib/utils.ts diff --git a/components.json b/components.json new file mode 100644 index 0000000..8d886db --- /dev/null +++ b/components.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "base-nova", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "menuColor": "default", + "menuAccent": "subtle", + "registries": {} +} diff --git a/package-lock.json b/package-lock.json index ee08548..b5745e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,13 @@ "version": "0.1.0", "hasInstallScript": true, "dependencies": { + "@base-ui/react": "^1.8.0", "@deck.gl/core": "^9.4.0", + "class-variance-authority": "^0.7.1", + "cn": "^0.4.0", "fflate": "^0.8.3", "jspdf": "^4.2.1", + "lucide-react": "^1.48.0", "maplibre-gl": "^6.10.0", "next": "16.3.5", "pmtiles": "^4.5.0", @@ -20,6 +24,8 @@ "react-joyride": "^3.2.0", "react-map-gl": "^8.1.3", "recharts": "^3.10.1", + "shadcn": "^4.21.0", + "tw-animate-css": "^1.4.0", "write-excel-file": "^4.1.1", "zarrita": "^0.7.5" }, @@ -53,7 +59,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", @@ -68,7 +73,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -78,7 +82,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -109,7 +112,6 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.8", @@ -122,11 +124,22 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-compilation-targets": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.29.7", @@ -139,21 +152,53 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/helper-globals": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.29.7", @@ -167,7 +212,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.29.7", @@ -181,11 +225,61 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -195,7 +289,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -205,7 +298,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -215,7 +307,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.29.7", @@ -229,7 +320,6 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.8" @@ -241,6 +331,90 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.9.tgz", + "integrity": "sha512-FFwIwzU+7SCOuxxV4YtJql6T9981ZVTm+FHO5GhVsRqCTdy0WwrEZh3l42ARpXruJUDGOptdduHW6Zr7pNPLNg==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz", + "integrity": "sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/runtime": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", @@ -254,7 +428,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -269,7 +442,6 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -288,7 +460,6 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.29.7", @@ -298,6 +469,66 @@ "node": ">=6.9.0" } }, + "node_modules/@base-ui/react": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.8.0.tgz", + "integrity": "sha512-P0/1sxo6SBVZOklKMIedvTWqw2s2IQzi9x5bIVsXu980cuSOD4NeuRSs+/L7LZQfDkZP/uRZyGPyfFl/B1oH+Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "@base-ui/utils": "0.4.0", + "@floating-ui/react-dom": "^2.1.9", + "@floating-ui/utils": "^0.2.12", + "use-sync-external-store": "^1.6.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@date-fns/tz": "^1.2.0", + "@types/react": "^17 || ^18 || ^19", + "date-fns": "^4.0.0", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@date-fns/tz": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "date-fns": { + "optional": true + } + } + }, + "node_modules/@base-ui/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-bO9fz25kKtPf+aZVyfQrC0PDmJdmVni31W2hCS5/Owb+inwdIL3XU26pCPRPlt4LSxZrBgLwubXQXQlKaFEZzw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "@floating-ui/utils": "^0.2.12", + "reselect": "^5.2.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "@types/react": "^17 || ^18 || ^19", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@deck.gl/core": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/@deck.gl/core/-/core-9.4.0.tgz", @@ -323,6 +554,240 @@ "mjolnir.js": "^3.1.1" } }, + "node_modules/@dotenvx/dotenvx": { + "version": "1.75.1", + "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.75.1.tgz", + "integrity": "sha512-/BITOC9dmS/edY2zQwZNicQ059O6RKabtQfyEafV0nGtfYRNHYy1DIPiYVcov40+tob9hfmBnbR963dS+EQ1DQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@dotenvx/primitives": "^0.8.0", + "commander": "^11.1.0", + "conf": "^10.2.0", + "dotenv": "^17.2.1", + "enquirer": "^2.4.1", + "env-paths": "^2.2.1", + "execa": "^5.1.1", + "fdir": "^6.2.0", + "ignore": "^5.3.0", + "object-treeify": "1.1.33", + "open": "^8.4.2", + "picomatch": "^4.0.4", + "systeminformation": "^5.22.11", + "undici": "^7.11.0", + "which": "^4.0.0", + "yocto-spinner": "^1.1.0" + }, + "bin": { + "dotenvx": "src/cli/dotenvx.js" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/@dotenvx/dotenvx/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@dotenvx/primitives": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@dotenvx/primitives/-/primitives-0.8.0.tgz", + "integrity": "sha512-VYJy0uhFm9zTJ1TxBaW/pA8bjbOM/OttaNMwZ1RHG4JKyRG7DhSdiqD1ipQoAyoD22olUtxbP78W9xY3Wd11bg==", + "license": "BSD-3-Clause" + }, "node_modules/@emnapi/core": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", @@ -581,6 +1046,18 @@ "type-fest": "^4.1.0" } }, + "node_modules/@hono/node-server": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.1.tgz", + "integrity": "sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4" + } + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -1152,7 +1629,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1163,7 +1639,6 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -1174,7 +1649,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1184,14 +1658,12 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1510,6 +1982,68 @@ "@math.gl/core": "4.1.0" } }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.1.tgz", + "integrity": "sha512-H2HxLvC3HDNybePJaLdSrU1hhUK5iQw+WvV1b01myFyI7sdVGe1u/IPTE5D9fGCiJDVtgMV/lmFkQXLmQyIFYA==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9 || ^2.0.5", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -1684,7 +2218,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -1698,7 +2231,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -1708,7 +2240,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -2089,6 +2620,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -2426,6 +2975,81 @@ "tailwindcss": "4.3.3" } }, + "node_modules/@ts-morph/common": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.27.0.tgz", + "integrity": "sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==", + "license": "MIT", + "dependencies": { + "fast-glob": "^3.3.3", + "minimatch": "^10.0.1", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@ts-morph/common/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@ts-morph/common/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", @@ -2612,6 +3236,12 @@ "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" }, + "node_modules/@types/validate-npm-package-name": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/validate-npm-package-name/-/validate-npm-package-name-4.0.2.tgz", + "integrity": "sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.59.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", @@ -3301,6 +3931,19 @@ "unzipit": "2.0.0" } }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.17.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", @@ -3341,6 +3984,63 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3395,7 +4095,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/aria-query": { @@ -3605,6 +4304,18 @@ "node": ">=0.10.0" } }, + "node_modules/ast-types": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.3.tgz", + "integrity": "sha512-FvWoWYfSCM6kRxCSH+MGLHIKKGRL6A6AW7Zek2O32REPQRdg131428uRTKMBYAeRd3XXAaHDS60Wpri7CdKDrA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -3622,6 +4333,15 @@ "node": ">= 0.4" } }, + "node_modules/atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", + "license": "MIT", + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -3696,6 +4416,43 @@ "require-from-string": "^2.0.2" } }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/brace-expansion": { "version": "1.1.18", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", @@ -3711,7 +4468,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -3724,7 +4480,6 @@ "version": "4.28.9", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -3754,6 +4509,30 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bundle-name": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz", + "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/bytewise": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz", @@ -3796,7 +4575,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3810,7 +4588,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -3827,7 +4604,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3914,6 +4690,45 @@ "url": "https://github.com/chalk/chalk-template?sponsor=1" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -3929,6 +4744,24 @@ "node": ">=6" } }, + "node_modules/cn": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cn/-/cn-0.4.0.tgz", + "integrity": "sha512-qOAeUhPwPzBxx6jaeQdo646XIKliKHc2j1ZF7Ngb1o0pJZKH5gq8FkA0aVdUdPFUDXNXyUCXpCtoJISo6dhizg==", + "license": "MIT", + "bin": { + "cn": "bin/cn.mjs" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "license": "MIT" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3953,52 +4786,181 @@ "integrity": "sha512-AIjYVxrV9X752LmPDLbVYv8aMCuHPSLZJXEo2qo/xJfv+NYhaZ4sMSF01rM+gHPaMgvPM0l5D/F+Qx+i2WfSmQ==", "license": "MIT", "dependencies": { - "array-back": "^6.2.3", - "find-replace": "^5.0.2", - "lodash.camelcase": "^4.3.0", - "typical": "^7.3.0" - }, - "engines": { - "node": ">=12.20" + "array-back": "^6.2.3", + "find-replace": "^5.0.2", + "lodash.camelcase": "^4.3.0", + "typical": "^7.3.0" + }, + "engines": { + "node": ">=12.20" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/command-line-usage": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.4.tgz", + "integrity": "sha512-85UdvzTNx/+s5CkSgBm/0hzP80RFHAa7PsfeADE5ezZF3uHz3/Tqj9gIKGT9PTtpycc3Ua64T0oVulGfKxzfqg==", + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "chalk-template": "^0.4.0", + "table-layout": "^4.1.1", + "typical": "^7.3.0" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/conf": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz", + "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==", + "license": "MIT", + "dependencies": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conf/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/conf/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" }, "peerDependencies": { - "@75lb/nature": "latest" + "ajv": "^8.0.0" }, "peerDependenciesMeta": { - "@75lb/nature": { + "ajv": { "optional": true } } }, - "node_modules/command-line-usage": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.4.tgz", - "integrity": "sha512-85UdvzTNx/+s5CkSgBm/0hzP80RFHAa7PsfeADE5ezZF3uHz3/Tqj9gIKGT9PTtpycc3Ua64T0oVulGfKxzfqg==", - "license": "MIT", - "dependencies": { - "array-back": "^6.2.2", - "chalk-template": "^0.4.0", - "table-layout": "^4.1.1", - "typical": "^7.3.0" + "node_modules/conf/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/conf/node_modules/json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==", + "license": "BSD-2-Clause" + }, + "node_modules/conf/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=12.20.0" + "node": ">=10" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, "node_modules/core-js": { "version": "3.50.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.50.0.tgz", @@ -4014,11 +4976,53 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -4039,6 +5043,18 @@ "utrie": "^1.0.2" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -4227,11 +5243,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4251,6 +5281,20 @@ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", "license": "MIT" }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4258,6 +5302,43 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -4276,6 +5357,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -4294,6 +5387,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -4304,6 +5406,15 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -4327,11 +5438,37 @@ "@types/trusted-types": "^2.0.7" } }, + "node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -4342,11 +5479,16 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.427", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.427.tgz", "integrity": "sha512-n14zb3FdsChZ2BNobqNHAJMcP3ifFv4paox2LvCrfVAQcqGiSURgbJl+PfMpHVCNFkStnNc+RRVtPBTVW5PDgw==", - "dev": true, "license": "ISC" }, "node_modules/emoji-regex": { @@ -4356,6 +5498,15 @@ "dev": true, "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/enhanced-resolve": { "version": "5.24.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.3.tgz", @@ -4370,6 +5521,37 @@ "node": ">=10.13.0" } }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-abstract": { "version": "1.24.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", @@ -4443,7 +5625,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4453,7 +5634,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4498,7 +5678,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -4568,12 +5747,17 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4947,6 +6131,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", @@ -5003,12 +6200,68 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/eventemitter3": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "license": "MIT" }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/execa": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", + "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/expect-type": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", @@ -5016,7 +6269,69 @@ "dev": true, "license": "Apache-2.0", "engines": { - "node": ">=12.0.0" + "node": ">=12.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.0.tgz", + "integrity": "sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" } }, "node_modules/extend-shallow": { @@ -5035,7 +6350,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -5115,11 +6429,26 @@ ], "license": "(MIT AND Zlib)" }, + "node_modules/fast-uri": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", - "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -5131,6 +6460,21 @@ "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", "license": "MIT" }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5148,7 +6492,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -5157,6 +6500,27 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/find-replace": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", @@ -5234,6 +6598,38 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.1.tgz", + "integrity": "sha512-KYAb4c9BJQI6QqGKthV68OHe0badztdXJWKo0WtBA9IuCFPTKvE5ZdUBglP833aMjhaSPNO4A5j/EkzZtGlKjA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -5254,7 +6650,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5291,6 +6686,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fuzzysort": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-3.1.0.tgz", + "integrity": "sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==", + "license": "MIT" + }, "node_modules/generator-function": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", @@ -5305,17 +6706,27 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -5336,11 +6747,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-own-enumerable-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-own-enumerable-keys/-/get-own-enumerable-keys-1.0.0.tgz", + "integrity": "sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -5350,6 +6772,22 @@ "node": ">= 0.4" } }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", @@ -5443,7 +6881,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5456,7 +6893,6 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/has-bigints": { @@ -5514,7 +6950,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5543,7 +6978,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -5569,6 +7003,15 @@ "hermes-estree": "0.25.1" } }, + "node_modules/hono": { + "version": "4.13.9", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.9.tgz", + "integrity": "sha512-7dMkQmZoC4E6F7AtaQSPhlWAdnBti+j7rreMZl8QB4jFiEhP9TWbGWUMi8WYzBCgmgulxuvLQupKqo+Co6Omyg==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/html2canvas": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", @@ -5583,11 +7026,55 @@ "node": ">=8.0.0" } }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -5607,7 +7094,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -5630,6 +7116,12 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -5660,6 +7152,24 @@ "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==", "license": "MIT" }, + "node_modules/ip-address": { + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -5678,6 +7188,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -5818,6 +7334,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -5831,7 +7362,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5877,7 +7407,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -5886,6 +7415,48 @@ "node": ">=0.10.0" } }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-lite": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-2.0.0.tgz", @@ -5922,7 +7493,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -5945,6 +7515,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -5957,6 +7548,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -5976,6 +7573,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-regexp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-3.1.0.tgz", + "integrity": "sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-set": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", @@ -6005,6 +7614,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", @@ -6056,6 +7677,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -6102,6 +7735,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -6113,7 +7761,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/isobject": { @@ -6153,18 +7800,25 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", - "dev": true, "funding": [ { "type": "github", @@ -6187,7 +7841,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -6211,6 +7864,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6218,6 +7877,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -6235,7 +7900,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -6244,6 +7908,18 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jspdf": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.2.1.tgz", @@ -6293,6 +7969,15 @@ "json-buffer": "3.0.1" } }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -6588,6 +8273,12 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6617,6 +8308,46 @@ "dev": true, "license": "MIT" }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -6634,12 +8365,20 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.48.0.tgz", + "integrity": "sha512-R0CIKY/fXiC6y9xRBADgsK+VW2p/pcTJOMhLZf1T+uG+vJUvyUR02nGOgmIIC7MPkiNK4Ox8QDnbqF8rJYWPZQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -6722,17 +8461,46 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" } }, + "node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -6742,7 +8510,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -6752,6 +8519,52 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -6784,7 +8597,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/murmurhash-js": { @@ -6834,6 +8646,35 @@ "dev": true, "license": "MIT" }, + "node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/next": { "version": "16.3.5", "resolved": "https://registry.npmjs.org/next/-/next-16.3.5.tgz", @@ -6938,12 +8779,39 @@ "version": "2.0.55", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.55.tgz", "integrity": "sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/numcodecs": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/numcodecs/-/numcodecs-0.3.2.tgz", @@ -6957,7 +8825,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6967,7 +8834,6 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6986,6 +8852,15 @@ "node": ">= 0.4" } }, + "node_modules/object-treeify": { + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", + "integrity": "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, "node_modules/object.assign": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", @@ -7090,6 +8965,71 @@ "node": ">=12.20.0" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/open": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.4.tgz", + "integrity": "sha512-++Zlftm0kVLPmzC06t6epuWmcRMDbI4z5P3NNX979WA/k23+NtSOynEGzsVfZwguKw2mi5umVgnBlJQMwRz4Pg==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.5.1", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.2.1", + "wsl-utils": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -7108,6 +9048,68 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -7158,11 +9160,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -7171,6 +9181,51 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7185,7 +9240,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7198,6 +9252,16 @@ "dev": true, "license": "MIT" }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -7215,7 +9279,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -7224,6 +9287,88 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/playwright": { "version": "1.63.0", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", @@ -7276,7 +9421,6 @@ "version": "8.5.28", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -7301,12 +9445,37 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-selector-parser": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.6.tgz", + "integrity": "sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/potpack": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz", "integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==", "license": "ISC" }, + "node_modules/powershell-utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz", + "integrity": "sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7317,6 +9486,43 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-ms": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.1.tgz", + "integrity": "sha512-HzMy3Geq23nVALD/M2LliU+F+M+gVNsvkQWWqeBZ8HDiCgzo6YPJ/Omrmtq24EFrIsk0a3EkQGEd7bDOo+IhGA==", + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -7335,6 +9541,23 @@ "integrity": "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==", "license": "MIT" }, + "node_modules/proxy-addr": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7345,11 +9568,26 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -7382,6 +9620,34 @@ "performance-now": "^2.1.0" } }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/react": { "version": "19.3.0", "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", @@ -7488,6 +9754,22 @@ } } }, + "node_modules/recast": { + "version": "0.23.21", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.21.tgz", + "integrity": "sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, "node_modules/recharts": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz", @@ -7633,7 +9915,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -7658,11 +9939,41 @@ "protocol-buffers-schema": "^3.3.1" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -7714,11 +10025,38 @@ "@rolldown/binding-win32-x64-msvc": "1.2.9" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -7799,6 +10137,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.28.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", @@ -7821,12 +10165,56 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" } }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -7891,6 +10279,122 @@ "node": ">=0.10.0" } }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shadcn": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/shadcn/-/shadcn-4.21.0.tgz", + "integrity": "sha512-UU2mFNusW8C5rvadKdH69vERYZqUlOOlXBcf0MYhYLdTGP6DPti7X4qovCu+RTfCqsAgq/T+YfE0Vnttxh9aiw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/plugin-transform-typescript": "^7.28.0", + "@babel/preset-typescript": "^7.27.1", + "@dotenvx/dotenvx": "^1.48.4", + "@modelcontextprotocol/sdk": "^1.26.0", + "@types/validate-npm-package-name": "^4.0.2", + "browserslist": "^4.26.2", + "cn": "^0.2.4", + "commander": "^14.0.0", + "cosmiconfig": "^9.0.0", + "dedent": "^1.6.0", + "deepmerge": "^4.3.1", + "diff": "^8.0.2", + "execa": "^9.6.0", + "fast-glob": "^3.3.3", + "fs-extra": "^11.3.1", + "fuzzysort": "^3.1.0", + "kleur": "^4.1.5", + "open": "^11.0.0", + "ora": "^8.2.0", + "postcss": "^8.5.6", + "postcss-selector-parser": "^7.1.0", + "prompts": "^2.4.2", + "recast": "^0.23.11", + "socks": "^2.8.8", + "stringify-object": "^5.0.0", + "ts-morph": "^26.0.0", + "tsconfig-paths": "^4.2.0", + "undici": "^7.27.2", + "validate-npm-package-name": "^7.0.1", + "zod": "^3.24.1", + "zod-to-json-schema": "^3.24.6" + }, + "bin": { + "shadcn": "dist/index.js" + }, + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/shadcn/node_modules/cn": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/cn/-/cn-0.2.6.tgz", + "integrity": "sha512-+i4L0zUGgRcEnhsxueVrP7iBGxBx5iD0WOTYg1MwFEu2ZyCmH5Ov2V1cul2Ht5UchRQQgftCf4be/RxspuW6QQ==", + "license": "MIT", + "bin": { + "cn": "bin/cn.mjs" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/shadcn/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/shadcn/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/shadcn/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/shadcn/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/sharp": { "version": "0.35.4", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", @@ -7958,7 +10462,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -7971,22 +10474,20 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -8001,7 +10502,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -8018,7 +10518,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -8037,7 +10536,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -8060,6 +10558,48 @@ "dev": true, "license": "ISC" }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.10.tgz", + "integrity": "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/sort-asc": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.2.0.tgz", @@ -8095,6 +10635,15 @@ "node": ">=0.10.0" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -8165,6 +10714,15 @@ "node": ">=0.1.14" } }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/std-env": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", @@ -8172,6 +10730,18 @@ "dev": true, "license": "MIT" }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -8186,6 +10756,56 @@ "node": ">= 0.4" } }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/string.prototype.includes": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", @@ -8299,16 +10919,68 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-5.0.0.tgz", + "integrity": "sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg==", + "license": "BSD-2-Clause", + "dependencies": { + "get-own-enumerable-keys": "^1.0.0", + "is-obj": "^3.0.0", + "is-regexp": "^3.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/yeoman/stringify-object?sponsor=1" + } + }, + "node_modules/stringify-object/node_modules/is-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-3.0.0.tgz", + "integrity": "sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -8380,6 +11052,32 @@ "node": ">=12.0.0" } }, + "node_modules/systeminformation": { + "version": "5.33.13", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.33.13.tgz", + "integrity": "sha512-GtyakkchTQKymM1WeAB652zi3rM1q00RKdc+jZe9KGstuc/mWUwAnQGN/RE5gVYvHIR8xYOUIUD9irVKDtA0wA==", + "license": "MIT", + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, "node_modules/table-layout": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", @@ -8508,7 +11206,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -8517,6 +11214,15 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -8530,6 +11236,16 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-morph": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-26.0.0.tgz", + "integrity": "sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==", + "license": "MIT", + "dependencies": { + "@ts-morph/common": "~0.27.0", + "code-block-writer": "^13.0.3" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -8562,6 +11278,15 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tw-animate-css": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", + "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8587,6 +11312,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -8669,7 +11425,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -8746,6 +11502,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.0.tgz", + "integrity": "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", @@ -8753,6 +11518,18 @@ "dev": true, "license": "MIT" }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -8768,6 +11545,24 @@ "node": ">=0.10.0" } }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unrs-resolver": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", @@ -8816,7 +11611,6 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -8862,6 +11656,12 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/utrie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", @@ -8872,6 +11672,24 @@ "base64-arraybuffer": "^1.0.2" } }, + "node_modules/validate-npm-package-name": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", + "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/victory-vendor": { "version": "37.3.6", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", @@ -9382,7 +12200,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -9519,6 +12336,12 @@ "node": ">=12.17" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/write-excel-file": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/write-excel-file/-/write-excel-file-4.1.1.tgz", @@ -9531,11 +12354,38 @@ "node": ">=18" } }, + "node_modules/wsl-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.0.tgz", + "integrity": "sha512-Hl0ZOAs672vg+06kfujwRhoS6/jehvULrlFkuF2dRu6pHgA8U06h3xqNIqNNU1LTXPcedxByAR4GS6pwQK0mgA==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, "license": "ISC" }, "node_modules/yocto-queue": { @@ -9551,6 +12401,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yocto-spinner": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-1.2.2.tgz", + "integrity": "sha512-DODGl1wJjA/s5pnJFKau9lIYHT81lnhob1i3e1TjxZRxEhWRKl74nTbWE6H5KlkViQQTo/Z29YFdxzTZAMY3ng==", + "license": "MIT", + "dependencies": { + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18.19" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz", + "integrity": "sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zarrita": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/zarrita/-/zarrita-0.7.5.tgz", @@ -9565,12 +12442,20 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, "node_modules/zod-validation-error": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", diff --git a/package.json b/package.json index 925155f..60da265 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,13 @@ "relief-tiles": "node scripts/relief-tiles/build.mjs" }, "dependencies": { + "@base-ui/react": "^1.8.0", "@deck.gl/core": "^9.4.0", + "class-variance-authority": "^0.7.1", + "cn": "^0.4.0", "fflate": "^0.8.3", "jspdf": "^4.2.1", + "lucide-react": "^1.48.0", "maplibre-gl": "^6.10.0", "next": "16.3.5", "pmtiles": "^4.5.0", @@ -24,6 +28,8 @@ "react-joyride": "^3.2.0", "react-map-gl": "^8.1.3", "recharts": "^3.10.1", + "shadcn": "^4.21.0", + "tw-animate-css": "^1.4.0", "write-excel-file": "^4.1.1", "zarrita": "^0.7.5" }, diff --git a/src/app/globals.css b/src/app/globals.css index 20d5aae..3eb0d27 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,4 +1,8 @@ @import "tailwindcss"; +@import "tw-animate-css"; +@import "shadcn/tailwind.css"; + +@custom-variant dark (&:is(.dark *)); /* ============================================================ EarthPrints design system @@ -35,11 +39,10 @@ 255, 0.34 ); /* 2.3:1 — decorative only, never body */ - --border: rgba(220, 219, 214, 0.12); --border-strong: rgba(220, 219, 214, 0.18); /** Brand dark teal — icons on white chips, light-mode accent. */ --accent-solid: #006c66; - --accent: #52d4c8; + --brand: #52d4c8; --accent-bright: #7df5e8; --accent-on-dark: #52d4c8; /** The guide keeps the dark accent in both themes, so it reads as one thing. */ @@ -66,6 +69,40 @@ /* What the layout actually reserves: zero once the panel is collapsed, so the panel keeps its own width and slides out from under the map. */ --editor-sidebar-track: var(--editor-sidebar-width); + /* shadcn tokens follow the editor tokens, which already switch with the + theme, so the .dark block below only carries what has no editor twin. */ + --background: var(--editor-bg-primary); + --foreground: var(--editor-fg-primary); + --card: var(--editor-bg-primary); + --card-foreground: var(--editor-fg-primary); + --popover: var(--editor-bg-depth); + --popover-foreground: var(--editor-fg-primary); + --primary: var(--brand); + --primary-foreground: var(--editor-bg-primary); + --secondary: var(--editor-bg-secondary); + --secondary-foreground: var(--editor-fg-primary); + --muted: var(--editor-bg-secondary); + --muted-foreground: var(--editor-fg-tertiary); + --accent: var(--editor-bg-secondary); + --accent-foreground: var(--editor-fg-primary); + --border: var(--editor-border); + --destructive: oklch(0.577 0.245 27.325); + --input: var(--editor-border); + --ring: var(--accent-solid); + --chart-1: oklch(0.87 0 0); + --chart-2: oklch(0.556 0 0); + --chart-3: oklch(0.439 0 0); + --chart-4: oklch(0.371 0 0); + --chart-5: oklch(0.269 0 0); + --radius: var(--editor-radius-sm); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); } :root[data-sidebar-collapsed="true"] { @@ -96,10 +133,9 @@ ); /* 5.0:1 — AA body text, subtitles */ --text-muted: rgba(10, 10, 10, 0.56); /* 3.8:1 — AA large text only (18px+) */ --text-dim: rgba(10, 10, 10, 0.36); /* 2.4:1 — decorative only, never body */ - --border: rgba(45, 45, 42, 0.12); --border-strong: rgba(45, 45, 42, 0.17); --accent-solid: #006c66; - --accent: #006c66; + --brand: #006c66; --accent-bright: #00837b; --accent-on-dark: #006c66; --editor-bg-depth: #ececea; @@ -124,7 +160,7 @@ --color-text-dim: var(--text-dim); --color-border: var(--border); --color-border-strong: var(--border-strong); - --color-accent: var(--accent); + --color-brand: var(--brand); --color-accent-solid: var(--accent-solid); --color-accent-bright: var(--accent-bright); --color-editor-bg-base: var(--editor-bg-base); @@ -148,6 +184,43 @@ --font-mono: var(--font-geist-mono), "Geist Mono", "Geist Mono Fallback", ui-monospace, monospace; + --font-heading: var(--font-sans); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --color-foreground: var(--foreground); + --color-background: var(--background); + --radius-sm: calc(var(--radius) * 0.6); + --radius-md: calc(var(--radius) * 0.8); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) * 1.4); + --radius-2xl: calc(var(--radius) * 1.8); + --radius-3xl: calc(var(--radius) * 2.2); + --radius-4xl: calc(var(--radius) * 2.6); } @layer base { @@ -166,6 +239,7 @@ transition: background 0.5s ease, color 0.5s ease; + @apply bg-background text-foreground; } /* The app only ever renders the editor shell, which owns the viewport. */ @@ -190,6 +264,12 @@ button { cursor: pointer; } + * { + @apply border-border outline-ring/50; + } + html { + @apply font-sans; + } } /* ============================================================ @@ -277,3 +357,20 @@ /* The guide draws its own outline around the lit area (see MapTour), because Joyride's overlay is a single path holding the cutout and an outer rectangle the size of the viewport: stroking it frames the whole page. */ + +.dark { + --destructive: oklch(0.704 0.191 22.216); + --chart-1: oklch(0.87 0 0); + --chart-2: oklch(0.556 0 0); + --chart-3: oklch(0.439 0 0); + --chart-4: oklch(0.371 0 0); + --chart-5: oklch(0.269 0 0); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} \ No newline at end of file diff --git a/src/components/about/AboutContent.tsx b/src/components/about/AboutContent.tsx index e9917b2..9c5081a 100644 --- a/src/components/about/AboutContent.tsx +++ b/src/components/about/AboutContent.tsx @@ -4,9 +4,9 @@ import { TEAM_MEMBERS } from "@/lib/constants/about"; const LINK_BADGE_CLASS = "inline-flex items-center rounded-full border px-2.5 py-1 text-[11px] font-semibold " + - "leading-none tracking-[0.02em] text-accent no-underline transition-[background-color,border-color] duration-150 " + - "border-[color-mix(in_srgb,var(--accent)_40%,transparent)] bg-[color-mix(in_srgb,var(--accent)_14%,transparent)] " + - "hover:border-[color-mix(in_srgb,var(--accent)_55%,transparent)] hover:bg-[color-mix(in_srgb,var(--accent)_22%,transparent)]"; + "leading-none tracking-[0.02em] text-brand no-underline transition-[background-color,border-color] duration-150 " + + "border-[color-mix(in_srgb,var(--brand)_40%,transparent)] bg-[color-mix(in_srgb,var(--brand)_14%,transparent)] " + + "hover:border-[color-mix(in_srgb,var(--brand)_55%,transparent)] hover:bg-[color-mix(in_srgb,var(--brand)_22%,transparent)]"; export function AboutContent() { return ( @@ -40,7 +40,7 @@ export function AboutContent() {

{member.name}

-

+

{member.role}

diff --git a/src/components/layout/EditorViewTabs.tsx b/src/components/layout/EditorViewTabs.tsx index 3039c63..846cb58 100644 --- a/src/components/layout/EditorViewTabs.tsx +++ b/src/components/layout/EditorViewTabs.tsx @@ -55,7 +55,7 @@ export function EditorViewTabs({ value, onChange }: EditorViewTabsProps) { style={{ ["--editor-view-tab-count" as string]: TABS.length }} >
); diff --git a/src/components/map/ColormapPicker.tsx b/src/components/map/ColormapPicker.tsx index 439551e..9c835bc 100644 --- a/src/components/map/ColormapPicker.tsx +++ b/src/components/map/ColormapPicker.tsx @@ -65,7 +65,7 @@ export function ColormapPicker({ value, onChange }: ColormapPickerProps) { title={COLORMAPS[id].description} className={`flex items-center gap-1.5 rounded-md border px-2 py-0.5 text-[11.5px] font-semibold transition-colors ${ active - ? "border-accent bg-accent/10 text-accent" + ? "border-brand bg-brand/10 text-brand" : "border-editor-border text-editor-fg-tertiary hover:border-editor-border-strong hover:text-editor-fg-secondary" }`} > diff --git a/src/components/map/DownloadButton.tsx b/src/components/map/DownloadButton.tsx index eee0122..3a91fff 100644 --- a/src/components/map/DownloadButton.tsx +++ b/src/components/map/DownloadButton.tsx @@ -440,13 +440,13 @@ export function DownloadButton({ className="flex w-full items-center justify-between rounded-md px-2 py-1.5 text-left text-[12px] font-medium text-editor-fg-secondary hover:bg-editor-bg-secondary hover:text-editor-fg-primary" > Square Fingerprint Badge - + {showBadgeSizes ? "▲ .png" : "▼ .png"} {showBadgeSizes ? ( -
+
Select resolution:
diff --git a/src/components/map/MapControls.tsx b/src/components/map/MapControls.tsx new file mode 100644 index 0000000..ffd61f5 --- /dev/null +++ b/src/components/map/MapControls.tsx @@ -0,0 +1,57 @@ +"use client"; + +import { LocateFixed, Minus, Plus } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { ButtonGroup } from "@/components/ui/button-group"; + +const CONTROL_CLASS = + "bg-background text-editor-fg-secondary hover:bg-muted hover:text-editor-fg-primary dark:bg-background dark:hover:bg-muted"; + +type MapControlsProps = { + onZoomIn: () => void; + onZoomOut: () => void; + onLocate: () => void; + locating: boolean; +}; + +export function MapControls({ + onZoomIn, + onZoomOut, + onLocate, + locating, +}: MapControlsProps) { + return ( +
+ + + + + +
+ ); +} diff --git a/src/components/map/MapReadout.tsx b/src/components/map/MapReadout.tsx index a0b7333..f835bbb 100644 --- a/src/components/map/MapReadout.tsx +++ b/src/components/map/MapReadout.tsx @@ -108,7 +108,7 @@ export function MapReadout({ className="grid h-9 w-9 place-items-center rounded-full border border-dashed border-editor-border-strong" aria-hidden="true" > - +

Click the map @@ -292,7 +292,7 @@ function PlotViewToggle({ onClick={() => onChange(option.id)} className={`rounded-[5px] px-2 py-0.5 text-[11.5px] font-semibold transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${ active - ? "bg-accent text-white" + ? "bg-brand text-white" : "text-editor-fg-tertiary hover:text-editor-fg-secondary" }`} > @@ -342,7 +342,7 @@ function TimeBasisToggle({ onClick={() => onChange(option.id)} className={`rounded-[5px] px-2 py-0.5 text-[11.5px] font-semibold transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${ active - ? "bg-accent text-white" + ? "bg-brand text-white" : "text-editor-fg-tertiary hover:text-editor-fg-secondary" }`} > @@ -372,7 +372,7 @@ function SeriesLoader({ progress }: { progress: SeriesProgress | null }) { Loading time series - + {pct === null ? "…" : `${pct}%`}

diff --git a/src/components/map/MapSearch.tsx b/src/components/map/MapSearch.tsx index ef1de6a..78a0464 100644 --- a/src/components/map/MapSearch.tsx +++ b/src/components/map/MapSearch.tsx @@ -137,7 +137,7 @@ export function MapSearch({ onSelect, className }: MapSearchProps) {
diff --git a/src/components/map/MapTour.tsx b/src/components/map/MapTour.tsx index 89acc87..131bedd 100644 --- a/src/components/map/MapTour.tsx +++ b/src/components/map/MapTour.tsx @@ -146,7 +146,7 @@ const BUTTON_BASE = const BACK_BUTTON = `${BUTTON_BASE} border border-editor-border bg-editor-bg-primary text-editor-fg-primary hover:border-editor-border-strong`; /** Primary: solid accent, matching the plot tabs and the selected year chips. */ -const PRIMARY_BUTTON = `${BUTTON_BASE} border border-transparent bg-accent text-white hover:opacity-90`; +const PRIMARY_BUTTON = `${BUTTON_BASE} border border-transparent bg-brand text-white hover:opacity-90`; function TourCard({ backProps, diff --git a/src/components/map/PatchMenu.tsx b/src/components/map/PatchMenu.tsx index 8965578..48fd3f9 100644 --- a/src/components/map/PatchMenu.tsx +++ b/src/components/map/PatchMenu.tsx @@ -108,13 +108,13 @@ export function PatchMenu({ tabIndex={open ? 0 : -1} className={`flex w-full items-center gap-2 rounded-editor-sm px-2 py-1.5 text-left text-[12.5px] text-editor-fg-secondary transition-colors hover:bg-editor-bg-secondary hover:text-editor-fg-primary ${ showPatch - ? "bg-[color-mix(in_srgb,var(--accent)_12%,transparent)] text-accent hover:bg-[color-mix(in_srgb,var(--accent)_16%,transparent)] hover:text-accent" + ? "bg-[color-mix(in_srgb,var(--brand)_12%,transparent)] text-brand hover:bg-[color-mix(in_srgb,var(--brand)_16%,transparent)] hover:text-brand" : "" }`} onClick={onTogglePatch} >
- + {yearsLabel}
@@ -121,7 +121,7 @@ export function YearSelector({ } className={`relative flex items-center justify-center rounded-[5px] py-1 font-mono text-[11px] tabular-nums transition-all ${ isSelected - ? "bg-accent font-bold text-white shadow-xs" + ? "bg-brand font-bold text-white shadow-xs" : isCached ? "border border-editor-border bg-editor-bg-primary/80 text-editor-fg-primary hover:border-editor-border-strong hover:bg-editor-bg-primary" : "border border-transparent text-editor-fg-tertiary hover:border-editor-border/60 hover:bg-editor-bg-primary/40 hover:text-editor-fg-secondary" diff --git a/src/components/ui/IconButton.tsx b/src/components/ui/IconButton.tsx index 01e7344..8788f05 100644 --- a/src/components/ui/IconButton.tsx +++ b/src/components/ui/IconButton.tsx @@ -3,22 +3,14 @@ type IconButtonProps = { onClick?: () => void; className?: string; "aria-label": string; - /** Visible hover/focus label. Falls back to aria-label when omitted. */ tooltip?: string; "aria-expanded"?: boolean; "aria-controls"?: string; "aria-pressed"?: boolean; disabled?: boolean; - /** When set, renders an anchor to this URL instead of a button. */ href?: string; - /** - * "plain" drops the border/background so the button reads as a bare icon — - * used inside the mobile floating islands, which already carry their own - * border and shadow. - */ variant?: "default" | "plain"; tooltipPlacement?: "bottom" | "left"; - /** Anchor for the guided tour, so it can light the control and not a wrapper. */ "data-tour"?: string; }; @@ -31,10 +23,10 @@ const ICON_BUTTON_CLASS = // Toggle buttons highlight with the accent when pressed. const ICON_BUTTON_PRESSED_CLASS = - "border-[color-mix(in_srgb,var(--accent)_45%,transparent)] " + - "bg-[color-mix(in_srgb,var(--accent)_16%,transparent)] text-accent " + - "hover:text-accent hover:border-[color-mix(in_srgb,var(--accent)_45%,transparent)] " + - "hover:bg-[color-mix(in_srgb,var(--accent)_16%,transparent)]"; + "border-[color-mix(in_srgb,var(--brand)_45%,transparent)] " + + "bg-[color-mix(in_srgb,var(--brand)_16%,transparent)] text-brand " + + "hover:text-brand hover:border-[color-mix(in_srgb,var(--brand)_45%,transparent)] " + + "hover:bg-[color-mix(in_srgb,var(--brand)_16%,transparent)]"; // Borderless variant — no border or resting background. const ICON_BUTTON_PLAIN_CLASS = @@ -43,8 +35,8 @@ const ICON_BUTTON_PLAIN_CLASS = "hover:bg-editor-bg-secondary hover:text-editor-fg-primary"; const ICON_BUTTON_PLAIN_PRESSED_CLASS = - "bg-[color-mix(in_srgb,var(--accent)_16%,transparent)] text-accent " + - "hover:text-accent hover:bg-[color-mix(in_srgb,var(--accent)_16%,transparent)]"; + "bg-[color-mix(in_srgb,var(--brand)_16%,transparent)] text-brand " + + "hover:text-brand hover:bg-[color-mix(in_srgb,var(--brand)_16%,transparent)]"; // Appears on hover or keyboard focus. Decorative only — the button keeps its // aria-label so screen readers do not double-announce. diff --git a/src/components/ui/ProgressBar.tsx b/src/components/ui/ProgressBar.tsx index e21e567..52614ae 100644 --- a/src/components/ui/ProgressBar.tsx +++ b/src/components/ui/ProgressBar.tsx @@ -32,10 +32,10 @@ export function ProgressBar({ {...props} > {indeterminate ? ( - + ) : ( )} diff --git a/src/components/ui/button-group.tsx b/src/components/ui/button-group.tsx new file mode 100644 index 0000000..99c8f4a --- /dev/null +++ b/src/components/ui/button-group.tsx @@ -0,0 +1,87 @@ +import { mergeProps } from "@base-ui/react/merge-props" +import { useRender } from "@base-ui/react/use-render" +import { cva, type VariantProps } from "class-variance-authority" +import { cn } from "cn" + +import { Separator } from "@/components/ui/separator" + +const buttonGroupVariants = cva( + "flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1", + { + variants: { + orientation: { + horizontal: + "*:data-slot:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg! [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0", + vertical: + "flex-col *:data-slot:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-lg! [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0", + }, + }, + defaultVariants: { + orientation: "horizontal", + }, + } +) + +function ButtonGroup({ + className, + orientation, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function ButtonGroupText({ + className, + render, + ...props +}: useRender.ComponentProps<"div">) { + return useRender({ + defaultTagName: "div", + props: mergeProps<"div">( + { + className: cn( + "flex items-center gap-2 rounded-lg border bg-muted px-2.5 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4", + className + ), + }, + props + ), + render, + state: { + slot: "button-group-text", + }, + }) +} + +function ButtonGroupSeparator({ + className, + orientation = "vertical", + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + ButtonGroup, + ButtonGroupSeparator, + ButtonGroupText, + buttonGroupVariants, +} diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..997e0c7 --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,57 @@ +import { Button as ButtonPrimitive } from "@base-ui/react/button" +import { cva, type VariantProps } from "class-variance-authority" +import { cn } from "cn" + +const buttonVariants = cva( + "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/80", + outline: + "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", + ghost: + "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50", + destructive: + "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: + "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", + sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", + lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + icon: "size-8", + "icon-xs": + "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3", + "icon-sm": + "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg", + "icon-lg": "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant = "default", + size = "default", + ...props +}: ButtonPrimitive.Props & VariantProps) { + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx new file mode 100644 index 0000000..d69c134 --- /dev/null +++ b/src/components/ui/separator.tsx @@ -0,0 +1,24 @@ +"use client" + +import { Separator as SeparatorPrimitive } from "@base-ui/react/separator" +import { cn } from "cn" + +function Separator({ + className, + orientation = "horizontal", + ...props +}: SeparatorPrimitive.Props) { + return ( + + ) +} + +export { Separator } diff --git a/src/components/ui/toggle-group.tsx b/src/components/ui/toggle-group.tsx new file mode 100644 index 0000000..fc7757c --- /dev/null +++ b/src/components/ui/toggle-group.tsx @@ -0,0 +1,89 @@ +"use client" + +import * as React from "react" +import { Toggle as TogglePrimitive } from "@base-ui/react/toggle" +import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group" +import { type VariantProps } from "class-variance-authority" +import { cn } from "cn" + +import { toggleVariants } from "@/components/ui/toggle" + +const ToggleGroupContext = React.createContext< + VariantProps & { + spacing?: number + orientation?: "horizontal" | "vertical" + } +>({ + size: "default", + variant: "default", + spacing: 2, + orientation: "horizontal", +}) + +function ToggleGroup({ + className, + variant, + size, + spacing = 2, + orientation = "horizontal", + children, + ...props +}: ToggleGroupPrimitive.Props & + VariantProps & { + spacing?: number + orientation?: "horizontal" | "vertical" + }) { + return ( + + + {children} + + + ) +} + +function ToggleGroupItem({ + className, + children, + variant = "default", + size = "default", + ...props +}: TogglePrimitive.Props & VariantProps) { + const context = React.useContext(ToggleGroupContext) + + return ( + + {children} + + ) +} + +export { ToggleGroup, ToggleGroupItem } diff --git a/src/components/ui/toggle.tsx b/src/components/ui/toggle.tsx new file mode 100644 index 0000000..f38073f --- /dev/null +++ b/src/components/ui/toggle.tsx @@ -0,0 +1,44 @@ +"use client" + +import { Toggle as TogglePrimitive } from "@base-ui/react/toggle" +import { cva, type VariantProps } from "class-variance-authority" +import { cn } from "cn" + +const toggleVariants = cva( + "group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + { + variants: { + variant: { + default: "bg-transparent", + outline: "border border-input bg-transparent hover:bg-muted", + }, + size: { + default: + "h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + sm: "h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", + lg: "h-9 min-w-9 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Toggle({ + className, + variant = "default", + size = "default", + ...props +}: TogglePrimitive.Props & VariantProps) { + return ( + + ) +} + +export { Toggle, toggleVariants } diff --git a/src/lib/map/smoothWheelZoom.ts b/src/lib/map/smoothWheelZoom.ts index 6951312..f155aca 100644 --- a/src/lib/map/smoothWheelZoom.ts +++ b/src/lib/map/smoothWheelZoom.ts @@ -1,4 +1,9 @@ -import type { LngLat, Map as MapLibreMap, MapLibreEvent } from "maplibre-gl"; +import type { + LngLat, + Map as MapLibreMap, + MapLibreEvent, + PointLike, +} from "maplibre-gl"; // Wheel pixels per zoom level. A mouse notch (~100px) moves half a level. const PIXELS_PER_ZOOM_LEVEL = 200; @@ -9,6 +14,13 @@ const PINCH_MULTIPLIER = 8; const GLIDE_TAU_MS = 90; const SETTLE_EPSILON = 0.002; const OWN_EVENT = { smoothWheelZoom: true }; +// Pointing at space, `unproject` returns a location on the far side of the +// globe. Anchoring to it spins the globe away, so only anchor on locations +// comfortably on the visible face. +const MAX_ANCHOR_DISTANCE_DEG = 70; +// A single frame never needs a bigger correction than this; anything larger +// means the anchor cannot be held, and applying it would jump the map. +const MAX_CORRECTION_PX = 60; type WheelLike = Pick; @@ -24,6 +36,25 @@ export function glideStep(current: number, target: number, dtMs: number) { return current + (target - current) * (1 - Math.exp(-dtMs / GLIDE_TAU_MS)); } +type LonLat = [number, number]; + +function toVector([lon, lat]: LonLat): [number, number, number] { + const phi = (lat * Math.PI) / 180; + const lambda = (lon * Math.PI) / 180; + return [ + Math.cos(phi) * Math.cos(lambda), + Math.cos(phi) * Math.sin(lambda), + Math.sin(phi), + ]; +} + +export function angularDistanceDeg(a: LonLat, b: LonLat): number { + const [ax, ay, az] = toVector(a); + const [bx, by, bz] = toVector(b); + const dot = Math.min(1, Math.max(-1, ax * bx + ay * by + az * bz)); + return (Math.acos(dot) * 180) / Math.PI; +} + /** * Replaces MapLibre's scroll zoom, which applies trackpad-classified wheel * events instantly and so steps on many mice. Every wheel event moves a @@ -33,7 +64,7 @@ export function attachSmoothWheelZoom(map: MapLibreMap): () => void { map.scrollZoom.disable(); const container = map.getCanvasContainer(); let target: number | null = null; - let around: LngLat | null = null; + let anchor: { lngLat: LngLat; point: [number, number] } | null = null; let frame = 0; let lastTime = 0; @@ -43,13 +74,30 @@ export function attachSmoothWheelZoom(map: MapLibreMap): () => void { target = null; }; + // `easeTo({ around })` is ignored under the globe projection, so keep the + // cursor's location in place by hand: zoom, then shift the centre by how + // far that location drifted on screen. Zooming out on the globe stays + // centred, since a shrinking globe cannot keep a point under the cursor. + const zoomAround = (zoom: number) => { + const zoomingOut = zoom < map.getZoom(); + map.jumpTo({ zoom }, OWN_EVENT); + if (!anchor) return; + if (zoomingOut && map.getProjection().type === "globe") return; + const drifted = map.project(anchor.lngLat); + const dx = drifted.x - anchor.point[0]; + const dy = drifted.y - anchor.point[1]; + if (Math.hypot(dx, dy) > MAX_CORRECTION_PX) return; + const centre = map.project(map.getCenter()); + const shifted: PointLike = [centre.x + dx, centre.y + dy]; + map.jumpTo({ center: map.unproject(shifted) }, OWN_EVENT); + }; + const tick = (time: number) => { - if (target === null || around === null) return stop(); + if (target === null) return stop(); const current = map.getZoom(); const done = Math.abs(target - current) < SETTLE_EPSILON; - const zoom = done ? target : glideStep(current, target, time - lastTime); + zoomAround(done ? target : glideStep(current, target, time - lastTime)); lastTime = time; - map.easeTo({ zoom, around, duration: 0 }, OWN_EVENT); if (done) return stop(); frame = requestAnimationFrame(tick); }; @@ -57,10 +105,16 @@ export function attachSmoothWheelZoom(map: MapLibreMap): () => void { const onWheel = (event: WheelEvent) => { event.preventDefault(); const rect = container.getBoundingClientRect(); - around = map.unproject([ + const point: [number, number] = [ event.clientX - rect.left, event.clientY - rect.top, - ]); + ]; + const lngLat = map.unproject(point); + const onVisibleFace = + map.getProjection().type !== "globe" || + angularDistanceDeg(lngLat.toArray(), map.getCenter().toArray()) < + MAX_ANCHOR_DISTANCE_DEG; + anchor = onVisibleFace ? { lngLat, point } : null; const base = target ?? map.getZoom(); target = Math.min( map.getMaxZoom(), diff --git a/src/lib/theme.ts b/src/lib/theme.ts index 06cda4d..83836af 100644 --- a/src/lib/theme.ts +++ b/src/lib/theme.ts @@ -25,6 +25,7 @@ export function storeTheme(theme: Theme): void { export function applyTheme(theme: Theme): void { document.documentElement.classList.toggle("light", theme === "light"); + document.documentElement.classList.toggle("dark", theme === "dark"); } export const themeInitScript = ` @@ -38,6 +39,7 @@ export const themeInitScript = ` ? "light" : "dark"; document.documentElement.classList.toggle("light", theme === "light"); + document.documentElement.classList.toggle("dark", theme === "dark"); } catch (e) {} })(); `.trim(); diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..f815ded --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1 @@ +export { cn } from "cn" From 145d9f229ce0be3532817a926eaca596e0f49fc7 Mon Sep 17 00:00:00 2001 From: Anastasiia Ivanchenko Date: Sun, 27 Sep 2026 23:33:05 +0300 Subject: [PATCH 6/8] Update recharts version and clean up globals.css; remove unused components and streamline layout --- package-lock.json | 28 +- package.json | 2 +- src/app/globals.css | 160 +--- src/app/layout.tsx | 8 +- src/components/about/AboutContent.tsx | 77 -- src/components/layout/EditorShell.tsx | 153 ---- src/components/layout/EditorViewTabs.tsx | 79 -- src/components/layout/Footer.tsx | 49 -- src/components/layout/MapToolbar.tsx | 72 +- src/components/layout/Nav.tsx | 133 ---- src/components/layout/SidebarResizer.tsx | 139 ---- src/components/map/ColormapPicker.tsx | 84 --- src/components/map/DownloadButton.tsx | 508 ------------- src/components/map/EarthMap.tsx | 732 ------------------- src/components/map/ExportMapStage.tsx | 171 ----- src/components/map/ExportStage.tsx | 165 ----- src/components/map/FingerprintPlot.tsx | 512 ------------- src/components/map/GlobeSelectionOverlay.tsx | 130 +--- src/components/map/MapControls.tsx | 30 +- src/components/map/MapReadout.tsx | 387 ---------- src/components/map/MapSearch.tsx | 255 +++---- src/components/map/MapSideControls.tsx | 156 ---- src/components/map/MapTour.tsx | 193 +++-- src/components/map/PatchMenu.tsx | 167 ----- src/components/map/PlotSkeleton.tsx | 38 - src/components/map/ReliefMap.tsx | 334 ++++++--- src/components/map/TimeSeriesPlot.tsx | 107 --- src/components/map/UserPositionMarker.tsx | 7 +- src/components/map/ViewModeTabs.tsx | 26 + src/components/map/YearSelector.tsx | 144 ---- src/components/map/timeSeriesChartConfig.ts | 31 - src/components/nav/Brand.tsx | 35 - src/components/nav/EditorAboutDropdown.tsx | 71 -- src/components/nav/NavActions.tsx | 20 - src/components/panels/CellExport.tsx | 141 ++++ src/components/panels/CellPanel.tsx | 62 ++ src/components/panels/CellView.tsx | 206 ++++++ src/components/panels/ColormapSelect.tsx | 70 ++ src/components/panels/FloatingPanel.tsx | 27 + src/components/panels/HelpPanel.tsx | 109 +++ src/components/panels/SettingsPanel.tsx | 136 ++++ src/components/panels/YearPicker.tsx | 77 ++ src/components/plots/FingerprintPlot.tsx | 188 +++++ src/components/plots/TimeSeriesPlot.tsx | 105 +++ src/components/ui/IconButton.tsx | 130 ---- src/components/ui/ProgressBar.tsx | 44 -- src/components/ui/ThemeToggle.tsx | 17 - src/components/ui/alert.tsx | 75 ++ src/components/ui/avatar.tsx | 108 +++ src/components/ui/button-group.tsx | 28 +- src/components/ui/button.tsx | 14 +- src/components/ui/card.tsx | 102 +++ src/components/ui/chart.tsx | 372 ++++++++++ src/components/ui/combobox.tsx | 300 ++++++++ src/components/ui/empty.tsx | 103 +++ src/components/ui/field.tsx | 238 ++++++ src/components/ui/input-group.tsx | 158 ++++ src/components/ui/input.tsx | 19 + src/components/ui/item.tsx | 201 +++++ src/components/ui/kbd.tsx | 26 + src/components/ui/label.tsx | 19 + src/components/ui/progress.tsx | 82 +++ src/components/ui/radio-group.tsx | 37 + src/components/ui/scroll-area.tsx | 54 ++ src/components/ui/segmented-tabs.tsx | 54 ++ src/components/ui/select.tsx | 201 +++++ src/components/ui/separator.tsx | 12 +- src/components/ui/skeleton.tsx | 13 + src/components/ui/switch.tsx | 31 + src/components/ui/tabs.tsx | 81 ++ src/components/ui/textarea.tsx | 17 + src/components/ui/toggle-group.tsx | 38 +- src/components/ui/toggle.tsx | 16 +- src/hooks/useCellExport.ts | 247 +++++++ src/hooks/useCellSeries.ts | 224 ++++++ src/hooks/useGeolocation.ts | 46 ++ src/hooks/useMapSettings.ts | 30 + src/hooks/useSeriesDisplay.ts | 79 ++ src/icons/AutoZoomIcon.tsx | 25 - src/icons/CrosshairIcon.tsx | 21 - src/icons/DownloadIcon.tsx | 19 - src/icons/FingerprintIcon.tsx | 23 - src/icons/GitHubIcon.tsx | 13 - src/icons/GlobeIcon.tsx | 19 - src/icons/LocateIcon.tsx | 17 - src/icons/MapIcon.tsx | 19 - src/icons/MoonIcon.tsx | 17 - src/icons/PanelLeftIcon.tsx | 18 - src/icons/PatchIcon.tsx | 21 - src/icons/QuestionMarkIcon.tsx | 19 - src/icons/SearchIcon.tsx | 18 - src/icons/SunIcon.tsx | 17 - src/lib/constants/footer.ts | 2 - src/lib/constants/tour.ts | 32 +- src/lib/export/capture.ts | 85 +-- src/lib/export/captureFingerprint.ts | 58 ++ src/lib/export/captureMap.ts | 104 +++ src/lib/export/captureTimeSeries.tsx | 60 ++ src/lib/export/csv.test.ts | 8 +- src/lib/export/fingerprintImage.ts | 14 +- src/lib/export/fingerprintSquareLogo.test.ts | 5 +- src/lib/export/fingerprintSquareLogo.ts | 14 +- src/lib/export/pdf.test.ts | 14 +- src/lib/export/pdf.ts | 2 +- src/lib/export/plotSize.ts | 5 + src/lib/export/provenance.ts | 6 +- src/lib/export/rows.test.ts | 8 +- src/lib/export/rows.ts | 6 +- src/lib/map/selectionLayers.ts | 103 +++ src/lib/plots/chartTheme.ts | 16 + src/lib/plots/fingerprint.ts | 285 ++++++++ src/lib/sidebar.test.ts | 68 -- src/lib/sidebar.ts | 161 ---- src/lib/tour.test.ts | 33 +- src/lib/tour.ts | 23 +- src/lib/zarr/ZarrChunkReader.test.ts | 32 + src/lib/zarr/ZarrChunkReader.ts | 33 +- 117 files changed, 5451 insertions(+), 5532 deletions(-) delete mode 100644 src/components/about/AboutContent.tsx delete mode 100644 src/components/layout/EditorShell.tsx delete mode 100644 src/components/layout/EditorViewTabs.tsx delete mode 100644 src/components/layout/Footer.tsx delete mode 100644 src/components/layout/Nav.tsx delete mode 100644 src/components/layout/SidebarResizer.tsx delete mode 100644 src/components/map/ColormapPicker.tsx delete mode 100644 src/components/map/DownloadButton.tsx delete mode 100644 src/components/map/EarthMap.tsx delete mode 100644 src/components/map/ExportMapStage.tsx delete mode 100644 src/components/map/ExportStage.tsx delete mode 100644 src/components/map/FingerprintPlot.tsx delete mode 100644 src/components/map/MapReadout.tsx delete mode 100644 src/components/map/MapSideControls.tsx delete mode 100644 src/components/map/PatchMenu.tsx delete mode 100644 src/components/map/PlotSkeleton.tsx delete mode 100644 src/components/map/TimeSeriesPlot.tsx create mode 100644 src/components/map/ViewModeTabs.tsx delete mode 100644 src/components/map/YearSelector.tsx delete mode 100644 src/components/map/timeSeriesChartConfig.ts delete mode 100644 src/components/nav/Brand.tsx delete mode 100644 src/components/nav/EditorAboutDropdown.tsx delete mode 100644 src/components/nav/NavActions.tsx create mode 100644 src/components/panels/CellExport.tsx create mode 100644 src/components/panels/CellPanel.tsx create mode 100644 src/components/panels/CellView.tsx create mode 100644 src/components/panels/ColormapSelect.tsx create mode 100644 src/components/panels/FloatingPanel.tsx create mode 100644 src/components/panels/HelpPanel.tsx create mode 100644 src/components/panels/SettingsPanel.tsx create mode 100644 src/components/panels/YearPicker.tsx create mode 100644 src/components/plots/FingerprintPlot.tsx create mode 100644 src/components/plots/TimeSeriesPlot.tsx delete mode 100644 src/components/ui/IconButton.tsx delete mode 100644 src/components/ui/ProgressBar.tsx delete mode 100644 src/components/ui/ThemeToggle.tsx create mode 100644 src/components/ui/alert.tsx create mode 100644 src/components/ui/avatar.tsx create mode 100644 src/components/ui/card.tsx create mode 100644 src/components/ui/chart.tsx create mode 100644 src/components/ui/combobox.tsx create mode 100644 src/components/ui/empty.tsx create mode 100644 src/components/ui/field.tsx create mode 100644 src/components/ui/input-group.tsx create mode 100644 src/components/ui/input.tsx create mode 100644 src/components/ui/item.tsx create mode 100644 src/components/ui/kbd.tsx create mode 100644 src/components/ui/label.tsx create mode 100644 src/components/ui/progress.tsx create mode 100644 src/components/ui/radio-group.tsx create mode 100644 src/components/ui/scroll-area.tsx create mode 100644 src/components/ui/segmented-tabs.tsx create mode 100644 src/components/ui/select.tsx create mode 100644 src/components/ui/skeleton.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/components/ui/tabs.tsx create mode 100644 src/components/ui/textarea.tsx create mode 100644 src/hooks/useCellExport.ts create mode 100644 src/hooks/useCellSeries.ts create mode 100644 src/hooks/useGeolocation.ts create mode 100644 src/hooks/useMapSettings.ts create mode 100644 src/hooks/useSeriesDisplay.ts delete mode 100644 src/icons/AutoZoomIcon.tsx delete mode 100644 src/icons/CrosshairIcon.tsx delete mode 100644 src/icons/DownloadIcon.tsx delete mode 100644 src/icons/FingerprintIcon.tsx delete mode 100644 src/icons/GitHubIcon.tsx delete mode 100644 src/icons/GlobeIcon.tsx delete mode 100644 src/icons/LocateIcon.tsx delete mode 100644 src/icons/MapIcon.tsx delete mode 100644 src/icons/MoonIcon.tsx delete mode 100644 src/icons/PanelLeftIcon.tsx delete mode 100644 src/icons/PatchIcon.tsx delete mode 100644 src/icons/QuestionMarkIcon.tsx delete mode 100644 src/icons/SearchIcon.tsx delete mode 100644 src/icons/SunIcon.tsx delete mode 100644 src/lib/constants/footer.ts create mode 100644 src/lib/export/captureFingerprint.ts create mode 100644 src/lib/export/captureMap.ts create mode 100644 src/lib/export/captureTimeSeries.tsx create mode 100644 src/lib/export/plotSize.ts create mode 100644 src/lib/map/selectionLayers.ts create mode 100644 src/lib/plots/chartTheme.ts create mode 100644 src/lib/plots/fingerprint.ts delete mode 100644 src/lib/sidebar.test.ts delete mode 100644 src/lib/sidebar.ts diff --git a/package-lock.json b/package-lock.json index b5745e8..2d1909d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "react-dom": "19.3.0", "react-joyride": "^3.2.0", "react-map-gl": "^8.1.3", - "recharts": "^3.10.1", + "recharts": "^3.8.0", "shadcn": "^4.21.0", "tw-animate-css": "^1.4.0", "write-excel-file": "^4.1.1", @@ -9771,9 +9771,9 @@ } }, "node_modules/recharts": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz", - "integrity": "sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.8.0.tgz", + "integrity": "sha512-Z/m38DX3L73ExO4Tpc9/iZWHmHnlzWG4njQbxsF5aSjwqmHNDDIm0rdEBArkwsBvR8U6EirlEHiQNYWCVh9sGQ==", "license": "MIT", "workspaces": [ "www" @@ -9784,9 +9784,9 @@ "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", - "immer": "^11.1.8", + "immer": "^10.1.1", "react-redux": "8.x.x || 9.x.x", - "reselect": "5.2.0", + "reselect": "5.1.1", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" @@ -9800,6 +9800,22 @@ "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/recharts/node_modules/immer": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/recharts/node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", diff --git a/package.json b/package.json index 60da265..28875c2 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "react-dom": "19.3.0", "react-joyride": "^3.2.0", "react-map-gl": "^8.1.3", - "recharts": "^3.10.1", + "recharts": "^3.8.0", "shadcn": "^4.21.0", "tw-animate-css": "^1.4.0", "write-excel-file": "^4.1.1", diff --git a/src/app/globals.css b/src/app/globals.css index 3eb0d27..a628c21 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -11,7 +11,7 @@ Design tokens live as CSS custom properties so the theme can be swapped at runtime by toggling `.light` on :root. They are aliased into Tailwind's theme via `@theme inline` below, which keeps the - generated utilities (bg-accent, text-editor-fg-primary, …) pointing + generated utilities (bg-brand, text-editor-fg-primary, …) pointing at the live variables so they react to the theme switch. ============================================================ */ :root { @@ -61,34 +61,6 @@ --editor-radius-lg: 16px; /* Lovable rounded-4 on preview frame */ --editor-shadow: 0 18px 40px -24px rgba(0, 0, 0, 0.65); - /* Width of the open left panel, matching SIDEBAR_DEFAULT_WIDTH in - src/lib/sidebar.ts. The drag handle overwrites this inline on the root - element, and the boot script restores the stored value before first - paint. */ - --editor-sidebar-width: 400px; - /* What the layout actually reserves: zero once the panel is collapsed, so - the panel keeps its own width and slides out from under the map. */ - --editor-sidebar-track: var(--editor-sidebar-width); - /* shadcn tokens follow the editor tokens, which already switch with the - theme, so the .dark block below only carries what has no editor twin. */ - --background: var(--editor-bg-primary); - --foreground: var(--editor-fg-primary); - --card: var(--editor-bg-primary); - --card-foreground: var(--editor-fg-primary); - --popover: var(--editor-bg-depth); - --popover-foreground: var(--editor-fg-primary); - --primary: var(--brand); - --primary-foreground: var(--editor-bg-primary); - --secondary: var(--editor-bg-secondary); - --secondary-foreground: var(--editor-fg-primary); - --muted: var(--editor-bg-secondary); - --muted-foreground: var(--editor-fg-tertiary); - --accent: var(--editor-bg-secondary); - --accent-foreground: var(--editor-fg-primary); - --border: var(--editor-border); - --destructive: oklch(0.577 0.245 27.325); - --input: var(--editor-border); - --ring: var(--accent-solid); --chart-1: oklch(0.87 0 0); --chart-2: oklch(0.556 0 0); --chart-3: oklch(0.439 0 0); @@ -105,20 +77,33 @@ --sidebar-ring: oklch(0.708 0 0); } -:root[data-sidebar-collapsed="true"] { - --editor-sidebar-track: 0px; -} - -/* A panel clipped to zero width still holds its tab stops, so fold it out of - the document too. visibility flips discretely at the ends of a transition, - which leaves the slide-out itself visible on the way. */ -@media (min-width: 901px) { - :root[data-sidebar-collapsed="true"] .editor-sidebar { - visibility: hidden; - } +/* shadcn tokens point at the editor tokens, so they switch with the theme. + Redeclared under .theme-light because the export renders light plots + while the page itself may be dark. */ +:root, +.theme-light { + --background: var(--editor-bg-primary); + --foreground: var(--editor-fg-primary); + --card: var(--editor-bg-primary); + --card-foreground: var(--editor-fg-primary); + --popover: var(--editor-bg-primary); + --popover-foreground: var(--editor-fg-primary); + --primary: var(--brand); + --primary-foreground: var(--editor-bg-primary); + --secondary: var(--editor-bg-secondary); + --secondary-foreground: var(--editor-fg-primary); + --muted: var(--editor-bg-secondary); + --muted-foreground: var(--editor-fg-tertiary); + --accent: color-mix(in srgb, var(--editor-fg-primary) 8%, var(--editor-bg-primary)); + --accent-foreground: var(--editor-fg-primary); + --border: var(--editor-border); + --destructive: oklch(0.577 0.245 27.325); + --input: var(--editor-border); + --ring: var(--accent-solid); } -:root.light { +:root.light, +.theme-light { --bg: #ffffff; --bg-deep: #f4f4f2; --surface: #ffffff; @@ -201,6 +186,7 @@ --color-ring: var(--ring); --color-input: var(--input); --color-destructive: var(--destructive); + --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-muted-foreground: var(--muted-foreground); --color-muted: var(--muted); @@ -250,12 +236,6 @@ overscroll-behavior: none; } - /* The footer is rendered by the root layout on every route, but the app - only ever shows the editor, which hides it. */ - body:has(.editor-shell) .site-footer { - display: none; - } - a { color: inherit; text-decoration: none; @@ -272,92 +252,6 @@ } } -/* ============================================================ - Custom classes — only for what utilities can't express cleanly: - the theme-driven icon swap and styling the third-party MapLibre - canvas inside our stage. - ============================================================ */ - -/* While the seam is being dragged the pointer is captured, so the col-resize - cursor has to be forced across the whole document and text selection - suppressed or the drag paints a blue smear over the panel. */ -:root[data-sidebar-resizing="true"] { - cursor: col-resize; -} - -:root[data-sidebar-resizing="true"] * { - cursor: col-resize !important; - user-select: none !important; -} - -/* Collapsing eases; dragging must not, or the panel lags a frame behind the - seam the whole way across. */ -:root[data-sidebar-resizing="true"] .editor-sidebar { - transition: none; -} - -.theme-icon-moon { - display: none; -} - -:root.light .theme-icon-moon { - display: block; -} - -:root.light .theme-icon-sun { - display: none; -} - -.map-stage .maplibregl-map { - position: absolute; - inset: 0; - width: 100%; - height: 100%; -} - -.map-stage canvas { - outline: none; -} - -@keyframes zoomToSelectionIn { - from { - opacity: 0; - transform: scale(0.8); - } - to { - opacity: 1; - transform: scale(1); - } -} - -/* Indeterminate progress: a segment sweeping across the track while a - download is in flight. We show motion rather than a fabricated percent, - since the pixel fetch is a single slice request with no per-chunk hook. */ -@keyframes progressIndeterminate { - from { - transform: translateX(-100%); - } - to { - transform: translateX(250%); - } -} - -.progress-bar-indeterminate { - animation: progressIndeterminate 1.4s cubic-bezier(0.4, 0, 0.2, 1) infinite; -} - -@media (prefers-reduced-motion: reduce) { - .progress-bar-indeterminate { - animation: none; - width: 100%; - opacity: 0.55; - } -} - -/* The guide draws its own outline around the lit area (see MapTour), because - Joyride's overlay is a single path holding the cutout and an outer rectangle - the size of the viewport: stroking it frames the whole page. */ - .dark { --destructive: oklch(0.704 0.191 22.216); --chart-1: oklch(0.87 0 0); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8f6e65a..ccf1578 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,9 +1,7 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import { SITE_DESCRIPTION, SITE_NAME } from "@/lib/constants/site"; -import { Footer } from "@/components/layout/Footer"; import { ThemeProvider } from "@/providers/ThemeProvider"; -import { sidebarInitScript } from "@/lib/sidebar"; import { themeInitScript } from "@/lib/theme"; import "./globals.css"; @@ -53,13 +51,9 @@ export default function RootLayout({ >