json-maps 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # json-maps
2
+
3
+ A declarative JSON spec for interactive maps. Write JSON, get a map.
4
+
5
+ Built on [MapLibre GL](https://maplibre.org/) (free, no API key) with [CARTO](https://carto.com/) basemaps and React.
6
+
7
+ ## Quick Start
8
+
9
+ ```json
10
+ {
11
+ "basemap": "dark",
12
+ "center": [77.59, 12.97],
13
+ "zoom": 12,
14
+ "pitch": 45
15
+ }
16
+ ```
17
+
18
+ Every field is optional. An empty `{}` gives you a light basemap at world view.
19
+
20
+ ## Spec
21
+
22
+ | Field | Type | Description |
23
+ |-------|------|-------------|
24
+ | `basemap` | `"light"` \| `"dark"` \| `"streets"` \| URL | Map style |
25
+ | `center` | `[lng, lat]` | Map center |
26
+ | `zoom` | `number` | Zoom level 0-24 |
27
+ | `pitch` | `number` | Camera tilt 0-85 |
28
+ | `bearing` | `number` | Camera rotation -180 to 180 |
29
+ | `bounds` | `[west, south, east, north]` | Fit to bounding box |
30
+
31
+ More coming: markers, layers, controls, widgets, color system, DuckDB WASM data engine, event system.
32
+
33
+ ## Development
34
+
35
+ ```bash
36
+ npm install
37
+ npm run dev
38
+ ```
39
+
40
+ ## License
41
+
42
+ MIT
@@ -0,0 +1,107 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface PopupSpec {
4
+ title?: string;
5
+ description?: string;
6
+ image?: string;
7
+ }
8
+ interface MarkerSpec {
9
+ coordinates: [number, number];
10
+ color?: string;
11
+ icon?: string;
12
+ label?: string;
13
+ tooltip?: string;
14
+ popup?: string | PopupSpec;
15
+ draggable?: boolean;
16
+ }
17
+ interface ContinuousColor {
18
+ type: "continuous";
19
+ attr: string;
20
+ palette: string;
21
+ domain?: [number, number];
22
+ nullColor?: string;
23
+ }
24
+ interface CategoricalColor {
25
+ type: "categorical";
26
+ attr: string;
27
+ palette: string;
28
+ categories?: string[];
29
+ nullColor?: string;
30
+ }
31
+ type ColorValue = string | ContinuousColor | CategoricalColor;
32
+ interface ContinuousSize {
33
+ type: "continuous";
34
+ attr: string;
35
+ domain: [number, number];
36
+ range: [number, number];
37
+ }
38
+ type SizeValue = number | ContinuousSize;
39
+ interface LayerStyle {
40
+ fillColor?: ColorValue;
41
+ pointColor?: ColorValue;
42
+ lineColor?: ColorValue;
43
+ lineWidth?: number;
44
+ pointRadius?: SizeValue;
45
+ opacity?: number;
46
+ }
47
+ interface ClusterOptions {
48
+ radius?: number;
49
+ maxZoom?: number;
50
+ minPoints?: number;
51
+ colors?: [string, string, string];
52
+ }
53
+ interface GeoJsonLayerSpec {
54
+ type: "geojson";
55
+ data: string | Record<string, unknown>;
56
+ style?: LayerStyle;
57
+ tooltip?: string[];
58
+ cluster?: boolean;
59
+ clusterOptions?: ClusterOptions;
60
+ }
61
+ interface RouteStyle {
62
+ color?: string;
63
+ width?: number;
64
+ opacity?: number;
65
+ dashed?: boolean;
66
+ }
67
+ interface RouteLayerSpec {
68
+ type: "route";
69
+ coordinates: [number, number][];
70
+ style?: RouteStyle;
71
+ }
72
+ type LayerSpec = GeoJsonLayerSpec | RouteLayerSpec;
73
+ type ControlPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right";
74
+ interface ControlsSpec {
75
+ zoom?: boolean;
76
+ compass?: boolean;
77
+ fullscreen?: boolean;
78
+ locate?: boolean;
79
+ position?: ControlPosition;
80
+ }
81
+ interface LegendSpec {
82
+ layer: string;
83
+ title?: string;
84
+ position?: ControlPosition;
85
+ }
86
+ interface MapSpec {
87
+ basemap?: "light" | "dark" | "streets" | (string & {});
88
+ center?: [number, number];
89
+ zoom?: number;
90
+ pitch?: number;
91
+ bearing?: number;
92
+ bounds?: [number, number, number, number];
93
+ markers?: Record<string, MarkerSpec>;
94
+ layers?: Record<string, LayerSpec>;
95
+ controls?: ControlsSpec;
96
+ legend?: Record<string, LegendSpec>;
97
+ }
98
+ declare const BASEMAP_STYLES: Record<string, string>;
99
+ declare function resolveBasemapStyle(basemap?: string): string | null;
100
+
101
+ declare function MapRenderer({ spec }: {
102
+ spec: MapSpec;
103
+ }): react_jsx_runtime.JSX.Element;
104
+
105
+ declare const PALETTES: Record<string, string[]>;
106
+
107
+ export { BASEMAP_STYLES, type CategoricalColor, type ClusterOptions, type ColorValue, type ContinuousColor, type ContinuousSize, type ControlPosition, type ControlsSpec, type GeoJsonLayerSpec, type LayerSpec, type LayerStyle, type LegendSpec, MapRenderer, type MapSpec, type MarkerSpec, PALETTES, type PopupSpec, type RouteLayerSpec, type RouteStyle, type SizeValue, resolveBasemapStyle };
package/dist/index.js ADDED
@@ -0,0 +1,890 @@
1
+ "use client";
2
+
3
+ // components/map-renderer.tsx
4
+ import { Fragment, useEffect, useRef, useCallback, useState } from "react";
5
+ import { createPortal } from "react-dom";
6
+ import maplibregl from "maplibre-gl";
7
+ import "maplibre-gl/dist/maplibre-gl.css";
8
+
9
+ // lib/spec.ts
10
+ var BASEMAP_STYLES = {
11
+ light: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
12
+ dark: "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json",
13
+ streets: "https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json"
14
+ };
15
+ function resolveBasemapStyle(basemap) {
16
+ if (!basemap) return BASEMAP_STYLES.light;
17
+ if (BASEMAP_STYLES[basemap]) return BASEMAP_STYLES[basemap];
18
+ if (basemap.startsWith("http")) return basemap;
19
+ return null;
20
+ }
21
+
22
+ // lib/palettes.ts
23
+ var PALETTES = {
24
+ // Sequential
25
+ Burg: ["#ffc6c4", "#f4a3a8", "#e38191", "#cc607d", "#ad466c", "#8b3058", "#672044"],
26
+ RedOr: ["#f6d2a9", "#f5b78e", "#f19c7c", "#ea8171", "#dd686c", "#ca5268", "#b13f64"],
27
+ OrYel: ["#ecda9a", "#efc47e", "#f3ad6a", "#f7945d", "#f97b57", "#f66356", "#ee4d5a"],
28
+ Peach: ["#fde0c5", "#facba6", "#f8b58b", "#f2a275", "#e78f6a", "#d07c5e", "#b86b53"],
29
+ PinkYl: ["#fef6b5", "#ffdd9a", "#ffc285", "#ffa679", "#fa8a76", "#f16d7a", "#e15383"],
30
+ Mint: ["#e4f1e1", "#b4d9cc", "#89c0b6", "#63a6a0", "#448c8a", "#287274", "#0d585f"],
31
+ BluGrn: ["#c4e6c3", "#96d2a4", "#6dbc90", "#4da284", "#36877a", "#266c6e", "#1d4f60"],
32
+ DarkMint: ["#d2fbd4", "#a5dbc2", "#7bbcb0", "#559c9e", "#3a7c89", "#235d72", "#123f5a"],
33
+ Emrld: ["#d3f2a3", "#97e196", "#6cc08b", "#4c9b82", "#217a79", "#105965", "#074050"],
34
+ BluYl: ["#f7feae", "#b7e6a5", "#7ccba2", "#46aea0", "#089099", "#00718b", "#045275"],
35
+ Teal: ["#d1eeea", "#a8dbd9", "#85c4c9", "#68abb8", "#4f90a6", "#3b738f", "#2a5674"],
36
+ Purp: ["#f3e0f7", "#d1bbf0", "#b18fd3", "#8c6bb1", "#6c4f8e", "#4e3a6e", "#2f2047"],
37
+ Sunset: ["#f3e79b", "#fac484", "#f8a07e", "#eb7f86", "#ce6693", "#a059a0", "#5c53a5"],
38
+ SunsetDark: ["#fcde9c", "#faa476", "#f0746e", "#e34f6f", "#dc3977", "#b9257a", "#7c1d6f"],
39
+ Magenta: ["#f3cbd3", "#eaa9bd", "#dd88ac", "#ca699d", "#b14d8e", "#91357d", "#6c2167"],
40
+ // Diverging
41
+ TealRose: ["#009392", "#39b185", "#9ccb86", "#e9e29c", "#eeb479", "#e88471", "#cf597e"],
42
+ Geyser: ["#008080", "#70a494", "#b4c8a8", "#f6edbd", "#edbb8a", "#de8a5a", "#ca562c"],
43
+ Temps: ["#009392", "#39b185", "#9ccb86", "#e9e29c", "#edbb8a", "#e68b5e", "#d43d51"],
44
+ Fall: ["#3d5941", "#778868", "#b5b991", "#f6edbd", "#edbb8a", "#de8a5a", "#ca562c"],
45
+ ArmyRose: ["#798234", "#a3ad62", "#d0d3a2", "#fef2c0", "#f5c0a1", "#ec7d6a", "#d8443c"],
46
+ Tropic: ["#009b9e", "#42b7b9", "#a7d3d4", "#f1eceb", "#e4b4c2", "#ce78a7", "#7c1d6f"],
47
+ // Categorical
48
+ Bold: ["#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74", "#80BA5A", "#E68310", "#008695", "#CF1C90", "#f97b72", "#4b4b8f", "#A5AA99"],
49
+ Pastel: ["#66C5CC", "#F6CF71", "#F89C74", "#DCB0F2", "#87C55F", "#9EB9F3", "#FE88B1", "#C9DB74", "#8BE0A4", "#B497E7", "#D3B484", "#B3B3B3"],
50
+ Antique: ["#855C75", "#D9AF6B", "#AF6458", "#736F4C", "#526A83", "#625377", "#68855C", "#9C9C5E", "#A06177", "#8C785D", "#467378", "#7C7C7C"],
51
+ Vivid: ["#E58606", "#5D69B1", "#52BCA3", "#99C945", "#CC61B0", "#24796C", "#DAA51B", "#2F8AC4", "#764E9F", "#ED645A", "#CC3A8E", "#A5AA99"],
52
+ Prism: ["#5F4690", "#1D6996", "#38A6A5", "#0F8554", "#73AF48", "#EDAD08", "#E17C05", "#CC503E", "#94346E", "#6F4070", "#994E95", "#666666"],
53
+ Safe: ["#88CCEE", "#CC6677", "#DDCC77", "#117733", "#332288", "#AA4499", "#44AA99", "#999933", "#882255", "#661100", "#6699CC", "#888888"]
54
+ };
55
+
56
+ // components/map-renderer.tsx
57
+ import { DynamicIcon } from "lucide-react/dynamic";
58
+ import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
59
+ var DEFAULT_CENTER = [0, 20];
60
+ var DEFAULT_ZOOM = 1.5;
61
+ function colorValueToExpression(color) {
62
+ var _a, _b, _c;
63
+ if (typeof color === "string") return color;
64
+ const palette = PALETTES[color.palette];
65
+ if (!palette || palette.length === 0) return "#888888";
66
+ if (color.type === "continuous") {
67
+ const [min, max] = (_a = color.domain) != null ? _a : [0, 1];
68
+ const steps = palette.length;
69
+ const expr = ["interpolate", ["linear"], ["get", color.attr]];
70
+ for (let i = 0; i < steps; i++) {
71
+ expr.push(min + (max - min) * (i / (steps - 1)));
72
+ expr.push(palette[i]);
73
+ }
74
+ return expr;
75
+ }
76
+ if (color.type === "categorical") {
77
+ if (!color.categories || color.categories.length === 0)
78
+ return (_b = palette[0]) != null ? _b : "#888888";
79
+ const expr = ["match", ["get", color.attr]];
80
+ for (let i = 0; i < color.categories.length; i++) {
81
+ expr.push(color.categories[i]);
82
+ expr.push(palette[i % palette.length]);
83
+ }
84
+ expr.push((_c = color.nullColor) != null ? _c : "#cccccc");
85
+ return expr;
86
+ }
87
+ return "#888888";
88
+ }
89
+ function sizeValueToExpression(size, fallback) {
90
+ if (typeof size === "number") return size;
91
+ if (size.type === "continuous") {
92
+ const [dMin, dMax] = size.domain;
93
+ const [rMin, rMax] = size.range;
94
+ return ["interpolate", ["linear"], ["get", size.attr], dMin, rMin, dMax, rMax];
95
+ }
96
+ return fallback;
97
+ }
98
+ function MarkerDot({ color, icon, label }) {
99
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
100
+ icon ? /* @__PURE__ */ jsx(
101
+ "div",
102
+ {
103
+ className: "flex items-center justify-center w-7 h-7 rounded-full border-2 border-white shadow-lg transition-transform duration-150 hover:scale-[1.15]",
104
+ style: { background: color },
105
+ children: /* @__PURE__ */ jsx(DynamicIcon, { name: icon, size: 16, color: "#ffffff", strokeWidth: 2.5 })
106
+ }
107
+ ) : /* @__PURE__ */ jsx(
108
+ "div",
109
+ {
110
+ className: "h-4 w-4 rounded-full border-2 border-white shadow-lg transition-transform duration-150 hover:scale-[1.3]",
111
+ style: { background: color }
112
+ }
113
+ ),
114
+ label && /* @__PURE__ */ jsx(
115
+ "div",
116
+ {
117
+ className: "absolute top-full left-1/2 -translate-x-1/2 mt-1 whitespace-nowrap text-[10px] font-medium pointer-events-none",
118
+ style: {
119
+ textShadow: "0 0 4px rgba(255,255,255,0.9), 0 0 4px rgba(255,255,255,0.9)"
120
+ },
121
+ children: label
122
+ }
123
+ )
124
+ ] });
125
+ }
126
+ function PopupContent({ markerSpec }) {
127
+ const popup = markerSpec.popup;
128
+ if (!popup) return null;
129
+ const isRich = typeof popup === "object";
130
+ const title = isRich ? popup.title : markerSpec.label;
131
+ const description = isRich ? popup.description : popup;
132
+ const image = isRich ? popup.image : void 0;
133
+ return /* @__PURE__ */ jsxs("div", { className: "relative rounded-md border border-border bg-popover text-popover-foreground shadow-md overflow-hidden max-w-[260px]", children: [
134
+ image && /* @__PURE__ */ jsx("div", { className: "h-32 overflow-hidden", children: /* @__PURE__ */ jsx(
135
+ "img",
136
+ {
137
+ src: image,
138
+ alt: title != null ? title : "",
139
+ className: "w-full h-full object-cover"
140
+ }
141
+ ) }),
142
+ /* @__PURE__ */ jsxs("div", { className: "p-3 space-y-1", children: [
143
+ title && /* @__PURE__ */ jsx("div", { className: "font-semibold text-sm leading-tight", children: title }),
144
+ description && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground leading-relaxed", children: description })
145
+ ] })
146
+ ] });
147
+ }
148
+ function TooltipContent({ text }) {
149
+ return /* @__PURE__ */ jsx("div", { className: "rounded-md bg-foreground px-2 py-1 text-xs text-background shadow-md whitespace-nowrap", children: text });
150
+ }
151
+ function LayerTooltipContent({ properties, columns }) {
152
+ return /* @__PURE__ */ jsx("div", { className: "rounded-md border border-border bg-popover text-popover-foreground shadow-md px-3 py-2 max-w-[280px]", children: /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: columns.map((col) => {
153
+ const value = properties[col];
154
+ if (value === void 0 || value === null) return null;
155
+ return /* @__PURE__ */ jsxs("div", { className: "flex gap-2 text-xs leading-relaxed", children: [
156
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground shrink-0", children: col }),
157
+ /* @__PURE__ */ jsx("span", { className: "font-medium truncate", children: String(value) })
158
+ ] }, col);
159
+ }) }) });
160
+ }
161
+ var POSITION_CLASSES = {
162
+ "top-left": "top-2 left-2",
163
+ "top-right": "top-2 right-2",
164
+ "bottom-left": "bottom-2 left-2",
165
+ "bottom-right": "bottom-2 right-2"
166
+ };
167
+ function ControlButton({
168
+ onClick,
169
+ title,
170
+ children
171
+ }) {
172
+ return /* @__PURE__ */ jsx(
173
+ "button",
174
+ {
175
+ onClick,
176
+ title,
177
+ className: "flex items-center justify-center w-[29px] h-[29px] bg-white hover:bg-gray-100 text-gray-700 border-0 cursor-pointer transition-colors duration-150",
178
+ children
179
+ }
180
+ );
181
+ }
182
+ function MapControls({
183
+ controls,
184
+ mapRef,
185
+ containerRef
186
+ }) {
187
+ var _a, _b;
188
+ const position = (_a = controls.position) != null ? _a : "top-right";
189
+ const posClass = (_b = POSITION_CLASSES[position]) != null ? _b : POSITION_CLASSES["top-right"];
190
+ const [bearing, setBearing] = useState(0);
191
+ const [isFullscreen, setIsFullscreen] = useState(false);
192
+ useEffect(() => {
193
+ const map = mapRef.current;
194
+ if (!map || !controls.compass) return;
195
+ const onRotate = () => setBearing(map.getBearing());
196
+ map.on("rotate", onRotate);
197
+ return () => {
198
+ map.off("rotate", onRotate);
199
+ };
200
+ }, [mapRef, controls.compass]);
201
+ useEffect(() => {
202
+ const onChange = () => setIsFullscreen(!!document.fullscreenElement);
203
+ document.addEventListener("fullscreenchange", onChange);
204
+ return () => document.removeEventListener("fullscreenchange", onChange);
205
+ }, []);
206
+ const showZoom = controls.zoom !== false;
207
+ const showCompass = controls.compass !== false;
208
+ return /* @__PURE__ */ jsxs("div", { className: `absolute ${posClass} z-10 flex flex-col gap-1.5`, children: [
209
+ showZoom && /* @__PURE__ */ jsxs("div", { className: "rounded-md overflow-hidden shadow-md divide-y divide-gray-200 border border-gray-200", children: [
210
+ /* @__PURE__ */ jsx(
211
+ ControlButton,
212
+ {
213
+ onClick: () => {
214
+ var _a2;
215
+ return (_a2 = mapRef.current) == null ? void 0 : _a2.zoomIn();
216
+ },
217
+ title: "Zoom in",
218
+ children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
219
+ /* @__PURE__ */ jsx("line", { x1: "7", y1: "2", x2: "7", y2: "12" }),
220
+ /* @__PURE__ */ jsx("line", { x1: "2", y1: "7", x2: "12", y2: "7" })
221
+ ] })
222
+ }
223
+ ),
224
+ /* @__PURE__ */ jsx(
225
+ ControlButton,
226
+ {
227
+ onClick: () => {
228
+ var _a2;
229
+ return (_a2 = mapRef.current) == null ? void 0 : _a2.zoomOut();
230
+ },
231
+ title: "Zoom out",
232
+ children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("line", { x1: "2", y1: "7", x2: "12", y2: "7" }) })
233
+ }
234
+ )
235
+ ] }),
236
+ showCompass && /* @__PURE__ */ jsx("div", { className: "rounded-md overflow-hidden shadow-md border border-gray-200", children: /* @__PURE__ */ jsx(
237
+ ControlButton,
238
+ {
239
+ onClick: () => {
240
+ var _a2;
241
+ return (_a2 = mapRef.current) == null ? void 0 : _a2.easeTo({ bearing: 0, pitch: 0 });
242
+ },
243
+ title: "Reset bearing",
244
+ children: /* @__PURE__ */ jsxs(
245
+ "svg",
246
+ {
247
+ width: "14",
248
+ height: "14",
249
+ viewBox: "0 0 14 14",
250
+ fill: "none",
251
+ style: { transform: `rotate(${-bearing}deg)`, transition: "transform 0.2s" },
252
+ children: [
253
+ /* @__PURE__ */ jsx("polygon", { points: "7,1 9,7 7,6 5,7", fill: "#e74c3c" }),
254
+ /* @__PURE__ */ jsx("polygon", { points: "7,13 5,7 7,8 9,7", fill: "#94a3b8" })
255
+ ]
256
+ }
257
+ )
258
+ }
259
+ ) }),
260
+ controls.locate && /* @__PURE__ */ jsx("div", { className: "rounded-md overflow-hidden shadow-md border border-gray-200", children: /* @__PURE__ */ jsx(
261
+ ControlButton,
262
+ {
263
+ onClick: () => {
264
+ navigator.geolocation.getCurrentPosition(
265
+ (pos) => {
266
+ var _a2;
267
+ (_a2 = mapRef.current) == null ? void 0 : _a2.flyTo({
268
+ center: [pos.coords.longitude, pos.coords.latitude],
269
+ zoom: 14
270
+ });
271
+ },
272
+ () => {
273
+ }
274
+ );
275
+ },
276
+ title: "My location",
277
+ children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
278
+ /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "3" }),
279
+ /* @__PURE__ */ jsx("line", { x1: "7", y1: "0", x2: "7", y2: "3" }),
280
+ /* @__PURE__ */ jsx("line", { x1: "7", y1: "11", x2: "7", y2: "14" }),
281
+ /* @__PURE__ */ jsx("line", { x1: "0", y1: "7", x2: "3", y2: "7" }),
282
+ /* @__PURE__ */ jsx("line", { x1: "11", y1: "7", x2: "14", y2: "7" })
283
+ ] })
284
+ }
285
+ ) }),
286
+ controls.fullscreen && /* @__PURE__ */ jsx("div", { className: "rounded-md overflow-hidden shadow-md border border-gray-200", children: /* @__PURE__ */ jsx(
287
+ ControlButton,
288
+ {
289
+ onClick: () => {
290
+ var _a2;
291
+ if (document.fullscreenElement) {
292
+ document.exitFullscreen();
293
+ } else {
294
+ (_a2 = containerRef.current) == null ? void 0 : _a2.requestFullscreen();
295
+ }
296
+ },
297
+ title: isFullscreen ? "Exit fullscreen" : "Fullscreen",
298
+ children: isFullscreen ? /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
299
+ /* @__PURE__ */ jsx("polyline", { points: "5,1 5,5 1,5" }),
300
+ /* @__PURE__ */ jsx("polyline", { points: "9,1 9,5 13,5" }),
301
+ /* @__PURE__ */ jsx("polyline", { points: "5,13 5,9 1,9" }),
302
+ /* @__PURE__ */ jsx("polyline", { points: "9,13 9,9 13,9" })
303
+ ] }) : /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
304
+ /* @__PURE__ */ jsx("polyline", { points: "1,5 1,1 5,1" }),
305
+ /* @__PURE__ */ jsx("polyline", { points: "9,1 13,1 13,5" }),
306
+ /* @__PURE__ */ jsx("polyline", { points: "13,9 13,13 9,13" }),
307
+ /* @__PURE__ */ jsx("polyline", { points: "5,13 1,13 1,9" })
308
+ ] })
309
+ }
310
+ ) })
311
+ ] });
312
+ }
313
+ function MapLegend({
314
+ legendSpec,
315
+ layerSpec,
316
+ dark
317
+ }) {
318
+ var _a, _b, _c, _d, _e;
319
+ if (!layerSpec || !layerSpec.style) return null;
320
+ const position = (_a = legendSpec.position) != null ? _a : "bottom-left";
321
+ const posClass = (_b = POSITION_CLASSES[position]) != null ? _b : POSITION_CLASSES["bottom-left"];
322
+ const title = (_c = legendSpec.title) != null ? _c : legendSpec.layer;
323
+ const style = layerSpec.style;
324
+ const colorDef = (_e = (_d = style.pointColor) != null ? _d : style.fillColor) != null ? _e : style.lineColor;
325
+ if (!colorDef || typeof colorDef === "string") return null;
326
+ const palette = PALETTES[colorDef.palette];
327
+ if (!palette || palette.length === 0) return null;
328
+ const cardClass = dark ? "rounded-md border border-white/10 bg-black/80 backdrop-blur-sm shadow-md px-3 py-2 text-xs" : "rounded-md border border-gray-200 bg-white/95 backdrop-blur-sm shadow-md px-3 py-2 text-xs";
329
+ const titleClass = dark ? "font-semibold text-white mb-1.5" : "font-semibold text-gray-800 mb-1.5";
330
+ return /* @__PURE__ */ jsx("div", { className: `absolute ${posClass} z-10`, children: /* @__PURE__ */ jsxs("div", { className: cardClass, children: [
331
+ /* @__PURE__ */ jsx("div", { className: titleClass, children: title }),
332
+ colorDef.type === "continuous" && /* @__PURE__ */ jsx(ContinuousLegend, { colorDef, palette, dark }),
333
+ colorDef.type === "categorical" && /* @__PURE__ */ jsx(CategoricalLegend, { colorDef, palette, dark })
334
+ ] }) });
335
+ }
336
+ function ContinuousLegend({
337
+ colorDef,
338
+ palette,
339
+ dark
340
+ }) {
341
+ var _a;
342
+ const [min, max] = (_a = colorDef.domain) != null ? _a : [0, 1];
343
+ const gradient = `linear-gradient(to right, ${palette.join(", ")})`;
344
+ return /* @__PURE__ */ jsxs("div", { children: [
345
+ /* @__PURE__ */ jsx(
346
+ "div",
347
+ {
348
+ className: "h-2.5 w-36 rounded-sm",
349
+ style: { background: gradient }
350
+ }
351
+ ),
352
+ /* @__PURE__ */ jsxs("div", { className: `flex justify-between mt-0.5 text-[10px] ${dark ? "text-gray-400" : "text-gray-500"}`, children: [
353
+ /* @__PURE__ */ jsx("span", { children: min }),
354
+ /* @__PURE__ */ jsx("span", { children: max })
355
+ ] })
356
+ ] });
357
+ }
358
+ function CategoricalLegend({
359
+ colorDef,
360
+ palette,
361
+ dark
362
+ }) {
363
+ var _a;
364
+ const categories = (_a = colorDef.categories) != null ? _a : [];
365
+ if (categories.length === 0) return null;
366
+ return /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: categories.map((cat, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
367
+ /* @__PURE__ */ jsx(
368
+ "div",
369
+ {
370
+ className: "w-2.5 h-2.5 rounded-sm shrink-0",
371
+ style: { background: palette[i % palette.length] }
372
+ }
373
+ ),
374
+ /* @__PURE__ */ jsx("span", { className: `truncate ${dark ? "text-gray-300" : "text-gray-600"}`, children: cat })
375
+ ] }, cat)) });
376
+ }
377
+ function MapRenderer({ spec }) {
378
+ const containerRef = useRef(null);
379
+ const mapRef = useRef(null);
380
+ const markersRef = useRef(/* @__PURE__ */ new Map());
381
+ const portalsRef = useRef(/* @__PURE__ */ new Map());
382
+ const [, setPortalVersion] = useState(0);
383
+ const pendingLayerSyncRef = useRef(false);
384
+ const layerSpecsRef = useRef({});
385
+ const layerHandlersRef = useRef({});
386
+ const layerTooltipPopupRef = useRef(null);
387
+ const layerTooltipContainerRef = useRef(null);
388
+ const [layerTooltip, setLayerTooltip] = useState(
389
+ null
390
+ );
391
+ function addMarker(map, id, ms) {
392
+ var _a;
393
+ const color = ms.color || "#3b82f6";
394
+ const hasIcon = !!ms.icon;
395
+ const el = document.createElement("div");
396
+ el.style.cssText = hasIcon ? "width:28px;height:28px;cursor:pointer;" : "width:16px;height:16px;cursor:pointer;";
397
+ el.dataset.color = color;
398
+ if (ms.icon) el.dataset.icon = ms.icon;
399
+ const marker = new maplibregl.Marker({
400
+ element: el,
401
+ anchor: "center",
402
+ draggable: (_a = ms.draggable) != null ? _a : false
403
+ }).setLngLat(ms.coordinates).addTo(map);
404
+ const portals = { markerEl: el };
405
+ if (ms.popup) {
406
+ const container = document.createElement("div");
407
+ const popup = new maplibregl.Popup({
408
+ offset: 16,
409
+ closeButton: false,
410
+ maxWidth: "none"
411
+ }).setDOMContent(container);
412
+ marker.setPopup(popup);
413
+ portals.popupContainer = container;
414
+ }
415
+ if (ms.tooltip) {
416
+ const container = document.createElement("div");
417
+ const tooltipPopup = new maplibregl.Popup({
418
+ offset: 16,
419
+ closeButton: false,
420
+ closeOnClick: false,
421
+ maxWidth: "none"
422
+ }).setDOMContent(container);
423
+ el.addEventListener("mouseenter", () => {
424
+ var _a2;
425
+ if ((_a2 = marker.getPopup()) == null ? void 0 : _a2.isOpen()) return;
426
+ tooltipPopup.setLngLat(marker.getLngLat()).addTo(map);
427
+ });
428
+ el.addEventListener("mouseleave", () => {
429
+ tooltipPopup.remove();
430
+ });
431
+ portals.tooltipContainer = container;
432
+ portals.tooltipPopup = tooltipPopup;
433
+ }
434
+ markersRef.current.set(id, marker);
435
+ portalsRef.current.set(id, portals);
436
+ }
437
+ function removeMarker(id) {
438
+ var _a, _b, _c;
439
+ (_a = markersRef.current.get(id)) == null ? void 0 : _a.remove();
440
+ markersRef.current.delete(id);
441
+ (_c = (_b = portalsRef.current.get(id)) == null ? void 0 : _b.tooltipPopup) == null ? void 0 : _c.remove();
442
+ portalsRef.current.delete(id);
443
+ }
444
+ function addGeoJsonLayer(map, id, layer) {
445
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
446
+ const sourceId = `jm-${id}`;
447
+ try {
448
+ const style = (_a = layer.style) != null ? _a : {};
449
+ const opacity = (_b = style.opacity) != null ? _b : 0.8;
450
+ const isClustered = layer.cluster === true;
451
+ const clOpts = (_c = layer.clusterOptions) != null ? _c : {};
452
+ const sourceOpts = {
453
+ type: "geojson",
454
+ data: layer.data
455
+ };
456
+ if (isClustered) {
457
+ sourceOpts.cluster = true;
458
+ sourceOpts.clusterRadius = (_d = clOpts.radius) != null ? _d : 50;
459
+ sourceOpts.clusterMaxZoom = (_e = clOpts.maxZoom) != null ? _e : 14;
460
+ sourceOpts.clusterMinPoints = (_f = clOpts.minPoints) != null ? _f : 2;
461
+ }
462
+ map.addSource(sourceId, sourceOpts);
463
+ const fillColor = colorValueToExpression((_g = style.fillColor) != null ? _g : "#3b82f6");
464
+ map.addLayer({
465
+ id: `${sourceId}-fill`,
466
+ type: "fill",
467
+ source: sourceId,
468
+ filter: [
469
+ "any",
470
+ ["==", ["geometry-type"], "Polygon"],
471
+ ["==", ["geometry-type"], "MultiPolygon"]
472
+ ],
473
+ paint: {
474
+ "fill-color": fillColor,
475
+ "fill-opacity": opacity
476
+ }
477
+ });
478
+ const lineColor = colorValueToExpression((_h = style.lineColor) != null ? _h : "#333333");
479
+ map.addLayer({
480
+ id: `${sourceId}-line`,
481
+ type: "line",
482
+ source: sourceId,
483
+ paint: {
484
+ "line-color": lineColor,
485
+ "line-width": (_i = style.lineWidth) != null ? _i : 1,
486
+ "line-opacity": Math.min(opacity + 0.1, 1)
487
+ }
488
+ });
489
+ const pointColor = colorValueToExpression(
490
+ (_k = (_j = style.pointColor) != null ? _j : style.fillColor) != null ? _k : "#3b82f6"
491
+ );
492
+ const circleFilter = isClustered ? ["all", ["!", ["has", "point_count"]], ["any", ["==", ["geometry-type"], "Point"], ["==", ["geometry-type"], "MultiPoint"]]] : ["any", ["==", ["geometry-type"], "Point"], ["==", ["geometry-type"], "MultiPoint"]];
493
+ map.addLayer({
494
+ id: `${sourceId}-circle`,
495
+ type: "circle",
496
+ source: sourceId,
497
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
498
+ filter: circleFilter,
499
+ paint: {
500
+ "circle-color": pointColor,
501
+ "circle-radius": sizeValueToExpression((_l = style.pointRadius) != null ? _l : 5, 5),
502
+ "circle-opacity": opacity,
503
+ "circle-stroke-width": (_m = style.lineWidth) != null ? _m : 1,
504
+ "circle-stroke-color": lineColor
505
+ }
506
+ });
507
+ if (isClustered) {
508
+ const colors = (_n = clOpts.colors) != null ? _n : ["#22c55e", "#eab308", "#ef4444"];
509
+ map.addLayer({
510
+ id: `${sourceId}-cluster`,
511
+ type: "circle",
512
+ source: sourceId,
513
+ filter: ["has", "point_count"],
514
+ paint: {
515
+ "circle-color": ["step", ["get", "point_count"], colors[0], 100, colors[1], 750, colors[2]],
516
+ "circle-radius": ["step", ["get", "point_count"], 20, 100, 30, 750, 40],
517
+ "circle-stroke-width": 1,
518
+ "circle-stroke-color": "#fff",
519
+ "circle-opacity": 0.85
520
+ }
521
+ });
522
+ map.addLayer({
523
+ id: `${sourceId}-cluster-count`,
524
+ type: "symbol",
525
+ source: sourceId,
526
+ filter: ["has", "point_count"],
527
+ layout: {
528
+ "text-field": "{point_count_abbreviated}",
529
+ "text-size": 12
530
+ },
531
+ paint: { "text-color": "#fff" }
532
+ });
533
+ const onClusterClick = async (e) => {
534
+ var _a2;
535
+ const features = map.queryRenderedFeatures(e.point, { layers: [`${sourceId}-cluster`] });
536
+ if (!features.length) return;
537
+ const clusterId = (_a2 = features[0].properties) == null ? void 0 : _a2.cluster_id;
538
+ const source = map.getSource(sourceId);
539
+ const zoom = await source.getClusterExpansionZoom(clusterId);
540
+ map.easeTo({ center: features[0].geometry.coordinates, zoom });
541
+ };
542
+ map.on("click", `${sourceId}-cluster`, onClusterClick);
543
+ map.on("mouseenter", `${sourceId}-cluster`, () => {
544
+ map.getCanvas().style.cursor = "pointer";
545
+ });
546
+ map.on("mouseleave", `${sourceId}-cluster`, () => {
547
+ map.getCanvas().style.cursor = "";
548
+ });
549
+ const clusterHandlers = [
550
+ { event: "click", layer: `${sourceId}-cluster`, handler: onClusterClick }
551
+ ];
552
+ layerHandlersRef.current[`${sourceId}-cluster`] = clusterHandlers;
553
+ }
554
+ if (layer.tooltip && layer.tooltip.length > 0) {
555
+ const columns = layer.tooltip;
556
+ const subLayers = [
557
+ `${sourceId}-fill`,
558
+ `${sourceId}-circle`,
559
+ `${sourceId}-line`
560
+ ];
561
+ const handlers = [];
562
+ for (const subLayer of subLayers) {
563
+ const onMove = (e) => {
564
+ var _a2;
565
+ if (!e.features || e.features.length === 0) return;
566
+ const props = (_a2 = e.features[0].properties) != null ? _a2 : {};
567
+ map.getCanvas().style.cursor = "pointer";
568
+ setLayerTooltip({ properties: props, columns });
569
+ const popup = layerTooltipPopupRef.current;
570
+ if (popup) {
571
+ popup.setLngLat(e.lngLat).addTo(map);
572
+ }
573
+ };
574
+ const onLeave = () => {
575
+ var _a2;
576
+ map.getCanvas().style.cursor = "";
577
+ setLayerTooltip(null);
578
+ (_a2 = layerTooltipPopupRef.current) == null ? void 0 : _a2.remove();
579
+ };
580
+ map.on("mousemove", subLayer, onMove);
581
+ map.on("mouseleave", subLayer, onLeave);
582
+ handlers.push({ event: "mousemove", layer: subLayer, handler: onMove });
583
+ handlers.push({ event: "mouseleave", layer: subLayer, handler: onLeave });
584
+ }
585
+ layerHandlersRef.current[sourceId] = handlers;
586
+ }
587
+ } catch (err) {
588
+ console.warn(`[json-maps] Failed to add layer "${id}":`, err);
589
+ try {
590
+ removeLayer(map, id);
591
+ } catch (e) {
592
+ }
593
+ }
594
+ }
595
+ function addRouteLayer(map, id, layer) {
596
+ var _a, _b, _c, _d;
597
+ const sourceId = `jm-${id}`;
598
+ try {
599
+ const style = (_a = layer.style) != null ? _a : {};
600
+ map.addSource(sourceId, {
601
+ type: "geojson",
602
+ data: {
603
+ type: "Feature",
604
+ properties: {},
605
+ geometry: { type: "LineString", coordinates: layer.coordinates }
606
+ }
607
+ });
608
+ const paint = {
609
+ "line-color": (_b = style.color) != null ? _b : "#3b82f6",
610
+ "line-width": (_c = style.width) != null ? _c : 3,
611
+ "line-opacity": (_d = style.opacity) != null ? _d : 0.8
612
+ };
613
+ if (style.dashed) {
614
+ paint["line-dasharray"] = [6, 3];
615
+ }
616
+ map.addLayer({
617
+ id: `${sourceId}-line`,
618
+ type: "line",
619
+ source: sourceId,
620
+ layout: { "line-join": "round", "line-cap": "round" },
621
+ paint
622
+ });
623
+ } catch (err) {
624
+ console.warn(`[json-maps] Failed to add route "${id}":`, err);
625
+ try {
626
+ removeLayer(map, id);
627
+ } catch (e) {
628
+ }
629
+ }
630
+ }
631
+ function removeLayer(map, id) {
632
+ const sourceId = `jm-${id}`;
633
+ for (const key of [sourceId, `${sourceId}-cluster`]) {
634
+ const handlers = layerHandlersRef.current[key];
635
+ if (handlers) {
636
+ for (const h of handlers) {
637
+ map.off(h.event, h.layer, h.handler);
638
+ }
639
+ delete layerHandlersRef.current[key];
640
+ }
641
+ }
642
+ const subLayers = [
643
+ `${sourceId}-cluster-count`,
644
+ `${sourceId}-cluster`,
645
+ `${sourceId}-circle`,
646
+ `${sourceId}-line`,
647
+ `${sourceId}-fill`
648
+ ];
649
+ for (const layerId of subLayers) {
650
+ if (map.getLayer(layerId)) map.removeLayer(layerId);
651
+ }
652
+ if (map.getSource(sourceId)) map.removeSource(sourceId);
653
+ }
654
+ const syncMarkers = useCallback(() => {
655
+ var _a, _b, _c;
656
+ const map = mapRef.current;
657
+ if (!map) return;
658
+ const specMarkers = (_a = spec.markers) != null ? _a : {};
659
+ for (const id of [...markersRef.current.keys()]) {
660
+ if (!specMarkers[id]) removeMarker(id);
661
+ }
662
+ for (const [id, ms] of Object.entries(specMarkers)) {
663
+ const existing = markersRef.current.get(id);
664
+ if (existing) {
665
+ existing.setLngLat(ms.coordinates);
666
+ const el = existing.getElement();
667
+ if (el.dataset.color !== ((_b = ms.color) != null ? _b : "") || el.dataset.icon !== ((_c = ms.icon) != null ? _c : "")) {
668
+ removeMarker(id);
669
+ addMarker(map, id, ms);
670
+ }
671
+ } else {
672
+ addMarker(map, id, ms);
673
+ }
674
+ }
675
+ setPortalVersion((v) => v + 1);
676
+ }, [spec.markers]);
677
+ const syncLayers = useCallback(() => {
678
+ var _a;
679
+ const map = mapRef.current;
680
+ if (!map) return;
681
+ if (!map.isStyleLoaded()) {
682
+ if (!pendingLayerSyncRef.current) {
683
+ pendingLayerSyncRef.current = true;
684
+ const check = () => {
685
+ if (!mapRef.current || !pendingLayerSyncRef.current) return;
686
+ if (mapRef.current.isStyleLoaded()) {
687
+ pendingLayerSyncRef.current = false;
688
+ syncLayersRef.current();
689
+ } else {
690
+ requestAnimationFrame(check);
691
+ }
692
+ };
693
+ requestAnimationFrame(check);
694
+ }
695
+ return;
696
+ }
697
+ pendingLayerSyncRef.current = false;
698
+ const specLayers = (_a = spec.layers) != null ? _a : {};
699
+ const prevSpecs = layerSpecsRef.current;
700
+ for (const id of Object.keys(prevSpecs)) {
701
+ if (!specLayers[id]) {
702
+ removeLayer(map, id);
703
+ delete prevSpecs[id];
704
+ }
705
+ }
706
+ for (const [id, layerSpec] of Object.entries(specLayers)) {
707
+ const serialized = JSON.stringify(layerSpec);
708
+ const sourceExists = !!map.getSource(`jm-${id}`);
709
+ if (prevSpecs[id] === serialized && sourceExists) continue;
710
+ if (sourceExists) {
711
+ removeLayer(map, id);
712
+ }
713
+ if (layerSpec.type === "route") {
714
+ addRouteLayer(map, id, layerSpec);
715
+ } else {
716
+ addGeoJsonLayer(map, id, layerSpec);
717
+ }
718
+ prevSpecs[id] = serialized;
719
+ }
720
+ }, [spec.layers]);
721
+ const syncMarkersRef = useRef(syncMarkers);
722
+ const syncLayersRef = useRef(syncLayers);
723
+ useEffect(() => {
724
+ syncMarkersRef.current = syncMarkers;
725
+ }, [syncMarkers]);
726
+ useEffect(() => {
727
+ syncLayersRef.current = syncLayers;
728
+ }, [syncLayers]);
729
+ useEffect(() => {
730
+ var _a, _b, _c, _d, _e;
731
+ if (!containerRef.current) return;
732
+ const map = new maplibregl.Map({
733
+ container: containerRef.current,
734
+ style: (_a = resolveBasemapStyle(spec.basemap)) != null ? _a : resolveBasemapStyle(),
735
+ center: (_b = spec.center) != null ? _b : DEFAULT_CENTER,
736
+ zoom: (_c = spec.zoom) != null ? _c : DEFAULT_ZOOM,
737
+ pitch: (_d = spec.pitch) != null ? _d : 0,
738
+ bearing: (_e = spec.bearing) != null ? _e : 0,
739
+ attributionControl: false
740
+ });
741
+ map.on("load", () => {
742
+ if (spec.bounds) {
743
+ map.fitBounds(spec.bounds, {
744
+ padding: 40,
745
+ duration: 0
746
+ });
747
+ }
748
+ syncLayersRef.current();
749
+ });
750
+ const tooltipContainer = document.createElement("div");
751
+ const tooltipPopup = new maplibregl.Popup({
752
+ offset: 12,
753
+ closeButton: false,
754
+ closeOnClick: false,
755
+ maxWidth: "none"
756
+ }).setDOMContent(tooltipContainer);
757
+ layerTooltipContainerRef.current = tooltipContainer;
758
+ layerTooltipPopupRef.current = tooltipPopup;
759
+ mapRef.current = map;
760
+ return () => {
761
+ for (const id of markersRef.current.keys()) removeMarker(id);
762
+ markersRef.current.clear();
763
+ portalsRef.current.clear();
764
+ tooltipPopup.remove();
765
+ layerTooltipPopupRef.current = null;
766
+ layerTooltipContainerRef.current = null;
767
+ layerSpecsRef.current = {};
768
+ map.remove();
769
+ mapRef.current = null;
770
+ };
771
+ }, []);
772
+ const prevBasemapRef = useRef(spec.basemap);
773
+ useEffect(() => {
774
+ const map = mapRef.current;
775
+ if (!map) return;
776
+ if (prevBasemapRef.current === spec.basemap) return;
777
+ prevBasemapRef.current = spec.basemap;
778
+ const style = resolveBasemapStyle(spec.basemap);
779
+ if (style) {
780
+ map.setStyle(style);
781
+ map.once("styledata", () => {
782
+ syncMarkersRef.current();
783
+ syncLayersRef.current();
784
+ });
785
+ }
786
+ }, [spec.basemap]);
787
+ useEffect(() => {
788
+ var _a, _b, _c, _d;
789
+ if (!mapRef.current) return;
790
+ if (spec.bounds) {
791
+ mapRef.current.fitBounds(
792
+ spec.bounds,
793
+ { padding: 40 }
794
+ );
795
+ return;
796
+ }
797
+ const markers = spec.markers ? Object.values(spec.markers) : [];
798
+ if (markers.length > 0) {
799
+ const lngs = markers.map((m) => m.coordinates[0]);
800
+ const lats = markers.map((m) => m.coordinates[1]);
801
+ let west = Math.min(...lngs);
802
+ let south = Math.min(...lats);
803
+ let east = Math.max(...lngs);
804
+ let north = Math.max(...lats);
805
+ const MIN_SPAN = 0.01;
806
+ if (east - west < MIN_SPAN) {
807
+ const midLng = (west + east) / 2;
808
+ west = midLng - MIN_SPAN / 2;
809
+ east = midLng + MIN_SPAN / 2;
810
+ }
811
+ if (north - south < MIN_SPAN) {
812
+ const midLat = (north + south) / 2;
813
+ south = midLat - MIN_SPAN / 2;
814
+ north = midLat + MIN_SPAN / 2;
815
+ }
816
+ mapRef.current.fitBounds([west, south, east, north], {
817
+ padding: { top: 100, bottom: 100, left: 100, right: 100 },
818
+ maxZoom: 15
819
+ });
820
+ return;
821
+ }
822
+ mapRef.current.flyTo({
823
+ center: (_a = spec.center) != null ? _a : DEFAULT_CENTER,
824
+ zoom: (_b = spec.zoom) != null ? _b : DEFAULT_ZOOM,
825
+ pitch: (_c = spec.pitch) != null ? _c : 0,
826
+ bearing: (_d = spec.bearing) != null ? _d : 0
827
+ });
828
+ }, [spec.center, spec.zoom, spec.pitch, spec.bearing, spec.bounds, spec.markers]);
829
+ useEffect(() => {
830
+ syncMarkers();
831
+ }, [syncMarkers]);
832
+ useEffect(() => {
833
+ syncLayers();
834
+ }, [syncLayers]);
835
+ const entries = Array.from(portalsRef.current.entries());
836
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
837
+ /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative w-full h-full", children: [
838
+ spec.controls && /* @__PURE__ */ jsx(
839
+ MapControls,
840
+ {
841
+ controls: spec.controls,
842
+ mapRef,
843
+ containerRef
844
+ }
845
+ ),
846
+ spec.legend && Object.entries(spec.legend).map(([id, leg]) => {
847
+ var _a;
848
+ const layer = (_a = spec.layers) == null ? void 0 : _a[leg.layer];
849
+ const geoLayer = layer && layer.type === "geojson" ? layer : null;
850
+ return /* @__PURE__ */ jsx(MapLegend, { legendSpec: leg, layerSpec: geoLayer, dark: spec.basemap === "dark" }, id);
851
+ })
852
+ ] }),
853
+ entries.map(([id, p]) => {
854
+ var _a;
855
+ const ms = (_a = spec.markers) == null ? void 0 : _a[id];
856
+ if (!ms) return null;
857
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
858
+ createPortal(
859
+ /* @__PURE__ */ jsx(MarkerDot, { color: ms.color || "#3b82f6", icon: ms.icon, label: ms.label }),
860
+ p.markerEl
861
+ ),
862
+ p.popupContainer && ms.popup && createPortal(
863
+ /* @__PURE__ */ jsx(PopupContent, { markerSpec: ms }),
864
+ p.popupContainer
865
+ ),
866
+ p.tooltipContainer && ms.tooltip && createPortal(
867
+ /* @__PURE__ */ jsx(TooltipContent, { text: ms.tooltip }),
868
+ p.tooltipContainer
869
+ )
870
+ ] }, id);
871
+ }),
872
+ layerTooltipContainerRef.current && layerTooltip && createPortal(
873
+ /* @__PURE__ */ jsx(
874
+ LayerTooltipContent,
875
+ {
876
+ properties: layerTooltip.properties,
877
+ columns: layerTooltip.columns
878
+ }
879
+ ),
880
+ layerTooltipContainerRef.current
881
+ )
882
+ ] });
883
+ }
884
+ export {
885
+ BASEMAP_STYLES,
886
+ MapRenderer,
887
+ PALETTES,
888
+ resolveBasemapStyle
889
+ };
890
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../components/map-renderer.tsx","../lib/spec.ts","../lib/palettes.ts"],"sourcesContent":["\"use client\";\n\nimport { Fragment, useEffect, useRef, useCallback, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport maplibregl from \"maplibre-gl\";\nimport \"maplibre-gl/dist/maplibre-gl.css\";\nimport {\n type MapSpec,\n type MarkerSpec,\n type GeoJsonLayerSpec,\n type RouteLayerSpec,\n type ControlsSpec,\n type LegendSpec,\n type ColorValue,\n type ContinuousColor,\n type CategoricalColor,\n type SizeValue,\n resolveBasemapStyle,\n} from \"@/lib/spec\";\nimport { PALETTES } from \"@/lib/palettes\";\nimport { DynamicIcon } from \"lucide-react/dynamic\";\n\nconst DEFAULT_CENTER: [number, number] = [0, 20];\nconst DEFAULT_ZOOM = 1.5;\n\n/* ------------------------------------------------------------------ */\n/* Color helpers */\n/* ------------------------------------------------------------------ */\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction colorValueToExpression(color: ColorValue): any {\n if (typeof color === \"string\") return color;\n\n const palette = PALETTES[color.palette];\n if (!palette || palette.length === 0) return \"#888888\";\n\n if (color.type === \"continuous\") {\n const [min, max] = color.domain ?? [0, 1];\n const steps = palette.length;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const expr: any[] = [\"interpolate\", [\"linear\"], [\"get\", color.attr]];\n for (let i = 0; i < steps; i++) {\n expr.push(min + (max - min) * (i / (steps - 1)));\n expr.push(palette[i]);\n }\n return expr;\n }\n\n if (color.type === \"categorical\") {\n if (!color.categories || color.categories.length === 0)\n return palette[0] ?? \"#888888\";\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const expr: any[] = [\"match\", [\"get\", color.attr]];\n for (let i = 0; i < color.categories.length; i++) {\n expr.push(color.categories[i]);\n expr.push(palette[i % palette.length]);\n }\n expr.push(color.nullColor ?? \"#cccccc\");\n return expr;\n }\n\n return \"#888888\";\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction sizeValueToExpression(size: SizeValue, fallback: number): any {\n if (typeof size === \"number\") return size;\n if (size.type === \"continuous\") {\n const [dMin, dMax] = size.domain;\n const [rMin, rMax] = size.range;\n return [\"interpolate\", [\"linear\"], [\"get\", size.attr], dMin, rMin, dMax, rMax];\n }\n return fallback;\n}\n\n/* ------------------------------------------------------------------ */\n/* Portal content components */\n/* ------------------------------------------------------------------ */\n\nfunction MarkerDot({ color, icon, label }: { color: string; icon?: string; label?: string }) {\n return (\n <>\n {icon ? (\n <div\n className=\"flex items-center justify-center w-7 h-7 rounded-full border-2 border-white shadow-lg transition-transform duration-150 hover:scale-[1.15]\"\n style={{ background: color }}\n >\n {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}\n <DynamicIcon name={icon as any} size={16} color=\"#ffffff\" strokeWidth={2.5} />\n </div>\n ) : (\n <div\n className=\"h-4 w-4 rounded-full border-2 border-white shadow-lg transition-transform duration-150 hover:scale-[1.3]\"\n style={{ background: color }}\n />\n )}\n {label && (\n <div\n className=\"absolute top-full left-1/2 -translate-x-1/2 mt-1 whitespace-nowrap text-[10px] font-medium pointer-events-none\"\n style={{\n textShadow:\n \"0 0 4px rgba(255,255,255,0.9), 0 0 4px rgba(255,255,255,0.9)\",\n }}\n >\n {label}\n </div>\n )}\n </>\n );\n}\n\nfunction PopupContent({ markerSpec }: { markerSpec: MarkerSpec }) {\n const popup = markerSpec.popup;\n if (!popup) return null;\n\n const isRich = typeof popup === \"object\";\n const title = isRich ? popup.title : markerSpec.label;\n const description = isRich ? popup.description : popup;\n const image = isRich ? popup.image : undefined;\n\n return (\n <div className=\"relative rounded-md border border-border bg-popover text-popover-foreground shadow-md overflow-hidden max-w-[260px]\">\n {image && (\n <div className=\"h-32 overflow-hidden\">\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n src={image}\n alt={title ?? \"\"}\n className=\"w-full h-full object-cover\"\n />\n </div>\n )}\n <div className=\"p-3 space-y-1\">\n {title && (\n <div className=\"font-semibold text-sm leading-tight\">{title}</div>\n )}\n {description && (\n <div className=\"text-xs text-muted-foreground leading-relaxed\">\n {description}\n </div>\n )}\n </div>\n </div>\n );\n}\n\nfunction TooltipContent({ text }: { text: string }) {\n return (\n <div className=\"rounded-md bg-foreground px-2 py-1 text-xs text-background shadow-md whitespace-nowrap\">\n {text}\n </div>\n );\n}\n\ninterface LayerTooltipData {\n properties: Record<string, unknown>;\n columns: string[];\n}\n\nfunction LayerTooltipContent({ properties, columns }: LayerTooltipData) {\n return (\n <div className=\"rounded-md border border-border bg-popover text-popover-foreground shadow-md px-3 py-2 max-w-[280px]\">\n <div className=\"space-y-0.5\">\n {columns.map((col) => {\n const value = properties[col];\n if (value === undefined || value === null) return null;\n return (\n <div key={col} className=\"flex gap-2 text-xs leading-relaxed\">\n <span className=\"text-muted-foreground shrink-0\">\n {col}\n </span>\n <span className=\"font-medium truncate\">{String(value)}</span>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Map controls */\n/* ------------------------------------------------------------------ */\n\nconst POSITION_CLASSES: Record<string, string> = {\n \"top-left\": \"top-2 left-2\",\n \"top-right\": \"top-2 right-2\",\n \"bottom-left\": \"bottom-2 left-2\",\n \"bottom-right\": \"bottom-2 right-2\",\n};\n\nfunction ControlButton({\n onClick,\n title,\n children,\n}: {\n onClick: () => void;\n title: string;\n children: React.ReactNode;\n}) {\n return (\n <button\n onClick={onClick}\n title={title}\n className=\"flex items-center justify-center w-[29px] h-[29px] bg-white hover:bg-gray-100 text-gray-700 border-0 cursor-pointer transition-colors duration-150\"\n >\n {children}\n </button>\n );\n}\n\nfunction MapControls({\n controls,\n mapRef,\n containerRef,\n}: {\n controls: ControlsSpec;\n mapRef: React.RefObject<maplibregl.Map | null>;\n containerRef: React.RefObject<HTMLDivElement | null>;\n}) {\n const position = controls.position ?? \"top-right\";\n const posClass = POSITION_CLASSES[position] ?? POSITION_CLASSES[\"top-right\"];\n const [bearing, setBearing] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\n\n // Track bearing for compass rotation\n useEffect(() => {\n const map = mapRef.current;\n if (!map || !controls.compass) return;\n const onRotate = () => setBearing(map.getBearing());\n map.on(\"rotate\", onRotate);\n return () => { map.off(\"rotate\", onRotate); };\n }, [mapRef, controls.compass]);\n\n // Track fullscreen changes\n useEffect(() => {\n const onChange = () => setIsFullscreen(!!document.fullscreenElement);\n document.addEventListener(\"fullscreenchange\", onChange);\n return () => document.removeEventListener(\"fullscreenchange\", onChange);\n }, []);\n\n const showZoom = controls.zoom !== false;\n const showCompass = controls.compass !== false;\n\n return (\n <div className={`absolute ${posClass} z-10 flex flex-col gap-1.5`}>\n {/* Zoom controls */}\n {showZoom && (\n <div className=\"rounded-md overflow-hidden shadow-md divide-y divide-gray-200 border border-gray-200\">\n <ControlButton\n onClick={() => mapRef.current?.zoomIn()}\n title=\"Zoom in\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <line x1=\"7\" y1=\"2\" x2=\"7\" y2=\"12\" />\n <line x1=\"2\" y1=\"7\" x2=\"12\" y2=\"7\" />\n </svg>\n </ControlButton>\n <ControlButton\n onClick={() => mapRef.current?.zoomOut()}\n title=\"Zoom out\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <line x1=\"2\" y1=\"7\" x2=\"12\" y2=\"7\" />\n </svg>\n </ControlButton>\n </div>\n )}\n\n {/* Compass */}\n {showCompass && (\n <div className=\"rounded-md overflow-hidden shadow-md border border-gray-200\">\n <ControlButton\n onClick={() => mapRef.current?.easeTo({ bearing: 0, pitch: 0 })}\n title=\"Reset bearing\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 14 14\"\n fill=\"none\"\n style={{ transform: `rotate(${-bearing}deg)`, transition: \"transform 0.2s\" }}\n >\n <polygon points=\"7,1 9,7 7,6 5,7\" fill=\"#e74c3c\" />\n <polygon points=\"7,13 5,7 7,8 9,7\" fill=\"#94a3b8\" />\n </svg>\n </ControlButton>\n </div>\n )}\n\n {/* Locate */}\n {controls.locate && (\n <div className=\"rounded-md overflow-hidden shadow-md border border-gray-200\">\n <ControlButton\n onClick={() => {\n navigator.geolocation.getCurrentPosition(\n (pos) => {\n mapRef.current?.flyTo({\n center: [pos.coords.longitude, pos.coords.latitude],\n zoom: 14,\n });\n },\n () => {},\n );\n }}\n title=\"My location\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\">\n <circle cx=\"7\" cy=\"7\" r=\"3\" />\n <line x1=\"7\" y1=\"0\" x2=\"7\" y2=\"3\" />\n <line x1=\"7\" y1=\"11\" x2=\"7\" y2=\"14\" />\n <line x1=\"0\" y1=\"7\" x2=\"3\" y2=\"7\" />\n <line x1=\"11\" y1=\"7\" x2=\"14\" y2=\"7\" />\n </svg>\n </ControlButton>\n </div>\n )}\n\n {/* Fullscreen */}\n {controls.fullscreen && (\n <div className=\"rounded-md overflow-hidden shadow-md border border-gray-200\">\n <ControlButton\n onClick={() => {\n if (document.fullscreenElement) {\n document.exitFullscreen();\n } else {\n containerRef.current?.requestFullscreen();\n }\n }}\n title={isFullscreen ? \"Exit fullscreen\" : \"Fullscreen\"}\n >\n {isFullscreen ? (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\">\n <polyline points=\"5,1 5,5 1,5\" />\n <polyline points=\"9,1 9,5 13,5\" />\n <polyline points=\"5,13 5,9 1,9\" />\n <polyline points=\"9,13 9,9 13,9\" />\n </svg>\n ) : (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\">\n <polyline points=\"1,5 1,1 5,1\" />\n <polyline points=\"9,1 13,1 13,5\" />\n <polyline points=\"13,9 13,13 9,13\" />\n <polyline points=\"5,13 1,13 1,9\" />\n </svg>\n )}\n </ControlButton>\n </div>\n )}\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Legend */\n/* ------------------------------------------------------------------ */\n\nfunction MapLegend({\n legendSpec,\n layerSpec,\n dark,\n}: {\n legendSpec: LegendSpec;\n layerSpec: GeoJsonLayerSpec | null;\n dark: boolean;\n}) {\n if (!layerSpec || !layerSpec.style) return null;\n\n const position = legendSpec.position ?? \"bottom-left\";\n const posClass = POSITION_CLASSES[position] ?? POSITION_CLASSES[\"bottom-left\"];\n const title = legendSpec.title ?? legendSpec.layer;\n const style = layerSpec.style;\n\n // Find the first data-driven color for the legend\n const colorDef = (style.pointColor ?? style.fillColor ?? style.lineColor) as\n | ContinuousColor\n | CategoricalColor\n | string\n | undefined;\n\n if (!colorDef || typeof colorDef === \"string\") return null;\n\n const palette = PALETTES[colorDef.palette];\n if (!palette || palette.length === 0) return null;\n\n const cardClass = dark\n ? \"rounded-md border border-white/10 bg-black/80 backdrop-blur-sm shadow-md px-3 py-2 text-xs\"\n : \"rounded-md border border-gray-200 bg-white/95 backdrop-blur-sm shadow-md px-3 py-2 text-xs\";\n const titleClass = dark ? \"font-semibold text-white mb-1.5\" : \"font-semibold text-gray-800 mb-1.5\";\n\n return (\n <div className={`absolute ${posClass} z-10`}>\n <div className={cardClass}>\n <div className={titleClass}>{title}</div>\n {colorDef.type === \"continuous\" && (\n <ContinuousLegend colorDef={colorDef} palette={palette} dark={dark} />\n )}\n {colorDef.type === \"categorical\" && (\n <CategoricalLegend colorDef={colorDef} palette={palette} dark={dark} />\n )}\n </div>\n </div>\n );\n}\n\nfunction ContinuousLegend({\n colorDef,\n palette,\n dark,\n}: {\n colorDef: ContinuousColor;\n palette: string[];\n dark: boolean;\n}) {\n const [min, max] = colorDef.domain ?? [0, 1];\n const gradient = `linear-gradient(to right, ${palette.join(\", \")})`;\n\n return (\n <div>\n <div\n className=\"h-2.5 w-36 rounded-sm\"\n style={{ background: gradient }}\n />\n <div className={`flex justify-between mt-0.5 text-[10px] ${dark ? \"text-gray-400\" : \"text-gray-500\"}`}>\n <span>{min}</span>\n <span>{max}</span>\n </div>\n </div>\n );\n}\n\nfunction CategoricalLegend({\n colorDef,\n palette,\n dark,\n}: {\n colorDef: CategoricalColor;\n palette: string[];\n dark: boolean;\n}) {\n const categories = colorDef.categories ?? [];\n if (categories.length === 0) return null;\n\n return (\n <div className=\"space-y-0.5\">\n {categories.map((cat, i) => (\n <div key={cat} className=\"flex items-center gap-1.5\">\n <div\n className=\"w-2.5 h-2.5 rounded-sm shrink-0\"\n style={{ background: palette[i % palette.length] }}\n />\n <span className={`truncate ${dark ? \"text-gray-300\" : \"text-gray-600\"}`}>{cat}</span>\n </div>\n ))}\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Internal types */\n/* ------------------------------------------------------------------ */\n\ninterface MarkerPortals {\n markerEl: HTMLDivElement;\n popupContainer?: HTMLDivElement;\n tooltipContainer?: HTMLDivElement;\n tooltipPopup?: maplibregl.Popup;\n}\n\n/* ------------------------------------------------------------------ */\n/* MapRenderer */\n/* ------------------------------------------------------------------ */\n\nexport function MapRenderer({ spec }: { spec: MapSpec }) {\n const containerRef = useRef<HTMLDivElement>(null);\n const mapRef = useRef<maplibregl.Map | null>(null);\n const markersRef = useRef<Map<string, maplibregl.Marker>>(new Map());\n const portalsRef = useRef<Map<string, MarkerPortals>>(new Map());\n const [, setPortalVersion] = useState(0);\n\n // Layer tracking\n const pendingLayerSyncRef = useRef(false);\n const layerSpecsRef = useRef<Record<string, string>>({});\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const layerHandlersRef = useRef<Record<string, Array<{ event: string; layer: string; handler: any }>>>({});\n const layerTooltipPopupRef = useRef<maplibregl.Popup | null>(null);\n const layerTooltipContainerRef = useRef<HTMLDivElement | null>(null);\n const [layerTooltip, setLayerTooltip] = useState<LayerTooltipData | null>(\n null,\n );\n\n /* ---- Marker helpers ---- */\n\n function addMarker(map: maplibregl.Map, id: string, ms: MarkerSpec) {\n const color = ms.color || \"#3b82f6\";\n const hasIcon = !!ms.icon;\n\n const el = document.createElement(\"div\");\n el.style.cssText = hasIcon\n ? \"width:28px;height:28px;cursor:pointer;\"\n : \"width:16px;height:16px;cursor:pointer;\";\n el.dataset.color = color;\n if (ms.icon) el.dataset.icon = ms.icon;\n\n const marker = new maplibregl.Marker({\n element: el,\n anchor: \"center\",\n draggable: ms.draggable ?? false,\n })\n .setLngLat(ms.coordinates)\n .addTo(map);\n\n const portals: MarkerPortals = { markerEl: el };\n\n if (ms.popup) {\n const container = document.createElement(\"div\");\n const popup = new maplibregl.Popup({\n offset: 16,\n closeButton: false,\n maxWidth: \"none\",\n }).setDOMContent(container);\n marker.setPopup(popup);\n portals.popupContainer = container;\n }\n\n if (ms.tooltip) {\n const container = document.createElement(\"div\");\n const tooltipPopup = new maplibregl.Popup({\n offset: 16,\n closeButton: false,\n closeOnClick: false,\n maxWidth: \"none\",\n }).setDOMContent(container);\n\n el.addEventListener(\"mouseenter\", () => {\n if (marker.getPopup()?.isOpen()) return;\n tooltipPopup.setLngLat(marker.getLngLat()).addTo(map);\n });\n el.addEventListener(\"mouseleave\", () => {\n tooltipPopup.remove();\n });\n\n portals.tooltipContainer = container;\n portals.tooltipPopup = tooltipPopup;\n }\n\n markersRef.current.set(id, marker);\n portalsRef.current.set(id, portals);\n }\n\n function removeMarker(id: string) {\n markersRef.current.get(id)?.remove();\n markersRef.current.delete(id);\n portalsRef.current.get(id)?.tooltipPopup?.remove();\n portalsRef.current.delete(id);\n }\n\n /* ---- Layer helpers ---- */\n\n function addGeoJsonLayer(\n map: maplibregl.Map,\n id: string,\n layer: GeoJsonLayerSpec,\n ) {\n const sourceId = `jm-${id}`;\n\n try {\n const style = layer.style ?? {};\n const opacity = style.opacity ?? 0.8;\n const isClustered = layer.cluster === true;\n const clOpts = layer.clusterOptions ?? {};\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sourceOpts: any = {\n type: \"geojson\",\n data: layer.data as string | GeoJSON.GeoJSON,\n };\n if (isClustered) {\n sourceOpts.cluster = true;\n sourceOpts.clusterRadius = clOpts.radius ?? 50;\n sourceOpts.clusterMaxZoom = clOpts.maxZoom ?? 14;\n sourceOpts.clusterMinPoints = clOpts.minPoints ?? 2;\n }\n map.addSource(sourceId, sourceOpts);\n\n // Fill layer (polygons)\n const fillColor = colorValueToExpression(style.fillColor ?? \"#3b82f6\");\n map.addLayer({\n id: `${sourceId}-fill`,\n type: \"fill\",\n source: sourceId,\n filter: [\n \"any\",\n [\"==\", [\"geometry-type\"], \"Polygon\"],\n [\"==\", [\"geometry-type\"], \"MultiPolygon\"],\n ],\n paint: {\n \"fill-color\": fillColor,\n \"fill-opacity\": opacity,\n },\n });\n\n // Line layer (lines + polygon outlines)\n const lineColor = colorValueToExpression(style.lineColor ?? \"#333333\");\n map.addLayer({\n id: `${sourceId}-line`,\n type: \"line\",\n source: sourceId,\n paint: {\n \"line-color\": lineColor,\n \"line-width\": style.lineWidth ?? 1,\n \"line-opacity\": Math.min(opacity + 0.1, 1),\n },\n });\n\n // Circle layer (points) — for non-clustered, or unclustered points in clustered mode\n const pointColor = colorValueToExpression(\n style.pointColor ?? style.fillColor ?? \"#3b82f6\",\n );\n const circleFilter = isClustered\n ? [\"all\", [\"!\", [\"has\", \"point_count\"]], [\"any\", [\"==\", [\"geometry-type\"], \"Point\"], [\"==\", [\"geometry-type\"], \"MultiPoint\"]]]\n : [\"any\", [\"==\", [\"geometry-type\"], \"Point\"], [\"==\", [\"geometry-type\"], \"MultiPoint\"]];\n map.addLayer({\n id: `${sourceId}-circle`,\n type: \"circle\",\n source: sourceId,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n filter: circleFilter as any,\n paint: {\n \"circle-color\": pointColor,\n \"circle-radius\": sizeValueToExpression(style.pointRadius ?? 5, 5),\n \"circle-opacity\": opacity,\n \"circle-stroke-width\": style.lineWidth ?? 1,\n \"circle-stroke-color\": lineColor,\n },\n });\n\n // Cluster layers\n if (isClustered) {\n const colors = clOpts.colors ?? [\"#22c55e\", \"#eab308\", \"#ef4444\"];\n map.addLayer({\n id: `${sourceId}-cluster`,\n type: \"circle\",\n source: sourceId,\n filter: [\"has\", \"point_count\"],\n paint: {\n \"circle-color\": [\"step\", [\"get\", \"point_count\"], colors[0], 100, colors[1], 750, colors[2]],\n \"circle-radius\": [\"step\", [\"get\", \"point_count\"], 20, 100, 30, 750, 40],\n \"circle-stroke-width\": 1,\n \"circle-stroke-color\": \"#fff\",\n \"circle-opacity\": 0.85,\n },\n });\n map.addLayer({\n id: `${sourceId}-cluster-count`,\n type: \"symbol\",\n source: sourceId,\n filter: [\"has\", \"point_count\"],\n layout: {\n \"text-field\": \"{point_count_abbreviated}\",\n \"text-size\": 12,\n },\n paint: { \"text-color\": \"#fff\" },\n });\n\n // Click cluster to zoom in\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const onClusterClick = async (e: any) => {\n const features = map.queryRenderedFeatures(e.point, { layers: [`${sourceId}-cluster`] });\n if (!features.length) return;\n const clusterId = features[0].properties?.cluster_id as number;\n const source = map.getSource(sourceId) as maplibregl.GeoJSONSource;\n const zoom = await source.getClusterExpansionZoom(clusterId);\n map.easeTo({ center: (features[0].geometry as GeoJSON.Point).coordinates as [number, number], zoom });\n };\n map.on(\"click\", `${sourceId}-cluster`, onClusterClick);\n map.on(\"mouseenter\", `${sourceId}-cluster`, () => { map.getCanvas().style.cursor = \"pointer\"; });\n map.on(\"mouseleave\", `${sourceId}-cluster`, () => { map.getCanvas().style.cursor = \"\"; });\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const clusterHandlers: Array<{ event: string; layer: string; handler: any }> = [\n { event: \"click\", layer: `${sourceId}-cluster`, handler: onClusterClick },\n ];\n layerHandlersRef.current[`${sourceId}-cluster`] = clusterHandlers;\n }\n\n // Hover tooltip\n if (layer.tooltip && layer.tooltip.length > 0) {\n const columns = layer.tooltip;\n const subLayers = [\n `${sourceId}-fill`,\n `${sourceId}-circle`,\n `${sourceId}-line`,\n ];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const handlers: Array<{ event: string; layer: string; handler: any }> = [];\n\n for (const subLayer of subLayers) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const onMove = (e: any) => {\n if (!e.features || e.features.length === 0) return;\n const props = (e.features[0].properties ?? {}) as Record<\n string,\n unknown\n >;\n map.getCanvas().style.cursor = \"pointer\";\n setLayerTooltip({ properties: props, columns });\n\n const popup = layerTooltipPopupRef.current;\n if (popup) {\n popup.setLngLat(e.lngLat).addTo(map);\n }\n };\n const onLeave = () => {\n map.getCanvas().style.cursor = \"\";\n setLayerTooltip(null);\n layerTooltipPopupRef.current?.remove();\n };\n\n map.on(\"mousemove\", subLayer, onMove);\n map.on(\"mouseleave\", subLayer, onLeave);\n handlers.push({ event: \"mousemove\", layer: subLayer, handler: onMove });\n handlers.push({ event: \"mouseleave\", layer: subLayer, handler: onLeave });\n }\n\n layerHandlersRef.current[sourceId] = handlers;\n }\n } catch (err) {\n console.warn(`[json-maps] Failed to add layer \"${id}\":`, err);\n try { removeLayer(map, id); } catch { /* ignore */ }\n }\n }\n\n function addRouteLayer(\n map: maplibregl.Map,\n id: string,\n layer: RouteLayerSpec,\n ) {\n const sourceId = `jm-${id}`;\n\n try {\n const style = layer.style ?? {};\n\n map.addSource(sourceId, {\n type: \"geojson\",\n data: {\n type: \"Feature\",\n properties: {},\n geometry: { type: \"LineString\", coordinates: layer.coordinates },\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const paint: any = {\n \"line-color\": style.color ?? \"#3b82f6\",\n \"line-width\": style.width ?? 3,\n \"line-opacity\": style.opacity ?? 0.8,\n };\n if (style.dashed) {\n paint[\"line-dasharray\"] = [6, 3];\n }\n\n map.addLayer({\n id: `${sourceId}-line`,\n type: \"line\",\n source: sourceId,\n layout: { \"line-join\": \"round\", \"line-cap\": \"round\" },\n paint,\n });\n } catch (err) {\n console.warn(`[json-maps] Failed to add route \"${id}\":`, err);\n try { removeLayer(map, id); } catch { /* ignore */ }\n }\n }\n\n function removeLayer(map: maplibregl.Map, id: string) {\n const sourceId = `jm-${id}`;\n\n // Remove event listeners\n for (const key of [sourceId, `${sourceId}-cluster`]) {\n const handlers = layerHandlersRef.current[key];\n if (handlers) {\n for (const h of handlers) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (map as any).off(h.event, h.layer, h.handler);\n }\n delete layerHandlersRef.current[key];\n }\n }\n\n const subLayers = [\n `${sourceId}-cluster-count`,\n `${sourceId}-cluster`,\n `${sourceId}-circle`,\n `${sourceId}-line`,\n `${sourceId}-fill`,\n ];\n for (const layerId of subLayers) {\n if (map.getLayer(layerId)) map.removeLayer(layerId);\n }\n if (map.getSource(sourceId)) map.removeSource(sourceId);\n }\n\n /* ---- Sync functions ---- */\n\n const syncMarkers = useCallback(() => {\n const map = mapRef.current;\n if (!map) return;\n\n const specMarkers = spec.markers ?? {};\n\n for (const id of [...markersRef.current.keys()]) {\n if (!specMarkers[id]) removeMarker(id);\n }\n\n for (const [id, ms] of Object.entries(specMarkers)) {\n const existing = markersRef.current.get(id);\n\n if (existing) {\n existing.setLngLat(ms.coordinates);\n const el = existing.getElement();\n if (el.dataset.color !== (ms.color ?? \"\") || el.dataset.icon !== (ms.icon ?? \"\")) {\n removeMarker(id);\n addMarker(map, id, ms);\n }\n } else {\n addMarker(map, id, ms);\n }\n }\n\n setPortalVersion((v) => v + 1);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [spec.markers]);\n\n const syncLayers = useCallback(() => {\n const map = mapRef.current;\n if (!map) return;\n\n if (!map.isStyleLoaded()) {\n // Style not ready — poll via rAF until it is (one loop at a time)\n if (!pendingLayerSyncRef.current) {\n pendingLayerSyncRef.current = true;\n const check = () => {\n if (!mapRef.current || !pendingLayerSyncRef.current) return;\n if (mapRef.current.isStyleLoaded()) {\n pendingLayerSyncRef.current = false;\n syncLayersRef.current();\n } else {\n requestAnimationFrame(check);\n }\n };\n requestAnimationFrame(check);\n }\n return;\n }\n\n pendingLayerSyncRef.current = false;\n\n const specLayers = spec.layers ?? {};\n const prevSpecs = layerSpecsRef.current;\n\n // Remove layers no longer in spec\n for (const id of Object.keys(prevSpecs)) {\n if (!specLayers[id]) {\n removeLayer(map, id);\n delete prevSpecs[id];\n }\n }\n\n // Add or update layers\n for (const [id, layerSpec] of Object.entries(specLayers)) {\n const serialized = JSON.stringify(layerSpec);\n const sourceExists = !!map.getSource(`jm-${id}`);\n\n if (prevSpecs[id] === serialized && sourceExists) continue;\n\n if (sourceExists) {\n removeLayer(map, id);\n }\n\n if (layerSpec.type === \"route\") {\n addRouteLayer(map, id, layerSpec);\n } else {\n addGeoJsonLayer(map, id, layerSpec);\n }\n prevSpecs[id] = serialized;\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [spec.layers]);\n\n // Refs so basemap effect can call latest sync without re-triggering setStyle\n const syncMarkersRef = useRef(syncMarkers);\n const syncLayersRef = useRef(syncLayers);\n useEffect(() => { syncMarkersRef.current = syncMarkers; }, [syncMarkers]);\n useEffect(() => { syncLayersRef.current = syncLayers; }, [syncLayers]);\n\n /* ---- Effects ---- */\n\n // Create map once\n useEffect(() => {\n if (!containerRef.current) return;\n\n const map = new maplibregl.Map({\n container: containerRef.current,\n style: resolveBasemapStyle(spec.basemap) ?? resolveBasemapStyle()!,\n center: spec.center ?? DEFAULT_CENTER,\n zoom: spec.zoom ?? DEFAULT_ZOOM,\n pitch: spec.pitch ?? 0,\n bearing: spec.bearing ?? 0,\n attributionControl: false,\n });\n\n // Sync layers after initial style load (addSource/addLayer need style ready)\n map.on(\"load\", () => {\n if (spec.bounds) {\n map.fitBounds(spec.bounds as [number, number, number, number], {\n padding: 40,\n duration: 0,\n });\n }\n syncLayersRef.current();\n });\n\n // Create layer tooltip popup (reused for all layers)\n const tooltipContainer = document.createElement(\"div\");\n const tooltipPopup = new maplibregl.Popup({\n offset: 12,\n closeButton: false,\n closeOnClick: false,\n maxWidth: \"none\",\n }).setDOMContent(tooltipContainer);\n\n layerTooltipContainerRef.current = tooltipContainer;\n layerTooltipPopupRef.current = tooltipPopup;\n\n mapRef.current = map;\n\n return () => {\n for (const id of markersRef.current.keys()) removeMarker(id);\n markersRef.current.clear();\n portalsRef.current.clear();\n tooltipPopup.remove();\n layerTooltipPopupRef.current = null;\n layerTooltipContainerRef.current = null;\n layerSpecsRef.current = {};\n map.remove();\n mapRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Track basemap to skip redundant setStyle on mount\n const prevBasemapRef = useRef(spec.basemap);\n\n // Update basemap — only when basemap actually changes (uses refs for sync callbacks)\n useEffect(() => {\n const map = mapRef.current;\n if (!map) return;\n // Skip on mount — init effect already set the style\n if (prevBasemapRef.current === spec.basemap) return;\n prevBasemapRef.current = spec.basemap;\n const style = resolveBasemapStyle(spec.basemap);\n if (style) {\n map.setStyle(style);\n map.once(\"styledata\", () => {\n syncMarkersRef.current();\n syncLayersRef.current();\n });\n }\n }, [spec.basemap]);\n\n // Update viewport\n useEffect(() => {\n if (!mapRef.current) return;\n\n if (spec.bounds) {\n mapRef.current.fitBounds(\n spec.bounds as [number, number, number, number],\n { padding: 40 },\n );\n return;\n }\n\n // Auto-fit to markers (takes priority over center+zoom)\n const markers = spec.markers ? Object.values(spec.markers) : [];\n if (markers.length > 0) {\n const lngs = markers.map((m) => m.coordinates[0]);\n const lats = markers.map((m) => m.coordinates[1]);\n let west = Math.min(...lngs);\n let south = Math.min(...lats);\n let east = Math.max(...lngs);\n let north = Math.max(...lats);\n // Ensure a minimum span so fitBounds works with 1-2 close points\n const MIN_SPAN = 0.01; // ~1km\n if (east - west < MIN_SPAN) {\n const midLng = (west + east) / 2;\n west = midLng - MIN_SPAN / 2;\n east = midLng + MIN_SPAN / 2;\n }\n if (north - south < MIN_SPAN) {\n const midLat = (north + south) / 2;\n south = midLat - MIN_SPAN / 2;\n north = midLat + MIN_SPAN / 2;\n }\n mapRef.current.fitBounds([west, south, east, north], {\n padding: { top: 100, bottom: 100, left: 100, right: 100 },\n maxZoom: 15,\n });\n return;\n }\n\n mapRef.current.flyTo({\n center: spec.center ?? DEFAULT_CENTER,\n zoom: spec.zoom ?? DEFAULT_ZOOM,\n pitch: spec.pitch ?? 0,\n bearing: spec.bearing ?? 0,\n });\n }, [spec.center, spec.zoom, spec.pitch, spec.bearing, spec.bounds, spec.markers]);\n\n // Sync markers\n useEffect(() => {\n syncMarkers();\n }, [syncMarkers]);\n\n // Sync layers (syncLayers internally polls if style isn't ready yet)\n useEffect(() => {\n syncLayers();\n }, [syncLayers]);\n\n /* ---- Render portals ---- */\n\n const entries = Array.from(portalsRef.current.entries());\n\n return (\n <>\n <div ref={containerRef} className=\"relative w-full h-full\">\n {spec.controls && (\n <MapControls\n controls={spec.controls}\n mapRef={mapRef}\n containerRef={containerRef}\n />\n )}\n {spec.legend &&\n Object.entries(spec.legend).map(([id, leg]) => {\n const layer = spec.layers?.[leg.layer];\n const geoLayer = layer && layer.type === \"geojson\" ? layer : null;\n return (\n <MapLegend key={id} legendSpec={leg} layerSpec={geoLayer} dark={spec.basemap === \"dark\"} />\n );\n })}\n </div>\n {/* Marker portals */}\n {entries.map(([id, p]) => {\n const ms = spec.markers?.[id];\n if (!ms) return null;\n return (\n <Fragment key={id}>\n {createPortal(\n <MarkerDot color={ms.color || \"#3b82f6\"} icon={ms.icon} label={ms.label} />,\n p.markerEl,\n )}\n {p.popupContainer &&\n ms.popup &&\n createPortal(\n <PopupContent markerSpec={ms} />,\n p.popupContainer,\n )}\n {p.tooltipContainer &&\n ms.tooltip &&\n createPortal(\n <TooltipContent text={ms.tooltip} />,\n p.tooltipContainer,\n )}\n </Fragment>\n );\n })}\n {/* Layer tooltip portal */}\n {layerTooltipContainerRef.current &&\n layerTooltip &&\n createPortal(\n <LayerTooltipContent\n properties={layerTooltip.properties}\n columns={layerTooltip.columns}\n />,\n layerTooltipContainerRef.current,\n )}\n </>\n );\n}\n","export interface PopupSpec {\n title?: string;\n description?: string;\n image?: string;\n}\n\nexport interface MarkerSpec {\n coordinates: [number, number];\n color?: string;\n icon?: string;\n label?: string;\n tooltip?: string;\n popup?: string | PopupSpec;\n draggable?: boolean;\n}\n\n/* ---- Color system ---- */\n\nexport interface ContinuousColor {\n type: \"continuous\";\n attr: string;\n palette: string;\n domain?: [number, number];\n nullColor?: string;\n}\n\nexport interface CategoricalColor {\n type: \"categorical\";\n attr: string;\n palette: string;\n categories?: string[];\n nullColor?: string;\n}\n\nexport type ColorValue = string | ContinuousColor | CategoricalColor;\n\n/* ---- Data-driven size ---- */\n\nexport interface ContinuousSize {\n type: \"continuous\";\n attr: string;\n domain: [number, number];\n range: [number, number];\n}\n\nexport type SizeValue = number | ContinuousSize;\n\n/* ---- Layer system ---- */\n\nexport interface LayerStyle {\n fillColor?: ColorValue;\n pointColor?: ColorValue;\n lineColor?: ColorValue;\n lineWidth?: number;\n pointRadius?: SizeValue;\n opacity?: number;\n}\n\nexport interface ClusterOptions {\n radius?: number;\n maxZoom?: number;\n minPoints?: number;\n colors?: [string, string, string];\n}\n\nexport interface GeoJsonLayerSpec {\n type: \"geojson\";\n data: string | Record<string, unknown>;\n style?: LayerStyle;\n tooltip?: string[];\n cluster?: boolean;\n clusterOptions?: ClusterOptions;\n}\n\nexport interface RouteStyle {\n color?: string;\n width?: number;\n opacity?: number;\n dashed?: boolean;\n}\n\nexport interface RouteLayerSpec {\n type: \"route\";\n coordinates: [number, number][];\n style?: RouteStyle;\n}\n\nexport type LayerSpec = GeoJsonLayerSpec | RouteLayerSpec;\n\n/* ---- Controls ---- */\n\nexport type ControlPosition =\n | \"top-left\"\n | \"top-right\"\n | \"bottom-left\"\n | \"bottom-right\";\n\nexport interface ControlsSpec {\n zoom?: boolean;\n compass?: boolean;\n fullscreen?: boolean;\n locate?: boolean;\n position?: ControlPosition;\n}\n\n/* ---- Legend ---- */\n\nexport interface LegendSpec {\n layer: string;\n title?: string;\n position?: ControlPosition;\n}\n\n/* ---- Map spec ---- */\n\nexport interface MapSpec {\n basemap?: \"light\" | \"dark\" | \"streets\" | (string & {});\n center?: [number, number];\n zoom?: number;\n pitch?: number;\n bearing?: number;\n bounds?: [number, number, number, number];\n markers?: Record<string, MarkerSpec>;\n layers?: Record<string, LayerSpec>;\n controls?: ControlsSpec;\n legend?: Record<string, LegendSpec>;\n}\n\nexport const BASEMAP_STYLES: Record<string, string> = {\n light: \"https://basemaps.cartocdn.com/gl/positron-gl-style/style.json\",\n dark: \"https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json\",\n streets: \"https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json\",\n};\n\nexport function resolveBasemapStyle(basemap?: string): string | null {\n if (!basemap) return BASEMAP_STYLES.light!;\n if (BASEMAP_STYLES[basemap]) return BASEMAP_STYLES[basemap]!;\n if (basemap.startsWith(\"http\")) return basemap;\n return null;\n}\n","// CartoColor palette values (7-step for sequential/diverging, 12 for categorical)\n// Source: https://carto.com/carto-colors/\n\nexport const PALETTES: Record<string, string[]> = {\n // Sequential\n Burg: [\"#ffc6c4\", \"#f4a3a8\", \"#e38191\", \"#cc607d\", \"#ad466c\", \"#8b3058\", \"#672044\"],\n RedOr: [\"#f6d2a9\", \"#f5b78e\", \"#f19c7c\", \"#ea8171\", \"#dd686c\", \"#ca5268\", \"#b13f64\"],\n OrYel: [\"#ecda9a\", \"#efc47e\", \"#f3ad6a\", \"#f7945d\", \"#f97b57\", \"#f66356\", \"#ee4d5a\"],\n Peach: [\"#fde0c5\", \"#facba6\", \"#f8b58b\", \"#f2a275\", \"#e78f6a\", \"#d07c5e\", \"#b86b53\"],\n PinkYl: [\"#fef6b5\", \"#ffdd9a\", \"#ffc285\", \"#ffa679\", \"#fa8a76\", \"#f16d7a\", \"#e15383\"],\n Mint: [\"#e4f1e1\", \"#b4d9cc\", \"#89c0b6\", \"#63a6a0\", \"#448c8a\", \"#287274\", \"#0d585f\"],\n BluGrn: [\"#c4e6c3\", \"#96d2a4\", \"#6dbc90\", \"#4da284\", \"#36877a\", \"#266c6e\", \"#1d4f60\"],\n DarkMint: [\"#d2fbd4\", \"#a5dbc2\", \"#7bbcb0\", \"#559c9e\", \"#3a7c89\", \"#235d72\", \"#123f5a\"],\n Emrld: [\"#d3f2a3\", \"#97e196\", \"#6cc08b\", \"#4c9b82\", \"#217a79\", \"#105965\", \"#074050\"],\n BluYl: [\"#f7feae\", \"#b7e6a5\", \"#7ccba2\", \"#46aea0\", \"#089099\", \"#00718b\", \"#045275\"],\n Teal: [\"#d1eeea\", \"#a8dbd9\", \"#85c4c9\", \"#68abb8\", \"#4f90a6\", \"#3b738f\", \"#2a5674\"],\n Purp: [\"#f3e0f7\", \"#d1bbf0\", \"#b18fd3\", \"#8c6bb1\", \"#6c4f8e\", \"#4e3a6e\", \"#2f2047\"],\n Sunset: [\"#f3e79b\", \"#fac484\", \"#f8a07e\", \"#eb7f86\", \"#ce6693\", \"#a059a0\", \"#5c53a5\"],\n SunsetDark: [\"#fcde9c\", \"#faa476\", \"#f0746e\", \"#e34f6f\", \"#dc3977\", \"#b9257a\", \"#7c1d6f\"],\n Magenta: [\"#f3cbd3\", \"#eaa9bd\", \"#dd88ac\", \"#ca699d\", \"#b14d8e\", \"#91357d\", \"#6c2167\"],\n\n // Diverging\n TealRose: [\"#009392\", \"#39b185\", \"#9ccb86\", \"#e9e29c\", \"#eeb479\", \"#e88471\", \"#cf597e\"],\n Geyser: [\"#008080\", \"#70a494\", \"#b4c8a8\", \"#f6edbd\", \"#edbb8a\", \"#de8a5a\", \"#ca562c\"],\n Temps: [\"#009392\", \"#39b185\", \"#9ccb86\", \"#e9e29c\", \"#edbb8a\", \"#e68b5e\", \"#d43d51\"],\n Fall: [\"#3d5941\", \"#778868\", \"#b5b991\", \"#f6edbd\", \"#edbb8a\", \"#de8a5a\", \"#ca562c\"],\n ArmyRose: [\"#798234\", \"#a3ad62\", \"#d0d3a2\", \"#fef2c0\", \"#f5c0a1\", \"#ec7d6a\", \"#d8443c\"],\n Tropic: [\"#009b9e\", \"#42b7b9\", \"#a7d3d4\", \"#f1eceb\", \"#e4b4c2\", \"#ce78a7\", \"#7c1d6f\"],\n\n // Categorical\n Bold: [\"#7F3C8D\", \"#11A579\", \"#3969AC\", \"#F2B701\", \"#E73F74\", \"#80BA5A\", \"#E68310\", \"#008695\", \"#CF1C90\", \"#f97b72\", \"#4b4b8f\", \"#A5AA99\"],\n Pastel: [\"#66C5CC\", \"#F6CF71\", \"#F89C74\", \"#DCB0F2\", \"#87C55F\", \"#9EB9F3\", \"#FE88B1\", \"#C9DB74\", \"#8BE0A4\", \"#B497E7\", \"#D3B484\", \"#B3B3B3\"],\n Antique: [\"#855C75\", \"#D9AF6B\", \"#AF6458\", \"#736F4C\", \"#526A83\", \"#625377\", \"#68855C\", \"#9C9C5E\", \"#A06177\", \"#8C785D\", \"#467378\", \"#7C7C7C\"],\n Vivid: [\"#E58606\", \"#5D69B1\", \"#52BCA3\", \"#99C945\", \"#CC61B0\", \"#24796C\", \"#DAA51B\", \"#2F8AC4\", \"#764E9F\", \"#ED645A\", \"#CC3A8E\", \"#A5AA99\"],\n Prism: [\"#5F4690\", \"#1D6996\", \"#38A6A5\", \"#0F8554\", \"#73AF48\", \"#EDAD08\", \"#E17C05\", \"#CC503E\", \"#94346E\", \"#6F4070\", \"#994E95\", \"#666666\"],\n Safe: [\"#88CCEE\", \"#CC6677\", \"#DDCC77\", \"#117733\", \"#332288\", \"#AA4499\", \"#44AA99\", \"#999933\", \"#882255\", \"#661100\", \"#6699CC\", \"#888888\"],\n};\n"],"mappings":"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","names":["Fragment","_a"]}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "json-maps",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "description": "Declarative map renderer — drop in a JSON spec, get a full interactive map",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.js",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "sideEffects": false,
20
+ "scripts": {
21
+ "dev": "next dev --turbopack",
22
+ "build": "next build",
23
+ "build:lib": "tsup",
24
+ "start": "next start",
25
+ "lint": "eslint --max-warnings 0",
26
+ "prepublishOnly": "tsup"
27
+ },
28
+ "peerDependencies": {
29
+ "react": ">=18",
30
+ "react-dom": ">=18",
31
+ "maplibre-gl": ">=4",
32
+ "lucide-react": ">=0.300"
33
+ },
34
+ "dependencies": {
35
+ "@ai-sdk/anthropic": "^3.0.43",
36
+ "@mdx-js/loader": "^3.1.1",
37
+ "@mdx-js/react": "^3.1.1",
38
+ "@next/mdx": "^16.1.6",
39
+ "@radix-ui/react-slot": "^1.2.4",
40
+ "ai": "^6.0.86",
41
+ "class-variance-authority": "^0.7.1",
42
+ "clsx": "^2.1.1",
43
+ "lucide-react": "^0.562.0",
44
+ "maplibre-gl": "^5.18.0",
45
+ "next": "16.1.1",
46
+ "next-themes": "^0.4.6",
47
+ "react": "19.2.3",
48
+ "react-dom": "19.2.3",
49
+ "remark-gfm": "^4.0.1",
50
+ "shiki": "^3.21.0",
51
+ "sonner": "^2.0.7",
52
+ "tailwind-merge": "^3.4.0",
53
+ "zod": "^4.3.6"
54
+ },
55
+ "devDependencies": {
56
+ "@tailwindcss/postcss": "^4.1.18",
57
+ "@types/node": "^22.15.3",
58
+ "@types/react": "19.2.3",
59
+ "@types/react-dom": "19.2.3",
60
+ "postcss": "^8.5.6",
61
+ "tailwindcss": "^4.1.18",
62
+ "tsup": "^8.5.1",
63
+ "tw-animate-css": "^1.4.0",
64
+ "typescript": "5.9.2"
65
+ }
66
+ }